#滑动平均策略——python回测结果 (中山大学岭南学院量化投资协会)


策略如下:

  • 回测区间为2016年10月10日至2017年10月13日,选择沪深300进行回测。
  • 记录所有当天5日滑动平均价格高于20日滑动平均价格的股票
  • 将总资金额的一半n/2用于买入股票,每一支股票按照等额购入
  • 买入后的第二天清仓

回测过程:
数据获取(获取股票收盘价stock_price、大盘收盘价benchmark_price以及公司名称stocks):

def getPrice():
  stocks_name='沪深300'
  start_date='2016-10-01'
  end_date='2017-10-13'
  fields=['ClosingPx']
  #选择沪深300里面所有的股票代码
  stocks=index_components(stocks_name)
  #正式获取股票价格
  stock_price=get_price(stocks,start_date=start_date,end_date=end_date,fields=fields)
  #获得沪深300指数
  benchmark_name='399300.XSHE'
  benchmark_price=get_price(benchmark_name,start_date=start_date,end_date=end_date,fields=fields)
  return stock_price,benchmark_price,stocks
这里对沪深300指数解释一下:对样本空间内股票在最近一年(新股为上市以来)的日均成交金额由高到低进行排名,剔除排名在后50%的股票,然后对剩余股票按照日均总市值由高到低进行排名,选取排名在前300名的股票作为指数样本。

数据处理——计算滑动平均数(长短期):
def getRolling(data,shortPeriod,longPeriod):
  for i in range(len(data.ix[1])):
    col=data.ix[:,i]
    name=data.columns[i]
    data[name+'_'+str(shortPeriod)+'days']=col.rolling(window=shortPeriod).mean()
    data[name+'_'+str(longPeriod)+'days']=col.rolling(window=longPeriod).mean()
  return data

主体计算过程:由于每次仅用总资金额的一半即n/2,而且购买后第二天卖出,因此每次购买时资金额都是充足。而且每一支股票投入相同资金,因此直接将所有股票当天收益率取平均作为当天整体的收益率。循环过程从20天滑动平均收盘价开始,第i天作为得到信息的时间,第i+1天购入,第i+2天卖出。

def calculate(data,benchmark_price,stocks):
  #初始化累积收益率等
  IRR=1
  benchmark_revenue=1
  victories=0
  profits=[]
  benchmark_profits=[]
  #计算交易的日数
  length=0
  for i in range(len(data.index)-2):
    portfolio=[]
    print(data.index[i])
    for j,company in enumerate(stocks):
      if(data.ix[i,company+'_5days']>data.ix[i,company+'_20days']):
        portfolio.append(company)
    #如果当天存在出现该信号的公司,则购买
    if(portfolio):
      length=length+1
      #计算策略当日收益率
      new=data.ix[i+2,portfolio]/data.ix[i+1,portfolio]
      #计算大盘当日收益率
      benchmark_new=benchmark_price.ix[i+2]/benchmark_price.ix[i+1]
      benchmark_revenue=benchmark_revenue*benchmark_new
      data.ix[i+2,'benchmark_profits']=benchmark_revenue
      #计算胜率
      if(new.mean()>(benchmark_new)):
        victories=victories+1
      #固定印花说0.1%,手续费0.08%和滑点0.246%
      IRR=IRR*(new.mean()-(0.001+0.0008+0.00246))
      data.ix[i+2,'profits']=IRR
      
      
    #如果当天不出现信号,策略的累计收益率不变,大盘的依然要更新
    else:
      data.ix[i+2,'profits']=data.ix[i+1,'profits']
      benchmark_new=benchmark_price.ix[i+2]/benchmark_price.ix[i+1]
      benchmark_revenue=benchmark_revenue*benchmark_new
      data.ix[i+2,'benchmark_profits']=benchmark_revenue
  #计算最大回撤
  Max_drawdown=max_drawdown(data['profits'])
  #为了便于画图,我先将策略累计收益率和大盘结合
  plotData=pd.concat([data['profits'],data['benchmark_profits']],axis=1)
  plotData.plot()
  #计算夏普率
  Sharpo_Ratio=Sharpo(data['profits'],data['benchmark_profits'])
  return [IRR,victories/length,Sharpo_Ratio,Max_drawdown]

最大回撤计算:


def max_drawdown(timeseries):
    max=-100
    for i in np.arange(0,len(timeseries)-1,1):
      for j in np.arange(i,len(timeseries),1):
        if((timeseries.iloc[i]-timeseries.iloc[j])/timeseries.iloc[j])>max:
          max=(timeseries.iloc[i]-timeseries.iloc[j])/timeseries.iloc[j] 
    return max

夏普率:
def Sharpo(strategy_profits,benchmark_profits):
  Sharpo_ratio=(np.mean(strategy_profits)-np.mean(benchmark_profits))/np.std(strategy_profits)
  return Sharpo_ratio*np.sqrt(252)

胜率计算:VictoryRatio=日收益率大于大盘的日数\策略真正交易的日数

讲解结束,全部代码如下:

import pandas as pd
import numpy as np



def max_drawdown(timeseries):
    max=-100
    for i in np.arange(0,len(timeseries)-1,1):
      for j in np.arange(i,len(timeseries),1):
        if((timeseries.iloc[i]-timeseries.iloc[j])/timeseries.iloc[j])>max:
          max=(timeseries.iloc[i]-timeseries.iloc[j])/timeseries.iloc[j] 
    return max

def Sharpo(strategy_profits,benchmark_profits):
  Sharpo_ratio=(np.mean(strategy_profits)-np.mean(benchmark_profits))/np.std(strategy_profits)
  return Sharpo_ratio

def getPrice():
  stocks_name='沪深300'
  start_date='2016-10-01'
  end_date='2017-10-13'
  fields=['ClosingPx']
  #选择沪深300里面所有的股票代码
  stocks=index_components(stocks_name)
  #正式获取股票价格(不过不知道为什么只能从2016-10-10开始)
  stock_price=get_price(stocks,start_date=start_date,end_date=end_date,fields=fields)
  #获得沪深300指数(对样本空间内股票在最近一年(新股为上市以来)的日均成交金额由高到低进行排名,剔除排名在后50%的股票,然后对剩余股票按照日均总市值由高到低进行排名,选取排名在前300名的股票作为指数样本。)
  benchmark_name='399300.XSHE'
  benchmark_price=get_price(benchmark_name,start_date=start_date,end_date=end_date,fields=fields)
  return stock_price,benchmark_price,stocks


def getRolling(data,shortPeriod,longPeriod):
  for i in range(len(data.ix[1])):
    col=data.ix[:,i]
    name=data.columns[i]
    data[name+'_'+str(shortPeriod)+'days']=col.rolling(window=shortPeriod).mean()
    data[name+'_'+str(longPeriod)+'days']=col.rolling(window=longPeriod).mean()
  return data


def calculate(data,benchmark_price,stocks):
  #初始化累积收益率等
  IRR=1
  benchmark_revenue=1
  victories=0
  profits=[]
  benchmark_profits=[]
  #计算交易的日数
  length=0
  for i in range(len(data.index)-2):
    portfolio=[]
    print(data.index[i])
    for j,company in enumerate(stocks):
      if(data.ix[i,company+'_5days']>data.ix[i,company+'_20days']):
        portfolio.append(company)
    #如果当天存在出现该信号的公司,则购买
    if(portfolio):
      length=length+1
      #计算策略当日收益率
      new=data.ix[i+2,portfolio]/data.ix[i+1,portfolio]
      #计算大盘当日收益率
      benchmark_new=benchmark_price.ix[i+2]/benchmark_price.ix[i+1]
      benchmark_revenue=benchmark_revenue*benchmark_new
      data.ix[i+2,'benchmark_profits']=benchmark_revenue
      #计算胜率
      if(new.mean()>(benchmark_new)):
        victories=victories+1
      #固定印花说0.1%,手续费0.08%和滑点0.246%
      IRR=IRR*(new.mean()-(0.001+0.0008+0.00246))
      data.ix[i+2,'profits']=IRR
      
      
    #如果当天不出现信号,策略的累计收益率不变,大盘的依然要更新
    else:
      data.ix[i+2,'profits']=data.ix[i+1,'profits']
      benchmark_new=benchmark_price.ix[i+2]/benchmark_price.ix[i+1]
      benchmark_revenue=benchmark_revenue*benchmark_new
      data.ix[i+2,'benchmark_profits']=benchmark_revenue
  #计算最大回撤
  Max_drawdown=max_drawdown(data['profits'])
  #为了便于画图,我先将策略累计收益率和大盘结合
  plotData=pd.concat([data['profits'],data['benchmark_profits']],axis=1)
  plotData.plot()
  #计算夏普率
  Sharpo_Ratio=Sharpo(data['profits'],data['benchmark_profits'])
  return [IRR,victories/length,Sharpo_Ratio,Max_drawdown]

stock_price,benchmark_price,stocks=getPrice()
stock_price_rolling=getRolling(stock_price,5,20)  
IRR,victories,Sharpo_Ratio,Max_drawdown=calculate(stock_price_rolling[19:],benchmark_price[19:],stocks)

print([IRR,victories,Sharpo_Ratio*np.sqrt(252),Max_drawdown])

输出结果为:[0.42318161427509665, 0.5043859649122807, -37.242237755917365, 1.395223166619767]
最后计算得到年化收益率为-57.7%,胜率为50.4%,夏普率为-37.2,最大回撤为139.5%
策略和大盘的累计收益图片:

滑动平均累积收益率(加入交易成本).png

忽略了交易成本,输出结果为:[1.1195674560662801, 0.5043859649122807, -8.8431527706262312, 0.092890344378694187]
最后计算得到年化收益率为11.9%,胜率为50.4%,夏普率为-8.84,最大回撤为9.2%

滑动平均累积收益率(不加入交易成本).png

总结:从不计交易成本的输出结果我们可以看出,最终策略的累积收益率还是跑不赢大盘,策略本身对于累积收益率提升的贡献并不大。同时由于策略交易过于频繁,持有时间过于短暂(一天),每次交易的获益基本都会被交易成本吸收掉,导致累积收益率一路走低。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,802评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,109评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,683评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,458评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,452评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,505评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,901评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,550评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,763评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,556评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,629评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,330评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,898评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,897评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,140评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,807评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,339评论 2 342

推荐阅读更多精彩内容

  • 1 走过城镇,人群喧嚣着金迷如故。越过山林,千峰默默的层峦依旧。星绕炙炎,生万物却无你。量子构勒不出你的影子,元素...
    小汐妞儿阅读 1,980评论 4 3
  • 我这人有个缺点,就是一直在说话方面没有什么天赋,都是心里面想什么就直接说出来,有时候常常得罪人了,而自己却还蒙在鼓...
    清晨绿燕阅读 678评论 0 2
  • 1、只观察不评判 对待朋友,同级的人,无关紧要的人 2、直说感受,就是聊事,不表达情绪 3、对同级别的人,或者下级...
    剽悍的娃哥阅读 231评论 0 0
  • "谦虚使人进步,骄傲使人落后。"这两句话老马时刻牢记着。可就是因为谦虚,让他郁闷得很。为啥?这话还得从头说...
    言行合一阅读 266评论 0 0