The first time I backtested a trading strategy, it returned a 100% win rate. Six trades. I almost shipped it.
That number told me nothing, and neither does the number you get from any single backtest run. What matters is a filter — a small set of criteria that data has to pass before you are allowed to believe it. This post is the filter, applied to 14 real Freqtrade strategies over a 200-day window with exchange fees included.
The setup (so you can reproduce it)
- Data: 200 days of OHLCV for
BTC/USDT,ETH/USDT,SOL/USDT. - Fees: 0.1% worst-case, included in every result below.
- Window: 2026-03-10 → 2026-09-25, during which the market rose roughly +31%.
- Engine:
freqtrade backtesting --timerange 20260310-20260925 --breakdown.
freqtrade download-data --config config.json --days 200 --timeframes 5m 15m 1h
for s in $(ls user_data/strategies/*.py | xargs -n1 basename | sed 's/.py//'); do
freqtrade backtesting --config config.json --strategy "$s" \
--timerange 20260310-20260925 --breakdown > "logs/backtest_${s}.txt"
done
The filter
Three rules. Every one exists because of a specific way a backtest lies.
- Profit factor > 1.5. Profit factor is gross profit ÷ gross loss. Below 1.5, a handful of lucky trades is doing all the work.
- Max drawdown < 15%. A strategy you cannot psychologically survive is a strategy you will disable at the worst moment.
- Win rate > 55% on a usable sample. 100% on six trades is noise. I only count a strategy as a candidate above ~50 trades.
And one meta-rule: compare against buy-and-hold. In a market that rose 31%, a strategy returning +1.6% did not beat the market. It just lost less badly than a coin flip. That does not make it worthless — it makes it honest, and honesty is what you want in the thing that decides your entries.
The results
| strategy | tf | trades | win% | profit | profit factor | max drawdown | Sharpe |
|---|---|---|---|---|---|---|---|
| SwingHighToSky | 15m | 108 | 68.5% | +1.60% | 5.21 | 0.37% | 3.27 |
| BbandRsi | 1h | 62 | 74.2% | +3.99% | 1.38 | 7.92% | 0.62 |
| UniversalMACD | 5m | 6 | 100% | +1.48% | ∞ | 0.00% | 0.79 |
| CombinedBinHAndCluc | 5m | 9 | 100% | +1.41% | ∞ | 0.00% | 1.25 |
| ClucMay72018 | 5m | 7 | 85.7% | +0.51% | 6.52 | 0.09% | 0.73 |
Read it the way the filter demands:
-
UniversalMACDandCombinedBinHAndClucare traps. Perfect win rates, infinite profit factor — on 6 and 9 trades. An infinite profit factor means zero losing trades happened, which on a tiny sample is a coincidence, not an edge. -
BbandRsiearns the most in absolute terms (+3.99%) but its profit factor of 1.38 fails rule 1. Big return, fragile distribution. -
SwingHighToSkyis the only one passing all three rules with a sample worth trusting: 108 trades, 68.5% win rate, profit factor 5.21, drawdown 0.37%.
One strategy out of fourteen. That ratio is the point — not that there is a winner, but that the filter is strict enough to make the winner meaningful.
Why the Sharpe column matters more than profit
Profit tells you what happened. Sharpe tells you how rough the ride was. SwingHighToSky returns less in absolute terms than BbandRsi but with a Sharpe of 3.27 vs 0.62 — a far smoother equity curve. For a bot you intend to leave running unattended, smoothness is not a luxury; it is what stops you from turning it off.
What I did with this
I did not pick the highest number. I picked the one that survived the filter, put it in dry-run, and left it running — because the next question is not "which strategy wins" but "can I keep it running, trusted, for two weeks". That is an infrastructure problem, not a strategy problem, and it is the part most backtests never reach:
- the process has to come back if it dies,
- the P&L has to be recorded so you can compare weeks,
- a risk rule has to be enforced by code, not by willpower.
I packaged exactly that layer — a self-healing watchdog, a P&L logger, and a hard drawdown kill switch — into a small kit so you can put the filter's winner into a survivable setup in about five minutes.
→ Freqtrade Self-Healing Starter Kit ($9): the exact watchdog and config from this workflow, plus the setup guide.
The takeaway
Your backtest is a hypothesis generator, not an answer. Write the filter before you look at the results — otherwise you will rationalise the number you like. And always ask what the market did over the same window; a strategy that loses to buy-and-hold in a bull market still might be worth running, but only if you know that is what you are choosing.
This is part of a short series on running a trading bot you can actually leave alone.
The infrastructure layer (watchdog + P&L logger + drawdown kill switch) is packaged here:
Freqtrade Self-Healing Starter Kit — $9.
Related in this series:
- Run a self-healing crypto trading bot 24/7 for $0 (Freqtrade + watchdog)
- Keep any long-running Python process alive on Linux for $0 (the /proc watchdog pattern)
- Keep Freqtrade running 24/7: systemd vs a zero-dependency watchdog
The watchdog from this post is open source: **https://github.com/moon-hacks/proc-watchdog* (MIT).*
Prefer someone to just do it? I also offer a **done-for-you setup* (https://tntofficial.gumroad.com/l/vweza) — Freqtrade installed, backtested and left running in dry-run with the watchdog active.*
Top comments (0)