Apex++GAF: specification

A momentum rotation into the 4 strongest S&P 500 / Nasdaq-100 stocks whose revenue growth is accelerating, sized to a 50% volatility target, plus a 0.5x equity TQQQ sleeve. The sleeve is held only while QQQ's 20-day volatility is low. Account: US margin account at Interactive Brokers.

Data

ItemSource
Stock pricesDaily open, high, low, close, adjusted for splits and dividends (total return)
UniversePoint-in-time S&P 500 and Nasdaq-100 membership (constituents as of each date, including later-delisted names)
Market capPoint-in-time company market cap (all share classes)
QQQ, TQQQDaily total-return open, high, low, close
FundamentalsSharadar SF1, quarterly as-reported (ARQ): revenue, calendar quarter, filing date (column date)
Fed fundsEffective federal funds rate (FRED DFF)
Period2010-02-11 (first TQQQ session) to 2026-09-18

Account and costs

Timing

ItemDecided onTrades at
Stock sleeve picks, weights, vol scaleclose of day t-1open of day t
Stock sleeve rebalanceevery 20 sessions per sleeveopen
TQQQ vol gateevery session, close of day t-1open of day t, the session after it flips
TQQQ resize to 0.5 x equity while on11th session of each monthopen

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)
    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 12 or better; fill the remaining slots, up to 4
               names, with the highest-ranked eligible names not yet held
    w_i      = cap_i^1.5 / sum of cap^1.5 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.50 / 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.
  Maximum gross exposure 2.0x equity (1.5 stocks + 0.5 TQQQ).

DELISTING
  A held stock with no price on a session is converted to cash at its
  last close.

Growth-acceleration filter

Data: Sharadar SF1 (fundamentals table, the full bulk download fundamentals.csv.zip, or the API), dimension ARQ (as-reported quarterly), fields revenue, calendardate and the filing date. The current Sharadar API and bulk files name the filing-date column date; older documentation calls it datekey. Example: ticker A, calendardate 1999-09-30, date 2000-01-25, reportperiod 1999-10-31.

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 rank stays EMA50 / EMA200. The filter only removes names.

Margin model (IBKR Reg T with concentration stress)

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.

Results

Sharadar data, step-2 engine, IBKR margin model, 2026-10-07.

In-sample backtest, 2010-02-11 to 2026-09-18 (the filter was chosen after seeing this period):

CAGRMaxDDRolling 4y mean / medianWorst 4yMargin breaches
Apex++GAF71.4%-62.1%66.6% / 65.6%+35.1%1 (2010-05-06)
Apex++63.1%-68.8%62.2% / 63.2%+23.0%1 (2010-05-06)

Walk-forward test: filter on or off, chosen with training data only (expanding training from 1999-03-15; test periods 2007-10, 2011-14, 2015-18, 2019-22, 2023-26):

Test periodChoiceTraining score without / with filter
2007-2010Apex++39.1 / 11.1
2011-2014Apex++19.0 / 6.4
2015-2018Apex++17.0 / 10.0
2019-2022Apex++27.8 / 24.9
2023-2026Apex++GAF33.1 / 33.4
2007-2026, books started 1999-03-15Rolling 4y mean / medianWorst 4yCAGRMaxDD
Walk-forward choice (out-of-sample)49.0% / 52.9%-23.5%46.9%-83.0%
Apex++GAF fixed55.7% / 59.0%-27.9%53.9%-84.7%
Apex++ fixed52.6% / 54.4%-23.5%49.3%-83.0%

The filter lowered the 1999-2006 training score, mostly in the 2000-2002 bust. From 2007 it raised returns.

Required outputs for comparison

Report these, computed independently:

  1. Gate switch list: decision date, trade date, new state.
  2. Picks with weights for each sleeve on its first rebalance on or after each Jan 2 from 2011 to 2026.
  3. Calendar-year returns, 2010 to 2026.
  4. CAGR, maximum drawdown (with peak and trough dates), and every margin breach date.
  5. Rolling 4-year CAGR windows starting on the first session of each month from 2010-03 to 2022-09: mean, median and worst.