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.clears_table_events
							parent
							
								
									d5c3124722
								
							
						
					
					
						commit
						2c6b832e50
					
				| 
						 | 
					@ -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