> ## Documentation Index
> Fetch the complete documentation index at: https://uncoded.ch/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Backtester — Validate Before You Risk Capital

> Run any strategy or mode against historical market data. See trades, equity curves, drawdowns, and summary metrics — before you put real money on the line.

<Info>
  **The Backtester is where you validate strategies before they touch your capital.** Submit a strategy or mode, a symbol, and a historical window; the Backtester replays every candle in that window through the same execution rules your live TradingBot would apply, and gives you the per-trade ledger, the equity curve, and the summary metrics.
</Info>

## What the Backtester does for you

The Backtester is your **historical confidence builder**. Live trading has a small but real cost in mistakes — you can't undo a bad mode-and-symbol choice without taking a drawdown to find out. Backtesting lets you find out the cheap way.

You submit a backtest: "Run BasicMode on `BTCUSDT` from January 2024 to December 2024 with a `$10,000` starting balance and `0.075%` fees." A worker picks up the job, replays every candle in that window through the mode's logic, and persists the entire trade ledger plus the per-second equity curve. You then inspect: how many trades were there? What was the max drawdown? How did equity behave around the May 2024 chop? What's the Sharpe?

You can also share a backtest **read-only** with a partner, an auditor, or a fellow operator — using a generated share token, no account required.

The Backtester is the single most-used "before live" tool in the unCoded suite. Operators who skip backtesting before scaling up almost always wish they hadn't.

<CardGroup cols={2}>
  <Card title="Same execution rules as live" icon="equals">
    The Backtester applies the same buy-ladder, sell-ladder, and trailing-stop rules your live TradingBot uses. What you see in the simulation is what you'd get live.
  </Card>

  <Card title="Realistic fees and slippage" icon="coins">
    Configurable trading fee (default `0.075%`, the Binance/BNB rate) and slippage model. The reported equity is **net** of these costs, not idealized.
  </Card>

  <Card title="Per-trade ledger" icon="list-check">
    Every simulated trade is persisted: entry time, entry price, exit time, exit price, P\&L, P\&L percentage. Inspect any trade individually.
  </Card>

  <Card title="Equity curve sampling" icon="chart-line">
    Per-step equity samples let you draw the equity curve, locate drawdowns precisely, and see how the strategy responded to specific market regimes.
  </Card>

  <Card title="Concurrency-capped queue" icon="layer-group">
    Backtests are queued and processed by a worker pool. Submit many; they run in order without overwhelming the host.
  </Card>

  <Card title="Read-only share tokens" icon="link">
    Generate a share URL for any backtest. Anyone with the URL sees the full results in read-only mode — no API key, no account.
  </Card>

  <Card title="Reproducible by ID" icon="rotate">
    Every backtest is identified by a stable ID. The exact configuration is preserved so anyone can re-run "the same backtest" months later and get the same numbers.
  </Card>

  <Card title="Cached candle data" icon="database">
    The Backtester maintains a centralized historical-candle cache. Asking for a window already imported is instant; asking for a new window imports once, then is cached for everyone.
  </Card>
</CardGroup>

## What you can backtest

The Backtester supports two flavors of input.

<AccordionGroup>
  <Accordion title="Pre-built modes — every shipped mode is backtestable" icon="boxes-stacked">
    Pick any of unCoded's 9 pre-built modes (BasicMode, FullBullMarket, LongTimeLong, LongTimeLongMoreProfit, LowMoney, MinimalMoney, Tsl2Sell, MarketMaker, MarketMakerMinimal). The Backtester reads the same JSON mode files your live TradingBot uses, so the simulation is faithful to the real configuration.

    This is how you answer "would this mode have worked for the regime I'm worried about?" — pick the mode, pick the symbol, pick the window, run.
  </Accordion>

  <Accordion title="Custom strategies — your SignalEditor graphs" icon="diagram-project">
    Strategies authored in the SignalEditor produce a JSON definition. The Backtester accepts that JSON and runs the same evaluator against historical candles. Confidence in your backtest = confidence in your live strategy.

    For long historical windows (months or years), the Backtester is the right tool. For short windows (hours or days), the SignalEditor's built-in preview is faster.
  </Accordion>
</AccordionGroup>

## How a backtest works

<Steps>
  <Step title="You submit the job">
    Send a POST request specifying the symbol, the mode or strategy, the start and end times, and optional parameters (starting balance, fee percentage, slippage). The Backtester queues the job with a stable backtest ID and returns immediately.
  </Step>

  <Step title="Worker picks up the job">
    A worker thread pulls the next job from the queue and spawns the simulation engine — a compiled process optimized for fast historical replay. First-run startup warms the engine in a few seconds; subsequent runs reuse the warm cache.
  </Step>

  <Step title="Historical candles are replayed">
    The engine reads every candle in your chosen window. For each candle, it applies the same execution logic your live TradingBot would: evaluate buy conditions, place orders, manage the sell ladder, arm trailing stops, mark-to-market the position.
  </Step>

  <Step title="Trades are recorded">
    Every simulated trade is captured as a row: when did the buy fill, at what price, what was the position size, when did the sell ladder close it out, what was the realized P\&L? Every row is persisted to the Backtester's database with the backtest ID.
  </Step>

  <Step title="Equity is sampled">
    Per-step equity samples are recorded — typically every minute or every closed position, depending on the engine's configuration. These let you draw the equity curve over time and locate drawdowns precisely.
  </Step>

  <Step title="Summary metrics are computed">
    Once the run completes, summary metrics are computed: total return, average trade P\&L, max drawdown, win rate, Sharpe ratio, number of trades. The summary is persisted alongside the trade ledger.
  </Step>

  <Step title="You inspect the results">
    Query the backtest by ID. Pull the summary, the trade ledger, the equity samples. The frontend renders the equity curve. You decide whether to take this configuration to live trading, tweak it, or rule it out.
  </Step>
</Steps>

## What summary metrics actually mean

The Backtester returns a standard set of summary statistics. Here's what each one means and what to look for.

<AccordionGroup>
  <Accordion title="Total return" icon="percent">
    The percentage gain (or loss) over the full backtest window. The headline number.

    What to look for: a positive return is encouraging but not sufficient. A `+30%` total return over 12 months can hide a `-40%` drawdown in the middle. Always look at total return alongside max drawdown.

    What's "good"? Highly regime-dependent. In a crypto bull market, `+50%` to `+150%` is achievable. In chop, `+15%` to `+40%` is decent. In a bear, breakeven is a win.
  </Accordion>

  <Accordion title="Max drawdown" icon="chart-line-down">
    The largest peak-to-trough decline in equity during the backtest. The single most important risk metric.

    What to look for: max drawdown should be at most `2x` the worst drawdown you can stomach in real life. If you can tolerate a `-10%` real-time drawdown, the backtest's max drawdown should be ≤ `20%`.

    Why? Live trading hits drawdowns the backtest didn't. Live operator psychology compounds drawdowns (you might pull capital at the worst moment). A backtest's drawdown is the floor for what you'll see live, not the ceiling.
  </Accordion>

  <Accordion title="Number of trades" icon="hashtag">
    How many full round-trips occurred during the window.

    What to look for: trade count should be high enough that the backtest is statistically meaningful. `5–10` trades is too few to draw conclusions. `100+` trades over a year is solid. `1000+` trades in a year suggests you might be overtrading (high frequency = high fees = thin per-trade margin).

    Pro tip: divide by months. `100 trades / 12 months = ~8 trades/month`. Does that match your expectation? If you expected `~30 trades/month` and the backtest shows `8`, the mode is undertrading the symbol; reconsider.
  </Accordion>

  <Accordion title="Win rate" icon="trophy">
    Percentage of trades that closed profitable.

    What to look for: win rate alone is not a quality signal — you can have a 90% win rate that loses money (small wins, big losses) or a 30% win rate that's hugely profitable (small losses, big wins).

    Look for win rate alongside **average winner / average loser ratio**. A 50% win rate with `2x` ratio is a great strategy. A 90% win rate with `0.1x` ratio is a disaster waiting to happen.

    The pre-built modes (BasicMode, FullBullMarket, etc.) tend to have high win rates (`~70–90%`) by design — they have many small profit targets via the sell ladder. The asymmetry is reversed: when they lose, they lose larger. This is the deliberate trade-off of grid-style trading.
  </Accordion>

  <Accordion title="Sharpe ratio" icon="chart-mixed">
    Risk-adjusted return. Total return divided by return volatility.

    What to look for: Sharpe `> 1` is decent for crypto. Sharpe `> 2` is excellent. Sharpe `< 0` means you're losing money on a risk-adjusted basis (negative absolute return).

    Caveat: Sharpe is sensitive to the window. A strategy with Sharpe `3` over 6 months may have Sharpe `1` over 24 months. Always look at multiple windows.
  </Accordion>

  <Accordion title="Average trade duration" icon="clock">
    The mean time between buy and sell.

    What to look for: should match the mode's intended timescale. BasicMode's average is typically hours to days; FullBullMarket can be days to weeks; MinimalMoney can be hours.

    Anomaly to watch for: very long average duration suggests positions are stuck — the sell ladder is not getting hit, possibly because the price moved too far against entry. This is a flag to reconsider the mode-symbol pairing.
  </Accordion>

  <Accordion title="Profit factor" icon="balance-scale">
    Total winning P\&L divided by total losing P\&L (in absolute terms).

    What to look for: profit factor `> 1.5` is decent. `> 2.0` is strong. `< 1.0` means you're losing money.

    Profit factor is a useful complement to win rate — it tells you the magnitude story, not just the count story.
  </Accordion>
</AccordionGroup>

## Realistic limitations of backtesting

<Warning>
  **A good backtest is a sanity check, not a guarantee of future returns.** The Backtester replays the past honestly — including fees and a slippage assumption — but several real-world frictions are difficult to model exactly:

  * **Order book depth at your size.** A backtest assumes your orders fill at the recorded prices. In live trading, large orders walk the book.
  * **Exchange-specific behavior**: maker rebates, taker fees on different tiers, occasional partial fills, retry logic on transient errors.
  * **Operator behavior**: in live trading, you might pause, stress, change capital, or override a position. The backtest is a perfectly disciplined operator.
  * **Regime persistence**: a strategy that performed beautifully in 2021 bull is not guaranteed to perform in 2024 chop. Past regime ≠ future regime.
  * **Survivorship bias**: backtesting on `BTCUSDT` is fine because Bitcoin survived. Backtesting on a long-tail altcoin that delisted partway through your window is not — the engine assumes the symbol stays tradable.

  Treat backtest results as **necessary but not sufficient** for "this is good." Forward test on small capital before scaling up.
</Warning>

## Common backtesting pitfalls in detail

<AccordionGroup>
  <Accordion title="Overfitting to a single window" icon="bullseye">
    The biggest mistake. You tune a strategy on Jan-Dec 2024 until it produces beautiful equity. You go live in Jan 2025. The strategy underperforms.

    Why: you accidentally encoded the idiosyncrasies of 2024 (the specific dips, the specific rallies, the specific volatility profile) into your strategy parameters. The 2025 regime is different, your strategy is mismatched.

    **Mitigation**: tune on one window, validate on a different, untouched window. If a strategy survives both, you have evidence of generalization.
  </Accordion>

  <Accordion title="Look-ahead bias" icon="eye">
    Your backtest accidentally uses information that wouldn't have been available at the decision moment.

    Common form: an indicator's "current value" is computed including data that hadn't yet arrived in real time. The Backtester is careful to avoid this in its engine — every decision uses only data up to the candle close — but custom indicators or post-hoc analysis can re-introduce it.

    **Symptom**: backtest results that seem too good to be true. A `+200%` strategy with `-2%` max drawdown is almost certainly look-ahead biased.
  </Accordion>

  <Accordion title="Survivorship bias" icon="skull">
    Backtesting only on symbols that exist today. The symbols that delisted (because their projects failed) aren't in your universe — but in 2021, you might have allocated to them.

    **Mitigation**: backtest on majors only. `BTCUSDT`, `ETHUSDT`, `SOLUSDT`, `BNBUSDT` — symbols whose continued existence is high-confidence. Don't extrapolate from major-symbol backtests to long-tail altcoins.
  </Accordion>

  <Accordion title="Ignoring drawdown for total return" icon="chart-line">
    A backtest with `+50%` annual return looks great on paper. The same backtest with `-35%` max drawdown is terrible — most operators capitulate during a `-35%` drawdown and crystallize the loss.

    **Mitigation**: judge backtests on the worst quartile of drawdowns, not the headline return. Ask: "could I emotionally hold through this drawdown for the recovery?" If no, the strategy is wrong for you regardless of total return.
  </Accordion>

  <Accordion title="Curve-fitting parameters" icon="screwdriver-wrench">
    Sweeping a strategy's parameters until you find the combination with the best historical performance. The combination is overfit to history.

    **Symptom**: you ran a parameter sweep, picked the best, and the live performance is much worse than the backtest. This is the curve-fit revealing itself.

    **Mitigation**: pick parameters by reasoning, not by sweeping. If you swept anyway, validate the chosen combination on out-of-sample data.
  </Accordion>

  <Accordion title="Fee underestimation" icon="coins">
    Some backtests use unrealistic fee assumptions (`0.025%` instead of `0.075%`, or no fees). The reported return is much higher than live would be.

    **Mitigation**: always use the realistic fee for your venue. For Binance with BNB discount, `0.075%`. For Binance without BNB, `0.1%`. For Coinbase, taker fee can be `0.3%` for small accounts. Check your venue and your tier.
  </Accordion>

  <Accordion title="Slippage underestimation" icon="weight-scale">
    A backtest assuming zero slippage on every order will report higher returns than reality, especially on illiquid symbols or large position sizes.

    **Mitigation**: use a non-zero slippage parameter (the Backtester supports configurable slippage). For majors at moderate size, `0.05%` slippage is reasonable. For altcoins or large size, `0.2%` or more.
  </Accordion>

  <Accordion title="Not testing enough windows" icon="calendar">
    A single 12-month backtest is not enough. Markets have regimes — bull, bear, chop. A strategy that works in one may fail in another.

    **Mitigation**: test multiple windows: a bull window (e.g., 2020-21), a bear window (2022), a chop window (2023). If the strategy survives all three, you have evidence of robustness.
  </Accordion>
</AccordionGroup>

## How most operators use it

<CardGroup cols={2}>
  <Card title="Mode validation" icon="flask">
    Before switching from BasicMode to FullBullMarket, run both against the last 12 months of your symbol. Compare equity curves, drawdowns, and trade counts. Pick with eyes open.
  </Card>

  <Card title="Symbol selection" icon="coins">
    Run the same mode across `BTCUSDT`, `ETHUSDT`, `SOLUSDT`, etc. See which symbol the mode works on best. Add the winners to your active set.
  </Card>

  <Card title="Strategy refinement" icon="screwdriver-wrench">
    Authored a SignalEditor strategy that previews well? Run it against a longer historical window. If the deeper test holds up, promote to live with confidence. If it falls apart on out-of-sample data, refine.
  </Card>

  <Card title="Regime stress testing" icon="cloud-bolt">
    Pick known difficult windows — May 2021 crash, FTX collapse Nov 2022, March 2020 — and run your configuration against them specifically. See how it would have handled the worst.
  </Card>

  <Card title="Forward-test pipeline" icon="arrow-right">
    Backtest on 2024 → backtest on 2023 → forward-test on small capital for 2 weeks → if all three agree, promote to full capital. The deliberate four-step ladder.
  </Card>

  <Card title="Comparison reports" icon="chart-bar">
    Generate share tokens for 3-5 backtests of competing configurations. Send to a partner or advisor. They review the equity curves side-by-side and offer perspective.
  </Card>
</CardGroup>

## Operational footprint

<CardGroup cols={2}>
  <Card title="Resource cost (vendor-hosted)" icon="cloud">
    `€0` to you — the vendor runs it. Reasonable fair-use limits apply (most operators run far below the cap).
  </Card>

  <Card title="Resource cost (self-hosted)" icon="microchip">
    `500 MB – 2 GB` RAM per active backtest. With concurrency cap of `2`, plan for `1–4 GB` peak RAM. Most operators don't bother self-hosting.
  </Card>

  <Card title="Storage" icon="database">
    The Backtester maintains its own historical-candle database to avoid re-fetching from exchanges. Disk grows over time as you import more symbols and timeframes — typically `1–10 GB` for an active operator.
  </Card>

  <Card title="Failure mode" icon="triangle-exclamation">
    Backtester outages do not affect live trading. The TradingBot, SignalEditor, SignalsBot, TelegramBot, and Dashboard run completely independently. Backtesting just pauses until the service is back.
  </Card>

  <Card title="Throughput" icon="gauge">
    Most backtests complete in seconds to a minute. Multi-year `1m`-candle backtests take single-digit minutes. Throughput is concurrency-capped — don't expect to run 50 in parallel.
  </Card>

  <Card title="Determinism" icon="lock">
    Same inputs always produce same outputs. This is what makes share tokens reliable and what makes operator-to-operator comparison meaningful.
  </Card>
</CardGroup>

## Common questions

<AccordionGroup>
  <Accordion title="Why is the Backtester vendor-hosted by default?" icon="circle-question">
    Two reasons:

    **Compute economics.** A backtest can use significant CPU and RAM during its run. Centralizing the workload at the vendor's hosted service means your VPS doesn't need extra capacity for occasional backtests. Most operators run a couple of backtests a week — paying for `4 GB` extra RAM monthly to support that is wasteful.

    **Historical data caching.** The Backtester maintains a centralized cache of imported historical candles. When you ask for `BTCUSDT 1m from 2022-01-01`, the data is already there — instant access. If every operator self-hosted, every operator would re-fetch the same historical data from the exchange's API independently, hitting rate limits and slowing everyone down.

    Self-hosting is technically possible (the Docker images are available) — it makes sense for operators with serious privacy-of-strategy concerns or unusual workloads.
  </Accordion>

  <Accordion title="How long does a backtest take?" icon="circle-question">
    For typical workloads — a single mode, a single symbol, one year of `5-minute` candles — backtests complete in **a few seconds to a minute**. The compiled engine is fast.

    Longer windows (multi-year, `1-minute` candles) and complex SignalEditor strategies take longer — single-digit minutes. The Backtester reports progress, and you can poll its status endpoint or open the frontend to watch the run.
  </Accordion>

  <Accordion title="Can I backtest a strategy that hasn't been published live yet?" icon="circle-question">
    **Yes.** SignalEditor strategies can be exported as JSON and submitted to the Backtester directly. You don't need to "Start" a strategy to backtest it. The right workflow is: author → backtest → if good, start live; not author → start live → discover problems.
  </Accordion>

  <Accordion title="Are backtest results reproducible?" icon="circle-question">
    **Yes.** Same inputs (same symbol, same window, same mode/strategy, same fee, same slippage) always produce the same outputs. This is why backtests are share-tokenable — anyone with the URL sees the same numbers you saw.

    Reproducibility is also why the Backtester records the exact configuration of each backtest. You can always answer "what version of the strategy was this?"
  </Accordion>

  <Accordion title="How do share tokens work?" icon="circle-question">
    When you generate a share token for a backtest, the Backtester creates a long random URL that exposes the **read-only** view of that specific backtest. The share token does not expose your account, your other backtests, or any write capability — just this one backtest's results.

    Use share tokens to send results to a partner, your accountant, or a fellow operator without giving them API access. Revoke a share token at any time if you change your mind.
  </Accordion>

  <Accordion title="Do I get charged per backtest?" icon="circle-question">
    The vendor-hosted Backtester is included with your unCoded license. Reasonable fair-use limits apply — operators running hundreds of backtests a day may need a heavier-tier arrangement, but most operators are nowhere near those limits.

    If you're an academic or quantitative researcher running large parameter sweeps, contact support — there are paths for high-volume use.
  </Accordion>

  <Accordion title="Can the Backtester tell me which mode to use?" icon="circle-question">
    Not directly — there's no "find the best mode for me" button. But it gives you the data to choose: run several modes against the same symbol and window, compare the resulting equity curves and drawdowns, pick the one whose risk profile matches your risk tolerance.

    Many operators end up running **2–3 modes simultaneously** across different symbols, having validated each combination in backtest.
  </Accordion>

  <Accordion title="Can I backtest the SignalEditor's preview faster?" icon="circle-question">
    The SignalEditor's preview is for short windows (hours to a few weeks) and is designed for quick iteration. For multi-month or multi-year windows, the Backtester is faster and more rigorous.

    Workflow: iterate on the preview during authoring (fast, exploratory). Once you have a candidate strategy, run a long backtest against it (slower, definitive).
  </Accordion>

  <Accordion title="What happens if my backtest fails mid-run?" icon="circle-question">
    Failures (e.g., a worker crash, a database hiccup) are detected on Backtester startup. Stuck backtests are marked as `failed` so the system has a clean state. You can re-submit the same backtest; the Backtester will not double-bill for failed runs.
  </Accordion>

  <Accordion title="Is my strategy private when I submit a backtest?" icon="circle-question">
    The vendor-hosted Backtester sees your strategy JSON — you're submitting it for evaluation. Treat your strategy as you would treat anything you submit to a SaaS service.

    For operators with serious strategy-IP concerns, the self-hosted option keeps everything on your VPS. The trade-off is the operational overhead of running the Backtester yourself.
  </Accordion>

  <Accordion title="Can I parameter-sweep automatically?" icon="circle-question">
    The vendor-hosted Backtester does not have an automatic parameter-sweep feature. The right pattern is operator-side: write a small script that submits N backtests with varying parameters, then reads back the results.

    Beware curve-fitting (see pitfalls above). Parameter sweeps that pick "the best" historical performance often overfit. Use sweeps to find the **worst-acceptable** parameter combination, not the best.
  </Accordion>

  <Accordion title="How far back does the data go?" icon="circle-question">
    Standard symbols (`BTCUSDT`, `ETHUSDT`, etc.) typically have data going back to their listing date — for Bitcoin, this is years of `1m` candles. For newer symbols, data starts at the listing.

    If you need data older than what's already cached, the Backtester fetches it from the exchange on demand (subject to the venue's historical data limits — most exchanges keep at least a year of `1m` data, often more).
  </Accordion>
</AccordionGroup>

## Best practices

<Tip>
  * ✅ **Backtest before you start a new mode or strategy live** — catch the obvious failures cheap.
  * ✅ **Use realistic fee assumptions** — the default `0.075%` is correct for Binance with BNB; some venues are higher.
  * ✅ **Test multiple windows, not just one** — a strategy that works in 2023 but fails in 2022 is not robust.
  * ✅ **Look at drawdown, not just total return** — a `+30%` strategy with `-40%` mid-window drawdown is not the same as `+30%` with `-8%` drawdown.
  * ✅ **Forward-test on small capital after backtesting** — backtests are necessary, never sufficient.
  * ✅ **Don't over-optimize on the historical window** — if you tune a strategy until it fits 2024 perfectly, it almost certainly won't fit 2026.
  * ✅ **Re-run backtests after changing the mode** — never assume a tuned mode behaves the same as the default.
  * ✅ **Use share tokens for second opinions** — send your backtest to a partner or advisor for fresh eyes.
  * ✅ **Test on majors only** — backtests on long-tail altcoins suffer survivorship bias.
  * ✅ **Use realistic slippage** — `0.05%` on majors at moderate size, more for altcoins or larger size.
  * ✅ **Compare modes head-to-head** — same symbol, same window, different modes. The comparison is the answer.
  * ✅ **Document your backtest provenance** — which version of the strategy, what fee, what slippage, what window. Re-running 6 months later is much easier with documentation.
</Tip>

## What's next

<CardGroup cols={2}>
  <Card title="SignalEditor" icon="wand-magic-sparkles" href="/modules/signaleditor">
    Where you author the strategies you'll backtest.
  </Card>

  <Card title="Strategies & Modes" icon="boxes-stacked" href="/strategies/modes/overview">
    Pre-built modes you can run today — and backtest tomorrow.
  </Card>

  <Card title="TradingBot" icon="robot" href="/modules/tradingbot">
    Where validated strategies graduate to live execution.
  </Card>

  <Card title="Dashboard" icon="gauge" href="/modules/dashboard">
    Where you launch backtests from the UI and review results.
  </Card>

  <Card title="Operator Backtest Guide" icon="book" href="/operations/backtest-guide">
    A deeper, scenario-driven walkthrough of backtesting workflows.
  </Card>

  <Card title="Risk Management" icon="shield" href="/risk/overview">
    How to translate backtest drawdowns into live capital sizing.
  </Card>
</CardGroup>
