'Is there a proper way to produce a OHLCV pandas dataframe using ib api?
Here is the code that print out the data. However i don't see how to collect these data into a pandas dataframe. I used reqHistoricalData imported from ibapi (interactive broker) to request the data from TestApp class function which inherit EClient and EWrapper.
from ibapi.client import EClient
from ibapi.wrapper import EWrapper
from ibapi.contract import Contract
from ibapi.order import Order
from ibapi.ticktype import TickTypeEnum
import pandas as pd
import numpy as np
import os.path # To manage paths
import sys # To find out the script name (in argv[0])
from datetime import datetime
from time import sleep, strftime, localtime
from socket import error as SocketError
import errno
class TestApp(EWrapper, EClient):
def __init__(self):
EClient.__init__(self,self)
def error(self, reqId, errorCode, errorString):
print ('Error: ', reqId, errorCode, ' ', errorString)
def historicalData(self,reqId, bar):
print (bar.date, bar.open, bar.high, bar.low, bar.close, bar.volume)
def create_contract(symbol, sec_type, exch, prim_exch, curr):
contract = Contract()
contract.symbol = symbol
contract.secType = sec_type
contract.exchange = exch
contract.currency = curr
contract.primaryExchange = prim_exch
return contract
def create_order(order_type, quantity, action):
order = Order()
order.orderType = order_type
order.totalQuantity = quantity
order.action = action
return order
app = TestApp()
app.connect('127.0.0.1', 7497, 0)
contract = create_contract('AAPL', 'STK', 'SMART', 'NASDAQ', 'USD')
app.reqHistoricalData( reqId = 0,
contract = contract,
endDateTime = '',
durationStr = '1 Y',
barSizeSetting = '1 month',
whatToShow = 'TRADES',
useRTH = 1, # =1 for RTH data
formatDate = 1,
keepUpToDate = False,
chartOptions = []
)
app.run()
and the output is:
20181031 222.52 224.23 206.09 218.86 1752000
20181130 219.07 222.36 170.26 178.58 7249186
20181231 184.39 184.94 146.6 157.74 6851826
20190131 154.89 169.0 142.0 166.44 6383564
20190228 166.93 175.87 165.93 173.15 3478346
20190329 174.28 197.69 169.5 189.95 4956586
20190430 191.64 208.48 188.38 200.67 3812115
20190531 209.88 215.31 174.99 175.07 5642571
20190628 175.58 201.57 170.27 197.92 3592406
20190731 203.28 221.37 198.41 213.04 3418242
20190830 213.82 218.03 192.58 208.74 5078104
20190930 206.42 226.42 204.22 223.97 3768842
20191023 225.13 243.18 215.13 242.51 3253952
What i am looking for:
Open High Low Close Volume
Date
20181031 222.52 224.23 206.09 218.86 1752000
20181130 219.07 222.36 170.26 178.58 7249186
20181231 184.39 184.94 146.6 157.74 6851826
20190131 154.89 169.0 142.0 166.44 6383564
20190228 166.93 175.87 165.93 173.15 3478346
20190329 174.28 197.69 169.5 189.95 4956586
20190430 191.64 208.48 188.38 200.67 3812115
20190531 209.88 215.31 174.99 175.07 5642571
20190628 175.58 201.57 170.27 197.92 3592406
20190731 203.28 221.37 198.41 213.04 3418242
20190830 213.82 218.03 192.58 208.74 5078104
20190930 206.42 226.42 204.22 223.97 3768842
20191023 225.13 243.18 215.13 242.51 3253952
Solution 1:[1]
You could add a dataframe member to TestApp, and then add a row to it every time historicalData() is called:
...
self.cols = ['date', 'open', 'high', 'low', 'close', 'volume']
self.df = pd.DataFrame(columns=self.cols)
def historicalData(self, reqId, bar):
print (bar.date, bar.open, bar.high, bar.low, bar.close, bar.volume)
self.df.loc[len(self.df)] = [bar.date, bar.open, bar.high, bar.low, bar.close, bar.volume]
You would probably want to have a separate DataFrame for each reqId.
Solution 2:[2]
For bigger data sets, use list of dicts. The bar object returning from the api can be translated into a dict, which can be appended to a list member of the api.
class IBApi(TestWrapper, TestClient): # the actual API 'app' = API Object we interact with when sending/receiving
def __init__(self):
TestWrapper.__init__(self) # requires the wrapper...
TestClient.__init__(self, wrapper=self)
###.....
self.histbars=[]
def historicalData(self, reqId:int, bar: BarData):
bardict={'reqid':reqId,'datetime':bar.date,'open':bar.open,'high':bar.high,'low':bar.low,'close':bar.close,'vol':bar.volume,'wap':bar.wap,'barcount':bar.barCount}
self.histbars.append(bardict)
This resulting list of dicts easily converts into a dataframe:
df =DataFrame.from_records(apclient.histbars)
df
reqid datetime open high low close vol wap barCount
0 1 20220519 09:30:00 191.50 193.80 189.60 191.58 16178 191.778 7890
1 1 20220519 09:45:00 191.64 194.30 190.21 192.98 12876 192.433 6090
2 1 20220519 10:00:00 192.97 194.74 191.88 192.33 12974 193.27 7835
3 1 20220519 10:15:00 192.39 193.75 191.77 192.79 8370 192.906 4372
4 1 20220519 10:30:00 192.71 194.07 191.29 191.69 7269 192.425 3774
5 1 20220519 10:45:00 191.72 193.19 191.01 192.26 6565 192.093 3167
6 1 20220519 11:00:00 192.24 193.99 191.80 193.44 6664 192.998 3023
7 1 20220519 11:15:00 193.37 193.85 192.58 192.97 5105 193.132 2278
8 1 20220519 11:30:00 193.04 194.99 192.99 194.63 5787 194.196 2703
9 1 20220519 11:45:00 194.60 194.97 193.90 194.80 7207 194.467 2949
10 1 20220519 12:00:00 194.82 195.29 194.50 194.67 4862 194.839 2169
11 1 20220519 12:15:00 194.66 195.15 194.04 194.59 5753 194.605 2638
12 1 20220519 12:30:00 194.61 194.75 192.92 192.92 3618 193.83 1723
13 1 20220519 12:45:00 192.90 193.39 192.38 193.20 3921 192.911 1739
14 1 20220519 13:00:00 193.20 193.32 191.69 191.98 3309 192.602 1844
15 1 20220519 13:15:00 191.97 192.28 191.45 191.98 4601 191.883 2407
16 1 20220519 13:30:00 192.00 192.62 191.55 192.02 4240 192.034 2031
...
(excuse the manual format)
To do: simple lookup dict from request id to ticker, and format the datetime to pandas-readable datetime.
Solution 3:[3]
You can copy and paste two indicators back to back. Just remember that, there can only be one indicator() or study() call in a script.
The problem with your example is the scaling. When you have the RSI on price chart, it messes up with the scaling.
//@version=5
indicator(title="EMA 20/50/100/200 and RSI", overlay=true)
shortest = ta.ema(close, 20)
short = ta.ema(close, 50)
longer = ta.ema(close, 100)
longest = ta.ema(close, 200)
plot(shortest, color = color.red)
plot(short, color = color.orange)
plot(longer, color = color.aqua)
plot(longest, color = color.blue)
ma(source, length, type) =>
switch type
"SMA" => ta.sma(source, length)
"Bollinger Bands" => ta.sma(source, length)
"EMA" => ta.ema(source, length)
"SMMA (RMA)" => ta.rma(source, length)
"WMA" => ta.wma(source, length)
"VWMA" => ta.vwma(source, length)
rsiLengthInput = input.int(14, minval=1, title="RSI Length", group="RSI Settings")
rsiSourceInput = input.source(close, "Source", group="RSI Settings")
maTypeInput = input.string("SMA", title="MA Type", options=["SMA", "Bollinger Bands", "EMA", "SMMA (RMA)", "WMA", "VWMA"], group="MA Settings")
maLengthInput = input.int(14, title="MA Length", group="MA Settings")
bbMultInput = input.float(2.0, minval=0.001, maxval=50, title="BB StdDev", group="MA Settings")
up = ta.rma(math.max(ta.change(rsiSourceInput), 0), rsiLengthInput)
down = ta.rma(-math.min(ta.change(rsiSourceInput), 0), rsiLengthInput)
rsi = down == 0 ? 100 : up == 0 ? 0 : 100 - (100 / (1 + up / down))
rsiMA = ma(rsi, maLengthInput, maTypeInput)
isBB = maTypeInput == "Bollinger Bands"
plot(rsi, "RSI", color=#7E57C2)
plot(rsiMA, "RSI-based MA", color=color.yellow)
rsiUpperBand = hline(70, "RSI Upper Band", color=#787B86)
hline(50, "RSI Middle Band", color=color.new(#787B86, 50))
rsiLowerBand = hline(30, "RSI Lower Band", color=#787B86)
fill(rsiUpperBand, rsiLowerBand, color=color.rgb(126, 87, 194, 90), title="RSI Background Fill")
bbUpperBand = plot(isBB ? rsiMA + ta.stdev(rsi, maLengthInput) * bbMultInput : na, title = "Upper Bollinger Band", color=color.green)
bbLowerBand = plot(isBB ? rsiMA - ta.stdev(rsi, maLengthInput) * bbMultInput : na, title = "Lower Bollinger Band", color=color.green)
fill(bbUpperBand, bbLowerBand, color= isBB ? color.new(color.green, 90) : na, title="Bollinger Bands Background Fill")
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Josh |
| Solution 2 | |
| Solution 3 | vitruvius |
