文章目录
前言
提示:本文主要介绍了BigQuant的简单使用,并复现了基于技术指标的策略与基于QP优化的策略。
提示:以下是本篇文章正文内容,下面案例可供参考
一、BigQuant的重要模块说明
1.回测模块
m=M.trade.v2(
instruments=instruments,
start_date=start_date,
end_date=end_date,
initialize=initialize,
handle_data=handle_data,
order_price_field_buy='open',
order_price_field_sell='open',
capital_base=1000000
)
说明1: instrument为初始选股池
说明2:start_date为回测开始时间
说明3:end_date为回测结束时间
说明4:capital_base初始资产
说明5:handle_data是我们自己需要自定义的函数,该函数每天执行一次
def handle_data(context,data):
##技术指标所需要的最大天数
max_need_period=26
if context.trading_day_index<26:
return
if (context.trading_day_index)%20!=0:
return
print(context.trading_day_index)
##记录筛选后的股票和股票数目
stock_selected=[]
stock_count=0
for stock in stock_list:
sid=context.symbol(stock)
prices = data.history(sid,'price',26,'1d')
##是否符合MA指标
ma_short=data.history(sid,'price',context.ma_short_period,'1d').mean()
ma_long=data.history(sid,'price',context.ma_long_period,'1d').mean()
if ma_short>ma_long and data.can_trade(sid) and stock_count<10:
stock_selected.append(stock)
stock_count+=1
##是否符合MACD指标
try:
macd, signal, hist = talib.MACD(np.array(prices),
context.macd_short_period,context.macd_long_period,
context.macd_dea)
if macd[-1] - signal[-1] > 0 and macd[-2] - signal[-2] < 0 and stock_count<10:
if cur_position == 0 and data.can_trade(sid):
stock_selected.append(stock)
stock_count+=1
except:
pass
if stock_count==10:
break
cash=context.portfolio.cash
per_stock=1/len(stock_selected)
for stock in stock_selected:
sid=context.symbol(stock)
price=data.current(sid,'price')
context.order_target_percent(sid,per_stock)
二、基于技术指标的策略实现
1.库函数导入与全局变量初始化
其中stocks是初始选股池,我们可以自行定义
import pandas as pd
import talib as ta
df=pd.read_csv('stocks.csv')
stock_list=[row['stock'] for index,row in df.iterrows()]
2.每日逻辑函数编写
策略1: MACD金叉+MA多头买入
说明:1)每20日进行一次调仓
2)最多买入10只股票
#选股池
instruments=stock_list
#回测时间
start_date='2019-01-03'
end_date='2021-01-22'
def initialize(context):
##买入卖出均为0.3%,最少5元
context.set_commission(PerOrder(buy_cost=0.0003,sell_cost=0.0003,min_cost=5))
##macd参数设置 dif短线:12 dif长线:26 dea:9
context.macd_short_period=12
context.macd_long_period=26
context.macd_dea=9
#ma参数设置:ma短线5 ma长线20
context.ma_short_period=5
context.ma_long_period=20
##每天执行一次
def handle_data(context,data):
##技术指标所需要的最大天数
max_need_period=26
if context.trading_day_index<26:
return
if (context.trading_day_index)%20!=0:
return
print(context.trading_day_index)
##记录筛选后的股票和股票树木
stock_selected=[]
stock_count=0
for stock in stock_list:
sid=context.symbol(stock)
prices = data.history(sid,'price',26,'1d')
##是否符合MA指标
ma_short=data.history(sid,'price',context.ma_short_period,'1d').mean()
ma_long=data.history(sid,'price',context.ma_long_period,'1d').mean()
if ma_short>ma_long and data.can_trade(sid) and stock_count<10:
stock_selected.append(stock)
stock_count+=1
##是否符合MACD指标
try:
macd, signal, hist = talib.MACD(np.array(prices),
context.macd_short_period,context.macd_long_period,
context.macd_dea)
if macd[-1] - signal[-1] > 0 and macd[-2] - signal[-2] < 0 and stock_count<10:
if cur_position == 0 and data.can_trade(sid):
stock_selected.append(stock)
stock_count+=1
except:
pass
if stock_count==10:
break
cash=context.portfolio.cash
per_stock=1/len(stock_selected)
for stock in stock_selected:
sid=context.symbol(stock)
price=data.current(sid,'price')
context.order_target_percent(sid,per_stock)
策略2: 利用MA指标进行择股
说明:1)每20日进行一次调仓
2)ma3>ma5且ma5>ma10且ma10>ma15时买入股票
3)最多买入5只股票
def handle_data(context,data):
##技术指标所需要的最大天数
max_need_period=26
if context.trading_day_index<26:
return
if (context.trading_day_index)%20!=0:
return
print(context.trading_day_index)
##记录筛选后的股票和股票树木
stock_selected=[]
stock_count=0
for stock in stock_list:
sid=context.symbol(stock)
prices = data.history(sid,'price',26,'1d')
##是否符合MA指标
ma_3=data.history(sid,'price',3,'1d').mean()
ma_5=data.history(sid,'price',5,'1d').mean()
ma_10=data.history(sid,'price',10,'1d').mean()
ma_15=data.history(sid,'price',15,'1d').mean()
if ma_3>ma_5 and ma_5>ma_10 and ma_10>ma_15 and data.can_trade(sid) and stock_count<5:
stock_selected.append(stock)
stock_count+=1
##是否符合MACD指标
if stock_count==5:
break
cash=context.portfolio.cash
per_stock=1/len(stock_selected)
for stock in stock_selected:
sid=context.symbol(stock)
price=data.current(sid,'price')
context.order_target_percent(sid,per_stock)
3.策略结果展示
策略1: MACD金叉+MA多头买入
策略2: MA择股
三、基于QP优化的策略实现
1.基于经典QP的handle函数
说明:构造优化问题缺点比较明显,最后最优权重会集中在某一支股票,不利于风险分散
def handle_data(context,data):
##技术指标所需要的最大天数
if context.trading_day_index<100:
return
if (context.trading_day_index)%20!=0:
return
print(context.trading_day_index)
price_df=pd.DataFrame()
stock_num=120
count=0
for stock in instruments:
count+=1
sid=context.symbol(stock)
prices = data.history(sid,'price',5,'1d')
price_df=pd.concat([price_df,prices],axis=1)
price_df.fillna(0,inplace=True)
if(count==stock_num):
break
## 120,5
price_np=price_df.to_numpy().T
##cov
##(120,120)
cov_df= pd.DataFrame(np.cov(price_np))
P=matrix(cov_df.to_numpy())
q=-matrix(np.mean(price_np,axis=1),(stock_num,1))
##Ax=b
A=matrix(1.,(1,stock_num))
b=matrix(1.)
##GX<=h
G = -matrix(np.eye(stock_num))
h = opt.matrix(0.0, (stock_num,1))
##solver:
solvers.options['show_progress'] = False
sol = solvers.qp(P,q,G,h,A,b)
weight=list(sol['x'])
stock_selected_list=[]
sum_weight=0.0
cur=0
for stock in stock_list:
sid=context.symbol(stock)
if data.can_trade(sid):
stock_selected_list.append(stock)
sum_weight+=weight[cur]
else:
weight[cur]=0
cur+=1
cur=0
for stock in stock_list:
if weight[cur]!=0:
sid=context.symbol(stock)
price=data.current(sid,'price')
context.order_target_percent(sid,weight[cur]/sum_weight)
cur+=1
2.增加限制条件使股票具有多样性:
方法:修改QP约束中的G矩阵与h矩阵
##GX<=h
temp_df1= pd.DataFrame(-np.eye(stock_num))
temp_df2= pd.DataFrame(np.eye(stock_num))
G_df=pd.concat([temp_df1,temp_df2],axis=0)
G = matrix(G_df.to_numpy())
##xi<=0.2
temp_list=[]
for i in range(stock_num):
temp_list.append([0.0])
for i in range(stock_num):
temp_list.append([0.2])
h = opt.matrix(np.array(temp_list))
3.融合技术指标的QP策略:
说明:1)按5日的数据构造协方差矩阵与收益均值矩阵
2)每20日进行一次调仓
3)如果满足ma3>ma5且ma5>ma10且ma10>ma15权重翻倍
def handle_data(context,data):
##技术指标所需要的最大天数
if context.trading_day_index<100:
return
if (context.trading_day_index)%20!=0:
return
print(context.trading_day_index)
price_df=pd.DataFrame()
stock_num=120
count=0
for stock in instruments:
count+=1
sid=context.symbol(stock)
prices = data.history(sid,'price',5,'1d')
price_df=pd.concat([price_df,prices],axis=1)
price_df.fillna(0,inplace=True)
if(count==stock_num):
break
## 120,5
price_np=price_df.to_numpy().T
##cov
##(120,120)
cov_df= pd.DataFrame(np.cov(price_np))
P=matrix(cov_df.to_numpy())
q=-matrix(np.mean(price_np,axis=1),(stock_num,1))
##Ax=b
A=matrix(1.,(1,stock_num))
b=matrix(1.)
##GX<=h
temp_df1= pd.DataFrame(-np.eye(stock_num))
temp_df2= pd.DataFrame(np.eye(stock_num))
G_df=pd.concat([temp_df1,temp_df2],axis=0)
G = matrix(G_df.to_numpy())
##xi<=0.2
temp_list=[]
for i in range(stock_num):
temp_list.append([0.0])
##每只权重不超过0.2
for i in range(stock_num):
temp_list.append([0.2])
h = opt.matrix(np.array(temp_list))
##solver:
solvers.options['show_progress'] = False
sol = solvers.qp(P,q,G,h,A,b)
weight=list(sol['x'])
stock_selected_list=[]
sum_weight=0.0
cur=0
for stock in stock_list:
sid=context.symbol(stock)
if data.can_trade(sid):
stock_selected_list.append(stock)
sid=context.symbol(stock)
##融入技术指标,如果满足条件,,权重加倍
ma_3=data.history(sid,'price',3,'1d').mean()
ma_5=data.history(sid,'price',5,'1d').mean()
ma_10=data.history(sid,'price',10,'1d').mean()
ma_15=data.history(sid,'price',15,'1d').mean()
sum_weight+=weight[cur]
if ma_3>ma_5 and ma_5>ma_10 and ma_10>ma_15:
sum_weight+=weight[cur]*2
weight[cur]*=2
else:
weight[cur]=0
cur+=1
cur=0
for stock in stock_list:
if weight[cur]!=0:
sid=context.symbol(stock)
price=data.current(sid,'price')
context.order_target_percent(sid,weight[cur]/sum_weight)
cur+=1
4.策略结果展示
策略1:经典基于QP优化策略
策略2:限制股票权重的QP优化策略
策略3:融合技术指标的QP优化策略