【量化交易日记0174】波动率续谈

论坛 期权论坛 期权     
KeyboardLess JO   2019-8-11 10:17   4913   0



实时组合状态更新,尽在http://49.51.196.202:5006/portfolio.html










下跌了,前方路滑,小心妖魔鬼怪出没。


延续昨天的话题。

波动率有很多种估计量。我们只介绍几个。为此,必须先引入几个符号。

首先是四个对数价格


然后,是正规化之后的开高收低价格。






可以看出,其实所谓的正规化价格,都是对数收益率:t期的正规收盘价、最高价、最低价是t期的绝对收盘、最高、最低价相对于t期绝对开盘价的对数收益率;t期的正规开盘价是t期的绝对开盘价相对于t-1期的绝对收盘价的对数收益率。

根据这些“价格”,我们就可以估计波动率。



RV:已实现波动率


已实现波动率是最简单的一种。它计算的就是收益率的波动率。在对数收益率均值为0的情况下,它的计算公式是:




均值不为0也没事,pandas 直接就搞定了
  1. da['rtn'] = da['ln_close'] - da['ln_close'].shift(1)
复制代码
  1. r_v = da['rtn'].std(ddof=1)
复制代码

这里不用close也无妨,尤其是在金融数据中,收盘价往往是有鬼的。而且更美妙的是,如果你有更高频的数据,比如tick数据,也完全可以用类似的方法来算波动率。

RV是我们最喜爱的波动率之一。因为它充分用到了日内交易的信息,是一种依赖于“价格路径”的波动率。那么,RV这么简单,又充分利用了信息,为什么还要研究其他的波动率呢?

因为要计算RV的数据不是那么轻易可得的。且不说tick数据,就算是分钟级的数据,也不是地上随便捡的。这就催生了后面的一系列波动率,学界称为range estimator,也就是依赖于价格范围的波动率估计量,它们所需要的信息就少的多。



PK估计量




PK估计量依赖于资产价格服从几何布朗运动的假设。这个假设在很多情况下是否成立是存疑的,所谓价格服从几何布朗运动就是:




等式左边是微分形式的收益率,右边第一项是漂移项,代表收益率的趋势,第二项是波动项,代表收益率除了存在趋势之外,还存在一个均值为0,标准差为σ的正态分布扰动。

这个假设是否成立,如果不成立影响大不大,如果影响大,是否有代替方法,这些话题都太过于深奥,我们不可能在日报里写完。但有一点我们是肯定的:PK估计量只用到了h(t)和l(t),那用到的信息肯定比RV少。效果肯定是要打折扣的了,但在可得数据受限的情况下也没有办法。



GK估计量


在PK估计量横空出世之后,又有学者意识到,你这个PK估计量没有用到收盘价呀。所以,基于这一点,Garman和Klass两位老师提出了GK估计量


据GK两位说,GK估计量比PK估计量有效7.4倍,这个有零有整的7.4倍是咋来的我也没有精力去考证了…GK估计量依旧依赖于几何布朗运动的假设。



GKYZ估计量


在GK估计量横空出世之后,两位中国学者对它又进行了改进,提出了GKYZ的估计量(把自己的名字首字母加到了GK后面)


加上后面的这一项是为了涵盖opening jump,也就是跳空高开或者低开。



RS估计量

Roger和Satchell两位老师提出了这样一个估计量


RS估计量没有考虑开盘跳空的情景,但是,RS估计量是波动率的无偏估计量。

除了需要使用的信息少,这些range estimator还有一个比较优良的性质:range estimator比RV更加接近正态分布。所以,在进行假设检验和预测的时候,会比RV更好一些。

前面说了,RV估计量是我们最喜爱的波动率估计量之一,另外一个我们也特别喜欢的估计量就是隐含波动率IV。使用期权价格推算得到的波动率估计量。在我们之前的文章【量化交易日记00122】拿什么拯救你 我的波指 中有提到。

附上计算这些波动率的代码


  1. import pandas as pd
复制代码
  1. import numpy as np
复制代码
  1. from datetime import timedelta
复制代码
复制代码
  1. pd.set_option('expand_frame_repr', False)
复制代码
复制代码
  1. data_path = ''
复制代码
  1. df = pd.read_csv(data_path)
复制代码
复制代码
  1. class PriceData:
复制代码
  1.     def __init__(self, data):
复制代码
复制代码
  1.         data['candle_begin_time'] = pd.to_datetime(data['candle_begin_time'])
复制代码
  1.         data['price'] = data[['open', 'high', 'low', 'close']].mean(axis=1)
复制代码
  1.         data[['ln_open', 'ln_high', 'ln_low', 'ln_close', 'ln_price']] = data[['open', 'high', 'low', 'close', 'price'
复制代码
  1.                                                                                ]].apply(lambda x: np.log(x))
复制代码
  1.         data['date'] = data['candle_begin_time'].dt.date
复制代码
  1.         data = data[['candle_begin_time', 'ln_open', 'ln_high', 'ln_low', 'ln_close', 'date']].copy()
复制代码
  1.         data['open'] = data['ln_open'] - data['ln_close'].shift(1)
复制代码
  1.         data['high'] = data['ln_high'] - data['ln_open']
复制代码
  1.         data['low'] = data['ln_low'] - data['ln_open']
复制代码
  1.         data['close'] = data['ln_close'] - data['ln_open']
复制代码
  1.         self.data = data
复制代码
复制代码
  1.     def rv_esti(self):
复制代码
  1.         da = self.data.copy()
复制代码
  1.         da['rtn'] = da['ln_close'] - da['ln_close'].shift(1)
复制代码
  1.         da.dropna(inplace=True, axis=0)
复制代码
  1.         result = {}
复制代码
复制代码
  1.         def f1(x):
复制代码
复制代码
  1.             date = x['date'].iloc[0]
复制代码
  1.             r_v = x['rtn'].std(ddof=1)
复制代码
  1.             result[date] = r_v
复制代码
复制代码
  1.         da.groupby('date').apply(f1)
复制代码
  1.         rv_df = pd.DataFrame(result, index=['rv']).T
复制代码
  1.         return rv_df
复制代码
复制代码
  1.     def pk_esti(self):
复制代码
  1.         da = self.data.copy()
复制代码
  1.         result = {}
复制代码
复制代码
  1.         def f1(x):
复制代码
  1.             date = x['date'].iloc[0]
复制代码
  1.             pk = ((x['high'].max() - x['low'].min()) ** 2) / (4 * np.log(2))
复制代码
  1.             result[date] = pk
复制代码
复制代码
  1.         da.groupby('date').apply(f1)
复制代码
  1.         pk_df = pd.DataFrame(result, index=['pk']).T
复制代码
  1.         return pk_df
复制代码
复制代码
  1.     def gk_esti(self):
复制代码
  1.         da = self.data.copy()
复制代码
  1.         result = {}
复制代码
复制代码
  1.         def f1(x):
复制代码
  1.             date = x['date'].iloc[0]
复制代码
  1.             gk = 0.5 * ((x['high'].max() - x['low'].min()) ** 2) - (2 * np.log(2) - 1) * (x['close'].iloc[-1]) ** 2
复制代码
  1.             result[date] = gk
复制代码
复制代码
  1.         da.groupby('date').apply(f1)
复制代码
  1.         gk_df = pd.DataFrame(result, index=['gk']).T
复制代码
  1.         return gk_df
复制代码
复制代码
  1.     def gkyz_esti(self):
复制代码
  1.         da = self.data.copy()
复制代码
  1.         result = {}
复制代码
复制代码
  1.         def f1(x):
复制代码
  1.             date = x['date'].iloc[0]
复制代码
  1.             if date != da['date'].iloc[0]:
复制代码
  1.                 last_ln_close = da.loc[da['date'] == (date - timedelta(days=1)), 'ln_close'].iloc[-1]
复制代码
  1.                 gk = 0.5 * ((x['high'].max() - x['low'].min()) ** 2) - (2 * np.log(2) - 1) * (x['close'].iloc[-1]) ** 2
复制代码
  1.                 gkyz = gk + (x['ln_open'].iloc[0] - last_ln_close) ** 2
复制代码
  1.                 result[date] = gkyz
复制代码
复制代码
  1.         da.groupby('date').apply(f1)
复制代码
  1.         gkyz_df = pd.DataFrame(result, index=['gkyz']).T
复制代码
  1.         return gkyz_df
复制代码
复制代码
  1.     def rs_esti(self):
复制代码
  1.         da = self.data.copy()
复制代码
  1.         result = {}
复制代码
复制代码
  1.         def f1(x):
复制代码
  1.             date = x['date'].iloc[0]
复制代码
  1.             rs = x['high'].max() * (x['high'].max() - x['close'].iloc[-1]) + \
复制代码
  1.                  x['low'].min() * (x['low'].min() - x['close'].iloc[-1])
复制代码
  1.             result[date] = rs
复制代码
复制代码
  1.         da.groupby('date').apply(f1)
复制代码
  1.         rs_df = pd.DataFrame(result, index=['rs']).T
复制代码
  1.         return rs_df
复制代码
复制代码
复制代码
  1. price_data = PriceData(df)
复制代码
复制代码
  1. res_df = price_data.rv_esti()
复制代码
  1. res_df['pk'] = price_data.pk_esti()
复制代码
  1. res_df['gk'] = price_data.gk_esti()
复制代码
  1. res_df['gkyz'] = price_data.gkyz_esti()
复制代码
  1. res_df['rs'] = price_data.rs_esti()
复制代码

代码比较丑陋。


说到底,我们为什么要计算这些波动率呢?研究的目的是研究的过程中最容易忘记的东西。


回归初心,由于CTA策略本身是做多波动率的策略,而且我们也能够观察到,历史上波动率高的情况下,CTA策略的收益会高很多。那么我们就倾向于在波动率大的时候加大力度。所以,在当前建仓时,如果能够知道未来的波动率,就可以指导仓位的轻重,从而规避回撤,增加收益。

接下来几天我们会专注波动率的话题,敬请期待。


参考文献
Akindynos-Nikolaos Balta, Robert Kosowski (2012): Improving Time-Series Momentum StrategiesThe Role of Volatility Estimators and Trading Signals  


题图

Vincent van Gogh: Seascape at Saintes-Maries
分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:15
帖子:3
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP