def position(number=1, entry_price=None) -> Position: if entry_price is None: entry_price = Price("1.00000") generator = PositionIdGenerator(id_tag_trader=IdTag("001")) for _i in range(number): generator.generate(TestStubs.symbol_audusd_fxcm()) order_factory = OrderFactory( strategy_id=StrategyId("S", "001"), id_tag_trader=IdTag("001"), id_tag_strategy=IdTag("001"), clock=LiveClock(), ) order = order_factory.market( TestStubs.symbol_audusd_fxcm(), OrderSide.BUY, Quantity(100000), ) position_id = PositionId(TestStubs.symbol_audusd_fxcm().value) order_filled = TestStubs.event_order_filled( order, position_id=position_id, fill_price=entry_price, ) position = Position(event=order_filled) return position
class OrderPerformanceTests(unittest.TestCase): def setUp(self): # Fixture Setup self.generator = ClientOrderIdGenerator(IdTag("001"), IdTag("001"), LiveClock()) self.order_factory = OrderFactory( trader_id=TraderId("TESTER", "000"), strategy_id=StrategyId("S", "001"), clock=TestClock(), ) def create_market_order(self): self.order_factory.market( AUDUSD_SIM, OrderSide.BUY, Quantity(100000), ) def create_limit_order(self): self.order_factory.limit( AUDUSD_SIM, OrderSide.BUY, Quantity(100000), Price("0.80010"), ) def test_order_id_generator(self): PerformanceHarness.profile_function(self.generator.generate, 100000, 1) # ~0.0ms / ~2.9μs / 2894ns minimum of 100,000 runs @ 1 iteration each run. def test_market_order_creation(self): PerformanceHarness.profile_function(self.create_market_order, 10000, 1) # ~0.0ms / ~13.8μs / 13801ns minimum of 10,000 runs @ 1 iteration each run. def test_limit_order_creation(self): PerformanceHarness.profile_function(self.create_limit_order, 10000, 1)
class TestSerializationPerformance(PerformanceHarness): def setup(self): # Fixture Setup self.venue = Venue("SIM") self.trader_id = TestStubs.trader_id() self.account_id = TestStubs.account_id() self.order_factory = OrderFactory( trader_id=self.trader_id, strategy_id=StrategyId("S-001"), clock=TestClock(), ) self.order = self.order_factory.market( AUDUSD, OrderSide.BUY, Quantity.from_int(100000), ) self.command = SubmitOrder( self.trader_id, StrategyId("SCALPER-001"), PositionId("P-123456"), self.order, UUID4(), 0, ) self.serializer = MsgPackSerializer() @pytest.fixture(autouse=True) @pytest.mark.benchmark(disable_gc=True, warmup=True) def setup_benchmark(self, benchmark): self.benchmark = benchmark @pytest.mark.benchmark(disable_gc=True, warmup=True) def test_serialize_submit_order(self): self.benchmark.pedantic( target=self.serializer.serialize, args=(self.command, ), iterations=10_000, rounds=1, )
def test_order_serializer_methods_raise_not_implemented_error(self): # Arrange order_factory = OrderFactory( trader_id=TraderId("TESTER", "000"), strategy_id=StrategyId("S", "001"), clock=TestClock(), ) order = order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity(100000), ) serializer = OrderSerializer() # Act # Assert self.assertRaises(NotImplementedError, serializer.serialize, order) self.assertRaises(NotImplementedError, serializer.deserialize, bytes())
def test_order_serializer_methods_raise_not_implemented_error(self): # Arrange order_factory = OrderFactory( trader_id=TraderId("TESTER-000"), strategy_id=StrategyId("S-001"), clock=TestClock(), ) order = order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity(100000, precision=0), ) serializer = OrderSerializer() # Act # Assert with pytest.raises(NotImplementedError): serializer.serialize(order) with pytest.raises(NotImplementedError): serializer.deserialize(bytes())
class SerializationPerformanceTests(unittest.TestCase): def setUp(self): # Fixture Setup self.venue = Venue("SIM") self.trader_id = TestStubs.trader_id() self.account_id = TestStubs.account_id() self.serializer = MsgPackCommandSerializer() self.order_factory = OrderFactory( trader_id=self.trader_id, strategy_id=StrategyId("S", "001"), clock=TestClock(), ) self.order = self.order_factory.market( AUDUSD, OrderSide.BUY, Quantity(100000), ) self.command = SubmitOrder( self.venue, self.trader_id, self.account_id, StrategyId("SCALPER", "01"), PositionId("P-123456"), self.order, uuid4(), UNIX_EPOCH, ) def serialize_submit_order(self): # Arrange self.serializer.serialize(self.command) def test_make_builtin_uuid(self): PerformanceHarness.profile_function(self.serialize_submit_order, 10000, 1)
class TestAnalyzer: def setup(self): # Fixture Setup self.analyzer = PerformanceAnalyzer() self.order_factory = OrderFactory( trader_id=TraderId("TESTER-000"), strategy_id=StrategyId("S-001"), clock=TestClock(), ) def test_get_realized_pnls_when_no_data_returns_none(self): # Arrange, Act result = self.analyzer.realized_pnls() # Assert assert result is None def test_get_realized_pnls_with_currency_when_no_data_returns_none(self): # Arrange, Act result = self.analyzer.realized_pnls(AUD) # Assert assert result is None def test_analyzer_tracks_returns(self): # Arrange t1 = datetime(year=2010, month=1, day=1) t2 = datetime(year=2010, month=1, day=2) t3 = datetime(year=2010, month=1, day=3) t4 = datetime(year=2010, month=1, day=4) t5 = datetime(year=2010, month=1, day=5) t6 = datetime(year=2010, month=1, day=6) t7 = datetime(year=2010, month=1, day=7) t8 = datetime(year=2010, month=1, day=8) t9 = datetime(year=2010, month=1, day=9) t10 = datetime(year=2010, month=1, day=10) # Act self.analyzer.add_return(t1, 0.05) self.analyzer.add_return(t2, -0.10) self.analyzer.add_return(t3, 0.10) self.analyzer.add_return(t4, -0.21) self.analyzer.add_return(t5, 0.22) self.analyzer.add_return(t6, -0.23) self.analyzer.add_return(t7, 0.24) self.analyzer.add_return(t8, -0.25) self.analyzer.add_return(t9, 0.26) self.analyzer.add_return(t10, -0.10) self.analyzer.add_return(t10, -0.10) result = self.analyzer.returns() # Assert assert len(result) == 10 def test_get_realized_pnls_when_all_flat_positions_returns_expected_series( self): # Arrange order1 = self.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) order2 = self.order_factory.market( AUDUSD_SIM.id, OrderSide.SELL, Quantity.from_int(100000), ) order3 = self.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) order4 = self.order_factory.market( AUDUSD_SIM.id, OrderSide.SELL, Quantity.from_int(100000), ) fill1 = TestStubs.event_order_filled( order1, instrument=AUDUSD_SIM, position_id=PositionId("P-1"), strategy_id=StrategyId("S-001"), last_px=Price.from_str("1.00000"), ) fill2 = TestStubs.event_order_filled( order2, instrument=AUDUSD_SIM, position_id=PositionId("P-1"), strategy_id=StrategyId("S-001"), last_px=Price.from_str("1.00010"), ) fill3 = TestStubs.event_order_filled( order3, instrument=AUDUSD_SIM, position_id=PositionId("P-2"), strategy_id=StrategyId("S-001"), last_px=Price.from_str("1.00000"), ) fill4 = TestStubs.event_order_filled( order4, instrument=AUDUSD_SIM, position_id=PositionId("P-2"), strategy_id=StrategyId("S-001"), last_px=Price.from_str("1.00020"), ) position1 = Position(instrument=AUDUSD_SIM, fill=fill1) position1.apply(fill2) position2 = Position(instrument=AUDUSD_SIM, fill=fill3) position2.apply(fill4) self.analyzer.add_positions([position1, position2]) # Act result = self.analyzer.realized_pnls(USD) # Assert assert len(result) == 2 assert result["P-1"] == 6.0 assert result["P-2"] == 16.0
class TestCashAccount: def setup(self): # Fixture Setup self.trader_id = TestIdStubs.trader_id() self.order_factory = OrderFactory( trader_id=self.trader_id, strategy_id=StrategyId("S-001"), clock=TestClock(), ) def test_instantiated_accounts_basic_properties(self): # Arrange, Act account = TestExecStubs.cash_account() # Assert assert account == account assert not account != account assert account.id == AccountId("SIM", "000") assert str(account) == "CashAccount(id=SIM-000, type=CASH, base=USD)" assert repr(account) == "CashAccount(id=SIM-000, type=CASH, base=USD)" assert isinstance(hash(account), int) def test_instantiate_single_asset_cash_account(self): # Arrange event = AccountState( account_id=AccountId("SIM", "000"), account_type=AccountType.CASH, base_currency=USD, reported=True, balances=[ AccountBalance( Money(1_000_000, USD), Money(0, USD), Money(1_000_000, USD), ), ], margins=[], info={}, event_id=UUID4(), ts_event=0, ts_init=0, ) # Act account = CashAccount(event) # Assert assert account.base_currency == USD assert account.last_event == event assert account.events == [event] assert account.event_count == 1 assert account.balance_total() == Money(1_000_000, USD) assert account.balance_free() == Money(1_000_000, USD) assert account.balance_locked() == Money(0, USD) assert account.balances_total() == {USD: Money(1_000_000, USD)} assert account.balances_free() == {USD: Money(1_000_000, USD)} assert account.balances_locked() == {USD: Money(0, USD)} def test_instantiate_multi_asset_cash_account(self): # Arrange event = AccountState( account_id=AccountId("SIM", "000"), account_type=AccountType.CASH, base_currency=None, # Multi-currency reported=True, balances=[ AccountBalance( Money(10.00000000, BTC), Money(0.00000000, BTC), Money(10.00000000, BTC), ), AccountBalance( Money(20.00000000, ETH), Money(0.00000000, ETH), Money(20.00000000, ETH), ), ], margins=[], info={}, # No default currency set event_id=UUID4(), ts_event=0, ts_init=0, ) # Act account = CashAccount(event) # Assert assert account.id == AccountId("SIM", "000") assert account.base_currency is None assert account.last_event == event assert account.events == [event] assert account.event_count == 1 assert account.balance_total(BTC) == Money(10.00000000, BTC) assert account.balance_total(ETH) == Money(20.00000000, ETH) assert account.balance_free(BTC) == Money(10.00000000, BTC) assert account.balance_free(ETH) == Money(20.00000000, ETH) assert account.balance_locked(BTC) == Money(0.00000000, BTC) assert account.balance_locked(ETH) == Money(0.00000000, ETH) assert account.balances_total() == { BTC: Money(10.00000000, BTC), ETH: Money(20.00000000, ETH), } assert account.balances_free() == { BTC: Money(10.00000000, BTC), ETH: Money(20.00000000, ETH), } assert account.balances_locked() == { BTC: Money(0.00000000, BTC), ETH: Money(0.00000000, ETH), } def test_apply_given_new_state_event_updates_correctly(self): # Arrange event1 = AccountState( account_id=AccountId("SIM", "001"), account_type=AccountType.CASH, base_currency=None, # Multi-currency reported=True, balances=[ AccountBalance( Money(10.00000000, BTC), Money(0.00000000, BTC), Money(10.00000000, BTC), ), AccountBalance( Money(20.00000000, ETH), Money(0.00000000, ETH), Money(20.00000000, ETH), ), ], margins=[], info={}, # No default currency set event_id=UUID4(), ts_event=0, ts_init=0, ) # Act account = CashAccount(event1) event2 = AccountState( account_id=AccountId("SIM", "001"), account_type=AccountType.CASH, base_currency=None, # Multi-currency reported=True, balances=[ AccountBalance( Money(9.00000000, BTC), Money(0.50000000, BTC), Money(8.50000000, BTC), ), AccountBalance( Money(20.00000000, ETH), Money(0.00000000, ETH), Money(20.00000000, ETH), ), ], margins=[], info={}, # No default currency set event_id=UUID4(), ts_event=0, ts_init=0, ) # Act account.apply(event=event2) # Assert assert account.last_event == event2 assert account.events == [event1, event2] assert account.event_count == 2 assert account.balance_total(BTC) == Money(9.00000000, BTC) assert account.balance_free(BTC) == Money(8.50000000, BTC) assert account.balance_locked(BTC) == Money(0.50000000, BTC) assert account.balance_total(ETH) == Money(20.00000000, ETH) assert account.balance_free(ETH) == Money(20.00000000, ETH) assert account.balance_locked(ETH) == Money(0.00000000, ETH) def test_calculate_balance_locked_buy(self): # Arrange event = AccountState( account_id=AccountId("SIM", "001"), account_type=AccountType.CASH, base_currency=USD, reported=True, balances=[ AccountBalance( Money(1_000_000.00, USD), Money(0.00, USD), Money(1_000_000.00, USD), ), ], margins=[], info={}, # No default currency set event_id=UUID4(), ts_event=0, ts_init=0, ) account = CashAccount(event) # Act result = account.calculate_balance_locked( instrument=AUDUSD_SIM, side=OrderSide.BUY, quantity=Quantity.from_int(1_000_000), price=Price.from_str("0.80"), ) # Assert assert result == Money(800_032.00, USD) # Notional + expected commission def test_calculate_balance_locked_sell(self): # Arrange event = AccountState( account_id=AccountId("SIM", "001"), account_type=AccountType.CASH, base_currency=USD, reported=True, balances=[ AccountBalance( Money(1_000_000.00, USD), Money(0.00, USD), Money(1_000_000.00, USD), ), ], margins=[], info={}, # No default currency set event_id=UUID4(), ts_event=0, ts_init=0, ) account = CashAccount(event) # Act result = account.calculate_balance_locked( instrument=AUDUSD_SIM, side=OrderSide.SELL, quantity=Quantity.from_int(1_000_000), price=Price.from_str("0.80"), ) # Assert assert result == Money(1_000_040.00, AUD) # Notional + expected commission def test_calculate_pnls_for_single_currency_cash_account(self): # Arrange event = AccountState( account_id=AccountId("SIM", "001"), account_type=AccountType.CASH, base_currency=USD, reported=True, balances=[ AccountBalance( Money(1_000_000.00, USD), Money(0.00, USD), Money(1_000_000.00, USD), ), ], margins=[], info={}, # No default currency set event_id=UUID4(), ts_event=0, ts_init=0, ) account = CashAccount(event) order = self.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(1_000_000), ) fill = TestEventStubs.order_filled( order, instrument=AUDUSD_SIM, position_id=PositionId("P-123456"), strategy_id=StrategyId("S-001"), last_px=Price.from_str("0.80000"), ) position = Position(AUDUSD_SIM, fill) # Act result = account.calculate_pnls( instrument=AUDUSD_SIM, position=position, fill=fill, ) # Assert assert result == [Money(-800016.00, USD)] def test_calculate_pnls_for_multi_currency_cash_account_btcusdt(self): # Arrange event = AccountState( account_id=AccountId("SIM", "001"), account_type=AccountType.CASH, base_currency=None, # Multi-currency reported=True, balances=[ AccountBalance( Money(10.00000000, BTC), Money(0.00000000, BTC), Money(10.00000000, BTC), ), AccountBalance( Money(20.00000000, ETH), Money(0.00000000, ETH), Money(20.00000000, ETH), ), ], margins=[], info={}, # No default currency set event_id=UUID4(), ts_event=0, ts_init=0, ) account = CashAccount(event) order1 = self.order_factory.market( BTCUSDT_BINANCE.id, OrderSide.SELL, Quantity.from_str("0.50000000"), ) fill1 = TestEventStubs.order_filled( order1, instrument=BTCUSDT_BINANCE, position_id=PositionId("P-123456"), strategy_id=StrategyId("S-001"), last_px=Price.from_str("45500.00"), ) position = Position(BTCUSDT_BINANCE, fill1) # Act result1 = account.calculate_pnls( instrument=BTCUSDT_BINANCE, position=position, fill=fill1, ) order2 = self.order_factory.market( BTCUSDT_BINANCE.id, OrderSide.BUY, Quantity.from_str("0.50000000"), ) fill2 = TestEventStubs.order_filled( order2, instrument=BTCUSDT_BINANCE, position_id=PositionId("P-123456"), strategy_id=StrategyId("S-001"), last_px=Price.from_str("45500.00"), ) position.apply(fill2) result2 = account.calculate_pnls( instrument=BTCUSDT_BINANCE, position=position, fill=fill2, ) # Assert assert result1 == [ Money(-0.50000000, BTC), Money(22727.25000000, USDT) ] assert result2 == [ Money(0.50000000, BTC), Money(-22772.75000000, USDT) ] def test_calculate_pnls_for_multi_currency_cash_account_adabtc(self): # Arrange event = AccountState( account_id=AccountId("SIM", "001"), account_type=AccountType.CASH, base_currency=None, # Multi-currency reported=True, balances=[ AccountBalance( Money(1.00000000, BTC), Money(0.00000000, BTC), Money(1.00000000, BTC), ), AccountBalance( Money(1000.00000000, ADA), Money(0.00000000, ADA), Money(1000.00000000, ADA), ), ], margins=[], info={}, # No default currency set event_id=UUID4(), ts_event=0, ts_init=0, ) account = CashAccount(event) order = self.order_factory.market( ADABTC_BINANCE.id, OrderSide.BUY, Quantity.from_int(100), ) fill = TestEventStubs.order_filled( order, instrument=ADABTC_BINANCE, position_id=PositionId("P-123456"), strategy_id=StrategyId("S-001"), last_px=Price.from_str("0.00004100"), ) position = Position(ADABTC_BINANCE, fill) # Act result = account.calculate_pnls( instrument=ADABTC_BINANCE, position=position, fill=fill, ) # Assert assert result == [Money(100.000000, ADA), Money(-0.00410410, BTC)] def test_calculate_commission_when_given_liquidity_side_none_raises_value_error( self, ): # Arrange account = TestExecStubs.cash_account() instrument = TestInstrumentProvider.xbtusd_bitmex() # Act, Assert with pytest.raises(ValueError): account.calculate_commission( instrument=instrument, last_qty=Quantity.from_int(100000), last_px=Decimal("11450.50"), liquidity_side=LiquiditySide.NONE, ) @pytest.mark.parametrize( "inverse_as_quote, expected", [ [False, Money(-0.00218331, BTC)], # Negative commission = credit [True, Money(-25.00, USD)], # Negative commission = credit ], ) def test_calculate_commission_for_inverse_maker_crypto( self, inverse_as_quote, expected): # Arrange account = TestExecStubs.cash_account() instrument = TestInstrumentProvider.xbtusd_bitmex() # Act result = account.calculate_commission( instrument=instrument, last_qty=Quantity.from_int(100000), last_px=Decimal("11450.50"), liquidity_side=LiquiditySide.MAKER, inverse_as_quote=inverse_as_quote, ) # Assert assert result == expected def test_calculate_commission_for_taker_fx(self): # Arrange account = TestExecStubs.cash_account() instrument = AUDUSD_SIM # Act result = account.calculate_commission( instrument=instrument, last_qty=Quantity.from_int(1500000), last_px=Decimal("0.80050"), liquidity_side=LiquiditySide.TAKER, ) # Assert assert result == Money(24.02, USD) def test_calculate_commission_crypto_taker(self): # Arrange account = TestExecStubs.cash_account() instrument = TestInstrumentProvider.xbtusd_bitmex() # Act result = account.calculate_commission( instrument=instrument, last_qty=Quantity.from_int(100000), last_px=Decimal("11450.50"), liquidity_side=LiquiditySide.TAKER, ) # Assert assert result == Money(0.00654993, BTC) def test_calculate_commission_fx_taker(self): # Arrange account = TestExecStubs.cash_account() instrument = TestInstrumentProvider.default_fx_ccy( "USD/JPY", Venue("IDEALPRO")) # Act result = account.calculate_commission( instrument=instrument, last_qty=Quantity.from_int(2200000), last_px=Decimal("120.310"), liquidity_side=LiquiditySide.TAKER, ) # Assert assert result == Money(5294, JPY)
class TestBacktestExecClientTests: def setup(self): # Fixture Setup self.clock = TestClock() self.uuid_factory = UUIDFactory() self.logger = Logger(self.clock) self.trader_id = TraderId("TESTER-000") self.account_id = AccountId("BINANCE", "000") self.cache = TestStubs.cache() self.portfolio = Portfolio( cache=self.cache, clock=self.clock, logger=self.logger, ) self.exec_engine = ExecutionEngine( portfolio=self.portfolio, cache=self.cache, clock=self.clock, logger=self.logger, ) self.exchange = SimulatedExchange( venue=Venue("BINANCE"), venue_type=VenueType.EXCHANGE, oms_type=OMSType.NETTING, account_type=AccountType.CASH, base_currency=None, # Multi-currency account starting_balances=[Money(1_000_000, USDT)], is_frozen_account=False, instruments=[ETHUSDT_BINANCE], modules=[], cache=self.exec_engine.cache, fill_model=FillModel(), clock=self.clock, logger=self.logger, ) self.exec_client = BacktestExecClient( exchange=self.exchange, account_id=self.account_id, account_type=AccountType.CASH, base_currency=None, # Multi-currency account engine=self.exec_engine, clock=self.clock, logger=self.logger, ) self.order_factory = OrderFactory( trader_id=self.trader_id, strategy_id=StrategyId("SCALPER-001"), clock=self.clock, ) def test_is_connected_when_not_connected_returns_false(self): # Arrange # Act # Assert assert not self.exec_client.is_connected def test_connect(self): # Arrange # Act self.exec_client.connect() # Assert assert self.exec_client.is_connected def test_disconnect(self): # Arrange self.exec_client.connect() # Act self.exec_client.disconnect() # Assert assert not self.exec_client.is_connected def test_reset(self): # Arrange # Act self.exec_client.reset() # Assert assert not self.exec_client.is_connected def test_dispose(self): # Arrange # Act self.exec_client.dispose() # Assert assert not self.exec_client.is_connected def test_submit_order_when_not_connected_logs_and_does_not_send(self): # Arrange strategy = TradingStrategy("000") order = self.order_factory.market( ETHUSDT_BINANCE.id, OrderSide.BUY, Quantity.from_int(100), ) command = SubmitOrder( self.trader_id, strategy.id, PositionId.null(), order, self.uuid_factory.generate(), self.clock.timestamp_ns(), ) # Act self.exec_client.submit_order(command) # Assert assert order.state == OrderState.INITIALIZED def test_submit_bracket_order_when_not_connected_logs_and_does_not_send( self): # Arrange strategy = TradingStrategy("000") entry = self.order_factory.market( ETHUSDT_BINANCE.id, OrderSide.BUY, Quantity.from_int(100), ) bracket = self.order_factory.bracket( entry, Price.from_str("500.00000"), Price.from_str("600.00000"), ) command = SubmitBracketOrder( self.trader_id, strategy.id, bracket, self.uuid_factory.generate(), self.clock.timestamp_ns(), ) # Act self.exec_client.submit_bracket_order(command) # Assert assert entry.state == OrderState.INITIALIZED def test_cancel_order_when_not_connected_logs_and_does_not_send(self): # Arrange order = self.order_factory.market( ETHUSDT_BINANCE.id, OrderSide.BUY, Quantity.from_int(100), ) command = CancelOrder( self.trader_id, self.order_factory.strategy_id, order.instrument_id, order.client_order_id, order.venue_order_id, self.uuid_factory.generate(), self.clock.timestamp_ns(), ) # Act self.exec_client.cancel_order(command) # Assert assert True # No exceptions raised def test_update_order_when_not_connected_logs_and_does_not_send(self): # Arrange order = self.order_factory.stop_market( ETHUSDT_BINANCE.id, OrderSide.BUY, Quantity.from_int(100), Price.from_str("1000.00"), ) command = UpdateOrder( self.trader_id, order.strategy_id, order.instrument_id, order.client_order_id, order.venue_order_id, Quantity.from_int(100), Price.from_str("1010.00"), None, self.uuid_factory.generate(), self.clock.timestamp_ns(), ) # Act self.exec_client.update_order(command) # Assert assert True # No exceptions raised
class TestMsgPackCommandSerializer: def setup(self): # Fixture Setup self.venue = Venue("SIM") self.trader_id = TestStubs.trader_id() self.account_id = TestStubs.account_id() self.serializer = MsgPackCommandSerializer() self.order_factory = OrderFactory( trader_id=self.trader_id, strategy_id=StrategyId("S-001"), clock=TestClock(), ) def test_serialize_and_deserialize_submit_order_commands(self): # Arrange order = self.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity(100000, precision=0), ) command = SubmitOrder( self.trader_id, StrategyId("SCALPER-001"), PositionId("P-123456"), order, uuid4(), 0, ) # Act serialized = self.serializer.serialize(command) deserialized = self.serializer.deserialize(serialized) # Assert assert deserialized == command assert deserialized.order == order print(command) print(len(serialized)) print(serialized) print(b64encode(serialized)) def test_serialize_and_deserialize_submit_bracket_order_no_take_profit_commands( self, ): # Arrange entry_order = self.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity(100000, precision=0), ) bracket_order = self.order_factory.bracket( entry_order, stop_loss=Price(0.99900, precision=5), take_profit=Price(1.00100, precision=5), ) command = SubmitBracketOrder( self.trader_id, StrategyId("SCALPER-001"), bracket_order, uuid4(), 0, ) # Act serialized = self.serializer.serialize(command) deserialized = self.serializer.deserialize(serialized) # Assert assert deserialized == command assert deserialized.bracket_order == bracket_order print(b64encode(serialized)) print(command) def test_serialize_and_deserialize_submit_bracket_order_with_take_profit_commands( self, ): # Arrange entry_order = self.order_factory.limit( AUDUSD_SIM.id, OrderSide.BUY, Quantity(100000, precision=0), Price(1.00000, precision=5), ) bracket_order = self.order_factory.bracket( entry_order, stop_loss=Price(0.99900, precision=5), take_profit=Price(1.00010, precision=5), ) command = SubmitBracketOrder( self.trader_id, StrategyId("SCALPER-001"), bracket_order, uuid4(), 0, ) # Act serialized = self.serializer.serialize(command) deserialized = self.serializer.deserialize(serialized) # Assert assert deserialized == command assert deserialized.bracket_order == bracket_order print(b64encode(serialized)) print(command) def test_serialize_and_deserialize_amend_order_commands(self): # Arrange command = UpdateOrder( self.trader_id, StrategyId("SCALPER-001"), AUDUSD_SIM.id, ClientOrderId("O-123456"), VenueOrderId("001"), Quantity(100000, precision=0), Price(1.00001, precision=5), None, uuid4(), 0, ) # Act serialized = self.serializer.serialize(command) deserialized = self.serializer.deserialize(serialized) # Assert assert deserialized == command print(b64encode(serialized)) print(command) def test_serialize_and_deserialize_cancel_order_commands(self): # Arrange command = CancelOrder( self.trader_id, StrategyId("SCALPER-001"), AUDUSD_SIM.id, ClientOrderId("O-123456"), VenueOrderId("001"), uuid4(), 0, ) # Act serialized = self.serializer.serialize(command) deserialized = self.serializer.deserialize(serialized) # Assert assert deserialized == command print(b64encode(serialized)) print(command)
class TestBettingAccount: def setup(self): # Fixture Setup self.trader_id = TestStubs.trader_id() self.instrument = BetfairTestStubs.betting_instrument() self.order_factory = OrderFactory( trader_id=self.trader_id, strategy_id=StrategyId("S-001"), clock=TestClock(), ) @staticmethod def _make_account_state(starting_balance: float): return AccountState( account_id=AccountId("SIM", "001"), account_type=AccountType.BETTING, base_currency=GBP, reported=True, balances=[ AccountBalance( GBP, Money(starting_balance, GBP), Money(0.00, GBP), Money(starting_balance, GBP), ), ], info={}, # No default currency set event_id=UUID4(), ts_event=0, ts_init=0, ) def _make_fill(self, price="0.5", volume=10, side="BUY", position_id="P-123456"): order = self.order_factory.market( self.instrument.id, getattr(OrderSide, side), Quantity.from_int(volume), ) fill = TestStubs.event_order_filled( order, instrument=self.instrument, position_id=PositionId(position_id), strategy_id=StrategyId("S-001"), last_px=Price.from_str(price), ) return fill def test_instantiated_accounts_basic_properties(self): # Arrange, Act account = TestStubs.betting_account() # Assert assert account == account assert not account != account assert account.id == AccountId("SIM", "000") assert str( account) == "BettingAccount(id=SIM-000, type=BETTING, base=GBP)" assert repr( account) == "BettingAccount(id=SIM-000, type=BETTING, base=GBP)" assert isinstance(hash(account), int) def test_instantiate_single_asset_cash_account(self): # Arrange event = AccountState( account_id=AccountId("SIM", "000"), account_type=AccountType.BETTING, base_currency=GBP, reported=True, balances=[ AccountBalance( GBP, Money(1_000_000, GBP), Money(0, GBP), Money(1_000_000, GBP), ), ], info={}, event_id=UUID4(), ts_event=0, ts_init=0, ) # Act account = BettingAccount(event) # Assert assert account.base_currency == GBP assert account.last_event == event assert account.events == [event] assert account.event_count == 1 assert account.balance_total() == Money(1_000_000, GBP) assert account.balance_free() == Money(1_000_000, GBP) assert account.balance_locked() == Money(0, GBP) assert account.balances_total() == {GBP: Money(1_000_000, GBP)} assert account.balances_free() == {GBP: Money(1_000_000, GBP)} assert account.balances_locked() == {GBP: Money(0, GBP)} def test_apply_given_new_state_event_updates_correctly(self): # Arrange event1 = AccountState( account_id=AccountId("SIM", "001"), account_type=AccountType.BETTING, base_currency=None, # Multi-currency reported=True, balances=[ AccountBalance( GBP, Money(10.00000000, GBP), Money(0.00000000, GBP), Money(10.00000000, GBP), ), ], info={}, # No default currency set event_id=UUID4(), ts_event=0, ts_init=0, ) # Act account = BettingAccount(event1) event2 = AccountState( account_id=AccountId("SIM", "001"), account_type=AccountType.BETTING, base_currency=None, # Multi-currency reported=True, balances=[ AccountBalance( GBP, Money(9.00000000, GBP), Money(0.50000000, GBP), Money(8.50000000, GBP), ), ], info={}, # No default currency set event_id=UUID4(), ts_event=0, ts_init=0, ) # Act account.apply(event=event2) # Assert assert account.last_event == event2 assert account.events == [event1, event2] assert account.event_count == 2 assert account.balance_total(GBP) == Money(9.00000000, GBP) assert account.balance_free(GBP) == Money(8.50000000, GBP) assert account.balance_locked(GBP) == Money(0.50000000, GBP) @pytest.mark.parametrize( "price, quantity, side, locked_balance", [ ("0.80", 10, "BUY", "10"), ("0.50", 10, "BUY", "10"), ("0.10", 20, "BUY", "20"), ("0.80", 10, "SELL", "2.5"), ("0.5", 10, "SELL", "10"), ("0.1", 10, "SELL", "90"), ], ) def test_calculate_balance_locked(self, price, quantity, side, locked_balance): # Arrange event = self._make_account_state(starting_balance=1000.0) account = BettingAccount(event) # Act result = account.calculate_balance_locked( instrument=self.instrument, side=OrderSideParser.from_str_py(side), quantity=Quantity.from_int(quantity), price=Price.from_str(price), ) # Assert assert result == Money(Price.from_str(locked_balance), GBP) def test_calculate_pnls_for_single_currency_cash_account(self): # Arrange event = self._make_account_state(starting_balance=1000.0) account = BettingAccount(event) fill = self._make_fill(price="0.8", volume=100) position = Position(self.instrument, fill) # Act result = account.calculate_pnls( instrument=self.instrument, position=position, fill=fill, ) # Assert assert result == [Money("-80.00", GBP)] def test_calculate_pnls_partially_closed(self): # Arrange event = self._make_account_state(starting_balance=1000.0) account = BettingAccount(event) fill1 = self._make_fill(price="0.50", volume=100, side="BUY") fill2 = self._make_fill(price="0.80", volume=100, side="SELL") position = Position(self.instrument, fill1) position.apply(fill2) # Act result = account.calculate_pnls( instrument=self.instrument, position=position, fill=fill2, ) # Assert # TODO - this should really be 75 GBP given the position (but we are currently not using position?) # assert result == [Money("75.00", GBP)] assert result == [Money("80.00", GBP)] def test_calculate_commission_when_given_liquidity_side_none_raises_value_error( self, ): # Arrange account = TestStubs.cash_account() # Act, Assert with pytest.raises(ValueError): account.calculate_commission( instrument=self.instrument, last_qty=Quantity.from_int(1), last_px=Decimal("1"), liquidity_side=LiquiditySide.NONE, )
class MsgPackCommandSerializerTests(unittest.TestCase): def setUp(self): # Fixture Setup self.venue = Venue("SIM") self.trader_id = TestStubs.trader_id() self.account_id = TestStubs.account_id() self.serializer = MsgPackCommandSerializer() self.order_factory = OrderFactory( trader_id=self.trader_id, strategy_id=StrategyId("S", "001"), clock=TestClock(), ) def test_serialize_and_deserialize_submit_order_commands(self): # Arrange order = self.order_factory.market(AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(100000)) command = SubmitOrder( self.venue, self.trader_id, self.account_id, StrategyId("SCALPER", "01"), PositionId("P-123456"), order, uuid4(), UNIX_EPOCH, ) # Act serialized = self.serializer.serialize(command) deserialized = self.serializer.deserialize(serialized) # Assert self.assertEqual(command, deserialized) self.assertEqual(order, deserialized.order) print(command) print(len(serialized)) print(serialized) print(b64encode(serialized)) def test_serialize_and_deserialize_submit_bracket_order_no_take_profit_commands( self): # Arrange entry_order = self.order_factory.market(AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(100000)) bracket_order = self.order_factory.bracket( entry_order, stop_loss=Price("0.99900"), ) command = SubmitBracketOrder( self.venue, self.trader_id, self.account_id, StrategyId("SCALPER", "01"), bracket_order, uuid4(), UNIX_EPOCH, ) # Act serialized = self.serializer.serialize(command) deserialized = self.serializer.deserialize(serialized) # Assert self.assertEqual(command, deserialized) self.assertEqual(bracket_order, deserialized.bracket_order) print(b64encode(serialized)) print(command) def test_serialize_and_deserialize_submit_bracket_order_with_take_profit_commands( self): # Arrange entry_order = self.order_factory.limit( AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(100000), Price("1.00000"), ) bracket_order = self.order_factory.bracket( entry_order, stop_loss=Price("0.99900"), take_profit=Price("1.00010"), ) command = SubmitBracketOrder( self.venue, self.trader_id, self.account_id, StrategyId("SCALPER", "01"), bracket_order, uuid4(), UNIX_EPOCH, ) # Act serialized = self.serializer.serialize(command) deserialized = self.serializer.deserialize(serialized) # Assert self.assertEqual(command, deserialized) self.assertEqual(bracket_order, deserialized.bracket_order) print(b64encode(serialized)) print(command) def test_serialize_and_deserialize_modify_order_commands(self): # Arrange command = ModifyOrder( self.venue, self.trader_id, self.account_id, ClientOrderId("O-123456"), Quantity(100000), Price("1.00001"), uuid4(), UNIX_EPOCH, ) # Act serialized = self.serializer.serialize(command) deserialized = self.serializer.deserialize(serialized) # Assert self.assertEqual(command, deserialized) print(b64encode(serialized)) print(command) def test_serialize_and_deserialize_cancel_order_commands(self): # Arrange command = CancelOrder( self.venue, self.trader_id, self.account_id, ClientOrderId("O-123456"), uuid4(), UNIX_EPOCH, ) # Act serialized = self.serializer.serialize(command) deserialized = self.serializer.deserialize(serialized) # Assert self.assertEqual(command, deserialized) print(b64encode(serialized)) print(command)
class OrderTests(unittest.TestCase): def setUp(self): # Fixture Setup self.account_id = TestStubs.account_id() self.order_factory = OrderFactory( strategy_id=StrategyId("S", "001"), id_tag_trader=IdTag("001"), id_tag_strategy=IdTag("001"), clock=TestClock(), uuid_factory=TestUUIDFactory(), ) def test_get_opposite_side_returns_expected_sides(self): # Arrange # Act result1 = opposite_side(OrderSide.BUY) result2 = opposite_side(OrderSide.SELL) # Assert self.assertEqual(OrderSide.SELL, result1) self.assertEqual(OrderSide.BUY, result2) def test_get_flatten_side_with_long_or_short_position_side_returns_expected_sides(self): # Arrange # Act result1 = flatten_side(PositionSide.LONG) result2 = flatten_side(PositionSide.SHORT) # Assert self.assertEqual(OrderSide.SELL, result1) self.assertEqual(OrderSide.BUY, result2) def test_market_order_with_quantity_zero_raises_exception(self): # Arrange # Act self.assertRaises( ValueError, MarketOrder, ClientOrderId("O-123456"), StrategyId("S", "001"), AUDUSD_FXCM, OrderSide.BUY, Quantity(), TimeInForce.DAY, uuid4(), UNIX_EPOCH, ) def test_market_order_with_invalid_tif_raises_exception(self): # Arrange # Act self.assertRaises( ValueError, MarketOrder, ClientOrderId("O-123456"), StrategyId("S", "001"), AUDUSD_FXCM, OrderSide.BUY, Quantity(100), TimeInForce.GTD, uuid4(), UNIX_EPOCH, ) def test_stop_order_with_gtd_and_expire_time_none_raises_exception(self): # Arrange # Act self.assertRaises( TypeError, StopMarketOrder, ClientOrderId("O-123456"), StrategyId("S", "001"), AUDUSD_FXCM, OrderSide.BUY, Quantity(100000), price=Price("1.00000"), init_id=uuid4(), timestamp=UNIX_EPOCH, time_in_force=TimeInForce.GTD, expire_time=None, ) def test_reset_order_factory(self): # Arrange self.order_factory.limit( AUDUSD_FXCM, OrderSide.BUY, Quantity(100000), Price("1.00000"), ) # Act self.order_factory.reset() order2 = self.order_factory.limit( AUDUSD_FXCM, OrderSide.BUY, Quantity(100000), Price("1.00000"), ) self.assertEqual(ClientOrderId("O-19700101-000000-001-001-1"), order2.cl_ord_id) def test_limit_order_can_create_expected_decimal_price(self): # Arrange # Act order1 = self.order_factory.limit( AUDUSD_FXCM, OrderSide.BUY, Quantity(100000), Price("1.00000"), ) order2 = self.order_factory.limit( AUDUSD_FXCM, OrderSide.BUY, Quantity(100000), Price("1.00000"), ) order3 = self.order_factory.limit( AUDUSD_FXCM, OrderSide.BUY, Quantity(100000), Price("1.00000"), ) order4 = self.order_factory.limit( AUDUSD_FXCM, OrderSide.BUY, Quantity(100000), Price("1.00001"), ) # Assert self.assertEqual(Price("1.00000"), order1.price) self.assertEqual(Price("1.00000"), order2.price) self.assertEqual(Price("1.00000"), order3.price) self.assertEqual(Price("1.00001"), order4.price) def test_initialize_buy_market_order(self): # Arrange # Act order = self.order_factory.market( AUDUSD_FXCM, OrderSide.BUY, Quantity(100000), ) # Assert self.assertEqual(OrderType.MARKET, order.type) self.assertEqual(OrderState.INITIALIZED, order.state()) self.assertEqual(1, order.event_count()) self.assertTrue(isinstance(order.last_event(), OrderInitialized)) self.assertFalse(order.is_working()) self.assertFalse(order.is_completed()) self.assertTrue(order.is_buy()) self.assertFalse(order.is_sell()) self.assertEqual(None, order.filled_timestamp) self.assertEqual(UNIX_EPOCH, order.last_event().timestamp) def test_initialize_sell_market_order(self): # Arrange # Act order = self.order_factory.market( AUDUSD_FXCM, OrderSide.SELL, Quantity(100000),) # Assert self.assertEqual(OrderType.MARKET, order.type) self.assertEqual(OrderState.INITIALIZED, order.state()) self.assertEqual(1, order.event_count()) self.assertTrue(isinstance(order.last_event(), OrderInitialized)) self.assertFalse(order.is_working()) self.assertFalse(order.is_completed()) self.assertFalse(order.is_buy()) self.assertTrue(order.is_sell()) self.assertEqual(None, order.filled_timestamp) # def test_order_str_and_repr(self): # # Arrange # # Act # order = self.order_factory.market( # AUDUSD_FXCM, # OrderSide.BUY, # Quantity(100000), # ) # Assert TODO: String formatting # self.assertEqual("MarketOrder(cl_ord_id=O-19700101-000000-001-001-1, state=INITIALIZED, BUY 100K AUD/USD.FXCM MARKET DAY)", str(order)) # noqa # self.assertTrue(repr(order).startswith("<MarketOrder(cl_ord_id=O-19700101-000000-001-001-1, state=INITIALIZED, BUY 100K AUD/USD.FXCM MARKET DAY) object at")) # noqa def test_initialize_limit_order(self): # Arrange # Act order = self.order_factory.limit( AUDUSD_FXCM, OrderSide.BUY, Quantity(100000), Price("1.00000"), ) # Assert self.assertEqual(OrderType.LIMIT, order.type) self.assertEqual(OrderState.INITIALIZED, order.state()) self.assertEqual(TimeInForce.DAY, order.time_in_force) self.assertFalse(order.is_completed()) def test_initialize_limit_order_with_expire_time(self): # Arrange # Act order = self.order_factory.limit( AUDUSD_FXCM, OrderSide.BUY, Quantity(100000), Price("1.00000"), TimeInForce.GTD, UNIX_EPOCH, ) # Assert self.assertEqual(AUDUSD_FXCM, order.symbol) self.assertEqual(OrderType.LIMIT, order.type) self.assertEqual(Price("1.00000"), order.price) self.assertEqual(OrderState.INITIALIZED, order.state()) self.assertEqual(TimeInForce.GTD, order.time_in_force) self.assertEqual(UNIX_EPOCH, order.expire_time) self.assertFalse(order.is_completed()) def test_initialize_stop_order(self): # Arrange # Act order = self.order_factory.stop( AUDUSD_FXCM, OrderSide.BUY, Quantity(100000), Price("1.00000"), ) # Assert self.assertEqual(OrderType.STOP_MARKET, order.type) self.assertEqual(OrderState.INITIALIZED, order.state()) self.assertEqual(TimeInForce.DAY, order.time_in_force) self.assertFalse(order.is_completed()) def test_initialize_bracket_order_market_with_no_take_profit(self): # Arrange entry_order = self.order_factory.market( AUDUSD_FXCM, OrderSide.BUY, Quantity(100000)) # Act bracket_order = self.order_factory.bracket(entry_order, Price("0.99990")) # Assert self.assertEqual(AUDUSD_FXCM, bracket_order.stop_loss.symbol) self.assertFalse(bracket_order.has_take_profit) self.assertEqual(ClientOrderId("O-19700101-000000-001-001-1"), bracket_order.entry.cl_ord_id) self.assertEqual(ClientOrderId("O-19700101-000000-001-001-2"), bracket_order.stop_loss.cl_ord_id) self.assertEqual(OrderSide.SELL, bracket_order.stop_loss.side) self.assertEqual(Quantity(100000), bracket_order.entry.quantity) self.assertEqual(Quantity(100000), bracket_order.stop_loss.quantity) self.assertEqual(Price("0.99990"), bracket_order.stop_loss.price) self.assertEqual(TimeInForce.GTC, bracket_order.stop_loss.time_in_force) self.assertEqual(None, bracket_order.stop_loss.expire_time) self.assertEqual(BracketOrderId("BO-19700101-000000-001-001-1"), bracket_order.id) self.assertEqual(UNIX_EPOCH, bracket_order.timestamp) def test_can_initialize_bracket_order_stop_with_take_profit(self): # Arrange entry_order = self.order_factory.stop( AUDUSD_FXCM, OrderSide.BUY, Quantity(100000), Price("0.99995"), ) # Act bracket_order = self.order_factory.bracket( entry_order, Price("0.99990"), Price("1.00010"), ) # Assert self.assertEqual(AUDUSD_FXCM, bracket_order.stop_loss.symbol) self.assertTrue(bracket_order.has_take_profit) self.assertEqual(AUDUSD_FXCM, bracket_order.take_profit.symbol) self.assertEqual(ClientOrderId("O-19700101-000000-001-001-1"), bracket_order.entry.cl_ord_id) self.assertEqual(ClientOrderId("O-19700101-000000-001-001-2"), bracket_order.stop_loss.cl_ord_id) self.assertEqual(ClientOrderId("O-19700101-000000-001-001-3"), bracket_order.take_profit.cl_ord_id) self.assertEqual(OrderSide.SELL, bracket_order.stop_loss.side) self.assertEqual(OrderSide.SELL, bracket_order.take_profit.side) self.assertEqual(Quantity(100000), bracket_order.stop_loss.quantity) self.assertEqual(Quantity(100000), bracket_order.take_profit.quantity) self.assertEqual(Price("0.99990"), bracket_order.stop_loss.price) self.assertEqual(Price("1.00010"), bracket_order.take_profit.price) self.assertEqual(TimeInForce.GTC, bracket_order.stop_loss.time_in_force) self.assertEqual(TimeInForce.GTC, bracket_order.take_profit.time_in_force) self.assertEqual(None, bracket_order.entry.expire_time) self.assertEqual(None, bracket_order.stop_loss.expire_time) self.assertEqual(None, bracket_order.take_profit.expire_time) self.assertEqual(BracketOrderId("BO-19700101-000000-001-001-1"), bracket_order.id) self.assertEqual(UNIX_EPOCH, bracket_order.timestamp) # def test_bracket_order_str_and_repr(self): # # Arrange # # Act # entry_order = self.order_factory.market( # AUDUSD_FXCM, # OrderSide.BUY, # Quantity(100000), # ) # # bracket_order = self.order_factory.bracket( # entry_order, # Price("0.99990"), # Price("1.00010"), # ) # Assert # TODO: Fix string formatting # self.assertEqual("BracketOrder(id=BO-19700101-000000-001-001-1, EntryMarketOrder(cl_ord_id=O-19700101-000000-001-001-1, state=INITIALIZED, BUY 100K AUD/USD.FXCM MARKET DAY), SL=0.99990, TP=1.00010)", str(bracket_order)) # noqa # self.assertTrue(repr(bracket_order).startswith("<BracketOrder(id=BO-19700101-000000-001-001-1, EntryMarketOrder(cl_ord_id=O-19700101-000000-001-001-1, state=INITIALIZED, BUY 100K AUD/USD.FXCM MARKET DAY), SL=0.99990, TP=1.00010) object at")) # noqa # self.assertTrue(repr(bracket_order).endswith(">")) def test_can_apply_order_submitted_event_to_order(self): # Arrange order = self.order_factory.market( AUDUSD_FXCM, OrderSide.BUY, Quantity(100000), ) submitted = TestStubs.event_order_submitted(order) # Act order.apply(submitted) # Assert self.assertEqual(OrderState.SUBMITTED, order.state()) self.assertEqual(2, order.event_count()) self.assertEqual(submitted, order.last_event()) self.assertFalse(order.is_completed()) def test_can_apply_order_accepted_event_to_order(self): # Arrange order = self.order_factory.market( AUDUSD_FXCM, OrderSide.BUY, Quantity(100000)) submitted = TestStubs.event_order_submitted(order) accepted = TestStubs.event_order_accepted(order) order.apply(submitted) # Act order.apply(accepted) # Assert self.assertEqual(OrderState.ACCEPTED, order.state()) self.assertFalse(order.is_completed()) def test_can_apply_order_rejected_event_to_order(self): # Arrange order = self.order_factory.market( AUDUSD_FXCM, OrderSide.BUY, Quantity(100000)) submitted = TestStubs.event_order_submitted(order) rejected = TestStubs.event_order_rejected(order) order.apply(submitted) # Act order.apply(rejected) # Assert self.assertEqual(OrderState.REJECTED, order.state()) self.assertTrue(order.is_completed()) def test_can_apply_order_working_event_to_stop_order(self): # Arrange order = self.order_factory.stop( AUDUSD_FXCM, OrderSide.BUY, Quantity(100000), Price("1.00000"), ) submitted = TestStubs.event_order_submitted(order) accepted = TestStubs.event_order_accepted(order) working = TestStubs.event_order_working(order) order.apply(submitted) order.apply(accepted) # Act order.apply(working) # Assert # print(order) self.assertEqual(OrderState.WORKING, order.state()) self.assertEqual(OrderId("1"), order.id) self.assertFalse(order.is_completed()) self.assertTrue(order.is_working()) self.assertEqual(None, order.filled_timestamp) def test_can_apply_order_expired_event_to_stop_order(self): # Arrange order = self.order_factory.stop( AUDUSD_FXCM, OrderSide.BUY, Quantity(100000), Price("0.99990"), TimeInForce.GTD, UNIX_EPOCH) submitted = TestStubs.event_order_submitted(order) accepted = TestStubs.event_order_accepted(order) working = TestStubs.event_order_working(order) expired = TestStubs.event_order_expired(order) order.apply(submitted) order.apply(accepted) order.apply(working) # Act order.apply(expired) # Assert self.assertEqual(OrderState.EXPIRED, order.state()) self.assertTrue(order.is_completed()) def test_can_apply_order_cancelled_event_to_order(self): # Arrange order = self.order_factory.market( AUDUSD_FXCM, OrderSide.BUY, Quantity(100000)) submitted = TestStubs.event_order_submitted(order) accepted = TestStubs.event_order_accepted(order) cancelled = TestStubs.event_order_cancelled(order) order.apply(submitted) order.apply(accepted) # Act order.apply(cancelled) # Assert self.assertEqual(OrderState.CANCELLED, order.state()) self.assertTrue(order.is_completed()) def test_can_apply_order_modified_event_to_stop_order(self): # Arrange order = self.order_factory.stop( AUDUSD_FXCM, OrderSide.BUY, Quantity(100000), Price("1.00000")) submitted = TestStubs.event_order_submitted(order) accepted = TestStubs.event_order_accepted(order) working = TestStubs.event_order_working(order) modified = OrderModified( self.account_id, order.cl_ord_id, OrderId("1"), Quantity(120000), Price("1.00001"), UNIX_EPOCH, uuid4(), UNIX_EPOCH) order.apply(submitted) order.apply(accepted) order.apply(working) # Act order.apply(modified) # Assert self.assertEqual(OrderState.WORKING, order.state()) self.assertEqual(OrderId("1"), order.id) self.assertEqual(Quantity(120000), order.quantity) self.assertEqual(Price("1.00001"), order.price) self.assertTrue(order.is_working()) self.assertFalse(order.is_completed()) self.assertEqual(5, order.event_count()) def test_can_apply_order_filled_event_to_market_order(self): # Arrange order = self.order_factory.market( AUDUSD_FXCM, OrderSide.BUY, Quantity(100000)) submitted = TestStubs.event_order_submitted(order) accepted = TestStubs.event_order_accepted(order) filled = TestStubs.event_order_filled( order, PositionId("P-123456"), StrategyId("S", "001"), Price("1.00001")) order.apply(submitted) order.apply(accepted) # Act order.apply(filled) # Assert self.assertEqual(OrderState.FILLED, order.state()) self.assertEqual(Quantity(100000), order.filled_qty) self.assertEqual(Price("1.00001"), order.avg_price) self.assertTrue(order.is_completed()) self.assertEqual(UNIX_EPOCH, order.filled_timestamp) def test_can_apply_order_filled_event_to_buy_limit_order(self): # Arrange order = self.order_factory.limit( AUDUSD_FXCM, OrderSide.BUY, Quantity(100000), Price("1.00000")) submitted = TestStubs.event_order_submitted(order) accepted = TestStubs.event_order_accepted(order) working = TestStubs.event_order_working(order) filled = OrderFilled( self.account_id, order.cl_ord_id, OrderId("1"), ExecutionId("E-1"), PositionId("P-1"), StrategyId("S", "NULL"), order.symbol, order.side, order.quantity, Quantity(), Price("1.00001"), Money(0, USD), LiquiditySide.MAKER, USD, USD, UNIX_EPOCH, uuid4(), UNIX_EPOCH, ) order.apply(submitted) order.apply(accepted) order.apply(working) # Act order.apply(filled) # Assert self.assertEqual(OrderState.FILLED, order.state()) self.assertEqual(Quantity(100000), order.filled_qty) self.assertEqual(Price("1.00000"), order.price) self.assertEqual(Price("1.00001"), order.avg_price) self.assertEqual(Decimal("0.00001"), order.slippage) self.assertTrue(order.is_completed()) self.assertEqual(UNIX_EPOCH, order.filled_timestamp) def test_can_apply_order_partially_filled_event_to_buy_limit_order(self): # Arrange order = self.order_factory.limit( AUDUSD_FXCM, OrderSide.BUY, Quantity(100000), Price("1.00000")) submitted = TestStubs.event_order_submitted(order) accepted = TestStubs.event_order_accepted(order) working = TestStubs.event_order_working(order) partially = OrderFilled( self.account_id, order.cl_ord_id, OrderId("1"), ExecutionId("E-1"), PositionId("P-1"), StrategyId("S", "NULL"), order.symbol, order.side, Quantity(50000), Quantity(50000), Price("0.999999"), Money(0, USD), LiquiditySide.MAKER, USD, USD, UNIX_EPOCH, uuid4(), UNIX_EPOCH) order.apply(submitted) order.apply(accepted) order.apply(working) # Act order.apply(partially) # Assert self.assertEqual(OrderState.PARTIALLY_FILLED, order.state()) self.assertEqual(Quantity(50000), order.filled_qty) self.assertEqual(Price("1.00000"), order.price) self.assertEqual(Price("0.999999"), order.avg_price) self.assertEqual(Decimal("-0.000001"), order.slippage) self.assertFalse(order.is_completed()) self.assertEqual(UNIX_EPOCH, order.filled_timestamp)
class PositionTests(unittest.TestCase): def setUp(self): # Fixture Setup self.account_id = TestStubs.account_id() self.order_factory = OrderFactory( trader_id=TraderId("TESTER", "000"), strategy_id=StrategyId("S", "001"), clock=TestClock(), ) def test_side_from_order_side_given_undefined_raises_value_error(self): # Arrange # Act # Assert self.assertRaises(ValueError, Position.side_from_order_side, OrderSide.UNDEFINED) @parameterized.expand([ [OrderSide.BUY, PositionSide.LONG], [OrderSide.SELL, PositionSide.SHORT], ]) def test_side_from_order_side_given_valid_sides_returns_expected_side(self, order_side, expected): # Arrange # Act position_side = Position.side_from_order_side(order_side) # Assert self.assertEqual(expected, position_side) def test_position_filled_with_buy_order_returns_expected_attributes(self): # Arrange order = self.order_factory.market( AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(100000), ) fill = TestStubs.event_order_filled( order, instrument=AUDUSD_SIM, position_id=PositionId("P-123456"), strategy_id=StrategyId("S", "001"), fill_price=Price("1.00001"), ) last = Price("1.00050") # Act position = Position(fill) # Assert self.assertFalse(position != position) # Equality operator test self.assertEqual(ClientOrderId("O-19700101-000000-000-001-1"), position.from_order) self.assertEqual(Quantity(100000), position.quantity) self.assertEqual(Quantity(100000), position.peak_quantity) self.assertEqual(OrderSide.BUY, position.entry) self.assertEqual(PositionSide.LONG, position.side) self.assertEqual(UNIX_EPOCH, position.opened_time) self.assertIsNone(position.open_duration) self.assertEqual(Decimal("1.00001"), position.avg_open) self.assertEqual(1, position.event_count) self.assertEqual([order.cl_ord_id], position.cl_ord_ids) self.assertEqual([OrderId.null()], position.order_ids) self.assertEqual([ExecutionId("E-19700101-000000-000-001-1")], position.execution_ids) self.assertEqual(ExecutionId("E-19700101-000000-000-001-1"), position.last_execution_id) self.assertEqual(PositionId("P-123456"), position.id) self.assertEqual(1, len(position.events)) self.assertTrue(position.is_long) self.assertFalse(position.is_short) self.assertTrue(position.is_open) self.assertFalse(position.is_closed) self.assertEqual(0, position.realized_points) self.assertEqual(0, position.realized_return) self.assertEqual(Money(-2.00, USD), position.realized_pnl) self.assertEqual(Money(49.00, USD), position.unrealized_pnl(last)) self.assertEqual(Money(47.00, USD), position.total_pnl(last)) self.assertEqual(Money(2.00, USD), position.commission) self.assertEqual([Money(2.00, USD)], position.commissions()) self.assertEqual("Position(id=P-123456, LONG 100,000 AUD/USD.SIM)", repr(position)) def test_position_filled_with_sell_order_returns_expected_attributes(self): # Arrange order = self.order_factory.market( AUDUSD_SIM.symbol, OrderSide.SELL, Quantity(100000), ) fill = TestStubs.event_order_filled( order, instrument=AUDUSD_SIM, position_id=PositionId("P-123456"), strategy_id=StrategyId("S", "001"), fill_price=Price("1.00001"), ) last = Price("1.00050") # Act position = Position(fill) # Assert self.assertEqual(Quantity(100000), position.quantity) self.assertEqual(Quantity(100000), position.peak_quantity) self.assertEqual(PositionSide.SHORT, position.side) self.assertEqual(UNIX_EPOCH, position.opened_time) self.assertEqual(Decimal("1.00001"), position.avg_open) self.assertEqual(1, position.event_count) self.assertEqual([ExecutionId("E-19700101-000000-000-001-1")], position.execution_ids) self.assertEqual(ExecutionId("E-19700101-000000-000-001-1"), position.last_execution_id) self.assertEqual(PositionId("P-123456"), position.id) self.assertFalse(position.is_long) self.assertTrue(position.is_short) self.assertTrue(position.is_open) self.assertFalse(position.is_closed) self.assertEqual(0, position.realized_points) self.assertEqual(0, position.realized_return) self.assertEqual(Money(-2.00, USD), position.realized_pnl) self.assertEqual(Money(-49.00, USD), position.unrealized_pnl(last)) self.assertEqual(Money(-51.00, USD), position.total_pnl(last)) self.assertEqual(Money(2.00, USD), position.commission) self.assertEqual([Money(2.00, USD)], position.commissions()) self.assertEqual("Position(id=P-123456, SHORT 100,000 AUD/USD.SIM)", repr(position)) def test_position_partial_fills_with_buy_order_returns_expected_attributes(self): # Arrange order = self.order_factory.market( AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(100000), ) fill = TestStubs.event_order_filled( order, instrument=AUDUSD_SIM, position_id=PositionId("P-123456"), strategy_id=StrategyId("S", "001"), fill_price=Price("1.00001"), fill_qty=Quantity(50000), ) last = Price("1.00048") position = Position(fill) # Act # Assert self.assertEqual(Quantity(50000), position.quantity) self.assertEqual(Quantity(50000), position.peak_quantity) self.assertEqual(PositionSide.LONG, position.side) self.assertEqual(UNIX_EPOCH, position.opened_time) self.assertEqual(Decimal("1.00001"), position.avg_open) self.assertEqual(1, position.event_count) self.assertTrue(position.is_long) self.assertFalse(position.is_short) self.assertTrue(position.is_open) self.assertFalse(position.is_closed) self.assertEqual(0, position.realized_points) self.assertEqual(0, position.realized_return) self.assertEqual(Money(-2.00, USD), position.realized_pnl) self.assertEqual(Money(23.50, USD), position.unrealized_pnl(last)) self.assertEqual(Money(21.50, USD), position.total_pnl(last)) self.assertEqual(Money(2.00, USD), position.commission) self.assertEqual([Money(2.00, USD)], position.commissions()) self.assertEqual("Position(id=P-123456, LONG 50,000 AUD/USD.SIM)", repr(position)) def test_position_partial_fills_with_sell_order_returns_expected_attributes(self): # Arrange order = self.order_factory.market( AUDUSD_SIM.symbol, OrderSide.SELL, Quantity(100000), ) fill1 = TestStubs.event_order_filled( order, instrument=AUDUSD_SIM, position_id=PositionId("P-123456"), strategy_id=StrategyId("S", "001"), fill_price=Price("1.00001"), fill_qty=Quantity(50000), ) fill2 = TestStubs.event_order_filled( order, instrument=AUDUSD_SIM, position_id=PositionId("P-123456"), strategy_id=StrategyId("S", "001"), fill_price=Price("1.00002"), fill_qty=Quantity(50000), ) position = Position(fill1) last = Price("1.00050") # Act position.apply(fill2) # Assert self.assertEqual(Quantity(100000), position.quantity) self.assertEqual(PositionSide.SHORT, position.side) self.assertEqual(UNIX_EPOCH, position.opened_time) self.assertEqual(Decimal("1.000015"), position.avg_open) self.assertEqual(2, position.event_count) self.assertFalse(position.is_long) self.assertTrue(position.is_short) self.assertTrue(position.is_open) self.assertFalse(position.is_closed) self.assertEqual(0, position.realized_points) self.assertEqual(0, position.realized_return) self.assertEqual(Money(-4.00, USD), position.realized_pnl) self.assertEqual(Money(-48.50, USD), position.unrealized_pnl(last)) self.assertEqual(Money(-52.50, USD), position.total_pnl(last)) self.assertEqual([Money(4.00, USD)], position.commissions()) self.assertEqual(Money(4.00, USD), position.commission) self.assertEqual("Position(id=P-123456, SHORT 100,000 AUD/USD.SIM)", repr(position)) def test_position_filled_with_buy_order_then_sell_order_returns_expected_attributes(self): # Arrange order = self.order_factory.market( AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(150000), ) fill1 = TestStubs.event_order_filled( order, instrument=AUDUSD_SIM, position_id=PositionId("P-123456"), strategy_id=StrategyId("S", "001"), fill_price=Price("1.00001"), ) position = Position(fill1) fill2 = OrderFilled( self.account_id, order.cl_ord_id, OrderId("2"), ExecutionId("E2"), PositionId("T123456"), StrategyId("S", "001"), order.symbol, OrderSide.SELL, order.quantity, order.quantity, Quantity(), Price("1.00011"), AUDUSD_SIM.quote_currency, AUDUSD_SIM.is_inverse, Money(0, USD), LiquiditySide.TAKER, UNIX_EPOCH + timedelta(minutes=1), uuid4(), UNIX_EPOCH, ) last = Price("1.00050") # Act position.apply(fill2) # Assert self.assertEqual(Quantity(), position.quantity) self.assertEqual(PositionSide.FLAT, position.side) self.assertEqual(UNIX_EPOCH, position.opened_time) self.assertEqual(timedelta(minutes=1), position.open_duration) self.assertEqual(Decimal("1.00001"), position.avg_open) self.assertEqual(2, position.event_count) self.assertEqual(datetime(1970, 1, 1, 0, 1, tzinfo=pytz.utc), position.closed_time) self.assertEqual(Decimal("1.00011"), position.avg_close) self.assertFalse(position.is_long) self.assertFalse(position.is_short) self.assertFalse(position.is_open) self.assertTrue(position.is_closed) self.assertEqual(Decimal("0.00010"), position.realized_points) self.assertEqual(Decimal('0.00009999900000999990000099999000'), position.realized_return) self.assertEqual(Money(12.00, USD), position.realized_pnl) self.assertEqual(Money(0, USD), position.unrealized_pnl(last)) self.assertEqual(Money(12.00, USD), position.total_pnl(last)) self.assertEqual([Money(3.00, USD)], position.commissions()) self.assertEqual(Money(3.00, USD), position.commission) self.assertEqual("Position(id=P-123456, FLAT AUD/USD.SIM)", repr(position)) def test_position_filled_with_sell_order_then_buy_order_returns_expected_attributes(self): # Arrange order1 = self.order_factory.market( AUDUSD_SIM.symbol, OrderSide.SELL, Quantity(100000), ) order2 = self.order_factory.market( AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(100000), ) fill1 = TestStubs.event_order_filled( order1, instrument=AUDUSD_SIM, position_id=PositionId("P-19700101-000000-000-001-1"), ) position = Position(fill1) fill2 = TestStubs.event_order_filled( order2, instrument=AUDUSD_SIM, position_id=PositionId("P-19700101-000000-000-001-1"), strategy_id=StrategyId("S", "001"), fill_price=Price("1.00001"), fill_qty=Quantity(50000), ) fill3 = TestStubs.event_order_filled( order2, instrument=AUDUSD_SIM, position_id=PositionId("P-19700101-000000-000-001-1"), strategy_id=StrategyId("S", "001"), fill_price=Price("1.00003"), fill_qty=Quantity(50000), ) last = Price("1.00050") # Act position.apply(fill2) position.apply(fill3) # Assert self.assertEqual(Quantity(), position.quantity) self.assertEqual(PositionSide.FLAT, position.side) self.assertEqual(UNIX_EPOCH, position.opened_time) self.assertEqual(Decimal("1.0"), position.avg_open) self.assertEqual(3, position.event_count) self.assertEqual([order1.cl_ord_id, order2.cl_ord_id], position.cl_ord_ids) self.assertEqual(UNIX_EPOCH, position.closed_time) self.assertEqual(Decimal("1.00002"), position.avg_close) self.assertFalse(position.is_long) self.assertFalse(position.is_short) self.assertFalse(position.is_open) self.assertTrue(position.is_closed) self.assertEqual(Money(-8.00, USD), position.realized_pnl) self.assertEqual(Money(0, USD), position.unrealized_pnl(last)) self.assertEqual(Money(-8.000, USD), position.total_pnl(last)) self.assertEqual([Money(6.00, USD)], position.commissions()) self.assertEqual(Money(6.00, USD), position.commission) self.assertEqual("Position(id=P-19700101-000000-000-001-1, FLAT AUD/USD.SIM)", repr(position)) def test_position_filled_with_no_change_returns_expected_attributes(self): # Arrange order1 = self.order_factory.market( AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(100000), ) order2 = self.order_factory.market( AUDUSD_SIM.symbol, OrderSide.SELL, Quantity(100000), ) fill1 = TestStubs.event_order_filled( order1, instrument=AUDUSD_SIM, position_id=PositionId("P-19700101-000000-000-001-1"), ) position = Position(fill1) fill2 = TestStubs.event_order_filled( order2, instrument=AUDUSD_SIM, position_id=PositionId("P-19700101-000000-000-001-1"), strategy_id=StrategyId("S", "001"), fill_price=Price("1.00000"), ) last = Price("1.00050") # Act position.apply(fill2) # Assert self.assertEqual(Quantity(), position.quantity) self.assertEqual(PositionSide.FLAT, position.side) self.assertEqual(UNIX_EPOCH, position.opened_time) self.assertEqual(Decimal("1.0"), position.avg_open) self.assertEqual(2, position.event_count) self.assertEqual([order1.cl_ord_id, order2.cl_ord_id], position.cl_ord_ids) self.assertEqual([ ExecutionId("E-19700101-000000-000-001-1"), ExecutionId("E-19700101-000000-000-001-2") ], position.execution_ids, ) self.assertEqual(UNIX_EPOCH, position.closed_time) self.assertEqual(Decimal("1.0"), position.avg_close) self.assertFalse(position.is_long) self.assertFalse(position.is_short) self.assertFalse(position.is_open) self.assertTrue(position.is_closed) self.assertEqual(0, position.realized_points) self.assertEqual(0, position.realized_return) self.assertEqual(Money(-4.00, USD), position.realized_pnl) self.assertEqual(Money(0, USD), position.unrealized_pnl(last)) self.assertEqual(Money(-4.00, USD), position.total_pnl(last)) self.assertEqual([Money(4.00, USD)], position.commissions()) self.assertEqual(Money(4.00, USD), position.commission) self.assertEqual("Position(id=P-19700101-000000-000-001-1, FLAT AUD/USD.SIM)", repr(position)) def test_position_long_with_multiple_filled_orders_returns_expected_attributes(self): # Arrange order1 = self.order_factory.market( AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(100000), ) order2 = self.order_factory.market( AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(100000), ) order3 = self.order_factory.market( AUDUSD_SIM.symbol, OrderSide.SELL, Quantity(200000), ) fill1 = TestStubs.event_order_filled( order1, instrument=AUDUSD_SIM, position_id=PositionId("P-123456"), strategy_id=StrategyId("S", "001"), ) fill2 = TestStubs.event_order_filled( order2, instrument=AUDUSD_SIM, position_id=PositionId("P-123456"), strategy_id=StrategyId("S", "001"), fill_price=Price("1.00001"), ) fill3 = TestStubs.event_order_filled( order3, instrument=AUDUSD_SIM, position_id=PositionId("P-123456"), strategy_id=StrategyId("S", "001"), fill_price=Price("1.00010"), ) last = Price("1.00050") # Act position = Position(fill1) position.apply(fill2) position.apply(fill3) # Assert self.assertEqual(Quantity(), position.quantity) self.assertEqual(PositionSide.FLAT, position.side) self.assertEqual(UNIX_EPOCH, position.opened_time) self.assertEqual(Decimal("1.000005"), position.avg_open) self.assertEqual(3, position.event_count) self.assertEqual([order1.cl_ord_id, order2.cl_ord_id, order3.cl_ord_id], position.cl_ord_ids) self.assertEqual(UNIX_EPOCH, position.closed_time) self.assertEqual(Decimal("1.0001"), position.avg_close) self.assertFalse(position.is_long) self.assertFalse(position.is_short) self.assertFalse(position.is_open) self.assertTrue(position.is_closed) self.assertEqual(Money(11.00, USD), position.realized_pnl) self.assertEqual(Money(0, USD), position.unrealized_pnl(last)) self.assertEqual(Money(11.00, USD), position.total_pnl(last)) self.assertEqual([Money(8.00, USD)], position.commissions()) self.assertEqual(Money(8.00, USD), position.commission) self.assertEqual("Position(id=P-123456, FLAT AUD/USD.SIM)", repr(position)) def test_pnl_calculation_from_trading_technologies_example(self): # https://www.tradingtechnologies.com/xtrader-help/fix-adapter-reference/pl-calculation-algorithm/understanding-pl-calculations/ # noqa # Arrange order1 = self.order_factory.market( ETHUSDT_BINANCE.symbol, OrderSide.BUY, Quantity(12), ) order2 = self.order_factory.market( ETHUSDT_BINANCE.symbol, OrderSide.BUY, Quantity(17), ) order3 = self.order_factory.market( ETHUSDT_BINANCE.symbol, OrderSide.SELL, Quantity(9), ) order4 = self.order_factory.market( ETHUSDT_BINANCE.symbol, OrderSide.SELL, Quantity(4), ) order5 = self.order_factory.market( ETHUSDT_BINANCE.symbol, OrderSide.BUY, Quantity(3), ) # Act fill1 = TestStubs.event_order_filled( order1, instrument=BTCUSDT_BINANCE, position_id=PositionId("P-19700101-000000-000-001-1"), fill_price=Price(100), ) position = Position(fill1) fill2 = TestStubs.event_order_filled( order2, instrument=BTCUSDT_BINANCE, position_id=PositionId("P-19700101-000000-000-001-1"), fill_price=Price(99), ) position.apply(fill2) self.assertEqual(Quantity(29), position.quantity) self.assertEqual(Money("-2.88300000", USDT), position.realized_pnl) self.assertEqual(Decimal("99.41379310344827586206896552"), position.avg_open) fill3 = TestStubs.event_order_filled( order3, instrument=BTCUSDT_BINANCE, position_id=PositionId("P-19700101-000000-000-001-1"), strategy_id=StrategyId("S", "001"), fill_price=Price(101), ) position.apply(fill3) self.assertEqual(Quantity(20), position.quantity) self.assertEqual(Money("10.48386207", USDT), position.realized_pnl) self.assertEqual(Decimal("99.41379310344827586206896552"), position.avg_open) fill4 = TestStubs.event_order_filled( order4, instrument=BTCUSDT_BINANCE, position_id=PositionId("P-19700101-000000-000-001-1"), strategy_id=StrategyId("S", "001"), fill_price=Price(105), ) position.apply(fill4) self.assertEqual(Quantity(16), position.quantity) self.assertEqual(Money("32.40868966", USDT), position.realized_pnl) self.assertEqual(Decimal("99.41379310344827586206896552"), position.avg_open) fill5 = TestStubs.event_order_filled( order5, instrument=BTCUSDT_BINANCE, position_id=PositionId("P-19700101-000000-000-001-1"), strategy_id=StrategyId("S", "001"), fill_price=Price(103), ) position.apply(fill5) self.assertEqual(Quantity(19), position.quantity) self.assertEqual(Money("32.09968966", USDT), position.realized_pnl) self.assertEqual(Decimal("99.98003629764065335753176042"), position.avg_open) self.assertEqual("Position(id=P-19700101-000000-000-001-1, LONG 19 ETH/USDT.BINANCE)", repr(position)) def test_position_realised_pnl_with_interleaved_order_sides(self): # Arrange order1 = self.order_factory.market( BTCUSDT_BINANCE.symbol, OrderSide.BUY, Quantity("12.000000"), ) order2 = self.order_factory.market( BTCUSDT_BINANCE.symbol, OrderSide.BUY, Quantity("17.000000"), ) order3 = self.order_factory.market( BTCUSDT_BINANCE.symbol, OrderSide.SELL, Quantity("9.000000"), ) order4 = self.order_factory.market( BTCUSDT_BINANCE.symbol, OrderSide.BUY, Quantity("3.000000"), ) order5 = self.order_factory.market( BTCUSDT_BINANCE.symbol, OrderSide.SELL, Quantity("4.000000"), ) # Act fill1 = TestStubs.event_order_filled( order1, instrument=BTCUSDT_BINANCE, position_id=PositionId("P-19700101-000000-000-001-1"), fill_price=Price("10000.00"), ) position = Position(fill1) fill2 = TestStubs.event_order_filled( order2, instrument=BTCUSDT_BINANCE, position_id=PositionId("P-19700101-000000-000-001-1"), fill_price=Price("9999.00"), ) position.apply(fill2) self.assertEqual(Quantity("29.000000"), position.quantity) self.assertEqual(Money("-289.98300000", USDT), position.realized_pnl) self.assertEqual(Decimal("9999.413793103448275862068966"), position.avg_open) fill3 = TestStubs.event_order_filled( order3, instrument=BTCUSDT_BINANCE, position_id=PositionId("P-19700101-000000-000-001-1"), strategy_id=StrategyId("S", "001"), fill_price=Price("10001.00"), ) position.apply(fill3) self.assertEqual(Quantity(20), position.quantity) self.assertEqual(Money("-365.71613793", USDT), position.realized_pnl) self.assertEqual(Decimal("9999.413793103448275862068966"), position.avg_open) fill4 = TestStubs.event_order_filled( order4, instrument=BTCUSDT_BINANCE, position_id=PositionId("P-19700101-000000-000-001-1"), strategy_id=StrategyId("S", "001"), fill_price=Price("10003.00"), ) position.apply(fill4) self.assertEqual(Quantity(23), position.quantity) self.assertEqual(Money("-395.72513793", USDT), position.realized_pnl) self.assertEqual(Decimal("9999.881559220389805097451274"), position.avg_open) fill5 = TestStubs.event_order_filled( order5, instrument=BTCUSDT_BINANCE, position_id=PositionId("P-19700101-000000-000-001-1"), strategy_id=StrategyId("S", "001"), fill_price=Price("10005"), ) position.apply(fill5) self.assertEqual(Quantity(19), position.quantity) self.assertEqual(Money("-415.27137481", USDT), position.realized_pnl) self.assertEqual(Decimal("9999.881559220389805097451274"), position.avg_open) self.assertEqual("Position(id=P-19700101-000000-000-001-1, LONG 19.000000 BTC/USDT.BINANCE)", repr(position)) def test_calculate_pnl_when_given_position_side_flat_returns_zero(self): # Arrange order = self.order_factory.market( BTCUSDT_BINANCE.symbol, OrderSide.BUY, Quantity(12), ) fill = TestStubs.event_order_filled( order, instrument=BTCUSDT_BINANCE, position_id=PositionId("P-123456"), strategy_id=StrategyId("S", "001"), fill_price=Price("10500.00"), ) position = Position(fill) # Act result = position.calculate_pnl(Price("10500.00"), Price("10500.00"), Quantity(100000)) # Assert self.assertEqual(Money(0, USDT), result) def test_calculate_pnl_for_long_position_win(self): # Arrange order = self.order_factory.market( BTCUSDT_BINANCE.symbol, OrderSide.BUY, Quantity(12), ) fill = TestStubs.event_order_filled( order, instrument=BTCUSDT_BINANCE, position_id=PositionId("P-123456"), strategy_id=StrategyId("S", "001"), fill_price=Price("10500.00"), ) position = Position(fill) # Act pnl = position.calculate_pnl( avg_open=Price("10500.00"), avg_close=Price("10510.00"), quantity=Quantity(12), ) # Assert self.assertEqual(Money("120.00000000", USDT), pnl) self.assertEqual(Money("-126.00000000", USDT), position.realized_pnl) self.assertEqual(Money("120.00000000", USDT), position.unrealized_pnl(Price("10510.00"))) self.assertEqual(Money("-6.00000000", USDT), position.total_pnl(Price("10510.00"))) self.assertEqual([Money("126.00000000", USDT)], position.commissions()) self.assertEqual(Money("126.00000000", USDT), position.commission) def test_calculate_pnl_for_long_position_loss(self): # Arrange order = self.order_factory.market( BTCUSDT_BINANCE.symbol, OrderSide.BUY, Quantity(12), ) fill = TestStubs.event_order_filled( order, instrument=BTCUSDT_BINANCE, position_id=PositionId("P-123456"), strategy_id=StrategyId("S", "001"), fill_price=Price("10500.00"), ) position = Position(fill) # Act pnl = position.calculate_pnl( avg_open=Price("10500.00"), avg_close=Price("10480.50"), quantity=Quantity(10), ) # Assert self.assertEqual(Money("-195.00000000", USDT), pnl) self.assertEqual(Money("-126.00000000", USDT), position.realized_pnl) self.assertEqual(Money("-234.00000000", USDT), position.unrealized_pnl(Price("10480.50"))) self.assertEqual(Money("-360.00000000", USDT), position.total_pnl(Price("10480.50"))) self.assertEqual([Money("126.00000000", USDT)], position.commissions()) self.assertEqual(Money("126.00000000", USDT), position.commission) def test_calculate_pnl_for_short_position_winning(self): # Arrange order = self.order_factory.market( BTCUSDT_BINANCE.symbol, OrderSide.SELL, Quantity("10.150000"), ) fill = TestStubs.event_order_filled( order, instrument=BTCUSDT_BINANCE, position_id=PositionId("P-123456"), strategy_id=StrategyId("S", "001"), fill_price=Price("10500.00"), ) position = Position(fill) # Act pnl = position.calculate_pnl( Price("10500.00"), Price("10390.00"), Quantity("10.150000"), ) # Assert self.assertEqual(Money("1116.50000000", USDT), pnl) self.assertEqual(Money("1116.50000000", USDT), position.unrealized_pnl(Price("10390.00"))) self.assertEqual(Money("-106.57500000", USDT), position.realized_pnl) self.assertEqual([Money("106.57500000", USDT)], position.commissions()) self.assertEqual(Money("106.57500000", USDT), position.commission) self.assertEqual(Money("105458.50000000", USDT), position.notional_value(Price("10390.00"))) def test_calculate_pnl_for_short_position_loss(self): # Arrange order = self.order_factory.market( BTCUSDT_BINANCE.symbol, OrderSide.SELL, Quantity("10"), ) fill = TestStubs.event_order_filled( order, instrument=BTCUSDT_BINANCE, position_id=PositionId("P-123456"), strategy_id=StrategyId("S", "001"), fill_price=Price("10500.00"), ) position = Position(fill) # Act pnl = position.calculate_pnl( Price("10500.00"), Price("10670.50"), Quantity("10.000000"), ) # Assert self.assertEqual(Money("-1705.00000000", USDT), pnl) self.assertEqual(Money("-1705.00000000", USDT), position.unrealized_pnl(Price("10670.50"))) self.assertEqual(Money("-105.00000000", USDT), position.realized_pnl) self.assertEqual([Money("105.00000000", USDT)], position.commissions()) self.assertEqual(Money("105.00000000", USDT), position.commission) self.assertEqual(Money("106705.00000000", USDT), position.notional_value(Price("10670.50"))) def test_calculate_pnl_for_inverse1(self): # Arrange order = self.order_factory.market( XBTUSD_BITMEX.symbol, OrderSide.SELL, Quantity(100000), ) fill = TestStubs.event_order_filled( order, instrument=XBTUSD_BITMEX, position_id=PositionId("P-123456"), strategy_id=StrategyId("S", "001"), fill_price=Price("10000.00"), ) position = Position(fill) # Act pnl = position.calculate_pnl( Price("10000.00"), Price("11000.00"), Quantity(100000), ) # Assert self.assertEqual(Money(-10000.00, USD), pnl) self.assertEqual(Money(-10000.00, USD), position.unrealized_pnl(Price("11000.00"))) self.assertEqual(Money(0.00, USD), position.realized_pnl) self.assertEqual(Money(0.00, USD), position.commission) self.assertEqual(Money(100000.00, USD), position.notional_value(Price("11000.00"))) def test_calculate_pnl_for_inverse2(self): # Arrange order = self.order_factory.market( ETHUSD_BITMEX.symbol, OrderSide.SELL, Quantity(100000), ) fill = TestStubs.event_order_filled( order, instrument=ETHUSD_BITMEX, position_id=PositionId("P-123456"), strategy_id=StrategyId("S", "001"), fill_price=Price("375.95"), ) position = Position(fill) # Act pnl = position.calculate_pnl( Price("375.95"), Price("365.50"), Quantity(100000), # xrate=Decimal("0.0294337") # Currently not handling quanto settlement ) # Assert self.assertEqual(Money(1582.66, USD), position.unrealized_pnl(Price("370.00"))) self.assertEqual(Money(100000.00, USD), position.notional_value(Price("370.00"))) def test_calculate_unrealized_pnl_for_long(self): # Arrange order1 = self.order_factory.market( BTCUSDT_BINANCE.symbol, OrderSide.BUY, Quantity("2.000000"), ) order2 = self.order_factory.market( BTCUSDT_BINANCE.symbol, OrderSide.BUY, Quantity("2.000000"), ) fill1 = TestStubs.event_order_filled( order1, instrument=BTCUSDT_BINANCE, position_id=PositionId("P-123456"), strategy_id=StrategyId("S", "001"), fill_price=Price("10500.00"), ) fill2 = TestStubs.event_order_filled( order2, instrument=BTCUSDT_BINANCE, position_id=PositionId("P-123456"), strategy_id=StrategyId("S", "001"), fill_price=Price("10500.00"), ) position = Position(fill1) position.apply(fill2) # Act pnl = position.unrealized_pnl(Price("11505.60")) # Assert self.assertEqual(Money("4022.40000000", USDT), pnl) self.assertEqual(Money("-42.00000000", USDT), position.realized_pnl) self.assertEqual([Money("42.00000000", USDT)], position.commissions()) self.assertEqual(Money("42.00000000", USDT), position.commission) def test_calculate_unrealized_pnl_for_short(self): # Arrange order = self.order_factory.market( BTCUSDT_BINANCE.symbol, OrderSide.SELL, Quantity("5.912000"), ) fill = TestStubs.event_order_filled( order, instrument=BTCUSDT_BINANCE, position_id=PositionId("P-123456"), strategy_id=StrategyId("S", "001"), fill_price=Price("10505.60"), ) position = Position(fill) pnl = position.unrealized_pnl(Price("10407.15")) # Assert self.assertEqual(Money("582.03640000", USDT), pnl) self.assertEqual(Money("-62.10910720", USDT), position.realized_pnl) self.assertEqual([Money("62.10910720", USDT)], position.commissions()) self.assertEqual(Money("62.10910720", USDT), position.commission)
class BinanceOrderRequestBuilderTests(unittest.TestCase): def setUp(self): # Fixture Setup self.order_factory = OrderFactory( trader_id=TraderId("TESTER", "000"), strategy_id=StrategyId("S", "001"), clock=TestClock(), ) def test_order_with_gtd_tif_raises_value_error(self): # Arrange order = self.order_factory.limit( symbol=BTCUSDT, order_side=OrderSide.BUY, quantity=Quantity("1.0"), price=Price("50000"), time_in_force=TimeInForce.GTD, expire_time=UNIX_EPOCH + timedelta(minutes=1), post_only=True, ) self.assertRaises(ValueError, BinanceOrderRequestBuilder.build_py, order) def test_order_with_day_tif_raises_value_error(self): # Arrange order = self.order_factory.limit( symbol=BTCUSDT, order_side=OrderSide.BUY, quantity=Quantity("1.0"), price=Price("50000"), time_in_force=TimeInForce.DAY, post_only=True, ) self.assertRaises(ValueError, BinanceOrderRequestBuilder.build_py, order) def test_market_order(self): # Arrange order = self.order_factory.market( symbol=BTCUSDT, order_side=OrderSide.BUY, quantity=Quantity("0.10000000"), ) # Act result = BinanceOrderRequestBuilder.build_py(order) # Assert expected = { 'newClientOrderId': 'O-19700101-000000-000-001-1', 'recvWindow': 10000, 'type': 'MARKET', } self.assertEqual(expected, result) def test_limit_buy_post_only_order(self): # Arrange order = self.order_factory.limit( symbol=BTCUSDT, order_side=OrderSide.BUY, quantity=Quantity("1.0"), price=Price("50000"), post_only=True, ) # Act result = BinanceOrderRequestBuilder.build_py(order) # Assert expected = { 'newClientOrderId': 'O-19700101-000000-000-001-1', 'recvWindow': 10000, 'type': 'LIMIT_MAKER', } self.assertEqual(expected, result) def test_limit_hidden_order_raises_value_error(self): # Arrange order = self.order_factory.limit( symbol=BTCUSDT, order_side=OrderSide.BUY, quantity=Quantity("1.0"), price=Price("50000"), time_in_force=TimeInForce.GTC, post_only=False, hidden=True, ) self.assertRaises(ValueError, BinanceOrderRequestBuilder.build_py, order) def test_limit_buy_ioc(self): # Arrange order = self.order_factory.limit( symbol=BTCUSDT, order_side=OrderSide.BUY, quantity=Quantity("1.0"), price=Price("50000"), time_in_force=TimeInForce.IOC, post_only=False, ) # Act result = BinanceOrderRequestBuilder.build_py(order) # Assert expected = { 'newClientOrderId': 'O-19700101-000000-000-001-1', 'recvWindow': 10000, 'timeInForce': 'IOC', 'type': 'LIMIT', } self.assertEqual(expected, result) def test_limit_sell_fok_order(self): # Arrange order = self.order_factory.limit( symbol=BTCUSDT, order_side=OrderSide.SELL, quantity=Quantity("1.0"), price=Price("50000"), time_in_force=TimeInForce.FOK, post_only=False, ) # Act result = BinanceOrderRequestBuilder.build_py(order) # Assert expected = { 'newClientOrderId': 'O-19700101-000000-000-001-1', 'recvWindow': 10000, 'timeInForce': 'FOK', 'type': 'LIMIT', } self.assertEqual(expected, result) def test_stop_market_buy_order(self): # Arrange order = self.order_factory.stop_market( symbol=BTCUSDT, order_side=OrderSide.SELL, quantity=Quantity("1.0"), price=Price("100000"), time_in_force=TimeInForce.GTC, ) # Act result = BinanceOrderRequestBuilder.build_py(order) # Assert expected = { 'newClientOrderId': 'O-19700101-000000-000-001-1', 'recvWindow': 10000, 'stopPrice': '100000', 'type': 'TAKE_PROFIT', } self.assertEqual(expected, result)
class TestPortfolio: def setup(self): # Fixture Setup self.clock = TestClock() self.logger = Logger(self.clock) self.trader_id = TestStubs.trader_id() self.order_factory = OrderFactory( trader_id=self.trader_id, strategy_id=StrategyId("S-001"), clock=TestClock(), ) self.msgbus = MessageBus( trader_id=self.trader_id, clock=self.clock, logger=self.logger, ) self.cache = TestStubs.cache() self.portfolio = Portfolio( msgbus=self.msgbus, cache=self.cache, clock=self.clock, logger=self.logger, ) self.exec_engine = ExecutionEngine( msgbus=self.msgbus, cache=self.cache, clock=self.clock, logger=self.logger, ) # Prepare components self.cache.add_instrument(AUDUSD_SIM) self.cache.add_instrument(GBPUSD_SIM) self.cache.add_instrument(BTCUSDT_BINANCE) self.cache.add_instrument(BTCUSD_BITMEX) self.cache.add_instrument(ETHUSD_BITMEX) self.cache.add_instrument(BETTING_INSTRUMENT) def test_account_when_no_account_returns_none(self): # Arrange, Act, Assert assert self.portfolio.account(SIM) is None def test_account_when_account_returns_the_account_facade(self): # Arrange state = AccountState( account_id=AccountId("BINANCE", "1513111"), account_type=AccountType.CASH, base_currency=None, reported=True, balances=[ AccountBalance( BTC, Money(10.00000000, BTC), Money(0.00000000, BTC), Money(10.00000000, BTC), ) ], info={}, event_id=UUID4(), ts_event=0, ts_init=0, ) self.portfolio.update_account(state) # Act result = self.portfolio.account(BINANCE) # Assert assert result.id.issuer == "BINANCE" def test_balances_locked_when_no_account_for_venue_returns_none(self): # Arrange, Act, Assert assert self.portfolio.balances_locked(SIM) is None def test_margins_init_when_no_account_for_venue_returns_none(self): # Arrange, Act, Assert assert self.portfolio.margins_init(SIM) is None def test_margins_maint_when_no_account_for_venue_returns_none(self): # Arrange, Act, Assert assert self.portfolio.margins_maint(SIM) is None def test_unrealized_pnl_for_instrument_when_no_instrument_returns_none( self): # Arrange, Act, Assert assert self.portfolio.unrealized_pnl(USDJPY_SIM.id) is None def test_unrealized_pnl_for_venue_when_no_account_returns_empty_dict(self): # Arrange, Act, Assert assert self.portfolio.unrealized_pnls(SIM) == {} def test_net_position_when_no_positions_returns_zero(self): # Arrange, Act, Assert assert self.portfolio.net_position(AUDUSD_SIM.id) == Decimal(0) def test_net_exposures_when_no_positions_returns_none(self): # Arrange, Act, Assert assert self.portfolio.net_exposures(SIM) is None def test_is_net_long_when_no_positions_returns_false(self): # Arrange, Act, Assert assert self.portfolio.is_net_long(AUDUSD_SIM.id) is False def test_is_net_short_when_no_positions_returns_false(self): # Arrange, Act, Assert assert self.portfolio.is_net_short(AUDUSD_SIM.id) is False def test_is_flat_when_no_positions_returns_true(self): # Arrange, Act, Assert assert self.portfolio.is_flat(AUDUSD_SIM.id) is True def test_is_completely_flat_when_no_positions_returns_true(self): # Arrange, Act, Assert assert self.portfolio.is_flat(AUDUSD_SIM.id) is True def test_open_value_when_no_account_returns_none(self): # Arrange, Act, Assert assert self.portfolio.net_exposures(SIM) is None def test_update_tick(self): # Arrange tick = TestStubs.quote_tick_5decimal(GBPUSD_SIM.id) # Act self.portfolio.update_tick(tick) # Assert assert self.portfolio.unrealized_pnl(GBPUSD_SIM.id) is None def test_update_orders_working_cash_account(self): # Arrange AccountFactory.register_calculated_account("BINANCE") account_id = AccountId("BINANCE", "000") state = AccountState( account_id=account_id, account_type=AccountType.CASH, base_currency=None, # Multi-currency account reported=True, balances=[ AccountBalance( BTC, Money(10.00000000, BTC), Money(0.00000000, BTC), Money(10.00000000, BTC), ), AccountBalance( USDT, Money(100000.00000000, USDT), Money(0.00000000, USDT), Money(100000.00000000, USDT), ), ], info={}, event_id=UUID4(), ts_event=0, ts_init=0, ) self.portfolio.update_account(state) # Create two working orders order = self.order_factory.limit( BTCUSDT_BINANCE.id, OrderSide.BUY, Quantity.from_str("1.0"), Price.from_str("50000.00"), ) self.cache.add_order(order, position_id=None) # Act: push order state to ACCEPTED self.exec_engine.process( TestStubs.event_order_submitted(order, account_id=account_id)) self.exec_engine.process( TestStubs.event_order_accepted(order, account_id=account_id)) # Assert assert self.portfolio.balances_locked( BINANCE)[USDT].as_decimal() == 50100 def test_update_orders_working_margin_account(self): # Arrange AccountFactory.register_calculated_account("BINANCE") account_id = AccountId("BINANCE", "01234") state = AccountState( account_id=account_id, account_type=AccountType.MARGIN, base_currency=None, # Multi-currency account reported=True, balances=[ AccountBalance( BTC, Money(10.00000000, BTC), Money(0.00000000, BTC), Money(10.00000000, BTC), ), AccountBalance( ETH, Money(20.00000000, ETH), Money(0.00000000, ETH), Money(20.00000000, ETH), ), AccountBalance( USDT, Money(100000.00000000, USDT), Money(0.00000000, USDT), Money(100000.00000000, USDT), ), ], info={}, event_id=UUID4(), ts_event=0, ts_init=0, ) self.portfolio.update_account(state) # Create two working orders order1 = self.order_factory.stop_market( BTCUSDT_BINANCE.id, OrderSide.BUY, Quantity.from_str("10.5"), Price.from_str("25000.00"), ) order2 = self.order_factory.stop_market( BTCUSDT_BINANCE.id, OrderSide.BUY, Quantity.from_str("10.5"), Price.from_str("25000.00"), ) self.cache.add_order(order1, position_id=None) self.cache.add_order(order2, position_id=None) # Push states to ACCEPTED order1.apply(TestStubs.event_order_submitted(order1)) self.cache.update_order(order1) order1.apply(TestStubs.event_order_accepted(order1)) self.cache.update_order(order1) filled1 = TestStubs.event_order_filled( order1, instrument=BTCUSDT_BINANCE, strategy_id=StrategyId("S-1"), account_id=account_id, position_id=PositionId("P-1"), last_px=Price.from_str("25000.00"), ) self.exec_engine.process(filled1) # Update the last quote last = QuoteTick( instrument_id=BTCUSDT_BINANCE.id, bid=Price.from_str("25001.00"), ask=Price.from_str("25002.00"), bid_size=Quantity.from_int(1), ask_size=Quantity.from_int(1), ts_event=0, ts_init=0, ) # Act self.portfolio.update_tick(last) self.portfolio.initialize_orders() # Assert assert self.portfolio.margins_init(BINANCE) == {} def test_order_accept_updates_margin_init(self): # Arrange AccountFactory.register_calculated_account("BINANCE") state = AccountState( account_id=AccountId("BETFAIR", "01234"), account_type=AccountType.MARGIN, base_currency=GBP, reported=True, balances=[ AccountBalance( currency=GBP, total=Money(1000, GBP), free=Money(1000, GBP), locked=Money(0, GBP), ), ], info={}, event_id=UUID4(), ts_event=0, ts_init=0, ) AccountFactory.register_calculated_account("BETFAIR") self.portfolio.update_account(state) # Create a passive order order1 = self.order_factory.limit( BETTING_INSTRUMENT.id, OrderSide.BUY, Quantity.from_str("100"), Price.from_str("0.5"), ) self.cache.add_order(order1, position_id=None) # Push states to ACCEPTED order1.apply(TestStubs.event_order_submitted(order1)) self.cache.update_order(order1) order1.apply( TestStubs.event_order_accepted(order1, venue_order_id=VenueOrderId("1"))) self.cache.update_order(order1) # Act self.portfolio.initialize_orders() # Assert assert self.portfolio.margins_init(BETFAIR)[ BETTING_INSTRUMENT.id] == Money(200, GBP) def test_update_positions(self): # Arrange AccountFactory.register_calculated_account("BINANCE") account_id = AccountId("BINANCE", "01234") state = AccountState( account_id=account_id, account_type=AccountType.CASH, base_currency=None, # Multi-currency account reported=True, balances=[ AccountBalance( BTC, Money(10.00000000, BTC), Money(0.00000000, BTC), Money(10.00000000, BTC), ), AccountBalance( ETH, Money(20.00000000, ETH), Money(0.00000000, ETH), Money(20.00000000, ETH), ), ], info={}, event_id=UUID4(), ts_event=0, ts_init=0, ) self.portfolio.update_account(state) # Create a closed position order1 = self.order_factory.market( BTCUSDT_BINANCE.id, OrderSide.BUY, Quantity.from_str("10.50000000"), ) order2 = self.order_factory.market( BTCUSDT_BINANCE.id, OrderSide.SELL, Quantity.from_str("10.50000000"), ) self.cache.add_order(order1, position_id=None) self.cache.add_order(order2, position_id=None) # Push states to ACCEPTED order1.apply(TestStubs.event_order_submitted(order1)) self.cache.update_order(order1) order1.apply(TestStubs.event_order_accepted(order1)) self.cache.update_order(order1) fill1 = TestStubs.event_order_filled( order1, instrument=BTCUSDT_BINANCE, strategy_id=StrategyId("S-1"), account_id=account_id, position_id=PositionId("P-1"), last_px=Price.from_str("25000.00"), ) fill2 = TestStubs.event_order_filled( order2, instrument=BTCUSDT_BINANCE, strategy_id=StrategyId("S-1"), account_id=account_id, position_id=PositionId("P-1"), last_px=Price.from_str("25000.00"), ) position1 = Position(instrument=BTCUSDT_BINANCE, fill=fill1) position1.apply(fill2) order3 = self.order_factory.market( BTCUSDT_BINANCE.id, OrderSide.BUY, Quantity.from_str("10.00000000"), ) fill3 = TestStubs.event_order_filled( order3, instrument=BTCUSDT_BINANCE, strategy_id=StrategyId("S-1"), account_id=account_id, position_id=PositionId("P-2"), last_px=Price.from_str("25000.00"), ) position2 = Position(instrument=BTCUSDT_BINANCE, fill=fill3) # Update the last quote last = QuoteTick( instrument_id=BTCUSDT_BINANCE.id, bid=Price.from_str("25001.00"), ask=Price.from_str("25002.00"), bid_size=Quantity.from_int(1), ask_size=Quantity.from_int(1), ts_event=0, ts_init=0, ) # Act self.cache.add_position(position1, OMSType.HEDGING) self.cache.add_position(position2, OMSType.HEDGING) self.portfolio.initialize_positions() self.portfolio.update_tick(last) # Assert assert self.portfolio.is_net_long(BTCUSDT_BINANCE.id) def test_opening_one_long_position_updates_portfolio(self): # Arrange AccountFactory.register_calculated_account("BINANCE") account_id = AccountId("BINANCE", "01234") state = AccountState( account_id=account_id, account_type=AccountType.MARGIN, base_currency=None, # Multi-currency account reported=True, balances=[ AccountBalance( BTC, Money(10.00000000, BTC), Money(0.00000000, BTC), Money(10.00000000, BTC), ), AccountBalance( ETH, Money(20.00000000, ETH), Money(0.00000000, ETH), Money(20.00000000, ETH), ), AccountBalance( USDT, Money(100000.00000000, USDT), Money(0.00000000, USDT), Money(100000.00000000, USDT), ), ], info={}, event_id=UUID4(), ts_event=0, ts_init=0, ) self.portfolio.update_account(state) order = self.order_factory.market( BTCUSDT_BINANCE.id, OrderSide.BUY, Quantity.from_str("10.000000"), ) fill = TestStubs.event_order_filled( order=order, instrument=BTCUSDT_BINANCE, strategy_id=StrategyId("S-001"), account_id=account_id, position_id=PositionId("P-123456"), last_px=Price.from_str("10500.00"), ) last = QuoteTick( instrument_id=BTCUSDT_BINANCE.id, bid=Price.from_str("10510.00"), ask=Price.from_str("10511.00"), bid_size=Quantity.from_str("1.000000"), ask_size=Quantity.from_str("1.000000"), ts_event=0, ts_init=0, ) self.cache.add_quote_tick(last) self.portfolio.update_tick(last) position = Position(instrument=BTCUSDT_BINANCE, fill=fill) # Act self.cache.add_position(position, OMSType.HEDGING) self.portfolio.update_position( TestStubs.event_position_opened(position)) # Assert assert self.portfolio.net_exposures(BINANCE) == { USDT: Money(105100.00000000, USDT) } assert self.portfolio.unrealized_pnls(BINANCE) == { USDT: Money(100.00000000, USDT) } assert self.portfolio.margins_maint(BINANCE) == { BTCUSDT_BINANCE.id: Money(105.00000000, USDT) } assert self.portfolio.net_exposure(BTCUSDT_BINANCE.id) == Money( 105100.00000000, USDT) assert self.portfolio.unrealized_pnl(BTCUSDT_BINANCE.id) == Money( 100.00000000, USDT) assert self.portfolio.net_position( order.instrument_id) == Decimal("10.00000000") assert self.portfolio.is_net_long(order.instrument_id) assert not self.portfolio.is_net_short(order.instrument_id) assert not self.portfolio.is_flat(order.instrument_id) assert not self.portfolio.is_completely_flat() def test_opening_one_short_position_updates_portfolio(self): # Arrange AccountFactory.register_calculated_account("BINANCE") account_id = AccountId("BINANCE", "01234") state = AccountState( account_id=account_id, account_type=AccountType.MARGIN, base_currency=None, # Multi-currency account reported=True, balances=[ AccountBalance( BTC, Money(10.00000000, BTC), Money(0.00000000, BTC), Money(10.00000000, BTC), ), AccountBalance( ETH, Money(20.00000000, ETH), Money(0.00000000, ETH), Money(20.00000000, ETH), ), AccountBalance( USDT, Money(100000.00000000, USDT), Money(0.00000000, USDT), Money(100000.00000000, USDT), ), ], info={}, event_id=UUID4(), ts_event=0, ts_init=0, ) self.portfolio.update_account(state) order = self.order_factory.market( BTCUSDT_BINANCE.id, OrderSide.SELL, Quantity.from_str("0.515"), ) fill = TestStubs.event_order_filled( order=order, instrument=BTCUSDT_BINANCE, strategy_id=StrategyId("S-001"), account_id=account_id, position_id=PositionId("P-123456"), last_px=Price.from_str("15000.00"), ) last = QuoteTick( instrument_id=BTCUSDT_BINANCE.id, bid=Price.from_str("15510.15"), ask=Price.from_str("15510.25"), bid_size=Quantity.from_str("12.62"), ask_size=Quantity.from_str("3.1"), ts_event=0, ts_init=0, ) self.cache.add_quote_tick(last) self.portfolio.update_tick(last) position = Position(instrument=BTCUSDT_BINANCE, fill=fill) # Act self.cache.add_position(position, OMSType.HEDGING) self.portfolio.update_position( TestStubs.event_position_opened(position)) # Assert assert self.portfolio.net_exposures(BINANCE) == { USDT: Money(7987.77875000, USDT) } assert self.portfolio.unrealized_pnls(BINANCE) == { USDT: Money(-262.77875000, USDT) } assert self.portfolio.margins_maint(BINANCE) == { BTCUSDT_BINANCE.id: Money(7.72500000, USDT) } assert self.portfolio.net_exposure(BTCUSDT_BINANCE.id) == Money( 7987.77875000, USDT) assert self.portfolio.unrealized_pnl(BTCUSDT_BINANCE.id) == Money( -262.77875000, USDT) assert self.portfolio.net_position( order.instrument_id) == Decimal("-0.515") assert not self.portfolio.is_net_long(order.instrument_id) assert self.portfolio.is_net_short(order.instrument_id) assert not self.portfolio.is_flat(order.instrument_id) assert not self.portfolio.is_completely_flat() def test_opening_positions_with_multi_asset_account(self): # Arrange AccountFactory.register_calculated_account("BITMEX") account_id = AccountId("BITMEX", "01234") state = AccountState( account_id=account_id, account_type=AccountType.MARGIN, base_currency=None, # Multi-currency account reported=True, balances=[ AccountBalance( BTC, Money(10.00000000, BTC), Money(0.00000000, BTC), Money(10.00000000, BTC), ), AccountBalance( ETH, Money(20.00000000, ETH), Money(0.00000000, ETH), Money(20.00000000, ETH), ), ], info={}, event_id=UUID4(), ts_event=0, ts_init=0, ) self.portfolio.update_account(state) last_ethusd = QuoteTick( instrument_id=ETHUSD_BITMEX.id, bid=Price.from_str("376.05"), ask=Price.from_str("377.10"), bid_size=Quantity.from_str("16"), ask_size=Quantity.from_str("25"), ts_event=0, ts_init=0, ) last_btcusd = QuoteTick( instrument_id=BTCUSD_BITMEX.id, bid=Price.from_str("10500.05"), ask=Price.from_str("10501.51"), bid_size=Quantity.from_str("2.54"), ask_size=Quantity.from_str("0.91"), ts_event=0, ts_init=0, ) self.cache.add_quote_tick(last_ethusd) self.cache.add_quote_tick(last_btcusd) self.portfolio.update_tick(last_ethusd) self.portfolio.update_tick(last_btcusd) order = self.order_factory.market( ETHUSD_BITMEX.id, OrderSide.BUY, Quantity.from_int(10000), ) fill = TestStubs.event_order_filled( order=order, instrument=ETHUSD_BITMEX, strategy_id=StrategyId("S-001"), account_id=account_id, position_id=PositionId("P-123456"), last_px=Price.from_str("376.05"), ) position = Position(instrument=ETHUSD_BITMEX, fill=fill) # Act self.cache.add_position(position, OMSType.HEDGING) self.portfolio.update_position( TestStubs.event_position_opened(position)) # Assert assert self.portfolio.net_exposures(BITMEX) == { ETH: Money(26.59220848, ETH) } assert self.portfolio.margins_maint(BITMEX) == { ETHUSD_BITMEX.id: Money(0.20608962, ETH) } assert self.portfolio.net_exposure(ETHUSD_BITMEX.id) == Money( 26.59220848, ETH) assert self.portfolio.unrealized_pnl(ETHUSD_BITMEX.id) == Money( 0.00000000, ETH) def test_unrealized_pnl_when_insufficient_data_for_xrate_returns_none( self): # Arrange AccountFactory.register_calculated_account("BITMEX") state = AccountState( account_id=AccountId("BITMEX", "01234"), account_type=AccountType.MARGIN, base_currency=BTC, reported=True, balances=[ AccountBalance( BTC, Money(10.00000000, BTC), Money(0.00000000, BTC), Money(10.00000000, BTC), ), AccountBalance( ETH, Money(20.00000000, ETH), Money(0.00000000, ETH), Money(20.00000000, ETH), ), ], info={}, event_id=UUID4(), ts_event=0, ts_init=0, ) self.portfolio.update_account(state) order = self.order_factory.market( ETHUSD_BITMEX.id, OrderSide.BUY, Quantity.from_int(100), ) self.cache.add_order(order, position_id=None) self.exec_engine.process(TestStubs.event_order_submitted(order)) self.exec_engine.process(TestStubs.event_order_accepted(order)) fill = TestStubs.event_order_filled( order=order, instrument=ETHUSD_BITMEX, strategy_id=StrategyId("S-1"), position_id=PositionId("P-123456"), last_px=Price.from_str("376.05"), ) self.exec_engine.process(fill) position = Position(instrument=ETHUSD_BITMEX, fill=fill) self.portfolio.update_position( TestStubs.event_position_opened(position)) # Act result = self.portfolio.unrealized_pnls(BITMEX) # # Assert assert result == {} def test_market_value_when_insufficient_data_for_xrate_returns_none(self): # Arrange AccountFactory.register_calculated_account("BITMEX") account_id = AccountId("BITMEX", "01234") state = AccountState( account_id=account_id, account_type=AccountType.MARGIN, base_currency=BTC, reported=True, balances=[ AccountBalance( BTC, Money(10.00000000, BTC), Money(0.00000000, BTC), Money(10.00000000, BTC), ), ], info={}, event_id=UUID4(), ts_event=0, ts_init=0, ) self.portfolio.update_account(state) order = self.order_factory.market( ETHUSD_BITMEX.id, OrderSide.BUY, Quantity.from_int(100), ) fill = TestStubs.event_order_filled( order=order, instrument=ETHUSD_BITMEX, strategy_id=StrategyId("S-1"), account_id=account_id, position_id=PositionId("P-123456"), last_px=Price.from_str("376.05"), ) last_ethusd = QuoteTick( instrument_id=ETHUSD_BITMEX.id, bid=Price.from_str("376.05"), ask=Price.from_str("377.10"), bid_size=Quantity.from_str("16"), ask_size=Quantity.from_str("25"), ts_event=0, ts_init=0, ) last_xbtusd = QuoteTick( instrument_id=BTCUSD_BITMEX.id, bid=Price.from_str("50000.00"), ask=Price.from_str("50000.00"), bid_size=Quantity.from_str("1"), ask_size=Quantity.from_str("1"), ts_event=0, ts_init=0, ) position = Position(instrument=ETHUSD_BITMEX, fill=fill) self.portfolio.update_position( TestStubs.event_position_opened(position)) self.cache.add_position(position, OMSType.HEDGING) self.cache.add_quote_tick(last_ethusd) self.cache.add_quote_tick(last_xbtusd) self.portfolio.update_tick(last_ethusd) self.portfolio.update_tick(last_xbtusd) # Act result = self.portfolio.net_exposures(BITMEX) # Assert assert result == {BTC: Money(0.00200000, BTC)} def test_opening_several_positions_updates_portfolio(self): # Arrange AccountFactory.register_calculated_account("SIM") account_id = AccountId("SIM", "01234") state = AccountState( account_id=account_id, account_type=AccountType.MARGIN, base_currency=USD, reported=True, balances=[ AccountBalance( USD, Money(1_000_000, USD), Money(0, USD), Money(1_000_000, USD), ), ], info={}, event_id=UUID4(), ts_event=0, ts_init=0, ) self.portfolio.update_account(state) last_audusd = QuoteTick( instrument_id=AUDUSD_SIM.id, bid=Price.from_str("0.80501"), ask=Price.from_str("0.80505"), bid_size=Quantity.from_int(1), ask_size=Quantity.from_int(1), ts_event=0, ts_init=0, ) last_gbpusd = QuoteTick( instrument_id=GBPUSD_SIM.id, bid=Price.from_str("1.30315"), ask=Price.from_str("1.30317"), bid_size=Quantity.from_int(1), ask_size=Quantity.from_int(1), ts_event=0, ts_init=0, ) self.cache.add_quote_tick(last_audusd) self.cache.add_quote_tick(last_gbpusd) self.portfolio.update_tick(last_audusd) self.portfolio.update_tick(last_gbpusd) order1 = self.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) order2 = self.order_factory.market( GBPUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) self.cache.add_order(order1, position_id=None) self.cache.add_order(order2, position_id=None) fill1 = TestStubs.event_order_filled( order1, instrument=AUDUSD_SIM, strategy_id=StrategyId("S-1"), account_id=account_id, position_id=PositionId("P-1"), last_px=Price.from_str("1.00000"), ) fill2 = TestStubs.event_order_filled( order2, instrument=GBPUSD_SIM, strategy_id=StrategyId("S-1"), account_id=account_id, position_id=PositionId("P-2"), last_px=Price.from_str("1.00000"), ) self.cache.update_order(order1) self.cache.update_order(order2) position1 = Position(instrument=AUDUSD_SIM, fill=fill1) position2 = Position(instrument=GBPUSD_SIM, fill=fill2) position_opened1 = TestStubs.event_position_opened(position1) position_opened2 = TestStubs.event_position_opened(position2) # Act self.cache.add_position(position1, OMSType.HEDGING) self.cache.add_position(position2, OMSType.HEDGING) self.portfolio.update_position(position_opened1) self.portfolio.update_position(position_opened2) # Assert assert self.portfolio.net_exposures(SIM) == { USD: Money(210816.00, USD) } assert self.portfolio.unrealized_pnls(SIM) == { USD: Money(10816.00, USD) } assert self.portfolio.margins_maint(SIM) == { AUDUSD_SIM.id: Money(3002.00, USD), GBPUSD_SIM.id: Money(3002.00, USD), } assert self.portfolio.net_exposure(AUDUSD_SIM.id) == Money( 80501.00, USD) assert self.portfolio.net_exposure(GBPUSD_SIM.id) == Money( 130315.00, USD) assert self.portfolio.unrealized_pnl(AUDUSD_SIM.id) == Money( -19499.00, USD) assert self.portfolio.unrealized_pnl(GBPUSD_SIM.id) == Money( 30315.00, USD) assert self.portfolio.net_position(AUDUSD_SIM.id) == Decimal(100000) assert self.portfolio.net_position(GBPUSD_SIM.id) == Decimal(100000) assert self.portfolio.is_net_long(AUDUSD_SIM.id) assert not self.portfolio.is_net_short(AUDUSD_SIM.id) assert not self.portfolio.is_flat(AUDUSD_SIM.id) assert not self.portfolio.is_completely_flat() def test_modifying_position_updates_portfolio(self): # Arrange AccountFactory.register_calculated_account("SIM") account_id = AccountId("SIM", "01234") state = AccountState( account_id=account_id, account_type=AccountType.MARGIN, base_currency=USD, reported=True, balances=[ AccountBalance( USD, Money(1_000_000, USD), Money(0, USD), Money(1_000_000, USD), ), ], info={}, event_id=UUID4(), ts_event=0, ts_init=0, ) self.portfolio.update_account(state) last_audusd = QuoteTick( instrument_id=AUDUSD_SIM.id, bid=Price.from_str("0.80501"), ask=Price.from_str("0.80505"), bid_size=Quantity.from_int(1), ask_size=Quantity.from_int(1), ts_event=0, ts_init=0, ) self.cache.add_quote_tick(last_audusd) self.portfolio.update_tick(last_audusd) order1 = self.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) fill1 = TestStubs.event_order_filled( order1, instrument=AUDUSD_SIM, strategy_id=StrategyId("S-1"), account_id=account_id, position_id=PositionId("P-123456"), last_px=Price.from_str("1.00000"), ) position = Position(instrument=AUDUSD_SIM, fill=fill1) self.cache.add_position(position, OMSType.HEDGING) self.portfolio.update_position( TestStubs.event_position_opened(position)) order2 = self.order_factory.market( AUDUSD_SIM.id, OrderSide.SELL, Quantity.from_int(50000), ) order2_filled = TestStubs.event_order_filled( order2, instrument=AUDUSD_SIM, strategy_id=StrategyId("S-1"), account_id=account_id, position_id=PositionId("P-123456"), last_px=Price.from_str("1.00000"), ) position.apply(order2_filled) # Act self.portfolio.update_position( TestStubs.event_position_changed(position)) # Assert assert self.portfolio.net_exposures(SIM) == {USD: Money(40250.50, USD)} assert self.portfolio.unrealized_pnls(SIM) == { USD: Money(-9749.50, USD) } assert self.portfolio.margins_maint(SIM) == { AUDUSD_SIM.id: Money(1501.00, USD) } assert self.portfolio.net_exposure(AUDUSD_SIM.id) == Money( 40250.50, USD) assert self.portfolio.unrealized_pnl(AUDUSD_SIM.id) == Money( -9749.50, USD) assert self.portfolio.net_position(AUDUSD_SIM.id) == Decimal(50000) assert self.portfolio.is_net_long(AUDUSD_SIM.id) assert not self.portfolio.is_net_short(AUDUSD_SIM.id) assert not self.portfolio.is_flat(AUDUSD_SIM.id) assert not self.portfolio.is_completely_flat() assert self.portfolio.unrealized_pnls(BINANCE) == {} assert self.portfolio.net_exposures(BINANCE) is None def test_closing_position_updates_portfolio(self): # Arrange AccountFactory.register_calculated_account("SIM") account_id = AccountId("SIM", "01234") state = AccountState( account_id=account_id, account_type=AccountType.MARGIN, base_currency=USD, reported=True, balances=[ AccountBalance( USD, Money(1_000_000, USD), Money(0, USD), Money(1_000_000, USD), ), ], info={}, event_id=UUID4(), ts_event=0, ts_init=0, ) self.portfolio.update_account(state) order1 = self.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) fill1 = TestStubs.event_order_filled( order1, instrument=AUDUSD_SIM, strategy_id=StrategyId("S-1"), account_id=account_id, position_id=PositionId("P-123456"), last_px=Price.from_str("1.00000"), ) position = Position(instrument=AUDUSD_SIM, fill=fill1) self.cache.add_position(position, OMSType.HEDGING) self.portfolio.update_position( TestStubs.event_position_opened(position)) order2 = self.order_factory.market( AUDUSD_SIM.id, OrderSide.SELL, Quantity.from_int(100000), ) order2_filled = TestStubs.event_order_filled( order2, instrument=AUDUSD_SIM, strategy_id=StrategyId("S-1"), account_id=account_id, position_id=PositionId("P-123456"), last_px=Price.from_str("1.00010"), ) position.apply(order2_filled) self.cache.update_position(position) # Act self.portfolio.update_position( TestStubs.event_position_closed(position)) # Assert assert self.portfolio.net_exposures(SIM) == {} assert self.portfolio.unrealized_pnls(SIM) == {} assert self.portfolio.margins_maint(SIM) == {} assert self.portfolio.net_exposure(AUDUSD_SIM.id) == Money(0, USD) assert self.portfolio.unrealized_pnl(AUDUSD_SIM.id) == Money(0, USD) assert self.portfolio.net_position(AUDUSD_SIM.id) == Decimal(0) assert not self.portfolio.is_net_long(AUDUSD_SIM.id) assert not self.portfolio.is_net_short(AUDUSD_SIM.id) assert self.portfolio.is_flat(AUDUSD_SIM.id) assert self.portfolio.is_completely_flat() def test_several_positions_with_different_instruments_updates_portfolio( self): # Arrange account_id = AccountId("SIM", "01234") state = AccountState( account_id=account_id, account_type=AccountType.MARGIN, base_currency=USD, reported=True, balances=[ AccountBalance( USD, Money(1_000_000, USD), Money(0, USD), Money(1_000_000, USD), ), ], info={}, event_id=UUID4(), ts_event=0, ts_init=0, ) self.portfolio.update_account(state) order1 = self.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) order2 = self.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) order3 = self.order_factory.market( GBPUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) order4 = self.order_factory.market( GBPUSD_SIM.id, OrderSide.SELL, Quantity.from_int(100000), ) fill1 = TestStubs.event_order_filled( order1, instrument=AUDUSD_SIM, strategy_id=StrategyId("S-1"), account_id=account_id, position_id=PositionId("P-1"), last_px=Price.from_str("1.00000"), ) fill2 = TestStubs.event_order_filled( order2, instrument=AUDUSD_SIM, strategy_id=StrategyId("S-1"), account_id=account_id, position_id=PositionId("P-2"), last_px=Price.from_str("1.00000"), ) fill3 = TestStubs.event_order_filled( order3, instrument=GBPUSD_SIM, strategy_id=StrategyId("S-1"), account_id=account_id, position_id=PositionId("P-3"), last_px=Price.from_str("1.00000"), ) fill4 = TestStubs.event_order_filled( order4, instrument=GBPUSD_SIM, strategy_id=StrategyId("S-1"), account_id=account_id, position_id=PositionId("P-3"), last_px=Price.from_str("1.00100"), ) position1 = Position(instrument=AUDUSD_SIM, fill=fill1) position2 = Position(instrument=AUDUSD_SIM, fill=fill2) position3 = Position(instrument=GBPUSD_SIM, fill=fill3) last_audusd = QuoteTick( instrument_id=AUDUSD_SIM.id, bid=Price.from_str("0.80501"), ask=Price.from_str("0.80505"), bid_size=Quantity.from_int(1), ask_size=Quantity.from_int(1), ts_event=0, ts_init=0, ) last_gbpusd = QuoteTick( instrument_id=GBPUSD_SIM.id, bid=Price.from_str("1.30315"), ask=Price.from_str("1.30317"), bid_size=Quantity.from_int(1), ask_size=Quantity.from_int(1), ts_event=0, ts_init=0, ) self.cache.add_quote_tick(last_audusd) self.cache.add_quote_tick(last_gbpusd) self.portfolio.update_tick(last_audusd) self.portfolio.update_tick(last_gbpusd) self.cache.add_position(position1, OMSType.HEDGING) self.cache.add_position(position2, OMSType.HEDGING) self.cache.add_position(position3, OMSType.HEDGING) # Act self.portfolio.update_position( TestStubs.event_position_opened(position1)) self.portfolio.update_position( TestStubs.event_position_opened(position2)) self.portfolio.update_position( TestStubs.event_position_opened(position3)) position3.apply(fill4) self.cache.update_position(position3) self.portfolio.update_position( TestStubs.event_position_closed(position3)) # Assert assert { USD: Money(-38998.00, USD) } == self.portfolio.unrealized_pnls(SIM) assert { USD: Money(161002.00, USD) } == self.portfolio.net_exposures(SIM) assert Money(161002.00, USD) == self.portfolio.net_exposure(AUDUSD_SIM.id) assert Money(-38998.00, USD) == self.portfolio.unrealized_pnl(AUDUSD_SIM.id) assert self.portfolio.unrealized_pnl(GBPUSD_SIM.id) == Money(0, USD) assert self.portfolio.net_position(AUDUSD_SIM.id) == Decimal(200000) assert self.portfolio.net_position(GBPUSD_SIM.id) == Decimal(0) assert self.portfolio.is_net_long(AUDUSD_SIM.id) assert self.portfolio.is_flat(GBPUSD_SIM.id) assert not self.portfolio.is_completely_flat()
class TestOrders: def setup(self): # Fixture Setup self.trader_id = TestStubs.trader_id() self.strategy_id = TestStubs.strategy_id() self.account_id = TestStubs.account_id() self.order_factory = OrderFactory( trader_id=self.trader_id, strategy_id=self.strategy_id, clock=TestClock(), ) def test_opposite_side_given_invalid_value_raises_value_error(self): # Arrange, Act, Assert with pytest.raises(ValueError): Order.opposite_side(0) # <-- invalid value def test_flatten_side_given_invalid_value_or_flat_raises_value_error(self): # Arrange, Act with pytest.raises(ValueError): Order.flatten_side(0) # <-- invalid value with pytest.raises(ValueError): Order.flatten_side(PositionSide.FLAT) @pytest.mark.parametrize( "side, expected", [ [OrderSide.BUY, OrderSide.SELL], [OrderSide.SELL, OrderSide.BUY], ], ) def test_opposite_side_returns_expected_sides(self, side, expected): # Arrange, Act result = Order.opposite_side(side) # Assert assert result == expected @pytest.mark.parametrize( "side, expected", [ [PositionSide.LONG, OrderSide.SELL], [PositionSide.SHORT, OrderSide.BUY], ], ) def test_flatten_side_returns_expected_sides(self, side, expected): # Arrange, Act result = Order.flatten_side(side) # Assert assert result == expected def test_market_order_with_quantity_zero_raises_value_error(self): # Arrange, Act, Assert with pytest.raises(ValueError): MarketOrder( self.trader_id, self.strategy_id, AUDUSD_SIM.id, ClientOrderId("O-123456"), OrderSide.BUY, Quantity.zero(), TimeInForce.DAY, UUID4(), 0, ) def test_market_order_with_invalid_tif_raises_value_error(self): # Arrange, Act, Assert with pytest.raises(ValueError): MarketOrder( self.trader_id, self.strategy_id, AUDUSD_SIM.id, ClientOrderId("O-123456"), OrderSide.BUY, Quantity.zero(), TimeInForce.GTD, # <-- invalid UUID4(), 0, ) def test_stop_market_order_with_gtd_and_expire_time_none_raises_type_error( self): # Arrange, Act, Assert with pytest.raises(TypeError): StopMarketOrder( self.trader_id, self.strategy_id, AUDUSD_SIM.id, ClientOrderId("O-123456"), OrderSide.BUY, Quantity.from_int(100000), price=Price.from_str("1.00000"), init_id=UUID4(), ts_init=0, time_in_force=TimeInForce.GTD, expire_time=None, ) def test_stop_limit_buy_order_with_gtd_and_expire_time_none_raises_type_error( self): # Arrange, Act, Assert with pytest.raises(TypeError): StopLimitOrder( self.trader_id, self.strategy_id, AUDUSD_SIM.id, ClientOrderId("O-123456"), OrderSide.BUY, Quantity.from_int(100000), price=Price.from_str("1.00001"), trigger=Price.from_str("1.00000"), init_id=UUID4(), ts_init=0, time_in_force=TimeInForce.GTD, expire_time=None, ) def test_overfill_limit_buy_order_raises_value_error(self): # Arrange, Act, Assert order = self.order_factory.limit( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), Price.from_str("1.00000"), ) order.apply(TestStubs.event_order_submitted(order)) order.apply(TestStubs.event_order_accepted(order)) over_fill = TestStubs.event_order_filled( order, instrument=AUDUSD_SIM, last_qty=Quantity.from_int(110000) # <-- overfill ) # Assert with pytest.raises(ValueError): order.apply(over_fill) def test_reset_order_factory(self): # Arrange self.order_factory.limit( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), Price.from_str("1.00000"), ) # Act self.order_factory.reset() order2 = self.order_factory.limit( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), Price.from_str("1.00000"), ) assert order2.client_order_id.value == "O-19700101-000000-000-001-1" def test_initialize_buy_market_order(self): # Arrange, Act order = self.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) # Assert assert order.symbol == AUDUSD_SIM.id.symbol assert order.venue == AUDUSD_SIM.id.venue assert order.type == OrderType.MARKET assert order.status == OrderStatus.INITIALIZED assert order.event_count == 1 assert isinstance(order.last_event, OrderInitialized) assert order.is_active assert not order.is_inflight assert not order.is_working assert not order.is_completed assert order.is_buy assert order.is_aggressive assert not order.is_sell assert not order.is_contingency assert not order.is_passive assert not order.is_parent_order assert not order.is_child_order assert order.ts_last == 0 assert order.last_event.ts_init == 0 assert isinstance(order.init_event, OrderInitialized) def test_initialize_sell_market_order(self): # Arrange, Act order = self.order_factory.market( AUDUSD_SIM.id, OrderSide.SELL, Quantity.from_int(100000), ) # Assert assert order.type == OrderType.MARKET assert order.status == OrderStatus.INITIALIZED assert order.event_count == 1 assert isinstance(order.last_event, OrderInitialized) assert len(order.events) == 1 assert order.is_active assert not order.is_inflight assert not order.is_working assert not order.is_completed assert not order.is_buy assert order.is_sell assert order.ts_last == 0 assert isinstance(order.init_event, OrderInitialized) def test_order_equality(self): # Arrange, Act order = self.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) # Assert assert order == order def test_order_hash_str_and_repr(self): # Arrange order = self.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), tags="ENTRY", ) # Act, Assert assert isinstance(hash(order), int) assert ( str(order) == "MarketOrder(BUY 100_000 AUD/USD.SIM MARKET GTC, status=INITIALIZED, client_order_id=O-19700101-000000-000-001-1, venue_order_id=None, tags=ENTRY)" # noqa ) assert ( repr(order) == "MarketOrder(BUY 100_000 AUD/USD.SIM MARKET GTC, status=INITIALIZED, client_order_id=O-19700101-000000-000-001-1, venue_order_id=None, tags=ENTRY)" # noqa ) def test_market_order_to_dict(self): # Arrange order = self.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) # Act result = order.to_dict() # Assert assert result == { "trader_id": "TESTER-000", "strategy_id": "S-001", "instrument_id": "AUD/USD.SIM", "client_order_id": "O-19700101-000000-000-001-1", "venue_order_id": None, "position_id": None, "account_id": None, "execution_id": None, "type": "MARKET", "side": "BUY", "quantity": "100000", "time_in_force": "GTC", "reduce_only": False, "filled_qty": "0", "avg_px": None, "slippage": "0", "status": "INITIALIZED", "order_list_id": None, "parent_order_id": None, "child_order_ids": None, "contingency": "NONE", "contingency_ids": None, "tags": None, "ts_last": 0, "ts_init": 0, } def test_initialize_limit_order(self): # Arrange, Act order = self.order_factory.limit( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), Price.from_str("1.00000"), ) # Assert assert order.type == OrderType.LIMIT assert order.status == OrderStatus.INITIALIZED assert order.time_in_force == TimeInForce.GTC assert order.is_passive assert order.is_active assert not order.is_aggressive assert not order.is_completed assert isinstance(order.init_event, OrderInitialized) assert ( str(order) == "LimitOrder(BUY 100_000 AUD/USD.SIM LIMIT @ 1.00000 GTC, status=INITIALIZED, client_order_id=O-19700101-000000-000-001-1, venue_order_id=None, tags=None)" # noqa ) assert ( repr(order) == "LimitOrder(BUY 100_000 AUD/USD.SIM LIMIT @ 1.00000 GTC, status=INITIALIZED, client_order_id=O-19700101-000000-000-001-1, venue_order_id=None, tags=None)" # noqa ) def test_limit_order_to_dict(self): # Arrange order = self.order_factory.limit( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), Price.from_str("1.00000"), display_qty=Quantity.from_int(20000), ) # Act result = order.to_dict() # Assert assert result == { "trader_id": "TESTER-000", "strategy_id": "S-001", "instrument_id": "AUD/USD.SIM", "client_order_id": "O-19700101-000000-000-001-1", "venue_order_id": None, "position_id": None, "account_id": None, "execution_id": None, "type": "LIMIT", "side": "BUY", "quantity": "100000", "price": "1.00000", "liquidity_side": "NONE", "expire_time_ns": 0, "time_in_force": "GTC", "filled_qty": "0", "avg_px": None, "slippage": "0", "status": "INITIALIZED", "is_post_only": False, "is_reduce_only": False, "display_qty": "20000", "order_list_id": None, "parent_order_id": None, "child_order_ids": None, "contingency": "NONE", "contingency_ids": None, "tags": None, "ts_last": 0, "ts_init": 0, } def test_initialize_limit_order_with_expire_time(self): # Arrange, Act order = self.order_factory.limit( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), Price.from_str("1.00000"), TimeInForce.GTD, expire_time=UNIX_EPOCH, ) # Assert assert order.instrument_id == AUDUSD_SIM.id assert order.type == OrderType.LIMIT assert order.price == Price.from_str("1.00000") assert order.status == OrderStatus.INITIALIZED assert order.time_in_force == TimeInForce.GTD assert order.expire_time == UNIX_EPOCH assert not order.is_completed assert isinstance(order.init_event, OrderInitialized) def test_initialize_stop_market_order(self): # Arrange, Act order = self.order_factory.stop_market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), Price.from_str("1.00000"), ) # Assert assert order.type == OrderType.STOP_MARKET assert order.status == OrderStatus.INITIALIZED assert order.time_in_force == TimeInForce.GTC assert order.is_passive assert not order.is_aggressive assert order.is_active assert not order.is_completed assert isinstance(order.init_event, OrderInitialized) assert ( str(order) == "StopMarketOrder(BUY 100_000 AUD/USD.SIM STOP_MARKET @ 1.00000 GTC, status=INITIALIZED, client_order_id=O-19700101-000000-000-001-1, venue_order_id=None, tags=None)" # noqa ) assert ( repr(order) == "StopMarketOrder(BUY 100_000 AUD/USD.SIM STOP_MARKET @ 1.00000 GTC, status=INITIALIZED, client_order_id=O-19700101-000000-000-001-1, venue_order_id=None, tags=None)" # noqa ) def test_stop_market_order_to_dict(self): # Arrange order = self.order_factory.stop_market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), Price.from_str("1.00000"), ) # Act result = order.to_dict() # Assert assert result == { "trader_id": "TESTER-000", "strategy_id": "S-001", "instrument_id": "AUD/USD.SIM", "client_order_id": "O-19700101-000000-000-001-1", "venue_order_id": None, "position_id": None, "account_id": None, "execution_id": None, "type": "STOP_MARKET", "side": "BUY", "quantity": "100000", "price": "1.00000", "liquidity_side": "NONE", "expire_time_ns": 0, "time_in_force": "GTC", "filled_qty": "0", "avg_px": None, "slippage": "0", "status": "INITIALIZED", "is_reduce_only": False, "order_list_id": None, "parent_order_id": None, "child_order_ids": None, "contingency": "NONE", "contingency_ids": None, "tags": None, "ts_last": 0, "ts_init": 0, } def test_initialize_stop_limit_order(self): # Arrange, Act order = self.order_factory.stop_limit( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), Price.from_str("1.00000"), Price.from_str("1.10010"), tags="ENTRY", ) # Assert assert order.type == OrderType.STOP_LIMIT assert order.status == OrderStatus.INITIALIZED assert order.time_in_force == TimeInForce.GTC assert order.is_passive assert not order.is_aggressive assert not order.is_completed assert isinstance(order.init_event, OrderInitialized) assert ( str(order) == "StopLimitOrder(BUY 100_000 AUD/USD.SIM STOP_LIMIT @ 1.00000 GTC, trigger=1.10010, status=INITIALIZED, client_order_id=O-19700101-000000-000-001-1)" # noqa ) assert ( repr(order) == "StopLimitOrder(BUY 100_000 AUD/USD.SIM STOP_LIMIT @ 1.00000 GTC, trigger=1.10010, status=INITIALIZED, client_order_id=O-19700101-000000-000-001-1)" # noqa ) def test_stop_limit_order_to_dict(self): # Arrange order = self.order_factory.stop_limit( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), Price.from_str("1.00000"), Price.from_str("1.10010"), tags="STOP_LOSS", ) # Act result = order.to_dict() # Assert assert result == { "trader_id": "TESTER-000", "strategy_id": "S-001", "instrument_id": "AUD/USD.SIM", "client_order_id": "O-19700101-000000-000-001-1", "venue_order_id": None, "position_id": None, "account_id": None, "execution_id": None, "type": "STOP_LIMIT", "side": "BUY", "quantity": "100000", "trigger": "1.10010", "price": "1.00000", "liquidity_side": "NONE", "expire_time_ns": 0, "time_in_force": "GTC", "filled_qty": "0", "avg_px": None, "slippage": "0", "status": "INITIALIZED", "is_post_only": False, "is_reduce_only": False, "display_qty": None, "order_list_id": None, "parent_order_id": None, "child_order_ids": None, "contingency": "NONE", "contingency_ids": None, "tags": "STOP_LOSS", "ts_last": 0, "ts_init": 0, } def test_order_list_equality(self): # Arrange bracket1 = self.order_factory.bracket_market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), Price.from_str("1.00000"), Price.from_str("1.00010"), ) bracket2 = self.order_factory.bracket_market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), Price.from_str("1.00000"), Price.from_str("1.00010"), ) # Act, Assert assert bracket1 == bracket1 assert bracket1 != bracket2 def test_bracket_market_order_list(self): # Arrange, Act bracket = self.order_factory.bracket_market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), Price.from_str("0.99990"), Price.from_str("1.00010"), TimeInForce.GTC, ) # Assert assert bracket.id == OrderListId("1") assert bracket.instrument_id == AUDUSD_SIM.id assert len(bracket.orders) == 3 assert bracket.orders[0].type == OrderType.MARKET assert bracket.orders[1].type == OrderType.STOP_MARKET assert bracket.orders[2].type == OrderType.LIMIT assert bracket.orders[0].instrument_id == AUDUSD_SIM.id assert bracket.orders[1].instrument_id == AUDUSD_SIM.id assert bracket.orders[2].instrument_id == AUDUSD_SIM.id assert bracket.orders[0].client_order_id == ClientOrderId( "O-19700101-000000-000-001-1") assert bracket.orders[1].client_order_id == ClientOrderId( "O-19700101-000000-000-001-2") assert bracket.orders[2].client_order_id == ClientOrderId( "O-19700101-000000-000-001-3") assert bracket.orders[0].side == OrderSide.BUY assert bracket.orders[1].side == OrderSide.SELL assert bracket.orders[2].side == OrderSide.SELL assert bracket.orders[0].quantity == Quantity.from_int(100000) assert bracket.orders[1].quantity == Quantity.from_int(100000) assert bracket.orders[2].quantity == Quantity.from_int(100000) assert bracket.orders[1].price == Price.from_str("0.99990") assert bracket.orders[2].price == Price.from_str("1.00010") assert bracket.orders[1].time_in_force == TimeInForce.GTC assert bracket.orders[2].time_in_force == TimeInForce.GTC assert bracket.orders[1].expire_time is None assert bracket.orders[2].expire_time is None assert bracket.orders[0].contingency == ContingencyType.OTO assert bracket.orders[1].contingency == ContingencyType.OCO assert bracket.orders[2].contingency == ContingencyType.OCO assert bracket.orders[0].contingency_ids == [ ClientOrderId("O-19700101-000000-000-001-2"), ClientOrderId("O-19700101-000000-000-001-3"), ] assert bracket.orders[1].contingency_ids == [ ClientOrderId("O-19700101-000000-000-001-3") ] assert bracket.orders[2].contingency_ids == [ ClientOrderId("O-19700101-000000-000-001-2") ] assert bracket.orders[0].child_order_ids == [ ClientOrderId("O-19700101-000000-000-001-2"), ClientOrderId("O-19700101-000000-000-001-3"), ] assert bracket.orders[1].parent_order_id == ClientOrderId( "O-19700101-000000-000-001-1") assert bracket.orders[2].parent_order_id == ClientOrderId( "O-19700101-000000-000-001-1") assert bracket.ts_init == 0 def test_bracket_limit_order_list(self): # Arrange, Act bracket = self.order_factory.bracket_limit( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), Price.from_str("1.00000"), Price.from_str("0.99990"), Price.from_str("1.00010"), TimeInForce.GTC, ) # Assert assert bracket.id == OrderListId("1") assert bracket.instrument_id == AUDUSD_SIM.id assert len(bracket.orders) == 3 assert bracket.orders[0].type == OrderType.LIMIT assert bracket.orders[1].type == OrderType.STOP_MARKET assert bracket.orders[2].type == OrderType.LIMIT assert bracket.orders[0].instrument_id == AUDUSD_SIM.id assert bracket.orders[1].instrument_id == AUDUSD_SIM.id assert bracket.orders[2].instrument_id == AUDUSD_SIM.id assert bracket.orders[0].client_order_id == ClientOrderId( "O-19700101-000000-000-001-1") assert bracket.orders[1].client_order_id == ClientOrderId( "O-19700101-000000-000-001-2") assert bracket.orders[2].client_order_id == ClientOrderId( "O-19700101-000000-000-001-3") assert bracket.orders[0].side == OrderSide.BUY assert bracket.orders[1].side == OrderSide.SELL assert bracket.orders[2].side == OrderSide.SELL assert bracket.orders[0].quantity == Quantity.from_int(100000) assert bracket.orders[1].quantity == Quantity.from_int(100000) assert bracket.orders[2].quantity == Quantity.from_int(100000) assert bracket.orders[1].price == Price.from_str("0.99990") assert bracket.orders[2].price == Price.from_str("1.00010") assert bracket.orders[1].time_in_force == TimeInForce.GTC assert bracket.orders[2].time_in_force == TimeInForce.GTC assert bracket.orders[1].expire_time is None assert bracket.orders[2].expire_time is None assert bracket.orders[0].contingency == ContingencyType.OTO assert bracket.orders[1].contingency == ContingencyType.OCO assert bracket.orders[2].contingency == ContingencyType.OCO assert bracket.orders[0].contingency_ids == [ ClientOrderId("O-19700101-000000-000-001-2"), ClientOrderId("O-19700101-000000-000-001-3"), ] assert bracket.orders[1].contingency_ids == [ ClientOrderId("O-19700101-000000-000-001-3") ] assert bracket.orders[2].contingency_ids == [ ClientOrderId("O-19700101-000000-000-001-2") ] assert bracket.orders[0].child_order_ids == [ ClientOrderId("O-19700101-000000-000-001-2"), ClientOrderId("O-19700101-000000-000-001-3"), ] assert bracket.orders[1].parent_order_id == ClientOrderId( "O-19700101-000000-000-001-1") assert bracket.orders[2].parent_order_id == ClientOrderId( "O-19700101-000000-000-001-1") assert bracket.ts_init == 0 def test_order_list_str_and_repr(self): # Arrange, Act bracket = self.order_factory.bracket_market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), Price.from_str("0.99990"), Price.from_str("1.00010"), ) # Assert assert str(bracket) == ( "OrderList(id=1, instrument_id=AUD/USD.SIM, orders=[MarketOrder(BUY 100_000 AUD/USD.SIM MARKET GTC, status=INITIALIZED, client_order_id=O-19700101-000000-000-001-1, venue_order_id=None, tags=ENTRY), StopMarketOrder(SELL 100_000 AUD/USD.SIM STOP_MARKET @ 0.99990 GTC, status=INITIALIZED, client_order_id=O-19700101-000000-000-001-2, venue_order_id=None, tags=STOP_LOSS), LimitOrder(SELL 100_000 AUD/USD.SIM LIMIT @ 1.00010 GTC, status=INITIALIZED, client_order_id=O-19700101-000000-000-001-3, venue_order_id=None, tags=TAKE_PROFIT)])" # noqa ) assert repr(bracket) == ( "OrderList(id=1, instrument_id=AUD/USD.SIM, orders=[MarketOrder(BUY 100_000 AUD/USD.SIM MARKET GTC, status=INITIALIZED, client_order_id=O-19700101-000000-000-001-1, venue_order_id=None, tags=ENTRY), StopMarketOrder(SELL 100_000 AUD/USD.SIM STOP_MARKET @ 0.99990 GTC, status=INITIALIZED, client_order_id=O-19700101-000000-000-001-2, venue_order_id=None, tags=STOP_LOSS), LimitOrder(SELL 100_000 AUD/USD.SIM LIMIT @ 1.00010 GTC, status=INITIALIZED, client_order_id=O-19700101-000000-000-001-3, venue_order_id=None, tags=TAKE_PROFIT)])" # noqa ) def test_apply_order_denied_event(self): # Arrange order = self.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) denied = OrderDenied( self.trader_id, self.strategy_id, AUDUSD_SIM.id, order.client_order_id, "SOME_REASON", UUID4(), 0, ) # Act order.apply(denied) # Assert assert order.status == OrderStatus.DENIED assert order.event_count == 2 assert order.last_event == denied assert not order.is_active assert order.is_completed def test_apply_order_submitted_event(self): # Arrange order = self.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) submitted = TestStubs.event_order_submitted(order) # Act order.apply(submitted) # Assert assert order.status == OrderStatus.SUBMITTED assert order.event_count == 2 assert order.last_event == submitted assert order.is_active assert order.is_inflight assert not order.is_working assert not order.is_completed assert not order.is_pending_update assert not order.is_pending_cancel def test_apply_order_accepted_event(self): # Arrange order = self.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) order.apply(TestStubs.event_order_submitted(order)) # Act order.apply(TestStubs.event_order_accepted(order)) # Assert assert order.status == OrderStatus.ACCEPTED assert order.is_active assert not order.is_inflight assert order.is_working assert not order.is_completed assert ( str(order) == "MarketOrder(BUY 100_000 AUD/USD.SIM MARKET GTC, status=ACCEPTED, client_order_id=O-19700101-000000-000-001-1, venue_order_id=1, tags=None)" # noqa ) assert ( repr(order) == "MarketOrder(BUY 100_000 AUD/USD.SIM MARKET GTC, status=ACCEPTED, client_order_id=O-19700101-000000-000-001-1, venue_order_id=1, tags=None)" # noqa ) def test_apply_order_rejected_event(self): # Arrange order = self.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) order.apply(TestStubs.event_order_submitted(order)) # Act order.apply(TestStubs.event_order_rejected(order)) # Assert assert order.status == OrderStatus.REJECTED assert not order.is_active assert not order.is_inflight assert not order.is_working assert order.is_completed def test_apply_order_expired_event(self): # Arrange order = self.order_factory.stop_market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), Price.from_str("0.99990"), TimeInForce.GTD, expire_time=UNIX_EPOCH, ) order.apply(TestStubs.event_order_submitted(order)) order.apply(TestStubs.event_order_accepted(order)) # Act order.apply(TestStubs.event_order_expired(order)) # Assert assert order.status == OrderStatus.EXPIRED assert not order.is_active assert not order.is_inflight assert not order.is_working assert order.is_completed def test_apply_order_triggered_event(self): # Arrange order = self.order_factory.stop_limit( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), Price.from_str("1.00000"), Price.from_str("0.99990"), TimeInForce.GTD, expire_time=UNIX_EPOCH, ) order.apply(TestStubs.event_order_submitted(order)) order.apply(TestStubs.event_order_accepted(order)) # Act order.apply(TestStubs.event_order_triggered(order)) # Assert assert order.status == OrderStatus.TRIGGERED assert order.is_active assert not order.is_inflight assert order.is_working assert not order.is_completed def test_order_status_pending_cancel(self): # Arrange order = self.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) order.apply(TestStubs.event_order_submitted(order)) order.apply(TestStubs.event_order_accepted(order)) # Act order.apply(TestStubs.event_order_pending_cancel(order)) # Assert assert order.status == OrderStatus.PENDING_CANCEL assert order.is_active assert order.is_inflight assert order.is_working assert not order.is_completed assert not order.is_pending_update assert order.is_pending_cancel assert order.event_count == 4 def test_apply_order_canceled_event(self): # Arrange order = self.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) order.apply(TestStubs.event_order_submitted(order)) order.apply(TestStubs.event_order_accepted(order)) order.apply(TestStubs.event_order_pending_cancel(order)) # Act order.apply(TestStubs.event_order_canceled(order)) # Assert assert order.status == OrderStatus.CANCELED assert not order.is_active assert not order.is_inflight assert not order.is_working assert order.is_completed assert not order.is_pending_update assert not order.is_pending_cancel assert order.event_count == 5 def test_order_status_pending_replace(self): # Arrange order = self.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) order.apply(TestStubs.event_order_submitted(order)) order.apply(TestStubs.event_order_accepted(order)) # Act order.apply(TestStubs.event_order_pending_update(order)) # Assert assert order.status == OrderStatus.PENDING_UPDATE assert order.is_active assert order.is_inflight assert order.is_working assert not order.is_completed assert order.is_pending_update assert not order.is_pending_cancel assert order.event_count == 4 def test_apply_order_updated_event_to_stop_order(self): # Arrange order = self.order_factory.stop_market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), Price.from_str("1.00000"), ) order.apply(TestStubs.event_order_submitted(order)) order.apply(TestStubs.event_order_accepted(order)) order.apply(TestStubs.event_order_pending_update(order)) updated = OrderUpdated( order.trader_id, order.strategy_id, order.account_id, order.instrument_id, order.client_order_id, VenueOrderId("1"), Quantity.from_int(120000), Price.from_str("1.00001"), None, UUID4(), 0, 0, ) # Act order.apply(updated) # Assert assert order.status == OrderStatus.ACCEPTED assert order.venue_order_id == VenueOrderId("1") assert order.quantity == Quantity.from_int(120000) assert order.price == Price.from_str("1.00001") assert order.is_active assert not order.is_inflight assert order.is_working assert not order.is_completed assert order.event_count == 5 def test_apply_order_updated_venue_id_change(self): # Arrange order = self.order_factory.stop_market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), Price.from_str("1.00000"), ) order.apply(TestStubs.event_order_submitted(order)) order.apply(TestStubs.event_order_accepted(order)) order.apply(TestStubs.event_order_pending_update(order)) updated = OrderUpdated( order.trader_id, order.strategy_id, order.account_id, order.instrument_id, order.client_order_id, VenueOrderId("2"), Quantity.from_int(120000), Price.from_str("1.00001"), None, UUID4(), 0, 0, ) # Act order.apply(updated) # Assert assert order.venue_order_id == VenueOrderId("2") assert order.venue_order_ids == [VenueOrderId("1")] def test_apply_order_filled_event_to_order_without_accepted(self): # Arrange order = self.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) order.apply(TestStubs.event_order_submitted(order)) order.apply(TestStubs.event_order_accepted(order)) filled = TestStubs.event_order_filled( order, instrument=AUDUSD_SIM, position_id=PositionId("P-123456"), strategy_id=StrategyId("S-001"), last_px=Price.from_str("1.00001"), ) # Act order.apply(filled) # Assert assert order.status == OrderStatus.FILLED assert order.filled_qty == Quantity.from_int(100000) assert order.leaves_qty == Quantity.zero() assert order.avg_px == Decimal("1.00001") assert len(order.execution_ids) == 1 assert not order.is_active assert not order.is_inflight assert not order.is_working assert order.is_completed assert order.ts_last == 0 def test_apply_order_filled_event_to_market_order(self): # Arrange order = self.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) order.apply(TestStubs.event_order_submitted(order)) order.apply(TestStubs.event_order_accepted(order)) filled = TestStubs.event_order_filled( order, instrument=AUDUSD_SIM, position_id=PositionId("P-123456"), strategy_id=StrategyId("S-001"), last_px=Price.from_str("1.00001"), ) # Act order.apply(filled) # Assert assert order.status == OrderStatus.FILLED assert order.filled_qty == Quantity.from_int(100000) assert order.avg_px == Decimal("1.00001") assert len(order.execution_ids) == 1 assert not order.is_active assert not order.is_inflight assert not order.is_working assert order.is_completed assert order.ts_last == 0 def test_apply_partial_fill_events_to_market_order_results_in_partially_filled( self, ): # Arrange order = self.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) order.apply(TestStubs.event_order_submitted(order)) order.apply(TestStubs.event_order_accepted(order)) fill1 = TestStubs.event_order_filled( order, instrument=AUDUSD_SIM, execution_id=ExecutionId("1"), position_id=PositionId("P-123456"), strategy_id=StrategyId("S-001"), last_px=Price.from_str("1.00001"), last_qty=Quantity.from_int(20000), ) fill2 = TestStubs.event_order_filled( order, instrument=AUDUSD_SIM, execution_id=ExecutionId("2"), position_id=PositionId("P-123456"), strategy_id=StrategyId("S-001"), last_px=Price.from_str("1.00002"), last_qty=Quantity.from_int(40000), ) # Act order.apply(fill1) order.apply(fill2) # Assert assert order.status == OrderStatus.PARTIALLY_FILLED assert order.filled_qty == Quantity.from_int(60000) assert order.leaves_qty == Quantity.from_int(40000) assert order.avg_px == Decimal("1.000014") assert len(order.execution_ids) == 2 assert order.is_active assert not order.is_inflight assert order.is_working assert not order.is_completed assert order.ts_last == 0 def test_apply_filled_events_to_market_order_results_in_filled(self): # Arrange order = self.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) order.apply(TestStubs.event_order_submitted(order)) order.apply(TestStubs.event_order_accepted(order)) fill1 = TestStubs.event_order_filled( order, instrument=AUDUSD_SIM, execution_id=ExecutionId("1"), position_id=PositionId("P-123456"), strategy_id=StrategyId("S-001"), last_px=Price.from_str("1.00001"), last_qty=Quantity.from_int(20000), ) fill2 = TestStubs.event_order_filled( order, instrument=AUDUSD_SIM, execution_id=ExecutionId("2"), position_id=PositionId("P-123456"), strategy_id=StrategyId("S-001"), last_px=Price.from_str("1.00002"), last_qty=Quantity.from_int(40000), ) fill3 = TestStubs.event_order_filled( order, instrument=AUDUSD_SIM, execution_id=ExecutionId("3"), position_id=PositionId("P-123456"), strategy_id=StrategyId("S-001"), last_px=Price.from_str("1.00003"), last_qty=Quantity.from_int(40000), ) # Act order.apply(fill1) order.apply(fill2) order.apply(fill3) # Assert assert order.status == OrderStatus.FILLED assert order.filled_qty == Quantity.from_int(100000) assert order.avg_px == Decimal("1.000018571428571428571428571") assert len(order.execution_ids) == 3 assert not order.is_active assert not order.is_inflight assert not order.is_working assert order.is_completed assert order.ts_last == 0 def test_apply_order_filled_event_to_buy_limit_order(self): # Arrange order = self.order_factory.limit( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), Price.from_str("1.00000"), ) order.apply(TestStubs.event_order_submitted(order)) order.apply(TestStubs.event_order_accepted(order)) filled = OrderFilled( order.trader_id, order.strategy_id, order.account_id, order.instrument_id, order.client_order_id, VenueOrderId("1"), ExecutionId("E-1"), PositionId("P-1"), order.side, order.type, order.quantity, Price.from_str("1.00001"), AUDUSD_SIM.quote_currency, Money(0, USD), LiquiditySide.MAKER, UUID4(), 0, 0, ) # Act order.apply(filled) # Assert assert order.status == OrderStatus.FILLED assert order.filled_qty == Quantity.from_int(100000) assert order.price == Price.from_str("1.00000") assert order.avg_px == Decimal("1.00001") assert order.slippage == Decimal("0.00001") assert not order.is_active assert not order.is_inflight assert not order.is_working assert order.is_completed assert order.ts_last == 0 def test_apply_order_partially_filled_event_to_buy_limit_order(self): # Arrange order = self.order_factory.limit( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), Price.from_str("1.00000"), ) order.apply(TestStubs.event_order_submitted(order)) order.apply(TestStubs.event_order_accepted(order)) partially = OrderFilled( order.trader_id, order.strategy_id, order.account_id, order.instrument_id, order.client_order_id, VenueOrderId("1"), ExecutionId("E-1"), PositionId("P-1"), order.side, order.type, Quantity.from_int(50000), Price.from_str("0.999999"), AUDUSD_SIM.quote_currency, Money(0, USD), LiquiditySide.MAKER, UUID4(), 1_000_000_000, 1_000_000_000, ) # Act order.apply(partially) # Assert assert order.status == OrderStatus.PARTIALLY_FILLED assert order.filled_qty == Quantity.from_int(50000) assert order.price == Price.from_str("1.00000") assert order.avg_px == Decimal("0.999999") assert order.slippage == Decimal("-0.000001") assert order.is_active assert not order.is_inflight assert order.is_working assert not order.is_completed assert order.ts_last == 1_000_000_000, order.ts_last
class OrderTests(unittest.TestCase): def setUp(self): # Fixture Setup self.account_id = TestStubs.account_id() self.order_factory = OrderFactory( trader_id=TraderId("TESTER-000"), strategy_id=StrategyId("S-001"), clock=TestClock(), ) def test_opposite_side_given_invalid_value_raises_value_error(self): # Arrange # Act # Assert self.assertRaises(ValueError, Order.opposite_side, 0) def test_flatten_side_given_invalid_value_or_flat_raises_value_error(self): # Arrange # Act self.assertRaises(ValueError, Order.flatten_side, 0) self.assertRaises(ValueError, Order.flatten_side, PositionSide.FLAT) @parameterized.expand([ [OrderSide.BUY, OrderSide.SELL], [OrderSide.SELL, OrderSide.BUY], ]) def test_opposite_side_returns_expected_sides(self, side, expected): # Arrange # Act result = Order.opposite_side(side) # Assert self.assertEqual(expected, result) @parameterized.expand([ [PositionSide.LONG, OrderSide.SELL], [PositionSide.SHORT, OrderSide.BUY], ]) def test_flatten_side_returns_expected_sides(self, side, expected): # Arrange # Act result = Order.flatten_side(side) # Assert self.assertEqual(expected, result) def test_market_order_with_quantity_zero_raises_value_error(self): # Arrange # Act self.assertRaises( ValueError, MarketOrder, ClientOrderId("O-123456"), StrategyId("S-001"), AUDUSD_SIM.id, OrderSide.BUY, Quantity.zero(), TimeInForce.DAY, uuid4(), 0, ) def test_market_order_with_invalid_tif_raises_value_error(self): # Arrange # Act self.assertRaises( ValueError, MarketOrder, ClientOrderId("O-123456"), StrategyId("S-001"), AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100), TimeInForce.GTD, uuid4(), 0, ) def test_stop_market_order_with_gtd_and_expire_time_none_raises_type_error( self): # Arrange # Act self.assertRaises( TypeError, StopMarketOrder, ClientOrderId("O-123456"), StrategyId("S-001"), AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), price=Price.from_str("1.00000"), init_id=uuid4(), timestamp_ns=0, time_in_force=TimeInForce.GTD, expire_time=None, ) def test_stop_limit_buy_order_with_gtd_and_expire_time_none_raises_type_error( self): # Arrange # Act self.assertRaises( TypeError, StopLimitOrder, ClientOrderId("O-123456"), StrategyId("S-001"), AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), price=Price.from_str("1.00001"), trigger=Price.from_str("1.00000"), init_id=uuid4(), timestamp_ns=0, time_in_force=TimeInForce.GTD, expire_time=None, ) def test_reset_order_factory(self): # Arrange self.order_factory.limit( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), Price.from_str("1.00000"), ) # Act self.order_factory.reset() order2 = self.order_factory.limit( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), Price.from_str("1.00000"), ) self.assertEqual(ClientOrderId("O-19700101-000000-000-001-1"), order2.client_order_id) def test_limit_order_can_create_expected_decimal_price(self): # Arrange # Act order1 = self.order_factory.limit( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), Price.from_str("1.00000"), ) order2 = self.order_factory.limit( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), Price.from_str("1.00001"), ) # Assert self.assertEqual(Price.from_str("1.00000"), order1.price) self.assertEqual(Price.from_str("1.00001"), order2.price) def test_initialize_buy_market_order(self): # Arrange # Act order = self.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) # Assert self.assertEqual(AUDUSD_SIM.id.symbol, order.symbol) self.assertEqual(AUDUSD_SIM.id.venue, order.venue) self.assertEqual(OrderType.MARKET, order.type) self.assertEqual(OrderState.INITIALIZED, order.state) self.assertEqual(1, order.event_count) self.assertTrue(isinstance(order.last_event, OrderInitialized)) self.assertFalse(order.is_working) self.assertFalse(order.is_completed) self.assertTrue(order.is_buy) self.assertFalse(order.is_sell) self.assertFalse(order.is_passive) self.assertTrue(order.is_aggressive) self.assertEqual(0, order.execution_ns) self.assertEqual(0, order.last_event.timestamp_ns) self.assertEqual(OrderInitialized, type(order.init_event)) self.assertTrue(order == order) self.assertFalse(order != order) def test_initialize_sell_market_order(self): # Arrange # Act order = self.order_factory.market( AUDUSD_SIM.id, OrderSide.SELL, Quantity.from_int(100000), ) # Assert self.assertEqual(OrderType.MARKET, order.type) self.assertEqual(OrderState.INITIALIZED, order.state) self.assertEqual(1, order.event_count) self.assertTrue(isinstance(order.last_event, OrderInitialized)) self.assertEqual(1, len(order.events)) self.assertFalse(order.is_working) self.assertFalse(order.is_completed) self.assertFalse(order.is_buy) self.assertTrue(order.is_sell) self.assertEqual(0, order.execution_ns) self.assertEqual(OrderInitialized, type(order.init_event)) def test_order_equality(self): # Arrange # Act order = self.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) # Assert self.assertTrue(order == order) self.assertFalse(order != order) def test_order_str_and_repr(self): # Arrange # Act order = self.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) # Assert self.assertEqual( "MarketOrder(BUY 100_000 AUD/USD.SIM MARKET GTC, state=INITIALIZED, " "client_order_id=O-19700101-000000-000-001-1)", str(order), ) self.assertEqual( "MarketOrder(BUY 100_000 AUD/USD.SIM MARKET GTC, state=INITIALIZED, " "client_order_id=O-19700101-000000-000-001-1)", repr(order), ) def test_initialize_limit_order(self): # Arrange # Act order = self.order_factory.limit( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), Price.from_str("1.00000"), ) # Assert self.assertEqual(OrderType.LIMIT, order.type) self.assertEqual(OrderState.INITIALIZED, order.state) self.assertEqual(TimeInForce.GTC, order.time_in_force) self.assertTrue(order.is_passive) self.assertFalse(order.is_aggressive) self.assertFalse(order.is_completed) self.assertEqual(OrderInitialized, type(order.init_event)) self.assertEqual( "LimitOrder(BUY 100_000 AUD/USD.SIM LIMIT @ 1.00000 GTC, " "state=INITIALIZED, client_order_id=O-19700101-000000-000-001-1)", str(order), ) self.assertEqual( "LimitOrder(BUY 100_000 AUD/USD.SIM LIMIT @ 1.00000 GTC, " "state=INITIALIZED, client_order_id=O-19700101-000000-000-001-1)", repr(order), ) def test_initialize_limit_order_with_expire_time(self): # Arrange # Act order = self.order_factory.limit( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), Price.from_str("1.00000"), TimeInForce.GTD, expire_time=UNIX_EPOCH, ) # Assert self.assertEqual(AUDUSD_SIM.id, order.instrument_id) self.assertEqual(OrderType.LIMIT, order.type) self.assertEqual(Price.from_str("1.00000"), order.price) self.assertEqual(OrderState.INITIALIZED, order.state) self.assertEqual(TimeInForce.GTD, order.time_in_force) self.assertEqual(UNIX_EPOCH, order.expire_time) self.assertFalse(order.is_completed) self.assertEqual(OrderInitialized, type(order.init_event)) def test_initialize_stop_market_order(self): # Arrange # Act order = self.order_factory.stop_market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), Price.from_str("1.00000"), ) # Assert self.assertEqual(OrderType.STOP_MARKET, order.type) self.assertEqual(OrderState.INITIALIZED, order.state) self.assertEqual(TimeInForce.GTC, order.time_in_force) self.assertTrue(order.is_passive) self.assertFalse(order.is_aggressive) self.assertFalse(order.is_completed) self.assertEqual(OrderInitialized, type(order.init_event)) self.assertEqual( "StopMarketOrder(BUY 100_000 AUD/USD.SIM STOP_MARKET @ 1.00000 GTC, " "state=INITIALIZED, client_order_id=O-19700101-000000-000-001-1)", str(order), ) self.assertEqual( "StopMarketOrder(BUY 100_000 AUD/USD.SIM STOP_MARKET @ 1.00000 GTC, " "state=INITIALIZED, client_order_id=O-19700101-000000-000-001-1)", repr(order), ) def test_initialize_stop_limit_order(self): # Arrange # Act order = self.order_factory.stop_limit( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), Price.from_str("1.00000"), Price.from_str("1.10010"), ) # Assert self.assertEqual(OrderType.STOP_LIMIT, order.type) self.assertEqual(OrderState.INITIALIZED, order.state) self.assertEqual(TimeInForce.GTC, order.time_in_force) self.assertTrue(order.is_passive) self.assertFalse(order.is_aggressive) self.assertFalse(order.is_completed) self.assertEqual(OrderInitialized, type(order.init_event)) self.assertEqual( "StopLimitOrder(BUY 100_000 AUD/USD.SIM STOP_LIMIT @ 1.00000 GTC, " "trigger=1.10010, state=INITIALIZED, client_order_id=O-19700101-000000-000-001-1)", str(order), ) self.assertEqual( "StopLimitOrder(BUY 100_000 AUD/USD.SIM STOP_LIMIT @ 1.00000 GTC, " "trigger=1.10010, state=INITIALIZED, client_order_id=O-19700101-000000-000-001-1)", repr(order), ) def test_bracket_order_equality(self): # Arrange entry1 = self.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) entry2 = self.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) bracket_order1 = self.order_factory.bracket(entry1, Price.from_str("1.00000"), Price.from_str("1.00010")) bracket_order2 = self.order_factory.bracket(entry2, Price.from_str("1.00000"), Price.from_str("1.00010")) # Act # Assert self.assertTrue(bracket_order1 == bracket_order1) self.assertTrue(bracket_order1 != bracket_order2) def test_initialize_bracket_order(self): # Arrange entry_order = self.order_factory.stop_market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), Price.from_str("0.99995"), ) # Act bracket_order = self.order_factory.bracket( entry_order, Price.from_str("0.99990"), Price.from_str("1.00010"), TimeInForce.GTC, TimeInForce.GTC, ) # Assert self.assertEqual(AUDUSD_SIM.id, bracket_order.stop_loss.instrument_id) self.assertTrue(bracket_order.take_profit is not None) self.assertEqual(AUDUSD_SIM.id, bracket_order.take_profit.instrument_id) self.assertEqual( ClientOrderId("O-19700101-000000-000-001-1"), bracket_order.entry.client_order_id, ) self.assertEqual( ClientOrderId("O-19700101-000000-000-001-2"), bracket_order.stop_loss.client_order_id, ) self.assertEqual( ClientOrderId("O-19700101-000000-000-001-3"), bracket_order.take_profit.client_order_id, ) self.assertEqual(OrderSide.SELL, bracket_order.stop_loss.side) self.assertEqual(OrderSide.SELL, bracket_order.take_profit.side) self.assertEqual(Quantity.from_int(100000), bracket_order.stop_loss.quantity) self.assertEqual(Quantity.from_int(100000), bracket_order.take_profit.quantity) self.assertEqual(Price.from_str("0.99990"), bracket_order.stop_loss.price) self.assertEqual(Price.from_str("1.00010"), bracket_order.take_profit.price) self.assertEqual(TimeInForce.GTC, bracket_order.stop_loss.time_in_force) self.assertEqual(TimeInForce.GTC, bracket_order.take_profit.time_in_force) self.assertEqual(None, bracket_order.entry.expire_time) self.assertEqual(None, bracket_order.stop_loss.expire_time) self.assertEqual(None, bracket_order.take_profit.expire_time) self.assertEqual(ClientOrderLinkId("BO-19700101-000000-000-001-1"), bracket_order.id) self.assertEqual(0, bracket_order.timestamp_ns) def test_bracket_order_str_and_repr(self): # Arrange # Act entry_order = self.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) bracket_order = self.order_factory.bracket( entry_order, Price.from_str("0.99990"), Price.from_str("1.00010"), ) # Assert self.assertEqual( "BracketOrder(id=BO-19700101-000000-000-001-1, " "EntryMarketOrder(BUY 100_000 AUD/USD.SIM MARKET GTC, " "state=INITIALIZED, client_order_id=O-19700101-000000-000-001-1), " "SL=0.99990, TP=1.00010)", str(bracket_order), ) # noqa self.assertEqual( "BracketOrder(id=BO-19700101-000000-000-001-1, " "EntryMarketOrder(BUY 100_000 AUD/USD.SIM MARKET GTC, " "state=INITIALIZED, client_order_id=O-19700101-000000-000-001-1), " "SL=0.99990, TP=1.00010)", repr(bracket_order), ) # noqa def test_apply_order_invalid_event(self): # Arrange order = self.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) invalid = OrderInvalid( order.client_order_id, "SOME_REASON", uuid4(), 0, ) # Act order.apply(invalid) # Assert self.assertEqual(OrderState.INVALID, order.state) self.assertEqual(2, order.event_count) self.assertEqual(invalid, order.last_event) self.assertTrue(order.is_completed) def test_apply_order_denied_event(self): # Arrange order = self.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) denied = OrderDenied( order.client_order_id, "SOME_REASON", uuid4(), 0, ) # Act order.apply(denied) # Assert self.assertEqual(OrderState.DENIED, order.state) self.assertEqual(2, order.event_count) self.assertEqual(denied, order.last_event) self.assertTrue(order.is_completed) def test_apply_order_submitted_event(self): # Arrange order = self.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) submitted = TestStubs.event_order_submitted(order) # Act order.apply(submitted) # Assert self.assertEqual(OrderState.SUBMITTED, order.state) self.assertEqual(2, order.event_count) self.assertEqual(submitted, order.last_event) self.assertFalse(order.is_working) self.assertFalse(order.is_completed) def test_apply_order_accepted_event(self): # Arrange order = self.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) order.apply(TestStubs.event_order_submitted(order)) # Act order.apply(TestStubs.event_order_accepted(order)) # Assert self.assertEqual(OrderState.ACCEPTED, order.state) self.assertTrue(order.is_working) self.assertFalse(order.is_completed) self.assertEqual( "MarketOrder(BUY 100_000 AUD/USD.SIM MARKET GTC, state=ACCEPTED, " "client_order_id=O-19700101-000000-000-001-1, venue_order_id=1)", str(order), ) self.assertEqual( "MarketOrder(BUY 100_000 AUD/USD.SIM MARKET GTC, state=ACCEPTED, " "client_order_id=O-19700101-000000-000-001-1, venue_order_id=1)", repr(order), ) def test_apply_order_rejected_event(self): # Arrange order = self.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) order.apply(TestStubs.event_order_submitted(order)) # Act order.apply(TestStubs.event_order_rejected(order)) # Assert self.assertEqual(OrderState.REJECTED, order.state) self.assertFalse(order.is_working) self.assertTrue(order.is_completed) def test_apply_order_expired_event(self): # Arrange order = self.order_factory.stop_market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), Price.from_str("0.99990"), TimeInForce.GTD, expire_time=UNIX_EPOCH, ) order.apply(TestStubs.event_order_submitted(order)) order.apply(TestStubs.event_order_accepted(order)) # Act order.apply(TestStubs.event_order_expired(order)) # Assert self.assertEqual(OrderState.EXPIRED, order.state) self.assertFalse(order.is_working) self.assertTrue(order.is_completed) def test_apply_order_triggered_event(self): # Arrange order = self.order_factory.stop_limit( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), Price.from_str("1.00000"), Price.from_str("0.99990"), TimeInForce.GTD, expire_time=UNIX_EPOCH, ) order.apply(TestStubs.event_order_submitted(order)) order.apply(TestStubs.event_order_accepted(order)) # Act order.apply(TestStubs.event_order_triggered(order)) # Assert self.assertEqual(OrderState.TRIGGERED, order.state) self.assertTrue(order.is_working) self.assertFalse(order.is_completed) def test_apply_order_canceled_event(self): # Arrange order = self.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) order.apply(TestStubs.event_order_submitted(order)) order.apply(TestStubs.event_order_accepted(order)) order.apply(TestStubs.event_order_pending_cancel(order)) # Act order.apply(TestStubs.event_order_canceled(order)) # Assert self.assertEqual(OrderState.CANCELED, order.state) self.assertFalse(order.is_working) self.assertTrue(order.is_completed) self.assertEqual(5, order.event_count) def test_apply_order_updated_event_to_stop_order(self): # Arrange order = self.order_factory.stop_market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), Price.from_str("1.00000"), ) order.apply(TestStubs.event_order_submitted(order)) order.apply(TestStubs.event_order_accepted(order)) order.apply(TestStubs.event_order_pending_replace(order)) updated = OrderUpdated( self.account_id, order.client_order_id, VenueOrderId("1"), Quantity.from_int(120000), Price.from_str("1.00001"), 0, uuid4(), 0, ) # Act order.apply(updated) # Assert self.assertEqual(OrderState.ACCEPTED, order.state) self.assertEqual(VenueOrderId("1"), order.venue_order_id) self.assertEqual(Quantity.from_int(120000), order.quantity) self.assertEqual(Price.from_str("1.00001"), order.price) self.assertTrue(order.is_working) self.assertFalse(order.is_completed) self.assertEqual(5, order.event_count) def test_apply_order_updated_venue_id_change(self): # Arrange order = self.order_factory.stop_market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), Price.from_str("1.00000"), ) order.apply(TestStubs.event_order_submitted(order)) order.apply(TestStubs.event_order_accepted(order)) order.apply(TestStubs.event_order_pending_replace(order)) updated = OrderUpdated( self.account_id, order.client_order_id, VenueOrderId("2"), Quantity.from_int(120000), Price.from_str("1.00001"), 0, uuid4(), 0, ) # Act order.apply(updated) # Assert self.assertEqual(VenueOrderId("2"), order.venue_order_id) self.assertEqual([VenueOrderId("1")], order.venue_order_ids) def test_apply_order_filled_event_to_order_without_accepted(self): # Arrange order = self.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) order.apply(TestStubs.event_order_submitted(order)) order.apply(TestStubs.event_order_accepted(order)) filled = TestStubs.event_order_filled( order, instrument=AUDUSD_SIM, position_id=PositionId("P-123456"), strategy_id=StrategyId("S-001"), last_px=Price.from_str("1.00001"), ) # Act order.apply(filled) # Assert self.assertEqual(OrderState.FILLED, order.state) self.assertEqual(Quantity.from_int(100000), order.filled_qty) self.assertEqual(Decimal("1.00001"), order.avg_px) self.assertEqual(1, len(order.execution_ids)) self.assertFalse(order.is_working) self.assertTrue(order.is_completed) self.assertEqual(0, order.execution_ns) def test_apply_order_filled_event_to_market_order(self): # Arrange order = self.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) order.apply(TestStubs.event_order_submitted(order)) order.apply(TestStubs.event_order_accepted(order)) filled = TestStubs.event_order_filled( order, instrument=AUDUSD_SIM, position_id=PositionId("P-123456"), strategy_id=StrategyId("S-001"), last_px=Price.from_str("1.00001"), ) # Act order.apply(filled) # Assert self.assertEqual(OrderState.FILLED, order.state) self.assertEqual(Quantity.from_int(100000), order.filled_qty) self.assertEqual(Decimal("1.00001"), order.avg_px) self.assertEqual(1, len(order.execution_ids)) self.assertFalse(order.is_working) self.assertTrue(order.is_completed) self.assertEqual(0, order.execution_ns) def test_apply_partial_fill_events_to_market_order_results_in_partially_filled( self, ): # Arrange order = self.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) order.apply(TestStubs.event_order_submitted(order)) order.apply(TestStubs.event_order_accepted(order)) fill1 = TestStubs.event_order_filled( order, instrument=AUDUSD_SIM, execution_id=ExecutionId("1"), position_id=PositionId("P-123456"), strategy_id=StrategyId("S-001"), last_px=Price.from_str("1.00001"), last_qty=Quantity.from_int(20000), ) fill2 = TestStubs.event_order_filled( order, instrument=AUDUSD_SIM, execution_id=ExecutionId("2"), position_id=PositionId("P-123456"), strategy_id=StrategyId("S-001"), last_px=Price.from_str("1.00002"), last_qty=Quantity.from_int(40000), ) # Act order.apply(fill1) order.apply(fill2) # Assert self.assertEqual(OrderState.PARTIALLY_FILLED, order.state) self.assertEqual(Quantity.from_int(60000), order.filled_qty) self.assertEqual(Decimal("1.000014"), order.avg_px) self.assertEqual(2, len(order.execution_ids)) self.assertTrue(order.is_working) self.assertFalse(order.is_completed) self.assertEqual(0, order.execution_ns) def test_apply_filled_events_to_market_order_results_in_filled(self): # Arrange order = self.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) order.apply(TestStubs.event_order_submitted(order)) order.apply(TestStubs.event_order_accepted(order)) fill1 = TestStubs.event_order_filled( order, instrument=AUDUSD_SIM, execution_id=ExecutionId("1"), position_id=PositionId("P-123456"), strategy_id=StrategyId("S-001"), last_px=Price.from_str("1.00001"), last_qty=Quantity.from_int(20000), ) fill2 = TestStubs.event_order_filled( order, instrument=AUDUSD_SIM, execution_id=ExecutionId("2"), position_id=PositionId("P-123456"), strategy_id=StrategyId("S-001"), last_px=Price.from_str("1.00002"), last_qty=Quantity.from_int(40000), ) fill3 = TestStubs.event_order_filled( order, instrument=AUDUSD_SIM, execution_id=ExecutionId("3"), position_id=PositionId("P-123456"), strategy_id=StrategyId("S-001"), last_px=Price.from_str("1.00003"), last_qty=Quantity.from_int(40000), ) # Act order.apply(fill1) order.apply(fill2) order.apply(fill3) # Assert self.assertEqual(OrderState.FILLED, order.state) self.assertEqual(Quantity.from_int(100000), order.filled_qty) self.assertEqual(Decimal("1.000018571428571428571428571"), order.avg_px) self.assertEqual(3, len(order.execution_ids)) self.assertFalse(order.is_working) self.assertTrue(order.is_completed) self.assertEqual(0, order.execution_ns) def test_apply_order_filled_event_to_buy_limit_order(self): # Arrange order = self.order_factory.limit( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), Price.from_str("1.00000"), ) order.apply(TestStubs.event_order_submitted(order)) order.apply(TestStubs.event_order_accepted(order)) filled = OrderFilled( self.account_id, order.client_order_id, VenueOrderId("1"), ExecutionId("E-1"), PositionId("P-1"), StrategyId.null(), order.instrument_id, order.side, order.quantity, Price.from_str("1.00001"), AUDUSD_SIM.quote_currency, Money(0, USD), LiquiditySide.MAKER, 0, uuid4(), 0, ) # Act order.apply(filled) # Assert self.assertEqual(OrderState.FILLED, order.state) self.assertEqual(Quantity.from_int(100000), order.filled_qty) self.assertEqual(Price.from_str("1.00000"), order.price) self.assertEqual(Decimal("1.00001"), order.avg_px) self.assertEqual(Decimal("0.00001"), order.slippage) self.assertFalse(order.is_working) self.assertTrue(order.is_completed) self.assertEqual(0, order.execution_ns) def test_apply_order_partially_filled_event_to_buy_limit_order(self): # Arrange order = self.order_factory.limit( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), Price.from_str("1.00000"), ) order.apply(TestStubs.event_order_submitted(order)) order.apply(TestStubs.event_order_accepted(order)) partially = OrderFilled( self.account_id, order.client_order_id, VenueOrderId("1"), ExecutionId("E-1"), PositionId("P-1"), StrategyId.null(), order.instrument_id, order.side, Quantity.from_int(50000), Price.from_str("0.999999"), AUDUSD_SIM.quote_currency, Money(0, USD), LiquiditySide.MAKER, 1_000_000_000, uuid4(), 1_000_000_000, ) # Act order.apply(partially) # Assert self.assertEqual(OrderState.PARTIALLY_FILLED, order.state) self.assertEqual(Quantity.from_int(50000), order.filled_qty) self.assertEqual(Price.from_str("1.00000"), order.price) self.assertEqual(Decimal("0.999999"), order.avg_px) self.assertEqual(Decimal("-0.000001"), order.slippage) self.assertTrue(order.is_working) self.assertFalse(order.is_completed) self.assertEqual(1_000_000_000, order.execution_ns)
class MsgPackOrderSerializerTests(unittest.TestCase): def setUp(self): # Fixture Setup self.serializer = MsgPackOrderSerializer() self.order_factory = OrderFactory( trader_id=TraderId("TESTER", "000"), strategy_id=StrategyId("S", "001"), clock=TestClock(), ) def test_serialize_and_deserialize_market_orders(self): # Arrange order = self.order_factory.market( AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(100000), ) # Act serialized = self.serializer.serialize(order) deserialized = self.serializer.deserialize(serialized) # Assert self.assertEqual(order, deserialized) print(b64encode(serialized)) print(order) def test_serialize_and_deserialize_limit_orders(self): # Arrange order = self.order_factory.limit( AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(100000), Price("1.00000"), TimeInForce.DAY, expire_time=None, ) # Act serialized = self.serializer.serialize(order) deserialized = self.serializer.deserialize(serialized) # Assert self.assertEqual(order, deserialized) print(b64encode(serialized)) print(order) def test_serialize_and_deserialize_limit_orders_with_expire_time(self): # Arrange order = LimitOrder( ClientOrderId("O-123456"), StrategyId("S", "001"), AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(100000), price=Price("1.00000"), time_in_force=TimeInForce.GTD, expire_time=UNIX_EPOCH, init_id=uuid4(), timestamp=UNIX_EPOCH, ) # Act serialized = self.serializer.serialize(order) deserialized = self.serializer.deserialize(serialized) # Assert self.assertEqual(order, deserialized) print(b64encode(serialized)) print(order) def test_serialize_and_deserialize_stop_orders_with_expire_time(self): # Arrange order = StopMarketOrder( ClientOrderId("O-123456"), StrategyId("S", "001"), AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(100000), price=Price("1.00000"), time_in_force=TimeInForce.GTD, expire_time=UNIX_EPOCH, init_id=uuid4(), timestamp=UNIX_EPOCH, ) # Act serialized = self.serializer.serialize(order) deserialized = self.serializer.deserialize(serialized) # Assert self.assertEqual(order, deserialized) print(b64encode(serialized)) print(order)
class PositionTests(unittest.TestCase): def setUp(self): # Fixture Setup self.account_id = TestStubs.account_id() self.order_factory = OrderFactory( strategy_id=StrategyId("S", "001"), id_tag_trader=IdTag("001"), id_tag_strategy=IdTag("001"), clock=TestClock()) print("\n") def test_position_filled_with_buy_order_returns_expected_attributes(self): # Arrange order = self.order_factory.market( AUDUSD_FXCM, OrderSide.BUY, Quantity(100000), ) fill = TestStubs.event_order_filled( order, PositionId("P-123456"), StrategyId("S", "001"), Price("1.00001"), ) last = QuoteTick( AUDUSD_FXCM, Price("1.00050"), Price("1.00048"), Quantity(1), Quantity(1), UNIX_EPOCH, ) # Act position = Position(fill) # Assert self.assertEqual(ClientOrderId("O-19700101-000000-001-001-1"), position.from_order) self.assertEqual(Quantity(100000), position.quantity) self.assertEqual(Quantity(100000), position.peak_quantity) self.assertEqual(OrderSide.BUY, position.entry) self.assertEqual(PositionSide.LONG, position.side) self.assertEqual(UNIX_EPOCH, position.opened_time) self.assertIsNone(position.open_duration) self.assertEqual(1.00001, position.avg_open_price) self.assertEqual(1, position.event_count()) self.assertEqual({order.cl_ord_id}, position.cl_ord_ids()) self.assertEqual({ExecutionId("E-19700101-000000-001-001-1")}, position.execution_ids()) self.assertEqual(ExecutionId("E-19700101-000000-001-001-1"), position.last_execution_id()) self.assertEqual(PositionId("P-123456"), position.id) self.assertTrue(position.is_long()) self.assertFalse(position.is_short()) self.assertFalse(position.is_closed()) self.assertEqual(0.0, position.realized_points) self.assertEqual(0.0, position.realized_return) self.assertEqual(Money(0, USD), position.realized_pnl) self.assertEqual(Money(49.00, USD), position.unrealized_pnl(last)) self.assertEqual(Money(49.00, USD), position.total_pnl(last)) def test_position_filled_with_sell_order_returns_expected_attributes(self): # Arrange order = self.order_factory.market( AUDUSD_FXCM, OrderSide.SELL, Quantity(100000)) fill = TestStubs.event_order_filled( order, PositionId("P-123456"), StrategyId("S", "001"), Price("1.00001"), ) last = QuoteTick( AUDUSD_FXCM, Price("1.00050"), Price("1.00048"), Quantity(1), Quantity(1), UNIX_EPOCH, ) # Act position = Position(fill) # Assert self.assertEqual(Quantity(100000), position.quantity) self.assertEqual(Quantity(100000), position.peak_quantity) self.assertEqual(PositionSide.SHORT, position.side) self.assertEqual(UNIX_EPOCH, position.opened_time) self.assertEqual(1.00001, position.avg_open_price) self.assertEqual(1, position.event_count()) self.assertEqual({ExecutionId("E-19700101-000000-001-001-1")}, position.execution_ids()) self.assertEqual(ExecutionId("E-19700101-000000-001-001-1"), position.last_execution_id()) self.assertEqual(PositionId("P-123456"), position.id) self.assertFalse(position.is_long()) self.assertTrue(position.is_short()) self.assertFalse(position.is_closed()) self.assertEqual(0.0, position.realized_points) self.assertEqual(0.0, position.realized_return) self.assertEqual(Money(0, USD), position.realized_pnl) self.assertEqual(Money(-47.00, USD), position.unrealized_pnl(last)) self.assertEqual(Money(-47.00, USD), position.total_pnl(last)) def test_position_partial_fills_with_buy_order_returns_expected_attributes(self): # Arrange order = self.order_factory.market( AUDUSD_FXCM, OrderSide.BUY, Quantity(100000)) fill = TestStubs.event_order_filled( order, position_id=PositionId("P-123456"), strategy_id=StrategyId("S", "001"), fill_price=Price("1.00001"), filled_qty=Quantity(50000), leaves_qty=Quantity(50000), ) last = QuoteTick( AUDUSD_FXCM, Price("1.00050"), Price("1.00048"), Quantity(1), Quantity(1), UNIX_EPOCH, ) position = Position(fill) # Act # Assert self.assertEqual(Quantity(50000), position.quantity) self.assertEqual(Quantity(50000), position.peak_quantity) self.assertEqual(PositionSide.LONG, position.side) self.assertEqual(UNIX_EPOCH, position.opened_time) self.assertEqual(1.00001, position.avg_open_price) self.assertEqual(1, position.event_count()) self.assertTrue(position.is_long()) self.assertFalse(position.is_short()) self.assertFalse(position.is_closed()) self.assertEqual(0.0, position.realized_points) self.assertEqual(0.0, position.realized_return) self.assertEqual(Money(0, USD), position.realized_pnl) self.assertEqual(Money(24.50, USD), position.unrealized_pnl(last)) self.assertEqual(Money(24.50, USD), position.total_pnl(last)) def test_position_partial_fills_with_sell_order_returns_expected_attributes(self): # Arrange order = self.order_factory.market( AUDUSD_FXCM, OrderSide.SELL, Quantity(100000)) fill1 = TestStubs.event_order_filled( order, position_id=PositionId("P-123456"), strategy_id=StrategyId("S", "001"), fill_price=Price("1.00001"), filled_qty=Quantity(50000), leaves_qty=Quantity(50000), ) fill2 = TestStubs.event_order_filled( order, position_id=PositionId("P-123456"), strategy_id=StrategyId("S", "001"), fill_price=Price("1.00002"), filled_qty=Quantity(50000), leaves_qty=Quantity(), ) position = Position(fill1) last = QuoteTick( AUDUSD_FXCM, Price("1.00050"), Price("1.00048"), Quantity(1), Quantity(1), UNIX_EPOCH) # Act position.apply(fill2) # Assert self.assertEqual(Quantity(100000), position.quantity) self.assertEqual(PositionSide.SHORT, position.side) self.assertEqual(UNIX_EPOCH, position.opened_time) self.assertEqual(1.000015, position.avg_open_price) self.assertEqual(2, position.event_count()) self.assertFalse(position.is_long()) self.assertTrue(position.is_short()) self.assertFalse(position.is_closed()) self.assertEqual(0.0, position.realized_points) self.assertEqual(0.0, position.realized_return) self.assertEqual(Money(0, USD), position.realized_pnl) self.assertEqual(Money(-46.50, USD), position.unrealized_pnl(last)) self.assertEqual(Money(-46.50, USD), position.total_pnl(last)) def test_position_filled_with_buy_order_then_sell_order_returns_expected_attributes(self): # Arrange order = self.order_factory.market( AUDUSD_FXCM, OrderSide.BUY, Quantity(100000)) fill1 = TestStubs.event_order_filled( order, position_id=PositionId("P-123456"), strategy_id=StrategyId("S", "001"), fill_price=Price("1.00001"), ) position = Position(fill1) fill2 = OrderFilled( self.account_id, order.cl_ord_id, OrderId("2"), ExecutionId("E2"), PositionId("T123456"), StrategyId("S", "001"), order.symbol, OrderSide.SELL, order.quantity, Quantity(), Price("1.00001"), Money(0, USD), LiquiditySide.TAKER, AUD, USD, UNIX_EPOCH + timedelta(minutes=1), uuid4(), UNIX_EPOCH, ) last = QuoteTick( AUDUSD_FXCM, Price("1.00050"), Price("1.00048"), Quantity(1), Quantity(1), UNIX_EPOCH, ) # Act position.apply(fill2) # Assert self.assertEqual(Quantity(), position.quantity) self.assertEqual(PositionSide.FLAT, position.side) self.assertEqual(UNIX_EPOCH, position.opened_time) self.assertEqual(timedelta(minutes=1), position.open_duration) self.assertEqual(1.00001, position.avg_open_price) self.assertEqual(2, position.event_count()) self.assertEqual(datetime(1970, 1, 1, 0, 1, tzinfo=pytz.utc), position.closed_time) self.assertEqual(1.00001, position.avg_close_price) self.assertFalse(position.is_long()) self.assertFalse(position.is_short()) self.assertTrue(position.is_closed()) self.assertEqual(0.0, position.realized_points) self.assertEqual(0.0, position.realized_return) self.assertEqual(Money(0, USD), position.realized_pnl) self.assertEqual(Money(0, USD), position.unrealized_pnl(last)) self.assertEqual(Money(0, USD), position.total_pnl(last)) def test_position_filled_with_sell_order_then_buy_order_returns_expected_attributes(self): # Arrange order1 = self.order_factory.market( AUDUSD_FXCM, OrderSide.SELL, Quantity(100000), ) order2 = self.order_factory.market( AUDUSD_FXCM, OrderSide.BUY, Quantity(100000), ) fill1 = TestStubs.event_order_filled(order1) position = Position(fill1) fill2 = TestStubs.event_order_filled( order2, position_id=PositionId("P-123456"), strategy_id=StrategyId("S", "001"), fill_price=Price("1.00001"), filled_qty=Quantity(50000), leaves_qty=Quantity(50000), ) fill3 = TestStubs.event_order_filled( order2, position_id=PositionId("P-123456"), strategy_id=StrategyId("S", "001"), fill_price=Price("1.00003"), filled_qty=Quantity(50000), leaves_qty=Quantity(0), ) last = QuoteTick( AUDUSD_FXCM, Price("1.00050"), Price("1.00048"), Quantity(1), Quantity(1), UNIX_EPOCH, ) # Act position.apply(fill2) position.apply(fill3) # Assert self.assertEqual(Quantity(), position.quantity) self.assertEqual(PositionSide.FLAT, position.side) self.assertEqual(UNIX_EPOCH, position.opened_time) self.assertEqual(1.0, position.avg_open_price) self.assertEqual(3, position.event_count()) self.assertEqual({order1.cl_ord_id, order2.cl_ord_id}, position.cl_ord_ids()) self.assertEqual(UNIX_EPOCH, position.closed_time) self.assertEqual(1.00002, position.avg_close_price) self.assertFalse(position.is_long()) self.assertFalse(position.is_short()) self.assertTrue(position.is_closed()) self.assertEqual(Money(-2.000, USD), position.realized_pnl) self.assertEqual(Money(0, USD), position.unrealized_pnl(last)) self.assertEqual(Money(-2.000, USD), position.total_pnl(last)) def test_position_filled_with_no_change_returns_expected_attributes(self): # Arrange order1 = self.order_factory.market( AUDUSD_FXCM, OrderSide.BUY, Quantity(100000)) order2 = self.order_factory.market( AUDUSD_FXCM, OrderSide.SELL, Quantity(100000)) fill1 = TestStubs.event_order_filled(order1) position = Position(fill1) fill2 = TestStubs.event_order_filled( order2, position_id=PositionId("P-123456"), strategy_id=StrategyId("S", "001"), fill_price=Price("1.00000"), ) last = QuoteTick( AUDUSD_FXCM, Price("1.00050"), Price("1.00048"), Quantity(1), Quantity(1), UNIX_EPOCH, ) # Act position.apply(fill2) # Assert self.assertEqual(Quantity(), position.quantity) self.assertEqual(PositionSide.FLAT, position.side) self.assertEqual(UNIX_EPOCH, position.opened_time) self.assertEqual(1.0, position.avg_open_price) self.assertEqual(2, position.event_count()) self.assertEqual({order1.cl_ord_id, order2.cl_ord_id}, position.cl_ord_ids()) self.assertEqual({ ExecutionId("E-19700101-000000-001-001-1"), ExecutionId("E-19700101-000000-001-001-2") }, position.execution_ids(), ) self.assertEqual(UNIX_EPOCH, position.closed_time) self.assertEqual(1.0, position.avg_close_price) self.assertFalse(position.is_long()) self.assertFalse(position.is_short()) self.assertTrue(position.is_closed()) self.assertEqual(0.0, position.realized_points) self.assertEqual(0.0, position.realized_return) self.assertEqual(Money(0, USD), position.realized_pnl) self.assertEqual(Money(0, USD), position.unrealized_pnl(last)) self.assertEqual(Money(0, USD), position.total_pnl(last)) def test_position_long_with_multiple_filled_orders_returns_expected_attributes(self): # Arrange order1 = self.order_factory.market( AUDUSD_FXCM, OrderSide.BUY, Quantity(100000), ) order2 = self.order_factory.market( AUDUSD_FXCM, OrderSide.BUY, Quantity(100000), ) order3 = self.order_factory.market( AUDUSD_FXCM, OrderSide.SELL, Quantity(200000), ) fill1 = TestStubs.event_order_filled(order1, PositionId("P-123456"), StrategyId("S", "001")) fill2 = TestStubs.event_order_filled(order2, PositionId("P-123456"), StrategyId("S", "001"), fill_price=Price("1.00001")) fill3 = TestStubs.event_order_filled(order3, PositionId("P-123456"), StrategyId("S", "001"), fill_price=Price("1.00010")) last = QuoteTick( AUDUSD_FXCM, Price("1.00050"), Price("1.00048"), Quantity(1), Quantity(1), UNIX_EPOCH, ) # Act position = Position(fill1) position.apply(fill2) position.apply(fill3) # Assert self.assertEqual(Quantity(), position.quantity) self.assertEqual(PositionSide.FLAT, position.side) self.assertEqual(UNIX_EPOCH, position.opened_time) self.assertEqual(1.000005, position.avg_open_price) self.assertEqual(3, position.event_count()) self.assertEqual({order1.cl_ord_id, order2.cl_ord_id, order3.cl_ord_id}, position.cl_ord_ids()) self.assertEqual(UNIX_EPOCH, position.closed_time) self.assertEqual(1.0001, position.avg_close_price) self.assertFalse(position.is_long()) self.assertFalse(position.is_short()) self.assertTrue(position.is_closed()) self.assertEqual(Money(19.00, USD), position.realized_pnl) self.assertEqual(Money(0, USD), position.unrealized_pnl(last)) self.assertEqual(Money(19.00, USD), position.total_pnl(last)) def test_position_realised_pnl_with_interleaved_orders_sides(self): # Arrange order1 = self.order_factory.market( BTCUSD_BINANCE, OrderSide.BUY, Quantity(12), ) order2 = self.order_factory.market( BTCUSD_BINANCE, OrderSide.BUY, Quantity(17), ) order3 = self.order_factory.market( BTCUSD_BINANCE, OrderSide.SELL, Quantity(9), ) order4 = self.order_factory.market( BTCUSD_BINANCE, OrderSide.BUY, Quantity(3), ) order5 = self.order_factory.market( BTCUSD_BINANCE, OrderSide.SELL, Quantity(4), ) # Act fill1 = TestStubs.event_order_filled( order1, fill_price=Price("10000.00"), base_currency=BTC, quote_currency=USDT, ) position = Position(fill1) fill2 = TestStubs.event_order_filled( order2, position_id=PositionId("P-123456"), strategy_id=StrategyId("S", "001"), fill_price=Price("9999.00"), base_currency=BTC, quote_currency=USDT, ) position.apply(fill2) self.assertEqual(Quantity(29), position.quantity) self.assertEqual(Money(0, BTC), position.realized_pnl) self.assertEqual(9999.413793103447, position.avg_open_price) fill3 = TestStubs.event_order_filled( order3, position_id=PositionId("P-123456"), strategy_id=StrategyId("S", "001"), fill_price=Price("10001.00"), base_currency=BTC, quote_currency=USDT, ) position.apply(fill3) self.assertEqual(Quantity(20), position.quantity) self.assertEqual(Money(0.00142767, BTC), position.realized_pnl) self.assertEqual(9999.413793103447, position.avg_open_price) fill4 = TestStubs.event_order_filled( order4, position_id=PositionId("P-123456"), strategy_id=StrategyId("S", "001"), fill_price=Price("10003.00"), base_currency=BTC, quote_currency=USDT, ) position.apply(fill4) self.assertEqual(Quantity(23), position.quantity) self.assertEqual(Money(0.00142767, BTC), position.realized_pnl) self.assertEqual(9999.88155922039, position.avg_open_price) fill5 = TestStubs.event_order_filled( order5, position_id=PositionId("P-123456"), strategy_id=StrategyId("S", "001"), fill_price=Price("10005"), base_currency=BTC, quote_currency=USDT, ) position.apply(fill5) self.assertEqual(Quantity(19), position.quantity) self.assertEqual(Money(0.00347507, BTC), position.realized_pnl) self.assertEqual(9999.88155922039, position.avg_open_price)
class ReportProviderTests(unittest.TestCase): def setUp(self): # Fixture Setup self.account_id = TestStubs.account_id() self.order_factory = OrderFactory( trader_id=TraderId("TESTER", "000"), strategy_id=StrategyId("S", "001"), clock=TestClock(), ) def test_generate_accounts_report_with_initial_account_state_returns_expected( self): # Arrange state = AccountState( account_id=AccountId("BITMEX", "1513111"), balances=[Money("10.00000000", BTC)], balances_free=[Money("10.00000000", BTC)], balances_locked=[Money("0.00000000", BTC)], info={}, event_id=uuid4(), event_timestamp=UNIX_EPOCH, ) account = Account(state) report_provider = ReportProvider() # Act report = report_provider.generate_account_report(account) # Assert self.assertEqual(1, len(report)) def test_generate_orders_report_with_no_order_returns_emtpy_dataframe( self): # Arrange report_provider = ReportProvider() # Act report = report_provider.generate_orders_report([]) # Assert self.assertTrue(report.empty) def test_generate_orders_fills_report_with_no_order_returns_emtpy_dataframe( self): # Arrange report_provider = ReportProvider() # Act report = report_provider.generate_order_fills_report([]) # Assert self.assertTrue(report.empty) def test_generate_positions_report_with_no_positions_returns_emtpy_dataframe( self): # Arrange report_provider = ReportProvider() # Act report = report_provider.generate_positions_report([]) # Assert self.assertTrue(report.empty) def test_generate_orders_report(self): # Arrange report_provider = ReportProvider() order1 = self.order_factory.limit( AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(1500000), Price("0.80010"), ) order1.apply(TestStubs.event_order_submitted(order1)) order1.apply(TestStubs.event_order_accepted(order1)) order1.apply(TestStubs.event_order_working(order1)) order2 = self.order_factory.limit( AUDUSD_SIM.symbol, OrderSide.SELL, Quantity(1500000), Price("0.80000"), ) order2.apply(TestStubs.event_order_submitted(order2)) order2.apply(TestStubs.event_order_accepted(order2)) order2.apply(TestStubs.event_order_working(order2)) event = TestStubs.event_order_filled( order1, instrument=AUDUSD_SIM, position_id=PositionId("P-1"), fill_price=Price("0.80011"), ) order1.apply(event) orders = [order1, order2] # Act report = report_provider.generate_orders_report(orders) # Assert self.assertEqual(2, len(report)) self.assertEqual("cl_ord_id", report.index.name) self.assertEqual(order1.cl_ord_id.value, report.index[0]) self.assertEqual("AUD/USD", report.iloc[0]["symbol"]) self.assertEqual("BUY", report.iloc[0]["side"]) self.assertEqual("LIMIT", report.iloc[0]["type"]) self.assertEqual(1500000, report.iloc[0]["quantity"]) self.assertEqual(0.80011, report.iloc[0]["avg_price"]) self.assertEqual(0.00001, report.iloc[0]["slippage"]) self.assertEqual("None", report.iloc[1]["avg_price"]) def test_generate_order_fills_report(self): # Arrange report_provider = ReportProvider() order1 = self.order_factory.limit( AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(1500000), Price("0.80010"), ) order1.apply(TestStubs.event_order_submitted(order1)) order1.apply(TestStubs.event_order_accepted(order1)) order1.apply(TestStubs.event_order_working(order1)) order2 = self.order_factory.limit( AUDUSD_SIM.symbol, OrderSide.SELL, Quantity(1500000), Price("0.80000"), ) submitted2 = TestStubs.event_order_submitted(order2) accepted2 = TestStubs.event_order_accepted(order2) working2 = TestStubs.event_order_working(order2) order2.apply(submitted2) order2.apply(accepted2) order2.apply(working2) filled = TestStubs.event_order_filled( order1, instrument=AUDUSD_SIM, position_id=PositionId("P-1"), strategy_id=StrategyId("S", "1"), fill_price=Price("0.80011"), ) order1.apply(filled) orders = [order1, order2] # Act report = report_provider.generate_order_fills_report(orders) # Assert self.assertEqual(1, len(report)) self.assertEqual("cl_ord_id", report.index.name) self.assertEqual(order1.cl_ord_id.value, report.index[0]) self.assertEqual("AUD/USD", report.iloc[0]["symbol"]) self.assertEqual("BUY", report.iloc[0]["side"]) self.assertEqual("LIMIT", report.iloc[0]["type"]) self.assertEqual(1500000, report.iloc[0]["quantity"]) self.assertEqual(0.80011, report.iloc[0]["avg_price"]) self.assertEqual(0.00001, report.iloc[0]["slippage"]) def test_generate_positions_report(self): # Arrange report_provider = ReportProvider() order1 = self.order_factory.market( AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(100000), ) order2 = self.order_factory.market( AUDUSD_SIM.symbol, OrderSide.SELL, Quantity(100000), ) fill1 = TestStubs.event_order_filled( order1, instrument=AUDUSD_SIM, position_id=PositionId("P-123456"), strategy_id=StrategyId("S", "001"), fill_price=Price("1.00010"), ) fill2 = TestStubs.event_order_filled( order2, instrument=AUDUSD_SIM, position_id=PositionId("P-123457"), strategy_id=StrategyId("S", "001"), fill_price=Price("1.00010"), ) position1 = Position(fill1) position1.apply(fill2) position2 = Position(fill1) position2.apply(fill2) positions = [position1, position2] # Act report = report_provider.generate_positions_report(positions) # Assert self.assertEqual(2, len(report)) self.assertEqual("position_id", report.index.name) self.assertEqual(position1.id.value, report.index[0]) self.assertEqual("AUD/USD", report.iloc[0]["symbol"]) self.assertEqual("BUY", report.iloc[0]["entry"]) self.assertEqual(100000, report.iloc[0]["peak_quantity"]) self.assertEqual(1.0001, report.iloc[0]["avg_open"]) self.assertEqual(1.0001, report.iloc[0]["avg_close"]) self.assertEqual(UNIX_EPOCH, report.iloc[0]["opened_time"]) self.assertEqual(UNIX_EPOCH, report.iloc[0]["closed_time"]) self.assertEqual(0, report.iloc[0]["realized_points"]) self.assertEqual(0, report.iloc[0]["realized_return"])
class PortfolioTests(unittest.TestCase): def setUp(self): # Fixture Setup clock = TestClock() logger = Logger(clock, level_stdout=LogLevel.DEBUG) trader_id = TraderId("TESTER-000") self.order_factory = OrderFactory( trader_id=trader_id, strategy_id=StrategyId("S-001"), clock=TestClock(), ) cache_db = BypassCacheDatabase( trader_id=trader_id, logger=logger, ) self.cache = Cache( database=cache_db, logger=logger, ) self.portfolio = Portfolio( cache=self.cache, clock=clock, logger=logger, ) self.exec_engine = ExecutionEngine( portfolio=self.portfolio, cache=self.cache, clock=clock, logger=logger, ) self.risk_engine = RiskEngine( exec_engine=self.exec_engine, portfolio=self.portfolio, cache=self.cache, clock=clock, logger=logger, ) # Wire up components self.exec_engine.register_risk_engine(self.risk_engine) # Prepare components self.cache.add_instrument(AUDUSD_SIM) self.cache.add_instrument(GBPUSD_SIM) self.cache.add_instrument(BTCUSDT_BINANCE) self.cache.add_instrument(BTCUSD_BITMEX) self.cache.add_instrument(ETHUSD_BITMEX) def test_account_when_no_account_returns_none(self): # Arrange # Act # Assert self.assertIsNone(self.portfolio.account(SIM)) def test_account_when_account_returns_the_account_facade(self): # Arrange account_state = AccountState( account_id=AccountId("BINANCE", "1513111"), account_type=AccountType.CASH, base_currency=None, reported=True, balances=[ AccountBalance( BTC, Money(10.00000000, BTC), Money(0.00000000, BTC), Money(10.00000000, BTC), ) ], info={}, event_id=uuid4(), updated_ns=0, timestamp_ns=0, ) self.exec_engine.process(account_state) # Act result = self.portfolio.account(BINANCE) # Assert self.assertEqual("BINANCE", result.id.issuer) def test_net_position_when_no_positions_returns_zero(self): # Arrange # Act # Assert self.assertEqual(Decimal(0), self.portfolio.net_position(AUDUSD_SIM.id)) def test_is_net_long_when_no_positions_returns_false(self): # Arrange # Act # Assert self.assertEqual(False, self.portfolio.is_net_long(AUDUSD_SIM.id)) def test_is_net_short_when_no_positions_returns_false(self): # Arrange # Act # Assert self.assertEqual(False, self.portfolio.is_net_short(AUDUSD_SIM.id)) def test_is_flat_when_no_positions_returns_true(self): # Arrange # Act # Assert self.assertEqual(True, self.portfolio.is_flat(AUDUSD_SIM.id)) def test_is_completely_flat_when_no_positions_returns_true(self): # Arrange # Act # Assert self.assertEqual(True, self.portfolio.is_flat(AUDUSD_SIM.id)) def test_unrealized_pnl_for_instrument_when_no_instrument_returns_none( self): # Arrange # Act # Assert self.assertIsNone(self.portfolio.unrealized_pnl(USDJPY_SIM.id)) def test_unrealized_pnl_for_venue_when_no_account_returns_empty_dict(self): # Arrange # Act # Assert self.assertEqual({}, self.portfolio.unrealized_pnls(SIM)) def test_initial_margins_when_no_account_returns_none(self): # Arrange # Act # Assert self.assertEqual(None, self.portfolio.initial_margins(SIM)) def test_maint_margins_when_no_account_returns_none(self): # Arrange # Act # Assert self.assertEqual(None, self.portfolio.maint_margins(SIM)) def test_open_value_when_no_account_returns_none(self): # Arrange # Act # Assert self.assertEqual(None, self.portfolio.net_exposures(SIM)) def test_update_tick(self): # Arrange tick = TestStubs.quote_tick_5decimal(GBPUSD_SIM.id) # Act self.portfolio.update_tick(tick) # Assert self.assertIsNone(self.portfolio.unrealized_pnl(GBPUSD_SIM.id)) def test_update_orders_working(self): # Arrange state = AccountState( account_id=AccountId("BINANCE", "01234"), account_type=AccountType.CASH, base_currency=None, # Multi-currency account reported=True, balances=[ AccountBalance( BTC, Money(10.00000000, BTC), Money(0.00000000, BTC), Money(10.00000000, BTC), ), AccountBalance( ETH, Money(20.00000000, ETH), Money(0.00000000, ETH), Money(20.00000000, ETH), ), ], info={}, event_id=uuid4(), updated_ns=0, timestamp_ns=0, ) self.exec_engine.process(state) # Create two working orders order1 = self.order_factory.stop_market( BTCUSDT_BINANCE.id, OrderSide.BUY, Quantity.from_str("10.5"), Price.from_str("25000.00"), ) order2 = self.order_factory.stop_market( BTCUSDT_BINANCE.id, OrderSide.BUY, Quantity.from_str("10.5"), Price.from_str("25000.00"), ) self.exec_engine.cache.add_order(order1, PositionId.null()) self.exec_engine.cache.add_order(order2, PositionId.null()) # Push states to ACCEPTED order1.apply(TestStubs.event_order_submitted(order1)) self.exec_engine.cache.update_order(order1) order1.apply(TestStubs.event_order_accepted(order1)) self.exec_engine.cache.update_order(order1) filled1 = TestStubs.event_order_filled( order1, instrument=BTCUSDT_BINANCE, position_id=PositionId("P-1"), strategy_id=StrategyId("S-1"), last_px=Price.from_str("25000.00"), ) self.exec_engine.process(filled1) # Update the last quote last = QuoteTick( BTCUSDT_BINANCE.id, Price.from_str("25001.00"), Price.from_str("25002.00"), Quantity.from_int(1), Quantity.from_int(1), 0, 0, ) # Act self.portfolio.update_tick(last) self.portfolio.initialize_orders() # Assert self.assertEqual({}, self.portfolio.initial_margins(BINANCE)) def test_update_positions(self): # Arrange state = AccountState( account_id=AccountId("BINANCE", "01234"), account_type=AccountType.CASH, base_currency=None, # Multi-currency account reported=True, balances=[ AccountBalance( BTC, Money(10.00000000, BTC), Money(0.00000000, BTC), Money(10.00000000, BTC), ), AccountBalance( ETH, Money(20.00000000, ETH), Money(0.00000000, ETH), Money(20.00000000, ETH), ), ], info={}, event_id=uuid4(), updated_ns=0, timestamp_ns=0, ) self.exec_engine.process(state) # Create a closed position order1 = self.order_factory.market( BTCUSDT_BINANCE.id, OrderSide.BUY, Quantity.from_str("10.50000000"), ) order2 = self.order_factory.market( BTCUSDT_BINANCE.id, OrderSide.SELL, Quantity.from_str("10.50000000"), ) self.exec_engine.cache.add_order(order1, PositionId.null()) self.exec_engine.cache.add_order(order2, PositionId.null()) # Push states to ACCEPTED order1.apply(TestStubs.event_order_submitted(order1)) self.exec_engine.cache.update_order(order1) order1.apply(TestStubs.event_order_accepted(order1)) self.exec_engine.cache.update_order(order1) fill1 = TestStubs.event_order_filled( order1, instrument=BTCUSDT_BINANCE, position_id=PositionId("P-1"), strategy_id=StrategyId("S-1"), last_px=Price.from_str("25000.00"), ) fill2 = TestStubs.event_order_filled( order2, instrument=BTCUSDT_BINANCE, position_id=PositionId("P-1"), strategy_id=StrategyId("S-1"), last_px=Price.from_str("25000.00"), ) position1 = Position(instrument=BTCUSDT_BINANCE, fill=fill1) position1.apply(fill2) order3 = self.order_factory.market( BTCUSDT_BINANCE.id, OrderSide.BUY, Quantity.from_str("10.00000000"), ) fill3 = TestStubs.event_order_filled( order3, instrument=BTCUSDT_BINANCE, position_id=PositionId("P-2"), strategy_id=StrategyId("S-1"), last_px=Price.from_str("25000.00"), ) position2 = Position(instrument=BTCUSDT_BINANCE, fill=fill3) # Update the last quote last = QuoteTick( BTCUSDT_BINANCE.id, Price.from_str("25001.00"), Price.from_str("25002.00"), Quantity.from_int(1), Quantity.from_int(1), 0, 0, ) # Act self.cache.add_position(position1) self.cache.add_position(position2) self.portfolio.initialize_positions() self.portfolio.update_tick(last) # Assert self.assertTrue(self.portfolio.is_net_long(BTCUSDT_BINANCE.id)) def test_opening_one_long_position_updates_portfolio(self): # Arrange state = AccountState( account_id=AccountId("BINANCE", "01234"), account_type=AccountType.CASH, base_currency=None, # Multi-currency account reported=True, balances=[ AccountBalance( BTC, Money(10.00000000, BTC), Money(0.00000000, BTC), Money(10.00000000, BTC), ), AccountBalance( ETH, Money(20.00000000, ETH), Money(0.00000000, ETH), Money(20.00000000, ETH), ), ], info={}, event_id=uuid4(), updated_ns=0, timestamp_ns=0, ) self.exec_engine.process(state) order = self.order_factory.market( BTCUSDT_BINANCE.id, OrderSide.BUY, Quantity.from_str("10.000000"), ) fill = TestStubs.event_order_filled( order=order, instrument=BTCUSDT_BINANCE, position_id=PositionId("P-123456"), strategy_id=StrategyId("S-001"), last_px=Price.from_str("10500.00"), ) last = QuoteTick( BTCUSDT_BINANCE.id, Price.from_str("10510.00"), Price.from_str("10511.00"), Quantity.from_str("1.000000"), Quantity.from_str("1.000000"), 0, 0, ) self.cache.add_quote_tick(last) self.portfolio.update_tick(last) position = Position(instrument=BTCUSDT_BINANCE, fill=fill) # Act self.cache.add_position(position) self.portfolio.update_position( TestStubs.event_position_opened(position)) # Assert self.assertEqual( {USDT: Money(105100.00000000, USDT)}, self.portfolio.net_exposures(BINANCE), ) self.assertEqual( {USDT: Money(100.00000000, USDT)}, self.portfolio.unrealized_pnls(BINANCE), ) self.assertEqual( {USDT: Money(105.10000000, USDT)}, self.portfolio.maint_margins(BINANCE), ) self.assertEqual( Money(105100.00000000, USDT), self.portfolio.net_exposure(BTCUSDT_BINANCE.id), ) self.assertEqual( Money(100.00000000, USDT), self.portfolio.unrealized_pnl(BTCUSDT_BINANCE.id), ) self.assertEqual( Decimal("10.00000000"), self.portfolio.net_position(order.instrument_id), ) self.assertTrue(self.portfolio.is_net_long(order.instrument_id)) self.assertFalse(self.portfolio.is_net_short(order.instrument_id)) self.assertFalse(self.portfolio.is_flat(order.instrument_id)) self.assertFalse(self.portfolio.is_completely_flat()) def test_opening_one_short_position_updates_portfolio(self): # Arrange state = AccountState( account_id=AccountId("BINANCE", "01234"), account_type=AccountType.CASH, base_currency=None, # Multi-currency account reported=True, balances=[ AccountBalance( BTC, Money(10.00000000, BTC), Money(0.00000000, BTC), Money(10.00000000, BTC), ), AccountBalance( ETH, Money(20.00000000, ETH), Money(0.00000000, ETH), Money(20.00000000, ETH), ), ], info={}, event_id=uuid4(), updated_ns=0, timestamp_ns=0, ) self.exec_engine.process(state) order = self.order_factory.market( BTCUSDT_BINANCE.id, OrderSide.SELL, Quantity.from_str("0.515"), ) fill = TestStubs.event_order_filled( order=order, instrument=BTCUSDT_BINANCE, position_id=PositionId("P-123456"), strategy_id=StrategyId("S-001"), last_px=Price.from_str("15000.00"), ) last = QuoteTick( BTCUSDT_BINANCE.id, Price.from_str("15510.15"), Price.from_str("15510.25"), Quantity.from_str("12.62"), Quantity.from_str("3.1"), 0, 0, ) self.cache.add_quote_tick(last) self.portfolio.update_tick(last) position = Position(instrument=BTCUSDT_BINANCE, fill=fill) # Act self.cache.add_position(position) self.portfolio.update_position( TestStubs.event_position_opened(position)) # Assert self.assertEqual( {USDT: Money(7987.77875000, USDT)}, self.portfolio.net_exposures(BINANCE), ) self.assertEqual( {USDT: Money(-262.77875000, USDT)}, self.portfolio.unrealized_pnls(BINANCE), ) self.assertEqual( {USDT: Money(7.98777875, USDT)}, self.portfolio.maint_margins(BINANCE), ) self.assertEqual( Money(7987.77875000, USDT), self.portfolio.net_exposure(BTCUSDT_BINANCE.id), ) self.assertEqual( Money(-262.77875000, USDT), self.portfolio.unrealized_pnl(BTCUSDT_BINANCE.id), ) self.assertEqual( Decimal("-0.515"), self.portfolio.net_position(order.instrument_id), ) self.assertFalse(self.portfolio.is_net_long(order.instrument_id)) self.assertTrue(self.portfolio.is_net_short(order.instrument_id)) self.assertFalse(self.portfolio.is_flat(order.instrument_id)) self.assertFalse(self.portfolio.is_completely_flat()) def test_opening_positions_with_multi_asset_account(self): # Arrange state = AccountState( account_id=AccountId("BITMEX", "01234"), account_type=AccountType.CASH, base_currency=None, # Multi-currency account reported=True, balances=[ AccountBalance( BTC, Money(10.00000000, BTC), Money(0.00000000, BTC), Money(10.00000000, BTC), ), AccountBalance( ETH, Money(20.00000000, ETH), Money(0.00000000, ETH), Money(20.00000000, ETH), ), ], info={}, event_id=uuid4(), updated_ns=0, timestamp_ns=0, ) self.exec_engine.process(state) last_ethusd = QuoteTick( ETHUSD_BITMEX.id, Price.from_str("376.05"), Price.from_str("377.10"), Quantity.from_str("16"), Quantity.from_str("25"), 0, 0, ) last_btcusd = QuoteTick( BTCUSD_BITMEX.id, Price.from_str("10500.05"), Price.from_str("10501.51"), Quantity.from_str("2.54"), Quantity.from_str("0.91"), 0, 0, ) self.cache.add_quote_tick(last_ethusd) self.cache.add_quote_tick(last_btcusd) self.portfolio.update_tick(last_ethusd) self.portfolio.update_tick(last_btcusd) order = self.order_factory.market( ETHUSD_BITMEX.id, OrderSide.BUY, Quantity.from_int(10000), ) fill = TestStubs.event_order_filled( order=order, instrument=ETHUSD_BITMEX, position_id=PositionId("P-123456"), strategy_id=StrategyId("S-001"), last_px=Price.from_str("376.05"), ) position = Position(instrument=ETHUSD_BITMEX, fill=fill) # Act self.cache.add_position(position) self.portfolio.update_position( TestStubs.event_position_opened(position)) # Assert self.assertEqual( {ETH: Money(26.59220848, ETH)}, self.portfolio.net_exposures(BITMEX), ) self.assertEqual( {ETH: Money(0.20608962, ETH)}, self.portfolio.maint_margins(BITMEX), ) self.assertEqual( Money(26.59220848, ETH), self.portfolio.net_exposure(ETHUSD_BITMEX.id), ) self.assertEqual( Money(0.00000000, ETH), self.portfolio.unrealized_pnl(ETHUSD_BITMEX.id), ) def test_unrealized_pnl_when_insufficient_data_for_xrate_returns_none( self): # Arrange state = AccountState( account_id=AccountId("BITMEX", "01234"), account_type=AccountType.MARGIN, base_currency=BTC, reported=True, balances=[ AccountBalance( BTC, Money(10.00000000, BTC), Money(0.00000000, BTC), Money(10.00000000, BTC), ), AccountBalance( ETH, Money(20.00000000, ETH), Money(0.00000000, ETH), Money(20.00000000, ETH), ), ], info={}, event_id=uuid4(), updated_ns=0, timestamp_ns=0, ) self.exec_engine.process(state) order = self.order_factory.market( ETHUSD_BITMEX.id, OrderSide.BUY, Quantity.from_int(100), ) self.exec_engine.cache.add_order(order, PositionId.null()) self.exec_engine.process(TestStubs.event_order_submitted(order)) self.exec_engine.process(TestStubs.event_order_accepted(order)) fill = TestStubs.event_order_filled( order=order, instrument=ETHUSD_BITMEX, position_id=PositionId("P-123456"), strategy_id=StrategyId("S-001"), last_px=Price.from_str("376.05"), ) self.exec_engine.process(fill) position = Position(instrument=ETHUSD_BITMEX, fill=fill) self.portfolio.update_position( TestStubs.event_position_opened(position)) # Act result = self.portfolio.unrealized_pnls(BITMEX) # # Assert self.assertEqual({}, result) def test_market_value_when_insufficient_data_for_xrate_returns_none(self): # Arrange state = AccountState( account_id=AccountId("BITMEX", "01234"), account_type=AccountType.MARGIN, base_currency=BTC, reported=True, balances=[ AccountBalance( BTC, Money(10.00000000, BTC), Money(0.00000000, BTC), Money(10.00000000, BTC), ), ], info={}, event_id=uuid4(), updated_ns=0, timestamp_ns=0, ) self.exec_engine.process(state) order = self.order_factory.market( ETHUSD_BITMEX.id, OrderSide.BUY, Quantity.from_int(100), ) fill = TestStubs.event_order_filled( order=order, instrument=ETHUSD_BITMEX, position_id=PositionId("P-123456"), strategy_id=StrategyId("S-001"), last_px=Price.from_str("376.05"), ) last_ethusd = QuoteTick( ETHUSD_BITMEX.id, Price.from_str("376.05"), Price.from_str("377.10"), Quantity.from_str("16"), Quantity.from_str("25"), 0, 0, ) last_xbtusd = QuoteTick( BTCUSD_BITMEX.id, Price.from_str("50000.00"), Price.from_str("50000.00"), Quantity.from_str("1"), Quantity.from_str("1"), 0, 0, ) position = Position(instrument=ETHUSD_BITMEX, fill=fill) self.portfolio.update_position( TestStubs.event_position_opened(position)) self.cache.add_position(position) self.cache.add_quote_tick(last_ethusd) self.cache.add_quote_tick(last_xbtusd) self.portfolio.update_tick(last_ethusd) self.portfolio.update_tick(last_xbtusd) # Act result = self.portfolio.net_exposures(BITMEX) # Assert self.assertEqual({BTC: Money(0.00200000, BTC)}, result) def test_opening_several_positions_updates_portfolio(self): # Arrange state = AccountState( account_id=AccountId("SIM", "01234"), account_type=AccountType.MARGIN, base_currency=USD, reported=True, balances=[ AccountBalance( USD, Money(1_000_000, USD), Money(0, USD), Money(1_000_000, USD), ), ], info={}, event_id=uuid4(), updated_ns=0, timestamp_ns=0, ) self.exec_engine.process(state) last_audusd = QuoteTick( AUDUSD_SIM.id, Price.from_str("0.80501"), Price.from_str("0.80505"), Quantity.from_int(1), Quantity.from_int(1), 0, 0, ) last_gbpusd = QuoteTick( GBPUSD_SIM.id, Price.from_str("1.30315"), Price.from_str("1.30317"), Quantity.from_int(1), Quantity.from_int(1), 0, 0, ) self.cache.add_quote_tick(last_audusd) self.cache.add_quote_tick(last_gbpusd) self.portfolio.update_tick(last_audusd) self.portfolio.update_tick(last_gbpusd) order1 = self.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) order2 = self.order_factory.market( GBPUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) self.exec_engine.cache.add_order(order1, PositionId.null()) self.exec_engine.cache.add_order(order2, PositionId.null()) fill1 = TestStubs.event_order_filled( order1, instrument=AUDUSD_SIM, position_id=PositionId("P-1"), strategy_id=StrategyId("S-1"), last_px=Price.from_str("1.00000"), ) fill2 = TestStubs.event_order_filled( order2, instrument=GBPUSD_SIM, position_id=PositionId("P-2"), strategy_id=StrategyId("S-1"), last_px=Price.from_str("1.00000"), ) self.exec_engine.cache.update_order(order1) self.exec_engine.cache.update_order(order2) position1 = Position(instrument=AUDUSD_SIM, fill=fill1) position2 = Position(instrument=GBPUSD_SIM, fill=fill2) position_opened1 = TestStubs.event_position_opened(position1) position_opened2 = TestStubs.event_position_opened(position2) # Act self.cache.add_position(position1) self.cache.add_position(position2) self.portfolio.update_position(position_opened1) self.portfolio.update_position(position_opened2) # Assert self.assertEqual( {USD: Money(210816.00, USD)}, self.portfolio.net_exposures(SIM), ) self.assertEqual( {USD: Money(10816.00, USD)}, self.portfolio.unrealized_pnls(SIM), ) self.assertEqual({USD: Money(3912.06, USD)}, self.portfolio.maint_margins(SIM)), self.assertEqual( Money(80501.00, USD), self.portfolio.net_exposure(AUDUSD_SIM.id), ) self.assertEqual( Money(130315.00, USD), self.portfolio.net_exposure(GBPUSD_SIM.id), ) self.assertEqual( Money(-19499.00, USD), self.portfolio.unrealized_pnl(AUDUSD_SIM.id), ) self.assertEqual( Money(30315.00, USD), self.portfolio.unrealized_pnl(GBPUSD_SIM.id), ) self.assertEqual(Decimal(100000), self.portfolio.net_position(AUDUSD_SIM.id)) self.assertEqual(Decimal(100000), self.portfolio.net_position(GBPUSD_SIM.id)) self.assertTrue(self.portfolio.is_net_long(AUDUSD_SIM.id)) self.assertFalse(self.portfolio.is_net_short(AUDUSD_SIM.id)) self.assertFalse(self.portfolio.is_flat(AUDUSD_SIM.id)) self.assertFalse(self.portfolio.is_completely_flat()) def test_modifying_position_updates_portfolio(self): # Arrange state = AccountState( account_id=AccountId("SIM", "01234"), account_type=AccountType.MARGIN, base_currency=USD, reported=True, balances=[ AccountBalance( USD, Money(1_000_000, USD), Money(0, USD), Money(1_000_000, USD), ), ], info={}, event_id=uuid4(), updated_ns=0, timestamp_ns=0, ) self.exec_engine.process(state) last_audusd = QuoteTick( AUDUSD_SIM.id, Price.from_str("0.80501"), Price.from_str("0.80505"), Quantity.from_int(1), Quantity.from_int(1), 0, 0, ) self.cache.add_quote_tick(last_audusd) self.portfolio.update_tick(last_audusd) order1 = self.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) fill1 = TestStubs.event_order_filled( order1, instrument=AUDUSD_SIM, position_id=PositionId("P-123456"), strategy_id=StrategyId("S-1"), last_px=Price.from_str("1.00000"), ) position = Position(instrument=AUDUSD_SIM, fill=fill1) self.exec_engine.cache.add_position(position) self.portfolio.update_position( TestStubs.event_position_opened(position)) order2 = self.order_factory.market( AUDUSD_SIM.id, OrderSide.SELL, Quantity.from_int(50000), ) order2_filled = TestStubs.event_order_filled( order2, instrument=AUDUSD_SIM, position_id=PositionId("P-123456"), strategy_id=StrategyId("S-1"), last_px=Price.from_str("1.00000"), ) position.apply(order2_filled) # Act self.portfolio.update_position( TestStubs.event_position_changed(position)) # Assert self.assertEqual( {USD: Money(40250.50, USD)}, self.portfolio.net_exposures(SIM), ) self.assertEqual( {USD: Money(-9749.50, USD)}, self.portfolio.unrealized_pnls(SIM), ) self.assertEqual( {USD: Money(1208.32, USD)}, self.portfolio.maint_margins(SIM), ) self.assertEqual( Money(40250.50, USD), self.portfolio.net_exposure(AUDUSD_SIM.id), ) self.assertEqual( Money(-9749.50, USD), self.portfolio.unrealized_pnl(AUDUSD_SIM.id), ) self.assertEqual(Decimal(50000), self.portfolio.net_position(AUDUSD_SIM.id)) self.assertTrue(self.portfolio.is_net_long(AUDUSD_SIM.id)) self.assertFalse(self.portfolio.is_net_short(AUDUSD_SIM.id)) self.assertFalse(self.portfolio.is_flat(AUDUSD_SIM.id)) self.assertFalse(self.portfolio.is_completely_flat()) self.assertEqual({}, self.portfolio.unrealized_pnls(BINANCE)) self.assertIsNone(self.portfolio.net_exposures(BINANCE)) def test_closing_position_updates_portfolio(self): # Arrange state = AccountState( account_id=AccountId("SIM", "01234"), account_type=AccountType.MARGIN, base_currency=USD, reported=True, balances=[ AccountBalance( USD, Money(1_000_000, USD), Money(0, USD), Money(1_000_000, USD), ), ], info={}, event_id=uuid4(), updated_ns=0, timestamp_ns=0, ) self.exec_engine.process(state) order1 = self.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) fill1 = TestStubs.event_order_filled( order1, instrument=AUDUSD_SIM, position_id=PositionId("P-123456"), strategy_id=StrategyId("S-1"), last_px=Price.from_str("1.00000"), ) position = Position(instrument=AUDUSD_SIM, fill=fill1) self.exec_engine.cache.add_position(position) self.portfolio.update_position( TestStubs.event_position_opened(position)) order2 = self.order_factory.market( AUDUSD_SIM.id, OrderSide.SELL, Quantity.from_int(100000), ) order2_filled = TestStubs.event_order_filled( order2, instrument=AUDUSD_SIM, position_id=PositionId("P-123456"), strategy_id=StrategyId("S-1"), last_px=Price.from_str("1.00010"), ) position.apply(order2_filled) self.exec_engine.cache.update_position(position) # Act self.portfolio.update_position( TestStubs.event_position_closed(position)) # Assert self.assertEqual({}, self.portfolio.net_exposures(SIM)) self.assertEqual({}, self.portfolio.unrealized_pnls(SIM)) self.assertEqual({}, self.portfolio.maint_margins(SIM)) self.assertEqual(Money(0, USD), self.portfolio.net_exposure(AUDUSD_SIM.id)) self.assertEqual(Money(0, USD), self.portfolio.unrealized_pnl(AUDUSD_SIM.id)) self.assertEqual(Decimal(0), self.portfolio.net_position(AUDUSD_SIM.id)) self.assertFalse(self.portfolio.is_net_long(AUDUSD_SIM.id)) self.assertFalse(self.portfolio.is_net_short(AUDUSD_SIM.id)) self.assertTrue(self.portfolio.is_flat(AUDUSD_SIM.id)) self.assertTrue(self.portfolio.is_completely_flat()) def test_several_positions_with_different_instruments_updates_portfolio( self): # Arrange state = AccountState( account_id=AccountId("SIM", "01234"), account_type=AccountType.MARGIN, base_currency=USD, reported=True, balances=[ AccountBalance( USD, Money(1_000_000, USD), Money(0, USD), Money(1_000_000, USD), ), ], info={}, event_id=uuid4(), updated_ns=0, timestamp_ns=0, ) self.exec_engine.process(state) order1 = self.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) order2 = self.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) order3 = self.order_factory.market( GBPUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) order4 = self.order_factory.market( GBPUSD_SIM.id, OrderSide.SELL, Quantity.from_int(100000), ) fill1 = TestStubs.event_order_filled( order1, instrument=AUDUSD_SIM, position_id=PositionId("P-1"), strategy_id=StrategyId("S-1"), last_px=Price.from_str("1.00000"), ) fill2 = TestStubs.event_order_filled( order2, instrument=AUDUSD_SIM, position_id=PositionId("P-2"), strategy_id=StrategyId("S-1"), last_px=Price.from_str("1.00000"), ) fill3 = TestStubs.event_order_filled( order3, instrument=GBPUSD_SIM, position_id=PositionId("P-3"), strategy_id=StrategyId("S-1"), last_px=Price.from_str("1.00000"), ) fill4 = TestStubs.event_order_filled( order4, instrument=GBPUSD_SIM, position_id=PositionId("P-3"), strategy_id=StrategyId("S-1"), last_px=Price.from_str("1.00100"), ) position1 = Position(instrument=AUDUSD_SIM, fill=fill1) position2 = Position(instrument=AUDUSD_SIM, fill=fill2) position3 = Position(instrument=GBPUSD_SIM, fill=fill3) last_audusd = QuoteTick( AUDUSD_SIM.id, Price.from_str("0.80501"), Price.from_str("0.80505"), Quantity.from_int(1), Quantity.from_int(1), 0, 0, ) last_gbpusd = QuoteTick( GBPUSD_SIM.id, Price.from_str("1.30315"), Price.from_str("1.30317"), Quantity.from_int(1), Quantity.from_int(1), 0, 0, ) self.cache.add_quote_tick(last_audusd) self.cache.add_quote_tick(last_gbpusd) self.portfolio.update_tick(last_audusd) self.portfolio.update_tick(last_gbpusd) self.cache.add_position(position1) self.cache.add_position(position2) self.cache.add_position(position3) # Act self.portfolio.update_position( TestStubs.event_position_opened(position1)) self.portfolio.update_position( TestStubs.event_position_opened(position2)) self.portfolio.update_position( TestStubs.event_position_opened(position3)) position3.apply(fill4) self.cache.update_position(position3) self.portfolio.update_position( TestStubs.event_position_closed(position3)) # Assert self.assertEqual( {USD: Money(-38998.00, USD)}, self.portfolio.unrealized_pnls(SIM), ) self.assertEqual( {USD: Money(161002.00, USD)}, self.portfolio.net_exposures(SIM), ) self.assertEqual({USD: Money(3912.06, USD)}, self.portfolio.maint_margins(SIM)), self.assertEqual( Money(161002.00, USD), self.portfolio.net_exposure(AUDUSD_SIM.id), ) self.assertEqual( Money(-38998.00, USD), self.portfolio.unrealized_pnl(AUDUSD_SIM.id), ) self.assertEqual(Money(0, USD), self.portfolio.unrealized_pnl(GBPUSD_SIM.id)) self.assertEqual(Decimal(200000), self.portfolio.net_position(AUDUSD_SIM.id)) self.assertEqual(Decimal(0), self.portfolio.net_position(GBPUSD_SIM.id)) self.assertTrue(self.portfolio.is_net_long(AUDUSD_SIM.id)) self.assertTrue(self.portfolio.is_flat(GBPUSD_SIM.id)) self.assertFalse(self.portfolio.is_completely_flat())
class ExecutionClientTests(unittest.TestCase): def setUp(self): # Fixture Setup self.clock = TestClock() self.uuid_factory = UUIDFactory() self.logger = TestLogger(self.clock) self.trader_id = TraderId("TESTER", "000") self.account_id = TestStubs.account_id() portfolio = Portfolio( clock=self.clock, logger=self.logger, ) portfolio.register_cache(DataCache(self.logger)) database = BypassExecutionDatabase(trader_id=self.trader_id, logger=self.logger) self.exec_engine = ExecutionEngine( database=database, portfolio=portfolio, clock=self.clock, logger=self.logger, ) self.venue = Venue("SIM") self.client = ExecutionClient( venue=self.venue, account_id=self.account_id, engine=self.exec_engine, clock=self.clock, logger=self.logger, ) self.order_factory = OrderFactory( trader_id=TraderId("TESTER", "000"), strategy_id=StrategyId("S", "001"), clock=TestClock(), ) def test_connect_when_not_implemented_raises_exception(self): self.assertRaises(NotImplementedError, self.client.connect) def test_disconnect_when_not_implemented_raises_exception(self): self.assertRaises(NotImplementedError, self.client.disconnect) def test_reset_when_not_implemented_raises_exception(self): self.assertRaises(NotImplementedError, self.client.reset) def test_dispose_when_not_implemented_raises_exception(self): self.assertRaises(NotImplementedError, self.client.dispose) def test_submit_order_raises_exception(self): order = self.order_factory.limit( AUDUSD_SIM.symbol, OrderSide.SELL, Quantity(100000), Price("1.00000"), ) command = SubmitOrder( self.venue, self.trader_id, self.account_id, StrategyId("SCALPER", "001"), PositionId.null(), order, self.uuid_factory.generate(), self.clock.utc_now(), ) self.assertRaises(NotImplementedError, self.client.submit_order, command) def test_submit_bracket_order_raises_not_implemented_error(self): entry_order = self.order_factory.stop_market( AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(100000), Price("0.99995"), ) # Act bracket_order = self.order_factory.bracket( entry_order, Price("0.99990"), Price("1.00010"), ) command = SubmitBracketOrder( self.venue, self.trader_id, self.account_id, StrategyId("SCALPER", "001"), bracket_order, self.uuid_factory.generate(), self.clock.utc_now(), ) self.assertRaises(NotImplementedError, self.client.submit_bracket_order, command) def test_amend_order_raises_not_implemented_error(self): # Arrange # Act command = AmendOrder( self.venue, self.trader_id, self.account_id, ClientOrderId("O-123456789"), Quantity(120000), Price("1.00000"), self.uuid_factory.generate(), self.clock.utc_now(), ) # Assert self.assertRaises(NotImplementedError, self.client.amend_order, command) def test_cancel_order_raises_not_implemented_error(self): # Arrange # Act command = CancelOrder( self.venue, self.trader_id, self.account_id, ClientOrderId("O-123456789"), OrderId("001"), self.uuid_factory.generate(), self.clock.utc_now(), ) # Assert self.assertRaises(NotImplementedError, self.client.cancel_order, command) def test_handle_event_sends_to_execution_engine(self): # Arrange order = self.order_factory.market( AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(100000), ) fill = TestStubs.event_order_filled( order, AUDUSD_SIM, PositionId("P-123456"), StrategyId("S", "001"), Price("1.00001"), ) # Act self.client._handle_event_py(fill) # Accessing protected method # Assert self.assertEqual(1, self.exec_engine.event_count)
class AnalyzerTests(unittest.TestCase): def setUp(self): # Fixture Setup self.analyzer = PerformanceAnalyzer() self.order_factory = OrderFactory( trader_id=TraderId("TESTER", "000"), strategy_id=StrategyId("S", "001"), clock=TestClock(), ) def test_get_daily_returns_when_no_data_returns_empty_series(self): # Arrange # Act result = self.analyzer.get_daily_returns() # Assert self.assertTrue(result.empty) def test_get_realized_pnls_when_no_data_returns_empty_series(self): # Arrange # Act result = self.analyzer.get_realized_pnls() # Assert self.assertTrue(result.empty) def test_analyzer_tracks_daily_returns(self): # Arrange t1 = datetime(year=2010, month=1, day=1) t2 = datetime(year=2010, month=1, day=2) t3 = datetime(year=2010, month=1, day=3) t4 = datetime(year=2010, month=1, day=4) t5 = datetime(year=2010, month=1, day=5) t6 = datetime(year=2010, month=1, day=6) t7 = datetime(year=2010, month=1, day=7) t8 = datetime(year=2010, month=1, day=8) t9 = datetime(year=2010, month=1, day=9) t10 = datetime(year=2010, month=1, day=10) # Act self.analyzer.add_return(t1, 0.05) self.analyzer.add_return(t2, -0.10) self.analyzer.add_return(t3, 0.10) self.analyzer.add_return(t4, -0.21) self.analyzer.add_return(t5, 0.22) self.analyzer.add_return(t6, -0.23) self.analyzer.add_return(t7, 0.24) self.analyzer.add_return(t8, -0.25) self.analyzer.add_return(t9, 0.26) self.analyzer.add_return(t10, -0.10) self.analyzer.add_return(t10, -0.10) result = self.analyzer.get_daily_returns() # Assert self.assertEqual(10, len(result)) self.assertEqual(-0.12, sum(result)) self.assertEqual(-0.20, result.iloc[9]) def test_get_realized_pnls_when_all_flat_positions_returns_expected_series( self): # Arrange order1 = self.order_factory.market( AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(100000), ) order2 = self.order_factory.market( AUDUSD_SIM.symbol, OrderSide.SELL, Quantity(100000), ) order3 = self.order_factory.market( AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(100000), ) order4 = self.order_factory.market( AUDUSD_SIM.symbol, OrderSide.SELL, Quantity(100000), ) fill1 = TestStubs.event_order_filled( order1, instrument=AUDUSD_SIM, position_id=PositionId("P-1"), strategy_id=StrategyId("S", "001"), fill_price=Price("1.00000"), ) fill2 = TestStubs.event_order_filled( order2, instrument=AUDUSD_SIM, position_id=PositionId("P-1"), strategy_id=StrategyId("S", "001"), fill_price=Price("1.00010"), ) fill3 = TestStubs.event_order_filled( order3, instrument=AUDUSD_SIM, position_id=PositionId("P-2"), strategy_id=StrategyId("S", "001"), fill_price=Price("1.00000"), ) fill4 = TestStubs.event_order_filled( order4, instrument=AUDUSD_SIM, position_id=PositionId("P-2"), strategy_id=StrategyId("S", "001"), fill_price=Price("1.00020"), ) position1 = Position(fill1) position1.apply(fill2) position2 = Position(fill3) position2.apply(fill4) self.analyzer.add_positions([position1, position2]) # Act result = self.analyzer.get_realized_pnls() # Assert self.assertEqual(2, len(result)) self.assertEqual(6.0, result['P-1']) self.assertEqual(16.0, result['P-2'])
class TestOrderUnpackerSerializer: def setup(self): # Fixture Setup self.unpacker = OrderUnpacker() self.order_factory = OrderFactory( trader_id=TraderId("TESTER-000"), strategy_id=StrategyId("S-001"), clock=TestClock(), ) def test_pack_and_unpack_market_orders(self): # Arrange order = self.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity(100000, precision=0), ) # Act packed = OrderInitialized.to_dict(order.last_event) unpacked = self.unpacker.unpack(packed) # Assert assert unpacked == order def test_pack_and_unpack_limit_orders(self): # Arrange order = self.order_factory.limit( AUDUSD_SIM.id, OrderSide.BUY, Quantity(100000, precision=0), Price(1.00000, precision=5), TimeInForce.DAY, ) # Act packed = OrderInitialized.to_dict(order.last_event) unpacked = self.unpacker.unpack(packed) # Assert assert unpacked == order def test_pack_and_unpack_limit_orders_with_expire_time(self): # Arrange order = LimitOrder( ClientOrderId("O-123456"), StrategyId("S-001"), AUDUSD_SIM.id, OrderSide.BUY, Quantity(100000, precision=0), price=Price(1.00000, precision=5), time_in_force=TimeInForce.GTD, expire_time=UNIX_EPOCH, init_id=uuid4(), timestamp_ns=0, ) # Act packed = OrderInitialized.to_dict(order.last_event) unpacked = self.unpacker.unpack(packed) # Assert assert unpacked == order def test_pack_and_unpack_stop_market_orders_with_expire_time(self): # Arrange order = StopMarketOrder( ClientOrderId("O-123456"), StrategyId("S-001"), AUDUSD_SIM.id, OrderSide.BUY, Quantity(100000, precision=0), price=Price(1.00000, precision=5), time_in_force=TimeInForce.GTD, expire_time=UNIX_EPOCH, init_id=uuid4(), timestamp_ns=0, ) # Act packed = OrderInitialized.to_dict(order.last_event) unpacked = self.unpacker.unpack(packed) # Assert assert unpacked == order def test_pack_and_unpack_stop_limit_orders(self): # Arrange order = StopLimitOrder( ClientOrderId("O-123456"), StrategyId("S-001"), AUDUSD_SIM.id, OrderSide.BUY, Quantity(100000, precision=0), price=Price(1.00000, precision=5), trigger=Price(1.00010, precision=5), time_in_force=TimeInForce.GTC, expire_time=None, init_id=uuid4(), timestamp_ns=0, ) # Act packed = OrderInitialized.to_dict(order.last_event) unpacked = self.unpacker.unpack(packed) # Assert assert unpacked == order def test_pack_and_unpack_stop_limit_orders_with_expire_time(self): # Arrange order = StopLimitOrder( ClientOrderId("O-123456"), StrategyId("S-001"), AUDUSD_SIM.id, OrderSide.BUY, Quantity(100000, precision=0), price=Price(1.00000, precision=5), trigger=Price(1.00010, precision=5), time_in_force=TimeInForce.GTD, expire_time=UNIX_EPOCH, init_id=uuid4(), timestamp_ns=0, ) # Act packed = OrderInitialized.to_dict(order.last_event) unpacked = self.unpacker.unpack(packed) # Assert assert unpacked == order
class OrderTests(unittest.TestCase): def setUp(self): # Fixture Setup self.account_id = TestStubs.account_id() self.order_factory = OrderFactory( trader_id=TraderId("TESTER", "000"), strategy_id=StrategyId("S", "001"), clock=TestClock(), ) def test_opposite_side_given_undefined_raises_value_error(self): # Arrange # Act # Assert self.assertRaises(ValueError, Order.opposite_side, OrderSide.UNDEFINED) def test_flatten_side_given_undefined_or_flat_raises_value_error(self): # Arrange # Act # Assert self.assertRaises(ValueError, Order.flatten_side, PositionSide.UNDEFINED) self.assertRaises(ValueError, Order.flatten_side, PositionSide.FLAT) @parameterized.expand([ [OrderSide.BUY, OrderSide.SELL], [OrderSide.SELL, OrderSide.BUY], ]) def test_opposite_side_returns_expected_sides(self, side, expected): # Arrange # Act result = Order.opposite_side(side) # Assert self.assertEqual(expected, result) @parameterized.expand([ [PositionSide.LONG, OrderSide.SELL], [PositionSide.SHORT, OrderSide.BUY], ]) def test_flatten_side_returns_expected_sides(self, side, expected): # Arrange # Act result = Order.flatten_side(side) # Assert self.assertEqual(expected, result) def test_market_order_with_quantity_zero_raises_exception(self): # Arrange # Act self.assertRaises( ValueError, MarketOrder, ClientOrderId("O-123456"), StrategyId("S", "001"), AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(), TimeInForce.DAY, uuid4(), UNIX_EPOCH, ) def test_market_order_with_invalid_tif_raises_exception(self): # Arrange # Act self.assertRaises( ValueError, MarketOrder, ClientOrderId("O-123456"), StrategyId("S", "001"), AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(100), TimeInForce.GTD, uuid4(), UNIX_EPOCH, ) def test_stop_order_with_gtd_and_expire_time_none_raises_exception(self): # Arrange # Act self.assertRaises( TypeError, StopMarketOrder, ClientOrderId("O-123456"), StrategyId("S", "001"), AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(100000), price=Price("1.00000"), init_id=uuid4(), timestamp=UNIX_EPOCH, time_in_force=TimeInForce.GTD, expire_time=None, ) def test_reset_order_factory(self): # Arrange self.order_factory.limit( AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(100000), Price("1.00000"), ) # Act self.order_factory.reset() order2 = self.order_factory.limit( AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(100000), Price("1.00000"), ) self.assertEqual(ClientOrderId("O-19700101-000000-000-001-1"), order2.cl_ord_id) def test_limit_order_can_create_expected_decimal_price(self): # Arrange # Act order1 = self.order_factory.limit( AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(100000), Price("1.00000"), ) order2 = self.order_factory.limit( AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(100000), Price("1.00000"), ) order3 = self.order_factory.limit( AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(100000), Price("1.00000"), ) order4 = self.order_factory.limit( AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(100000), Price("1.00001"), ) # Assert self.assertEqual(Price("1.00000"), order1.price) self.assertEqual(Price("1.00000"), order2.price) self.assertEqual(Price("1.00000"), order3.price) self.assertEqual(Price("1.00001"), order4.price) def test_initialize_buy_market_order(self): # Arrange # Act order = self.order_factory.market( AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(100000), ) # Assert self.assertEqual(OrderType.MARKET, order.type) self.assertEqual(OrderState.INITIALIZED, order.state) self.assertEqual(1, order.event_count) self.assertTrue(isinstance(order.last_event, OrderInitialized)) self.assertFalse(order.is_working) self.assertFalse(order.is_completed) self.assertTrue(order.is_buy) self.assertFalse(order.is_sell) self.assertFalse(order.is_passive) self.assertTrue(order.is_aggressive) self.assertEqual(None, order.filled_timestamp) self.assertEqual(UNIX_EPOCH, order.last_event.timestamp) self.assertEqual(OrderInitialized, type(order.init_event)) self.assertTrue(order == order) self.assertFalse(order != order) def test_initialize_sell_market_order(self): # Arrange # Act order = self.order_factory.market( AUDUSD_SIM.symbol, OrderSide.SELL, Quantity(100000), ) # Assert self.assertEqual(OrderType.MARKET, order.type) self.assertEqual(OrderState.INITIALIZED, order.state) self.assertEqual(1, order.event_count) self.assertTrue(isinstance(order.last_event, OrderInitialized)) self.assertEqual(1, len(order.events)) self.assertTrue(order.is_active) self.assertFalse(order.is_working) self.assertFalse(order.is_completed) self.assertFalse(order.is_buy) self.assertTrue(order.is_sell) self.assertEqual(None, order.filled_timestamp) self.assertEqual(OrderInitialized, type(order.init_event)) def test_order_equality(self): # Arrange # Act order = self.order_factory.market( AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(100000), ) # Assert self.assertTrue(order == order) self.assertFalse(order != order) def test_order_str_and_repr(self): # Arrange # Act order = self.order_factory.market( AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(100000), ) # Assert self.assertEqual("MarketOrder(cl_ord_id=O-19700101-000000-000-001-1, id=NULL, state=INITIALIZED, BUY 100,000 AUD/USD.SIM MARKET GTC)", str(order)) # noqa self.assertEqual("MarketOrder(cl_ord_id=O-19700101-000000-000-001-1, id=NULL, state=INITIALIZED, BUY 100,000 AUD/USD.SIM MARKET GTC)", repr(order)) # noqa def test_initialize_limit_order(self): # Arrange # Act order = self.order_factory.limit( AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(100000), Price("1.00000"), ) # Assert self.assertEqual(OrderType.LIMIT, order.type) self.assertEqual(OrderState.INITIALIZED, order.state) self.assertEqual(TimeInForce.GTC, order.time_in_force) self.assertTrue(order.is_passive) self.assertTrue(order.is_active) self.assertFalse(order.is_aggressive) self.assertFalse(order.is_completed) self.assertEqual(OrderInitialized, type(order.init_event)) def test_initialize_limit_order_with_expire_time(self): # Arrange # Act order = self.order_factory.limit( AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(100000), Price("1.00000"), TimeInForce.GTD, UNIX_EPOCH, ) # Assert self.assertEqual(AUDUSD_SIM.symbol, order.symbol) self.assertEqual(OrderType.LIMIT, order.type) self.assertEqual(Price("1.00000"), order.price) self.assertEqual(OrderState.INITIALIZED, order.state) self.assertEqual(TimeInForce.GTD, order.time_in_force) self.assertEqual(UNIX_EPOCH, order.expire_time) self.assertFalse(order.is_completed) self.assertEqual(OrderInitialized, type(order.init_event)) def test_initialize_stop_order(self): # Arrange # Act order = self.order_factory.stop_market( AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(100000), Price("1.00000"), ) # Assert self.assertEqual(OrderType.STOP_MARKET, order.type) self.assertEqual(OrderState.INITIALIZED, order.state) self.assertEqual(TimeInForce.GTC, order.time_in_force) self.assertTrue(order.is_passive) self.assertFalse(order.is_aggressive) self.assertFalse(order.is_completed) self.assertEqual(OrderInitialized, type(order.init_event)) def test_bracket_order_equality(self): # Arrange entry1 = self.order_factory.market( AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(100000), ) entry2 = self.order_factory.market( AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(100000), ) bracket_order1 = self.order_factory.bracket(entry1, Price("1.00000")) bracket_order2 = self.order_factory.bracket(entry2, Price("1.00000")) # Act # Assert self.assertTrue(bracket_order1 == bracket_order1) self.assertTrue(bracket_order1 != bracket_order2) def test_initialize_bracket_order_market_with_no_take_profit(self): # Arrange entry_order = self.order_factory.market( AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(100000), ) # Act bracket_order = self.order_factory.bracket(entry_order, Price("0.99990")) # Assert self.assertEqual(AUDUSD_SIM.symbol, bracket_order.stop_loss.symbol) self.assertFalse(bracket_order.take_profit is not None) self.assertEqual(ClientOrderId("O-19700101-000000-000-001-1"), bracket_order.entry.cl_ord_id) self.assertEqual(ClientOrderId("O-19700101-000000-000-001-2"), bracket_order.stop_loss.cl_ord_id) self.assertEqual(OrderSide.SELL, bracket_order.stop_loss.side) self.assertEqual(Quantity(100000), bracket_order.entry.quantity) self.assertEqual(Quantity(100000), bracket_order.stop_loss.quantity) self.assertEqual(Price("0.99990"), bracket_order.stop_loss.price) self.assertEqual(TimeInForce.GTC, bracket_order.stop_loss.time_in_force) self.assertEqual(None, bracket_order.stop_loss.expire_time) self.assertEqual(BracketOrderId("BO-19700101-000000-000-001-1"), bracket_order.id) self.assertEqual(UNIX_EPOCH, bracket_order.timestamp) def test_initialize_bracket_order_stop_with_take_profit(self): # Arrange entry_order = self.order_factory.stop_market( AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(100000), Price("0.99995"), ) # Act bracket_order = self.order_factory.bracket( entry_order, Price("0.99990"), Price("1.00010"), ) # Assert self.assertEqual(AUDUSD_SIM.symbol, bracket_order.stop_loss.symbol) self.assertTrue(bracket_order.take_profit is not None) self.assertEqual(AUDUSD_SIM.symbol, bracket_order.take_profit.symbol) self.assertEqual(ClientOrderId("O-19700101-000000-000-001-1"), bracket_order.entry.cl_ord_id) self.assertEqual(ClientOrderId("O-19700101-000000-000-001-2"), bracket_order.stop_loss.cl_ord_id) self.assertEqual(ClientOrderId("O-19700101-000000-000-001-3"), bracket_order.take_profit.cl_ord_id) self.assertEqual(OrderSide.SELL, bracket_order.stop_loss.side) self.assertEqual(OrderSide.SELL, bracket_order.take_profit.side) self.assertEqual(Quantity(100000), bracket_order.stop_loss.quantity) self.assertEqual(Quantity(100000), bracket_order.take_profit.quantity) self.assertEqual(Price("0.99990"), bracket_order.stop_loss.price) self.assertEqual(Price("1.00010"), bracket_order.take_profit.price) self.assertEqual(TimeInForce.GTC, bracket_order.stop_loss.time_in_force) self.assertEqual(TimeInForce.GTC, bracket_order.take_profit.time_in_force) self.assertEqual(None, bracket_order.entry.expire_time) self.assertEqual(None, bracket_order.stop_loss.expire_time) self.assertEqual(None, bracket_order.take_profit.expire_time) self.assertEqual(BracketOrderId("BO-19700101-000000-000-001-1"), bracket_order.id) self.assertEqual(UNIX_EPOCH, bracket_order.timestamp) def test_bracket_order_str_and_repr(self): # Arrange # Act entry_order = self.order_factory.market( AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(100000), ) bracket_order = self.order_factory.bracket( entry_order, Price("0.99990"), Price("1.00010"), ) # Assert self.assertEqual("BracketOrder(id=BO-19700101-000000-000-001-1, EntryMarketOrder(cl_ord_id=O-19700101-000000-000-001-1, id=NULL, state=INITIALIZED, BUY 100,000 AUD/USD.SIM MARKET GTC), SL=0.99990, TP=1.00010)", str(bracket_order)) # noqa self.assertEqual("BracketOrder(id=BO-19700101-000000-000-001-1, EntryMarketOrder(cl_ord_id=O-19700101-000000-000-001-1, id=NULL, state=INITIALIZED, BUY 100,000 AUD/USD.SIM MARKET GTC), SL=0.99990, TP=1.00010)", repr(bracket_order)) # noqa def test_apply_order_invalid_event(self): # Arrange order = self.order_factory.market( AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(100000), ) invalid = OrderInvalid( order.cl_ord_id, "SOME_REASON", uuid4(), UNIX_EPOCH, ) # Act order.apply(invalid) # Assert self.assertEqual(OrderState.INVALID, order.state) self.assertEqual(2, order.event_count) self.assertEqual(invalid, order.last_event) self.assertFalse(order.is_active) self.assertTrue(order.is_completed) def test_apply_order_denied_event(self): # Arrange order = self.order_factory.market( AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(100000), ) denied = OrderDenied( order.cl_ord_id, "SOME_REASON", uuid4(), UNIX_EPOCH, ) # Act order.apply(denied) # Assert self.assertEqual(OrderState.DENIED, order.state) self.assertEqual(2, order.event_count) self.assertEqual(denied, order.last_event) self.assertFalse(order.is_active) self.assertTrue(order.is_completed) def test_apply_order_submitted_event(self): # Arrange order = self.order_factory.market( AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(100000), ) submitted = TestStubs.event_order_submitted(order) # Act order.apply(submitted) # Assert self.assertEqual(OrderState.SUBMITTED, order.state) self.assertEqual(2, order.event_count) self.assertEqual(submitted, order.last_event) self.assertTrue(order.is_active) self.assertFalse(order.is_working) self.assertFalse(order.is_completed) def test_apply_order_accepted_event(self): # Arrange order = self.order_factory.market( AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(100000), ) order.apply(TestStubs.event_order_submitted(order)) # Act order.apply(TestStubs.event_order_accepted(order)) # Assert self.assertEqual(OrderState.ACCEPTED, order.state) self.assertTrue(order.is_active) self.assertTrue(order.is_working) self.assertFalse(order.is_completed) def test_apply_order_rejected_event(self): # Arrange order = self.order_factory.market( AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(100000), ) order.apply(TestStubs.event_order_submitted(order)) # Act order.apply(TestStubs.event_order_rejected(order)) # Assert self.assertEqual(OrderState.REJECTED, order.state) self.assertFalse(order.is_active) self.assertFalse(order.is_working) self.assertTrue(order.is_completed) def test_apply_order_expired_event(self): # Arrange order = self.order_factory.stop_market( AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(100000), Price("0.99990"), TimeInForce.GTD, UNIX_EPOCH, ) order.apply(TestStubs.event_order_submitted(order)) order.apply(TestStubs.event_order_accepted(order)) # Act order.apply(TestStubs.event_order_expired(order)) # Assert self.assertEqual(OrderState.EXPIRED, order.state) self.assertFalse(order.is_active) self.assertFalse(order.is_working) self.assertTrue(order.is_completed) def test_apply_order_cancelled_event(self): # Arrange order = self.order_factory.market( AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(100000), ) order.apply(TestStubs.event_order_submitted(order)) order.apply(TestStubs.event_order_accepted(order)) # Act order.apply(TestStubs.event_order_cancelled(order)) # Assert self.assertEqual(OrderState.CANCELLED, order.state) self.assertFalse(order.is_active) self.assertFalse(order.is_working) self.assertTrue(order.is_completed) def test_apply_order_amended_event_to_stop_order(self): # Arrange order = self.order_factory.stop_market( AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(100000), Price("1.00000"), ) order.apply(TestStubs.event_order_submitted(order)) order.apply(TestStubs.event_order_accepted(order)) amended = OrderAmended( self.account_id, order.cl_ord_id, OrderId("1"), Quantity(120000), Price("1.00001"), UNIX_EPOCH, uuid4(), UNIX_EPOCH, ) # Act order.apply(amended) # Assert self.assertEqual(OrderState.ACCEPTED, order.state) self.assertEqual(OrderId("1"), order.id) self.assertEqual(Quantity(120000), order.quantity) self.assertEqual(Price("1.00001"), order.price) self.assertTrue(order.is_active) self.assertTrue(order.is_working) self.assertFalse(order.is_completed) self.assertEqual(4, order.event_count) def test_apply_order_filled_event_to_order_without_accepted(self): # Arrange order = self.order_factory.market( AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(100000), ) order.apply(TestStubs.event_order_submitted(order)) order.apply(TestStubs.event_order_accepted(order)) filled = TestStubs.event_order_filled( order, instrument=AUDUSD_SIM, position_id=PositionId("P-123456"), strategy_id=StrategyId("S", "001"), fill_price=Price("1.00001"), ) # Act order.apply(filled) # Assert self.assertEqual(OrderState.FILLED, order.state) self.assertEqual(Quantity(100000), order.filled_qty) self.assertEqual(Decimal("1.00001"), order.avg_price) self.assertEqual(1, len(order.execution_ids)) self.assertFalse(order.is_active) self.assertFalse(order.is_working) self.assertTrue(order.is_completed) self.assertEqual(UNIX_EPOCH, order.filled_timestamp) def test_apply_order_filled_event_to_market_order(self): # Arrange order = self.order_factory.market( AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(100000), ) order.apply(TestStubs.event_order_submitted(order)) order.apply(TestStubs.event_order_accepted(order)) filled = TestStubs.event_order_filled( order, instrument=AUDUSD_SIM, position_id=PositionId("P-123456"), strategy_id=StrategyId("S", "001"), fill_price=Price("1.00001"), ) # Act order.apply(filled) # Assert self.assertEqual(OrderState.FILLED, order.state) self.assertEqual(Quantity(100000), order.filled_qty) self.assertEqual(Decimal("1.00001"), order.avg_price) self.assertEqual(1, len(order.execution_ids)) self.assertFalse(order.is_active) self.assertFalse(order.is_working) self.assertTrue(order.is_completed) self.assertEqual(UNIX_EPOCH, order.filled_timestamp) def test_apply_partial_fill_events_to_market_order_results_in_partially_filled(self): # Arrange order = self.order_factory.market( AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(100000), ) order.apply(TestStubs.event_order_submitted(order)) order.apply(TestStubs.event_order_accepted(order)) fill1 = TestStubs.event_order_filled( order, instrument=AUDUSD_SIM, position_id=PositionId("P-123456"), strategy_id=StrategyId("S", "001"), fill_price=Price("1.00001"), fill_qty=Quantity(20000), ) fill2 = TestStubs.event_order_filled( order, instrument=AUDUSD_SIM, position_id=PositionId("P-123456"), strategy_id=StrategyId("S", "001"), fill_price=Price("1.00002"), fill_qty=Quantity(40000), ) # Act order.apply(fill1) order.apply(fill2) # Assert self.assertEqual(OrderState.PARTIALLY_FILLED, order.state) self.assertEqual(Quantity(60000), order.filled_qty) self.assertEqual(Decimal("1.000014"), order.avg_price) self.assertEqual(2, len(order.execution_ids)) self.assertTrue(order.is_active) self.assertTrue(order.is_working) self.assertFalse(order.is_completed) self.assertEqual(UNIX_EPOCH, order.filled_timestamp) def test_apply_filled_events_to_market_order_results_in_filled(self): # Arrange order = self.order_factory.market( AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(100000), ) order.apply(TestStubs.event_order_submitted(order)) order.apply(TestStubs.event_order_accepted(order)) fill1 = TestStubs.event_order_filled( order, instrument=AUDUSD_SIM, position_id=PositionId("P-123456"), strategy_id=StrategyId("S", "001"), fill_price=Price("1.00001"), fill_qty=Quantity(20000), ) fill2 = TestStubs.event_order_filled( order, instrument=AUDUSD_SIM, position_id=PositionId("P-123456"), strategy_id=StrategyId("S", "001"), fill_price=Price("1.00002"), fill_qty=Quantity(40000), ) fill3 = TestStubs.event_order_filled( order, instrument=AUDUSD_SIM, position_id=PositionId("P-123456"), strategy_id=StrategyId("S", "001"), fill_price=Price("1.00003"), fill_qty=Quantity(40000), ) # Act order.apply(fill1) order.apply(fill2) order.apply(fill3) # Assert self.assertEqual(OrderState.FILLED, order.state) self.assertEqual(Quantity(100000), order.filled_qty) self.assertEqual(Decimal("1.000018571428571428571428571"), order.avg_price) self.assertEqual(3, len(order.execution_ids)) self.assertFalse(order.is_active) self.assertFalse(order.is_working) self.assertTrue(order.is_completed) self.assertEqual(UNIX_EPOCH, order.filled_timestamp) def test_apply_order_filled_event_to_buy_limit_order(self): # Arrange order = self.order_factory.limit( AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(100000), Price("1.00000"), ) order.apply(TestStubs.event_order_submitted(order)) order.apply(TestStubs.event_order_accepted(order)) filled = OrderFilled( self.account_id, order.cl_ord_id, OrderId("1"), ExecutionId("E-1"), PositionId("P-1"), StrategyId.null(), order.symbol, order.side, order.quantity, order.quantity, Quantity(), Price("1.00001"), AUDUSD_SIM.quote_currency, AUDUSD_SIM.is_inverse, Money(0, USD), LiquiditySide.MAKER, UNIX_EPOCH, uuid4(), UNIX_EPOCH, ) # Act order.apply(filled) # Assert self.assertEqual(OrderState.FILLED, order.state) self.assertEqual(Quantity(100000), order.filled_qty) self.assertEqual(Price("1.00000"), order.price) self.assertEqual(Decimal("1.00001"), order.avg_price) self.assertEqual(Decimal("0.00001"), order.slippage) self.assertFalse(order.is_active) self.assertFalse(order.is_working) self.assertTrue(order.is_completed) self.assertEqual(UNIX_EPOCH, order.filled_timestamp) def test_apply_order_partially_filled_event_to_buy_limit_order(self): # Arrange order = self.order_factory.limit( AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(100000), Price("1.00000"), ) order.apply(TestStubs.event_order_submitted(order)) order.apply(TestStubs.event_order_accepted(order)) partially = OrderFilled( self.account_id, order.cl_ord_id, OrderId("1"), ExecutionId("E-1"), PositionId("P-1"), StrategyId.null(), order.symbol, order.side, Quantity(50000), Quantity(50000), Quantity(50000), Price("0.999999"), AUDUSD_SIM.quote_currency, AUDUSD_SIM.is_inverse, Money(0, USD), LiquiditySide.MAKER, UNIX_EPOCH, uuid4(), UNIX_EPOCH, ) # Act order.apply(partially) # Assert self.assertEqual(OrderState.PARTIALLY_FILLED, order.state) self.assertEqual(Quantity(50000), order.filled_qty) self.assertEqual(Price("1.00000"), order.price) self.assertEqual(Decimal("0.999999"), order.avg_price) self.assertEqual(Decimal("-0.000001"), order.slippage) self.assertTrue(order.is_active) self.assertTrue(order.is_working) self.assertFalse(order.is_completed) self.assertEqual(UNIX_EPOCH, order.filled_timestamp)
class PositionTests(unittest.TestCase): def setUp(self): # Fixture Setup self.account_id = TestStubs.account_id() self.order_factory = OrderFactory( trader_id=TraderId("TESTER-000"), strategy_id=StrategyId("S-001"), clock=TestClock(), ) def test_side_from_order_side_given_invalid_value_returns_none(self): # Arrange # Act self.assertRaises(ValueError, Position.side_from_order_side, 0) @parameterized.expand([ [OrderSide.BUY, PositionSide.LONG], [OrderSide.SELL, PositionSide.SHORT], ]) def test_side_from_order_side_given_valid_sides_returns_expected_side( self, order_side, expected): # Arrange # Act position_side = Position.side_from_order_side(order_side) # Assert self.assertEqual(expected, position_side) def test_position_filled_with_buy_order_returns_expected_attributes(self): # Arrange order = self.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) fill = TestStubs.event_order_filled( order, instrument=AUDUSD_SIM, position_id=PositionId("P-123456"), strategy_id=StrategyId("S-001"), last_px=Price.from_str("1.00001"), ) last = Price.from_str("1.00050") # Act position = Position(instrument=AUDUSD_SIM, fill=fill) # Assert assert position.symbol == AUDUSD_SIM.id.symbol assert position.venue == AUDUSD_SIM.id.venue assert not position.is_opposite_side(fill.order_side) self.assertFalse(position != position) # Equality operator test self.assertEqual(ClientOrderId("O-19700101-000000-000-001-1"), position.from_order) self.assertEqual(Quantity.from_int(100000), position.quantity) self.assertEqual(Quantity.from_int(100000), position.peak_qty) self.assertEqual(OrderSide.BUY, position.entry) self.assertEqual(PositionSide.LONG, position.side) self.assertEqual(0, position.opened_timestamp_ns) self.assertEqual(0, position.open_duration_ns) self.assertEqual(Decimal("1.00001"), position.avg_px_open) self.assertEqual(1, position.event_count) self.assertEqual([order.client_order_id], position.client_order_ids) self.assertEqual([VenueOrderId("1")], position.venue_order_ids) self.assertEqual([ExecutionId("E-19700101-000000-000-001-1")], position.execution_ids) self.assertEqual(ExecutionId("E-19700101-000000-000-001-1"), position.last_execution_id) self.assertEqual(PositionId("P-123456"), position.id) self.assertEqual(1, len(position.events)) self.assertTrue(position.is_long) self.assertFalse(position.is_short) self.assertTrue(position.is_open) self.assertFalse(position.is_closed) self.assertEqual(0, position.realized_points) self.assertEqual(0, position.realized_return) self.assertEqual(Money(-2.00, USD), position.realized_pnl) self.assertEqual(Money(49.00, USD), position.unrealized_pnl(last)) self.assertEqual(Money(47.00, USD), position.total_pnl(last)) self.assertEqual([Money(2.00, USD)], position.commissions()) self.assertEqual("Position(LONG 100_000 AUD/USD.SIM, id=P-123456)", repr(position)) def test_position_filled_with_sell_order_returns_expected_attributes(self): # Arrange order = self.order_factory.market( AUDUSD_SIM.id, OrderSide.SELL, Quantity.from_int(100000), ) fill = TestStubs.event_order_filled( order, instrument=AUDUSD_SIM, position_id=PositionId("P-123456"), strategy_id=StrategyId("S-001"), last_px=Price.from_str("1.00001"), ) last = Price.from_str("1.00050") # Act position = Position(instrument=AUDUSD_SIM, fill=fill) # Assert self.assertEqual(Quantity.from_int(100000), position.quantity) self.assertEqual(Quantity.from_int(100000), position.peak_qty) self.assertEqual(PositionSide.SHORT, position.side) self.assertEqual(0, position.opened_timestamp_ns) self.assertEqual(Decimal("1.00001"), position.avg_px_open) self.assertEqual(1, position.event_count) self.assertEqual([ExecutionId("E-19700101-000000-000-001-1")], position.execution_ids) self.assertEqual(ExecutionId("E-19700101-000000-000-001-1"), position.last_execution_id) self.assertEqual(PositionId("P-123456"), position.id) self.assertFalse(position.is_long) self.assertTrue(position.is_short) self.assertTrue(position.is_open) self.assertFalse(position.is_closed) self.assertEqual(0, position.realized_points) self.assertEqual(0, position.realized_return) self.assertEqual(Money(-2.00, USD), position.realized_pnl) self.assertEqual(Money(-49.00, USD), position.unrealized_pnl(last)) self.assertEqual(Money(-51.00, USD), position.total_pnl(last)) self.assertEqual([Money(2.00, USD)], position.commissions()) self.assertEqual("Position(SHORT 100_000 AUD/USD.SIM, id=P-123456)", repr(position)) def test_position_partial_fills_with_buy_order_returns_expected_attributes( self): # Arrange order = self.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) fill = TestStubs.event_order_filled( order, instrument=AUDUSD_SIM, position_id=PositionId("P-123456"), strategy_id=StrategyId("S-001"), last_px=Price.from_str("1.00001"), last_qty=Quantity.from_int(50000), ) last = Price.from_str("1.00048") position = Position(instrument=AUDUSD_SIM, fill=fill) # Act # Assert self.assertEqual(Quantity.from_int(50000), position.quantity) self.assertEqual(Quantity.from_int(50000), position.peak_qty) self.assertEqual(PositionSide.LONG, position.side) self.assertEqual(0, position.opened_timestamp_ns) self.assertEqual(Decimal("1.00001"), position.avg_px_open) self.assertEqual(1, position.event_count) self.assertTrue(position.is_long) self.assertFalse(position.is_short) self.assertTrue(position.is_open) self.assertFalse(position.is_closed) self.assertEqual(0, position.realized_points) self.assertEqual(0, position.realized_return) self.assertEqual(Money(-2.00, USD), position.realized_pnl) self.assertEqual(Money(23.50, USD), position.unrealized_pnl(last)) self.assertEqual(Money(21.50, USD), position.total_pnl(last)) self.assertEqual([Money(2.00, USD)], position.commissions()) self.assertEqual("Position(LONG 50_000 AUD/USD.SIM, id=P-123456)", repr(position)) def test_position_partial_fills_with_sell_order_returns_expected_attributes( self): # Arrange order = self.order_factory.market( AUDUSD_SIM.id, OrderSide.SELL, Quantity.from_int(100000), ) fill1 = TestStubs.event_order_filled( order, instrument=AUDUSD_SIM, execution_id=ExecutionId("1"), position_id=PositionId("P-123456"), strategy_id=StrategyId("S-001"), last_px=Price.from_str("1.00001"), last_qty=Quantity.from_int(50000), ) fill2 = TestStubs.event_order_filled( order, instrument=AUDUSD_SIM, execution_id=ExecutionId("2"), position_id=PositionId("P-123456"), strategy_id=StrategyId("S-001"), last_px=Price.from_str("1.00002"), last_qty=Quantity.from_int(50000), ) position = Position(instrument=AUDUSD_SIM, fill=fill1) last = Price.from_str("1.00050") # Act position.apply(fill2) # Assert self.assertEqual(Quantity.from_int(100000), position.quantity) self.assertEqual(PositionSide.SHORT, position.side) self.assertEqual(0, position.opened_timestamp_ns) self.assertEqual(Decimal("1.000015"), position.avg_px_open) self.assertEqual(2, position.event_count) self.assertFalse(position.is_long) self.assertTrue(position.is_short) self.assertTrue(position.is_open) self.assertFalse(position.is_closed) self.assertEqual(0, position.realized_points) self.assertEqual(0, position.realized_return) self.assertEqual(Money(-4.00, USD), position.realized_pnl) self.assertEqual(Money(-48.50, USD), position.unrealized_pnl(last)) self.assertEqual(Money(-52.50, USD), position.total_pnl(last)) self.assertEqual([Money(4.00, USD)], position.commissions()) self.assertEqual("Position(SHORT 100_000 AUD/USD.SIM, id=P-123456)", repr(position)) def test_position_filled_with_buy_order_then_sell_order_returns_expected_attributes( self, ): # Arrange order = self.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(150000), ) fill1 = TestStubs.event_order_filled( order, instrument=AUDUSD_SIM, position_id=PositionId("P-123456"), strategy_id=StrategyId("S-001"), last_px=Price.from_str("1.00001"), execution_ns=1_000_000_000, ) position = Position(instrument=AUDUSD_SIM, fill=fill1) fill2 = OrderFilled( self.account_id, order.client_order_id, VenueOrderId("2"), ExecutionId("E2"), PositionId("T123456"), StrategyId("S-001"), order.instrument_id, OrderSide.SELL, order.quantity, Price.from_str("1.00011"), AUDUSD_SIM.quote_currency, Money(0, USD), LiquiditySide.TAKER, 2_000_000_000, uuid4(), 0, ) last = Price.from_str("1.00050") # Act position.apply(fill2) # Assert assert position.is_opposite_side(fill2.order_side) self.assertEqual(Quantity.zero(), position.quantity) self.assertEqual(PositionSide.FLAT, position.side) self.assertEqual(1_000_000_000, position.opened_timestamp_ns) self.assertEqual(1_000_000_000, position.open_duration_ns) self.assertEqual(Decimal("1.00001"), position.avg_px_open) self.assertEqual(2, position.event_count) self.assertEqual(2_000_000_000, position.closed_timestamp_ns) self.assertEqual(Decimal("1.00011"), position.avg_px_close) self.assertFalse(position.is_long) self.assertFalse(position.is_short) self.assertFalse(position.is_open) self.assertTrue(position.is_closed) self.assertEqual(Decimal("0.00010"), position.realized_points) self.assertEqual(Decimal("0.00009999900000999990000099999000"), position.realized_return) self.assertEqual(Money(12.00, USD), position.realized_pnl) self.assertEqual(Money(0, USD), position.unrealized_pnl(last)) self.assertEqual(Money(12.00, USD), position.total_pnl(last)) self.assertEqual([Money(3.00, USD)], position.commissions()) self.assertEqual("Position(FLAT AUD/USD.SIM, id=P-123456)", repr(position)) def test_position_filled_with_sell_order_then_buy_order_returns_expected_attributes( self, ): # Arrange order1 = self.order_factory.market( AUDUSD_SIM.id, OrderSide.SELL, Quantity.from_int(100000), ) order2 = self.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) fill1 = TestStubs.event_order_filled( order1, instrument=AUDUSD_SIM, position_id=PositionId("P-19700101-000000-000-001-1"), ) position = Position(instrument=AUDUSD_SIM, fill=fill1) fill2 = TestStubs.event_order_filled( order2, instrument=AUDUSD_SIM, execution_id=ExecutionId("1"), position_id=PositionId("P-19700101-000000-000-001-1"), strategy_id=StrategyId("S-001"), last_px=Price.from_str("1.00001"), last_qty=Quantity.from_int(50000), ) fill3 = TestStubs.event_order_filled( order2, instrument=AUDUSD_SIM, execution_id=ExecutionId("2"), position_id=PositionId("P-19700101-000000-000-001-1"), strategy_id=StrategyId("S-001"), last_px=Price.from_str("1.00003"), last_qty=Quantity.from_int(50000), ) last = Price.from_str("1.00050") # Act position.apply(fill2) position.apply(fill3) # Assert self.assertEqual(Quantity.zero(), position.quantity) self.assertEqual(PositionSide.FLAT, position.side) self.assertEqual(0, position.opened_timestamp_ns) self.assertEqual(Decimal("1.0"), position.avg_px_open) self.assertEqual(3, position.event_count) self.assertEqual([order1.client_order_id, order2.client_order_id], position.client_order_ids) self.assertEqual(0, position.closed_timestamp_ns) self.assertEqual(Decimal("1.00002"), position.avg_px_close) self.assertFalse(position.is_long) self.assertFalse(position.is_short) self.assertFalse(position.is_open) self.assertTrue(position.is_closed) self.assertEqual(Money(-8.00, USD), position.realized_pnl) self.assertEqual(Money(0, USD), position.unrealized_pnl(last)) self.assertEqual(Money(-8.000, USD), position.total_pnl(last)) self.assertEqual([Money(6.00, USD)], position.commissions()) self.assertEqual( "Position(FLAT AUD/USD.SIM, id=P-19700101-000000-000-001-1)", repr(position)) def test_position_filled_with_no_change_returns_expected_attributes(self): # Arrange order1 = self.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) order2 = self.order_factory.market( AUDUSD_SIM.id, OrderSide.SELL, Quantity.from_int(100000), ) fill1 = TestStubs.event_order_filled( order1, instrument=AUDUSD_SIM, position_id=PositionId("P-19700101-000000-000-001-1"), ) position = Position(instrument=AUDUSD_SIM, fill=fill1) fill2 = TestStubs.event_order_filled( order2, instrument=AUDUSD_SIM, position_id=PositionId("P-19700101-000000-000-001-1"), strategy_id=StrategyId("S-001"), last_px=Price.from_str("1.00000"), ) last = Price.from_str("1.00050") # Act position.apply(fill2) # Assert self.assertEqual(Quantity.zero(), position.quantity) self.assertEqual(PositionSide.FLAT, position.side) self.assertEqual(0, position.opened_timestamp_ns) self.assertEqual(Decimal("1.0"), position.avg_px_open) self.assertEqual(2, position.event_count) self.assertEqual([order1.client_order_id, order2.client_order_id], position.client_order_ids) self.assertEqual( [ ExecutionId("E-19700101-000000-000-001-1"), ExecutionId("E-19700101-000000-000-001-2"), ], position.execution_ids, ) self.assertEqual(0, position.closed_timestamp_ns) self.assertEqual(Decimal("1.0"), position.avg_px_close) self.assertFalse(position.is_long) self.assertFalse(position.is_short) self.assertFalse(position.is_open) self.assertTrue(position.is_closed) self.assertEqual(0, position.realized_points) self.assertEqual(0, position.realized_return) self.assertEqual(Money(-4.00, USD), position.realized_pnl) self.assertEqual(Money(0, USD), position.unrealized_pnl(last)) self.assertEqual(Money(-4.00, USD), position.total_pnl(last)) self.assertEqual([Money(4.00, USD)], position.commissions()) self.assertEqual( "Position(FLAT AUD/USD.SIM, id=P-19700101-000000-000-001-1)", repr(position)) def test_position_long_with_multiple_filled_orders_returns_expected_attributes( self, ): # Arrange order1 = self.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) order2 = self.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) order3 = self.order_factory.market( AUDUSD_SIM.id, OrderSide.SELL, Quantity.from_int(200000), ) fill1 = TestStubs.event_order_filled( order1, instrument=AUDUSD_SIM, position_id=PositionId("P-123456"), strategy_id=StrategyId("S-001"), ) fill2 = TestStubs.event_order_filled( order2, instrument=AUDUSD_SIM, position_id=PositionId("P-123456"), strategy_id=StrategyId("S-001"), last_px=Price.from_str("1.00001"), ) fill3 = TestStubs.event_order_filled( order3, instrument=AUDUSD_SIM, position_id=PositionId("P-123456"), strategy_id=StrategyId("S-001"), last_px=Price.from_str("1.00010"), ) last = Price.from_str("1.00050") # Act position = Position(instrument=AUDUSD_SIM, fill=fill1) position.apply(fill2) position.apply(fill3) # Assert self.assertEqual(Quantity.zero(), position.quantity) self.assertEqual(PositionSide.FLAT, position.side) self.assertEqual(0, position.opened_timestamp_ns) self.assertEqual(Decimal("1.000005"), position.avg_px_open) self.assertEqual(3, position.event_count) self.assertEqual( [ order1.client_order_id, order2.client_order_id, order3.client_order_id ], position.client_order_ids, ) self.assertEqual(0, position.closed_timestamp_ns) self.assertEqual(Decimal("1.0001"), position.avg_px_close) self.assertFalse(position.is_long) self.assertFalse(position.is_short) self.assertFalse(position.is_open) self.assertTrue(position.is_closed) self.assertEqual(Money(11.00, USD), position.realized_pnl) self.assertEqual(Money(0, USD), position.unrealized_pnl(last)) self.assertEqual(Money(11.00, USD), position.total_pnl(last)) self.assertEqual([Money(8.00, USD)], position.commissions()) self.assertEqual("Position(FLAT AUD/USD.SIM, id=P-123456)", repr(position)) def test_pnl_calculation_from_trading_technologies_example(self): # https://www.tradingtechnologies.com/xtrader-help/fix-adapter-reference/pl-calculation-algorithm/understanding-pl-calculations/ # noqa # Arrange order1 = self.order_factory.market( ETHUSDT_BINANCE.id, OrderSide.BUY, Quantity.from_int(12), ) order2 = self.order_factory.market( ETHUSDT_BINANCE.id, OrderSide.BUY, Quantity.from_int(17), ) order3 = self.order_factory.market( ETHUSDT_BINANCE.id, OrderSide.SELL, Quantity.from_int(9), ) order4 = self.order_factory.market( ETHUSDT_BINANCE.id, OrderSide.SELL, Quantity.from_int(4), ) order5 = self.order_factory.market( ETHUSDT_BINANCE.id, OrderSide.BUY, Quantity.from_int(3), ) # Act fill1 = TestStubs.event_order_filled( order1, instrument=ETHUSDT_BINANCE, position_id=PositionId("P-19700101-000000-000-001-1"), last_px=Price.from_int(100), ) position = Position(instrument=ETHUSDT_BINANCE, fill=fill1) fill2 = TestStubs.event_order_filled( order2, instrument=ETHUSDT_BINANCE, position_id=PositionId("P-19700101-000000-000-001-1"), last_px=Price.from_int(99), ) position.apply(fill2) self.assertEqual(Quantity.from_int(29), position.quantity) self.assertEqual(Money(-0.28830000, USDT), position.realized_pnl) self.assertEqual(Decimal("99.41379310344827586206896552"), position.avg_px_open) fill3 = TestStubs.event_order_filled( order3, instrument=ETHUSDT_BINANCE, position_id=PositionId("P-19700101-000000-000-001-1"), strategy_id=StrategyId("S-001"), last_px=Price.from_int(101), ) position.apply(fill3) self.assertEqual(Quantity.from_int(20), position.quantity) self.assertEqual(Money(13.89666207, USDT), position.realized_pnl) self.assertEqual(Decimal("99.41379310344827586206896552"), position.avg_px_open) fill4 = TestStubs.event_order_filled( order4, instrument=ETHUSDT_BINANCE, position_id=PositionId("P-19700101-000000-000-001-1"), strategy_id=StrategyId("S-001"), last_px=Price.from_int(105), ) position.apply(fill4) self.assertEqual(Quantity.from_int(16), position.quantity) self.assertEqual(Money(36.19948966, USDT), position.realized_pnl) self.assertEqual(Decimal("99.41379310344827586206896552"), position.avg_px_open) fill5 = TestStubs.event_order_filled( order5, instrument=ETHUSDT_BINANCE, position_id=PositionId("P-19700101-000000-000-001-1"), strategy_id=StrategyId("S-001"), last_px=Price.from_int(103), ) position.apply(fill5) self.assertEqual(Quantity.from_int(19), position.quantity) self.assertEqual(Money(36.16858966, USDT), position.realized_pnl) self.assertEqual(Decimal("99.98003629764065335753176042"), position.avg_px_open) self.assertEqual( "Position(LONG 19.00000 ETH/USDT.BINANCE, id=P-19700101-000000-000-001-1)", repr(position), ) def test_position_realised_pnl_with_interleaved_order_sides(self): # Arrange order1 = self.order_factory.market( BTCUSDT_BINANCE.id, OrderSide.BUY, Quantity.from_str("12.000000"), ) order2 = self.order_factory.market( BTCUSDT_BINANCE.id, OrderSide.BUY, Quantity.from_str("17.000000"), ) order3 = self.order_factory.market( BTCUSDT_BINANCE.id, OrderSide.SELL, Quantity.from_str("9.000000"), ) order4 = self.order_factory.market( BTCUSDT_BINANCE.id, OrderSide.BUY, Quantity.from_str("3.000000"), ) order5 = self.order_factory.market( BTCUSDT_BINANCE.id, OrderSide.SELL, Quantity.from_str("4.000000"), ) # Act fill1 = TestStubs.event_order_filled( order1, instrument=BTCUSDT_BINANCE, position_id=PositionId("P-19700101-000000-000-001-1"), last_px=Price.from_str("10000.00"), ) position = Position(instrument=BTCUSDT_BINANCE, fill=fill1) fill2 = TestStubs.event_order_filled( order2, instrument=BTCUSDT_BINANCE, position_id=PositionId("P-19700101-000000-000-001-1"), last_px=Price.from_str("9999.00"), ) position.apply(fill2) self.assertEqual(Quantity.from_str("29.000000"), position.quantity) self.assertEqual(Money(-289.98300000, USDT), position.realized_pnl) self.assertEqual(Decimal("9999.413793103448275862068966"), position.avg_px_open) fill3 = TestStubs.event_order_filled( order3, instrument=BTCUSDT_BINANCE, position_id=PositionId("P-19700101-000000-000-001-1"), strategy_id=StrategyId("S-001"), last_px=Price.from_str("10001.00"), ) position.apply(fill3) self.assertEqual(Quantity.from_int(20), position.quantity) self.assertEqual(Money(-365.71613793, USDT), position.realized_pnl) self.assertEqual(Decimal("9999.413793103448275862068966"), position.avg_px_open) fill4 = TestStubs.event_order_filled( order4, instrument=BTCUSDT_BINANCE, position_id=PositionId("P-19700101-000000-000-001-1"), strategy_id=StrategyId("S-001"), last_px=Price.from_str("10003.00"), ) position.apply(fill4) self.assertEqual(Quantity.from_int(23), position.quantity) self.assertEqual(Money(-395.72513793, USDT), position.realized_pnl) self.assertEqual(Decimal("9999.881559220389805097451274"), position.avg_px_open) fill5 = TestStubs.event_order_filled( order5, instrument=BTCUSDT_BINANCE, position_id=PositionId("P-19700101-000000-000-001-1"), strategy_id=StrategyId("S-001"), last_px=Price.from_str("10005"), ) position.apply(fill5) self.assertEqual(Quantity.from_int(19), position.quantity) self.assertEqual(Money(-415.27137481, USDT), position.realized_pnl) self.assertEqual(Decimal("9999.881559220389805097451274"), position.avg_px_open) self.assertEqual( "Position(LONG 19.000000 BTC/USDT.BINANCE, id=P-19700101-000000-000-001-1)", repr(position), ) def test_calculate_pnl_when_given_position_side_flat_returns_zero(self): # Arrange order = self.order_factory.market( BTCUSDT_BINANCE.id, OrderSide.BUY, Quantity.from_int(12), ) fill = TestStubs.event_order_filled( order, instrument=BTCUSDT_BINANCE, position_id=PositionId("P-123456"), strategy_id=StrategyId("S-001"), last_px=Price.from_str("10500.00"), ) position = Position(instrument=BTCUSDT_BINANCE, fill=fill) # Act result = position.calculate_pnl( Price.from_str("10500.00"), Price.from_str("10500.00"), Quantity.from_int(100000), ) # Assert self.assertEqual(Money(0, USDT), result) def test_calculate_pnl_for_long_position_win(self): # Arrange order = self.order_factory.market( BTCUSDT_BINANCE.id, OrderSide.BUY, Quantity.from_int(12), ) fill = TestStubs.event_order_filled( order, instrument=BTCUSDT_BINANCE, position_id=PositionId("P-123456"), strategy_id=StrategyId("S-001"), last_px=Price.from_str("10500.00"), ) position = Position(instrument=BTCUSDT_BINANCE, fill=fill) # Act pnl = position.calculate_pnl( avg_px_open=Price.from_str("10500.00"), avg_px_close=Price.from_str("10510.00"), quantity=Quantity.from_int(12), ) # Assert self.assertEqual(Money(120.00000000, USDT), pnl) self.assertEqual(Money(-126.00000000, USDT), position.realized_pnl) self.assertEqual( Money(120.00000000, USDT), position.unrealized_pnl(Price.from_str("10510.00")), ) self.assertEqual(Money(-6.00000000, USDT), position.total_pnl(Price.from_str("10510.00"))) self.assertEqual([Money(126.00000000, USDT)], position.commissions()) def test_calculate_pnl_for_long_position_loss(self): # Arrange order = self.order_factory.market( BTCUSDT_BINANCE.id, OrderSide.BUY, Quantity.from_int(12), ) fill = TestStubs.event_order_filled( order, instrument=BTCUSDT_BINANCE, position_id=PositionId("P-123456"), strategy_id=StrategyId("S-001"), last_px=Price.from_str("10500.00"), ) position = Position(instrument=BTCUSDT_BINANCE, fill=fill) # Act pnl = position.calculate_pnl( avg_px_open=Price.from_str("10500.00"), avg_px_close=Price.from_str("10480.50"), quantity=Quantity.from_int(10), ) # Assert self.assertEqual(Money(-195.00000000, USDT), pnl) self.assertEqual(Money(-126.00000000, USDT), position.realized_pnl) self.assertEqual( Money(-234.00000000, USDT), position.unrealized_pnl(Price.from_str("10480.50")), ) self.assertEqual(Money(-360.00000000, USDT), position.total_pnl(Price.from_str("10480.50"))) self.assertEqual([Money(126.00000000, USDT)], position.commissions()) def test_calculate_pnl_for_short_position_winning(self): # Arrange order = self.order_factory.market( BTCUSDT_BINANCE.id, OrderSide.SELL, Quantity.from_str("10.150000"), ) fill = TestStubs.event_order_filled( order, instrument=BTCUSDT_BINANCE, position_id=PositionId("P-123456"), strategy_id=StrategyId("S-001"), last_px=Price.from_str("10500.00"), ) position = Position(instrument=BTCUSDT_BINANCE, fill=fill) # Act pnl = position.calculate_pnl( Price.from_str("10500.00"), Price.from_str("10390.00"), Quantity.from_str("10.150000"), ) # Assert self.assertEqual(Money(1116.50000000, USDT), pnl) self.assertEqual( Money(1116.50000000, USDT), position.unrealized_pnl(Price.from_str("10390.00")), ) self.assertEqual(Money(-106.57500000, USDT), position.realized_pnl) self.assertEqual([Money(106.57500000, USDT)], position.commissions()) self.assertEqual( Money(105458.50000000, USDT), position.notional_value(Price.from_str("10390.00")), ) def test_calculate_pnl_for_short_position_loss(self): # Arrange order = self.order_factory.market( BTCUSDT_BINANCE.id, OrderSide.SELL, Quantity.from_str("10"), ) fill = TestStubs.event_order_filled( order, instrument=BTCUSDT_BINANCE, position_id=PositionId("P-123456"), strategy_id=StrategyId("S-001"), last_px=Price.from_str("10500.00"), ) position = Position(instrument=BTCUSDT_BINANCE, fill=fill) # Act pnl = position.calculate_pnl( Price.from_str("10500.00"), Price.from_str("10670.50"), Quantity.from_str("10.000000"), ) # Assert self.assertEqual(Money(-1705.00000000, USDT), pnl) self.assertEqual( Money(-1705.00000000, USDT), position.unrealized_pnl(Price.from_str("10670.50")), ) self.assertEqual(Money(-105.00000000, USDT), position.realized_pnl) self.assertEqual([Money(105.00000000, USDT)], position.commissions()) self.assertEqual( Money(106705.00000000, USDT), position.notional_value(Price.from_str("10670.50")), ) def test_calculate_pnl_for_inverse1(self): # Arrange order = self.order_factory.market( XBTUSD_BITMEX.id, OrderSide.SELL, Quantity.from_int(100000), ) fill = TestStubs.event_order_filled( order, instrument=XBTUSD_BITMEX, position_id=PositionId("P-123456"), strategy_id=StrategyId("S-001"), last_px=Price.from_str("10000.00"), ) position = Position(instrument=XBTUSD_BITMEX, fill=fill) # Act pnl = position.calculate_pnl( avg_px_open=Price.from_str("10000.00"), avg_px_close=Price.from_str("11000.00"), quantity=Quantity.from_int(100000), ) # Assert self.assertEqual(Money(-0.90909091, BTC), pnl) self.assertEqual(Money(-0.90909091, BTC), position.unrealized_pnl(Price.from_str("11000.00"))) self.assertEqual(Money(-0.00750000, BTC), position.realized_pnl) self.assertEqual(Money(9.09090909, BTC), position.notional_value(Price.from_str("11000.00"))) def test_calculate_pnl_for_inverse2(self): # Arrange order = self.order_factory.market( ETHUSD_BITMEX.id, OrderSide.SELL, Quantity.from_int(100000), ) fill = TestStubs.event_order_filled( order, instrument=ETHUSD_BITMEX, position_id=PositionId("P-123456"), strategy_id=StrategyId("S-001"), last_px=Price.from_str("375.95"), ) position = Position(instrument=ETHUSD_BITMEX, fill=fill) # Act, Assert self.assertEqual(Money(4.27745208, ETH), position.unrealized_pnl(Price.from_str("370.00"))) self.assertEqual(Money(270.27027027, ETH), position.notional_value(Price.from_str("370.00"))) def test_calculate_unrealized_pnl_for_long(self): # Arrange order1 = self.order_factory.market( BTCUSDT_BINANCE.id, OrderSide.BUY, Quantity.from_str("2.000000"), ) order2 = self.order_factory.market( BTCUSDT_BINANCE.id, OrderSide.BUY, Quantity.from_str("2.000000"), ) fill1 = TestStubs.event_order_filled( order1, instrument=BTCUSDT_BINANCE, position_id=PositionId("P-123456"), strategy_id=StrategyId("S-001"), last_px=Price.from_str("10500.00"), ) fill2 = TestStubs.event_order_filled( order2, instrument=BTCUSDT_BINANCE, position_id=PositionId("P-123456"), strategy_id=StrategyId("S-001"), last_px=Price.from_str("10500.00"), ) position = Position(instrument=BTCUSDT_BINANCE, fill=fill1) position.apply(fill2) # Act pnl = position.unrealized_pnl(Price.from_str("11505.60")) # Assert self.assertEqual(Money(4022.40000000, USDT), pnl) self.assertEqual(Money(-42.00000000, USDT), position.realized_pnl) self.assertEqual([Money(42.00000000, USDT)], position.commissions()) def test_calculate_unrealized_pnl_for_short(self): # Arrange order = self.order_factory.market( BTCUSDT_BINANCE.id, OrderSide.SELL, Quantity.from_str("5.912000"), ) fill = TestStubs.event_order_filled( order, instrument=BTCUSDT_BINANCE, position_id=PositionId("P-123456"), strategy_id=StrategyId("S-001"), last_px=Price.from_str("10505.60"), ) position = Position(instrument=BTCUSDT_BINANCE, fill=fill) pnl = position.unrealized_pnl(Price.from_str("10407.15")) # Assert self.assertEqual(Money(582.03640000, USDT), pnl) self.assertEqual(Money(-62.10910720, USDT), position.realized_pnl) self.assertEqual([Money(62.10910720, USDT)], position.commissions()) def test_calculate_unrealized_pnl_for_long_inverse(self): # Arrange order = self.order_factory.market( XBTUSD_BITMEX.id, OrderSide.BUY, Quantity.from_int(100000), ) fill = TestStubs.event_order_filled( order, instrument=XBTUSD_BITMEX, position_id=PositionId("P-123456"), strategy_id=StrategyId("S-001"), last_px=Price.from_str("10500.00"), ) position = Position(XBTUSD_BITMEX, fill) # Act pnl = position.unrealized_pnl(Price.from_str("11505.60")) # Assert self.assertEqual(Money(0.83238969, BTC), pnl) self.assertEqual(Money(-0.00714286, BTC), position.realized_pnl) self.assertEqual([Money(0.00714286, BTC)], position.commissions()) def test_calculate_unrealized_pnl_for_short_inverse(self): # Arrange order = self.order_factory.market( XBTUSD_BITMEX.id, OrderSide.SELL, Quantity.from_int(1250000), ) fill = TestStubs.event_order_filled( order, instrument=XBTUSD_BITMEX, position_id=PositionId("P-123456"), strategy_id=StrategyId("S-001"), last_px=Price.from_str("15500.00"), ) position = Position(XBTUSD_BITMEX, fill) # Act pnl = position.unrealized_pnl(Price.from_str("12506.65")) # Assert self.assertEqual(Money(19.30166700, BTC), pnl) self.assertEqual(Money(-0.06048387, BTC), position.realized_pnl) self.assertEqual([Money(0.06048387, BTC)], position.commissions())
class PortfolioTests(unittest.TestCase): def setUp(self): # Fixture Setup clock = TestClock() logger = TestLogger(clock) self.order_factory = OrderFactory( trader_id=TraderId("TESTER", "000"), strategy_id=StrategyId("S", "001"), clock=TestClock(), ) state = AccountState( account_id=AccountId("BINANCE", "1513111"), balances=[Money("10.00000000", BTC)], balances_free=[Money("0.00000000", BTC)], balances_locked=[Money("0.00000000", BTC)], info={}, event_id=uuid4(), event_timestamp=UNIX_EPOCH, ) self.data_cache = DataCache(logger) self.account = Account(state) self.portfolio = Portfolio(clock, logger) self.portfolio.register_account(self.account) self.portfolio.register_cache(self.data_cache) self.data_cache.add_instrument(AUDUSD_SIM) self.data_cache.add_instrument(GBPUSD_SIM) self.data_cache.add_instrument(BTCUSDT_BINANCE) self.data_cache.add_instrument(BTCUSD_BITMEX) self.data_cache.add_instrument(ETHUSD_BITMEX) def test_account_when_no_account_returns_none(self): # Arrange # Act # Assert self.assertIsNone(self.portfolio.account(SIM)) def test_account_when_account_returns_the_account_facade(self): # Arrange # Act result = self.portfolio.account(BINANCE) # Assert self.assertEqual(self.account, result) def test_net_position_when_no_positions_returns_zero(self): # Arrange # Act # Assert self.assertEqual(Decimal(0), self.portfolio.net_position(AUDUSD_SIM.symbol)) def test_is_net_long_when_no_positions_returns_false(self): # Arrange # Act # Assert self.assertEqual(False, self.portfolio.is_net_long(AUDUSD_SIM.symbol)) def test_is_net_short_when_no_positions_returns_false(self): # Arrange # Act # Assert self.assertEqual(False, self.portfolio.is_net_short(AUDUSD_SIM.symbol)) def test_is_flat_when_no_positions_returns_true(self): # Arrange # Act # Assert self.assertEqual(True, self.portfolio.is_flat(AUDUSD_SIM.symbol)) def test_is_completely_flat_when_no_positions_returns_true(self): # Arrange # Act # Assert self.assertEqual(True, self.portfolio.is_flat(AUDUSD_SIM.symbol)) def test_unrealized_pnl_for_symbol_when_no_instrument_returns_none(self): # Arrange # Act # Assert self.assertIsNone(self.portfolio.unrealized_pnl(USDJPY_SIM.symbol)) def test_unrealized_pnl_for_venue_when_no_account_returns_empty_dict(self): # Arrange # Act # Assert self.assertEqual({}, self.portfolio.unrealized_pnls(SIM)) def test_initial_margins_when_no_account_returns_none(self): # Arrange # Act # Assert self.assertEqual(None, self.portfolio.initial_margins(SIM)) def test_maint_margins_when_no_account_returns_none(self): # Arrange # Act # Assert self.assertEqual(None, self.portfolio.maint_margins(SIM)) def test_open_value_when_no_account_returns_none(self): # Arrange # Act # Assert self.assertEqual(None, self.portfolio.market_values(SIM)) def test_update_tick(self): # Arrange tick = TestStubs.quote_tick_5decimal(GBPUSD_SIM.symbol) # Act self.portfolio.update_tick(tick) # Assert self.assertIsNone(self.portfolio.unrealized_pnl(GBPUSD_SIM.symbol)) def test_update_orders_working(self): # Arrange self.portfolio.register_account(self.account) # Create two working orders order1 = self.order_factory.stop_market( BTCUSDT_BINANCE.symbol, OrderSide.BUY, Quantity("10.5"), Price("25000.00"), ) order2 = self.order_factory.stop_market( BTCUSDT_BINANCE.symbol, OrderSide.BUY, Quantity("10.5"), Price("25000.00"), ) # Push state to FILLED order1.apply(TestStubs.event_order_submitted(order1)) order1.apply(TestStubs.event_order_accepted(order1)) filled1 = TestStubs.event_order_filled( order1, instrument=BTCUSDT_BINANCE, position_id=PositionId("P-1"), strategy_id=StrategyId("S", "1"), fill_price=Price("25000.00"), ) order1.apply(filled1) # Push state to ACCEPTED order2.apply(TestStubs.event_order_submitted(order2)) order2.apply(TestStubs.event_order_accepted(order2)) # Update the last quote last = QuoteTick( BTCUSDT_BINANCE.symbol, Price("25001.00"), Price("25002.00"), Quantity(1), Quantity(1), UNIX_EPOCH, ) # Act self.portfolio.update_tick(last) self.portfolio.initialize_orders({order1, order2}) # Assert self.assertEqual({}, self.portfolio.initial_margins(BINANCE)) def test_update_positions(self): # Arrange self.portfolio.register_account(self.account) # Create a closed position order1 = self.order_factory.market( BTCUSDT_BINANCE.symbol, OrderSide.BUY, Quantity("10.50000000"), ) order2 = self.order_factory.market( BTCUSDT_BINANCE.symbol, OrderSide.SELL, Quantity("10.50000000"), ) filled1 = TestStubs.event_order_filled( order1, instrument=BTCUSDT_BINANCE, position_id=PositionId("P-1"), strategy_id=StrategyId("S", "1"), fill_price=Price("25000.00"), ) filled2 = TestStubs.event_order_filled( order2, instrument=BTCUSDT_BINANCE, position_id=PositionId("P-1"), strategy_id=StrategyId("S", "1"), fill_price=Price("25000.00"), ) position1 = Position(filled1) position1.apply(filled2) order3 = self.order_factory.market( BTCUSDT_BINANCE.symbol, OrderSide.BUY, Quantity("10.00000000"), ) filled3 = TestStubs.event_order_filled( order3, instrument=BTCUSDT_BINANCE, strategy_id=StrategyId("S", "1"), fill_price=Price("25000.00"), ) position2 = Position(filled3) # Update the last quote last = QuoteTick( BTCUSDT_BINANCE.symbol, Price("25001.00"), Price("25002.00"), Quantity(1), Quantity(1), UNIX_EPOCH, ) # Act self.portfolio.initialize_positions({position1, position2}) self.portfolio.update_tick(last) # Assert self.assertTrue(self.portfolio.is_net_long(BTCUSDT_BINANCE.symbol)) def test_opening_one_long_position_updates_portfolio(self): # Arrange order = self.order_factory.market( BTCUSDT_BINANCE.symbol, OrderSide.BUY, Quantity("10.000000"), ) fill = TestStubs.event_order_filled( order=order, instrument=BTCUSDT_BINANCE, position_id=PositionId("P-123456"), strategy_id=StrategyId("S", "001"), fill_price=Price("10500.00"), ) last = QuoteTick( BTCUSDT_BINANCE.symbol, Price("10510.00"), Price("10511.00"), Quantity("1.000000"), Quantity("1.000000"), UNIX_EPOCH, ) self.data_cache.add_quote_tick(last) self.portfolio.update_tick(last) position = Position(fill) # Act self.portfolio.update_position( TestStubs.event_position_opened(position)) # Assert self.assertEqual({USDT: Money("105100.00000000", USDT)}, self.portfolio.market_values(BINANCE)) self.assertEqual({USDT: Money("100.00000000", USDT)}, self.portfolio.unrealized_pnls(BINANCE)) self.assertEqual({}, self.portfolio.maint_margins(BINANCE)) self.assertEqual(Money("105100.00000000", USDT), self.portfolio.market_value(BTCUSDT_BINANCE.symbol)) self.assertEqual(Money("100.00000000", USDT), self.portfolio.unrealized_pnl(BTCUSDT_BINANCE.symbol)) self.assertEqual(Decimal("10.00000000"), self.portfolio.net_position(order.symbol)) self.assertTrue(self.portfolio.is_net_long(order.symbol)) self.assertFalse(self.portfolio.is_net_short(order.symbol)) self.assertFalse(self.portfolio.is_flat(order.symbol)) self.assertFalse(self.portfolio.is_completely_flat()) def test_opening_one_short_position_updates_portfolio(self): # Arrange order = self.order_factory.market( BTCUSDT_BINANCE.symbol, OrderSide.SELL, Quantity("0.515"), ) fill = TestStubs.event_order_filled(order=order, instrument=BTCUSDT_BINANCE, position_id=PositionId("P-123456"), strategy_id=StrategyId("S", "001"), fill_price=Price("15000.00")) last = QuoteTick( BTCUSDT_BINANCE.symbol, Price("15510.15"), Price("15510.25"), Quantity("12.62"), Quantity("3.1"), UNIX_EPOCH, ) self.data_cache.add_quote_tick(last) self.portfolio.update_tick(last) position = Position(fill) # Act self.portfolio.update_position( TestStubs.event_position_opened(position)) # Assert self.assertEqual({USDT: Money("7987.77875000", USDT)}, self.portfolio.market_values(BINANCE)) self.assertEqual({USDT: Money("-262.77875000", USDT)}, self.portfolio.unrealized_pnls(BINANCE)) self.assertEqual({}, self.portfolio.maint_margins(BINANCE)) self.assertEqual(Money("7987.77875000", USDT), self.portfolio.market_value(BTCUSDT_BINANCE.symbol)) self.assertEqual(Money("-262.77875000", USDT), self.portfolio.unrealized_pnl(BTCUSDT_BINANCE.symbol)) self.assertEqual(Decimal("-0.515"), self.portfolio.net_position(order.symbol)) self.assertFalse(self.portfolio.is_net_long(order.symbol)) self.assertTrue(self.portfolio.is_net_short(order.symbol)) self.assertFalse(self.portfolio.is_flat(order.symbol)) self.assertFalse(self.portfolio.is_completely_flat()) def test_opening_positions_with_multi_asset_account(self): # Arrange state = AccountState( account_id=AccountId("BITMEX", "01234"), balances=[Money("10.00000000", BTC), Money("10.00000000", ETH)], balances_free=[ Money("0.00000000", BTC), Money("10.00000000", ETH) ], balances_locked=[ Money("0.00000000", BTC), Money("0.00000000", ETH) ], info={}, event_id=uuid4(), event_timestamp=UNIX_EPOCH, ) account = Account(state) self.portfolio.register_account(account) last_ethusd = QuoteTick( ETHUSD_BITMEX.symbol, Price("376.05"), Price("377.10"), Quantity("16"), Quantity("25"), UNIX_EPOCH, ) last_btcusd = QuoteTick( BTCUSD_BITMEX.symbol, Price("10500.05"), Price("10501.51"), Quantity("2.54"), Quantity("0.91"), UNIX_EPOCH, ) self.data_cache.add_quote_tick(last_ethusd) self.data_cache.add_quote_tick(last_btcusd) self.portfolio.update_tick(last_ethusd) self.portfolio.update_tick(last_btcusd) order = self.order_factory.market( ETHUSD_BITMEX.symbol, OrderSide.BUY, Quantity(10000), ) fill = TestStubs.event_order_filled( order=order, instrument=ETHUSD_BITMEX, position_id=PositionId("P-123456"), strategy_id=StrategyId("S", "001"), fill_price=Price("376.05"), ) position = Position(fill) # Act self.portfolio.update_position( TestStubs.event_position_opened(position)) # Assert self.assertEqual({ETH: Money("2.65922085", ETH)}, self.portfolio.market_values(BITMEX)) self.assertEqual({ETH: Money("0.03855870", ETH)}, self.portfolio.maint_margins(BITMEX)) self.assertEqual(Money("2.65922085", ETH), self.portfolio.market_value(ETHUSD_BITMEX.symbol)) self.assertEqual(Money("0.00000000", ETH), self.portfolio.unrealized_pnl(ETHUSD_BITMEX.symbol)) def test_unrealized_pnl_when_insufficient_data_for_xrate_returns_none( self): # Arrange state = AccountState( account_id=AccountId("BITMEX", "01234"), balances=[Money("10.00000000", BTC), Money("10.00000000", ETH)], balances_free=[ Money("10.00000000", BTC), Money("10.00000000", ETH) ], balances_locked=[ Money("0.00000000", BTC), Money("0.00000000", ETH) ], info={}, event_id=uuid4(), event_timestamp=UNIX_EPOCH, ) account = Account(state) self.portfolio.register_account(account) order = self.order_factory.market( ETHUSD_BITMEX.symbol, OrderSide.BUY, Quantity(100), ) fill = TestStubs.event_order_filled( order=order, instrument=ETHUSD_BITMEX, position_id=PositionId("P-123456"), strategy_id=StrategyId("S", "001"), fill_price=Price("376.05"), ) position = Position(fill) self.portfolio.update_position( TestStubs.event_position_opened(position)) # Act result = self.portfolio.unrealized_pnls(BITMEX) # # Assert self.assertIsNone(result) def test_market_value_when_insufficient_data_for_xrate_returns_none(self): # Arrange state = AccountState( account_id=AccountId("BITMEX", "01234"), balances=[Money("10.00000000", BTC), Money("10.00000000", ETH)], balances_free=[ Money("10.00000000", BTC), Money("10.00000000", ETH) ], balances_locked=[ Money("0.00000000", BTC), Money("0.00000000", ETH) ], info={}, event_id=uuid4(), event_timestamp=UNIX_EPOCH, ) account = Account(state) self.portfolio.register_account(account) order = self.order_factory.market( ETHUSD_BITMEX.symbol, OrderSide.BUY, Quantity(100), ) fill = TestStubs.event_order_filled( order=order, instrument=ETHUSD_BITMEX, position_id=PositionId("P-123456"), strategy_id=StrategyId("S", "001"), fill_price=Price("376.05"), ) last_ethusd = QuoteTick( ETHUSD_BITMEX.symbol, Price("376.05"), Price("377.10"), Quantity("16"), Quantity("25"), UNIX_EPOCH, ) position = Position(fill) self.portfolio.update_position( TestStubs.event_position_opened(position)) self.data_cache.add_quote_tick(last_ethusd) self.portfolio.update_tick(last_ethusd) # Act result = self.portfolio.market_values(BITMEX) # Assert # TODO: Currently no Quanto thus no xrate required self.assertEqual({ETH: Money('0.02659221', ETH)}, result) def test_opening_several_positions_updates_portfolio(self): # Arrange state = AccountState( AccountId("SIM", "01234"), balances=[Money(1_000_000.00, USD)], balances_free=[Money(1_000_000.00, USD)], balances_locked=[Money(0.00, USD)], info={"default_currency": "USD"}, event_id=uuid4(), event_timestamp=UNIX_EPOCH, ) account = Account(state) self.portfolio.register_account(account) last_audusd = QuoteTick( AUDUSD_SIM.symbol, Price("0.80501"), Price("0.80505"), Quantity(1), Quantity(1), UNIX_EPOCH, ) last_gbpusd = QuoteTick( GBPUSD_SIM.symbol, Price("1.30315"), Price("1.30317"), Quantity(1), Quantity(1), UNIX_EPOCH, ) self.data_cache.add_quote_tick(last_audusd) self.data_cache.add_quote_tick(last_gbpusd) self.portfolio.update_tick(last_audusd) self.portfolio.update_tick(last_gbpusd) order1 = self.order_factory.market( AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(100000), ) order2 = self.order_factory.market( GBPUSD_SIM.symbol, OrderSide.BUY, Quantity(100000), ) order1_filled = TestStubs.event_order_filled( order1, instrument=AUDUSD_SIM, position_id=PositionId("P-1"), strategy_id=StrategyId("S", "1"), fill_price=Price("1.00000"), ) order2_filled = TestStubs.event_order_filled( order2, instrument=AUDUSD_SIM, position_id=PositionId("P-2"), strategy_id=StrategyId("S", "1"), fill_price=Price("1.00000"), ) position1 = Position(order1_filled) position2 = Position(order2_filled) position_opened1 = TestStubs.event_position_opened(position1) position_opened2 = TestStubs.event_position_opened(position2) # Act self.portfolio.update_position(position_opened1) self.portfolio.update_position(position_opened2) # Assert self.assertEqual({USD: Money("4216.32", USD)}, self.portfolio.market_values(SIM)) self.assertEqual({USD: Money("10816.00", USD)}, self.portfolio.unrealized_pnls(SIM)) self.assertEqual({USD: Money("130.71", USD)}, self.portfolio.maint_margins(SIM)) self.assertEqual(Money("1610.02", USD), self.portfolio.market_value(AUDUSD_SIM.symbol)) self.assertEqual(Money("2606.30", USD), self.portfolio.market_value(GBPUSD_SIM.symbol)) self.assertEqual(Money("-19499.00", USD), self.portfolio.unrealized_pnl(AUDUSD_SIM.symbol)) self.assertEqual(Money("30315.00", USD), self.portfolio.unrealized_pnl(GBPUSD_SIM.symbol)) self.assertEqual(Decimal(100000), self.portfolio.net_position(AUDUSD_SIM.symbol)) self.assertEqual(Decimal(100000), self.portfolio.net_position(GBPUSD_SIM.symbol)) self.assertTrue(self.portfolio.is_net_long(AUDUSD_SIM.symbol)) self.assertFalse(self.portfolio.is_net_short(AUDUSD_SIM.symbol)) self.assertFalse(self.portfolio.is_flat(AUDUSD_SIM.symbol)) self.assertFalse(self.portfolio.is_completely_flat()) def test_modifying_position_updates_portfolio(self): # Arrange state = AccountState( AccountId("SIM", "01234"), balances=[Money(1_000_000.00, USD)], balances_free=[Money(1_000_000.00, USD)], balances_locked=[Money(0.00, USD)], info={"default_currency": "USD"}, event_id=uuid4(), event_timestamp=UNIX_EPOCH, ) account = Account(state) self.portfolio.register_account(account) last_audusd = QuoteTick( AUDUSD_SIM.symbol, Price("0.80501"), Price("0.80505"), Quantity(1), Quantity(1), UNIX_EPOCH, ) self.data_cache.add_quote_tick(last_audusd) self.portfolio.update_tick(last_audusd) order1 = self.order_factory.market( AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(100000), ) order1_filled = TestStubs.event_order_filled( order1, instrument=AUDUSD_SIM, position_id=PositionId("P-123456"), strategy_id=StrategyId("S", "1"), fill_price=Price("1.00000"), ) position = Position(order1_filled) self.portfolio.update_position( TestStubs.event_position_opened(position)) order2 = self.order_factory.market( AUDUSD_SIM.symbol, OrderSide.SELL, Quantity(50000), ) order2_filled = TestStubs.event_order_filled( order2, instrument=AUDUSD_SIM, position_id=PositionId("P-123456"), strategy_id=StrategyId("S", "1"), fill_price=Price("1.00000"), ) position.apply(order2_filled) # Act self.portfolio.update_position( TestStubs.event_position_changed(position)) # Assert self.assertEqual({USD: Money("805.01", USD)}, self.portfolio.market_values(SIM)) self.assertEqual({USD: Money("-9749.50", USD)}, self.portfolio.unrealized_pnls(SIM)) self.assertEqual({USD: Money("24.96", USD)}, self.portfolio.maint_margins(SIM)) self.assertEqual(Money("805.01", USD), self.portfolio.market_value(AUDUSD_SIM.symbol)) self.assertEqual(Money("-9749.50", USD), self.portfolio.unrealized_pnl(AUDUSD_SIM.symbol)) self.assertEqual(Decimal(50000), self.portfolio.net_position(AUDUSD_SIM.symbol)) self.assertTrue(self.portfolio.is_net_long(AUDUSD_SIM.symbol)) self.assertFalse(self.portfolio.is_net_short(AUDUSD_SIM.symbol)) self.assertFalse(self.portfolio.is_flat(AUDUSD_SIM.symbol)) self.assertFalse(self.portfolio.is_completely_flat()) self.assertEqual({}, self.portfolio.unrealized_pnls(BINANCE)) self.assertEqual({}, self.portfolio.market_values(BINANCE)) def test_closing_position_updates_portfolio(self): # Arrange state = AccountState( AccountId("SIM", "01234"), balances=[Money(1_000_000.00, USD)], balances_free=[Money(1_000_000.00, USD)], balances_locked=[Money(0.00, USD)], info={"default_currency": "USD"}, event_id=uuid4(), event_timestamp=UNIX_EPOCH, ) account = Account(state) self.portfolio.register_account(account) order1 = self.order_factory.market( AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(100000), ) order1_filled = TestStubs.event_order_filled( order1, instrument=AUDUSD_SIM, position_id=PositionId("P-123456"), strategy_id=StrategyId("S", "1"), fill_price=Price("1.00000"), ) position = Position(order1_filled) self.portfolio.update_position( TestStubs.event_position_opened(position)) order2 = self.order_factory.market( AUDUSD_SIM.symbol, OrderSide.SELL, Quantity(100000), ) order2_filled = TestStubs.event_order_filled( order2, instrument=AUDUSD_SIM, position_id=PositionId("P-123456"), strategy_id=StrategyId("S", "1"), fill_price=Price("1.00010"), ) position.apply(order2_filled) # Act self.portfolio.update_position( TestStubs.event_position_closed(position)) # Assert self.assertEqual({}, self.portfolio.market_values(SIM)) self.assertEqual({}, self.portfolio.unrealized_pnls(SIM)) self.assertEqual({}, self.portfolio.maint_margins(SIM)) self.assertEqual(Money("0", USD), self.portfolio.market_value(AUDUSD_SIM.symbol)) self.assertEqual(Money("0", USD), self.portfolio.unrealized_pnl(AUDUSD_SIM.symbol)) self.assertEqual(Decimal(0), self.portfolio.net_position(AUDUSD_SIM.symbol)) self.assertFalse(self.portfolio.is_net_long(AUDUSD_SIM.symbol)) self.assertFalse(self.portfolio.is_net_short(AUDUSD_SIM.symbol)) self.assertTrue(self.portfolio.is_flat(AUDUSD_SIM.symbol)) self.assertTrue(self.portfolio.is_completely_flat()) def test_several_positions_with_different_symbols_updates_portfolio(self): # Arrange state = AccountState( AccountId("SIM", "01234"), balances=[Money(1_000_000.00, USD)], balances_free=[Money(1_000_000.00, USD)], balances_locked=[Money(0.00, USD)], info={"default_currency": "USD"}, event_id=uuid4(), event_timestamp=UNIX_EPOCH, ) account = Account(state) self.portfolio.register_account(account) order1 = self.order_factory.market( AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(100000), ) order2 = self.order_factory.market( AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(100000), ) order3 = self.order_factory.market( GBPUSD_SIM.symbol, OrderSide.BUY, Quantity(100000), ) order4 = self.order_factory.market( GBPUSD_SIM.symbol, OrderSide.SELL, Quantity(100000), ) order1_filled = TestStubs.event_order_filled( order1, instrument=GBPUSD_SIM, position_id=PositionId("P-1"), strategy_id=StrategyId("S", "1"), fill_price=Price("1.00000"), ) order2_filled = TestStubs.event_order_filled( order2, instrument=GBPUSD_SIM, position_id=PositionId("P-2"), strategy_id=StrategyId("S", "1"), fill_price=Price("1.00000"), ) order3_filled = TestStubs.event_order_filled( order3, instrument=GBPUSD_SIM, position_id=PositionId("P-3"), strategy_id=StrategyId("S", "1"), fill_price=Price("1.00000"), ) order4_filled = TestStubs.event_order_filled( order4, instrument=GBPUSD_SIM, position_id=PositionId("P-3"), strategy_id=StrategyId("S", "1"), fill_price=Price("1.00100"), ) position1 = Position(order1_filled) position2 = Position(order2_filled) position3 = Position(order3_filled) last_audusd = QuoteTick( AUDUSD_SIM.symbol, Price("0.80501"), Price("0.80505"), Quantity(1), Quantity(1), UNIX_EPOCH, ) last_gbpusd = QuoteTick( GBPUSD_SIM.symbol, Price("1.30315"), Price("1.30317"), Quantity(1), Quantity(1), UNIX_EPOCH, ) self.data_cache.add_quote_tick(last_audusd) self.data_cache.add_quote_tick(last_gbpusd) self.portfolio.update_tick(last_audusd) self.portfolio.update_tick(last_gbpusd) # Act self.portfolio.update_position( TestStubs.event_position_opened(position1)) self.portfolio.update_position( TestStubs.event_position_opened(position2)) self.portfolio.update_position( TestStubs.event_position_opened(position3)) position3.apply(order4_filled) self.portfolio.update_position( TestStubs.event_position_closed(position3)) # Assert self.assertEqual({USD: Money("-38998.00", USD)}, self.portfolio.unrealized_pnls(SIM)) self.assertEqual({USD: Money("3220.04", USD)}, self.portfolio.market_values(SIM)) self.assertEqual({USD: Money("99.82", USD)}, self.portfolio.maint_margins(SIM)) self.assertEqual(Money("3220.04", USD), self.portfolio.market_value(AUDUSD_SIM.symbol)) self.assertEqual(Money("-38998.00", USD), self.portfolio.unrealized_pnl(AUDUSD_SIM.symbol)) self.assertEqual(Money("0", USD), self.portfolio.unrealized_pnl(GBPUSD_SIM.symbol)) self.assertEqual(Decimal(200000), self.portfolio.net_position(AUDUSD_SIM.symbol)) self.assertEqual(Decimal(0), self.portfolio.net_position(GBPUSD_SIM.symbol)) self.assertTrue(self.portfolio.is_net_long(AUDUSD_SIM.symbol)) self.assertTrue(self.portfolio.is_flat(GBPUSD_SIM.symbol)) self.assertFalse(self.portfolio.is_completely_flat())
class BacktestExecClientTests(unittest.TestCase): def setUp(self): # Fixture Setup self.clock = TestClock() self.uuid_factory = UUIDFactory() self.logger = TestLogger(self.clock) self.trader_id = TraderId("TESTER", "000") self.account_id = AccountId("BINANCE", "000") self.portfolio = Portfolio( clock=self.clock, logger=self.logger, ) self.portfolio.register_cache(DataCache(self.logger)) self.analyzer = PerformanceAnalyzer() database = BypassExecutionDatabase( trader_id=self.trader_id, logger=self.logger, ) self.exec_engine = ExecutionEngine( database=database, portfolio=self.portfolio, clock=self.clock, logger=self.logger, ) self.exchange = SimulatedExchange( venue=Venue("BINANCE"), oms_type=OMSType.NETTING, generate_position_ids=True, is_frozen_account=False, starting_balances=[Money(1_000_000, USD)], instruments=[ETHUSDT_BINANCE], modules=[], exec_cache=self.exec_engine.cache, fill_model=FillModel(), clock=self.clock, logger=self.logger, ) self.exec_client = BacktestExecClient( exchange=self.exchange, account_id=self.account_id, engine=self.exec_engine, clock=self.clock, logger=self.logger, ) self.order_factory = OrderFactory( trader_id=self.trader_id, strategy_id=StrategyId("SCALPER", "000"), clock=self.clock, ) def test_is_connected_when_not_connected_returns_false(self): # Arrange # Act # Assert self.assertFalse(self.exec_client.is_connected()) def test_connect(self): # Arrange # Act self.exec_client.connect() # Assert self.assertTrue(self.exec_client.is_connected()) def test_disconnect(self): # Arrange self.exec_client.connect() # Act self.exec_client.disconnect() # Assert self.assertFalse(self.exec_client.is_connected()) def test_reset(self): # Arrange # Act self.exec_client.reset() # Assert self.assertFalse( self.exec_client.is_connected()) # No exceptions raised def test_dispose(self): # Arrange # Act self.exec_client.dispose() # Assert self.assertFalse( self.exec_client.is_connected()) # No exceptions raised def test_submit_order_when_not_connected_logs_and_does_not_send(self): # Arrange strategy = TradingStrategy("000") order = self.order_factory.market( ETHUSDT_BINANCE.symbol, OrderSide.BUY, Quantity(100), ) command = SubmitOrder( BINANCE, self.trader_id, self.account_id, strategy.id, PositionId.null(), order, self.uuid_factory.generate(), self.clock.utc_now(), ) # Act self.exec_client.submit_order(command) # Assert self.assertEqual(OrderState.INITIALIZED, order.state) def test_submit_bracket_order_when_not_connected_logs_and_does_not_send( self): # Arrange strategy = TradingStrategy("000") entry = self.order_factory.market( ETHUSDT_BINANCE.symbol, OrderSide.BUY, Quantity(100), ) bracket = self.order_factory.bracket(entry, Price("500.00000")) command = SubmitBracketOrder( BINANCE, self.trader_id, self.account_id, strategy.id, bracket, self.uuid_factory.generate(), self.clock.utc_now(), ) # Act self.exec_client.submit_bracket_order(command) # Assert self.assertEqual(OrderState.INITIALIZED, entry.state) def test_cancel_order_when_not_connected_logs_and_does_not_send(self): # Arrange order = self.order_factory.market( ETHUSDT_BINANCE.symbol, OrderSide.BUY, Quantity(100), ) command = CancelOrder( BINANCE, self.trader_id, self.account_id, order.cl_ord_id, self.uuid_factory.generate(), self.clock.utc_now(), ) # Act self.exec_client.cancel_order(command) # Assert self.assertTrue(True) # No exceptions raised def test_modify_order_when_not_connected_logs_and_does_not_send(self): # Arrange order = self.order_factory.stop_market( ETHUSDT_BINANCE.symbol, OrderSide.BUY, Quantity(100), Price("1000.00"), ) command = ModifyOrder( BINANCE, self.trader_id, self.account_id, order.cl_ord_id, Quantity(100), Price("1010.00"), self.uuid_factory.generate(), self.clock.utc_now(), ) # Act self.exec_client.modify_order(command) # Assert self.assertTrue(True) # No exceptions raised
def position_which_is_closed(position_id, close_price=None) -> Position: if close_price is None: close_price = Price("1.0001") order_factory = OrderFactory( strategy_id=StrategyId("S", "001"), id_tag_trader=IdTag("001"), id_tag_strategy=IdTag("001"), ) order = order_factory.market( TestStubs.symbol_audusd_fxcm(), OrderSide.SELL, Quantity(100000), ) filled1 = OrderFilled( TestStubs.account_id(), order.cl_ord_id, OrderId("1"), ExecutionId(order.cl_ord_id.value.replace('O', 'E')), position_id, StrategyId("S", "1"), order.symbol, order.side, order.quantity, Quantity(), close_price, Money(0, USD), LiquiditySide.TAKER, USD, # Stub event USD, # Stub event UNIX_EPOCH + timedelta(minutes=5), uuid4(), UNIX_EPOCH + timedelta(minutes=5), ) filled2 = OrderFilled( TestStubs.account_id(), order.cl_ord_id, OrderId("2"), ExecutionId(order.cl_ord_id.value.replace('O', 'E')), position_id, StrategyId("S", "1"), order.symbol, OrderSide.BUY, order.quantity, Quantity(), close_price, Money(0, USD), LiquiditySide.TAKER, USD, # Stub event USD, # Stub event UNIX_EPOCH + timedelta(minutes=5), uuid4(), UNIX_EPOCH + timedelta(minutes=5), ) position = Position(filled1) position.apply(filled2) return position