Skip to content
All strategies

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

Classification: (a) concrete trading idea. The source takes the top-N US stocks by dollar volume, fits PCA with 3 components, regresses each name on the factors, standardizes the latest residual into a z-score, and buys the names with z < -1.5, weighting by |z|. It rebalances every 30 days and reports about 7% a year with a ~40% drawdown (unverified). We are NOT porting it to USEQ. Lesson L128 bans daily-bar USEQ, and a 20-stock monthly rebalance gives about 115 rebalances in 10 years, which deflation always……Show moreShow less

Classification: (a) concrete trading idea. The source takes the top-N US stocks by dollar volume, fits PCA with 3 components, regresses each name on the factors, standardizes the latest residual into a z-score, and buys the names with z < -1.5, weighting by |z|. It rebalances every 30 days and reports about 7% a year with a ~40% drawdown (unverified). We are NOT porting it to USEQ. Lesson L128 bans daily-bar USEQ, and a 20-stock monthly rebalance gives about 115 rebalances in 10 years, which deflation always strips. The mechanism is restated from first principles on Bybit USDT linear perps (0.8% corpus share against a >=5% target; pure-price trigger, per L160). Universe (point-in-time, per L151): the candidate pool is every Bybit USDT perp whose catalog daily history starts on or before 2021-12-31 (78 contracts; a data-availability rule, not a hand pick). At each daily close the active universe is the top 20 by trailing 30-day dollar volume among names with at least 90 daily bars as of that date. Factor model, following the paper rather than the tutorial: PCA(3) on the 60-day x 20-name matrix of standardized daily log RETURNS (the tutorial used log price levels, which are non-stationary). For each name, OLS on the 3 factor returns, cumulate the residuals into X, and set z = (X_last - mean X) / std X over the 60-day window. Changes from the tutorial: it trades both tails (the paper is symmetric); each position is a standalone single-name bet with no hedge legs (L137); the decision runs daily with a z-based exit instead of a blind 30-day rebalance (L166). Declared flat state: no position when no name has |z| >= 1.5 (expected ~30-40% of days).

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.