Signals
Six AI-generated trade ideas every six hours — three bullish, three bearish — tracked live to target, stop, or expiry.
What a signal is
A signal is a structured, time-stamped trade idea: an asset, a direction, an entry, a target, a stop, a confidence, and a written thesis. Signals are never executed automatically. They are inputs to a user’s own decision.
Generation cadence
The platform generates a fresh batch every six hours at 00:00, 06:00, 12:00, and 18:00 UTC. Each batch contains exactly three bullish and three bearish picks, selected by Claude Haiku 4.5 from the candidate set.
Candidate set
The candidate set is pulled from CoinGecko Pro (/coins/markets):
- Top 100 by market cap.
- At least 1% absolute price move in the last 24 hours.
The model is prompted to pick six tickers from this set, set entry / target / stop prices that are internally consistent (target > entry > stop for longs, inverse for shorts), and assign a confidence label of high, medium, or low.
Reasoning
For every selected signal, a second LLM call generates the long-form thesis (summaryLong) and a short tagline (summaryShort, ≤140 chars). The thesis is stored per-locale in a JSON map so the same signal can be served in any of the supported languages without re-translation at request time.
Signal schema
{
"id": "uuid",
"symbol": "SOL",
"coingeckoId": "solana",
"name": "Solana",
"image": "https://…/solana.png",
"direction": "bullish",
"confidence": "high",
"entry": 178.40,
"target": 195.00,
"stopLoss": 169.20,
"initialPrice": 178.40,
"summaryShort": "SOL reclaiming 180 with funding flipping; clean structure into a target.",
"summaryLong": "…",
"status": "active",
"highestPct": 4.8,
"lowestPct": -1.2,
"trackedSince": "2026-05-02T00:00:00Z",
"removedAt": null,
"priceAtRemoval": null,
"removalReason": null,
"generationDay": "2026-05-02T00",
"reasoning": {
"en": { "summary": "…", "generatedAt": "2026-05-02T00:01:12Z" },
"es": { "summary": "…", "generatedAt": "2026-05-02T00:01:18Z" }
}
}
Lifecycle
| Status | Meaning |
|---|---|
active | Currently tracked. Live price feeds in to update highestPct and lowestPct. |
completed | Target was reached. Still surfaced for a window before being archived. |
removed | Archived. Carries a removalReason. |
removalReason | Trigger |
|---|---|
target_reached | Live price hit the target on the correct side. |
stop_loss_breached | Live price hit the stop on the wrong side. |
prediction_changed | A new generation slot picked the opposite direction for the same asset. |
expired | The signal aged out after SIGNALS_EXPIRY_HOURS (default 7 days). |
Slot semantics
Each generation run claims a slot (signals_generation_runs table) keyed by YYYY-MM-DDTHH. The claim is exclusive, so a fleet of replicas cannot double-generate. If the same asset is picked again in the same direction in the next slot, the existing record is carried over (timestamp bumped) instead of being duplicated. If the next slot picks the opposite direction, the previous signal is closed with prediction_changed and a new one is opened.
Live tracking
A background loop refreshes prices every 5 minutes and:
- Updates
highestPct/lowestPctfrom the running max/min sincetrackedSince. - Flips status to
completedon target hit. - Flips status to
removedwith the rightremovalReasonon stop hit or expiry. - Invalidates the in-memory
activeSignalsCacheso the next API read sees the change.
Surfacing
| Surface | Path |
|---|---|
| Web app — Discover view | artifacts/true-finance-ai/src/components/discover/SignalsView.tsx |
| Admin dashboard — generation status | artifacts/true-admin/src/components/DailySignalsTab.tsx |
| Activity stream (SSE) | GET /api/v1/activity/stream — broadcasts signal.removed and other transitions on the feed:{wallet} channel. See Realtime API. |
The confidence label is a self-report from the model, not a calibrated probability. Entry / target / stop prices are decision aids, not orders. Past highestPct is observed, not promised. Never size positions purely on a signal; always reconcile against your own thesis and risk budget.
Endpoints
GET /api/signals/active Returns the current active and completed signals. Cached in-memory for 60 seconds.
GET /api/signals/history Paginated archive of removed signals. Query: limit, cursor, optional direction and symbol filters.
GET /api/signals/{id} Returns a single signal.
GET /api/signals/{id}/why Returns the AI-generated thesis for the signal in the requested locale (query: lang). Generates on demand if missing for that locale.
Safety, limits, failure modes
- Confidence is ordinal, not cardinal. A
highlabel is not a probability; it is the model’s own ranking among the picks in that slot. - Entry can be missed. The signal price snapshot is taken at generation; live price can already be past entry by the time the user sees it.
- Stop is a level, not an order. The platform does not place a stop on the user’s behalf. Use True Agents if you want enforcement.
- Slot starvation. If CoinGecko returns fewer than three movers in either direction, the slot is generated with a partial set and a
partial_slotflag is logged. - Reasoning may be missing in your locale at first request — the on-demand
/whyendpoint will fill it in.
See also
- Event Catalyst — catalyst feed used for context in the thesis.
- True Agents — turn a signal into an enforced strategy.
- Realtime API — subscribe to signal transitions over SSE.