def test_handle_trade_tick_when_volume_below_threshold_updates(self): # Arrange bar_store = ObjectStorer() handler = bar_store.store instrument = AUDUSD_SIM bar_spec = BarSpecification(10000, BarAggregation.VOLUME, PriceType.LAST) bar_type = BarType(instrument.id, bar_spec) aggregator = VolumeBarAggregator( instrument, bar_type, handler, Logger(TestClock()), ) tick1 = TradeTick( instrument_id=instrument.id, price=Price.from_str("1.00001"), size=Quantity.from_int(1), aggressor_side=AggressorSide.BUY, trade_id=TradeId("123456"), ts_event=0, ts_init=0, ) # Act aggregator.handle_trade_tick(tick1) # Assert assert len(bar_store.get_store()) == 0
def test_handle_quote_tick_when_volume_below_threshold_updates(self): # Arrange bar_store = ObjectStorer() handler = bar_store.store instrument = AUDUSD_SIM bar_spec = BarSpecification(10000, BarAggregation.VOLUME, PriceType.BID) bar_type = BarType(instrument.id, bar_spec) aggregator = VolumeBarAggregator( instrument, bar_type, handler, Logger(TestClock()), ) tick1 = QuoteTick( instrument_id=instrument.id, bid=Price.from_str("1.00001"), ask=Price.from_str("1.00004"), bid_size=Quantity.from_int(3000), ask_size=Quantity.from_int(2000), ts_event=0, ts_init=0, ) # Act aggregator.handle_quote_tick(tick1) # Assert assert len(bar_store.get_store()) == 0
def test_handle_quote_tick_when_value_below_threshold_updates(self): # Arrange bar_store = ObjectStorer() handler = bar_store.store instrument_id = TestIdStubs.audusd_id() bar_spec = BarSpecification(100000, BarAggregation.VALUE, PriceType.BID) bar_type = BarType(instrument_id, bar_spec) aggregator = ValueBarAggregator( AUDUSD_SIM, bar_type, handler, Logger(TestClock()), ) tick1 = QuoteTick( instrument_id=AUDUSD_SIM.id, bid=Price.from_str("1.00001"), ask=Price.from_str("1.00004"), bid_size=Quantity.from_int(3000), ask_size=Quantity.from_int(2000), ts_event=0, ts_init=0, ) # Act aggregator.handle_quote_tick(tick1) # Assert assert len(bar_store.get_store()) == 0 assert aggregator.get_cumulative_value() == Decimal("3000.03000")
def test_handle_trade_tick_when_value_below_threshold_updates(self): # Arrange bar_store = ObjectStorer() handler = bar_store.store instrument_id = TestIdStubs.audusd_id() bar_spec = BarSpecification(100000, BarAggregation.VALUE, PriceType.LAST) bar_type = BarType(instrument_id, bar_spec) aggregator = ValueBarAggregator( AUDUSD_SIM, bar_type, handler, Logger(TestClock()), ) tick1 = TradeTick( instrument_id=AUDUSD_SIM.id, price=Price.from_str("15000.00"), size=Quantity.from_str("3.5"), aggressor_side=AggressorSide.BUY, trade_id=TradeId("123456"), ts_event=0, ts_init=0, ) # Act aggregator.handle_trade_tick(tick1) # Assert assert len(bar_store.get_store()) == 0 assert aggregator.get_cumulative_value() == Decimal("52500.000")
def test_run_trade_ticks_through_aggregator_results_in_expected_bars(self): # Arrange bar_store = ObjectStorer() handler = bar_store.store instrument = ETHUSDT_BINANCE bar_spec = BarSpecification(1000, BarAggregation.VOLUME, PriceType.LAST) bar_type = BarType(instrument.id, bar_spec) aggregator = VolumeBarAggregator( instrument, bar_type, handler, Logger(TestClock()), ) wrangler = TradeTickDataWrangler(instrument=ETHUSDT_BINANCE) provider = TestDataProvider() ticks = wrangler.process( provider.read_csv_ticks("binance-ethusdt-trades.csv")[:10000]) # Act for tick in ticks: aggregator.handle_trade_tick(tick) # Assert last_bar = bar_store.get_store()[-1] assert len(bar_store.get_store()) == 26 assert last_bar.open == Price.from_str("425.17") assert last_bar.high == Price.from_str("425.24") assert last_bar.low == Price.from_str("424.69") assert last_bar.close == Price.from_str("425.14") assert last_bar.volume == Quantity.from_int(1000)
def test_run_quote_ticks_through_aggregator_results_in_expected_bars(self): # Arrange bar_store = ObjectStorer() handler = bar_store.store instrument = AUDUSD_SIM bar_spec = BarSpecification(1000, BarAggregation.VOLUME, PriceType.MID) bar_type = BarType(instrument.id, bar_spec) aggregator = VolumeBarAggregator( instrument, bar_type, handler, Logger(TestClock()), ) # Setup data wrangler = QuoteTickDataWrangler(instrument) provider = TestDataProvider() ticks = wrangler.process( data=provider.read_csv_ticks("truefx-audusd-ticks.csv")[:10000], default_volume=1, ) # Act for tick in ticks: aggregator.handle_quote_tick(tick) # Assert last_bar = bar_store.get_store()[-1] assert len(bar_store.get_store()) == 10 assert last_bar.open == Price.from_str("0.670635") assert last_bar.high == Price.from_str("0.670705") assert last_bar.low == Price.from_str("0.670370") assert last_bar.close == Price.from_str("0.670655") assert last_bar.volume == Quantity.from_int(1000)
def __init__(self, bar_type: BarType): super().__init__() self.object_storer = ObjectStorer() self.bar_type = bar_type self.ema1 = ExponentialMovingAverage(10) self.ema2 = ExponentialMovingAverage(20) self.position_id: Optional[PositionId] = None self.calls: List[str] = []
def test_update_timed_with_test_clock_sends_single_bar_to_handler(self): # Arrange clock = TestClock() bar_store = ObjectStorer() handler = bar_store.store instrument_id = TestIdStubs.audusd_id() bar_spec = BarSpecification(1, BarAggregation.MINUTE, PriceType.MID) bar_type = BarType(instrument_id, bar_spec) aggregator = TimeBarAggregator( AUDUSD_SIM, bar_type, handler, TestClock(), Logger(clock), ) tick1 = QuoteTick( instrument_id=AUDUSD_SIM.id, bid=Price.from_str("1.00001"), ask=Price.from_str("1.00004"), bid_size=Quantity.from_int(1), ask_size=Quantity.from_int(1), ts_event=0, ts_init=0, ) tick2 = QuoteTick( instrument_id=AUDUSD_SIM.id, bid=Price.from_str("1.00002"), ask=Price.from_str("1.00005"), bid_size=Quantity.from_int(1), ask_size=Quantity.from_int(1), ts_event=0, ts_init=0, ) tick3 = QuoteTick( instrument_id=AUDUSD_SIM.id, bid=Price.from_str("1.00000"), ask=Price.from_str("1.00003"), bid_size=Quantity.from_int(1), ask_size=Quantity.from_int(1), ts_event=2 * 60 * 1_000_000_000, # 2 minutes in nanoseconds ts_init=2 * 60 * 1_000_000_000, # 2 minutes in nanoseconds ) # Act aggregator.handle_quote_tick(tick1) aggregator.handle_quote_tick(tick2) aggregator.handle_quote_tick(tick3) # Assert assert len(bar_store.get_store()) == 1 assert Price.from_str("1.000025") == bar_store.get_store()[0].open assert Price.from_str("1.000035") == bar_store.get_store()[0].high assert Price.from_str("1.000025") == bar_store.get_store()[0].low assert Price.from_str("1.000035") == bar_store.get_store()[0].close assert Quantity.from_int(2) == bar_store.get_store()[0].volume assert 60_000_000_000 == bar_store.get_store()[0].ts_init
def test_handle_quote_tick_when_value_beyond_threshold_sends_bar_to_handler( self): # Arrange bar_store = ObjectStorer() handler = bar_store.store instrument_id = TestIdStubs.audusd_id() bar_spec = BarSpecification(100000, BarAggregation.VALUE, PriceType.BID) bar_type = BarType(instrument_id, bar_spec) aggregator = ValueBarAggregator( AUDUSD_SIM, bar_type, handler, Logger(TestClock()), ) tick1 = QuoteTick( instrument_id=AUDUSD_SIM.id, bid=Price.from_str("1.00001"), ask=Price.from_str("1.00004"), bid_size=Quantity.from_int(20000), ask_size=Quantity.from_int(20000), ts_event=0, ts_init=0, ) tick2 = QuoteTick( instrument_id=AUDUSD_SIM.id, bid=Price.from_str("1.00002"), ask=Price.from_str("1.00005"), bid_size=Quantity.from_int(60000), ask_size=Quantity.from_int(20000), ts_event=0, ts_init=0, ) tick3 = QuoteTick( instrument_id=AUDUSD_SIM.id, bid=Price.from_str("1.00000"), ask=Price.from_str("1.00003"), bid_size=Quantity.from_int(30500), ask_size=Quantity.from_int(20000), ts_event=0, ts_init=0, ) # Act aggregator.handle_quote_tick(tick1) aggregator.handle_quote_tick(tick2) aggregator.handle_quote_tick(tick3) # Assert assert len(bar_store.get_store()) == 1 assert bar_store.get_store()[0].open == Price.from_str("1.00001") assert bar_store.get_store()[0].high == Price.from_str("1.00002") assert bar_store.get_store()[0].low == Price.from_str("1.00000") assert bar_store.get_store()[0].close == Price.from_str("1.00000") assert bar_store.get_store()[0].volume == Quantity.from_str("99999") assert aggregator.get_cumulative_value() == Decimal("10501.400")
def test_handle_quote_tick_when_volume_at_threshold_sends_bar_to_handler( self): # Arrange bar_store = ObjectStorer() handler = bar_store.store instrument = AUDUSD_SIM bar_spec = BarSpecification(10000, BarAggregation.VOLUME, PriceType.BID) bar_type = BarType(instrument.id, bar_spec) aggregator = VolumeBarAggregator( instrument, bar_type, handler, Logger(TestClock()), ) tick1 = QuoteTick( instrument_id=instrument.id, bid=Price.from_str("1.00001"), ask=Price.from_str("1.00004"), bid_size=Quantity.from_int(3000), ask_size=Quantity.from_int(2000), ts_event=0, ts_init=0, ) tick2 = QuoteTick( instrument_id=instrument.id, bid=Price.from_str("1.00002"), ask=Price.from_str("1.00005"), bid_size=Quantity.from_int(4000), ask_size=Quantity.from_int(2000), ts_event=0, ts_init=0, ) tick3 = QuoteTick( instrument_id=instrument.id, bid=Price.from_str("1.00000"), ask=Price.from_str("1.00003"), bid_size=Quantity.from_int(3000), ask_size=Quantity.from_int(2000), ts_event=0, ts_init=0, ) # Act aggregator.handle_quote_tick(tick1) aggregator.handle_quote_tick(tick2) aggregator.handle_quote_tick(tick3) # Assert assert len(bar_store.get_store()) == 1 assert bar_store.get_store()[0].open == Price.from_str("1.00001") assert bar_store.get_store()[0].high == Price.from_str("1.00002") assert bar_store.get_store()[0].low == Price.from_str("1.00000") assert bar_store.get_store()[0].close == Price.from_str("1.00000") assert bar_store.get_store()[0].volume == Quantity.from_int(10000)
def test_handle_trade_tick_when_volume_at_threshold_sends_bar_to_handler( self): # Arrange bar_store = ObjectStorer() handler = bar_store.store instrument = AUDUSD_SIM bar_spec = BarSpecification(10000, BarAggregation.VOLUME, PriceType.LAST) bar_type = BarType(instrument.id, bar_spec) aggregator = VolumeBarAggregator( instrument, bar_type, handler, Logger(TestClock()), ) tick1 = TradeTick( instrument_id=instrument.id, price=Price.from_str("1.00001"), size=Quantity.from_int(3000), aggressor_side=AggressorSide.BUY, trade_id=TradeId("123456"), ts_event=0, ts_init=0, ) tick2 = TradeTick( instrument_id=instrument.id, price=Price.from_str("1.00002"), size=Quantity.from_int(4000), aggressor_side=AggressorSide.BUY, trade_id=TradeId("123457"), ts_event=0, ts_init=0, ) tick3 = TradeTick( instrument_id=instrument.id, price=Price.from_str("1.00000"), size=Quantity.from_int(3000), aggressor_side=AggressorSide.BUY, trade_id=TradeId("123458"), ts_event=0, ts_init=0, ) # Act aggregator.handle_trade_tick(tick1) aggregator.handle_trade_tick(tick2) aggregator.handle_trade_tick(tick3) # Assert assert len(bar_store.get_store()) == 1 assert bar_store.get_store()[0].open == Price.from_str("1.00001") assert bar_store.get_store()[0].high == Price.from_str("1.00002") assert bar_store.get_store()[0].low == Price.from_str("1.00000") assert bar_store.get_store()[0].close == Price.from_str("1.00000") assert bar_store.get_store()[0].volume == Quantity.from_int(10000)
def test_instantiate_given_invalid_bar_spec_raises_value_error(self): # Arrange clock = TestClock() bar_store = ObjectStorer() handler = bar_store.store instrument = AUDUSD_SIM bar_spec = BarSpecification(100, BarAggregation.TICK, PriceType.MID) bar_type = BarType(instrument.id, bar_spec) # Act, Assert with pytest.raises(ValueError): TimeBarAggregator( instrument, bar_type, handler, clock, Logger(clock), )
def test_instantiate_with_various_bar_specs(self, bar_spec, expected): # Arrange clock = TestClock() bar_store = ObjectStorer() handler = bar_store.store instrument_id = TestIdStubs.audusd_id() bar_type = BarType(instrument_id, bar_spec) # Act aggregator = TimeBarAggregator( AUDUSD_SIM, bar_type, handler, clock, Logger(clock), ) # Assert assert aggregator.next_close_ns == expected
def test_handle_trade_tick_when_volume_beyond_threshold_sends_bars_to_handler( self): # Arrange bar_store = ObjectStorer() handler = bar_store.store instrument_id = TestIdStubs.audusd_id() bar_spec = BarSpecification(100000, BarAggregation.VALUE, PriceType.LAST) bar_type = BarType(instrument_id, bar_spec) aggregator = ValueBarAggregator( AUDUSD_SIM, bar_type, handler, Logger(TestClock()), ) tick1 = TradeTick( instrument_id=AUDUSD_SIM.id, price=Price.from_str("20.00001"), size=Quantity.from_str("3000.00"), aggressor_side=AggressorSide.BUY, trade_id=TradeId("123456"), ts_event=0, ts_init=0, ) tick2 = TradeTick( instrument_id=AUDUSD_SIM.id, price=Price.from_str("20.00002"), size=Quantity.from_str("4000.00"), aggressor_side=AggressorSide.BUY, trade_id=TradeId("123457"), ts_event=0, ts_init=0, ) tick3 = TradeTick( instrument_id=AUDUSD_SIM.id, price=Price.from_str("20.00000"), size=Quantity.from_str("5000.00"), aggressor_side=AggressorSide.BUY, trade_id=TradeId("123458"), ts_event=0, ts_init=0, ) # Act aggregator.handle_trade_tick(tick1) aggregator.handle_trade_tick(tick2) aggregator.handle_trade_tick(tick3) # Assert assert len(bar_store.get_store()) == 2 assert bar_store.get_store()[0].open == Price.from_str("20.00001") assert bar_store.get_store()[0].high == Price.from_str("20.00002") assert bar_store.get_store()[0].low == Price.from_str("20.00001") assert bar_store.get_store()[0].close == Price.from_str("20.00002") assert bar_store.get_store()[0].volume == Quantity.from_str("5000.00") assert bar_store.get_store()[1].open == Price.from_str("20.00002") assert bar_store.get_store()[1].high == Price.from_str("20.00002") assert bar_store.get_store()[1].low == Price.from_str("20.00000") assert bar_store.get_store()[1].close == Price.from_str("20.00000") assert bar_store.get_store()[1].volume == Quantity.from_str("5000.00") assert aggregator.get_cumulative_value() == Decimal("40000.11000")
class MockStrategy(TradingStrategy): """ Provides a mock trading strategy for testing. Parameters ---------- bar_type : BarType The bar type for the strategy. """ def __init__(self, bar_type: BarType): super().__init__() self.object_storer = ObjectStorer() self.bar_type = bar_type self.ema1 = ExponentialMovingAverage(10) self.ema2 = ExponentialMovingAverage(20) self.position_id: Optional[PositionId] = None self.calls: List[str] = [] def on_start(self) -> None: self.calls.append(inspect.currentframe().f_code.co_name) self.register_indicator_for_bars(self.bar_type, self.ema1) self.register_indicator_for_bars(self.bar_type, self.ema2) def on_instrument(self, instrument) -> None: self.calls.append(inspect.currentframe().f_code.co_name) self.object_storer.store(instrument) def on_ticker(self, ticker): self.calls.append(inspect.currentframe().f_code.co_name) self.object_storer.store(ticker) def on_quote_tick(self, tick): self.calls.append(inspect.currentframe().f_code.co_name) self.object_storer.store(tick) def on_trade_tick(self, tick) -> None: self.calls.append(inspect.currentframe().f_code.co_name) self.object_storer.store(tick) def on_bar(self, bar) -> None: self.calls.append(inspect.currentframe().f_code.co_name) self.object_storer.store(bar) if bar.type != self.bar_type: return if self.ema1.value > self.ema2.value: buy_order = self.order_factory.market( self.bar_type.instrument_id, OrderSide.BUY, 100000, ) self.submit_order(buy_order) self.position_id = buy_order.client_order_id elif self.ema1.value < self.ema2.value: sell_order = self.order_factory.market( self.bar_type.instrument_id, OrderSide.SELL, 100000, ) self.submit_order(sell_order) self.position_id = sell_order.client_order_id def on_data(self, data) -> None: self.calls.append(inspect.currentframe().f_code.co_name) self.object_storer.store(data) def on_strategy_data(self, data) -> None: self.calls.append(inspect.currentframe().f_code.co_name) self.object_storer.store(data) def on_event(self, event) -> None: self.calls.append(inspect.currentframe().f_code.co_name) self.object_storer.store(event) def on_stop(self) -> None: self.calls.append(inspect.currentframe().f_code.co_name) def on_resume(self) -> None: self.calls.append(inspect.currentframe().f_code.co_name) def on_reset(self) -> None: self.calls.append(inspect.currentframe().f_code.co_name) def on_save(self) -> Dict[str, bytes]: self.calls.append(inspect.currentframe().f_code.co_name) return {"UserState": b"1"} def on_load(self, state: Dict[str, bytes]) -> None: self.calls.append(inspect.currentframe().f_code.co_name) self.object_storer.store(state) def on_dispose(self) -> None: self.calls.append(inspect.currentframe().f_code.co_name)
def __init__(self, config: ActorConfig = None): super().__init__(config) self.object_storer = ObjectStorer() self.calls: List[str] = []
class MockActor(Actor): """ Provides a mock actor for testing. """ def __init__(self, config: ActorConfig = None): super().__init__(config) self.object_storer = ObjectStorer() self.calls: List[str] = [] def on_start(self) -> None: self.calls.append(inspect.currentframe().f_code.co_name) def on_stop(self) -> None: self.calls.append(inspect.currentframe().f_code.co_name) def on_resume(self) -> None: self.calls.append(inspect.currentframe().f_code.co_name) def on_reset(self) -> None: self.calls.append(inspect.currentframe().f_code.co_name) def on_dispose(self) -> None: self.calls.append(inspect.currentframe().f_code.co_name) def on_degrade(self) -> None: self.calls.append(inspect.currentframe().f_code.co_name) def on_fault(self) -> None: self.calls.append(inspect.currentframe().f_code.co_name) def on_instrument(self, instrument) -> None: self.calls.append(inspect.currentframe().f_code.co_name) self.object_storer.store(instrument) def on_ticker(self, ticker): self.calls.append(inspect.currentframe().f_code.co_name) self.object_storer.store(ticker) def on_quote_tick(self, tick): self.calls.append(inspect.currentframe().f_code.co_name) self.object_storer.store(tick) def on_trade_tick(self, tick) -> None: self.calls.append(inspect.currentframe().f_code.co_name) self.object_storer.store(tick) def on_bar(self, bar) -> None: self.calls.append(inspect.currentframe().f_code.co_name) self.object_storer.store(bar) def on_data(self, data) -> None: self.calls.append(inspect.currentframe().f_code.co_name) self.object_storer.store(data) def on_strategy_data(self, data) -> None: self.calls.append(inspect.currentframe().f_code.co_name) self.object_storer.store(data) def on_event(self, event) -> None: self.calls.append(inspect.currentframe().f_code.co_name) self.object_storer.store(event)