For code/output blocks: Use ``` (aka backtick or grave accent) in a single line before and after the block. See: http://commonmark.org/help/

Pyfolio Analyzer Integration Error



  • Afternoon,

    I have tried to run the example from the documents using the pyfolio analyzer integration and I've hit an error I'm not sure how to solve. I have had a look all over the internet and it seems to be an issue with the plots identifying the x axis.

    I had to use my data feed which works when I have used it with other backtrader run throughs because for some reason I couldn't find the sample data. All of my packages are up to date.

    I have attached my code (slightly different from sample) as well as the error message.

    Notebook:

    
    from ib_insync import *
    
    ib = IB()
    ib.connect('127.0.0.1', 7496, clientId=2)
    
    contract = Stock('KGN', 'SMART', 'AUD')
    bars = ib.reqHistoricalData(
                contract,
                endDateTime ='',
                durationStr='100 D',
                barSizeSetting='1 Hour',
                whatToShow='TRADES',
                useRTH=True,
                formatDate=1)
    df = util.df(bars)
    df = df.set_index('date')
    
    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
    
        def start(self):
            if self.p.printout:
                txtfields = list()
                txtfields.append('Len')
                txtfields.append('Datetime')
                txtfields.append('Open')
                txtfields.append('High')
                txtfields.append('Low')
                txtfields.append('Close')
                txtfields.append('Volume')
                txtfields.append('OpenInterest')
                print(','.join(txtfields))
    
        def next(self):
            if self.p.printout:
                # Print only 1st data ... is just a check that things are running
                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))
    
            # Data 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('SELL {} @%{}'.format(size, data.close[0]))
    
                elif toss < 5:
                    self.buy(data=data, size=self.p.stake)
                    if self.p.printout:
                        print('BUY  {} @%{}'.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.PandasData(dataname=df)
        cerebro.adddata(data0, name='d0')
    
        
    
        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('-- RETURNS')
                print(returns)
                print('-- POSITIONS')
                print(positions)
                print('-- TRANSACTIONS')
                print(transactions)
                print('-- GROSS LEVERAGE')
                print(gross_lev)
    
            import pyfolio as pf
            pf.create_full_tear_sheet(
                returns,
                positions=positions)
    
        if args.plot:
            cerebro.plot(style=args.plot_style)
    
    
    def parse_args(args=None):
    
        parser = argparse.ArgumentParser(
            formatter_class=argparse.ArgumentDefaultsHelpFormatter,
            description='Sample for pivot point and cross plotting')
    
        parser.add_argument('--data0', required=False,
                            default='../../datas/yhoo-1996-2015.txt',
                            help='Data to be read in')
    
    
        parser.add_argument('--fromdate', required=False,
                            default='2005-01-01',
                            help='Starting date in YYYY-MM-DD format')
    
        parser.add_argument('--todate', required=False,
                            default='2006-12-31',
                            help='Ending date in YYYY-MM-DD format')
    
        parser.add_argument('--printout', required=False, action='store_true',
                            help=('Print data lines'))
    
        parser.add_argument('--cash', required=False, action='store',
                            type=float, default=50000,
                            help=('Cash to start with'))
    
        parser.add_argument('--plot', required=False, action='store_true',
                            help=('Plot the result'))
    
        parser.add_argument('--plot-style', required=False, action='store',
                            default='bar', choices=['bar', 'candle', 'line'],
                            help=('Plot style'))
    
        parser.add_argument('--no-pyfolio', required=False, action='store_true',
                            help=('Do not do pyfolio things'))
    
        import sys
        aargs = args if args is not None else sys.argv[1:]
        return parser.parse_args(aargs)
    
    runstrat([])
    

    Error Message:
    C:\Users\Elliot Parker\Anaconda3\lib\site-packages\pyfolio\tears.py:200: UserWarning: The latest version of pyfolio requires users to supply benchmark returns. Your current tearsheets will not include plots and analyses that require a benchmark. In the future, please pass benchmark_rets, or pass None to silence this warning.
    warnings.warn(BENCHMARK_RETS_WARNING)
    Start date 2018-05-17
    End date 2018-10-05
    Total months 5
    Backtest
    Annual return 0.4%
    Cumulative returns 0.2%
    Annual volatility 0.3%
    Sharpe ratio 1.50
    Calmar ratio 11.23
    Stability 0.00
    Max drawdown -0.0%
    Omega ratio 4.36
    Sortino ratio 7.73
    Skew 8.73
    Kurtosis 83.21
    Tail ratio 0.91
    Daily value at risk -0.0%
    Gross leverage 0.00
    C:\Users\Elliot Parker\Anaconda3\lib\site-packages\numpy\core\fromnumeric.py:51: FutureWarning: 'argmin' is deprecated. Use 'idxmin' instead. The behavior of 'argmin' will be corrected to return the positional minimum in the future. Use 'series.values.argmin' to get the position of the minimum now.
    return getattr(obj, method)(*args, **kwds)
    Worst drawdown periods Net drawdown in % Peak date Valley date Recovery date Duration
    0 0.04 2018-05-17 2018-05-18 2018-05-20 2
    1 0.01 2018-06-03 2018-07-30 NaT NaN
    2 0.00 2018-05-23 2018-05-28 2018-06-03 8
    3 0.00 2018-05-21 2018-05-22 2018-05-23 3
    4 0.00 2018-05-17 2018-05-17 2018-05-17 1

    ValueError Traceback (most recent call last)
    <ipython-input-22-03b64962ce57> in <module>()
    ----> 1 runstrat([])

    <ipython-input-21-889ed9e6aa8f> in runstrat(args)
    106 pf.create_full_tear_sheet(
    107 returns,
    --> 108 positions=positions)
    109
    110 if args.plot:

    ~\Anaconda3\lib\site-packages\pyfolio\tears.py in create_full_tear_sheet(returns, positions, transactions, market_data, benchmark_rets, slippage, live_start_date, sector_mappings, bayesian, round_trips, estimate_intraday, hide_positions, cone_std, bootstrap, unadjusted_returns, style_factor_panel, sectors, caps, shares_held, volumes, percentile, turnover_denom, set_context, factor_returns, factor_loadings, pos_in_dollars, header_rows, factor_partitions)
    220 turnover_denom=turnover_denom,
    221 header_rows=header_rows,
    --> 222 set_context=set_context)
    223
    224 create_interesting_times_tear_sheet(returns,

    ~\Anaconda3\lib\site-packages\pyfolio\plotting.py in call_w_context(*args, **kwargs)
    50 if set_context:
    51 with plotting_context(), axes_style():
    ---> 52 return func(*args, **kwargs)
    53 else:
    54 return func(*args, **kwargs)

    ~\Anaconda3\lib\site-packages\pyfolio\tears.py in create_returns_tear_sheet(returns, positions, transactions, live_start_date, cone_std, benchmark_rets, bootstrap, turnover_denom, header_rows, return_fig)
    621
    622 plotting.plot_rolling_sharpe(
    --> 623 returns, ax=ax_rolling_sharpe)
    624
    625 # Drawdowns

    ~\Anaconda3\lib\site-packages\pyfolio\plotting.py in plot_rolling_sharpe(returns, rolling_window, legend_loc, ax, **kwargs)
    989 returns, rolling_window)
    990 rolling_sharpe_ts.plot(alpha=.7, lw=3, color='orangered', ax=ax,
    --> 991 **kwargs)
    992
    993 ax.set_title('Rolling Sharpe ratio (6-month)')

    ~\Anaconda3\lib\site-packages\pandas\plotting_core.py in call(self, kind, ax, figsize, use_index, title, grid, legend, style, logx, logy, loglog, xticks, yticks, xlim, ylim, rot, fontsize, colormap, table, yerr, xerr, label, secondary_y, **kwds)
    2501 colormap=colormap, table=table, yerr=yerr,
    2502 xerr=xerr, label=label, secondary_y=secondary_y,
    -> 2503 **kwds)
    2504 call.doc = plot_series.doc
    2505

    ~\Anaconda3\lib\site-packages\pandas\plotting_core.py in plot_series(data, kind, ax, figsize, use_index, title, grid, legend, style, logx, logy, loglog, xticks, yticks, xlim, ylim, rot, fontsize, colormap, table, yerr, xerr, label, secondary_y, **kwds)
    1925 yerr=yerr, xerr=xerr,
    1926 label=label, secondary_y=secondary_y,
    -> 1927 **kwds)
    1928
    1929

    ~\Anaconda3\lib\site-packages\pandas\plotting_core.py in _plot(data, x, y, subplots, ax, kind, **kwds)
    1727 plot_obj = klass(data, subplots=subplots, ax=ax, kind=kind, **kwds)
    1728
    -> 1729 plot_obj.generate()
    1730 plot_obj.draw()
    1731 return plot_obj.result

    ~\Anaconda3\lib\site-packages\pandas\plotting_core.py in generate(self)
    253 self._add_table()
    254 self._make_legend()
    --> 255 self._adorn_subplots()
    256
    257 for ax in self.axes:

    ~\Anaconda3\lib\site-packages\pandas\plotting_core.py in _adorn_subplots(self)
    427 naxes=nrows * ncols, nrows=nrows,
    428 ncols=ncols, sharex=self.sharex,
    --> 429 sharey=self.sharey)
    430
    431 for ax in self.axes:

    ~\Anaconda3\lib\site-packages\pandas\plotting_tools.py in _handle_shared_axes(axarr, nplots, naxes, nrows, ncols, sharex, sharey)
    315 if sharex or len(ax.get_shared_x_axes()
    316 .get_siblings(ax)) > 1:
    --> 317 _remove_labels_from_axis(ax.xaxis)
    318
    319 except IndexError:

    ~\Anaconda3\lib\site-packages\pandas\plotting_tools.py in _remove_labels_from_axis(axis)
    278
    279 def _remove_labels_from_axis(axis):
    --> 280 for t in axis.get_majorticklabels():
    281 t.set_visible(False)
    282

    ~\Anaconda3\lib\site-packages\matplotlib\axis.py in get_majorticklabels(self)
    1186 def get_majorticklabels(self):
    1187 'Return a list of Text instances for the major ticklabels'
    -> 1188 ticks = self.get_major_ticks()
    1189 labels1 = [tick.label1 for tick in ticks if tick.label1On]
    1190 labels2 = [tick.label2 for tick in ticks if tick.label2On]

    ~\Anaconda3\lib\site-packages\matplotlib\axis.py in get_major_ticks(self, numticks)
    1337 'get the tick instances; grow as necessary'
    1338 if numticks is None:
    -> 1339 numticks = len(self.get_major_locator()())
    1340 if len(self.majorTicks) < numticks:
    1341 # update the new tick label properties from the old

    ~\Anaconda3\lib\site-packages\matplotlib\dates.py in call(self)
    1052 def call(self):
    1053 'Return the locations of the ticks'
    -> 1054 self.refresh()
    1055 return self._locator()
    1056

    ~\Anaconda3\lib\site-packages\matplotlib\dates.py in refresh(self)
    1072 def refresh(self):
    1073 'Refresh internal information based on current limits.'
    -> 1074 dmin, dmax = self.viewlim_to_dt()
    1075 self._locator = self.get_locator(dmin, dmax)
    1076

    ~\Anaconda3\lib\site-packages\matplotlib\dates.py in viewlim_to_dt(self)
    830 vmin, vmax = vmax, vmin
    831
    --> 832 return num2date(vmin, self.tz), num2date(vmax, self.tz)
    833
    834 def _get_unit(self):

    ~\Anaconda3\lib\site-packages\matplotlib\dates.py in num2date(x, tz)
    439 tz = _get_rc_timezone()
    440 if not cbook.iterable(x):
    --> 441 return _from_ordinalf(x, tz)
    442 else:
    443 x = np.asarray(x)

    ~\Anaconda3\lib\site-packages\matplotlib\dates.py in _from_ordinalf(x, tz)
    254
    255 ix = int(x)
    --> 256 dt = datetime.datetime.fromordinal(ix).replace(tzinfo=UTC)
    257
    258 remainder = float(x) - ix

    ValueError: ordinal must be >= 1


    ValueError Traceback (most recent call last)
    ~\Anaconda3\lib\site-packages\IPython\core\formatters.py in call(self, obj)
    330 pass
    331 else:
    --> 332 return printer(obj)
    333 # Finally look for special method names
    334 method = get_real_method(obj, self.print_method)

    ~\Anaconda3\lib\site-packages\IPython\core\pylabtools.py in <lambda>(fig)
    235
    236 if 'png' in formats:
    --> 237 png_formatter.for_type(Figure, lambda fig: print_figure(fig, 'png', **kwargs))
    238 if 'retina' in formats or 'png2x' in formats:
    239 png_formatter.for_type(Figure, lambda fig: retina_figure(fig, **kwargs))

    ~\Anaconda3\lib\site-packages\IPython\core\pylabtools.py in print_figure(fig, fmt, bbox_inches, **kwargs)
    119
    120 bytes_io = BytesIO()
    --> 121 fig.canvas.print_figure(bytes_io, **kw)
    122 data = bytes_io.getvalue()
    123 if fmt == 'svg':

    ~\Anaconda3\lib\site-packages\matplotlib\backend_bases.py in print_figure(self, filename, dpi, facecolor, edgecolor, orientation, format, **kwargs)
    2206 orientation=orientation,
    2207 dryrun=True,
    -> 2208 **kwargs)
    2209 renderer = self.figure._cachedRenderer
    2210 bbox_inches = self.figure.get_tightbbox(renderer)

    ~\Anaconda3\lib\site-packages\matplotlib\backends\backend_agg.py in print_png(self, filename_or_obj, *args, **kwargs)
    505
    506 def print_png(self, filename_or_obj, *args, **kwargs):
    --> 507 FigureCanvasAgg.draw(self)
    508 renderer = self.get_renderer()
    509 original_dpi = renderer.dpi

    ~\Anaconda3\lib\site-packages\matplotlib\backends\backend_agg.py in draw(self)
    428 if toolbar:
    429 toolbar.set_cursor(cursors.WAIT)
    --> 430 self.figure.draw(self.renderer)
    431 finally:
    432 if toolbar:

    ~\Anaconda3\lib\site-packages\matplotlib\artist.py in draw_wrapper(artist, renderer, *args, **kwargs)
    53 renderer.start_filter()
    54
    ---> 55 return draw(artist, renderer, *args, **kwargs)
    56 finally:
    57 if artist.get_agg_filter() is not None:

    ~\Anaconda3\lib\site-packages\matplotlib\figure.py in draw(self, renderer)
    1293
    1294 mimage._draw_list_compositing_images(
    -> 1295 renderer, self, artists, self.suppressComposite)
    1296
    1297 renderer.close_group('figure')

    ~\Anaconda3\lib\site-packages\matplotlib\image.py in _draw_list_compositing_images(renderer, parent, artists, suppress_composite)
    136 if not_composite or not has_images:
    137 for a in artists:
    --> 138 a.draw(renderer)
    139 else:
    140 # Composite any adjacent images together

    ~\Anaconda3\lib\site-packages\matplotlib\artist.py in draw_wrapper(artist, renderer, *args, **kwargs)
    53 renderer.start_filter()
    54
    ---> 55 return draw(artist, renderer, *args, **kwargs)
    56 finally:
    57 if artist.get_agg_filter() is not None:

    ~\Anaconda3\lib\site-packages\matplotlib\axes_base.py in draw(self, renderer, inframe)
    2397 renderer.stop_rasterizing()
    2398
    -> 2399 mimage._draw_list_compositing_images(renderer, self, artists)
    2400
    2401 renderer.close_group('axes')

    ~\Anaconda3\lib\site-packages\matplotlib\image.py in _draw_list_compositing_images(renderer, parent, artists, suppress_composite)
    136 if not_composite or not has_images:
    137 for a in artists:
    --> 138 a.draw(renderer)
    139 else:
    140 # Composite any adjacent images together

    ~\Anaconda3\lib\site-packages\matplotlib\artist.py in draw_wrapper(artist, renderer, *args, **kwargs)
    53 renderer.start_filter()
    54
    ---> 55 return draw(artist, renderer, *args, **kwargs)
    56 finally:
    57 if artist.get_agg_filter() is not None:

    ~\Anaconda3\lib\site-packages\matplotlib\axis.py in draw(self, renderer, *args, **kwargs)
    1131 renderer.open_group(name)
    1132
    -> 1133 ticks_to_draw = self._update_ticks(renderer)
    1134 ticklabelBoxes, ticklabelBoxes2 = self._get_tick_bboxes(ticks_to_draw,
    1135 renderer)

    ~\Anaconda3\lib\site-packages\matplotlib\axis.py in _update_ticks(self, renderer)
    972
    973 interval = self.get_view_interval()
    --> 974 tick_tups = list(self.iter_ticks())
    975 if self._smart_bounds and tick_tups:
    976 # handle inverted limits

    ~\Anaconda3\lib\site-packages\matplotlib\axis.py in iter_ticks(self)
    915 Iterate through all of the major and minor ticks.
    916 """
    --> 917 majorLocs = self.major.locator()
    918 majorTicks = self.get_major_ticks(len(majorLocs))
    919 self.major.formatter.set_locs(majorLocs)

    ~\Anaconda3\lib\site-packages\matplotlib\dates.py in call(self)
    1052 def call(self):
    1053 'Return the locations of the ticks'
    -> 1054 self.refresh()
    1055 return self._locator()
    1056

    ~\Anaconda3\lib\site-packages\matplotlib\dates.py in refresh(self)
    1072 def refresh(self):
    1073 'Refresh internal information based on current limits.'
    -> 1074 dmin, dmax = self.viewlim_to_dt()
    1075 self._locator = self.get_locator(dmin, dmax)
    1076

    ~\Anaconda3\lib\site-packages\matplotlib\dates.py in viewlim_to_dt(self)
    830 vmin, vmax = vmax, vmin
    831
    --> 832 return num2date(vmin, self.tz), num2date(vmax, self.tz)
    833
    834 def _get_unit(self):

    ~\Anaconda3\lib\site-packages\matplotlib\dates.py in num2date(x, tz)
    439 tz = _get_rc_timezone()
    440 if not cbook.iterable(x):
    --> 441 return _from_ordinalf(x, tz)
    442 else:
    443 x = np.asarray(x)

    ~\Anaconda3\lib\site-packages\matplotlib\dates.py in _from_ordinalf(x, tz)
    254
    255 ix = int(x)
    --> 256 dt = datetime.datetime.fromordinal(ix).replace(tzinfo=UTC)
    257
    258 remainder = float(x) - ix

    ValueError: ordinal must be >= 1

    <matplotlib.figure.Figure at 0x1d5546a0>


  • administrators

    From Docs - PyFolio Overview

    Note
    
    As of (at least) 2017-07-25 the pyfolio APIs have changed and create_full_tear_sheet no longer has a gross_lev as a named argument.
    
    Consequently the sample for integration doesn’t work