trader.resilience.retrying module

Protocol-preserving retry wrappers for read-only adapters.

These classes implement the read Protocols and nothing else, on purpose. RetryingBroker satisfies BrokerAdapter but not OrderPlacingBroker, so it cannot be handed to OrderExecutor and cannot be made to retry a submission. That matters more than it might look: a timeout on submit_limit_buy is not evidence the order failed to reach Alpaca, so a retry can place a second one. This project has already paid for a duplicate order once, on a live paper account, and a comment asking future code not to retry writes is weaker than an object that has no write to retry.

There is also no __getattr__ passthrough. A wrapper that forwarded unknown attributes would re-expose the wrapped broker’s submit_limit_buy and undo the whole guarantee, silently.

What is not covered here. Broker reads made inside a cycle (run_once reads positions, orders, and the account) keep the raw order-placing broker, because a read-only wrapper cannot be passed where an OrderPlacingBroker is required. Those are covered by the daemon’s cycle-level catch-all and its backoff instead — one failed read costs one cycle, not the process. Market data is the flakiest dependency and is wrapped, so a yfinance blip is absorbed inside the cycle rather than discarding it.

class trader.resilience.retrying.RetryingBroker(broker, *, attempts, base_seconds, sleep=<built-in function sleep>)[source]

Bases: object

BrokerAdapter with bounded retry. Holds no order-placing method.

Parameters:
  • broker (BrokerAdapter)

  • attempts (int)

  • base_seconds (int)

  • sleep (Callable[[float], None])

get_account()[source]

The account summary, retried on transient failure.

Return type:

Account

get_positions()[source]

Open positions, retried on transient failure.

Return type:

list[Position]

get_clock()[source]

The market clock, retried on transient failure.

Return type:

MarketClock

get_asset(symbol)[source]

Asset metadata, retried on transient failure.

Parameters:

symbol (str)

Return type:

TradableAsset | None

class trader.resilience.retrying.RetryingMarketData(provider, *, attempts, base_seconds, sleep=<built-in function sleep>)[source]

Bases: object

MarketDataProvider with bounded retry.

Wrapped where the broker’s in-cycle reads are not, because this one is genuinely flaky: yfinance is an unofficial scrape with no availability guarantee, and losing a cycle to one failed bar fetch is avoidable.

Parameters:
  • provider (MarketDataProvider)

  • attempts (int)

  • base_seconds (int)

  • sleep (Callable[[float], None])

get_quote(symbol)[source]

The latest quote, retried on transient failure.

Parameters:

symbol (str)

Return type:

Quote

get_history(symbol, start, end, interval=None)[source]

Historical bars, retried on transient failure.

interval defaults to None here rather than to “1d” so an omitted argument reaches the provider omitted, and the provider’s own default stays the single definition of it. Duplicating “1d” here would mean two places to change it, one of which nobody would remember.

Parameters:
  • symbol (str)

  • start (date)

  • end (date)

  • interval (str | None)

Return type:

list[Bar]