Apex++GAF-DC2: specification
A momentum rotation into the 4 strongest S&P 500 / Nasdaq-100 stocks whose revenue growth is accelerating, with cash in the top half of recent filers already in the index and not among the most financially distressed, sized to a 60% volatility target, plus a 0.5x equity TQQQ sleeve. The sleeve is held only while QQQ's 20-day volatility is low. Account type: US margin account.
Performance
2010-02-11 to 2026-09-18. In-sample results include trading costs and financing. Rules and the evaluation period were selected using these data.
Daily closing values start at $1. ETF benchmarks show buy-and-hold total returns.
Each point measures four calendar years from the first trading session of its start month, using the existing portfolio. Windows overlap.
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 12 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.60 / 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 with no price on a session is converted to cash 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 rank stays EMA50 / EMA200. The filter only removes names.
Cash and distress filters
Use quarterly reports as originally filed. The comparison pool includes reports filed in the preceding 90 calendar days by companies that had entered the index universe by the filing date. Share classes map to the same company; for example, 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.
The rank stays EMA50 / EMA200. The filters only remove names: a filtered stock is neither bought nor kept by the buffer.
Margin and concentration assumptions
The following formulas define the backtest's limits on borrowing and concentrated positions. Available leverage and financing costs can differ from these modeled assumptions.
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
In-sample backtest, 2010-02-11 to 2026-09-18. Returns include the trading costs and financing assumptions above. Strategy rules and the evaluation period were selected using these data, so results carry selection bias.
| CAGR | MaxDD | Rolling 4y mean / median | Worst 4y | Margin breaches | |
|---|---|---|---|---|---|
| Apex++GAF-DC2 | 85.0% | -57.2% (2015-12-29 to 2016-02-09) | 75.1% / 75.5% | +33.4% | 0 |
| Apex++GAF-DC | 78.2% | -56.4% | 70.5% / 71.2% | +33.6% | 0 |
| Apex++GAF | 71.4% | -62.1% | 66.8% / 65.0% | +31.6% | 1 |
Retrospective walk-forward, post-2010 regime. Training windows start in 2010-03; each fold selects a configuration using its training data and scores it on the following period (2015-18, 2019-22, 2023-26). These folds remain part of the research sample because the strategy rules and evaluation period were selected using the full history. Every fold selects the cash and distress filters, with cap power 2.0 and volatility target 0.60, from the 12-configuration grid.
| 4-year windows starting 2015-01 to 2022-09 | Rolling 4y mean / median | Worst 4y |
|---|---|---|
| Apex++GAF-DC2 (fixed config) | 71.1% / 70.0% | +32.0% |
| Apex++GAF-DC (fixed config) | 65.0% / 63.0% | +32.1% |
| Apex++GAF (fixed config) | 66.8% / 63.5% | +35.1% |
| 12-config walk-forward, stitched | 75.6% / 75.0% | +32.7% |
Calendar-year returns, Apex++GAF-DC2 (in-sample):
| 2010 | 2011 | 2012 | 2013 | 2014 | 2015 | 2016 | 2017 | 2018 |
|---|---|---|---|---|---|---|---|---|
| +111% | -13% | +116% | +227% | +84% | +81% | -7% | +178% | -19% |
| 2019 | 2020 | 2021 | 2022 | 2023 | 2024 | 2025 | 2026 (to 09-18) |
|---|---|---|---|---|---|---|---|
| +90% | +224% | +90% | -39% | +97% | +367% | +60% | +185% |
Sensitivity tests, 2010-02-11 to 2026-09-18. An independent implementation applies each change separately. CAGR uses the initial $100,000 account value.
| Test | CAGR |
|---|---|
| Base strategy | 85.4% |
| Trading costs of 10 bps per side | 84.2% |
| Trading costs of 25 bps per side | 80.8% |
| NVDA excluded | 77.1% |
| All borrowing at fed funds + 3.00%, zero interest on cash | 82.3% |
| Half target exposure | 45.1% |
Half target exposure has a maximum drawdown of -32.5%. These tests use the same historical period as the base strategy.