Quickstart

A working backtest in under 5 minutes

Copy, paste, run

python
import horizon as hz
from horizon.asset_classes import AssetClass, Equity
from horizon.data import SyntheticGBM
from horizon.discovery import StaticUniverse
from horizon.discovery.base import Market
from horizon.portfolio import KellyOptimizer
from horizon.quant import BollingerMeanRev
from horizon.risk import RiskProfile

result = hz.run(
    mode="backtest",
    strategies=[BollingerMeanRev(window=20, entry_z=2.0)],
    asset_classes=[Equity],
    universe=StaticUniverse([
        Market(id=t, asset_class=AssetClass.Equity)
        for t in ["AAPL", "MSFT", "NVDA", "GOOGL", "AMZN"]
    ]),
    portfolio=KellyOptimizer(kelly_fraction=0.25, max_gross_leverage=1.0),
    risk=RiskProfile.moderate(),
    data_source=SyntheticGBM(
        market_ids=["AAPL", "MSFT", "NVDA", "GOOGL", "AMZN"],
        n_bars=252,
        seed=42,
    ),
    backtest=hz.BacktestConfig(initial_cash_usd=100_000),
)

print(f"Sharpe:       {result.sharpe:+.3f}")
print(f"Total return: {result.total_return:+.2%}")
print(f"Max drawdown: {result.max_drawdown:.2%}")
print(f"Trades:       {result.n_trades}")

Save as first.py and run:

bash
PYTHONPATH=. python3 first.py

That’s a full 252-bar, 5-equity backtest with Kelly sizing, Bollinger z-score signals, and a moderate risk profile. Every layer of the pipeline fired on every tick.

What just happened

On each tick:

  1. SyntheticGBM fed a price bar for each ticker
  2. FeatureStore updated rolling histories and computed Bollinger z-scores
  3. BollingerMeanRev checked if any z-score crossed ±2, emitting signals in the opposite direction
  4. KellyOptimizer sized each signal using edge / vol², scaled by the 0.25 Kelly fraction
  5. EquityExecutor converted dollar targets to share quantities
  6. RiskProfile.moderate() checked every order against stops, drawdown guards, and notional caps
  7. The paper venue matched orders, applied fees, and updated positions

Add custom risk controls

Replace RiskProfile.moderate() with explicit config:

python
from horizon.risk import DrawdownGuard, RiskConfig, StopLoss
from horizon.risk.drawdown import DrawdownAction

risk=RiskConfig(
    max_gross_leverage=1.0,
    stop_loss=StopLoss(per_position_pct=0.05),
    drawdown=[
        DrawdownGuard(daily_pct=0.05, action=DrawdownAction.HaltNew),
        DrawdownGuard(weekly_pct=0.10, action=DrawdownAction.ReduceHalf),
        DrawdownGuard(monthly_pct=0.20, action=DrawdownAction.Flatten),
    ],
)
  • 5% stop loss closes positions that lose 5% of cost
  • 5% daily drawdown halts new entries
  • 10% weekly cuts every position in half
  • 20% monthly flattens everything

Compare result.max_drawdown with and without these guards.

Swap strategies

python
from horizon.quant import TSMomentum, MovingAverageCrossStrategy, RSIMeanRev

# Time-series momentum
strategies=[TSMomentum(lookback=20)]

# Moving average crossover
strategies=[MovingAverageCrossStrategy(fast=10, slow=30)]

# RSI mean reversion
strategies=[RSIMeanRev(window=14)]

# Run all at once: the portfolio optimizer sees the combined signal list
strategies=[
    TSMomentum(lookback=20),
    BollingerMeanRev(window=15),
    MovingAverageCrossStrategy(fast=10, slow=30),
    RSIMeanRev(window=14),
]

Run a preset instead

bash
PYTHONPATH=. python3 -m horizon.cli run equity_mean_reversion

Zero Python written. See Presets & CLI for how to build your own.

Next