A Multi-Factor Systematic Equity Portfolio
with Volatility Targeting and a Drawdown Overlay
1. Universe and data
The investable universe is a curated set of ≈ 90 large- and mid-capitalisation US equities spanning every GICS sector. SPY is included as the market benchmark for beta and factor regressions but is excluded from signal generation. Daily open-high-low-close-volume bars are sourced from the Yahoo Finance public API and persisted in Postgres (table prices, primary key (security_id, date)) with idempotent upserts so the worker is safe to re-run.
Fundamentals are pulled weekly from the same source: trailing P/E, P/B, ROE, debt-to-equity, market capitalisation, and earnings growth. Survivorship bias in the universe is acknowledged and not corrected — the historical results overstate the true ex-ante hit rate by a small margin we estimate at 30–80 bps annualised.
2. Strategy sleeves
2.1. Cross-sectional momentum (40%)
Following Jegadeesh and Titman (1993) and Asness, Moskowitz, and Pedersen (2013), we rank the universe each rebalance by 12-month price return skipping the most recent month:
The portfolio holds the top-decile names long and the bottom-decile short, equal weighted within each leg, capped at 30 names per leg. Skipping the most-recent month removes the short-horizon reversal effect that contaminates raw 12-month rankings.
2.2. Quality (25%)
Quality combines profitability, leverage, and growth into a composite z-score:
Names with , and positive year-on-year earnings growth are eligible; the top 30 by composite score are held long with equal weight.
2.3. Low volatility (20%)
We compute realised 60-day daily volatility,
and hold the bottom-quintile (lowest realised vol) names long, equal weighted, capped at 50 names. This sleeve harvests the well-documented betting-against-beta premium (Frazzini and Pedersen, 2014).
2.4. Short-term mean reversion (15%)
A name is held long if its Wilder 14-day RSI is below 30 and its current price is above its 200-day simple moving average. Positions are exited after five trading days regardless of further mean reversion, ensuring the sleeve does not double-up on momentum-driven breakdowns.
3. Portfolio construction
Sleeve signals are summed weighted by their static allocation to produce raw target weights. We then apply, in order:
- Per-name cap of 5%, applied as a hard clamp.
- Per-sector cap of 25%, scaling all positive weights within an over-cap sector pro-rata.
- Volatility target: scale the gross weight vector so the ex-ante portfolio volatility equals 12%. With sample covariance on the trailing 60 days,(4)
- Drawdown overlay: if the rolling drawdown exceeds 8%, multiply gross by 0.5 until the drawdown recovers below the threshold.
- Cap aggregate gross leverage at 1.5×.
4. Execution model
The simulator fills orders at the close-of-day adjusted price plus a slippage charge of
where the first term is a fixed half-spread proxy, the second adds a high-low spread component capped at 20 bps, and the third applies a 15 bps impact penalty when the order size exceeds 1% of trailing 30-day average dollar volume. Commissions are per share with a one-dollar minimum.
5. Risk model
The 1-day historical Value-at-Risk and Expected Shortfall are
We additionally regress the portfolio return on five factor proxies built from the universe: market (SPY), size (small-minus-big by market cap), value (low-minus-high P/B), profitability (high-minus-low ROE), and a low-vol proxy in place of CMA (low-vol minus high-vol).
6. Targets and expectations
| Metric | Target | Comparable evidence |
|---|---|---|
| Annualised return | ~ 18% | 15–22% |
| Sharpe ratio | > 1.5 | 1.3–1.7 |
| Maximum drawdown | < 12% | 8–14% |
| Annualised volatility | ~ 12% | 10–13% |
7. Implementation notes
The system is implemented as Python workers writing to a Postgres instance, with a Next.js read-only front-end. The scheduler runs the daily pipeline at 16:30 ET on weekdays and the fundamentals refresh on Sunday at 02:00. There is no broker API in the path: order generation and fills are entirely synthetic, against the most recent recorded price. All portfolio state lives in the database — there is no in-memory cache that can diverge from the persisted truth.
References
- Asness, C., Moskowitz, T., Pedersen, L. (2013). Value and Momentum Everywhere. JoF.
- Frazzini, A., Pedersen, L. (2014). Betting Against Beta. JFE.
- Fama, E., French, K. (2015). A Five-Factor Asset Pricing Model. JFE.
- Jegadeesh, N., Titman, S. (1993). Returns to Buying Winners and Selling Losers. JoF.