Track entry dataframes and profits for trades in Freqtrade backtests with comprehensive indicator analysis and visualization.
Freqtrade Trade Optimization Tool is a comprehensive toolkit for analyzing Freqtrade trading strategies. It captures the exact market conditions (OHLCV + technical indicators) when trades are entered and records exit profits, then provides an interactive web-based dashboard for analyzing trade patterns and generating optimization recommendations.
Use only in backtests
π Open the Trade Optimization Tool Dashboard
β
Capture Entry Candles - Store complete OHLCV + indicators at trade entry
β
Track Exit Profits - Record profit/loss and exit reasons
β
Auto-Save Protection - Crash-safe incremental JSON saves
β
Interactive Analysis - Web-based dashboard with drag-and-drop JSON upload
β
Smart Recommendations - Identify winning indicator ranges and optimization opportunities
β
Demo Mode - Explore features without your own trade data
β
Professional Exports - Clean, structured JSON for further analysis
Original Concept: Vascenso Development
Implementation: AI assisted development
Community: We welcome contributions! Found a bug? Have an idea? Please open an issue or submit a PR!
No installation needed!
- Click the link above to open in your browser
- Click "β‘ Load Demo Data" to see example trades
- Or upload your own trade statistics in JSON file
- View complete trade analysis
- See entry indicator values
- Analyze winning vs losing trades
- Get optimization recommendations
- Copy the module to your Freqtrade strategy directory:
cp dataframe_trade_stats.py /path/to/freqtrade/user_data/strategies/- Import in your strategy:
from dataframe_trade_stats import DataframeTradeStatisticsclass YourStrategy(IStrategy):
def __init__(self, config: Dict):
super().__init__(config)
# Initialize trade statistics tracker
self.trade_stats = DataframeTradeStatistics(
enabled=config.get("store_trade_statistics", False) if config else False,
auto_save_on_exit=True,
strategy_name=self.version()
)Called when a buy order is filled:
def order_filled(self, pair: str, trade: Trade, order: Order,
current_time: datetime, **kwargs) -> None:
"""
Called every time an order has been fulfilled.
"""
if order.side == 'buy' and order.status == 'closed':
# Capture the entry candle with all indicators
if self.trade_stats.enabled:
dataframe, _ = self.dp.get_analyzed_dataframe(trade.pair, self.timeframe)
last_candle = dataframe.iloc[-1].squeeze()
self.trade_stats.store_entry_dataframe(
pair=pair,
trade=trade,
candle=last_candle,
current_time=current_time
)Called when exiting a trade:
def confirm_trade_exit(self, pair: str, trade: Trade, order_type: str,
amount: float, rate: float, time_in_force: str,
exit_reason: str, current_time: datetime, **kwargs) -> bool:
"""
Called right before a trade will be exited.
"""
if self.trade_stats.enabled:
self.trade_stats.store_exit_profit(
pair=pair,
trade=trade,
exit_rate=rate,
exit_reason=exit_reason
)
return TrueAdd to your backtest_config.json:
{
"store_trade_statistics": true
}See examples/example_strategy.py for a complete working example with entry/exit signals and trade statistics integration.
The module generates a structured JSON file with all trade data:
{
"metadata": {
"export_time": "2026-01-06T17:30:00.123456",
"total_trades": 150,
"trades_with_profit": 95,
"win_rate": "63.33%",
"mode": "final_export"
},
"trades": {
"enter_signal_BTC/USDT_2026-01-01T12:00:00": {
"entry_candle": {
"open": 42500.50,
"high": 42750.00,
"low": 42400.00,
"close": 42650.00,
"volume": 250.5,
"rsi": 65.2,
"bb_upper": 43100.0,
"bb_lower": 42200.0,
"macd": 250.5
},
"entry_price": 42650.00,
"entry_time": "2026-01-01T12:00:00+00:00",
"pair": "BTC/USDT",
"enter_tag": "enter_signal",
"amount": 0.5,
"profit": 0.0245,
"profit_abs": 125.50,
"exit_price": 43678.00,
"exit_reason": "exit_signal",
"trade_duration_candles": 12
}
}
}- Open
Trade-Statistics-Analysis.htmlin your browser - Choose your data source:
- π€ Upload your trade statistics JSON
- β‘ Load demo data to explore features
- Analyze your trades:
- View winning vs losing trades
- Identify successful indicator ranges
- Get optimization recommendations
- Apply recommendations to your strategy
- See results
- Aim is to remove negative profit trades
| Feature | Description |
|---|---|
| Trade Overview | Total trades, win rate, profit distribution |
| Winning Trades | Filter and analyze only profitable trades |
| Losing Trades | Understand what went wrong |
| Indicator Ranges | See ranges that work for winners/losers |
| Recommendations | Indicators optimization suggestions |
| Sorting | Sort by profit, confidence, or indicator name |
DataframeTradeStatistics(
enabled: bool = False,
auto_save_on_exit: bool = True,
output_dir: str = "user_data/trade_statistics",
strategy_name: str = "YourStrategy"
)Parameters:
enabled- Enable/disable statistics collection (default: False)auto_save_on_exit- Auto-save JSON after each trade exits (default: True)output_dir- Directory for output files (default:user_data/trade_statistics)strategy_name- Strategy name for file naming (default: "YourStrategy")
Store the last candle when a buy order is filled.
self.trade_stats.store_entry_dataframe(
pair='BTC/USDT',
trade=trade_object,
candle=dataframe.iloc[-1].squeeze(),
current_time=current_time
)Store profit when a trade is exited.
self.trade_stats.store_exit_profit(
pair='BTC/USDT',
trade=trade_object,
exit_rate=43678.00,
exit_reason='exit_signal'
)Export all collected trade statistics to JSON.
json_file = self.trade_stats.export_to_json(
output_path='my_trades.json'
)Returns: Path to the generated JSON file
Get a summary of collected statistics anytime.
summary = self.trade_stats.get_statistics_summary()
print(f"Total Trades: {summary['total_trades']}")
print(f"Win Rate: {summary['win_rate']}")
print(f"Avg Profit: {summary['avg_profit']:.2%}")Returns: Dictionary with statistics summary
Clear all stored trade data (useful for sequential backtests).
self.trade_stats.clear()Find the indicator ranges that correlate with winning trades. Use dashboard recommendations to refine your entry conditions.
Capture complete market conditions at entry. Replay exact scenarios to test improvements.
Analyze losing trades to understand failure patterns. Identify conditions to avoid.
Monitor strategy performance across multiple backtests. Compare win rates and profit metrics.
Verify that your technical indicators are working as expected. See the exact values when entries occur.
class MyStrategy(IStrategy):
def __init__(self, config: Dict):
super().__init__(config)
self.trade_stats = DataframeTradeStatistics(
enabled=True,
strategy_name=self.version()
)
def order_filled(self, pair: str, trade: Trade, order: Order,
current_time: datetime, **kwargs) -> None:
if order.side == 'buy' and order.status == 'closed':
if self.trade_stats.enabled:
dataframe, _ = self.dp.get_analyzed_dataframe(pair, self.timeframe)
last_candle = dataframe.iloc[-1].squeeze()
self.trade_stats.store_entry_dataframe(
pair, trade, last_candle, current_time
)
def confirm_trade_exit(self, pair: str, trade: Trade, order_type: str,
amount: float, rate: float, time_in_force: str,
exit_reason: str, current_time: datetime,
**kwargs) -> bool:
if self.trade_stats.enabled:
self.trade_stats.store_exit_profit(pair, trade, rate, exit_reason)
return TrueAdd to your strategy config:
{
"store_trade_statistics": true,
"user_data_dir": "user_data",
}By default, statistics are saved to: user_data/trade_statistics/
Custom directory:
self.trade_stats = DataframeTradeStatistics(
enabled=True,
output_dir="my_custom_path/trades"
)Solution: Verify that:
enabled=Truein DataframeTradeStatistics initializationstore_trade_statistics: truein configorder_filledandconfirm_trade_exitare properly implemented
Solution: Check that:
- Output directory exists or is writable
- Strategy reaches trade exit (check backtest results)
- Check logs for error messages
Solution:
- Ensure indicators are calculated in
populate_indicators() - Add sufficient history periods for indicator calculation
- Use
dataframe.iloc[-1].squeeze()to get complete last candle
We welcome contributions! Whether it's:
- π Bug reports
- π‘ Feature ideas
- π Documentation improvements
- π§ Code enhancements
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
- Python: 3.8+
- Freqtrade: Latest stable version
- Dependencies:
- pandas
- numpy
- freqtrade
This project is licensed under the MIT License - see LICENSE file for details.
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Freqtrade Community: Freqtrade Docs
- Freqtrade - Free, open source crypto trading bot
- β Star the repo if you find it useful!
- π Link to us in your strategy documentation
- π’ Share with the crypto trading community
Made with β€οΈ for the Freqtrade Community