Archived. IrrationalSignals is no longer a commercial product. This site is an engineering & product case study.
Build Case Study

An end-to-end ML product — and the decision to wind it down.

IrrationalSignals was an intraday trading-signals product for US equities: a statistical model behind a tiered, API-first SaaS. The work spanned the full stack — data pipeline, model, API, billing, and brand. This is what it was, the decisions behind it, where it fell short, and why it was wound down.

At a glance

Role
End-to-end ownership — product, data/ML, backend, infra, GTM
What it was
Intraday equity signals, delivered via a tiered REST API + Python SDK
Stack
R + Python pipeline · PostgreSQL · FastAPI · Cloudflare (Tunnel, Worker, KV) · LightGBM
Coverage
Technology, Consumer Cyclical, Communication Services · 6 signal runs/day
Surface
Auth, API keys, rate limits, merchant-of-record billing, Discord delivery, GA4 instrumentation
Outcome
Shipped and operated live; wound down when the commercial thesis didn't hold

Problem & thesis

Retail traders are flooded with signals and starved of context. Almost none come with a stated confidence, an entry/exit reference, or any honesty about how the number was derived. The bet was narrow and falsifiable: within a few liquid sectors, short-horizon statistical patterns recur often enough to surface intraday, and a trader will pay for them if every signal ships with its own win rate and an execution reference.

Scope was deliberately narrowed to three sectors — Technology, Consumer Cyclical, Communication Services — where the model showed the most consistent behaviour, rather than claiming whole-market coverage. Narrow-but-honest beat broad-but-vague.

The system

An hourly pipeline ran during market hours and produced signals six times a day, each analysing that hour's completed 30-minute bar:

  • Collect — hourly price, index, and commodity bars plus intraday 5-minute data, pulled from a market-data API into PostgreSQL.
  • Engineer features — layered context tables: price context, market context, intraday context, and cross-asset ratio context, each computed as its own stage with explicit dependency ordering.
  • Generate & rank — a heuristic discovery layer proposes candidate signals; a LightGBM ranker scores them. The ranker only ships to production behind an AUC ≥ 0.70 deploy gate, and is retrained weekly.
  • Attach guidance — each signal gets an entry reference, an empirically-derived exit target, an expected return, and a 2-hour horizon.
  • Deliver — written to the API's database and pushed to Discord, fronted by a Cloudflare Worker doing auth, rate-limit, and response caching at the edge.

How the win rate was actually computed

This is the part that mattered most, because it's where most signal products quietly overstate. Every signal carried a win_rate_type declaring how trustworthy its own number was:

  • per_symbol — individually validated for that symbol and pattern with ≥10 samples. Highest confidence.
  • all_symbol — symbol-specific but below the eligibility threshold.
  • pooled — a signal-level average across symbols, where the symbol itself wasn't individually validated. Explicitly flagged as lower confidence, because real per-symbol performance can diverge substantially.

A consumer could filter on win_rate_type == "per_symbol" to take only individually-validated signals. In practice most live signals were per_symbol, with pooled reserved as a flagged minority fallback. Surfacing the provenance of a number — instead of just the number — is the difference between a statistic and a sales pitch.

Expected return, honestly bounded

Expected return wasn't a model fantasy. It was the signal type's empirical 2.5-hour paper-forward return over the trailing 90 days, refreshed weekly, clipped to a 0.3%–1.5% band and defaulting to 0.5% when a signal type had fewer than 30 samples. The exit target was simply entry × (1 + expected_return) — a reference, not a promise. Bounding and defaulting like this kept thin-sample signal types from emitting absurd targets.

Supporting models & monitoring

  • A separate execution-score model and a weekly exit-target recompute kept guidance calibrated to recent price behaviour.
  • A daily health monitor watched for distribution shift, paper-vs-actual win-rate gap, and coverage drops, with a one-flag rollback path for the ranker.
  • Signal outcomes were labelled hourly into a dataset that fed the next retrain — a closed loop from prediction to outcome to model.

Product decisions

API-first, with tiering that mapped to real cost and risk

The product was the API; the website was just a storefront. Three tiers — Free, Pro ($99/mo), Pro Max ($249/mo) — but the tier boundaries weren't arbitrary feature-gating. They tracked conviction and operational cost:

  • Free saw one signal a day from the first market hour — enough to evaluate the data, not enough to run on.
  • Higher tiers got more signals per hour, proportionally sampled across win-rate buckets so a paying user saw a representative spread, not just the cherry-picked top.
  • Pro Max unlocked the genuinely expensive-to-serve parts: live preflight checks (price drift vs. entry, intraday range position, relative volume) fetched at request time, plus a precise horizon-end timestamp and same-day lookback.

Instrumentation built for the question that matters

GA4 was wired to the authenticated api_users.id, so behaviour could be joined across anonymous landing, sign-up, and authenticated API use — the only way to actually answer "which acquisition path produces users who keep calling the API." Building the measurement before the marketing was a deliberate choice.

The full operational surface

It ran like a real service: JWT sessions, tiered API keys (isk_{tier}_{random}), per-IP registration limits and login lockouts, RFC 7807 error bodies, security headers on every response, a status page, and legal docs. Billing ran through Whop as a merchant of record — a deliberate choice to offload US sales-tax and EU VAT compliance onto the MoR rather than registering for tax collection across jurisdictions directly. None of this is glamorous; all of it is what "shipped" actually means.

Where it fell short

A case study that only lists wins isn't credible. The honest weaknesses:

  • Tail shocks no calibration could capture. Backtesting showed the edge holding across volatility regimes — including elevated ones — and it performed in live trading for a good stretch. What eventually broke it was a different animal: rare, exogenous dislocations driven by geopolitics and commodities (US–Iran tensions, oil spikes) — out-of-distribution moves no calibration window can anticipate. The model worked for a while, then stopped working when the market went somewhere the data had never been.
  • Paper-forward returns were the wrong thing to lead with. The gap between paper and realised returns was well understood — the daily paper-vs-actual monitor existed precisely to watch it. The misstep was one of positioning, not measurement: out of impatience, the early marketing led with paper-forward evidence and a "70%+ win rate" headline. The methodology and the win_rate_type honesty layer were the stronger story and should have led instead.

Why it was wound down

The engineering held up. The harder truths were about the edge and the market — and naming them is more useful than pretending otherwise.

Two things decided it. First, the model performed for a stretch, then ran into a period of extreme, exogenous volatility — the geopolitical and commodity shocks (US–Iran, oil) that sit outside anything a calibration window can capture. An edge that works until the market dislocates is a hard thing to build a subscription on. Second, the commercial thesis required something the data never confirmed — that a retail audience would pay recurring for a statistical edge net of their own execution costs. The unit economics didn't close.

So the service was decommissioned deliberately and in order, with the system and this record preserved. Knowing when to stop is a product skill too.

Example output

A representative signal payload — the shape the API actually returned:

MRVL BUY
78%
win rate · per_symbol
Entry
$87.45
Exit target
$88.52
Expected return
+1.23%
Horizon
2h

Illustrative example, not a live or current signal.

The full field reference — including the tiered response model and preflight checks — remains available in the archived API docs.