← back to Freqle

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 test

The 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 code

Every 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 outdated below.
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_roi take-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_stop and 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. Like ml, 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 automatic strategy-updater rewrite 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 · instant

Reads 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() inside populate_* — applies the newest candle's value to every earlier row.
whole-series reduction
.max() / .min() / .mean() / .std() / .median() / .quantile() on a column with no rolling/ewm/expanding window — 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 / close inside populate_* — most often the Heikin-Ashi swap. Not a future-data leak: Heikin-Ashi is strictly causal. But freqtrade fills every order at open and simulates stop/ROI against high/low, so replacing them backtests against prices that never traded. It isn't random either — HA_open is 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/sell from a v3 populate_entry_trend/populate_exit_trend. v3 reads enter_*/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 stoploss is a price move, not a margin move. At 10× a stoploss = -0.296 is 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 the leverage() callback is ignored outright and shorts are silently dropped.
2. What can actually close a trade?
Check three things together: use_exit_signal, whether minimal_roi is empty, and whether populate_exit_trend does 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 1m is the fix.
3. Is any config dead?
exit_profit_only only applies to exit signals. With use_exit_signal = False it 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_count set?
ADX, RSI, ATR, MACD and every EMA are recursive: their value at a bar depends on every bar before it. Leave startup_candle_count at 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 .loc blocks 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's check_for_trade_entry requires not 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's chikou_span (which is literally close.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 senkou spans, 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 honest startup_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.

⚖️ Backtest trust check static, instant, free escalates to → 🔮 Lookahead runs the strategy, catches what static reading can't
🏆

Strategy League

ranked · fixed

A 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.

Actual sandbox command
freqtrade backtesting \ --user-data-dir /freqle/user_data \ --config /freqle/config.json \ --strategy <ClassName> \ --strategy-path /freqle/user_data/strategies \ --timerange 20210101-20260101 \ --enable-protections \ --export trades --cache none
Base config (config.json), right now
{
  "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 glossary

Every 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 period

The 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.

Actual sandbox command
freqtrade backtesting \ --user-data-dir /freqle/user_data \ --config /freqle/config.json \ --strategy <ClassName> \ --strategy-path /freqle/user_data/strategies \ --timerange &lt;period, one of the presets below&gt; \ --enable-protections \ --export trades --cache none
Selectable periods
  • 20200101-20210101 — 2020 · DeFi Summer & Pre-Halving Rally
  • 20210101-20220101 — 2021 · Institutional Bull Market
  • 20220101-20230101 — 2022 · Post-Bull Crash & Macro Tightening
  • 20230101-20250101 — 2023–2024 · Recovery & ETF Anticipation
  • 20250101-20260101 — 2025–2026 · Current Cycle

Walk forward

recent data

Out-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.

Actual sandbox command
freqtrade backtesting \ --user-data-dir /freqle/user_data \ --config /freqle/config.json \ --strategy <ClassName> \ --strategy-path /freqle/user_data/strategies \ --timerange 20260101-20260701 \ --enable-protections \ --export trades --cache none
🔮

Lookahead analysis

bias hunt · empirical

Runs 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.

⚖️ Backtest trust check static, instant, free ← same target, deeper method 🔮 Lookahead actually runs the strategy

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.

Actual sandbox command
freqtrade lookahead-analysis \ --user-data-dir /freqle/user_data \ --config /freqle/config.json \ --strategy <ClassName> \ --strategy-path /freqle/user_data/strategies \ --timerange 20210101-20260101 \ --lookahead-analysis-exportfilename /freqle/user_data/lookahead.csv
🔁

Recursive analysis

indicator drift

Runs 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.

Actual sandbox command
freqtrade recursive-analysis \ --user-data-dir /freqle/user_data \ --config /freqle/config.json \ --strategy <ClassName> \ --strategy-path /freqle/user_data/strategies \ --timerange 20210101-20260101

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 caveats

A 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.

🗂️

Job history

audit trail

Every 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 reading

This 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.

Still unclear on something? Get in touch or check the roadmap for what's planned next.