Pyfolio 集成#
Ticket #108 <https://github.com/mementum/backtrader/issues/108> _ 中提到了 portfolio 工具(pyfolio)的集成。
初步查看教程发现,由于“zipline”和“pyfolio”之间的紧密集成,它似乎有些困难,但实际上, ``pyfolio``提供的一些其他用途的示例*测试*数据非常有用,可以解读背后发生的一切,从而让集成变得更加奇妙。
大多数组件已经就位于 backtrader 中:
分析器基础设施
子分析器
一个TimeReturn分析器
只需要一个主要的``PyFolio``分析器和3个简单的 子分析器 。此外,还需要一个依赖项``pandas``中已经需要的方法。
最具挑战性的部分是…“获取所有正确的依赖项”。
更新``pandas``
更新``numpy``
更新``scikit-learn``
更新``seaborn``
在类Unix环境下,只需要一点时间。在Windows下,即使安装了特定的 Microsoft 编译器(在本例中为 Python 2.7 链),也会失败。但是一个众所周知的带有最新软件包的 Windows 网站会帮助你。如果你需要的话,请访问它:
http://www.lfd.uci.edu/~gohlke/pythonlibs/如果没有完整地进行测试,那么集成就不完整,这就是为什么通常会提供示例样本。
没有 PyFolio#
示例使用 random.randint
来决定何时 买入/* 卖出*,所以这只是一个简单的检查,用于确认一切正常:
$ ./pyfoliotest.py –printout –no-pyfolio –plot
输出结果:
Len,Datetime,Open,High,Low,Close,Volume,OpenInterest 0001,2005-01-03T23:59:59,38.36,38.90,37.65,38.18,25482800.00,0.00 以%23.58的价格购买 1000 股 0002,2005-01-04T23:59:59,38.45,38.54,36.46,36.58,26625300.00,0.00 以%36.58的价格购买 1000 股 以%22.47的价格卖出 500 股 0003,2005-01-05T23:59:59,36.69,36.98,36.06,36.13,18469100.00,0.00 … 以%37.51的价格卖出 500 股 0502,2006-12-28T23:59:59,25.62,25.72,25.30,25.36,11908400.00,0.00 0503,2006-12-29T23:59:59,25.42,25.82,25.33,25.54,16297800.00,0.00 以%17.14的价格卖出 250 股 以%37.01的价格卖出 250 股
有 3 条数据,并且随机选择了多个 买入*和 卖出*操作,在测试运行的默认持续时间为 2 年的期间内散布。
PyFolio 运行结果#
在 Jupyter Notebook 中运行 pyfolio
的结果很好,包括内联绘图。以下是 notebook 的内容注意:``runstrat``得到了空的参数`[]`,以使用默认参数运行并跳过由 notebook 本身传递的参数
`python
%matplotlib inline
`
```python from __future__ import (absolute_import, division, print_function,
unicode_literals)
import argparse import datetime import random
import backtrader as bt
- class St(bt.Strategy):
- params = (
(‘printout’, False), (‘stake’, 1000),
)
- def __init__(self):
pass
- if self.p.printout:
txtfields = list() txtfields.append(‘Len’) txtfields.append(‘日期时间’) txtfields.append(‘开盘价’) txtfields.append(‘最高价’) txtfields.append(‘最低价’) txtfields.append(‘收盘价’) txtfields.append(‘成交量’) txtfields.append(‘持仓量’) print(‘,’.join(txtfields))
- def next(self):
- if self.p.printout:
# 仅打印第一条数据,用于检查程序是否运行正常 txtfields = list() txtfields.append(‘%04d’ % len(self)) txtfields.append(self.data.datetime.datetime(0).isoformat()) txtfields.append(‘%.2f’ % self.data0.open[0]) txtfields.append(‘%.2f’ % self.data0.high[0]) txtfields.append(‘%.2f’ % self.data0.low[0]) txtfields.append(‘%.2f’ % self.data0.close[0]) txtfields.append(‘%.2f’ % self.data0.volume[0]) txtfields.append(‘%.2f’ % self.data0.openinterest[0]) print(‘,’.join(txtfields))
# 数据0 for data in self.datas:
toss = random.randint(1, 10) curpos = self.getposition(data) if curpos.size:
- if toss > 5:
size = curpos.size // 2 self.sell(data=data, size=size) if self.p.printout:
print(‘卖出 {} @%{}’.format(size, data.close[0]))
- elif toss < 5:
self.buy(data=data, size=self.p.stake) if self.p.printout:
print(‘买入 {} @%{}’.format(self.p.stake, data.close[0]))
- def runstrat(args=None):
args = parse_args(args)
cerebro = bt.Cerebro() cerebro.broker.set_cash(args.cash)
dkwargs = dict() if args.fromdate:
fromdate = datetime.datetime.strptime(args.fromdate, ‘%Y-%m-%d’) dkwargs[‘fromdate’] = fromdate
- if args.todate:
todate = datetime.datetime.strptime(args.todate, ‘%Y-%m-%d’) dkwargs[‘todate’] = todate
data0 = bt.feeds.BacktraderCSVData(dataname=args.data0, **dkwargs) cerebro.adddata(data0, name=’数据0’)
data1 = bt.feeds.BacktraderCSVData(dataname=args.data1, **dkwargs) cerebro.adddata(data1, name=’数据1’)
``
python
data2 = bt.feeds.BacktraderCSVData(dataname=args.data2, **dkwargs) cerebro.adddata(data2, name=’Data2’)
cerebro.addstrategy(St, printout=args.printout) if not args.no_pyfolio:
cerebro.addanalyzer(bt.analyzers.PyFolio, _name=’pyfolio’)
results = cerebro.run() if not args.no_pyfolio:
strat = results[0] pyfoliozer = strat.analyzers.getbyname(‘pyfolio’)
returns, positions, transactions, gross_lev = pyfoliozer.get_pf_items() if args.printout:
print(’– 收益率’) print(returns) print(’– 持仓’) print(positions) print(’– 交易记录’) print(transactions) print(’– 总杠杆’) print(gross_lev)
import pyfolio as pf pf.create_full_tear_sheet(
returns, positions=positions, transactions=transactions, gross_lev=gross_lev, live_start_date=’2005-05-01’, round_trips=True)
- if args.plot:
cerebro.plot(style=args.plot_style)
def parse_args(args=None):
- parser = argparse.ArgumentParser(
formatter_class=argparse.ArgumentDefaultsHelpFormatter, description=’Pivot point and cross plotting 示例’)
- parser.add_argument(’–data0’, required=False,
default=’../../datas/yhoo-1996-2015.txt’, help=’要读取的数据’)
- parser.add_argument(’–data1’, required=False,
default=’../../datas/orcl-1995-2014.txt’, help=’要读取的数据’)
- ``````python
- parser.add_argument(’–data2’, required=False,
default=’../../datas/nvda-1999-2014.txt’, help=’要读取的数据’)
- parser.add_argument(’–fromdate’, required=False,
default=’2005-01-01’, help=’起始日期,格式为YYYY-MM-DD’)
- parser.add_argument(’–todate’, required=False,
default=’2006-12-31’, help=’结束日期,格式为YYYY-MM-DD’)
- parser.add_argument(’–printout’, required=False, action=’store_true’,
help=’打印数据行’)
- parser.add_argument(’–cash’, required=False, action=’store’,
type=float, default=50000, help=’起始现金金额’)
- parser.add_argument(’–plot’, required=False, action=’store_true’,
help=’绘制结果’)
- parser.add_argument(’–plot-style’, required=False, action=’store’,
default=’bar’, choices=[‘bar’, ‘candle’, ‘line’], help=’绘图样式’)
- parser.add_argument(’–no-pyfolio’, required=False, action=’store_true’,
help=’不进行pyfolio操作’)
import sys aargs = args if args is not None else sys.argv[1:] return parser.parse_args(aargs)
全部历史数据开始日期:2005年01月03日
全部历史数据结束日期:2006年12月29日
样本外月份:20
回测月份:3
``` <div> <table border=”1” class=”dataframe”>
- <thead>
- <tr style=”text-align: right;”>
<th>性能统计指标</th> <th>全部历史数据</th> <th>回测</th> <th>样本外</th>
</tr>
</thead> <tbody>
- <tr>
<th>年化收益率</th> <td>0.06</td> <td>-0.05</td> <td>0.08</td>
</tr> <tr>
<th>年化波动率</th> <td>0.09</td> <td>0.09</td> <td>0.10</td>
</tr> <tr>
<th>夏普比率</th> <td>0.62</td> <td>-0.55</td> <td>0.83</td>
</tr> <tr>
<th>卡玛比率</th> <td>0.78</td> <td>-1.13</td> <td>1.09</td>
</tr> <tr>
<th>时间序列稳定性</th> <td>0.75</td> <td>-0.47</td> <td>0.70</td>
</tr> <tr>
<th>最大回撤</th> <td>-0.07</td> <td>-0.04</td> <td>-0.07</td>
</tr> <tr>
<th>欧米加比率</th> <td>1.16</td> <td>0.88</td> <td>1.22</td>
</tr> <tr>
<th>索提诺比率</th> <td>0.97</td> <td>-0.76</td> <td>1.33</td>
</tr> <tr>
<th>偏度</th> <td>1.24</td> <td>0.35</td> <td>1.37</td>
</tr> <tr>
<th>峰度</th> <td>12.72</td> <td>5.66</td> <td>13.59</td>
</tr> <tr>
<th>尾部比率</th> <td>0.87</td> <td>0.46</td> <td>0.91</td>
</tr> <tr>
<th>常识比率</th> <td>0.91</td> <td>0.43</td> <td>0.98</td>
</tr> <tr>
<th>信息比率</th> <td>-0.02</td> <td>0.03</td> <td>-0.04</td>
</tr> <tr>
<th>α</th> <td>0.03</td> <td>-0.02</td> <td>0.03</td>
</tr> <tr>
<th>β</th> <td>0.31</td> <td>0.25</td> <td>0.33</td>
</tr>
</tbody>
</table> </div>
``` <div> <table border=”1” class=”dataframe”>
- <thead>
- <tr style=”text-align: right;”>
<th>最大回撤期间</th> <th>净回撤(%)</th> <th>顶峰日期</th> <th>谷底日期</th> <th>恢复日期</th> <th>持续时间</th>
</tr>
</thead> <tbody>
- <tr>
<th>0</th> <td>7.06</td> <td>2005-07-11</td> <td>2006-04-17</td> <td>2006-05-24</td> <td>228</td>
</tr> <tr>
<th>1</th> <td>5.53</td> <td>2005-02-18</td> <td>2005-05-11</td> <td>2005-05-16</td> <td>62</td>
</tr> <tr>
<th>2</th> <td>3.33</td> <td>2006-07-03</td> <td>2006-07-13</td> <td>2006-09-21</td> <td>59</td>
</tr> <tr>
<th>3</th> <td>2.11</td> <td>2006-09-25</td> <td>2006-10-03</td> <td>2006-10-24</td> <td>22</td>
</tr> <tr>
<th>4</th> <td>2.11</td> <td>2006-10-31</td> <td>2006-12-07</td> <td>2006-12-19</td> <td>36</td>
</tr>
</tbody>
</table> </div>.. parsed-literal:
[-0.012 -0.025]
压力事件 | 平均值 | 最小值 | 最大值 |
---|---|---|---|
低波动率牛市 | 0.02% | -2.68% | 4.85% |
所有时期前10个空头仓位 | 最大值 |
---|
所有时期前10个仓位 | 最大值 |
---|---|
数据2 | 93.59% |
数据0 | 80.42% |
数据1 | 34.47% |
曾经担任的所有职位 | 最高 |
---|---|
Data2 | 93.59% |
Data0 | 80.42% |
Data1 | 34.47% |
D:drobinWinPython-64bit-2.7.10.3python-2.7.10.amd64libsite-packagespyfolioplotting.py:1210: FutureWarning: .resample() is now a deferred operation use .resample(...).mean() instead of .resample(...) **kwargs)
- <table border=”1” class=”dataframe”>
- <thead>
- <tr style=”text-align: right;”>
<th>总结统计</th> <th>所有交易</th> <th>多头交易</th>
</tr>
</thead> <tbody>
- <tr>
<th>总交易次数</th> <td>661.00</td> <td>661.00</td>
</tr> <tr>
<th>盈利比例</th> <td>0.53</td> <td>0.53</td>
</tr> <tr>
<th>赢利交易次数</th> <td>350.00</td> <td>350.00</td>
</tr> <tr>
<th>亏损交易次数</th> <td>305.00</td> <td>305.00</td>
</tr> <tr>
<th>持平交易次数</th> <td>6.00</td> <td>6.00</td>
</tr>
</tbody>
</table> </div>
<div> <table border=”1” class=”dataframe”>
- <thead>
- <tr style=”text-align: right;”>
<th>PnL统计</th> <th>所有交易</th> <th>多头交易</th>
</tr>
</thead> <tbody>
- <tr>
<th>总利润</th> <td>$5675.87</td> <td>$5675.87</td>
</tr> <tr>
<th>总盈利</th> <td>$21571.73</td> <td>$21571.73</td>
</tr> <tr>
<th>总亏损</th> <td>$-15895.86</td> <td>$-15895.86</td>
</tr> <tr>
<th>盈利因子</th> <td>$1.36</td> <td>$1.36</td>
</tr> <tr>
<th>平均净利润</th> <td>$8.59</td> <td>$8.59</td>
</tr> <tr>
<th>平均盈利交易</th> <td>$61.63</td> <td>$61.63</td>
</tr> <tr>
<th>平均亏损交易</th> <td>$-52.12</td> <td>$-52.12</td>
</tr> <tr>
<th>平均赢利与亏损比率</th> <td>$1.18</td> <td>$1.18</td>
</tr> <tr>
<th>最大盈利交易</th> <td>$1024.99</td> <td>$1024.99</td>
</tr> <tr>
<th>最大亏损交易</th> <td>$-1155.00</td> <td>$-1155.00</td>
</tr>
</tbody>
</table> </div>
<div> <table border=”1” class=”dataframe”>
- <thead>
- <tr style=”text-align: right;”>
<th>持续时间统计</th> <th>所有交易</th> <th>多头交易</th>
</tr>
</thead> <tbody>
- <tr>
<th>平均持续时间</th> <td>17 天 00:00:00.001512</td> <td>17 天 00:00:00.001512</td>
</tr> <tr>
<th>中位数持续时间</th> <td>16 天 00:00:00</td> <td>16 天 00:00:00</td>
</tr> <tr>
<th>平均每天交易次数</th> <td>11.80</td> <td>11.80</td>
</tr> <tr>
<th>平均每月交易次数</th> <td>247.88</td> <td>247.88</td>
</tr>
</tbody>
</table> </div>
<div> <table border=”1” class=”dataframe”>
- <thead>
- <tr style=”text-align: right;”>
<th>回报统计</th> <th>所有交易</th> <th>多头交易</th>
</tr>
</thead> <tbody>
- <tr>
<th>平均回报率(所有交易)</th> <td>0.02%</td> <td>0.02%</td>
</tr> <tr>
<th>平均回报率(盈利交易)</th> <td>0.12%</td> <td>0.12%</td>
</tr> <tr>
<th>平均回报率(亏损交易)</th> <td>-0.10%</td> <td>-0.10%</td>
</tr> <tr>
<th>中位数回报率(所有交易)</th> <td>0.00%</td> <td>0.00%</td>
</tr> <tr>
<th>中位数回报率(盈利交易)</th> <td>0.02%</td> <td>0.02%</td>
</tr> <tr>
<th>中位数回报率(亏损交易)</th> <td>-0.02%</td> <td>-0.02%</td>
</tr> <tr>
<th>最大盈利交易</th> <td>2.11%</td> <td>2.11%</td>
</tr> <tr>
<th>最大亏损交易</th> <td>-2.37%</td> <td>-2.37%</td>
</tr>
</tbody>
</table> </div><table border=”1” class=”dataframe”>
- <thead>
- <tr style=”text-align: right;”>
<th>符号统计</th> <th>Data0</th> <th>Data1</th> <th>Data2</th>
</tr>
</thead> <tbody>
- <tr>
<th>平均回报所有round_trips</th> <td>-0.02%</td> <td>0.01%</td> <td>0.06%</td>
</tr> <tr>
<th>平均回报盈利</th> <td>0.12%</td> <td>0.05%</td> <td>0.19%</td>
</tr> <tr>
<th>平均回报亏损</th> <td>-0.14%</td> <td>-0.04%</td> <td>-0.14%</td>
</tr> <tr>
<th>中位数回报所有round_trips</th> <td>-0.00%</td> <td>0.00%</td> <td>0.01%</td>
</tr> <tr>
<th>中位数回报盈利</th> <td>0.03%</td> <td>0.01%</td> <td>0.05%</td>
</tr> <tr>
<th>中位数回报亏损</th> <td>-0.02%</td> <td>-0.01%</td> <td>-0.04%</td>
</tr> <tr>
<th>最大盈利交易</th> <td>1.91%</td> <td>0.71%</td> <td>2.11%</td>
</tr> <tr>
<th>最大亏损交易</th> <td>-2.37%</td> <td>-0.64%</td> <td>-0.99%</td>
</tr>
</tbody>
</table>
- <table border=”1” class=”dataframe”>
- <thead>
- <tr style=”text-align: right;”>
<th>可盈利性 (PnL / PnL总计) 每个名称</th> <th>pnl</th>
</tr> <tr>
<th>符号</th> <th></th>
</tr>
</thead> <tbody>
- <tr>
<th>Data2</th> <td>1.11%</td>
</tr> <tr>
<th>Data1</th> <td>0.14%</td>
</tr> <tr>
<th>Data0</th> <td>-0.25%</td>
</tr>
</tbody>
</table>
<matplotlib.figure.Figure at 0x23982b70>.. 缩略图:: output_2_21.png
使用示例:
$ ./pyfoliotest.py --help
用法: pyfoliotest.py [-h] [--data0 DATA0] [--data1 DATA1] [--data2 DATA2]
[--fromdate FROMDATE] [--todate TODATE] [--printout]
[--cash CASH] [--plot] [--plot-style {bar,candle,line}]
[--no-pyfolio]
用于支点点和交叉绘图的示例
可选参数:
-h,-help 显示此帮助消息并退出
--data0 DATA0 要读取的数据(默认值:../../datas/yhoo-1996-2015.txt)
--data1 DATA1 要读取的数据(默认值:../../datas/orcl-1995-2014.txt)
--data2 DATA2 要读取的数据(默认值:../../datas/nvda-1999-2014.txt)
--fromdate FROMDATE 起始日期(YYYY-MM-DD格式,默认值:2005-01-01)
--todate TODATE 结束日期(YYYY-MM-DD格式,默认值:2006-12-31)
--printout 打印数据行(默认值:False)
--cash CASH 起始现金金额(默认值:50000)
--plot 绘制结果(默认值:False)
--plot-style {bar,candle,line}
绘图样式(默认值:bar)
--no-pyfolio 不执行pyfolio相关操作(默认值:False)