예제 #1
0
    def test_get_realized_pnls_when_all_flat_positions_returns_expected_series(self):
        # Arrange
        order1 = self.order_factory.market(
            AUDUSD_SIM.id,
            OrderSide.BUY,
            Quantity(100000),
        )

        order2 = self.order_factory.market(
            AUDUSD_SIM.id,
            OrderSide.SELL,
            Quantity(100000),
        )

        order3 = self.order_factory.market(
            AUDUSD_SIM.id,
            OrderSide.BUY,
            Quantity(100000),
        )

        order4 = self.order_factory.market(
            AUDUSD_SIM.id,
            OrderSide.SELL,
            Quantity(100000),
        )

        fill1 = TestStubs.event_order_filled(
            order1,
            instrument=AUDUSD_SIM,
            position_id=PositionId("P-1"),
            strategy_id=StrategyId("S", "001"),
            fill_price=Price("1.00000"),
        )

        fill2 = TestStubs.event_order_filled(
            order2,
            instrument=AUDUSD_SIM,
            position_id=PositionId("P-1"),
            strategy_id=StrategyId("S", "001"),
            fill_price=Price("1.00010"),
        )

        fill3 = TestStubs.event_order_filled(
            order3,
            instrument=AUDUSD_SIM,
            position_id=PositionId("P-2"),
            strategy_id=StrategyId("S", "001"),
            fill_price=Price("1.00000"),
        )

        fill4 = TestStubs.event_order_filled(
            order4,
            instrument=AUDUSD_SIM,
            position_id=PositionId("P-2"),
            strategy_id=StrategyId("S", "001"),
            fill_price=Price("1.00020"),
        )

        position1 = Position(fill1)
        position1.apply(fill2)

        position2 = Position(fill3)
        position2.apply(fill4)

        self.analyzer.add_positions([position1, position2])

        # Act
        result = self.analyzer.get_realized_pnls()

        # Assert
        self.assertEqual(2, len(result))
        self.assertEqual(6.0, result['P-1'])
        self.assertEqual(16.0, result['P-2'])
    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 = TestStubs.event_order_filled(
            order,
            instrument=AUDUSD_SIM,
            position_id=PositionId("P-123456"),
            strategy_id=StrategyId("S-001"),
            last_px=Price.from_str("1.00001"),
            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"),
            ExecutionId("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"),
            ExecutionId("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_position_realised_pnl_with_interleaved_order_sides(self):
        # Arrange
        order1 = self.order_factory.market(
            BTCUSDT_BINANCE.id,
            OrderSide.BUY,
            Quantity.from_str("12.000000"),
        )

        order2 = self.order_factory.market(
            BTCUSDT_BINANCE.id,
            OrderSide.BUY,
            Quantity.from_str("17.000000"),
        )

        order3 = self.order_factory.market(
            BTCUSDT_BINANCE.id,
            OrderSide.SELL,
            Quantity.from_str("9.000000"),
        )

        order4 = self.order_factory.market(
            BTCUSDT_BINANCE.id,
            OrderSide.BUY,
            Quantity.from_str("3.000000"),
        )

        order5 = self.order_factory.market(
            BTCUSDT_BINANCE.id,
            OrderSide.SELL,
            Quantity.from_str("4.000000"),
        )

        # Act
        fill1 = TestStubs.event_order_filled(
            order1,
            instrument=BTCUSDT_BINANCE,
            position_id=PositionId("P-19700101-000000-000-001-1"),
            last_px=Price.from_str("10000.00"),
        )

        position = Position(instrument=BTCUSDT_BINANCE, fill=fill1)

        fill2 = TestStubs.event_order_filled(
            order2,
            instrument=BTCUSDT_BINANCE,
            position_id=PositionId("P-19700101-000000-000-001-1"),
            last_px=Price.from_str("9999.00"),
        )

        position.apply(fill2)
        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 = TestStubs.event_order_filled(
            order3,
            instrument=BTCUSDT_BINANCE,
            position_id=PositionId("P-19700101-000000-000-001-1"),
            strategy_id=StrategyId("S-001"),
            last_px=Price.from_str("10001.00"),
        )

        position.apply(fill3)
        assert position.quantity == Quantity.from_int(20)
        assert position.realized_pnl == Money(-365.71613793, USDT)
        assert position.avg_px_open == Decimal("9999.413793103448275862068966")

        fill4 = TestStubs.event_order_filled(
            order4,
            instrument=BTCUSDT_BINANCE,
            position_id=PositionId("P-19700101-000000-000-001-1"),
            strategy_id=StrategyId("S-001"),
            last_px=Price.from_str("10003.00"),
        )

        position.apply(fill4)
        assert position.quantity == Quantity.from_int(23)
        assert position.realized_pnl == Money(-395.72513793, USDT)
        assert position.avg_px_open == Decimal("9999.881559220389805097451274")

        fill5 = TestStubs.event_order_filled(
            order5,
            instrument=BTCUSDT_BINANCE,
            position_id=PositionId("P-19700101-000000-000-001-1"),
            strategy_id=StrategyId("S-001"),
            last_px=Price.from_str("10005"),
        )

        position.apply(fill5)
        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 BTC/USDT.BINANCE, id=P-19700101-000000-000-001-1)"
        )
    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_positions_with_multi_asset_account(self):
        # Arrange
        state = AccountState(
            account_id=AccountId("BITMEX", "01234"),
            balances=[Money("10.00000000", BTC), Money("10.00000000", ETH)],
            balances_free=[Money("0.00000000", BTC), Money("10.00000000", ETH)],
            balances_locked=[Money("0.00000000", BTC), Money("0.00000000", ETH)],
            info={},
            event_id=uuid4(),
            event_timestamp=UNIX_EPOCH,
        )

        account = Account(state)

        self.portfolio.register_account(account)

        last_ethusd = QuoteTick(
            ETHUSD_BITMEX.symbol,
            Price("376.05"),
            Price("377.10"),
            Quantity("16"),
            Quantity("25"),
            UNIX_EPOCH,
        )

        last_btcusd = QuoteTick(
            BTCUSD_BITMEX.symbol,
            Price("10500.05"),
            Price("10501.51"),
            Quantity("2.54"),
            Quantity("0.91"),
            UNIX_EPOCH,
        )

        self.data_cache.add_quote_tick(last_ethusd)
        self.data_cache.add_quote_tick(last_btcusd)
        self.portfolio.update_tick(last_ethusd)
        self.portfolio.update_tick(last_btcusd)

        order = self.order_factory.market(
            ETHUSD_BITMEX.symbol,
            OrderSide.BUY,
            Quantity(10000),
        )

        fill = TestStubs.event_order_filled(
            order=order,
            instrument=ETHUSD_BITMEX,
            position_id=PositionId("P-123456"),
            strategy_id=StrategyId("S", "001"),
            fill_price=Price("376.05"),
        )

        position = Position(fill)

        # Act
        self.portfolio.update_position(TestStubs.event_position_opened(position))

        # Assert
        self.assertEqual({ETH: Money("2.65922085", ETH)}, self.portfolio.market_values(BITMEX))
        self.assertEqual({ETH: Money("0.03855870", ETH)}, self.portfolio.maint_margins(BITMEX))
        self.assertEqual(Money("2.65922085", ETH), self.portfolio.market_value(ETHUSD_BITMEX.symbol))
        self.assertEqual(Money("0.00000000", ETH), self.portfolio.unrealized_pnl(ETHUSD_BITMEX.symbol))
    def test_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_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_position_filled_with_buy_order_then_sell_order_returns_expected_attributes(
        self, ):
        # Arrange
        order = self.order_factory.market(
            AUDUSD_SIM.id,
            OrderSide.BUY,
            Quantity(150000),
        )

        fill1 = TestStubs.event_order_filled(
            order,
            instrument=AUDUSD_SIM,
            position_id=PositionId("P-123456"),
            strategy_id=StrategyId("S", "001"),
            last_px=Price("1.00001"),
            execution_ns=1_000_000_000,
        )

        position = Position(fill=fill1)

        fill2 = OrderFilled(
            self.account_id,
            order.client_order_id,
            VenueOrderId("2"),
            ExecutionId("E2"),
            PositionId("T123456"),
            StrategyId("S", "001"),
            order.instrument_id,
            OrderSide.SELL,
            order.quantity,
            Price("1.00011"),
            order.quantity,
            Quantity(),
            AUDUSD_SIM.quote_currency,
            AUDUSD_SIM.is_inverse,
            Money(0, USD),
            LiquiditySide.TAKER,
            2_000_000_000,
            uuid4(),
            0,
        )

        last = Price("1.00050")

        # Act
        position.apply(fill2)

        # Assert
        self.assertEqual(Quantity(), position.quantity)
        self.assertEqual(PositionSide.FLAT, position.side)
        self.assertEqual(1_000_000_000, position.opened_timestamp_ns)
        self.assertEqual(1_000_000_000, position.open_duration_ns)
        self.assertEqual(Decimal("1.00001"), position.avg_px_open)
        self.assertEqual(2, position.event_count)
        self.assertEqual(2_000_000_000, position.closed_timestamp_ns)
        self.assertEqual(Decimal("1.00011"), position.avg_px_close)
        self.assertFalse(position.is_long)
        self.assertFalse(position.is_short)
        self.assertFalse(position.is_open)
        self.assertTrue(position.is_closed)
        self.assertEqual(Decimal("0.00010"), position.realized_points)
        self.assertEqual(Decimal("0.00009999900000999990000099999000"),
                         position.realized_return)
        self.assertEqual(Money(12.00, USD), position.realized_pnl)
        self.assertEqual(Money(0, USD), position.unrealized_pnl(last))
        self.assertEqual(Money(12.00, USD), position.total_pnl(last))
        self.assertEqual([Money(3.00, USD)], position.commissions())
        self.assertEqual(Money(3.00, USD), position.commission)
        self.assertEqual("Position(FLAT AUD/USD.SIM, id=P-123456)",
                         repr(position))
    def test_position_long_with_multiple_filled_orders_returns_expected_attributes(
        self, ):
        # Arrange
        order1 = self.order_factory.market(
            AUDUSD_SIM.id,
            OrderSide.BUY,
            Quantity(100000),
        )

        order2 = self.order_factory.market(
            AUDUSD_SIM.id,
            OrderSide.BUY,
            Quantity(100000),
        )

        order3 = self.order_factory.market(
            AUDUSD_SIM.id,
            OrderSide.SELL,
            Quantity(200000),
        )

        fill1 = TestStubs.event_order_filled(
            order1,
            instrument=AUDUSD_SIM,
            position_id=PositionId("P-123456"),
            strategy_id=StrategyId("S", "001"),
        )

        fill2 = TestStubs.event_order_filled(
            order2,
            instrument=AUDUSD_SIM,
            position_id=PositionId("P-123456"),
            strategy_id=StrategyId("S", "001"),
            last_px=Price("1.00001"),
        )

        fill3 = TestStubs.event_order_filled(
            order3,
            instrument=AUDUSD_SIM,
            position_id=PositionId("P-123456"),
            strategy_id=StrategyId("S", "001"),
            last_px=Price("1.00010"),
        )

        last = Price("1.00050")

        # Act
        position = Position(fill=fill1)
        position.apply(fill2)
        position.apply(fill3)

        # Assert
        self.assertEqual(Quantity(), position.quantity)
        self.assertEqual(PositionSide.FLAT, position.side)
        self.assertEqual(0, position.opened_timestamp_ns)
        self.assertEqual(Decimal("1.000005"), position.avg_px_open)
        self.assertEqual(3, position.event_count)
        self.assertEqual(
            [
                order1.client_order_id, order2.client_order_id,
                order3.client_order_id
            ],
            position.client_order_ids,
        )
        self.assertEqual(0, position.closed_timestamp_ns)
        self.assertEqual(Decimal("1.0001"), position.avg_px_close)
        self.assertFalse(position.is_long)
        self.assertFalse(position.is_short)
        self.assertFalse(position.is_open)
        self.assertTrue(position.is_closed)
        self.assertEqual(Money(11.00, USD), position.realized_pnl)
        self.assertEqual(Money(0, USD), position.unrealized_pnl(last))
        self.assertEqual(Money(11.00, USD), position.total_pnl(last))
        self.assertEqual([Money(8.00, USD)], position.commissions())
        self.assertEqual(Money(8.00, USD), position.commission)
        self.assertEqual("Position(FLAT AUD/USD.SIM, id=P-123456)",
                         repr(position))
예제 #10
0
    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_update_position_for_closed_position(self):
        # Arrange
        self.database.add_instrument(AUDUSD_SIM)

        order1 = self.strategy.order_factory.market(
            AUDUSD_SIM.id,
            OrderSide.BUY,
            Quantity.from_int(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,
                last_px=Price.from_str("1.00001"),
            ))
        self.database.update_order(order1)

        # Act
        position = Position(instrument=AUDUSD_SIM, fill=order1.last_event)
        self.database.add_position(position)

        order2 = self.strategy.order_factory.market(
            AUDUSD_SIM.id,
            OrderSide.SELL,
            Quantity.from_int(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,
            last_px=Price.from_str("1.00001"),
        )

        order2.apply(filled)
        self.database.update_order(order2)

        position.apply(filled)

        # Act
        self.database.update_position(position)

        # Assert
        assert self.database.load_position(position.id) == position
    def test_several_positions_with_different_symbols_updates_portfolio(self):
        # Arrange
        state = AccountState(
            AccountId("SIM", "01234"),
            balances=[Money(1_000_000.00, USD)],
            balances_free=[Money(1_000_000.00, USD)],
            balances_locked=[Money(0.00, USD)],
            info={"default_currency": "USD"},
            event_id=uuid4(),
            event_timestamp=UNIX_EPOCH,
        )

        account = Account(state)

        self.portfolio.register_account(account)

        order1 = self.order_factory.market(
            AUDUSD_SIM.symbol,
            OrderSide.BUY,
            Quantity(100000),
        )

        order2 = self.order_factory.market(
            AUDUSD_SIM.symbol,
            OrderSide.BUY,
            Quantity(100000),
        )

        order3 = self.order_factory.market(
            GBPUSD_SIM.symbol,
            OrderSide.BUY,
            Quantity(100000),
        )

        order4 = self.order_factory.market(
            GBPUSD_SIM.symbol,
            OrderSide.SELL,
            Quantity(100000),
        )

        order1_filled = TestStubs.event_order_filled(
            order1,
            instrument=GBPUSD_SIM,
            position_id=PositionId("P-1"),
            strategy_id=StrategyId("S", "1"),
            fill_price=Price("1.00000"),
        )

        order2_filled = TestStubs.event_order_filled(
            order2,
            instrument=GBPUSD_SIM,
            position_id=PositionId("P-2"),
            strategy_id=StrategyId("S", "1"),
            fill_price=Price("1.00000"),
        )

        order3_filled = TestStubs.event_order_filled(
            order3,
            instrument=GBPUSD_SIM,
            position_id=PositionId("P-3"),
            strategy_id=StrategyId("S", "1"),
            fill_price=Price("1.00000"),
        )

        order4_filled = TestStubs.event_order_filled(
            order4,
            instrument=GBPUSD_SIM,
            position_id=PositionId("P-3"),
            strategy_id=StrategyId("S", "1"),
            fill_price=Price("1.00100"),
        )

        position1 = Position(order1_filled)
        position2 = Position(order2_filled)
        position3 = Position(order3_filled)

        last_audusd = QuoteTick(
            AUDUSD_SIM.symbol,
            Price("0.80501"),
            Price("0.80505"),
            Quantity(1),
            Quantity(1),
            UNIX_EPOCH,
        )

        last_gbpusd = QuoteTick(
            GBPUSD_SIM.symbol,
            Price("1.30315"),
            Price("1.30317"),
            Quantity(1),
            Quantity(1),
            UNIX_EPOCH,
        )

        self.data_cache.add_quote_tick(last_audusd)
        self.data_cache.add_quote_tick(last_gbpusd)
        self.portfolio.update_tick(last_audusd)
        self.portfolio.update_tick(last_gbpusd)

        # Act
        self.portfolio.update_position(TestStubs.event_position_opened(position1))
        self.portfolio.update_position(TestStubs.event_position_opened(position2))
        self.portfolio.update_position(TestStubs.event_position_opened(position3))

        position3.apply(order4_filled)
        self.portfolio.update_position(TestStubs.event_position_closed(position3))

        # Assert
        self.assertEqual({USD: Money("-38998.00", USD)}, self.portfolio.unrealized_pnls(SIM))
        self.assertEqual({USD: Money("3220.04", USD)}, self.portfolio.market_values(SIM))
        self.assertEqual({USD: Money("99.82", USD)}, self.portfolio.maint_margins(SIM))
        self.assertEqual(Money("3220.04", USD), self.portfolio.market_value(AUDUSD_SIM.symbol))
        self.assertEqual(Money("-38998.00", USD), self.portfolio.unrealized_pnl(AUDUSD_SIM.symbol))
        self.assertEqual(Money("0", USD), self.portfolio.unrealized_pnl(GBPUSD_SIM.symbol))
        self.assertEqual(Decimal(200000), self.portfolio.net_position(AUDUSD_SIM.symbol))
        self.assertEqual(Decimal(0), self.portfolio.net_position(GBPUSD_SIM.symbol))
        self.assertTrue(self.portfolio.is_net_long(AUDUSD_SIM.symbol))
        self.assertTrue(self.portfolio.is_flat(GBPUSD_SIM.symbol))
        self.assertFalse(self.portfolio.is_completely_flat())
    def test_closing_position_updates_portfolio(self):
        # Arrange
        state = AccountState(
            AccountId("SIM", "01234"),
            balances=[Money(1_000_000.00, USD)],
            balances_free=[Money(1_000_000.00, USD)],
            balances_locked=[Money(0.00, USD)],
            info={"default_currency": "USD"},
            event_id=uuid4(),
            event_timestamp=UNIX_EPOCH,
        )

        account = Account(state)

        self.portfolio.register_account(account)

        order1 = self.order_factory.market(
            AUDUSD_SIM.symbol,
            OrderSide.BUY,
            Quantity(100000),
        )

        order1_filled = TestStubs.event_order_filled(
            order1,
            instrument=AUDUSD_SIM,
            position_id=PositionId("P-123456"),
            strategy_id=StrategyId("S", "1"),
            fill_price=Price("1.00000"),
        )

        position = Position(order1_filled)

        self.portfolio.update_position(TestStubs.event_position_opened(position))

        order2 = self.order_factory.market(
            AUDUSD_SIM.symbol,
            OrderSide.SELL,
            Quantity(100000),
        )

        order2_filled = TestStubs.event_order_filled(
            order2,
            instrument=AUDUSD_SIM,
            position_id=PositionId("P-123456"),
            strategy_id=StrategyId("S", "1"),
            fill_price=Price("1.00010"),
        )

        position.apply(order2_filled)

        # Act
        self.portfolio.update_position(TestStubs.event_position_closed(position))

        # Assert
        self.assertEqual({}, self.portfolio.market_values(SIM))
        self.assertEqual({}, self.portfolio.unrealized_pnls(SIM))
        self.assertEqual({}, self.portfolio.maint_margins(SIM))
        self.assertEqual(Money("0", USD), self.portfolio.market_value(AUDUSD_SIM.symbol))
        self.assertEqual(Money("0", USD), self.portfolio.unrealized_pnl(AUDUSD_SIM.symbol))
        self.assertEqual(Decimal(0), self.portfolio.net_position(AUDUSD_SIM.symbol))
        self.assertFalse(self.portfolio.is_net_long(AUDUSD_SIM.symbol))
        self.assertFalse(self.portfolio.is_net_short(AUDUSD_SIM.symbol))
        self.assertTrue(self.portfolio.is_flat(AUDUSD_SIM.symbol))
        self.assertTrue(self.portfolio.is_completely_flat())
    def test_opening_several_positions_updates_portfolio(self):
        # Arrange
        state = AccountState(
            AccountId("SIM", "01234"),
            balances=[Money(1_000_000.00, USD)],
            balances_free=[Money(1_000_000.00, USD)],
            balances_locked=[Money(0.00, USD)],
            info={"default_currency": "USD"},
            event_id=uuid4(),
            event_timestamp=UNIX_EPOCH,
        )

        account = Account(state)

        self.portfolio.register_account(account)

        last_audusd = QuoteTick(
            AUDUSD_SIM.symbol,
            Price("0.80501"),
            Price("0.80505"),
            Quantity(1),
            Quantity(1),
            UNIX_EPOCH,
        )

        last_gbpusd = QuoteTick(
            GBPUSD_SIM.symbol,
            Price("1.30315"),
            Price("1.30317"),
            Quantity(1),
            Quantity(1),
            UNIX_EPOCH,
        )

        self.data_cache.add_quote_tick(last_audusd)
        self.data_cache.add_quote_tick(last_gbpusd)
        self.portfolio.update_tick(last_audusd)
        self.portfolio.update_tick(last_gbpusd)

        order1 = self.order_factory.market(
            AUDUSD_SIM.symbol,
            OrderSide.BUY,
            Quantity(100000),
        )

        order2 = self.order_factory.market(
            GBPUSD_SIM.symbol,
            OrderSide.BUY,
            Quantity(100000),
        )

        order1_filled = TestStubs.event_order_filled(
            order1,
            instrument=AUDUSD_SIM,
            position_id=PositionId("P-1"),
            strategy_id=StrategyId("S", "1"),
            fill_price=Price("1.00000"),
        )

        order2_filled = TestStubs.event_order_filled(
            order2,
            instrument=AUDUSD_SIM,
            position_id=PositionId("P-2"),
            strategy_id=StrategyId("S", "1"),
            fill_price=Price("1.00000"),
        )

        position1 = Position(order1_filled)
        position2 = Position(order2_filled)
        position_opened1 = TestStubs.event_position_opened(position1)
        position_opened2 = TestStubs.event_position_opened(position2)

        # Act
        self.portfolio.update_position(position_opened1)
        self.portfolio.update_position(position_opened2)

        # Assert
        self.assertEqual({USD: Money("4216.32", USD)}, self.portfolio.market_values(SIM))
        self.assertEqual({USD: Money("10816.00", USD)}, self.portfolio.unrealized_pnls(SIM))
        self.assertEqual({USD: Money("130.71", USD)}, self.portfolio.maint_margins(SIM))
        self.assertEqual(Money("1610.02", USD), self.portfolio.market_value(AUDUSD_SIM.symbol))
        self.assertEqual(Money("2606.30", USD), self.portfolio.market_value(GBPUSD_SIM.symbol))
        self.assertEqual(Money("-19499.00", USD), self.portfolio.unrealized_pnl(AUDUSD_SIM.symbol))
        self.assertEqual(Money("30315.00", USD), self.portfolio.unrealized_pnl(GBPUSD_SIM.symbol))
        self.assertEqual(Decimal(100000), self.portfolio.net_position(AUDUSD_SIM.symbol))
        self.assertEqual(Decimal(100000), self.portfolio.net_position(GBPUSD_SIM.symbol))
        self.assertTrue(self.portfolio.is_net_long(AUDUSD_SIM.symbol))
        self.assertFalse(self.portfolio.is_net_short(AUDUSD_SIM.symbol))
        self.assertFalse(self.portfolio.is_flat(AUDUSD_SIM.symbol))
        self.assertFalse(self.portfolio.is_completely_flat())
    def test_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(
                    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,
        )

        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 = 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=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 = TestEventStubs.position_opened(position1)
        position_opened2 = TestEventStubs.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_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(12),
        )

        order2 = self.order_factory.market(
            ETHUSDT_BINANCE.id,
            OrderSide.BUY,
            Quantity(17),
        )

        order3 = self.order_factory.market(
            ETHUSDT_BINANCE.id,
            OrderSide.SELL,
            Quantity(9),
        )

        order4 = self.order_factory.market(
            ETHUSDT_BINANCE.id,
            OrderSide.SELL,
            Quantity(4),
        )

        order5 = self.order_factory.market(
            ETHUSDT_BINANCE.id,
            OrderSide.BUY,
            Quantity(3),
        )

        # Act
        fill1 = TestStubs.event_order_filled(
            order1,
            instrument=BTCUSDT_BINANCE,
            position_id=PositionId("P-19700101-000000-000-001-1"),
            last_px=Price(100),
        )

        position = Position(fill=fill1)

        fill2 = TestStubs.event_order_filled(
            order2,
            instrument=BTCUSDT_BINANCE,
            position_id=PositionId("P-19700101-000000-000-001-1"),
            last_px=Price(99),
        )

        position.apply(fill2)
        self.assertEqual(Quantity(29), position.quantity)
        self.assertEqual(Money("-2.88300000", USDT), position.realized_pnl)
        self.assertEqual(Decimal("99.41379310344827586206896552"),
                         position.avg_px_open)

        fill3 = TestStubs.event_order_filled(
            order3,
            instrument=BTCUSDT_BINANCE,
            position_id=PositionId("P-19700101-000000-000-001-1"),
            strategy_id=StrategyId("S", "001"),
            last_px=Price(101),
        )

        position.apply(fill3)
        self.assertEqual(Quantity(20), position.quantity)
        self.assertEqual(Money("10.48386207", USDT), position.realized_pnl)
        self.assertEqual(Decimal("99.41379310344827586206896552"),
                         position.avg_px_open)

        fill4 = TestStubs.event_order_filled(
            order4,
            instrument=BTCUSDT_BINANCE,
            position_id=PositionId("P-19700101-000000-000-001-1"),
            strategy_id=StrategyId("S", "001"),
            last_px=Price(105),
        )

        position.apply(fill4)
        self.assertEqual(Quantity(16), position.quantity)
        self.assertEqual(Money("32.40868966", USDT), position.realized_pnl)
        self.assertEqual(Decimal("99.41379310344827586206896552"),
                         position.avg_px_open)

        fill5 = TestStubs.event_order_filled(
            order5,
            instrument=BTCUSDT_BINANCE,
            position_id=PositionId("P-19700101-000000-000-001-1"),
            strategy_id=StrategyId("S", "001"),
            last_px=Price(103),
        )

        position.apply(fill5)
        self.assertEqual(Quantity(19), position.quantity)
        self.assertEqual(Money("32.09968966", USDT), position.realized_pnl)
        self.assertEqual(Decimal("99.98003629764065335753176042"),
                         position.avg_px_open)
        self.assertEqual(
            "Position(LONG 19 ETH/USDT.BINANCE, id=P-19700101-000000-000-001-1)",
            repr(position),
        )
    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()
예제 #18
0
    def test_position_long_with_multiple_filled_orders_returns_expected_attributes(
        self, ):
        # Arrange
        order1 = self.order_factory.market(
            AUDUSD_SIM.id,
            OrderSide.BUY,
            Quantity.from_int(100000),
        )

        order2 = self.order_factory.market(
            AUDUSD_SIM.id,
            OrderSide.BUY,
            Quantity.from_int(100000),
        )

        order3 = self.order_factory.market(
            AUDUSD_SIM.id,
            OrderSide.SELL,
            Quantity.from_int(200000),
        )

        fill1 = TestStubs.event_order_filled(
            order1,
            instrument=AUDUSD_SIM,
            position_id=PositionId("P-123456"),
            strategy_id=StrategyId("S-001"),
        )

        fill2 = TestStubs.event_order_filled(
            order2,
            instrument=AUDUSD_SIM,
            position_id=PositionId("P-123456"),
            strategy_id=StrategyId("S-001"),
            last_px=Price.from_str("1.00001"),
        )

        fill3 = TestStubs.event_order_filled(
            order3,
            instrument=AUDUSD_SIM,
            position_id=PositionId("P-123456"),
            strategy_id=StrategyId("S-001"),
            last_px=Price.from_str("1.00010"),
        )

        last = Price.from_str("1.00050")

        # Act
        position = Position(instrument=AUDUSD_SIM, fill=fill1)
        position.apply(fill2)
        position.apply(fill3)

        # Assert
        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_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)
예제 #20
0
 def test_side_from_order_side_given_invalid_value_returns_none(self):
     # Arrange, Act
     with pytest.raises(ValueError):
         Position.side_from_order_side(0)
    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)
예제 #22
0
    def test_pnl_calculation_from_trading_technologies_example(self):
        # https://www.tradingtechnologies.com/xtrader-help/fix-adapter-reference/pl-calculation-algorithm/understanding-pl-calculations/  # noqa

        # Arrange
        order1 = self.order_factory.market(
            ETHUSDT_BINANCE.id,
            OrderSide.BUY,
            Quantity.from_int(12),
        )

        order2 = self.order_factory.market(
            ETHUSDT_BINANCE.id,
            OrderSide.BUY,
            Quantity.from_int(17),
        )

        order3 = self.order_factory.market(
            ETHUSDT_BINANCE.id,
            OrderSide.SELL,
            Quantity.from_int(9),
        )

        order4 = self.order_factory.market(
            ETHUSDT_BINANCE.id,
            OrderSide.SELL,
            Quantity.from_int(4),
        )

        order5 = self.order_factory.market(
            ETHUSDT_BINANCE.id,
            OrderSide.BUY,
            Quantity.from_int(3),
        )

        # Act
        fill1 = TestStubs.event_order_filled(
            order1,
            instrument=ETHUSDT_BINANCE,
            position_id=PositionId("P-19700101-000000-000-001-1"),
            last_px=Price.from_int(100),
        )

        position = Position(instrument=ETHUSDT_BINANCE, fill=fill1)

        fill2 = TestStubs.event_order_filled(
            order2,
            instrument=ETHUSDT_BINANCE,
            position_id=PositionId("P-19700101-000000-000-001-1"),
            last_px=Price.from_int(99),
        )

        position.apply(fill2)
        assert position.quantity == Quantity.from_int(29)
        assert position.realized_pnl == Money(-0.28830000, USDT)
        assert position.avg_px_open == Decimal("99.41379310344827586206896552")

        fill3 = TestStubs.event_order_filled(
            order3,
            instrument=ETHUSDT_BINANCE,
            position_id=PositionId("P-19700101-000000-000-001-1"),
            strategy_id=StrategyId("S-001"),
            last_px=Price.from_int(101),
        )

        position.apply(fill3)
        assert position.quantity == Quantity.from_int(20)
        assert position.realized_pnl == Money(13.89666207, USDT)
        assert position.avg_px_open == Decimal("99.41379310344827586206896552")

        fill4 = TestStubs.event_order_filled(
            order4,
            instrument=ETHUSDT_BINANCE,
            position_id=PositionId("P-19700101-000000-000-001-1"),
            strategy_id=StrategyId("S-001"),
            last_px=Price.from_int(105),
        )

        position.apply(fill4)
        assert position.quantity == Quantity.from_int(16)
        assert position.realized_pnl == Money(36.19948966, USDT)
        assert position.avg_px_open == Decimal("99.41379310344827586206896552")

        fill5 = TestStubs.event_order_filled(
            order5,
            instrument=ETHUSDT_BINANCE,
            position_id=PositionId("P-19700101-000000-000-001-1"),
            strategy_id=StrategyId("S-001"),
            last_px=Price.from_int(103),
        )

        position.apply(fill5)
        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 ETH/USDT.BINANCE, id=P-19700101-000000-000-001-1)"
        )
    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_update_positions(self):
        # Arrange
        self.portfolio.register_account(self.account)

        # Create a closed position
        order1 = self.order_factory.market(
            BTCUSDT_BINANCE.symbol,
            OrderSide.BUY,
            Quantity("10.50000000"),
        )

        order2 = self.order_factory.market(
            BTCUSDT_BINANCE.symbol,
            OrderSide.SELL,
            Quantity("10.50000000"),
        )

        filled1 = TestStubs.event_order_filled(
            order1,
            instrument=BTCUSDT_BINANCE,
            position_id=PositionId("P-1"),
            strategy_id=StrategyId("S", "1"),
            fill_price=Price("25000.00"),
        )

        filled2 = TestStubs.event_order_filled(
            order2,
            instrument=BTCUSDT_BINANCE,
            position_id=PositionId("P-1"),
            strategy_id=StrategyId("S", "1"),
            fill_price=Price("25000.00"),
        )

        position1 = Position(filled1)
        position1.apply(filled2)

        order3 = self.order_factory.market(
            BTCUSDT_BINANCE.symbol,
            OrderSide.BUY,
            Quantity("10.00000000"),
        )

        filled3 = TestStubs.event_order_filled(
            order3,
            instrument=BTCUSDT_BINANCE,
            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.symbol,
            Price("25001.00"),
            Price("25002.00"),
            Quantity(1),
            Quantity(1),
            UNIX_EPOCH,
        )

        # Act
        self.portfolio.initialize_positions({position1, position2})
        self.portfolio.update_tick(last)

        # Assert
        self.assertTrue(self.portfolio.is_net_long(BTCUSDT_BINANCE.symbol))