NOCTURNA Rebuild Roadmap¶
This file records the agreed plan and the decisions behind it. It is updated as phases land.
Objective¶
Maximise after-cost, risk-adjusted growth of a single operator's account within hard risk limits. No strategy trades live unless it passes the promotion gates below.
Operator decisions¶
| Topic | Decision |
|---|---|
| Architecture | One in-repo event-driven core; identical strategy code in backtest, paper and live; Alpaca via alpaca-py |
| Market data budget | $0 — free Alpaca data only (full-market SIP history ending ≥ 15 min ago, live IEX stream ≤ 30 symbols, crypto data) |
| Asset scope | US equities/ETFs and crypto, each admitted only if it passes the gates |
| Risk profile | Conservative / low risk |
| Users | Single operator (public registration disabled) |
Conservative risk limits (confirmed by the operator after the real-data run below)¶
- Maximum drawdown kill switch: 10% below the equity high-water mark (halt and flatten)
- Drawdown ladder: exposure shrinks linearly from 5% drawdown to 25% at the limit
- Daily loss limit: 3% (no new risk for the rest of the day)
- Risk per trade for stop-based strategies: 0.5% of equity
- Portfolio volatility target: 8% annualised (scale down only, no leverage)
- Caps: 60% per position, 15% crypto in total, 100% gross
Promotion gates¶
A strategy moves research → paper → small live → scaled live only if:
- Out-of-sample Sharpe ≥ 1.0 net of costs (portfolio level), in excess of T-bills (BIL) since 22 October 2026: every Sharpe-based gate uses daily returns minus BIL's, and idle cash is held in BIL (see STRATEGY_SHORTLIST.md)
- Deflated Sharpe ratio ≥ 0.95 and probability of backtest overfitting ≤ 0.20
- Still profitable at 2–3× assumed slippage; stable across neighbouring parameters
- Maximum drawdown within the risk limits, with an automatic kill switch
- ≥ 60 trading days of paper trading tracking the backtest within tolerance (below)
- Live capital ramp: 10% → 25% → 50% → 100%
Paper soak tolerance (agreed with the operator)¶
flask soak report compares paper trading with a backtest of the same days, started on the
bar the engine first acted on, with the same equity, lineup, sizing and risk limits. The
soak passes when all five hold:
- At least 60 trading days recorded (a day counts when the engine recorded its equity)
- Every engine error, refused order, unexpected fill, halt and kill-switch trip reviewed
- Every reconciliation difference with Alpaca explained; a cash difference counts as explained when the account's own entries (dividends, interest, fees) add up to it within a cent
- Tracking error, the annualised spread of daily return differences, below 2%
- Total return within 1 percentage point of the backtest
Returns are compared from the first session at whose close both had traded. Expect small differences around dividends: the backtest's bars are dividend-adjusted, while Alpaca pays the dividend as cash. The report also measures opening-auction fills against the open as traded, next to the 5 bps the backtest assumes.
Strategy lineup¶
| Mode | Strategy | Data | Status |
|---|---|---|---|
| SENTINEL | Time-series momentum on 12 liquid ETFs across asset classes (tsmom): sign of the 12-month return, or the clipped t-statistic of daily returns (statistically significant trends), each sized 40% / EWMA volatility, monthly (Moskowitz, Ooi & Pedersen 2012) |
Daily bars (free) | Built; alone 2017–2026: walk-forward Sharpe 0.32, long-only full period 0.71; a lineup component |
| LUCIFER | Replaced. Month-end pension-rebalancing flows (Harvey, Mazzoleni & Melone, NBER w33554, 2025): simulate a 60/40 SPY/IEF portfolio; Threshold signal (drift > ~2 pp) and Calendar signal (last week of month, reversal on the first business day); trade the equity-vs-bond spread against the expected flows. Two variants to test: long/short spread scaled to ~4–5% vol, and long-only SPY/IEF switch | Daily bars (free) | Built (rebalancing); rejected for now: the signal works close-to-close (Sharpe 0.64 on 2017–2026 SPY/IEF, before costs) but only 0.24 at the next open, and it turns over about 43 times a year. Needs close-of-day execution |
| CRYPTO BREAKOUT | Donchian ensemble (Zarattini, Pagani & Barbon 2025): 9 lookbacks 5–360 days, trailing stop at channel midpoint, volatility-targeted; long-only, no leverage, BTC/ETH/SOL; the engine trades the stops at the next bar | Alpaca crypto data (free, from 2021) | Built (crypto-trend); alone 2023–2026: Sharpe 0.85, walk-forward 2024–2026 0.45; a lineup component at the 15% crypto limit |
| MONTH-END | Month-end payment cycle (Etula, Rinne, Suominen & Vaittinen, RFS 2020): SPY from the close of T−k to the close of T+3 around each month's last session, T-bills otherwise; k follows the settlement cycle | Daily bars (free) | Built (month-end) to rules fixed before any backtest (STRATEGY_SHORTLIST.md); rejected: walk-forward Sharpe over T-bills 0.05 (gate 1.0), 21.4% against T-bills' 20.3% from 2020 to October 2026 |
| TREND-FILTER | Trend filter on the 60/40's equity sleeve (Faber 2007): SPY only while its month-end close is above the mean of the last 8, 10 or 12 month-end closes, T-bills otherwise | Daily bars (free); long-history proxies from French's Data Library and FRED, 1970–2026 | Built (trend-filter) to rules fixed before any backtest (STRATEGY_SHORTLIST.md); rejected: walk-forward Sharpe over T-bills 0.53 on Alpaca data (gate 1.0), the same as the 60/40's; 0.56 against the 60/40's 0.58 over 1974–2026, with a shallower maximum drawdown (−12.2% against −14.1%) |
| LOW-VOL | Low-volatility stocks in place of SPY in the 60/40 (Blitz & van Vliet 2007; Baker, Bradley & Wurgler 2011), through USMV or SPLV | Daily bars (free); French's variance portfolios (monthly) | Built (low-vol) to rules fixed before any backtest (STRATEGY_SHORTLIST.md); rejected: walk-forward Sharpe over T-bills 0.15 against the 60/40's 0.53. The effect holds in French's lowest-variance quintile, but the funds trailed it by 4–6 points a year over 2016–2026 |
| GOLD | The 60/40 with a GLD sleeve of 10, 15 or 20%, taken equally from stocks and bonds (Devanathan, Tzikas & Boyd, arXiv 2609.07946, 2026) | Daily bars (free); World Bank monthly gold prices, 1975–2026 | Built (gold) to rules fixed before any backtest (STRATEGY_SHORTLIST.md), judged on gate B (beat the 60/40); rejected: walk-forward Sharpe over T-bills 0.70 against the 60/40's 0.53, but a 0.54 probability of backtest overfitting, and 0.52 against 0.65 over 1975–2000 |
| COMMODITIES | The 60/40 with a DBC sleeve of 5, 10 or 15%, taken equally from stocks and bonds (Levine, Ooi, Richardson & Sasseville, FAJ 2018) | Daily bars (free); AQR's commodity index, monthly, 1975–2025 | Built (commodities) to rules fixed before any backtest (STRATEGY_SHORTLIST.md), judged on gate B; rejected: walk-forward Sharpe 0.56 against the 60/40's 0.53, but a 0.85 probability of backtest overfitting and a deeper drawdown over 2001–2025 |
| INTERNATIONAL | The 60/40 with a third, half or two thirds of its stocks in VEA (Anarkulova, Cederburg & O'Doherty, JFE 2022) | Daily bars (free); French's developed ex-US market, monthly, 1990–2026 | Built (international) to rules fixed before any backtest (STRATEGY_SHORTLIST.md), judged on gate B; rejected: walk-forward Sharpe 0.39 against 0.53, and lower in both halves of 1990–2026 |
| REAPER | Large-cap/ETF short-term reversal with a trend filter, low-turnover construction | Daily bars (free) | Deferred: net-of-cost reversal evidence is for individual large-cap stocks (residual reversal), whose honest test needs point-in-time index membership (not free); no peer-reviewed ETF evidence was found |
| EVE | Grid trading — expected return ≈ 0 and ruin risk in trends | — | Retired from live; research only |
| Overlays | Regime model used only to scale exposure; crypto Fear & Greed and x70 signals removed from equity decisions | — | Planned |
The next candidates, with their sources and the evidence for and against them, are in STRATEGY_SHORTLIST.md; none is built until the operator picks one and its rules are fixed there. Round 1's pick (MONTH-END) and round 2's two picks (TREND-FILTER and LOW-VOL) were built and rejected; no candidate is open.
Phase 5 outcome. The lineup (60/40 core, ETF trend, crypto trend at 15%; two mixes and two trend variants fixed before any lineup backtest) was validated as one candidate on Alpaca data from 2017 to October 2026 and rejected: walk-forward Sharpe 0.76 (gate 1.0), 4.6% a year, maximum drawdown -11.0%, probability of backtest overfitting 0.24 (gate 0.20); deflated Sharpe 0.999, 3x costs, stability and drawdown passed. Out of sample it did not beat the 60/40 benchmark (Sharpe 0.81, 5.5% a year, drawdown -14.1%); its best full-period mix (Sharpe 1.01) was in-sample selection. No candidate is promoted, so paper trading keeps the benchmark.
The original LUCIFER (opening-range breakout on "stocks in play") was dropped because its stock selection needs full-market real-time volume, which requires a $99/month data plan.
Known risks of the selected strategies¶
- Rebalancing flows: paper sample ends 2023-03-17, so 2023–2026 must be tested out of sample; published in 2025 and may be crowded; at least one large fund (Korea's NPS) suspended rebalancing in H1 2026; paper used futures, we use ETFs (IEF is 7–10y, not the 10-year note).
- Crypto breakout: published Sharpe > 1.5 comes from a 2015–2025 crypto bull sample; independent recent tests show mainly drawdown reduction, not extra return.
Phases¶
| Phase | Scope | Status |
|---|---|---|
| 0 | Safety freeze: paper-only guard, closed registration, no fabricated data, truthful README | Done |
| 1 | Platform foundations: dependency clean-up with hashed lockfile, validated settings, auth fixes, DB-backed scoped API keys, token revocation, enforced rate limits, proxy-aware client IPs, Alembic migrations + PostgreSQL, persisted audit log | Done |
| 2 | Event-driven core: domain model, event flow, one broker interface (simulated + Alpaca via alpaca-py), data layer with explicit ranges and closed bars, removal of the legacy engine and alpaca-trade-api |
Done |
| 3 | Risk engine: pre-trade controls (price collar, size/exposure caps, duplicate detection, kill switch), equity-based loss limits, volatility-targeted sizing, PDT logic removed | Done |
| 4 | Validation pipeline: walk-forward, combinatorial purged cross-validation, deflated Sharpe, overfitting probability, cost stress, regime splits, tear sheets | Done |
| 5 | Strategy rebuild per the lineup above, each gated by Phase 4 | Done: four candidates built and validated on Alpaca data; none passed, so the engine keeps the 60/40 benchmark |
| 6 | Operations: engine as its own service, structured logs, metrics, alerts, health/readiness, Docker/Compose fixes, backups, runbooks | Done: see RUNBOOK.md |
| 7 | Versioned API with OpenAPI docs; dashboard rebuilt with source and auth | Done: the API under /api/v1 with an OpenAPI 3.1 document kept true by contract tests; the dashboard (src/static/) and Telegram commands |
| 8 | CI quality gates: strict typing on core, coverage targets, mutation testing on risk, blocking security scans | Done: mypy --strict on the trading core, coverage floors (94% overall, 95% per core module), weekly Cosmic Ray mutation testing of risk.py and sizing.py, blocking bandit and pip-audit, dashboard and Compose-stack jobs; see REFERENCE.md |
| 9 | ≥ 60-day paper soak, then staged live ramp | In progress: the soak report (flask soak report) checks the tolerance above; the soak starts when the operator starts the engine on the paper account |
Sequencing notes¶
- Phase 2 removed
alpaca-trade-apiwith the legacy engine, the paper toy engine (/api/paper), the ML tuner and the sentiment analyser, and moved to Python 3.13. The dependency audit in CI now ignores nothing. - The live engine runs only the 60/40 SPY/IEF benchmark until a strategy passes the Phase 4 gates. It exercises orders, fills, reconciliation and history in paper trading meanwhile. From 22 October 2026 the equity its sizing leaves unused is held in T-bills (BIL). Out of sample from 2020 to October 2026 that raised its return from 5.48% to 6.26% a year, with the same 14% maximum drawdown.
- Phase 3 widened the rebalance band to 2% of equity and rebalances on every bar, because the volatility and drawdown scales move with the market.
- The PDT logic went with the legacy engine in Phase 2 and needs no replacement: FINRA eliminated the pattern-day-trader rule and its $25,000 minimum (Rule 4210 amendments, SEC approval 14 April 2026, effective 4 June 2026, Regulatory Notice 26-10), and Alpaca now applies its intraday margin rule instead. The engine uses no leverage (gross exposure is capped at 100% of equity).
- The risk limits were confirmed on real data (Alpaca, SPY/IEF 60/40, January 2017 to October 2026, a single history with one large drawdown in 2022):
| Settings | Return/yr | Volatility | Sharpe | Max drawdown |
|---|---|---|---|---|
| Kill switch 10%, ladder, 8% volatility target (chosen) | 6.6% | 6.9% | 0.96 | -12.1% |
| No kill switch or ladder, 8% target | 7.1% | 7.3% | 0.98 | -14.1% |
| No kill switch or ladder, 10% target | 7.9% | 8.2% | 0.96 | -16.8% |
| Plain 60/40 | 9.5% | 10.7% | 0.90 | -21.2% |
The kill switch and ladder cost about 0.5% a year for 2 points less drawdown; the volatility
target sets the return. The validated benchmark family was rejected (walk-forward Sharpe
0.81, PBO 0.74, out-of-sample drawdown -14.1% after a kill on 2022-06-11), as a passive
yardstick should be.
- Phase 4 validates one candidate at a time. The 1.0 Sharpe gate is for the portfolio; a
component can be validated with --min-sharpe, and Phase 5 validates the combined lineup as
its own candidate.
- Alpaca's free plan serves equity history from 2016: with 36 months of training, about six
years are out of sample. flask validate --data history adds proxies from free data back to
1970 (French's Data Library and FRED) for candidates trading SPY and IEF. On them, the live
60/40 (held at 60/40, the same limits) earned 8.11% a year from 1974 to August 2026, with a
maximum drawdown of −15.2% and five kill-switch trips (1974, 1987, 2001, 2008, 2022). Over
2020 to October 2026 on Alpaca data it earned 6.32% a year, with a −12.0% maximum drawdown.
- The Alpaca adapter is tested against fakes with alpaca-py's real request models; its first
run against a live paper account should be watched (order acknowledgements, fill events,
crypto fee postings, position symbol formats).
- In the Compose stack the engine runs as its own process (ENGINE_MODE=external), so an API
redeploy never restarts it; an engine restart rebuilds from the broker's state and the
persisted risk state. The stack was verified end to end without Alpaca keys (commands
across processes, crash restart, alerts when the engine stops answering, the emergency
stop's broker fallback, backup and in-place restore, the HTTPS proxy); its first run
against the paper account should be watched.