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.
Open in Colab →
Runs in your browser. No install, no API keys. The notebook clones the repo, installs dependencies, and drops you on a template strategy cell to edit. Runtime → Run all and you have a scorecard.
Clone from GitHub →
Run locally against your own data providers, API keys, and strategy library. Recommended if you're iterating seriously — Colab is for the first pass.
git clone https://github.com/robbiebusinessacc/maif-backtester.git
cd maif-backtester
pip install -r requirements.txt
python3 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},
) 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.