#滑動平均策略——python回測結(jié)果 (中山大學(xué)嶺南學(xué)院量化投資協(xié)會)


策略如下:

  • 回測區(qū)間為2016年10月10日至2017年10月13日,選擇滬深300進(jìn)行回測。
  • 記錄所有當(dāng)天5日滑動平均價格高于20日滑動平均價格的股票
  • 將總資金額的一半n/2用于買入股票,每一支股票按照等額購入
  • 買入后的第二天清倉

回測過程:
數(shù)據(jù)獲?。ǐ@取股票收盤價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指數(shù)
  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指數(shù)解釋一下:對樣本空間內(nèi)股票在最近一年(新股為上市以來)的日均成交金額由高到低進(jìn)行排名,剔除排名在后50%的股票,然后對剩余股票按照日均總市值由高到低進(jìn)行排名,選取排名在前300名的股票作為指數(shù)樣本。

數(shù)據(jù)處理——計算滑動平均數(shù)(長短期):
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,而且購買后第二天賣出,因此每次購買時資金額都是充足。而且每一支股票投入相同資金,因此直接將所有股票當(dāng)天收益率取平均作為當(dāng)天整體的收益率。循環(huán)過程從20天滑動平均收盤價開始,第i天作為得到信息的時間,第i+1天購入,第i+2天賣出。

def calculate(data,benchmark_price,stocks):
  #初始化累積收益率等
  IRR=1
  benchmark_revenue=1
  victories=0
  profits=[]
  benchmark_profits=[]
  #計算交易的日數(shù)
  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)
    #如果當(dāng)天存在出現(xiàn)該信號的公司,則購買
    if(portfolio):
      length=length+1
      #計算策略當(dāng)日收益率
      new=data.ix[i+2,portfolio]/data.ix[i+1,portfolio]
      #計算大盤當(dāng)日收益率
      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%,手續(xù)費0.08%和滑點0.246%
      IRR=IRR*(new.mean()-(0.001+0.0008+0.00246))
      data.ix[i+2,'profits']=IRR
      
      
    #如果當(dāng)天不出現(xiàn)信號,策略的累計收益率不變,大盤的依然要更新
    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'])
  #為了便于畫圖,我先將策略累計收益率和大盤結(jié)合
  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=日收益率大于大盤的日數(shù)\策略真正交易的日數(shù)

講解結(jié)束,全部代碼如下:

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指數(shù)(對樣本空間內(nèi)股票在最近一年(新股為上市以來)的日均成交金額由高到低進(jìn)行排名,剔除排名在后50%的股票,然后對剩余股票按照日均總市值由高到低進(jìn)行排名,選取排名在前300名的股票作為指數(shù)樣本。)
  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=[]
  #計算交易的日數(shù)
  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)
    #如果當(dāng)天存在出現(xiàn)該信號的公司,則購買
    if(portfolio):
      length=length+1
      #計算策略當(dāng)日收益率
      new=data.ix[i+2,portfolio]/data.ix[i+1,portfolio]
      #計算大盤當(dāng)日收益率
      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%,手續(xù)費0.08%和滑點0.246%
      IRR=IRR*(new.mean()-(0.001+0.0008+0.00246))
      data.ix[i+2,'profits']=IRR
      
      
    #如果當(dāng)天不出現(xiàn)信號,策略的累計收益率不變,大盤的依然要更新
    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'])
  #為了便于畫圖,我先將策略累計收益率和大盤結(jié)合
  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])

輸出結(jié)果為:[0.42318161427509665, 0.5043859649122807, -37.242237755917365, 1.395223166619767]
最后計算得到年化收益率為-57.7%,勝率為50.4%,夏普率為-37.2,最大回撤為139.5%
策略和大盤的累計收益圖片:

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

忽略了交易成本,輸出結(jié)果為:[1.1195674560662801, 0.5043859649122807, -8.8431527706262312, 0.092890344378694187]
最后計算得到年化收益率為11.9%,勝率為50.4%,夏普率為-8.84,最大回撤為9.2%

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

總結(jié):從不計交易成本的輸出結(jié)果我們可以看出,最終策略的累積收益率還是跑不贏大盤,策略本身對于累積收益率提升的貢獻(xiàn)并不大。同時由于策略交易過于頻繁,持有時間過于短暫(一天),每次交易的獲益基本都會被交易成本吸收掉,導(dǎo)致累積收益率一路走低。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 1 走過城鎮(zhèn),人群喧囂著金迷如故。越過山林,千峰默默的層巒依舊。星繞炙炎,生萬物卻無你。量子構(gòu)勒不出你的影子,元素...
    小汐妞兒閱讀 2,044評論 4 3
  • 我這人有個缺點,就是一直在說話方面沒有什么天賦,都是心里面想什么就直接說出來,有時候常常得罪人了,而自己卻還蒙在鼓...
    清晨綠燕閱讀 776評論 0 2
  • 1、只觀察不評判 對待朋友,同級的人,無關(guān)緊要的人 2、直說感受,就是聊事,不表達(dá)情緒 3、對同級別的人,或者下級...
    剽悍的娃哥閱讀 302評論 0 0
  • "謙虛使人進(jìn)步,驕傲使人落后。"這兩句話老馬時刻牢記著??删褪且驗橹t虛,讓他郁悶得很。為啥?這話還得從頭說...
    言行合一閱讀 311評論 0 0

友情鏈接更多精彩內(nèi)容