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

StatusMeaning
activeCurrently tracked. Live price feeds in to update highestPct and lowestPct.
completedTarget was reached. Still surfaced for a window before being archived.
removedArchived. Carries a removalReason.
removalReasonTrigger
target_reachedLive price hit the target on the correct side.
stop_loss_breachedLive price hit the stop on the wrong side.
prediction_changedA new generation slot picked the opposite direction for the same asset.
expiredThe 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 / lowestPct from the running max/min since trackedSince.
  • Flips status to completed on target hit.
  • Flips status to removed with the right removalReason on stop hit or expiry.
  • Invalidates the in-memory activeSignalsCache so the next API read sees the change.

Surfacing

SurfacePath
Web app — Discover viewartifacts/true-finance-ai/src/components/discover/SignalsView.tsx
Admin dashboard — generation statusartifacts/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.
Signals are model output, not investment advice

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.

For Developers

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 high label 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_slot flag is logged.
  • Reasoning may be missing in your locale at first request — the on-demand /why endpoint will fill it in.

See also

Last updated: