finlab.report
Backtest report module. The Report object is generated by the sim() function and contains complete backtesting performance information.
Use Cases
- View backtest performance (annualized return, Sharpe ratio, max drawdown)
- Analyze trade records (entry/exit dates, holding periods, returns)
- Visualize equity curves and drawdowns
- Save reports for future reference
- Upload to FinLab cloud for live tracking
- Run in-depth analysis (liquidity, stock selection ability, holdings distribution)
Quick Examples
Basic Usage: Generate and View Report
from finlab import data
from finlab.backtest import sim
# Build strategy and backtest
close = data.get('price:收盤價')
position = close > close.average(20)
# Generate backtest report
report = sim(position, resample='M')
# Display report (includes visualizations)
report.display()
Get Performance Metrics
# Get performance statistics (dict)
stats = report.get_stats()
print(f"Annualized return: {stats['daily_mean'] * 100:.2f}%")
print(f"Sharpe ratio: {stats['daily_sharpe']:.2f}")
print(f"Max drawdown: {stats['max_drawdown'] * 100:.2f}%")
print(f"Win rate: {stats['win_ratio'] * 100:.2f}%")
# Get structured performance metrics (nested dict)
metrics = report.get_metrics()
print(f"Annualized return: {metrics['profitability']['annualReturn']:.2%}")
print(f"Sharpe ratio: {metrics['ratio']['sharpeRatio']:.2f}")
print(f"Max drawdown: {metrics['risk']['maxDrawdown']:.2%}")
View Trade Records
# Get all trade records
trades = report.get_trades()
print(f"Total trades: {len(trades)}")
print(trades.head())
# Filter profitable trades
profitable_trades = trades[trades['return'] > 0]
print(f"Profitable trade ratio: {len(profitable_trades) / len(trades):.1%}")
Detailed Guide
See Backtesting Tutorial for: - Complete backtesting workflow - All methods of the Report object - Performance metric interpretation - Trade record analysis
API Reference
Report
The Report object is generated by the sim() function and contains complete backtesting performance information.
Key Report Methods:
| Method | Description | Usage Frequency |
|---|---|---|
display() |
Display full report (with visualizations) | Very High |
get_stats() |
Get performance statistics (dict) | Very High |
get_metrics() |
Get structured performance metrics (nested dict) | Very High |
get_trades() |
Get trade records (DataFrame) | Very High |
get_mae_mfe() |
Get Maximum Adverse/Favorable Excursion | High |
position_info() |
Get recent holdings and rebalancing info | High |
upload() |
Upload to FinLab cloud | Medium |
run_analysis() |
Run analysis plugin modules | Medium |
display_mae_mfe_analysis() |
Display volatility analysis charts | Medium |
to_text() |
Text format backtest summary | Low |
to_terminal() |
Terminal ASCII chart display | Low |
to_html() |
Export HTML report | Low |
to_pickle() / from_pickle() |
Save/load report | Low |
display()
Display the full backtest report including visualizations and performance metrics.
Usage Examples:
# Basic usage
report.display()
# Return plotly figure object for further customization
fig = report.display(return_fig=True)
fig.update_layout(title='My Strategy Backtest')
fig.show()
Display Contents: - Equity curve (strategy vs benchmark) - Drawdown curve - Performance metrics table (annualized return, Sharpe ratio, max drawdown, etc.)
get_stats()
Get performance statistics.
Signature: get_stats(resample='1d', riskfree_rate=0.02)
Returns: dict
Usage Examples:
stats = report.get_stats()
print(f"Backtest period: {stats['start']} ~ {stats['end']}")
print(f"Annualized return: {stats['daily_mean'] * 100:.2f}%")
print(f"Sharpe ratio: {stats['daily_sharpe']:.2f}")
print(f"Sortino ratio: {stats['daily_sortino']:.2f}")
print(f"Max drawdown: {stats['max_drawdown'] * 100:.2f}%")
print(f"Avg drawdown: {stats['avg_drawdown'] * 100:.2f}%")
print(f"Win rate: {stats['win_ratio'] * 100:.2f}%")
Key Return Fields:
| Field | Description |
|---|---|
start |
Backtest start date (str) |
end |
Backtest end date (str) |
daily_mean |
Annualized return |
daily_sharpe |
Sharpe ratio |
daily_sortino |
Sortino ratio |
max_drawdown |
Maximum drawdown |
avg_drawdown |
Average drawdown |
win_ratio |
Win rate |
return_table |
Monthly/yearly return table (dict) |
drawdown_details |
Max drawdown details |
get_metrics()
Get structured performance metrics (nested dict), suitable for cloud upload or programmatic processing.
Signature: get_metrics(stats_=None, riskfree_rate=0.02)
Returns: dict with the following categories:
Usage Examples:
metrics = report.get_metrics()
# Profitability metrics
print(f"Annual return: {metrics['profitability']['annualReturn']:.2%}")
print(f"Alpha: {metrics['profitability']['alpha']:.4f}")
print(f"Beta: {metrics['profitability']['beta']:.4f}")
# Risk metrics
print(f"Max drawdown: {metrics['risk']['maxDrawdown']:.2%}")
print(f"VaR: {metrics['risk']['valueAtRisk']:.2%}")
# Ratio metrics
print(f"Sharpe ratio: {metrics['ratio']['sharpeRatio']:.2f}")
print(f"Sortino ratio: {metrics['ratio']['sortinoRatio']:.2f}")
# Win rate metrics
print(f"Win rate: {metrics['winrate']['winRate']:.2%}")
Return Structure:
| Category | Fields | Description |
|---|---|---|
backtest |
startDate, endDate, feeRatio, taxRatio, tradeAt, market, freq |
Backtest settings |
profitability |
annualReturn, alpha, beta, avgNStock, maxNStock |
Profitability metrics |
risk |
maxDrawdown, avgDrawdown, avgDrawdownDays, valueAtRisk, cvalueAtRisk |
Risk metrics |
ratio |
sharpeRatio, sortinoRatio, calmarRatio, volatility, profitFactor, tailRatio |
Ratio metrics |
winrate |
winRate, m12WinRate, expectancy, mae, mfe |
Win rate metrics |
liquidity |
capacity, disposalStockRatio, warningStockRatio, fullDeliveryStockRatio |
Liquidity metrics |
get_trades()
Get detailed information for all trade records.
Returns: pd.DataFrame
Usage Examples:
trades = report.get_trades()
# Analyze trade performance
print(f"Total trades: {len(trades)}")
print(f"Average return: {trades['return'].mean():.2%}")
print(f"Win rate: {(trades['return'] > 0).mean():.2%}")
# Filter big wins or big losses
big_wins = trades[trades['return'] > 0.2]
big_losses = trades[trades['return'] < -0.1]
# Statistics by stock
trades_by_stock = trades.groupby('stock_id')['return'].agg(['count', 'mean'])
print(trades_by_stock.sort_values('mean', ascending=False).head())
DataFrame Columns:
| Column | Description |
|---|---|
symbol / stock_id |
Stock ticker (with name) |
entry_sig_date |
Entry signal date |
exit_sig_date |
Exit signal date |
entry_date |
Entry date |
exit_date |
Exit date |
position |
Holding weight |
period |
Holding days |
return |
Return rate |
trade_price@entry_date |
Entry price |
trade_price@exit_date |
Exit price |
mae |
Maximum adverse excursion during holding |
gmfe |
Maximum favorable excursion during holding |
bmfe |
Maximum favorable excursion before MAE |
mdd |
Maximum drawdown during holding |
pdays |
Number of profitable days |
industry |
Industry sector |
get_mae_mfe()
Get Maximum Adverse Excursion (MAE) and Maximum Favorable Excursion (MFE) analysis.
Usage Examples:
# Get MAE/MFE data
mae_mfe = report.get_mae_mfe()
# Visualize MAE/MFE distribution
report.display_mae_mfe_analysis()
Applications: - Setting stop-loss levels: Observe MAE distribution to determine a reasonable stop-loss threshold - Setting take-profit levels: Observe MFE distribution to determine a reasonable take-profit threshold - Optimizing entry/exit: If MFE is large but final return is small, take-profit is too late
MAE/MFE Interpretation
- MAE (Maximum Adverse Excursion): Maximum unrealized loss during holding period
- MFE (Maximum Favorable Excursion): Maximum unrealized profit during holding period
- Ideal scenario: Large MFE, small MAE, indicating good entry timing
upload()
Upload the backtest report to FinLab cloud for live tracking.
Usage Examples:
# Upload report
report.upload(name='My Momentum Strategy')
# View later on FinLab website:
# https://ai.finlab.tw/strategies
Prerequisites
- Must be logged in to FinLab (
finlab.login()) - Requires VIP membership
- After upload, can be used for live trading and performance tracking
run_analysis()
Run strategy analysis plugin modules.
Signature: run_analysis(analysis, display=True, **kwargs)
Usage Examples:
# Pass analysis module name (string)
report.run_analysis('Liquidity')
report.run_analysis('MaeMfe')
report.run_analysis('AlphaBeta')
report.run_analysis('PeriodStats')
report.run_analysis('Drawdown')
# Don't display charts, only get results
result = report.run_analysis('Liquidity', display=False)
See Strategy Analysis Modules for detailed documentation.
to_text()
Present the backtest report in text format, suitable for LINE notifications, logging, etc.
Signature: to_text(name=None)
Usage Examples:
to_terminal()
Display cumulative return ASCII chart and drawdown in the terminal.
Signature: to_terminal(height=12, width=80, show_benchmark=True, show_drawdown=True)
Usage Examples:
report.to_terminal()
# Custom height and width
report.to_terminal(height=8, width=60)
# Hide benchmark and drawdown
report.to_terminal(show_benchmark=False, show_drawdown=False)
Installation
Requires asciichartpy: pip install 'finlab[terminal]'
to_pickle() / from_pickle()
Save and load reports.
Usage Examples:
# Save report
report.to_pickle('my_strategy_report.pkl')
# Load report
from finlab.core.report import Report
loaded_report = Report.from_pickle('my_strategy_report.pkl')
loaded_report.display()
When to Use
- Backtesting takes a long time and you want to save results
- Need to compare backtest reports from different periods
- Share backtest results with others
FAQ
Q: How do I compare performance of multiple strategies?
report1 = sim(position1, resample='M')
report2 = sim(position2, resample='M')
stats1 = report1.get_stats()
stats2 = report2.get_stats()
import pandas as pd
comparison = pd.DataFrame({
'Strategy A': {
'Annual Return': stats1['daily_mean'],
'Sharpe Ratio': stats1['daily_sharpe'],
'Max Drawdown': stats1['max_drawdown'],
},
'Strategy B': {
'Annual Return': stats2['daily_mean'],
'Sharpe Ratio': stats2['daily_sharpe'],
'Max Drawdown': stats2['max_drawdown'],
}
})
print(comparison)
Q: Why can't I see my report?
# Cause 1: Forgot to call .display()
report # Does not display
report.display() # Correct
# Cause 2: Jupyter Notebook environment issue
# Add at the beginning of the notebook:
%matplotlib inline
# Cause 3: Data is empty (no trades)
trades = report.get_trades()
if len(trades) == 0:
print("Strategy has no trade records, please check entry conditions")
Q: How do I customize the report visualization style?
# Get plotly figure object
fig = report.display(return_fig=True)
# Change title
fig.update_layout(title='My Strategy Backtest Report')
# Change colors
fig.update_traces(line_color='blue', selector=dict(name='策略'))
# Adjust chart size
fig.update_layout(width=1200, height=600)
# Display
fig.show()
Resources
- Backtesting Tutorial - Complete backtesting workflow
- Strategy Analysis Modules - In-depth analysis tools
- Multi-Strategy Portfolio Management - Portfolio object
- Complete Strategy Development Workflow - End-to-end workflow