Skip to content
All library documents

Backtrader Calmar Analysis with a Moving Average Crossover Strategy

Code backtrader

Summary

This Backtrader example defines a long signal from a crossover between a shorter and a longer simple moving average, then attaches the Calmar analyzer to the run. It loads price data from a Yahoo Finance CSV feed, permits optional date bounds, and exposes command-line settings for the broker, position sizer, strategy, engine, and plot.

After the backtest, the script prints the analyzer’s results and can plot the run. The example shows how to wire a risk-adjusted performance analyzer into a signal strategy workflow, but it does not explain the Calmar calculation or report any performance figures. The strategy skeleton also includes an unused method and provides no discussion of costs, data quality, parameter selection, or out-of-sample validation, so it should be treated as an example of framework setup rather than evidence of a profitable approach.

Key ideas

  • The strategy generates a long signal when the shorter simple moving average crosses the longer one.
  • The backtest attaches Backtrader’s Calmar analyzer and prints its output.
  • The data feed, date range, broker, sizing, and strategy settings can be supplied as run options.
  • The script gives no performance results or validation of the moving average signal.

Tags

Full text
# calmar-test.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


class St(bt.SignalStrategy):
    params = (
    )

    def __init__(self):
        ma1, ma2, = bt.ind.SMA(period=15), bt.ind.SMA(period=50)
        self.signal_add(bt.signal.SIGNAL_LONG, bt.ind.CrossOver(ma1, ma2))

    def next2(self):
        pass


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

    cerebro = bt.Cerebro()

    # Data feed kwargs
    kwargs = dict()

    # Parse from/to-date
    dtfmt, tmfmt = '%Y-%m-%d', 'T%H:%M:%S'
    for a, d in ((getattr(args, x), x) for x in ['fromdate', 'todate']):
        if a:
            strpfmt = dtfmt + tmfmt * ('T' in a)
            kwargs[d] = datetime.datetime.strptime(a, strpfmt)

    # Data feed
    data0 = bt.feeds.YahooFinanceCSVData(dataname=args.data0, **kwargs)
    cerebro.adddata(data0)

    # Broker
    cerebro.broker = bt.brokers.BackBroker(**eval('dict(' + args.broker + ')'))

    cerebro.addanalyzer(bt.analyzers.Calmar)
    # Sizer
    cerebro.addsizer(bt.sizers.FixedSize, **eval('dict(' + args.sizer + ')'))

    # Strategy
    cerebro.addstrategy(St, **eval('dict(' + args.strat + ')'))

    # Execute
    st0 = cerebro.run(**eval('dict(' + args.cerebro + ')'))[0]
    i = 1
    for k, v in st0.analyzers.calmar.get_analysis().items():
        print(i, ': '.join((str(k), str(v))))
        i += 1

    if args.plot:  # Plot if requested to
        cerebro.plot(**eval('dict(' + args.plot + ')'))


def parse_args(pargs=None):
    parser = argparse.ArgumentParser(
        formatter_class=argparse.ArgumentDefaultsHelpFormatter,
        description=(
            'Sample Skeleton'
        )
    )

    parser.add_argument('--data0', default='../../datas/orcl-1995-2014.txt',
                        required=False, help='Data to read in')

    # Defaults for dates
    parser.add_argument('--fromdate', required=False, default='',
                        help='Date[time] in YYYY-MM-DD[THH:MM:SS] format')

    parser.add_argument('--todate', required=False, default='',
                        help='Date[time] in YYYY-MM-DD[THH:MM:SS] format')

    parser.add_argument('--cerebro', required=False, default='',
                        metavar='kwargs', help='kwargs in key=value format')

    parser.add_argument('--broker', required=False, default='',
                        metavar='kwargs', help='kwargs in key=value format')

    parser.add_argument('--sizer', required=False, default='',
                        metavar='kwargs', help='kwargs in key=value format')

    parser.add_argument('--strat', required=False, default='',
                        metavar='kwargs', help='kwargs in key=value format')

    parser.add_argument('--plot', required=False, default='',
                        nargs='?', const='{}',
                        metavar='kwargs', help='kwargs in key=value format')

    return parser.parse_args(pargs)


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.