Arrows for the BuySell Observer
backtrader was conceived to try to deliver ease of use. Creation of indicators and other usual suspects should be easy.
And of course, customizing existing items should also be part of the deal.
A topic in the community, BuySell Arrows, which originates from the migration from issues is a good example.
The current behavior can be seen by running the sample:
./buysellarrows.py --plot style="'ohlc'"
With following output
The sample when using does the following:
-
Define a subclass of the
BuySell
observer -
Override the
plotlines
definition and simply change the markers which indicate the buy and sell operations -
Monkey patches the existing observer with the custom one
In code terms.
class MyBuySell(bt.observers.BuySell):
plotlines = dict(
buy=dict(marker='$\u21E7$', markersize=12.0),
sell=dict(marker='$\u21E9$', markersize=12.0)
)
and:
# Patch observer if needed
if args.myobserver:
bt.observers.BuySell = MyBuySell
Note
Monkey patching is not strictly necesary. One can also go the standard way:
cerebro = bt.Cerebro(stdstats=False) # remove the standard observers
...
cerebro.addobserver(MyObserver, barplot=True)
...
And the custom observer would also be used, but the other regularly instantiated observers would be missing. Hence the monkey patching in the sample to simply modify the observer and keep the look.
Running it again with the reight parameter:
$ ./buysellarrows.py --plot style="'ohlc'" --myobserver
Which then outputs.
Because matplotlib
allows the use of Unicode characters, the default look
of the observer can be changed to anything and nos just arrows. Be my
guest. For example from Wikipedia:
Sample Usage
$ ./buysellarrows.py --help
usage: buysellarrows.py [-h] [--data DATA | --yahoo TICKER]
[--fromdate FROMDATE] [--todate TODATE]
[--cerebro kwargs] [--broker kwargs] [--sizer kwargs]
[--strat kwargs] [--plot [kwargs]] [--myobserver]
buysell arrows ...
optional arguments:
-h, --help show this help message and exit
--data DATA Data to read in (default:
../../datas/2005-2006-day-001.txt)
--yahoo TICKER Yahoo ticker to download (default: )
--fromdate FROMDATE Date[time] in YYYY-MM-DD[THH:MM:SS] format (default: )
--todate TODATE Date[time] in YYYY-MM-DD[THH:MM:SS] format (default: )
--cerebro kwargs kwargs in key=value format (default: )
--broker kwargs kwargs in key=value format (default: )
--sizer kwargs kwargs in key=value format (default: )
--strat kwargs kwargs in key=value format (default: )
--plot [kwargs] kwargs in key=value format (default: )
--myobserver Patch in Custom BuySell observer (default: False)
Sample Code
from __future__ import (absolute_import, division, print_function,
unicode_literals)
import argparse
import datetime
import backtrader as bt
class MyBuySell(bt.observers.BuySell):
plotlines = dict(
buy=dict(marker='$\u21E7$', markersize=12.0),
sell=dict(marker='$\u21E9$', markersize=12.0)
)
class MACrossOver(bt.SignalStrategy):
params = (('ma', bt.ind.MovAv.SMA), ('p1', 10), ('p2', 30),)
def __init__(self):
ma1, ma2 = self.p.ma(period=self.p.p1), self.p.ma(period=self.p.p2)
self.signal_add(bt.SIGNAL_LONGSHORT, bt.ind.CrossOver(ma1, ma2))
def runstrat(args=None):
args = parse_args(args)
cerebro = bt.Cerebro()
# Data feed kwargs
kwargs = dict(dataname=args.yahoo or args.data)
# 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 kwargs
if args.yahoo:
data0 = bt.feeds.YahooFinanceData(**kwargs)
else:
data0 = bt.feeds.BacktraderCSVData(**kwargs)
cerebro.adddata(data0)
# Broker
kwargs = eval('dict(' + args.broker + ')')
cerebro.broker = bt.brokers.BackBroker(**kwargs)
# Sizer
kwargs = eval('dict(' + args.sizer + ')')
cerebro.addsizer(bt.sizers.FixedSize, **kwargs)
# Strategy
kwargs = eval('dict(' + args.strat + ')')
cerebro.addstrategy(MACrossOver, **kwargs)
# better net liquidation value view
cerebro.addobserver(bt.observers.Value)
# Patch observer if needed
if args.myobserver:
bt.observers.BuySell = MyBuySell
# Execute
cerebro.run(**(eval('dict(' + args.cerebro + ')')))
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='buysell arrows ...')
pgroup = parser.add_mutually_exclusive_group(required=False)
pgroup.add_argument('--data', required=False,
default='../../datas/2005-2006-day-001.txt',
help='Data to read in')
pgroup.add_argument('--yahoo', required=False, default='',
metavar='TICKER', help='Yahoo ticker to download')
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')
parser.add_argument('--myobserver', required=False, action='store_true',
help='Patch in Custom BuySell observer')
return parser.parse_args(pargs)
if __name__ == '__main__':
runstrat()