Skip to content
All library documents

Configuring Time-Period Returns and Sharpe Ratio Analysis in Backtrader

Code backtrader

Summary

This Backtrader example runs a simple moving-average crossover strategy on a CSV price feed and attaches analyzers for periodic returns and the Sharpe ratio. The user can choose daily, weekly, monthly, or yearly analysis periods, set starting cash and date bounds, and optionally write results or plot the data. Sharpe settings include annualization, a risk-free rate, a conversion factor, sample standard deviation, and whether to convert the risk-free rate to the selected timeframe.

The script illustrates how to configure and run these analyzers; it does not report results or evaluate the strategy's effectiveness. Interpretation depends on the input data, chosen timeframe, and Sharpe assumptions, including risk-free-rate conversion and standard deviation treatment. The example also relies on Backtrader's built-in crossover strategy, so it is principally a template for performance measurement rather than a new trading method. No transaction-cost model or other validation procedure is specified.

Key ideas

  • The example calculates periodic returns and a Sharpe ratio for a moving-average crossover strategy.
  • Return and Sharpe analysis can use daily, weekly, monthly, or yearly periods.
  • Optional settings control annualization and the treatment of the risk-free rate and return variability.
  • The script demonstrates analyzer setup but provides no strategy results or evidence of profitability.

Tags

Full text
# sharpe-timereturn.py


```py
#!/usr/bin/env python
# -*- coding: utf-8; py-indent-offset:4 -*-
###############################################################################
#
# Copyright (C) 2015-2023 Daniel Rodriguez
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
###############################################################################
from __future__ import (absolute_import, division, print_function,
                        unicode_literals)

import argparse
import datetime

import backtrader as bt


def runstrat(pargs=None):
    args = parse_args(pargs)

    # Create a cerebro
    cerebro = bt.Cerebro()

    if args.cash is not None:
        cerebro.broker.set_cash(args.cash)

    # Get the dates from the args
    fromdate = datetime.datetime.strptime(args.fromdate, '%Y-%m-%d')
    todate = datetime.datetime.strptime(args.todate, '%Y-%m-%d')

    # Create the 1st data
    data = bt.feeds.BacktraderCSVData(
        dataname=args.data,
        fromdate=fromdate,
        todate=todate)

    cerebro.adddata(data)  # Add the data to cerebro

    # Add the strategy
    cerebro.addstrategy(bt.strategies.SMA_CrossOver)

    tframes = dict(
        days=bt.TimeFrame.Days,
        weeks=bt.TimeFrame.Weeks,
        months=bt.TimeFrame.Months,
        years=bt.TimeFrame.Years)

    # Add the Analyzers
    cerebro.addanalyzer(bt.analyzers.TimeReturn,
                        timeframe=tframes[args.tframe])

    shkwargs = dict()
    if args.annualize:
        shkwargs['annualize'] = True

    if args.riskfreerate is not None:
        shkwargs['riskfreerate'] = args.riskfreerate

    if args.factor is not None:
        shkwargs['factor'] = args.factor

    if args.stddev_sample:
        shkwargs['stddev_sample'] = True

    if args.no_convertrate:
        shkwargs['convertrate'] = False

    cerebro.addanalyzer(bt.analyzers.SharpeRatio,
                        timeframe=tframes[args.tframe],
                        **shkwargs)

    # Add a writer to get output
    cerebro.addwriter(bt.WriterFile, csv=args.writercsv, rounding=4)

    cerebro.run()  # And run it

    # Plot if requested
    if args.plot:
        pkwargs = dict(style='bar')
        if args.plot is not True:  # evals to True but is not True
            npkwargs = eval('dict(' + args.plot + ')')  # args were passed
            pkwargs.update(npkwargs)

        cerebro.plot(**pkwargs)


def parse_args(pargs=None):
    parser = argparse.ArgumentParser(
        formatter_class=argparse.ArgumentDefaultsHelpFormatter,
        description='TimeReturns and SharpeRatio')

    parser.add_argument('--data', '-d',
                        default='../../datas/2005-2006-day-001.txt',
                        help='data to add to the system')

    parser.add_argument('--cash', default=None, type=float, required=False,
                        help='Starting Cash')

    parser.add_argument('--fromdate', '-f',
                        default='2005-01-01',
                        help='Starting date in YYYY-MM-DD format')

    parser.add_argument('--todate', '-t',
                        default='2006-12-31',
                        help='Starting date in YYYY-MM-DD format')

    parser.add_argument('--writercsv', '-wcsv', action='store_true',
                        help='Tell the writer to produce a csv stream')

    parser.add_argument('--tframe', '--timeframe', default='years',
                        required=False,
                        choices=['days', 'weeks', 'months', 'years'],
                        help='TimeFrame for the Returns/Sharpe calculations')

    parser.add_argument('--annualize', required=False, action='store_true',
                        help='Annualize Sharpe Ratio')

    parser.add_argument('--riskfreerate', required=False, action='store',
                        type=float, default=None,
                        help='Riskfree Rate (annual) for Sharpe')

    parser.add_argument('--factor', required=False, action='store',
                        type=float, default=None,
                        help=('Riskfree Rate conversion factor for Sharpe '
                              'to downgrade riskfree rate to timeframe'))

    parser.add_argument('--stddev-sample', required=False, action='store_true',
                        help='Consider Bessels correction for stddeviation')

    parser.add_argument('--no-convertrate', required=False,
                        action='store_true',
                        help=('Upgrade returns to target timeframe rather than'
                              'downgrading the riskfreerate'))

    # Plot options
    parser.add_argument('--plot', '-p', nargs='?', required=False,
                        metavar='kwargs', const=True,
                        help=('Plot the read data applying any kwargs passed\n'
                              '\n'
                              'For example:\n'
                              '\n'
                              '  --plot style="candle" (to plot candles)\n'))

    if pargs is not None:
        return parser.parse_args(pargs)

    return parser.parse_args()


if __name__ == '__main__':
    runstrat()

```

Shown in full with attribution under the source's licence. Licence: GPL-3.0

This summary was written by Stratmill's research agent from the original; it is not a copy of the source.