【问题标题】:Pandas DataFrame and Series - IB TWS HistoricalDataPandas DataFrame 和系列 - IB TWS 历史数据
【发布时间】:2020-10-28 20:59:25
【问题描述】:

我正在尝试将 pandas 模块应用到我的代码中,以便重新组织从 IB TWS 服务器收到的消息。

代码是

from ibapi.client import EClient
from ibapi.wrapper import EWrapper
from ibapi.contract import Contract


class MyWrapper(EWrapper):

    def nextValidId(self, orderId:int):
        print("Setting nextValidOrderId: %d", orderId)
        self.nextValidOrderId = orderId
        self.start()

    def historicalData(self, reqId, bar):
        print("HistoricalData. ", reqId, "Date:", bar.date, "Open:", bar.open, "High:", bar.high, "Low:", bar.low, "Close:", bar.close, "Volume:", bar.volume, "Average:", bar.average, "Count:", bar.barCount)

    def historicalDataUpdate(self, reqId, bar):
        print("HistoricalDataUpdate. ", reqId, "Date:", bar.date, "Open:", bar.open, "High:", bar.high, "Low:", bar.low, "Close:", bar.close, "Volume:", bar.volume, "Average:", bar.average, "Count:", bar.barCount)

    def error(self, reqId, errorCode, errorString):
        print("Error. Id: " , reqId, " Code: " , errorCode , " Msg: " , errorString)

    def start(self):
        queryTime = ""

        contract = Contract()
        contract.secType = "STK"
        contract.symbol = "NIO"
        contract.currency = "USD"
        contract.exchange = "SMART"

        app.reqHistoricalData(1, contract, queryTime, "1 D", "5 secs", "TRADES", 0, 1, True, [])

app = EClient(MyWrapper())
app.connect("127.0.0.1", 7496, clientId=123)
app.run()

此代码检索给定股票的历史数据,然后返回最新更新。

我面临的问题是返回的消息是这样组织的

HistoricalDataUpdate.  1 Date: 20200708  08:31:00 Open: 14.17 High: 14.17 Low: 14.17 Close: 14.17 Volume: -1 Average: 14.15 Count: -1

当我尝试以重新组织的方式检索数据时,例如

HistoricalDataUpdate.  1 Date:            Open:  High:  Low:   Close:  Volume:  Average:  Count:
                       20200708  08:31:00 14.17  14.17  14.17  14.17   -1       14.15     -1

我们将不胜感激。

【问题讨论】:

    标签: pandas api tws


    【解决方案1】:
    1. 这真的是 ETL(提取、转换、加载)
    2. 我可以看到每个数据元素的格式为 Name:。使用它作为 reg expr 获取所有名称标记
    3. 使用此列表,根据标记和下一个标记的位置将每个标记提取到字典中
    4. 获取第一个token之前的数据标签
    5. 终于把它变成了熊猫数据框
    text= "HistoricalDataUpdate.  1 Date: 20200708  08:31:00 Open: 14.17 High: 14.17 Low: 14.17 Close: 14.17 Volume: -1 Average: 14.15 Count: -1"
    tokens = re.findall("([A-Z][a-z]*:)", text)
    json = {t:text[re.search(tokens[i], text).span(0)[1]:re.search(tokens[i+1], text).span(0)[0]] 
            if i+1<len(tokens) 
            else text[re.search(tokens[i], text).span(0)[1]:] 
            for i,t in enumerate(tokens)}
    json = {"label":text[:re.search(tokens[0], text).span(0)[0]], **json}
    df = pd.DataFrame([json])
    df
    

    输出

        label   Date:   Open:   High:   Low:    Close:  Volume: Average:    Count:
    0   HistoricalDataUpdate. 1 20200708 08:31:00   14.17   14.17   14.17   14.17   -1  14.15   -1
    
    
    

    【讨论】:

    • ` def historyData(self, reqId, bar): print("HistoricalData.", reqId, "Date:", bar.date, "Open:", bar.open, "High:" , bar.high, "Low:", bar.low, "Close:", bar.close, "Volume:", bar.volume, "Average:", bar.average, "Count:", bar.barCount) ` 这是你的代码吗?为什么要打印这么难解析的东西?
    • 这些是我发现用于定义每个请求的名称。我会在您提供的标有“文本”的示例的第一行中添加什么? “HistoricalDataUpdate.1 Date: 20200708 08:31:00 Open: 14.17 High: 14.17 Low: 14.17 Close: 14.17 Volume: -1 Average: 14.15 Count: -1”只是代码打印的输出示例,因此每个值会根据接收到的数据而有所不同。
    • 我建议将其保存在数据结构中,而不是将其发送到标准输出并尝试对print() 语句进行逆向工程。 bar 看起来是一个相当不错的数据结构
    • 我将如何解析 bar?我已将打印语句更改为 print("HistoricalData.ReqId:", reqId, "BarData:", bar) print("HistoricalDataUpdate.ReqId:", reqId, "BarData:", bar)
    • 真的正如其他答案所暗示的那样。 print("HistoricalData. ", reqId, "Date:", bar.date, "Open:", bar.open, "High:", bar.high, "Low:", bar.low, "Close:", bar.close, "Volume:", bar.volume, "Average:", bar.average, "Count:", bar.barCount) 可以改为创建一个 dict 并将其附加到数据框。
    【解决方案2】:

    回调为您提供 ibapi.common.BarData ,您可以读取它的 var 以获取像 {date:..., open:123...} 等这样的字典。

    Pandas 可以从 dicts 列表中创建数据帧,因此将它们存储在列表中

    也许你想要日期作为索引,pandas 也可以,令人惊讶的是它可以读取格式。

    您可以在完成后将数据保存在 csv 文件中。

    from ibapi.client import EClient
    from ibapi.wrapper import EWrapper
    from ibapi.contract import Contract
    import pandas as pd
    
    class MyWrapper(EWrapper):
        def __init__(self):
            self.data = []
            self.df=None
            
        def nextValidId(self, orderId:int):
            print("Setting nextValidOrderId: %d", orderId)
            self.nextValidOrderId = orderId
            self.start()
    
        def historicalData(self, reqId, bar):
            self.data.append(vars(bar));
            
        def historicalDataUpdate(self, reqId, bar):
            line = vars(bar)
            # pop date and make it the index, add rest to df
            # will overwrite last bar at that same time
            self.df.loc[pd.to_datetime(line.pop('date'))] = line
            
        def historicalDataEnd(self, reqId: int, start: str, end: str):
            print("HistoricalDataEnd. ReqId:", reqId, "from", start, "to", end)
            self.df = pd.DataFrame(self.data)
            self.df['date'] = pd.to_datetime(self.df['date'])
            self.df.set_index('date', inplace=True)
            
        def error(self, reqId, errorCode, errorString):
            print("Error. Id: " , reqId, " Code: " , errorCode , " Msg: " , errorString)
    
        def start(self):
            queryTime = ""
            
            # so everyone can get data use fx
            fx = Contract()
            fx.secType = "CASH" 
            fx.symbol = "USD"
            fx.currency = "JPY"
            fx.exchange = "IDEALPRO"
            
            # setting update to 1 minute still sends an update every tick? but timestamps are 1 min
            # I don't think keepUpToDate sends a realtimeBar every 5 secs, just updates the last bar.
            app.reqHistoricalData(1, fx, queryTime, "1 D", "1 min", "MIDPOINT", 0, 1, True, [])
    
    wrap = MyWrapper()        
    app = EClient(wrap)
    app.connect("127.0.0.1", 7497, clientId=123)
    
    #I just use this in jupyter so I can interact with df
    import threading
    threading.Thread(target = app.run).start()
    
    #this isn't needed in jupyter, just run another cell
    import time
    time.sleep(300) # in 5 minutes check the df and close
    
    print(wrap.df)
    wrap.df.to_csv("myfile.csv")#save in file
    app.disconnect()
    
    #in jupyter to show plot
    %matplotlib inline 
    wrap.df.close.plot()
    

    我使用 jupyter notebook,所以我添加了线程,所以我仍然可以交互。

    这是一些输出。接收和打印的第一个数据来自historyDataEnd。数据帧由具有日期时间索引的变量组成,因此可以按时间添加条形。

    HistoricalDataEnd. ReqId: 1 from 20200707 14:23:19 to 20200708 14:23:19

    然后在 300 秒后打印数据帧。检查 ohlc 是否合乎逻辑,并注意每分钟都有一个新柱。 14:28 柱只是我假设的前 19 秒,因为我的五分钟(300 秒)从 14:23:19 开始。这正是您希望并期望保持图表最新的行为。

    2020-07-08 14:24:00  107.231  107.236  107.231  107.233     -1       -1   
    2020-07-08 14:25:00  107.233  107.234   107.23  107.232     -1       -1   
    2020-07-08 14:26:00  107.232  107.232  107.225  107.232     -1       -1   
    2020-07-08 14:27:00  107.232  107.239  107.231  107.239     -1       -1   
    2020-07-08 14:28:00  107.239  107.239  107.236  107.236     -1       -1   
    

    您可以看到它获取了所有条形图(仅在图表中关闭)并使其保持最新状态。

    【讨论】:

    • " # 将更新设置为 1 分钟仍然会在每个刻度上发送更新?但时间戳是 1 分钟 # 我不认为 keepUpToDate 每 5 秒发送一次 realtimeBar,只是更新最后一个柱。"您所看到的是 IB 的 iServer 在每次报价之间发送空返回。我与 IB Chat 讨论了他们的 API,并被告知他们正在修复它。 WRT 你的答案,我将尝试实现数据框但是我将不得不编辑代码,例如提醒 defhistoryDataEnd,因为我希望启用historyDataUpdate。
    • 我在 Jupyter (python notebook) 中运行它。 IB 错了,这不是我收到的。启用historyDataUpdate,它发生在historyDatEnd 之后。 historyDataEnd 是制作数据框的地方。还有其他更简单的方法,但这是一个显示所有可能性的示例。我这样做是为了测试historyDataUpdate 的功能。我将添加一些输出。
    • 我希望从我的代码中排除historyDataEnd。我可以在historyDataUpdate 中创建数据框吗?
    • 如果您知道列名,您甚至可以在请求之前制作数据框。我展示的是keepUpToDate=True 的用例。这是为了获取一些历史记录并保持最新。如果这不是您的用例,那么这可能不是您需要的数据请求。
    • 阅读文档,interactivebrokers.github.io/tws-api/connection.html#connect - 只是说在发出请求之前等待 nextValidId。您不需要生成 orderId,它只是提醒下一个应该是什么。每个订单都必须增加它们,但您可以随心所欲地跟踪它们。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-10-26
    • 2020-06-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多