Skip to content

DATA-RECOVERY-001: Add feed recovery, stale handling, fallback, and provider health #35

Description

@jamalfrnk

Problem

Once DATA-HL-001 lands a real Hyperliquid feed, it needs production-grade reliability: reconnect/backoff, staleness detection, fallback behavior, and an exposed health surface -- none of which exist for the current CoinGecko polling loop either (it has basic staleness via STALE_AFTER_MS but no reconnect/backoff/fallback concept since it's simple HTTP polling).

Repository evidence

server/src/market-data/ingestion.ts has a staleness threshold but no WS reconnect logic (there's nothing to reconnect -- it's poll-based today). This issue's scope only becomes concrete once DATA-HL-001's WS client exists.

Risk

P1 -- without this, a Hyperliquid WS disconnect silently stalls prices with no visible degraded state, which directly enables one of the mission's explicit hard requirements: never let stale data drive a new paper fill under a normal-looking success state.

Scope

Exponential reconnect backoff with jitter and a max cap; heartbeat/liveness + silent-stall detection; duplicate and out-of-order event handling; missed-candle recovery via REST backfill; resubscription after reconnect; resource cleanup; update coalescing/backpressure; last-known-good state with timestamp; stale-data thresholds; primary-failure, fallback-failure, and total-failure states; reconnection-storm protection. Expose: last successful provider message time, last successful REST snapshot, consecutive failures, reconnect attempts, current provider, current mode (live/degraded/fallback/unavailable), data age by symbol.

Non-goals

Not implementing a second live provider beyond CoinGecko-as-fallback-display (already exists conceptually); not building alerting/paging (that's operational, out of scope here).

Dependencies

DATA-HL-001 (needs the real WS client to attach recovery logic to).

Proposed implementation

A MarketHealthService tracking per-symbol freshness and connection state, wired into the existing getIngestionHealth()//api/market-data/health endpoint (extended, not replaced) and into PaperPricingService's reference-price gating (no new paper order when reference price is untrustworthy).

Acceptance criteria

Simulated disconnect triggers backoff+reconnect+resubscribe; simulated stale feed blocks new paper-order execution with a clear rejection reason while preserving last-known-good display data; health endpoint reflects all listed states accurately.

Definition of done

Server tests simulate disconnect/reconnect, duplicate/out-of-order events, and stale-price order rejection; reviewer PASS.

Test plan

Unit tests against a mocked WS transport: reconnect backoff sequence, resubscription after reconnect, duplicate-candle dedup, out-of-order rejection, stale-price paper-order rejection.

Security review

Reconnection-storm protection prevents this from becoming a self-inflicted DoS vector against Hyperliquid's public endpoints.

Accessibility review

N/A (server-side; client-facing freshness indicators are CHART-001's concern).

Observability requirements

This issue is the observability work for market data -- see acceptance criteria.

Performance considerations

Coalesce update fan-out so one price tick doesn't cause N redundant broadcasts; document the coalescing interval chosen.

Rollback plan

Revert PR; falls back to DATA-HL-001's baseline behavior without recovery hardening.

Documentation requirements

Extend docs/architecture/market-data.md.

Completion evidence

(added on merge)

Reviewer verdict

(pending)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1Required for reliable production operationepic:market-dataHyperliquid/CoinGecko market-data ingestion

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions