"""Scott Hart — "Two brakes and a basket".

One paragraph, so a judge can check the logic against the code:

Every day I score six liquid, factor-distinct names by their 3-month return.
I hold the two strongest that are still above their own 200-day average,
sized inversely to their 20-day volatility, capped at 28% each. If the
broad market (SPY) has lost its 200-day average, everything I hold must
itself be above trend, and total exposure is halved. If nothing passes,
the book sits in cash. Rebalance weekly, or immediately when the brake
flips, and only if the drift is big enough to be worth the trade.

Why two brakes and not one: the first brake (per-name trend) keeps me out of a
name that is falling even while the index looks fine. The second (index trend)
is the whole-market switch. They agree most of the time; when they disagree the
exposure shrinks instead of the logic getting clever.

Long only, no leverage, <= 28% per name, weekly cadence. Written to be admitted
and to survive windows it has never seen, not to win a 10-day sprint.
"""
from future import annotations

from statistics import mean, pstdev

--- basket ---------------------------------------------------------------

Six names, chosen for different failure modes rather than for backtest fit:

US equity, tech-heavy equity, gold, long duration, short duration, corporates.

BASKET = ("SPY", "QQQ", "GLD", "TLT", "SHY", "HYG")

When the index brake is on, only names whose job is not to fall with equities

are eligible. Not a tuned list: it is the difference between "smaller dose of

the thing that is falling" and "something else".

DEFENSIVE = ("SHY", "TLT", "GLD")
BRAKE_EXPOSURE = 0.50
VOL_BRAKE_ANN = 0.25 # 20d annualised vol above this = fast brake
VOL_BRAKE_EXPOSURE = 0.25

MOM_DAYS = 63 # ~3 months of trading days
TREND_DAYS = 200 # per-name brake
REGIME_DAYS = 200 # index brake
VOL_DAYS = 20
MAX_W = 0.28 # under the 30% concentration cap with margin
FULL_EXPOSURE = 1.00
BRAKE_EXPOSURE = 0.50 # index trend broken half the book
MIN_POSITIVE = 0.0 # a name must have positive 3-month return to be held
REBALANCE_EVERY = 5 # decide() is daily
weekly DRIFT_BAND = 0.06 # don't trade for 1-2% noise
MIN_TRADE_USD = 500.0

_tick = 0
_last_weights: dict[str, float] = {}
_regime: bool | None = None

def _closes(bars):
return [float(b["close"]) for b in bars] if bars else []

def _sma(closes, days):
if len(closes) < days:
return None
return mean(closes[-days:])

def _mom(bars, days):
c = _closes(bars)
if len(c) < days + 1 or c[-days - 1] <= 0:
return None
return c[-1] / c[-days - 1] - 1.0

def _vol(bars, days):
c = _closes(bars)[-(days + 1):]
if len(c) < 10:
return None
rets = [c[i] / c[i - 1] - 1.0 for i in range(1, len(c)) if c[i - 1] > 0]
if len(rets) < 8:
return None
v = pstdev(rets)
return v if v > 1e-4 else 1e-4

def _targets(ms):
"""Return {ticker: weight_of_equity} for the book I want to hold."""
spy = ms.get("SPY") or []
spy_closes = _closes(spy)
spy_sma = _sma(spy_closes, REGIME_DAYS) if spy_closes else None
regime_ok = bool(spy_closes and spy_sma is not None and spy_closes[-1] > spy_sma)
exposure = FULL_EXPOSURE if regime_ok else BRAKE_EXPOSURE

fast brake: a vol spike gets you out of the way before the slow average catches up

⎗
✓
spy_vol = _vol(spy, VOL_DAYS) if spy else None
if spy_vol is not None and spy_vol * (252 ** 0.5) > VOL_BRAKE_ANN:
    exposure = min(exposure, VOL_BRAKE_EXPOSURE)
eligible = BASKET if regime_ok else DEFENSIVE

scored = []
for t in eligible:
    bars = ms.get(t) or []
    if len(bars) < TREND_DAYS + 1:
        continue
    closes = _closes(bars)
    sma = _sma(closes, TREND_DAYS)
    m = _mom(bars, MOM_DAYS)
    v = _vol(bars, VOL_DAYS)
    if sma is None or m is None or v is None:
        continue
    # per-name brake: must be trending up AND have positive momentum
    if closes[-1] <= sma or m <= MIN_POSITIVE:
        continue
    scored.append((m, t, v))

if not scored:
    return {}, regime_ok

scored.sort(reverse=True)
picks = scored[:2]
inv = {t: 1.0 / v for _, t, v in picks}
total = sum(inv.values())
weights = {t: min((inv[t] / total) * exposure, MAX_W) for _, t, _ in picks}
return weights, regime_ok

def _orders_to_target(portfolio_state, weights, last_prices):
positions = {p["ticker"]: p for p in portfolio_state.get("positions", [])}
equity = float(portfolio_state.get("cash", 0.0))
for t, p in positions.items():
px = last_prices.get(t)
if px:
equity += float(p.get("quantity", 0.0)) * float(px)
if equity <= 0:
return []

⎗
✓
orders = []
# sells first, so the buys are funded without needing leverage
for t, p in positions.items():
    px = last_prices.get(t)
    if not px:
        continue
    cur_val = float(p.get("quantity", 0.0)) * float(px)
    want_val = weights.get(t, 0.0) * equity
    if cur_val - want_val > MIN_TRADE_USD:
        qty = int((cur_val - want_val) // float(px))
        if qty > 0:
            orders.append({"ticker": t, "side": "sell", "quantity": qty})
for t, w in weights.items():
    px = last_prices.get(t)
    if not px:
        continue
    cur_val = float(positions.get(t, {}).get("quantity", 0.0)) * float(px)
    want_val = w * equity
    if want_val - cur_val > MIN_TRADE_USD:
        qty = int((want_val - cur_val) // float(px))
        if qty > 0:
            orders.append({"ticker": t, "side": "buy", "quantity": qty})
return orders

def decide(market_state, portfolio_state, cash):
global _tick, _last_weights, _regime
_tick += 1

⎗
✓
last_prices = dict(portfolio_state.get("last_prices") or {})
weights, regime_ok = _targets(market_state)

regime_flip = _regime is not None and regime_ok != _regime
_regime = regime_ok

due = (_tick - 1) % REBALANCE_EVERY == 0
if not due and not regime_flip and _last_weights:
    # only trade off-cycle if the drift out of target is real
    positions = {p["ticker"]: p.get("quantity", 0.0) for p in portfolio_state.get("positions", [])}
    equity = float(cash)
    for t, q in positions.items():
        px = last_prices.get(t)
        if px:
            equity += float(q) * float(px)
    if equity > 0:
        drift = 0.0
        for t in set(list(weights) + list(positions)):
            px = last_prices.get(t)
            if not px:
                continue
            cur = float(positions.get(t, 0.0)) * float(px) / equity
            drift = max(drift, abs(cur - weights.get(t, 0.0)))
        if drift < DRIFT_BAND:
            return []

orders = _orders_to_target(portfolio_state, weights, last_prices)
if orders:
    _last_weights = dict(weights)
return orders
Edit

Pub: 13 Nov 2026 10:21 UTC

Views: 8