def get_portfolio_and_data_handler(self): data_handler = Mock(spec=DataHandler) data_handler.get_last_available_price.side_effect = lambda tickers: self.data_handler_prices[tickers] \ if tickers else None timer = SettableTimer() timer.set_current_time(self.start_time) portfolio = Portfolio(data_handler, self.initial_cash, timer) return portfolio, data_handler, timer
class TestRules(TestCase): def setUp(self): self.timer = SettableTimer() def test_market_open_rule(self): self.timer.set_current_time( str_to_date("2018-01-01 00:00:00.000000", DateFormat.FULL_ISO)) now = self.timer.now() next_trigger_time = MarketOpenEvent.next_trigger_time(now) self.assertEqual( str_to_date("2018-01-01 09:30:00.000000", DateFormat.FULL_ISO), next_trigger_time) self.timer.set_current_time( str_to_date("2018-01-01 09:29:59.999999", DateFormat.FULL_ISO)) now = self.timer.now() next_trigger_time = MarketOpenEvent.next_trigger_time(now) self.assertEqual( str_to_date("2018-01-01 09:30:00.000000", DateFormat.FULL_ISO), next_trigger_time) self.timer.set_current_time( str_to_date("2018-01-01 09:30:00.000000", DateFormat.FULL_ISO)) now = self.timer.now() next_trigger_time = MarketOpenEvent.next_trigger_time(now) self.assertEqual( str_to_date("2018-01-02 09:30:00.000000", DateFormat.FULL_ISO), next_trigger_time)
def get_portfolio_and_data_handler(self): data_handler = Mock(spec=DataHandler) data_handler.get_last_available_price.side_effect = lambda tickers: self.data_handler_prices[tickers] \ if tickers else None contract_mapper = Mock(spec=ContractTickerMapper) contract_mapper.contract_to_ticker.side_effect = lambda contract: DummyTicker(contract.symbol) timer = SettableTimer() timer.set_current_time(self.start_time) portfolio = Portfolio(data_handler, self.initial_cash, timer, contract_mapper) return portfolio, data_handler, timer
def test_designated_contracts(self): future_ticker = PortaraFutureTicker("", "AB{}", 1, 1, designated_contracts="MZ") timer = SettableTimer() future_ticker.initialize_data_provider(timer, self.data_provider) timer.set_current_time(str_to_date("2021-06-15")) specific_ticker = future_ticker.get_current_specific_ticker() self.assertEqual(specific_ticker, PortaraTicker("AB2021Z", SecurityType.FUTURE, 1))
def _get_prices_df(self, ticker: Ticker, start_date: datetime, end_date: datetime) -> PricesDataFrame: """ Returns non-adjusted open and close prices, indexed with the Market Open and Market Close time.""" if isinstance(ticker, FutureTicker): ticker.initialize_data_provider(SettableTimer(end_date), self._data_provider) tickers_chain = ticker.get_expiration_dates() if start_date >= tickers_chain.index[-1] or end_date <= tickers_chain.index[0]: # If the futures chain starts after the _end_date or ends before the _start_date - no data available return PricesDataFrame() # Get all tickers from the chain that were valid between the start_date and expiration date of the # currently valid ticker end_date = tickers_chain[tickers_chain == ticker.get_current_specific_ticker()].index[0] tickers_chain = tickers_chain.loc[start_date:end_date] tickers = tickers_chain.values.tolist() open_prices = self._data_provider.get_price(tickers, PriceField.Open, start_date, end_date) close_prices = self._data_provider.get_price(tickers, PriceField.Close, start_date, end_date) else: open_prices = self._data_provider.get_price([ticker], PriceField.Open, start_date, end_date) close_prices = self._data_provider.get_price([ticker], PriceField.Close, start_date, end_date) open_prices.index = [dt + MarketOpenEvent.trigger_time() for dt in open_prices.index] close_prices.index = [dt + MarketCloseEvent.trigger_time() for dt in close_prices.index] prices = concat([open_prices, close_prices]).sort_index() return prices
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 _get_test_case_set_up(): timer = SettableTimer() tickers = [QuandlTicker("MSFT", "WIKI"), QuandlTicker("AAPL", "WIKI")] price_data_provider_mock = _create_price_provider_mock(tickers) data_handler = DailyDataHandler(price_data_provider_mock, timer) return timer, tickers, data_handler
def test_futures_chain_without_adjustment(self): timer = SettableTimer(self.end_date) self.future_ticker_1.initialize_data_provider(timer, self.data_provider) futures_chain = FuturesChain(self.future_ticker_1, self.data_provider, FuturesAdjustmentMethod.NTH_NEAREST) # AB2021M is the current specific ticker till 2021-06-14 inclusive, afterwards the AB2021U start_date = str_to_date("2021-06-13") end_date = str_to_date("2021-06-17") fields = PriceField.ohlcv() prices = futures_chain.get_price(fields, start_date, end_date, Frequency.DAILY) prices_m_contract = self.data_provider.get_price( PortaraTicker("AB2021M", SecurityType.FUTURE, 1), fields, start_date, str_to_date("2021-06-14"), Frequency.DAILY) prices_u_contract = self.data_provider.get_price( PortaraTicker("AB2021U", SecurityType.FUTURE, 1), fields, str_to_date("2021-06-15"), end_date, Frequency.DAILY) assert_dataframes_equal(prices, concat([prices_m_contract, prices_u_contract]), check_names=False)
def setUp(self): all_fields = PriceField.ohlcv() self._mocked_prices_arr = self._make_mock_data_array(self.tickers, all_fields) self._price_provider_mock = PresetDataProvider(self._mocked_prices_arr, self.data_start_date, self.data_end_date, self.frequency) self.timer = SettableTimer() self.alpha_model_type = DummyAlphaModel
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 setUpClass(cls) -> None: cls.end_date = str_to_date('2015-10-08') cls.start_date = cls.end_date - RelativeDelta(years=2) cls.timer = SettableTimer(cls.end_date) cls.frequency = Frequency.DAILY cls.TICKER_1 = BloombergFutureTicker("Cotton", "CT{} Comdty", 1, 3) cls.TICKER_2 = BloombergFutureTicker("Corn", 'C {} Comdty', 1, 5)
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): 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() self.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, self.timer, self.scheduler, self.spied_monitor, self.commission_model, self.contracts_to_tickers_mapper, self.portfolio, slippage_model) self._set_last_msft_price(100.0) self.order_1 = Order(self.msft_contract, quantity=10, execution_style=MarketOrder(), time_in_force=TimeInForce.OPG) self.order_2 = Order(self.msft_contract, quantity=-5, execution_style=MarketOrder(), time_in_force=TimeInForce.OPG) self.order_3 = Order(self.msft_contract, quantity=-7, execution_style=MarketOrder(), time_in_force=TimeInForce.OPG) self.order_4 = Order(self.msft_contract, quantity=4, execution_style=MarketOnCloseOrder(), time_in_force=TimeInForce.DAY)
def setUpClass(cls): cls.start_date = str_to_date('2008-10-08') cls.end_date = str_to_date('2018-12-20') cls.timer = SettableTimer() cls.timer.set_current_time(cls.end_date) cls.frequency = Frequency.DAILY cls.ticker_1 = BloombergFutureTicker("Euroswiss", "ES{} Index", 1, 3, 100, "HMUZ") cls.ticker_2 = BloombergFutureTicker("Corn", "C {} Comdty", 1, 3, 100, "HKNUZ") MarketCloseEvent.set_trigger_time({"hour": 20, "minute": 00, "second": 0, "microsecond": 0})
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 setUp(self): try: self.price_data_provider = get_data_provider() except Exception as e: raise self.skipTest(e) self.timer = SettableTimer() self.data_handler = DailyDataHandler(self.price_data_provider, self.timer) MarketOpenEvent.set_trigger_time({ "hour": 13, "minute": 30, "second": 0, "microsecond": 0 }) MarketCloseEvent.set_trigger_time({ "hour": 20, "minute": 0, "second": 0, "microsecond": 0 })
def _setup_data_handler(self, volume_value: Optional[float]) -> DataHandler: dates = pd.date_range(str_to_date("2019-12-01"), str_to_date("2020-01-30"), freq='D') prices_data_frame = QFDataFrame(data={PriceField.Volume: [volume_value] * len(dates)}, index=dates) prices_data_array = tickers_dict_to_data_array({ self.ticker: prices_data_frame, }, [self.ticker], [PriceField.Volume]) data_provider = PresetDataProvider(prices_data_array, dates[0], dates[-1], Frequency.DAILY) timer = SettableTimer(dates[-1]) return DailyDataHandler(data_provider, timer)
def setUp(self): self.timer = SettableTimer() self.tickers = [ QuandlTicker("MSFT", "WIKI"), QuandlTicker("AAPL", "WIKI") ] price_data_provider_mock = self._create_price_provider_mock( self.tickers) self.data_handler = DailyDataHandler(price_data_provider_mock, self.timer) MarketOpenEvent.set_trigger_time({ "hour": 13, "minute": 30, "second": 0, "microsecond": 0 }) MarketCloseEvent.set_trigger_time({ "hour": 20, "minute": 0, "second": 0, "microsecond": 0 })
def test_get_current_specific_ticker(self): timer = SettableTimer() self.future_ticker_1.initialize_data_provider(timer, self.data_provider) timer.set_current_time(str_to_date("2021-03-18")) specific_ticker = self.future_ticker_1.get_current_specific_ticker() self.assertEqual(specific_ticker, PortaraTicker("AB2021M", SecurityType.FUTURE, 1)) timer.set_current_time(str_to_date("2021-06-14")) specific_ticker = self.future_ticker_1.get_current_specific_ticker() self.assertEqual(specific_ticker, PortaraTicker("AB2021M", SecurityType.FUTURE, 1)) timer.set_current_time(str_to_date("2021-06-15")) specific_ticker = self.future_ticker_1.get_current_specific_ticker() self.assertEqual(specific_ticker, PortaraTicker("AB2021U", SecurityType.FUTURE, 1)) timer.set_current_time(datetime(2021, 12, 14, 23, 59)) specific_ticker = self.future_ticker_1.get_current_specific_ticker() self.assertEqual(specific_ticker, PortaraTicker("AB2021Z", SecurityType.FUTURE, 1))
class TestDataHandlerTimeHelper(TestCase): @classmethod def setUpClass(cls): cls.YESTERDAY_OPEN = str_to_date("2018-01-29 09:30:00.000000", DateFormat.FULL_ISO) cls.YESTERDAY_CLOSE = str_to_date("2018-01-29 16:00:00.000000", DateFormat.FULL_ISO) cls.TODAY_BEFORE_OPEN = str_to_date("2018-01-30 06:00:00.000000", DateFormat.FULL_ISO) cls.TODAY_OPEN = str_to_date("2018-01-30 09:30:00.000000", DateFormat.FULL_ISO) cls.TODAY_MIDDLE_DAY = str_to_date("2018-01-30 12:00:00.000000", DateFormat.FULL_ISO) cls.TODAY_CLOSE = str_to_date("2018-01-30 16:00:00.000000", DateFormat.FULL_ISO) cls.TODAY_AFTER_CLOSE = str_to_date("2018-01-30 20:00:00.000000", DateFormat.FULL_ISO) def setUp(self): self.timer = SettableTimer() self.time_helper = _DataHandlerTimeHelper(self.timer) def test_datetime_of_latest_market_event_before_market_open(self): self.timer.set_current_time(self.TODAY_BEFORE_OPEN) market_open_datetime = self.time_helper.datetime_of_latest_market_event( MarketOpenEvent) market_close_datetime = self.time_helper.datetime_of_latest_market_event( MarketCloseEvent) self.assertEqual(market_open_datetime, self.YESTERDAY_OPEN) self.assertEqual(market_close_datetime, self.YESTERDAY_CLOSE) def test_datetime_of_latest_market_event_during_trading_session(self): self.timer.set_current_time(self.TODAY_MIDDLE_DAY) market_open_datetime = self.time_helper.datetime_of_latest_market_event( MarketOpenEvent) market_close_datetime = self.time_helper.datetime_of_latest_market_event( MarketCloseEvent) self.assertEqual(market_open_datetime, self.TODAY_OPEN) self.assertEqual(market_close_datetime, self.YESTERDAY_CLOSE) def test_datetime_of_latest_market_event_after_market_close(self): self.timer.set_current_time(self.TODAY_AFTER_CLOSE) market_open_datetime = self.time_helper.datetime_of_latest_market_event( MarketOpenEvent) market_close_datetime = self.time_helper.datetime_of_latest_market_event( MarketCloseEvent) self.assertEqual(market_open_datetime, self.TODAY_OPEN) self.assertEqual(market_close_datetime, self.TODAY_CLOSE)
def setUp(self) -> None: self.ticker = BloombergTicker("Example Ticker") self.future_ticker = MagicMock(spec=BloombergFutureTicker) self.future_ticker.configure_mock(name="Example", family_id="Example{} Comdty", days_before_exp_date=1, point_value=1) self.alpha_model = MagicMock() # Mock trading session self.ts = MagicMock() self.ts.contract_ticker_mapper = SimulatedBloombergContractTickerMapper() self.ts.timer = SettableTimer(str_to_date("2000-01-04 08:00:00.0", DateFormat.FULL_ISO)) self.positions_in_portfolio = [] # type: List[Contract] """Contracts for which a position in the portfolio currently exists. This list is used to return backtest positions list by the broker.""" self.ts.broker.get_positions.side_effect = lambda: self.positions_in_portfolio
def map_future_ticker_to_contract(): mapping = { BloombergFutureTicker('Copper', 'HG{} Comdty', 1, 1, 250): IBContract("HG", SecurityType.FUTURE, "NYMEX", "25000", "USD"), BloombergTicker("PAH20 Comdty", SecurityType.FUTURE, 100): IBContract("PA", SecurityType.FUTURE, "NYMEX", "100", "", str_to_date("2020-03-27")) } data_provider = container.resolve(BloombergDataProvider) contract_ticker_mapper = IBContractTickerMapper(mapping, data_provider) current_time = str_to_date("2020-12-01") for future_ticker in mapping.keys(): if isinstance(future_ticker, BloombergFutureTicker): future_ticker.initialize_data_provider(SettableTimer(current_time), data_provider) print("\nMapping PAH20 Comdty ticker to IB contract") ticker = BloombergTicker("PAH20 Comdty", SecurityType.FUTURE, 100) contract = contract_ticker_mapper.ticker_to_contract(ticker) print(f"Ticker mapped onto the following contract: {contract}") print("\nMapping IBContract onto ticker") contract = IBContract("PA", SecurityType.FUTURE, "NYMEX", "100", "", str_to_date("2020-03-27")) ticker = contract_ticker_mapper.contract_to_ticker(contract) print(f"Contract mapped onto the following ticker: {ticker}") print( "\nMapping HGH20 Comdty ticker - Copper, March 2020 futures contracts") ticker = BloombergTicker("HGH20 Comdty", SecurityType.FUTURE, 250) contract = contract_ticker_mapper.ticker_to_contract(ticker) print(f"Ticker mapped onto the following contract: {contract}") print("\nMapping IBContract onto ticker") contract = IBContract("HG", SecurityType.FUTURE, "NYMEX", "25000", "USD", str_to_date("2021-01-27")) ticker = contract_ticker_mapper.contract_to_ticker(contract) print(f"Contract mapped onto the following ticker: {ticker}")
def _generate_buy_and_hold_returns(self, ticker: Ticker) -> SimpleReturnsSeries: """ Computes series of simple returns, which would be returned by the Buy and Hold strategy. """ if isinstance(ticker, FutureTicker): try: ticker.initialize_data_provider(SettableTimer(self._end_date), self._data_provider) futures_chain = FuturesChain( ticker, self._data_provider, FuturesAdjustmentMethod.BACK_ADJUSTED) prices_series = futures_chain.get_price( PriceField.Close, self._start_date, self._end_date) except NoValidTickerException: prices_series = PricesSeries() else: prices_series = self._data_provider.get_price( ticker, PriceField.Close, self._start_date, self._end_date) returns_tms = prices_series.to_simple_returns().replace( [-np.inf, np.inf], np.nan).fillna(0.0) returns_tms.name = "Buy and Hold" return returns_tms
class TestDailyDataHandler(TestCase): def setUp(self): self.timer = SettableTimer() self.tickers = [ QuandlTicker("MSFT", "WIKI"), QuandlTicker("AAPL", "WIKI") ] price_data_provider_mock = self._create_price_provider_mock( self.tickers) self.data_handler = DailyDataHandler(price_data_provider_mock, self.timer) MarketOpenEvent.set_trigger_time({ "hour": 13, "minute": 30, "second": 0, "microsecond": 0 }) MarketCloseEvent.set_trigger_time({ "hour": 20, "minute": 0, "second": 0, "microsecond": 0 }) def test_before_data_starts(self): self._assert_last_prices_are_correct("2009-12-28 06:00:00.000000", [nan, nan]) def test_at_market_open_when_data_available_for_every_ticker(self): self._assert_last_prices_are_correct("2009-12-28 13:30:00.000000", [25.0, 27.0]) def test_at_market_open_when_data_available_for_the_first_ticker(self): self._assert_last_prices_are_correct("2009-12-30 13:30:00.000000", [31.0, 30.0]) def test_during_trading_hours_when_data_available_for_every_ticker(self): self._assert_last_prices_are_correct("2009-12-28 14:30:00.000000", [25.0, 27.0]) def test_during_trading_hours_when_data_available_for_the_first_ticker( self): self._assert_last_prices_are_correct("2009-12-30 14:30:00.000000", [31.0, 30.0]) def test_at_market_close_when_data_available_for_the_first_ticker(self): self._assert_last_prices_are_correct("2009-12-30 20:00:00.000000", [32.0, 30.0]) def test_after_market_close_when_data_is_not_available_for_anything_anymore( self): self._assert_last_prices_are_correct("2009-12-30 20:00:00.000000", [32.0, 30.0]) def test_before_market_close_when_old_data_is_available(self): self._assert_last_prices_are_correct("2009-12-31 06:00:00.000000", [32.0, 30.0]) def _assert_last_prices_are_correct(self, curr_time_str, expected_values): current_time = str_to_date(curr_time_str, DateFormat.FULL_ISO) self.timer.set_current_time(current_time) expected_series = QFSeries(data=expected_values, index=self.tickers) actual_series = self.data_handler.get_last_available_price( self.tickers) assert_series_equal(expected_series, actual_series, check_names=False) def _create_price_provider_mock(self, tickers): mock_data_array = QFDataArray.create( dates=date_range(start='2009-12-28', end='2009-12-30', freq='D'), tickers=tickers, fields=PriceField.ohlcv(), data=[ # 2009-12-28 [ # Open High Low Close Volume [25.0, 25.1, 25.2, 26.0, 25.3], # MSFT [27.0, 27.1, 27.2, 28.0, 27.3] # AAPL ], # 2009-12-29 [ # Open High Low Close Volume [None, None, None, None, None], # MSFT [29.0, 29.1, 29.2, 30.0, 29.3] # AAPL ], # 2009-12-30 [ # Open High Low Close Volume [31.0, 31.1, 31.2, 32.0, 31.3], # MSFT [None, None, None, None, None] # AAPL ] ]) price_data_provider_mock = Mock(spec=DataProvider, frequency=Frequency.DAILY) price_data_provider_mock.get_price.side_effect = lambda t, fields, start_time, end_time, frequency: \ mock_data_array.loc[start_time:end_time, t, fields].to_pandas() return price_data_provider_mock
class TestDataHandler(TestCase): @classmethod def setUpClass(cls): cls.spx_index_ticker = BloombergTicker("SPX Index") cls.google_ticker = BloombergTicker("GOOGL US Equity") cls.microsoft_ticker = BloombergTicker("MSFT US Equity") cls.start_date = str_to_date("2018-01-02") cls.end_date = str_to_date("2018-01-31") cls.end_date_trimmed = str_to_date("2018-01-30") cls.get_history_field = "PX_TO_BOOK_RATIO" def setUp(self): try: self.price_data_provider = get_data_provider() except Exception as e: raise self.skipTest(e) self.timer = SettableTimer() self.data_handler = DailyDataHandler(self.price_data_provider, self.timer) MarketOpenEvent.set_trigger_time({ "hour": 13, "minute": 30, "second": 0, "microsecond": 0 }) MarketCloseEvent.set_trigger_time({ "hour": 20, "minute": 0, "second": 0, "microsecond": 0 }) def test_get_price_when_end_date_is_in_the_past(self): self.timer.set_current_time( str_to_date("2018-02-12 00:00:00.000000", DateFormat.FULL_ISO)) prices_tms = self.data_handler.get_price(self.spx_index_ticker, PriceField.Close, self.start_date, self.end_date) self.assertEqual(self.start_date, prices_tms.index[0].to_pydatetime()) self.assertEqual(self.end_date, prices_tms.index[-1].to_pydatetime()) def test_get_price_when_end_date_is_today_after_market_close(self): self.timer.set_current_time( str_to_date("2018-01-31") + MarketCloseEvent.trigger_time() + RelativeDelta(hours=1)) prices_tms = self.data_handler.get_price(self.spx_index_ticker, PriceField.Close, self.start_date, self.end_date) self.assertEqual(self.start_date, prices_tms.index[0].to_pydatetime()) self.assertEqual(self.end_date, prices_tms.index[-1].to_pydatetime()) def test_get_price_when_end_date_is_today_before_market_close(self): self.timer.set_current_time( str_to_date("2018-01-31") + MarketOpenEvent.trigger_time() + RelativeDelta(hours=1)) close_prices_tms = self.data_handler.get_price(self.spx_index_ticker, PriceField.Close, self.start_date, self.end_date) self.assertEqual(self.start_date, close_prices_tms.index[0].to_pydatetime()) self.assertEqual(self.end_date_trimmed, close_prices_tms.index[-1].to_pydatetime()) def test_get_open_price_when_end_date_is_today_before_market_close__single_ticker( self): self.timer.set_current_time( str_to_date("2018-01-31") + MarketOpenEvent.trigger_time() + RelativeDelta(hours=1)) current_date = datetime(self.timer.now().year, self.timer.now().month, self.timer.now().day) open_prices_tms = self.data_handler.get_price(self.spx_index_ticker, PriceField.Open, self.start_date, self.timer.now()) self.assertEqual(self.start_date, open_prices_tms.index[0].to_pydatetime()) self.assertEqual(current_date, open_prices_tms.index[-1].to_pydatetime()) prices_df = self.data_handler.get_price( self.spx_index_ticker, [PriceField.Open, PriceField.Close], self.start_date, self.timer.now()) self.assertEqual(self.start_date, prices_df.index[0].to_pydatetime()) self.assertEqual(current_date, prices_df.index[-1].to_pydatetime()) last_bar = prices_df.iloc[-1] self.assertTrue(isnan(last_bar.loc[PriceField.Close])) self.assertIsNotNone(last_bar.loc[PriceField.Open]) def test_get_open_price_when_end_date_is_today_before_market_close__multiple_tickers( self): self.timer.set_current_time( str_to_date("2018-01-31") + MarketOpenEvent.trigger_time() + RelativeDelta(hours=1)) current_date = datetime(self.timer.now().year, self.timer.now().month, self.timer.now().day) tickers = [self.spx_index_ticker, self.microsoft_ticker] open_prices_tms = self.data_handler.get_price(tickers, PriceField.Open, self.start_date, self.timer.now()) self.assertEqual(self.start_date, open_prices_tms.index[0].to_pydatetime()) self.assertEqual(current_date, open_prices_tms.index[-1].to_pydatetime()) prices_data_array = self.data_handler.get_price( tickers, [PriceField.Open, PriceField.Close], self.start_date, self.timer.now()) last_bar_df = prices_data_array.loc[current_date, :, :].to_pandas() self.assertTrue(last_bar_df[PriceField.Close].isna().all()) self.assertTrue(last_bar_df[PriceField.Open].notna().all()) def test_get_price_when_end_date_is_tomorrow(self): self.timer.set_current_time( str_to_date("2018-01-30") + MarketCloseEvent.trigger_time() + RelativeDelta(hours=1)) prices_tms = self.data_handler.get_price(self.spx_index_ticker, PriceField.Close, self.start_date, self.end_date_trimmed) self.assertEqual(self.start_date, prices_tms.index[0].to_pydatetime()) self.assertEqual(self.end_date_trimmed, prices_tms.index[-1].to_pydatetime()) def test_get_last_price_single_ticker(self): # just test if getting single ticker value works, when a single ticker (not wrapped in a list) is passed self.timer.set_current_time( str_to_date("2018-01-31") + MarketOpenEvent.trigger_time() + RelativeDelta(hours=1)) single_price = self.data_handler.get_last_available_price( self.spx_index_ticker) self.assertTrue(isinstance(single_price, float)) # at market open self.timer.set_current_time( str_to_date("2018-01-31") + MarketOpenEvent.trigger_time()) at_market_open = self.data_handler.get_last_available_price( [self.spx_index_ticker]) self.assertEqual(self.spx_index_ticker, at_market_open.index[0]) self.assertEqual(single_price, at_market_open[0]) # during the trading session self.timer.set_current_time( str_to_date("2018-01-31") + MarketOpenEvent.trigger_time() + RelativeDelta(hours=1)) during_the_day_last_prices = self.data_handler.get_last_available_price( [self.spx_index_ticker]) self.assertEqual(self.spx_index_ticker, during_the_day_last_prices.index[0]) self.assertEqual(single_price, during_the_day_last_prices[0]) # after the trading session self.timer.set_current_time( str_to_date("2018-01-31") + MarketCloseEvent.trigger_time() + RelativeDelta(hours=1)) after_close_last_prices = self.data_handler.get_last_available_price( [self.spx_index_ticker]) self.assertEqual(self.spx_index_ticker, after_close_last_prices.index[0]) self.assertNotEqual(during_the_day_last_prices[0], after_close_last_prices[0]) # before the trading session self.timer.set_current_time( str_to_date("2018-01-31") + MarketOpenEvent.trigger_time() - RelativeDelta(hours=1)) before_trading_session_prices = self.data_handler.get_last_available_price( [self.spx_index_ticker]) self.assertEqual(self.spx_index_ticker, before_trading_session_prices.index[0]) self.assertNotEqual(during_the_day_last_prices[0], before_trading_session_prices[0]) self.assertNotEqual(after_close_last_prices[0], before_trading_session_prices[0]) def test_get_last_price_with_multiple_tickers_when_current_data_is_unavailable( self): self.timer.set_current_time( str_to_date("2018-01-01") + MarketOpenEvent.trigger_time() + RelativeDelta(hours=1)) last_prices = self.data_handler.get_last_available_price( [self.spx_index_ticker, self.google_ticker]) self.assertEqual(self.spx_index_ticker, last_prices.index[0]) self.assertEqual(self.google_ticker, last_prices.index[1]) def test_get_last_price_with_empty_tickers_list(self): self.timer.set_current_time( str_to_date("2018-01-31") + MarketOpenEvent.trigger_time() + RelativeDelta(hours=1)) last_prices = self.data_handler.get_last_available_price([]) assert_series_equal(QFSeries(), last_prices) def test_get_history_when_end_date_is_in_the_past(self): self.timer.set_current_time( str_to_date("2018-02-12 00:00:00.000000", DateFormat.FULL_ISO)) prices_tms = self.data_handler.get_history(self.spx_index_ticker, self.get_history_field, self.start_date, self.end_date) self.assertEqual(self.start_date, prices_tms.index[0].to_pydatetime()) self.assertEqual(self.end_date, prices_tms.index[-1].to_pydatetime()) def test_get_history_when_end_date_is_today_after_market_close(self): self.timer.set_current_time( str_to_date("2018-01-31") + MarketCloseEvent.trigger_time() + RelativeDelta(hours=1)) prices_tms = self.data_handler.get_history(self.spx_index_ticker, self.get_history_field, self.start_date, self.end_date) self.assertEqual(self.start_date, prices_tms.index[0].to_pydatetime()) self.assertEqual(self.end_date, prices_tms.index[-1].to_pydatetime()) def test_get_history_when_end_date_is_today_before_market_close(self): self.timer.set_current_time( str_to_date("2018-01-31") + MarketOpenEvent.trigger_time() + RelativeDelta(hours=1)) prices_tms = self.data_handler.get_history(self.spx_index_ticker, self.get_history_field, self.start_date, self.end_date) self.assertEqual(self.start_date, prices_tms.index[0].to_pydatetime()) self.assertEqual(self.end_date_trimmed, prices_tms.index[-1].to_pydatetime()) def test_get_history_when_end_date_is_tomorrow(self): self.timer.set_current_time( str_to_date("2018-01-30") + MarketCloseEvent.trigger_time() + RelativeDelta(hours=1)) prices_tms = self.data_handler.get_history(self.spx_index_ticker, self.get_history_field, self.start_date, self.end_date_trimmed) self.assertEqual(self.start_date, prices_tms.index[0].to_pydatetime()) self.assertEqual(self.end_date_trimmed, prices_tms.index[-1].to_pydatetime()) def test_get_history_with_multiple_tickers(self): self.timer.set_current_time( str_to_date("2018-01-31") + MarketOpenEvent.trigger_time() + RelativeDelta(hours=1)) resilt_df = self.data_handler.get_history( [self.microsoft_ticker, self.google_ticker], self.get_history_field, self.start_date, self.end_date_trimmed) self.assertEqual(self.microsoft_ticker, resilt_df.columns[0]) self.assertEqual(self.google_ticker, resilt_df.columns[1]) self.assertEqual(self.start_date, resilt_df.index[0].to_pydatetime()) self.assertEqual(self.end_date_trimmed, resilt_df.index[-1].to_pydatetime()) self.assertEqual(resilt_df.shape, (20, 2)) def test_historical_price_many_tickers_many_fields(self): self.timer.set_current_time( str_to_date("2018-01-31") + MarketOpenEvent.trigger_time() + RelativeDelta(hours=1)) result_array = self.data_handler.historical_price( [self.microsoft_ticker], [PriceField.Open, PriceField.Close], nr_of_bars=5) self.assertEqual(QFDataArray, type(result_array)) self.assertEqual((5, 1, 2), result_array.shape) expected_dates_str = [ "2018-01-24", "2018-01-25", "2018-01-26", "2018-01-29", "2018-01-30" ] expected_dates = [ str_to_date(date_str) for date_str in expected_dates_str ] assert_same_index(pd.DatetimeIndex(expected_dates, name=DATES), result_array.dates.to_index(), check_index_type=True, check_names=True) def test_historical_price_many_tickers_one_field(self): self.timer.set_current_time( str_to_date("2018-01-04") + MarketOpenEvent.trigger_time() + RelativeDelta(hours=1)) result_df = self.data_handler.historical_price([self.microsoft_ticker], PriceField.Open, nr_of_bars=5) self.assertEqual(PricesDataFrame, type(result_df)) expected_dates_idx = pd.DatetimeIndex([ '2017-12-27', '2017-12-28', '2017-12-29', '2018-01-02', '2018-01-03' ], name=DATES) assert_same_index(expected_dates_idx, result_df.index, check_index_type=True, check_names=True) expected_tickers_idx = pd.Index([self.microsoft_ticker], name=TICKERS) assert_same_index(expected_tickers_idx, result_df.columns, check_index_type=True, check_names=True)
def setUp(self) -> None: self.contract_ticker_mapper = SimulatedBloombergContractTickerMapper() self.current_date = str_to_date("2017-01-01") self.timer = SettableTimer(self.current_date)
def _shift_timer_to_next_day(timer: SettableTimer): new_time = timer.time + RelativeDelta(days=1) timer.set_current_time(new_time)
def build(self, start_date: datetime, end_date: datetime) -> BacktestTradingSession: self._timer = SettableTimer(start_date) self._notifiers = Notifiers(self._timer) self._events_manager = self._create_event_manager(self._timer, self._notifiers) self._data_handler = DataHandler(self._data_provider, self._timer) self._portfolio = Portfolio(self._data_handler, self._initial_cash, self._timer, self._contract_ticker_mapper) self._backtest_result = BacktestResult(self._portfolio, self._backtest_name, start_date, end_date) self._monitor = self._monitor_setup() self._portfolio_handler = PortfolioHandler(self._portfolio, self._monitor, self._notifiers.scheduler) self._execution_handler = SimulatedExecutionHandler( self._data_handler, self._timer, self._notifiers.scheduler, self._monitor, self._commission_model, self._contract_ticker_mapper, self._portfolio, self._slippage_model) self._time_flow_controller = BacktestTimeFlowController( self._notifiers.scheduler, self._events_manager, self._timer, self._notifiers.empty_queue_event_notifier, end_date) self._broker = BacktestBroker(self._portfolio, self._execution_handler) self._order_factory = OrderFactory(self._broker, self._data_handler, self._contract_ticker_mapper) self._position_sizer = self._position_sizer_setup() setup_logging(self._logging_level) self._logger.info( "\n".join([ "Creating Backtest Trading Session.", "\tBacktest Name: {}".format(self._backtest_name), "\tData Provider: {}".format(self._data_provider.__class__.__name__), "\tContract - Ticker Mapper: {}".format(self._contract_ticker_mapper.__class__.__name__), "\tStart Date: {}".format(start_date), "\tEnd Date: {}".format(end_date), "\tInitial Cash: {:.2f}".format(self._initial_cash) ]) ) self._logger.info( "\n".join([ "Configuration of components:", "\tPosition sizer: {:s}".format(self._position_sizer.__class__.__name__), "\tTimer: {:s}".format(self._timer.__class__.__name__), "\tData Handler: {:s}".format(self._data_handler.__class__.__name__), "\tBacktest Result: {:s}".format(self._backtest_result.__class__.__name__), "\tMonitor: {:s}".format(self._monitor.__class__.__name__), "\tExecution Handler: {:s}".format(self._execution_handler.__class__.__name__), "\tSlippage Model: {:s}".format(self._slippage_model.__class__.__name__), "\tCommission Model: {:s}".format(self._commission_model.__class__.__name__), "\tBroker: {:s}".format(self._broker.__class__.__name__), ]) ) ts = BacktestTradingSession( contract_ticker_mapper=self._contract_ticker_mapper, start_date=start_date, end_date=end_date, position_sizer=self._position_sizer, data_handler=self._data_handler, timer=self._timer, notifiers=self._notifiers, portfolio=self._portfolio, events_manager=self._events_manager, monitor=self._monitor, broker=self._broker, order_factory=self._order_factory ) return ts
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 not isinstance(last_event, EndTradingEvent): event_manager.dispatch_next_event() last_event, _ = listener.registered_events[-1] expected_events = [ (EmptyQueueEvent, str_to_date("2018-04-10 08: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 13:30:00.000000", DateFormat.FULL_ISO)), (MarketOpenEvent, str_to_date("2018-04-10 13:30:00.000000", DateFormat.FULL_ISO)), (EmptyQueueEvent, str_to_date("2018-04-10 13:45:00.000000", DateFormat.FULL_ISO)), (PeriodicEvent1Hour, str_to_date("2018-04-10 13:45:00.000000", DateFormat.FULL_ISO)), (EmptyQueueEvent, str_to_date("2018-04-10 13:50:00.000000", DateFormat.FULL_ISO)), (SingleTimeEvent, str_to_date("2018-04-10 13:50:00.000000", DateFormat.FULL_ISO)), (EmptyQueueEvent, str_to_date("2018-04-10 14:45:00.000000", DateFormat.FULL_ISO)), (PeriodicEvent1Hour, str_to_date("2018-04-10 14:45:00.000000", DateFormat.FULL_ISO)), (EmptyQueueEvent, str_to_date("2018-04-10 14:50:00.000000", DateFormat.FULL_ISO)), (SingleTimeEvent, str_to_date("2018-04-10 14:50:00.000000", DateFormat.FULL_ISO)), (EmptyQueueEvent, str_to_date("2018-04-10 20:00:00.000000", DateFormat.FULL_ISO)), (MarketCloseEvent, str_to_date("2018-04-10 20:00:00.000000", DateFormat.FULL_ISO)), (EmptyQueueEvent, str_to_date("2018-04-10 21:00:00.000000", DateFormat.FULL_ISO)), (AfterMarketCloseEvent, str_to_date("2018-04-10 21:00:00.000000", DateFormat.FULL_ISO)), (EmptyQueueEvent, str_to_date("2018-04-10 21:00:00.000000", DateFormat.FULL_ISO)), (EndTradingEvent, str_to_date("2018-04-10 21:00:00.000000", DateFormat.FULL_ISO)), ] expected_events = [ type_of_event for type_of_event, time in expected_events ] actual_events = [ type(event) for event, time in listener.registered_events ] assert_lists_equal(expected_events, actual_events, absolute_tolerance=0.0) expected_single_time_events_data = { str_to_date("2018-04-10 13:50:00.000000", DateFormat.FULL_ISO): str_to_date("2018-04-10 13:45:00.000000", DateFormat.FULL_ISO), str_to_date("2018-04-10 14:50:00.000000", DateFormat.FULL_ISO): str_to_date("2018-04-10 14:45:00.000000", DateFormat.FULL_ISO) } for key in expected_single_time_events_data: self.assertEqual(expected_single_time_events_data[key], listener.registered_single_time_events[key])
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