def test_both_orders_executed_when_both_stop_prices_hit(self): self._set_bar_for_today(open=100.0, high=110.0, low=90.0, close=105.0, volume=100000000.0) self.exec_hanlder.on_market_close(...) assert_lists_equal([], self.exec_hanlder.get_open_orders()) verify(self.spied_monitor, times=2).record_transaction(...) verify(self.portfolio, times=2).transact_transaction(...) self.assertEqual(2, len(self.monitor.transactions)) actual_transaction_1 = self.monitor.transactions[0] self.assertEqual(self.msft_contract, actual_transaction_1.contract) self.assertEqual(-1, actual_transaction_1.quantity) self.assertEqual(self.stop_loss_order_1.execution_style.stop_price, actual_transaction_1.price) self.assertEqual(0.0, actual_transaction_1.commission) actual_transaction_2 = self.monitor.transactions[1] self.assertEqual(self.msft_contract, actual_transaction_2.contract) self.assertEqual(-1, actual_transaction_2.quantity) self.assertEqual(self.stop_loss_order_2.execution_style.stop_price, actual_transaction_2.price) self.assertEqual(0.0, actual_transaction_2.commission)
def test_market_opens_at_much_lower_price_than_it_closed_at_yesterday( self): self._set_bar_for_today(open=70.0, high=100.0, low=68.0, close=90.0, volume=100000000.0) self.exec_hanlder.on_market_close(...) assert_lists_equal([], self.exec_hanlder.get_open_orders()) verify(self.spied_monitor, times=2).record_transaction(...) verify(self.portfolio, times=2).transact_transaction(...) self.assertEqual(2, len(self.monitor.transactions)) actual_transaction_1 = self.monitor.transactions[0] self.assertEqual(self.msft_contract, actual_transaction_1.contract) self.assertEqual(-1, actual_transaction_1.quantity) self.assertEqual(70.0, actual_transaction_1.price) self.assertEqual(0.0, actual_transaction_1.commission) actual_transaction_2 = self.monitor.transactions[1] self.assertEqual(self.msft_contract, actual_transaction_2.contract) self.assertEqual(-1, actual_transaction_2.quantity) self.assertEqual(70.0, actual_transaction_2.price) self.assertEqual(0.0, actual_transaction_2.commission)
def test_no_orders_executed_on_market_open(self): # Move before the market open event self._set_bar_for_today(open_price=105.0, high_price=110.0, low_price=100.0, close_price=105.0, volume=100000000.0) self.timer.set_current_time(self.timer.now() + MarketOpenEvent.trigger_time() - RelativeDelta( minutes=self.number_of_minutes)) self.exec_handler.assign_order_ids([ self.stop_loss_order_1, self.stop_loss_order_2, self.stop_loss_order_3 ]) # Trigger the order execution event (the function also forwards time into the future) self._trigger_single_time_event() self.exec_handler.on_market_open(...) self.portfolio.transact_transaction.assert_not_called() self.monitor.record_transaction.assert_not_called() actual_orders = self.exec_handler.get_open_orders() expected_orders = [ self.stop_loss_order_1, self.stop_loss_order_2, self.stop_loss_order_3 ] assert_lists_equal(expected_orders, actual_orders)
def test_market_opens_at_much_higher_price_than_it_closed_at_yesterday( self): self.buy_stop_loss_order = Order(self.msft_contract, quantity=1, execution_style=StopOrder(120.0), time_in_force=TimeInForce.GTC) self.exec_handler.assign_order_ids([self.buy_stop_loss_order]) self._set_bar_for_today(open_price=120.0, high_price=130.0, low_price=68.0, close_price=90.0, volume=100000000.0) self._trigger_single_time_event() self.exec_handler.on_market_close(...) assert_lists_equal([], self.exec_handler.get_open_orders()) expected_transactions = [ Transaction(self.timer.now(), self.msft_contract, -1, self.stop_loss_order_1.execution_style.stop_price, 0), Transaction(self.timer.now(), self.msft_contract, -1, self.stop_loss_order_2.execution_style.stop_price, 0), Transaction(self.timer.now(), self.msft_contract, 1, 120, 0), ] self.monitor.record_transaction.assert_has_calls( call(t) for t in expected_transactions) self.portfolio.transact_transaction.assert_has_calls( call(t) for t in expected_transactions) self.assertEqual(self.monitor.record_transaction.call_count, 3) self.assertEqual(self.portfolio.transact_transaction.call_count, 3)
def test_square_root_market_slippage__with_volume(self): """The volatility used by the slippage model is mocked to be equal to 0.1. The volume is equal to 50.0""" price_impact = 0.1 max_volume_share_limit = 0.1 close_prices_volatility = 0.1 prices_without_slippage = [20.0, 30.0, 40.0] slippage_model = SquareRootMarketImpactSlippage(price_impact=price_impact, data_provider=self.data_provider, contract_ticker_mapper=self.contract_ticker_mapper, max_volume_share_limit=max_volume_share_limit) slippage_model._compute_volatility = Mock() slippage_model._compute_volatility.return_value = close_prices_volatility actual_fill_prices, actual_fill_volumes = slippage_model.process_orders(str_to_date('2020-01-01'), self.orders, prices_without_slippage) expected_fill_prices = [20.0 + 20.0 * price_impact * close_prices_volatility * math.sqrt(5.0 / 50.0), 30.0 - 30.0 * price_impact * close_prices_volatility * math.sqrt(5.0 / 50.0), 40.0 + 40.0 * price_impact * close_prices_volatility * math.sqrt(1 / 50.0)] expected_fill_volumes = [5.0, -5.0, 1.0] assert_lists_equal(expected_fill_prices, actual_fill_prices) assert_lists_equal(expected_fill_volumes, actual_fill_volumes)
def test_square_root_market_slippage__no_volume(self): """The volatility used by the slippage model is mocked to be equal to 0.1. The volume is equal to 50.0""" price_impact = 0.1 close_prices_volatility = 0.1 prices_without_slippage = [20.0, 30.0, 40.0] slippage_model = SquareRootMarketImpactSlippage(price_impact=price_impact, data_provider=self.data_provider, contract_ticker_mapper=self.contract_ticker_mapper) slippage_model._compute_volatility = Mock() slippage_model._compute_volatility.return_value = close_prices_volatility actual_fill_prices, actual_fill_volumes = slippage_model.process_orders(str_to_date('2020-01-01'), self.orders, prices_without_slippage) expected_fill_prices = [20.0 + 20.0 * price_impact * close_prices_volatility * math.sqrt(1250 / 50.0), 30.0 - 30.0 * price_impact * close_prices_volatility * math.sqrt(200 / 50.0), 40.0 + 40.0 * price_impact * close_prices_volatility * math.sqrt(1 / 50.0)] # Volumes should remain equal to the initial quantities expected_fill_volumes = [order.quantity for order in self.orders] # [1250, -200, 1] assert_lists_equal(expected_fill_prices, actual_fill_prices) assert_lists_equal(expected_fill_volumes, actual_fill_volumes)
def test_event_management(self): timer = SettableTimer(initial_time=str_to_date("2018-04-10 00:00:00.000000", DateFormat.FULL_ISO)) end_date = str_to_date("2018-04-10") notifiers = Notifiers(timer) event_manager = self._create_event_manager(timer, notifiers) BacktestTimeFlowController( notifiers.scheduler, event_manager, timer, notifiers.empty_queue_event_notifier, end_date ) listener = DummyListener(notifiers, event_manager, timer) last_event = None while type(last_event) != EndTradingEvent: event_manager.dispatch_next_event() last_event = listener.registered_events[-1] expected_events = [ (EmptyQueueEvent, str_to_date("2018-04-10 00:00:00.000000", DateFormat.FULL_ISO)), (BeforeMarketOpenEvent, str_to_date("2018-04-10 08:00:00.000000", DateFormat.FULL_ISO)), (EmptyQueueEvent, str_to_date("2018-04-10 08:00:00.000000", DateFormat.FULL_ISO)), (MarketOpenEvent, str_to_date("2018-04-10 09:30:00.000000", DateFormat.FULL_ISO)), (EmptyQueueEvent, str_to_date("2018-04-10 09:30:00.000000", DateFormat.FULL_ISO)), (MarketCloseEvent, str_to_date("2018-04-10 16:00:00.000000", DateFormat.FULL_ISO)), (EmptyQueueEvent, str_to_date("2018-04-10 16:00:00.000000", DateFormat.FULL_ISO)), (AfterMarketCloseEvent, str_to_date("2018-04-10 18:00:00.000000", DateFormat.FULL_ISO)), (EmptyQueueEvent, str_to_date("2018-04-10 18:00:00.000000", DateFormat.FULL_ISO)), (EndTradingEvent, str_to_date("2018-04-10 23:59:59.999999", DateFormat.FULL_ISO)), ] actual_events = [(type(event), event.time) for event in listener.registered_events] assert_lists_equal(expected_events, actual_events, absolute_tolerance=0.0)
def test_fill_open_and_close(self): self.exec_handler.assign_order_ids([self.order_1, self.order_2]) self.exec_handler.assign_order_ids([self.order_2, self.order_3]) self.exec_handler.assign_order_ids([self.order_3, self.order_4]) self.exec_handler.assign_order_ids([self.order_4, self.order_4]) self._set_current_price(101) self._trigger_single_time_event() self.exec_handler.on_market_open(...) self.assertEqual(self.monitor.record_transaction.call_count, 3) self.assertEqual(self.portfolio.transact_transaction.call_count, 3) actual_orders = self.exec_handler.get_open_orders() expected_orders = [self.order_4] assert_lists_equal(expected_orders, actual_orders) self.exec_handler.on_market_close(...) self.assertEqual(self.monitor.record_transaction.call_count, 4) self.assertEqual(self.portfolio.transact_transaction.call_count, 4) actual_orders = self.exec_handler.get_open_orders() expected_orders = [] assert_lists_equal(expected_orders, actual_orders)
def test_duration_of_drawdowns(self): series_of_max_drawdowns, duration_of_drawdowns = list_of_max_drawdowns( self.test_dd_prices_tms) drawdowns = [0.3, 0.75, 0.25] durations = [93, 62, 1] assert_lists_equal(durations, duration_of_drawdowns) assert_lists_equal(drawdowns, series_of_max_drawdowns)
def test_fill_close_and_open(self): self.exec_hanlder.accept_orders([self.order_1, self.order_2]) self.exec_hanlder.accept_orders([self.order_2, self.order_3]) self.exec_hanlder.accept_orders([self.order_3, self.order_4]) self.exec_hanlder.accept_orders([self.order_4, self.order_4]) self._set_price_for_now(101) self.exec_hanlder.on_market_close(...) verify(self.spied_monitor, times=3).record_transaction(...) verify(self.portfolio, times=3).transact_transaction(...) actual_orders = self.exec_hanlder.get_open_orders() expected_orders = [ self.order_1, self.order_2, self.order_2, self.order_3, self.order_3 ] assert_lists_equal(expected_orders, actual_orders) self.exec_hanlder.on_market_open(...) verify(self.spied_monitor, times=8).record_transaction(...) verify(self.portfolio, times=8).transact_transaction(...) actual_orders = self.exec_hanlder.get_open_orders() expected_orders = [] assert_lists_equal(expected_orders, actual_orders)
def test_market_opens_at_much_higher_price_than_it_closed_at_yesterday( self): self.buy_stop_loss_order = Order(self.msft_contract, quantity=1, execution_style=StopOrder(120.0), time_in_force=TimeInForce.GTC) self.exec_hanlder.accept_orders([self.buy_stop_loss_order]) self._set_bar_for_today(open=120.0, high=130.0, low=68.0, close=90.0, volume=100000000.0) self.exec_hanlder.on_market_close(...) assert_lists_equal([], self.exec_hanlder.get_open_orders()) verify(self.spied_monitor, times=3).record_transaction(...) verify(self.portfolio, times=3).transact_transaction(...) self.assertEqual(3, len(self.monitor.transactions)) actual_transaction_3 = self.monitor.transactions[2] self.assertEqual(self.msft_contract, actual_transaction_3.contract) self.assertEqual(1, actual_transaction_3.quantity) self.assertEqual(120.0, actual_transaction_3.price) self.assertEqual(0.0, actual_transaction_3.commission)
def test_price_based_slippage(self): slippage_model = PriceBasedSlippage(slippage_rate=0.1) actual_fill_prices, actual_fill_volumes = slippage_model.apply_slippage( self.orders, self.prices_without_slippage) expected_fill_prices = [1.1, 90.0, 1100.0] expected_fill_volumes = [1000, -10, 1] assert_lists_equal(expected_fill_prices, actual_fill_prices) assert_lists_equal(expected_fill_volumes, actual_fill_volumes)
def test_no_orders_executed_on_market_open(self): self.exec_hanlder.on_market_open(...) verifyZeroInteractions(self.portfolio, self.spied_monitor) actual_orders = self.exec_hanlder.get_open_orders() expected_orders = [ self.stop_loss_order_1, self.stop_loss_order_2, self.stop_loss_order_3 ] assert_lists_equal(expected_orders, actual_orders)
def test_3_orders_fill_only_at_open(self): self.exec_hanlder.accept_orders( [self.order_1, self.order_2, self.order_3]) self._set_price_for_now(101) self.exec_hanlder.on_market_close(...) verifyZeroInteractions(self.portfolio, self.spied_monitor) actual_orders = self.exec_hanlder.get_open_orders() expected_orders = [self.order_1, self.order_2, self.order_3] assert_lists_equal(expected_orders, actual_orders)
def test_rolling_window_slices(self): actual_slices = rolling_window_slices(self.index, size=pd.DateOffset(months=6), step=1) expected_slices = [ slice(pd.Timestamp('2017-01-02'), pd.Timestamp('2017-07-02')), slice(pd.Timestamp('2017-02-01'), pd.Timestamp('2017-08-01')), slice(pd.Timestamp('2017-03-01'), pd.Timestamp('2017-09-01')), slice(pd.Timestamp('2017-04-03'), pd.Timestamp('2017-10-03')), slice(pd.Timestamp('2017-05-01'), pd.Timestamp('2017-11-01')), slice(pd.Timestamp('2017-06-01'), pd.Timestamp('2017-12-01')) ] assert_lists_equal(expected_slices, actual_slices)
def test_price_based_slippage__nan_prices(self): slippage_rate = 0.1 slippage_model = PriceBasedSlippage(slippage_rate, self.data_provider) prices_without_slippage = [float('nan'), np.nan, float('nan')] expected_fill_prices = [float('nan'), float('nan'), float('nan')] actual_fill_prices, actual_fill_volumes = slippage_model.process_orders(str_to_date("2020-01-01"), self.orders, prices_without_slippage) assert_lists_equal(expected_fill_prices, actual_fill_prices)
def test_periods_list_from_bool_series(self): actual_periods_list = periods_list_from_bool_series(self.bool_series) expected_periods_list = [ (str_to_date("2017-01-02"), str_to_date("2017-01-06")), (str_to_date("2017-01-09"), str_to_date("2017-01-12")) ] assert_lists_equal(expected_periods_list, actual_periods_list) for start_date, end_date in actual_periods_list: self.assertEqual(datetime, type(start_date), "Error while checking start_date: {}".format(str(start_date))) self.assertEqual(datetime, type(end_date), "Error while checking end_date: {}".format(str(end_date)))
def test_fixed_slippage(self): slippage_model = FixedSlippage(slippage_per_share=0.05) actual_fill_prices, actual_fill_volumes = slippage_model.apply_slippage( self.orders, self.prices_without_slippage) expected_fill_prices = [1.05, 99.95, 1000.05] expected_fill_volumes = [1000, -10, 1] assert_lists_equal(expected_fill_prices, actual_fill_prices) assert_lists_equal(expected_fill_volumes, actual_fill_volumes)
def test_1_order_fill(self): self.exec_hanlder.accept_orders([self.order_1]) self._set_price_for_now(101) self.exec_hanlder.on_market_open(...) verify(self.spied_monitor, times=1).record_transaction(...) verify(self.portfolio, times=1).transact_transaction(...) actual_orders = self.exec_hanlder.get_open_orders() expected_orders = [] assert_lists_equal(expected_orders, actual_orders)
def test_fixed_slippage__nan_prices(self): slippage_per_share = 0.1 slippage_model = FixedSlippage(slippage_per_share, self.data_provider, self.contract_ticker_mapper) prices_without_slippage = [float('nan'), np.nan, float('nan')] expected_fill_prices = [float('nan'), float('nan'), float('nan')] actual_fill_prices, actual_fill_volumes = slippage_model.process_orders(str_to_date("2020-01-01"), self.orders, prices_without_slippage) assert_lists_equal(expected_fill_prices, actual_fill_prices)
def test_order_not_executed_when_bar_for_today_is_incomplete(self): self._set_bar_for_today(open_price=None, high_price=110.0, low_price=100.0, close_price=105.0, volume=100000000.0) self._trigger_single_time_event() self.exec_handler.on_market_close(...) self.portfolio.transact_transaction.assert_not_called() self.monitor.record_transaction.assert_not_called() actual_orders = self.exec_handler.get_open_orders() expected_orders = [self.stop_loss_order_1, self.stop_loss_order_2] assert_lists_equal(expected_orders, actual_orders)
def test_one_order_executed_when_one_stop_price_hit(self): self._set_bar_for_today(open_price=100.0, high_price=110.0, low_price=94.0, close_price=105.0, volume=100000000.0) self._trigger_single_time_event() self.exec_handler.on_market_close(...) assert_lists_equal([self.stop_loss_order_2], self.exec_handler.get_open_orders()) expected_transaction = Transaction(self.timer.now(), self.msft_ticker, -1, self.stop_loss_order_1.execution_style.stop_price, 0.0) self.monitor.record_transaction.assert_called_once_with(expected_transaction) self.portfolio.transact_transaction.assert_called_once_with(expected_transaction)
def test_1_order_fill(self): self.exec_handler.assign_order_ids([self.order_1]) self._set_current_price(101) self._trigger_single_time_event() self.exec_handler.on_market_open(...) self.monitor.record_transaction.assert_called_once() self.portfolio.transact_transaction.assert_called_once() actual_orders = self.exec_handler.get_open_orders() expected_orders = [] assert_lists_equal(expected_orders, actual_orders)
def test_order_not_executed_when_bar_for_today_is_incomplete(self): self._set_bar_for_today(open=None, high=110.0, low=100.0, close=105.0, volume=100000000.0) self.exec_hanlder.on_market_close(...) verifyZeroInteractions(self.portfolio, self.spied_monitor) actual_orders = self.exec_hanlder.get_open_orders() expected_orders = [self.stop_loss_order_1, self.stop_loss_order_2] assert_lists_equal(expected_orders, actual_orders)
def test_square_root_market_slippage__nan_prices_without_slippage(self): price_impact = 0.1 slippage_model = SquareRootMarketImpactSlippage(price_impact=price_impact, data_provider=self.data_provider) prices_without_slippage = [float('nan'), np.nan, float('nan')] expected_fill_prices = [float('nan'), float('nan'), float('nan')] actual_fill_prices, actual_fill_volumes = slippage_model.process_orders(str_to_date("2020-01-01"), self.orders, prices_without_slippage) assert_lists_equal(expected_fill_prices, actual_fill_prices)
def test_market_opens_at_much_lower_price_than_it_closed_at_yesterday(self): self._set_bar_for_today(open_price=70.0, high_price=100.0, low_price=68.0, close_price=90.0, volume=100000000.0) self._trigger_single_time_event() self.exec_handler.on_market_close(...) assert_lists_equal([], self.exec_handler.get_open_orders()) expected_transactions = [ Transaction(self.timer.now(), self.msft_ticker, -1, 70.0, 0), Transaction(self.timer.now(), self.msft_ticker, -1, 70.0, 0), ] self.monitor.record_transaction.assert_has_calls(call(t) for t in expected_transactions) self.portfolio.transact_transaction.assert_has_calls(call(t) for t in expected_transactions) self.assertEqual(self.monitor.record_transaction.call_count, 2) self.assertEqual(self.portfolio.transact_transaction.call_count, 2)
def test_volume_share_slippage(self): slippage_model = VolumeShareSlippage( volume_share_limit=0.1, price_impact=0.1, data_handler=self.data_handler, contract_ticker_mapper=DummyBloombergContractTickerMapper()) actual_fill_prices, actual_fill_volumes = slippage_model.apply_slippage( self.orders, self.prices_without_slippage) expected_fill_prices = [float("nan"), 99.9, 1000.01] expected_fill_volumes = [0, -1, 1] assert_lists_equal(expected_fill_prices, actual_fill_prices) assert_lists_equal(expected_fill_volumes, actual_fill_volumes)
def test_square_root_market_slippage__nan_average_daily_volume(self): price_impact = 0.1 avg_daily_volume = float('nan') prices_without_slippage = [20, 30, 40] expected_fill_prices = [float('nan'), float('nan'), float('nan')] slippage_model = SquareRootMarketImpactSlippage(price_impact=price_impact, data_provider=self.data_provider) slippage_model._compute_average_volume = Mock() slippage_model._compute_average_volume.return_value = avg_daily_volume actual_fill_prices, actual_fill_volumes = slippage_model.process_orders(str_to_date("2020-01-01"), self.orders, prices_without_slippage) assert_lists_equal(expected_fill_prices, actual_fill_prices)
def test_both_orders_executed_when_both_stop_prices_hit(self): self._set_bar_for_today(open_price=100.0, high_price=110.0, low_price=90.0, close_price=105.0, volume=100000000.0) self._trigger_single_time_event() self.exec_handler.on_market_close(...) assert_lists_equal([], self.exec_handler.get_open_orders()) expected_transactions = [ Transaction(self.timer.now(), self.msft_ticker, -1, self.stop_loss_order_1.execution_style.stop_price, 0), Transaction(self.timer.now(), self.msft_ticker, -1, self.stop_loss_order_2.execution_style.stop_price, 0), ] self.monitor.record_transaction.assert_has_calls(call(t) for t in expected_transactions) self.portfolio.transact_transaction.assert_has_calls(call(t) for t in expected_transactions) self.assertEqual(self.monitor.record_transaction.call_count, 2) self.assertEqual(self.portfolio.transact_transaction.call_count, 2)
def test_square_root_market_slippage__nan_volatility(self): price_impact = 0.1 close_prices_volatility = float('nan') prices_without_slippage = [float('nan'), np.nan, 40] expected_fill_prices = [float('nan'), float('nan'), float('nan')] slippage_model = SquareRootMarketImpactSlippage(price_impact=price_impact, data_provider=self.data_provider) slippage_model._compute_volatility = Mock() slippage_model._compute_volatility.return_value = close_prices_volatility actual_fill_prices, actual_fill_volumes = slippage_model.process_orders(str_to_date("2020-01-01"), self.orders, prices_without_slippage) assert_lists_equal(expected_fill_prices, actual_fill_prices)