Add implementation plan for /ES multi-timeframe confluence chart

Planning-only commit: no application code yet.

The plan specifies a realtime /ES chart that derives moving averages and
trendlines across multiple timeframes, projects them onto one chart in a
shared (time, price) plane, and alerts when levels from different
timeframes converge.

Key findings that shaped it, all verified against source rather than
assumed:

- Schwab streams realtime futures fine (CHART_FUTURES, LEVEL_ONE_FUTURES)
  but provides no futures price *history* at all. An account does not
  change this; it is an API-surface limit.
- Yahoo's chart endpoint needs no key and has exactly what Schwab lacks:
  ~730d of hourly ES=F (~750 sessions), enough to warm a 200DMA from
  startup. So it serves as both the no-keys dev source and the history
  seeder, behind one MarketDataSource protocol.
- Yahoo anchors daily bars to midnight ET while the CME session runs
  18:00-17:00 ET, so daily bars are built from hourly using our own
  session rules instead.
- Lightweight Charts v5 replaced addCandlestickSeries() with
  addSeries(CandlestickSeries, ...); most tutorials online are v4.

Build order defers judgment-heavy work: moving averages first (fully
deterministic), then confluence scoring, then hand-drawn trendlines.
Automatic trendline detection comes last, tuned against the hand-drawn
lines as ground truth.

Includes a real trimmed Yahoo response as a test fixture; it contains a
null in the OHLC arrays, which is the parsing case that needs handling.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Chris Amow 2026-08-09 20:31:40 -05:00
parent 3cc682d63a
commit 8e50d5cbc2
3 changed files with 1518 additions and 0 deletions

View file

@ -5,6 +5,11 @@ FastAPI backend + Vue 3 (from CDN, no build step) served at
Currently a placeholder: the frontend calls `/api/hello` and prints the JSON. Currently a placeholder: the frontend calls `/api/hello` and prints the JSON.
**Where this is going:** a realtime `/ES` chart that derives trendlines and moving
averages across many timeframes and alerts when they converge. The full spec lives in
[`docs/IMPLEMENTATION_PLAN.md`](docs/IMPLEMENTATION_PLAN.md) — read it before writing
code; it records decisions and verified API facts that are expensive to rediscover.
## Local development ## Local development
```bash ```bash

1145
docs/IMPLEMENTATION_PLAN.md Normal file

File diff suppressed because it is too large Load diff

368
tests/fixtures/yahoo_es_1h.json vendored Normal file
View file

@ -0,0 +1,368 @@
{
"chart": {
"result": [
{
"meta": {
"currency": "USD",
"symbol": "ES=F",
"exchangeName": "CME",
"fullExchangeName": "CME",
"instrumentType": "FUTURE",
"firstTradeDate": 969249600,
"regularMarketTime": 1786324854,
"hasPrePostMarketData": false,
"gmtoffset": -14400,
"timezone": "EDT",
"exchangeTimezoneName": "America/New_York",
"regularMarketPrice": 7778.0,
"fiftyTwoWeekHigh": 7820.25,
"fiftyTwoWeekLow": 6353.25,
"regularMarketDayHigh": 7779.25,
"regularMarketDayLow": 7763.0,
"regularMarketVolume": 25260,
"shortName": "E-Mini S&P 500 Sep 26",
"chartPreviousClose": 7765.5,
"previousClose": 7779.75,
"scale": 3,
"priceHint": 2,
"currentTradingPeriod": {
"pre": {
"timezone": "EDT",
"end": 1786248000,
"start": 1786248000,
"gmtoffset": -14400
},
"regular": {
"timezone": "EDT",
"end": 1786334340,
"start": 1786248000,
"gmtoffset": -14400
},
"post": {
"timezone": "EDT",
"end": 1786334340,
"start": 1786334340,
"gmtoffset": -14400
}
},
"tradingPeriods": [
[
{
"timezone": "EDT",
"end": 1785902340,
"start": 1785816000,
"gmtoffset": -14400
}
],
[
{
"timezone": "EDT",
"end": 1785988740,
"start": 1785902400,
"gmtoffset": -14400
}
],
[
{
"timezone": "EDT",
"end": 1786075140,
"start": 1785988800,
"gmtoffset": -14400
}
],
[
{
"timezone": "EDT",
"end": 1786161540,
"start": 1786075200,
"gmtoffset": -14400
}
],
[
{
"timezone": "EDT",
"end": 1786334340,
"start": 1786248000,
"gmtoffset": -14400
}
]
],
"dataGranularity": "1h",
"range": "5d",
"validRanges": [
"1d",
"5d",
"1mo",
"3mo",
"6mo",
"1y",
"2y",
"5y",
"10y",
"ytd",
"max"
]
},
"timestamp": [
1785816000,
1785819600,
1785823200,
1785826800,
1785830400,
1785834000,
1785837600,
1785841200,
1785844800,
1785848400,
1785852000,
1785855600,
1785859200,
1785862800,
1785866400,
1785870000,
1785873600,
1785877200,
1785880800,
1785884400,
1785888000,
1785891600,
1785895200,
1785898800,
1785902400,
1785906000,
1785909600,
1785913200,
1785916800,
1785920400,
1785924000,
1785927600,
1785931200,
1785934800,
1785938400,
1785942000,
1785945600,
1785949200,
1785952800,
1785956400
],
"indicators": {
"quote": [
{
"open": [
7644.0,
7645.25,
7647.75,
7644.5,
7643.5,
7637.0,
7637.75,
7643.25,
7654.5,
7651.0,
7685.25,
7715.5,
7736.5,
7755.25,
7768.25,
7773.0,
7763.75,
null,
7772.0,
7780.75,
7777.25,
7784.25,
7784.5,
7786.75,
7792.75,
7791.25,
7798.5,
7798.25,
7792.75,
7784.25,
7788.5,
7797.5,
7798.0,
7799.5,
7818.75,
7786.5,
7757.75,
7769.75,
7767.0,
7765.5
],
"close": [
7645.5,
7647.5,
7644.5,
7643.5,
7637.25,
7637.5,
7643.0,
7654.5,
7651.0,
7685.5,
7715.25,
7736.5,
7755.25,
7768.25,
7773.0,
7764.75,
7774.5,
null,
7780.75,
7777.5,
7784.25,
7784.25,
7786.75,
7792.0,
7791.25,
7798.75,
7798.0,
7793.0,
7784.25,
7788.5,
7797.5,
7798.0,
7799.5,
7819.25,
7786.25,
7758.0,
7769.5,
7767.0,
7765.5,
7748.25
],
"low": [
7641.75,
7642.75,
7642.75,
7641.5,
7635.25,
7631.75,
7635.5,
7641.5,
7651.0,
7649.25,
7682.5,
7714.0,
7733.25,
7754.25,
7763.5,
7761.25,
7761.25,
null,
7771.0,
7775.75,
7776.0,
7777.75,
7783.0,
7786.5,
7788.25,
7790.5,
7791.0,
7788.0,
7780.25,
7782.5,
7787.5,
7792.5,
7795.5,
7795.0,
7776.0,
7754.5,
7750.5,
7761.75,
7763.25,
7745.75
],
"volume": [
0,
5511,
7319,
14850,
18165,
14744,
19853,
29305,
21498,
205567,
243043,
187171,
123068,
152916,
128635,
293219,
80121,
null,
0,
6231,
8543,
8701,
8414,
7996,
4935,
6946,
9271,
15543,
21633,
12652,
12910,
14971,
28403,
203055,
265756,
189580,
148224,
81542,
72103,
207321
],
"high": [
7646.5,
7649.5,
7650.0,
7648.0,
7645.75,
7638.5,
7647.75,
7657.75,
7656.25,
7686.5,
7716.5,
7741.25,
7755.75,
7783.75,
7777.25,
7786.0,
7781.25,
null,
7783.25,
7782.75,
7785.5,
7786.0,
7788.75,
7792.5,
7792.75,
7800.0,
7799.5,
7799.5,
7796.75,
7792.0,
7798.0,
7799.75,
7805.25,
7820.25,
7818.75,
7791.5,
7773.75,
7773.0,
7774.5,
7771.5
]
}
]
}
}
],
"error": null
}
}