Python量化交易学习——Part10:量化交易中常用的回归分析方法——线性回归

在前面我们介绍了多因子策略,就是根据各个因子的大小对股票进行打分,然后按照一定的权重加权得到一个总分,最后根据总分再对股票进行筛选。这种方法的优点是相对比较稳健,不容易受到极端值的影响。但是多因子策略需要对各个因子的权重做一个主观的判定,这也是多因子策略在实际模型评价过程中比较困难和需要模型求取的关键所在。
本节将介绍一种在量化分析中常用的方法——回归分析,它可以用在股价的回归分析上,同时还可以对基本面数据进行分析。
##回归分析基础
回归分析预测法是在分析市场现象自变量和因变量之间相对关系的基础上,建立变量之间的回归方程,并将回归方程作为预测模型,根据自变量的变化来预测因变量。在对股市的发展状况进行预测时,如果我们能找到一种或多种相关性比较大的影响因子,就可以采用回归分析方法进行预测。
回归分析方法有多种类型,根据自变量的个数进行分类可分为一元回归分析和多元回归分析,在一元回归分析预测法中,自变量只有1个,而在多元回归分析法中,自变量有两个以上。根据自变量和因变量之间的关系不同,可以分为线性回归分析和非线性回归分析。

线性回归分析

一元线性回归

回归分析中只涉及两个变量的,成为一元回归分析。一元回归的主要任务时根据两个相关变量中的一个与估计另外一个变量,被估计的变量成为因变量(Y),用于估计的变量成为自变量(x),回归分析就是要找到一个数学模型Y=f(x),使得从X可以估计Y。
当Y=f(x)的形式是一个直线方程的时候,成为一元线性回归。
在一元线性回归中,Y和x存在的关系可以表示为:
Y ( z ) = a + b x + ϵ Y(z) = a+bx+\epsilon Y(z)=a+bx+ϵ
其中a,b是常数,随机扰动项 ϵ \epsilon ϵ是随机扰动项,一般便于求解,一般将上述公式表示为:
Y ( z ) = a + b x Y(z) = a+bx Y(z)=a+bx
简单举个例子:

import random
import statsmodels.api as sm
import numpy as np
import matplotlib.pyplot as plt

x1=np.asarray([1,2,3,4,5,6,7,8,9])  # 假设自变量
y=10+4*x1+random.random()  #假设的因变量+随机数

#进行回归分析
x = sm.add_constant(x1) #增加常数项1,将x变为二维数组
model = sm.OLS(y,x) #进行一元线性回归分析
results = model.fit() #获取参数值
print(results.params)
plt.scatter(x1, y)  #绘制原始数据散点图
plt.plot(x1,results.params[0]+results.params[1]*x1) #绘制求取的回归曲线
plt.show()

我们假设了一组数据x和y,求取最终得到的回归曲线,并通过图形表示出来,最终求得的参数为:
[10.25540119 4. ]
可以看到,与给的的函数关系式基本相同,对比图像结果如下:
在这里插入图片描述

多元线性回归

除了一元线性回归外,研究一个因变量与两个或两个以上自变量的回归,称为多元线性回归,是反映一种现象或事物的数量依多种现象或事物的数量的变动而相应地变动的规律,建立多个变量之间线性或非线性数学模型关系式的统计方法。
由于多元变量自变量个数多,计算起来比较麻烦,所以我们常用一些统计学的方法进行求解,比如最小二乘法。最小二乘法(LS算法)是一种数学优化方法,同时也是一种回归分析的求解方法,他通过最小化误差平方和的方法寻求与数据最佳匹配的函数,关于算法的详细原理,我这里不做过多的介绍了,在实际的使用过程中,python内集成了最小二乘法的数据库,我们通过下面这个例子来了解。

import statsmodels.api as sm
import numpy as np
import matplotlib.pyplot as plt
import random
#假设一组数据
x1 = np.asarray([1,2,3,4,5,6,7,8,9,4])
x2 = np.asarray([3,2,1,5,4,6,7,5,3,8])
y = 10+4*x1+5*x2+random.random()

#进行多元线性回归求解
x3 =np.column_stack((x1,x2)) #将x1和x2合并生成新的2维数组,其中x1作为第一列,x2作为第二列;
x = sm.add_constant(x3)
model = sm.OLS(y,x)
results =model.fit()
print(results.params)

返回参数值如下:[10.38930839 4. 5. ],与给的的参数值基本一致。
除了上述的方法外,还有一些其他的计算线性回归的方法,比如梯度下降法或使用python的TensorFlow工具包进行回归分析。这些我们在后续使用的过程中再具体进行讲解,我原来读过一本李航的《统计学习方法》里边有详细的讲解,大家有兴趣可以一起学习一下。

线性回归分析实战

对于某只股票来说,在短时间内的开盘价和收盘价会有一定的关系,我们这里通过回归分析建立开盘价与收盘价之间的线性模型,通过开票价对收盘价进行预测,策略如下:
(1)用前7日的开票价和收盘价做线性拟合
(2)根据当日的开票价与预测当日的收盘价
(3)如果当日开票价比当日预测的收盘价低就买入,否则卖出。
下面我们对策略进行实现,在实现策略之前,最好通过一个个程序段对主要策略进行实现。在实际的工作中也是如此,首先完善主策略,之后将其包装成只需要传递参数的函数:

import statsmodels.api as sm
import numpy as np
import matplotlib.pyplot as plt
import random
from gm.api import *
import datetime
set_token('自己的token码')
day_time, hour_and_mins = str(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')).split(" ")
symbol = "SZSE.002415"
data = history_n(symbol=symbol,frequency="1d",end_time=day_time,count=7,fields="close,open",df=True)
close = data["close"].values
open = data["open"].values
open = sm.add_constant(open)
model = sm.OLS(close,open) #前面为因变量,后面为自变量
result= model.fit()
bias,weight = (result.params) #前面为截距,后面为斜率
print(bias,weight)

运行后得到的结果如下:

16.748063796757435 0.47958454487210744

由此我们便得到了如下关系式:
c l o s e = b i a s + o p e n ∗ w e i g h t close= bias+open*weight close=bias+openweight
之后我们将上面的策略包装成为函数,可以看出,我们需要传递的主要有两个参数,第一个是股票代码symbol,另外一个是时间,剩下的都是内部计算得到的,当然,如果我们想改变拟合数据的数量,也可以把count值也作为参数传入,这样我们就可以用不同周期的开票价和收盘价进行拟合,以获得更加准确的数据,整合后的函数代码如下:

# coding=utf-8
from __future__ import print_function, absolute_import

import gm.api
from gm.api import *
import statsmodels.api as sm


def init(context):
    # 每天14:50 定时执行algo任务,
    # algo执行定时任务函数,只能传context参数
    # date_rule执行频率,目前暂时支持1d、1w、1m,其中1w、1m仅用于回测,实时模式1d以上的频率,需要在algo判断日期
    # time_rule执行时间, 注意多个定时任务设置同一个时间点,前面的定时任务会被后面的覆盖
    schedule(schedule_func=algo, date_rule='1d', time_rule='09:31:00')
    context.symbol = "SZSE.002415"
    context.count = 7


def get_predict_close(symbol,now,count = 7):
    last_day= gm.api.get_previous_trading_date("SHSE",now)
    data = history_n(symbol=symbol, frequency="1d", end_time=last_day, count=count, fields="close,open", df=True)
    close = data["close"].values
    open = data["open"].values
    open = sm.add_constant(open)
    model = sm.OLS(close, open)  # 前面为因变量,后面为自变量
    result = model.fit()
    bias, weight = (result.params)  # 前面为截距,后面为斜率
    # print(bias, weight)
    return (bias,weight)

def algo(context):
    bias,weight = get_predict_close(context.symbol,context.now,context.count)
    data = history_n(context.symbol,frequency="1d",end_time=context.now,count=1,fields="open",df=True)
    pre_close = bias+weight*data["open"].values[0]
    if pre_close >data["open"].values[0]:
        order_target_percent(symbol=context.symbol,percent=1,order_type=OrderType_Market,position_side=PositionSide_Long)
    else:
        order_target_percent(symbol=context.symbol,percent=0,order_type=OrderType_Market,position_side=PositionSide_Long)


# 查看最终的回测结果
def on_backtest_finished(context, indicator):
    print(indicator)


if __name__ == '__main__':
    '''
        strategy_id策略ID, 由系统生成
        filename文件名, 请与本文件名保持一致
        mode运行模式, 实时模式:MODE_LIVE回测模式:MODE_BACKTEST
        token绑定计算机的ID, 可在系统设置-密钥管理中生成
        backtest_start_time回测开始时间
        backtest_end_time回测结束时间
        backtest_adjust股票复权方式, 不复权:ADJUST_NONE前复权:ADJUST_PREV后复权:ADJUST_POST
        backtest_initial_cash回测初始资金
        backtest_commission_ratio回测佣金比例
        backtest_slippage_ratio回测滑点比例
        backtest_match_mode市价撮合模式,以下一tick/bar开盘价撮合:0,以当前tick/bar收盘价撮合:1
    '''
    run(strategy_id='自己的策略ID',
        filename='OLS_test.py',
        mode=MODE_BACKTEST,
        token='自己的token码',
        backtest_start_time='2022-11-01 09:30:00',
        backtest_end_time='2024-06-20 15:00:00',
        backtest_adjust=ADJUST_PREV,
        backtest_initial_cash=10000,
        backtest_commission_ratio=0.0001,
        backtest_slippage_ratio=0.0001,
        backtest_match_mode=1)


回测结果如下:
在这里插入图片描述
从回测结果看出,采用这个策略进行购买并不能取得超额收益,这到底是为什么,难道开盘价和收盘价之间不存在相对关系么?
我们通过相关系数分析一下,看一下开票价和收盘价的相关系数如何

import statsmodels.api as sm
import numpy as np
import matplotlib.pyplot as plt
import random
from gm.api import *
import datetime
set_token('a7f3404da7e8c9a3aea631b5ae0c893b1d57161d')
day_time, hour_and_mins = str(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')).split(" ")
symbol = "SZSE.002415"
data = history_n(symbol=symbol,frequency="1d",end_time=day_time,count=7,fields="close,open",df=True)
close = data["close"].values
open = data["open"].values
corr =np.corrcoef(close,open)
print(corr[0][1])

返回值如下:

0.4964730950221215

从返回值看,开票价和收盘价之间的相关系数只有0.496,不存在强的正相关关系,难道真的如此么,我们把数据量调大,count调到100再尝试一次,返回值为

0.9072392458175943

这么看开票价和收盘价确实存在强的正相关性,只是我们当初数据量不够的原因,但是这个数据又对我们预测毫无帮助,因为长期开盘价和收盘价必然存在相关性,股价每天波动已经被限制到了20%以内。所以这并不是一个好的策略。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值