Method

How it works.

Every number in this app is computed by hand-written pandas and numpy — no backtesting library, no TA library. This page explains what those computations do and, more usefully, where they can mislead you.

How a backtest runs

Daily closing prices are fetched for your ticker and date range. The strategy turns those prices into a position for each day: +1 long, −1 short, 0 flat. That position is multiplied by the next day's return, transaction costs are deducted in proportion to how much the position changed, and the results are compounded into an equity curve starting at 1.0.

Everything else — drawdown, Sharpe, the monthly heatmap — is derived from that one series of daily net returns.

Why results here are lower than elsewhere

A signal computed from Monday's close cannot be traded at Monday's close — you only know it once the day is over. Every strategy here shifts its signal forward one bar, so a signal derived from Monday is acted on at Tuesday's price.

Skipping that shift is the single most common way a backtest manufactures returns that do not exist. It is also invisible: the equity curve simply looks better. If a result here is worse than one you got elsewhere from the same idea, this is the first thing to check.

The same principle governs stop-losses (evaluated at the close, so a bar that gaps through your stop costs you the whole gap) and volatility sizing (measured on a trailing window ending yesterday).

The three strategies

Momentum

Goes long when the rate of change over your lookback exceeds a threshold and price is above its moving average; short on the mirror condition. The moving average is a trend filter — it stops the strategy buying a bounce inside a downtrend.

MACD

Long while the MACD line is above its signal line, short while below. It holds the crossover state rather than trading only on the crossing bar, so the position series stays continuous.

Bollinger

The opposite stance: buys when price falls below the lower band and sells when it rises above the upper one. It fades extremes instead of following them, and is flat most of the time. Running it alongside momentum on the same data is the fastest way to see how much the answer depends on the question.

Each has its own page with the rule written out, its parameters and how it did on data it was never tuned on: Momentum, MACD, Bollinger.

Stops and position sizing

Stop-loss / take-profit

Flattens a position once it is down (or up) a set percentage from entry. Two things worth knowing: the exit happens at the close, so a −10% day against a 5% stop costs the full −10%; and once stopped out the position stays flat until the signal actually changes, rather than re-entering the next bar and paying costs for nothing.

Volatility targeting

Scales exposure to target ÷ trailing volatility, capped by max leverage. Smaller in turbulent markets, larger in calm ones. It usually lowers return and lowers drawdown more — whether that is an improvement depends on what you are optimising.

Portfolios

Portfolio mode runs the same strategy on 2–10 tickers and combines the legs. Each leg is a complete backtest with its own signals, stops, sizing and costs.

Only days on which every holding traded are used. That matters: if one name listed recently, the whole portfolio is measured over its shorter history, and the app tells you how many bars that cost. Filling in the missing days instead would invent flat returns and quietly understate the portfolio's risk.

The diversification ratio is the weighted average volatility of the legs divided by the volatility of the portfolio. 1.0 means your holdings move as one and you gained nothing by splitting between them.

Validation — the part that matters

Any strategy can be tuned until its backtest looks good. Two checks here are designed to find out whether that has happened.

Longer explanations, with examples: walk-forward validation, overfitting and the permutation test.

Walk-forward

Parameters are optimised on the first 70% of the period, then scored — untouched — on the remaining 30%. Only that second number is evidence. A strategy that posts a strong in-sample Sharpe and a negative out-of-sample one has been fitted to noise.

Permutation test

Your position series is reshuffled hundreds of times, holding total market exposure identical, so only the timing changes. If your real result sits comfortably inside the distribution of shuffled ones, the edge came from being in the market, not from choosing when.

Deflated Sharpe ratio

Walk-forward picks the best of a grid, and the best of any search looks good even with no edge. The winner's Sharpe is measured against the maximum that search would be expected to produce on noise, not against zero.

Probability of backtest overfitting

The period is cut into eight blocks and every balanced way of splitting them is tried. On each, the combination that wins one half is checked against the other. If it lands below the median at least half the time, the selection is no better than choosing at random.

Whole-grid test

The three checks above ask whether the chosen combination is real. This one asks whether anything in the grid beats simply holding the stock, with every combination inside one bootstrap so the winner is judged as the best of a search. Three readings of the same draw — White's Reality Check, Hansen's SPA, and a Romano–Wolf stepdown that names which cells, if any, survive.

Confidence intervals

Every metric carries a band from a block bootstrap of the returns — resampled in blocks so the autocorrelation survives. A Sharpe whose band still contains zero is not weak evidence of an edge; it is no evidence.

Reading the metrics

Sharpe ratio

Return per unit of volatility, annualised. Useful for comparing two strategies on the same data; close to meaningless in isolation, and badly distorted by short periods.

Max drawdown

The worst peak-to-trough fall in the equity curve. Usually the number that decides whether a strategy is actually holdable — most people abandon a system during its drawdown, which converts a paper loss into a real one.

Calmar ratio

Annualised return divided by max drawdown. A return-per-unit-of-pain measure that punishes the deep-hole strategies Sharpe can flatter.

Profit factor

Gross profit divided by gross loss. Above 1 means the winners outweigh the losers; below 1 means they do not, whatever the win rate says.

Win rate

The share of non-flat days that were positive. Deliberately not the headline number — a strategy can win 70% of days and still lose money if the losses are bigger.

What a backtest cannot tell you

Survivorship bias. Price history only exists for companies that still trade. Every ticker you can type here is one that survived; the ones that went to zero are absent from the data and from your results.

Idealised execution. Every trade fills at the closing price at a flat cost. Real slippage widens exactly when you least want it to — in fast markets, and when you are trading size.

One sample. A single backtest is one draw from a distribution of possible histories. It is not a forecast, and this app is not financial advice.

See a worked example →