Skip to content

fix(polygon): per-ticker fallback for grouped-daily coverage gaps#131

Merged
cipher813 merged 1 commit into
mainfrom
fix/polygon-per-ticker-fallback
May 2, 2026
Merged

fix(polygon): per-ticker fallback for grouped-daily coverage gaps#131
cipher813 merged 1 commit into
mainfrom
fix/polygon-per-ticker-fallback

Conversation

@cipher813
Copy link
Copy Markdown
Owner

Summary

  • Polygon's bulk grouped-daily endpoint returns inconsistent ticker subsets across calls. Observed 2026-05-02: two calls 4h apart returned 913-ticker subsets that differed by 8 real S&P 500/400 names — tipping MorningEnrich's missing-from-closes hard-fail (12 > threshold 5) and halting the Saturday SF a second time today.
  • Fix adds a per-ticker fallback to polygon's /aggs/ticker endpoint for tickers the grouped response dropped. Same source (polygon), no silent yfinance fallback — feedback_no_silent_fails preserved.
  • Cost: 5 calls/min rate-limit + ~10-15 typical misses = 2-3 min MorningEnrich runtime increase. Well within the SF DataPhase1 budget. Cost-quiet path (no fallback calls when grouped is complete) is locked by a test.

Test plan

  • tests/test_polygon_per_ticker_fallback.py — 12 new tests covering: get_single_day_bar (happy / no-data / 403 / missing-vwap); _fetch_polygon_closes_per_ticker (recovery / caret-prefix stripping / exception isolation); _fetch_polygon_closes integration (invokes fallback on grouped miss / skips FRED indices / no fallback when grouped complete)
  • Existing 24 polygon + daily_closes tests still pass
  • Full suite green: 355 passed
  • Live verification via Saturday SF redrive (after merge): MorningEnrich grouped fetch → per-ticker fallback for any misses → ArcticDB daily_append succeeds → backfill regression preflight passes (PR fix(backfill): apply daily_closes delta + regression preflight #130) → Phase 1 complete → downstream Research/Predictor/Backtester run

🤖 Generated with Claude Code

Polygon's grouped-daily endpoint returns inconsistent ticker subsets
across calls. Observed 2026-05-02: two calls 4h apart against the same
universe returned different 913-ticker subsets that differed by 8 real
S&P 500/400 names (ASGN, GTM, HOLX, KMPR, LW, MOH, MTCH, PAYC). This
tipped MorningEnrich's missing-from-closes hard-fail (12 missing >
threshold of 5) and halted the Saturday SF a second time today.

Fix: when the bulk grouped response is missing a requested stock ticker,
fall through to polygon's per-ticker /aggs/ticker endpoint for that
single ticker. Same source (polygon), same response schema, no silent
yfinance fallback — the no-silent-fails contract is preserved. Each
fallback hit costs one rate-limit slot; with the default 5 calls/min
and ~10-15 typical grouped misses, MorningEnrich gains 2-3 minutes
versus the prior <1 second hot path. Well within the SF DataPhase1
budget.

FRED-handled indices (^TNX/^IRX/^VIX/^VIX3M) are excluded from the
fallback loop — polygon has no coverage for them, the per-ticker call
would just burn rate-limit slots returning nothing.

Two layers tested in tests/test_polygon_per_ticker_fallback.py (12
new tests, 355 total):

1. PolygonClient.get_single_day_bar — happy path / no-data / 403 /
   missing-vwap.
2. _fetch_polygon_closes_per_ticker — recovery success / caret-prefix
   stripping / per-call exception isolation.
3. _fetch_polygon_closes integration — invokes fallback on grouped
   miss, skips for FRED indices, no fallback when grouped is complete
   (cost-quiet path).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@cipher813 cipher813 merged commit 11897ca into main May 2, 2026
1 check passed
@cipher813 cipher813 deleted the fix/polygon-per-ticker-fallback branch May 2, 2026 13:42
cipher813 added a commit that referenced this pull request May 2, 2026
* feat(preflight): add sf_preflight.py — Saturday SF dry-rehearsal

Predicts whether the Saturday SF would succeed BEFORE launching a spot.
Today's recovery cycle (5 SF redrives, ~5 polygon API calls each) burned
free-tier quota and operator hours discovering bugs sequentially. This
module simulates the critical pre-Phase-1 path against real S3 + ArcticDB
state and reports per-step pass/fail in ~30s with 1 polygon call total.

Eight independent checks, mapped to today's incident stack:

  PR #130 (backfill regression)         → check_backfill_source_freshness
  PR #131 (polygon coverage flake)      → check_polygon_grouped_coverage
  PR #132 (missing-from-closes scoping) → check_predicted_missing_from_closes
  PR #133 (freshness scan scoping)      → check_universe_sample_freshness
  PR #134 (workflow ordering)           → check_universe_drift
  PR #135 (return shape)                → check_constituents_fetch
  Postflight contracts                  → check_postflight_contracts
  ArcticDB reachability                 → check_arctic_connectivity

Each check is a pure function taking a PreflightContext, returning a
CheckResult. The orchestrator runs them all (catching per-check
exceptions so one fail doesn't abort the suite) and emits human or
JSON output. Exit code 1 on any failure.

Two macOS-specific design notes:

1. ArcticDB libs are initialized once in check_arctic_connectivity and
   reused across downstream checks via the context — re-initializing
   adb.Arctic() crashes Aws::S3::S3Client::S3Client on macOS.
2. Checks are ordered with arctic_connectivity FIRST so its bundled AWS
   SDK loads before boto3 (which gets pulled in by collectors imports).

Polygon check skips gracefully (WARN, not FAIL) when POLYGON_API_KEY
is unset — supports laptop-side preflight where the .env isn't loaded.
On the spot the key is present and the check fires.

18 tests in tests/test_sf_preflight.py — happy path + each failure mode
each check is designed to catch + orchestrator isolation.

394 tests total.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* test(sf_preflight): set POLYGON_API_KEY in polygon-coverage tests

CI runs without POLYGON_API_KEY in env, so the no-key skip-to-WARN
guard short-circuited the 3 polygon-coverage tests before they
reached the mocked client. Set the env var via monkeypatch so the
guard passes through to the polygon mock. Also add explicit test
for the no-key path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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