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 get_prices(self, ticker: Ticker): """ Create a data frame with OHLC prices for the given ticker. """ self.timer.set_current_time(self.end_date) if isinstance(ticker, FutureTicker): futures_chain = FuturesChain( ticker, self.data_handler, method=FuturesAdjustmentMethod.NTH_NEAREST) prices_df = futures_chain.get_price( [ PriceField.Open, PriceField.High, PriceField.Low, PriceField.Close ], start_date=self.start_date, end_date=self.end_date, frequency=self.data_handler.frequency) else: prices_df = self.data_handler.get_price( ticker, [ PriceField.Open, PriceField.High, PriceField.Low, PriceField.Close ], start_date=self.start_date, end_date=self.end_date, frequency=self.data_handler.frequency) return prices_df
def _get_data_for_backtest(self) -> QFDataArray: """ Creates a QFDataArray containing OHLCV values for all tickers passes to Fast Alpha Models Tester. """ print("\nLoading all price values of tickers:") self._timer.set_current_time(self._end_date) tickers_dict = {} for ticker in tqdm(self._tickers, file=sys.stdout): if isinstance(ticker, FutureTicker): fc = FuturesChain(ticker, self._data_handler) tickers_dict[ticker] = fc.get_price(PriceField.ohlcv(), self._start_date, self._end_date, Frequency.DAILY) else: tickers_dict[ticker] = self._data_handler.get_price(ticker, PriceField.ohlcv(), self._start_date, self._end_date) prices_data_array = tickers_dict_to_data_array(tickers_dict, self._tickers, PriceField.ohlcv()) return prices_data_array
def _compute_target_value(self, signal: Signal, frequency=Frequency.DAILY) -> float: """ Caps the target value, so that according to historical volume data, the position will not exceed max_volume_percentage * mean volume within last 100 days. """ ticker: Ticker = signal.ticker portfolio_value = self._broker.get_portfolio_value() target_percentage = self._compute_target_percentage(signal) target_value = portfolio_value * target_percentage end_date = self._data_handler.timer.now() start_date = end_date - RelativeDelta(days=100) if isinstance(ticker, FutureTicker): # Check if a futures chain instance already exists for this ticker and create it if not # The default adjustment method will be taken (FuturesAdjustmentMethod.NTH_NEAREST) as the volume should # not be adjusted if ticker not in self._cached_futures_chains_dict.keys(): self._cached_futures_chains_dict[ticker] = FuturesChain( ticker, self._data_handler) volume_series: PricesSeries = self._cached_futures_chains_dict[ ticker].get_price(PriceField.Volume, start_date, end_date, frequency) else: volume_series: PricesSeries = self._data_handler.get_price( ticker, PriceField.Volume, start_date, end_date, frequency) mean_volume = volume_series.mean() specific_ticker = ticker.get_current_specific_ticker() if isinstance( ticker, FutureTicker) else ticker current_price = self._data_handler.get_last_available_price( specific_ticker, frequency) contract_size = ticker.point_value if isinstance(ticker, FutureTicker) else 1 divisor = current_price * contract_size quantity = target_value // divisor if abs(quantity) > mean_volume * self._max_volume_percentage: target_quantity = np.floor(mean_volume * self._max_volume_percentage) target_value = target_quantity * divisor * np.sign(quantity) self.logger.info( "InitialRiskWithVolumePositionSizer: {} - capping {}.\n" "Initial quantity: {}\n" "Reduced quantity: {}".format(self._data_handler.timer.now(), ticker.ticker, quantity, target_quantity)) assert is_finite_number( target_value), "target_value has to be a finite number" return target_value
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
def get_data(self, ticker_str: str, end_date: datetime, aggregate_volume: bool = False): """ Downloads the OHCLV Prices data frame for the given ticker. In case of a FutureTicker, the function downloads the Futures Chain and applies backward adjustment to the prices. Parameters ---------- ticker_str: str string representing the ticker for which the data should be downloaded end_date: datetime last date for the data to be fetched aggregate_volume: bool used only in case of future tickers - if set to True, the volume data would not be the volume for the given contract, but the volume aggregated across all contracts (for each day the volume will be simply the sum of all volumes of the existing contracts of the given future asset) """ end_date = end_date + RelativeDelta( hour=0, minute=0, second=0, microsecond=0) start_date = end_date - RelativeDelta(days=self.num_of_bars_needed + 50) ticker = self.ticker_name_to_ticker[ticker_str] if isinstance(ticker, FutureTicker): try: data_frame = self.futures_data[ticker].get_price( PriceField.ohlcv(), start_date, end_date, Frequency.DAILY) except KeyError: # Ticker was not preloaded or the FutureChain has expired self.futures_data[ticker] = FuturesChain( ticker, self.data_provider, FuturesAdjustmentMethod.BACK_ADJUSTED) data_frame = self.futures_data[ticker].get_price( PriceField.ohlcv(), start_date, end_date, Frequency.DAILY) if aggregate_volume: data_frame[ PriceField.Volume] = self._compute_aggregated_volume( ticker, start_date, end_date) else: data_frame = self.data_provider.get_price(ticker, PriceField.ohlcv(), start_date, end_date, Frequency.DAILY) data_frame = data_frame.dropna(how="all") return data_frame
def calculate_exposure(self, ticker: FutureTicker, current_exposure: Exposure) -> Exposure: num_of_bars_needed = self.slow_time_period current_time = self.timer.now() # Compute the start time start_time = current_time - RelativeDelta(days=num_of_bars_needed + 100) if self.futures_chain is None: # Create Futures Chain object self.futures_chain = FuturesChain(ticker, self.data_handler) # Get the data frame containing High, Low, Close prices data_frame = self.futures_chain.get_price( [PriceField.High, PriceField.Low, PriceField.Close], start_time, current_time) data_frame = data_frame.dropna(how='all').fillna(method='pad') close_tms = data_frame[PriceField.Close] close_tms = close_tms.iloc[-self.slow_time_period:] try: # Compute the ATR atr_df = data_frame[-num_of_bars_needed:] self.average_true_range = average_true_range(atr_df, normalized=True) ############################ # Opening position # ############################ current_price = close_tms.iloc[-1] # Compute the fast and slow simple moving averages fast_ma = sum(close_tms.iloc[-self.fast_time_period:] ) / self.fast_time_period slow_ma = sum(close_tms) / self.slow_time_period if fast_ma > slow_ma: # Long entries are only allowed if the fast moving average is above the slow moving average. # If today’s closing price is the highest close in the past 50 days, we buy. highest_price = max(close_tms[-self.fast_time_period:]) if current_price >= highest_price: if current_exposure == Exposure.OUT: self.time_of_opening_position = close_tms.index[-1] return Exposure.LONG else: # Short entries are only allowed if the fast moving average is below the slow moving average. # If today’s closing price is the lowest close in the past 50 days, we sell. lowest_price = min(close_tms[-self.fast_time_period:]) if current_price <= lowest_price: if current_exposure == Exposure.OUT: self.time_of_opening_position = close_tms.index[-1] return Exposure.SHORT ############################ # Closing position # ############################ if current_exposure == Exposure.LONG: # A long position is closed when it has moved three ATR units down from its highest closing price # since the position was opened. close_prices = close_tms.loc[ self.time_of_opening_position:].dropna() if current_price < max(close_prices) * ( 1 - self.risk_estimation_factor * self.average_true_range): return Exposure.OUT elif current_exposure == Exposure.SHORT: # A short position is closed when it has moved three ATR units up from its lowest closing price # since the position was opened. close_prices = close_tms.loc[ self.time_of_opening_position:].dropna() if current_price > min(close_prices) * ( 1 + self.risk_estimation_factor * self.average_true_range): return Exposure.OUT except (KeyError, ValueError): # No price at this day pass return current_exposure