Backtrader多重交易

  |  

即使在相同的数据上运行,现在也可以为每笔交易添加唯一标识符。

根据Tick Data and Resampling 版本backtrader的请求,支持“MultiTrades”,即:为订单分配tradeid的能力。此 id 被传递给Trades ,这使得有可能拥有不同类别的交易并同时打开它们。

可以在以下情况下指定tradeid

  • 使用kwarg tradeid调用 Strategy.buy/sell/close

  • 使用 kwarg tradeid 调用tradeid

  • 使用 kwarg tradeid创建 Order 实例

如果未指定,则默认值为:

  • tradeid = 0

为了测试一个小脚本已经实现,通过自定义MTradeObserver的实现来可视化结果,它根据tradeid在图上分配不同的标记(用于测试值 0、1 和 2)

该脚本支持使用三个 id (0, 1, 2) 或简单地使用 0 (默认)

不激活多个 id 的运行:

$ ./multitrades.py --plot

结果图表显示所有交易的 id 0 ,因此无法区分。

第二次运行通过在 0、1 和 2 之间循环来激活多重交易:

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

现在 3 个不同的标记交替显示每个交易可以使用tradeid成员进行区分。

笔记

backtrader尝试使用仿真现实的模型。因此“交易”不是由只处理订单的Broker实例计算的。

交易由策略计算。

因此,现实生活中的经纪人可能不支持tradeid (或类似的东西),在这种情况下,需要手动跟踪经纪人分配的唯一订单 ID。

现在,自定义观察者的代码

from __future__ import (absolute_import, division, print_function,
                        unicode_literals)

import math

import backtrader as bt


class 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

主要脚本用法:

$ ./multitrades.py --help
usage: 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]

MultiTrades

optional 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

脚本的代码。

from __future__ import (absolute_import, division, print_function,
                        unicode_literals)

import argparse
import datetime
import itertools

# The above could be sent to an independent module
import backtrader as bt
import backtrader.feeds as btfeeds
import backtrader.indicators as btind

import mtradeobserver


class 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()

推荐阅读

相关文章

Backtrader向 OHLC 提供买入价/卖出价数据

最近,backtrader通过实现line覆盖来运行从 ohlc-land 逃逸,这允许重新定义整个层次结构,例如,具有仅具有 bid,ask 和 datetime lines的data feeds。

Backtrader写下来

随着 1.1.7.88 版本的发布, backtrader有了一个新的补充:作家这可能早就到期了,应该已经存在了,问题 #14中的讨论也应该已经开始了开发。但迟到总比没有好。

Backtrader教程:分析仪

无论是回溯测试还是交易,能够分析交易系统的性能是了解是否不仅获得了利润,而且是否在风险太大的情况下实现了利润,或者与参考资产(或无风险资产)相比,是否真的值得付出努力的关键。 这就是对象家族的用武之Analyzer 地:提供对所发生事件甚至实际发生的事情的分析。

Backtrader追踪订单

版本1.9.35.116将StopTrail和StopTrailLimit订单运行类型添加到回测库中。笔记这仅在回测中实现,还没有针对实时经纪人的实现笔记更新为1.9.36.116版本。盈透证券支持StopTrail 、 StopTrailLimit和OCO 。

Backtrader订单历史

通过发布1.9.55.122, backtrader 现在可用于评估一组外部订单的性能。

BacktraderOCO订单

Release1.9.34.116 将 OCO (又名One Cancel Others)添加到回溯测试武器库中。 注意 这仅在回溯测试中实现,并且还没有针对即时代理的实现 注意 随版本1.9.36.116更新。盈透证券支持 StopTrail和 StopTrailLimit OCO。

Backtrader唐钟斯10天连胜

它已成为新闻。DJI正在创下历史新高,已经连续10个上涨日和9个历史高点。例如,请参阅: 当然,许多人已经注意到道琼斯指数处于这样的状态,这篇文章只是告诉我们它正在成为主流。

Backtrader教程:观察者 - 基准测试

工单 #89 是关于针对资产添加基准测试的。明智的是,人们实际上可能有一个策略,即使积极,也低于简单地跟踪资产所能提供的策略。

数据多时间帧

有时投资决策是使用不同的时间框架做出的: 每周评估趋势 每天执行条目 或者5分钟对60分钟。 这意味着需要将多个时间帧的数据组合在 backtrader 中以支援此类组合。 对它的本机支持已经内置。

Backtrader 教程:订单 - 目标订单

在1.8.10.96版本之前,可以使用反向交易者通过策略方法进行智能质押: buy和sell 。这一切都是为了在负责赌注大小的等式中添加一个Sizer 。 Sizer不能做的是决定操作是买入还是卖出。这意味着需要一个新概念,在其中添加一个小的智能层来做出这样的决定。