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_price_based_slippage__nan_prices(self): slippage_rate = 0.1 slippage_model = PriceBasedSlippage(slippage_rate, 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_price_based_slippage__no_volume_limits(self): """Volume should remain the same. Prices should be increased by the slippage rate.""" slippage_rate = 0.1 slippage_model = PriceBasedSlippage(slippage_rate, self.data_provider, self.contract_ticker_mapper) prices_without_slippage = [20.0, 30.0, 40.0] # Each price should be changed by +0.1 / -0.1 depending on whether it is a BUY or SELL expected_fill_prices = [22.0, 27.0, 44.0] # Volumes should remain equal to the initial quantities expected_fill_volumes = [order.quantity for order in self.orders] # [1250, -200, 1] 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) assert_lists_equal(expected_fill_volumes, actual_fill_volumes)
def test_price_based_slippage__with_volume(self): """Volume should be changed in case of exceeding limits. Slippage rate is set to 0.0, so the prices should remain unchanged.""" slippage_rate = 0.0 max_volume_share_limit = 0.1 slippage_model = PriceBasedSlippage(slippage_rate, self.data_provider, max_volume_share_limit) prices_without_slippage = [20.0, 30.0, 40.0] expected_fill_prices = prices_without_slippage # Mean historical volume is set to 50.0 for each of the tickers. As the max_volume_share_limit = 0.1, the limit # is set to +/-5.0 expected_fill_volumes = [5.0, -5.0, 1] 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) assert_lists_equal(expected_fill_volumes, actual_fill_volumes)
def setUp(self): self.scheduling_time_delay = 1 start_date = str_to_date("2018-02-04") before_close = start_date + MarketCloseEvent.trigger_time( ) - RelativeDelta(minutes=self.scheduling_time_delay) self.timer = SettableTimer(initial_time=before_close) contracts_to_tickers_mapper = SimulatedBloombergContractTickerMapper() msft_contract = Contract("MSFT US Equity", security_type='STK', exchange='TEST') self.msft_ticker = contracts_to_tickers_mapper.contract_to_ticker( msft_contract) self.data_handler = Mock(spec=DataHandler) self.scheduler = Mock(spec=Scheduler) self.commission_model = FixedCommissionModel(commission=0.0) self.monitor = Mock(spec=AbstractMonitor) self.portfolio = Mock(spec=Portfolio) slippage_model = PriceBasedSlippage(0.0, self.data_handler, contracts_to_tickers_mapper) self.exec_handler = SimulatedExecutionHandler( self.data_handler, self.timer, self.scheduler, self.monitor, self.commission_model, contracts_to_tickers_mapper, self.portfolio, slippage_model, RelativeDelta(minutes=self.scheduling_time_delay)) self.order_1 = Order(msft_contract, quantity=10, execution_style=MarketOrder(), time_in_force=TimeInForce.OPG) self.order_2 = Order(msft_contract, quantity=-5, execution_style=MarketOrder(), time_in_force=TimeInForce.OPG) self.order_3 = Order(msft_contract, quantity=-7, execution_style=MarketOrder(), time_in_force=TimeInForce.OPG) self.order_4 = Order(msft_contract, quantity=4, execution_style=MarketOnCloseOrder(), time_in_force=TimeInForce.DAY)
def __init__(self, data_provider: GeneralPriceProvider, settings: Settings, pdf_exporter: PDFExporter, excel_exporter: ExcelExporter): self._logger = qf_logger.getChild(self.__class__.__name__) self._backtest_name = "Backtest Results" self._initial_cash = 100000 self._monitor_type = LightBacktestMonitor self._logging_level = logging.WARNING self._contract_ticker_mapper = DummyBloombergContractTickerMapper() self._commission_model = FixedCommissionModel(0.0) self._slippage_model = PriceBasedSlippage(0.0) self._position_sizer_type = SimplePositionSizer self._position_sizer_param = None self._data_provider = data_provider self._settings = settings self._pdf_exporter = pdf_exporter self._excel_exporter = excel_exporter
def setUp(self): self.start_date = str_to_date("2018-02-04") self.msft_contract = Contract(self.MSFT_TICKER_STR, security_type='SEK', exchange='TEST') self.msft_ticker = BloombergTicker(self.MSFT_TICKER_STR) self.contracts_to_tickers_mapper = DummyBloombergContractTickerMapper() timer = SettableTimer(initial_time=self.start_date) self.data_handler = mock(strict=True) self.scheduler = mock() self.commission_model = FixedCommissionModel(commission=0.0) self.monitor = _MonitorMock() self.spied_monitor = spy(self.monitor) self.portfolio = mock() slippage_model = PriceBasedSlippage(0.0) self.exec_hanlder = SimulatedExecutionHandler( self.data_handler, timer, self.scheduler, self.spied_monitor, self.commission_model, self.contracts_to_tickers_mapper, self.portfolio, slippage_model) self._set_current_msft_price(100.0) self.stop_loss_order_1 = Order(self.msft_contract, quantity=-1, execution_style=StopOrder(95.0), time_in_force=TimeInForce.GTC) self.stop_loss_order_2 = Order(self.msft_contract, quantity=-1, execution_style=StopOrder(90.0), time_in_force=TimeInForce.GTC) self.stop_loss_order_3 = Order(self.msft_contract, quantity=-1, execution_style=StopOrder(50.0), time_in_force=TimeInForce.DAY) self.exec_hanlder.accept_orders([ self.stop_loss_order_1, self.stop_loss_order_2, self.stop_loss_order_3 ])
def setUp(self): self.scheduling_time_delay = 1 start_date = str_to_date("2018-02-04") before_close = start_date + MarketCloseEvent.trigger_time( ) - RelativeDelta(minutes=self.scheduling_time_delay) self.timer = SettableTimer(initial_time=before_close) self.msft_ticker = BloombergTicker("MSFT US Equity") self.data_handler = Mock(spec=DataHandler) self.data_handler.frequency = Frequency.DAILY self.data_handler.data_provider = Mock(spec=DataProvider) self.scheduler = Mock(spec=Scheduler) self.commission_model = FixedCommissionModel(commission=0.0) self.monitor = Mock(spec=AbstractMonitor) self.portfolio = Mock(spec=Portfolio) slippage_model = PriceBasedSlippage(0.0, self.data_handler) self.exec_handler = SimulatedExecutionHandler( self.data_handler, self.timer, self.scheduler, self.monitor, self.commission_model, self.portfolio, slippage_model, RelativeDelta(minutes=self.scheduling_time_delay)) self.order_1 = Order(self.msft_ticker, quantity=10, execution_style=MarketOrder(), time_in_force=TimeInForce.OPG) self.order_2 = Order(self.msft_ticker, quantity=-5, execution_style=MarketOrder(), time_in_force=TimeInForce.OPG) self.order_3 = Order(self.msft_ticker, quantity=-7, execution_style=MarketOrder(), time_in_force=TimeInForce.OPG) self.order_4 = Order(self.msft_ticker, quantity=4, execution_style=MarketOnCloseOrder(), time_in_force=TimeInForce.DAY)
def setUp(self): MarketOpenEvent.set_trigger_time({"hour": 13, "minute": 30, "second": 0, "microsecond": 0}) MarketCloseEvent.set_trigger_time({"hour": 20, "minute": 0, "second": 0, "microsecond": 0}) self.start_date = str_to_date("2018-02-04") self.number_of_minutes = 5 before_close = self.start_date + MarketCloseEvent.trigger_time() - RelativeDelta(minutes=self.number_of_minutes) self.msft_ticker = BloombergTicker(self.MSFT_TICKER_STR) self.timer = SettableTimer(initial_time=before_close) self.data_handler = Mock(spec=DataHandler) self.data_handler.data_provider = Mock(spec=DataProvider) scheduler = Mock(spec=Scheduler) ScheduleOrderExecutionEvent.clear() # Set the periodic bar events to intraday trading IntradayBarEvent.frequency = Frequency.MIN_1 commission_model = FixedCommissionModel(commission=0.0) self.monitor = Mock(spec=AbstractMonitor) self.portfolio = Mock(spec=Portfolio) slippage_model = PriceBasedSlippage(0.0, self.data_handler) self.exec_handler = SimulatedExecutionHandler(self.data_handler, self.timer, scheduler, self.monitor, commission_model, self.portfolio, slippage_model, RelativeDelta(minutes=self.number_of_minutes)) self._set_last_available_price(100.0) self.stop_loss_order_1 = Order(self.msft_ticker, quantity=-1, execution_style=StopOrder(95.0), time_in_force=TimeInForce.GTC) self.stop_loss_order_2 = Order(self.msft_ticker, quantity=-1, execution_style=StopOrder(90.0), time_in_force=TimeInForce.GTC) self.stop_loss_order_3 = Order(self.msft_ticker, quantity=-1, execution_style=StopOrder(50.0), time_in_force=TimeInForce.DAY) self.exec_handler.assign_order_ids([self.stop_loss_order_1, self.stop_loss_order_2, self.stop_loss_order_3])
def __init__(self, data_provider: DataProvider, start_date, end_date, initial_cash, frequency: Frequency = Frequency.MIN_1): """ Set up the backtest variables according to what has been passed in. """ super().__init__() self.logger = qf_logger.getChild(self.__class__.__name__) self.logger.info( "\n".join([ "Testing the Backtester:", "Start date: {:s}".format(date_to_str(start_date)), "End date: {:s}".format(date_to_str(end_date)), "Initial cash: {:.2f}".format(initial_cash), "Frequency of the simulated execution handler: {}".format(frequency) ]) ) timer = SettableTimer(start_date) notifiers = Notifiers(timer) if frequency <= Frequency.DAILY: data_handler = DailyDataHandler(data_provider, timer) else: data_handler = IntradayDataHandler(data_provider, timer) contract_ticker_mapper = SimulatedBloombergContractTickerMapper() portfolio = Portfolio(data_handler, initial_cash, timer, contract_ticker_mapper) signals_register = BacktestSignalsRegister() backtest_result = BacktestResult(portfolio=portfolio, backtest_name="Testing the Backtester", start_date=start_date, end_date=end_date, signals_register=signals_register) monitor = Mock(spec=BacktestMonitor) commission_model = FixedCommissionModel(0.0) slippage_model = PriceBasedSlippage(0.0, data_provider, contract_ticker_mapper) execution_handler = SimulatedExecutionHandler( data_handler, timer, notifiers.scheduler, monitor, commission_model, contract_ticker_mapper, portfolio, slippage_model) broker = BacktestBroker(portfolio, execution_handler) order_factory = OrderFactory(broker, data_handler, contract_ticker_mapper) event_manager = self._create_event_manager(timer, notifiers) time_flow_controller = BacktestTimeFlowController( notifiers.scheduler, event_manager, timer, notifiers.empty_queue_event_notifier, end_date ) position_sizer = SimplePositionSizer(broker, data_handler, order_factory, contract_ticker_mapper, signals_register) self.logger.info( "\n".join([ "Configuration of components:", "Position sizer: {:s}".format(position_sizer.__class__.__name__), "Timer: {:s}".format(timer.__class__.__name__), "Data Provider: {:s}".format(data_provider.__class__.__name__), "Backtest Result: {:s}".format(backtest_result.__class__.__name__), "Monitor: {:s}".format(monitor.__class__.__name__), "Execution Handler: {:s}".format(execution_handler.__class__.__name__), "Commission Model: {:s}".format(commission_model.__class__.__name__), "Broker: {:s}".format(broker.__class__.__name__), "Contract-Ticker Mapper: {:s}".format(contract_ticker_mapper.__class__.__name__) ]) ) self.broker = broker self.notifiers = notifiers self.initial_cash = initial_cash self.start_date = start_date self.end_date = end_date self.event_manager = event_manager self.contract_ticker_mapper = contract_ticker_mapper self.data_handler = data_handler self.portfolio = portfolio self.execution_handler = execution_handler self.position_sizer = position_sizer self.orders_filters = [] self.monitor = monitor self.timer = timer self.order_factory = order_factory self.time_flow_controller = time_flow_controller