Jump to a section
Overview
ChartX is a game for learning to read charts. A player sees the last hundred one-minute candles of a real instrument at a moment in the past, plus two price limits, one above and one below the current price. The question is which limit the price touches first. There is a solo exam mode and a head-to-head mode, an in-game economy, progression, competitions, support and identity checks.
I own the product and wrote the whole backend; every commit in the backend repository is mine. The web frontend began as another engineer's Vue app, which I later rewrote in Nuxt. Much of the code was written with AI coding agents working from my designs and reviewed by me.
Try the question generator
Play questions made by the ChartX generator
Real output of the actual code, recorded once and replayed here.
EURUSD · 2019-02-04 03:20 UTC · question 1 of 12. Candlestick chart of the last 100 one-minute candles, with an up limit at 1.14522 and a down limit at 1.14507.
Score 0/0
Which limit does the price touch first?
The questions above were produced by running ChartX's generator code on real market data, outside the platform, and saving the results. The page replays them; it does not call the generator live.
Architecture
Thirteen Go services (about 159,000 lines, with about 220 test files), each built and deployed on its own:
| Area | Services |
|---|---|
| Games | Solo exams, head-to-head games, competitions |
| Questions and data | Question generator, market-data service (TimescaleDB) |
| Players and money | Accounts and authentication, in-game economy, progression, risk |
| Operations | Admin, notifications, support |
A Python service handles identity-document checks. Services that change shared state write their events to an outbox table in the same transaction and a relay publishes them to Kafka, so a crash can never leave a database change without its event. PgBouncer sits in front of PostgreSQL, several services use Redis, and candle history lives in TimescaleDB.
Each service has its own GitHub Actions workflow that builds it and deploys it to Docker Swarm from a self-hosted runner. Logs go to Loki and are read in Grafana.
Question generator
- Pick a window. A random moment with 100 visible candles and up to 100 hidden ones after it. Windows with gaps, impossible prices or no volume are rejected.
- Set the limits. The distance is a multiple of ATR (average true range, a volatility measure) chosen from the target difficulty and the current volatility. Limits are pulled inside the nearest swing high or low, then nudged by a small random amount so players cannot learn exact distances.
- Score difficulty. Six weighted factors: volatility, trend strength, distance, expected time to a hit, closeness to support or resistance, and volume.
- Check against what happened. The hidden candles decide the answer. If neither limit is hit, or both are hit in the same candle, or the hit comes too soon or too late, the limits are adjusted and checked again.
- Keep the best candidate. Up to 12 candidates per window are scored for closeness to the target difficulty, a low estimated win rate and a hit near the middle of the hidden window.
Status
The platform ran end to end in a self-hosted development environment and never launched. That environment is offline now. Development ran from January 2025 to May 2026.
Limitations
- Not launched, so there are no usage figures, and nothing here has served real players.
- The deployment workflows build and deploy but do not run the tests.
- The preview uses the generator's default settings and its fixed fallback for win probability, because it ran without the production database.
- The preview's market data is Dukascopy's (EURUSD, February 2019; BTCUSD, February 2023).
Evidence index
Every claim this case study relies on, rendered from the evidence manifests.
ChartX backend, 13 Go services
Wrote the whole ChartX backend alone: 13 Go services (about 159,000 lines) on PostgreSQL and TimescaleDB, Redis and Kafka, where services that change shared state publish through a transactional outbox. Each service deploys to Docker Swarm through its own GitHub Actions workflow on self-hosted runners, with Loki and Grafana for logs.
Question generator for chart-reading exams
Designed the generator that picks a playable window of historical candles and sets an up and a down price limit from ATR volatility, nearby support and resistance, a little random noise and a target difficulty, then keeps the best of up to 12 candidates. The playable preview on this site replays its real output.