Product · 2026 · MarketLane AI

Three generations of an LLM stock-scan pipeline

MarketLane scans the NSE every day for high-quality swing setups. The scan pipeline has been rebuilt three times. Each generation moved accuracy, cost and latency in different directions, and the one in production is the one that balances all three.

What MarketLane does

MarketLane AI is a market-intelligence product for active NSE investors. It combines deterministic market analytics, signal detection and LLM reasoning into one decision-support system: dashboards, screeners, stock-level intelligence, alerts and an AI-written technical read for every candidate.

The heart of the product is the daily scan. Every evening it takes the full stock universe, narrows it to roughly a hundred candidates and asks: which of these are genuinely worth a swing trader’s attention tomorrow, and why?

That question is where most of the engineering lives. This page is about how the scan pipeline evolved, what each generation cost, and the architecture and practices underneath it.

The metric that drove every decision

Before touching the pipeline I needed a number that would not move with prompt wording.

Each trading day a reference set of top setups is produced by the most expensive analysis path we have, run over every candidate with no screening in between. The daily pipeline is then scored on how many of those it surfaced, weighted by the star rating of each setup so that missing a five-star setup hurts more than missing a two-star one.

                 Σ stars of top setups the pipeline surfaced
hit rate  =  ─────────────────────────────────────────────────
                 Σ stars of all top setups that day

The second and third numbers are the obvious ones: model cost per 100-symbol scan, and wall-clock time for the whole universe. Every architecture change below was judged on those three together.

Three generations side by side

Interactive · three pipeline generations · same NSE universe

v0 · Prefilter + single shot

Retired

One structured LLM call per symbol over a feature packet built in code. Heavy caching kept it cheap, but one model had to discover, judge and explain in a single pass.

Deterministic prefilteruniverse → ~100
Feature packettrend · volume · levels
Single-shot analystone structured call
Cache + persistsnapshot-keyed
UIstructured verdict

1 model call per symbol

v1 · Discover, analyze, validate, criticize

In production

A high-recall screen, a multimodal analyst reading daily and weekly charts, a deterministic validator over every measurable claim, and an adversarial critic. Code resolves the final state.

Deterministic prefilteruniverse → ~100
High-recall screenbatched · KEEP / BORDERLINE
Chart generationdaily + weekly
Multimodal analystreads the charts
Validatorrecomputes claims
Adversarial critichunts weaknesses
State resolvercode decides

3 to 4 model calls per surviving symbol

v2 · Multi-agent analysis and ranking

Shadow evaluation

Independent technical and fundamental analysts, a risk manager backed by a deterministic risk engine, a critic, a verifier, one bounded correction cycle, then deterministic universe-level ranking.

Freeze evidenceas-of cutoff · hashes
Technical agent
Fundamental agent
parallel
Risk engine + managercode sizes, model advises
Criticchallenges all three reports
Verifierfacts in code, semantics in model
Resolver + rankingnever an LLM

5 model calls per symbol, up to 10 with correction

Deterministic codeSmall modelMultimodal modelReasoning model
Measured trade-offsv0v1v2
Top-setup hit ratestar-weighted, % of the day's top setups found · higher is better
v081 to 85%
v190%
v287 to 88%
Cost per 100-symbol scanUSD, model spend · lower is better
v0$1.4
v1$4.5 · $2.0 with web search off
v2$7 to 9
Wall clock, 100 symbolsminutes, two workers in parallel · lower is better
v08 min
v117 min · 9 min with setup gate + small-model critic
v255 min · 20 to 25 min with gate, fundamentals after technical pass
Same data as a table
VersionTop-setup hit rateCost per 100-symbol scanWall clock, 100 symbols
v081 to 85%$1.48 min
v190%$4.5 ($2.0 with web search off)17 min (9 min with setup gate + small-model critic)
v287 to 88%$7 to 955 min (20 to 25 min with gate, fundamentals after technical pass)

v0: intelligent prefilter and one shot

The first pipeline was deliberately simple. A deterministic prefilter cut the universe to around a hundred names using trend, liquidity and volatility rules. For each survivor, code built a compact feature packet (returns, moving-average structure, relative volume, nearby levels) and a single structured LLM call produced the verdict and the explanation.

Caching did most of the heavy lifting. Feature packets, prompts and model responses were keyed on the market snapshot, so re-runs and partial re-scans were nearly free, and a full universe cost about $1.4.

It worked well enough to ship, with a hit rate of 81 to 85%. Its ceiling was structural. One model, in one pass, had to discover candidates, judge them, check its own arithmetic and write the explanation. When it was wrong there was no second opinion and no cheap way to tell.

v1: discover, analyze, validate, criticize

The second generation splits those responsibilities into stages, each with one job and the cheapest model that can do it.

Deterministic prefilter            code      universe → ~100
        ↓
High-recall screen                 small     batched, KEEP / BORDERLINE / REJECT
        ↓
Chart generation                   code      daily + weekly images
        ↓
Deep multimodal analyst            vision    reads the charts, proposes setup + thesis
        ↓
Deterministic validator            code      recomputes every measurable claim
        ↓
Bounded correction                 model     only if the validator found contradictions
        ↓
Adversarial critic                 small     looks for what the analyst missed
        ↓
Final-state resolver               code      ACTIONABLE · WATCH · DEVELOPING · REJECT

Three ideas carry this design.

High recall first, precision later. The screen is intentionally permissive. Its question is “could this plausibly be a worthwhile setup?”, and uncertainty favors survival. Precision is added by the deeper, more expensive stages rather than lost at the gate.

Code owns facts. Moving averages, ATR, volume ratios, pivots, breakout conditions and trade geometry are computed in code and handed to the model. The validator then recomputes every number the analyst asserts. A claim that does not match the data is a contradiction, and a contradiction triggers one bounded correction, never a free retry loop.

An adversary before publication. The critic receives the analyst’s thesis and the validated facts and is asked only to find weaknesses: extension, overhead supply, weak volume confirmation, a trigger that has not actually fired. It is also allowed to return nothing. Code, not the critic, resolves the final state from the combined evidence.

The multimodal step is where Gemini earns its place: daily and weekly charts are analyzed together, which is how a discretionary trader reads structure and which the feature packet alone never captured. Reasoning stages run on Claude Sonnet 5.5 and the critic on Claude Haiku 5.5.

The result was a hit rate of 90% at about $4.5 per 100 symbols with web search enabled, or $2 without it. Per-symbol latency roughly doubled against v0, which is acceptable for a nightly scan and is where the optimisations below were aimed.

v2: multi-agent analysis and ranking

The third generation asks a broader question. v1 says whether a setup is good. v2 tries to say how good it is relative to every other candidate, with fundamentals and risk included, and with every claim traceable to frozen evidence.

Freeze universe, as-of cutoff, policies, prompt hashes
        ↓
Acquire and seal the evidence bundle      market · technical facts · fundamentals · events
        ↓
Deterministic data gates                  stale bars, missing periods, unsupported sectors
        ↓
Technical agent      ∥     Fundamental agent        independent, neither sees the other's verdict
        ↓                          ↓
Risk engine (code)  +  Risk-manager agent           code sizes and vetoes, the model advises
        ↓
Critic                                              challenges all three reports
        ↓
Verifier                                            facts and arithmetic in code, semantics in a model
        ↓
One bounded correction cycle                        rerun only the implicated roles
        ↓
Deterministic resolver → universe barrier → deterministic ranking → optional allocation

The important design choices are the ones that keep authority out of the models.

  • Evidence is immutable and addressable. Every fact gets an ID with a value, unit, source and availability time. A claim must reference the facts that support it, and the verifier resolves those references rather than trusting that they exist.
  • Roles are typed calls, not autonomous agents. Each role has its own prompt, schema and authority. Agents cannot browse, call tools, amend evidence or see credentials. There is no open-ended debate loop.
  • Critic findings have no veto until verified. A fabricated criticism is marked refuted and cannot suppress a candidate just because it was labelled critical.
  • Ranking never comes from an LLM. Models propose categorical rubric labels with evidence. Code validates the prerequisites, maps labels to points, applies penalties for verified concerns, and sorts with deterministic tie-breaks. Missing data excludes a candidate rather than being scored as zero.
  • Point-in-time discipline. Fundamentals and events must have been publicly available before the cutoff. Retrieval time is not proof of historical availability.

In shadow evaluation v2 scores 87 to 88% on the hit rate, slightly below v1, at $7 to 9 per scan and several times the latency. That is not a failure. The hit-rate metric is a technical-setup metric, and v2 is deliberately spending budget on fundamental and risk dimensions that it does not reward. It is also the pipeline whose outputs are fully replayable, which matters more as the product moves toward portfolio-level reasoning.

Why v1 is the one in production

v0 v1 (prod) v2 (shadow)
Hit rate, star-weighted 81 to 85% 90% 87 to 88%
Cost per 100 symbols $1.4 $4.5 ($2 without web search) $7 to 9
Per-symbol latency ~9 s ~20 s (~10 s optimised) ~65 to 85 s (~22 s optimised)
100 symbols, two workers ~8 min ~17 min (~9 min optimised) ~55 min to 1.2 h (~20 to 25 min optimised)
Model calls per symbol 1 3 to 4 5, up to 10 with correction

v1 is the best balance the product has found: the highest measured accuracy, a cost that supports daily scans for a growing user base, and a nightly wall clock with room to spare. It is the one being refined. v2 runs beside it on the same frozen universes, and promotion is gated on predeclared quality, cost and latency thresholds rather than on a good week.

Optimisations that moved the numbers

Most of the cost and latency wins came from deciding what not to send to a model, not from faster models.

  • Setup gate before the expensive stages. A deterministic check on setup state drops candidates that cannot be actionable yet, so the multimodal analyst and critic only run where they can change the answer. This alone halves v1’s average per-symbol time.
  • Right-size every stage. The screen and the critic run on a small model. The multimodal analyst is the only stage that needs chart vision. Reasoning-heavy synthesis is the only stage that needs the large model.
  • Batch the recall screen, retry only the gaps. Stage one evaluates candidates in batches. One malformed result retries one candidate, never the batch.
  • Fundamentals after the technical pass. In v2, running the fundamental agent only for candidates that pass the technical gate cuts projected wall clock by more than half with no change in ranking for the survivors.
  • Cache on the data snapshot, not the symbol. A cache key is the candidate plus the market snapshot plus the feature and prompt versions. A new candle invalidates the analysis; a re-run on the same snapshot is free.
  • Prompt caching and compact context. Stable instructions and rubrics sit in the cached prefix; the per-candidate packet is the only variable part.
  • Bounded concurrency. Candidate-level parallelism behind a semaphore sized to provider quotas, with two workers per scan in production.
  • Web search as a switch. External context is the single largest cost line in v1. It is a per-scan policy, measured separately, so the product can trade it for cost on quiet days.

Infrastructure architecture

            React UI
               │
     Authenticated FastAPI              create run · status · evidence · rankings · cancel
               │
     Durable run record + outbox       PostgreSQL, idempotency key per request
               │
       Queue (SQS)  ──────────►  Scan workers  (bounded concurrency, lease + fencing token)
                                        │
                     ┌──────────────────┼──────────────────┐
                     │                  │                  │
              Market data           Model gateway       Object storage
         (broker-first OHLCV,     (Vertex AI · Anthropic;   (charts, raw and validated
          provenance recorded)     per-role model config)   reports, content-addressed)
                                        │
                              PostgreSQL: runs · candidates · stages · attempts · rankings
                                        │
                                   Langfuse traces · metrics · structured logs

The application runs on AWS. Model inference runs behind a thin gateway against Vertex AI and Anthropic, so the product is not architecturally tied to one provider and each role can be re-pointed by configuration. The same repository protocol has a SQLite and local-file implementation, so a full pipeline run works on a laptop against the same durable state machine, and both backends pass one shared contract test suite.

Reliability patterns

  • Explicit state machines, one for the scan and one per candidate, with execution state kept strictly separate from trading state.
  • Persist each stage before dependent work. A failed fundamental call never triggers a fresh technical call on resume.
  • Stage identity is a hash of run, symbol, stage, evidence, upstream report hashes, config, prompt and model. Retrying the same identity returns the same artifact. Late responses from an expired lease cannot overwrite newer results.
  • Budgets are reserved before a call and reconciled after. Admission fails closed when pricing or configuration is missing.
  • Circuit breakers on timeout, rate-limit and schema-failure rates stop a scan as PARTIAL rather than publishing misleading results.
  • Atomic publication. A ranking is written in full and then a single pointer flips. A failed publish leaves the previous one untouched.
  • Duplicate delivery is expected. Idempotency keys, a transactional outbox and conservative accounting for calls that may have run.

Observability: Langfuse, OpenTelemetry and CloudWatch

A nightly scan is a few hundred model calls spread across queue-driven workers, two cloud providers and three model vendors. When a scan is slow, expensive or wrong, the question is always “which stage, which symbol, which call?”. The telemetry is built so that question has a single answer, reachable from any of three entry points.

                     scan worker / API (Lambda)
                                │
        ┌───────────────────────┼────────────────────────┐
        │                       │                        │
  OpenTelemetry           Langfuse SDK              EMF metrics + logs
  TracerProvider +        (built on OTel, shares    (stdout, no extra
  MeterProvider           the same trace tree)       IAM permissions)
        │                       │                        │
   OTLP/HTTP → collector    Langfuse Cloud          CloudWatch metrics,
   (Grafana, X-Ray,         sessions · traces ·     dashboard, alarms,
    Honeycomb, …)           generations · scores    Logs Insights
        │                       │                        │
        └─── same trace id ─────┘        run_id / stage attempt records

One trace tree

The application registers one global OpenTelemetry tracer and meter provider. The Langfuse SDK is itself built on OpenTelemetry and attaches its span processor to whatever provider is registered, so a Langfuse generation and the OTel spans around it are the same trace with the same trace id. Nothing is instrumented twice.

The span hierarchy follows the pipeline:

Span What it wraps Key attributes
Worker invocation One queue message, including continuations after a Lambda soft deadline run id, continuation flag, worker result
Scan stage One stage attempt for one symbol: screen, analyst, validator, critic, resolver stage, symbol, attempt, status, artifact reference
Model request One structured call to a provider gen_ai.system, requested and served model, finish reason, token usage, cache read and write tokens, thinking and effort settings, fallback used, error type

Model spans use the OpenTelemetry GenAI semantic conventions where they exist, with MarketLane-specific attributes namespaced separately. The served model is recorded from the provider response, never from configuration, which is how a silent provider-side model swap gets noticed.

Metrics with the right dimensions

Counters and histograms are emitted from the same instrumentation: model calls, input tokens, output tokens, estimated USD, and a stage-latency histogram. Every one is dimensioned by stage, model, status and environment. That is the difference between “cost went up 30%” and “the critic’s cache-read ratio dropped after Tuesday’s prompt change”. Symbols, run ids and user ids are deliberately kept out of metric dimensions and live in structured logs and traces instead, so cardinality stays bounded.

Langfuse as the AI-specific lens

Langfuse carries what a generic tracing backend does not model well: prompt versions, structured-output schemas, per-generation token and cache accounting, and scores.

  • Sessions keyed by run id. Each daily scan creates one root trace, and every model generation in that scan records the same session id. Opening a session shows the scan-level funnel beside its per-symbol generations.
  • Scores make operations filterable. Alerts written, AI calls, cache hits, fallbacks, error count and estimated cost are attached to the root trace as numeric scores, so a week of scans can be sorted by cost or fallback rate without leaving Langfuse.
  • Stage events for shadow experiments. Frozen-snapshot experiment runs log one event per stage attempt with outcome, elapsed time, served model and tokens, and carry the OTel trace id so the same attempt can be opened in the OTLP backend.
  • Redaction by default. Prompts, chart images and parsed model outputs are replaced by a hash and an approximate byte size before leaving the process. Full capture is a separate explicit flag, off in every deployed environment, because scan prompts contain user-supplied notes and pasted fundamentals.

CloudWatch as the paging source

Langfuse and the OTLP backend are for diagnosis. Paging comes from CloudWatch, where the signals are cheapest and closest to the infrastructure.

  • Native metrics for API Gateway 5xx, Lambda errors, throttles and near-timeout durations, queue age and dead-letter depth, and database storage and connections.
  • Embedded Metric Format from stdout for application metrics: API latency and errors, model calls, tokens, estimated cost and fallbacks, market-data failures, scan outcomes and the count of stale active scans. Emitting through logs means the functions need no extra metrics permission.
  • A scan watchdog on a five-minute schedule reads the database and emits a stuck-scan metric for runs that have been queued or running past a threshold. It never starts or cancels scans.
  • Alarms are opt-in and only notify through an SNS topic that already has an on-call subscription. The stack never creates an unowned alert route.

Initial SLOs are operational targets rather than customer commitments: API availability and p95 latency, scans finalized within a window of starting, queue age, model fallback rate and market-data failure rate, each reviewed against two weeks of staging baseline before thresholds are tightened.

Cost accounting that matches the bill

Every priced provider call made under a stage checkpoint is written to a spend ledger with model, uncached, cache-read, cache-write and output tokens, and USD at published rates, including the TTL multiplier for prompt-cache writes. A budget policy enforces a hard programme cap and a per-experiment cap: launch is refused when the per-symbol estimate exceeds the remainder, and every call re-checks the ledger. Exhaustion is a terminal, recorded run status, not an exception in a log. The provider console remains the billing authority; the ledger exists so that the cost figures on this page can be attributed to stages and models.

Rules the telemetry follows

  • Fail open. A Langfuse or OTLP outage never changes a stage checkpoint, retry or scan outcome. With the flags off, the no-op tracer is used and spans cost nothing.
  • Flush before freeze. Workers flush batched spans and metrics before returning, so a Lambda freeze does not drop the tail of a scan.
  • Secrets stay out of config. Langfuse credentials are loaded at runtime from a secrets manager entry whose ARN is the only thing in infrastructure state. Roles get read access to that one entry.
  • Cross-link everything. Stage-attempt records in the database carry the trace id. From a bad row in the UI you can reach the Langfuse generation, the OTel span and the CloudWatch log line.

Evaluation

The hit-rate metric above is the headline, but it sits inside a wider harness.

  • Recall against an oracle. The screen is measured against the expensive analyst run directly over the whole universe. A high pass rate is not a quality signal; recall against the reference set is.
  • A false-negative dataset. Every setup the screen dropped and the oracle kept becomes a labelled record used to tune the screening prompt.
  • Forward outcomes. Each scan is frozen at its timestamp. Trigger reached, returns at one, three, five and ten sessions, maximum favorable and adverse excursion, and stop-or-target-first are computed later and compared across final state, setup family and critic severity.
  • Shadow comparison on identical inputs. v1 and v2 run on the same frozen universes and cutoffs, with ablations (technical only, technical plus fundamental, full critic and verifier) so each stage’s contribution is visible.
  • No post-hoc thresholds. Promotion gates for cost, latency and quality are declared before results are collected.

Principles that held across all three generations

  1. Deterministic before probabilistic. If software can compute it reliably, a model does not.
  2. Models interpret; code decides. Every verdict, risk veto and rank is produced by code from validated inputs.
  3. High recall at the gate, precision in depth. Cheap stages are permissive; expensive stages are selective.
  4. AI output is application data. Strict schemas, forbidden extra fields, bounded lists, finite numbers, versioned contracts.
  5. No fake probabilities. The system never emits “85% chance of success”. Judgments are technical interpretations, not calibrated forecasts.
  6. Every decision is explainable. A verdict ships with its evidence and its concerns, and the raw model response is retained next to the validated one.
  7. Freeze, hash, replay. Universes, cutoffs, prompts and policies are frozen per run, so any published row can be reproduced.
  8. Measure the trade-off, not the demo. A new generation is adopted only when accuracy, cost and latency are all measured on the same inputs.

Security boundaries

Model providers are treated as external dependencies. Document text, uploaded notes and web results are data, never instructions, and the pipeline tests prompt-injection text in source material explicitly. Agents have no tool access and no credentials. Reads are owner-scoped, inaccessible runs return 404, and public requests cannot override prompts, models or policies. Secrets stay server-side; the frontend only ever talks to MarketLane’s own API.

What is next

The near-term work is on v1: finishing the setup-gate rollout, moving the correction step to the small model where the validator’s feedback is purely numeric, and tightening the web-search policy. In parallel, v2’s shadow evaluation is collecting enough prospective data to decide whether its fundamental and risk dimensions earn their cost. Longer term, the evidence-and-ranking machinery from v2 is the foundation for portfolio-level reasoning and for connecting the scan to MarketLane Research, where filings and earnings material are retrieved as cited evidence.

MarketLane Research covers the other half of the problem: evidence-grounded equity research over SEC filings, XBRL facts and investor material through retrieval-augmented generation. The scan pipeline produces structured market intelligence; Research produces cited fundamental evidence. Together they form the base of a broader AI-native investment intelligence platform.