def calculate_single_forecast(self, multiplier: int = 1000) -> float: """ Calculates volatility forecast for single asset. It is expressed in the frequency of returns. Value is calculated based on the configuration as in the object attributes. The result of the calculation is returned as well as assigned as self.forecasted_volatility object attribute. Parameters ---------- multiplier: int ex. 100 (should be > 1) improves the optimization performance, as for very small values the results may be faulty; after optimization the results are scaled back (division by multiplier value) Returns ------- float Forecasted value of the volatility. """ assert self.forecasted_volatility is None, "The forecast was already calculated." assert multiplier >= 1 returns_tms = self.returns_tms * multiplier volatility_value = self._calculate_single_value(returns_tms) volatility_value = volatility_value / multiplier if self.annualise: volatility_value = annualise_with_sqrt(volatility_value, self.frequency) self.forecasted_volatility = volatility_value return volatility_value
def get_volatility(qf_series: QFSeries, frequency: Frequency = None, annualise: bool = True) -> float: """ Calculates a volatility for the given series of returns or prices. If a PricesSeries is given, the calculation window is shorter by 1 due to the initial conversion into returns. Parameters ---------- qf_series: QFSeries series of prices/returns (as numbers, e.g. 0.5 corresponds to 50% return) frequency: Frequency the frequency of samples in the returns series; it is only obligatory to specify frequency if the annualise parameter is set to True, which is a default value annualise: bool True if the volatility values should be annualised; False otherwise. If it is set to True, then it is obligatory to specify a frequency of the returns series. Returns ------- float volatility for the whole series. """ returns_tms = qf_series.to_log_returns() assert len( returns_tms) >= 2, "minimal num_of_rows to receive a real result is 2" assert not annualise or frequency is not None volatility = returns_tms.std() if annualise: volatility = annualise_with_sqrt(volatility, frequency) return volatility
def _calculate_risk_stats(self): self.cvar = cvar(self.returns_tms, 0.05) # default is the 5% CVaR log_cvar = simple_to_log_return(self.cvar) annualised_log_cvar = annualise_with_sqrt(log_cvar, self.frequency) self.annualised_cvar = log_to_simple_return(annualised_log_cvar) prices_tms = self.returns_tms.to_prices() self.max_drawdown = max_drawdown(prices_tms) self.avg_drawdown = avg_drawdown(prices_tms) self.avg_drawdown_duration = avg_drawdown_duration(prices_tms)
def get_volatility(ohlc: PricesDataFrame, frequency: Frequency = None, annualise: bool = True, alpha: float = None) -> float: """ Implementation of the algorithm described in 'Drift Independent Volatility Estimation Based on High, Low, Open and Close Prices', developed by Dennis Yang and Qiang Zhang, published in June 2000 issue of Journal of Business. The new estimator has the following nice properties: - unbiased in the continuous limit, - independent of the drift, - dealing with opening price jumps in a consistent way, - smallest variance among all estimators with the similar properties. Parameters ---------- ohlc: PricesDataFrame QFDataFrame consisting of four QFPricesSeries: open, high, low, close frequency: Frequency the frequency of samples in the returns series; it is only obligatory to specify frequency if the annualise parameter is set to True, which is a default value annualise: bool True if the volatility values should be annualised; False otherwise. If it is set to True, then it is obligatory to specify a frequency of the returns series. alpha: float expectation of u(u-c)+d(d-c) squared, values in range of (1.331, 1.5); authors of the algorithm suggest 1.34 in practice Returns ------- float Drift Independent Volatility of type float """ assert ohlc.num_of_rows >= 2 assert not annualise or frequency is not None var_RS = DriftIndependentVolatility._rogers_satchell_variance(ohlc) var_O = DriftIndependentVolatility._open_variance(ohlc) var_C = DriftIndependentVolatility._close_variance(ohlc) k = DriftIndependentVolatility._k_factor(ohlc, alpha) variance = var_O + k * var_C + (1 - k) * var_RS volatility = np.sqrt(variance) if annualise: volatility = annualise_with_sqrt(volatility, frequency) return volatility
def calculate_timeseries(self, window_len: int, multiplier: int = 1) -> QFSeries: """ Calculates volatility forecast for single asset. It is expressed in the frequency of returns. Value is calculated based on the configuration as in the object attributes. The result of the calculation is returned as well as assigned as self.forecasted_volatility object attribute. Parameters ---------- window_len size of the rolling window, number of rows used as input data to calculate forecasted volatility multiplier ex. 100 (should be > 1) improves the optimization performance, as for very small values the results may be faulty; after optimization the results are scaled back (division by multiplier value) Returns ------- Timeseries of forecasted volatility. """ assert self.forecasted_volatility is None, "The forecast was already calculated." assert window_len is not None, "For timeseries calculation the rolling window length must be specified." self.window_len = window_len assert multiplier >= 1 returns_tms = self.returns_tms * multiplier volatility_tms = returns_tms.rolling_window( window_len, self._calculate_single_value) volatility_tms = volatility_tms.dropna() volatility_tms = volatility_tms / multiplier if self.annualise: volatility_tms = annualise_with_sqrt(volatility_tms, self.frequency) self.forecasted_volatility = volatility_tms return volatility_tms
def test_annualise_with_sqrt(self): actual_annualised_values = annualise_with_sqrt( self.test_returns, frequency=Frequency.DAILY) expected_annualised_values = [i * sqrt(252) for i in self.test_returns] self.assertEqual(expected_annualised_values, actual_annualised_values)