1、期货量化策略理论基础
1865年美国芝加哥诞生了最早的期货合约,从此期货变成为重要的的衍生产品。期货可以开展包括套期保值、套利以及投机等不同动机的交易,本关卡将一一列举说明。
在那之前,我们了解一下期货的基本知识(国内)。
1.1期货的交易品种
期货交易品种包括:
1.农产品期货,品种包括玉米、大豆、豆油、棕榈油、棉花、白糖等,
2.能源化工,包括原油、燃油、动力煤、PTA等。
3.工业金属期货,包括:铜、铝、锌、铅、锌、镍、锡等。
4.金融期货,如上证50、沪深300、中证500股指期货、股债期货等。
5.贵金属期货,如黄金、白银等。
国内有上海期货交易所、大连商品交易所、郑州商品交易所、中金所、广期所等期货交易所。
下面列举各期货交易所交易品种,以供参考:
1.上海期货交易所:
铜
铜(BC)
铝
锌
铅
镍
锡
氧化铝
黄金
白银
螺纹钢
线材
热轧卷板
不锈钢
原油
低硫燃料油
燃料油
石油沥青
丁二烯橡胶
天然橡胶
20号胶
纸浆
SCFIS欧线
2.郑州商品交易所:
苹果AP
棉花CF
红枣CJ
棉纱CY
玻璃FG
粳稻JR
晚籼LR
甲醇MA
菜油OI
短纤PF
花生PK
普麦PM
对二甲苯PX
早籼RI
菜粕RM
菜籽RS
纯碱SA
硅铁SF
烧碱SH
锰硅SM
白糖SR
PTATA
尿素UR
强麦WH
煤ZC
3.大连商品交易所
豆一
豆二
胶合板
玉米
玉米淀粉
苯乙烯
乙二醇
纤维板
铁矿石
焦炭
鸡蛋
焦煤
聚乙烯
生猪
豆粕
棕榈油
液化石油气
聚丙烯
粳米
聚氯乙烯
豆油
4.中金所:
上证50、沪深300、中证500、中证1000股指期货
2/5/10/30国债期货
5.广期所
碳酸锂、工业硅
1.2期货基本交易规则
1.期货交易实行T+0交易制度,即当日买进,可当日卖出,平仓。期货交易有涨跌幅的限制,不同期货品种涨停跌停的幅度限制不同。此外期货可以做多和做空,也就是俗称的买涨或买跌。
2.期货每天有三个交易时段,分别是上午盘,下午盘和夜盘。每周一到周五(节假日除外),上午盘9:00~11:30(10:15-10:30休息),下午盘13:30~15:00,晚盘是21:00到次日凌晨。
3.保证金制度。期货交易属于杠杆交易,简单理解就是,交易者可以用比较小的资金(当做保证金),买原价的期货合约。例如买1手白糖期货,10吨/手,每吨报价6300元,那么1手白糖期货合约的价格就是6300*10=63000元,但因为保证金交易制度,交易者可以用比较少的资金7%(当做保证金)来买这个期货合约,也就是4410元。但要注意,盈亏都是按照原价来计算,所以期货交易属于自带杠杆的交易。因此存在强制平仓和止损机制:当交易者账户的净值低于一定水平时,交易所会要求其追加保证金或强制平仓部分或全部持仓,以防止亏损进一步扩大。
4.期货合约存续期有限,这一点与股票完全不同,每一种期货商品都有好几个同时在运行的合约,分为远期合约和近期合约,以白糖为例,就有2403/2405/2407/2409/2411/2501几个不同月份的期货合约。
5.期货到期交割规则,期货交易的是期货合约,期货合约有最后交易日,个人交易者在合约最后交易日前必须平仓,企业交易者如有交割资质,可继续持仓到商品交割日,进行实物交割。一般情况下个人都是投机交易,在到期前平仓换月即可。
1.3影响期货价格变动的因素
影响期货价格变动的因素有很多,以下是一些主要的因素:
供求关系:期货市场的价格变动受市场供求关系的影响。当供应大于需求时,期货价格通常会下跌;相反,当需求大于供应时,期货价格会上涨。供求关系受到多种因素的影响,包括结转库存量、产量、气候、经济状况、替代品的供求状况等。
经济周期:经济周期对期货价格有中长期的影响。在经济周期的各个阶段,市场需求、生产活动、原材料价格等都会发生变化,从而影响期货价格。
政府政策:政府制定的政策和措施,如财政政策、产业政策、贸易政策等,都会对期货价格产生影响。这些政策可能直接作用于市场,也可能通过影响市场参与者的预期来影响价格。
政治因素:政治局势的变化,如战争、政变、国际冲突等,都可能对期货价格产生影响。这些事件可能导致市场供应中断或需求变化,从而引发价格波动。
自然因素:气候和天气条件,如干旱、洪水、飓风等自然灾害,都可能对农产品等期货价格产生影响。这些事件可能影响作物的生长和产量,从而引发价格波动。
季节性因素:许多期货商品,尤其是农产品,具有明显的季节性。价格可能会随着季节的变化而波动,例如收获季节可能会导致供应增加,从而价格下降。
市场心理:交易者对市场的信心程度也会影响期货价格。当市场参与者对某个商品看好时,即使没有任何利好因素,该商品价格也可能会上涨。相反,当市场参与者对某个商品看淡时,即使没有任何利淡消息,该商品价格也可能会下跌。
金融货币因素:金融货币因素对期货价格的影响主要表现在预期年化利率和汇率两个方面。预期年化利率的调整可能会影响投资者的投资决策,从而影响期货价格。而汇率的变化则可能影响进口和出口的成本,从而影响期货价格。
咋一看是不是很复杂,实际上确实很复杂,期货市场的难度远比股票要大,毕竟选一家好公司的难度比判断商品价格升降要简单的多。
所以我们要引入量化的思维/方法来辅助我们做期货投机套保。
2、期货的套期保值策略
套期保值是期货交易中的一种风险对冲工具,其目的是通过期货合约的买卖,在期货市场上实现对冲,从而使期货价格的变动与现货价格的变动相互抵消,以减少风险。
说简单点,套期保值操作中,如果你有现货,那就卖出期货合约,而如果你已经做空现货,则买入期货合约。
2.1案例说明
我们下面将结合案例进行讲解。
假设你现在为某基金经理,所管理的基金持有沪深300指数ETF,持有5000万份,由于担心沪深300指数未来出现大幅度下跌而影响基金收益,因此你需要采取必要的保值措施,可采用沪深300指数期货进行套期保值。
需要做到以下几点:
1.以2024年第一个交易日240102为时间节点,找到沪深300指数收盘价点位、对应etf收盘价。
2.选择的期货合约为沪深300指数期货IF2406,找到同期收盘价。合约保证金为12%,最小变动单位0.2,合约乘数300。
3.暂不考虑交易费用、流动性问题。
2.2策略研究/构建
套期保值策略我们首先要搞明白的就是套期保值比例。是用于套期保值的期货合约头寸与被套期保值的资产头寸的比例。
用一个公式来说明就是:套期保值比例=用于套期保值的期货合约头寸/被套期保值的资产头寸
理论上,套期保值比例为1,达到完全套保,此时期货合约头寸与被套期保值的资产头寸完全相等,不存在风险敞口。但实际上,由于期货合约与资产的价差不可能完全相等,因此套期保值比例很难达到1。
#导入相关库,为了避免重复,这里一次性导入本内容所需要的所有库
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import akshare as ak
import warnings
import backtrader as bt
import datetime
import statsmodels.api as sm
warnings.filterwarnings('ignore')
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
#我们引入实际数据
hs300_etf=ak.fund_etf_hist_em(symbol="510300", period="daily", start_date="20230101", end_date="20240229", adjust="")#获取沪深300etf数据
hs300_zs= ak.stock_zh_index_daily_em(symbol="sh000300",start_date="20230101",end_date="20240229")#获取沪深300指数行情
hs300_IF2406=ak.futures_zh_daily_sina(symbol="IF2406")#获取
hs300_etf.set_index(pd.to_datetime(hs300_etf.日期),inplace=True)
hs300_zs.set_index(pd.to_datetime(hs300_zs.date),inplace=True)
hs300_IF2406.set_index(pd.to_datetime(hs300_IF2406.date),inplace=True)
hs300_IF2406=hs300_IF2406['2024-01':'2024-02']
# 创建共享x轴的子图
fig, (ax1, ax2) = plt.subplots(2,figsize=(10,6),dpi=100,sharex=True)
ax1.plot(hs300_zs['2024'].index,hs300_zs['2024'].close,'r-',label = "沪深300指数")
ax1.plot(hs300_IF2406['2024'].index,hs300_IF2406['2024'].close,'b-',label = "股指期货IF2406")
ax1.legend(loc='upper right')
ax2.plot(hs300_etf['2024'].index, hs300_etf['2024'].收盘, 'g-', label = "沪深300ETF")
ax2.legend(loc='upper right')
# 显示图形
plt.show()
#我们以上述案例为模版,计算下需要套期保值比例为1时,我们需要购买多少手对应期货合约。
#下面开始上代码
def hedge_ratio(spot_price,num,future_price,hedge_ratio_num):
'''spot_price:现货价格
num:现货数量
future_price:期货价格
hedge_ratio_num:套利保值比例
'''
spot_price_value=spot_price*num*hedge_ratio_num
hedge_ratio_=spot_price_value/(future_price*300)
print('套期比例为:%s时,对应期货需购买%s手'%(hedge_ratio_num,hedge_ratio_))
hedge_ratio(hs300_etf['2024'].收盘[0],50000000,hs300_IF2406['2024'].close[0],1)
上述为完全套保时理论持有的期货数量,我们模拟此后的损益情况.
2.3策略回测模拟
#接下来模拟套保后,整个投资组合市值变化(以240102日为基准),损益情况。
def Back_test_calculation():#封装一下变量
M=300#合约乘数
N=169#空头合约数量
G=hs300_IF2406['2024'].close[0]#估值开仓价格
P=0.12#保证金比例
HS=hs300_etf['2024'].收盘[0]#第一个交易日300etf收盘价
hs300etf_IF2406_value=pd.DataFrame()
#计算etf市值变动及盈亏情况
hs300etf_IF2406_value['沪深300etf市值']=hs300_etf['2024'].收盘*50000000#沪深300etf市值
hs300etf_IF2406_value['etf盈亏情况']=(hs300_etf['2024'].收盘-HS)*50000000#etf盈亏情况
#计算股指数据
hs300etf_IF2406_value['股指市值']=hs300_IF2406['2024'].close*M*N#股指市值
hs300etf_IF2406_value['股指盈亏情况']=-(hs300_IF2406['2024'].close-G)*M*N#股指盈亏情况
hs300etf_IF2406_value['动用保证金']=hs300_IF2406['2024'].close*M*N*P#动用保证金
#计算套保组合市值
hs300etf_IF2406_value['套保组合总市值']=hs300etf_IF2406_value.沪深300etf市值+hs300etf_IF2406_value.股指市值
#计算总盈亏金额
hs300etf_IF2406_value['组合总盈亏金额']=hs300etf_IF2406_value.etf盈亏情况+hs300etf_IF2406_value.股指盈亏情况
return hs300etf_IF2406_value
hs300etf_IF2406_value=Back_test_calculation()
hs300etf_IF2406_value
#可视化看看
fig ,axex= plt.subplots(2,1,figsize=(16,8),dpi=100) # 创建图
ax1,ax2=axex.flatten() # 创建子图
ax1.plot(hs300etf_IF2406_value.index,hs300etf_IF2406_value.沪深300etf市值,'r-',label = "沪深300etf市值")
ax1.plot(hs300etf_IF2406_value.index,hs300etf_IF2406_value.股指市值,'g-',label = "股指市值")
ax1.legend(loc='upper right')
ax2.plot(hs300etf_IF2406_value.index,hs300etf_IF2406_value.组合总盈亏金额/hs300etf_IF2406_value['2024'].套保组合总市值[0],'b-',label = "组合总盈亏比例")
ax2.legend(loc='upper right')
# 显示图形
plt.show()
2.4本节小结:
从结果来看,这次套保行为是比较成功的,若不套保,沪深300etf最大亏损接近6%,远远高于套保后的1%波动,甚至在etf下跌期间,套保组合收益反而为正。
当然你甚至可以在2月初的时候平仓空单股指,保留etf,那在2月至今的市场反弹中,就可以获得正收益了!
在实际操作中,套保策略的构建需要考虑更多细节,例如我们持有的是股票,该如何套保?这就需要我们计算个股与指数的相关系,拟合权重,然后构建套保组合。
我们上述案例是简化版本,目的就是让初学者了解套保的基本思路,如果达到这个效果就可以了,毕竟学习要一步步来。
2.5本节动手题
期货的保证金交易是一大特点,上述套保交易中也用到了保证金交易。
假设基金账户中初始保证金为2500万元,请你计算此区间风险度,对应公式为:风险度=实时已占用保证金/保证金总额
一般情况下:
当风险度高于85%为预警线,100%时则为追保线,达到100%就需要追加保证金,请你判断过去两个月的套保策略中,是否触发了预警线和追保线?
3、期货的套利策略
套利,即利用价格差进行套利。期货交易策略千千万,套利策略占一半。
期货套利策略是利用期货市场上价差进行套利的交易行为。根据具体的合约选择与策略构建,期货套利策略通常可以分为跨期套利、跨品种套利、跨市场套利三类策略。
跨期套利策略:这是同一市场中相同品种的不同月份期货合约之间的套利交易。
跨市场套利策略:这是不同市场内相同品种或高相关性品种期货合约之间的套利交易。
跨品种套利策略:这是高相关性品种间同一到期月份期货合约之间的套利交易。
这些都不是重点,重点是任何套利策略的构建不一定完全依托经济理论,或者市场理论,但大概率会依托于基础的统计研究。
3.1理论基础
套利策略的理论基础主要基于一价定律。
一价定律指的是在竞争市场上,如果两项资产是等同的,那么它们将倾向于拥有相同的市场价格。这个定律之所以成立,是因为套利者的存在。套利者会寻找等同资产的价格差异,卖出价格高的资产,买入价格低的资产,从而使得等同资产价格收敛。
基于这个原理,套利策略通过同时交易单种或多种金融产品以从价格差异中获利。这可以通过多种方式完成,例如在不同市场买卖相同证券(空间套利),商品的现货价格和期货合约同时买卖,买入现货,卖出期货等等。
需要注意的是,虽然套利策略被视为风险较低的交易手段,但仍然受到市场流动性、交易成本、监管限制等因素的影响。因此,进行套利交易需要投资者具备良好的市场分析和操作技巧,同时还需要及时把握市场变化、控制风险、选择合适的交易时机。
3.2跨品种套利策略的构建
理论有点复杂是吗?看不进去没关系,理论就是理论,我们先实操说。本节我们以跨品种套利为案例,详细讲解跨品种套利构建的全过程。
3.2.1品种选择及数据获取
国内国际期货品种太多了,我们不可能全部都去做,而套利说白了就是利用他们之间的价格高相关性进行的投机活动。所以我们第一步首要做的就是找到两个或多个具有相关性的期货品种。
例如豆油、菜籽油、棕榈油可以做为一个组合,黄金、白银也可以作为一个套利组合,螺纹钢/热卷也是一个组合。我们接下来用螺纹钢/热卷组合为例,讲解如何构建跨品种套利策略。
#获取螺纹钢、热卷期货合约价格。
RB_data= ak.futures_main_sina(symbol="RB0", start_date="20180101", end_date="20240229")
HC_data= ak.futures_main_sina(symbol="HC0", start_date="20180101", end_date="20240229")
RB_data.set_index(pd.to_datetime(RB_data['日期']), inplace=True)
HC_data.set_index(pd.to_datetime(HC_data['日期']), inplace=True)
%matplotlib inline
#先可视化一下走势
fig ,ax1= plt.subplots(figsize=(16,8),dpi=100) # 创建图
ax1.plot(RB_data.index,RB_data.收盘价,'r-',label = "螺纹钢期货收盘价")
ax1.plot(HC_data.index,HC_data.收盘价,'g-',label = "热卷期货收盘价")
ax1.legend(loc='upper right')
# 显示图形
plt.show()
RB_data
#新建一个dataframe,储存数据
RB_HC_data=pd.DataFrame()
RB_HC_data['螺纹钢收盘价']=RB_data.收盘价
RB_HC_data['热卷收盘价']=HC_data.收盘价
RB_HC_data['价差']=RB_data.收盘价-HC_data.收盘价
RB_HC_data.iloc[:,:2].corr()#相关性
这里从走势及相关系数就可以看出,螺纹钢和热卷的价格走势呈现明显的高相关性(上下游关系)。
但这还不够,套利交易实际上交易的是价差,而要计算价差就需要我们确定一点,两个品种之间的价差具有平稳性。
平稳性是指时间序列的均值和方差不随时间变化,即时间序列的统计特征不随时间变化。
所以我们接下来需要做的就是做平稳性检验
3.2.2平稳性检验
from statsmodels.tsa.stattools import adfuller
def adf_test(timeseries):
print("Results of Dickey-Fuller Test:")
dftest = adfuller(timeseries, autolag="AIC")
dfoutput = pd.Series(
dftest[0:4],
index=[
"Test Statistic",
"p-value",
"#Lags Used",
"Number of Observations Used",
],
)
for key, value in dftest[4].items():
dfoutput["Critical Value (%s)" % key] = value
print(dfoutput)
adf_test(RB_HC_data.价差)
很明显,螺纹钢和热卷价差更具有平稳的特征,原假设是时间序列是非平稳的,备择假设是时间序列是平稳的。p值小于0.05(0.1),所以拒绝原假设,认为序列是平稳的,那么我们就可以据此构建套利交易了。
3.2.3系数匹配
找出了合适的配对后,在开始交易之前我们需要知道配对之间怎么分配仓位。说人话就是买1手螺纹钢卖多少热卷?
在实际应用中,系数估计的方法有这么三种:
1 回归匹配;2 波动率匹配;3 合约价值匹配
本节以回归方式匹配为例,回归匹配是指利用回归模型来估计两个序列之间的系数。
RB=a+b*HC
b为回归系数,c为常数。
X=sm.add_constant(RB_HC_data.螺纹钢收盘价)
results=sm.OLS(RB_HC_data.螺纹钢收盘价,RB_HC_data.热卷收盘价).fit()
results.summary()
fig = plt.figure(figsize=(16,10))
ax = fig.add_subplot(1,1,1)
ax.plot(RB_HC_data.螺纹钢收盘价,color='r',label="螺纹钢收盘价")
ax.plot(RB_HC_data.热卷收盘价,color='g',label="热卷收盘价")
ax.plot(results.fittedvalues, 'b',label="OLS")
ax.legend(loc='best')
plt.show()
#放大看看
fig = plt.figure(figsize=(10,8))
ax = fig.add_subplot(1,1,1)
ax.plot(RB_HC_data.螺纹钢收盘价[-200:],color='r',label="螺纹钢收盘价")
ax.plot(RB_HC_data.热卷收盘价[-200:],color='g',label="热卷收盘价")
ax.plot(results.fittedvalues[-200:], 'b',label="OLS")
ax.legend(loc='best')
plt.show()
可以明显看到,ols拟合的数据,要更贴近螺纹钢的走势,接下来我们需要计算配对系数,公式为:拟合系数*热卷合约价值/螺纹钢合约价值
螺纹钢报价单位元/吨,10吨/手,最低交易保证金7%
热卷报价单位元/吨,10吨/手,最低交易保证金7%
#计算一下
num_data=0.9775*RB_HC_data.热卷收盘价[-1]*10/(RB_HC_data.螺纹钢收盘价[-1]*10)
num_data
#也就是说开1手螺纹钢可以对应开一手热卷,基本达到完美匹配。这也难怪套利品种首选螺纹钢和热卷。
除了回归方法外,也可以利用波动率或者干脆直接以市值方式开仓,具体怎么选择需要结合自己的研究成功决定。
接下来我们可以这个指标为基础进行策略构建了。
初步思路:
z-score 是对时间序列偏离其均值程度的衡量,表示时间序列偏离了其均值多少倍的标准差。
一个序列在时间 t 的 z-score,是它在时间 t 的值,减去序列的均值,再除以序列的标准差后得到的值。
在z-score低于-1.0的时候买入螺纹钢,卖出热卷
在z-score高于1.0的时候卖出螺纹钢,买入热卷
在z-score接近正负0.5的时候退出所有头寸
RB_HC_data.价差.plot()
def zscore(series):
return (series - series.mean()) / np.std(series)
#先看看走势zscore
fig = plt.figure(figsize=(14,8))
ax = fig.add_subplot(1,1,1)
ax.plot(zscore(RB_HC_data.价差),color='#407CE2')
ax.axhline(zscore(RB_HC_data.价差).mean(), color="black")
ax.axhline(1.0, color="red", linestyle="--")
ax.axhline(-1.0, color="green", linestyle="--")
ax.legend(["z-score", "mean", "+1", "-1"])
plt.show()
#换一种方法,毕竟不少时间都在正负一倍之外运行,可以用利用移动平均平滑一些。
RB_HC_data['价差20日均值']=RB_HC_data.价差.rolling(20).mean()
RB_HC_data['价差60日均值']=RB_HC_data.价差.rolling(60).mean()
RB_HC_data['价差60日std']=RB_HC_data.价差.rolling(60).std()
RB_HC_data
%matplotlib inline
fig = plt.figure(figsize=(14,8))
ax = fig.add_subplot(1,1,1)
ax.plot((RB_HC_data.价差20日均值-RB_HC_data.价差60日均值)/RB_HC_data.价差60日std,color='b')
ax.axhline(1.0, color="red", linestyle="--")
ax.axhline(-1.0, color="green", linestyle="--")
ax.legend(["z-score", "+1", "-1"])
plt.show()
3.3策略回测/评价
data_list={'RB_data':RB_data,'HC_data':HC_data}
data_list
cerebro = bt.Cerebro()
for n,futures in data_list.items():
futures['openinterest']=0#添加一列数据
futures=futures.loc[:,['开盘价', '最高价', '最低价', '收盘价', '成交量','openinterest','日期']]#选择数据
futures.columns=['open','high','low','close','volume','openinterest','datetime']#修改列名
data=futures.set_index(pd.to_datetime(futures['datetime'].astype('str'))).sort_index()#排
data.loc[:,['volume','openinterest']] = data.loc[:,['volume','openinterest']].fillna(0)
data.loc[:,['open','high','low','close']] = data.loc[:,['open','high','low','close']].fillna(method='pad')
# 导入数据
datafeed = bt.feeds.PandasData(dataname=data,
fromdate=datetime.datetime(2022,1,1),
todate=datetime.datetime(2024,2,29))
cerebro.adddata(datafeed, name=n) # 通过 name 实现数据集与股票的一一对应
print(f"{n}导入成功!")
class spread_trading(bt.Strategy):
params = dict(
window1=20, # 价差的短期移动均线
window2=60, # 价差的长期移动均线
upper=1,
lower=-1,
up_medium=0.5,
low_medium=-0.5,
)
def log(self, txt, dt=None):
dt = dt or self.datas[0].datetime.date(0)
print('%s, %s' % (dt.isoformat(), txt))
def __init__(self):
self.order = None
self.upper_limit = self.p.upper
self.lower_limit = self.p.lower
self.up_medium = self.p.up_medium
self.low_medium = self.p.low_medium
self.status = 0
# 计算价差
self.RB=self.getdatabyname('RB_data')
self.HC=self.getdatabyname('HC_data')
spread = self.RB.close-self.HC.close
# 计算价差的短期均线
self.spread_ma1 = bt.ind.SMA(spread, period=self.p.window1)
# 计算价差的长期均线
self.spread_ma2 = bt.ind.SMA(spread, period=self.p.window2)
# 计算价差的标准差
self.spread_std = bt.ind.StdDev(spread, period=self.p.window2)
# 对价差进行"标准化"处理
self.zscore = (self.spread_ma1-self.spread_ma2) / self.spread_std
def next(self):
# 如果 z-score>1 时,做空价格高的data0,做多价格低的data1;
if (self.zscore[0] > self.upper_limit) and (self.status != 1):
# 做空data0
#print(self.datetime.date(0),self.zscore[0],self.RB.close[0],self.HC.close[0])
self.sell(data=self.RB,size=10)
# 做多data1
self.buy(data=self.HC,size=10)
self.status = 1 # 处于 zscore 超过上线 的状态,标记为 1
# 如果 z-score<-1 时,做多价格低的data0,做空价格高的data1;
elif (self.zscore[0] < self.lower_limit) and (self.status != 2):
# 做多data0
#print(self.datetime.date(0),self.zscore[0],self.RB.close[0],self.HC.close[0])
self.buy(data=self.RB,size=10)
# 做空data1
self.sell(data=self.HC,size=10)
self.status = 2 # 处于 zscore 跌破下线的状况,标记为 2
# 如果 zscore 位于中间区域,认为已经不存在套利空间,则退出所有头寸
elif (self.zscore[0] <= self.up_medium) and (self.zscore[0] >= self.low_medium):
#print(self.datetime.date(0),self.zscore[0],self.RB.close[0],self.HC.close[0])
self.close(self.data0,size=10)
self.close(self.data1,size=10)
def stop(self):
print('==================================================')
print('Starting Value - %.2f' % self.broker.startingcash)
print('Ending Value - %.2f' % self.broker.getvalue())
print('==================================================')
def notify_order(self, order):#订单日志函数。
# 未被处理的订单
if order.status in [order.Submitted, order.Accepted]:#从status订单状态函数里面查询是否有被经纪商接收和传递的订单。
return
# 已经处理的订单
if order.status in [order.Completed, order.Canceled, order.Margin]:#从订单状态函数里面查询成交的。被撤销的。需要增加保证金的订单。
if order.isbuy():#判断是买单。
self.log(
'BUY EXECUTED, ref:%.0f, Price: %.2f, Cost: %.2f, Comm %.2f, Size: %.2f, Stock: %s' %
(order.ref, # 订单编号
order.executed.price, # 成交价
order.executed.value, # 成交额
order.executed.comm, # 佣金
order.executed.size, # 成交量
order.data._name)) # 股票名称
else: # Sell#判断是卖单的。
self.log('SELL EXECUTED, ref:%.0f, Price: %.2f, Cost: %.2f, Comm %.2f, Size: %.2f, Stock: %s' %
(order.ref,
order.executed.price,
order.executed.value,
order.executed.comm,
order.executed.size,
order.data._name))
# 初始资金 100,000
cerebro.broker.setcash(100000.0)
# 佣金,双边各 0.0003
cerebro.broker.setcommission(commission=5.0,stocklike=False,commtype=bt.CommInfoBase.COMM_FIXED,margin=0.07,mult=10,automargin=True,name='RB_data')
cerebro.broker.setcommission(commission=5.0, # 交易手续费
stocklike = False, # 期货必选
commtype=bt.CommInfoBase.COMM_FIXED,# 按固定收费
margin=0.07, # 保证金率
mult=10, # 合约乘数
automargin=True, # 按比例计算保证金
name='HC_data')
# 滑点:双边各 0.0001
cerebro.broker.set_slippage_perc(perc=0.001)
# 将编写的策略添加给大脑,别忘了 !
cerebro.addstrategy(spread_trading)
# 回测时需要添加 PyFolio 分析器
cerebro.addanalyzer(bt.analyzers.PyFolio, _name='pyfolio')
result = cerebro.run()
# 借助 pyfolio 进一步做回测结果分析
pyfolio = result[0].analyzers.pyfolio
returns, positions, transactions, gross_lev = pyfolio.get_pf_items()
(returns+1).cumprod().plot()
不考虑其它指标,单从收益结果看,收益率为正,且交易次数较少,中规中矩,只能说这个策略思路有效,作为案例是可以的,实盘还有巨大优化空间。
例如这个策略是日线级别,数据颗粒度较大,盘中价差扩大能不能交易?且未加入仓位控制!
此外套利策略还可以基于基差,跨期等等,但多数套利策略都会依托于相关性以及均值回归现象。
这些有兴趣的同学可以进一步研究。
3.4本节练习
本节我们学习了套利策略的构建方法,但受限于篇幅,很多内容并没有展开一一说明。
题中用螺纹钢和热卷作为例子,请你用豆油、菜籽油、棕榈油或黄金、白银作为目标复刻上述策略。
相信自己,代码敲起来!