-
-
Notifications
You must be signed in to change notification settings - Fork 115
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
WAP support on tickfilter bars #127
base: main
Are you sure you want to change the base?
Conversation
Align `ib_async.ticker.Ticker.Bar` with TWS API `IBApi.Bar` class Reference -> https://interactivebrokers.github.io/tws-api/classIBApi_1_1Bar.html Change summary - add `wap` column - sort columns aligned with `IBApi.Bar` . - set Bar class `slots=True` - using pympler asizeof.asizeof(bar)=736 - slot version asizeof.asizeof(bar)=152 - include wap calculation on: - TimeBars - TickBars - VolumeBars - set `ib_async.ticker.Ticker` dataclasse as slots=True - according to pympler asizeof(Ticker) = 7200 - slotted version asizeof(Ticker) = 3000
ib_async/ticker.py
Outdated
@@ -302,6 +304,11 @@ def on_source(self, time, price, size): | |||
bar.high = max(bar.high, price) | |||
bar.low = min(bar.low, price) | |||
bar.close = price | |||
# wap | |||
if (bar.volume + size) == 0: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi, how about:
new_volume = bar.volume + size
if new_volume != 0: # Prevent division by zero in empty bar
bar.wap = ((bar.wap * bar.volume) + (price * size)) / new_volume
bar.volume = new_volume
ib_async/ticker.py
Outdated
class Bar: | ||
time: Optional[datetime] | ||
open: float = nan | ||
high: float = nan | ||
low: float = nan | ||
close: float = nan | ||
volume: int = 0 | ||
wap: float = 0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems lika "nan" is the convention for default. If you decide so, also conditions on ln 362 and 398 will need update to isNan(bar.wap)
.
Although looks like wap can only ever be nan/0 in TimeBar when bar gets created on timer without any input data, so maybe these conditions in Tick and Volume bars are unnecessary?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i'll do a few tests with nan
, I just realize that using wap = 0
is running my plots
- set wap default value to `nan` - manage first bar to avoit operations with `nan` - align functionality within `Timebars`, `TickBars` and `VolumeBars`. They all emit on new ticks `self.bars.updateEvent.emit(self.bars, False)` with parameter `hasNewBar` set `False`. Aligned with `reqHistoricalData` and `reqRealTimeBars`
@pavel-slama please let me know your thoughts. I have run this a few days with market data and it seems to be working fine. @mattsta we are using
probably is too much to push it here, but i can push it into next |
IMHO entirely correct solution would keep wap as nan if there's no data - as no price data makes wap undefined not 0. And if the plots should show 0 in such cases - then it should be the plotting code doing the nan -> 0 conversion. But I wouldn't block the merge because of that. I can imagine it could screw up some code doing trading based on wap getting 0 when in fact there's no data. (I don't have permissions in this repo anyway, just offering advice) |
that's what is doing right now. this is from my paper account where i'm running ib_async with this patch.
I'm requesting |
Then it's all good +1 |
Interesting improvement! Some notes: We do populate wap data in the decoder for BarData in some places (but it's just called Lines 477 to 511 in e457696
Lines 801 to 819 in e457696
Lines 80 to 82 in e457696
For the collective bars, it would take a little more work because the correct vwap calculation is sum(price * size) / sum(size), so we need to actually keep a full history of everything submitted to the collective bar, then when the bar is updated/complete, we calculate the total. Also the So we can either update the wap/average calculation to only happen just before the "complete" time/tick/volume bar is emitted, or make it a dynamic implementation: @property
def average(self) -> float:
if self.ticks:
pv = 0
v = 0
for tick in self.ticks:
pv += tick.price * tick.size
v += tick.size
assert v
return pv / v
return float("nan") something like that, but we'd also have to add a new field for tracking all the individual (price, size) tick updates in the collective bars too. Or (if we have no use for the individual ticks for anything else), it could be done in more of an O(1) fashion by just tracking |
please let me know if more changes are needed to push this PR. I do acknowledge that setting |
Align
ib_async.ticker.Ticker.Bar
with TWS APIIBApi.Bar
classReference -> https://interactivebrokers.github.io/tws-api/classIBApi_1_1Bar.html
Change summary
wap
columnIBApi.Bar
.slots=True
ib_async.ticker.Ticker
dataclasse as slots=TrueTimebars
,TickBars
andVolumeBars
. They allemit on new ticks
self.bars.updateEvent.emit(self.bars, False)
with parameterhasNewBar
setFalse
. Aligned withreqHistoricalData
andreqRealTimeBars