BackTrader 中文文档(十七)

原文:www.backtrader.com/

欺骗开盘价

原文:www.backtrader.com/blog/posts/2017-05-01-cheat-on-open/cheat-on-open/

发布1.9.44.116版本增加了对Cheat-On-Open的支持。这似乎是人们要求的功能,他们在某个条的结束后进行了计算,但期望与open价格匹配

开盘价格出现间隙(向上或向下,取决于buysell是否生效)且现金不足以进行全仓操作时,这种用例将失败。这迫使经纪人拒绝操作

尽管人们可以尝试通过积极的[1]索引方法来预测未来,但这需要预加载数据,而这并不总是可用的

这种模式:

cerebro = bt.Cerebro(cheat_on_open=True)

这:

  • 在系统中激活了一个额外的周期,该周期调用策略中的next_opennextstart_openprenext_open方法

    决定增加另一组方法是为了明确区分基于检查的价格不再可用且未来未知的常规方法和作弊模式中的操作

    这也避免了对常规next方法的两次调用

xxx_open方法内部时,以下内容为真:

  • 指标尚未重新计算,并保留了在等效的xxx常规方法中上一个周期中看到的值

  • 经纪人尚未评估新周期的待处理订单,可以引入新订单,如果可能的话将进行评估

请注意:

  • Cerebro还有一个broker_coo(默认为True)参数,告诉 cerebro,如果激活了cheat-on-open,则尽可能在经纪人中也激活它

    模拟经纪人有一个名为:coo的参数和一个名为set_coo的方法

尝试欺骗开盘价

下面的示例具有具有 2 种不同行为的策略:

  • 如果cheat-on-openTrue,则只会从next_open操作

  • 如果cheat-on-openFalse,则只会从next操作

在两种情况下,匹配价格必须是相同

  • 如果不作弊,则订单在前一天结束时发布,并将与下一个到来的价格匹配,即open价格

  • 如果作弊,则订单在执行当天发布。因为订单是在经纪人评估订单之前发布的,所以它也将与下一个到来的价格匹配,即open价格

    这种第二种情况,允许计算全仓策略的确切赌注,因为可以直接访问当前的open价格

在两种情况下

  • 当前的openclose价格将从next中打印出来

常规执行:

$ ./cheat-on-open.py --cerebro cheat_on_open=False

...
2005-04-07 next, open 3073.4 close 3090.72
2005-04-08 next, open 3092.07 close 3088.92
Strat Len 68 2005-04-08 Send Buy, fromopen False, close 3088.92
2005-04-11 Buy Executed at price 3088.47
2005-04-11 next, open 3088.47 close 3080.6
2005-04-12 next, open 3080.42 close 3065.18
...

image

订单:

  • 在 2005-04-08 close之后发布

  • 在 2005-04-11 以3088.47open价格执行

欺骗执行:

$ ./cheat-on-open.py --cerebro cheat_on_open=True

...
2005-04-07 next, open 3073.4 close 3090.72
2005-04-08 next, open 3092.07 close 3088.92
2005-04-11 Send Buy, fromopen True, close 3080.6
2005-04-11 Buy Executed at price 3088.47
2005-04-11 next, open 3088.47 close 3080.6
2005-04-12 next, open 3080.42 close 3065.18
...

image

订单:

  • 在 2005 年 04 月 11 日在开盘之前发布

  • 它在 2005 年 04 月 11 日以3088.47open价格执行。

而且在图表上看到的整体结果也是相同的。

结论

在开盘前作弊允许在开盘前发布订单,这样可以例如允许精确计算all-in情景的赌注。

样本用法

$ ./cheat-on-open.py --help
usage: cheat-on-open.py [-h] [--data0 DATA0] [--fromdate FROMDATE]
                        [--todate TODATE] [--cerebro kwargs] [--broker kwargs]
                        [--sizer kwargs] [--strat kwargs] [--plot [kwargs]]

Cheat-On-Open Sample

optional arguments:
  -h, --help           show this help message and exit
  --data0 DATA0        Data to read in (default:
                       ../../datas/2005-2006-day-001.txt)
  --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: )

样本来源

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

import argparse
import datetime

import backtrader as bt

class St(bt.Strategy):
    params = dict(
        periods=[10, 30],
        matype=bt.ind.SMA,
    )

    def __init__(self):
        self.cheating = self.cerebro.p.cheat_on_open
        mas = [self.p.matype(period=x) for x in self.p.periods]
        self.signal = bt.ind.CrossOver(*mas)
        self.order = None

    def notify_order(self, order):
        if order.status != order.Completed:
            return

        self.order = None
        print('{} {} Executed at price {}'.format(
            bt.num2date(order.executed.dt).date(),
            'Buy' * order.isbuy() or 'Sell', order.executed.price)
        )

    def operate(self, fromopen):
        if self.order is not None:
            return
        if self.position:
            if self.signal < 0:
                self.order = self.close()
        elif self.signal > 0:
            print('{} Send Buy, fromopen {}, close {}'.format(
                self.data.datetime.date(),
                fromopen, self.data.close[0])
            )
            self.order = self.buy()

    def next(self):
        print('{} next, open {} close {}'.format(
            self.data.datetime.date(),
            self.data.open[0], self.data.close[0])
        )

        if self.cheating:
            return
        self.operate(fromopen=False)

    def next_open(self):
        if not self.cheating:
            return
        self.operate(fromopen=True)

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.BacktraderCSVData(dataname=args.data0, **kwargs)
    cerebro.adddata(data0)

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

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

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

    # 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=(
            'Cheat-On-Open Sample'
        )
    )

    parser.add_argument('--data0', default='../../datas/2005-2006-day-001.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()

交易日历

原文:www.backtrader.com/blog/posts/2017-04-16-trading-calendar/tradingcalendar/

发行版1.9.42.116增加了对交易日历的支持。在以下场景中重采样时,这是有用的:

  • 现在,每日到每周的重采样可以与该周的最后一根柱子一起传递。

    这是因为交易日历确定了下一个交易日,并且本周的最后一个交易日可以提前确定

  • 当会话结束不是常规结束时(可以已经在数据源中指定)的子每日到每日重新采样

交易日历接口

有一个基类TradingCalendarBase,它用作任何交易日历的基础。它定义了必须被重写的两个(2)方法:

class TradingCalendarBase(with_metaclass(MetaParams, object)):
    def _nextday(self, day):
        '''
        Returns the next trading day (datetime/date instance) after ``day``
        (datetime/date instance) and the isocalendar components

        The return value is a tuple with 2 components: (nextday, (y, w, d))
        where (y, w, d)
        '''
        raise NotImplementedError

    def schedule(self, day):
        '''
        Returns a tuple with the opening and closing times (``datetime.time``)
        for the given ``date`` (``datetime/date`` instance)
        '''
        raise NotImplementedError

实现

PandasMarketCalendar

此实现基于一个整洁的包,该包是 Quantopian 初始功能的衍生物。该软件包位于:pandas_market_calendars,并且可以轻松安装:

pip install pandas_market_calendars

实现具有以下接口:

class PandasMarketCalendar(TradingCalendarBase):
    '''
    Wrapper of ``pandas_market_calendars`` for a trading calendar. The package
    ``pandas_market_calendar`` must be installed

    Params:

      - ``calendar`` (default ``None``)

        The param ``calendar`` accepts the following:

        - string: the name of one of the calendars supported, for example
          `NYSE`. The wrapper will attempt to get a calendar instance

        - calendar instance: as returned by ``get_calendar('NYSE')``

      - ``cachesize`` (default ``365``)

        Number of days to cache in advance for lookup

    See also:

      - https://github.com/rsheftel/pandas_market_calendars

      - http://pandas-market-calendars.readthedocs.io/

    '''
    params = (
        ('calendar', None),  # A pandas_market_calendars instance or exch name
        ('cachesize', 365),  # Number of days to cache in advance
    )

交易日历

此实现允许使用自行收集的信息构建日历,方法是指定节假日、提前天数、非交易工作日和开放和关闭会话时间:

class TradingCalendar(TradingCalendarBase):
    '''
    Wrapper of ``pandas_market_calendars`` for a trading calendar. The package
    ``pandas_market_calendar`` must be installed

    Params:

      - ``open`` (default ``time.min``)

        Regular start of the session

      - ``close`` (default ``time.max``)

        Regular end of the session

      - ``holidays`` (default ``[]``)

        List of non-trading days (``datetime.datetime`` instances)

      - ``earlydays`` (default ``[]``)

        List of tuples determining the date and opening/closing times of days
        which do not conform to the regular trading hours where each tuple has
        (``datetime.datetime``, ``datetime.time``, ``datetime.time`` )

      - ``offdays`` (default ``ISOWEEKEND``)

        A list of weekdays in ISO format (Monday: 1 -> Sunday: 7) in which the
        market doesn't trade. This is usually Saturday and Sunday and hence the
        default

    '''
    params = (
        ('open', time.min),
        ('close', _time_max),
        ('holidays', []),  # list of non trading days (date)
        ('earlydays', []),  # list of tuples (date, opentime, closetime)
        ('offdays', ISOWEEKEND),  # list of non trading (isoweekdays)
    )

使用模式

全局交易日历

通过Cerebro,可以添加一个全局日历,该日历是所有数据源的默认日历,除非为数据源指定了一个:

def addcalendar(self, cal):
    '''Adds a global trading calendar to the system. Individual data feeds
    may have separate calendars which override the global one

    ``cal`` can be an instance of ``TradingCalendar`` a string or an
    instance of ``pandas_market_calendars``. A string will be will be
    instantiated as a ``PandasMarketCalendar`` (which needs the module
    ``pandas_market_calendar`` installed in the system.

    If a subclass of `TradingCalendarBase` is passed (not an instance) it
    will be instantiated
    '''

每个数据源

通过在addcalendar中描述的相同约定指定calendar参数。

例如:

...
data = bt.feeds.YahooFinanceData(dataname='YHOO', calendar='NYSE', ...)
cerebro.adddata(data)
...

示例

每日到每周

让我们来看一下下面可以找到的代码示例的运行情况。在 2016 年,复活节星期五(2016-03-25)也是NYSE的节假日。如果样本运行时没有交易日历,让我们看看那天附近会发生什么。

在这种情况下,正在从每日重新采样到每周(使用YHOO和 2016 年的每日数据):

$ ./tcal.py

...
Strategy len 56 datetime 2016-03-23 Data0 len 56 datetime 2016-03-23 Data1 len 11 datetime 2016-03-18
Strategy len 57 datetime 2016-03-24 Data0 len 57 datetime 2016-03-24 Data1 len 11 datetime 2016-03-18
Strategy len 58 datetime 2016-03-28 Data0 len 58 datetime 2016-03-28 Data1 len 12 datetime 2016-03-24
...

在这个输出中,第 1^(st)日期是策略进行的会计。第 2^(nd)日期是每日的。

周末如预期地在 2016-03-24(星期四)结束,但没有交易日历,重采样代码无法知道,因此传递了日期为 2016-03-18(前一周)的重采样条。当交易转移到 2016-03-28(星期一)时,重新采样器检测到周转变并在同一天传递了日期为 2016-03-24 的重采样条。

使用PandasMarketCalendar运行相同的操作,但用于NYSE(并添加绘图)

$ ./tcal.py --plot --pandascal NYSE

...
Strategy len 56 datetime 2016-03-23 Data0 len 56 datetime 2016-03-23 Data1 len 11 datetime 2016-03-18
Strategy len 57 datetime 2016-03-24 Data0 len 57 datetime 2016-03-24 Data1 len 12 datetime 2016-03-24
Strategy len 58 datetime 2016-03-28 Data0 len 58 datetime 2016-03-28 Data1 len 12 datetime 2016-03-24
...

有一个变化!由于日历,重新采样器知道在 2016-03-24 周结束,并在同一天传递了相应的 2016-03-24 周重采样条。

以及绘图。

image

由于信息可能不一定对每个市场都可用,因此可以制定日历。对于NYSE2016,它看起来像是:

class NYSE_2016(bt.TradingCalendar):
    params = dict(
        holidays=[
            datetime.date(2016, 1, 1),
            datetime.date(2016, 1, 18),
            datetime.date(2016, 2, 15),
            datetime.date(2016, 3, 25),
            datetime.date(2016, 5, 30),
            datetime.date(2016, 7, 4),
            datetime.date(2016, 9, 5),
            datetime.date(2016, 11, 24),
            datetime.date(2016, 12, 26),
        ]
    )

复活节星期五(2016-03-25)被列为假期之一。现在运行示例:

$ ./tcal.py --plot --owncal

...
Strategy len 56 datetime 2016-03-23 Data0 len 56 datetime 2016-03-23 Data1 len 11 datetime 2016-03-18
Strategy len 57 datetime 2016-03-24 Data0 len 57 datetime 2016-03-24 Data1 len 12 datetime 2016-03-24
Strategy len 58 datetime 2016-03-28 Data0 len 58 datetime 2016-03-28 Data1 len 12 datetime 2016-03-24
...

并且相同的结果已通过精心制作的日历定义获得。

分钟转换为每日

使用一些私有的日内数据和知道市场在 2016-11-25 提前收盘的知识(感恩节后的第二天,市场在 US/Eastern 时区在 13:00 关闭),进行另一次测试运行,这次是第二个样本。

注意

源数据直接来自显示的数据,并且处于 CET 时区,即使所涉及的资产 YHOO 在美国交易。 代码中使用 tzinput='CET'tz='US/Eastern' 的数据源来让平台适当地转换输入并显示输出

首先没有交易日历

$ ./tcal-intra.py

...
Strategy len 6838 datetime 2016-11-25 18:00:00 Data0 len 6838 datetime 2016-11-25 13:00:00 Data1 len 21 datetime 2016-11-23 16:00:00
Strategy len 6839 datetime 2016-11-25 18:01:00 Data0 len 6839 datetime 2016-11-25 13:01:00 Data1 len 21 datetime 20 16-11-23 16:00:00
Strategy len 6840 datetime 2016-11-28 14:31:00 Data0 len 6840 datetime 2016-11-28 09:31:00 Data1 len 22 datetime 2016-11-25 16:00:00
Strategy len 6841 datetime 2016-11-28 14:32:00 Data0 len 6841 datetime 2016-11-28 09:32:00 Data1 len 22 datetime 2016-11-25 16:00:00
...

如预期的那样,当天在 13:00 提前关闭,但是重新采样器不知道(正式会话在 16:00 结束),并且继续从前一天(2016-11-23)交付重新采样的每日柱,并且新的重新采样的每日柱首次在下一个交易日(2016-11-28)交付,日期为 2016-11-25。

注意

数据有一个额外的分钟柱 13:01,这可能是由于市场闭市后的竞价过程提供了最后一个价格。

我们可以向流中添加过滤器,以过滤掉在会话时间之外的柱子(过滤器将从交易日历中找到)

但这不是这个示例的重点。

使用 PandasMarketCalendar 实例进行相同的运行:

$ ./tcal-intra.py --pandascal NYSE

...
Strategy len 6838 datetime 2016-11-25 18:00:00 Data0 len 6838 datetime 2016-11-25 13:00:00 Data1 len 15 datetime 2016-11-25 13:00:00
Strategy len 6839 datetime 2016-11-25 18:01:00 Data0 len 6839 datetime 2016-11-25 13:01:00 Data1 len 15 datetime 2016-11-25 13:00:00
Strategy len 6840 datetime 2016-11-28 14:31:00 Data0 len 6840 datetime 2016-11-28 09:31:00 Data1 len 15 datetime 2016-11-25 13:00:00
Strategy len 6841 datetime 2016-11-28 14:32:00 Data0 len 6841 datetime 2016-11-28 09:32:00 Data1 len 15 datetime 2016-11-25 13:00:00
...

现在,当日内 1 分钟的数据源命中 2016-11-25 13:00 时(让我们忽略 13:01 的柱子),每日柱子就被交付,因为交易日历告诉重新采样代码这一天已经结束。

让我们添加一个精心制作的定义。与以前相同,但通过一些 earlydays 扩展它

class NYSE_2016(bt.TradingCalendar):
    params = dict(
        holidays=[
            datetime.date(2016, 1, 1),
            datetime.date(2016, 1, 18),
            datetime.date(2016, 2, 15),
            datetime.date(2016, 3, 25),
            datetime.date(2016, 5, 30),
            datetime.date(2016, 7, 4),
            datetime.date(2016, 9, 5),
            datetime.date(2016, 11, 24),
            datetime.date(2016, 12, 26),
        ],
        earlydays=[
            (datetime.date(2016, 11, 25),
             datetime.time(9, 30), datetime.time(13, 1))
        ],
        open=datetime.time(9, 30),
        close=datetime.time(16, 0),
    )

运行:

$ ./tcal-intra.py --owncal

...
Strategy len 6838 datetime 2016-11-25 18:00:00 Data0 len 6838 datetime 2016-11-25 13:00:00 Data1 len 15 datetime 2016-11-23 16:00:00
Strategy len 6839 datetime 2016-11-25 18:01:00 Data0 len 6839 datetime 2016-11-25 13:01:00 Data1 len 16 datetime 2016-11-25 13:01:00
Strategy len 6840 datetime 2016-11-28 14:31:00 Data0 len 6840 datetime 2016-11-28 09:31:00 Data1 len 16 datetime 2016-11-25 13:01:00
Strategy len 6841 datetime 2016-11-28 14:32:00 Data0 len 6841 datetime 2016-11-28 09:32:00 Data1 len 16 datetime 2016-11-25 13:01:00
...

热心的读者会注意到,精心制作的定义包含了将 13:01(用 datetime.time(13, 1) 定义)定义为我们 2016-11-25 的短日的会话结束。这仅仅是为了展示精心制作的 TradingCalendar 如何帮助调整事物。

现在,2016-11-25 的每日重新采样的柱子与 13:01 的 1 分钟柱子一起交付。

策略的额外奖金

第一个 datetime,属于策略的那个时间,总是在另一个时区,实际上是 UTC。也随着此版本 1.9.42.116 这可以被同步。下面的参数已添加到 Cerebro 中(在实例化期间或使用 cerebro.run 时使用)

- ``tz`` (default: ``None``)

  Adds a global timezone for strategies. The argument ``tz`` can be

    - ``None``: in this case the datetime displayed by strategies will be
      in UTC, which has been always the standard behavior

    - ``pytz`` instance. It will be used as such to convert UTC times to
      the chosen timezone

    - ``string``. Instantiating a ``pytz`` instance will be attempted.

    - ``integer``. Use, for the strategy, the same timezone as the
      corresponding ``data`` in the ``self.datas`` iterable (``0`` would
      use the timezone from ``data0``)

它也支持 cerebro.addtz 方法:

def addtz(self, tz):
    '''
    This can also be done with the parameter ``tz``

    Adds a global timezone for strategies. The argument ``tz`` can be

      - ``None``: in this case the datetime displayed by strategies will be
        in UTC, which has been always the standard behavior

      - ``pytz`` instance. It will be used as such to convert UTC times to
        the chosen timezone

      - ``string``. Instantiating a ``pytz`` instance will be attempted.

      - ``integer``. Use, for the strategy, the same timezone as the
        corresponding ``data`` in the ``self.datas`` iterable (``0`` would
        use the timezone from ``data0``)

    '''

重复上次的日内示例运行,并使用 0 作为 tz(与 data0 的时区同步),以下是关注相同日期和时间的输出:

$ ./tcal-intra.py --owncal --cerebro tz=0

...
Strategy len 6838 datetime 2016-11-25 13:00:00 Data0 len 6838 datetime 2016-11-25 13:00:00 Data1 len 15 datetime 2016-11-23 16:00:00
Strategy len 6839 datetime 2016-11-25 13:01:00 Data0 len 6839 datetime 2016-11-25 13:01:00 Data1 len 16 datetime 2016-11-25 13:01:00
Strategy len 6840 datetime 2016-11-28 09:31:00 Data0 len 6840 datetime 2016-11-28 09:31:00 Data1 len 16 datetime 2016-11-25 13:01:00
Strategy len 6841 datetime 2016-11-28 09:32:00 Data0 len 6841 datetime 2016-11-28 09:32:00 Data1 len 16 datetime 2016-11-25 13:01:00
...

时间戳现在是时区对齐的。

示例用法(tcal.py)

$ ./tcal.py --help
usage: tcal.py [-h] [--data0 DATA0] [--offline] [--fromdate FROMDATE]
               [--todate TODATE] [--cerebro kwargs] [--broker kwargs]
               [--sizer kwargs] [--strat kwargs] [--plot [kwargs]]
               [--pandascal PANDASCAL | --owncal]
               [--timeframe {Weeks,Months,Years}]

Trading Calendar Sample

optional arguments:
  -h, --help            show this help message and exit
  --data0 DATA0         Data to read in (default: YHOO)
  --offline             Read from disk with same name as ticker (default:
                        False)
  --fromdate FROMDATE   Date[time] in YYYY-MM-DD[THH:MM:SS] format (default:
                        2016-01-01)
  --todate TODATE       Date[time] in YYYY-MM-DD[THH:MM:SS] format (default:
                        2016-12-31)
  --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: )
  --pandascal PANDASCAL
                        Name of trading calendar to use (default: )
  --owncal              Apply custom NYSE 2016 calendar (default: False)
  --timeframe {Weeks,Months,Years}
                        Timeframe to resample to (default: Weeks)

示例用法(tcal-intra.py)

$ ./tcal-intra.py --help
usage: tcal-intra.py [-h] [--data0 DATA0] [--fromdate FROMDATE]
                     [--todate TODATE] [--cerebro kwargs] [--broker kwargs]
                     [--sizer kwargs] [--strat kwargs] [--plot [kwargs]]
                     [--pandascal PANDASCAL | --owncal] [--timeframe {Days}]

Trading Calendar Sample

optional arguments:
  -h, --help            show this help message and exit
  --data0 DATA0         Data to read in (default: yhoo-2016-11.csv)
  --fromdate FROMDATE   Date[time] in YYYY-MM-DD[THH:MM:SS] format (default:
                        2016-01-01)
  --todate TODATE       Date[time] in YYYY-MM-DD[THH:MM:SS] format (default:
                        2016-12-31)
  --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: )
  --pandascal PANDASCAL
                        Name of trading calendar to use (default: )
  --owncal              Apply custom NYSE 2016 calendar (default: False)
  --timeframe {Days}    Timeframe to resample to (default: Days)

示例代码(tcal.py)

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

import argparse
import datetime

import backtrader as bt

class NYSE_2016(bt.TradingCalendar):
    params = dict(
        holidays=[
            datetime.date(2016, 1, 1),
            datetime.date(2016, 1, 18),
            datetime.date(2016, 2, 15),
            datetime.date(2016, 3, 25),
            datetime.date(2016, 5, 30),
            datetime.date(2016, 7, 4),
            datetime.date(2016, 9, 5),
            datetime.date(2016, 11, 24),
            datetime.date(2016, 12, 26),
        ]
    )

class St(bt.Strategy):
    params = dict(
    )

    def __init__(self):
        pass

    def start(self):
        self.t0 = datetime.datetime.utcnow()

    def stop(self):
        t1 = datetime.datetime.utcnow()
        print('Duration:', t1 - self.t0)

    def prenext(self):
        self.next()

    def next(self):
        print('Strategy len {} datetime {}'.format(
            len(self), self.datetime.date()), end=' ')

        print('Data0 len {} datetime {}'.format(
            len(self.data0), self.data0.datetime.date()), end=' ')

        if len(self.data1):
            print('Data1 len {} datetime {}'.format(
                len(self.data1), self.data1.datetime.date()))
        else:
            print()

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)

    YahooData = bt.feeds.YahooFinanceData
    if args.offline:
        YahooData = bt.feeds.YahooFinanceCSVData  # change to read file

    # Data feed
    data0 = YahooData(dataname=args.data0, **kwargs)
    cerebro.adddata(data0)

    d1 = cerebro.resampledata(data0,
                              timeframe=getattr(bt.TimeFrame, args.timeframe))
    d1.plotinfo.plotmaster = data0
    d1.plotinfo.sameaxis = True

    if args.pandascal:
        cerebro.addcalendar(args.pandascal)
    elif args.owncal:
        cerebro.addcalendar(NYSE_2016)

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

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

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

    # 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=(
            'Trading Calendar Sample'
        )
    )

    parser.add_argument('--data0', default='YHOO',
                        required=False, help='Data to read in')

    parser.add_argument('--offline', required=False, action='store_true',
                        help='Read from disk with same name as ticker')

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

    parser.add_argument('--todate', required=False, default='2016-12-31',
                        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')

    pgroup = parser.add_mutually_exclusive_group(required=False)
    pgroup.add_argument('--pandascal', required=False, action='store',
                        default='', help='Name of trading calendar to use')

    pgroup.add_argument('--owncal', required=False, action='store_true',
                        help='Apply custom NYSE 2016 calendar')

    parser.add_argument('--timeframe', required=False, action='store',
                        default='Weeks', choices=['Weeks', 'Months', 'Years'],
                        help='Timeframe to resample to')

    return parser.parse_args(pargs)

if __name__ == '__main__':
    runstrat()

示例代码(tcal-intra.py)

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

import argparse
import datetime

import backtrader as bt

class NYSE_2016(bt.TradingCalendar):
    params = dict(
        holidays=[
            datetime.date(2016, 1, 1),
            datetime.date(2016, 1, 18),
            datetime.date(2016, 2, 15),
            datetime.date(2016, 3, 25),
            datetime.date(2016, 5, 30),
            datetime.date(2016, 7, 4),
            datetime.date(2016, 9, 5),
            datetime.date(2016, 11, 24),
            datetime.date(2016, 12, 26),
        ],
        earlydays=[
            (datetime.date(2016, 11, 25),
             datetime.time(9, 30), datetime.time(13, 1))
        ],
        open=datetime.time(9, 30),
        close=datetime.time(16, 0),
    )

class St(bt.Strategy):
    params = dict(
    )

    def __init__(self):
        pass

    def prenext(self):
        self.next()

    def next(self):
        print('Strategy len {} datetime {}'.format(
            len(self), self.datetime.datetime()), end=' ')

        print('Data0 len {} datetime {}'.format(
            len(self.data0), self.data0.datetime.datetime()), end=' ')

        if len(self.data1):
            print('Data1 len {} datetime {}'.format(
                len(self.data1), self.data1.datetime.datetime()))
        else:
            print()

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

    cerebro = bt.Cerebro()

    # Data feed kwargs
    # kwargs = dict(tz='US/Eastern')
    # import pytz
    # tz = tzinput = pytz.timezone('Europe/Berlin')
    tzinput = 'Europe/Berlin'
    # tz = tzinput
    tz = 'US/Eastern'
    kwargs = dict(tzinput=tzinput, tz=tz)

    # 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.BacktraderCSVData(dataname=args.data0, **kwargs)
    cerebro.adddata(data0)

    d1 = cerebro.resampledata(data0,
                              timeframe=getattr(bt.TimeFrame, args.timeframe))
    # d1.plotinfo.plotmaster = data0
    # d1.plotinfo.sameaxis = False

    if args.pandascal:
        cerebro.addcalendar(args.pandascal)
    elif args.owncal:
        cerebro.addcalendar(NYSE_2016())  # or NYSE_2016() to pass an instance

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

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

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

    # 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=(
            'Trading Calendar Sample'
        )
    )

    parser.add_argument('--data0', default='yhoo-2016-11.csv',
                        required=False, help='Data to read in')

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

    parser.add_argument('--todate', required=False, default='2016-12-31',
                        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')

    pgroup = parser.add_mutually_exclusive_group(required=False)
    pgroup.add_argument('--pandascal', required=False, action='store',
                        default='', help='Name of trading calendar to use')

    pgroup.add_argument('--owncal', required=False, action='store_true',
                        help='Apply custom NYSE 2016 calendar')

    parser.add_argument('--timeframe', required=False, action='store',
                        default='Days', choices=['Days'],
                        help='Timeframe to resample to')

    return parser.parse_args(pargs)

if __name__ == '__main__':
    runstrat()

多个示例

原文:www.backtrader.com/blog/posts/2017-04-09-multi-example/multi-example/

一些社区中的主题似乎是如何跟踪订单,特别是当有多个数据源在运行时,以及包括多个订单共同工作的情况,如bracket orders的情况

下面的脚本试图作为示例,允许用户:

  • 使用 3 个数据源

  • 选择使用

    • usebracket=False时,只包含类型为Market的单个buy订单

    • 设置bracket订单(usebracket=True

    在这种情况下,可以指定如何创建bracket set

    • rawbracket=True时,发出 3 个订单(1 个父级+2 个子级)

    • rawbracket=False时调用buy_bracket

    主要bracket订单在valid天后具有到期时间(默认为10

  • weekday与为每个数据源定义的enter值匹配时,将进入(或至少尝试进入)一个仓位,默认为[1, 3, 4]

  • 将退出一个开放的仓位

    • holding期后正常close。期间为每个数据源分别定义,参数hold默认为[7, 10, 15]

    如果存在bracket订单的stop方被触发,则会触发bracket订单的取消(这将自动取消另一方)

    • 或者如果使用brackets,则在执行stop(限制损失)或limit(获利)方之一时(系统将自动取消另一方)
  • 订单保留在

    • dict中,使用data作为键

    • 包含每个data条目的订单的list,这些订单对每个data都是打开的

    一旦订单已发出,管理就在notify_order中完成

此外:

  • 使用TestSizer,它将为buysell订单返回不同的值作为测试

    因为只有long操作被启动,所以只返回buy大小,并且输出日志中不包含sell大小的痕迹。

创建丰富的操作、持仓和订单管理日志,以帮助理解发生了什么。

让我们看一个带有usebracket=Truerawbracket=True默认值的示例执行(为了简化图表,将删除音量):

$ ./mult-values.py --plot volume=False

2001-01-02 d0 Position 0
2001-01-02 Data d0 OType buy Sizing to 1
2001-01-02 d0 Main 1 Stp 2 Lmt 3
2001-01-02 d1 Position 0
2001-01-02 d2 Position 0
2001-01-03 d0 Order 1 Status Accepted
2001-01-03 d0 Order 2 Status Accepted
2001-01-03 d0 Order 3 Status Accepted
2001-01-03 d0 Order 1 Status Completed
-- No longer alive main Ref
2001-01-03 d0 Position 1
2001-01-03 d1 Position 0
2001-01-03 d2 Position 0
2001-01-04 d0 Order 3 Status Completed
-- No longer alive limit Ref
2001-01-04 d0 Order 2 Status Canceled
-- No longer alive stop Ref
...
...
...
2006-12-27 d0 Order 2036 Status Accepted
2006-12-27 d0 Order 2037 Status Accepted
2006-12-27 d0 Order 2038 Status Accepted
2006-12-27 d0 Position 0
2006-12-27 d1 Position 0
2006-12-27 d2 Position 0
2006-12-28 d0 Position 0
2006-12-28 d1 Position 0
2006-12-28 d2 Position 0
2006-12-29 d0 Position 0
2006-12-29 d1 Position 0
2006-12-29 d2 Position 0

image

第二个执行设置rawbracket=False

$ ./mult-values.py --plot volume=False --strat rawbracket=False

这将输出完全相同的结果,这次使用了buy_bracket

image

最后禁用括号使用:

./mult-values.py --strat usebracket=False --plot volume=False

image

结论

这应该作为订单管理的良好示例,具有多个数据源和订单集。

示例用法

$ ./mult-values.py --help
usage: mult-values.py [-h] [--data0 DATA0] [--data1 DATA1] [--data2 DATA2]
                      [--fromdate FROMDATE] [--todate TODATE]
                      [--cerebro kwargs] [--broker kwargs] [--sizer kwargs]
                      [--strat kwargs] [--plot [kwargs]]

Multiple Values and Brackets

optional arguments:
  -h, --help           show this help message and exit
  --data0 DATA0        Data0 to read in (default:
                       ../../datas/nvda-1999-2014.txt)
  --data1 DATA1        Data1 to read in (default:
                       ../../datas/yhoo-1996-2014.txt)
  --data2 DATA2        Data1 to read in (default:
                       ../../datas/orcl-1995-2014.txt)
  --fromdate FROMDATE  Date[time] in YYYY-MM-DD[THH:MM:SS] format (default:
                       2001-01-01)
  --todate TODATE      Date[time] in YYYY-MM-DD[THH:MM:SS] format (default:
                       2007-01-01)
  --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: )

示例代码

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

import argparse
import datetime

import backtrader as bt

class TestSizer(bt.Sizer):
    params = dict(stake=1)

    def _getsizing(self, comminfo, cash, data, isbuy):
        dt, i = self.strategy.datetime.date(), data._id
        s = self.p.stake * (1 + (not isbuy))
        print('{} Data {} OType {} Sizing to {}'.format(
            dt, data._name, ('buy' * isbuy) or 'sell', s))

        return s

class St(bt.Strategy):
    params = dict(
        enter=[1, 3, 4],  # data ids are 1 based
        hold=[7, 10, 15],  # data ids are 1 based
        usebracket=True,
        rawbracket=True,
        pentry=0.015,
        plimits=0.03,
        valid=10,
    )

    def notify_order(self, order):
        if order.status == order.Submitted:
            return

        dt, dn = self.datetime.date(), order.data._name
        print('{} {} Order {} Status {}'.format(
            dt, dn, order.ref, order.getstatusname())
        )

        whichord = ['main', 'stop', 'limit', 'close']
        if not order.alive():  # not alive - nullify
            dorders = self.o[order.data]
            idx = dorders.index(order)
            dorders[idx] = None
            print('-- No longer alive {} Ref'.format(whichord[idx]))

            if all(x is None for x in dorders):
                dorders[:] = []  # empty list - New orders allowed

    def __init__(self):
        self.o = dict()  # orders per data (main, stop, limit, manual-close)
        self.holding = dict()  # holding periods per data

    def next(self):
        for i, d in enumerate(self.datas):
            dt, dn = self.datetime.date(), d._name
            pos = self.getposition(d).size
            print('{} {} Position {}'.format(dt, dn, pos))

            if not pos and not self.o.get(d, None):  # no market / no orders
                if dt.weekday() == self.p.enter[i]:
                    if not self.p.usebracket:
                        self.o[d] = [self.buy(data=d)]
                        print('{} {} Buy {}'.format(dt, dn, self.o[d][0].ref))

                    else:
                        p = d.close[0] * (1.0 - self.p.pentry)
                        pstp = p * (1.0 - self.p.plimits)
                        plmt = p * (1.0 + self.p.plimits)
                        valid = datetime.timedelta(self.p.valid)

                        if self.p.rawbracket:
                            o1 = self.buy(data=d, exectype=bt.Order.Limit,
                                          price=p, valid=valid, transmit=False)

                            o2 = self.sell(data=d, exectype=bt.Order.Stop,
                                           price=pstp, size=o1.size,
                                           transmit=False, parent=o1)

                            o3 = self.sell(data=d, exectype=bt.Order.Limit,
                                           price=plmt, size=o1.size,
                                           transmit=True, parent=o1)

                            self.o[d] = [o1, o2, o3]

                        else:
                            self.o[d] = self.buy_bracket(
                                data=d, price=p, stopprice=pstp,
                                limitprice=plmt, oargs=dict(valid=valid))

                        print('{} {} Main {} Stp {} Lmt {}'.format(
                            dt, dn, *(x.ref for x in self.o[d])))

                    self.holding[d] = 0

            elif pos:  # exiting can also happen after a number of days
                self.holding[d] += 1
                if self.holding[d] >= self.p.hold[i]:
                    o = self.close(data=d)
                    self.o[d].append(o)  # manual order to list of orders
                    print('{} {} Manual Close {}'.format(dt, dn, o.ref))
                    if self.p.usebracket:
                        self.cancel(self.o[d][1])  # cancel stop side
                        print('{} {} Cancel {}'.format(dt, dn, self.o[d][1]))

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, name='d0')

    data1 = bt.feeds.YahooFinanceCSVData(dataname=args.data1, **kwargs)
    data1.plotinfo.plotmaster = data0
    cerebro.adddata(data1, name='d1')

    data2 = bt.feeds.YahooFinanceCSVData(dataname=args.data2, **kwargs)
    data2.plotinfo.plotmaster = data0
    cerebro.adddata(data2, name='d2')

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

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

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

    # 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=(
            'Multiple Values and Brackets'
        )
    )

    parser.add_argument('--data0', default='../../datas/nvda-1999-2014.txt',
                        required=False, help='Data0 to read in')

    parser.add_argument('--data1', default='../../datas/yhoo-1996-2014.txt',
                        required=False, help='Data1 to read in')

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

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

    parser.add_argument('--todate', required=False, default='2007-01-01',
                        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()

括号订单

原文:www.backtrader.com/blog/posts/2017-04-01-bracket/bracket/

发行 1.9.37.116 版本添加了 bracket 订单,提供了由回测经纪人支持的非常广泛的订单范围(MarketLimitCloseStopStopLimitStopTrailStopTrailLimitOCO

注意

这是为了 回测交互经纪人 商店而实现的

bracket 订单不是单个订单,而实际上是由 3 个订单组成的。让我们考虑长边

  • 主边 buy 订单,通常设置为 LimitStopLimit 订单

  • 低边 sell 订单,通常设置为 Stop 订单以限制损失

  • 高边 sell 订单,通常设置为 Limit 订单以获利

对于短边,相应的 sell 和 2 x buy 订单。

低/高边订单实际上确实围绕主边订单创建了一个括号。

为了加入一些逻辑,以下规则适用:

  • 为了避免它们中的任何一个被独立触发,三个订单一起提交

  • 低/高边订单标记为主边的子订单

  • 直到主边执行完毕,子订单都不活跃

  • 主边取消则低边和高边都取消

  • 主边的执行激活了低边和高边

  • 一旦活跃

    • 任何低/高边订单的执行或取消都会自动取消另一个

使用模式

创建括号订单集的两种可能性

  • 单次发行 3 个订单

  • 手动发行 3 个订单

单次发行一个括号

backtraderStrategy 中提供了两种控制 bracket 订单的新方法。

  • buy_bracketsell_bracket

注意

签名和信息在下方或在 Strategy 参考部分。

通过一条语句完整发行 3 个订单集。例如:

brackets = self.buy_bracket(limitprice=14.00, price=13.50, stopprice=13.00)

注意 stoppricelimitprice 如何包裹主 price

这应该足够了。实际的目标 data 将是 data0,而 size 将由默认大小器自动确定。当然,可以指定许多其他参数以对执行进行精细控制。

返回值是:

  • 包含 3 个订单的列表,顺序为 [main, stop, limit]

因为当发出 sell_bracket 订单时,低边和高边将被转向,参数按照惯例命名为 stoplimit

  • stop 旨在停止损失(在长操作中是低边,在短操作中是高边)

  • limit 旨在获利(在长操作中是高边,在短操作中是低边)

手动发行一个括号

这涉及生成 3 个订单并调整 transmitparent 参数。规则如下:

  • 主边订单必须首先创建,且 transmit=False

  • 低/高边订单必须具有 parent=main_side_order

  • 要创建的第一个低/高边订单必须具有 transmit=False

  • 要创建的最后一个订单(低端或高端)设置transmit=True

一个实际示例,执行上述单个命令所做的事情:

mainside = self.buy(price=13.50, exectype=bt.Order.Limit, transmit=False)
lowside  = self.sell(price=13.00, size=mainsize.size, exectype=bt.Order.Stop,
                     transmit=False, parent=mainside)
highside = self.sell(price=14.00, size=mainsize.size, exectype=bt.Order.Limit,
                     transmit=True, parent=mainside)

还有很多事情要做:

  • 跟踪mainside的顺序以指示它是其他订单的父订单

  • 控制transmit以确保只有最后一个订单触发联合传输

  • 指定执行类型

  • 为低端和高端指定size

    因为size 必须相同。如果参数没有手动指定,并且最终用户引入了一个 sizer,那么 sizer 实际上可能为订单指示一个不同的值。这就是为什么在为mainside订单设置后,必须手动添加到调用中的原因。

它的一个示例

运行下面的示例会产生以下输出(为简洁起见)

$ ./bracket.py --plot

2005-01-28: Oref 1 / Buy at 2941.11055
2005-01-28: Oref 2 / Sell Stop at 2881.99275
2005-01-28: Oref 3 / Sell Limit at 3000.22835
2005-01-31: Order ref: 1 / Type Buy / Status Submitted
2005-01-31: Order ref: 2 / Type Sell / Status Submitted
2005-01-31: Order ref: 3 / Type Sell / Status Submitted
2005-01-31: Order ref: 1 / Type Buy / Status Accepted
2005-01-31: Order ref: 2 / Type Sell / Status Accepted
2005-01-31: Order ref: 3 / Type Sell / Status Accepted
2005-02-01: Order ref: 1 / Type Buy / Status Expired
2005-02-01: Order ref: 2 / Type Sell / Status Canceled
2005-02-01: Order ref: 3 / Type Sell / Status Canceled
...
2005-08-11: Oref 16 / Buy at 3337.3892
2005-08-11: Oref 17 / Sell Stop at 3270.306
2005-08-11: Oref 18 / Sell Limit at 3404.4724
2005-08-12: Order ref: 16 / Type Buy / Status Submitted
2005-08-12: Order ref: 17 / Type Sell / Status Submitted
2005-08-12: Order ref: 18 / Type Sell / Status Submitted
2005-08-12: Order ref: 16 / Type Buy / Status Accepted
2005-08-12: Order ref: 17 / Type Sell / Status Accepted
2005-08-12: Order ref: 18 / Type Sell / Status Accepted
2005-08-12: Order ref: 16 / Type Buy / Status Completed
2005-08-18: Order ref: 17 / Type Sell / Status Completed
2005-08-18: Order ref: 18 / Type Sell / Status Canceled
...
2005-09-26: Oref 22 / Buy at 3383.92535
2005-09-26: Oref 23 / Sell Stop at 3315.90675
2005-09-26: Oref 24 / Sell Limit at 3451.94395
2005-09-27: Order ref: 22 / Type Buy / Status Submitted
2005-09-27: Order ref: 23 / Type Sell / Status Submitted
2005-09-27: Order ref: 24 / Type Sell / Status Submitted
2005-09-27: Order ref: 22 / Type Buy / Status Accepted
2005-09-27: Order ref: 23 / Type Sell / Status Accepted
2005-09-27: Order ref: 24 / Type Sell / Status Accepted
2005-09-27: Order ref: 22 / Type Buy / Status Completed
2005-10-04: Order ref: 24 / Type Sell / Status Completed
2005-10-04: Order ref: 23 / Type Sell / Status Canceled
...

显示了 3 种不同的结果:

  • 在第 1 种情况下,主订单已过期,这自动取消了其他两个订单

  • 在第 2 种情况下,主订单已完成,低端(在买入情况下为止损)已执行,限制损失

  • 在第 3 种情况下,主订单已完成,高端(限价)已执行

    这可以注意到,因为Completed的 id 分别为2224,而high方订单是最后发出的,这意味着未执行的 low side 订单的 id 为 23。

视觉上

图片

可以立即看到,亏损交易和盈利交易都围绕着相同的数值对齐,这就是 bracketing 的目的。控制双方。

运行示例手动发出 3 个订单,但可以告诉它使用buy_bracket。让我们看看输出:

$ ./bracket.py --strat usebracket=True

产生相同的结果

图片

一些参考

查看新的buy_bracketsell_bracket方法

def buy_bracket(self, data=None, size=None, price=None, plimit=None,
                exectype=bt.Order.Limit, valid=None, tradeid=0,
                trailamount=None, trailpercent=None, oargs={},
                stopprice=None, stopexec=bt.Order.Stop, stopargs={},
                limitprice=None, limitexec=bt.Order.Limit, limitargs={},
                **kwargs):
    '''
    Create a bracket order group (low side - buy order - high side). The
    default behavior is as follows:

      - Issue a **buy** order with execution ``Limit``

      - Issue a *low side* bracket **sell** order with execution ``Stop``

      - Issue a *high side* bracket **sell** order with execution
        ``Limit``.

    See below for the different parameters

      - ``data`` (default: ``None``)

        For which data the order has to be created. If ``None`` then the
        first data in the system, ``self.datas[0] or self.data0`` (aka
        ``self.data``) will be used

      - ``size`` (default: ``None``)

        Size to use (positive) of units of data to use for the order.

        If ``None`` the ``sizer`` instance retrieved via ``getsizer`` will
        be used to determine the size.

        **Note**: The same size is applied to all 3 orders of the bracket

      - ``price`` (default: ``None``)

        Price to use (live brokers may place restrictions on the actual
        format if it does not comply to minimum tick size requirements)

        ``None`` is valid for ``Market`` and ``Close`` orders (the market
        determines the price)

        For ``Limit``, ``Stop`` and ``StopLimit`` orders this value
        determines the trigger point (in the case of ``Limit`` the trigger
        is obviously at which price the order should be matched)

      - ``plimit`` (default: ``None``)

        Only applicable to ``StopLimit`` orders. This is the price at which
        to set the implicit *Limit* order, once the *Stop* has been
        triggered (for which ``price`` has been used)

      - ``trailamount`` (default: ``None``)

        If the order type is StopTrail or StopTrailLimit, this is an
        absolute amount which determines the distance to the price (below
        for a Sell order and above for a buy order) to keep the trailing
        stop

      - ``trailpercent`` (default: ``None``)

        If the order type is StopTrail or StopTrailLimit, this is a
        percentage amount which determines the distance to the price (below
        for a Sell order and above for a buy order) to keep the trailing
        stop (if ``trailamount`` is also specified it will be used)

      - ``exectype`` (default: ``bt.Order.Limit``)

        Possible values: (see the documentation for the method ``buy``

      - ``valid`` (default: ``None``)

        Possible values: (see the documentation for the method ``buy``

      - ``tradeid`` (default: ``0``)

        Possible values: (see the documentation for the method ``buy``

      - ``oargs`` (default: ``{}``)

        Specific keyword arguments (in a ``dict``) to pass to the main side
        order. Arguments from the default ``**kwargs`` will be applied on
        top of this.

      - ``**kwargs``: additional broker implementations may support extra
        parameters. ``backtrader`` will pass the *kwargs* down to the
        created order objects

        Possible values: (see the documentation for the method ``buy``

        **Note**: this ``kwargs`` will be applied to the 3 orders of a
        bracket. See below for specific keyword arguments for the low and
        high side orders

      - ``stopprice`` (default: ``None``)

        Specific price for the *low side* stop order

      - ``stopexec`` (default: ``bt.Order.Stop``)

        Specific execution type for the *low side* order

      - ``stopargs`` (default: ``{}``)

        Specific keyword arguments (in a ``dict``) to pass to the low side
        order. Arguments from the default ``**kwargs`` will be applied on
        top of this.

      - ``limitprice`` (default: ``None``)

        Specific price for the *high side* stop order

      - ``stopexec`` (default: ``bt.Order.Limit``)

        Specific execution type for the *high side* order

      - ``limitargs`` (default: ``{}``)

        Specific keyword arguments (in a ``dict``) to pass to the high side
        order. Arguments from the default ``**kwargs`` will be applied on
        top of this.

    Returns:

      - A list containing the 3 orders [order, stop side, limit side]

    '''

def sell_bracket(self, data=None,
                 size=None, price=None, plimit=None,
                 exectype=bt.Order.Limit, valid=None, tradeid=0,
                 trailamount=None, trailpercent=None,
                 oargs={},
                 stopprice=None, stopexec=bt.Order.Stop, stopargs={},
                 limitprice=None, limitexec=bt.Order.Limit, limitargs={},
                 **kwargs):
    '''
    Create a bracket order group (low side - buy order - high side). The
    default behavior is as follows:

      - Issue a **sell** order with execution ``Limit``

      - Issue a *high side* bracket **buy** order with execution ``Stop``

      - Issue a *low side* bracket **buy** order with execution ``Limit``.

    See ``bracket_buy`` for the meaning of the parameters

    Returns:

      - A list containing the 3 orders [order, stop side, limit side]

    '''

示例用法

$ ./bracket.py --help
usage: bracket.py [-h] [--data0 DATA0] [--fromdate FROMDATE] [--todate TODATE]
                  [--cerebro kwargs] [--broker kwargs] [--sizer kwargs]
                  [--strat kwargs] [--plot [kwargs]]

Sample Skeleton

optional arguments:
  -h, --help           show this help message and exit
  --data0 DATA0        Data to read in (default:
                       ../../datas/2005-2006-day-001.txt)
  --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: )

示例代码

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

import argparse
import datetime

import backtrader as bt

class St(bt.Strategy):
    params = dict(
        ma=bt.ind.SMA,
        p1=5,
        p2=15,
        limit=0.005,
        limdays=3,
        limdays2=1000,
        hold=10,
        usebracket=False,  # use order_target_size
        switchp1p2=False,  # switch prices of order1 and order2
    )

    def notify_order(self, order):
        print('{}: Order ref: {} / Type {} / Status {}'.format(
            self.data.datetime.date(0),
            order.ref, 'Buy' * order.isbuy() or 'Sell',
            order.getstatusname()))

        if order.status == order.Completed:
            self.holdstart = len(self)

        if not order.alive() and order.ref in self.orefs:
            self.orefs.remove(order.ref)

    def __init__(self):
        ma1, ma2 = self.p.ma(period=self.p.p1), self.p.ma(period=self.p.p2)
        self.cross = bt.ind.CrossOver(ma1, ma2)

        self.orefs = list()

        if self.p.usebracket:
            print('-' * 5, 'Using buy_bracket')

    def next(self):
        if self.orefs:
            return  # pending orders do nothing

        if not self.position:
            if self.cross > 0.0:  # crossing up

                close = self.data.close[0]
                p1 = close * (1.0 - self.p.limit)
                p2 = p1 - 0.02 * close
                p3 = p1 + 0.02 * close

                valid1 = datetime.timedelta(self.p.limdays)
                valid2 = valid3 = datetime.timedelta(self.p.limdays2)

                if self.p.switchp1p2:
                    p1, p2 = p2, p1
                    valid1, valid2 = valid2, valid1

                if not self.p.usebracket:
                    o1 = self.buy(exectype=bt.Order.Limit,
                                  price=p1,
                                  valid=valid1,
                                  transmit=False)

                    print('{}: Oref {} / Buy at {}'.format(
                        self.datetime.date(), o1.ref, p1))

                    o2 = self.sell(exectype=bt.Order.Stop,
                                   price=p2,
                                   valid=valid2,
                                   parent=o1,
                                   transmit=False)

                    print('{}: Oref {} / Sell Stop at {}'.format(
                        self.datetime.date(), o2.ref, p2))

                    o3 = self.sell(exectype=bt.Order.Limit,
                                   price=p3,
                                   valid=valid3,
                                   parent=o1,
                                   transmit=True)

                    print('{}: Oref {} / Sell Limit at {}'.format(
                        self.datetime.date(), o3.ref, p3))

                    self.orefs = [o1.ref, o2.ref, o3.ref]

                else:
                    os = self.buy_bracket(
                        price=p1, valid=valid1,
                        stopprice=p2, stopargs=dict(valid=valid2),
                        limitprice=p3, limitargs=dict(valid=valid3),)

                    self.orefs = [o.ref for o in os]

        else:  # in the market
            if (len(self) - self.holdstart) >= self.p.hold:
                pass  # do nothing in this case

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.BacktraderCSVData(dataname=args.data0, **kwargs)
    cerebro.adddata(data0)

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

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

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

    # 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=(
            'Sample Skeleton'
        )
    )

    parser.add_argument('--data0', default='../../datas/2005-2006-day-001.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()

StopTrail(Limit)

原文:www.backtrader.com/blog/posts/2017-03-22-stoptrail/stoptrail/

版本1.9.35.116为回测工具库添加了StopTrailStopTrailLimit订单执行类型。

注意

这只在回测中实现了,尚未实现实时经纪人的功能

注意

更新至版本1.9.36.116。交互式经纪人支持StopTrailStopTrailLimitOCO

  • OCO始终将一组中的第 1 个订单指定为参数oco

  • StopTrailLimit:经纪人模拟和IB经纪人具有相同的行为。指定:price作为初始止损触发价格(还要指定trailamount),然后plimi作为初始限价。两者之间的差异将确定limitoffset(限价价格与停止触发价格之间的距离)

使用模式完全集成到了策略实例的标准buysellclose市场操作方法中。要注意:

  • 指定所需的执行类型,如exectype=bt.Order.StopTrail

  • 以及尾随价格是固定距离还是基于百分比的距离计算

    • 固定距离:trailamount=10

    • 基于百分比的距离:trailpercent=0.02(即:2%

如果通过发出buy进入市场,则使用StopTrailtrailamountsell就是这样做的操作:

  • 如果未指定price,则使用最新的close价格

  • trailamount从价格中减去以找到stop(或触发)价格

  • broker的下一次迭代将检查触发价格是否已达到

    • 如果:订单将以市价执行方式执行

    • 如果,则stop价格会通过使用最新的close价格重新计算,并减去trailamount的距离。

    • 如果新价格上涨,则会更新

    • 如果新价格会下跌(或保持不变),则将其丢弃

也就是说:尾随止损价格会随着价格上涨而上涨,但如果价格开始下跌,价格将保持不变,以潜在地确保利润。

如果已经用sell进入市场,那么只需使用StopTrail发出buy订单就可以做相反的操作,即:价格向下跟随。

一些使用模式

# For a StopTrail going downwards
# last price will be used as reference
self.buy(size=1, exectype=bt.Order.StopTrail, trailamount=0.25)
# or
self.buy(size=1, exectype=bt.Order.StopTrail, price=10.50, trailamount=0.25)

# For a StopTrail going upwards
# last price will be used as reference
self.sell(size=1, exectype=bt.Order.StopTrail, trailamount=0.25)
# or
self.sell(size=1, exectype=bt.Order.StopTrail, price=10.50, trailamount=0.25)

也可以指定trailpercent而不是trailamount,并且距离将以价格的百分比计算

# For a StopTrail going downwards with 2% distance
# last price will be used as reference
self.buy(size=1, exectype=bt.Order.StopTrail, trailpercent=0.02)
# or
self.buy(size=1, exectype=bt.Order.StopTrail, price=10.50, trailpercent=0.0.02)

# For a StopTrail going upwards with 2% difference
# last price will be used as reference
self.sell(size=1, exectype=bt.Order.StopTrail, trailpercent=0.02)
# or
self.sell(size=1, exectype=bt.Order.StopTrail, price=10.50, trailpercent=0.02)

对于StopTrailLimit

  • 当触发尾随止损价格时,唯一的区别在于发生了什么。

  • 在这种情况下,订单将作为限价订单执行(与StopLimit订单具有相同的行为,但在这种情况下具有动态触发价格)

    注意

    必须指定plimit=x.xbuysell,这将是限价

    注意

    限价价格不像停止/触发价格那样动态更改

例子永远都是值得一千言语的,因此通常的backtrader示例,

  • 使用移动平均线交叉进入市场的方法

  • 使用跟踪止损退出市场

执行与固定价格距离为50

$ ./trail.py --plot --strat trailamount=50.0

产生以下图表

图像

以及以下输出:

**************************************************
2005-02-14,3075.76,3025.76,3025.76
----------
2005-02-15,3086.95,3036.95,3036.95
2005-02-16,3068.55,3036.95,3018.55
2005-02-17,3067.34,3036.95,3017.34
2005-02-18,3072.04,3036.95,3022.04
2005-02-21,3063.64,3036.95,3013.64
...
...
**************************************************
2005-05-19,3051.79,3001.79,3001.79
----------
2005-05-20,3050.45,3001.79,3000.45
2005-05-23,3070.98,3020.98,3020.98
2005-05-24,3066.55,3020.98,3016.55
2005-05-25,3059.84,3020.98,3009.84
2005-05-26,3086.08,3036.08,3036.08
2005-05-27,3084.0,3036.08,3034.0
2005-05-30,3096.54,3046.54,3046.54
2005-05-31,3076.75,3046.54,3026.75
2005-06-01,3125.88,3075.88,3075.88
2005-06-02,3131.03,3081.03,3081.03
2005-06-03,3114.27,3081.03,3064.27
2005-06-06,3099.2,3081.03,3049.2
2005-06-07,3134.82,3084.82,3084.82
2005-06-08,3125.59,3084.82,3075.59
2005-06-09,3122.93,3084.82,3072.93
2005-06-10,3143.85,3093.85,3093.85
2005-06-13,3159.83,3109.83,3109.83
2005-06-14,3162.86,3112.86,3112.86
2005-06-15,3147.55,3112.86,3097.55
2005-06-16,3160.09,3112.86,3110.09
2005-06-17,3178.48,3128.48,3128.48
2005-06-20,3162.14,3128.48,3112.14
2005-06-21,3179.62,3129.62,3129.62
2005-06-22,3182.08,3132.08,3132.08
2005-06-23,3190.8,3140.8,3140.8
2005-06-24,3161.0,3140.8,3111.0
...
...
...
**************************************************
2006-12-19,4100.48,4050.48,4050.48
----------
2006-12-20,4118.54,4068.54,4068.54
2006-12-21,4112.1,4068.54,4062.1
2006-12-22,4073.5,4068.54,4023.5
2006-12-27,4134.86,4084.86,4084.86
2006-12-28,4130.66,4084.86,4080.66
2006-12-29,4119.94,4084.86,4069.94

而不是等待通常的交叉下行模式,系统使用跟踪止损退出市场。让我们以第 1 次操作为例

  • 进入多头时的收盘价格:3075.76

  • 系统计算的跟踪止损价格:3025.76(距离50个单位)

  • 样本计算的跟踪止损价格:3025.76(每行显示的最后价格)

在第一次计算之后:

  • 收盘价上涨至3086.95且止损价格调整为3036.95

  • 以下收盘价格均不超过3086.95且触发价格不变

在其他 2 次操作中也可以看到相同的模式。

为了比较,使用固定距离为30点的执行(只有图表)

$ ./trail.py --plot --strat trailamount=30.0

以及图表

图像

接着最后一个执行,使用trailpercent=0.02

$ ./trail.py --plot --strat trailpercent=0.02

相应的图表。

图像

示例用法

$ ./trail.py --help
usage: trail.py [-h] [--data0 DATA0] [--fromdate FROMDATE] [--todate TODATE]
                [--cerebro kwargs] [--broker kwargs] [--sizer kwargs]
                [--strat kwargs] [--plot [kwargs]]

StopTrail Sample

optional arguments:
  -h, --help           show this help message and exit
  --data0 DATA0        Data to read in (default:
                       ../../datas/2005-2006-day-001.txt)
  --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: )

示例代码

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

import argparse
import datetime

import backtrader as bt

class St(bt.Strategy):
    params = dict(
        ma=bt.ind.SMA,
        p1=10,
        p2=30,
        stoptype=bt.Order.StopTrail,
        trailamount=0.0,
        trailpercent=0.0,
    )

    def __init__(self):
        ma1, ma2 = self.p.ma(period=self.p.p1), self.p.ma(period=self.p.p2)
        self.crup = bt.ind.CrossUp(ma1, ma2)
        self.order = None

    def next(self):
        if not self.position:
            if self.crup:
                o = self.buy()
                self.order = None
                print('*' * 50)

        elif self.order is None:
            self.order = self.sell(exectype=self.p.stoptype,
                                   trailamount=self.p.trailamount,
                                   trailpercent=self.p.trailpercent)

            if self.p.trailamount:
                tcheck = self.data.close - self.p.trailamount
            else:
                tcheck = self.data.close * (1.0 - self.p.trailpercent)
            print(','.join(
                map(str, [self.datetime.date(), self.data.close[0],
                          self.order.created.price, tcheck])
                )
            )
            print('-' * 10)
        else:
            if self.p.trailamount:
                tcheck = self.data.close - self.p.trailamount
            else:
                tcheck = self.data.close * (1.0 - self.p.trailpercent)
            print(','.join(
                map(str, [self.datetime.date(), self.data.close[0],
                          self.order.created.price, tcheck])
                )
            )

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.BacktraderCSVData(dataname=args.data0, **kwargs)
    cerebro.adddata(data0)

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

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

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

    # 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=(
            'StopTrail Sample'
        )
    )

    parser.add_argument('--data0', default='../../datas/2005-2006-day-001.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()
posted @ 2024-04-15 11:10  绝不原创的飞龙  阅读(35)  评论(0编辑  收藏  举报