交易日历#

版本 1.9.42.116 增加了对 交易日历 的支持。在以下场景中,这将会非常有用:

  • 从日级别转换到周级别时,可以同时获取当前周的最后一天和下一周的第一天。

    这是因为交易日历可以确定下一个交易日,因此下一周的最后一天可以提前确定。

  • 在亚日级别转换到日级别时,如果结束时间不是常规时间(这已经可以在数据源中指定),可以使用交易日历。

交易日历接口#

有一个基类 TradingCalendarBase ,它作为任何交易日历的基类。它定义了需要重写的两个方法:

class TradingCalendarBase(with_metaclass(MetaParams, object)):
def _nextday(self, day):

‘’’ 返回 day (日期/时间实例)之后的下一个交易日(日期/时间实例)和isocalendar组件

返回值是一个包含2个组件的元组:(nextday, (y, w, d)),其中 (y, w, d) 是isocalendar组件 ‘’’ raise NotImplementedError

def schedule(self, day):

‘’’ 返回给定 date (日期/时间实例)的开盘和闭盘时间( datetime.time )的元组 ‘’’ raise NotImplementedError实现


PandasMarketCalendar#

这个实现基于一个很好的包,它是Quantopian最初可用功能的一个派生包。这个包在`pandas_market_calendars <https://github.com/rsheftel/pandas_market_calendars>`_中,可以很容易地安装:

pip install pandas_market_calendars

该实现具有以下接口:

class PandasMarketCalendar(TradingCalendarBase):
    '''
    ``pandas_market_calendars''的交易日历包装器。必须先安装
    ``pandas_market_calendar''这个包

    参数:

      - ``calendar`` (默认为 ``None`` )

        参数``calendar''接受以下值:

        - 字符串:支持的日历名称,例如 ``NYSE`` 。这个包装器会尝试获取一个日历实例- 日历实例:使用 ``get_calendar('NYSE')`` 返回的实例。
  • cachesize (默认为 365

    缓存提前进行查找的天数

参见:

‘’’ params = (

(‘calendar’, None), # pandas_market_calendars实例或交易所名称 (‘cachesize’, 365), # 提前缓存的天数

)

TradingCalendar#

使用自行收集的信息,通过指定假日、提前收盘日、非交易日和开盘和收盘时间,可以构建一个日历实例:

class TradingCalendar(TradingCalendarBase):
    '''
    用于交易日历的 ``pandas_market_calendars`` 的包装器。必须安装 ``pandas_market_calendar`` 包。    参数:

  - ``open`` (默认值 ``time.min`` )

    交易日的起始时间

  - ``close`` (默认值 ``time.max`` )

    交易日的结束时间

  - ``holidays`` (默认值 ``[]`` )

    非交易日的列表( ``datetime.datetime`` 实例)

  - ``earlydays`` (默认值 ``[]`` )

    列表,确定不符合常规交易时间的日期和开盘/收盘时间的元组,
    每个元组包括( ``datetime.datetime`` , ``datetime.time`` , ``datetime.time`` )

  - ``offdays`` (默认值 ``ISOWEEKEND`` )工作日历

Backtrader 默认的工作日历是一周中不具有交易的星期几 (ISO 格式,星期一: 1 -> 星期日: 7)。通常情况下是星期六和星期天。

‘’’ params = (

(‘open’, time.min), # 开市时间 (‘close’, _time_max), # 收市时间 (‘holidays’, []), # 不交易的日期列表 (‘earlydays’, []), # 提前收市的日期列表 (‘offdays’, ISOWEEKEND), # 不交易的星期列表

)

使用模式#

全局交易日历#

可以通过 Cerebro 添加一个全局的交易日历,这将是所有数据源的默认日历,除非数据源单独指定了日历:

def addcalendar(self, cal):
    '''向系统中添加一个全局的交易日历。每个数据源可以有独立的日历,这将覆盖全局日历

    ``cal`` 可以是 ``TradingCalendar`` 的实例,字符串或者 ``pandas_market_calendars`` 的实例。如果是字符串,将会被实例化为一个
    ``PandasMarketCalendar`` (需要在系统中安装 ``pandas_market_calendar`` 模块)。

    如果传入的是 ``TradingCalendarBase`` 的子类(不是实例),将会被实例化
    '''

每个数据源#

可以通过在数据源中指定 calendar 参数来设置特定的工作日历,遵循与上述 addcalendar 方法相同的约定。例如:

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

示例#

从日线数据到周线数据#

让我们看一下下面的代码的一个样例运行。在2016年,复活节星期五(2016-03-25)在 NYSE 也是一个节假日。如果在没有交易日历的情况下运行示例,让我们看一下在那个日期周围会发生什么。

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

```shell $ ./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 … ```

在这个输出中,第一个日期是策略执行的日期。第二个日期是日线数据的日期。

这个周的结束日期如预期的那样是2016-03-24(星期四),但是没有交易日历的情况下,重新采样代码无法知道这一点,因此交付的重新采样周期为2016-03-18(上一周)。当交易日进入2016-03-28(星期一)时,重新采样器检测到周的变化,交付了一个2016-03-24的重新采样周期。使用 PandasMarketCalendar 来运行相同的代码,针对 NYSE 进行回测(并添加绘图):

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

...
策略长度 56 日期时间 2016-03-23 数据0长度 56 日期时间 2016-03-23 数据1长度 11 日期时间 2016-03-18
策略长度 57 日期时间 2016-03-24 数据0长度 57 日期时间 2016-03-24 数据1长度 12 日期时间 2016-03-24
策略长度 58 日期时间 2016-03-28 数据0长度 58 日期时间 2016-03-28 数据1长度 12 日期时间 2016-03-24
...

有所改变!由于使用了日历,重新采样器知道一周的结束日期是2016-03-24,并在同一天提供相应的每周重新采样的数据。

还有绘图。

由于并非所有市场都可以获得这些信息,我们也可以自定义日历。对于 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```python

… 策略长度 56 日期时间 2016-03-23 数据0长度 56 日期时间 2016-03-23 数据1长度 11 日期时间 2016-03-18 策略长度 57 日期时间 2016-03-24 数据0长度 57 日期时间 2016-03-24 数据1长度 12 日期时间 2016-03-24 策略长度 58 日期时间 2016-03-28 数据0长度 58 日期时间 2016-03-28 数据1长度 12 日期时间 2016-03-24 …

并且通过自定义的日历定义得到了相同的结果。

分钟到日线#

使用私人分钟级数据,并且根据2016-11-25(感恩节后市场在 US/Eastern 时区于13:00关市)提供的 知识,再次进行测试,这次使用第二个样本。

注意

源数据是直接从展示的数据中获取的,并且是以 CET 时区为准的,即使相关资产 YHOO 是美国的交易, 代码中的数据源使用 tzinput='CET'tz='US/Eastern' 来让平台适当地进行输入和输出的转换。

首先,没有交易日历的情况。

$ ./tcal-intra.py

...
策略长度 6838 日期时间 2016-11-25 18:00:00 数据0长度 6838 日期时间 2016-11-25 13:00:00 数据1长度 21 日期时间 2016-11-23 16:00:00
策略长度 6839 日期时间 2016-11-25 18:01:00 数据0长度 6839 日期时间 2016-11-25 13:01:00 数据1长度 21 日期时间 2016-11-23 16:00:00
策略长度 6840 日期时间 2016-11-28 14:31:00 数据0长度 6840 日期时间 2016-11-28 09:31:00 数据1长度 22 日期时间 2016-11-25 16:00:00
策略长度 6841 日期时间 2016-11-28 14:32:00 数据0长度 6841 日期时间 2016-11-28 09:32:00 数据1长度 22 日期时间 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

… 策略长度 6838 时间戳 2016-11-25 18:00:00 数据0长度 6838 时间戳 2016-11-25 13:00:00 数据1长度 15 时间戳 2016-11-25 13:00:00 策略长度 6839 时间戳 2016-11-25 18:01:00 数据0长度 6839 时间戳 2016-11-25 13:01:00 数据1长度 15 时间戳 2016-11-25 13:00:00 策略长度 6840 时间戳 2016-11-28 14:31:00 数据0长度 6840 时间戳 2016-11-28 09:31:00 数据1长度 15 时间戳 2016-11-25 13:00:00 策略长度 6841 时间戳 2016-11-28 14:32:00 数据0长度 6841 时间戳 2016-11-28 09:32:00 数据1长度 15 时间戳 2016-11-25 13:00:00 …

当intraday 1分钟数据流到达2016-11-25的13:00时,2016-11-25的日线数据被传递给策略(忽略了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…

策略长度 6838 日期时间 2016-11-25 18:00:00 Data0 长度 6838 日期时间 2016-11-25 13:00:00 Data1 长度 15 日期时间 2016-11-23 16:00:00 策略长度 6839 日期时间 2016-11-25 18:01:00 Data0 长度 6839 日期时间 2016-11-25 13:01:00 Data1 长度 16 日期时间 2016-11-25 13:01:00 策略长度 6840 日期时间 2016-11-28 14:31:00 Data0 长度 6840 日期时间 2016-11-28 09:31:00 Data1 长度 16 日期时间 2016-11-25 13:01:00 策略长度 6841 日期时间 2016-11-28 14:32:00 Data0 长度 6841 日期时间 2016-11-28 09:32:00 Data1 长度 16 日期时间 2016-11-25 13:01:00 …

一位热心的读者会注意到,Crafted Calendar的定义指定了 2016-11-25 的短会话结束时间为 13:01 (使用 datetime.time(13, 1) )。这只是为了展示如何使用Crafted Calendar来帮助我们进行拟合。

现在2016-11-25这一天的每日重采样数据和13:01的1分钟数据一起提供。

额外奖励给策略#

第一个日期时间,即策略的日期时间,总是处于一个不同的时区中,实际上是 UTC 时区。而且,从 1.9.42.116 版本开始,这可以被同步。现在已经添加了以下参数到 Cerebro 中(可以在实例化时或 cerebro.run 时使用):

  • tz (默认值: None

    为策略添加一个全局时区。参数 tz 可以是:

    • None :在这种情况下,策略显示的日期时间将是UTC,这一直是标准行为

    • pytz 实例。将会将UTC时间转换为所选时区

    • 字符串 。将尝试实例化一个 pytz 实例。- integer . 在策略中使用与 self.datas 可迭代对象中的相应 data 相同的时区( 0`将使用 data0`的时区)

还可以使用 cerebro.addtz 方法进行支持:

```python def addtz(self, tz):

‘’’ 这也可以通过参数 tz 来完成

为策略添加全局时区。参数 tz 可以是以下几种类型

  • None :此时策略显示的日期时间将为UTC时间,这一直是标准行为

  • pytz 实例:将被用来将UTC时间转换为选择的时区

  • string :尝试实例化一个 pytz 实例

  • integer :使用与 self.datas 可迭代对象中相应 data 相同的时区( 0`将使用 data0`的时区)

‘’’

```

通过重复运行分钟级别示例,并将 0`用于 tz (与`data0 的时区同步),以下是输出结果,重点关注与上述相同的日期和时间:``` $ ./tcal-intra.py –owncal –cerebro tz=0

… 策略长度 6838 时间戳 2016-11-25 13:00:00 Data0长度 6838 时间戳 2016-11-25 13:00:00 Data1长度 15 时间戳 2016-11-23 16:00:00 策略长度 6839 时间戳 2016-11-25 13:01:00 Data0长度 6839 时间戳 2016-11-25 13:01:00 Data1长度 16 时间戳 2016-11-25 13:01:00 策略长度 6840 时间戳 2016-11-28 09:31:00 Data0长度 6840 时间戳 2016-11-28 09:31:00 Data1长度 16 时间戳 2016-11-25 13:01:00 策略长度 6841 时间戳 2016-11-28 09:32:00 Data0长度 6841 时间戳 2016-11-28 09:32:00 Data1长度 16 时间戳 2016-11-25 13:01:00 …

时间戳已根据时区调整。

示例用法(tcal.py)#

$ ./tcal.py --help
用法: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}]

交易日历示例

可选参数:
  -h, --help            显示此帮助消息并退出
  --data0 DATA0         要读取的数据(默认:YHOO)
  --offline             从与股票代码相同名称的磁盘中读取(默认:False)
  --fromdate FROMDATE   日期[时间](默认:2016-01-01)
  --todate TODATE       日期[时间](默认:2016-12-31)
  --cerebro kwargs      以键=值格式的kwargs(默认:)
  --broker kwargs       以键=值格式的kwargs(默认:)
  --sizer kwargs        以键=值格式的kwargs(默认:)
  --strat kwargs        以键=值格式的kwargs(默认:)
  --plot [kwargs]       以键=值格式的kwargs(默认:)
  --pandascal PANDASCAL
                        要使用的交易日历的名称(默认:)
  --owncal              应用自定义的纽约证券交易所(NYSE)2016日历(默认:False)
  --timeframe {Weeks,Months,Years}
                        要重新采样的时间框架(默认:Weeks)

示例用法(tcal-intra.py)#

$ ./tcal-intra.py --help
用法: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}]

交易日历示例

```可选参数: -h,–help:显示帮助消息并退出 –data0 DATA0:要读取的数据(默认值:yhoo-2016-11.csv) –fromdate FROMDATE:开始日期[时间],格式为YYYY-MM-DD[THH:MM:SS](默认值:2016-01-01) –todate TODATE:结束日期[时间],格式为YYYY-MM-DD[THH:MM:SS](默认值:2016-12-31) –cerebro kwargs:以key=value格式的kwargs(默认值:) –broker kwargs:以key=value格式的kwargs(默认值:) –sizer kwargs:以key=value格式的kwargs(默认值:) –strat kwargs:以key=value格式的kwargs(默认值:) –plot [kwargs]:以key=value格式的kwargs(默认值:) –pandascal PANDASCAL:要使用的交易日历的名称(默认值:) –owncal:应用自定义的纽约证券交易所(NYSE)2016年日历(默认值:False) –timeframe {Days}:要将时间段重新采样为的时间段(默认值: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()