예제 #1
0
    def test_initialize_limit_order(self):
        # Arrange
        # Act
        order = self.order_factory.limit(
            AUDUSD_SIM.symbol,
            OrderSide.BUY,
            Quantity(100000),
            Price("1.00000"),
        )

        # Assert
        self.assertEqual(OrderType.LIMIT, order.type)
        self.assertEqual(OrderState.INITIALIZED, order.state)
        self.assertEqual(TimeInForce.DAY, order.time_in_force)
        self.assertFalse(order.is_completed)
    def trailing_stop_sell(self, last_bar: Bar):
        """
        Users simple trailing stop SELL for (LONG positions).
        """
        price: Decimal = last_bar.low - (self.atr.value * self.trail_atr_multiple)
        order: StopMarketOrder = self.order_factory.stop_market(
            instrument_id=self.instrument_id,
            order_side=OrderSide.SELL,
            quantity=Quantity(self.trade_size),
            price=Price(price, self.instrument.price_precision),
            reduce_only=True,
        )

        self.trailing_stop = order
        self.submit_order(order)
예제 #3
0
    def test_amend_order_with_default_settings_sends_to_client(self):
        # Arrange
        self.exec_engine.start()

        strategy = TradingStrategy(order_id_tag="001")
        strategy.register_trader(
            TraderId("TESTER", "000"),
            self.clock,
            self.logger,
        )

        self.exec_engine.register_strategy(strategy)

        order = strategy.order_factory.market(
            AUDUSD_SIM.id,
            OrderSide.BUY,
            Quantity(100000),
        )

        submit = SubmitOrder(
            self.venue,
            self.trader_id,
            self.account_id,
            strategy.id,
            PositionId.null(),
            order,
            self.uuid_factory.generate(),
            self.clock.utc_now(),
        )

        amend = AmendOrder(
            self.venue,
            self.trader_id,
            self.account_id,
            order.cl_ord_id,
            order.quantity,
            Price("1.00010"),
            self.uuid_factory.generate(),
            self.clock.utc_now(),
        )

        self.risk_engine.execute(submit)

        # Act
        self.risk_engine.execute(amend)

        # Assert
        assert self.exec_client.calls == ['connect', 'submit_order', 'amend_order']
예제 #4
0
    def test_add_position(self):
        # Arrange
        order = self.strategy.order_factory.market(
            AUDUSD_SIM.id,
            OrderSide.BUY,
            Quantity(100000),
        )

        position_id = PositionId('P-1')
        self.cache.add_order(order, position_id)

        order_filled = TestStubs.event_order_filled(
            order,
            instrument=AUDUSD_SIM,
            position_id=PositionId('P-1'),
            fill_price=Price("1.00000"),
        )

        position = Position(order_filled)

        # Act
        self.cache.add_position(position)

        # Assert
        self.assertTrue(self.cache.position_exists(position.id))
        self.assertIn(position.id, self.cache.position_ids())
        self.assertIn(position, self.cache.positions())
        self.assertIn(position, self.cache.positions_open())
        self.assertIn(
            position,
            self.cache.positions_open(instrument_id=position.instrument_id))
        self.assertIn(position,
                      self.cache.positions_open(strategy_id=self.strategy.id))
        self.assertIn(
            position,
            self.cache.positions_open(instrument_id=position.instrument_id,
                                      strategy_id=self.strategy.id))
        self.assertNotIn(position, self.cache.positions_closed())
        self.assertNotIn(
            position,
            self.cache.positions_closed(instrument_id=position.instrument_id))
        self.assertNotIn(
            position,
            self.cache.positions_closed(strategy_id=self.strategy.id))
        self.assertNotIn(
            position,
            self.cache.positions_closed(instrument_id=position.instrument_id,
                                        strategy_id=self.strategy.id))
예제 #5
0
    def _handle_fills(self, instrument: Instrument, data: Dict[str,
                                                               Any]) -> None:
        if data["type"] != "order":
            self._log.error(f"Fill not for order, {data}")
            return

        # Parse identifiers
        venue_order_id = VenueOrderId(str(data["orderId"]))
        client_order_id = self._order_ids.get(venue_order_id)
        if client_order_id is None:
            client_order_id = ClientOrderId(str(uuid.uuid4()))
            # TODO(cs): WIP
            # triggers = await self._http_client.get_trigger_order_triggers(venue_order_id.value)
            #
            # for trigger in triggers:
            #     client_order_id = self._open_triggers.get(trigger)
            #     if client_order_id is not None:
            #         break
            # if client_order_id is None:
            #     client_order_id = ClientOrderId(str(uuid.uuid4()))

        # Fetch strategy ID
        strategy_id: StrategyId = self._cache.strategy_id_for_order(
            client_order_id)
        if strategy_id is None:
            self._generate_external_trade_report(instrument, data)
            return

        self.generate_order_filled(
            strategy_id=strategy_id,
            instrument_id=instrument.id,
            client_order_id=client_order_id,
            venue_order_id=venue_order_id,
            venue_position_id=None,  # NETTING accounts
            trade_id=TradeId(str(data["id"])),  # Trade ID
            order_side=OrderSideParser.from_str_py(data["side"].upper()),
            order_type=self._order_types[venue_order_id],
            last_qty=Quantity(data["size"], instrument.size_precision),
            last_px=Price(data["price"], instrument.price_precision),
            quote_currency=instrument.quote_currency,
            commission=Money(data["fee"],
                             Currency.from_str(data["feeCurrency"])),
            liquidity_side=LiquiditySide.MAKER
            if data["liquidity"] == "maker" else LiquiditySide.TAKER,
            ts_event=pd.to_datetime(data["time"], utc=True).to_datetime64(),
        )
        if not self._calculated_account:
            self._loop.create_task(self._update_account_state())
    def test_update_order_for_completed_order(self):
        # Arrange
        order = self.strategy.order_factory.market(
            AUDUSD_SIM.symbol,
            OrderSide.BUY,
            Quantity(100000),
        )

        position_id = PositionId('P-1')
        self.cache.add_order(order, position_id)
        order.apply(TestStubs.event_order_submitted(order))
        self.cache.update_order(order)

        order.apply(TestStubs.event_order_accepted(order))
        self.cache.update_order(order)

        order.apply(
            TestStubs.event_order_filled(order,
                                         instrument=AUDUSD_SIM,
                                         fill_price=Price("1.00001")), )

        # Act
        self.cache.update_order(order)

        # Assert
        self.assertTrue(self.cache.order_exists(order.cl_ord_id))
        self.assertIn(order.cl_ord_id, self.cache.order_ids())
        self.assertIn(order, self.cache.orders())
        self.assertIn(order, self.cache.orders_completed())
        self.assertIn(order, self.cache.orders_completed(symbol=order.symbol))
        self.assertIn(
            order, self.cache.orders_completed(strategy_id=self.strategy.id))
        self.assertIn(
            order,
            self.cache.orders_completed(symbol=order.symbol,
                                        strategy_id=self.strategy.id))
        self.assertNotIn(order, self.cache.orders_working())
        self.assertNotIn(order, self.cache.orders_working(symbol=order.symbol))
        self.assertNotIn(
            order, self.cache.orders_working(strategy_id=self.strategy.id))
        self.assertNotIn(
            order,
            self.cache.orders_working(symbol=order.symbol,
                                      strategy_id=self.strategy.id))
        self.assertEqual(order.id, self.cache.order_id(order.cl_ord_id))
        self.assertEqual(0, self.cache.orders_working_count())
        self.assertEqual(1, self.cache.orders_completed_count())
        self.assertEqual(1, self.cache.orders_total_count())
예제 #7
0
 def l1_feed():
     updates = []
     for _, row in TestDataProvider.usdjpy_ticks().iterrows():
         for side, order_side in zip(("bid", "ask"),
                                     (OrderSide.BUY, OrderSide.SELL)):
             updates.append({
                 "op":
                 "update",
                 "order":
                 Order(
                     price=Price(row[side], precision=6),
                     volume=Quantity(1e9, precision=2),
                     side=order_side,
                 ),
             })
     return updates
예제 #8
0
    def test_load_order_when_stop_market_order_in_database_returns_order(self):
        # Arrange
        order = self.strategy.order_factory.stop_market(
            AUDUSD_SIM.id,
            OrderSide.BUY,
            Quantity(100000),
            Price("1.00000"),
        )

        self.database.add_order(order)

        # Act
        result = self.database.load_order(order.client_order_id)

        # Assert
        self.assertEqual(order, result)
    def test_instrument_close_price(self):
        # Arrange
        update = InstrumentClosePrice(
            instrument_id=InstrumentId(Symbol("BTCUSDT"), Venue("BINANCE")),
            close_price=Price(100.0, precision=0),
            close_type=InstrumentCloseType.EXPIRED,
            ts_event=0,
            ts_init=0,
        )

        # Act, Assert
        assert InstrumentClosePrice.from_dict(
            InstrumentClosePrice.to_dict(update)) == update
        assert (
            "InstrumentClosePrice(instrument_id=BTCUSDT.BINANCE, close_price=100, close_type=EXPIRED)"
            == repr(update))
예제 #10
0
    def test_parse_trade_tick_from_string(self):
        # Arrange
        tick = TradeTick(
            AUDUSD_FXCM,
            Price("1.00000"),
            Quantity(10000),
            Maker.BUYER,
            MatchId("123456789"),
            UNIX_EPOCH,
        )

        # Act
        result = TradeTick.from_serializable_string(AUDUSD_FXCM, tick.to_serializable_string())

        # Assert
        self.assertEqual(tick, result)
예제 #11
0
    def test_initialize_stop_order(self):
        # Arrange
        # Act
        order = self.order_factory.stop_market(
            AUDUSD_SIM.symbol,
            OrderSide.BUY,
            Quantity(100000),
            Price("1.00000"),
        )

        # Assert
        self.assertEqual(OrderType.STOP_MARKET, order.type)
        self.assertEqual(OrderState.INITIALIZED, order.state)
        self.assertEqual(TimeInForce.GTC, order.time_in_force)
        self.assertFalse(order.is_completed)
        self.assertEqual(OrderInitialized, type(order.init_event))
예제 #12
0
 def modify_order_command(instrument_id=None, client_order_id=None):
     if instrument_id is None:
         instrument_id = BetfairTestStubs.instrument_id()
     return ModifyOrder(
         trader_id=TestStubs.trader_id(),
         strategy_id=TestStubs.strategy_id(),
         instrument_id=instrument_id,
         client_order_id=client_order_id
         or ClientOrderId("O-20210410-022422-001-001-1"),
         venue_order_id=VenueOrderId("001"),
         quantity=Quantity.from_int(50),
         price=Price(0.74347, precision=5),
         trigger=None,
         command_id=BetfairTestStubs.uuid(),
         ts_init=BetfairTestStubs.clock().timestamp_ns(),
     )
    def test_amend_order_raises_not_implemented_error(self):
        # Arrange
        # Act
        command = AmendOrder(
            self.venue,
            self.trader_id,
            self.account_id,
            ClientOrderId("O-123456789"),
            Quantity(120000),
            Price("1.00000"),
            self.uuid_factory.generate(),
            self.clock.utc_now(),
        )

        # Assert
        self.assertRaises(NotImplementedError, self.client.amend_order, command)
예제 #14
0
    def test_handle_trade_tick_sends_to_data_engine(self):
        # Arrange
        tick = TradeTick(
            AUDUSD_SIM.symbol,
            Price("1.00050"),
            Quantity(1),
            OrderSide.BUY,
            TradeMatchId("123456"),
            UNIX_EPOCH,
        )

        # Act
        self.client._handle_trade_tick_py(tick)

        # Assert
        self.assertEqual(1, self.data_engine.data_count)
예제 #15
0
    def test_from_serializable_string_given_valid_string_returns_expected_tick(self):
        # Arrange
        tick = TradeTick(
            AUDUSD_SIM.id,
            Price("1.00000"),
            Quantity(10000),
            OrderSide.BUY,
            TradeMatchId("123456789"),
            UNIX_EPOCH,
        )

        # Act
        result = TradeTick.from_serializable_str(AUDUSD_SIM.id, tick.to_serializable_str())

        # Assert
        self.assertEqual(tick, result)
예제 #16
0
 def l1_feed():
     provider = TestDataProvider()
     updates = []
     for _, row in provider.read_csv_ticks("truefx-usdjpy-ticks.csv").iterrows():
         for side, order_side in zip(("bid", "ask"), (OrderSide.BUY, OrderSide.SELL)):
             updates.append(
                 {
                     "op": "update",
                     "order": Order(
                         price=Price(row[side], precision=6),
                         size=Quantity(1e9, precision=2),
                         side=order_side,
                     ),
                 }
             )
     return updates
    def test_pack_and_unpack_limit_orders(self):
        # Arrange
        order = self.order_factory.limit(
            AUDUSD_SIM.id,
            OrderSide.BUY,
            Quantity(100000, precision=0),
            Price(1.00000, precision=5),
            TimeInForce.DAY,
        )

        # Act
        packed = OrderInitialized.to_dict(order.last_event)
        unpacked = self.unpacker.unpack(packed)

        # Assert
        assert unpacked == order
예제 #18
0
    def test_to_serializable_returns_expected_string(self):
        # Arrange
        tick = TradeTick(
            AUDUSD_SIM.symbol,
            Price("1.00000"),
            Quantity(10000),
            OrderSide.BUY,
            TradeMatchId("123456789"),
            UNIX_EPOCH,
        )

        # Act
        result = tick.to_serializable_string()

        # Assert
        self.assertEqual("1.00000,10000,BUY,123456789,0", result)
예제 #19
0
    def test_handle_trade_tick_when_count_at_threshold_sends_bar_to_handler(
            self):
        # Arrange
        bar_store = ObjectStorer()
        handler = bar_store.store
        instrument_id = TestStubs.audusd_id()
        bar_spec = BarSpecification(3, BarAggregation.TICK, PriceType.LAST)
        bar_type = BarType(instrument_id, bar_spec)
        aggregator = TickBarAggregator(bar_type, handler,
                                       TestLogger(TestClock()))

        tick1 = TradeTick(
            instrument_id=AUDUSD_SIM.id,
            price=Price("1.00001"),
            size=Quantity(1),
            side=OrderSide.BUY,
            match_id=TradeMatchId("123456"),
            timestamp=UNIX_EPOCH,
        )

        tick2 = TradeTick(
            instrument_id=AUDUSD_SIM.id,
            price=Price("1.00002"),
            size=Quantity(1),
            side=OrderSide.BUY,
            match_id=TradeMatchId("123457"),
            timestamp=UNIX_EPOCH,
        )

        tick3 = TradeTick(
            instrument_id=AUDUSD_SIM.id,
            price=Price("1.00000"),
            size=Quantity(1),
            side=OrderSide.BUY,
            match_id=TradeMatchId("123458"),
            timestamp=UNIX_EPOCH,
        )

        # Act
        aggregator.handle_trade_tick(tick1)
        aggregator.handle_trade_tick(tick2)
        aggregator.handle_trade_tick(tick3)

        # Assert
        self.assertEqual(1, len(bar_store.get_store()))
        self.assertEqual(Price("1.00001"), bar_store.get_store()[0].bar.open)
        self.assertEqual(Price("1.00002"), bar_store.get_store()[0].bar.high)
        self.assertEqual(Price("1.00000"), bar_store.get_store()[0].bar.low)
        self.assertEqual(Price("1.00000"), bar_store.get_store()[0].bar.close)
        self.assertEqual(Quantity(3), bar_store.get_store()[0].bar.volume)
예제 #20
0
 def test_priced_order_with_GTD_time_in_force_and_expire_time_none_raises_exception(
         self):
     # Arrange
     # Act
     self.assertRaises(ValueError,
                       Order,
                       OrderId('O-123456'),
                       AUDUSD_FXCM,
                       OrderSide.BUY,
                       OrderType.LIMIT,
                       Quantity(100000),
                       GUID(uuid.uuid4()),
                       UNIX_EPOCH,
                       price=Price(1.00000, 5),
                       time_in_force=TimeInForce.GTD,
                       expire_time=None)
    def test_unrealized_pnl_when_insufficient_data_for_xrate_returns_none(
            self):
        # Arrange
        state = AccountState(
            account_id=AccountId("BITMEX", "01234"),
            balances=[Money("10.00000000", BTC),
                      Money("10.00000000", ETH)],
            balances_free=[
                Money("10.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)
        order = self.order_factory.market(
            ETHUSD_BITMEX.symbol,
            OrderSide.BUY,
            Quantity(100),
        )

        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)

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

        # Act
        result = self.portfolio.unrealized_pnls(BITMEX)

        # # Assert
        self.assertIsNone(result)
예제 #22
0
    def test_process_trade_tick_when_subscribers_then_sends_to_registered_handlers(
        self,
    ):
        # Arrange
        self.data_engine.register_client(self.binance_client)
        self.binance_client.connect()

        handler1 = []
        subscribe1 = Subscribe(
            client_name=BINANCE.value,
            data_type=DataType(
                TradeTick, metadata={"InstrumentId": ETHUSDT_BINANCE.id}
            ),
            handler=handler1.append,
            command_id=self.uuid_factory.generate(),
            timestamp_ns=self.clock.timestamp_ns(),
        )

        handler2 = []
        subscribe2 = Subscribe(
            client_name=BINANCE.value,
            data_type=DataType(
                TradeTick, metadata={"InstrumentId": ETHUSDT_BINANCE.id}
            ),
            handler=handler2.append,
            command_id=self.uuid_factory.generate(),
            timestamp_ns=self.clock.timestamp_ns(),
        )

        self.data_engine.execute(subscribe1)
        self.data_engine.execute(subscribe2)

        tick = TradeTick(
            ETHUSDT_BINANCE.id,
            Price("1050.00000"),
            Quantity(100),
            OrderSide.BUY,
            TradeMatchId("123456789"),
            0,
        )

        # Act
        self.data_engine.process(tick)

        # Assert
        self.assertEqual([tick], handler1)
        self.assertEqual([tick], handler2)
예제 #23
0
    def make_order(engine: MockLiveExecutionEngine) -> LimitOrder:
        strategy = TradingStrategy(order_id_tag="001")
        strategy.register_trader(
            TraderId("TESTER", "000"),
            BetfairTestStubs.clock(),
            BetfairTestStubs.logger(),
        )

        engine.register_strategy(strategy)

        order = strategy.order_factory.limit(
            BetfairTestStubs.instrument_id(),
            OrderSide.BUY,
            Quantity(10),
            Price("0.50"),
        )
        return order
예제 #24
0
 def test_stop_order_with_gtd_and_expire_time_none_raises_exception(self):
     # Arrange
     # Act
     self.assertRaises(
         TypeError,
         StopMarketOrder,
         ClientOrderId("O-123456"),
         StrategyId("S", "001"),
         AUDUSD_SIM.symbol,
         OrderSide.BUY,
         Quantity(100000),
         price=Price("1.00000"),
         init_id=uuid4(),
         timestamp=UNIX_EPOCH,
         time_in_force=TimeInForce.GTD,
         expire_time=None,
     )
    def create_sell_order(self, last: QuoteTick):
        """
        A market makers simple sell limit method (example).
        """
        price: Decimal = last.ask + (self.atr.value * self.atr_multiple)
        order: LimitOrder = self.order_factory.limit(
            instrument_id=self.instrument_id,
            order_side=OrderSide.SELL,
            quantity=Quantity(self.trade_size),
            price=Price(price, self.price_precision),
            time_in_force=TimeInForce.GTC,
            post_only=True,  # Default value is True
            hidden=False,  # Default value is False
        )

        self.sell_order = order
        self.submit_order(order)
예제 #26
0
async def test_post_order_update_success(execution_client, exec_engine):
    # Add fake order to cache
    order = BetfairTestStubs.make_order(exec_engine)
    order.apply(BetfairTestStubs.event_order_submitted(order=order))
    order.apply(
        BetfairTestStubs.event_order_accepted(
            order=order, venue_order_id=VenueOrderId("229435133092")))
    exec_engine.cache.add_order(order, PositionId("1"))
    client_order_id = exec_engine.cache.orders()[0].client_order_id

    f = asyncio.Future()
    f.set_result(BetfairTestStubs.replace_orders_resp_success())
    execution_client._post_update_order(f, client_order_id)
    await asyncio.sleep(0)
    event = exec_engine.events[0]
    assert isinstance(event, OrderUpdated)
    assert event.price == Price("0.47619")
예제 #27
0
def test_duplicate_trades(betfair_data_client):
    messages = []
    for update in BetfairTestStubs.raw_market_updates(market="1.180305278",
                                                      runner1="2696769",
                                                      runner2="4297085"):
        messages.extend(
            on_market_update(
                instrument_provider=betfair_data_client.instrument_provider(),
                update=update,
            ))
        if update["pt"] >= 1615222877785:
            break
    trades = [
        m for m in messages
        if isinstance(m, TradeTick) and m.price == Price("0.69930", 5)
    ]
    assert len(trades) == 5
예제 #28
0
    def event_order_filled(
        order,
        instrument,
        position_id=None,
        strategy_id=None,
        fill_price=None,
        fill_qty=None,
        liquidity_side=LiquiditySide.TAKER,
    ) -> OrderFilled:
        if position_id is None:
            position_id = PositionId(order.cl_ord_id.value.replace("P", "T"))
        if strategy_id is None:
            strategy_id = StrategyId.null()
        if fill_price is None:
            fill_price = Price("1.00000")
        if fill_qty is None:
            fill_qty = order.quantity

        commission = instrument.calculate_commission(
            quantity=order.quantity,
            avg_price=fill_price,
            liquidity_side=liquidity_side,
        )

        return OrderFilled(
            account_id=TestStubs.account_id(),
            cl_ord_id=order.cl_ord_id,
            order_id=OrderId("1"),
            execution_id=ExecutionId(order.cl_ord_id.value.replace("O", "E")),
            position_id=position_id,
            strategy_id=strategy_id,
            symbol=order.symbol,
            order_side=order.side,
            fill_qty=fill_qty,
            cum_qty=Quantity(order.filled_qty + fill_qty),
            leaves_qty=Quantity(
                max(0, order.quantity - order.filled_qty - fill_qty)),
            fill_price=order.price if fill_price is None else fill_price,
            currency=instrument.quote_currency,
            is_inverse=instrument.is_inverse,
            commission=commission,
            liquidity_side=liquidity_side,
            execution_time=UNIX_EPOCH,
            event_id=uuid4(),
            event_timestamp=UNIX_EPOCH,
        )
    def trailing_stop_sell(self, last_bar: Bar):
        """
        Users simple trailing stop SELL for (LONG positions).
        """
        # Round price to nearest 0.5 (for XBT/USD)
        price = round((last_bar.low -
                       (self.atr.value * self.trail_atr_multiple)) * 2) / 2
        order: StopMarketOrder = self.order_factory.stop_market(
            symbol=self.symbol,
            order_side=OrderSide.SELL,
            quantity=Quantity(self.trade_size),
            price=Price(price, self.instrument.price_precision),
            reduce_only=True,
        )

        self.trailing_stop = order
        self.submit_order(order)
    def create_buy_order(self, last: QuoteTick):
        """
        A market makers simple buy limit method (example).
        """
        price: Decimal = last.bid - (self.atr.value * self.atr_multiple)
        order: LimitOrder = self.order_factory.limit(
            symbol=self.symbol,
            order_side=OrderSide.BUY,
            quantity=Quantity(self.trade_size),
            price=Price(price, self.price_precision),
            time_in_force=TimeInForce.GTC,
            post_only=True,  # Default value is True
            hidden=False,  # Default value is False
        )

        self.buy_order = order
        self.submit_order(order)