fix: add 30s timeout to get_bars to prevent silent hang on 200-bar fetch

This commit is contained in:
Justin Oros
2026-02-23 08:33:13 -07:00
parent fc7669a7f5
commit 66f39f0690
+13 -1
View File
@@ -1,12 +1,15 @@
import time import time
import logging import logging
import backoff import backoff
import concurrent.futures
import alpaca_trade_api as tradeapi import alpaca_trade_api as tradeapi
import requests.exceptions import requests.exceptions
logging.getLogger('backoff').setLevel(logging.CRITICAL) logging.getLogger('backoff').setLevel(logging.CRITICAL)
_RETRYABLE_ERRORS = (tradeapi.rest.APIError, ConnectionError, requests.exceptions.ConnectionError) BARS_REQUEST_TIMEOUT = 30
_RETRYABLE_ERRORS = (tradeapi.rest.APIError, ConnectionError, requests.exceptions.ConnectionError, requests.exceptions.Timeout, TimeoutError)
def _is_position_not_found(e): def _is_position_not_found(e):
@@ -27,11 +30,20 @@ class AlpacaClient:
@backoff.on_exception(backoff.expo, _RETRYABLE_ERRORS, max_tries=5, jitter=backoff.full_jitter) @backoff.on_exception(backoff.expo, _RETRYABLE_ERRORS, max_tries=5, jitter=backoff.full_jitter)
def get_bars(self, symbol, timeframe, **kwargs): def get_bars(self, symbol, timeframe, **kwargs):
def _fetch():
bars = self.api.get_bars(symbol, timeframe, **kwargs) bars = self.api.get_bars(symbol, timeframe, **kwargs)
if bars is None: if bars is None:
return None return None
return bars.df return bars.df
with concurrent.futures.ThreadPoolExecutor(max_workers=1) as executor:
future = executor.submit(_fetch)
try:
return future.result(timeout=BARS_REQUEST_TIMEOUT)
except concurrent.futures.TimeoutError:
logging.warning(f"get_bars timed out after {BARS_REQUEST_TIMEOUT}s for {symbol} {timeframe}")
raise TimeoutError(f"get_bars hung for {symbol} {timeframe}")
@backoff.on_exception(backoff.expo, _RETRYABLE_ERRORS, max_tries=5, jitter=backoff.full_jitter) @backoff.on_exception(backoff.expo, _RETRYABLE_ERRORS, max_tries=5, jitter=backoff.full_jitter)
def get_latest_quote(self, symbol): def get_latest_quote(self, symbol):
return self.api.get_latest_quote(symbol) return self.api.get_latest_quote(symbol)