理论背景:
我们知道,股价的走势符合几何布朗运动(《应用随机过程》),则有结论(具体过程可以看随机过程,布朗运动,伊藤引理那部分自己推),
$$\Delta S = \mu * \Delta t + \sigma *S*\varepsilon*\sqrt{\Delta t},\varepsilon \sim N(0,1)$$

其中S表示对数价格,μ表示单位时间内的期望对数收益率,σ表示单位时间内的股票价格的对数波动率。

公式的意义为:股价 $\Delta t$ 时间内的对数收益率等于收益率的连续复利加随机波动,而ε控制的是你所定义的波动程度分位数的系数,就像正态分布下正负一倍标准差包含68.3%的数据一样,系数越高则会包含更多波动。

但,研报中验证得出收益率符合t分布而非正态分布,因此有

$$\Delta S = \mu * \Delta t + \sigma *S*\varepsilon*\sqrt{\Delta t},\varepsilon \sim t分布$$

t分布是一个小样本估计正态分布的分布,其ε除了需要分位数之外还需要自由度,且自由度大于100多的时候和正态分布的差不多。

这个策略就是画出一条线,股价上穿这条线意味着牛市开始,下穿这条线意味这牛市结束。

平常没行情的股价都是在一定框框内(分位数)波动,如果价格的波动比我们预想的波动(分位数)还大,那么就意味着不是昙花一现的行情而是趋势。

公式:
QQ截图20200912212936.png

公式一是按照研报中写的,公式二到五是我改进的。

公式的意思是,今天的牛线是用 $\Delta t$ 天之前的收盘价和计算出的μ,σ,ε计算得到。这意味最近 $\Delta t$ 天的数据我们是一点都没用到的(计算μ,σ是可以用到 前$\Delta t$ 天的数据的,但是在上证综指跑出来的结果差别并没有太大)。

因为计算ε需要自由度和分位数,分位数是个超参需要调,自由度的处理方法如下:我们要用 $\Delta t$ 天之前的对数收益率的期望来做连续复利,那么用了多少个 $\Delta t$ 天的数据,自由度就应该是多少。

记自由度为N, $\Delta t$ 为T,则μ应该求的是N*T天的均值,然后再向前推T天(推不推都行,跑出来的结果差别没有太大);σ同理;ε用N和分位数来求。(这个逻辑是git作者的,我不是很苟同,但是这么做也还行吧)。

核心代码

@jit
def cal_bull(close,mu,sigma,T,epsilon):
    return close * np.exp(T*mu + np.sqrt(T)*sigma*epsilon)

epsilon = scipy.stats.t.ppf(1-alpha/2,n) #ε
ripesd['logreturn'] = ripesd['close'].map(lambda x: np.log(x)).diff() #对数收益率
ripesd['mu'] = ripesd['logreturn'].rolling(n*T).mean() #μ
ripesd['sigma'] = ripesd['logreturn'].rolling(n*T).std() #σ
ripesd['close_-t'] = ripesd['close'].shift(T)
ripesd['bull'] = ripesd.apply(lambda x: cal_bull(x['close_-t'],x['mu'],x['sigma'],T,epsilon),axis=1)
ripesd = ripesd.dropna()

#上穿下穿买卖点
ripesd['buy'] = ripesd.apply(lambda x: True if x['close'] >= x['bull'] and x['close_-1'] <= x['bull_-1'] else False,axis=1)
ripesd['sell'] = ripesd.apply(lambda x: True if x['close'] <= x['bull'] and x['close_-1'] >= x['bull_-1'] else False,axis=1)


@jit
def cal_bull_3ver(close,mu,sigma,T,epsilon):
    return close * ((1+mu)**T + np.sqrt(T)*sigma*epsilon)

epsilon = scipy.stats.t.ppf(1-alpha/2,n)
ripesd['return'] = ripesd['close'].pct_change() #算术收益率
ripesd['mu'] = ripesd['return'].rolling(n*T).mean()
ripesd['sigma'] = ripesd['return'].rolling(n*T).std()
ripesd['close_-t'] = ripesd['close'].shift(T)
ripesd['bull'] = ripesd.apply(lambda x: cal_bull_3ver(x['close_-t'],x['mu'],x['sigma'],T,epsilon),axis=1)

原文:https://zhuanlan.zhihu.com/p/36437315