Methodology & Formulas
Every number on VERTEX is computed from Alpaca market data using the formulas below. We document everything so you know exactly what you're looking at — no black boxes.
Quick Navigation
1. Data Source & Feed Alpaca IEX 2. Vol Risk Premium (VRP) study_expected_move 3. Expected Move ATM straddle 4. RSI (14-period) rsi_series 5. Stochastic Oscillator stoch_series 6. Gaussian Channel gaussian_channel 7. Backtest Engine run_backtest 8. ML Signal Discovery study_ml 9. Random Benchmark random_bench1. Data Source & Feed
data_sourceAll data comes from Alpaca Markets via their REST API. The backend connects to two endpoints:
- Market data:
https://data.alpaca.markets— stock bars, quotes, option bars, snapshots - Paper trading / contracts:
https://paper-api.alpaca.markets— option contract listings
Feed Detection
The backend auto-detects the available feed at startup:
IEX feed is the default for free-tier Alpaca accounts. It provides NMS (National Market System) data but is 15-minute delayed for options. SIP feed requires a paid Alpaca subscription.
Historical Bar Data
Stock bars use the /v2/stocks/{symbol}/bars endpoint with pagination (max 10,000 per page). Option bars use /v1beta1/options/bars with chunking (40 symbols per request) to avoid truncation.
Trading Calendar
Market-open dates come from Alpaca's /v2/calendar endpoint. Expiration dates are computed as the Nth market-open day on/after the trade date — skipping weekends and holidays.
2. Vol Risk Premium (VRP)
vrpWhat it measures: Is the market's implied move (what options cost) richer than what actually happens? Positive VRP means options are expensive — sellers have an edge.
Computation (study_expected_move())
For each trading day, we sell an ATM straddle at the open and value it at the close:
Key Metrics
| Metric | Formula |
|---|---|
| Edge Ratio | avg(implied) / avg(realized_intrinsic) — how many × realized the market prices in |
| Short Straddle Avg | mean(short_pnl) × 100 ($/contract/day) |
| Win Rate | days_where(short_pnl > 0) / total_days |
| Day Sharpe | mean(short_pnl) / stdev(short_pnl) — risk-adjusted return per day |
| Within Band % | |close - K| ≤ implied — % of days where price stayed within the implied range |
3. Expected Move
expected_moveWhat it is: The ATM (at-the-money) straddle premium — the market's consensus forecast of how much the underlying will move, expressed as ±$X.XX.
This is displayed on the Dashboard Option Ladder panel as "Expected Move: ±$X.XX" and on the Chart page as colored ribbons (the call breakeven above, put breakeven below). Multi-DTE ribbons show how the expected move expands with time.
4. RSI (14-Period Relative Strength Index)
rsiSource: rsi_series(close, n=14) in backend.py. Based on Wilder's smoothed RSI.
| Range | Interpretation |
|---|---|
| ≥ 70 | Overbought — potential reversal or continuation of strong trend |
| ≤ 30 | Oversold — potential bounce or continued selling |
| 50 | Neutral — no directional edge |
RSI is computed per timeframe (5min, 15min, 30min, 1day) on the Chart page and shown in the Quote sidebar and momentum table.
5. Stochastic Oscillator (%K / %D)
stochasticSource: stoch_series(df, k=14, d=3, s=3) in backend.py.
| Range | Interpretation |
|---|---|
| > 80 | Overbought — price near top of recent range |
| < 20 | Oversold — price near bottom of recent range |
| %K crosses %D | Possible momentum shift (like MACD crossover) |
Stochastics are shown in the lower pane of the Chart page. Up to 5 timeframe overlays are drawn simultaneously. The 80/20 guide lines mark traditional overbought/oversold boundaries.
6. Gaussian Channel
gaussian_channelSource: gaussian_channel(h, l, c, per=144, poles=4, mult=1.414, ...) — an N-pole Ehlers Gaussian filter adapted from DonovanWall's Pine Script v4. The channel tracks the smoothed trend centerline with adaptive volatility bands.
Step-by-step computation
1. Source = HLC3
2. True Range — same as ATR calculation:
3. Filter coefficients — derived from period and poles:
4. Recursive pole filter — applies alpha iteratively for N poles:
5. Bands — filtered TR scaled by multiplier:
6. Direction — based on filter slope:
trend_pullback strategy (entries in the direction of the daily Gaussian slope) and em_fade (only trades when the Gaussian is flat — mean-reversion regime). A rising Gauss + pullback to VWAP is a classic trend-pullback entry.
7. Backtest Engine
backtestSource: run_backtest() — simulates 4 strategies on historical data with realistic costs and walk-forward validation.
The 4 Strategies
| Strategy | Entry Logic | Stop / Target |
|---|---|---|
| Trend Pullback | Gaussian rising (daily slope +), price near VWAP, %K rising on 5m & 15m, %K < 65 (long side); symmetrical for short | Stop: 1R below entry (short: above) Target: 2R |
| Expected Move Fade | Gaussian flat, price stretched ~80% toward EM band edge, momentum rolling back | Stop: beyond the EM band edge by 0.5× half-band Target: VWAP |
| VWAP Reversion | Price deviates from VWAP by > vwap_stretch (default 0.4%) |
Stop: entry price ± deviation Target: VWAP |
| Gaussian Follow | Price crosses the 15-min Gaussian filter line — trend-following entry in the direction of the cross | Exit on opposite cross or EOD. No fixed target. Stop: entry ± 1R |
Walk-Forward Split
Every strategy is split into In-Sample (IS) and Out-of-Sample (OOS) periods:
If IS performance is strong but OOS collapses, the strategy is likely overfit — the pattern doesn't generalize. Honest backtest pages flag this gap.
R-Multiple System
All P&L is expressed in R-multiples (risk units), making performance comparable across strategies and markets:
Default round-trip cost: 2 bps (configurable via cost_bps parameter).
Key Metrics
| Metric | Definition |
|---|---|
| Win Rate | % of trades with positive R |
| Expectancy (R) | mean(R) — average risk-adjusted return per trade |
| Profit Factor | sum(win_R) / -sum(loss_R) — gross profit/gross loss ratio |
| Max DD (R) | Largest peak-to-trough decline in cumulative R equity |
| Total Return | sum(ret_pct) — cumulative % return |
8. ML Signal Discovery
mlSource: study_ml() — trains a HistGradientBoostingClassifier (scikit-learn) to predict whether the price will be higher in N bars.
Feature Set (15 features)
| Feature | Description |
|---|---|
rsi5, rsi15, rsi30 | RSI on 5min, 15min, 30min timeframe |
k5, k15, k30 | Stochastic %K on 5min, 15min, 30min |
vwap_dist | % distance from VWAP: (price - VWAP) / price × 100 |
g5_dist, g15_dist, g1d_dist | % distance from Gaussian filter centerline (5min, 15min, 1day) |
em_pos | Position within expected-move bands: (price - EM_mid) / EM_half |
tod | Time-of-day in minutes since 09:30 ET |
ret1, ret3, ret6 | Prior 1-bar, 3-bar, 6-bar returns (same-session only) |
volrel | Volume relative to 20-bar average: volume / mean(volume[-20:]) |
Walk-Forward Validation
Interpretation
| AUC | Verdict |
|---|---|
| 0.50 | Random — model cannot predict direction. Displayed as NOISE. |
| 0.50–0.53 | Barely above random — likely curve-fitting. Shown as NOISE. |
| 0.53–0.55 | Mild edge — use with caution. MODERATE confidence. |
| 0.55+ | Real predictive power. HIGH confidence. |
| < 0.50 | Worse than random — the model is anti-predictive. Displayed honestly. |
9. Random Benchmark
random_benchmarkSource: random_bench(ntrades) in backend.py. Every backtest compares strategy performance to a seeded random-entry benchmark with identical risk parameters.
The random benchmark is the single most important honesty signal in VERTEX. If a strategy's expectancy doesn't exceed random, the strategy card shows "✗ LOSES TO RANDOM" in red. If it beats random but still loses money (negative expectancy), it shows "✓ BEATS RANDOM (still loses $)" in amber.