Skip to content

Latest commit

 

History

History
639 lines (371 loc) · 31.9 KB

File metadata and controls

639 lines (371 loc) · 31.9 KB

【backtrader 与 IB(盈透证券)实盘交易教程 2】如何使用 backtrader 连接 ib 进行实盘交易?(backtrader 文档中 IB 接口教程的翻译)

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

在上一讲中,已经配置了 backtrader 和 TWS 运行需要的环境,这篇主要讲如何通过 backtrader 实现算法

盈透证券

backtrader 和盈透证券的对接整合支持下面的两种功能:

  • 实时数据(Live Data feeding)
  • 实盘交易(Live Trading)

注意:尽管已经进行了详细的测试以避免在各种场景下的错误,这些代码(和其他任何软件一样)可能包含 bug。(意译)

示例代码

backtrader 在 github 上的源代码中,包含了一个完整的例子:

地址在:https://github.com/mementum/backtrader/blob/master/samples/ibtest/ibtest.py

这个示例代码不可能包含每个使用的场景,但是尽力去提供远见卓识并且应该强调的是,写的策略在回测和实盘的模块中,两者没有本质区别。

需要提醒的一件事是:这个示例代码需要等待一个 data.LIVE 的数据状态的通知,才会发生交易活动。很可能,在任何的实盘策略中,这个都是需要考虑的。(个人理解,在实盘交易的时候需要加载一部分历史数据,然后慢慢加载实盘数据,在历史数据中的信号,应该忽略交易,需要根据 data.LIVE 这个数据状态去忽略)

存储模式(Store Model)和直接模式(Direct Model)

backtrader 可以通过两种模式和 IB(盈透证券)进行对接交互

  1. 存储模式(Store Model,优选)
  2. 通过 data 和 broker 的直接交互模式(Direct Model)

存储模式提供了一个把 datas 和 brokers 进行清晰分离的模式,下面的两个代码块可以很好的说明两者的不同。

# 存储模式
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')
# host,port,clientId 被作为参数传递给 IBStore,用于在 backtrader 和 TWS 之间建立一个链接
# 然后就通过 ibstore 的 getdata 方法创建了一个 data feed,使用的参数和其他创建 data feed 的参数是一样的 
# 直接模式
import backtrader as bt

data = bt.feeds.IBData(dataname='EUR.USD-CASH-IDEALPRO',
                       host='127.0.0.1', port=7496, clientId=35)
# ibstore 的参数直接传递给 data 了
# ibstore 将会通过 IBData 间接创造,而不是在代码里面直接创造
# 有些不清楚,究竟哪些属于 ibstore,那些属于 data
# 个人感觉:如果使用的是一个数据,差别不是很大,如果要是通过 ibstore 获取多个数据用于交易,在两种方法之间是有一定的区别的,这个需要在实盘的时候去验证,究竟哪种速度更快一些。直觉上是,如果是多个数据,创建多个连接到 TWS 的连接会比较好。 

IBStore 的介绍

ibstore 在 IBpy 和 backtrader 的 data feed、broker 之间提供了一层对接,是支持实时数据和实时交易的基石。

一个 store 是包含下面功能的观念:

  • 是一个实体的中心商店,在这里,这个实体是 IB . 可能需要也可能不需要参数

  • 通过下面的方法可以获得一个 broker 的实例

    IBStore.getbroker(*args, **kwargs)

  • 通过下面的方法可以获得数据

    IBStore.getdata(*args, **kwargs)

    获取数据的时候,使用的很多的关键字参数,和回测时候的 data feed 的参数很类似,比如 dataname, fromdate, todate, sessionstart, sessionend, timeframe, compression。此外,还提供了其他的参数,具体可以参考下面的指引。

    IBStore 提供了下面的功能:

    • 连接目标(host and port 参数)

    • 认证(clientId参数)

    • 再连接控制(reconnect and timeout 参数)

    • 时间补偿检查(timeoffset参数,见下文)

    • 通知和检查 bug

      notifyall (default: False): 在这种情况下,任何 IB 提供的信息(包括简单的信息性)的都会被发送到 cerebro 和 strategy 中

      _debug (default: False):在这种情况下,从 TWS 获取到的任何信息都会被输出默认的工作台中

IBData feeds

数据选项(Data Options)

通过直接或者 getdata 获取的 IBData,支持下面的数据选项:

  • 历史数据下载请求

    如果请求的历史数据超过了 IB 的限制,有可能会被分割成几次进行请求。(读过 TWS API 的时候,发现,IB 对每个周期的数据,都有最大的请求量)

  • 三种不同的实时数据

    • tickPrice 事件 (通过 IB reqMktData)

      用于现金类的交易工具(从 TWS API 9.7 版本一来开始实验,对于其他的交易工具不支持).根据非正式的互联网上的说法,通过获取一个 tick price 事件,以便能够观察 bid price 是对现金的市场价格进行追踪的方法。

      时间戳会根据本地的交易系统生成,如果终端用户希望能够根据自己的时区进行调整,可以对 IB 服务器的时间(从 IB reqCurrentTime获得)增加一个补偿(offset)

    • tickString 事件 (同 RTVolume (通过 IB reqMktData)

      从 IB approx 接受 OHLC/Volume 的快照数据,大约每 250ms 返回一次(如果没有交易发生,可能需要更长的时间)

    • RealTimeBars 事件 (通过 IB reqRealTimeBars)

      每 5 秒钟获取一个历史的 5 秒的 bar(IB 自己切割生成的)。如果选择的交易周期小于 5 秒的话,这个功能会自动不能使用。

      重要提醒:RealTimeBars 不可以用于 TWS Demo

    默认情况下是使用tickString,除非用户专门设定想要使用RealTimeBars

  • Backfilling

    除非用户自己请求一个历史数据的下载,否则,data feed 会自动进行填充历史数据

    • 在开始的时候:尽可能获取最大的数据量。举例:对于以日作为交易周期的策略,IB 默认的最大的历史数据的时间长度是 1 年,所以填充的历史数据将会是 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 分钟。

    注:在前面我的教程中,一直不提倡使用 resampledata、replay 这些函数。因为这确实会影响到效率,有可能会造成交易成本更高。

    但是考虑到 IB 的 bar 是每五秒一个的,我们大多数个人交易者,估计都不会使用 5 秒钟作为主要的交易周期,所以,如果您的策略不是很多个,比如几百个,几千个那种,如果交易频率不是很高,比如好几天交易一次那种,可以考虑使用这个函数,毕竟可以减少一部分工作量。对于机构交易者或者策略比较多、交易比较频繁的个人交易者,需要考虑单独写一个数据合成模块,用于接收数据、合成数据、发送数据。

数据合约检查

在开始阶段,data feed 会尝试下载指定的合约的详细信息(查看附属资料了解如何指定它)。如果没有发现这个合约或者有多个可以匹配的合约,data feed 将会拒绝进入下一步,并且会给出通知信息。看一些具体的例子:

简单但是模糊的合约说明:

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

在 2016 年 6 月份的时候,因为对于默认的类型:股票、SMART交易所、默认货币(None),以美元交易的单个合约只有一个。

但是,同样的合约说明方法使用在 AAPL 上,将会出错:

data = ibstore.getdata(dataname='AAPL') # Error -> multiple contracts

这是因为 SMART发现在几个交易所有这个合约交易,并且以几种不同的货币进行交易,如果使用下面的合约说明方法就没有问题了:

data = ibstore.getdata(dataname='AAPL-STK-SMART-USD') # 1 contract found

数据通知

data feed 可以通过下面的一个或者几个途径反馈当前的状态:

  • Cerebro.notify_data(如果被重写了)
  • Cerebro.adddatacb 添加的 callback
  • Strategy.notify_data(如果被重写了)

下面是在策略里面用 notify_data 反馈状态的例子:

class IBStrategy(bt.Strategy):

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

        if status == data.LIVE:  # the data has switched to live data
           # do something
           pass 

下面的通知的改变将会被发送到系统中

  • CONNECTED

    当初始化连接成功的时候会发送

  • DISCONNECTED

    在这种情况下,数据将不会再更新了,并且导致系统什么都不能做了。可能的情况包括:

    • 错误的合约设定
    • 在历史数据下载的时候被中断
    • 超过 TWS 的最大连接数
  • CONNBROKEN

    与 TWS 或者 data farm 建立的连接中断了,当需要的时候,data feed 会尝试重新连接和补充数据,并且恢复自动化交易。

  • NOTSUBSCRIBED

    合约和连接都是正常的,但是由于缺乏许可并不能获取到数据。data feed 将会发送信息,表明它不能获取数据

  • DELAYED

    仅仅用于说明历史数据或者填充操作正在进行,策略当前处理的数据不是实时数据。

  • LIVE

    仅仅用于说明从现在开始,策略处理的数据都是实时数据

策略的开发人员需要考虑当连接断开或者获取的是延时的数据的时候采取什么样的行动。

数据的 TimeFrame 和 Compressions

在 backtrader 的生态系统中,data feed 在创建的时候支持 timeframecompression 参数,这些参数也可以通过 data 的属性值data._timeframe 和 data._compression 获取。

当通过resampledata 或者 replaydata添加一个 data 到 cerebro 的时候,timeframe 和 compression 的参数组合具有重要作用,可以让内部的 resampler 和 replayer 知道要合成的 bar 的交易周期是多少。._timeframe._compression 在 resampled 或者 replayed 的时候将会被重写。但是在实盘数据中,另一方面,这个信息有着重要的作用,举例说明:

data = ibstore.getdata(dataname='EUR.USD-CASH-IDEALPRO',
                       timeframe=bt.TimeFrame.Ticks,
                       compression=1,  # 1 is the default
                       rtbar=True,  # use RealTimeBars
                      )
cerebro.adddata(data) 

backtrader 的使用者在请求 tick 数据,这个很重要是因为:

  • 不会发生历史数据的填充(IB 支持的最小的单位是 1 秒)
  • 即使这个数据支持RealTimeBars,并且RealTimeBars被请求,他们也不会被使用,因为RealTimeBars的最小的间隔是 5 秒

在使用中,除非使用的在 1 秒钟上交易的策略,数据需要被 resampled/replayed。上面的例子如果使用 RealTimeBars 的话,可以用下面的代码:

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

和上面解释的一样,在这个例子中,数据的 ._timeframe._compression将会被 resampledata 重写。将会发生下面的逻辑:

  • 当请求 20 秒的 bar 的数据的时候,填充(backfilling)将会发生
  • 因为时间间隔大于等于 5 秒钟,如果这个交易品种(data)支持,对于实盘数据,RealTimeBars 将会被使用
  • 从 TWS 传过来的数据是每 5 秒钟一个,这个是不重要的,因为对于策略来说,每 20 秒传过来一个最近 20 秒的 bar.

上面的代码和下面不使用 RealTimeBars 的代码产生的效果是一样的:

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

在这个例子中,将会发生下面的逻辑:

  • 当请求 20 秒的时间间隔的数据的时候,将会发生填充(backfilling )
  • 因为没有现金类的交易品种,tickString将会被使用
  • 从 TWS 传过来的数据是大约每 250 毫秒一次,这个是不重要的,因为对于策略来说,每 20 秒传过来一个最近 20 秒的 bar.

最后,对于一个外汇类的交易产品来说,使用下面的例子会发生如下的逻辑:

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

在这个例子中:

  • 当请求 20 秒的时间间隔的数据的时候,将会发生填充(backfilling )

  • 因为这个是外汇类的交易品种,即使 rtbar=True 参数被设定,tickprice 将会被使用

  • 从 TWS 传过来的数据是大约每 250 毫秒一次,这个是不重要的,因为对于策略来说,每 20 秒传过来一个最近 20 秒的 bar.

个人总结一下:因为 TWS 每秒钟存在请求限制,所以,如果每 250 毫秒从 TWS 获取 1 个数据的话,1 秒钟大概就 4 个数据了,这样同时获取的交易品种的数据其实挺少的。对于非外汇类的交易品种,每 5 秒获取一次数据可能会好一些;对于外汇类的交易品种,可能需要考虑优化下内部实现的逻辑。

时间管理

data feed 将会从 TWS 获取的 ContractDetails 中自动决定时区是什么。

提醒
  • 需要安装 pytz。

  • 如果没有安装,使用者需要提供一个tzinfo符合期望输出的时区作为 tz 参数的值传给 data。

  • 如果安装了 pytz,但是使用者觉得自动决定的时区不太好用,可以提供一个时区的字符串作为参数 tz 的值,backtrader 会根据给定的时区名字实例化一个pytz.timezone

报告的时间是和交易品种的时区相关的,一些时间的例子:

  • 交易品种: EuroStoxxx 50 in the Eurex (ticker: ESTX50-YYYYMM-DTB)

    时区将会是CET (中部欧洲时间) 亦名 Europe/Berlin

  • 交易品种: ES-Mini (ticker: ES-YYYYMM-GLOBEX)

    时区将会是 EST5EDT 亦名 EST 亦名 US/Eastern

  • 交易品种: EUR.JPY forex pair (ticker EUR.JPY-CASH-IDEALPRO)

    时区将会是 EST5EDT 亦名 EST 亦名 US/Eastern

    实际上这是 IB 设置的,因为外汇交易几乎是 24 小时不间断的,所以他们没有一个真实的时区。

因为电脑保持的是实际地点的时区而不是交易所所在的时区,时区的设置确保了不管交易者实际在什么地方,交易可以保持一致。

请阅读时间管理部分的使用说明。

提醒

TWS 例子在报告时区的时候是不精确的,因为不允许下载数据。(EuroStoxx 50 期货就是一个例子)

实盘数据 和 Resampling/Replaying

关于实盘数据何时形成 bar 并传送给策略,一个设计上的决定是:

  • 尽可能和实时数据一样形成

有些很明显的情况,当时间周期是 ticks 的时候,但是使用 Resampling/Replaying,延迟很可能会发生。举例说明,下面合成一个 5 秒的 bar

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

  • 前面一个 tick 的时间是在23:05:27.325000形成
  • 交易不活跃,下一个 tick 形成是在23:05:59.025000

这可能不太明显,但是 backtrader 并不知道交易不活跃,下一个 tick 形成需要 32 秒之后,如果没有提供其他的机制的话,在23:05:30.000000应该形成的 bar 将会在 29 秒之后形成,这个也太晚了。

这就是为什么实时数据每 x(浮点数)唤醒一次,给到 Resampler/Replayer,让他们知道没有新的数据进来。在创建实时数据的时候,这个是通过参数 qcheck(默认值是 0.5 秒)控制的。

这意味着,resampler 有机会在每qcheck秒产生一个 bar,如果本地时钟认为抽样周期结束,这个 bar 该产生了。拥有这个机制,上面的抽样形成 bar 的例子,将会在(23:05:30.000000) 之后的最多 0.5 秒就会产生一个 bar,因为默认的检查时间是 0.5 秒,所以,bar 形成的不会晚于23:05:30.500000,这将会比上面早 29 秒。

缺点
  • 一些 ticks 可能来的太晚了,导致形成的 bar 没有包含这些 tick。比如,如果 TWS 服务器上产生了一个 tick 的时间戳是在23:05:29.995000,对于23:05.30.000000要形成的 bar,可能太晚,因为从 TWS 服务器,传输到本地需要时间,本地获取到的时候,可能已经超过了23:05.30.000000。这个通常发生在:

  • timeoffset被禁止(False),导致在 IBStore 中,IB 报告的时间和本地时钟的时间的差距可能是很明显的。

  • 一个比较好的消除上述问题的方法是通过提高 qcheck 的时间,以便允许考虑到更晚一些的时间

    data = ibstore.getdata('TWTR', qcheck=2.0, ...)

    尽管提高 qcheck 的间隔,延迟了 bar 产生的时间,这应该能增加额外的空间。

提醒

显然,在 5 秒钟的 bar 中,延迟 2 秒钟和 10 分钟的 bar 中延迟 2 秒钟差别是非常大的。

如果因为某种原因,终端使用者不想要使用timeoffset,也不想要通过qcheck管理时间,使用后面的例子仍然可以产生一些效果:

  • 把 _latethrough 设置成 True 传递给getdata / IBData

    data = ibstore.getdata('TWTR', _latethrough=True, ...)

  • 当 resampling/replaying 把takelate设置成 True

    cerebro.resampledata(data, takelate=True)

IBBroker - Trading Live

提醒

在 backtrader 的模拟的 broker 中,请求 tradeid的功能是被实现的,这个可以通过对统一资产的同时发生的不同交易进行追踪,以便正确的分配交易费用给合适的交易。

上面的概念在实盘交易的 broker 中是不被支持的,因为交易费用是通过 broker 报告的,想要把他们在不同的 trade_id 之间分开是不可能的。

trade_id 仍然可以被指定,但是没有什么作用了。

使用 broker

为了使用 IB broker,在 backtrader 创造的回测用的 broker 必须被替代.

  • 使用 Store 模式(被推荐)
import backtrader as bt

cerebro = bt.Cerebro()
ibstore = bt.stores.IBStore(host='127.0.0.1', port=7496, clientId=35)
cerebro.broker = ibstore.getbroker()  # or cerebro.setbroker(...) 
  • 使用直接模式
import backtrader as bt

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

broker 参数

不论是通过直接模式还是通过 store 模式,broker 都不支持参数,这是因为 broker 仅仅是一个实际交易所的代理。实际交易所给什么,broker 就获取什么。

一些限制

现金和账户价值报告

在回测的时候,模拟的 broker 在下个 next 之前计算 value 和 cash,但是在实盘交易中并不一定是这样。

  • 如果 cash 和 value 被请求,那么执行 next 的时候将会被延迟,因为需要等到这个请求操作返回数据
  • 实际的交易所可能还没有计算这些新的数据

backtrader 通过订阅accounUpdate 信息,告诉 TWS 尽快提供更新的 cash 和 value,但是并不知道新的信息会在什么时候到达。

通过 IBBroker 的函数 getcash 和 getvalue 获取的 cash 和 value 值,是从 IB 获取到的最新的值。

提醒

另外一个限制就是,cash 和 value 的值是以账户的本币显示的,不管是否交易更多的货币。这是一个设计上的决定。

持仓

backtrader 使用 Position 保存 TWS 报道的一个交易品种的平均价格和尺寸多少。通过订单的执行或者订单状态的信息,可以使用内部计算,但是如果这些信息丢失了一部分(网络有可能发生丢包),这种计算将不会使用。

当然,当刚连接到 TWS 的时候,交易品种已经有一些持仓了,如果交易发生,因为初始化的补偿,策略计算的Trades很可能不会和原先一样

交易

模拟和实盘下单的时候没有区别,直接使用 buy,sell,close,cancel 这几个函数,更多的使用说明,可以参考策略部分

返回的订单对象

和 backtrader 的订单对象是兼容的。 (subclass in the same hierarchy)

订单执行类型

IB 支持各种各样的订单,一些是 IB 内部模拟支持的,一些事交易所本身支持的。决定 IB 的哪些订单类型被支持,有一个动机:

  • 和 backtrader 回测可以使用的订单类型兼容。背后的逻辑是只有经过历史检验,策略进行回测过后,才能进入生产环境。

所以,只支持下面的一些订单类型:

  • Order.Market
  • Order.Close
  • Order.Limit
  • Order.Stop (当止损被触发之后,将会下一个市价单)
  • Order.StopLimit (当止损被触发之后,将会下一个限价单)
注意

止损的触发是 IB 实现,backtrader 没有修改默认的设置:

0 - the default value. The "double bid/ask" method will be used for
orders for OTC stocks and US options. All other orders will use the
"last" method. 

如果用户想要修改止损单实现方法,可以根据 IB API 的文档,通过 buy 或者 sell 的关键字参数修改。举例说明,在策略的 next 中

def next(self):
    # some logic before
    self.buy(data, m_triggerMethod=2) 

这改变了止损单的类型到 2,在这种情况下,止损单将根据最新价进行触发。需要根据 IB API 文档来了解止损的触发机制。

订单有效期

订单的有效期在回测与实盘的时候是一样的。订单有效期的参数会按照下面的方法进行转化成 IB 的订单的有效期参数。

  • None -> GTC (取消前有效)

    没有设置有效期,所以订单会在取消前有效

  • datetime/date 转化成 GTD (指定日期前有效)

    传递一个时间日期格式的参数,代表在这个时间之前,订单是有效的

  • timedelta(x) 转化成 GTD (here timedelta(x) != timedelta())

    这个将会被转化为now + timedelta(x)前订单是有效的

  • float 转化成 GTD

    If the value has been taken from the raw float datetime storage used by backtrader the order must valid until the datetime indicated by that float

    如果提供的是一个 float 代表时间(the raw float datetime storage),那么,在达到这个时间之前,订单有效

  • timedelta() or 0 转化成 DAY

    使用timedelta() or 0 ,代表这个订单将会是当天有效。

通知

标准的Order状态将会通过notify_order(如果被重写)传递给 strategy。

  • Submitted - 订单被传递给 TWS

  • Accepted - 订单被下进去

  • Rejected - 订单下进去的时候被拒绝或者因为到期被系统取消

  • Partial - 订单部分成交

  • Completed - 订单完全成交

  • Canceled (or Cancelled)

    在 IB 下,有几个可能的意思:

    • 用户自己取消

    • 服务器或者交易所取消

    • 订单有效期到了

  • Expired - 如果从 TWS 收到一个openOrder的信息,其orderState表明PendingCancel or Canceled,这个 order 的状态将会被标记成Expired

参考

IBStore

class backtrader.stores.IBStore()

包装 ibpy ibConnection 实例的 Singleton 类,IBData and IBBroker的参数也可以通过 store 进行指定。

参数
  • host (default:127.0.0.1): 需要 IB TWS 或者 IB Gateway 正在运行着,尽管实际上通常是 localhost,但是这里不能这样设置.
  • port (default: 7496): 连接的端口号,模拟的端口号需要设置为 7497
  • clientId (default: None): 用那个 clientid 连接 TWS,如果是默认的 None 的话,将会在 1-65535 之间产生一个随机的数用作 clientid
  • notifyall (default: False):如果是 False 的话,只有错误信息会通过CerebroStrategynotify_store进行传递,如果设置成 True 的话,任何从 TWS 发送的信息都会被通知。
  • _debug (default: False):打印从 TWS 获取的所有信息到标准工作台
  • reconnect (default: 3):在第一次连接失败之后,尝试重新连接的次数,设置为-1 的时候,连接失败之后会一直尝试重新连接
  • timeout (default: 3.0):在每次重新连接的时候间隔的时间
  • timeoffset (default: True):如果设置成 True,从reqCurrentTime获取到的 IB 服务器时间将被用于计算与本地之间的差额,这个差额将被用于价格通知的时候用以修正本地的时间,这个时间补偿将会传递到 backtrader 生态系统的其他部分,比如抽样形成 bar 的时候。
  • timerefresh (default: 60.0):秒数,多少秒更新 timeoffset 一次
  • indcash (default: True):Manage IND codes as if they were cash for price retrieval。(这句话暂时不理解,英文留这里)

IBBroker

class backtrader.brokers.IBBroker(**kwargs)

IB 交易所的实盘用的 broker.这个类在 backtrader 的 API 和 IB 的 API 之间提供一个对接

重要提醒
  • tradeid:实际上是不支持的,因为 pnl 是直接从 IB 拿到的,如果按照和原先一样的 FIFO 的方法,根据 tradeid 计算的每个 trade 的 pnl 是不正确的

  • Position:如果在策略中,关于某个交易品种有持仓,但是通过其他的策略或者人工进行了下单或者操作,影响了持仓,将会导致在这个策略中计算的 trades 不能反应实际情况。为了避免这种情况,broker 需要有一个它自己的仓位管理,允许有多个 tradeid(以便能够实现 pnl 也能够在本地进行计算),但是可能违背了实盘交易的目的。

IBData

class backtrader.feeds.IBData(**kwargs)

IB brokers 的 data feed.在参数设置的时候,支持下面的交易品种的设定方法:

  • TICKER # Stock type and SMART exchange
  • TICKER-STK # Stock and SMART exchange
  • TICKER-STK-EXCHANGE # Stock
  • TICKER-STK-EXCHANGE-CURRENCY # Stock
  • TICKER-CFD # CFD and SMART exchange
  • TICKER-CFD-EXCHANGE # CFD
  • TICKER-CDF-EXCHANGE-CURRENCY # Stock
  • TICKER-IND-EXCHANGE # Index
  • TICKER-IND-EXCHANGE-CURRENCY # Index
  • TICKER-YYYYMM-EXCHANGE # Future
  • TICKER-YYYYMM-EXCHANGE-CURRENCY # Future
  • TICKER-YYYYMM-EXCHANGE-CURRENCY-MULT # Future
  • TICKER-FUT-EXCHANGE-CURRENCY-YYYYMM-MULT # Future
  • TICKER-YYYYMM-EXCHANGE-CURRENCY-STRIKE-RIGHT # FOP
  • TICKER-YYYYMM-EXCHANGE-CURRENCY-STRIKE-RIGHT-MULT # FOP
  • TICKER-FOP-EXCHANGE-CURRENCY-YYYYMM-STRIKE-RIGHT # FOP
  • TICKER-FOP-EXCHANGE-CURRENCY-YYYYMM-STRIKE-RIGHT-MULT # FOP
  • CUR1.CUR2-CASH-IDEALPRO # Forex
  • TICKER-YYYYMMDD-EXCHANGE-CURRENCY-STRIKE-RIGHT # OPT
  • TICKER-YYYYMMDD-EXCHANGE-CURRENCY-STRIKE-RIGHT-MULT # OPT
  • TICKER-OPT-EXCHANGE-CURRENCY-YYYYMMDD-STRIKE-RIGHT # OPT
  • TICKER-OPT-EXCHANGE-CURRENCY-YYYYMMDD-STRIKE-RIGHT-MULT # OPT
参数
  • sectype (default: STK)

    如果在交易品种设定的时候,如果没有指定,那么默认的类型是股票

  • exchange (default: SMART)

    如果在交易品种设定的时候,如果没有指定,将默认使用SMART

  • currency (default: '')

    如果在交易品种设定的时候,如果没有指定,将默认使用的货币

  • historical (default: False)

    如果被设置成 True 的话,data feed 将会在第一次下载数据之后停止。标准的 data feed 参数fromdate and todate 将会被参考使用,如果请求的时间间隔大于 IB 能够允许的一次请求的间隔,将会分成几次下载。

  • what (default: None)

    如果参数是默认的 None,在请求历史数据的时候,不同的资产数据类型将使用默认的

    • 外汇类的将使用‘BID’

    • 其他类型的使用‘TRADES’

    如果想要使用其他类型的参数,可以参考 IB API

  • rtbar (default: False)

    如果设置成 True 的话,将会提供 IB 的 5 秒钟的实时 K 线数据,用作最小的 tick 数据。根据文档,他们和实时的价格是对应的 (once collated and curated by IB)

    如果设置成 False 的时候,RTVolume price 将会被使用,这将会提供 IB 的 tick 数据。如果是外汇类的资产(比如 EUR.JPY)RTVolume price 将会被始终使用。(from it the bid price (industry de-facto standard with IB according to the literature scattered over the Internet))

    即使被设置成 True,如果合成的 bar 的交易周期在 5 秒钟一下,也不会生成实时 K 线,因为 IB 不提供 5 秒钟一下交易周期的 K 线。

  • qcheck (default: 0.5)

    如果没有数据被接受到,多少秒钟将会被唤醒,以便能够有机会合成 K 线和传递通知。(Time in seconds to wake up if no data is received to give a chance to resample/replay packets properly and pass notifications up the chain)

  • backfill_start (default: True)

    在开始的时候执行填充历史数据,将会在一次请求中尽可能获取更多的历史数据。(Perform backfilling at the start. The maximum possible historical data will be fetched in a single request.)

  • backfill (default: True)

    在断开连接重新连接之后执行数据填充,将会填充需要的最小的数据量。(Perform backfilling after a disconnection/reconnection cycle. The gap duration will be used to download the smallest possible amount of data)

  • backfill_from (default: None)

    在初次填充的时候可以使用额外的数据源。当历史数据结束之后,如果还没有满足需要的数据量,将会从 IB 继续请求历史数据。这种情况下,首先使用硬盘上的已经存储的历史数据源进行填充,但是不限于。(An additional data source can be passed to do an initial layer of backfilling. Once the data source is depleted and if requested, backfilling from IB will take place. This is ideally meant to backfill from already stored sources like a file on disk, but not limited to.)

  • latethrough (default: False)

    如果源数据被 resample/replay,对于已经形成的 bar 来说,一些 ticks 可能来的太晚了。如果设置成 True,这些来的太晚的 ticks 也被允许参与合成 bar.检查 Resampler 的文档以便了解哪些情况下考虑了这些来的太晚的 tick.在timeoffset设置成 False 之后,IBStore 的时间和 TWS 服务器的时间是不同步的,这种 tick 来的太晚的情况可能发生的更多。(If the data source is resampled/replayed, some ticks may come in too late for the already delivered resampled/replayed bar. If this is True those ticks will bet let through in any case.Check the Resampler documentation to see who to take those ticks into account.This can happen especially if timeoffset is set to False in the IBStore instance and the TWS server time is not in sync with that of the local computer)

  • tradename (default: None)

    对于某些一种品种提供价格,交易在另一个品种的CFD产品非常有用

    • SPY-STK-SMART-USD -> dataname将会被指定为 SPY-STK-SMART-USD
    • SPY-CFD-SMART-USD -> 这个是对应的 CFD 产品,它不提供价格数据,但是在这种情况下,tradename将会被设置成 SPY-CFD-SMART-USD

默认参数允许使用简单的TICKER,在这种情况下,默认的交易品种类型和交易所将会被应用。对于有些交易品种而言,这样的设定有可能导致存在多种的交易品种,所以需要详细指定具体的资产类型、交易所、货币类型

  • AAPL-STK-SMART-USD 是设定数据名称的全称。使用这种方式,IBData(dataname='AAPL', currency='USD'),指定了具体的TICKER和货币,但是没有指定交易品种类型和交易所,将会使用默认的STK and SMART

    注:个人理解,在使用的时候尽可能使用全称。