def test_exceed_free_balance_multi_currency_raises_account_balance_negative_exception( 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( Money(10.00000000, BTC), Money(0.00000000, BTC), Money(10.00000000, BTC), ), 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 order order = self.order_factory.market( # <-- order value 150_000 USDT BTCUSDT_BINANCE.id, OrderSide.BUY, Quantity.from_str("3.0"), ) self.cache.add_order(order, position_id=None) self.exec_engine.process( TestEventStubs.order_submitted(order, account_id=account_id)) # Act, Assert: push account to negative balance (wouldn't normally be allowed by risk engine) with pytest.raises(AccountBalanceNegative): fill = TestEventStubs.order_filled( order, instrument=BTCUSDT_BINANCE, account_id=account_id, last_px=Price.from_str("100_000"), ) self.exec_engine.process(fill)
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_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 __init__( self, loop: asyncio.AbstractEventLoop, client: BetfairClient, base_currency: Currency, msgbus: MessageBus, cache: Cache, clock: LiveClock, logger: Logger, market_filter: Dict, instrument_provider: BetfairInstrumentProvider, ): super().__init__( loop=loop, client_id=ClientId(BETFAIR_VENUE.value), venue=BETFAIR_VENUE, oms_type=OMSType.NETTING, account_type=AccountType.BETTING, base_currency=base_currency, instrument_provider=instrument_provider or BetfairInstrumentProvider( client=client, logger=logger, filters=market_filter), msgbus=msgbus, cache=cache, clock=clock, logger=logger, ) self._client: BetfairClient = client self.stream = BetfairOrderStreamClient( client=self._client, logger=logger, message_handler=self.handle_order_stream_update, ) self.venue_order_id_to_client_order_id: Dict[VenueOrderId, ClientOrderId] = {} self.pending_update_order_client_ids: Set[Tuple[ClientOrderId, VenueOrderId]] = set() self.published_executions: Dict[ClientOrderId, TradeId] = defaultdict(list) self._set_account_id(AccountId(BETFAIR_VENUE.value, "001")) # TODO(cs): Temporary AccountFactory.register_calculated_account(BETFAIR_VENUE.value)
def __init__( self, loop: asyncio.AbstractEventLoop, client: BetfairClient, account_id: AccountId, base_currency: Currency, msgbus: MessageBus, cache: Cache, clock: LiveClock, logger: Logger, market_filter: Dict, instrument_provider: BetfairInstrumentProvider, ): super().__init__( loop=loop, client_id=ClientId(BETFAIR_VENUE.value), instrument_provider=instrument_provider or BetfairInstrumentProvider(client=client, logger=logger, market_filter=market_filter), venue_type=VenueType.EXCHANGE, account_id=account_id, account_type=AccountType.BETTING, base_currency=base_currency, msgbus=msgbus, cache=cache, clock=clock, logger=logger, config={"name": "BetfairExecClient"}, ) self._client: BetfairClient = client self.stream = BetfairOrderStreamClient( client=self._client, logger=logger, message_handler=self.handle_order_stream_update, ) self.venue_order_id_to_client_order_id: Dict[VenueOrderId, ClientOrderId] = {} self.pending_update_order_client_ids: Set[Tuple[ClientOrderId, VenueOrderId]] = set() self.published_executions: Dict[ClientOrderId, ExecutionId] = defaultdict(list) AccountFactory.register_calculated_account(account_id.issuer)
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_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_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_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_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 margin_account(): return AccountFactory.create( TestEventStubs.margin_account_state( account_id=TestIdStubs.account_id()))
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_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( 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) 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 = 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(50000), ) 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.00000"), ) position.apply(order2_filled) # Act self.portfolio.update_position( TestEventStubs.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 __init__( self, loop: asyncio.AbstractEventLoop, client: FTXHttpClient, msgbus: MessageBus, cache: Cache, clock: LiveClock, logger: Logger, instrument_provider: FTXInstrumentProvider, us: bool = False, account_polling_interval: int = 60, calculated_account: bool = False, ): super().__init__( loop=loop, client_id=ClientId(FTX_VENUE.value), venue=FTX_VENUE, oms_type=OMSType.NETTING, instrument_provider=instrument_provider, account_type=AccountType.MARGIN, base_currency=USD, msgbus=msgbus, cache=cache, clock=clock, logger=logger, ) self._http_client = client self._ws_client = FTXWebSocketClient( loop=loop, clock=clock, logger=logger, msg_handler=self._handle_ws_message, reconnect_handler=self._handle_ws_reconnect, key=client.api_key, secret=client.api_secret, us=us, auto_ping_interval=15.0, # Recommended by FTX # log_send=True, # Uncomment for development and debugging # log_recv=True, # Uncomment for development and debugging ) self._ws_buffer: List[bytes] = [] # Tasks self._task_poll_account: Optional[asyncio.Task] = None self._task_buffer_ws_msgs: Optional[asyncio.Task] = None # Hot Caches self._instrument_ids: Dict[str, InstrumentId] = {} self._order_ids: Dict[VenueOrderId, ClientOrderId] = {} self._order_types: Dict[VenueOrderId, OrderType] = {} self._triggers: Dict[int, VenueOrderId] = {} self._open_triggers: Dict[int, ClientOrderId] = {} # Settings self._account_polling_interval = account_polling_interval self._calculated_account = calculated_account self._initial_leverage_set = False if us: self._log.info("Set FTX US.", LogColor.BLUE) self._log.info( f"Set account polling interval {self._account_polling_interval}s.", LogColor.BLUE, ) if self._calculated_account: self._log.info("Set calculated account.", LogColor.BLUE) AccountFactory.register_calculated_account(FTX_VENUE.value)
def betting_account(account_id=None): return AccountFactory.create( TestEventStubs.betting_account_state( account_id=account_id or TestIdStubs.account_id()))
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_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 cash_account(): return AccountFactory.create( TestEventStubs.cash_account_state( account_id=TestIdStubs.account_id()))