量化投资模型策略深度研究(二)
均值回复策略此策略的基本理论认为,价格围绕其价值中枢而上下波动,想捕捉到交易机会,只需要判断出这个中枢以及波动的方向便可以。统计套利是用的最多的均值回复策略,认为价格出现背离类似股票的价值终究会缩小到合理的区间范围。
配对交易示例
前段时间听到国投电力和川投能源两只股票,这两个公司的主要资产是一家水电公司48%,52%的股份,两只股价常在两倍之间变动。
# 首先我们简单看下两只股票的走势
import seaborn as sns
import pandas as pd
import matplotlib.pylab as pylab
from CAL.PyCAL import font
begin_date, end_date = '20130101', '20150514'
ct,gt = '600674', '600886'
ct = DataAPI.MktEqudAdjGet(ticker=ct, beginDate=begin_date, endDate=end_date)
gt = DataAPI.MktEqudAdjGet(ticker=gt, beginDate=begin_date, endDate=end_date)
liangtou =pd.DataFrame()
liangtou['川投股价'] = ct.closePrice
liangtou['国投股价'] = gt.closePrice
liangtou['川投国投股价比例'] =ct.closePrice / gt.closePrice
liangtou.plot( figsize=(16,10))
pylab.legend(, prop=font)
利用量化实验室的strategy模式,咱来编写一个策略
起始建仓: 全仓600886
起始日期: 2014-12-01
结束日期: 2015-05-14
起始资金: 10w
调仓频率: 1天
调仓信号: 川投能源收盘价/国投能源收盘价*100%-200% 之差如果大于1%, 卖出886买入774, 如果小于-1%,则卖出774买入886
start = '2014-12-01' # 回测起始时间
end = '2015-05-14' # 回测结束时间
benchmark = '600886.XSHG' # 策略参考标准
universe = ['600674.XSHG', '600886.XSHG'] # 证券池
capital_base = 100000 # 起始资金
refresh_rate = 1 # 调仓频率,即每 refresh_rate 个交易日执行一次 handle_data() 函数
ct_stk, gt_stk = universe
def initialize(account): # 初始化虚拟账户状态
pass
def handle_data(account): # 每天执行一次
if len(account.universe) < 2: # 有停牌的话, 就跳过.
return
ct_price, gt_price = account.referencePrice, account.referencePrice
percent = int(100*ct_price/gt_price-100)
#第一次, 满仓买入600886
if not account.valid_secpos:
order(gt_stk, capital_base/gt_price)
return
if percent>1 and account.secpos.get(ct_stk, 0): #买886, 卖774
amount = account.secpos.get(ct_stk, 0)
print account.current_date, '买886,数量:%s, 卖774,数量:%s' %(amount*ct_price/gt_price, amount)
order(ct_stk, -amount)
order(gt_stk, amount*ct_price/gt_price)
elif percent<-1 and account.secpos.get(gt_stk, 0): #卖886, 买774
amount = account.secpos.get(gt_stk, 0)
print account.current_date, '卖886,数量:%s, 买774,数量:%s' %(amount, amount*ct_price/gt_price)
order(gt_stk, -amount)
order(ct_stk, amount*gt_price/ct_price)http://s9.sinaimg.cn/mw690/001uapR3zy75b0fMg8U18&690
2014-12-30 00:00:00 卖886,数量:14500, 买774,数量:14087.0708474
2015-02-03 00:00:00 买886,数量:15354.1736134, 卖774,数量:14500
2015-05-05 00:00:00 卖886,数量:15300, 买774,数量:14963.0007675
2015-05-08 00:00:00 买886,数量:16311.5583561, 卖774,数量:15600
延伸研究《基于时间序列的协整关系的配对交易》
技术情绪型策略
此类策略主要通过追踪投资者情绪相关指标来判断预期回报,如交易量、波动性指标和交易价格等。比如高频交易通过限价指令簿的形态来判断近期市场情绪。
基于期权PCR择时
投资者情绪对于资产价格变动影响是很大的,特别是投资者情绪的转变对于资产价格短期运动变化的解释力相比系统因素而言更强。P/C比例和VIX指数是期权市场中最常用的两个情绪指标,它们的运用可参考方正金工2015-6-19的报告《恐慌OR乐观指数,VIX信号日内、日间大不同》和2015-6-26的报告《P/C比例,市场情绪及投资者结构的风向标》。
本文是基于期权PCR指数的择时策略。
P/C作为市场情绪指标
计算方式
P/C比例作为一种反向情绪指标,是看跌期权的成交量(成交额,持仓量等)与看涨期权的成交量(持仓量)的比值。
指标含义
看跌期权的成交量可以作为市场看空力量多寡的衡量;
看涨期权的成交量可以描述市场看多力量。
指标应用
当P/C比例过小达到一个极端时,被视为市场过度乐观,此时市场将遏制原来的上涨趋势;
当P/C比例过大到达另一个极端时,被视为市场过度悲观,此时市场可能出现反弹。
策略思路
比较交易日之前两日的PCR(Put Call Ratio)指数:
PCR上升时,市场恐慌情绪蔓延,卖出
PCR下降时,恐慌情绪有所舒缓,买入
注:国内唯一一只期权上证50ETF期权,跟踪标的为华夏上证50ETF(510050)基金
1. 计算历史PCR指数
from matplotlib import pylab
import numpy as np
import pandas as pd
import DataAPI
import seaborn as sns
sns.set_style('white')
def getHistDayOptions(var, date):
# 使用DataAPI.OptGet,拿到已退市和上市的所有期权的基本信息;
# 同时使用DataAPI.MktOptdGet,拿到历史上某一天的期权成交信息;
# 返回历史上指定日期交易的所有期权信息,包括:
# optIDvarSecIDcontractTypestrikePriceexpDatetradeDateclosePrice turnoverValue
# 以optID为index。
dateStr = date.toISO().replace('-', '')
optionsMkt = DataAPI.MktOptdGet(tradeDate = dateStr, field = , pandas = "1")
optionsMkt = optionsMkt.set_index(u"optID")
optionsMkt.closePrice.name = u"price"
optionsID = map(str, optionsMkt.index.values.tolist())
fieldNeeded = ["optID", u"varSecID", u'contractType', u'strikePrice', u'expDate']
optionsInfo = DataAPI.OptGet(optID=optionsID, contractStatus = , field=fieldNeeded, pandas="1")
optionsInfo = optionsInfo.set_index(u"optID")
options = concat(, axis=1, join='inner').sort_index()
return options
def calDayTurnoverValuePCR(optionVarSecID, date):
# 计算历史每日的看跌看涨期权交易额的比值
# PCR: put call ratio
options = getHistDayOptions(optionVarSecID, date)
call = options
put= options
callTurnoverValue = call.turnoverValue.sum()
putTurnoverValue = put.turnoverValue.sum()
return 1.0 * putTurnoverValue / callTurnoverValue
def getHistPCR(beginDate, endDate):
# 计算历史一段时间内的PCR指数并返回
optionVarSecID = u"510050.XSHG"
cal = Calendar('China.SSE')
dates = cal.bizDatesList(beginDate, endDate)
dates = map(Date.toDateTime, dates)
histPCR = pd.DataFrame(0.0, index=dates, columns=['PCR'])
histPCR.index.name = 'date'
for date in histPCR.index:
histPCR['PCR'] =calDayTurnoverValuePCR(optionVarSecID, Date.fromDateTime(date))
return histPCR
def getDayPCR(date):
# 计算历史一段时间内的PCR指数并返回
optionVarSecID = u"510050.XSHG"
return calDayTurnoverValuePCR(optionVarSecID, date)
secID = '510050.XSHG'
begin = Date(2015, 2, 9)
end = Date(2015, 7, 30)
getHistPCR(begin, end).tail()
2. PCR指数与华夏上证50ETF基金的走势对比
secID = '510050.XSHG'
begin = Date(2015, 2, 9)
end = Date(2015, 7, 30)
# 历史PCR
histPCR = getHistPCR(begin, end)
# 华夏上证50ETF
etf = DataAPI.MktFunddGet(secID, beginDate=begin.toISO().replace('-', ''), endDate=end.toISO().replace('-', ''), field=['tradeDate', 'closePrice'])
etf['tradeDate'] = pd.to_datetime(etf['tradeDate'])
etf = etf.set_index('tradeDate')
font.set_size(12)
pylab.figure(figsize = (16,8))
ax1 = histPCR.plot(x=histPCR.index, y='PCR', style='r')
ax1.set_xlabel(u'日期', fontproperties=font)
ax1.set_ylabel(u'PCR(%)', fontproperties=font)
ax2 = ax1.twinx()
ax2.plot(etf.index,etf.closePrice)
ax2.set_ylabel(u'ETF Price', fontproperties=font)
http://s1.sinaimg.cn/mw690/001uapR3zy75b0PFCYE70&690
从上图可以看出,每次PC指标的上升都对应着标的价格的下挫
3. 基于PCR指数的择时策略示例
start = datetime(2015, 2, 9) # 回测起始时间
end= datetime(2015, 7, 31) # 回测结束时间
benchmark = '510050.XSHG' # 策略参考标准
universe = ['510050.XSHG'] # 股票池
capital_base = 100000 # 起始资金
commission = Commission(0.0,0.0)
longest_history = 1
histPCR = getHistPCR(start, end)
def initialize(account): # 初始化虚拟账户状态
account.fund = universe
def handle_data(account): # 每个交易日的买入卖出指令
hist = account.get_history(longest_history)
fund = account.fund
#获取回测当日的前一天日期
dt = Date.fromDateTime(account.current_date)
cal = Calendar('China.IB')
lastTDay = cal.advanceDate(dt,'-1B',BizDayConvention.Preceding) #计算出倒数第一个交易日
lastLastTDay = cal.advanceDate(lastTDay,'-1B',BizDayConvention.Preceding)#计算出倒数第二个交易日
last_day_str = lastTDay.strftime("%Y-%m-%d")
last_last_day_str = lastLastTDay.strftime("%Y-%m-%d")
# 计算买入卖出信号
try:
http://s5.sinaimg.cn/mw690/001uapR3zy75b0ODO6g74&690
基于PCR指数上升时空仓、下降时进场的策略来买卖标的,可以比较有效地降低标的大跌的风险
页:
[1]