Skip to content

Add real market-data vendors behind a multi-vendor provider chain#15

Merged
vingrad merged 1 commit into
mainfrom
feat/marketdata-real-vendors
Jun 12, 2026
Merged

Add real market-data vendors behind a multi-vendor provider chain#15
vingrad merged 1 commit into
mainfrom
feat/marketdata-real-vendors

Conversation

@vingrad

@vingrad vingrad commented Jun 12, 2026

Copy link
Copy Markdown
Owner

Replaces the marketdata.HTTPProvider stub with a production multi-vendor architecture for the finance planner.

What's in here

  • Vendor registry + adapters: FMP (quotes, latest-only fundamentals, EOD bars; BYOK via DDE_MARKETDATA_API_KEY) and keyless Stooq (EOD bars via CSV). alphavantage/tiingo remain selectable placeholders, so existing configs keep today's behavior. Adding a vendor is one adapter file plus one registry line.
  • Fallback chain: DDE_MARKETDATA_VENDOR=fmp,stooq is tried in order per read; capability gaps, missing tickers, rate limits and outages fall through. The composed name (fmp+stooq) flows into provenance.
  • TTL cache (outermost layer): bar ranges are fetched at day granularity and split at the start of yesterday — older bars are immutable and cached without expiry, only the recent stub refreshes on TTL, so a year-long window refetches two days instead of a year. TTL of 0 disables a cache, matching the PlanCacheTTL convention.
  • Per-vendor rate limiting: min-interval pacing plus a daily budget (FMP: 250ms / 240/day). Budget is only spent on requests actually sent — hopeless waits fail fast, cancelled waits are refunded.
  • Point-in-time honesty: past-asOf quotes derive from daily bars (never the live endpoint), free-tier fundamentals are tagged latest_only and refused for past asOf. Backtests stay on offline fixtures.
  • Fail-fast config: a vendor that needs a key but has none refuses to start with a clear message instead of degrading silently.

Notes

  • Stooq currently fronts its CSV endpoint with an anti-bot browser check; the adapter detects the HTML page and degrades (ErrUnavailable) rather than attempting to bypass it.
  • Free tiers are dev/demo only (typically non-commercial) — bring your own paid key for production use; documented in README and docs/domains.md.

Testing

  • Shared conformance suite runs the point-in-time contract against offline, FMP (httptest), Stooq (httptest), the chain, and the cache-wrapped chain.
  • Golden-fixture tests per adapter (incl. key-redaction and hit-counter assertions), cache/limiter/chain unit tests with injected clocks.
  • Full suite passes with -race; golangci-lint clean.
  • Live smoke tests are env-gated (DDE_MARKETDATA_LIVE_TEST=1).

Replace the HTTP provider stub with a pluggable vendor architecture for
the finance planner:

- Vendor registry with two real adapters: FMP (quotes, latest-only
  fundamentals, EOD bars; requires DDE_MARKETDATA_API_KEY) and keyless
  Stooq (EOD bars via CSV). alphavantage/tiingo stay as selectable
  placeholders, so existing configs keep working.
- Ordered fallback chain (DDE_MARKETDATA_VENDOR=fmp,stooq): capability
  gaps, missing tickers, rate limits and outages fall through to the
  next vendor; the composed name lands in provenance.
- TTL cache as the outermost layer. Bar ranges are fetched at day
  granularity and split at the start of yesterday: older bars are
  immutable and cached without expiry, only the recent stub refreshes
  on TTL, so a year-long window refetches two days instead of a year.
  A TTL of 0 disables the respective cache.
- Per-vendor rate limiting (min interval + daily budget). Budget is
  only spent on requests actually sent: hopeless waits fail fast and
  cancelled waits are refunded.
- Point-in-time honesty: past-asOf quotes are derived from daily bars,
  never the live endpoint; free-tier fundamentals are tagged
  latest_only and refused for past asOf instead of faking history.
  Backtests stay on offline fixtures.
- Secrets: vendor errors carry path + status only, never the API key.

Vendors that need a key fail at startup with a clear message rather
than degrading silently. A shared conformance suite runs the
point-in-time contract against every provider implementation,
including the chain and the cache.
@vingrad vingrad merged commit 84c4c72 into main Jun 12, 2026
3 checks passed
@vingrad vingrad deleted the feat/marketdata-real-vendors branch June 12, 2026 19:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant