实现一个简单的指数定投策略,写一个回测脚本验证一下。

策略

  1. 定投创业板指数(场内ETF,或场外指数基金)
  2. 一般的定投每期固定金额,而这里采取更激进的方法,每期定投金额随指数降低而增加,即越跌越投(策略详见脚本)

获取指数历史行情
如何获取历史行情数据? 由于我们是按月定投,只需要获取按月K线图即可。有付费软件和网站当然可以获取历史行情数据,但作为一个打游戏从来不充钱的人,本着能不花钱就不花钱的原则,在网上努力找到了免费的资源。
在浏览器输入https://q.stock.sohu.com/hisHq?code=zs_399006&start=20210101&end=20210825&stat=1&order=A&period=m&callback=historySearchHandler&rt=jsonp&r=0.8391495715053367&0.9677250558488026

  • code:合约代码,399006就是深证创业板指数代码
  • star, end 分别代表开始、结束日期
  • order 表示排序方式,Asce升序
  • period 周期?month月K

获得数据格式如下:

historySearchHandler([{"status":0,"hq":[["2021-01-29","2977.32","3128.87","162.61","5.48%","2963.41","3427.44","482663424","212739920.00","-"],["2021-02-26","3134.79","2914.11","-214.76","-6.86%","2887.81","3476.00","327237024","139207552.00","-"],["2021-03-31","2958.53","2758.50","-155.61","-5.34%","2603.94","3023.21","393472384","150975600.00","-"],["2021-04-30","2766.42","3091.40","332.90","12.07%","2711.84","3110.16","318636480","125344720.00","-"],["2021-05-31","3059.58","3309.07","217.67","7.04%","2864.05","3309.75","293336448","123553176.00","-"],["2021-06-30","3290.53","3477.18","168.11","5.08%","3122.81","3484.19","362395232","159291744.00","-"],["2021-07-30","3486.78","3440.18","-37.00","-1.06%","3157.99","3576.12","453730144","222532864.00","-"],["2021-08-25","3448.26","3348.66","-91.52","-2.66%","3162.76","3569.84","373719520","182940192.00","-"]],"code":"zs_399006","stat":["累计:","2021-01-29至2021-08-25","382.40","12.89%",2603.94,3576.12,3005190656,1316585768,"-"]}])

简单处理一下为Json格式:

{"status":0,"hq":[["2021-01-29","2977.32","3128.87","162.61","5.48%","2963.41","3427.44","482663424","212739920.00","-"],["2021-02-26","3134.79","2914.11","-214.76","-6.86%","2887.81","3476.00","327237024","139207552.00","-"],["2021-03-31","2958.53","2758.50","-155.61","-5.34%","2603.94","3023.21","393472384","150975600.00","-"],["2021-04-30","2766.42","3091.40","332.90","12.07%","2711.84","3110.16","318636480","125344720.00","-"],["2021-05-31","3059.58","3309.07","217.67","7.04%","2864.05","3309.75","293336448","123553176.00","-"],["2021-06-30","3290.53","3477.18","168.11","5.08%","3122.81","3484.19","362395232","159291744.00","-"],["2021-07-30","3486.78","3440.18","-37.00","-1.06%","3157.99","3576.12","453730144","222532864.00","-"],["2021-08-25","3448.26","3348.66","-91.52","-2.66%","3162.76","3569.84","373719520","182940192.00","-"]],"code":"zs_399006","stat":["累计:","2021-01-29至2021-08-25","382.40","12.89%",2603.94,3576.12,3005190656,1316585768,"-"]}

python脚本实现

#!/usr/bin/python3

import sys
import json
import csv

dates = []
prices = []
baseAmt = 1000.0 
maxAmt = 10000.0

with open('399006.month.json', 'r') as f:
    data = json.load(f)
    print("data['status']:", data['status'])
    print("data['code']:", data['code'])
    #print("data['hq'][0][1]:", data['hq'][0][1])

    for hq in data['hq']:
        dates.append(hq[0])
        prices.append(float(hq[2]))

    if len(dates) != len(prices):
        print("error: len not equal!")
        sys.exit()

#    for i in range(len(dates)):
#        print("%s %.2f" % (dates[i], prices[i]) )

def quant(start, end, strategy):
    res = [] # totalAmt, pnl, maxPnl, minPnl
    basePx = 0.0
    amounts = []
    totalVol = 0.0
    totalAmt = 0.0
    pnl = 0.0
    maxPnl = 0.0
    minPnl = 100.0
    if start > end or start > len(dates) or start < 0:
        return [0,0,0,0]
    i = start - 1
    while i < end and i < len(dates):
        basePx = prices[i] if prices[i] > basePx else basePx
        if strategy == "fixed":
            amt = baseAmt
        elif strategy == "linear":
            amt = baseAmt * ( basePx / prices[i] )
        elif strategy == "square":
            amt = baseAmt * ( basePx / prices[i] ) ** 2
        else:
            print("unknown strategy: ", strategy)
            return [0,0,0,0]
        amt = maxAmt if amt > maxAmt else amt
        amounts.append(amt)
        totalAmt += amt
        vol = amt / prices[i]
        totalVol += vol
        pnl = (totalVol * prices[i] - totalAmt) / totalAmt
        maxPnl = pnl if pnl > maxPnl else maxPnl
        minPnl = pnl if pnl < minPnl else minPnl      
#        print("%s  %.2f %.2f %.2f %.2f %.2f" %(dates[i], prices[i], basePx, amt, vol, pnl*100))
        i += 1
    return [totalAmt, totalVol, pnl]

def main():
    begin = 1
    header = ['beginDate','beginPrice','endPrice','totalAmt','totalVol','pnl','totalAmt','totalVol','pnl','totalAmt','totalVol','pnl']
    rows = []
    with open('result.csv', 'w') as f:
        f_csv = csv.writer(f)
        f_csv.writerow(header)
        while begin <= len(dates)-36:
            row = [dates[begin-1],prices[begin-1],prices[begin+35]]
            row += quant(begin,begin+35,"fixed")
            row += quant(begin,begin+35,"linear")
            row += quant(begin,begin+35,"square")
            rows += row
            print(row);
            begin += 1
            f_csv.writerow(row) 
'''
    res = quant(1,36,"linear")
    print("totalAmt:", res[0])
    print("     PNL:", res[1]*100)
    print("  maxPNL:", res[2]*100)
    print("  minPNL:", res[3]*100)
    res = quant(1,36,"square")
    print("totalAmt:", res[0])
    print("     PNL:", res[1]*100)
    print("  maxPNL:", res[2]*100)
    print("  minPNL:", res[3]*100)
''' 

if __name__=="__main__":
    main()

结果:
固定定投36个月
固定定投36个月定投至今
定投至今

Ref:
https://www.zhihu.com/question/22145919

Logo

加入社区!打开量化的大门,首批课程上线啦!

更多推荐