OKX Cross-Sectional 52-Week-High Anchoring Spread: Dollar-Neutral Long Names Nearest Their Trailing High / Short Names Furthest Below It (15 OKX USDT Swaps, Daily Bars, Weekly Rebalance)
Outcome: Abandoned
OkxFiftyTwoWeekHighAnchoringSpread
Outcome Summary
OkxFiftyTwoWeekHighAnchoringSpread tested whether anchoring to the 52-week high produces a cross-sectional edge. Each Monday it ranked 15 OKX perpetual swaps by their proximity to the trailing high and held equal-dollar long and short legs. The hypothesis itself flagged sandbox-timeout risk, and the code used bounded deques with monotonic-queue maxima. Even so, after 6 iterations the Layer 3 sandbox smoke test still went over the 300s limit on a 2400-day daily window. The run was abandoned in verification, before any backtest, optimization, analyst or risk review took place.
A dollar-neutral, multi-instrument, long-short cross-sectional strategy on 15 OKX USDT perpetual swaps (OkxCollector USDT-swap OHLCV, catalog ids <BASE>USDT.OKX, 1-DAY bars, every leg verified in the catalog with daily history starting 2020-01 to 2020-12). Signal: the George & Hwang (2004) 52-week-high ratio PTH_i = close_i / max(high_i over the trailing N daily bars). The mechanism is anchoring rather than return momentum. Traders use the trailing high as a reference point and under-react to news that pushes a……Show moreShow less
A dollar-neutral, multi-instrument, long-short cross-sectional strategy on 15 OKX USDT perpetual swaps (OkxCollector USDT-swap OHLCV, catalog ids <BASE>USDT.OKX, 1-DAY bars, every leg verified in the catalog with daily history starting 2020-01 to 2020-12). Signal: the George & Hwang (2004) 52-week-high ratio PTH_i = close_i / max(high_i over the trailing N daily bars). The mechanism is anchoring rather than return momentum. Traders use the trailing high as a reference point and under-react to news that pushes a coin toward it, because resistance sellers and break-even sellers cluster there. Coins that are deep below their high carry overhang from trapped holders selling into every bounce. This is NOT a return-ranked momentum rotation: two coins with identical 30-day returns can have opposite PTH ranks. Every Monday 00:00 UTC daily close, rank the 15 names by PTH. Go long the top K and short the bottom K at equal dollar weight per leg, so the book is net-zero dollar. Names already in the right bucket are held, not churned. Data check per L186/L181: all 15 1-DAY bar dirs exist, with first bars from BTC/ETH/BCH/ETC 2020-01-02 to AAVE 2020-12-12, giving ~5.8 years of overlapping history. Implementation note to avoid sandbox_timeout: keep a fixed-length deque of highs per instrument, never rescan the full history per bar, and rank only on the rebalance bar.
Fills the long_short (16.5%), OKX (1.9%) and multi-instrument buckets and avoids BINANCE/BTC concentration. The 52-week-high anchoring family has no prior hypothesis. It avoids L111 (no supplementary gate), L179 (rank-based trigger; the weekly clock only sets cadence) and L102 (no options). Legs were verified in the catalog per L186. Leverage is deliberately absent from optimization_plan to avoid the Layer-1.5 'fixes leverage' mismatch; gross exposure is set by per_leg_weight x K.
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.OKX-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
Weekly Monday UTC close ranking of 15 OKX swaps by close divided by trailing daily high. Holds equal-dollar long top-K and short bottom-K targets, resizing retained legs in place, with daily-close leg and book stops.
Preserves the previous strategy's signal, universe, schedule, risk thresholds and research parameter permissions. Replaces repeated max(buffer) scans with mathematically equivalent bounded monotonic queues: amortized O(1) per incoming bar and O(1) per PTH lookup; sorting remains weekly over 15 names. No performance test was run, so the 300-second timeout is not confirmed resolved. The previous implementation was already bounded and used active-order queries; another timeout should trigger engineering profiling of data loading, funding accrual, execution and reporting rather than another speculative signal edit. No final-exam data was read. The requested outbox file could not be written because this session permits only filesystem reads; the complete task-bound JSON is returned here.
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.OKX-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).
If the book stop is meant as a cooldown, skip that day's rebalance after a stop has fired, or require N weeks flat.
The book stop calls _flatten_all(), but on a Monday close _rebalance runs in the same calculate_signal call. It sets new targets right away, and those are worked on the following bars. So the 12% book stop can be followed by immediate re-entry into a near-identical book, which defeats much of its purpose. The hypothesis does not specify stops, so this is not a mismatch.
No change required. Optionally attach reduce-only stop_market orders to short legs, where squeezes are the main tail risk.
Leg and book stops are checked only on daily closes, with no intrabar protection. A 25% leg stop on an 8% leg caps the planned loss at about 2% of equity per leg, but gaps can exceed it. This is documented and acceptable on 1D bars.
Acceptable for the current universe. Consider ranking over the names that are available (needing at least 2K) instead of requiring all 15.
Every rebalance flattens the whole book if any one of the 15 names lacks a full lookback or has a bar older than stale_bar_days. A single delisted or halted OKX swap would therefore stop the strategy permanently. The base alignment barrier also skips any day on which one leg is missing a bar.
Profile _work_pending/_risk_checks. Return early from _risk_checks when no positions are open, and call _close_pending only for names that have pending items.
The sandbox needed 261s for 1480 days of 15 daily legs, and runtime_review_required=true. The strategy's own per-bar work is bounded: fixed deques, max() over at most 365 highs only when ranking, and a 15-name loop. The cost therefore probably comes from repeated cache.orders scans in _close_pending/_entry_order_inflight and engine overhead, not from history rescans. The full span is 1849 days, so a single backtest should finish, but optimization trials will be expensive.
Smoke test: Sharpe 0.33 with CI [-0.64, 1.22], PF 1.09, max DD 31.9%. The stressed-vol tercile lost 12.7%, and rate_shock_2022 lost 19.2%. Funding is NOT folded (funding_events_available=0), yet OKX funding exists from 2022-01. A long-high/short-low book held about 38 days per leg tends to sit long the crowded names that pay funding, so the full backtest must account for funding. The ledger is also inconsistent: avg_trade_pnl_usd is +$62 while avg_trade_return_pct is -0.29%. In-place resizes and netting flips probably distort the per-position % basis. Reconcile this before using the % figure against the 0.15% floor. Runtime was 261s for 1480 days, so profile the per-bar overhead before optimization.
Analysis
Verification failed (Layer 3 — backtest process): the strategy could not be loaded/built in the backtest process: in _run_backtest supp = _resolve_supp(input_config) ^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/opt/strategy-factory/src/backtesting/subprocess_entry.py", line 118, in _resolve_supp supp = load_for_subprocess( ^^^^^^^^^^^^^^^^^^^^ File "/opt/strategy-factory/src/backtesting/supp_collector.py", line 664, in load_for_subprocess supp = asyncio.run( ^^^^^^^^^^^^ File "/usr/lib/python3.12/asyncio/runners.py", line……Show moreShow less
Verification failed (Layer 3 — backtest process): the strategy could not be loaded/built in the backtest process:
in _run_backtest
supp = _resolve_supp(input_config)
^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/opt/strategy-factory/src/backtesting/subprocess_entry.py", line 118, in _resolve_supp
supp = load_for_subprocess(
^^^^^^^^^^^^^^^^^^^^
File "/opt/strategy-factory/src/backtesting/supp_collector.py", line 664, in load_for_subprocess
supp = asyncio.run(
^^^^^^^^^^^^
File "/usr/lib/python3.12/asyncio/runners.py", line 194, in run
return runner.run(main)
^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/asyncio/runners.py", line 118, in run
return self._loop.run_until_complete(task)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "uvloop/loop.pyx", line 1518, in uvloop.loop.Loop.run_until_complete
File "/opt/strategy-factory/src/backtesting/supp_collector.py", line 683, in _ensure_then_load_supp
await _ensure_perp_funding_collected(legs, db_url, funding_windows)
File "/opt/strategy-factory/src/backtesting/supp_collector.py", line 727, in _ensure_perp_funding_collected
raise InputDataError({
src.backtesting.input_audit.InputDataError: data_unavailable: BTCUSDT.OKX: Required funding remains unavailable after collection
Iteration History
Verification failed (Layer 4 — QA review) [class=code_defect]: - [edge_concern] The sandbox has no funding cash flows for these perpetual positions. Full-history economics should assess funding costs and the losses concentrated in stressed regimes. - [critical] The base template does not call calculate_signal() until min_bars_required() primary bars have accumulated. Because the primary high deque is populated only inside calculate_signal(), the first 252 primary highs are discarded. With high_lookback_days=252,……Show moreShow less
Verification failed (Layer 4 — QA review) [class=code_defect]:
- [edge_concern] The sandbox has no funding cash flows for these perpetual positions. Full-history economics should assess funding costs and the losses concentrated in stressed regimes.
- [critical] The base template does not call calculate_signal() until min_bars_required() primary bars have accumulated. Because the primary high deque is populated only inside calculate_signal(), the first 252 primary highs are discarded. With high_lookback_days=252, the primary deque becomes ready only around bar 504; the configured 370-bar warmup leaves it incomplete. Extras accumulate highs from their first bar, so the strategy can rank and trade a 14-name universe while excluding BTC. Restart warmup reproduces this inconsistency. (line 62) — fix: Populate the primary trailing-high buffer from the base primary history when signal processing starts, with timestamp deduplication, or allow calculate_signal() to run from the first bar and gate trading separately until all 15 buffers contain the required lookback. Verify readiness after the configured 370-bar warmup.
- [critical] Retained positions keep their existing quantities, while replacement positions receive 8% of current equity each. Retained positions' current dollar notionals drift with prices and differ from newly opened legs. Equal counts of longs and shorts therefore do not establish the hypothesis's equal-dollar, dollar-neutral book at weekly rebalances. Independently sized sequential entries and delayed flips introduce further imbalance. (line 181) — fix: Use one equity snapshot to calculate the complete target book, measure retained legs at current prices, and submit quantity adjustments sufficient to restore equal-dollar long and short totals. Preserve retained positions without closing and reopening them. If unchanged quantities are mandatory, resolve that conflict with the dollar-neutral requirement explicitly.
Verification failed (Layer 4 — QA review) [class=hypothesis_mismatch]: - [edge_concern] The sandbox omits perpetual funding cash flows and reports negative average closed-position returns despite positive account growth. Full-history analysis should assess funding costs and distinguish position-level returns from book-level returns. - [critical] The rebalance condition detects a new week but does not require Monday 00:00 UTC. With _last_rebal_week initialized to -1, the first fully warmed, aligned bar triggers a……Show moreShow less
Verification failed (Layer 4 — QA review) [class=hypothesis_mismatch]:
- [edge_concern] The sandbox omits perpetual funding cash flows and reports negative average closed-position returns despite positive account growth. Full-history analysis should assess funding costs and distinguish position-level returns from book-level returns.
- [critical] The rebalance condition detects a new week but does not require Monday 00:00 UTC. With _last_rebal_week initialized to -1, the first fully warmed, aligned bar triggers a rebalance on any weekday. Likewise, if readiness delays processing across Monday, the first ready bar later that week triggers the rotation. This violates the hypothesis's explicit Monday-only schedule. (line 231) — fix: Require the bar timestamp to be Monday 00:00 UTC before creating a new target book, and persist the last executed scheduled timestamp. Continue working existing pending targets independently of this schedule.
- [warning] The 25% leg stop is evaluated only at daily closes. An intraday breach followed by recovery will not trigger it, and a gap can produce a loss substantially beyond the configured percentage. (line 108) — fix: Document this as a daily-close stop, or maintain reduce-only protective stop orders whose quantities track position resizing.
- [warning] The smoke test took approximately 297 seconds, nearly its 300-second limit. The strategy loads 15 daily instruments, keeps bounded 120–365-bar high buffers, and ranks only at weekly rebalances; no expanding-history model fit or supplementary rescan is evident. Nevertheless, repeated optimization runs need an engineering runtime review. (line 166) — fix: Profile order handling, pending-target processing, and engine overhead before optimization while preserving the universe, history, and permitted parameters.
Verification failed (Layer 3 — backtest process): the strategy could not be loaded/built in the backtest process:
in _run_backtest
supp = _resolve_supp(input_config)
^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/opt/strategy-factory/src/backtesting/subprocess_entry.py", line 118, in _resolve_supp
supp = load_for_subprocess(
^^^^^^^^^^^^^^^^^^^^
File "/opt/strategy-factory/src/backtesting/supp_collector.py", line 664, in load_for_subprocess
supp = asyncio.run(
^^^^^^^^^^^^
File "/usr/lib/python3.12/asyncio/runners.py", line 194, in run
return runner.run(main)
^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/asyncio/runners.py", line 118, in run
return self._loop.run_until_complete(task)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "uvloop/loop.pyx", line 1518, in uvloop.loop.Loop.run_until_complete
File "/opt/strategy-factory/src/backtesting/supp_collector.py", line 683, in _ensure_then_load_supp
await _ensure_perp_fu
Outcome Summary
A 15-instrument strategy has to keep its per-bar work small (bounded rolling maxima, ranking only on the rebalance bar, and no per-bar cache/order scans across the universe), and that cost should be checked against the 300s sandbox budget before iterating. In this case even the hypothesis's explicit warning and the monotonic-queue design did not get it under the limit.
After 6 iterations it was abandoned in the verification loop. The Layer 3 sandbox backtest went over the 300s wall-clock limit on a ~20,000-bar window (2400 days of BTCUSDT.OKX 1-DAY bars), so the backtest-review, analyst and risk stages never ran.
A dollar-neutral weekly long-short spread across 15 OKX USDT swaps on daily bars. It went long the names closest to their trailing 52-week high and short the names furthest below it (George & Hwang anchoring ratio, close / trailing max high), as opposed to ranking by return momentum.
No performance figures exist. The strategy never passed verification, so there is no backtest report and no optimization report: no return, Sharpe or trade count.
Backtest and paper results are hypothetical. Trading involves risk of loss.