Live since May 2026 · snapshot 2026-06-04

How a live AI trade-signal system actually works.

Drishti is a public, always-on system that asks a frontier AI model for a trade idea on ten crypto and metal perpetual futures — every 15 minutes, around the clock. This page opens the box: the loop, the prompt, the experiments, and the anatomy of a single signal.

Assets tracked
10
7 crypto · 2 gold · 1 silver
Cycle
15m
around the clock
Prompt
v23
Claude Opus 4.8
Resolved win rate
58.3%
49W · 35L · 24E
Avg reward:risk
1.30
across closed signals
Signals to date
118
10 open now

Illustrative only — not financial advice. Drishti generates and tracks paper signals to study AI decision-making in markets. Nothing here is a recommendation to trade.

The loop

One cycle, every fifteen minutes

Drishti never closes. A single systemd loop on a small VM runs three phases in order, then sleeps to the next quarter-hour and does it again — about a hundred times a day.

1Track & Close2Decide3Publish15MIN CYCLE
Tracking open signals
1. Track & Closetracker.py

Walk the tape on every open signal; close the ones that resolved.

2. Decidegenerator.py

For every asset without an open signal, build features → ask the model → gate → save.

3. Publishpublisher.py

Write the JSON the website reads, then git-push so Vercel redeploys.

Phase by phase

What actually happens each cycle

Close what resolved, decide on what's open, publish the result. The middle phase — the decision — is where the AI lives.

1

Track & Close

tracker.py

For each open signal, the tracker replays 1-minute candles from when it was created. If price touched take-profit first it closes as a win; if it hit the stop first, a loss; if neither within 24 hours, it expires (counted as neither). On a same-bar wick the stop wins — a deliberately pessimistic tie-break.

First-touch detection on 1m bars, pessimistic on gaps24h hard expiry — expired ≠ lossPosts tp1_hit / sl_hit / expired to Slack
2

Decide

generator.py

The heart of the system. For each of the ten assets that has no open signal, it assembles ~30 engineered features, hands them to Claude Opus 4.8 with a forced-tool-call schema at temperature 0, runs the answer through a risk gate, and persists it. A directional call posts to Slack immediately.

~30 features per asset across 12 data sourcesForced tool-call, temperature 0, one idea per assetno_trade is a valid, encouraged answer
Inside one asset's decision
Build features

Fetch + compute ~30 signals: multi-timeframe technicals, funding, open interest, options skew, liquidations, macro overlay, cross-asset correlation, and per-asset daily-move stats. A failed feed renders n/a rather than crashing the cycle.

Ask the model

Claude Opus 4.8 receives the system prompt + the per-asset feature block and must answer through a JSON tool schema (DrishtiDecision). Temperature 0 for determinism; one open idea per asset is enforced before the call.

Risk gate

A thin, mechanical check — not a strategy. It rejects anything whose levels are on the wrong side of entry, and enforces a reward-to-risk floor of ≥ 1. Everything else the model decided stands.

Persist & notify

Valid signals are written to DuckDB with their full feature hash and cost, then posted to Slack. The whole per-asset path is fail-soft: one bad asset never sinks the cycle.

3

Publish

publisher.py

The publisher serializes the live state to JSON (signals, stats, the prompt itself, cost, health), rebases on the latest main, commits, and pushes. Vercel sees the push and redeploys the public site — about 96 times a day, just under the platform's daily cap.

Owns signals.json, prompt.json, site_meta.json …git pull --rebase --autostash → commit → pushContent-gated: no-op commits are skipped
A signal

Anatomy of a single call

This is a real closed signal from the snapshot. Every field is something the model chose — or something the system measured afterward.

AAVE
✓ TP1 hit · +2.84%v21

Every signal is a single structured object the model fills in. Tap any part to see what it means.

direction

Long, short, or no_trade. The model is told to pick the side the evidence supports — never to favour a direction a priori.

Before the model is asked anything, the generator assembles ~36 features per asset across 11 categories. Tap a category to see what's inside.

EMA stack (5m·15m·1h·4h)RSIATR %Bollinger widthWilder ADX-14 + ADX-20MTF alignment score
structural n/aoperational n/a

A missing feature isn't always a problem. Structural absence is by design — gold tokens have no options chain — and the model is told not to dock confidence for it. Operational absence is a feed that should be there but failed; that one is a quality signal, and the model leans toward caution.

Three ways a signal closes

The tracker walks 1-minute candles until price touches a level — or 24 hours pass.

Take-profitEntryStop-loss
WINTake-profit hit first.Counts toward win rate.
The prompt

How the instructions evolved

The signal quality lives almost entirely in the prompt. It has been through three deliberate generations — each one measurable against the last.

Philosophy

Hand the model the one fact it lacked — how far each asset typically travels in a day — as data, not instruction.

Why it changed

Live data showed ~25% of signals expiring and oversized (>3%) targets winning only ~14%. Giving the model a direct read on reachable spans should shrink both problems — and being data-only keeps the change measurable against v22.

What changed
  • Per-asset <daily_move> data block: 7d/30d avg range, p90 busy-day, net move
  • DATA ONLY — no new instruction, no new risk-gate cap
  • RR floor unchanged; a fresh v23 cohort opens here
Rigor

Treating it like an experiment

A live AI system is only as trustworthy as the way you measure it. Drishti keeps a clean north star, runs controlled A/Bs, grades its own confidence, and alerts on silent failure.

North star: resolved win rate

win = TP1 hit first · loss = SL hit first · expired (neither in 24h) is excluded. Win rate = won ÷ (won + lost). A clean, honest denominator — expiries don't pad the number either way.

Closed signals
58.3%win rate
Won49
Lost35
Expired24
won ÷ (won + lost) · expired excluded
Same features, two prompts, side by side
identical
~30 features
per asset, per cycle
Production
history OFF

no per-asset track record, no past-signal attribution

Shadow control
history ON

the previous prompt, run in parallel for comparison

Production currently runs history-free — the model sees no record of its own past signals — to test whether that record was making it gun-shy. A parallel shadow arm runs the history-on prompt on the same features, labelled v21-histon, so the two can be compared under identical market conditions.

Score the model's calibration

The model reports its own outcome odds with every signal. After it closes, those odds are graded by a multiclass Brier score — squared error against what actually happened. Lower is better. Drag the odds, pick the real outcome.

The model's odds
Take-profit55%
Stop-loss30%
Expire15%
Odds auto-normalise to 100%.
What actually happened
Model Brier
0.315
Naive base-rate
0.000
Worse than just guessing the base rate — no skill on this call.

What can go wrong (and how you'd know)

A migration once silently deleted every new-prompt and shadow row on each cycle, so the A/B never accumulated. It looked like an empty table, not an error. The fix removed the destructive delete; the lesson became three data-integrity Slack alerts — count-drop, stale, and budget — that now catch silent pipeline failure the way a missing chart never could.

Infra

The plumbing that keeps it honest

Twelve data feeds, a fleet of scheduled jobs, cost tracking down to the cent, and a publisher that keeps the public site — and its own documentation — in sync with reality.

12 live data sources, fused every cycle
Delta Exchange
prices · candles · universe
Binance
funding · OI · liquidations · positioning
Deribit
options skew · IV · DVOL (BTC/ETH)
CoinGlass
OI-weighted funding · aggregated liqs
Bybit
cross-venue funding · order book
Coinalyze
aggregated cross-exchange derivatives
yfinance
DXY · SPX · VIX · US10Y · Gold
CoinGecko
BTC dominance · total mcap
DefiLlama
stablecoin supply
Alternative.me
Fear & Greed index
Farside
BTC/ETH spot ETF flows
OpenRouter
model gateway → Claude Opus 4.8
Cost observability
Today vs daily limit$38.63 / $100
38.6% of limit
Spend by experiment arm
primary
shadow
$6.63 primary$4.34 shadow (39.6%)
Spend by model
claude-4.7-opus-20260416$5.27 · 86
claude-4.8-opus-20260528$1.37 · 32
Scheduled jobs on the VM
drishti.serviceevery 15 minThe main loop — track → decide → publish.
autodeployevery 1 minPulls code-only pushes to main and restarts the service.
monitorevery 5 minProbes cycle health; alerts if the loop goes stale.
etf-flowsdailyIngests BTC/ETH spot-ETF net flows.
Data-integrity alerts

Born from a bug that silently deleted data — silent failure should never look like “everything’s fine.”

signal_count_drop

A cycle emits far fewer signals than its recent baseline — the canary for a broken generator.

signals_stale

The published signals.json stops refreshing while the model isn't quota-blocked.

budget_warning

Cumulative spend nears the daily model-gateway limit.

Resilience & self-healing
Publish self-heal

A stuck git repo auto-recovers — rebase-abort, reset-hard, retry — instead of going dark.

Watchdog + crash-loop guard

A systemd watchdog restarts a hung process; a start-limit stops restart thrash if the process keeps crashing.

Stall auto-restart

An on-VM monitor restarts the service when a cycle or publish stall is detected.

Off-VM dead-man's switch

A heartbeat pages even if the whole VM or network dies — silence itself triggers the alert.

Always-on ops paging

Outage alerts bypass the trade-only Slack filter so they always deliver, regardless of verbosity settings.

Self-documenting on every prompt change

Bump the prompt version or swap the model and the publisher regenerates the machine-readable surfaces automatically — the llms.txt family, site metadata, and an auto changelog entry — all gated on actual content change so nothing churns. The public 'System' and 'Changelog' tabs read those files directly, so the site explains its current self without a human editing copy.

Edit prompt / model in code
Publisher detects the version delta
Regenerates site_meta + llms.txt + changelog
Vercel redeploys — docs match reality