def _atr_fraction_at_risk(self, ticker, time_period): """ Parameters ---------- ticker Ticker for which the calculation should be made time_period time period in days for which the ATR is calculated Returns ------- float fraction_at_risk value for an AlphaModel and a Ticker, calculated as Normalized Average True Range multiplied by the risk_estimation_factor, being a property of each AlphaModel: fraction_at_risk = ATR / last_close * risk_estimation_factor """ num_of_bars_needed = time_period + 1 fields = [PriceField.High, PriceField.Low, PriceField.Close] try: prices_df = self.data_provider.historical_price( ticker, fields, num_of_bars_needed) fraction_at_risk = average_true_range( prices_df, normalized=True) * self.risk_estimation_factor return fraction_at_risk except ValueError: self.logger.error( f"Could not calculate the fraction_at_risk for the ticker {ticker.name}", exc_info=True) return nan
def test_alpha_model__calculate_fraction_at_risk(self): data_handler = MagicMock() prices_df = PricesDataFrame.from_records(data=[(6.0, 4.0, 5.0) for _ in range(10)], columns=[PriceField.High, PriceField.Low, PriceField.Close]) data_handler.historical_price.return_value = prices_df atr = average_true_range(prices_df, normalized=True) risk_estimation_factor = 3 alpha_model = AlphaModel(risk_estimation_factor=risk_estimation_factor, data_provider=data_handler) fraction_at_risk = alpha_model.calculate_fraction_at_risk(self.ticker) self.assertEqual(risk_estimation_factor * atr, fraction_at_risk)
def compute_atr(self, prices_df: PricesDataFrame): try: prices_df = prices_df[[ PriceField.Close, PriceField.Open, PriceField.High, PriceField.Low ]] prices_df = prices_df.dropna(how='all').fillna( method='ffill').dropna() # Compute the ATR atr_values = average_true_range( prices_df.iloc[-self.num_of_bars_atr:], normalized=True) except Exception: raise NotEnoughDataException( "Not enough data to compute the average true range") return atr_values
def _atr_fraction_at_risk(self, ticker, time_period): """ Parameters ---------- ticker Ticker for which the calculation should be made time_period time period in days for which the ATR is calculated Returns ------- fraction_at_risk value for an AlphaModel and a Ticker, calculated as Normalized Average True Range multiplied by the risk_estimation_factor, being a property of each AlphaModel: fraction_at_risk = ATR / last_close * risk_estimation_factor """ num_of_bars_needed = time_period + 1 fields = [PriceField.High, PriceField.Low, PriceField.Close] prices_df = self.data_handler.historical_price(ticker, fields, num_of_bars_needed) fraction_at_risk = average_true_range( prices_df, normalized=True) * self.risk_estimation_factor return fraction_at_risk
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