API reference
finlab.optimize
On this page5
finlab.optimize takes the stock-selection conditions you wrote, backtests every combination of them, and lines the results up side by side. Use it when you have several technical, fundamental or size conditions and want to know which ones work best together.
Which function to use
| You want to | Use |
|---|---|
| Backtest every combination of several conditions | sim_conditions() |
| Compare backtests you already ran | ReportCollection |
| Get the metric table for every combination | report_collection.get_stats() |
| Compare metrics as a bar chart or heatmap | report_collection.plot_stats() |
| Compare every combination's equity curve | report_collection.plot_creturns() |
Test every combination of conditions
sim_conditions()
Backtests every combination of the given conditions, so you can find the intersection that performs best.
from finlab import data
from finlab.optimize.combinations import sim_conditions
data.set_market('us')
with data.us_universe(index='SP500'):
close = data.get('price:adj_close')
revenue = data.get('us_income_statement:revenue')
operating_income = data.get('us_income_statement:operating_income')
# Define 3 conditions
c1 = (close > close.average(20)) & (close > close.average(60))
c2 = operating_income.pct_change(4) > 0 # Operating income up year over year
c3 = revenue.pct_change(4) > 0.1 # Revenue up more than 10% year over year
exits = close < close.average(20)
conditions = {'c1': c1, 'c2': c2, 'c3': c3}
# Test all combinations (7 total: c1, c2, c3, c1&c2, c1&c3, c2&c3, c1&c2&c3)
report_collection = sim_conditions(
conditions=conditions,
hold_until={'exit': exits, 'stop_loss': 0.1},
resample='M',
position_limit=0.1,
upload=False,
)
# Visual comparison
report_collection.plot_stats('heatmap') # Heatmap
report_collection.plot_stats('bar').show() # Bar chart
report_collection.plot_creturns().show() # Cumulative return line chart| Parameter | Type | Default | Description |
|---|---|---|---|
conditions | dict | required | Condition name to boolean DataFrame, e.g. {'c1': c1, 'c2': c2}. Names appear as the combination labels ('c1 & c2'). |
hold_until | dict or None | None | Keyword arguments for FinlabDataFrame.hold_until() applied to every combination, e.g. {'exit': exits, 'stop_loss': 0.1}. None backtests the combined condition directly. |
*args, **kwargs | Passed to finlab.backtest.sim(), e.g. resample='M', position_limit=0.1, upload=False. |
Returns a ReportCollection holding one backtest report per combination.
With n conditions it runs 2^n - 1 backtests: each condition alone, then every & intersection. Quarterly conditions are converted to filing dates before they are combined. A combination whose backtest fails is logged and skipped.
Compare backtest reports
ReportCollection()
Compares backtest reports side by side, to find the strongest strategy in a set or to see how a strategy changes as more conditions are stacked on it. sim_conditions() returns one; you can also build one from reports you already have.
from finlab import data, backtest
from finlab.optimize.combinations import ReportCollection
data.set_market('us')
close = data.get('price:adj_close')
position = close >= close.rolling(300).max()
collection = ReportCollection({
'monthly': backtest.sim(position, resample='M', upload=False),
'weekly': backtest.sim(position, resample='W', upload=False),
})
collection.get_stats()| Parameter | Type | Default | Description |
|---|---|---|---|
reports | dict[str, Report] | required | Name to backtest report, e.g. {'strategy1': sim(...), 'strategy2': sim(...)}. |
Attributes: reports (the dict passed in) and stats (the table from the last get_stats() call, None before).
report_collection.get_stats()
Returns the metric comparison table, one column per strategy and one row per metric, and also stores it in stats.
stats = report_collection.get_stats()
stats.loc[['cagr', 'daily_sharpe', 'max_drawdown']]| Metric | Description |
|---|---|
cagr | Annualized strategy return (compound, CAGR) |
daily_sharpe | Annualized Sharpe ratio |
daily_sortino | Annualized Sortino ratio |
max_drawdown | Maximum drawdown of the strategy (negative) |
avg_drawdown | Average drawdown (negative) |
ytd | Strategy return so far this year |
win_ratio | Share of winning trades |
avg_return | Average return per trade |
avg_mae | Average maximum adverse excursion per trade (negative) |
avg_bmfe | Average maximum favorable excursion reached before the maximum adverse excursion; higher values leave more room to take profit before a stop-loss |
avg_gmfe | Average maximum favorable excursion per trade |
avg_mdd | Average maximum drawdown per trade (negative) |
report_collection.plot_stats()
Visualizes the metric comparison table as a grouped bar chart or a heatmap.
report_collection.plot_stats('bar', indicators=['cagr', 'daily_sharpe']).show()
report_collection.plot_stats('heatmap', heatmap_sort_by='daily_sharpe')| Parameter | Type | Default | Description |
|---|---|---|---|
mode | str | 'bar' | 'bar': grouped bar chart per metric. 'heatmap': percentile rank of every metric across strategies. |
heatmap_sort_by | str or list[str] | 'avg_score' | Column(s) the heatmap is sorted by, descending. 'avg_score' is the average percentile across all metrics; higher means a stronger overall profile. |
indicators | list[str] or None | None | Show only these metrics; None shows all. |
Returns a plotly.graph_objects.Figure for 'bar', a styled pd.DataFrame for 'heatmap', and None for any other mode.
report_collection.plot_creturns()
Draws every strategy's cumulative return as a line chart, ordered by final value, to compare equity curves.
report_collection.plot_creturns().show()Returns a plotly.graph_objects.Figure.
Common questions
What is avg_score on the heatmap?
plot_stats('heatmap') turns each metric into a percentile rank across the strategies. avg_score is the average of those ranks, so a higher score means a stronger overall profile. The heatmap is sorted by avg_score by default; pass heatmap_sort_by to sort by another column or several.
Why doesn't the bar chart appear?
plot_stats('bar') and plot_creturns() return a plotly Figure; call .show() to draw it. The heatmap is a styled DataFrame and displays on its own in a notebook.
Next steps
- Strategy parameter optimization Why optimize, how to read the 12 metrics, how to avoid overfitting, and pairing it with stops.
- Backtesting Rebalance frequency, stops and trading costs in
sim(). - Complete strategy workflow From research and optimization to live trading.
Source code is on GitHub.
Was this page helpful?