def test_unrealized_pnl_when_insufficient_data_for_xrate_returns_none( self): # Arrange state = AccountState( account_id=AccountId("BITMEX", "01234"), account_type=AccountType.MARGIN, base_currency=BTC, reported=True, balances=[ AccountBalance( BTC, Money(10.00000000, BTC), Money(0.00000000, BTC), Money(10.00000000, BTC), ), AccountBalance( ETH, Money(20.00000000, ETH), Money(0.00000000, ETH), Money(20.00000000, ETH), ), ], info={}, event_id=uuid4(), updated_ns=0, timestamp_ns=0, ) self.exec_engine.process(state) order = self.order_factory.market( ETHUSD_BITMEX.id, OrderSide.BUY, Quantity.from_int(100), ) self.exec_engine.cache.add_order(order, PositionId.null()) self.exec_engine.process(TestStubs.event_order_submitted(order)) self.exec_engine.process(TestStubs.event_order_accepted(order)) fill = TestStubs.event_order_filled( order=order, instrument=ETHUSD_BITMEX, position_id=PositionId("P-123456"), strategy_id=StrategyId("S-001"), last_px=Price.from_str("376.05"), ) self.exec_engine.process(fill) position = Position(instrument=ETHUSD_BITMEX, fill=fill) self.portfolio.update_position( TestStubs.event_position_opened(position)) # Act result = self.portfolio.unrealized_pnls(BITMEX) # # Assert self.assertEqual({}, result)
def test_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_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 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_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_updated_ns=0, timestamp_ns=0, ) account = Account(state) report_provider = ReportProvider() # Act report = report_provider.generate_account_report(account) # Assert self.assertEqual(1, len(report))
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_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_account_state_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, updated_ns=0, timestamp_ns=0, ) print(event) # Act # Assert assert ( 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 == str(event)) assert ( 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 == repr(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, )
def test_account_statement(betfair_client, uuid, clock): detail = betfair_client.account.get_account_details() funds = betfair_client.account.get_account_funds() timestamp_ns = clock.timestamp_ns() result = betfair_account_to_account_state( account_detail=detail, account_funds=funds, event_id=uuid, ts_updated_ns=timestamp_ns, timestamp_ns=timestamp_ns, ) expected = AccountState( account_id=AccountId(issuer="BETFAIR", number="Testy-McTest"), account_type=AccountType.CASH, base_currency=AUD, reported=True, # reported balances=[ AccountBalance(AUD, Money(1000.0, AUD), Money(0.00, AUD), Money(1000.0, AUD)) ], info={ "funds": funds, "detail": detail }, event_id=uuid, ts_updated_ns=result.timestamp_ns, timestamp_ns=result.timestamp_ns, ) assert result == expected
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_account_when_account_returns_the_account_facade(self): # Arrange account_state = AccountState( account_id=AccountId("BINANCE", "1513111"), account_type=AccountType.CASH, base_currency=None, reported=True, balances=[ AccountBalance( BTC, Money(10.00000000, BTC), Money(0.00000000, BTC), Money(10.00000000, BTC), ) ], info={}, event_id=uuid4(), updated_ns=0, timestamp_ns=0, ) self.exec_engine.process(account_state) # Act result = self.portfolio.account(BINANCE) # Assert self.assertEqual("BINANCE", result.id.issuer)
def test_equity_with_single_asset_account_returns_expected_money(self): # Arrange event = AccountState( account_id=AccountId("SIM", "001"), account_type=AccountType.CASH, base_currency=USD, reported=True, balances=[ AccountBalance( USD, Money(100000.00, USD), Money(0.00, USD), Money(100000.00, USD), ), ], info={}, event_id=uuid4(), ts_updated_ns=0, timestamp_ns=0, ) account = Account(event) # Wire up account to portfolio account.register_portfolio(self.portfolio) self.portfolio.register_account(account) # Act result = account.equity() # Assert self.assertEqual(Money(100000.00, USD), result)
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( USDT, Money(10000, USDT), Money(0, USDT), Money(10000, USDT), ) ], info={}, event_id=uuid4(), ts_updated_ns=0, timestamp_ns=1_000_000_000, ) # Act serialized = self.serializer.serialize(event) deserialized = self.serializer.deserialize(serialized) # Assert assert deserialized == event
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_serialize_and_deserialize_account_state_with_base_currency_events(self): # Arrange event = AccountState( account_id=AccountId("SIM", "000"), account_type=AccountType.MARGIN, base_currency=USD, reported=True, balances=[ AccountBalance( Money(1525000, USD), Money(25000, USD), Money(1500000, USD), ), ], margins=[ MarginBalance( Money(5000, USD), Money(20000, USD), AUDUSD_SIM.id, ), ], 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, updated_ns, timestamp_ns, account_id="001", ) -> AccountState: currency = Currency.from_str(account_detail["currencyCode"]) balance = float(account_funds["availableToBetBalance"]) locked = -float(account_funds["exposure"]) free = balance - locked return AccountState( account_id=AccountId(issuer=BETFAIR_VENUE.value, number=account_id), account_type=AccountType.CASH, base_currency=currency, reported=True, balances=[ AccountBalance( currency=currency, total=Money(balance, currency), locked=Money(locked, currency), free=Money(free, currency), ), ], info={"funds": account_funds, "detail": account_detail}, event_id=event_id, updated_ns=updated_ns, timestamp_ns=timestamp_ns, )
def test_margin_available_for_multi_asset_account(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_updated_ns=0, timestamp_ns=0, ) account = Account(event) # Wire up account to portfolio account.register_portfolio(self.portfolio) self.portfolio.register_account(account) # Act result1 = account.margin_available(BTC) account.update_initial_margin(Money(0.00010000, BTC)) result2 = account.margin_available(BTC) account.update_maint_margin(Money(0.00020000, BTC)) result3 = account.margin_available(BTC) result4 = account.margin_available(ETH) # Assert self.assertEqual(Money(10.00000000, BTC), result1) self.assertEqual(Money(9.99990000, BTC), result2) self.assertEqual(Money(9.99970000, BTC), result3) self.assertEqual(Money(20.00000000, ETH), result4)
def test_update_maint_margin(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_updated_ns=0, timestamp_ns=0, ) # Act account = Account(event) # Wire up account to portfolio account.register_portfolio(self.portfolio) self.portfolio.register_account(account) margin = Money(0.00050000, BTC) # Act account.update_maint_margin(margin) # Assert self.assertEqual(margin, account.maint_margin(BTC)) self.assertEqual({BTC: margin}, account.maint_margins())
def test_unrealized_pnl_with_multi_asset_account_when_no_open_positions_returns_zero( 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(), updated_ns=0, timestamp_ns=0, ) account = Account(event) # Wire up account to portfolio account.register_portfolio(self.portfolio) self.portfolio.register_account(account) # Act result = account.unrealized_pnl(BTC) # Assert self.assertEqual(Money(0.00000000, BTC), result)
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 parse_account_balances_http(assets: List[BinanceFuturesAssetInfo]) -> List[AccountBalance]: balances: List[AccountBalance] = [] for a in assets: currency = Currency.from_str(a.asset) total = Decimal(a.walletBalance) locked = Decimal(a.initialMargin) + Decimal(a.maintMargin) free = total - locked balance = AccountBalance( total=Money(total, currency), locked=Money(locked, currency), free=Money(free, currency), ) balances.append(balance) return balances
def parse_account_balances_ws(raw_balances: List[BinanceFuturesBalance]) -> List[AccountBalance]: balances: List[AccountBalance] = [] for b in raw_balances: currency = Currency.from_str(b.a) free = Decimal(b.wb) locked = Decimal(b.bc) + Decimal(b.bc) total: Decimal = free + locked balance = AccountBalance( total=Money(total, currency), locked=Money(locked, currency), free=Money(free, currency), ) balances.append(balance) return balances
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 _handle_account_info(self, info: Dict[str, Any]) -> None: total = Money(info["totalAccountValue"], USD) free = Money(info["freeCollateral"], USD) locked = Money(total - free, USD) balance = AccountBalance( total=total, locked=locked, free=free, ) # TODO(cs): Uncomment for development # self._log.info(str(json.dumps(info, indent=4)), color=LogColor.GREEN) margins: List[MarginBalance] = [] # TODO(cs): Margins on FTX are fractions - determine solution # for position in info["positions"]: # margin = MarginBalance( # initial=Money(position["initialMarginRequirement"], USD), # maintenance=Money(position["maintenanceMarginRequirement"], USD), # instrument_id=InstrumentId(Symbol(position["future"]), FTX_VENUE), # ) # margins.append(margin) self.generate_account_state( balances=[balance], margins=margins, reported=True, ts_event=self._clock.timestamp_ns(), info=info, ) self._log.info( f"initialMarginRequirement={info['initialMarginRequirement']}, " f"maintenanceMarginRequirement={info['maintenanceMarginRequirement']}, " f"marginFraction={info['marginFraction']}, " f"openMarginFraction={info['openMarginFraction']}, " f"totalAccountValue={info['totalAccountValue']}, " f"totalPositionSize={info['totalPositionSize']}", LogColor.BLUE, )
def test_margin_available_for_single_asset_account(self): # Arrange event = AccountState( account_id=AccountId("SIM", "001"), account_type=AccountType.CASH, base_currency=USD, reported=True, balances=[ AccountBalance( USD, Money(100000.00, USD), Money(0.00, USD), Money(100000.00, USD), ), ], info={}, event_id=uuid4(), ts_updated_ns=0, timestamp_ns=0, ) account = Account(event) # Wire up account to portfolio account.register_portfolio(self.portfolio) self.portfolio.register_account(account) # Act result1 = account.margin_available() account.update_initial_margin(Money(500.00, USD)) result2 = account.margin_available() account.update_maint_margin(Money(1000.00, USD)) result3 = account.margin_available() # Assert self.assertEqual(Money(100000.00, USD), result1) self.assertEqual(Money(99500.00, USD), result2) self.assertEqual(Money(98500.00, USD), result3)
def parse_balances( raw_balances: List[Dict[str, str]], asset_key: str, free_key: str, locked_key: str, ) -> List[AccountBalance]: parsed_balances: Dict[Currency, Tuple[Decimal, Decimal, Decimal]] = {} for b in raw_balances: currency = Currency.from_str(b[asset_key]) free = Decimal(b[free_key]) locked = Decimal(b[locked_key]) total: Decimal = free + locked parsed_balances[currency] = (total, locked, free) balances: List[AccountBalance] = [ AccountBalance( total=Money(values[0], currency), locked=Money(values[1], currency), free=Money(values[2], currency), ) for currency, values in parsed_balances.items() ] return balances
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) ]