交互经纪#

与交互经纪的集成支持以下两种功能:

  • 实时数据 提供

  • 实时交易

注意

尽管我们已经尽力测试了尽可能多的错误条件和情况,但代码可能(像任何其他软件一样)包含错误。

在投入生产之前,请使用**模拟交易**账户或TWS上的**演示**进行全面测试您的策略。

注意

与交互经纪的交互是使用``IbPy``模块完成的,使用前必须安装该模块。在Pypi上没有该包(在撰写本文时),但可以使用以下命令使用``pip``进行安装:

pip install git+https://github.com/blampe/IbPy.git

如果在您的系统上没有安装``git``(Windows安装?),下面的方法可能也可以使用:安装指令


通过以下指令从 GitHub 安装:

示例代码#

源代码包含了一个完整的示例在:

  • samples/ibtest/ibtest.py

该示例无法涵盖所有可能的用例,但它试图提供广泛的见解,并且应该强调在使用回测模块和实时数据模块时没有实质性区别

一个重要的事项可以确定:

  • 在进行任何交易活动之前,示例会等待 data.LIVE 数据状态的通知。

    在任何实时策略中,这可能是需要考虑的内容

存储模型与直接模型#

与 Interactive Brokers 的交互支持通过两种模型: #. 存储模型(首选)

  1. 与数据源类和经纪人类直接交互

存储模型在创建经纪人和数据时提供了明确的分离模式。下面的两个代码片段应该更好地作为示例。

首先,使用**Store**模型:

```python import backtrader as bt

ibstore = bt.stores.IBStore(host=’127.0.0.1’, port=7496, clientId=35) data = ibstore.getdata(dataname=’EUR.USD-CASH-IDEALPRO’) ```

这里的参数:

  • hostportclientId 被传递到它们应该去的地方,也就是 IBStore,该类使用这些参数打开连接。

然后,使用 getdata 创建一个**数据源**,它是 backtrader 中所有数据源的共同参数:

  • dataname ,它请求 EUR /USD 外汇对。现在使用直接模式:

    import backtrader as bt

    data = bt.feeds.IBData(dataname=’EUR.USD-CASH-IDEALPRO’,

    host=’127.0.0.1’, port=7496, clientId=35)

在这里:

  • 用于存储的参数会传递到数据上。

  • 这些参数将用于在后台创建一个``IBStore``实例。

缺点:

  • 可读性下降了,因为不清楚哪些属于数据,哪些属于存储。

IBStore - 存储#

存储是实时数据源/交易支持的关键,它在``IbPy``模块和数据源/经纪人代理的需要之间提供了一个适配层。

*存储*是一个概念,涵盖了以下功能: - 作为一个实体(此处为IB)的中央商店:

可能需要参数

  • 通过以下方法获取 broker 实例的访问权:

    • IBStore.getbroker(*args, **kwargs)

  • 通过以下方法获取 data feed实例的访问权:

    • IBStore.getdata(*args, **kwargs)

      在该情况下,许多``**kwargs``参数对于数据feed(如``dataname``、fromdatetodatesessionstartsessionendtimeframecompression)是通用的

      数据可能提供其他参数,请参考下面的参考资料。

``IBStore``提供:

  • 连接目标(``host``和``port``参数)- 标识(``clientId``参数)

  • 重连控制(``reconnect``和``timeout``参数)

  • 时间偏移检查(``timeoffset``参数,请参阅下文)

  • 通知和调试

    notifyall``(默认:``False):在这种情况下,IB发送的任何``error``信息(许多只是信息性的)将被转发到 Cerebro /Strategy

    _debug``(默认:``False):在这种情况下,从TWS接收的每一条消息都将被打印到标准输出

IB数据源#

数据选项#

无论是直接还是通过``getdata``,``IBData``数据源支持以下数据选项:

  • 历史下载请求如果持续时间超过IB对于给定的 时间段/压缩 组合所施加的限制,这些将被分成多个请求。

  • 实时数据有3种类型

    • tickPrice``事件(通过IB的``reqMktData

      用于 CASH 产品(至少TWS API 9.70的实验表明不支持其他类型)

      通过查看``BID``价格接收 tick 价格事件,非官方互联网文献似乎表明这是追踪``CASH``市场价格的方法。

      时间戳在系统中本地生成。如果用户希望,可以使用与IB ``reqCurrentTime``计算得出的IB服务器时间的偏移量。

    • tickString``事件(也称为``RTVolume``(通过IB的``reqMktData

      接收IB每250毫秒(或更长时间,如果没有交易发生)提供的 OHLC/Volume 快照。

    • RealTimeBars``事件(通过IB的``reqRealTimeBars

      每5秒接收历史5秒钟的数据条(由IB固定持续时间)。如果所选择的时间范围/组合低于级别秒/5,则此功能将自动禁用。

注意

“实时行情”在 TWS Demo 中不起作用。

默认行为是在大多数情况下使用“tickString”,除非用户明确希望使用“实时行情”。

  • 数据回填

除非用户要求仅执行历史下载,否则数据源将自动进行数据回填:

  • 在开始时:使用最大可能的持续时间。例如:对于“天/1”(时间范围/压缩)组合,IB 的最大默认持续时间是1年,并且将回填这段时间的数据。

  • 在数据断开连接后:在这种情况下,为了回填操作,下载的数据量将通过查看断开连接前收到的最新数据来减少至最小。

请注意,最终考虑的时间范围/压缩组合可能不是在数据源创建时指定的组合,而是在系统插入时指定的组合。看下面的例子:

data = ibstore.getdata(dataname=’EUR.USD-CASH-IDEALPRO’,

timeframe=bt.TimeFrame.Seconds, compression=5) cerebro.resampledata(data, timeframe=bt.TimeFrame.Minutes, compression=2)

如现在清楚的是,最终考虑的 时间框架/压缩 组合是 分钟/2

数据合同检查#

在启动阶段, data 数据源将尝试下载指定合同的详细信息(详见参考文档中如何指定合同的说明)。如果未找到此类合同,或者找到多个匹配项,则数据源将拒绝继续操作,并将其通知给系统。以下是一些示例。

简单但明确的合同说明:

data = ibstore.getdata(dataname=’TWTR’) # Twitter

只能找到一个实例(2016-06),因为对于默认类型”STK”、交易所”SMART”和货币(默认为空),将找到一个以”USD”交易的合同。

类似的方法使用”AAPL”将无法成功:

data = ibstore.getdata(dataname=’AAPL’) # Error -> 多个合同

因为”SMART”在几个真实交易所中均能找到合同,而”AAPL”在其中一些交易所以不同的货币进行交易。下面的写法是正确的:data = ibstore.getdata(dataname=’AAPL-STK-SMART-USD’) # 找到1个合约

数据通知#

数据源将通过以下一种或多种方式报告当前状态 (请查看 Cerebro 和*Strategy*参考文档)

  • ``Cerebro.notify_data``(如果被重写)

  • 使用``Cerebro.adddatacb``添加的回调

  • ``Strategy.notify_data``(如果被重写)

在*策略*中的一个示例:

class IBStrategy(bt.Strategy):

    def notify_data(self, data, status, *args, **kwargs):

        if status == data.LIVE:  # 数据已切换到实时数据
           # 做一些事情
           pass系统发生变化后将发送以下通知:

- ``CONNECTED``

  在初始连接成功时发送

- ``DISCONNECTED``

  在这种情况下无法再检索数据,并且数据将指示系统无法完成任何操作。可能的情况包括:

    - 指定了错误的合同
    - 在历史下载过程中发生中断
    - 超过重新连接到TWS的次数

- ``CONNBROKEN``

  与TWS或数据中心的连接已断开。数据源将尝试(通过存储)重新连接和回填所需的数据,并恢复操作

- ``NOTSUBSCRIBED``

  合同和连接正常,但由于权限不足,无法检索数据。数据将向系统表明它无法检索到数据

- ``DELAYED``

  用于指示 *历史*/* 回溯*操作正在进行中,而策略处理的数据不是实时数据

- ``LIVE``

  用于指示从此点开始策略要处理的数据是实时数据

*策略*的开发者应该考虑在出现断开连接或接收到**延迟**数据时采取哪些操作。

数据时间框架和压缩#

backtrader 生态系统中,数据源支持创建时的 timeframecompression 参数。这些参数也可以通过 data._timeframedata._compression 访问。

当将数据传递给 cerebro 实例的 resampledatareplaydata 时, 时间框架/压缩 组合的意义具有特定的目的,让内部的重新采样/重播对象理解预期的目标是什么。._timeframe._compression 在重新采样/重播时将被覆盖。

另一方面,在实时数据源中,这些信息可能会起到重要作用。请参考以下示例:用户正在请求**tick**数据,这很重要,因为:

  • 不会进行回填(IB支持的最小单位是 Seconds/1

  • 即使请求并支持了``RealTimeBars``,也不会使用它们,因为``RealTimeBar``的最小分辨率是 Seconds/5

无论如何,在使用 Ticks/1 的分辨率之外的情况下,数据必须进行 重新采样/回放 。在上述案例中,请求实时条并处理的方式是这样的:

data = ibstore.getdata(dataname=’TWTR-STK-SMART’, rtbar=True) cerebro.resampledata(data, timeframe=bt.TimeFrame.Seconds, compression=20)

在这种情况下,并根据之前的解释,resampledata``将覆盖数据的._timeframe``和``._compression``属性。将会发生以下事件:

  • 将以 Seconds/20 的分辨率进行回填

  • 实时数据将使用``RealTimeBars``,因为分辨率等于或大于 Seconds/5 ,并且数据支持该分辨率(不是 CASH 产品)

  • 来自TWS的系统事件最多每5秒发生一次。这可能并不重要,因为系统每20秒只向策略发送一个条形图。不包括``RealTimeBars``的代码示例:

    data = ibstore.getdata(dataname=’TWTR-STK-SMART’) cerebro.resampledata(data, timeframe=bt.TimeFrame.Seconds, compression=20)

在这种情况下:

  • 请求的分辨率为 Seconds/20 ,将进行*回填*

  • 由于不是 CASH 产品,将使用``tickString``来获取实时数据

  • 来自TWS的系统事件将至少每250毫秒发送一次。这可能并不重要,因为系统只会每20秒向策略发送一个数据条

最后,使用 CASH 产品,时间间隔最长为20秒的示例:

data = ibstore.getdata(dataname=’EUR.USD-CASH-IDEALPRO’) cerebro.resampledata(data, timeframe=bt.TimeFrame.Seconds, compression=20)

在这种情况下:

  • 请求的分辨率为 Seconds/20 ,将进行*回填* - tickPrice 将用于实时数据,因为这是一项现金产品

    即使添加了 rtbar=True

  • TWS 对系统的事件最多每 250ms 发生一次。这可能并不重要,因为系统每 20 秒只会向策略发送一个柱。

时间管理#

数据源将自动从 TWS 报告的 ContractDetails 对象中确定时区。

注意

这需要安装 pytz。如果未安装,用户应该在数据源的 tz 参数中提供一个与所需输出时区兼容的 tzinfo 兼容实例

注意

如果安装了 pytz 并且用户觉得自动时区确定不起作用,tz 参数可以包含一个时区名称的字符串。backtrader 将尝试用给定的名称实例化一个 pytz.timezone

报告的 datetime 将是与产品相关的时区的时间。以下是一些示例:

  • 产品:欧洲斯托克斯 50 在 Eurex 中(代码:* ESTX50-YYYYMM-DTB*)

    时区将是 CET ( 中欧时间 ),也即 Europe/Berlin- 产品: ES-Mini (代码: * ES-YYYYMM-GLOBEX*)

    时区为``EST5EDT``也被称为``EST``或``US/Eastern``

  • 产品: EUR.JPY 外汇对 (代码: * EUR.JPY-CASH-IDEALPRO*)

    时区为``EST5EDT``也被称为``EST``或``US/Eastern``

    实际上,这是 Interactive Brokers 的设置,因为外汇对几乎可以在24小时内不间断交易,因此它们不会有真正的时区。

这种行为确保交易的一致性,无论交易者实际位置在哪里,因为计算机很可能具有实际位置的时区而不是交易场所的时区。

请阅读手册中的**时间管理**部分。

..注意:: TWS Demo不准确地报告没有数据下载权限的资产的时区(EuroStoxx 50期货就是这种情况的例子)

广播和重新采样/重播#

关于何时交付实时广播数据的设计决策是:- 尽可能实时地传递给它们

这看起来很明显,并且对于``Ticks``的时间框架而言这是正确的,但是如果 重新采样/重放 起作用,可能会出现延迟。用例:

  • 将重新采样配置为 Seconds/5 ,如下所示:

    cerebro.resampledata(data, timeframe=bt.TimeFrame.Seconds, compression=5)

  • 传递了一个时间为``23:05:27.325000``的Tick

  • 市场上的交易很慢,下一个Tick在``23:05:59.025000``传递

这并不明显,但是 backtrader 不知道交易很慢,并且下一个Tick将在大约``32``秒后到来。如果没有相应的处理,以时间``23:05:30.000000``的重新采样Bar会晚``29秒``左右传递。

这就是为什么实时数据源每隔``x``秒( 浮点*值)唤醒 Resampler/Replayer*,让它知道没有新数据。在创建实时数据源时,可以使用参数``qcheck``(默认值为``0.5``秒)来控制这一点。

这意味着每隔``qcheck``秒,重新采样器就有机会传递一个Bar,如果本地时钟显示重新采样周期已经结束。有了这个机制,上述场景的重新采样Bar(23:05:30.000000)将在报告时间之后最多``qcheck``秒传递。

因为默认值是``0.5``,所以最晚的时间会是: 23:05:30.500000。比以前提前了将近29秒。缺点:

  • 某些ticks可能会来得太迟,超过已经传递的重新采样/重放的柱状数据

如果在传递后,TWS从服务器收到一个时间戳为``23:05:29.995000``的延迟消息,这对于已报告的时间为``23:05.30.000000``来说,显然太晚了。

这主要发生在以下情况下:

  • IBStore``中的``timeoffset 被禁用(设置为``False``) ,并且*IB*报告的时间与本地时钟之间的时间差很大。

为了避免大多数延迟样本,最好的方法是:

  • 增加``qcheck``值,以允许延迟的消息被考虑进来:

    data = ibstore.getdata(‘TWTR’, qcheck=2.0, …)

这样即使延迟了 重新采样/重播 的柱状数据的传递,也会增加额外的空间。

注意

当然,对于``Seconds/5``的重新采样和``Minutes/10``的重新采样,2.0秒的延迟具有不同的意义如果出于任何原因,最终用户希望禁用“timeoffset”并且不通过“qcheck”进行管理,

仍然可以获取晚期样本:

  • 将参数 _latethrough 设置为 True,传递给 getdata / IBData 函数:

    data = ibstore.getdata(‘TWTR’, _latethrough=True, …)

  • 重新采样/回放 时将 takelate 设置为 True

    cerebro.resampledata(data, takelate=True)

IBBroker - 实盘交易#

注意

根据要求,在 backtrader 中的 模拟交易 中实现了 tradeid 功能。 这允许正确跟踪在同一资产上并行执行的交易,并将佣金正确分配给相应的 tradeid

该概念在此实盘交易中不受支持,因为佣金是由经纪人按照无法将其分离为不同的 tradeid 值的时间报告的。

仍然可以指定 tradeid,但不再有意义。

使用经纪人 ================使用 IB Broker ,需要替换由 cerebro 创建的标准经纪商模拟实例。

使用 Store 模型(首选):

``` python import backtrader as bt

cerebro = bt.Cerebro() ibstore = bt.stores.IBStore(host=’127.0.0.1’, port=7496, clientId=35) cerebro.broker = ibstore.getbroker() # 或者 cerebro.setbroker(…) ```

使用直接方法:

``` python import backtrader as bt

cerebro = bt.Cerebro() cerebro.broker = bt.brokers.IBBroker(host=’127.0.0.1’, port=7496, clientId=35) ```

经纪商参数#

无论是直接使用还是使用``getbroker``,``IBBroker``经纪商不支持任何参数。这是因为经纪商只是一个真实 Broker 的代理人。而真实经纪商提供的功能将不能被剥夺。

一些限制 =================现金和价值报告 ————————

当内部的 backtrader 经纪人模拟在调用策略的 next 方法之前对 value``(净清算价值)和 ``cash 进行计算时,对于实盘经纪人来说无法保证同样的情况。

  • 如果请求了这些值,next 的执行可能会被延迟,直到收到答案

  • 经纪人可能尚未计算这些值

backtrader 告诉 TWS,在值发生改变时立即提供更新的值( backtrader 订阅 accountUpdate 消息),但它并不知道这些消息何时会到达。

IBBrokergetcashgetvalue 方法报告的值始终是从 IB 收到的最新值。

注意

进一步的限制是,即使账户有多种货币的价值可用,这些值也都以账户的基础货币报告。这是一个设计选择。

持仓#

backtrader 使用 TWS 报告的资产的 ``Position``(价格和大小)。在执行订单和订单状态消息后,可以使用内部计算,但如果丢失了其中一些消息(套接字有时会丢失数据包),这些计算将不能正确执行。

当然,如果连接到 TWS 时,将执行交易的资产已经存在一个开放的头寸,那么由策略进行的 Trades 的计算将无法正常工作,这是由于初始偏移造成的。与之前的标准用法没有变化。只需使用策略中可用的方法(请参阅“Strategy”参考文档获取详细说明):

  • buy

  • sell

  • close

  • cancel

返回的订单对象#

与backtrader的``Order``对象兼容(在同一层级的子类)

订单执行类型#

IB支持多种订单执行类型,其中一些由IB进行模拟,一些由交易所本身支持。最初支持哪些订单执行类型的决定是有动机的:

  • 与“backtrader”中可用的 经纪人模拟 兼容

    这样做的理由是,经过回测的结果将进入实际生产。

因此,订单执行类型仅限于 经纪人模拟 中可用的类型:- ``Order.Market``(市价单) - ``Order.Close``(收盘价单) - ``Order.Limit``(限价单) - ``Order.Stop``(当触发 Stop 时,会紧接着进行 Market 单) - ``Order.StopLimit``(当触发 Stop 时,会紧接着进行 Limit 单)

备注

IB 通过不同的策略来触发止损。 backtrader 不会修改默认设置,也就是 0

0 - 默认值。 “双买/卖” 方法将适用于场外股票和美国期权的订单。其他所有订单将使用 “最新” 方法。

如果用户想要修改此设置,可以在购买和销售中提供额外的 **kwargs 参数,遵循 IB 文档的说明。例如,在策略的 next 方法中:

def next(self):

# 一些逻辑… self.buy(data, m_triggerMethod=2)

这将把策略设置为 ``2``(”最新” 方法,即基于最近价格触发停止订单)。

如需进一步了解止损触发的详细信息,请参考 IB API 文档。

订单有效期#

在回测过程中(使用 valid 来表示买入和卖出的有效期),对应 IB 订单 ,也有相同的有效期概念和含义。因此,对于以下值,valid 参数将被转换如下:

  • ``None -> GTC``(Good Til Cancelled,撤销前有效) 因为没有指定有效期,我们默认订单有效直到被取消。

    • datetime/date 转换为 ``GTD``(Good Til Date)

      传入 datetime.datetime/datetime.date 实例表示订单有效直到某个特定时间点。

    • timedelta(x) 转换为 GTD``(这里的 ``timedelta(x) != timedelta()

      这被解释为订单从当前时间 + timedelta(x) 开始有效。

    • float 转换为 GTD

      如果值来自 backtrader 使用的原始 float datetime 存储,则订单有效直到该 float 指示的日期时间。

    • timedelta() or 0 转换为 DAY

      一个值被指定了,但是为 Null ,被解释为订单在当前 day (session)有效。

Notifications =============标准的“Order”状态将通过“notify_order”方法(如果被覆盖)通知给*策略*。

  • “Submitted” - 订单已发送到TWS

  • “Accepted” - 订单已下达

  • “Rejected” - 订单下达失败或在其生命周期中被系统取消

  • “Partial” - 已部分执行订单

  • “Completed” - 订单已完全执行

  • “Canceled”(或“Cancelled”)

在IB中,这有以下几种含义:

  • 用户手动取消

  • 服务器/交易所取消了订单

  • 订单有效性过期

将应用一种启发式算法,如果已从TWS接收到具有“orderState”指示“PendingCancel”或“Canceled”的“openOrder”消息,则将该订单标记为“Expired”。

  • “Expired” - 请参阅上面的解释

参考#

IBStore#

IBBroker ========.. 当前模块:: backtrader.brokers .. 自动类:: IBBroker

IBData#