上文我们写了第一个简单的策略,买入并持有指数基金。
我们通过回测发现,指数基金在中国是否能取得很好的收益,本质上是存疑的。
那有的同学可能说,那我们使用定投策略投资指数基金的话会怎么样呢?
假设我们每月投入1万元购买指数基金,期限从2010-1-1到2019-12-31。
我们回测下这个数据。
我们可能想图形化展示下我们的投资过程,各种指标的变化情况。
backtrader提供了基本的图形能力。
backtrader的图形化依赖于mataplotlib,因此必须安装该lib。
cerebo.plot()
显示效果如下:
backtrader提供了analyzer工具,可以用来对投资过程进行分析。
比如我们熟知的sharp ratio,买卖次数,回撤情况等等。
可以在回测运行前通过addanalyzer增加分析器,然后回测后查看分析结果。
# Analyzer
cerebro.addanalyzer(btanalyzers.SharpeRatio, _name='mysharpe')
cerebro.addanalyzer(btanalyzers.AnnualReturn, _name='myannual')
cerebro.addanalyzer(btanalyzers.DrawDown, _name='mydrawdown')
# Run over everything
thestrats = cerebro.run()
thestrat = thestrats[0]
print('Sharpe Ratio:', thestrat.analyzers.mysharpe.get_analysis())
print('Annual Return:', thestrat.analyzers.myannual.get_analysis())
print('Drawdown Info:', thestrat.analyzers.mydrawdown.get_analysis())
分析结果如下:
$python example3.py
Starting Portfolio Value: 1200000.00
Final Portfolio Value: 1335469.29
Sharpe Ratio: OrderedDict([('sharperatio', 0.08794344140568065)])
Annual Return: OrderedDict([(2010, 0.009994798081538692), (2011, -0.06385257455175308), (2012, -0.001059256049693591), (2013, 0.0513281370753218), (2014, 0.18051195171848144), (2015, 0.2553421396965294), (2016, -0.12786696056384306), (2017, -0.0024386306434659444), (2018, -0.2973329165832902), (2019, 0.23710565581150012)])
Drawdown Info: AutoOrderedDict([('len', 1110), ('drawdown', 44.11989120158515), ('moneydown', 1054413.8328226043), ('max', AutoOrderedDict([('len', 1110), ('drawdown', 56.18897640055283), ('moneydown', 1342850.8628497515)]))])
从回测结果可以看出来,十年的收益可以说很一般,即使算XIRR收益估计也就是和货币基金差不多。
另外从analyzer数据看回撤也非常之大。
#!/usr/bin/env python
# -*- coding: utf-8; py-indent-offset:4 -*-
"""计算定投某个标的的盈利情况"""
from __future__ import (absolute_import, division, print_function,
unicode_literals)
import datetime # For datetime objects
# Import the backtrader platform
import backtrader as bt
import backtrader.analyzers as btanalyzers
import backtrader.feeds as btfeed
import click
# Create a Stratey
class TestStrategy(bt.Strategy):
def log(self, txt, dt=None):
''' Logging function fot this strategy'''
dt = dt or self.datas[0].datetime.date(0)
print('%s, %s' % (dt.isoformat(), txt))
def __init__(self):
# Keep a reference to the "close" line in the data[0] dataseries
self.dataclose = self.datas[0].close
# To keep track of pending orders
self.month = -1
def next(self):
# Simply log the closing price of the series from the reference
# 每月进行购买一次
if self.month != self.datas[0].datetime.date(0).month:
self.month = self.datas[0].datetime.date(0).month
self.buy(size=10000/self.dataclose[0])
class TSCSVData(btfeed.GenericCSVData):
params = (
("fromdate", datetime.datetime(2010, 1, 1)),
("todate", datetime.datetime(2019, 12, 31)),
('nullvalue', 0.0),
('dtformat', ('%Y-%m-%d')),
('openinterest', -1)
)
def backtest():
filename = "bs_sh.000905.csv"
cash = 1200000.0
# Create a cerebro entity
cerebro = bt.Cerebro()
# Add a strategy
cerebro.addstrategy(TestStrategy)
# Create a Data Feed
data = TSCSVData(dataname="./datas/{0}".format(filename))
# Add the Data Feed to Cerebro
cerebro.adddata(data)
# Set our desired cash start
cerebro.broker.setcash(cash)
# Print out the starting conditions
print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())
# Analyzer
cerebro.addanalyzer(btanalyzers.SharpeRatio, _name='mysharpe')
cerebro.addanalyzer(btanalyzers.AnnualReturn, _name='myannual')
cerebro.addanalyzer(btanalyzers.DrawDown, _name='mydrawdown')
# Run over everything
thestrats = cerebro.run()
# Print out the final result
print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
thestrat = thestrats[0]
print('Sharpe Ratio:', thestrat.analyzers.mysharpe.get_analysis())
print('Annual Return:', thestrat.analyzers.myannual.get_analysis())
print('Drawdown Info:', thestrat.analyzers.mydrawdown.get_analysis())
cerebro.plot()
if __name__ == '__main__':
backtest()