线性回归——螺纹钢(1)

用python做机器学习去解决交易问题的话,第一个模型就是简单的线性回归,难度上并不大,应用上看看效果。这次还是用聚宽网来做,这样就省得下数据来,缺点也明显,就是计算力真的有限,每次回测都得等1分钟。

假设一:螺纹钢走势和短期历史价格呈现线性相关

用螺纹钢1分钟走势进行回测,在知道过去5/10/30/60分钟前的价格,以及前1分钟价格的2/3/4次的前提之下,使用lasso做回归发现,方差和标准差是(9.8852876247738557, 3.1440877253622959),还是比较大,因为平均数3多一些,说明效果很一般。

代码如下

from jqdata import *
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
# import numpy as np
# import matplotlib.pyplot as plt
from sklearn.linear_model import Lasso,Ridge
from sklearn.model_selection import GridSearchCV
## 初始化函数,设定基准等等
def initialize(context):
    
    set_option('use_real_price', True)
    # 期货类每笔交易时的手续费是:买入时万分之0.23,卖出时万分之0.23,平今仓为万分之23
    set_order_cost(OrderCost(open_commission=0.000023, close_commission=0.000023,close_today_commission=0.0023), type='index_futures')
    # 设定保证金比例
    set_option('futures_margin_rate', 0.15)
    run_daily( find, 'every_bar')

def find(context):
    close=get_price('RB9999.XSGE', start_date='2018-08-01', end_date='2018-10-31', frequency='1m', fields=['close'])
    high=get_price('RB9999.XSGE', start_date='2018-08-01', end_date='2018-10-31', frequency='1m', fields=['high'])
    low=get_price('RB9999.XSGE', start_date='2018-08-01', end_date='2018-10-31', frequency='1m', fields=['low'])
    volume=get_price('RB9999.XSGE', start_date='2018-08-01', end_date='2018-10-31', frequency='1m', fields=['volume'])
    preclose=get_price('RB9999.XSGE', start_date='2018-08-01', end_date='2018-10-31', frequency='1m', fields=['pre_close'])
    
    
    # print(all)
    a=pd.concat([preclose,close,high,low,volume],axis=1)
    a['volatility']=a['close']-a['pre_close']
    
    ‘’‘加入特征,5/10/30/60分钟前的价格,以及前1分钟价格的2/3/4次方作为因子’‘’
    a['pre5']=a['close'].shift(5)
    a['pre10']=a['close'].shift(10)
    a['pre30']=a['close'].shift(30)
    a['pre60']=a['close'].shift(60)
    a['pre_close*2']=a['pre_close']**2
    a['pre_close*3']=a['pre_close']**3
    a['pre_close*4']=a['pre_close']**4
    
    ‘’‘
    计算螺纹钢正常波动率和方差
    # av=np.average(np.abs(a['volatility']))
    # u=(a['volatility']-av)**2
    # avu=np.average(u) 
    # sqavu=np.sqrt(avu)
    ’‘’
    
    x=a.iloc[60:,[0,4,6,7,8,9,10,11,12]]
    y=a['close'][60:]
    xtrain,xtest,ytrain,ytest=train_test_split(x,y,random_state=1)
    
    # lr=LinearRegression()
    # lr.fit(xtrain,ytrain)
    # yhat=lr.predict(xtest)
    
    ‘’‘使用lasso用网格搜索法,10折交叉验证,得到均方误差和标准差’‘’
    model1=Lasso()
    al=np.linspace(-2,3,100)
    modcv=GridSearchCV(model1,param_grid={'alpha':al},cv=10)
    modcv.fit(xtrain,ytrain)
    yhat=modcv.predict(xtest)
    
    
    mse=np.average((yhat-ytest)**2)
    
    mseq=np.sqrt(mse)
    print(mse,mseq)

假设2,螺纹价格和铁矿石/焦炭/焦煤线性相关

把螺纹钢价格作为标签,其他三个作为因子,建立线性回归,这个模型有两种可能,第一种就是只看波动率的绝对值,不看方向,第二种是二者都看。

建立线性回归模型,用的是lasso,其实用正常的线性回归也一样,反正也没有加高次项,代码如下

# 导入函数库
from jqdata import *
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
# import numpy as np
# import matplotlib.pyplot as plt

from sklearn.linear_model import Lasso,Ridge
from sklearn.model_selection import GridSearchCV
## 初始化函数,设定基准等等
def initialize(context):
    
    set_option('use_real_price', True)
    # 期货类每笔交易时的手续费是:买入时万分之0.23,卖出时万分之0.23,平今仓为万分之23
    set_order_cost(OrderCost(open_commission=0.000023, close_commission=0.000023,close_today_commission=0.0023), type='index_futures')
    # 设定保证金比例
    set_option('futures_margin_rate', 0.15)
    run_daily( find, 'every_bar')

def find(context):
    # pd.set_option('precision',2)
   
    close=get_price('RB9999.XSGE', start_date='2018-08-01', end_date='2018-10-31', frequency='1m', fields=['close'])
    # high=get_price('RB9999.XSGE', start_date='2018-08-01', end_date='2018-10-31', frequency='1m', fields=['high'])
    # low=get_price('RB9999.XSGE', start_date='2018-08-01', end_date='2018-10-31', frequency='1m', fields=['low'])
    # volume=get_price('RB9999.XSGE', start_date='2018-08-01', end_date='2018-10-31', frequency='1m', fields=['volume'])
    preclose=get_price('RB9999.XSGE', start_date='2018-08-01', end_date='2018-10-31', frequency='1m', fields=['pre_close'])
    
    jmclose=get_price('JM9999.XDCE', start_date='2018-08-01', end_date='2018-10-31', frequency='1m', fields=['close'])
    tksclose=get_price('I9999.XDCE', start_date='2018-08-01', end_date='2018-10-31', frequency='1m', fields=['close'])
    jtclose=get_price('J9999.XDCE', start_date='2018-08-01', end_date='2018-10-31', frequency='1m', fields=['close'])
    
    jmpre=get_price('JM9999.XDCE', start_date='2018-08-01', end_date='2018-10-31', frequency='1m', fields=['pre_close'])
    tkpre=get_price('I9999.XDCE', start_date='2018-08-01', end_date='2018-10-31', frequency='1m', fields=['pre_close'])
    jtpre=get_price('J9999.XDCE', start_date='2018-08-01', end_date='2018-10-31', frequency='1m', fields=['pre_close'])
    
    
    # 有价格波动的方向
    lwvo1=close['close']-preclose['pre_close']
    jmvo1=jmclose['close']-jmpre['pre_close']
    tkvo1=tksclose['close']-tkpre['pre_close']
    jtvo1=jtclose['close']-jtpre['pre_close']
    
    # 不考虑方向只考虑波动幅度
    lwvo=np.abs(lwvo1)
    jmvo=np.abs(jmvo1)
    tkvo=np.abs(tkvo1)
    jtvo=np.abs(jtvo1)
    
    # # 要是归一化处理呢
    # lwvo=lwvo2/lwvo1
    # jmvo=jmvo2/jmvo1
    # tkvo=tkvo2/tkvo1
    # jtvo=jtvo2/jtvo1
    
    
    a=pd.concat([lwvo1,tkvo1,jtvo1,jmvo1],axis=1)
    a.columns=['lw','tk','jt','jm']
    a.dropna(inplace=True)
    
    # a['volatility']=a['close']-a['pre_close']
    # a['jmvo']=a['jmclose']-a['jmpre']
    # a['tkvo']=a['tksclose']-a['tkpre']
    # a['jtvo']=a['jtclose']-a['jtpre']
    
    
    
    # a['pre5']=a['close'].shift(5)
    # a['pre10']=a['close'].shift(10)
    # a['pre30']=a['close'].shift(30)
    # a['pre60']=a['close'].shift(60)
    # a['pre_close*2']=a['pre_close']**2
    # a['pre_close*3']=a['pre_close']**3
    # a['pre_close*4']=a['pre_close']**4
    '''
    # av=np.average(np.abs(a['volatility']))
    # u=(a['volatility']-av)**2
    # avu=np.average(u) 
    # sqavu=np.sqrt(avu)
    
    '''
    x=a.iloc[:,[1,2,3]]
    y=a['lw']
    xtrain,xtest,ytrain,ytest=train_test_split(x,y,random_state=1)
    
    lrg=LinearRegression()
    lrg.fit(xtrain,ytrain)
    yhat=lr.predict(xtest)
    
    
    # model1=Ridge()
    # al=np.linspace(-2,3,100)
    # modcv=GridSearchCV(model1,param_grid={'alpha':al},cv=10)
    # modcv.fit(xtrain,ytrain)
    # yhat=modcv.predict(xtest)
    
    # 随后计算r2值,算法有3种,一种直接调公式,注意传入的是xtest,ytest使用测试集来评价模型好坏
    r2=r2_score(ytest,yhat)
    print('方法1的r2是%s'%r2)
    
    # # 方法二就是直接算r2
    # ymean=np.mean(ytest)
    # tss=np.mean((ytest-ymean)**2)
    # rss=np.mean((ytest-yhat)**2)
    # r2_2=1-rss/tss
    # print('方法2的r2是%s'%r2_2)
    
    # # 方法3是用lr.score(),传入xtest,ytest
    # r2_3=lr.score(xtest,ytest)
    # print('方法3的r2是%s'%r2_3)
    
    corrcoef=lr.coef_
    print('回归方程系数%s'%corrcoef)

    # # print(modcv.best_params_)
    
    
    # # print(x)
    
    # # print(a)
    
    # # print(av)
    # # print(u)
    # # print(avu)
    # # print(sqavu)
    
    
    
    
    '''
    # plt.plot(u)
    # plt.plot()
    # plt.show()
    '''
    

第一种只考虑波动率,不看方向,结论是r2是0.19。

第二种二者都要的话,结论是r2是0.39,预测值和实际值的相关系数为0.625

结论很明显,黑色系品种之间同涨同跌的情况还是比较多,这才使得方向成为提高准确率的一个因素,但是就算有所提高,R2整体还是太低,基本没法用。

如果正常用线性回归,得出的结论基本一致。但是铁矿/焦炭/焦煤的线性回归系数和截距分别是:

array([ 1.10955314, 0.62495403, 0.18057547]), -0.017346562494255414),明显可以看出来还是铁矿相关性更强,这个结论不用算也知道,焦煤相关性很低,不用线性回归也知道。

 

 


 

注意事项:

1.因为螺纹钢和铁矿石/焦煤/焦炭的交易时间不一样,主要是夜盘,所以数据在合并之后,会螺纹钢会出现大量空值——巨坑爹,这个小问题看起来不大,但是很容易忽略,因为在抓数据的时候,是没有空值的,而且很少会想到收盘时间不同造成的空值。而且聚宽网也很搞笑,报错的时候只报错了一个“keyerror 0”,完全不明白是在说什么。

2.计算r2的时候,一共有3种方法都可以,方法2就是公式,1,3两种方法就是调用简单函数,但是不同的地方是参数已经不是每种模型都有score函数,所以尽量使用r2_score;

还有就是rss在计算的时候,用的预测的yhat和ytest,而且要计算ytest的tss和rss,而不是用全样本去计算,这个地方很容出错。


问题:

1.这个结论是建立在1分钟图上,那么能否推广呢?

2.既然铁矿石相关性这么强,很明显可以套利了,但是方向性存在问题,应该怎么解决?还是根本不用解决靠止损硬套?

3.套利的话,很明显靠人工有点慢,这回必须靠软件了,不知道聚宽行不行,不行的话,还得自己建立模型,基础函数搭建实在太浪费时间

4.1分钟用套利的话,成本必然高的吓人,不知道夏普比率有多少

4.这个模型用的是1分钟图,如果换一下时间周期会不会更好?

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值