Provider abstraction: providers/ package with BaseDataProvider template

One class per provider (TwelveData, Oanda, Tiingo), all subclassing
BaseDataProvider which owns cache, validation/cleaning, freshness +
boundary guarantees, fetch_range pagination, and streaming stubs —
subclasses implement only _fetch_raw. config.json data_providers.active
selects the live feed; data_provider.py / oanda_provider.py stay as
back-compat shims. Module 16 provider-abstraction tests (253 total green).

Claude-Session: https://claude.ai/code/session_01Mc7XKYyPySVXiUpcN82QEK
This commit is contained in:
Harwinder Sekhon
2026-07-28 20:09:00 -04:00
parent f760e9070a
commit 083a4e8d63
17 changed files with 1573 additions and 935 deletions
+18 -6
View File
@@ -282,15 +282,27 @@ def telegram_channel_id(app: Optional[AppConfig] = None) -> str:
return env if env is not None else app.telegram_channel_id
def provider_api_key(app: Optional[AppConfig] = None) -> str:
"""API key for the ACTIVE provider. The env override is per-provider
(TWELVE_DATA_API_KEY applies only when twelve_data is active)."""
# Per-provider env-var overrides for API keys (env → config.json → "").
_PROVIDER_ENV_KEYS = {
"twelve_data": "TWELVE_DATA_API_KEY",
"tiingo": "TIINGO_API_KEY",
}
def provider_api_key(
app: Optional[AppConfig] = None, provider: Optional[str] = None
) -> str:
"""API key for `provider` (default: the ACTIVE provider). The env
override is per-provider (TWELVE_DATA_API_KEY applies only to
twelve_data, TIINGO_API_KEY only to tiingo)."""
app = app or get_app_config()
if app.active_provider == "twelve_data":
env = os.environ.get("TWELVE_DATA_API_KEY")
name = provider or app.active_provider
env_key = _PROVIDER_ENV_KEYS.get(name)
if env_key:
env = os.environ.get(env_key)
if env:
return env
block = app.provider_settings.get(app.active_provider, {})
block = app.provider_settings.get(name, {})
return str(block.get("api_key", ""))