Backtesting answers a narrow question: given this history and these assumptions, what would the coded rule have done? Risk management answers a broader one: what is the bot not allowed to do when the history is wrong, the API lies, or you simply have a bad month? Mixing the two — treating a pretty equity curve as a risk policy — is how automated systems get larger than the adult in the room intended.
This is a practical guide for founders and CTOs who will hire an agency to implement backtests and live limits. DayDecoder will write software and tests. We will not certify a strategy as good, and we will not promise that a backtest survives live markets.
Risk disclaimer. All trading can result in loss of capital. Backtests, walk-forward studies, Monte Carlo shuffles and paper trading are hypothetical. They omit or distort fills, gaps, liquidity, operational failures and your own behaviour. Leverage increases losses. This article is not investment, trading or financial advice. DayDecoder does not promise profits, a maximum drawdown, or that any bot will be profitable. Only risk money you can afford to lose. Use a regulated adviser if you need advice.
Key takeaways
- A backtest is a software test plus a research experiment. Keep the assumptions visible: costs, latency, session, adjusted prices.
- Look-ahead and survivorship are defects, not details. If you cannot explain the bar index, stop.
- Walk-forward and a true holdout beat a large optimiser on the same years you will brag about.
- Live risk is hard limits in the order path: size, frequency, loss, symbols, a kill switch. Charts are not limits.
- Position sizing rules belong in the spec. Hidden doubling is still doubling.
Build a backtest you could defend in an incident review
Write the assumptions at the top of the report: data vendor, timezone, when a bar is closed, spread model (fixed, historical, none — “none” is a confession), commissions, whether you use mid or conservative side, delay between signal and order, and whether orders can fail.
Point-in-time data for listings and delistings if you trade stocks. FX CFDs have their own fiction: the broker’s spread on a Monday is not a CSV from 2019. Crypto never sleeps, except when the exchange does.
The engine should share lot rounding and session maps with live code. If live uses the broker’s size step and the backtest uses float lots, you are testing a different bot. See architecture and the Python API build.
The usual ways a backtest lies
| Lie | What it looks like | What to do |
|---|---|---|
| Look-ahead | Using the close to trade the same bar’s low | Only use completed bars unless the spec is intra-bar with a model of path |
| Costs ignored | High turnover, beautiful curve | Put conservative costs in from the first run |
| Overfit | Twenty knobs, one pair, two years | Fewer knobs, holdout, walk-forward |
| Survivorship | Universe of names that still exist | Point-in-time membership |
| Fills at mid | Every stop “just got it” | Model adverse selection; never assume your stop is a right |
| Clock bugs | DST, session filters, “Friday close” | Store UTC, convert explicitly |
If a vendor shows only one equity curve and a “win rate”, ask for the assumption list and a losing period. If they will not show a losing period, they have not shown you a backtest. They have shown you a brochure.
Walk-forward, holdout, and when to stop touching it
Split time, not random rows. Trading data is serial. Train (or choose parameters) on window A, test on the following window B, roll. Do not merge B back in until you have recorded B’s result. A final holdout you never tune against is the only number that deserves a little trust, and even then it is still history.
More computer time is not more truth. The Strategy Tester’s genetic pass or a Python grid search will find a story. Your job is to make that expensive and embarrassing, not easy.
We do not publish target Sharpe ratios. A number like that on a marketing page would be a fabricated stat. Your own risk tolerance is a business decision: how much drawdown you can operationally stand without pulling the plug in a panic (which is also a trading decision).
Risk management that exists in code
Live limits we actually implement when asked:
- Max position per symbol and per book, in units the venue understands.
- Max gross notional if you can define it.
- Max orders per minute (protects you from loops).
- Max realised + marked loss per session, then flatten or pause. Marking needs a price you trust; if you don’t have one, pause.
- Allowlist of symbols and accounts.
- A kill switch a human can hit, and a watchdog if heartbeats fail.
- No new orders if reconciler is stale.
These limits should be tested with unit tests (“an order that would exceed X is rejected”) and with a staging dry-run. They should not be a Google Sheet.
Martingale, grid-without-cap, and “recover the loss with bigger size” are not risk management. If you still want them, they go in the spec in plain language so everyone knows what they signed. We will still put a hard cap on size, because unbounded size is a software defect even when it is a trading idea.
Paper, demo, live
Paper without the real broker’s matching is a unit test. Demo/sandbox with the broker is an integration test. Live is production, starting small. Promoting yourself because a week was green is not a process. See the MT5 VPS article for the operational twin of this idea.
Reporting without fiction
Report: number of trades, turnover, costs assumed, max drawdown in the test, time in market, and the assumption list. Do not report “accuracy” as if this were a classifier Kaggle. Do not put a backtest in a customer pitch as if it were a track record. In many places, hypothetical performance has specific advertising rules. We are not your compliance department; we will not dress a tester graph as a live result.
How we engage
A typical slice: implement the engine assumptions you chose, wire limits, tests, and a report template. Strategy research remains yours. Indicative effort is often a five-figure software piece when the venue and data are clear; research rabbit holes are not in that number. Indicative, not a quote.
DayDecoder, Ahmedabad, 10+ years, 103 customers. Email contact@daydecoder.com.
Costs, financing and overnight
If you hold overnight, swaps and financing can dominate a cute intraday backtest that assumed flat at 21:00 and then someone removed the flatten. Put the holding rule in the spec. Model conservative overnight costs. If you cannot get historical swap, say “unknown, live may be worse” rather than zero. Zero is a number, and people will believe it.
Borrow, locate, and corporate actions for equities are their own profession. If your bot is a CFD on an index, still read the broker’s overnight policy. If your bot is cash equities, do not skip actions because the CSV did not have a column.
Position sizing as a first-class module
Fixed fraction, volatility targeting, or fixed contracts — pick one and test it as code with fixtures. Changing size based on last trade’s win is often a disguised martingale. If you do it, name it. Caps still apply. A sizing module that can return “zero, risk budget spent” is a feature. A sizing module that always finds a way to be in the market is a smell.
Share sizing with live. If live is “1 contract because we are scared” and backtest is “2% of a million”, you learned nothing about the rule except that fear is a risk system you have not coded.
Operational risk is market risk
A VPS restart in a position, a daylight-saving misfire, a leftover unique-id generator that resets, a clock jump after a pause. These do not show in a CSV of closes. Add them to a pre-live checklist. Chaos on demo is cheaper than chaos on live. The MT5 VPS article and the Python API article are the same story in two process shapes.
People risk: only one person knows the kill switch; they are on a flight. Two people, a written path, a drill. Boring on purpose.
What we refuse to put in a report
We will not title a tester graph as if it were a live track record, and we will not dress research as a low-risk pitch. We will not hide the assumption list. We will not average ten overfit passes and call it robustness. If you need a marketing PDF, that is a different vendor. Our artefact is a report a sceptical engineer can rebuild.
Want a backtest engine and live limits, not a miracle curve? Custom software or contact. Bring the venue and the assumptions you are willing to print.
Stress, gaps and the days you should stare at
Pick ugly days on purpose: a flash crash analogue, a gap over a stop, a week of fat spreads, a holiday session. If the engine cannot represent a gap, it should say “unmodelled” rather than fill you at a fantasy price. Live, gaps happen. A backtest that never saw one is a children’s story.
Parameter sensitivity: nudge costs up, delay up, size down. If the story vanishes, you did not have a rule. You had a thin edge against a kind CSV. That is still allowed as research. It is not allowed as a live size argument.
We still will not call the surviving runs “robust alpha”. We will call them “less obviously broken under these stresses”. Hire us for that sentence. It is the honest one. If a partner needs a stronger sentence for a pitch, they need a regulated marketing review, not a louder engineer.
Keep a changelog of engine assumptions the way you keep a changelog of code. When you add commission, that is a new report, not an edit of the old PNG. People will compare PNGs across months and think the strategy improved. It may have been the fee model. Label the axes, the date range, and the git hash of the engine. Unlabelled equity curves are decoration. We will not put them on daydecoder.com as if they were results.
Handover checklist
Hand over the engine git hash, the assumption list, the holdout dates, the live limit table, unit tests for those limits, and a sample report with labelled axes. Include a written prohibition on pasting tester graphs into public pages as results — we follow it on daydecoder.com and we expect you to follow it in sales. Include the kill-switch drill date. If the drill never happened, the limits are theory. Theory does not flatten a book. Schedule the drill before any live key is issued, even at toy size. Toy size still teaches the people path, which is the path that fails first when someone is on a plane. Record who ran the drill and what broke; the notes are the next sprint, not an embarrassment to bury. If nothing broke, you probably did not pull a cable or expire a key; do it again until something harmless fails on demo.
FAQ
Which backtest library should we use?
Any engine you can inspect and that can share sizing rules with live. A famous library with look-ahead in the examples is worse than a small engine you understand. We will pick based on your stack, not a popularity list.
Can you make the backtest match live?
We can reduce obvious divergences (clock, size, costs). We cannot make a CSV equal a 2026 Monday spread. Anyone who guarantees a match is not doing engineering.
Is stop-loss a complete risk system?
No. Stops are an order type. They fail in gaps. Book-level limits and a kill switch are still required.
Should we use machine learning to size positions?
Only if you can test it without leaking the future, and still wrap it in hard caps. ML does not get a pass on the risk plane. See preparing your own model.
Will you publish our Sharpe on your website?
No. We do not publish fabricated or client performance stats. Your results stay yours.
Written by
Prakash Tank
Founder & Full-Stack Architect, DayDecoder
Prakash started as a certified Laravel developer and grew into a full-stack architect who owns a project end to end: requirements, architecture, development, deployment, and support. He designs event-driven systems with...
More about Prakash