def test_account_state_event_to_from_dict_and_str_repr(self): # Arrange uuid = UUID4() balance = AccountBalance( currency=USD, total=Money(1525000, USD), locked=Money(0, USD), free=Money(1525000, USD), ) event = AccountState( account_id=AccountId("SIM", "000"), account_type=AccountType.MARGIN, base_currency=USD, reported=True, balances=[balance], info={}, event_id=uuid, ts_event=0, ts_init=0, ) # Act, Assert assert AccountState.from_dict(AccountState.to_dict(event)) == event assert ( str(event) == f"AccountState(account_id=SIM-000, account_type=MARGIN, base_currency=USD, is_reported=True, balances=[AccountBalance(total=1_525_000.00 USD, locked=0.00 USD, free=1_525_000.00 USD)], event_id={uuid})" # noqa ) assert ( repr(event) == f"AccountState(account_id=SIM-000, account_type=MARGIN, base_currency=USD, is_reported=True, balances=[AccountBalance(total=1_525_000.00 USD, locked=0.00 USD, free=1_525_000.00 USD)], event_id={uuid})" # noqa )
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)
def margin_account_state(account_id=None) -> AccountState: return AccountState( account_id=account_id or TestIdStubs.account_id(), account_type=AccountType.MARGIN, base_currency=USD, reported=True, # reported balances=[ AccountBalance( Money(1_000_000, USD), Money(0, USD), Money(1_000_000, USD), ), ], margins=[ MarginBalance( Money(10_000, USD), Money(50_000, USD), TestIdStubs.audusd_id(), ), ], info={}, event_id=UUID4(), ts_event=0, ts_init=0, )
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_serialize_and_deserialize_account_state_without_base_currency_events(self): # Arrange event = AccountState( account_id=AccountId("SIM", "000"), account_type=AccountType.MARGIN, base_currency=None, reported=True, balances=[ AccountBalance( Money(10000, USDT), Money(0, USDT), Money(10000, USDT), ), ], margins=[], info={}, event_id=UUID4(), ts_event=0, ts_init=1_000_000_000, ) # Act serialized = self.serializer.serialize(event) deserialized = self.serializer.deserialize(serialized) # Assert assert deserialized == event
def betfair_account_to_account_state( account_detail, account_funds, event_id, ts_event, ts_init, account_id="001", ) -> AccountState: currency = Currency.from_str(account_detail["currencyCode"]) balance = float(account_funds["availableToBetBalance"]) locked = -float( account_funds["exposure"]) if account_funds["exposure"] else 0.0 free = balance - locked return AccountState( account_id=AccountId(issuer=BETFAIR_VENUE.value, number=account_id), account_type=AccountType.BETTING, base_currency=currency, reported=False, balances=[ AccountBalance( total=Money(balance, currency), locked=Money(locked, currency), free=Money(free, currency), ), ], margins=[], info={ "funds": account_funds, "detail": account_detail }, event_id=event_id, ts_event=ts_event, ts_init=ts_init, )
async def test_account_statement(self): with patch.object(BetfairClient, "request", return_value=BetfairResponses.account_details()): detail = await self.client.get_account_details() with patch.object( BetfairClient, "request", return_value=BetfairResponses.account_funds_no_exposure()): funds = await self.client.get_account_funds() result = betfair_account_to_account_state( account_detail=detail, account_funds=funds, event_id=self.uuid, ts_event=0, ts_init=0, ) expected = AccountState( account_id=AccountId(issuer="BETFAIR", number="Testy-McTest"), account_type=AccountType.CASH, base_currency=GBP, reported=True, # reported balances=[ AccountBalance(GBP, Money(1000.0, GBP), Money(0.00, GBP), Money(1000.0, GBP)) ], info={ "funds": funds, "detail": detail }, event_id=self.uuid, ts_event=result.ts_event, ts_init=result.ts_init, ) assert result == expected
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_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_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_generate_accounts_report_with_initial_account_state_returns_expected( self): # Arrange state = AccountState( account_id=AccountId("BITMEX", "1513111"), account_type=AccountType.MARGIN, base_currency=BTC, reported=True, balances=[ AccountBalance( currency=BTC, total=Money(10.00000000, BTC), free=Money(10.00000000, BTC), locked=Money(0.00000000, BTC), ) ], info={}, event_id=UUID4(), ts_event=0, ts_init=0, ) account = MarginAccount(state) # Act report = ReportProvider.generate_account_report(account) # Assert assert len(report) == 1
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( BTC, Money(1.00000000, BTC), Money(0.00000000, BTC), Money(1.00000000, BTC), ), AccountBalance( ADA, Money(1000.00000000, ADA), Money(0.00000000, ADA), Money(1000.00000000, ADA), ), ], 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 = TestStubs.event_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_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( 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, ) # 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_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_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_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 serialize(state: AccountState): result = [] base = state.to_dict(state) del base["balances"] for balance in state.balances: data = { "balance_currency": balance.currency.code, "balance_total": balance.total.as_double(), "balance_locked": balance.locked.as_double(), "balance_free": balance.free.as_double(), } result.append({**base, **data}) return result
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 _deserialize(values): balances = [] for v in values: balances.append( dict( currency=v["balance_currency"], total=v["balance_total"], locked=v["balance_locked"], free=v["balance_free"], )) state = { k: v for k, v in values[0].items() if not k.startswith("balance_") } state["balances"] = orjson.dumps(balances) return AccountState.from_dict(state)
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 betting_account_state(account_id=None) -> AccountState: return AccountState( account_id=account_id or TestIdStubs.account_id(), account_type=AccountType.BETTING, base_currency=GBP, reported=False, # reported balances=[ AccountBalance( Money(1_000, GBP), Money(0, GBP), Money(1_000, GBP), ), ], margins=[], info={}, event_id=UUID4(), ts_event=0, ts_init=0, )
def event_cash_account_state(account_id=None) -> AccountState: return AccountState( account_id=account_id or TestStubs.account_id(), account_type=AccountType.CASH, base_currency=USD, reported=True, # reported 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, )
def serialize(state: AccountState): result: Dict[Tuple[Currency, Optional[InstrumentId]], Dict] = {} base = state.to_dict(state) del base["balances"] del base["margins"] base.update({ "balance_total": None, "balance_locked": None, "balance_free": None, "balance_currency": None, "margin_initial": None, "margin_maintenance": None, "margin_currency": None, "margin_instrument_id": None, }) for balance in state.balances: key = (balance.currency, None) if key not in result: result[key] = base.copy() result[key].update({ "balance_total": balance.total.as_double(), "balance_locked": balance.locked.as_double(), "balance_free": balance.free.as_double(), "balance_currency": balance.currency.code, }) for margin in state.margins: key = (margin.currency, margin.instrument_id) if key not in result: result[key] = base.copy() result[key].update({ "margin_initial": margin.initial.as_double(), "margin_maintenance": margin.maintenance.as_double(), "margin_currency": margin.currency.code, "margin_instrument_id": margin.instrument_id.value, }) return list(result.values())
def generate_account_report(account: Account) -> pd.DataFrame: """ Generate an account report for the given optional time range. Parameters ---------- account : Account The account for the report. Returns ------- pd.DataFrame """ states = account.events if not states: return pd.DataFrame() account_states = [AccountState.to_dict(s) for s in states] balances = [{ **balance, **state } for state in account_states for balance in orjson.loads(state.pop("balances", "[]"))] if not account_states: return pd.DataFrame() report = pd.DataFrame(data=balances).set_index("ts_event").sort_index() report.index = [ unix_nanos_to_dt(ts_event) for ts_event in report.index ] del report["ts_init"] del report["type"] del report["event_id"] return report
def _deserialize(values): balances = [] for v in values: total = v.get("balance_total") if total is None: continue balances.append( dict( total=total, locked=v["balance_locked"], free=v["balance_free"], currency=v["balance_currency"], )) margins = [] for v in values: initial = v.get("margin_initial") if initial is None: continue margins.append( dict( initial=initial, maintenance=v["margin_maintenance"], currency=v["margin_currency"], instrument_id=v["margin_instrument_id"], )) state = { k: v for k, v in values[0].items() if not k.startswith("balance_") and not k.startswith("margin_") } state["balances"] = orjson.dumps(balances) state["margins"] = orjson.dumps(margins) return AccountState.from_dict(state)
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_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_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_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 == {}