Strategy backtesting

A backtest without building a backtester.

Backtesting infrastructure is a month of work that mostly consists of data plumbing and off-by-one bugs in the rebalance loop. /v1/backtest gives you a regime-aware walk-forward strategy comparison over a date window you choose, with transaction costs applied by default.

  • Walk-forward, not in-sample
  • Costs applied by default
  • Research-labeled output
run-backtest.sh
curl https://stockup.cc/v1/backtest \
  -H "x-api-key: $STOCKUP_API_KEY" \
  -H "content-type: application/json" \
  -d '{
    "startDate": "2015-01-01",
    "endDate": "2025-12-31",
    "transactionCost": 0.001,
    "question": "How did the regime-aware mix
      compare through the 2022 drawdown?"
  }'
Every field is optional. An empty body runs the default verified comparison.
Walk-forwardNo whole-window fitting
10 bps defaultCosts on unless you change them
Regime-awareWeights respond to state
AuditableReplayable decision record
Request fields

Four optional fields, sensible defaults.

FieldDefaultWhat it does
startDateengine defaultBeginning of the test window, YYYY-MM-DD. Omit to use the full available history.
endDateengine defaultEnd of the window. Pair with startDate to isolate a specific period such as a single drawdown.
transactionCost0.001Per-trade cost as a decimal. 0.001 is 10 basis points. Raise it to model a retail spread; set it to 0 only to measure the cost drag itself.
questionautoSteers the written interpretation that accompanies the numbers. Does not change the simulation.
Two design decisions

Why the defaults are what they are.

Costs are on by default

A zero-cost backtest is the single most common way a strategy looks good on a slide and loses money in production. High-turnover approaches are exactly the ones that flatter themselves when trading is free, so transactionCost defaults to 10 basis points rather than to zero. You can set it to 0, but you have to choose to.

Walk-forward, not whole-window

Fitting weights across the entire history and then reporting performance on that same history is not a test, it is a description. The engine runs a walk-forward portfolio comparison so that at each step the allocation only reflects information available up to that point.

Neither of these makes a backtest predictive. They just remove the two most common ways of accidentally lying to yourself.

isolate-a-drawdown.sh
# Cost sensitivity: same window, three cost
# assumptions. If the ranking flips, turnover
# was carrying the result.
for COST in 0 0.001 0.005; do
  curl -s https://stockup.cc/v1/backtest \
    -H "x-api-key: $STOCKUP_API_KEY" \
    -H "content-type: application/json" \
    -d "{
      \"startDate\": \"2021-11-01\",
      \"endDate\":   \"2022-12-31\",
      \"transactionCost\": $COST
    }" | jq '.'
done
Cheap sanity check, and it catches an overfit result faster than any single run.
Research labeling

The output is about the past. It says so.

Backtest responses are explicitly research-labeled. This is a product constraint, not decoration: it means the payload cannot be dropped into a marketing surface as an expected return without you deliberately stripping the label off.

That matters because backtested performance is the most over-claimed number in retail finance. A walk-forward comparison with realistic costs is genuinely useful for understanding how an approach behaved under different conditions. It is not a forecast, it has survivorship and regime-coverage limits, and any product presenting it to end users needs to say so.

The response also carries coverageBreakdown and calculations, so you can see how much history the result actually rested on rather than assuming the window you requested was fully covered.

response shape
{
  "decisionId": "dec_...",
  "releaseFingerprint": "...",
  "quantitativeCoreHash": "...",
  "coverageBreakdown": { ... },
  "calculations": [ ... ],
  "research": { ... },
  "audit": { ... }
  // plus the backtest result fields
}
Same envelope as the other quantitative endpoints.
Pairs well with

Backtest, then explain.

Macro

Understand which regimes the window contained

A strategy that shone from 2015 to 2021 may simply have been long a single regime. Classify the environment, then read the backtest against it.

Market regime API →

Risk

Check the risk profile, not just the return

Backtest return without drawdown and CVaR is half a picture. The risk endpoint computes the tail metrics on the resulting book.

Portfolio risk API →

Provenance

Retrieve the run months later

Pass the returned decisionId to /v1/audit for the owner-scoped immutable record, including the engine build that produced it.

Read the docs →

Backtest FAQ

Honest limits.

Can I backtest my own custom strategy rules?

This endpoint runs the engine's verified strategy comparison rather than an arbitrary user-supplied rule DSL. If you need a bespoke rule set evaluated, that is an Enterprise conversation.

Does it account for survivorship bias?

Treat any backtest, including this one, as subject to survivorship and coverage limits. That is precisely why the response reports coverage instead of presenting a clean number — check coverageBreakdown before you trust a window.

How long does a run take?

Longer than a quote lookup. Backtests are a good candidate for a background job in your own architecture rather than a request made during a page render.

Can I show backtested returns to my users?

That is your call and your regulatory exposure, not ours. If you do, present it as backtested research with the cost assumption and date window visible, never as an expected return. See the risk disclaimer and terms.

Is the result deterministic?

The same window, cost assumption, and engine build produce the same comparison. The releaseFingerprint in the response tells you which build ran, so you can detect when a difference came from an engine change rather than from your inputs.

Skip the rebalance loop

Get a walk-forward comparison in one request.

Start with 100,000 free Quan 3.4 L tokens. No card required.

Create a free API key →