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 网站会帮助你。如果你需要的话,请访问它:

没有 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

``````python def start(self):

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%
.. raw:: html
曾经担任的所有职位 最高
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)