Live demo: https://pattern-forge-five.vercel.app/
Architecture: the browser talks to Next.js route handlers that validate closed candles from Hyperliquid's public HTTP API. A separate WebSocket carries live mid-price and never writes into replay. Recorded replay uses only the selected prefix; incomplete higher-timeframe groups are omitted, not guessed. The public Vercel demo does not serve PostgreSQL — persistence is for local use and CI.
The application uses Next.js. Unused Vite/Vinext/Cloudflare migration dependencies were removed during the September 2026 dependency audit; no application code or active deployment depends on them.
Project walkthrough: user journey, dependencies and implementation steps.
A chart-first workspace for public Hyperliquid quotes, candle snapshots and recorded Polymarket Perps markets. This is not an order terminal, strategy recommendation, live account monitor or proof of profitability.
- Market search and selector: BTC/ETH/SOL public snapshots; ten recorded markets; the original saved BTC case. Only the selected market's candles are requested.
- A separate live mid-price uses Hyperliquid's public WebSocket. Switching to a recording closes the connection. A missing or disconnected stream loses its live status and clears the price while reconnecting. It never modifies replay.
- One main chart with separate indicator controls. EMA periods, Bollinger Bands, confirmed swing geometry and candle markers are optional price overlays.
- A lower pane can show volume, Wilder RSI, MACD or be hidden entirely.
- Analysis is hidden by default. Trend & location (Murphy) draws EMA and Bollinger Bands and names the last-bar setup on the chart. Candlestick patterns marks the candles that formed them.
- Recorded markets have a replay cursor, previous/next bar and end controls. Indicators and higher-timeframe aggregates use only the selected prefix.
- Export contains the current interval's candles through the replay cursor, source information and the input hash, not orders or account data.
The public root renders MarketWorkspace.tsx. Older local research integration
files are excluded from this publication. It does not render the old inspector,
Scenario Lab, account metrics or localhost polling.
public/recordings/catalog.json lists ten authentic hourly market datasets:
gold, silver, WTI oil, S&P 500, Nasdaq 100, BTC, ETH, SOL, HYPE and SPCX.
They are Polymarket Perps contracts, not spot gold, cash indices or equity
ownership. The original market-data recordings were downloaded read-only from
the owner's Google Cloud Storage on 28 August 2026. The chart does not expose
bucket access or credentials and does not query the private bucket at runtime.
scripts/export_recordings.py converts a supplied directory of Parquet inputs
into the public OHLC-only files. Each entry records the SHA-256 of its input,
date range, count and number of gaps. The last captured candle is conservatively
omitted because it may still have been forming when recorded. Current archives
end on 21–22 August 2026, depending on market. They are not a live feed.
Only complete UTC-aligned groups become 4h/daily candles. Incomplete periods and missing hours are omitted, never interpolated. Daily candles are complete 24-hour aggregates, not official settlement prices. The last available closed bar can be older than the replay cursor when an aggregation has gaps.
EMA is seeded with a full-window SMA. Bollinger Bands use population standard deviation. RSI uses Wilder smoothing. MACD uses SMA-seeded EMAs, so its warm-up may differ from vendors using more history or a first-value seed.
Price and EMA ordering is a small descriptive summary inspired by Murphy's reading sequence; it is not an implementation of an entire trading framework. Pattern and pivot thresholds are explicitly uncalibrated. No win probabilities, entry recommendations or model performance are manufactured.
Indicator reference definitions: RSI, MACD, Bollinger Bands.
UI references: TradingView Supercharts, Koyfin views and Lightweight Charts panes. The existing Lightweight Charts dependency and attribution are retained.
Use the existing Node environment and lockfile. npm run dev starts Next;
For a fresh clone, use Node 22.13 or newer and run npm ci first.
npm test runs mechanics and archive-validation tests; npm run test:build
also produces the production build. The ingester's offline tests run with
python -m unittest discover -s ingest/tests -t .; the PostgreSQL checks in
ingest/tests/test_persistence.py are skipped unless DATABASE_URL is set.
Deployment uses the existing linked Vercel project. Keep the raw downloads outside this checkout; public JSON is generated
data, not a hand-edited fixture. Do not run any trading service to test this UI.
The browser requests GET /api/markets/BTC (also ETH and SOL). The Next.js
service validates closed candles from a fixed Hyperliquid endpoint. Other
symbols return 400; a total upstream failure returns 503. Partial success names
the unavailable intervals and is retried on the next request. There is no
arbitrary URL proxy, credential input, order endpoint or trading account.
Simultaneous requests for the same market share upstream work. Complete
snapshots are cached briefly within one process, bounded to the three supported
markets. This is not a distributed cache or database. The response includes
the snapshot's asOf; X-Snapshot-Cache identifies reuse. Server-Timing
measures this handler's elapsed wall time, not exchange or browser latency.
Timeout and cache settings are marked as uncalibrated engineering choices.
GET /api/health checks process readiness, not exchange availability.
To run the same app in Docker:
docker build -t pattern-forge .
docker run --rm -p 127.0.0.1:3000:3000 pattern-forgeThe multi-stage image runs as a non-root user and excludes local environment files. CI builds that image, runs tests during the build, starts it, and checks readiness, the page and an invalid-market request. A second job starts a real PostgreSQL service, ingests the recorded payload twice, reads it back from a separate process, restarts the database and reads it again. Vercel remains the public deployment; it does not use this Docker image and does not serve stored candles. No new paid service is required.
The quote feed uses the documented allMids subscription. It supplies a venue mid-price, not a last-trade price. Its displayed timestamp is local receipt time, not a latency measurement. Heartbeats, capped retries, malformed-message rejection and cleanup are covered by synthetic tests.
I kept streaming quotes separate from candle snapshots: they answer different questions, and mixing a current quote into an archived candle would make replay misleading. I used an API to centralize validation and share duplicate requests, not to hide an exchange URL behind an unnecessary microservice.
Snapshots were previously held in one process's memory, so a restart lost them
and the workspace could show nothing when the endpoint was unreachable. A small
Python ingester now writes closed candles to PostgreSQL, and the app reads them
back through GET /api/stored/BTC (also ETH and SOL). The chart, the replay
cursor and the timeframe comparison are unchanged: a stored market is just
another source in the selector, listed under Stored candles.
# Database, ingester and app together. The named volume keeps the candles.
docker compose up --build
# One pass against the live endpoint, into an existing database.
DATABASE_URL=postgresql://... python -m ingest.ingest --symbols BTC --intervals 1h,4h
# Offline: load the recorded payload instead of calling the endpoint.
DATABASE_URL=postgresql://... python -m ingest.ingest \
--fixture ingest/fixtures/public-candles.json --now 1788220800000 --symbols BTC --intervals 1hThe ingester reads the same documented
candleSnapshot endpoint
the browser uses, and applies the same boundary rules in ingest/candles.py:
a still-forming candle, a candle that does not span its interval, a non-numeric
price and inconsistent OHLC bounds are all refused before any write. Rows
sharing an opening timestamp with different values are treated as a conflict,
not a duplicate, and reject the whole response.
(symbol, interval, open_time) is the primary key, so re-reading the same
window cannot create a second row. The upsert suppresses no-op updates, which
means each pass reports how many candles were inserted, updated and left
unchanged; a corrected candle updates in place instead of appearing twice.
Every attempt, including a failure, is recorded in ingest_runs with its own
fetch_ms and write_ms, measured around the fetch and write calls. Reads
report Server-Timing: query;dur= for the handler.
Those numbers are wall-clock measurements between explicit start and end points in one process. They are not throughput, database server time, network time or exchange-to-screen latency, and no synchronized clocks are involved.
Limits: closed candles only, so this is a persisted snapshot history and not a tick feed. One ingester process, no retention, partitioning or backfill policy, and reads are bounded to 300 bars per interval. An empty store answers 404 with an instruction rather than an empty chart, and a database that is unreachable answers 503 rather than silently falling back to the live endpoint. The duplicate and restart behaviour is checked against a real PostgreSQL server in CI, using the recorded payload; the counts describe one statement's view, not concurrent ingesters.
Choose recorded Gold, move the replay cursor back, then enable timeframe comparison. Move forward and observe which complete higher-timeframe candles become available. Export the prefix and compare its final timestamp with the cursor. This investigates a recorded sequence without hindsight from future candles; it is not a profitable strategy or a validated trading signal.
Public snapshots retain validated intervals when another request fails. Missing intervals are labeled and disabled; the chart selects an available interval instead of showing it under the failed interval's name. Refresh retries all intervals. Tests cover HTTP failures, malformed data, total outages and partial timeouts with synthetic responses. No uptime, customer-adoption or operational performance claim is made. Code is published for inspection; no additional reuse license is granted. Third-party licenses and chart attribution remain.