def test_order_amended(self): # Arrange uuid = uuid4() event = OrderAmended( account_id=AccountId("SIM", "000"), cl_ord_id=ClientOrderId("O-2020872378423"), order_id=OrderId("123456"), quantity=Quantity(500000), price=Price('1.95000'), amended_time=UNIX_EPOCH, event_id=uuid, event_timestamp=UNIX_EPOCH, ) # Act self.assertEqual( f"OrderAmended(account_id=SIM-000, cl_order_id=O-2020872378423, " f"order_id=123456, qty=500,000, price=1.95000, id={uuid})", str(event)) self.assertEqual( f"OrderAmended(account_id=SIM-000, cl_order_id=O-2020872378423, " f"order_id=123456, qty=500,000, price=1.95000, id={uuid})", repr(event))
def test_can_submit_order(self): # Arrange strategy = TradingStrategy(order_id_tag='001') strategy.change_clock(self.clock) self.exec_engine.register_strategy(strategy) position_id = strategy.position_id_generator.generate() order = strategy.order_factory.market(AUDUSD_FXCM, OrderSide.BUY, Quantity(100000)) submit_order = SubmitOrder(self.trader_id, self.account_id, strategy.id, position_id, order, self.guid_factory.generate(), self.clock.time_now()) # Act self.exec_engine.execute_command(submit_order) # Assert self.assertIn(submit_order, self.exec_client.received_commands) self.assertTrue(self.exec_db.order_exists(order.id)) self.assertEqual(position_id, self.exec_db.get_position_id(order.id))
def test_pack_and_unpack_stop_limit_orders_with_expire_time(self): # Arrange order = StopLimitOrder( self.trader_id, self.strategy_id, AUDUSD_SIM.id, ClientOrderId("O-123456"), OrderSide.BUY, Quantity(100000, precision=0), price=Price(1.00000, precision=5), trigger=Price(1.00010, precision=5), time_in_force=TimeInForce.GTD, expire_time=UNIX_EPOCH, init_id=UUID4(), ts_init=0, ) # Act packed = OrderInitialized.to_dict(order.last_event) unpacked = self.unpacker.unpack(packed) # Assert assert unpacked == order
def test_serialize_and_deserialize_order_amended_events(self): # Arrange event = OrderUpdated( self.trader_id, self.strategy_id, self.account_id, AUDUSD_SIM.id, ClientOrderId("O-123456"), VenueOrderId("1"), Quantity(100000, precision=0), Price(0.80010, precision=5), Price(0.80050, precision=5), UUID4(), 0, 0, ) # Act serialized = self.serializer.serialize(event) deserialized = self.serializer.deserialize(serialized) # Assert assert deserialized == event
def test_order_serializer_methods_raise_not_implemented_error(self): # Arrange order_factory = OrderFactory( trader_id=TraderId("TESTER", "000"), strategy_id=StrategyId("S", "001"), clock=TestClock(), ) order = order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity(100000), ) serializer = OrderSerializer() # Act # Assert with pytest.raises(NotImplementedError): serializer.serialize(order) with pytest.raises(NotImplementedError): serializer.deserialize(bytes())
def test_cancel_order_when_not_connected_logs_and_does_not_send(self): # Arrange order = self.order_factory.market( ETHUSDT_BINANCE.symbol, OrderSide.BUY, Quantity(100), ) command = CancelOrder( BINANCE, self.trader_id, self.account_id, order.cl_ord_id, order.id, self.uuid_factory.generate(), self.clock.utc_now(), ) # Act self.exec_client.cancel_order(command) # Assert self.assertTrue(True) # No exceptions raised
def test_apply_order_invalid_event(self): # Arrange order = self.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) invalid = OrderInvalid( order.client_order_id, "SOME_REASON", uuid4(), 0, ) # Act order.apply(invalid) # Assert self.assertEqual(OrderState.INVALID, order.state) self.assertEqual(2, order.event_count) self.assertEqual(invalid, order.last_event) self.assertTrue(order.is_completed)
def test_apply_order_denied_event(self): # Arrange order = self.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity(100000), ) denied = OrderDenied( order.cl_ord_id, "SOME_REASON", uuid4(), UNIX_EPOCH, ) # Act order.apply(denied) # Assert self.assertEqual(OrderState.DENIED, order.state) self.assertEqual(2, order.event_count) self.assertEqual(denied, order.last_event) self.assertTrue(order.is_completed)
def test_limit_buy_post_only_reduce_only_order(self): # Arrange order = self.order_factory.limit( symbol=BTCUSDT, order_side=OrderSide.BUY, quantity=Quantity("1.0"), price=Price("50000"), post_only=True, reduce_only=True, ) # Act result = self.builder.build_py(order) # Assert expected_args = ['BTC/USDT', 'Limit', 'Buy', '1.0', '50000'] expected_custom_params = { 'clOrdID': 'O-19700101-000000-000-001-1', 'execInst': 'ParticipateDoNotInitiate,ReduceOnly', 'timeInForce': 'GoodTillCancel' } self.assertTrue(order.is_post_only) self.assertEqual((expected_args, expected_custom_params), result)
def test_passive_post_only_insert(self): # Arrange: Prepare market self.cache.add_instrument(USDJPY_SIM) # Market is 10 @ 15 snapshot = TestStubs.order_book_snapshot(instrument_id=USDJPY_SIM.id, bid_volume=1000, ask_volume=1000) self.data_engine.process(snapshot) self.exchange.process_order_book(snapshot) # Act order = self.strategy.order_factory.limit( instrument_id=USDJPY_SIM.id, order_side=OrderSide.SELL, quantity=Quantity.from_int(2000), price=Price.from_str("14"), post_only=True, ) self.strategy.submit_order(order) self.exchange.process(0) # Assert assert order.status == OrderStatus.ACCEPTED
def test_hash_str_repr(self): # Arrange bar = Bar( AUDUSD_1_MIN_BID, Price.from_str("1.00001"), Price.from_str("1.00004"), Price.from_str("1.00002"), Price.from_str("1.00003"), Quantity.from_int(100000), 0, 0, ) # Act, Assert assert isinstance(hash(bar), int) assert ( str(bar) == "AUD/USD.SIM-1-MINUTE-BID-EXTERNAL,1.00001,1.00004,1.00002,1.00003,100000,0" ) assert ( repr(bar) == "Bar(AUD/USD.SIM-1-MINUTE-BID-EXTERNAL,1.00001,1.00004,1.00002,1.00003,100000,0)" )
def test_execute_command(self): order = self.strategy.order_factory.market( BTCUSDT_BINANCE.id, OrderSide.BUY, Quantity.from_str("1.00000000"), ) command = SubmitOrder( self.trader_id, self.strategy.id, None, order, self.uuid_factory.generate(), self.clock.timestamp_ns(), ) def execute_command(): self.exec_engine.execute(command) self.benchmark.pedantic(execute_command, iterations=100, rounds=100, warmup_rounds=5)
def test_order_amended(self): # Arrange uuid = uuid4() event = OrderUpdated( account_id=AccountId("SIM", "000"), client_order_id=ClientOrderId("O-2020872378423"), venue_order_id=VenueOrderId("123456"), quantity=Quantity.from_int(500000), price=Price.from_str("1.95000"), updated_ns=0, event_id=uuid, timestamp_ns=0, ) # Act assert ( f"OrderUpdated(account_id=SIM-000, cl_order_id=O-2020872378423, " f"venue_order_id=123456, qty=500_000, price=1.95000, event_id={uuid})" == str(event)) assert ( f"OrderUpdated(account_id=SIM-000, cl_order_id=O-2020872378423, " f"venue_order_id=123456, qty=500_000, price=1.95000, event_id={uuid})" == repr(event))
def test_cancel_order_when_not_connected_logs_and_does_not_send(self): # Arrange order = self.order_factory.market( ETHUSDT_BINANCE.id, OrderSide.BUY, Quantity.from_int(100), ) command = CancelOrder( self.trader_id, self.order_factory.strategy_id, order.instrument_id, order.client_order_id, order.venue_order_id, self.uuid_factory.generate(), self.clock.timestamp_ns(), ) # Act self.exec_client.cancel_order(command) # Assert assert True # No exceptions raised
def test_handle_trade_tick_when_volume_below_threshold_updates(self): # Arrange bar_store = ObjectStorer() handler = bar_store.store symbol = TestStubs.symbol_audusd_fxcm() bar_spec = BarSpecification(10000, BarAggregation.VOLUME, PriceType.LAST) bar_type = BarType(symbol, bar_spec) aggregator = VolumeBarAggregator(bar_type, handler, TestLogger(TestClock())) tick1 = TradeTick( symbol=AUDUSD_SIM.symbol, price=Price("1.00001"), size=Quantity(1), side=OrderSide.BUY, match_id=TradeMatchId("123456"), timestamp=UNIX_EPOCH, ) # Act aggregator.handle_trade_tick(tick1) # Assert self.assertEqual(0, len(bar_store.get_store()))
def test_handle_trade_tick_when_count_below_threshold_updates(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, Logger(TestClock())) tick1 = TradeTick( instrument_id=AUDUSD_SIM.id, price=Price("1.00001"), size=Quantity(1), aggressor_side=AggressorSide.BUY, match_id=TradeMatchId("123456"), timestamp_ns=0, ) # Act aggregator.handle_trade_tick(tick1) # Assert self.assertEqual(0, len(bar_store.get_store()))
def trailing_stop_buy(self, last_bar: Bar): """ Users simple trailing stop BUY for (SHORT positions). Parameters ---------- last_bar : Bar The last bar received. """ # Round price to nearest 0.5 (for XBT/USD) price = (round((last_bar.high + (self.atr.value * self.trail_atr_multiple)) * 2) / 2) order: StopMarketOrder = self.order_factory.stop_market( instrument_id=self.instrument_id, order_side=OrderSide.BUY, 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_serialize_and_deserialize_limit_orders_with_expire_time(self): # Arrange order = LimitOrder( ClientOrderId("O-123456"), StrategyId("S", "001"), AUDUSD_SIM.id, OrderSide.BUY, Quantity(100000), price=Price("1.00000"), time_in_force=TimeInForce.GTD, expire_time=UNIX_EPOCH, init_id=uuid4(), timestamp_ns=0, ) # Act serialized = self.serializer.serialize(order) deserialized = self.serializer.deserialize(serialized) # Assert assert deserialized == order print(b64encode(serialized)) print(order)
def test_can_modify_atomic_order_working_stop_loss(self): # Arrange strategy = TestStrategy1(bar_type=TestStubs.bartype_usdjpy_1min_bid()) self.data_client.register_strategy(strategy) self.exec_engine.register_strategy(strategy) strategy.start() self.exec_client.process_tick(TestStubs.tick_3decimal(self.usdjpy.symbol)) # Prepare market atomic_order = strategy.order_factory.atomic_market( USDJPY_FXCM, OrderSide.BUY, Quantity(100000), Price(85.000, 3)) strategy.submit_atomic_order(atomic_order, strategy.position_id_generator.generate()) # Act strategy.modify_order(atomic_order.stop_loss, atomic_order.entry.quantity, Price(85.100, 3)) # Assert self.assertEqual(Price(85.100, 3), strategy.order(atomic_order.stop_loss.id).price) self.assertEqual(8, strategy.object_storer.count) self.assertTrue(isinstance(strategy.object_storer.get_store()[7], OrderModified))
def test_submit_bracket_market_order(self): # Arrange # Prepare market tick = TestStubs.quote_tick_3decimal(USDJPY_SIM.symbol) self.data_engine.process(tick) self.exchange.process_tick(tick) entry_order = self.strategy.order_factory.market( USDJPY_SIM.symbol, OrderSide.BUY, Quantity(100000), ) bracket_order = self.strategy.order_factory.bracket( entry_order, Price("80.000"), ) # Act self.strategy.submit_bracket_order(bracket_order) # Assert self.assertEqual(OrderState.FILLED, entry_order.state)
def parse_bars_http( instrument: Instrument, bar_type: BarType, data: List[Dict[str, Any]], ts_event_delta: int, ts_init: int, ) -> List[Bar]: bars: List[Bar] = [] for row in data: bar: Bar = Bar( bar_type=bar_type, open=Price(row["open"], instrument.price_precision), high=Price(row["high"], instrument.price_precision), low=Price(row["low"], instrument.price_precision), close=Price(row["close"], instrument.price_precision), volume=Quantity(row["volume"], instrument.size_precision), check=True, ts_event=secs_to_nanos(row["time"]) + ts_event_delta, ts_init=ts_init, ) bars.append(bar) return bars
def test_order_initialized(self): # Arrange uuid = uuid4() event = OrderInitialized(cl_ord_id=ClientOrderId("O-2020872378423"), strategy_id=StrategyId("SCALPER", "001"), symbol=Symbol("BTC/USDT", Exchange("BINANCE")), order_side=OrderSide.BUY, order_type=OrderType.LIMIT, quantity=Quantity("0.561000"), time_in_force=TimeInForce.DAY, event_id=uuid, event_timestamp=UNIX_EPOCH, options={"Price": "15200.10"}) # Act # Assert self.assertEqual( f"OrderInitialized(cl_ord_id=O-2020872378423, id={uuid})", str(event)) self.assertEqual( f"OrderInitialized(cl_ord_id=O-2020872378423, id={uuid})", repr(event))
def test_update_order_for_working_order(self): # Arrange order = self.strategy.order_factory.stop_market( AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(100000), Price("1.00000"), ) self.database.add_order(order) order.apply(TestStubs.event_order_submitted(order)) self.database.update_order(order) order.apply(TestStubs.event_order_accepted(order)) self.database.update_order(order) # Act order.apply(TestStubs.event_order_working(order)) self.database.update_order(order) # Assert self.assertEqual(order, self.database.load_order(order.cl_ord_id))
def test_serialize_and_deserialize_amend_order_commands(self): # Arrange command = ModifyOrder( self.trader_id, StrategyId("SCALPER-001"), AUDUSD_SIM.id, ClientOrderId("O-123456"), VenueOrderId("001"), Quantity(100000, precision=0), Price(1.00001, precision=5), None, UUID4(), 0, ) # Act serialized = self.serializer.serialize(command) deserialized = self.serializer.deserialize(serialized) # Assert assert deserialized == command print(b64encode(serialized)) print(command)
def test_can_update_order_for_working_order(self): # Arrange order = self.strategy.order_factory.market(AUDUSD_FXCM, OrderSide.BUY, Quantity(100000)) position_id = self.strategy.position_id_generator.generate() self.database.add_order(order, self.strategy.id, position_id) order_working = TestStubs.event_order_working(order) order.apply(order_working) # Act self.database.update_order(order) # Assert self.assertTrue(self.database.order_exists(order.id)) self.assertTrue(order.id in self.database.get_order_ids()) self.assertTrue(order.id in self.database.get_orders()) self.assertTrue( order.id in self.database.get_orders_working(self.strategy.id)) self.assertTrue(order.id in self.database.get_orders_working()) self.assertTrue(order.id not in self.database.get_orders_completed( self.strategy.id)) self.assertTrue(order.id not in self.database.get_orders_completed())
def test_serialize_and_deserialize_stop_orders_with_expire_time(self): # Arrange order = StopMarketOrder( ClientOrderId("O-123456"), StrategyId("S", "001"), AUDUSD_SIM.symbol, OrderSide.BUY, Quantity(100000), price=Price("1.00000"), time_in_force=TimeInForce.GTD, expire_time=UNIX_EPOCH, init_id=uuid4(), timestamp=UNIX_EPOCH, ) # Act serialized = self.serializer.serialize(order) deserialized = self.serializer.deserialize(serialized) # Assert self.assertEqual(order, deserialized) print(b64encode(serialized)) print(order)
def test_calculate_pnl_for_inverse2(self): # Arrange order = self.order_factory.market( ETHUSD_BITMEX.id, OrderSide.SELL, Quantity.from_int(100000), ) fill = TestEventStubs.order_filled( order, instrument=ETHUSD_BITMEX, position_id=PositionId("P-123456"), strategy_id=StrategyId("S-001"), last_px=Price.from_str("375.95"), ) position = Position(instrument=ETHUSD_BITMEX, fill=fill) # Act, Assert assert position.unrealized_pnl(Price.from_str("370.00")) == Money( 4.27745208, ETH) assert position.notional_value(Price.from_str("370.00")) == Money( 270.27027027, ETH)
def test_submit_order_when_not_connected_logs_and_does_not_send(self): # Arrange strategy = TradingStrategy("000") order = self.order_factory.market( ETHUSDT_BINANCE.id, OrderSide.BUY, Quantity.from_int(100), ) command = SubmitOrder( self.trader_id, strategy.id, PositionId.null(), order, self.uuid_factory.generate(), self.clock.timestamp_ns(), ) # Act self.exec_client.submit_order(command) # Assert assert order.state == OrderState.INITIALIZED
def test_submit_order_when_trading_halted_then_denies_order(self): # Arrange self.exec_engine.start() strategy = TradingStrategy() strategy.register( trader_id=self.trader_id, portfolio=self.portfolio, msgbus=self.msgbus, cache=self.cache, clock=self.clock, logger=self.logger, ) order = strategy.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) submit_order = SubmitOrder( self.trader_id, strategy.id, None, order, self.uuid_factory.generate(), self.clock.timestamp_ns(), ) # Halt trading self.risk_engine.set_trading_state(TradingState.HALTED) # Act self.risk_engine.execute(submit_order) # Assert assert self.risk_engine.command_count == 1 # <-- command never reaches engine
def test_submit_order_when_duplicate_id_then_denies(self): # Arrange self.exec_engine.start() strategy = TradingStrategy() strategy.register( trader_id=self.trader_id, portfolio=self.portfolio, msgbus=self.msgbus, cache=self.cache, clock=self.clock, logger=self.logger, ) order = strategy.order_factory.market( AUDUSD_SIM.id, OrderSide.BUY, Quantity.from_int(100000), ) submit_order = SubmitOrder( self.trader_id, strategy.id, None, order, self.uuid_factory.generate(), self.clock.timestamp_ns(), ) self.risk_engine.execute(submit_order) # Act self.risk_engine.execute(submit_order) # Assert assert self.exec_engine.command_count == 1 assert self.exec_client.calls == ["_start", "submit_order"]