How Freqle's checks actually work
A technical, illustrated walkthrough of every check on a strategy page — what it computes, what it looks like in the actual candles, and what a pass or fail means. Written for people who already know what a backtest is.
Code
reference, not a testThe strategy's source exactly as scraped from GitHub or uploaded. Every other tab below annotates or executes on this code.
class ToTheMoon(IStrategy): timeframe = '1h' can_short = True def populate_indicators(self, df, metadata): df['moon_phase'] = df['date'].apply(ephem_phase) return df
Tags
static · from the codeEvery pill under a strategy's title is a fact Freqle's tagger extracted
by reading the source. Most are
plain labels; method pills are the exception — clicking one jumps straight
to that method's definition in the Code tab, within the same file.
Structure
can_short- declares
can_short = True— trades both directions; needs futures/margin mode. timeframe- the strategy's own declared candle timeframe (e.g.
5m). Pins the sandbox's timeframe for this strategy, overriding the League/Backtest config's fallback. interface_version- freqtrade's strategy API version (1/2/3). 3 is current; lower needs a rewrite before it'll run — see
outdatedbelow. stoploss- the fixed stoploss value, e.g.
-0.99= a 99% max loss per trade allowed before force-exit. has_minimal_roi- declares a
minimal_roitake-profit ladder (time-based target profit %). startup_candle_count- how many warm-up candles it needs loaded before its first valid signal.
Risk & trade management
trailing- uses a trailing stop (
trailing_stopand friends). dca- position adjustment / averaging into an open trade (
adjust_trade_position). custom_stoploss- defines a dynamic stoploss method instead of (or alongside) the fixed one.
custom_exit- defines a custom exit method alongside (or instead of) plain signals.
protections- uses freqtrade's built-in Protections (cooldown, max-drawdown guard, etc.).
process_only_new_candles- only re-evaluates on fresh candles, not every tick — shown when explicitly set either way.
Multi-timeframe & hyperopt
informative_tfs- additional timeframes it pulls in as informative pairs — a multi-timeframe strategy.
hyperopt/hyperopt_params- defines hyperoptimizable parameters (
IntParameter,DecimalParameter, ...); the count is how many.
External libraries
indicator_libs- which TA libraries it imports — talib, pandas_ta, technical, qtpylib, finta, ta, scipy.
ml/ml_libs- imports a machine-learning stack (scikit-learn, PyTorch, XGBoost, ...). ML strategies are currently excluded from sandbox testing — the sandbox has no model libraries or trained model files — so they keep their code analysis and bias checks but get no League rank.
llm/llm_libs- calls out to an LLM API (OpenAI, Anthropic, ...) — detected via import or a matching API string.
freqai- actually starts FreqAI (
self.freqai.start(...)), not just defines the boilerplate feature-engineering methods. Likeml, currently excluded from sandbox testing: a FreqAI backtest trains a model per pair per window — hours of compute the sandbox doesn't allow. third_party_import- imports a module Freqle doesn't recognize or provide — likely to fail a backtest with a missing-module error unless an admin uploads it (see the admin Includes panel).
Detected content
method- notable methods/helpers the strategy defines itself (custom indicators,
custom_exit, ...) — click one to jump straight to its definition in the Code tab. indicator- canonical indicator names recognized in the code (RSI, EMA, MACD, Supertrend, ...).
concept- higher-level trading concepts recognized (grid, scalping, mean-reversion, DCA, ...).
Data quality
outdated/outdated_markers- uses deprecated pre-v3 method/attribute names (e.g.
populate_buy_trend). Freqle can attempt an automaticstrategy-updaterrewrite to the current API. parse_error- the file didn't even parse as valid Python — tags for it are best-effort, from a regex fallback instead of the real AST.
Backtest trust check
static · instantReads the source only — no candles, no sandbox — asking one question: can these backtest numbers be trusted? Three ways they can't be. The code saw the future (a leak), it traded at prices that never existed (a realism problem), or an indicator needs bars that hadn't printed yet — which is sometimes perfectly legitimate, so it's raised for review rather than scored against you.
Red = candles that haven't closed yet at decision time. The dashed arrow shows a signal that only fires because the code peeked at tomorrow's spike before it happened — invisible in a live run, invisible even in a correctly-built backtest, and exactly what this check exists to catch.
✕ leaks the future
df['next_close'] = df['close'].shift(-3) df.loc[df['next_close'] > df['close'], 'enter_long'] = 1
✓ causal
df['rsi'] = ta.RSI(df) df.loc[df['rsi'] < 30, 'enter_long'] = 1
Patterns it flags
- negative shift
.shift(-n)— pulls a future row back onto the current one.- centered rolling
rolling(center=True)— the window straddles the current candle, so it includes bars that haven't closed yet.- iloc[-1] / tail
.iloc[-1]or.tail()insidepopulate_*— applies the newest candle's value to every earlier row.- whole-series reduction
.max()/.min()/.mean()/.std()/.median()/.quantile()on a column with norolling/ewm/expandingwindow — e.g.df['close'] / df['close'].max(). The reduction spans the entire series, future included, so it's a normalization / z-score / threshold leak. The causal fix is a windowed version,df['close'].rolling(n).max().- OHLC overwrite realism warning
- Assigning over
open/high/low/closeinsidepopulate_*— most often the Heikin-Ashi swap. Not a future-data leak: Heikin-Ashi is strictly causal. But freqtrade fills every order atopenand simulates stop/ROI againsthigh/low, so replacing them backtests against prices that never traded. It isn't random either —HA_openis the midpoint of the previous synthetic body, so it lags below the real open in an uptrend and above it in a downtrend. Longs fill cheap, shorts fill rich, on every single trade. - dead v2 signal realism warning
- Writing the freqtrade v2 columns
buy/sellfrom a v3populate_entry_trend/populate_exit_trend. v3 readsenter_*/exit_*only, so that logic never fires — the backtest isn't inflated, it's measuring a strategy that doesn't trade.
Five things to check yourself
These cost no score — they're not bias, and the League already handles the leverage story through how it weights metrics. They're flagged because they're the questions worth asking before you trust any backtest, including your own. Every one is visible by reading the top of the file:
- 1. Does the leverage outrun the stoploss?
- freqtrade's
stoplossis a price move, not a margin move. At 10× astoploss = -0.296is 296% of your margin — you're liquidated near a 10% adverse move and the stop can never fire. Multiply the two: if you get past 100%, the stoploss is decorative. And only a futures/isolated backtest models liquidation at all; in spot theleverage()callback is ignored outright and shorts are silently dropped. - 2. What can actually close a trade?
- Check three things together:
use_exit_signal, whetherminimal_roiis empty, and whetherpopulate_exit_trenddoes anything. If all three are off, every exit is the stoploss or the trailing stop — and freqtrade's own docs call trailing-stop backtests optimistic, because within one candle it assumes price reached the high (ratcheting the trail up) before reversing to trigger. That's a best-case path, so the whole equity curve rests on the least reliable part of the simulator.--timeframe-detail 1mis the fix. - 3. Is any config dead?
exit_profit_onlyonly applies to exit signals. Withuse_exit_signal = Falseit does nothing at all. Settings that look like risk management but never execute are worth spotting — they tell you the author wasn't testing what they thought they were.- 4. Is
startup_candle_countset? - ADX, RSI, ATR, MACD and every EMA
are recursive: their value at a bar depends on every bar before it. Leave
startup_candle_countat its default of 0 and freqtrade trims no warmup, so the backtest opens with indicator values that could never exist live. Set it to a few times your longest period, then confirm with recursive analysis. - 5. Do several conditions share one
enter_tag? - The tag columns are
single columns, and
.locblocks run in source order — so a candle matching two conditions keeps only the last tag. Your per-tag breakdown then isn't measuring what it says. Worse, if a long and a short condition both fire on the same candle, freqtrade'scheck_for_trade_entryrequiresnot any([exit_long, enter_short])and takes no trade at all — signals vanish silently, in backtest and live alike.
Repainting indicators
A repainting indicator is one whose historical values change after the fact. On a chart it looks uncanny — it caught every top. In a live run it can't, because the value you backtested against didn't exist yet at that candle. Two different causes, and only one of them is a leak:
- needs future bars to confirm
argrelextrema,scipy.signal.find_peaks, ZigZag, Williams fractals, pivot points, and Ichimoku'schikou_span(which is literallyclose.shift(-26)). A pivot at bar i is only knowable once bars i+1…i+n have printed. Reading it at bar i is a genuine leak.- needs warmup, not future bars
- SuperTrend, Ichimoku's
senkouspans, and every Wilder-smoothed indicator (ADX, RSI, ATR) plus EMAs. These are causal — no future data — but recursive, so the value at a given candle depends on how much history preceded it. That's a backtest/live divergence, fixed with an honeststartup_candle_count, not a leak. This is what Recursive analysis measures.
Non-causal doesn't automatically mean cheating. argrelextrema
and friends have two entirely legitimate uses, and the static check can't tell them apart
from misuse — so it flags for review rather than calling it bias:
✓ confirmed pivot, shifted into the past
lows = argrelextrema(c, np.less_equal, order=5) # only knowable 5 bars later — so delay it 5 bars df['pivot_low'] = df['pivot_low'].shift(5)
✕ traded at the bar it was found
lows = argrelextrema(c, np.less_equal, order=5) df.loc[df['pivot_low'].notna(), 'enter_long'] = 1
The other legitimate use is ML label generation. If you're training a
model, finding the true historical tops and bottoms with future-looking code is exactly
right — that's what a supervised label is. It only becomes a leak if the label
bleeds into the feature set, or if the raw label is traded directly instead of the model's
prediction. FreqAI and ML strategies do this correctly all the time, which is why a bare
argrelextrema in populate_indicators is a prompt to check, not a
verdict.
A clean result means the code doesn't match any of these patterns — it's a lint, not proof of profitability. Subtler leakage (e.g. through an informative pair merged without a shift, or a helper that indexes ahead) can still slip past a static read; that's what Lookahead and Recursive exist to catch empirically, by actually running the strategy. A flagged line is clickable on the strategy page — it jumps straight to that line in the code.
Strategy League
ranked · fixedA full sandboxed backtest under one config every strategy shares — same pairs, same timerange, same wallet, stake and fee assumptions. That's the only reason the League Score is comparable across completely unrelated strategies.
Every canonical strategy is tested on the exact same stretch of history. You can't shrink, shift or cherry-pick this window — that's what keeps the ranking honest.
Re-run periodically by the background sweep as new candle data lands, so a League result can go stale — check its timestamp before trusting it.
FreqAI and other machine-learning strategies (tags freqai /
ml) are excluded from the League for now: the sandbox has no model libraries or
trained model files, and a FreqAI backtest would need hours of training compute per strategy.
They keep all their static analysis, tags and bias checks.
Backtests run with --enable-protections, so each strategy's own
protections (cooldowns, stoploss guards, max-drawdown halts) apply
exactly as they would in dry-run/live. freqtrade's backtester skips them by default;
Freqle turns them on so a strategy can't post a better number by ignoring the very risk
brakes it would actually be running with.
What the Score rewards
Every metric is percentile-ranked across the whole League, then weighted. The weighting deliberately leans on the metrics that leverage can't inflate: Sharpe, Sortino, profit factor and expectancy are all unchanged when you lever a strategy up, because levering scales the wins and the losses alike. CAGR isn't, and compounding makes it convex — so 10× leverage doesn't multiply it by ten, it multiplies it by a lot more. CAGR still counts, it just no longer dominates.
Calmar stops being credited past a 60% drawdown. Calmar is CAGR ÷ worst drawdown, and its denominator saturates at −100% while its numerator has no ceiling — so without a guard, a strategy that almost blew up scores like a spectacular one. Past that drawdown we stop crediting the ratio and let the drawdown term speak for itself.
Expectancy is freqtrade's own per-trade edge, normalised by the average loss. It's the term that separates a genuine edge from a high-frequency strategy whose average trade barely clears the fee — something a good win rate or profit factor can hide completely.
Bias: removed, or just penalised?
Depends entirely on how sure we are. Lookahead analysis re-runs the strategy on truncated data and compares the signals, so a positive is near-conclusive rather than a guess — and it's the only check that can catch a repainting indicator. Those strategies leave the League entirely: that backtest isn't comparable to an honest one at any multiplier, and removing it also stops it distorting everyone else's percentiles. The strategy page and all its results stay public; it simply isn't ranked.
The static Backtest trust check only reads the code, so it can be wrong — it scales the score instead of excluding: down to 30% for a future-data leak or an overwritten candle column, 70% for a signal wired to freqtrade v2 columns that never fires. Indicators merely worth a second look cost nothing at all.
Recursive analysis affects the ranking
not at all. It flags any indicator whose value shifts with how much history it's
given — which is true of every EMA, ADX, RSI and ATR ever written. It measures "uses a
recursive indicator", not "is cheating", and the fix is an honest
startup_candle_count, not a demotion.
Why the League admits some of it is luck
Ranking is a selection procedure. Test thousands of strategies against one dataset and the best-looking results are partly just the luckiest draws — with 1,000 strategies, about 50 will look "significant at p < 0.05" by chance alone. So each strategy's significance is Benjamini-Hochberg-corrected across the whole board, and /ranking reports roughly how many of its top 20 are expected to be false positives. That's a property of the field, not an accusation against any one strategy — so it's disclosed rather than charged to anybody's score. Deflating each score by the size of the field would punish an honest strategy for its neighbours merely existing.
{
"trading_mode": "spot",
"dry_run": true,
"timeframe": "5m",
"max_open_trades": 10,
"stake_currency": "USDT",
"stake_amount": 100,
"dry_run_wallet": 1000,
"exchange": { "name": "binance", "pair_whitelist": [ ...33 pairs ] },
"pairlists": [ { "method": "StaticPairList" } ]
}The exact 33 pairs — traded on Binance
BTC/USDTETH/USDTBNB/USDTSOL/USDTXRP/USDTADA/USDTDOGE/USDTTRX/USDTAVAX/USDTLINK/USDTDOT/USDTPOL/USDTLTC/USDTBCH/USDTUNI/USDTXLM/USDTATOM/USDTETC/USDTFIL/USDTAPT/USDTICP/USDTHBAR/USDTVET/USDTARB/USDTOP/USDTAAVE/USDTINJ/USDTCRV/USDTSNX/USDTCOMP/USDTSUI/USDTNEAR/USDTSEI/USDT
Candle data is sourced from Binance (spot & futures). It's the only exchange for now — more may be added later, at which point strategies would be tested per-exchange.
can_short strategies run the same command against "trading_mode":
"futures" instead, pairs get a :USDT settlement suffix, and
margin_mode: "isolated" is added.
Robustness
is the edge real?A single backtest is one roll of the dice — one ordering of trades over one slice of history. A big headline return can come from a genuine edge, or from a handful of lucky trades and a well-chosen window. Freqle's robustness check asks the harder question: would this strategy still work if the luck ran differently?
Monte Carlo — the range of outcomes
Freqle resamples the strategy's own trades 2,000 times with replacement (a bootstrap) and compounds each synthetic run, producing a distribution of results instead of a single number. A dependable edge gives a tight band of outcomes sitting well above zero; a fragile one gives a wide band that straddles break-even — the same headline return, but mostly luck. On the chart the shaded band is the 5th–95th percentile (darker = 25th–75th), the blue line the median, and the dashed red line marks break-even.
- likely annual return
- the median (50th percentile) annualised return across all resampled runs.
- range (5th–95th)
- how wide the plausible outcomes are — a narrow range is a more predictable strategy.
- chance of profit
- the share of resampled runs that ended in the black.
- worst-5% outcome (CVaR)
- the average result in the worst 5% of runs — your realistic bad case, not the theoretical worst.
- risk of ruin
- the fraction of runs whose account effectively went to zero.
MAE / MFE — how each trade behaved
Every trade is one dot: x = maximum adverse excursion (the worst unrealised loss it sat through — "heat taken", ≤ 0) and y = maximum favorable excursion (the best unrealised profit it reached before closing, ≥ 0). Wins are green, losses red. The shape of the cloud says a lot about entries and exits:
Each dot is one trade. Green wins cluster near the right edge (little heat taken before they worked) — clean entries. Red losses drift left — they sat through a lot of heat before the stop. A dot high up but red gave back open profit (exit too loose); a wall of dots pinned at the right edge with no heat at all is a lookahead red flag.
- wins hugging the y-axis (small MAE)
- clean entries — they barely went against you before working out.
- losses stretched far left (large negative MAE)
- trades that took heavy heat — a hint the stoploss is too loose or entries are early.
- dots high on the y-axis that still closed red
- gave back open profit — the exit or trailing stop may be too loose.
- a wall of dots pinned at MAE ≈ 0
- suspiciously clean fills — worth a second look for lookahead, since real trades almost always take some heat.
Significance — better than a coin flip?
A sign-permutation test keeps each trade's size but flips its win/loss at random, thousands of times, to model a strategy with no directional edge. The p-value is how often that no-edge coin-flip beats the real result. A low p (below 0.05) means the wins genuinely outweigh the losses beyond chance; a high p means the profit is hard to tell apart from luck.
The verdict
Freqle folds these signals — Monte Carlo outcome spread, significance, rolling-window consistency, drawdown, the buy-and-hold benchmark, and any confirmed lookahead/recursive bias — into one plain-language grade, so a big number can't paper over a fragile edge:
- Robust
- significant, consistent, profitable across resamples, and beats holding — no material concerns.
- Moderate
- broadly sound with one caveat worth reading before trusting it.
- Fragile
- several warning signs — the edge may not survive live trading.
- Unreliable
- confirmed bias, a real risk of ruin, or a stack of red flags — treat the numbers with deep suspicion.
The thresholds are applied at display time, so the verdict can be refined without re-running a single backtest. Monte Carlo, significance and the grading approach are adapted from kiploks' open-source robustness engine (Apache-2.0); a full run also lands in the downloadable report.
Reading the numbers
the stat glossaryEvery backtest and League row is a wall of numbers, and the same term means subtly different things across tools. Here's exactly what each one means on Freqle and how to read it — because no single number tells you whether a strategy is good. A huge return with a huge drawdown, or a 90% win rate that still loses money, are both common. Read them together.
How much it made
- Total return
- Net profit over the whole period as a % of the starting wallet. Simple, but not comparable across strategies tested over different-length windows.
- CAGR
- Compound Annual Growth Rate — the total return re-expressed as a steady yearly rate. This is the fair way to compare a 6-month run against a 3-year one.
How much it hurt
- Max drawdown
- The largest peak-to-trough drop in account equity across the run, as a %. This is the pain you'd have had to sit through — the number that decides whether you could actually hold the strategy. A 300% return with an 80% drawdown is a strategy almost nobody can stomach live.
- Avg / max trade duration
- How long positions are held. Context for whether the strategy's rhythm fits how closely you can watch the market.
Quality of the edge
- Win rate
- Share of trades that closed in profit. On its own it's misleading: a 90% win rate that gives it all back on the losing 10% still bleeds. Always read alongside profit factor and expectancy.
- Profit factor
- Gross profit ÷ gross loss. Above 1.0 makes money; ~1.3–1.5 is healthy; below 1.0 loses. Watch for a factor propped up by one or two monster trades — that's luck, not an edge.
- Expectancy
- Average profit or loss per trade. Positive means each trade adds value on average; it's win-rate and average-win/average-loss rolled into one honest number.
- Consistency
- Share of rolling sub-periods (e.g. months) that were profitable. A strategy whose entire year came from one lucky month scores low here even with a great headline return — exactly the fragility a single number hides.
Risk-adjusted return
These ask "how much return did you get per unit of pain?" — a smoother equity curve scores higher than a wild one with the same end result. Backtest values run optimistic, so treat them as relative rankings, not promises.
- Sharpe ratio
- Return per unit of total volatility. Rough read: below 1 is weak, 1–2 is decent, above 2 is strong. Penalises big up-swings as much as down-swings.
- Sortino ratio
- Like Sharpe, but only counts downside volatility — it doesn't punish a strategy for jumping in your favour. Usually the fairer of the two for trend-followers.
- Calmar ratio
- CAGR ÷ max drawdown — return earned per unit of worst-case pain. Rewards strategies that grow without deep valleys.
Versus just holding
- Market change
- How much the market itself moved over the same window — the buy-and-hold benchmark. If the market did +120%, beating that is the bar.
- vs Market (alpha)
- Strategy return minus that benchmark. Positive means the strategy actually added something over simply buying and holding; negative means you'd have made more with less effort (and less fee) by doing nothing. This is the single most honest "was it worth it?" number, and it's why a +50% strategy in a +200% bull market is really a losing bet.
DCA depth (dca-tagged strategies only)
A DCA (position-adjustment) strategy can average into a losing trade with extra entry orders instead of stopping out — great for smoothing an entry, but each extra order ties up more capital in one pair. These come straight from freqtrade's own backtest data, not a separate simulation.
- DCA orders (max / avg)
- How many extra entry orders (beyond the base order) a trade filled, across the whole backtest — the worst single trade and the average. A max far above the average means a handful of trades went deep while most didn't need to.
- Max stake exposure
- The single largest amount of capital any one trade tied up via DCA, and that peak as a multiple of the configured base stake (e.g. "6.2x base"). This is the real risk number for a DCA strategy: a high order count with a low stake multiple is fine; a high multiple means one pair going wrong can lock up most of the wallet.
The League ranks on a risk-and-bias-aware score, not raw return, for exactly these reasons — see Strategy League and Robustness.
Backtests
by periodThe same sandbox and pairs as the League, but over a period you choose instead of the League's fixed window — for checking a strategy against a stretch of market you specifically care about.
Same history as the League chart above, but you pick the slice — last 30/90/365 days, or a specific cycle. Not used for League ranking; ranking needs everyone on the same window.
20200101-20210101— 2020 · DeFi Summer & Pre-Halving Rally20210101-20220101— 2021 · Institutional Bull Market20220101-20230101— 2022 · Post-Bull Crash & Macro Tightening20230101-20250101— 2023–2024 · Recovery & ETF Anticipation20250101-20260101— 2025–2026 · Current Cycle
Walk forward
recent dataOut-of-sample validation on a recent window the strategy was never
checked against during its League run — not literally "up to today", but a separate
fixed range (sandbox_forward_timerange) set independently from the
League's own timerange, currently 20260101-20260701.
By default the two ranges happen to line up back-to-back, but they're two independently-configured settings, not one rolling window — an admin changing the League's timerange doesn't automatically move where walk-forward starts. A strategy that scores well on the left but falls apart on the right learned the historical window, not the market — the classic overfitting tell.
Same underlying freqtrade command as Backtests, just windowed to recent data by Freqle: freqtrade docs: Backtesting →
Lookahead analysis
bias hunt · empiricalRuns freqtrade's lookahead-analysis: re-executes the
strategy with the data after "now" perturbed, and checks whether any signal at "now"
changed. If it did, the strategy was reading candles it shouldn't have had yet.
Two runs, identical up to "now" (solid line). Only the candles after it differ (dashed vs dotted). A causal indicator (top) lands on the same value either way — a biased one (bottom) diverges, because it was quietly reading ahead.
Empirical and execution-based, where the Backtest trust check tab is static source scanning — it catches leakage the lint can't see just by reading the code. freqtrade docs: Lookahead analysis →
Recursive analysis
indicator driftRuns freqtrade's recursive-analysis: re-backtests the
same candle at the same timestamp, but with progressively less warm-up history feeding
the indicators, and compares the results.
Three runs, three different startup lengths, all evaluated at the same candle. A stable indicator lands on the same value regardless of warm-up (top); one with recursive bias drifts depending on how much history it got to "warm up" on (bottom) — a subtler, harder-to-spot cousin of lookahead bias.
freqtrade docs: Recursive analysis →
If this flags a strategy: the usual fix is raising startup_candle_count
so the indicator gets a longer, stable warm-up before it ever produces a signal — not
patching the indicator logic itself. But that only helps if the candle data actually
reaches back far enough to cover it; a bigger warm-up needs data starting earlier than
the backtest window itself, not just from its start date. Freqle's own candle download
already builds in a fixed lookback buffer before the earliest selectable period for
exactly this reason (see WARMUP_BUFFER_DAYS in backtest_config.py)
— if a strategy's warm-up genuinely exceeds that, its early results will still show the
same under-fed-indicator symptoms this check is designed to catch.
Remarks
practical caveatsA few things worth knowing when reading a strategy's numbers, not checks Freqle runs but real gotchas that affect how much to trust a backtest.
Trailing stop % scales with leverage
freqtrade's trailing_stop_positive (and stoploss)
are price-percentage thresholds — the leverage multiplier doesn't change that number, it
changes what a given price move does to your position. A 1% trailing stop is reasonable
at 1x, but that exact same 1% is ordinary price noise at 10x leverage — a strategy using
a 1% trail at 10x will get whipsawed out on moves that were never a real reversal. As a
rule of thumb, a tight trail needs to widen roughly in proportion to leverage (10x
leverage → something closer to a 10% trail), not stay fixed.
custom_exit / custom_stoploss / trailing / ROI on coarse timeframes
This is confirmed in freqtrade's own docs, not just a Freqle caveat:
per the Backtesting page's "Assumptions made by backtesting" section,
custom_exit() and custom_stoploss() are evaluated once per
candle on the strategy's main timeframe by default — not on the intra-candle price
movement a live bot would actually see. Trailing stops and ROI checks have the same
once-per-candle limitation. The coarser the timeframe, the bigger the gap: on a 4h
candle, that's one decision point standing in for what could have been up to 48 separate
5m checks in live trading — plenty of room for an exit that should have fired at minute
15 to instead ride out the whole 4h candle unnoticed. Freqtrade's own mitigation is the
--timeframe-detail option (re-evaluating those callbacks on a finer
timeframe while keeping the main one for entries) — Freqle doesn't currently enable
this, for every check, League included. Treat backtest numbers for strategies
leaning on these features at 30m+ timeframes with extra skepticism; a strategy with only
plain entry/exit signals and a fixed stoploss/ROI table is affected much less, since
those already check against the candle's own high/low regardless.
freqtrade docs: Backtesting — Assumptions made by backtesting →
Job history
audit trailEvery check ever queued or run for this strategy — status, timing, and a link to the raw sandbox output for each. Not a test itself; it's what you open when a result above looks off and you want to see exactly what actually ran.
Resources
keep readingThis page is about what Freqle checks and why. For the coding side — how to actually write a strategy that avoids these problems — see the Strategy Coding FAQ: overfitting, common freqtrade pitfalls (lookahead bias, recursive indicators, candle-timing quirks), trailing vs. fixed stops, and spot vs. futures mode.