Portfolio — Case Study
AI Trading · Market AnalysisFour timeframes must confirm. Then — and only then — a signal fires.
A personal AI-powered market intelligence platform: a Python scanner runs every 15 minutes during NYSE hours across a curated watchlist, requires all 4 timeframes to confirm before raising a signal, auto-tracks outcomes at 1h/4h/next-day checkpoints, and generates Claude-powered morning briefs from live market data and news. Everything is presented through a secure multi-user React 18 dashboard deployed on Google Cloud Run.
4
Timeframes Must Confirm
Claude
Morning Brief Engine
FastAPI
Python Backend
Cloud Run
Auto-Scaling Infra
The Challenge
Most trading scanners generate noise — they alert on any pattern that appears on a single timeframe, regardless of whether the higher timeframe confirms it. The goal was to build a scanner that only surfaces setups with genuine conviction: the Daily must set the direction, the 1H must confirm the trend, the 15M must show the setup forming, and the 5M must show the entry trigger — all four in sequence. The scanner fetches OHLCV price bars from the Twelve Data API and computes all indicators in Python: EMA-based MACD (12/26/9) and Stochastics (14/3).
Single-timeframe alerts are trivially easy to produce but almost always wrong because they ignore context. A 5-minute setup against the daily trend is not a setup — it is noise. The platform was designed from first principles around the requirement that every signal must represent a case where four independent timeframe conditions are all satisfied in sequence. At the Daily and 1H gates both conditions must pass simultaneously; at the 15M and 5M gates, MACD or Stochastics qualifying is sufficient.
Combined with Claude AI morning briefs that pull current market narrative via Brave Search API, the platform surfaces setups in context. Signals auto-generate outcome checkpoints at 1h, 4h, and next-day, resolving each to WIN/LOSS/FLAT against live prices so calibration can be measured over time.
What Was Delivered
Technology Stack
AI
Backend
Infrastructure
Market Data
Frontend
Scanner Logic
Key Features
A signal only fires when all four timeframes pass in sequence. Daily: close above SMA(50) — overall uptrend must be intact. 1H: close above SMA(20) AND MACD histogram turning positive — intermediate trend confirmed. 15M: MACD histogram turning positive OR Stochastics recovering from oversold (K < 45) — setup visible. 5M: same conditions — entry trigger confirmed. Any gate failure stops evaluation immediately and no signal is raised. Indicators are computed in Python from OHLCV bars fetched via the Twelve Data API — not sourced from TradingView.
Each morning the system fetches daily OHLCV for five index instruments (NQ Futures, ES Futures, QQQ, DXY, VIX) from Twelve Data, computes MACD, Stochastics, and SMA levels, runs a full watchlist scan, and fetches news via Brave Search API — three macro queries plus one per watchlist symbol. All of this is assembled into a structured prompt sent to Claude (claude-sonnet-4-6), which returns a 7-section brief: market summary, NQ analysis, key levels, macro context, session bias, scan analysis, and watch list. Briefs are stored in PostgreSQL and viewable in the Brief tab with full history.
The scanner (src/scanner.py) runs on APScheduler every 15 minutes during NYSE market hours (Monday to Friday, 09:30–16:00 ET). For each instrument in the watchlist, it fetches OHLCV bars from Twelve Data across all four timeframes, computes EMA-based MACD (12/26/9) and Stochastics (14/3) in Python, and evaluates the 4-gate sequence. Confirmed signals are written to PostgreSQL with full indicator context — stoch K/D, MACD histogram, SMA levels, and price at signal time. A background thread runs continuously to resolve pending outcome rows against live price data.
The Twelve Data API is the sole market data source. It supplies OHLCV price bars at all four timeframes for the scanner, real-time quote snapshots for the watchlist dashboard, and a symbol search endpoint covering stocks, ETFs, futures, forex, crypto, and indices. The morning brief engine uses a separate Twelve Data call for index instrument OHLCV. All indicator calculations — MACD, Stochastics, SMA — are performed in Python against the raw bars; there is no third-party charting or indicator service involved.
The FastAPI backend runs as a Docker container on Google Cloud Run at port 8080, scaling to zero outside market hours and up on demand during active trading sessions. Production data lives in PostgreSQL on GCP Cloud SQL; the same codebase uses SQLite in development with no environment-specific code paths. A single gcloud run deploy command pushes the latest image. Scanner configuration — MACD periods, stochastic thresholds — is stored in PostgreSQL and loaded at startup, so adjustments made in the Settings tab persist across redeploys.
The React 18 + Vite 5 SPA has five tabs: Dashboard (live watchlist price grid with symbol search, scanner countdown, market open/closed status), Signals (4TF signal feed with per-gate funnel panel showing how many instruments passed each gate in the last scan), Brief (today's morning brief with history), Stats (win rate, average P&L, best and worst signals by check type — 1h, 4h, next-day), and Settings (scanner config, language, profile, user management). The dashboard uses REST polling — signals every 10 seconds, watchlist every 30 seconds. Authentication is JWT-based with 10-hour tokens; admins invite new users via a 48-hour email token flow. Each user maintains their own named watchlists independently. The interface is available in 7 languages via react-i18next.
System Architecture
Every 15 minutes during NYSE hours, APScheduler triggers the Python scanner. For each instrument in the user's watchlist, the scanner fetches OHLCV bars from the Twelve Data API across four timeframes and evaluates the gate sequence in order: Daily SMA trend, 1H MACD momentum, 15M setup, 5M entry trigger. Any gate failure stops evaluation for that instrument — no signal is raised. Confirmed signals are written to PostgreSQL with their full indicator snapshot.
When a signal is confirmed, the system also creates three outcome rows — one for each resolution checkpoint (1h, 4h, next-day). A background thread runs continuously alongside the scanner, fetching the current price at each due checkpoint via Twelve Data and resolving the outcome to WIN, LOSS, or FLAT. These resolution results feed the Stats tab: win rate, average P&L, and the best-performing signal type across all three check windows.
At 09:00 each morning a separate scheduled job runs the brief engine: Twelve Data supplies OHLCV for five index instruments, Brave Search supplies macro news and per-symbol headlines, and Claude (claude-sonnet-4-6) synthesises a 7-section structured brief. The brief is stored in PostgreSQL and surfaced in the Brief tab alongside full brief history. The React dashboard polls the FastAPI backend via REST — no WebSocket, no push. The deployment is a single Docker container on Cloud Run that scales to zero between market sessions.
Twelve Data API (OHLCV bars · real-time quotes · symbol search)
▼
Python Scanner (src/scanner.py)
│ APScheduler: every 15 min, Mon–Fri 09:30–16:00 ET
│ 4-timeframe gate — evaluated in sequence, any fail stops scan:
│ Daily: close > SMA(50) uptrend intact
│ 1H: close > SMA(20) AND MACD histogram turning up
│ 15M: MACD turning up OR Stoch recovering (<45)
│ 5M: same as 15M entry trigger
▼
PostgreSQL on GCP Cloud SQL (prod) / SQLite (dev)
│ Signals · watchlists · scanner config · morning briefs
│ Per signal: 3 outcome rows auto-created (1h · 4h · next-day)
▼
Background thread: outcome resolver
│ Fetches live price at each checkpoint via Twelve Data
│ Resolves: WIN (>0.1%) · LOSS (<-0.1%) · FLAT
│
▼ 09:00 daily
Morning Brief Engine
│ Twelve Data: OHLCV for NQ · ES · QQQ · DXY · VIX
│ Brave Search API: 3 macro queries + 1 per watchlist symbol
│ Claude (claude-sonnet-4-6): 7-section structured brief
│ → stored in DB · viewable in dashboard with full history
▼
FastAPI + React 18 Dashboard (Google Cloud Run, scales to zero)
│ JWT auth (10h tokens) · invite flow (48h token email)
│ REST polling: signals 10s · watchlist 30s
│ 5 tabs: Dashboard · Signals · Brief · Stats · Settings
└── Scanner config persisted to DB — survives redeploys
What This Means In Practice
4
Timeframes Must All Confirm
15min
Scan Interval
Daily
AI Morning Brief
0
False Signals From Single Timeframe