fix: retry on requests ConnectionError in AlpacaClient

This commit is contained in:
Justin Oros
2026-02-19 09:05:02 -07:00
parent b4a645cf01
commit 357bcaee67
+14 -11
View File
@@ -2,9 +2,12 @@ import time
import logging import logging
import backoff import backoff
import alpaca_trade_api as tradeapi import alpaca_trade_api as tradeapi
import requests.exceptions
logging.getLogger('backoff').setLevel(logging.CRITICAL) logging.getLogger('backoff').setLevel(logging.CRITICAL)
_RETRYABLE_ERRORS = (tradeapi.rest.APIError, ConnectionError, requests.exceptions.ConnectionError)
def _is_position_not_found(e): def _is_position_not_found(e):
return isinstance(e, tradeapi.rest.APIError) and "position does not exist" in str(e) return isinstance(e, tradeapi.rest.APIError) and "position does not exist" in str(e)
@@ -14,50 +17,50 @@ class AlpacaClient:
def __init__(self, api_key_id, api_secret_key, base_url, api_version="v2"): def __init__(self, api_key_id, api_secret_key, base_url, api_version="v2"):
self.api = tradeapi.REST(api_key_id, api_secret_key, base_url, api_version=api_version) self.api = tradeapi.REST(api_key_id, api_secret_key, base_url, api_version=api_version)
@backoff.on_exception(backoff.expo, (tradeapi.rest.APIError, ConnectionError), max_tries=5, jitter=backoff.full_jitter) @backoff.on_exception(backoff.expo, _RETRYABLE_ERRORS, max_tries=5, jitter=backoff.full_jitter)
def get_account(self): def get_account(self):
return self.api.get_account() return self.api.get_account()
@backoff.on_exception(backoff.expo, (tradeapi.rest.APIError, ConnectionError), max_tries=5, jitter=backoff.full_jitter) @backoff.on_exception(backoff.expo, _RETRYABLE_ERRORS, max_tries=5, jitter=backoff.full_jitter)
def get_clock(self): def get_clock(self):
return self.api.get_clock() return self.api.get_clock()
@backoff.on_exception(backoff.expo, (tradeapi.rest.APIError, ConnectionError), 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):
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
@backoff.on_exception(backoff.expo, (tradeapi.rest.APIError, ConnectionError), 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)
@backoff.on_exception(backoff.expo, (tradeapi.rest.APIError, ConnectionError), max_tries=5, jitter=backoff.full_jitter) @backoff.on_exception(backoff.expo, _RETRYABLE_ERRORS, max_tries=5, jitter=backoff.full_jitter)
def submit_order(self, **kwargs): def submit_order(self, **kwargs):
return self.api.submit_order(**kwargs) return self.api.submit_order(**kwargs)
@backoff.on_exception(backoff.expo, (tradeapi.rest.APIError, ConnectionError), max_tries=5, jitter=backoff.full_jitter) @backoff.on_exception(backoff.expo, _RETRYABLE_ERRORS, max_tries=5, jitter=backoff.full_jitter)
def get_order(self, order_id): def get_order(self, order_id):
return self.api.get_order(order_id) return self.api.get_order(order_id)
@backoff.on_exception(backoff.expo, (tradeapi.rest.APIError, ConnectionError), max_tries=5, jitter=backoff.full_jitter) @backoff.on_exception(backoff.expo, _RETRYABLE_ERRORS, max_tries=5, jitter=backoff.full_jitter)
def cancel_order(self, order_id): def cancel_order(self, order_id):
return self.api.cancel_order(order_id) return self.api.cancel_order(order_id)
@backoff.on_exception(backoff.expo, (tradeapi.rest.APIError, ConnectionError), max_tries=5, jitter=backoff.full_jitter) @backoff.on_exception(backoff.expo, _RETRYABLE_ERRORS, max_tries=5, jitter=backoff.full_jitter)
def list_positions(self): def list_positions(self):
return self.api.list_positions() return self.api.list_positions()
@backoff.on_exception(backoff.expo, (tradeapi.rest.APIError, ConnectionError), max_tries=5, jitter=backoff.full_jitter) @backoff.on_exception(backoff.expo, _RETRYABLE_ERRORS, max_tries=5, jitter=backoff.full_jitter)
def list_orders(self, **kwargs): def list_orders(self, **kwargs):
return self.api.list_orders(**kwargs) return self.api.list_orders(**kwargs)
@backoff.on_exception(backoff.expo, (tradeapi.rest.APIError, ConnectionError), max_tries=5, jitter=backoff.full_jitter) @backoff.on_exception(backoff.expo, _RETRYABLE_ERRORS, max_tries=5, jitter=backoff.full_jitter)
def close_all_positions(self): def close_all_positions(self):
return self.api.close_all_positions() return self.api.close_all_positions()
@backoff.on_exception(backoff.expo, (tradeapi.rest.APIError, ConnectionError), max_tries=5, jitter=backoff.full_jitter, giveup=_is_position_not_found) @backoff.on_exception(backoff.expo, _RETRYABLE_ERRORS, max_tries=5, jitter=backoff.full_jitter, giveup=_is_position_not_found)
def get_position(self, symbol): def get_position(self, symbol):
return self.api.get_position(symbol) return self.api.get_position(symbol)