Movatterモバイル変換


[0]ホーム

URL:


Skip to content

MultiTrades

One can now add a unique identifier to each trade, even if running on the samedata.

Following a request atTick Data and Resamplingrelease 1.1.12.88 ofbacktrader support “MultiTrades”, ie: the ability toassign atradeid to orders. This id is passed on toTrades which makesit possible to have different categories of trades and have them simultaneouslyopen.

Thetradeid can be specified when:

  • Calling Strategy.buy/sell/close with kwargtradeid

  • Calling Broker.buy/sell with kwargtradeid

  • Creating an Order instance with kwargtradeid

If not specified the default value is:

  • tradeid = 0

To test a small script has been implemented, visualizing the result with theimplementation of a customMTradeObserver which assigns different markers onthe plot accordingtradeid (for the test values 0, 1 and 2 are used)

The script supports using the three ids (0, 1, 2) or simply use 0 (default)

An execution without enabling multiple ids:

$ ./multitrades.py --plot

With the resulting chart showing all Trades carry id0 and therefore cannotbe diferentiated.

image

A second execution enables multitrades by cycling amongs 0, 1 and 2:

$ ./multitrades.py --plot --mtrade

And now 3 different markers alternate showing each Trade can be distinguishedusing thetradeid member.

image

Note

backtrader tries to use models which mimic reality. Therefore “trades”are not calculated by theBroker instance which only takes care ofoders.

Trades are calculated by the Strategy.

And hencetradeid (or something similar) may not be supported by a reallife broker in which case manually keeping track of the unique orde idassigned by the broker would be needed.

Now, the code for the custom observer

from __future__ import (absolute_import, division, print_function,                        unicode_literals)import mathimport backtrader as btclass MTradeObserver(bt.observer.Observer):    lines = ('Id_0', 'Id_1', 'Id_2')    plotinfo = dict(plot=True, subplot=True, plotlinelabels=True)    plotlines = dict(        Id_0=dict(marker='*', markersize=8.0, color='lime', fillstyle='full'),        Id_1=dict(marker='o', markersize=8.0, color='red', fillstyle='full'),        Id_2=dict(marker='s', markersize=8.0, color='blue', fillstyle='full')    )    def next(self):        for trade in self._owner._tradespending:            if trade.data is not self.data:                continue            if not trade.isclosed:                continue            self.lines[trade.tradeid][0] = trade.pnlcomm

The main script usage:

$ ./multitrades.py --helpusage: multitrades.py [-h] [--data DATA] [--fromdate FROMDATE]                      [--todate TODATE] [--mtrade] [--period PERIOD]                      [--onlylong] [--cash CASH] [--comm COMM] [--mult MULT]                      [--margin MARGIN] [--stake STAKE] [--plot]                      [--numfigs NUMFIGS]MultiTradesoptional arguments:  -h, --help            show this help message and exit  --data DATA, -d DATA  data to add to the system  --fromdate FROMDATE, -f FROMDATE                        Starting date in YYYY-MM-DD format  --todate TODATE, -t TODATE                        Starting date in YYYY-MM-DD format  --mtrade              Activate MultiTrade Ids  --period PERIOD       Period to apply to the Simple Moving Average  --onlylong, -ol       Do only long operations  --cash CASH           Starting Cash  --comm COMM           Commission for operation  --mult MULT           Multiplier for futures  --margin MARGIN       Margin for each future  --stake STAKE         Stake to apply in each operation  --plot, -p            Plot the read data  --numfigs NUMFIGS, -n NUMFIGS                        Plot using numfigs figures

The code for the script.

from __future__ import (absolute_import, division, print_function,                        unicode_literals)import argparseimport datetimeimport itertools# The above could be sent to an independent moduleimport backtrader as btimport backtrader.feeds as btfeedsimport backtrader.indicators as btindimport mtradeobserverclass MultiTradeStrategy(bt.Strategy):    '''This strategy buys/sells upong the close price crossing    upwards/downwards a Simple Moving Average.    It can be a long-only strategy by setting the param "onlylong" to True    '''    params = dict(        period=15,        stake=1,        printout=False,        onlylong=False,        mtrade=False,    )    def log(self, txt, dt=None):        if self.p.printout:            dt = dt or self.data.datetime[0]            dt = bt.num2date(dt)            print('%s, %s' % (dt.isoformat(), txt))    def __init__(self):        # To control operation entries        self.order = None        # Create SMA on 2nd data        sma = btind.MovAv.SMA(self.data, period=self.p.period)        # Create a CrossOver Signal from close an moving average        self.signal = btind.CrossOver(self.data.close, sma)        # To alternate amongst different tradeids        if self.p.mtrade:            self.tradeid = itertools.cycle([0, 1, 2])        else:            self.tradeid = itertools.cycle([0])    def next(self):        if self.order:            return  # if an order is active, no new orders are allowed        if self.signal > 0.0:  # cross upwards            if self.position:                self.log('CLOSE SHORT , %.2f' % self.data.close[0])                self.close(tradeid=self.curtradeid)            self.log('BUY CREATE , %.2f' % self.data.close[0])            self.curtradeid = next(self.tradeid)            self.buy(size=self.p.stake, tradeid=self.curtradeid)        elif self.signal < 0.0:            if self.position:                self.log('CLOSE LONG , %.2f' % self.data.close[0])                self.close(tradeid=self.curtradeid)            if not self.p.onlylong:                self.log('SELL CREATE , %.2f' % self.data.close[0])                self.curtradeid = next(self.tradeid)                self.sell(size=self.p.stake, tradeid=self.curtradeid)    def notify_order(self, order):        if order.status in [bt.Order.Submitted, bt.Order.Accepted]:            return  # Await further notifications        if order.status == order.Completed:            if order.isbuy():                buytxt = 'BUY COMPLETE, %.2f' % order.executed.price                self.log(buytxt, order.executed.dt)            else:                selltxt = 'SELL COMPLETE, %.2f' % order.executed.price                self.log(selltxt, order.executed.dt)        elif order.status in [order.Expired, order.Canceled, order.Margin]:            self.log('%s ,' % order.Status[order.status])            pass  # Simply log        # Allow new orders        self.order = None    def notify_trade(self, trade):        if trade.isclosed:            self.log('TRADE PROFIT, GROSS %.2f, NET %.2f' %                     (trade.pnl, trade.pnlcomm))        elif trade.justopened:            self.log('TRADE OPENED, SIZE %2d' % trade.size)def runstrategy():    args = parse_args()    # Create a cerebro    cerebro = bt.Cerebro()    # 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 = btfeeds.BacktraderCSVData(        dataname=args.data,        fromdate=fromdate,        todate=todate)    # Add the 1st data to cerebro    cerebro.adddata(data)    # Add the strategy    cerebro.addstrategy(MultiTradeStrategy,                        period=args.period,                        onlylong=args.onlylong,                        stake=args.stake,                        mtrade=args.mtrade)    # Add the commission - only stocks like a for each operation    cerebro.broker.setcash(args.cash)    # Add the commission - only stocks like a for each operation    cerebro.broker.setcommission(commission=args.comm,                                 mult=args.mult,                                 margin=args.margin)    # Add the MultiTradeObserver    cerebro.addobserver(mtradeobserver.MTradeObserver)    # And run it    cerebro.run()    # Plot if requested    if args.plot:        cerebro.plot(numfigs=args.numfigs, volume=False, zdown=False)def parse_args():    parser = argparse.ArgumentParser(description='MultiTrades')    parser.add_argument('--data', '-d',                        default='../../datas/2006-day-001.txt',                        help='data to add to the system')    parser.add_argument('--fromdate', '-f',                        default='2006-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('--mtrade', action='store_true',                        help='Activate MultiTrade Ids')    parser.add_argument('--period', default=15, type=int,                        help='Period to apply to the Simple Moving Average')    parser.add_argument('--onlylong', '-ol', action='store_true',                        help='Do only long operations')    parser.add_argument('--cash', default=100000, type=int,                        help='Starting Cash')    parser.add_argument('--comm', default=2, type=float,                        help='Commission for operation')    parser.add_argument('--mult', default=10, type=int,                        help='Multiplier for futures')    parser.add_argument('--margin', default=2000.0, type=float,                        help='Margin for each future')    parser.add_argument('--stake', default=1, type=int,                        help='Stake to apply in each operation')    parser.add_argument('--plot', '-p', action='store_true',                        help='Plot the read data')    parser.add_argument('--numfigs', '-n', default=1,                        help='Plot using numfigs figures')    return parser.parse_args()if __name__ == '__main__':    runstrategy()

[8]ページ先頭

©2009-2025 Movatter.jp