网站首页 > 保险知识 >

2014年股票投资策略(2014股市分析)

2023-04-24 10:09:35 保险知识 阅读 0

Bitget下载

注册下载Bitget下载,邀请好友,即有机会赢取 3,000 USDT

APP下载   官网注册

网格

  • 震荡期用小网格: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

策略流程


  1. 选股网格策略的逻辑是在股票便宜时买进,估值过高时卖出,因此选出波动率较高的股票很有必要,因此本篇的选股流程如下:
  2. 先设置股票池行业为中证行业:信息技术、电信业务;估值不能过高:PE<50;市值约束:取满足上述条件的市值较小的30只(但实际上这一约束一直没有发挥作用,因为总数都不足30只);高波动:分行业在市值最小的30只中选出过去一年波动率最大的5只股票;上述流程后,我们有了10只股票构成的股票池,每隔60个交易日更新一次股票池。
  3. 网格三种大小的网格都会相应尝试一下看看效果。[-3%买,5%卖]、[-5%买,10%卖]、[-8%买,15%卖]
  4. 资金安排在仓位控制时,满仓的概念是(总资金/股票池总数*2.5),这是为了提高资金利用率,因为3个月的周期内可能不是每只股票都能达到满仓。
  5. 止损基准前两日亏损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
【网格策略】整理转分享

回测&结论


对于三种不同网格,在不同市场时期回测,得到结果:

  1. 震荡期用小网格2012/01/01 - 2014/11/01这段震荡时期中,小网格策略实现48.3%的年化收益,同期基准收益0.8%. 看来市场震荡期网格操作大有用武之地;
  2. 慢趋势期用中网格2015/08/31 - 2016/08/01这段慢熊时期中,中网格策略实现50%年化收益,同期基准年化收益-5.5%. 这是因为市场有一定趋势时,网格大有一定容错率,否则容易过快实现亏损,得不到收益;
  3. 长期投资用大网格:2010/01/01 - 2016/08/01这段时间,大网格实现15.5%的年化收益,同期基准年化收益为-0.1%. 同样的逻辑,时间越长,市场越可能走成趋势,需要提高容错率;
  4. 以上收益的来源并不来自选股/行业,而是来自操作方法。在2010/01/01 - 2016/08/01这段时间,仍按上述方法选股、止损,但只等额持有股票,不做网格操作,年化收益只有6.6%,而最大回撤高于60%,表现并不理想。
  • 总之,网格操作可能确实有其合理之处,也能达到一些低吸高抛的效果。

相关内容

2014年股票投资策略(2014股市分析)文档下载.: PDF DOC TXT

猜你喜欢