finlab.report
Backtest report module. The Report object is generated by the sim() function and contains complete backtesting performance information.
Custom benchmark
Before running the backtest, subclass the strategy's Market (for example TWMarket), override get_benchmark() to return a date-indexed price or cumulative-value Series, and pass the market to sim(). This example uses adjusted 0050 prices:
from finlab import data
from finlab.backtest import sim
from finlab.markets.tw import TWMarket
class ETFBenchmarkMarket(TWMarket):
@staticmethod
def get_benchmark():
return data.get("etl:adj_close")["0050"]
close = data.get("price:收盤價")
position = close > close.average(20)
report = sim(position, market=ETFBenchmarkMarket(), resample="M", upload=False)
report.display()
Return prices or cumulative values covering the backtest period, not daily returns. To change the benchmark, rerun sim() with the new Market; assigning report.benchmark = ... afterward does not update all charts and statistics. US strategies can subclass USMarket or USFundMarket the same way (for example returning data.get("us_fund_price:adj_close")["SPY"]). See Market and the custom market guide for the full get_benchmark() contract.
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(path, title=None) |
Export HTML report with an optional browser tab title | 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