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)
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']
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))
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())
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
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))
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)
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))
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)
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)
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)
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
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)
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)
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)
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)
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
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)
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")
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
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)