More correct no-data output handling
When we get a timeout or a `NoData` condition still return a tuple of empty sequences instead of `None` from `Client.bars()`. Move the sampling period-duration table to module level.ib_1m_hist
parent
61ca5f7e19
commit
54567d33da
|
@ -184,7 +184,8 @@ _adhoc_futes_set = {
|
||||||
'lb.nymex', # random len lumber
|
'lb.nymex', # random len lumber
|
||||||
|
|
||||||
# metals
|
# metals
|
||||||
'xauusd.cmdty', # gold spot
|
# https://misc.interactivebrokers.com/cstools/contract_info/v3.10/index.php?action=Conid%20Info&wlId=IB&conid=69067924
|
||||||
|
'xauusd.cmdty', # london gold spot ^
|
||||||
'gc.nymex',
|
'gc.nymex',
|
||||||
'mgc.nymex', # micro
|
'mgc.nymex', # micro
|
||||||
|
|
||||||
|
@ -242,8 +243,6 @@ _exch_skip_list = {
|
||||||
'PSE',
|
'PSE',
|
||||||
}
|
}
|
||||||
|
|
||||||
# https://misc.interactivebrokers.com/cstools/contract_info/v3.10/index.php?action=Conid%20Info&wlId=IB&conid=69067924
|
|
||||||
|
|
||||||
_enters = 0
|
_enters = 0
|
||||||
|
|
||||||
|
|
||||||
|
@ -269,6 +268,19 @@ def bars_to_np(bars: list) -> np.ndarray:
|
||||||
return nparr
|
return nparr
|
||||||
|
|
||||||
|
|
||||||
|
# NOTE: pacing violations exist for higher sample rates:
|
||||||
|
# https://interactivebrokers.github.io/tws-api/historical_limitations.html#pacing_violations
|
||||||
|
# Also see note on duration limits being lifted on 1m+ periods,
|
||||||
|
# but they say "use with discretion":
|
||||||
|
# https://interactivebrokers.github.io/tws-api/historical_limitations.html#non-available_hd
|
||||||
|
_samplings: dict[int, tuple[str, str]] = {
|
||||||
|
1: ('1 secs', f'{int(2e3)} S'),
|
||||||
|
# TODO: benchmark >1 D duration on query to see if
|
||||||
|
# throughput can be made faster during backfilling.
|
||||||
|
60: ('1 min', '1 D'),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class Client:
|
class Client:
|
||||||
'''
|
'''
|
||||||
IB wrapped for our broker backend API.
|
IB wrapped for our broker backend API.
|
||||||
|
@ -326,6 +338,12 @@ class Client:
|
||||||
# ohlc sample period in seconds
|
# ohlc sample period in seconds
|
||||||
sample_period_s: int = 1,
|
sample_period_s: int = 1,
|
||||||
|
|
||||||
|
# optional "duration of time" equal to the
|
||||||
|
# length of the returned history frame.
|
||||||
|
duration: Optional[str] = None,
|
||||||
|
|
||||||
|
**kwargs,
|
||||||
|
|
||||||
) -> list[dict[str, Any]]:
|
) -> list[dict[str, Any]]:
|
||||||
'''
|
'''
|
||||||
Retreive OHLCV bars for a fqsn over a range to the present.
|
Retreive OHLCV bars for a fqsn over a range to the present.
|
||||||
|
@ -334,18 +352,8 @@ class Client:
|
||||||
# See API docs here:
|
# See API docs here:
|
||||||
# https://interactivebrokers.github.io/tws-api/historical_data.html
|
# https://interactivebrokers.github.io/tws-api/historical_data.html
|
||||||
bars_kwargs = {'whatToShow': 'TRADES'}
|
bars_kwargs = {'whatToShow': 'TRADES'}
|
||||||
|
bars_kwargs.update(kwargs)
|
||||||
# NOTE: pacing violations exist for higher sample rates:
|
bar_size, duration = _samplings[sample_period_s]
|
||||||
# https://interactivebrokers.github.io/tws-api/historical_limitations.html#pacing_violations
|
|
||||||
# Also see note on duration limits being lifted on 1m+ periods,
|
|
||||||
# but they say "use with discretion":
|
|
||||||
# https://interactivebrokers.github.io/tws-api/historical_limitations.html#non-available_hd
|
|
||||||
bar_size, duration = {
|
|
||||||
1: ('1 secs', f'{int(2e3)} S'),
|
|
||||||
# TODO: benchmark >1 D duration on query to see if
|
|
||||||
# throughput can be made faster during backfilling.
|
|
||||||
60: ('1 min', '1 D'),
|
|
||||||
}[sample_period_s]
|
|
||||||
|
|
||||||
global _enters
|
global _enters
|
||||||
# log.info(f'REQUESTING BARS {_enters} @ end={end_dt}')
|
# log.info(f'REQUESTING BARS {_enters} @ end={end_dt}')
|
||||||
|
@ -386,8 +394,18 @@ class Client:
|
||||||
# whatToShow='TRADES',
|
# whatToShow='TRADES',
|
||||||
)
|
)
|
||||||
if not bars:
|
if not bars:
|
||||||
# trigger ``NoData`` raise by ``get_bars()`` caller.
|
# NOTE: there's 2 cases here to handle (and this should be
|
||||||
return None
|
# read alongside the implementation of
|
||||||
|
# ``.reqHistoricalDataAsync()``):
|
||||||
|
# - no data is returned for the period likely due to
|
||||||
|
# a weekend, holiday or other non-trading period prior to
|
||||||
|
# ``end_dt`` which exceeds the ``duration``,
|
||||||
|
# - a timeout occurred in which case insync internals return
|
||||||
|
# an empty list thing with bars.clear()...
|
||||||
|
return [], np.empty(0)
|
||||||
|
# TODO: we could maybe raise ``NoData`` instead if we
|
||||||
|
# rewrite the method in the first case? right now there's no
|
||||||
|
# way to detect a timeout.
|
||||||
|
|
||||||
nparr = bars_to_np(bars)
|
nparr = bars_to_np(bars)
|
||||||
return bars, nparr
|
return bars, nparr
|
||||||
|
|
Loading…
Reference in New Issue