def test_should_not_accept_day_old_tick(self): cache = FxPricesCache() one_day_in_seconds = 86400 one_day_ago = get_time(-1 * one_day_in_seconds) try: tick = TickEvent('CHF_USD', one_day_ago, 1.1, 1.2) cache.set_rate(tick) self.fail( 'FX price cache should not have accepted a day old tick - [%s]' % tick) except AssertionError: pass
def test_should_not_accept_tick_older_than_max_acceptable_age(self): max_tick_age = 100 cache = FxPricesCache(max_tick_age=max_tick_age) older_than_max_tick_age = get_time(-2 * max_tick_age) try: tick = TickEvent('CHF_USD', older_than_max_tick_age, 1.1, 1.2) cache.set_rate(tick) self.fail( 'FX price cache should not have accepted a tick older than [%s] seconds - [%s]' % (max_tick_age, tick)) except AssertionError: pass
def setUp(self): self.portfolio = FxPortfolio('USD', 5000) self.cache = FxPricesCache() self.ccy_exposure_manager = \ CcyExposureLimitRiskEvaluator('USD', self.cache, ccy_limit=5000, ccy_limit_short=-5000) self.portfolio.set_price_cache(self.cache).set_ccy_exposure_manager( self.ccy_exposure_manager) self.large_buy_order = OrderEvent('EUR_USD', 1000000000, 'buy') self.large_sell_order = OrderEvent('EUR_USD', 1000000000, 'sell') self.buy_order = OrderEvent('EUR_USD', 1000, 'buy') self.sell_order = OrderEvent('EUR_USD', 1000, 'sell')
def test_should_not_filter_order_without_rates(self): rmo = CcyExposureLimitRiskEvaluator('ABC', FxPricesCache()) try: rmo.filter_order(OrderEvent('CHF_USD', 1000, 'buy')) self.fail('Filtered order without fx rates') except AssertionError: pass
def setUp(self): self.portfolio = FxPortfolio('USD', 5000) self.cache = FxPricesCache() self.ccy_exposure_manager = CcyExposureLimitRiskEvaluator( 'USD', self.cache) self.portfolio.set_price_cache(self.cache) self.portfolio.set_ccy_exposure_manager(self.ccy_exposure_manager) self.buy_100_CHF_USD = OrderEvent('CHF_USD', 100, 'buy') self.sell_100_CHF_USD = OrderEvent('CHF_USD', 100, 'sell') self.buy_50_CHF_USD = OrderEvent('CHF_USD', 50, 'buy') self.sell_50_CHF_USD = OrderEvent('CHF_USD', 50, 'sell') self.buy_100_EUR_USD = OrderEvent('EUR_USD', 100, 'buy') self.sell_100_EUR_USD = OrderEvent('EUR_USD', 100, 'sell') self.buy_50_EUR_USD = OrderEvent('EUR_USD', 50, 'buy') self.sell_50_EUR_USD = OrderEvent('EUR_USD', 50, 'sell')
def wire(self, com_q=None, in_q=None, out_q=None, hb_q=None, e_q=None): rates_cache = FxPricesCache(max_tick_age=self.max_tick_age) rates_cache_loop = EventLoop(events_q=in_q, handler=rates_cache, forward_q=out_q) rates_cache_loop.set_command_q(com_q) self.component = rates_cache return rates_cache_loop
def test_should_have_latest_of_twice_appended_rate(self): cache = FxPricesCache() cache.set_rate(TickEvent('CHF_USD', get_time(), 1.1, 1.2)) cache.set_rate(TickEvent('CHF_USD', get_time(), 1.15, 1.25)) rates = cache.get_rate('CHF_USD') self.assertEqual(1.15, rates['bid']) self.assertEqual(1.25, rates['ask'])
def play_event_loop(self, tick): events = Queue() cache = FxPricesCache() looper = EventLoop(events, cache) price_thread = Thread(target=looper.start, args=[]) price_thread.start() events.put(tick) sleep(2 * looper.heartbeat) looper.stop() price_thread.join(timeout=2 * looper.heartbeat) return cache
def test_should_init_with_rates_cache(self): cache = FxPricesCache() self.assertEquals( CcyExposureLimitRiskEvaluator('BC', cache, ccy_limits={}).rates_cache, cache)
def test_should_give_error_when_rate_tuple_not_set(self): cache = FxPricesCache() try: cache.get_rate_tuple('CHF_USD') except KeyError: pass
def test_should_have_appended_rate_tuple(self): cache = FxPricesCache() cache.set_rate(TickEvent('CHF_USD', get_time(), 1.1, 1.2)) rates = cache.get_rate_tuple('CHF_USD') self.assertEqual((1.1, 1.2), rates)
def test_can_append_rate(self): cache = FxPricesCache() cache.set_rate(TickEvent('CHF_USD', get_time(), 1.1, 1.2))
def test_prices_cache_exists(self): self.assertIsNotNone(FxPricesCache())
def test_should_have_preset_ccy_exposure_limit(self): rm = CcyExposureLimitRiskEvaluator('BC', FxPricesCache(), ccy_limit=123) self.assertEquals(rm.ccy_limit, 123)
def test_accept_day_old_tick_if_less_than_max_acceptable_age(self): one_day_in_seconds = 86400 cache = FxPricesCache(max_tick_age=2 * one_day_in_seconds) one_day_ago = get_time(-1 * one_day_in_seconds) tick = TickEvent('CHF_USD', one_day_ago, 1.1, 1.2) cache.set_rate(tick)
class TestFxPortfolio(unittest.TestCase): def setUp(self): self.portfolio = FxPortfolio('USD', 5000) self.cache = FxPricesCache() self.ccy_exposure_manager = CcyExposureLimitRiskEvaluator( 'USD', self.cache) self.portfolio.set_price_cache(self.cache) self.portfolio.set_ccy_exposure_manager(self.ccy_exposure_manager) self.buy_100_CHF_USD = OrderEvent('CHF_USD', 100, 'buy') self.sell_100_CHF_USD = OrderEvent('CHF_USD', 100, 'sell') self.buy_50_CHF_USD = OrderEvent('CHF_USD', 50, 'buy') self.sell_50_CHF_USD = OrderEvent('CHF_USD', 50, 'sell') self.buy_100_EUR_USD = OrderEvent('EUR_USD', 100, 'buy') self.sell_100_EUR_USD = OrderEvent('EUR_USD', 100, 'sell') self.buy_50_EUR_USD = OrderEvent('EUR_USD', 50, 'buy') self.sell_50_EUR_USD = OrderEvent('EUR_USD', 50, 'sell') def test_should_yield_positions_list_with_1_item_when_appended_executed_order( self): executed_order = ExecutedOrder(self.buy_100_CHF_USD, 1.1, 100) self.portfolio.append_position(executed_order) self.assertEqual(len(self.portfolio.list_positions()), 1) def test_should_yield_positions_list_with_1_item_when_appended_2_executed_order( self): executed_order1 = ExecutedOrder(self.buy_100_CHF_USD, 1.1, 100) self.portfolio.append_position(executed_order1) executed_order2 = ExecutedOrder(self.buy_50_CHF_USD, 1.15, 50) self.portfolio.append_position(executed_order2) self.assertEqual(len(self.portfolio.list_positions()), 1) def test_should_yield_executions_list_with_2_matching_executions_when_appended_2_executions( self): executed_order1 = ExecutedOrder(self.buy_100_CHF_USD, 1.1, 100) self.portfolio.append_position(executed_order1) executed_order2 = ExecutedOrder(self.buy_50_CHF_USD, 1.15, 50) self.portfolio.append_position(executed_order2) executions = self.portfolio.list_executions() self.assertEqual(executed_order1, executions[0]) self.assertEqual(executed_order2, executions[1]) def test_should_yield_position_with_expected_units_when_appended_2_executed_buy_and_sell_order( self): executed_order1 = ExecutedOrder(self.buy_100_CHF_USD, 1.1, 100) self.portfolio.append_position(executed_order1) executed_order2 = ExecutedOrder(self.sell_50_CHF_USD, 1.15, 50) self.portfolio.append_position(executed_order2) self.assertEqual(50, self.portfolio.list_position('CHF_USD')) def test_should_re_evaluate_long_position_when_new_market_rate_goes_up( self): executed_order = ExecutedOrder(self.buy_100_CHF_USD, 1.1, 100) self.portfolio.append_position(executed_order) self.cache.set_rate(TickEvent('CHF_USD', get_time(), 1.2, 1.3)) revalued = self.portfolio.reval_position('CHF_USD') self.assertEqual(20, revalued) def test_should_give_average_execution_price_long_only(self): executed_order1 = ExecutedOrder(self.buy_100_CHF_USD, 1.1, 100) self.portfolio.append_position(executed_order1) executed_order2 = ExecutedOrder(self.buy_100_CHF_USD, 1.2, 100) self.portfolio.append_position(executed_order2) expected = round((100 * 1.1 + 100 * 1.2) / 200, 2) self.assertEqual(expected, self.portfolio.get_avg_price('CHF_USD')) def test_should_re_evaluates_all_positions_after_1_order_is_executed(self): executed_order = ExecutedOrder(self.buy_100_CHF_USD, 1.1, 100) self.cache.set_rate(TickEvent('CHF_USD', get_time(), 1.21, 1.22)) self.portfolio.append_position(executed_order) expected = round(100 / 1.22 - 100, 2) self.assertEqual(expected, self.portfolio.reval_positions()) def test_should_re_evaluates_all_positions_after_2_orders_are_executed_net_long( self): executed_order1 = ExecutedOrder(self.buy_100_CHF_USD, 1.1, 100) self.portfolio.append_position(executed_order1) executed_order2 = ExecutedOrder(self.sell_50_CHF_USD, 1.15, 50) self.portfolio.append_position(executed_order2) self.cache.set_rate(TickEvent('CHF_USD', get_time(), 1.21, 1.22)) expected = round(50 / 1.22 - 50, 2) self.assertEqual(expected, self.portfolio.reval_positions()) def test_should_re_evaluates_all_positions_after_2_orders_are_executed_in_diff_ccy( self): executed_order1 = ExecutedOrder(self.buy_100_CHF_USD, 1.1, 100) self.portfolio.append_position(executed_order1) executed_order2 = ExecutedOrder(self.buy_100_EUR_USD, 0.9, 100) self.portfolio.append_position(executed_order2) self.cache.set_rate(TickEvent('CHF_USD', get_time(), 1.21, 1.22)) self.cache.set_rate(TickEvent('EUR_USD', get_time(), 0.91, 0.92)) expected = round(100 / 1.22 + 100 / 0.92 - 200, 2) self.assertEqual(expected, self.portfolio.reval_positions()) def test_should_re_evaluates_all_positions_after_2_orders_are_executed_in_diff_ccy_buy_sell( self): executed_order1 = ExecutedOrder(self.buy_100_CHF_USD, 1.1, 100) self.portfolio.append_position(executed_order1) executed_order2 = ExecutedOrder(self.sell_50_EUR_USD, 0.9, 50) self.portfolio.append_position(executed_order2) self.cache.set_rate(TickEvent('CHF_USD', get_time(), 1.21, 1.22)) self.cache.set_rate(TickEvent('EUR_USD', get_time(), 0.91, 0.92)) expected = round(100 / 1.22 - 50 / 0.91 - 50, 2) self.assertEqual(expected, self.portfolio.reval_positions()) def test_should_re_evaluates_all_positions_after_2_orders_are_executed_net_short( self): executed_order1 = ExecutedOrder(self.sell_100_CHF_USD, 1.1, 100) self.portfolio.append_position(executed_order1) executed_order2 = ExecutedOrder(self.buy_50_CHF_USD, 1.15, 50) self.portfolio.append_position(executed_order2) self.cache.set_rate(TickEvent('CHF_USD', get_time(), 1.21, 1.22)) expected = round(-50 / 1.21 + 50, 2) self.assertEqual(expected, self.portfolio.reval_positions()) def test_should_not_allow_ccy_exposure_manager_of_different_base_ccy(self): try: portfolio = FxPortfolio('USD', 100) portfolio.set_ccy_exposure_manager( CcyExposureLimitRiskEvaluator('CHF', self.cache)) except ValueError: pass def test_should_update_realized_pnl_when_appending_long_first_and_equal_short_second( self): executed_order = ExecutedOrder(self.buy_100_EUR_USD, 1.4, 100) executed_order2 = ExecutedOrder(self.sell_100_EUR_USD, 1.5, 100) self.portfolio.append_position(executed_order) self.portfolio.append_position(executed_order2) self.assertEqual(10, self.portfolio.get_realized_pnl()) def test_should_update_realized_pnl_when_appending_short_first_and_equal_long_second( self): executed_order = ExecutedOrder(self.sell_100_EUR_USD, 1.4, 100) executed_order2 = ExecutedOrder(self.buy_100_EUR_USD, 1.5, 100) self.portfolio.append_position(executed_order) self.portfolio.append_position(executed_order2) self.assertEqual(10, self.portfolio.get_realized_pnl()) def test_should_not_update_realized_pnl_when_execution_opposite_of_previously_open_position_but_diff_instrument( self): executed_order = ExecutedOrder(self.buy_100_EUR_USD, 1.4, 100) executed_order2 = ExecutedOrder(self.sell_100_CHF_USD, 1.1, 100) self.portfolio.append_position(executed_order) self.portfolio.append_position(executed_order2) self.assertEqual(0, self.portfolio.get_realized_pnl()) def test_should_update_realized_pnl_when_closing_one_position_while_another_open_position_in_diff_instrument_exists( self): executed_order = ExecutedOrder(self.buy_100_EUR_USD, 1.4, 100) executed_order2 = ExecutedOrder(self.sell_100_CHF_USD, 1.1, 100) executed_order3 = ExecutedOrder(self.sell_100_EUR_USD, 1.45, 100) self.portfolio.append_position(executed_order) self.portfolio.append_position(executed_order2) self.portfolio.append_position(executed_order3) self.assertEqual(5, self.portfolio.get_realized_pnl()) def test_should_update_negative_realized_pnl_when_appending_closing_position_at_loss( self): executed_order = ExecutedOrder(self.buy_100_EUR_USD, 1.4, 100) executed_order2 = ExecutedOrder(self.sell_100_EUR_USD, 1.3, 100) self.portfolio.append_position(executed_order) self.portfolio.append_position(executed_order2) self.assertEqual(-10, self.portfolio.get_realized_pnl()) def test_should_have_zero_unrealized_pnl_after_closing_position(self): executed_order = ExecutedOrder(self.buy_100_EUR_USD, 1.4, 100) executed_order2 = ExecutedOrder(self.sell_100_EUR_USD, 1.3, 100) self.portfolio.append_position(executed_order) self.portfolio.append_position(executed_order2) self.cache.set_rate(TickEvent('EUR_USD', get_time(), 0.91, 0.92)) self.assertEqual(0, self.portfolio.reval_positions()) def test_should_have_correct_realized_pnl_after_partial_close(self): executed_order = ExecutedOrder(self.buy_100_EUR_USD, 1.4, 100) executed_order2 = ExecutedOrder(self.sell_50_EUR_USD, 1.3, 50) self.portfolio.append_position(executed_order) self.portfolio.append_position(executed_order2) self.cache.set_rate(TickEvent('EUR_USD', get_time(), 0.91, 0.92)) self.assertEqual(-5, self.portfolio.get_realized_pnl()) def test_should_have_correct_unrealized_pnl_after_partial_close(self): executed_order = ExecutedOrder(self.buy_100_EUR_USD, 0.905, 100) executed_order2 = ExecutedOrder(self.sell_50_EUR_USD, 0.900, 50) self.portfolio.append_position(executed_order) self.portfolio.append_position(executed_order2) self.cache.set_rate(TickEvent('EUR_USD', get_time(), 0.910, 0.920)) expected = round(50 / 0.920 - 50, 2) self.assertEqual(expected, self.portfolio.reval_positions())
class TestFxPortfolio(unittest.TestCase): def setUp(self): self.portfolio = FxPortfolio('USD', 10000) self.cache = FxPricesCache() self.portfolio.set_price_cache(self.cache) def test_should_yields_empty_list_when_no_trade_exists(self): self.assertIsNotNone(self.portfolio.list_positions()) self.assertEqual(0, len(self.portfolio.list_positions())) def test_should_append_executed_order(self): executed_order = ExecutedOrder(OrderEvent('CHF_USD', 100, 'buy'), 1.1, 100) self.portfolio.append_position(executed_order) def test_should_yield_not_none_positions_list_when_appended_executed_order( self): executed_order = ExecutedOrder(OrderEvent('CHF_USD', 100, 'buy'), 1.1, 100) self.portfolio.append_position(executed_order) self.assertIsNotNone(self.portfolio.list_positions()) def test_should_yield_non_empty_positions_list_when_appended_executed_order( self): executed_order = ExecutedOrder(OrderEvent('CHF_USD', 100, 'buy'), 1.1, 100) self.portfolio.append_position(executed_order) self.assertIsNotNone(self.portfolio.list_positions()) self.assertTrue(len(self.portfolio.list_positions()) > 0) def test_should_yield_not_none_executions_list_when_appended_executed_order( self): executed_order = ExecutedOrder(OrderEvent('CHF_USD', 100, 'buy'), 1.1, 100) self.portfolio.append_position(executed_order) self.assertIsNotNone(self.portfolio.list_executions()) def test_should_yield_non_empty_executions_list_when_appended_executed_order( self): executed_order = ExecutedOrder(OrderEvent('CHF_USD', 100, 'buy'), 1.1, 100) self.portfolio.append_position(executed_order) self.assertIsNotNone([], self.portfolio.list_positions()) self.assertTrue(len(self.portfolio.list_executions()) > 0) def test_should_yield_executions_list_with_1_item_when_appended_executed_order( self): executed_order = ExecutedOrder(OrderEvent('CHF_USD', 100, 'buy'), 1.1, 100) self.portfolio.append_position(executed_order) self.assertEqual(len(self.portfolio.list_executions()), 1) def test_should_yield_executions_list_with_2_items_when_appended_2_executed_order( self): executed_order1 = ExecutedOrder(OrderEvent('CHF_USD', 100, 'buy'), 1.1, 100) self.portfolio.append_position(executed_order1) executed_order2 = ExecutedOrder(OrderEvent('CHF_USD', 100, 'buy'), 1.15, 50) self.portfolio.append_position(executed_order2) self.assertEqual(len(self.portfolio.list_executions()), 2) def test_should_yield_position_with_expected_units_when_appended_executed_order( self): executed_order = ExecutedOrder(OrderEvent('CHF_USD', 100, 'buy'), 1.1, 100) self.portfolio.append_position(executed_order) self.assertEqual(100, self.portfolio.list_position('CHF_USD')) def test_should_yield_position_with_expected_units_when_appended_2_executed_order( self): executed_order1 = ExecutedOrder(OrderEvent('CHF_USD', 100, 'buy'), 1.1, 100) self.portfolio.append_position(executed_order1) executed_order2 = ExecutedOrder(OrderEvent('CHF_USD', 100, 'buy'), 1.15, 50) self.portfolio.append_position(executed_order2) self.assertEqual(150, self.portfolio.list_position('CHF_USD')) def test_should_yield_position_with_expected_units_when_appended_2_executed_buy_and_sell_order_negative_pos( self): executed_order1 = ExecutedOrder(OrderEvent('CHF_USD', 100, 'sell'), 1.1, 100) self.portfolio.append_position(executed_order1) executed_order2 = ExecutedOrder(OrderEvent('CHF_USD', 50, 'buy'), 1.15, 50) self.portfolio.append_position(executed_order2) self.assertEqual(-50, self.portfolio.list_position('CHF_USD')) def test_should_give_average_execution_price_long_short(self): executed_order1 = ExecutedOrder(OrderEvent('CHF_USD', 100, 'buy'), 1.1, 100) self.portfolio.append_position(executed_order1) executed_order2 = ExecutedOrder(OrderEvent('CHF_USD', 50, 'sell'), 1.2, 50) self.portfolio.append_position(executed_order2) expected = 1.1 self.assertEqual(50, self.portfolio.list_position('CHF_USD')) self.assertEqual(expected, self.portfolio.get_avg_price('CHF_USD')) def test_should_give_average_execution_price_short_long(self): executed_order1 = ExecutedOrder(OrderEvent('CHF_USD', 50, 'sell'), 1.1, 50) self.portfolio.append_position(executed_order1) executed_order2 = ExecutedOrder(OrderEvent('CHF_USD', 100, 'buy'), 1.2, 100) self.portfolio.append_position(executed_order2) expected = 1.2 self.assertEqual(50, self.portfolio.list_position('CHF_USD')) self.assertEqual(expected, self.portfolio.get_avg_price('CHF_USD')) def test_should_give_average_execution_price_long_less_than_short(self): executed_order1 = ExecutedOrder(OrderEvent('CHF_USD', 100, 'buy'), 1.1, 100) self.portfolio.append_position(executed_order1) executed_order2 = ExecutedOrder(OrderEvent('CHF_USD', 150, 'sell'), 1.2, 150) self.portfolio.append_position(executed_order2) expected = 1.2 self.assertEqual(-50, self.portfolio.list_position('CHF_USD')) self.assertEqual(expected, self.portfolio.get_avg_price('CHF_USD')) def test_should_give_average_execution_price_short_only(self): executed_order1 = ExecutedOrder(OrderEvent('CHF_USD', 100, 'sell'), 1.1, 100) self.portfolio.append_position(executed_order1) executed_order2 = ExecutedOrder(OrderEvent('CHF_USD', 100, 'sell'), 1.2, 100) self.portfolio.append_position(executed_order2) expected = abs(round((-100 * 1.1 - 100 * 1.2) / 200, 2)) self.assertEqual(expected, self.portfolio.get_avg_price('CHF_USD')) def test_should_re_evaluate_short_position_when_new_market_rate_goes_up( self): executed_order = ExecutedOrder(OrderEvent('CHF_USD', 100, 'sell'), 1.1, 100) self.portfolio.append_position(executed_order) self.cache.set_rate(TickEvent('CHF_USD', get_time(), 1.2, 1.3)) revalued = self.portfolio.reval_position('CHF_USD') self.assertEqual(-10, revalued) def test_should_re_evaluate_long_position_when_new_market_rate_goes_down( self): executed_order = ExecutedOrder(OrderEvent('CHF_USD', 100, 'buy'), 1.1, 100) self.portfolio.append_position(executed_order) self.cache.set_rate(TickEvent('CHF_USD', get_time(), 1.051, 1.064)) revalued = self.portfolio.reval_position('CHF_USD') expected = round(100 * (1.064 - 1.1), 2) self.assertEqual(expected, revalued) def test_should_re_evaluate_short_position_when_new_market_rate_goes_down( self): executed_order = ExecutedOrder(OrderEvent('CHF_USD', 100, 'sell'), 1.1, 100) self.portfolio.append_position(executed_order) self.cache.set_rate(TickEvent('CHF_USD', get_time(), 1.051, 1.064)) revalued = self.portfolio.reval_position('CHF_USD') expected = round(-100 * (1.051 - 1.1), 2) self.assertEqual(expected, revalued) def test_should_have_portfolio_limit(self): self.assertIsNotNone(self.portfolio) def test_should_have_default_portfolio_limit(self): self.assertTrue(self.portfolio.port_limit > 0) def test_should_have_preset_portfolio_limit(self): portfolio = FxPortfolio('USD', 10000, port_limit=1230) self.assertEquals(portfolio.port_limit, 1230) def test_should_give_realized_pnl(self): self.assertIsNotNone(self.portfolio.get_realized_pnl()) def test_should_have_balance(self): self.assertIsNotNone(self.portfolio.get_balance())
def setUp(self): self.portfolio = FxPortfolio('USD', 10000) self.cache = FxPricesCache() self.portfolio.set_price_cache(self.cache)
def step_impl(context): context.rates_cache = FxPricesCache()
def setUp(self): self.cache = FxPricesCache()
def setUp(self): self.cache = FxPricesCache() self.rm = CcyExposureLimitRiskEvaluator('USD', self.cache, ccy_limit_short=-100000) self.assign_dummy_rates(self.rm)
class TestFxPricesCache(unittest.TestCase): def setUp(self): self.cache = FxPricesCache() def test_prices_cache_exists(self): self.assertIsNotNone(FxPricesCache()) def test_can_append_rate(self): cache = FxPricesCache() cache.set_rate(TickEvent('CHF_USD', get_time(), 1.1, 1.2)) def test_should_have_appended_rate(self): cache = FxPricesCache() cache.set_rate(TickEvent('CHF_USD', get_time(), 1.1, 1.2)) rates = cache.get_rate('CHF_USD') self.assertEqual(1.1, rates['bid']) self.assertEqual(1.2, rates['ask']) def test_should_have_appended_rate_tuple(self): cache = FxPricesCache() cache.set_rate(TickEvent('CHF_USD', get_time(), 1.1, 1.2)) rates = cache.get_rate_tuple('CHF_USD') self.assertEqual((1.1, 1.2), rates) def test_should_have_latest_of_twice_appended_rate(self): cache = FxPricesCache() cache.set_rate(TickEvent('CHF_USD', get_time(), 1.1, 1.2)) cache.set_rate(TickEvent('CHF_USD', get_time(), 1.15, 1.25)) rates = cache.get_rate('CHF_USD') self.assertEqual(1.15, rates['bid']) self.assertEqual(1.25, rates['ask']) def test_should_have_latest_of_twice_appended_rate_tuple(self): cache = FxPricesCache() cache.set_rate(TickEvent('CHF_USD', get_time(), 1.1, 1.2)) cache.set_rate(TickEvent('CHF_USD', get_time(), 1.15, 1.25)) rates = cache.get_rate_tuple('CHF_USD') self.assertEqual((1.15, 1.25), rates) def test_should_give_error_when_rate_not_set(self): cache = FxPricesCache() try: cache.get_rate('CHF_USD') except KeyError: pass def test_should_give_error_when_rate_tuple_not_set(self): cache = FxPricesCache() try: cache.get_rate_tuple('CHF_USD') except KeyError: pass def test_give_unity_for_same_ccy(self): self.assertEqual(self.cache.get_rate('CHF_CHF'), { 'bid': 1.0, 'ask': 1.0 }) def test_give_unity_tuple_for_same_ccy(self): self.assertEqual(self.cache.get_rate_tuple('CHF_CHF'), (1.0, 1.0)) def test_should_not_accept_day_old_tick(self): cache = FxPricesCache() one_day_in_seconds = 86400 one_day_ago = get_time(-1 * one_day_in_seconds) try: tick = TickEvent('CHF_USD', one_day_ago, 1.1, 1.2) cache.set_rate(tick) self.fail( 'FX price cache should not have accepted a day old tick - [%s]' % tick) except AssertionError: pass def test_should_not_accept_tick_older_than_max_acceptable_age(self): max_tick_age = 100 cache = FxPricesCache(max_tick_age=max_tick_age) older_than_max_tick_age = get_time(-2 * max_tick_age) try: tick = TickEvent('CHF_USD', older_than_max_tick_age, 1.1, 1.2) cache.set_rate(tick) self.fail( 'FX price cache should not have accepted a tick older than [%s] seconds - [%s]' % (max_tick_age, tick)) except AssertionError: pass def test_accept_day_old_tick_if_less_than_max_acceptable_age(self): one_day_in_seconds = 86400 cache = FxPricesCache(max_tick_age=2 * one_day_in_seconds) one_day_ago = get_time(-1 * one_day_in_seconds) tick = TickEvent('CHF_USD', one_day_ago, 1.1, 1.2) cache.set_rate(tick)
class TestFxPortfolio(unittest.TestCase): def setUp(self): self.portfolio = FxPortfolio('USD', 5000) self.cache = FxPricesCache() self.ccy_exposure_manager = \ CcyExposureLimitRiskEvaluator('USD', self.cache, ccy_limit=5000, ccy_limit_short=-5000) self.portfolio.set_price_cache(self.cache).set_ccy_exposure_manager( self.ccy_exposure_manager) self.large_buy_order = OrderEvent('EUR_USD', 1000000000, 'buy') self.large_sell_order = OrderEvent('EUR_USD', 1000000000, 'sell') self.buy_order = OrderEvent('EUR_USD', 1000, 'buy') self.sell_order = OrderEvent('EUR_USD', 1000, 'sell') def test_should_reduce_units_of_very_large_buy_order(self): self.cache.set_rate(TickEvent('EUR_USD', get_time(), 1.0, 1.0)) try: filtered_order = self.portfolio.check_order(self.large_buy_order) self.assertEqual(self.ccy_exposure_manager.ccy_limit, filtered_order.units) except RuntimeError as e: self.fail( 'Expecting a filtered order per currency manager\'s limit, but got exception - [%s]' % e) def test_should_reduce_units_of_very_large_sell_order(self): self.cache.set_rate(TickEvent('EUR_USD', get_time(), 1.0, 1.0)) try: filtered_order = self.portfolio.check_order(self.large_sell_order) self.assertEqual(self.ccy_exposure_manager.ccy_limit_short, -1 * filtered_order.units) except RuntimeError as e: self.fail( 'Expecting a filtered order per currency manager\'s limit, but got exception - [%s]' % e) def test_should_return_correct_order_after_checking(self): self.cache.set_rate(TickEvent('EUR_USD', get_time(), 1.0, 1.0)) try: filtered_order = self.portfolio.check_order(self.buy_order) self.assertEqual(self.buy_order.units, filtered_order.units) self.assertEqual(self.buy_order.instrument, filtered_order.instrument) self.assertEqual(self.buy_order.side, filtered_order.side) self.assertEqual(self.buy_order.order_type, filtered_order.order_type) except RuntimeError as e: self.fail( 'Expecting a filtered order per currency manager\'s limit, but got exception - [%s]' % e) def test_should_return_correct_order_after_processing(self): self.cache.set_rate(TickEvent('EUR_USD', get_time(), 1.0, 1.0)) try: filtered_order = self.portfolio.process(self.buy_order) self.assertEqual(self.buy_order.units, filtered_order.units) self.assertEqual(self.buy_order.instrument, filtered_order.instrument) self.assertEqual(self.buy_order.side, filtered_order.side) self.assertEqual(self.buy_order.order_type, filtered_order.order_type) except RuntimeError as e: self.fail( 'Expecting a filtered order per currency manager\'s limit, but got exception - [%s]' % e) def test_should_reduce_units_of_large_buy_order_using_position_limit_manager( self): rm = FxPositionLimitRiskEvaluator(posLimit=100) self.portfolio.add_risk_manager(rm) order = OrderEvent('EUR_USD', 1000, 'buy') self.cache.set_rate(TickEvent('EUR_USD', get_time(), 1.0, 1.0)) try: filtered_order = self.portfolio.check_order(order) self.assertEqual(rm.posLimit, filtered_order.units) except RuntimeError as e: self.fail( 'Expecting a filtered order per currency manager\'s limit, but got exception - [%s]' % e) def test_should_reduce_units_of_large_sell_order_using_position_limit_manager( self): rm = FxPositionLimitRiskEvaluator(posLimitShort=-100) self.portfolio.add_risk_manager(rm) order = OrderEvent('EUR_USD', 1000, 'sell') self.cache.set_rate(TickEvent('EUR_USD', get_time(), 1.0, 1.0)) try: filtered_order = self.portfolio.check_order(order) self.assertEqual(rm.posLimitShort, -1 * filtered_order.units) except RuntimeError as e: self.fail( 'Expecting a filtered order per currency manager\'s limit, but got exception - [%s]' % e) def test_should_reduce_units_of_large_buy_order_using_ccy_exposure_manager_when_pos_limit_manager_is_generous( self): rm = FxPositionLimitRiskEvaluator(posLimit=1000000) self.portfolio.add_risk_manager(rm) self.cache.set_rate(TickEvent('EUR_USD', get_time(), 1.0, 1.0)) try: filtered_order = self.portfolio.check_order(self.large_buy_order) self.assertEqual(self.ccy_exposure_manager.ccy_limit, filtered_order.units) except RuntimeError as e: self.fail( 'Expecting a filtered order per currency manager\'s limit, but got exception - [%s]' % e) def test_should_reduce_units_of_large_sell_order_using_ccy_exposure_manager_when_pos_limit_manager_is_generous( self): rm = FxPositionLimitRiskEvaluator(posLimitShort=-1000000) self.portfolio.add_risk_manager(rm) self.cache.set_rate(TickEvent('EUR_USD', get_time(), 1.0, 1.0)) try: filtered_order = self.portfolio.check_order(self.large_sell_order) self.assertEqual(self.ccy_exposure_manager.ccy_limit_short, -1 * filtered_order.units) except RuntimeError as e: self.fail( 'Expecting a filtered order per currency manager\'s limit, but got exception - [%s]' % e) def test_should_be_able_to_process_fills(self): self.cache.set_rate(TickEvent('EUR_USD', get_time(), 1.11, 1.15)) executed_order = ExecutedOrder(self.buy_order, 1.12, 100) processed_value = self.portfolio.process(executed_order) self.assertIsNone(processed_value) self.assertEqual({'EUR_USD': 100}, self.portfolio.list_positions()) def test_should_be_able_to_process_fills_or_signals(self): self.cache.set_rate(TickEvent('EUR_USD', get_time(), 1.11, 1.15)) # process a signal first processed_value = self.portfolio.process(self.buy_order) self.assertEqual(self.buy_order.units, processed_value.units) self.assertEqual(self.buy_order.instrument, processed_value.instrument) self.assertEqual(self.buy_order.side, processed_value.side) self.assertEqual(self.buy_order.order_type, processed_value.order_type) # process a fill now executed_order = ExecutedOrder(processed_value, 1.12, 100) processed_value = self.portfolio.process(executed_order) self.assertIsNone(processed_value) self.assertEqual({'EUR_USD': 100}, self.portfolio.list_positions()) def test_should_not_allow_to_add_same_risk_manager(self): rm = FxPositionLimitRiskEvaluator(posLimitShort=-1000000) self.portfolio.add_risk_manager(rm) try: self.portfolio.add_risk_manager(rm) self.fail('Should have failed while adding same risk manager') except: pass