我如何使用 Python API 获取我在盈透证券的账户头寸?

How do I get my accounts' positions at Interactive Brokers using Python API?

已编辑:我找到了有关错误消息的解决方案 - 这是 IB API 上的错误。我在下面作为答案显示的代码应该对那些正在寻找一个干净的解决方案来从他们在 IB 的账户中读取头寸和资产净值的人有用。

原始问题[参见下面的解决方案;在此处留下原始问题以供参考]:我正在尝试使用公司 API 获取我在 Interactive Brokers [IB] 的所有账户头寸。他们的文档虽然内容丰富,但非常混乱。示例代码中充满了不必要的命令 - 我想要一些非常精简的东西。

我需要帮助:

  1. 如何获取信息 "per account" [已解决]
  2. 如何将变量引入 DataFrame [已解决]
  3. 如何避免 IB 的 API 打印一系列错误消息[已解决]

到目前为止的代码:

from ibapi.client import EClient 
from ibapi.wrapper import EWrapper
from ibapi.common import * #for TickerId type
import pandas as pd

class ib_class(EWrapper, EClient): 
    def __init__(self): 
        EClient.__init__(self, self)
        self.all_positions = pd.DataFrame([], columns = ['Account','Symbol', 'Quantity', 'Average Cost'])

    def position(self, account, contract, pos, avgCost):
        index = str(account)+str(contract.symbol)
        self.all_positions.loc[index]=account,contract.symbol,pos,avgCost

    def error(self, reqId:TickerId, errorCode:int, errorString:str):
        if reqId > -1:
            print("Error. Id: " , reqId, " Code: " , errorCode , " Msg: " , errorString)

    def positionEnd(self):
        self.disconnect()

ib_api = ib_class() 
ib_api.connect("127.0.0.1", 7496, 0) 
ib_api.reqPositions()
current_positions = ib_api.all_positions
ib_api.run()

当我 运行 上面的代码时,我得到一系列 (i) 帐号,(ii) 合约代码,(iii) 头寸和 (iv) 平均成本。因此,这回答了问题 #1。您可能希望 "print" 这些值以查看 IB 的 API 如何向您发送信息。

我还能够定义一个 DataFrame 变量 all_positions 来接收我正在寻找的信息。请参阅下面的结果。请注意,我必须创建一个 "index" 变量,它是 DataFrame 每一行的唯一标识符(作为帐号和符号的组合)。如果没有这个 'index',我找不到将 'append' 信息发送到 DataFrame 的方法(欢迎任何关于如何做到这一点的想法):

至于上一期(报错信息):

Brian 对 "error" 函数(见下文)的建议消除了“-1”错误。但我仍然得到以下信息:

unhandled exception in EReader thread Traceback (most recent call last): File "C:\Users\danil\Anaconda3\lib\site-packages\ibapi-9.76.1-py3.7.egg\ibapi\reader.py", line 34, in run data = self.conn.recvMsg() File "C:\Users\danil\Anaconda3\lib\site-packages\ibapi-9.76.1-py3.7.egg\ibapi\connection.py", line 99, in recvMsg buf = self._recvAllMsg() File "C:\Users\danil\Anaconda3\lib\site-packages\ibapi-9.76.1-py3.7.egg\ibapi\connection.py", line 119, in _recvAllMsg buf = self.socket.recv(4096) OSError: [WinError 10038] An operation was attempted on something that is not a socket

你的代码没问题。如果您想忽略错误,只需覆盖错误回调即可。

from ibapi.common import * #for TickerId type

def error(self, reqId:TickerId, errorCode:int, errorString:str):
    if reqId > -1:
        print("Error. Id: " , reqId, " Code: " , errorCode , " Msg: " , errorString)

> -1 的错误是针对需要 ID 的操作。

如果您正在获取数据或下订单,我仍会跟踪连接状态。

你对ddf进行了两次初始化。 您还调用 super,它只记录我认为的数据。

断开连接时的套接字错误是一个错误,或者只是他们在 IB 做的方式。它应该已经修复,但它可能需要一段时间才能发布。也许尝试更新,我在 4 月 26 日看到了修复它的拉取请求。

经过大量实验,这是我编写的最终代码,我将其放在名为 AB_API 的“.py”文件中(这样我就可以“将其作为库导入”):

def read_positions(): #read all accounts positions and return DataFrame with information

    from ibapi.client import EClient
    from ibapi.wrapper import EWrapper
    from ibapi.common import TickerId
    from threading import Thread

    import pandas as pd
    import time

    class ib_class(EWrapper, EClient):

        def __init__(self):
            EClient.__init__(self, self)

            self.all_positions = pd.DataFrame([], columns = ['Account','Symbol', 'Quantity', 'Average Cost', 'Sec Type'])

        def error(self, reqId:TickerId, errorCode:int, errorString:str):
            if reqId > -1:
                print("Error. Id: " , reqId, " Code: " , errorCode , " Msg: " , errorString)

        def position(self, account, contract, pos, avgCost):
            index = str(account)+str(contract.symbol)
            self.all_positions.loc[index]= account, contract.symbol, pos, avgCost, contract.secType

    def run_loop():
        app.run()
    
    app = ib_class()
    app.connect('127.0.0.1', 7496, 0)
    #Start the socket in a thread
    api_thread = Thread(target=run_loop, daemon=True)
    api_thread.start()
    time.sleep(1) #Sleep interval to allow time for connection to server

    app.reqPositions() # associated callback: position
    print("Waiting for IB's API response for accounts positions requests...\n")
    time.sleep(3)
    current_positions = app.all_positions
    current_positions.set_index('Account',inplace=True,drop=True) #set all_positions DataFrame index to "Account"
    
    app.disconnect()

    return(current_positions)


def read_navs(): #read all accounts NAVs

    from ibapi.client import EClient
    from ibapi.wrapper import EWrapper
    from ibapi.common import TickerId
    from threading import Thread

    import pandas as pd
    import time

    class ib_class(EWrapper, EClient):

        def __init__(self):
            EClient.__init__(self, self)

            self.all_accounts = pd.DataFrame([], columns = ['reqId','Account', 'Tag', 'Value' , 'Currency'])

        def error(self, reqId:TickerId, errorCode:int, errorString:str):
            if reqId > -1:
                print("Error. Id: " , reqId, " Code: " , errorCode , " Msg: " , errorString)

        def accountSummary(self, reqId, account, tag, value, currency):
            index = str(account)
            self.all_accounts.loc[index]=reqId, account, tag, value, currency

    def run_loop():
        app.run()
    
    app = ib_class()
    app.connect('127.0.0.1', 7496, 0)
    #Start the socket in a thread
    api_thread = Thread(target=run_loop, daemon=True)
    api_thread.start()
    time.sleep(1) #Sleep interval to allow time for connection to server

    app.reqAccountSummary(0,"All","NetLiquidation")  # associated callback: accountSummary / Can use "All" up to 50 accounts; after that might need to use specific group name(s) created on TWS workstation
    print("Waiting for IB's API response for NAVs requests...\n")
    time.sleep(3)
    current_nav = app.all_accounts
    
    app.disconnect()

    return(current_nav)

我现在可以使用两个单行函数读取所有账户头寸和资产净值:

import IB_API

print("Testing IB's API as an imported library:")

all_positions = IB_API.read_positions()
all_navs = IB_API.read_navs()

如果您想 运行 进行非常快速的测试(没有 importing/calling 函数),就在 read_positions() 和read_navs(),执行以下操作:

print("Testing IB's API as an imported library:")

all_positions = read_positions()
print(all_positions)
print()
all_navs = read_navs()
print(all_navs)

我意识到有些用户对导入函数的概念感到困惑,因此提出了上面的快速建议。

我希望 Interactive Brokers 为客户提供一些简单的示例,使客户更容易测试一些想法并决定他们是否要使用他们的 API。

我得到的错误是由 IB 的 API 上的一个错误产生的(你可以看到我问过的关于具体错误的另一个问题)。我添加这个答案是为了提供关于如何降级到旧版本的更清晰的步骤:

1) 从这里下载旧版本:http://interactivebrokers.github.io/downloads/TWS%20API%20Install%20974.01.msi

IB 不显示旧版本的 link,但他们将文件保留在 Github(注意 link 地址末尾的版本名称)

2) 卸载当前版本。在 IB 自己的网站上,执行以下操作:

  • 照常从 Windows 控制面板中的 Add/Remove 工具中卸载 API
  • 如果仍有任何文件,请删除 C:\TWS API\ 文件夹以防止版本不匹配。
  • 找到文件 C:\Windows\SysWOW64\TwsSocketClient.dll。删除这个文件。 [没有找到这个文件]
  • 在安装不同的 API 版本之前重新启动计算机。

3) 使用 .msi 文件安装 API

4) 运行 Anaconda 提示并导航到 dir C:\TWS API\source\pythonclient

5) 运行: python setup.py install

6) python setup.py 安装后,重新启动 Spyder(如果它已打开;我在上面的第 5 步之前关闭了我的)

我安装了这个旧版本后,错误消失了!现在我的控制台上的输出又干净了!