Skip to content

Comparing results

Comparing Results

ti.run() accepts multiple Backtest objects. When more than one is passed, all result methods automatically switch to comparison mode — metrics become a DataFrame and charts overlay all portfolios.

Side-by-Side Comparison

import tiportfolio as ti

tickers = ["QQQ", "BIL", "GLD"]
data = ti.fetch_data(tickers, start="2019-01-01", end="2024-12-31")

monthly = ti.Backtest(
    ti.Portfolio('monthly', [ti.Signal.Monthly(), ti.Select.All(), ti.Weigh.Equally(), ti.Action.Rebalance()], tickers),
    data,
)
quarterly = ti.Backtest(
    ti.Portfolio('quarterly', [ti.Signal.Quarterly(), ti.Select.All(), ti.Weigh.Equally(), ti.Action.Rebalance()], tickers),
    data,
)
yearly = ti.Backtest(
    ti.Portfolio('yearly', [ti.Signal.Schedule(month=1), ti.Select.All(), ti.Weigh.Equally(), ti.Action.Rebalance()], tickers),
    data,
)

result = ti.run(monthly, quarterly, yearly)

Summary Table

result.summary()
# Returns pd.DataFrame — rows are metrics, columns are portfolio names:
#
#                    monthly  quarterly  yearly
# total_return        0.42      0.39      0.35
# cagr                0.07      0.065     0.059
# sharpe              0.91      0.88      0.84
# max_drawdown       -0.18     -0.19     -0.21
# ...

For the full metric set:

result.full_summary()
# Same shape — pd.DataFrame with all daily/monthly/yearly stats per portfolio

Overlaid Charts

result.plot()                  # equity curves on one chart, one line per portfolio
result.plot_histogram()        # return distributions overlaid
result.plot_security_weights() # separate panel per portfolio

Accessing Individual Results

Both positional index and portfolio name work regardless of how many tests were run:

result[0]              # first portfolio's BacktestResult
result["monthly"]      # by portfolio name
result["quarterly"].summary()   # individual summary DataFrame (single column)
result["quarterly"].trades      # individual trades DataFrame

This means you can always write result[0] even for a single backtest, making it easy to add comparisons later without changing the rest of your code.

Comparing Allocation Strategies

A common pattern — test the same schedule with different weighting methods:

import pandas as pd

tickers = ["QQQ", "BIL", "GLD"]
data = ti.fetch_data(tickers, start="2019-01-01", end="2024-12-31")

def monthly_portfolio(name, weigh_algo):
    return ti.Backtest(
        ti.Portfolio(name, [ti.Signal.Monthly(), ti.Select.All(), weigh_algo, ti.Action.Rebalance()], tickers),
        data,
    )

result = ti.run(
    monthly_portfolio("equal_weight",    ti.Weigh.Equally()),
    monthly_portfolio("fixed_ratio",     ti.Weigh.Ratio(weights={"QQQ": 0.7, "BIL": 0.2, "GLD": 0.1})),
    monthly_portfolio("vol_target",      ti.Weigh.BasedOnHV(initial_ratio={"QQQ": 0.7, "BIL": 0.2, "GLD": 0.1}, target_hv=0.60, lookback=pd.DateOffset(months=1))),
    monthly_portfolio("erc",             ti.Weigh.ERC(lookback=pd.DateOffset(months=3))),
)

result.plot()
result.summary()