Skip to content

Latest commit

 

History

History
648 lines (551 loc) · 28.5 KB

File metadata and controls

648 lines (551 loc) · 28.5 KB

【backtrader 与 IB(盈透证券)实盘交易教程 3】一个能够在 IB 上实现自动化交易的例子(2021-12-20 修改)

原文:https://yunjinqi.blog.csdn.net/article/details/122031522

这个例子参考了 backtrader 在 github 上的 samples 中的 ibtest.py 的代码,但是做了一些简单的改进工作,使用了 2 个半小时。

  • 做了大量的注释,尤其是每个参数的作用
  • 尝试对每个函数,主要的代码做了注释
  • 修改了 parser 的使用方式,直接在策略里面设定好需要使用的参数
  • 指出了代码里面可能的一些 bug
  • 使用起来更加简单了

修改记录

运行这个程序的过程中,对若干代码进行了修改,解决了好几个小 bug,还需要时间看有没有其他问题。

#!/usr/bin/env python
# -*- coding: utf-8; py-indent-offset:4 -*-

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

import argparse
import datetime

# The above could be sent to an independent module
import backtrader as bt
from backtrader.utils import flushfile  # win32 quick stdout flushing

class TestStrategy(bt.Strategy):
    # 策略的参数,具体的参数的意义在下面有注释
    params = dict(
        smaperiod=5, 
        trade=False,
        stake=10,
        exectype=bt.Order.Market,
        stopafter=0,
        valid=None,
        cancel=0,
        donotsell=False,
        stoptrail=False,
        stoptraillimit=False,
        trailamount=None,
        trailpercent=None,
        limitoffset=None,
        oca=False,
        bracket=False,
    )

    def __init__(self):
        # To control operation entries
        self.orderid = list()
        self.order = None

        self.counttostop = 0
        self.datastatus = 0

        # Create SMA on 2nd data
        self.sma = bt.indicators.MovAv.SMA(self.data, period=self.p.smaperiod)

        print('--------------------------------------------------')
        print('Strategy Created')
        print('--------------------------------------------------')

    def notify_data(self, data, status, *args, **kwargs):
        print('*' * 5, 'DATA NOTIF:', data._getstatusname(status), *args)
        if status == data.LIVE:
            self.counttostop = self.p.stopafter
            self.datastatus = 1   # 如果是实盘数据开始了,那么,datastatus 就变为 1

    def notify_store(self, msg, *args, **kwargs):
        # 显示从 IB 传送过来的信息
        print('*' * 5, 'STORE NOTIF:', msg)

    def notify_order(self, order):
        # 通知订单
        if order.status in [order.Completed, order.Cancelled, order.Rejected]:
            self.order = None

        print('-' * 50, 'ORDER BEGIN', datetime.datetime.now())
        print(order)
        print('-' * 50, 'ORDER END')

    def notify_trade(self, trade):
        # 通知 trade
        print('-' * 50, 'TRADE BEGIN', datetime.datetime.now())
        print(trade)
        print('-' * 50, 'TRADE END')

    def prenext(self):
        print("成功进入 prenext")
        self.next(frompre=True)

    def next(self, frompre=False):
        # print("成功进入 next")
        # 准备显示第一个数据
        # txt = list()
        # txt.append('Data0')
        # txt.append('%04d' % len(self.data0))
        # dtfmt = '%Y-%m-%dT%H:%M:%S.%f'
        # txt.append('{}'.format(self.data.datetime[0]))
        # txt.append('%s' % self.data.datetime.datetime(0).strftime(dtfmt))
        # txt.append('{}'.format(self.data.open[0]))
        # txt.append('{}'.format(self.data.high[0]))
        # txt.append('{}'.format(self.data.low[0]))
        # txt.append('{}'.format(self.data.close[0]))
        # txt.append('{}'.format(self.data.volume[0]))
        # txt.append('{}'.format(self.data.openinterest[0]))
        # txt.append('{}'.format(self.sma[0]))
        if self.datastatus>0:
            print(f"{self.data._name},\
                datetime:{bt.num2date(self.data.datetime[0])} , \
                open:{self.data.open[0]},\
                high:{self.data.high[0]},\
                low:{self.data.low[0]},\
                close:{self.data.close[0]},\
                volume:{self.data.volume[0]},\
                openinterest:{self.data.openinterest[0]},\
                ma:{self.sma[0]}")
        # 如果有第二个数据,那么也打印第二个数据
        if len(self.datas) > 1 and len(self.data1):
            # txt = list()
            # txt.append('Data1')
            # txt.append('%04d' % len(self.data1))
            # dtfmt = '%Y-%m-%dT%H:%M:%S.%f'
            # txt.append('{}'.format(self.data1.datetime[0]))
            # txt.append('%s' % self.data1.datetime.datetime(0).strftime(dtfmt))
            # txt.append('{}'.format(self.data1.open[0]))
            # txt.append('{}'.format(self.data1.high[0]))
            # txt.append('{}'.format(self.data1.low[0]))
            # txt.append('{}'.format(self.data1.close[0]))
            # txt.append('{}'.format(self.data1.volume[0]))
            # txt.append('{}'.format(self.data1.openinterest[0]))
            # txt.append('{}'.format(float('NaN')))
            # print(', '.join(txt))
            print(f"{self.data1._name},\
                datetime:{bt.num2date(self.data1.datetime[0])} , \
                open:{self.data1.open[0]},\
                high:{self.data1.high[0]},\
                low:{self.data1.low[0]},\
                close:{self.data1.close[0]},\
                volume:{self.data1.volume[0]},\
                openinterest:{self.data1.openinterest[0]},\
                ma:{self.sma[0]}")
        # 多少个 next 之后策略停止
        if self.counttostop:  # stop after x live lines
            self.counttostop -= 1
            if not self.counttostop:
                self.env.runstop()
                return
        # 如果 trade 设置的是 False,下面将不会运行
        if not self.p.trade:
            return
        position_size = self.getposition(self.data).size
        print(f"self.datastatus:{self.datastatus},position_size:{position_size},len(self.orderid):{len(self.orderid)}")
        # 如果是实盘数据,并且没有持仓,并且没有下单
        if self.datastatus and position_size==0 and len(self.orderid) < 1:
            print("准备下单")
            # 如果不是 oca 订单类型,就直接下一个市价单,否则按照当前价格的 90%下一个限价单,有效期是默认取消前有效,如果设置的是 0 的话,当天有效
            exectype = self.p.exectype if not self.p.oca else bt.Order.Limit
            close = self.data0.close[0]
            price = round(close * 1.001, 2)
            self.order = self.buy(size=self.p.stake,
                                  exectype=exectype,
                                  price=price,
                                  valid=self.p.valid,
                                  transmit=not self.p.bracket)

            self.orderid.append(self.order)
            print(f"下单成功:exectype-{exectype},self.p.bracket:{self.p.bracket},self.p.oca:{self.p.oca}")
            # 如果是一篮子订单,按照 90%的价格设置止损价,按照 110%的价格设置止盈价
            if self.p.bracket:
                # low side
                self.sell(size=self.p.stake,
                          exectype=bt.Order.Stop,
                          price=round(price * 0.90, 2),
                          valid=self.p.valid,
                          transmit=False,
                          parent=self.order)

                # high side
                self.sell(size=self.p.stake,
                          exectype=bt.Order.Limit,
                          price=round(close * 1.10, 2),
                          valid=self.p.valid,
                          transmit=True,
                          parent=self.order)
            # 如果是 oca 订单,按照 80%的价格下一个限价单
            elif self.p.oca:
                self.buy(size=self.p.stake,
                         exectype=bt.Order.Limit,
                         price=round(self.data0.close[0] * 0.80, 2),
                         oco=self.order)
            # 如果是跟踪止损单
            elif self.p.stoptrail:
                self.sell(size=self.p.stake,
                          exectype=bt.Order.StopTrail,
                          # price=round(self.data0.close[0] * 0.90, 2),
                          valid=self.p.valid,
                          trailamount=self.p.trailamount,
                          trailpercent=self.p.trailpercent)
            # 如果是跟踪止损限价单
            elif self.p.stoptraillimit:
                p = round(self.data0.close[0] - self.p.trailamount, 2)
                # p = self.data0.close[0]
                self.sell(size=self.p.stake,
                          exectype=bt.Order.StopTrailLimit,
                          price=p,
                          plimit=p + self.p.limitoffset,
                          valid=self.p.valid,
                          trailamount=self.p.trailamount,
                          trailpercent=self.p.trailpercent)
        # 如果持仓大于 0,并没有不允许卖出平仓 
        elif self.position.size > 0 and not self.p.donotsell:
            # 如果没有订单,市价单卖出二分之一仓位
            if self.order is None:
                self.order = self.sell(size=self.p.stake // 2,
                                       exectype=bt.Order.Market,
                                       price=self.data0.close[0])
        # 如果当前存在订单,并且 cancel 设置大于 0 的时候,在 next 运行的次数大于 self.cancel 的时候,会触发取消订单
        elif self.order is not None and self.p.cancel:
            if self.datastatus > self.p.cancel:
                self.cancel(self.order)
        # 每个 next,datastatus 加 1
        if self.datastatus:
            self.datastatus += 1

    def start(self):
        print("开始运行,获取时区")
        if self.data0.contractdetails is not None:
            print('Timezone from ContractDetails: {}'.format(
                  self.data0.contractdetails.m_timeZoneId))

        header = ['Datetime', 'Open', 'High', 'Low', 'Close', 'Volume',
                  'OpenInterest', 'SMA']
        print(', '.join(header))
        print(dir(self.data0.contractdetails))

        self.done = False

def runstrategy(args):
    # args = parse_args()

    # Create a cerebro
    cerebro = bt.Cerebro()
    # IBstore 参数
    storekwargs = dict(
        host=args.host, port=args.port,
        clientId=args.clientId, timeoffset=not args.no_timeoffset,
        reconnect=args.reconnect, timeout=args.timeout,
        notifyall=args.notifyall, _debug=args.debug
    )

    if args.usestore:
        ibstore = bt.stores.IBStore(**storekwargs)

    if args.broker:
        if args.usestore:
            broker = ibstore.getbroker()
        else:
            broker = bt.brokers.IBBroker(**storekwargs)

        cerebro.setbroker(broker)

    timeframe = args.timeframe
    # Manage data1 parameters
    tf1 = args.timeframe1
    tf1 = tf1 if tf1 is not None else timeframe
    cp1 = args.compression1
    cp1 = cp1 if cp1 is not None else args.compression

    if args.resample or args.replay:
        datatf = datatf1 = bt.TimeFrame.Ticks
        datacomp = datacomp1 = 1
    else:
        datatf = timeframe
        datacomp = args.compression
        datatf1 = tf1
        datacomp1 = cp1

    fromdate = None
    if args.fromdate:
        dtformat = '%Y-%m-%d' + ('T%H:%M:%S' * ('T' in args.fromdate))
        fromdate = datetime.datetime.strptime(args.fromdate, dtformat)
    # 获取数据
    IBDataFactory = ibstore.getdata if args.usestore else bt.feeds.IBData
    # 数据的参数
    datakwargs = dict(
        timeframe=datatf, compression=datacomp,
        historical=args.historical, fromdate=fromdate,
        rtbar=args.rtbar,
        qcheck=args.qcheck,
        what=args.what,
        backfill_start=not args.no_backfill_start,
        backfill=not args.no_backfill,
        latethrough=args.latethrough,
        tz=args.timezone
    )
    # 如果没有用 store 模式并且没有用设置 broker,更新参数
    if not args.usestore and not args.broker:   # neither store nor broker
        datakwargs.update(storekwargs)  # pass the store args over the data
    # 获取数据
    data0 = IBDataFactory(dataname=args.data0, **datakwargs)
    # 是否获取数据 1
    data1 = None
    if args.data1 is not None:
        if args.data1 != args.data0:
            datakwargs['timeframe'] = datatf1
            datakwargs['compression'] = datacomp1
            data1 = IBDataFactory(dataname=args.data1, **datakwargs)
        else:
            data1 = data0

    rekwargs = dict(
        timeframe=tf1, compression=cp1,
        bar2edge=not args.no_bar2edge,
        adjbartime=not args.no_adjbartime,
        rightedge=not args.no_rightedge,
        takelate=not args.no_takelate,
    )

    if args.replay:
        cerebro.replaydata(data0, **rekwargs)

        if data1 is not None:
            rekwargs['timeframe'] = tf1
            rekwargs['compression'] = cp1
            cerebro.replaydata(data1, **rekwargs)

    elif args.resample:
        cerebro.resampledata(data0, **rekwargs)

        if data1 is not None:
            rekwargs['timeframe'] = tf1
            rekwargs['compression'] = cp1
            cerebro.resampledata(data1, **rekwargs)

    else:
        cerebro.adddata(data0)
        if data1 is not None:
            cerebro.adddata(data1)
    # 数据的有效期,这个地方需要额外考虑是 0 的时候,当天有效的设置,这里没有设置好,是 0 的时候,有效期基本上相当于市价单,没有成交就会立即撤单
    if args.valid is None:
        valid = None
    else:
        valid = datetime.timedelta(seconds=args.valid)
    # Add the strategy
    cerebro.addstrategy(TestStrategy,
                        smaperiod=args.smaperiod,
                        trade=args.trade,
                        exectype=args.exectype,
                        stake=args.stake,
                        stopafter=args.stopafter,
                        valid=valid,
                        cancel=args.cancel,
                        donotsell=args.donotsell,
                        stoptrail=args.stoptrail,
                        stoptraillimit=args.traillimit,
                        trailamount=args.trailamount,
                        trailpercent=args.trailpercent,
                        limitoffset=args.limitoffset,
                        oca=args.oca,
                        bracket=args.bracket)

    # Live data ... avoid long data accumulation by switching to "exactbars"
    cerebro.run(exactbars=args.exactbars)

    if args.plot and args.exactbars < 1:  # plot if possible
        cerebro.plot()

args ={"exactbars":1,# exactbars level, use 0/-1/-2 to enable plotting
       "stopafter":0,# Stop after x lines of LIVE data,默认情况下,永远不停止,如果是一个大于 0 的整数,就代表多少个 next 之后会停止
       "plot":False,# Plot if possible
       "usestore":True,#使用 ib 的时候是否使用 store 模式
       "notifyall":False,#是否把所有信息都会通过 store notify 告知给策略 
        "debug":False,#默认不显示所有的从 IB 获取的信息
        "host":'127.0.0.1',#连接 TWS 的时候使用的 host
        "port":7497,#连接 TWS 的端口号,默认是 7496,模拟交易使用 7497
        "qcheck":0.5,#在 resample 或者 replay 的时候多少时间间隔检查一次,用于生成 bar
        "clientId":None,#默认情况下可以随机生成一个,开多个 TWS 的时候可以指定每个 clientid,有一个 master id 可以控制其他的 clientid
        "no_timeoffset":False,# 是否使用 IB 的系统时间和本地时钟之间做时间的补偿,使得本地时间和系统时间能够对齐,以方便能够生成 bar 更准确
        "reconnect":3,#当连接中断之后,尝试的重新连接次数
        "timeout":3,#每次尝试连接的时候,间隔的时间,默认是 3 秒
        "data0":True,#加载到系统的第一个数据
        "data1":None,#加载到系统的第二个数据
        "timezone":None,#默认情况下,根据从 IB 获取的时间设置时区,也可以自己去设置
        "what":None,#用于请求的历史数据类型
        "no_backfill_start":False,#默认开始的时候不填充历史数据
        "latethrough":False,#当 resample 或者 replay 的时候,让来的太迟的 tick 传递过去
        "no_backfill":False,#当连接中断之后,是否填充,默认是不填充
        "rtbar":False,#是否使用 5 秒钟的 bar 代替 250ms 的 tick,默认是不会
        "historical":False,#是否仅仅下载历史数据,默认是否
        "fromdate":False,#从哪里开始下载历史数据,参数可以设置具体的时间 YYYY-MM-DD[THH:MM:SS]
        "smaperiod":5,#策略运行使用的参数
        "replay":False,#replay
        "resample":True,#resample,和 replay 两个功能不能同时都设置成 True
        "timeframe":bt.TimeFrame.Ticks,#交易的时间间隔
        "compression":1,#多少个交易的时间间隔形成一个 bar
        "timeframe1":bt.TimeFrame.Seconds,#data1 的
        "compression1":5,#data1 的
        "no_takelate":False,#当 latethrough 设置成 True 的时候,resample 或者 replay 形成新的 bar 的时候,是否使用来的比较迟的 tick
        "no_bar2edge":False,# no bar2edge for resample/replay
        "no_adjbartime":False,#no adjbartime for resample/replay
        "no_rightedge":False,#no rightedge for resample/replay
        "broker":False,#使用 IB 作为 broker
        "trade":True,#是否进行买卖活动,设置成 False 的时候,不会进行买卖
        "donotsell":False,#买了之后是否卖,默认是买了之后会卖的
        "exectype":bt.Order.Market,# 下单的类型,默认是市价单
        "stake":0.1,#每次下单的手数
        "valid":None,#订单的有效期设置,None 代表一直有效,0 代表当天有效
        # 下面几种止损不能共存,只能选一个
        "stoptrail":False,#是否下一个市价止损单,默认是否
        "traillimit":False,#是否下一个限价跟踪止损单,默认是否
        "oca":False,#是否下一个 oca 订单,应该是 backtrader 中的 oco 订单
        "bracket":False,#是否下一个一篮子订单
        # 下面几种也是不能共存的
        "trailamount":None,#StopTrail 订单设置的参数
        "trailpercent":None,#StopTrail 订单设置的参数
        "limitoffset":None,#订单的参数
        "cancel":0,# 限价单使用,如果 n 个 bar 之后还没有成交,取消订单

       }

class AO():
    def __init__(self,args):
        for key,value in args.items():
            setattr(self,key,value)

if __name__ == '__main__':
    args['data0'] = 'EUR.USD-CASH-IDEALPRO'
    new_args = AO(args)
    runstrategy(new_args)

# def parse_args():
#     parser = argparse.ArgumentParser(
#         formatter_class=argparse.ArgumentDefaultsHelpFormatter,
#         description='Test Interactive Brokers integration')

#     parser.add_argument('--exactbars', default=1, type=int,
#                         required=False, action='store',
#                         help='exactbars level, use 0/-1/-2 to enable plotting')

#     parser.add_argument('--plot',
#                         required=False, action='store_true',
#                         help='Plot if possible')

#     parser.add_argument('--stopafter', default=0, type=int,
#                         required=False, action='store',
#                         help='Stop after x lines of LIVE data')

#     parser.add_argument('--usestore',
#                         required=False, action='store_true',
#                         help='Use the store pattern')

#     parser.add_argument('--notifyall',
#                         required=False, action='store_true',
#                         help='Notify all messages to strategy as store notifs')

#     parser.add_argument('--debug',
#                         required=False, action='store_true',
#                         help='Display all info received form IB')

#     parser.add_argument('--host', default='127.0.0.1',
#                         required=False, action='store',
#                         help='Host for the Interactive Brokers TWS Connection')

#     parser.add_argument('--qcheck', default=0.5, type=float,
#                         required=False, action='store',
#                         help=('Timeout for periodic '
#                               'notification/resampling/replaying check'))

#     parser.add_argument('--port', default=7496, type=int,
#                         required=False, action='store',
#                         help='Port for the Interactive Brokers TWS Connection')

#     parser.add_argument('--clientId', default=None, type=int,
#                         required=False, action='store',
#                         help='Client Id to connect to TWS (default: random)')

#     parser.add_argument('--no-timeoffset',
#                         required=False, action='store_true',
#                         help=('Do not Use TWS/System time offset for non '
#                               'timestamped prices and to align resampling'))

#     parser.add_argument('--reconnect', default=3, type=int,
#                         required=False, action='store',
#                         help='Number of recconnection attempts to TWS')

#     parser.add_argument('--timeout', default=3.0, type=float,
#                         required=False, action='store',
#                         help='Timeout between reconnection attempts to TWS')

#     parser.add_argument('--data0', default=None,
#                         required=True, action='store',
#                         help='data 0 into the system')

#     parser.add_argument('--data1', default=None,
#                         required=False, action='store',
#                         help='data 1 into the system')

#     parser.add_argument('--timezone', default=None,
#                         required=False, action='store',
#                         help='timezone to get time output into (pytz names)')

#     parser.add_argument('--what', default=None,
#                         required=False, action='store',
#                         help='specific price type for historical requests')

#     parser.add_argument('--no-backfill_start',
#                         required=False, action='store_true',
#                         help='Disable backfilling at the start')

#     parser.add_argument('--latethrough',
#                         required=False, action='store_true',
#                         help=('if resampling replaying, adjusting time '
#                               'and disabling time offset, let late samples '
#                               'through'))

#     parser.add_argument('--no-backfill',
#                         required=False, action='store_true',
#                         help='Disable backfilling after a disconnection')

#     parser.add_argument('--rtbar', default=False,
#                         required=False, action='store_true',
#                         help='Use 5 seconds real time bar updates if possible')

#     parser.add_argument('--historical',
#                         required=False, action='store_true',
#                         help='do only historical download')

#     parser.add_argument('--fromdate',
#                         required=False, action='store',
#                         help=('Starting date for historical download '
#                               'with format: YYYY-MM-DD[THH:MM:SS]'))

#     parser.add_argument('--smaperiod', default=5, type=int,
#                         required=False, action='store',
#                         help='Period to apply to the Simple Moving Average')

#     pgroup = parser.add_mutually_exclusive_group(required=False)

#     pgroup.add_argument('--replay',
#                         required=False, action='store_true',
#                         help='replay to chosen timeframe')

#     pgroup.add_argument('--resample',
#                         required=False, action='store_true',
#                         help='resample to chosen timeframe')

#     parser.add_argument('--timeframe', default=bt.TimeFrame.Names[0],
#                         choices=bt.TimeFrame.Names,
#                         required=False, action='store',
#                         help='TimeFrame for Resample/Replay')

#     parser.add_argument('--compression', default=1, type=int,
#                         required=False, action='store',
#                         help='Compression for Resample/Replay')

#     parser.add_argument('--timeframe1', default=None,
#                         choices=bt.TimeFrame.Names,
#                         required=False, action='store',
#                         help='TimeFrame for Resample/Replay - Data1')

#     parser.add_argument('--compression1', default=None, type=int,
#                         required=False, action='store',
#                         help='Compression for Resample/Replay - Data1')

#     parser.add_argument('--no-takelate',
#                         required=False, action='store_true',
#                         help=('resample/replay, do not accept late samples '
#                               'in new bar if the data source let them through '
#                               '(latethrough)'))

#     parser.add_argument('--no-bar2edge',
#                         required=False, action='store_true',
#                         help='no bar2edge for resample/replay')

#     parser.add_argument('--no-adjbartime',
#                         required=False, action='store_true',
#                         help='no adjbartime for resample/replay')

#     parser.add_argument('--no-rightedge',
#                         required=False, action='store_true',
#                         help='no rightedge for resample/replay')

#     parser.add_argument('--broker',
#                         required=False, action='store_true',
#                         help='Use IB as broker')

#     parser.add_argument('--trade',
#                         required=False, action='store_true',
#                         help='Do Sample Buy/Sell operations')

#     parser.add_argument('--donotsell',
#                         required=False, action='store_true',
#                         help='Do not sell after a buy')

#     parser.add_argument('--exectype', default=bt.Order.ExecTypes[0],
#                         choices=bt.Order.ExecTypes,
#                         required=False, action='store',
#                         help='Execution to Use when opening position')

#     parser.add_argument('--stake', default=10, type=int,
#                         required=False, action='store',
#                         help='Stake to use in buy operations')

#     parser.add_argument('--valid', default=None, type=int,
#                         required=False, action='store',
#                         help='Seconds to keep the order alive (0 means DAY)')

#     pgroup = parser.add_mutually_exclusive_group(required=False)
#     pgroup.add_argument('--stoptrail',
#                         required=False, action='store_true',
#                         help='Issue a stoptraillimit after buy( do not sell')

#     pgroup.add_argument('--traillimit',
#                         required=False, action='store_true',
#                         help='Issue a stoptrail after buying (do not sell')

#     pgroup.add_argument('--oca',
#                         required=False, action='store_true',
#                         help='Test oca by putting 2 orders in a group')

#     pgroup.add_argument('--bracket',
#                         required=False, action='store_true',
#                         help='Test bracket orders by issuing high/low sides')

#     pgroup = parser.add_mutually_exclusive_group(required=False)
#     pgroup.add_argument('--trailamount', default=None, type=float,
#                         required=False, action='store',
#                         help='trailamount for StopTrail order')

#     pgroup.add_argument('--trailpercent', default=None, type=float,
#                         required=False, action='store',
#                         help='trailpercent for StopTrail order')

#     parser.add_argument('--limitoffset', default=None, type=float,
#                         required=False, action='store',
#                         help='limitoffset for StopTrailLimit orders')

#     parser.add_argument('--cancel', default=0, type=int,
#                         required=False, action='store',
#                         help=('Cancel a buy order after n bars in operation,'
#                               ' to be combined with orders like Limit'))

#     return parser.parse_args()