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