- Platform
- Crypto trading bot
- License
- GPL-3.0
- Pricing
- Free and open source
- Live trading
- Dry-run and live execution on supported exchanges
- Best for
- Candle-based crypto strategies that need backtesting, dry-run validation, monitoring, and live exchange execution in one tool
Freqtrade is a practical choice for a candle-based crypto strategy that must move from historical testing to a live-data dry run and then exchange execution. The same Python strategy class supplies indicators and entry or exit signals in each mode. Its backtester is useful for screening and debugging, but its fill model is intentionally simpler than a live exchange.
How the workflow fits together
Freqtrade reads OHLCV candles, calculates a strategy's indicators and signals, and then simulates or submits trades. Backtesting loops through stored candles. Dry-run mode consumes live exchange data and records simulated orders. Live mode submits orders through CCXT and stores operational state in a database. FreqUI, a REST API, Telegram integration, and exported backtest results support analysis and monitoring.
The supported exchange table distinguishes exchanges and modes tested by the maintainers from the wider set exposed by CCXT. Spot, futures, margin modes, and on-exchange stop-loss support vary by venue. Moving a strategy between exchanges therefore requires a capability and market-structure review, not only a configuration change.
| Stage | What it establishes | What it does not establish |
|---|---|---|
| Backtest | Signal behavior on downloaded candles under documented assumptions | Real fill probability, latency, market impact, or live reliability |
| Hyperopt | Parameters that score well under a chosen loss and data range | Out-of-sample performance or protection from multiple testing |
lookahead-analysis and recursive-analysis |
Common future-data and unstable-indicator problems | Absence of every possible leak or implementation error |
| Dry run | Live-data signal timing and bot operations without real orders | Actual queue position, rejection behavior, slippage, or exchange custody risk |
| Live trading | Behavior with real exchange orders and balances | Future profitability or safety without independent monitoring and limits |
A complete pinned backtest example
This small fixture makes Freqtrade's candle timing visible. It uses the official freqtradeorg/freqtrade:2026.8 image, one static spot pair, twelve synthetic five-minute candles, a 1,000 USDT stake, a 10,000 USDT starting wallet, and an explicit 0.1% fee charged on entry and exit. Create this layout:
user_data/
config.json
data/binance/BTC_USDT-5m.json
strategies/DocumentedCross.py
Save the following as user_data/config.json. The empty API fields are intentional because this is a backtest. Do not place real credentials in a committed configuration file.
{
"$schema": "https://schema.freqtrade.io/schema.json",
"max_open_trades": 1,
"stake_currency": "USDT",
"stake_amount": 1000,
"tradable_balance_ratio": 1.0,
"dry_run_wallet": 10000,
"timeframe": "5m",
"dry_run": true,
"entry_pricing": {
"price_side": "same",
"use_order_book": false,
"order_book_top": 1,
"price_last_balance": 0.0,
"check_depth_of_market": {
"enabled": false,
"bids_to_ask_delta": 1
}
},
"exit_pricing": {
"price_side": "same",
"use_order_book": false,
"order_book_top": 1
},
"exchange": {
"name": "binance",
"api_key": "",
"secret": "",
"ccxt_config": {},
"ccxt_async_config": {},
"pair_whitelist": ["BTC/USDT"],
"pair_blacklist": []
},
"pairlists": [{"method": "StaticPairList"}]
}
Freqtrade's JSON candle rows contain Unix milliseconds, open, high, low, close, and volume. Save these constructed bars as user_data/data/binance/BTC_USDT-5m.json:
[
[1767225600000, 100000, 100500, 99500, 100000, 10],
[1767225900000, 100000, 101500, 99800, 101000, 10],
[1767226200000, 101000, 102500, 100800, 102000, 10],
[1767226500000, 102000, 103500, 101500, 103000, 10],
[1767226800000, 103000, 104500, 102500, 104000, 10],
[1767227100000, 104000, 105500, 103500, 105000, 10],
[1767227400000, 105000, 105200, 103800, 104000, 10],
[1767227700000, 104000, 104200, 102800, 103000, 10],
[1767228000000, 103000, 103200, 101800, 102000, 10],
[1767228300000, 102000, 102200, 100800, 101000, 10],
[1767228600000, 101000, 101200, 99800, 100000, 10],
[1767228900000, 100000, 100200, 98800, 99000, 10]
]
Save the strategy as user_data/strategies/DocumentedCross.py:
from pandas import DataFrame
from freqtrade.strategy import IStrategy
class DocumentedCross(IStrategy):
INTERFACE_VERSION = 3
timeframe = "5m"
can_short = False
startup_candle_count = 1
minimal_roi = {"0": 10.0}
stoploss = -0.50
trailing_stop = False
use_exit_signal = True
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
return dataframe
def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
dataframe.loc[
(dataframe["close"] > 101_500)
& (dataframe["close"].shift(1) <= 101_500)
& (dataframe["volume"] > 0),
["enter_long", "enter_tag"],
] = (1, "cross_above_101500")
return dataframe
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
dataframe.loc[
(dataframe["close"] < 104_500)
& (dataframe["close"].shift(1) >= 104_500)
& (dataframe["volume"] > 0),
["exit_long", "exit_tag"],
] = (1, "cross_below_104500")
return dataframe
Run it from the directory containing user_data:
docker run --rm \
-v "$PWD/user_data:/freqtrade/user_data" \
freqtradeorg/freqtrade:2026.8 backtesting \
--config /freqtrade/user_data/config.json \
--strategy DocumentedCross \
--data-format-ohlcv json \
--timerange 20260101-20260102 \
--fee 0.001 \
--cache none \
--export trades \
--backtest-directory /freqtrade/user_data/backtest_results
The exact fixture produced one trade. The report and exported trade record contained:
Entry: 2026-01-01 00:15 UTC at 102000.0
Exit: 2026-01-01 00:35 UTC at 104000.0
Fee: 0.1% on entry and exit
Profit: 17.5812 USDT (1.7571% of the trade)
Final wallet: 10017.5812 USDT
The signal above 101,500 occurs on the 00:10 candle, then the entry fills at the next candle's 102,000 open. The signal below 104,500 occurs at 00:30, then the exit fills at the 00:35 open of 104,000. --cache none prevents an older result from hiding code or data changes, and the exported archive preserves the resolved configuration and strategy.
This is an execution-semantics fixture, not a strategy. The thresholds, prices, volume, permissive stop loss, and effectively disabled return-on-investment exit were constructed to force one readable trade. The positive result and 100% win rate have no statistical meaning. The backtester does not use the volume to limit this fill and applies no spread or slippage. Although candles are local, Freqtrade still contacted Binance during this run to load current market metadata, so it does not recreate January 2026 precision or minimum-order rules and requires network access unless those internals are supplied another way.
Backtesting assumptions that matter
The official backtesting documentation states that an entry normally fills at the candle open. An order at a custom price fills without slippage when that price falls inside the candle's high-low range. Exit signals normally fill at the next candle's open, and exit signal, stop loss, return-on-investment, and trailing-stop conditions follow a defined priority when they collide within one candle.
Fees are included, using exchange market information by default or an explicit --fee ratio applied on entry and exit. The backtester does not reconstruct order-book depth, queue priority, latency, rejected orders, or price impact. It also uses current trading limits and precision because historical exchange limits are unavailable. Dynamic pairlists can reflect current rather than historical membership, so the documentation recommends a static pairlist for reproducible tests.
Backtest results may be cached for a day. Changes in imported strategy modules or newly downloaded data may not invalidate the cache, so use --cache none when validating a changed experiment. Archive the candle data and dependency versions as well as Freqtrade's exported strategy and sanitized configuration.
Hyperopt and FreqAI need separate validation
Hyperopt searches strategy parameters against a selected loss function. Repeatedly optimizing and inspecting the same period creates multiple-testing bias, even when the command runs correctly. Reserve untouched evaluation data and prefer rolling or walk-forward checks across different market regimes.
FreqAI trains supervised prediction models from user-defined features and targets. It can simulate periodic retraining in historical tests and retrain in a background thread during dry or live runs. The tooling does not make arbitrary features safe. FreqAI's documentation warns that feature engineering runs across the full training timerange, so a feature that reads future values can still leak information. Model retraining also adds compute, dependency, staleness, and monitoring requirements.
Futures and live-operation risks
Futures support is venue-specific. Funding data may be unavailable for older periods, liquidation fees are not included, and cross-position effects may be incomplete in dry-run or backtesting mode. The leverage documentation also assumes that Freqtrade is the only system trading the leveraged account. These limits can materially change profit and liquidation estimates.
Before enabling live mode, use a separate exchange subaccount with restricted API keys, withdrawals disabled, conservative exposure limits, and exchange-supported stop losses where available. Monitor clock synchronization, connectivity, rejected or stale orders, database health, balances, and discrepancies between local and exchange state. A dry run is necessary, but it cannot reproduce real fills or custody failures.
Maintenance and license
Freqtrade is licensed under GPL-3.0. Review the license with counsel if modified code will be distributed as part of a proprietary product. Keeping current matters operationally because exchange APIs and rules change, but upgrades should first be tested against pinned data and a dry-run deployment.
When to choose Freqtrade
Choose Freqtrade for systematic crypto strategies expressed on candles when an integrated backtest, optimization, dry-run, monitoring, and execution workflow is more important than order-book simulation. Choose an execution-focused engine such as NautilusTrader when tick ordering, latency, or detailed order lifecycle drives the result. Compare Freqtrade with Jesse separately when both candle-based crypto bot workflows fit the project.