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( Money(1_000_000, USD), Money(0, USD), Money(1_000_000, USD), ), ], margins=[], 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 = TestEventStubs.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 = TestEventStubs.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 = TestEventStubs.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 = TestEventStubs.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( TestEventStubs.position_opened(position1)) self.portfolio.update_position( TestEventStubs.position_opened(position2)) self.portfolio.update_position( TestEventStubs.position_opened(position3)) position3.apply(fill4) self.cache.update_position(position3) self.portfolio.update_position( TestEventStubs.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()
def test_update_positions(self): # Arrange self.portfolio.register_account(self.account) # Create a closed position order1 = self.order_factory.market( BTCUSDT_BINANCE.id, OrderSide.BUY, Quantity("10.50000000"), ) order2 = self.order_factory.market( BTCUSDT_BINANCE.id, 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.id, OrderSide.BUY, Quantity("10.00000000"), ) filled3 = TestStubs.event_order_filled( order3, instrument=BTCUSDT_BINANCE, position_id=PositionId("P-2"), strategy_id=StrategyId("S", "1"), fill_price=Price("25000.00"), ) position2 = Position(filled3) # Update the last quote last = QuoteTick( BTCUSDT_BINANCE.id, 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.id))
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.id, Price("0.80501"), Price("0.80505"), Quantity(1), Quantity(1), UNIX_EPOCH, ) last_gbpusd = QuoteTick( GBPUSD_SIM.id, 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.id, OrderSide.BUY, Quantity(100000), ) order2 = self.order_factory.market( GBPUSD_SIM.id, 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.id)) self.assertEqual(Money("2606.30", USD), self.portfolio.market_value(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_position_closed_and_reopened_returns_expected_attributes(self): # Arrange order = self.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(150000), ) fill1 = TestEventStubs.order_filled( order, instrument=AUDUSD_SIM, position_id=PositionId("P-123456"), strategy_id=StrategyId("S-001"), last_px=Price.from_str("1.00001"), ts_filled_ns=1_000_000_000, ) position = Position(instrument=AUDUSD_SIM, fill=fill1) fill2 = OrderFilled( self.trader_id, StrategyId("S-001"), self.account_id, order.instrument_id, order.client_order_id, VenueOrderId("2"), TradeId("E2"), PositionId("P-123456"), OrderSide.SELL, OrderType.MARKET, order.quantity, Price.from_str("1.00011"), AUDUSD_SIM.quote_currency, Money(0, USD), LiquiditySide.TAKER, UUID4(), 2_000_000_000, 0, ) position.apply(fill2) fill3 = OrderFilled( self.trader_id, StrategyId("S-001"), self.account_id, order.instrument_id, order.client_order_id, VenueOrderId("2"), TradeId("E3"), PositionId("P-123456"), OrderSide.BUY, OrderType.MARKET, order.quantity, Price.from_str("1.00012"), AUDUSD_SIM.quote_currency, Money(0, USD), LiquiditySide.TAKER, UUID4(), 3_000_000_000, 0, ) # Act position.apply(fill3) # Assert last = Price.from_str("1.00030") assert position.is_opposite_side(fill2.order_side) assert position.quantity == Quantity.from_int(150000) assert position.side == PositionSide.LONG assert position.ts_opened == 1_000_000_000 assert position.duration_ns == 0 assert position.avg_px_open == Decimal("1.00001") assert position.event_count == 3 assert position.ts_closed == 0 assert position.avg_px_close == Decimal("1.00011") assert position.is_long assert position.is_open assert not position.is_short assert not position.is_closed assert position.realized_points == Decimal("0.00010") assert position.realized_return == Decimal( "0.00009999900000999990000099999000") assert position.realized_pnl == Money(12.00, USD) assert position.unrealized_pnl(last) == Money(43.50, USD) assert position.total_pnl(last) == Money(55.50, USD) assert position.commissions() == [Money(3.00, USD)] assert repr( position) == "Position(LONG 150_000 AUD/USD.SIM, id=P-123456)"
def test_submit_order_when_position_already_closed_then_denies(self): # Arrange self.exec_engine.start() strategy = TradingStrategy() strategy.register( trader_id=self.trader_id, portfolio=self.portfolio, msgbus=self.msgbus, cache=self.cache, clock=self.clock, logger=self.logger, ) order1 = strategy.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) order2 = strategy.order_factory.market( AUDUSD_SIM.id, OrderSide.SELL, Quantity.from_int(100000), ) order3 = strategy.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) submit_order1 = SubmitOrder( trader_id=self.trader_id, strategy_id=strategy.id, position_id=None, order=order1, command_id=self.uuid_factory.generate(), ts_init=self.clock.timestamp_ns(), ) self.risk_engine.execute(submit_order1) self.exec_engine.process(TestStubs.event_order_submitted(order1)) self.exec_engine.process(TestStubs.event_order_accepted(order1)) self.exec_engine.process( TestStubs.event_order_filled(order1, AUDUSD_SIM)) submit_order2 = SubmitOrder( trader_id=self.trader_id, strategy_id=strategy.id, position_id=PositionId("P-19700101-000000-000-000-1"), order=order2, command_id=self.uuid_factory.generate(), ts_init=self.clock.timestamp_ns(), ) self.risk_engine.execute(submit_order2) self.exec_engine.process(TestStubs.event_order_submitted(order2)) self.exec_engine.process(TestStubs.event_order_accepted(order2)) self.exec_engine.process( TestStubs.event_order_filled(order2, AUDUSD_SIM)) submit_order3 = SubmitOrder( trader_id=self.trader_id, strategy_id=strategy.id, position_id=PositionId("P-19700101-000000-000-000-1"), order=order3, command_id=self.uuid_factory.generate(), ts_init=self.clock.timestamp_ns(), ) # Act self.risk_engine.execute(submit_order3) # Assert assert self.exec_engine.command_count == 2 assert self.exec_client.calls == [ "_start", "submit_order", "submit_order" ]
def test_position_exists_when_no_position_returns_false(self): # Arrange # Act # Assert self.assertFalse(self.database.position_exists(PositionId('P-123456')))
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 = TestEventStubs.order_filled( order1, instrument=AUDUSD_SIM, position_id=PositionId("P-123456"), strategy_id=StrategyId("S-001"), ) fill2 = TestEventStubs.order_filled( order2, instrument=AUDUSD_SIM, position_id=PositionId("P-123456"), strategy_id=StrategyId("S-001"), last_px=Price.from_str("1.00001"), ) fill3 = TestEventStubs.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 assert position.quantity == Quantity.zero() assert position.side == PositionSide.FLAT assert position.ts_opened == 0 assert position.avg_px_open == Decimal("1.000005") assert position.event_count == 3 assert position.client_order_ids == [ order1.client_order_id, order2.client_order_id, order3.client_order_id, ] assert position.ts_closed == 0 assert position.avg_px_close == Decimal("1.0001") assert not position.is_long assert not position.is_short assert not position.is_open assert position.is_closed assert position.realized_pnl == Money(11.00, USD) assert position.unrealized_pnl(last) == Money(0, USD) assert position.total_pnl(last) == Money(11.00, USD) assert position.commissions() == [Money(8.00, USD)] assert repr(position) == "Position(FLAT AUD/USD.SIM, id=P-123456)"
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( Money(10.00000000, BTC), Money(0.00000000, BTC), Money(10.00000000, BTC), ), ], margins=[], 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 = TestEventStubs.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( TestEventStubs.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_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( 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={}, # 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 = TestStubs.event_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 = TestStubs.event_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_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( 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={}, 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 = TestEventStubs.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( TestEventStubs.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( 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={}, 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(TestEventStubs.order_submitted(order)) self.exec_engine.process(TestEventStubs.order_accepted(order)) fill = TestEventStubs.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( TestEventStubs.position_opened(position)) # Act result = self.portfolio.unrealized_pnls(BITMEX) # # Assert assert result == {}
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( 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), ), AccountBalance( Money(100000.00000000, USDT), Money(0.00000000, USDT), Money(100000.00000000, USDT), ), ], margins=[], 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 = TestEventStubs.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( TestEventStubs.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_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( 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={}, 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(TestEventStubs.order_submitted(order1)) self.cache.update_order(order1) order1.apply(TestEventStubs.order_accepted(order1)) self.cache.update_order(order1) fill1 = TestEventStubs.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 = TestEventStubs.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 = TestEventStubs.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_update_orders_open_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( 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), ), AccountBalance( Money(100000.00000000, USDT), Money(0.00000000, USDT), Money(100000.00000000, USDT), ), ], margins=[], info={}, event_id=UUID4(), ts_event=0, ts_init=0, ) self.portfolio.update_account(state) # Create two open 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(TestEventStubs.order_submitted(order1)) self.cache.update_order(order1) order1.apply(TestEventStubs.order_accepted(order1)) self.cache.update_order(order1) filled1 = TestEventStubs.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_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)) order1_filled = OrderFilled( self.account_id, order1.id, ExecutionId('E1'), PositionIdBroker('T123456'), order1.symbol, order1.side, order1.quantity, Price(1.00000, 5), Currency.USD, UNIX_EPOCH, GUID(uuid.uuid4()), UNIX_EPOCH) order2_filled = OrderFilled( self.account_id, order2.id, ExecutionId('E2'), PositionIdBroker('T123456'), order2.symbol, order2.side, order2.quantity, Price(1.00001, 5), Currency.USD, UNIX_EPOCH, GUID(uuid.uuid4()), UNIX_EPOCH) order3_filled = OrderFilled( self.account_id, order3.id, ExecutionId('E3'), PositionIdBroker('T123456'), order3.symbol, order3.side, order3.quantity, Price(1.00010, 5), Currency.USD, UNIX_EPOCH, GUID(uuid.uuid4()), UNIX_EPOCH) last = Tick(AUDUSD_FXCM, Price(1.00050, 5), Price(1.00048, 5), Volume(1), Volume(1), UNIX_EPOCH) # Act position = Position(PositionId('P-123456'), order1_filled) position.apply(order2_filled) position.apply(order3_filled) # Assert self.assertEqual(Quantity(), position.quantity) self.assertEqual(MarketPosition.FLAT, position.market_position) self.assertEqual(UNIX_EPOCH, position.opened_time) self.assertEqual(1.000005, position.average_open_price) self.assertEqual(3, position.event_count) self.assertEqual([order1.id, order2.id, order3.id], position.get_order_ids()) self.assertEqual(ExecutionId('E3'), position.last_execution_id) self.assertEqual(PositionIdBroker('T123456'), position.id_broker) self.assertEqual(UNIX_EPOCH, position.closed_time) self.assertEqual(1.0001, position.average_close_price) self.assertFalse(position.is_long) self.assertFalse(position.is_short) self.assertTrue(position.is_closed) self.assertEqual(9.499999999995623e-05, position.realized_points) self.assertEqual(9.499952500233122e-05, position.realized_return) self.assertEqual(Money(19.000, Currency.USD), position.realized_pnl) self.assertEqual(0.0, position.unrealized_points(last)) self.assertEqual(0.0, position.unrealized_return(last)) self.assertEqual(Money(00, Currency.USD), position.unrealized_pnl(last)) self.assertEqual(9.499999999995623e-05, position.total_points(last)) self.assertEqual(9.499952500233122e-05, position.total_return(last)) self.assertEqual(Money(19.000, Currency.USD), position.total_pnl(last))
def position_id(): return PositionId("1")
def test_update_position_for_closed_position(self): # Arrange order1 = self.strategy.order_factory.market( AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(100000), ) position_id = PositionId('P-1') self.database.add_order(order1) order1.apply(TestStubs.event_order_submitted(order1)) self.database.update_order(order1) order1.apply(TestStubs.event_order_accepted(order1)) self.database.update_order(order1) order1.apply( TestStubs.event_order_filled( order1, instrument=AUDUSD_SIM, position_id=position_id, fill_price=Price("1.00001"), )) self.database.update_order(order1) # Act position = Position(order1.last_event) self.database.add_position(position) order2 = self.strategy.order_factory.market( AUDUSD_SIM.symbol, OrderSide.SELL, Quantity(100000), ) self.database.add_order(order2) order2.apply(TestStubs.event_order_submitted(order2)) self.database.update_order(order2) order2.apply(TestStubs.event_order_accepted(order2)) self.database.update_order(order2) filled = TestStubs.event_order_filled( order2, instrument=AUDUSD_SIM, position_id=position_id, fill_price=Price("1.00001"), ) order2.apply(filled) self.database.update_order(order2) position.apply(filled) # Act self.database.update_position(position) # Assert self.assertEqual(position, self.database.load_position(position.id))
def test_is_position_closed_when_not_implemented_raises_exception(self): self.assertRaises(NotImplementedError, self.facade.is_position_closed, PositionId("P-123456"))
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 = TestEventStubs.order_filled( order1, instrument=AUDUSD_SIM, position_id=PositionId("P-19700101-000000-000-001-1"), ) position = Position(instrument=AUDUSD_SIM, fill=fill1) fill2 = TestEventStubs.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 assert position.quantity == Quantity.zero() assert position.side == PositionSide.FLAT assert position.ts_opened == 0 assert position.avg_px_open == Decimal("1.0") assert position.event_count == 2 assert position.client_order_ids == [ order1.client_order_id, order2.client_order_id ] assert position.trade_ids == [ TradeId("E-19700101-000000-000-001-1"), TradeId("E-19700101-000000-000-001-2"), ] assert position.ts_closed == 0 assert position.avg_px_close == Decimal("1.0") assert not position.is_long assert not position.is_short assert not position.is_open assert position.is_closed assert position.realized_points == 0 assert position.realized_return == 0 assert position.realized_pnl == Money(-4.00, USD) assert position.unrealized_pnl(last) == Money(0, USD) assert position.total_pnl(last) == Money(-4.00, USD) assert position.commissions() == [Money(4.00, USD)] assert repr( position ) == "Position(FLAT AUD/USD.SIM, id=P-19700101-000000-000-001-1)"
def test_position_exists_when_no_position_returns_false(self): # Arrange # Act # Assert self.assertFalse(self.cache.position_exists(PositionId("P-123456")))
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 = TestEventStubs.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 = TestEventStubs.order_filled( order2, instrument=ETHUSDT_BINANCE, position_id=PositionId("P-19700101-000000-000-001-1"), last_px=Price.from_int(99), ) position.apply(fill2) assert position.quantity == Quantity.from_int(29) assert position.realized_pnl == Money(-0.28830000, USDT) assert position.avg_px_open == Decimal("99.41379310344827586206896552") fill3 = TestEventStubs.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) assert position.quantity == Quantity.from_int(20) assert position.realized_pnl == Money(13.89666207, USDT) assert position.avg_px_open == Decimal("99.41379310344827586206896552") fill4 = TestEventStubs.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) assert position.quantity == Quantity.from_int(16) assert position.realized_pnl == Money(36.19948966, USDT) assert position.avg_px_open == Decimal("99.41379310344827586206896552") fill5 = TestEventStubs.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) assert position.quantity == Quantity.from_int(19) assert position.realized_pnl == Money(36.16858966, USDT) assert position.avg_px_open == Decimal("99.98003629764065335753176042") assert ( repr(position) == "Position(LONG 19.00000 ETHUSDT.BINANCE, id=P-19700101-000000-000-001-1)" )
def test_update_position_for_closed_position(self): # Arrange order1 = self.strategy.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity(100000), ) position_id = PositionId('P-1') self.cache.add_order(order1, position_id) order1.apply(TestStubs.event_order_submitted(order1)) self.cache.update_order(order1) order1.apply(TestStubs.event_order_accepted(order1)) self.cache.update_order(order1) order1_filled = TestStubs.event_order_filled( order1, instrument=AUDUSD_SIM, position_id=PositionId('P-1'), fill_price=Price("1.00001"), ) position = Position(order1_filled) self.cache.add_position(position) order2 = self.strategy.order_factory.market( AUDUSD_SIM.id, OrderSide.SELL, Quantity(100000), ) order2.apply(TestStubs.event_order_submitted(order2)) self.cache.update_order(order2) order2.apply(TestStubs.event_order_accepted(order2)) self.cache.update_order(order2) order2_filled = TestStubs.event_order_filled( order2, instrument=AUDUSD_SIM, position_id=PositionId('P-1'), fill_price=Price("1.00001"), ) position.apply(order2_filled) # Act self.cache.update_position(position) # Assert self.assertTrue(self.cache.position_exists(position.id)) self.assertIn(position.id, self.cache.position_ids()) self.assertIn(position, self.cache.positions()) self.assertIn(position, self.cache.positions_closed()) self.assertIn( position, self.cache.positions_closed(instrument_id=position.instrument_id)) self.assertIn( position, self.cache.positions_closed(strategy_id=self.strategy.id)) self.assertIn( position, self.cache.positions_closed(instrument_id=position.instrument_id, strategy_id=self.strategy.id)) self.assertNotIn(position, self.cache.positions_open()) self.assertNotIn( position, self.cache.positions_open(instrument_id=position.instrument_id)) self.assertNotIn( position, self.cache.positions_open(strategy_id=self.strategy.id)) self.assertNotIn( position, self.cache.positions_open(instrument_id=position.instrument_id, strategy_id=self.strategy.id)) self.assertEqual(position, self.cache.position(position_id)) self.assertEqual(0, self.cache.positions_open_count()) self.assertEqual(1, self.cache.positions_closed_count()) self.assertEqual(1, self.cache.positions_total_count())
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 = TestEventStubs.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 = TestEventStubs.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) assert position.quantity == Quantity.from_str("29.000000") assert position.realized_pnl == Money(-289.98300000, USDT) assert position.avg_px_open == Decimal("9999.413793103448275862068966") fill3 = TestEventStubs.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) assert position.quantity == Quantity.from_int(20) assert position.realized_pnl == Money(-365.71613793, USDT) assert position.avg_px_open == Decimal("9999.413793103448275862068966") fill4 = TestEventStubs.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) assert position.quantity == Quantity.from_int(23) assert position.realized_pnl == Money(-395.72513793, USDT) assert position.avg_px_open == Decimal("9999.881559220389805097451274") fill5 = TestEventStubs.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) assert position.quantity == Quantity.from_int(19) assert position.realized_pnl == Money(-415.27137481, USDT) assert position.avg_px_open == Decimal("9999.881559220389805097451274") assert ( repr(position) == "Position(LONG 19.000000 BTCUSDT.BINANCE, id=P-19700101-000000-000-001-1)" )
def test_position_partial_fills_with_sell_order_returns_expected_attributes(self): # Arrange order = self.order_factory.market( AUDUSD_FXCM, OrderSide.SELL, Quantity(100000)) order_partially_filled1 = OrderPartiallyFilled( self.account_id, order.id, ExecutionId('E1'), PositionIdBroker('T123456'), order.symbol, order.side, Quantity(50000), Quantity(50000), Price(1.00001, 5), Currency.USD, UNIX_EPOCH, GUID(uuid.uuid4()), UNIX_EPOCH) order_partially_filled2 = OrderPartiallyFilled( self.account_id, order.id, ExecutionId('E2'), PositionIdBroker('T123456'), order.symbol, order.side, Quantity(100000), Quantity(), Price(1.00002, 5), Currency.USD, UNIX_EPOCH, GUID(uuid.uuid4()), UNIX_EPOCH) position = Position(PositionId('P-123456'), order_partially_filled1) last = Tick(AUDUSD_FXCM, Price(1.00050, 5), Price(1.00048, 5), Volume(1), Volume(1), UNIX_EPOCH) # Act position.apply(order_partially_filled2) # Assert self.assertEqual(Quantity(100000), position.quantity) self.assertEqual(MarketPosition.SHORT, position.market_position) self.assertEqual(UNIX_EPOCH, position.opened_time) self.assertEqual(1.00002, position.average_open_price) self.assertEqual(2, position.event_count) self.assertEqual(ExecutionId('E2'), position.last_execution_id) self.assertEqual(PositionIdBroker('T123456'), position.id_broker) 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, Currency.USD), position.realized_pnl) self.assertEqual(-0.000460000000000127, position.unrealized_points(last)) self.assertEqual(-0.00045999080018412335, position.unrealized_return(last)) self.assertEqual(Money(-46.00, Currency.USD), position.unrealized_pnl(last)) self.assertEqual(-0.000460000000000127, position.total_points(last)) self.assertEqual(-0.00045999080018412335, position.total_return(last)) self.assertEqual(Money(-46.00, Currency.USD), position.total_pnl(last))
def position_id() -> PositionId: return PositionId("001")
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)) order_filled1 = OrderFilled( self.account_id, order.id, ExecutionId('E1'), PositionIdBroker('T123456'), order.symbol, OrderSide.BUY, order.quantity, Price(1.00001, 5), Currency.USD, UNIX_EPOCH, GUID(uuid.uuid4()), UNIX_EPOCH) position = Position(PositionId('P-123456'), order_filled1) order_filled2 = OrderFilled( self.account_id, order.id, ExecutionId('E2'), PositionIdBroker('T123456'), order.symbol, OrderSide.SELL, order.quantity, Price(1.00001, 5), Currency.USD, UNIX_EPOCH + timedelta(minutes=1), GUID(uuid.uuid4()), UNIX_EPOCH) last = Tick(AUDUSD_FXCM, Price(1.00050, 5), Price(1.00048, 5), Volume(1), Volume(1), UNIX_EPOCH) # Act position.apply(order_filled2) # Assert self.assertEqual(Quantity(), position.quantity) self.assertEqual(MarketPosition.FLAT, position.market_position) self.assertEqual(UNIX_EPOCH, position.opened_time) self.assertEqual(timedelta(minutes=1), position.open_duration) self.assertEqual(1.00001, position.average_open_price) self.assertEqual(2, position.event_count) self.assertEqual(ExecutionId('E2'), position.last_execution_id) self.assertEqual(PositionIdBroker('T123456'), position.id_broker) self.assertEqual(datetime.datetime(1970, 1, 1, 0, 1, tzinfo=datetime.timezone.utc), position.closed_time) self.assertEqual(1.00001, position.average_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, Currency.USD), position.realized_pnl) self.assertEqual(0.0, position.unrealized_points(last)) self.assertEqual(0.0, position.unrealized_return(last)) self.assertEqual(Money(0, Currency.USD), position.unrealized_pnl(last)) self.assertEqual(0.0, position.total_points(last)) self.assertEqual(0.0, position.total_return(last)) self.assertEqual(Money(0, Currency.USD), position.total_pnl(last))
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.id, Price("376.05"), Price("377.10"), Quantity("16"), Quantity("25"), UNIX_EPOCH, ) last_btcusd = QuoteTick( BTCUSD_BITMEX.id, 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.id, 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.id)) self.assertEqual(Money("0.00000000", ETH), self.portfolio.unrealized_pnl(ETHUSD_BITMEX.id))
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)) order_filled1 = OrderFilled( self.account_id, order1.id, ExecutionId('E123456'), PositionIdBroker('T123456'), order1.symbol, order1.side, order1.quantity, Price(1.00000, 5), Currency.USD, UNIX_EPOCH, GUID(uuid.uuid4()), UNIX_EPOCH) position = Position(PositionId('P-123456'), order_filled1) order_filled2 = OrderPartiallyFilled( self.account_id, order2.id, ExecutionId('E1234561'), PositionIdBroker('T123456'), order2.symbol, order2.side, Quantity(50000), Quantity(50000), Price(1.00001, 5), Currency.USD, UNIX_EPOCH, GUID(uuid.uuid4()), UNIX_EPOCH) order_filled3 = OrderPartiallyFilled( self.account_id, order2.id, ExecutionId('E1234562'), PositionIdBroker('T123456'), order2.symbol, order2.side, Quantity(100000), Quantity(), Price(1.00003, 5), Currency.USD, UNIX_EPOCH, GUID(uuid.uuid4()), UNIX_EPOCH) last = Tick(AUDUSD_FXCM, Price(1.00050, 5), Price(1.00048, 5), Volume(1), Volume(1), UNIX_EPOCH) # Act position.apply(order_filled2) position.apply(order_filled3) # Assert self.assertEqual(Quantity(), position.quantity) self.assertEqual(MarketPosition.FLAT, position.market_position) self.assertEqual(UNIX_EPOCH, position.opened_time) self.assertEqual(1.0, position.average_open_price) self.assertEqual(3, position.event_count) self.assertEqual([order1.id, order2.id], position.get_order_ids()) self.assertEqual(ExecutionId('E1234562'), position.last_execution_id) self.assertEqual(PositionIdBroker('T123456'), position.id_broker) self.assertEqual(UNIX_EPOCH, position.closed_time) self.assertEqual(1.00003, position.average_close_price) self.assertFalse(position.is_long) self.assertFalse(position.is_short) self.assertTrue(position.is_closed) self.assertEqual(-2.999999999997449e-05, position.realized_points) self.assertEqual(-2.999999999997449e-05, position.realized_return) self.assertEqual(Money(-3.000, Currency.USD), position.realized_pnl) self.assertEqual(0.0, position.unrealized_points(last)) self.assertEqual(0.0, position.unrealized_return(last)) self.assertEqual(Money(00, Currency.USD), position.unrealized_pnl(last)) self.assertEqual(-2.999999999997449e-05, position.total_points(last)) self.assertEqual(-2.999999999997449e-05, position.total_return(last)) self.assertEqual(Money(-3.000, Currency.USD), position.total_pnl(last))
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.id, 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.id, 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.id, 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.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.assertEqual({}, self.portfolio.market_values(BINANCE))
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( Money(1_000_000, USD), Money(0, USD), Money(1_000_000, USD), ), ], margins=[], 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 = TestEventStubs.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( TestEventStubs.position_opened(position)) order2 = self.order_factory.market( AUDUSD_SIM.id, OrderSide.SELL, Quantity.from_int(100000), ) order2_filled = TestEventStubs.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( TestEventStubs.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()