Apex++GAF-DC2: specification
A momentum rotation in large US stocks with accelerating revenue, plus a gated 3x Nasdaq-100 ETF sleeve, run in a US margin account. Rules, backtest, risks and a daily trade log.
1. What it does
- Universe: stocks that are S&P 500 or Nasdaq-100 members on that date. One share class per company.
- Trend filter: close above its 200-day average, and the 200-day average higher than 20 sessions ago.
- Growth filter: year-over-year revenue growth is higher than in the prior quarter.
- Balance-sheet filters: cash/assets in the top half of recent filers; not in the 10% most distressed.
- Ranking: 50-day over 200-day exponential average of the close, highest first.
- Picks: 4 names per sleeve. A held name stays while it passes the filters and ranks 7 or better. Empty slots go to the highest-ranked names not held.
- Weighting: market cap squared, then scaled so the basket targets 70% annualized volatility (scale capped at 1.5).
- Rebalancing: 4 sleeves of 1/4 equity each. Each sleeve rebalances every 20 sessions, staggered 5 sessions apart. Trades at the next open.
- Leverage: at most 2.0x equity at a rebalance (1.5x stocks + 0.5x TQQQ). Positions drift between rebalances.
- TQQQ sleeve: 0.5x equity while the gate is on, resized monthly. Gate turns off when QQQ's 20-day volatility rises above 32% and back on below 28%.
- Delisting: a held stock whose series ends is cashed at its last close if it was acquired. Any other cause counts as a total loss.
The exact specification is in section 6.
2. Why it works
Momentum and growth. Stocks in strong trends tend to keep going for months. Requiring accelerating revenue drops trends that the business does not back. The cash and distress filters drop fragile balance sheets. Cap-squared weights tilt the book toward the largest leaders.
The post-2008 regime. The backtest starts on 2010-02-11, TQQQ's first trading day, and covers only this regime. Rules and policies in effect during it:
- Short-sale circuit breaker (Reg SHO Rule 201, the alternative uptick rule). After a stock falls 10% in a day, short sales must be above the national best bid for the rest of that day and the next. Adopted 2010-02-24; compliance from 2011-02-28. SEC release, compliance date.
- Single-stock pauses after the 2010-05-06 Flash Crash: a 5-minute pause after a 10% move in 5 minutes, approved 2010-06-10. Limit Up-Limit Down bands replaced them from 2013-04-08. SEC 2010, SEC 2012, SEC bulletin.
- Market-wide circuit breakers at 7%, 13% and 20% declines in the S&P 500, approved 2012-05-31 and in effect from 2013-04-08. NYSE, FINRA filing.
- Zero rates and a Fed backstop: the Fed cut the federal funds target range to 0-0.25% on 2008-12-16 and kept it there until 2015-12-16. It cut to 0-0.25% again on 2020-03-15 and kept it there until 2022-03-16. On 2020-03-23 it added open-ended asset purchases and corporate credit facilities. Fed 2008-12-16, Fed 2015-12-16, Fed 2020-03-15, Fed 2020-03-23, Fed 2022-03-16.
A daily-reset 3x ETF loses most in long, choppy declines, where volatility decay compounds. The trading rules above are designed to slow disorderly selling in single stocks and the whole market. The gate takes the sleeve out when QQQ's volatility rises.
Limits: these rules do not stop bear markets. In 2022 the Nasdaq-100 fell about 33% and TQQQ about 79%. Regimes can change.
3. Results
In-sample backtest, 2010-02-11 to 2026-09-18. Returns include 5 bps per side trading costs, borrowing costs and the margin checks below. Strategy rules and the evaluation period were selected using these data, so results carry selection bias.
Two margin models. Headline: the concentration-stress margin model in section 6. Second: Robinhood-style standard margin (25% maintenance on stocks, 75% on TQQQ, 50% initial; borrowing at the fed funds target upper bound + 1.25% to $50,000, + 1.05% to $100,000, + 0.75% to $1,000,000; first $1,000 free; no interest on cash). Both check maintenance at every close and at the daily lows.
| CAGR | MaxDD | Rolling 4y median | Rolling 4y 10th pct | Worst 4y | Margin breaches | |
|---|---|---|---|---|---|---|
| Apex++GAF-DC2 | 93.1% | -59.1% (2011-02-14 to 2011-11-25) | 79.2% | 59.0% | +40.5% | 1 |
| Same, Robinhood-style margin | 88.9% | -57.8% (2024-12-24 to 2025-04-08) | 77.9% | 54.6% | +38.2% | 0 |
The headline model's one margin breach is at the daily low of 2021-03-05; the backtest sells pro rata at the next open to restore maintenance (section 6) and continues.
Rolling windows: 151 four-year windows, one starting on the first session of each month from 2010-03 to 2022-09. They overlap.
Daily closing values start at $1. ETF benchmarks show buy-and-hold total returns.
Calendar-year returns (headline model):
| 2010 (from 02-11) | 2011 | 2012 | 2013 | 2014 | 2015 | 2016 | 2017 | 2018 |
|---|---|---|---|---|---|---|---|---|
| +142% | -34% | +124% | +364% | +38% | +88% | +4% | +175% | -11% |
| 2019 | 2020 | 2021 | 2022 | 2023 | 2024 | 2025 | 2026 (to 09-18) |
|---|---|---|---|---|---|---|---|
| +91% | +323% | +117% | -42% | +115% | +367% | +42% | +257% |
2022 onward. From 2022-01-03 to 2026-09-18 the CAGR is 106.2% (Robinhood-style margin: 101.0%). The rank buffer and the volatility target were chosen on the full history, including these years, so this is not a clean out-of-sample test.
Sensitivity tests. Each change applied separately (base 93.6%).
| Test | CAGR |
|---|---|
| Trading costs of 10 bps per side | 92.3% |
| Trading costs of 25 bps per side | 88.3% |
| All borrowing at fed funds + 3.00%, zero interest on cash | 90.2% |
| NVDA excluded | 82.4% |
| Five largest contributors excluded | 46.2% |
| Half target exposure | 49.2% (MaxDD -32.6%) |
Walk-forward
Each January from 2014 to 2025, the rule set with the best average one-year return over January starts from 2011 to the prior year is picked and run for the next year in a fresh account. The 12 one-year returns are compounded. Robinhood-style margin.
| Rule sets | Picked | Compounded yearly return |
|---|---|---|
| Apex++GAF (no cash or distress filters, buffer 12), fixed | - | 62.7% |
| Apex++GAF vs DC2 at buffer 12 | DC2 in 12 of 12 years | 68.7% |
| Apex++GAF vs DC2 at buffer 12 vs DC2 at buffer 7 | buffer 7 in 2014 and 2019-2025 | 69.6% |
| The same three vs DC2 at buffer 7 with a 70% volatility target | 70% target in 2019-2025 | 72.5% |
| DC2 at buffer 7: 60% vs 70% volatility target | 70% in 11 of 12 years | 75.5% |
| DC2 at buffer 7, 70% volatility target (current rules), fixed | - | 75.6% |
| DC2 without the TQQQ sleeve, fixed | - | 58.2% |
| No TQQQ sleeve vs gated TQQQ sleeve | gated sleeve in 12 of 12 years | 75.6% |
- Buffer 7 was chosen on the full history, and so was the 70% volatility target, including every test year above, so the 75.6% fixed row is not a walk-forward result. The walk-forward that chooses among all four DC2 lineage rule sets gives 72.5%.
- Rows without a volatility target use 60%.
- All rule sets compared were designed on the full history.
- TQQQ gate on four-year test folds (2007-2010, 2011-2014, 2015-2018, 2019-2022, 2023-2026), chosen on training-period CAGR: picked in 5 of 5 folds with training from 1999 and 4 of 4 with training from 2010-02-11, and ahead of no sleeve in every test fold. Before 2010, TQQQ is a simulated 3x daily series.
- Gate thresholds from 26% to 34% with 0 to 4 points of hysteresis give 88-92% CAGR (Robinhood-style margin).
Data integrity
- Survivorship: survivorship-bias-free, point-in-time price, fundamentals and index-membership data. The universe is S&P 500 and Nasdaq-100 membership as of each date, including later-delisted names.
- Prices: daily open, high, low and close, adjusted for splits and dividends (total return).
- Fundamentals: quarterly figures as originally reported. A report is used from the first trading session after filing.
- Delistings: five held names delisted in the backtest, all acquisitions, cashed at their last close: BDK (2010-03-15), FRX1 (2014-07-01), PLL1 (2015-08-31), ESRX (2018-12-21), XLNX (2022-02-14).
- Costs: 5 bps per side on every trade, margin interest, and maintenance checks at the daily lows.
- In-sample: rules, parameters and the start date were chosen on these data.
- Concentration: five names (NVDA, MU, PLTR, TSLA, MRNA) supply 53% of the stock book's positive log-return contribution. Without them the CAGR is 46.2%.
4. Risks
- Drawdowns: -59% in the backtest; -42% in 2022. Expect deeper ones.
- Leverage: up to 2x at rebalances, more after drift. A gap down past the margin buffer forces sales at the open.
- Concentration: one stock can exceed 100% of equity (AMZN reached 184% in 2015). A single-name crash hits hard.
- TQQQ: daily reset. A choppy decline while the gate is on can lose most of the sleeve.
- Regime: the backtest covers one rate and market-structure regime (2010+). It may not repeat.
- Overfitting: the rules were selected on the same data. Live returns are likely lower.
- Delisting: a non-acquisition delisting is a total loss of that position.
5. Trade log
Daily trade log: every backtest trading day (date, ticker, buy or sell, weight before and after as % of equity, reason) and the live log, updated after each nightly run. Weights only; no prices, share counts or account amounts.
6. Exact specification
Data
| Item | Required data |
|---|---|
| Stock prices | Daily open, high, low, close, adjusted for splits and dividends (total return) |
| Universe | Point-in-time S&P 500 and Nasdaq-100 membership (constituents as of each date, including later-delisted names) |
| Market cap | Point-in-time company market cap (all share classes) |
| QQQ, TQQQ | Daily total-return open, high, low, close |
| Fundamentals | Quarterly figures as originally reported: revenue, assets, cash, debt, net income, current ratio, calendar quarter and filing date |
| Fed funds | Effective federal funds rate |
| Period | 2010-02-11 (first TQQQ session) to 2026-09-18 |
Account and costs
- Starting equity $100,000. On 2010-02-11 (session 0) the TQQQ sleeve and stock sleeve 0 are filled at the open, decided on the 2010-02-10 close. Sleeves 1-3 hold cash until their first rebalance (2010-02-19, 2010-02-26, 2010-03-05).
- Trading cost: 5 bps per side on traded notional. No taxes.
- Modeled borrowing cost: fed funds + 1.50% on the first $100,000 borrowed, + 1.00% up to $1,000,000, + 0.75% above. Blended, 360-day year.
- Cash credit: fed funds - 0.50% on cash above $10,000; when equity is below $100,000, scaled by equity / $100,000.
Timing
| Item | Decided on | Trades at |
|---|---|---|
| Stock sleeve picks, weights, vol scale | close of day t-1 | open of day t |
| Stock sleeve rebalance | every 20 sessions per sleeve | open |
| TQQQ vol gate | every session, close of day t-1 | open of day t, the session after it flips |
| TQQQ resize to 0.5 x equity while on | 11th session of each month | open |
Rules
STOCK BOOK: 4 sleeves, each 1/4 of equity
Sleeve k (k = 0..3) rebalances on sessions 5k, 5k+20, 5k+40, ...
counted from 2010-02-11 (session 0).
On a sleeve's rebalance, using data through the prior close:
universe = point-in-time S&P 500 + Nasdaq-100 members; one share
class per company (the higher-ranked class)
eligible = close > SMA200 and SMA200 > its value 20 sessions earlier
and growth_accel > 0 (see Growth-acceleration filter)
and cash_ok and distress_ok (see Cash and distress filters)
rank = EMA50 / EMA200 of the close, highest first, eligible names only
picks = keep each name the sleeve already holds while it is eligible
and ranked 7 or better; fill the remaining slots, up to 4
names, with the highest-ranked eligible names not yet held
w_i = cap_i^2 / sum of cap^2 over the picks
(cap = point-in-time company market cap)
basket_vol = sample stdev (ddof 1) of the last 20 daily returns of the
portfolio sum(w_i * r_i), annualized x sqrt(252)
(r = simple daily total returns of the picks)
scale = clip(0.70 / basket_vol, 0.0, 1.5)
target_i = (equity at today's open / 4) * w_i * scale
Between rebalances, sleeve positions are not traded (they drift).
TQQQ SLEEVE
qqq_vol = sample stdev (ddof 1) of the last 20 daily QQQ total returns
x sqrt(252), on the prior close
gate starts ON
if ON and qqq_vol > 0.32: OFF -> sell all TQQQ at the open
if OFF and qqq_vol < 0.28: ON -> buy TQQQ to 0.50 x equity at the open
(between 0.28 and 0.32 the gate keeps its state)
While ON, also trade TQQQ to 0.50 x equity on the 11th session of
each month.
CASH
cash = equity - stocks - TQQQ. Negative cash is a margin loan.
At each rebalance at most 2.0x equity (1.5 stocks + 0.5 TQQQ); positions
drift between rebalances.
DELISTING
A held stock whose price series ends is converted to cash at the next
open: at its last close when the delisting is an acquisition or merger
(the deal consideration, cash and/or acquirer stock), and at 0 for any
other cause (bankruptcy, regulatory or voluntary delisting, unknown),
per the corporate-actions records. A stock with a gap in its
prices that later trades again is converted at its last close.
Growth-acceleration filter
Use revenue as originally reported for each calendar quarter. In the formulas, calendardate identifies the quarter and datekey is the filing date. A report becomes available on the first trading session after filing.
For each company, one report per calendardate quarter (if duplicated, keep the earliest datekey).
growth(q) = revenue(q) / revenue(q-4) - 1 (requires revenue(q-4) > 0)
growth_accel(q) = growth(q) - growth(q-1)
q-k = the report whose calendardate is exactly k quarters before q, and
whose datekey is on or before q's datekey. Otherwise growth_accel is missing.
Point in time:
A report's value is usable from the first trading session strictly after its datekey.
It is carried forward until the company's next report becomes usable,
for at most 315 sessions; after that it is missing.
A secondary share class with no SF1 rows uses its company key's rows (GOOG -> GOOGL).
Eligibility: growth_accel > 0.
A missing value makes the stock ineligible.
The filter only removes names; the rank stays EMA50 / EMA200.
Cash and distress filters
Use quarterly reports as originally filed. The comparison pool is reports filed in the preceding 90 calendar days by companies already in the index universe by the filing date. Share classes map to one company; GOOG uses GOOGL's reports.
For each company, one report per calendardate quarter (if duplicated, keep the earliest date).
Per report (dk = its filing date):
A = assets (missing unless assets > 0)
cash = cashneq / A
distress = debt / A - netinc / A - currentratio
Any missing input -> that value is missing.
Flag, fixed once at filing:
known(c, dk) = company c (its company key, the primary share class) was in the
point-in-time stock universe -- an S&P 500 or Nasdaq-100 member
with a price on that session, exactly the universe the stock
selection uses -- on at least one trading session dated on or
before dk (any share class of c counts)
pool_x(dk) = the reports of the report universe with a non-missing x, a filing
date in [dk - 90 calendar days, dk], and a company c with
known(c, dk) (the report being flagged is in its own pool only
if its company is known by dk; it is flagged either way)
cash_ok = cash > 50th percentile of pool_cash(dk)
distress_ok = distress < 90th percentile of pool_distress(dk)
Percentile: linear interpolation between closest ranks (numpy.percentile default).
A report with a missing cash (distress) value has no cash (distress) flag; it is
skipped, and the company's previous report with a value keeps applying.
A report whose pool is empty has no flag (possible only before 1998-01-02).
Point in time (as GAF, per flag):
A report's flag is usable from the first trading session strictly after dk.
It is carried forward until the company's next report with that value becomes
usable, for at most 315 sessions; after that it is missing.
A secondary share class with no SF1 rows uses its company key's rows.
Eligibility (added to Apex++GAF's): cash_ok AND distress_ok.
A missing flag makes the stock ineligible.
A filtered stock is neither bought nor kept by the buffer.
Margin and concentration assumptions
These formulas set the headline backtest's limits on borrowing and concentrated positions. Real brokers' leverage and financing can differ.
maintenance = max( 0.25 x stock value + 0.75 x TQQQ value,
div(n) x sum_i stress_i x stock_value_i )
n = number of distinct companies held
div(n) = 1.00 for n <= 2, 0.95 for 3-5, 0.75 for 6-9, 0.60 for 10+
stress_i = min(1, max(0.30,
0.30 + 0.065 x max(0, ret1y_i - 0.50),
0.50 x vol30_i))
ret1y_i = 252-session total return
vol30_i = population stdev (ddof 0) of the last 30 daily log returns x sqrt(252)
initial margin on any purchase:
equity >= max(0.50 x long market value, maintenance rule sum, 1.1 x stress term)
after the trade; purchases are scaled down pro rata to satisfy it.
maintenance check: at every close, and at the daily lows (all positions
marked at their low). A deficit is a breach: at the next open, sell all
positions pro rata until equity >= 1.10 x maintenance.
7. Changelog
- 2026-10-11: stock volatility target 60% -> 70%.
- 2026-10-10: rank buffer 12 to 7 (a held name stays while it ranks 7 or better).
- 2026-10-10: delisting rule cause-aware (acquisitions cashed at the last close, other delistings a total loss).
- 2026-10-10: cash and distress filters compare each stock only with filings available at the time.