Quant BuffetRelax, Not Over Thinking

Quant Buffet API

Examples

Copy-paste strategy patterns for the lab.

1. SMA trend (from scratch)

from __future__ import annotations

import numpy as np
import pandas as pd

from backtest.data import load_daily_prices
from backtest.engine import EngineConfig, PortfolioEngine
from backtest.metrics import compute_metrics

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


def make_on_day(prices: pd.DataFrame):
    cols = [c for c in ASSETS if c in prices.columns]
    sma = prices[cols].rolling(200, min_periods=200).mean()
    state = {"last": None}

    def on_day(engine: PortfolioEngine, dt: pd.Timestamp) -> None:
        if sma.loc[dt].isna().all():
            return
        key = (dt.year, dt.month)
        if state["last"] == key:
            return
        state["last"] = key
        long = [s for s in cols if prices.at[dt, s] > sma.at[dt, s]]
        w = 1.0 / len(long) if long else 0.0
        engine.set_target_weights(dt, {s: w for s in long})

    ready = sma.dropna(how="all").index.min()
    return on_day, ready

2. Template-based SMA trend

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})

3. Cross-sectional mean reversion

from __future__ import annotations

import numpy as np
import pandas as pd

from backtest.data import load_daily_prices
from backtest.engine import EngineConfig, PortfolioEngine
from backtest.metrics import compute_metrics

ASSETS = ["SPY", "QQQ", "IWM", "TLT"]
LOOKBACK = 20
ENTRY_Z = 1.0


def make_on_day(prices: pd.DataFrame):
    cols = [c for c in ASSETS if c in prices.columns]
    rets = prices[cols].pct_change()
    mu = rets.rolling(LOOKBACK, min_periods=LOOKBACK).mean()
    sigma = rets.rolling(LOOKBACK, min_periods=LOOKBACK).std()
    z = (rets - mu) / sigma.replace(0, np.nan)
    state = {"last": None}

    def on_day(engine: PortfolioEngine, dt: pd.Timestamp) -> None:
        if z.loc[dt].isna().all():
            return
        key = (dt.year, dt.month)
        if state["last"] == key:
            return
        state["last"] = key
        # Buy recent losers (negative z), equal weight
        picks = [s for s in cols if z.at[dt, s] < -ENTRY_Z]
        w = 1.0 / len(picks) if picks else 0.0
        engine.set_target_weights(dt, {s: w for s in picks})

    ready = z.dropna(how="all").index.min()
    return on_day, ready

4. Local script (full pipeline)

from __future__ import annotations

import numpy as np
import pandas as pd

from backtest.data import load_daily_prices
from backtest.engine import EngineConfig, PortfolioEngine
from backtest.metrics import compute_metrics

ASSETS = ["SPY", "TLT"]

def make_on_day(prices: pd.DataFrame):
    ...

if __name__ == "__main__":
    prices = load_daily_prices(ASSETS, start="2010-01-01")
    on_day, ready = make_on_day(prices)
    engine = PortfolioEngine(prices, EngineConfig())
    result = engine.run(on_day, start=ready)
    print(compute_metrics(result.equity, trades_count=len(result.trades)))