def test_robust_vol_calc_min_period(self): prices = get_data("syscore.tests.pricetestdata_min_period.csv") returns = prices.diff() vol = robust_vol_calc(returns, min_periods=9) self.assertAlmostEqual(vol.iloc[-1], 0.45829858614978286) vol = robust_vol_calc(returns, min_periods=10) self.assertTrue(np.isnan(vol.iloc[-1]))
def test_robust_vol_calc(self): prices = get_data("syscore.tests.pricetestdata.csv") returns = prices.diff() vol = robust_vol_calc(returns, days=35) self.assertAlmostEqual(vol.iloc[-1], 0.41905275480464305) vol = robust_vol_calc(returns, days=100) self.assertAlmostEqual(vol.iloc[-1], 0.43906619578902956)
def calc_ewmac_forecast(price, Lfast, Lslow=None): """ Calculate the ewmac trading rule forecast, given a price and EWMA speeds Lfast, Lslow and vol_lookback """ # price: This is the stitched price series # We can't use the price of the contract we're trading, or the volatility # will be jumpy # And we'll miss out on the rolldown. See # https://qoppac.blogspot.com/2015/05/systems-building-futures-rolling.html price = price.resample("1B").last() if Lslow is None: Lslow = 4 * Lfast # We don't need to calculate the decay parameter, just use the span # directly fast_ewma = price.ewm(span=Lfast).mean() slow_ewma = price.ewm(span=Lslow).mean() raw_ewmac = fast_ewma - slow_ewma vol = robust_vol_calc(price.diff()) return raw_ewmac / vol
def test_robust_vol_calc_floor(self): prices = get_data("syscore.tests.pricetestdata_vol_floor.csv") returns = prices.diff() vol = robust_vol_calc(returns) self.assertAlmostEqual(vol.iloc[-1], 0.54492982003602064) vol = robust_vol_calc(returns, vol_floor=False) self.assertAlmostEqual(vol.iloc[-1], 0.42134038479240132) vol = robust_vol_calc(returns, floor_min_quant=0.5) self.assertAlmostEqual(vol.iloc[-1], 1.6582199589924964) vol = robust_vol_calc(returns, floor_min_periods=500) self.assertAlmostEqual(vol.iloc[-1], 0.42134038479240132) vol = robust_vol_calc(returns, floor_days=10, floor_min_periods=5) self.assertAlmostEqual(vol.iloc[-1], 0.42134038479240132)
def _get_daily_vol_from_sim_data(self, instrument_code: str) -> pd.Series: price = self.data.daily_prices(instrument_code) # backadjusted prices can be negative abs_price = abs(price) return_vol = robust_vol_calc(price.diff()) daily_vol_as_ratio = return_vol / abs_price daily_perc_vol = 100.0 * daily_vol_as_ratio return daily_perc_vol
def ewmac_calc_vol(price, Lfast, Lslow, vol_days=35): """ Calculate the ewmac trading rule forecast, given a price and EWMA speeds Lfast, Lslow and number of days to lookback for volatility Assumes that 'price' is daily data This version recalculates the price volatility, and does not do capping or scaling :param price: The price or other series to use (assumed Tx1) :type price: pd.Series :param Lfast: Lookback for fast in days :type Lfast: int :param Lslow: Lookback for slow in days :type Lslow: int :param vol_days: Lookback for volatility in days :type vol_days: int :returns: pd.Series -- unscaled, uncapped forecast >>> from systems.tests.testdata import get_test_object_futures >>> from systems.basesystem import System >>> (rawdata, data, config)=get_test_object_futures() >>> system=System( [rawdata], data, config) >>> >>> ewmac(rawdata.get_daily_prices("EDOLLAR"), rawdata.daily_returns_volatility("EDOLLAR"), 64, 256).tail(2) 2015-12-10 5.327019 2015-12-11 4.927339 Freq: B, dtype: float64 """ # price: This is the stitched price series # We can't use the price of the contract we're trading, or the volatility will be jumpy # And we'll miss out on the rolldown. See # https://qoppac.blogspot.com/2015/05/systems-building-futures-rolling.html # We don't need to calculate the decay parameter, just use the span # directly fast_ewma = price.ewm(span=Lfast).mean() slow_ewma = price.ewm(span=Lslow).mean() raw_ewmac = fast_ewma - slow_ewma vol = robust_vol_calc(price, vol_days) return raw_ewmac / vol.ffill()
def conditioned_factor_trading_rule( demean_factor_value, condition_demean_factor_value, smooth=90 ): vol = robust_vol_calc(demean_factor_value) normalised_factor_value = demean_factor_value / vol sign_condition = condition_demean_factor_value.apply(np.sign) sign_condition_resample = sign_condition.reindex( normalised_factor_value.index ).ffill() conditioned_factor = normalised_factor_value * sign_condition_resample smoothed_conditioned_factor = conditioned_factor.ewm(span=smooth).mean() return smoothed_conditioned_factor
def get_daily_returns_volatility(self, instrument_code): """ Get the daily return (not %) volatility from previous stage, or calculate KEY INPUT :param instrument_code: :type str: :returns: Tx1 pd.DataFrames """ system = self.parent if hasattr(system, "rawdata"): returns_vol = system.rawdata.daily_returns_volatility( instrument_code) else: price = self.get_daily_price(instrument_code) returns_vol = robust_vol_calc(price.diff()) return returns_vol
def ewmac_forecast_with_defaults(price, Lfast=32, Lslow=128): """ Calculate the ewmac trading fule forecast, given a price and EWMA speeds Lfast, Lslow and vol_lookback Assumes that 'price' is daily data This version recalculates the price volatility, and does not do capping or scaling :param price: The price or other series to use (assumed Tx1) :type price: pd.Series :param Lfast: Lookback for fast in days :type Lfast: int :param Lslow: Lookback for slow in days :type Lslow: int :returns: pd.DataFrame -- unscaled, uncapped forecast """ # price: This is the stitched price series # We can't use the price of the contract we're trading, or the volatility # will be jumpy # And we'll miss out on the rolldown. See # https://qoppac.blogspot.com/2015/05/systems-building-futures-rolling.html # We don't need to calculate the decay parameter, just use the span # directly fast_ewma = price.ewm(span=Lfast).mean() slow_ewma = price.ewm(span=Lslow).mean() raw_ewmac = fast_ewma - slow_ewma vol = robust_vol_calc(price.diff()) return raw_ewmac / vol
def factor_trading_rule(demean_factor_value, smooth=90): vol = robust_vol_calc(demean_factor_value) normalised_factor_value = demean_factor_value / vol smoothed_normalised_factor_value = normalised_factor_value.ewm(span=smooth).mean() return smoothed_normalised_factor_value
def test_robust_vol_calc_min_value(self): prices = get_data("syscore.tests.pricetestdata_zero_vol.csv") returns = prices.diff() vol = robust_vol_calc(returns, vol_abs_min=0.01) self.assertEqual(vol.iloc[-1], 0.01)
def calculate_daily_returns_vol(self, instrument_code: str) -> pd.Series: price = self._daily_prices_direct_from_data(instrument_code) returns_vol = robust_vol_calc(price.diff()) return returns_vol