0%

Volatility and Correlation

波动率

波动率(volatility)指的是资产价格的波动强弱程度, 类似于概率论中随机变量标准差的概念。 波动率不能直接观测, 可以从资产收益率中看出波动率的一些特征。

  • 存在波动率聚集(volatility clustering)
  • 波动率随时间连续变化,一般不出现波动率的跳跃式变动(存疑)
  • 波动率一般在固定的范围内变化,意味着动态的波动率是平稳的
  • 在资产价格大幅上扬和大幅下跌两种情形下, 波动率的反映不同, 大幅下跌时波动率一般也更大, 这种现象称为杠杆效应(leverage effect)

通常波动率被分为两种,一种是回望型波动率(backward looking),另外一种是前瞻波动率(forward looking)。前者是用历史数据算出来的波动率,后者是根据现在的期权市场报价用定价模型反推出来的波动率。前者是已经发生了的历史价格的波动,根据历史数据计算的一个波动率,后者是市场对未来标的价格的波动程度(波动率)的预测,通常不准确。

除此之外我认为还有两个概念经常被混淆,分别是实际波动率(Actual Volatility)和理论波动率(Theory Volatility)。实际波动率是对期权有效期内投资回报率波动程度的度量,对一个期权头寸组合而言,它的实际波动率应该指的是实盘交易中离散的动态对冲过程中delta敞口变化导致采样到的标的波动,这个概念通常与历史波动率混淆,即使用历史波动率来代替组合实际采样到的波动率。举例说明,假设标的\(X\)存在极端的日内波动,一个交易日内的振幅为\(0\%, +r\%, -r\%, 0\%\) 。对一个Gamma-Scalping的头寸组合存在如下几种对冲情况

  • 组合在标的振幅为\(+r\%, -r\%, 0\%\)进行了全部对冲,则组合采样标的的当日的波动幅度为\(r+2r+r\)
  • 组合在标的振幅为\(+r\%, -r\%, 0\%\)进行部分对冲,由于Greeks敞口的减少组合采样标的的当日的波动幅度小于\(r+2r+r\)
  • 组合在标的振幅为\(+0.5r\%, 0\%, -0.5r\%, 0\%\)等位置(既不是在日内最高点、最低点)进行完全或部分对冲,由于Greeks敞口的减少组合采样标的的当日的波动幅度小于\(r+2r+r\)
  • 组合在当日未进行对冲,则在日终收盘组合采样到的波动幅度为\(0\%\)

理论波动率(Theory Volatility)指的是“虚假理论定价模型”中假设的那个波动率。就如那个经典笑话中所讲的,面对一瓶红酒,经济学家假设存在一个开瓶器,这个开瓶器应该符合某些特征的一种存在,然后按图索骥。波动率这里被假设为一个符号参数(未知,但是假设存在并被使用),对简化抽象的的模型这个参数的嵌入使得模型在模型假设的世界里面逻辑自洽,再反向推导这个初始假设存在的参数。这样将理论波动率设定为波动率\(\sigma\),这样不同的波动率算法计算的波动率都可以被近似认定为这个理论波动率的有偏差的数值,这样不同算法(假设)的波动率就可以进行比较了。例如历史波动率和隐含波动率,一个向后一个向前从含义上并不能进行比较,但是如果假设历史波动率是过去一段时间的波动率(理论波动率)数值,隐含波动率是未来一段时间的波动率的数值,在逻辑上就不会显得那么突兀别扭。或者可以直接理解为理论波动率是一个可以看到但是看不清楚的波动率,所有的波动率算法都是试图更小误差的观察这个波动率的工具。

回望型波动率

历史波动率通常使用标的收盘价的对数收益率年化标准差计算 \[\begin{equation} \begin{aligned} r_t &= {\rm log}(S_{t} / S_{t-1})\\ \sigma_{\rm hv} &= \sqrt{\frac{1}{n-1}\sum^{n}_{t=1}(r_i - \overline{r})^2} \end{aligned} \end{equation}\]

对日线数据通常假设收益率均值为0,减少一个要估算的参数增加一个自由度,历史波动率算法修改为 \[ \sigma_{\rm hv}^{\prime} = \sqrt{\frac{1}{n}\sum^{n}_{t=1}(r_i)^2} \]

Exponential Decay Volatility

波动率算法修改为一段时间内方差的均值,可以使用指数衰减的权重因子为时间序列靠后的数据赋予更高的权重,使当前的波动率受近期影响大于远期影响。塔勒布在《动态对冲》的Chapter-6 Volatility and Correlation中通过设定证券的收益率序列均值为0,将单日收益率转换为单日的“波动率价格序列”,然后进行滤波平滑处理。

test \[\begin{equation} \begin{aligned} \sigma_{\rm hv} &= \sqrt{\frac{1}{\Omega}\sum^{n}_{t=1} \lambda^{t}\cdot r^{2}_{t}}\\ \Omega &= \sum^{n}_{t=1}\lambda^t = (\lambda +\lambda^2 +\lambda^3+\cdots+\lambda^{n}) \end{aligned} \end{equation}\]

可以用递归函数计算波动率的时间序列 \[\begin{equation} \begin{aligned} \sigma^2 &= \frac{\sum^{n}_{t=1} \lambda^{t}\cdot r^{2}_{t}}{\lambda +\lambda^2 +\lambda^3+\cdots+\lambda^{n}}\\ &=\frac{\sum^{n}_{t=1} \lambda^{t-1}\cdot r^{2}_{t}}{1+\lambda +\lambda^2 +\cdots+\lambda^{n}}\\ &=(1-\lambda)\sum^{n}_{t=1} \lambda^{t-1}\cdot r^{2}_{t}\\ &=(1-\lambda)r^{2}_{t} + \lambda \sigma_{t-1}^2 \end{aligned} \end{equation}\]

几何级数 \[ \frac{1}{1-x} = \sum^{\infty}_{n=0}x^n = 1+x+x^2+\cdots+x^{n}+\cdots \] \[ \forall x: \left|x \right|<1 \]

\(\alpha= 1-\lambda\)\[\begin{equation} \begin{aligned} \sigma^2 &=\alpha r^{2}_{t} + (1-\alpha) \sigma_{t-1}^2\\ &={\rm EMA}(r^2)\\ &= \alpha\cdot r^{2}_{t} + (1-\alpha){\rm EMA}(r_{t-1}^2) \end{aligned} \end{equation}\]

修改了波动率算法为方差时间序列的均值之后可以直接使用常见的MA和EMA算法了

方差的MA计算波动率 \[ \sigma_{\rm hv} = \sqrt{\rm MA(r^2)}\\ \]

1
2
3
4
// 麦语言
RET:=CLOSE/REF(CLOSE,1);
RETV:=LN(RET) * LN(RET);
MA1:SQRT(MA(RETV, N) * 244)*100;

方差的EMA计算波动率 \[ \sigma_{\rm hv} = \sqrt{\rm EMA(r^2)} \] \[ {\rm EMA}(r^2) = \alpha \cdot r^2_{t} + (1-\alpha) {\rm EMA}(r_{t-1}^2) \] \[ \alpha = \frac{2}{n+1} \]

1
2
3
4
// 麦语言
RET:=CLOSE/REF(CLOSE,1);
RETV:=LN(RET) * LN(RET);
EMA1:SQRT(EMA(RETV, N) * 244)*100;

如下图,EMA算法要比MA算法对短期变动更敏感 hv_ema

相关性

相关性是一个统计指标,表示两个变量线性相关(即它们以固定的比率一起变化)的程度。它是一个用于描述简单关系而没有陈述因果关系的常用工具。相关性可以检验两个变量之间的关系。但是相关性无法告诉我们在所探索的两个变量之外是否存在其他变量,或这些变量有何影响,即两个变量一起变动并不一定意味着我们知道是不是一个变量引起了另一个变量的变动。因此我们常说,“相关性并不意味着因果关系”。

通常使用无量纲的相关系数来描述相关性,相关系数 \[ \rho = \frac{1}{n-1}\frac{\sum^{n}_{t=1}(x-\overline{x})(y_t-\overline{y})}{\sigma_x \sigma_y} \] 忽略收益率时间序列的均值,两个资产的收益率相关性修改为 \[ \rho = \frac{1}{n}\frac{\sum^{n}_{t=1}x_t y_t}{\sigma_x^{\prime} \sigma_y^{\prime}} \] 同样可以使用指数衰减调整相关系数调整长短期记忆,使相关性的时间序列对短期时间序列的变化更加敏感 \[ \rho = \frac{1}{\Omega} \frac{\sum^{n}_{t=1}\lambda^{t} x_t y_t}{\sigma_x \sigma_y} \]

correlation_coefficient

相关性\(\rho=1\)不表示两个时间序列完全相同,两个时间序列相对自身的变化比率(收益率时间序列)环比相同的情况下,相关性同样为1。

相关系数

What is the Beta Coefficient?

Beta 系数是衡量证券或投资组合对整体市场变动的敏感性或相关性的指标。 我们可以通过将单个证券/投资组合的回报与整体市场的回报进行比较来推导出风险的统计度量,并确定可归因于市场的风险比例。

The Capital Asset Pricing Model (CAPM) \[{\rm R_e} - {\rm R_f} = \beta \times[{\rm R_m - R_f}]\]

  • \({\rm R_e}\) Stock Return
  • \({\rm R_f}\) Risk Free Rate
  • \({\rm R_m}\) Market Return
  • \(\beta\) Beta Coefficient

How to Calculate the Beta Coefficient \[\beta=\frac{\rm Covariance (R_e, R_m)}{\rm Variance(R_m)}\]

  • \({\rm Covariance (R_e, R_m)} = \sum \frac{\rm (R_{e,n} - R_{e,avg}) (R_{m,n} - R_{m, avg})}{\rm n-1}\)

  • \({\rm Variance (R_m)} = \sum \frac{\rm (R_{m,n} - R_{m,avg})^2}{n-1}\)

Adjusted Beta Coefficient

  • \({\rm Covariance (R_e, R_m)} = \sum \frac{\rm (R_{e,n} - 0) (R_{m,n} - 0)}{\rm n}\)
  • \({\rm Variance (R_m)} = \sum \frac{\rm (R_{m,n} - 0)^2}{n}\)

Measuring Historical Volatility

除了使用标的资产价格的close-to-close计算波动率之外,还有几种算法被开发出来计算历史波动率

PARKINSON

The first advanced volatility estimator was created by Parkinson in 1980, and instead of using closing prices it uses the high and low price. One drawback of this estimator is that it assumes continuous trading, hence it underestimates the volatility as potential movements when the market is shut are ignored.

\[ \sigma_{\rm p} = \sqrt{\frac{F}{N}} \sqrt{\frac{1}{4Ln(2)}\sum^{N}_{i=1}(Ln(\frac{h_i}{l_i}))^2} \]

  • \(F\) 年化系数

GARMAN-KLASS

Later in 1980 the Garman-Klass volatility estimator was created. It is an extension of Parkinson which includes opening and closing prices (if opening prices are not available the close from the previous day can be used instead). As overnight jumps are ignored the measure underestimates the volatility.

\[ \sigma_{\rm GK} = \sqrt{\frac{F}{N}} \sqrt{\sum^{N}_{i=1}\frac{1}{2}(Ln(\frac{h_i}{l_i}))^2 - (2Ln(2) - 1)(Ln(\frac{c_i}{o_i}))^2} \]

ROGERS-SATCHELL

All of the previous advanced volatility measures assume the average return (or drift) is zero. Securities which have a drift, or non-zero mean, require a more sophisticated measure of volatility. The Rogers-Satchell volatility created in the early 1990s is able to properly measure the volatility for securities with non-zero mean. It does not, however, handle jumps, hence it underestimates the volatility.

\[ \sigma_{\rm RS} = \sqrt{\frac{F}{N}} \sqrt{\sum^{N}_{i=1}Ln(\frac{h_i}{c_i})Ln(\frac{h_i}{c_i}) + Ln(\frac{l_i}{c_i})Ln(\frac{l_i}{c_i})} \]

GARMAN-KLASS YANG-ZHANG EXTENSION

Yang-Zhang modified the Garman-Klass volatility measure in order to let it handle jumps. The measure does assume a zero drift, hence it will overestimate the volatility if a security has a non-zero mean return. As the effect of drift is small, the fact continuous prices are not available usually means it underestimates the volatility (but by a smaller amount than the previous alternative measures).

\[ \sigma_{\rm GKYZ} =\sqrt{\frac{F}{N}} \sqrt{\sum^{N}_{i=1} (Ln(\frac{o_i}{c_{i-1}}))^2 + \frac{1}{2}(Ln(\frac{h_i}{l_i}))^2 - (2Ln(2)-1)(Ln(\frac{c_i}{o_i}))^2} \]

YANG-ZHANG

It is the sum of the overnight volatility (close-to-open volatility) and a weighted average of the Rogers-Satchell volatility and the open-to-close volatility. The assumption of continuous prices does mean the measure tends to slightly underestimate the volatility.

\[ \sigma_{\rm YZ} = \sqrt{F} \sqrt{\sigma^2_{\rm overnight \ volatility} + \sigma^2_{\rm open \ to \ close \ volatility} + (1-k)\sigma_{RS}^2} \]

\[ k = \frac{0.34}{1.34+ \frac{N+1}{N-1}} \]

\[ \sigma^2_{\rm overnight \ volatility} = \frac{1}{N-1} \sum^{N}_{i=1}[Ln (\frac{o_i}{c_{i-1}}) - \overline{Ln(\frac{o_i}{c_{i-1}})}]^2 \]

\[ \sigma^2_{\rm open \ to \ close \ volatility} = \frac{1}{N-1} \sum^{N}_{i=1}[Ln (\frac{o_i}{c_{i}}) - \overline{Ln(\frac{o_i}{c_{i}})}]^2 \]

衡量同一段时间内的HV,不同算法呈现数值差异较大

hv

粗糙的评估历史波动率收敛速度(算法有效性)可以使用GBM生成时间序列,观察不同算法在不同波动率水平下收敛到预定波动率参数需要的时间序列长度。 \[ {\rm ln}S(t+\Delta t)- {\rm ln}S(t) = (\mu-\frac{\sigma^2}{2})\Delta t + \sigma \varepsilon \sqrt{\Delta t} \] 将一个交易日的时间分割成多份生成当日的股价随机游走的路径图,将这个时间序列假设为当日真实的交易情况生成K线。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# params
mu = 1e-8
sigma = 0.2
diff_time = 1 / 244. / (4 * 4)

# path
spot = 5000
size = 201 * (4 * 4 + 1) - 1
lns = (mu - sigma * sigma / 2) * diff_time + sigma * normal(0, 1, size=size) * sqrt(diff_time)
lns = insert(lns, 0, log(spot))
lns = lns.cumsum()

# k-line
lns = lns.reshape(-1, 17)
spot_path = exp(lns)
open_s = spot_path[1:, 0]
high = spot_path.max(axis=1)[1:]
low = spot_path.min(axis=1)[1:]
close = spot_path[1:, -1]
pre_close = spot_path[:-1, -1]

k-line

由于仿真数据使用\(\sigma = 0.2\)生成,最接近\(\sigma=0.2\)的算法粗略的评估是最好的 hv20 hv60

可以看到"先进算法"系统性低估波动率,而"原始算法"倾向于高估波动率,此外股价的路径严重影响波动率的计算。连续的单向移动会导致波动率的计算出现较大偏误,在这个问题上,强制假设日收益率为0(均值为0)会高估波动率,但是“先进算法”采样更多的数据点之后并没有降低漂移的影响,从长序列数据看呈现系统性低估的偏差。

hv30

由于实际交易不是连续时间交易上述算法中PARKINSONGARMAN-KLASSROGERS-SATCHELL在实盘数据中只使用了当日K线数据,日间波动没有被计算,可以将上一个交易日的收盘价合并到当日的K线数据中重新生成K线进行调整。

1
2
3
4
5
data = vstack([pre_close, open_s, high, low, close]).T
adj_open_s = data[:, 0]
adj_close = data[:, -1]
adj_high = data.max(axis=1)
adj_low = data.min(axis=1)

调整之后波动率数据同比上升,但是不能观察到具有更好的收敛效果 hv20_1 hv60_1

pk gk rs adj-pk adj-gk adj-rs pk gk rs adj-pk adj-gk adj-rs
count 300 300 300 300 300 300 300 300 300 300 300 300
mean 0.1727 0.1581 0.1558 0.1789 0.1640 0.1611 0.1732 0.1583 0.1562 0.1794 0.1641 0.1610
std 0.0163 0.0115 0.0147 0.0164 0.0118 0.0128 0.0060 0.0041 0.0055 0.0065 0.0042 0.0053
min 0.1388 0.1321 0.1223 0.1422 0.1376 0.1299 0.1614 0.1463 0.1426 0.1650 0.1551 0.1487
25% 0.1600 0.1506 0.1455 0.1672 0.1552 0.1524 0.1689 0.1548 0.1518 0.1745 0.1609 0.1569
50% 0.1716 0.1579 0.1577 0.1777 0.1654 0.1616 0.1734 0.1587 0.1570 0.1797 0.1639 0.1616
75% 0.1864 0.1671 0.1659 0.1933 0.1726 0.1698 0.1774 0.1615 0.1607 0.1839 0.1675 0.1654
max 0.2059 0.1862 0.1857 0.2109 0.1907 0.1926 0.1862 0.1669 0.1672 0.1919 0.1741 0.1721

With 100% autocorrelation, returns are perfectly correlated (a positive return is followed by a positive return, i.e. trending markets). Should autocorrelation be -100% correlated then a positive return is followed by a negative return (mean reverting or range trading markets). If we assume markets are 100% daily correlated with a 1% daily return, this means the weekly return is 5%. The daily volatility is therefore c16% (1% × \(\sqrt{252}\)) while the weekly volatility of c35% (5% × \(\sqrt{52}\)) is more than twice as large.

这里是使用不同周期频率的HV评估标的价格时间序列自相关性的方法,例如对比5min的K线和日K线计算的HV,使用日线/5分钟线HV(1d) / HV(5min),如果低频的HV高于高频的HV,说明这段时间内标的的价格存在正相关,也就是存在动量或趋势,对这种情景下的delta-hedging,日间对冲要比日内频繁对冲的预期P&L高,如果小于1则存在负相关应该频繁的进行对冲,在1附近则是说明标的价格独立同分布,在这个计量区间内震荡为主。

test1

如上图,上证50的HV数据显示,对50指数在过去两年时间内,逐日进行delta-hedging的预期收益要强于日内频繁对冲,即逐日对冲会在对冲过程中捕捉到更高的波动率。

代码实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
function hv = calc_hv_pk(high, low, size, factor)
array_length = length(high);
hv = nan(array_length,1);

for idx=1:size-1
hv(idx) = 0.0;
end

shift = size -1;
const_num = 4*log(2);
for idx=size:array_length
h = high(idx-shift:idx);
l = low(idx-shift:idx);
hv(idx) = sqrt(sum(power(log(h ./ l), 2)) / (const_num*size)) .* factor;
end
end


function hv = calc_hv_rs(open, high, low, close, size, factor)
array_length = length(high);
hv = nan(array_length,1);

for idx=1:size-1
hv(idx) = 0.0;
end

shift = size -1;
for idx=size:array_length
o = open(idx-shift:idx);
h = high(idx-shift:idx);
l = low(idx-shift:idx);
c = close(idx-shift:idx);
hv(idx) = sqrt(sum(log(h ./ c) .* log(h ./ o) + log(l ./ c) .* log(l ./ o)) ./ size) .* factor;
end
end

function hv = calc_hv_gk(open, high, low, close, size, factor)
array_length = length(high);
hv = nan(array_length,1);

for idx=1:size-1
hv(idx) = 0.0;
end

shift = size -1;
const_num = 2 .* log(2) - 1;
for idx=size:array_length
o = open(idx-shift:idx);
h = high(idx-shift:idx);
l = low(idx-shift:idx);
c = close(idx-shift:idx);
hv(idx) = sqrt(sum(0.5 .* power(log(h ./ l), 2) - const_num * power(log(c ./ o), 2)) ./ size) * factor;
end
end


function hv = calc_hv_gkyz(open, high, low, close, pre_close, size, factor)
array_length = length(high);
hv = nan(array_length,1);

for idx=1:size-1
hv(idx) = 0.0;
end

shift = size -1;
const_num = 2 .* log(2) - 1;
for idx=size:array_length
o = open(idx-shift:idx);
h = high(idx-shift:idx);
l = low(idx-shift:idx);
c = close(idx-shift:idx);
pc = pre_close(idx-shift:idx);
hv(idx) = sqrt(sum(power(log(o ./ pc), 2) + 0.5 .* power(log(h ./ l), 2) - const_num .* power(log(c ./ o), 2)) ./ size) .* factor;
end
end


function hv = calc_hv_yz(open, high, low, close, pre_close, size, factor)
array_length = length(high);
hv = nan(array_length,1);

for idx=1:size-1
hv(idx) = 0.0;
end

shift = size -1;
k = 0.34 ./ (1.34 + (size + 1) ./ (size - 1));
for idx=size:array_length
o = open(idx-shift:idx);
h = high(idx-shift:idx);
l = low(idx-shift:idx);
c = close(idx-shift:idx);
pc = pre_close(idx-shift:idx);

% overnight volatility
loc = log(o ./ pc);
ov = sum(power(loc - mean(loc), 2)) ./ (size - 1);

% open to close volatility
lco = log(c ./ o);
oc = sum(power(lco - mean(lco), 2)) ./ (size - 1);

% Rogers-Satchell volatility
rs = sum(log(h ./ c) .* log(h ./ o) + log(l ./ c) .* log(l ./ o)) ./ size;
hv(idx) = sqrt(ov + k .* oc + (1 - k) .* rs).* factor;
end
end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
py::array_t<double> calc_hv_cc(
const py::array_t<double> &close,
const int &time_period,
const double &factor) {
// 参数转换
py::buffer_info buf_close = close.request();
auto *ptr_close = static_cast<double *>(buf_close.ptr);

// result
auto result = py::array_t<double>(buf_close.size);
py::buffer_info buffer_result = result.request();
auto *ptr_result = static_cast<double *>(buffer_result.ptr);

//// calculate function
// 方差, var[0] = 0, var[1] = ret[0]
auto size = buf_close.size;
std::vector<double> var(size, 0.0);
double ret = 0.0;
for (int i = 1; i < size; i++) {
ret = log(ptr_close[i] / ptr_close[i - 1]);
var[i] = std::pow(ret, 2);
}

// 波动率
for (int i = 0; i < time_period; i++) {
ptr_result[i] = 0;
}

for (int i = time_period; i < size; i++) {
double tv = 0.0;
for (int j = i - time_period + 1; j <= i; j++) {
tv += var[j];
}
ptr_result[i] = sqrt(tv / time_period) * factor;
}

return result;
}

py::array_t<double> calc_hv_cc_ema(
const py::array_t<double> &close,
const int &time_period,
const double &factor) {

// 参数转换
py::buffer_info buf_close = close.request();
auto *ptr_close = static_cast<double *>(buf_close.ptr);

// result
auto result = py::array_t<double>(buf_close.size);
py::buffer_info buffer_result = result.request();
auto *ptr_result = static_cast<double *>(buffer_result.ptr);

//// calculate function
// 方差, var[0] = 0, var[1] = ret[0]
auto size = buf_close.size;
std::vector<double> var(size, 0.0);
double ret = 0.0;
for (int i = 1; i < size; i++) {
ret = log(ptr_close[i] / ptr_close[i - 1]);
var[i] = std::pow(ret, 2);
}

// 指数加权
const double alpha = 2. / (time_period + 1);
const double beta = 1. - alpha;
double tv = var[0];
for (int i = 1; i < size; i++) {
tv = alpha * var[i] + beta * tv;
ptr_result[i] = sqrt(tv) * factor;
}
const int shift = int(log(0.05) / log(beta));
// 波动率
for (int i = 0; i < shift + 1; i++) {
ptr_result[i] = 0;
}

return result;
}

py::array_t<double> calc_corr_cc(
const py::array_t<double> &close_x,
const py::array_t<double> &close_y,
const int &time_period) {

// 参数转换
py::buffer_info buf_x = close_x.request();
auto *ptr_x = static_cast<double *>(buf_x.ptr);
py::buffer_info buf_y = close_y.request();
auto *ptr_y = static_cast<double *>(buf_y.ptr);

// result
auto result = py::array_t<double>(buf_x.size);
py::buffer_info buffer_result = result.request();
auto *ptr_result = static_cast<double *>(buffer_result.ptr);

// 方差, var[0] = 0, var[1] = ret[0]
auto size = buf_x.size;
std::vector<double> var_x(size);
std::vector<double> var_y(size);
std::vector<double> var_xy(size);
double ret_x = 0.0;
double ret_y = 0.0;
for (int i = 1; i < size; i++) {
ret_x = log(ptr_x[i] / ptr_x[i - 1]);
ret_y = log(ptr_y[i] / ptr_y[i - 1]);
var_x[i] = std::pow(ret_x, 2);
var_y[i] = std::pow(ret_y, 2);
var_xy[i] = ret_x * ret_y;
}

// 相关性
for (int i = 0; i < time_period + 1; i++) {
ptr_result[i] = 0;
}

for (int i = time_period + 1; i < size; i++) {
double std_x = 0.0;
double std_y = 0.0;
double std_xy = 0.0;
for (int j = i - time_period; j < i; j++) {
std_x += var_x[j];
std_y += var_y[j];
std_xy += var_xy[j];
}
ptr_result[i] = std_xy / (sqrt(std_x) * sqrt(std_y));
}

return result;
}

py::array_t<double> calc_corr_cc_ema(
const py::array_t<double> &close_x,
const py::array_t<double> &close_y,
const int &time_period) {

// 参数转换
py::buffer_info buf_x = close_x.request();
auto *ptr_x = static_cast<double *>(buf_x.ptr);
py::buffer_info buf_y = close_y.request();
auto *ptr_y = static_cast<double *>(buf_y.ptr);

// result
auto result = py::array_t<double>(buf_x.size);
py::buffer_info buffer_result = result.request();
auto *ptr_result = static_cast<double *>(buffer_result.ptr);

// 方差, var[0] = 0, var[1] = ret[0]
auto size = buf_x.size;
std::vector<double> var_x(size);
std::vector<double> var_y(size);
std::vector<double> var_xy(size);
double ret_x = 0.0;
double ret_y = 0.0;
for (int i = 1; i < size; i++) {
ret_x = log(ptr_x[i] / ptr_x[i - 1]);
ret_y = log(ptr_y[i] / ptr_y[i - 1]);
var_x[i] = std::pow(ret_x, 2);
var_y[i] = std::pow(ret_y, 2);
var_xy[i] = ret_x * ret_y;
}

// 指数加权
const double alpha = 2. / (time_period + 1);
const double beta = 1. - alpha;
double corr = var_xy[0];
double vx = var_x[0];
double vy = var_y[0];
for (int i = 1; i < size; i++) {
vx = alpha * var_x[i] + beta * vx;
vy = alpha * var_y[i] + beta * vy;
corr = alpha * var_xy[i] + beta * corr;
ptr_result[i] = corr / (sqrt(vx) * sqrt(vy));
}
// 相关性
const int shift = int(log(0.05) / log(beta));
// 波动率
for (int i = 0; i < shift + 1; i++) {
ptr_result[i] = 0;
}

return result;
}

py::array_t<double> calc_hv_pk(
const py::array_t<double> &high,
const py::array_t<double> &low,
const int &time_period,
const double &factor) {
// 参数转换
py::buffer_info buf_high = high.request();
auto *ptr_high = static_cast<double *>(buf_high.ptr);
py::buffer_info buf_low = low.request();
auto *ptr_low = static_cast<double *>(buf_low.ptr);

// result
auto result = py::array_t<double>(buf_high.size);
py::buffer_info buffer_result = result.request();
auto *ptr_result = static_cast<double *>(buffer_result.ptr);

// calculate function
auto size = buf_high.size;
std::vector<double> var(size, 0.0);
const double const_num = 0.25 / log(2.);
for (int i = 0; i < size; i++) {
var[i] = const_num * std::pow(log(ptr_high[i] / ptr_low[i]), 2);
}

// 波动率
for (int i = 0; i < time_period; i++) {
ptr_result[i] = 0;
}
for (int i = time_period - 1; i < size; i++) {
double tv = 0.0;
for (int j = i - time_period + 1; j <= i; j++) {
tv += var[j];
}
ptr_result[i] = sqrt(tv / time_period) * factor;
}
return result;
}

py::array_t<double> calc_hv_rs(
const py::array_t<double> &open,
const py::array_t<double> &high,
const py::array_t<double> &low,
const py::array_t<double> &close,
const int &time_period,
const double &factor) {
// 参数转换
py::buffer_info buf_open = open.request();
auto *ptr_open = static_cast<double *>(buf_open.ptr);
py::buffer_info buf_high = high.request();
auto *ptr_high = static_cast<double *>(buf_high.ptr);
py::buffer_info buf_low = low.request();
auto *ptr_low = static_cast<double *>(buf_low.ptr);
py::buffer_info buf_close = close.request();
auto *ptr_close = static_cast<double *>(buf_close.ptr);

// result
auto result = py::array_t<double>(buf_high.size);
py::buffer_info buffer_result = result.request();
auto *ptr_result = static_cast<double *>(buffer_result.ptr);

// calculate function
auto size = buf_high.size;
std::vector<double> var(size, 0.0);
for (int i = 0; i < size; i++) {
double tmp = log(ptr_high[i] / ptr_close[i]) * log(ptr_high[i] / ptr_open[i]) +
log(ptr_low[i] / ptr_close[i]) * log(ptr_low[i] / ptr_open[i]);
var[i] = nan_to_num(tmp);
}

// 波动率
for (int i = 0; i < time_period; i++) {
ptr_result[i] = 0;
}

for (int i = time_period - 1; i < size; i++) {
double tv = 0.0;
for (int j = i - time_period + 1; j <= i; j++) {
tv += var[j];
}
ptr_result[i] = sqrt(tv / time_period) * factor;
}
return result;
}


py::array_t<double> calc_hv_gk(
const py::array_t<double> &open,
const py::array_t<double> &high,
const py::array_t<double> &low,
const py::array_t<double> &close,
const int &time_period,
const double &factor) {
// 参数转换
py::buffer_info buf_open = open.request();
auto *ptr_open = static_cast<double *>(buf_open.ptr);
py::buffer_info buf_high = high.request();
auto *ptr_high = static_cast<double *>(buf_high.ptr);
py::buffer_info buf_low = low.request();
auto *ptr_low = static_cast<double *>(buf_low.ptr);
py::buffer_info buf_close = close.request();
auto *ptr_close = static_cast<double *>(buf_close.ptr);

// result
auto result = py::array_t<double>(buf_high.size);
py::buffer_info buffer_result = result.request();
auto *ptr_result = static_cast<double *>(buffer_result.ptr);

// calculate function
auto size = buf_high.size;
std::vector<double> var(size, 0.0);
const double const_num = 2. * log(2.) - 1.;
for (int i = 0; i < size; i++) {
var[i] = 0.5 * std::pow(log(ptr_high[i] / ptr_low[i]), 2) -
const_num * std::pow(log(ptr_close[i] / ptr_open[i]), 2);
}

// 波动率
for (int i = 0; i < time_period; i++) {
ptr_result[i] = 0;
}

for (int i = time_period - 1; i < size; i++) {
double tv = 0.0;
for (int j = i - time_period + 1; j <= i; j++) {
tv += var[j];
}
ptr_result[i] = sqrt(tv / time_period) * factor;
}
return result;
}


py::array_t<double> calc_hv_gkyz(
const py::array_t<double> &open,
const py::array_t<double> &high,
const py::array_t<double> &low,
const py::array_t<double> &close,
const py::array_t<double> &pre_close,
const int &time_period,
const double &factor) {
// 参数转换
py::buffer_info buf_open = open.request();
auto *ptr_open = static_cast<double *>(buf_open.ptr);
py::buffer_info buf_high = high.request();
auto *ptr_high = static_cast<double *>(buf_high.ptr);
py::buffer_info buf_low = low.request();
auto *ptr_low = static_cast<double *>(buf_low.ptr);
py::buffer_info buf_close = close.request();
auto *ptr_close = static_cast<double *>(buf_close.ptr);
py::buffer_info buf_pre_close = pre_close.request();
auto *ptr_pre_close = static_cast<double *>(buf_pre_close.ptr);

// result
auto result = py::array_t<double>(buf_high.size);
py::buffer_info buffer_result = result.request();
auto *ptr_result = static_cast<double *>(buffer_result.ptr);

// calculate function
auto size = buf_high.size;
std::vector<double> var(size, 0.0);
const double const_num = 2. * log(2.) - 1.;
for (int i = 0; i < size; i++) {
var[i] = std::pow(log(ptr_open[i] / ptr_pre_close[i]), 2) + 0.5 * std::pow(log(ptr_high[i] / ptr_low[i]), 2) -
const_num * std::pow(log(ptr_close[i] / ptr_open[i]), 2);
}

// 波动率
for (int i = 0; i < time_period; i++) {
ptr_result[i] = 0;
}

for (int i = time_period - 1; i < size; i++) {
double tv = 0.0;
for (int j = i - time_period + 1; j <= i; j++) {
tv += var[j];
}
ptr_result[i] = sqrt(tv / time_period) * factor;
}
return result;
}