Quant Buffet API

Templates

The nine make_* factories in backtest.templates, with exact parameter names and rebalance cadence.

`backtest.templates` provides reusable **make_*** factories that return (on_day, ready) — the same contract your own make_on_day must satisfy. Import one and delegate instead of rewriting a common pattern.

Pattern

python
from backtest.templates import make_sma_trend

ASSETS = ["SPY", "QQQ", "IWM"]

def make_on_day(prices: pd.DataFrame):
    return make_sma_trend(prices, ASSETS, {"sma_days": 200})

Every factory has the identical signature: make_x(prices, assets, params) -> (on_day, ready). params is a plain dict and unknown keys are ignored silently, which makes a misspelled parameter name invisible — the run succeeds using the default.

Complete parameter reference

FactoryParameters (default)RebalanceBehaviour
make_sma_trendsma_days (200)MonthlyEqual weight across assets trading above their own SMA; cash when none qualify.
make_dual_mafast (50), slow (200)DailyEqual weight across assets whose fast MA exceeds their slow MA.
make_abs_momentumlookback (252)MonthlyEqual weight across assets with a positive trailing return.
make_dual_momentumlookback (252), cash_symbol ("BIL")MonthlySingle best performer if its return is positive, otherwise 100% cash.
make_momentum_rotationlookback (126), top_n (1), invert (falsy)MonthlyTop-N by trailing return; negative names are dropped unless that would empty the book.
make_equal_weightrebalance ("once" for buy-and-hold)Monthly, or onceEqual weight across every asset that has a price that day.
make_mean_reversionlookback (20), entry_z (-1.0), exit_z (0.0)DailyStateful: enters a name at or below entry_z, exits at or above exit_z.
make_vol_targettarget_vol (0.10), vol_lookback (63)MonthlyEach asset gets min(1, target / vol); normalised down if the total exceeds 1.
make_risk_parityvol_lookback (63)MonthlyInverse-volatility weights, normalised to sum to 1.

How each factory computes `ready`

Factoryready is
make_sma_trend, make_dual_maFirst date any SMA (the slow one) is non-NaN
make_abs_momentum, make_dual_momentum, make_momentum_rotationFirst date any trailing return is non-NaN
make_mean_reversionFirst date any z-score is non-NaN
make_vol_target, make_risk_parityFirst date any volatility estimate is non-NaN
make_equal_weightFirst date at least half the book has prices

Dispatching by name

python
from backtest.templates import TEMPLATES, build_strategy

sorted(TEMPLATES)
# ['abs_momentum', 'dual_ma', 'dual_momentum', 'equal_weight',
#  'mean_reversion', 'momentum_rotation', 'risk_parity',
#  'sma_trend', 'vol_target']

ASSETS = ["SPY", "EFA", "TLT", "GLD", "BIL"]

def make_on_day(prices: pd.DataFrame):
    # build_strategy(name, prices, assets, params) -> (on_day, ready)
    return build_strategy("dual_momentum", prices, ASSETS,
                          {"lookback": 252, "cash_symbol": "BIL"})

build_strategy(template, prices, assets, params) looks the name up in the TEMPLATES dict and raises ValueError: Unknown template: … for anything unrecognised. This is how the batch catalog runner drives all nine factories from JSON configuration.

Wrapping a template with your own overlay

Because factories return a plain callable, you can call one and then intercept its decisions — the cleanest way to add a market filter, a volatility cap, or a cash floor to a proven pattern.

python
from backtest.templates import make_momentum_rotation

ASSETS = ["XLK", "XLF", "XLE", "XLV", "XLI", "XLP", "XLU", "XLY", "BIL"]


def make_on_day(prices: pd.DataFrame):
    base_on_day, ready = make_momentum_rotation(
        prices, ASSETS, {"lookback": 126, "top_n": 3}
    )

    # Add your own risk overlay on top of a template
    spy_sma = prices["XLK"].rolling(200, min_periods=200).mean()

    def on_day(engine: PortfolioEngine, dt: pd.Timestamp) -> None:
        m = spy_sma.at[dt] if dt in spy_sma.index else np.nan
        if pd.notna(m) and prices.at[dt, "XLK"] < m:
            engine.set_target_weights(dt, {"BIL": 1.0})   # market filter: risk off
            return
        base_on_day(engine, dt)

    return on_day, ready

Custom parameters

python
from backtest.templates import make_abs_momentum

ASSETS = ["SPY", "EFA", "EEM", "TLT", "GLD", "BIL"]

def make_on_day(prices: pd.DataFrame):
    # make_abs_momentum reads only 'lookback' — top_n / cash_symbol are ignored
    return make_abs_momentum(prices, ASSETS, {"lookback": 126})