Strategies
Bybit PCA-Residual (Avellaneda-Lee s-score) Reversion: Point-in-Time Top-20 Bybit USDT Perps, 3-Factor PCA on 60-Day Daily Returns, Long z<-1.5 / Short z>+1.5 as Standalone Single-Name Bets, Exit at |z|<0.5 or 10-Day Clock (1-DAY bars, flat when no name is stretched)
Outcome: Abandoned
BybitPcaResidualSScoreReversionDaily
Outcome Summary
The hypothesis ported the Avellaneda-Lee PCA s-score reversion idea from US stocks to a point-in-time top-N universe of Bybit USDT perps on daily bars, trading both tails as unhedged single-name bets. Over 3 iterations the strategy kept failing Layer 3 sandbox verification by exceeding the 300s wall-clock limit. The last revision changed only performance: it cut the pool to 8 deep perps (BTC, ETH, SOL, XRP, DOGE, ADA, LINK, LTC) and left the signal unchanged, but it still did not pass. The run was abandoned before producing any backtest metrics.
Hypothesis
Iteration 3 is a performance-only fix. The timeout comes from ParquetDataCatalog's per-instrument glob of the catalog tree, a fixed cost for each leg regardless of window length (profiled on sibling multi-leg strategies at ~10 s+ per leg under host load ~40). Per-bar strategy work is small and does not grow with the span. The leg count drops from 13 to 8, the deepest Bybit USDT perps with data before 2022. Signal, exits and sizing are unchanged. Deviation: the hypothesis asks for a top-20 point-in-time universe from a 78-contract pool, which cannot fit inside the 300 s sandbox cap in this harness. 8 names with 3 factors is a thinner cross-section, and residuals will lean more on the BTC/ETH market factor. If the analyst needs the full 20-name cross-section, the sandbox budget, not the code, is the constraint. max_positions goes from 6 to 4 to keep slots below the 8-name universe (gross is still <= 1x equity).
verification_loop: sandbox_timeout: Verification failed (Layer 3 — sandbox backtest): smoke test exceeded the 300s wall-clock limit on a ~20000-bar window (2400 days of BTCUSDT.BYBIT-1-DAY-LAST-EXTERNAL). This almost always means per-bar work that scales with history — e.g. rescanning the full funding/supplementary series, or rebuilding a list and calling min()/sorted() inside calculate_signal()/on_bar() on every bar. Precompute sorted timestamp arrays ONCE in __init__ and use bisect, or cache lookups keyed by timestamp, so per-bar cost is O(log n) not O(n).
Implementation
Avellaneda-Lee PCA-residual s-score reversion on the daily point-in-time top-N (by trailing 30-day dollar volume) of 8 liquid Bybit USDT perps. PCA(3) runs on the correlation matrix of 60-day daily log returns, each name is OLS-regressed on the eigenportfolio factor returns, and the residuals are cumulated into a 60-day z-score. Long when z<=-1.5, short when z>=+1.5, as standalone single-name bets (at most 4 at a time, each 1/4 of equity, gross <= 1x). Exit when |z|<0.5 or after a 10-day clock. Flat when no name is stretched.
Verification Results
sandbox_timeout: Verification failed (Layer 3 — sandbox backtest): smoke test exceeded the 300s wall-clock limit on a ~20000-bar window (2400 days of BTCUSDT.BYBIT-1-DAY-LAST-EXTERNAL). This almost always means per-bar work that scales with history — e.g. rescanning the full funding/supplementary series, or rebuilding a list and calling min()/sorted() inside calculate_signal()/on_bar() on every bar. Precompute sorted timestamp arrays ONCE in __init__ and use bisect, or cache lookups keyed by timestamp, so per-bar cost is O(log n) not O(n).
Outcome Summary
Multi-leg cross-sectional strategies can exceed the 300s sandbox limit because of per-leg catalog loading, not per-bar compute, so keep the leg count small from the first draft or check that the design fits the sandbox time budget.
It was abandoned in a verification loop. The Layer 3 sandbox backtest timed out, exceeding the 300s limit on a ~20000-bar window (2400 days of BTCUSDT.BYBIT 1-DAY bars). It still timed out after iteration 3 cut the universe from 13 legs to 8 to reduce catalog load.
Avellaneda-Lee PCA-residual reversion on Bybit USDT perps: fit 3-factor PCA on 60-day daily returns, turn each name's cumulated residual into a z-score, and trade standalone single-name longs at z<-1.5 and shorts at z>+1.5, exiting at |z|<0.5 or after 10 days.
No performance numbers exist. The backtest report is empty, and the strategy never reached optimization, backtest review, analyst review or risk review.
Backtest and paper results are hypothetical. Trading involves risk of loss.