§ For MAIF members

From strategy to scorecard.

Plug your strategy into the pipeline and get back a four-page scorecard. The framework handles position sizing, execution, frictions, Monte Carlo, engine cross-validation, and reporting. You bring the logic.


§ 01 quickstart.py

The whole thing.

A full example, end to end. Copy, replace MyStrategy with your own logic, and run. The output is my_scorecard.png in the current directory.

"""
QUICKSTART: Add your own strategy and get a full scorecard.
"""

from datetime import date
import pandas as pd
from strategy.base import Strategy, Signal


# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# STEP 1: Write your strategy
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

class MyStrategy(Strategy):

    @property
    def name(self) -> str:
        return "My Strategy"

    def generate_signals(self, df: pd.DataFrame) -> pd.Series:
        signals = pd.Series(Signal.HOLD, index=df.index)

        # ── YOUR LOGIC HERE ────────────────────────────────
        # Example: buy when 10-day SMA crosses above 30-day SMA
        fast = df["Close"].rolling(10).mean()
        slow = df["Close"].rolling(30).mean()

        for i in range(1, len(df)):
            if fast.iloc[i] > slow.iloc[i] and fast.iloc[i-1] <= slow.iloc[i-1]:
                signals.iloc[i] = Signal.BUY
            elif fast.iloc[i] < slow.iloc[i] and fast.iloc[i-1] >= slow.iloc[i-1]:
                signals.iloc[i] = Signal.SELL
        # ───────────────────────────────────────────────────

        return signals


# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# STEP 2: Fetch data and generate your scorecard
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

from data_layer import DataLayer, YahooFinanceProvider
from backtester import BacktestConfig, generate_scorecard, EventDrivenBacktester

dl = DataLayer()
dl.add_provider(YahooFinanceProvider())
df = dl.fetch("SPY", date(2022, 1, 1), date(2025, 12, 31))

config = BacktestConfig(
    initial_capital=100_000,
    commission_per_order=1.00,
    slippage_bps=2.0,
)

ed_result = EventDrivenBacktester(config).run(MyStrategy(), df)

generate_scorecard(
    strategy=MyStrategy(),
    df=df,
    config=config,
    output_path="my_scorecard.png",
    event_driven_results={"result": ed_result},
)

§ 02 Next

Read the scorecard honestly.

A strategy that passes the Monte Carlo stress, survives the GAN crash regime, and agrees across both execution engines is a strategy worth trading. One that doesn't isn't — no matter how pretty the historical equity curve looks.

The scorecard is the answer. Trust the grades, not the backtest.