网格
- 震荡期用小网格:2012/01/01 - 2014/11/01这段震荡时期中,小网格策略实现48.3%的年化收益,同期基准收益0.8%. 看来市场震荡期网格操作大有用武之地;
- 慢趋势期用中网格:2015/08/31 - 2016/08/01这段慢熊时期中,中网格策略实现50%年化收益,同期基准年化收益-5.5%. 这是因为市场有一定趋势时,网格大有一定容错率,否则容易过快实现亏损,得不到收益;
- 长期投资用大网格:2010/01/01 - 2016/08/01这段时间,大网格实现15.5%的年化收益,同期基准年化收益为-0.1%. 同样的逻辑,时间越长,市场越可能走成趋势,需要提高容错率。
前言
- 网格策略是一种旨在达到低吸高抛的策略,主要思想就是在股价比设定的基准价下跌时逐渐加仓,而上涨时逐渐减仓:
价格 | 目标仓位 | (-3%, 5%)网格 | (-5%, 10%)网格 | (-8%, 15%)网格 |
买4 | 100% | 0.88 | 0.8 | 0.68 |
买3 | 90% | 0.91 | 0.85 | 0.76 |
买2 | 70% | 0.94 | 0.9 | 0.84 |
买1 | 40% | 0.97 | 0.95 | 0.92 |
基准 | 不操作 | 1 | 1 | 1 |
卖1 | 60% | 1.05 | 1.1 | 1.15 |
卖2 | 30% | 1.1 | 1.2 | 1.3 |
卖3 | 10% | 1.15 | 1.3 | 1.45 |
卖4 | 0% | 1.2 | 1.4 | 1.6 |
策略流程
- 选股网格策略的逻辑是在股票便宜时买进,估值过高时卖出,因此选出波动率较高的股票很有必要,因此本篇的选股流程如下:
- 先设置股票池行业为中证行业:信息技术、电信业务;估值不能过高:PE<50;市值约束:取满足上述条件的市值较小的30只(但实际上这一约束一直没有发挥作用,因为总数都不足30只);高波动:分行业在市值最小的30只中选出过去一年波动率最大的5只股票;上述流程后,我们有了10只股票构成的股票池,每隔60个交易日更新一次股票池。
- 网格三种大小的网格都会相应尝试一下看看效果。[-3%买,5%卖]、[-5%买,10%卖]、[-8%买,15%卖]
- 资金安排在仓位控制时,满仓的概念是(总资金/股票池总数*2.5),这是为了提高资金利用率,因为3个月的周期内可能不是每只股票都能达到满仓。
- 止损基准前两日亏损3%以上则空仓;个股累计亏损30%则平仓该股。
实现
import pandas as pdimport numpy as npfrom CAL.PyCAL import *from __future__ import division__cal = Calendar('CHINA.SSE')# 连续下跌天数def continuous_loss_day(returnSeries): '''对于日期顺序排列的pd series,给出最近的连续下跌天数''' return len(returnSeries) - 1 - max([v for v in range(len(returnSeries)) if returnSeries[v] > 0])# 连续回撤def continuous_drawdown(returnSeries): '''对于日期顺序排列的pd series,给出最近的连续回撤''' condrawdown = 1 - (1 + returnSeries[max([v for v in range(len(returnSeries)) if returnSeries[v] > 0]):][1:]).prod() condrawdown = 0 if np.isnan(condrawdown) else condrawdown return condrawdown# 最大回撤def max_drawdown(returnSeries, days_num): '''给出最近days_num个交易日内最大回撤''' valueSeries = (returnSeries + 1).cumprod()[-days_num:] return max([1 - valueSeries[x] / valueSeries[:x].max() for x in valueSeries.index])# 前days_num日累计收益def cumulative_return(returnSeries, days_num): return (1 + returnSeries[-days_num:]).prod() - 1# (参考)现价与MA价比率def price_to_MA(returnSeries, days_num): '''返回最新价格与days_num交易日MA价的比值''' valueSeries = (returnSeries + 1).cumprod() if days_num > len(returnSeries): print "MA窗口期超过上限,请调整到%i天以内!"%len(returnSeries) return (valueSeries[-1:] / valueSeries[-days_num:].mean())[0]# 建仓至今股票累计收益率def stock_return(account, symbol): try: return account.referencePrice[symbol] / account.valid_seccost[symbol] except KeyError, e: print e,'not in valid_seccost / referencePrice.'start = '2012-01-01' # 回测起始时间end = '2014-11-01' # 回测结束时间benchmark = 'HS300' # 策略参考标准universe = set_universe(IndZZ.XinXiJiZhuL1) + set_universe(IndZZ.DianXinYeWuL1) # 证券池,支持股票和基金capital_base = 100000 # 起始资金freq = 'd' # 策略类型,'d'表示日间策略使用日线回测,'m'表示日内策略使用分钟线回测refresh_rate = 1 # 调仓频率,表示执行handle_data的时间间隔,若freq = 'd'时间间隔的单位为交易日,若freq = 'm'时间间隔为分钟def update_info(account): newInfo = account.get_history(refresh_rate) tmpDict = {} for x in newInfo: if not x == 'tradeDate': tmpDict[x] = pd.DataFrame(newInfo[x]).merge( pd.DataFrame(newInfo['tradeDate']).rename_axis({0:'tradeDate'}, axis = 1) , left_index = True, right_index = True).set_index('tradeDate') tmpDict[x]['return'] = tmpDict[x]['closePrice'] / tmpDict[x]['preClosePrice'] - 1 newPan = pd.Panel(tmpDict) account.history_info = pd.concat([account.history_info, newPan], axis = 1)# 选股函数,每3个月重新确定一次股票池def stock_filter(tradeDate, industry): data = DataAPI.MktStockFactorsOneDayGet(tradeDate, set_universe(industry, tradeDate), field = 'secID,PE,LFLO') secs = list(data[data.PE < 50].sort('LFLO')[:30].secID) beginDate = __cal.advanceDate(tradeDate, '-1Y') histDf = DataAPI.MktEqudAdjGet(secID = secs, beginDate = beginDate, endDate = tradeDate, field = 'secID,tradeDate,preClosePrice,closePrice') histDf['return'] = histDf.closePrice / histDf.preClosePrice - 1 secList = list(histDf.pivot(index = 'tradeDate', columns = 'secID', values = 'return').std(axis = 0).sort_values()[-5:].index) return secListdef initialize(account): # 初始化虚拟账户状态 account.history_info = pd.Panel() account.stock_pool = [] account.adjust_stock_pool = 0 account.base_price = {} passdef handle_data(account): # 初始化及更新行情信息 if account.history_info.empty: iniInfo = account.get_history(20) tmpDict = {} for x in iniInfo: if not x == 'tradeDate': tmpDict[x] = pd.DataFrame(iniInfo[x]).merge( pd.DataFrame(iniInfo['tradeDate']).rename_axis({0:'tradeDate'}, axis = 1) , left_index = True, right_index = True).set_index('tradeDate') tmpDict[x]['return'] = tmpDict[x]['closePrice'] / tmpDict[x]['preClosePrice'] - 1 account.history_info = pd.Panel(tmpDict) else: update_info(account) # 选股 if account.stock_pool == [] or account.adjust_stock_pool >= 60: account.stock_pool = stock_filter(account.previous_date, IndZZ.XinXiJiZhuL1) + \ stock_filter(account.previous_date, IndZZ.DianXinYeWuL1) account.adjust_stock_pool = 0 for stk in account.valid_secpos: if stk not in account.stock_pool: order_to(stk, 0) else: account.adjust_stock_pool += 1 # 止损 if cumulative_return(account.history_info.minor_xs('return')['benchmark'], 2) < -0.03: for stk in account.valid_secpos: order_to(stk, 0) for stk in account.valid_secpos: if stock_return(account, stk) < - 0.3: order_to(stk, 0) # 初始化底仓价格及根据网格调整仓位 for stk in account.stock_pool: if continuous_loss_day(account.history_info.minor_xs('return')[stk]) >= 5: continue if price_to_MA(account.history_info.minor_xs('return')[stk], 5) > 1.5 or \ price_to_MA(account.history_info.minor_xs('return')[stk], 10) > 1.5: continue if (not stk in account.referencePrice) or account.referencePrice[stk] == 0: continue if not stk in account.base_price: account.base_price[stk] = account.referencePrice[stk] # setup_position if account.referencePrice[stk] / account.base_price[stk] < buy4: order_pct_to(stk, 1/len(account.stock_pool)*cap_ratio) elif account.referencePrice[stk] / account.base_price[stk] < buy3: order_pct_to(stk, 0.9 * 1/len(account.stock_pool)*cap_ratio) elif account.referencePrice[stk] / account.base_price[stk] < buy2: order_pct_to(stk, 0.7 * 1/len(account.stock_pool)*cap_ratio) elif account.referencePrice[stk] / account.base_price[stk] < buy1: order_pct_to(stk, 0.4 * 1/len(account.stock_pool)*cap_ratio) elif account.referencePrice[stk] / account.base_price[stk] > sell4: order_pct_to(stk, 0 * 1/len(account.stock_pool)*cap_ratio) elif account.referencePrice[stk] / account.base_price[stk] > sell3: order_pct_to(stk, 0.1 * 1/len(account.stock_pool)*cap_ratio) elif account.referencePrice[stk] / account.base_price[stk] > sell2: order_pct_to(stk, 0.3 * 1/len(account.stock_pool)*cap_ratio) elif account.referencePrice[stk] / account.base_price[stk] > sell1: order_pct_to(stk, 0.6 * 1/len(account.stock_pool)*cap_ratio) returnbuy4,buy3,buy2,buy1,sell4,sell3,sell2,sell1 = 0.88,0.91,0.94,0.97,1.2,1.15,1.1,1.05cap_ratio = 2.5
import pandas as pdimport numpy as npfrom CAL.PyCAL import *from __future__ import division__cal = Calendar('CHINA.SSE')# 连续下跌天数def continuous_loss_day(returnSeries): '''对于日期顺序排列的pd series,给出最近的连续下跌天数''' return len(returnSeries) - 1 - max([v for v in range(len(returnSeries)) if returnSeries[v] > 0])# 连续回撤def continuous_drawdown(returnSeries): '''对于日期顺序排列的pd series,给出最近的连续回撤''' condrawdown = 1 - (1 + returnSeries[max([v for v in range(len(returnSeries)) if returnSeries[v] > 0]):][1:]).prod() condrawdown = 0 if np.isnan(condrawdown) else condrawdown return condrawdown# 最大回撤def max_drawdown(returnSeries, days_num): '''给出最近days_num个交易日内最大回撤''' valueSeries = (returnSeries + 1).cumprod()[-days_num:] return max([1 - valueSeries[x] / valueSeries[:x].max() for x in valueSeries.index])# 前days_num日累计收益def cumulative_return(returnSeries, days_num): return (1 + returnSeries[-days_num:]).prod() - 1# (参考)现价与MA价比率def price_to_MA(returnSeries, days_num): '''返回最新价格与days_num交易日MA价的比值''' valueSeries = (returnSeries + 1).cumprod() if days_num > len(returnSeries): print "MA窗口期超过上限,请调整到%i天以内!"%len(returnSeries) return (valueSeries[-1:] / valueSeries[-days_num:].mean())[0]# 建仓至今股票累计收益率def stock_return(account, symbol): try: return account.referencePrice[symbol] / account.valid_seccost[symbol] except KeyError, e: print e,'not in valid_seccost / referencePrice.'start = '2015-08-01' # 回测起始时间end = '2016-08-01' # 回测结束时间benchmark = 'HS300' # 策略参考标准universe = set_universe(IndZZ.XinXiJiZhuL1) + set_universe(IndZZ.DianXinYeWuL1) # 证券池,支持股票和基金capital_base = 100000 # 起始资金freq = 'd' # 策略类型,'d'表示日间策略使用日线回测,'m'表示日内策略使用分钟线回测refresh_rate = 1 # 调仓频率,表示执行handle_data的时间间隔,若freq = 'd'时间间隔的单位为交易日,若freq = 'm'时间间隔为分钟def update_info(account): newInfo = account.get_history(refresh_rate) tmpDict = {} for x in newInfo: if not x == 'tradeDate': tmpDict[x] = pd.DataFrame(newInfo[x]).merge( pd.DataFrame(newInfo['tradeDate']).rename_axis({0:'tradeDate'}, axis = 1) , left_index = True, right_index = True).set_index('tradeDate') tmpDict[x]['return'] = tmpDict[x]['closePrice'] / tmpDict[x]['preClosePrice'] - 1 newPan = pd.Panel(tmpDict) account.history_info = pd.concat([account.history_info, newPan], axis = 1)# 选股函数,每3个月重新确定一次股票池def stock_filter(tradeDate, industry): data = DataAPI.MktStockFactorsOneDayGet(tradeDate, set_universe(industry, tradeDate), field = 'secID,PE,LFLO') secs = list(data[data.PE < 50].sort('LFLO')[:30].secID) beginDate = __cal.advanceDate(tradeDate, '-1Y') histDf = DataAPI.MktEqudAdjGet(secID = secs, beginDate = beginDate, endDate = tradeDate, field = 'secID,tradeDate,preClosePrice,closePrice') histDf['return'] = histDf.closePrice / histDf.preClosePrice - 1 secList = list(histDf.pivot(index = 'tradeDate', columns = 'secID', values = 'return').std(axis = 0).sort_values()[-5:].index) return secListdef initialize(account): # 初始化虚拟账户状态 account.history_info = pd.Panel() account.stock_pool = [] account.adjust_stock_pool = 0 account.base_price = {} passdef handle_data(account): # 初始化及更新行情信息 if account.history_info.empty: iniInfo = account.get_history(20) tmpDict = {} for x in iniInfo: if not x == 'tradeDate': tmpDict[x] = pd.DataFrame(iniInfo[x]).merge( pd.DataFrame(iniInfo['tradeDate']).rename_axis({0:'tradeDate'}, axis = 1) , left_index = True, right_index = True).set_index('tradeDate') tmpDict[x]['return'] = tmpDict[x]['closePrice'] / tmpDict[x]['preClosePrice'] - 1 account.history_info = pd.Panel(tmpDict) else: update_info(account) # 选股 if account.stock_pool == [] or account.adjust_stock_pool >= 60: account.stock_pool = stock_filter(account.previous_date, IndZZ.XinXiJiZhuL1) + \ stock_filter(account.previous_date, IndZZ.DianXinYeWuL1) account.adjust_stock_pool = 0 for stk in account.valid_secpos: if stk not in account.stock_pool: order_to(stk, 0) else: account.adjust_stock_pool += 1 # 止损 if cumulative_return(account.history_info.minor_xs('return')['benchmark'], 2) < -0.03: for stk in account.valid_secpos: order_to(stk, 0) for stk in account.valid_secpos: if stock_return(account, stk) < - 0.3: order_to(stk, 0) # 初始化底仓价格及根据网格调整仓位 for stk in account.stock_pool: if continuous_loss_day(account.history_info.minor_xs('return')[stk]) >= 5: continue if price_to_MA(account.history_info.minor_xs('return')[stk], 5) > 1.5 or \ price_to_MA(account.history_info.minor_xs('return')[stk], 10) > 1.5: continue if (not stk in account.referencePrice) or account.referencePrice[stk] == 0: continue if not stk in account.base_price: account.base_price[stk] = account.referencePrice[stk] # setup_position if account.referencePrice[stk] / account.base_price[stk] < buy4: order_pct_to(stk, 1/len(account.stock_pool)*cap_ratio) elif account.referencePrice[stk] / account.base_price[stk] < buy3: order_pct_to(stk, 0.9 * 1/len(account.stock_pool)*cap_ratio) elif account.referencePrice[stk] / account.base_price[stk] < buy2: order_pct_to(stk, 0.7 * 1/len(account.stock_pool)*cap_ratio) elif account.referencePrice[stk] / account.base_price[stk] < buy1: order_pct_to(stk, 0.4 * 1/len(account.stock_pool)*cap_ratio) elif account.referencePrice[stk] / account.base_price[stk] > sell4: order_pct_to(stk, 0 * 1/len(account.stock_pool)*cap_ratio) elif account.referencePrice[stk] / account.base_price[stk] > sell3: order_pct_to(stk, 0.1 * 1/len(account.stock_pool)*cap_ratio) elif account.referencePrice[stk] / account.base_price[stk] > sell2: order_pct_to(stk, 0.3 * 1/len(account.stock_pool)*cap_ratio) elif account.referencePrice[stk] / account.base_price[stk] > sell1: order_pct_to(stk, 0.6 * 1/len(account.stock_pool)*cap_ratio) returnbuy4,buy3,buy2,buy1,sell4,sell3,sell2,sell1 = 0.8,0.85,0.9,0.95,1.1,1.2,1.3,1.4cap_ratio = 2.5
import pandas as pdimport numpy as npfrom CAL.PyCAL import *from __future__ import division__cal = Calendar('CHINA.SSE')# 连续下跌天数def continuous_loss_day(returnSeries): '''对于日期顺序排列的pd series,给出最近的连续下跌天数''' return len(returnSeries) - 1 - max([v for v in range(len(returnSeries)) if returnSeries[v] > 0])# 连续回撤def continuous_drawdown(returnSeries): '''对于日期顺序排列的pd series,给出最近的连续回撤''' condrawdown = 1 - (1 + returnSeries[max([v for v in range(len(returnSeries)) if returnSeries[v] > 0]):][1:]).prod() condrawdown = 0 if np.isnan(condrawdown) else condrawdown return condrawdown# 最大回撤def max_drawdown(returnSeries, days_num): '''给出最近days_num个交易日内最大回撤''' valueSeries = (returnSeries + 1).cumprod()[-days_num:] return max([1 - valueSeries[x] / valueSeries[:x].max() for x in valueSeries.index])# 前days_num日累计收益def cumulative_return(returnSeries, days_num): return (1 + returnSeries[-days_num:]).prod() - 1# (参考)现价与MA价比率def price_to_MA(returnSeries, days_num): '''返回最新价格与days_num交易日MA价的比值''' valueSeries = (returnSeries + 1).cumprod() if days_num > len(returnSeries): print "MA窗口期超过上限,请调整到%i天以内!"%len(returnSeries) return (valueSeries[-1:] / valueSeries[-days_num:].mean())[0]# 建仓至今股票累计收益率def stock_return(account, symbol): try: return account.referencePrice[symbol] / account.valid_seccost[symbol] except KeyError, e: print e,'not in valid_seccost / referencePrice.'start = '2010-01-01' # 回测起始时间end = '2016-08-01' # 回测结束时间benchmark = 'HS300' # 策略参考标准universe = set_universe(IndZZ.XinXiJiZhuL1) + set_universe(IndZZ.DianXinYeWuL1) # 证券池,支持股票和基金capital_base = 100000 # 起始资金freq = 'd' # 策略类型,'d'表示日间策略使用日线回测,'m'表示日内策略使用分钟线回测refresh_rate = 1 # 调仓频率,表示执行handle_data的时间间隔,若freq = 'd'时间间隔的单位为交易日,若freq = 'm'时间间隔为分钟def update_info(account): newInfo = account.get_history(refresh_rate) tmpDict = {} for x in newInfo: if not x == 'tradeDate': tmpDict[x] = pd.DataFrame(newInfo[x]).merge( pd.DataFrame(newInfo['tradeDate']).rename_axis({0:'tradeDate'}, axis = 1) , left_index = True, right_index = True).set_index('tradeDate') tmpDict[x]['return'] = tmpDict[x]['closePrice'] / tmpDict[x]['preClosePrice'] - 1 newPan = pd.Panel(tmpDict) account.history_info = pd.concat([account.history_info, newPan], axis = 1)# 选股函数,每3个月重新确定一次股票池def stock_filter(tradeDate, industry): data = DataAPI.MktStockFactorsOneDayGet(tradeDate, set_universe(industry, tradeDate), field = 'secID,PE,LFLO') secs = list(data[data.PE < 50].sort('LFLO')[:30].secID) beginDate = __cal.advanceDate(tradeDate, '-1Y') histDf = DataAPI.MktEqudAdjGet(secID = secs, beginDate = beginDate, endDate = tradeDate, field = 'secID,tradeDate,preClosePrice,closePrice') histDf['return'] = histDf.closePrice / histDf.preClosePrice - 1 secList = list(histDf.pivot(index = 'tradeDate', columns = 'secID', values = 'return').std(axis = 0).sort_values()[-5:].index) return secListdef initialize(account): # 初始化虚拟账户状态 account.history_info = pd.Panel() account.stock_pool = [] account.adjust_stock_pool = 0 account.base_price = {} passdef handle_data(account): # 初始化及更新行情信息 if account.history_info.empty: iniInfo = account.get_history(20) tmpDict = {} for x in iniInfo: if not x == 'tradeDate': tmpDict[x] = pd.DataFrame(iniInfo[x]).merge( pd.DataFrame(iniInfo['tradeDate']).rename_axis({0:'tradeDate'}, axis = 1) , left_index = True, right_index = True).set_index('tradeDate') tmpDict[x]['return'] = tmpDict[x]['closePrice'] / tmpDict[x]['preClosePrice'] - 1 account.history_info = pd.Panel(tmpDict) else: update_info(account) # 选股 if account.stock_pool == [] or account.adjust_stock_pool >= 60: account.stock_pool = stock_filter(account.previous_date, IndZZ.XinXiJiZhuL1) + \ stock_filter(account.previous_date, IndZZ.DianXinYeWuL1) account.adjust_stock_pool = 0 for stk in account.valid_secpos: if stk not in account.stock_pool: order_to(stk, 0) else: account.adjust_stock_pool += 1 # 止损 if cumulative_return(account.history_info.minor_xs('return')['benchmark'], 2) < -0.03: for stk in account.valid_secpos: order_to(stk, 0) for stk in account.valid_secpos: if stock_return(account, stk) < - 0.3: order_to(stk, 0) # 初始化底仓价格及根据网格调整仓位 for stk in account.stock_pool: if continuous_loss_day(account.history_info.minor_xs('return')[stk]) >= 5: continue if price_to_MA(account.history_info.minor_xs('return')[stk], 5) > 1.5 or \ price_to_MA(account.history_info.minor_xs('return')[stk], 10) > 1.5: continue if (not stk in account.referencePrice) or account.referencePrice[stk] == 0: continue if not stk in account.base_price: account.base_price[stk] = account.referencePrice[stk] # setup_position if account.referencePrice[stk] / account.base_price[stk] < buy4: order_pct_to(stk, 1/len(account.stock_pool)*cap_ratio) elif account.referencePrice[stk] / account.base_price[stk] < buy3: order_pct_to(stk, 0.9 * 1/len(account.stock_pool)*cap_ratio) elif account.referencePrice[stk] / account.base_price[stk] < buy2: order_pct_to(stk, 0.7 * 1/len(account.stock_pool)*cap_ratio) elif account.referencePrice[stk] / account.base_price[stk] < buy1: order_pct_to(stk, 0.4 * 1/len(account.stock_pool)*cap_ratio) elif account.referencePrice[stk] / account.base_price[stk] > sell4: order_pct_to(stk, 0 * 1/len(account.stock_pool)*cap_ratio) elif account.referencePrice[stk] / account.base_price[stk] > sell3: order_pct_to(stk, 0.1 * 1/len(account.stock_pool)*cap_ratio) elif account.referencePrice[stk] / account.base_price[stk] > sell2: order_pct_to(stk, 0.3 * 1/len(account.stock_pool)*cap_ratio) elif account.referencePrice[stk] / account.base_price[stk] > sell1: order_pct_to(stk, 0.6 * 1/len(account.stock_pool)*cap_ratio) returncap_ratio = 2.5buy4,buy3,buy2,buy1,sell4,sell3,sell2,sell1 = 0.68,0.76,0.84,0.92,1.6,1.45,1.3,1.15
import pandas as pdimport numpy as npfrom CAL.PyCAL import *from __future__ import division__cal = Calendar('CHINA.SSE')# 连续下跌天数def continuous_loss_day(returnSeries): '''对于日期顺序排列的pd series,给出最近的连续下跌天数''' return len(returnSeries) - 1 - max([v for v in range(len(returnSeries)) if returnSeries[v] > 0])# 连续回撤def continuous_drawdown(returnSeries): '''对于日期顺序排列的pd series,给出最近的连续回撤''' condrawdown = 1 - (1 + returnSeries[max([v for v in range(len(returnSeries)) if returnSeries[v] > 0]):][1:]).prod() condrawdown = 0 if np.isnan(condrawdown) else condrawdown return condrawdown# 最大回撤def max_drawdown(returnSeries, days_num): '''给出最近days_num个交易日内最大回撤''' valueSeries = (returnSeries + 1).cumprod()[-days_num:] return max([1 - valueSeries[x] / valueSeries[:x].max() for x in valueSeries.index])# 前days_num日累计收益def cumulative_return(returnSeries, days_num): return (1 + returnSeries[-days_num:]).prod() - 1# (参考)现价与MA价比率def price_to_MA(returnSeries, days_num): '''返回最新价格与days_num交易日MA价的比值''' valueSeries = (returnSeries + 1).cumprod() if days_num > len(returnSeries): print "MA窗口期超过上限,请调整到%i天以内!"%len(returnSeries) return (valueSeries[-1:] / valueSeries[-days_num:].mean())[0]# 建仓至今股票累计收益率def stock_return(account, symbol): try: return account.referencePrice[symbol] / account.valid_seccost[symbol] except KeyError, e: print e,'not in valid_seccost / referencePrice.'start = '2010-01-01' # 回测起始时间end = '2016-08-01' # 回测结束时间benchmark = 'HS300' # 策略参考标准universe = set_universe(IndZZ.XinXiJiZhuL1) + set_universe(IndZZ.DianXinYeWuL1) # 证券池,支持股票和基金capital_base = 100000 # 起始资金freq = 'd' # 策略类型,'d'表示日间策略使用日线回测,'m'表示日内策略使用分钟线回测refresh_rate = 1 # 调仓频率,表示执行handle_data的时间间隔,若freq = 'd'时间间隔的单位为交易日,若freq = 'm'时间间隔为分钟def update_info(account): newInfo = account.get_history(refresh_rate) tmpDict = {} for x in newInfo: if not x == 'tradeDate': tmpDict[x] = pd.DataFrame(newInfo[x]).merge( pd.DataFrame(newInfo['tradeDate']).rename_axis({0:'tradeDate'}, axis = 1) , left_index = True, right_index = True).set_index('tradeDate') tmpDict[x]['return'] = tmpDict[x]['closePrice'] / tmpDict[x]['preClosePrice'] - 1 newPan = pd.Panel(tmpDict) account.history_info = pd.concat([account.history_info, newPan], axis = 1)# 选股函数,每3个月重新确定一次股票池def stock_filter(tradeDate, industry): data = DataAPI.MktStockFactorsOneDayGet(tradeDate, set_universe(industry, tradeDate), field = 'secID,PE,LFLO') secs = list(data[data.PE < 50].sort('LFLO')[:30].secID) beginDate = __cal.advanceDate(tradeDate, '-1Y') histDf = DataAPI.MktEqudAdjGet(secID = secs, beginDate = beginDate, endDate = tradeDate, field = 'secID,tradeDate,preClosePrice,closePrice') histDf['return'] = histDf.closePrice / histDf.preClosePrice - 1 secList = list(histDf.pivot(index = 'tradeDate', columns = 'secID', values = 'return').std(axis = 0).sort_values()[-5:].index) return secListdef initialize(account): # 初始化虚拟账户状态 account.history_info = pd.Panel() account.stock_pool = [] account.adjust_stock_pool = 0 account.base_price = {} passdef handle_data(account): # 初始化及更新行情信息 if account.history_info.empty: iniInfo = account.get_history(20) tmpDict = {} for x in iniInfo: if not x == 'tradeDate': tmpDict[x] = pd.DataFrame(iniInfo[x]).merge( pd.DataFrame(iniInfo['tradeDate']).rename_axis({0:'tradeDate'}, axis = 1) , left_index = True, right_index = True).set_index('tradeDate') tmpDict[x]['return'] = tmpDict[x]['closePrice'] / tmpDict[x]['preClosePrice'] - 1 account.history_info = pd.Panel(tmpDict) else: update_info(account) # 选股 if account.stock_pool == [] or account.adjust_stock_pool >= 60: account.stock_pool = stock_filter(account.previous_date, IndZZ.XinXiJiZhuL1) + \ stock_filter(account.previous_date, IndZZ.DianXinYeWuL1) account.adjust_stock_pool = 0 for stk in account.valid_secpos: if stk not in account.stock_pool: order_to(stk, 0) else: account.adjust_stock_pool += 1 # 止损 if cumulative_return(account.history_info.minor_xs('return')['benchmark'], 2) < -0.03: for stk in account.valid_secpos: order_to(stk, 0) for stk in account.valid_secpos: if stock_return(account, stk) < - 0.3: order_to(stk, 0) # 初始化底仓价格及根据网格调整仓位 buylist = [x for x in account.stock_pool if (x in account.referencePrice and account.referencePrice[x]>0)] for stk in buylist: order_pct_to(stk, 1/len(buylist)) returncap_ratio = 2.5buy4,buy3,buy2,buy1,sell4,sell3,sell2,sell1 = 0.68,0.76,0.84,0.92,1.6,1.45,1.3,1.15
回测&结论
对于三种不同网格,在不同市场时期回测,得到结果:
- 震荡期用小网格:2012/01/01 - 2014/11/01这段震荡时期中,小网格策略实现48.3%的年化收益,同期基准收益0.8%. 看来市场震荡期网格操作大有用武之地;
- 慢趋势期用中网格:2015/08/31 - 2016/08/01这段慢熊时期中,中网格策略实现50%年化收益,同期基准年化收益-5.5%. 这是因为市场有一定趋势时,网格大有一定容错率,否则容易过快实现亏损,得不到收益;
- 长期投资用大网格:2010/01/01 - 2016/08/01这段时间,大网格实现15.5%的年化收益,同期基准年化收益为-0.1%. 同样的逻辑,时间越长,市场越可能走成趋势,需要提高容错率;
- 以上收益的来源并不来自选股/行业,而是来自操作方法。在2010/01/01 - 2016/08/01这段时间,仍按上述方法选股、止损,但只等额持有股票,不做网格操作,年化收益只有6.6%,而最大回撤高于60%,表现并不理想。
- 总之,网格操作可能确实有其合理之处,也能达到一些低吸高抛的效果。
相关内容
Gearbox发明者:ezETH脱锚致115个CreditAccounts被清算,25.77ETH的清算损失由内部储备金自动弥补
Particle测试网Phase1上线24小时内UniversalAccount数超10万
Bitget怎么下载 Bitget网格交易AI
Bitget交易平台官网app Bitget 网格交易
ps排版(ps排版教程)
五和招商银行代办(招商银行卡代办)
热门虚拟货币量化交易软件下载虚拟货币交易APP下载
区块链真的会颠覆互联网格局吗?
鸥易下载V5.070
好了,交易所正式版下载V2.1.30
文章来源:
财源网
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 931614094@qq.com 举报,一经查实,本站将立刻删除。