Skip to content

View translation

HyperliquidMajors4hDualEmaTrendLongShortAtrBasket

Hypotheses

Hyperliquid Majors 4H Dual-EMA Time-Series (Absolute) Trend Long/Short Basket (ATR-Trailing, 3-Parameter, Managed-Futures Style)

Hypotheses

A LONG/SHORT, multi-instrument absolute-momentum (time-series trend) basket on liquid HYPERLIQUID perpetuals (BTCUSD, ETHUSD, SOLUSD, plus 2-3 more majors such as AVAXUSD/BNBUSD/ARBUSD that have the full ~2.3yr 4H history). Each asset is traded INDEPENDENTLY on its own trend state via a simple dual-EMA cross on 4-HOUR bars: long when the fast EMA is above the slow EMA, short when below, position-sized by ATR so each leg carries equal volatility risk. This deliberately targets the THREE most under-represented buckets simultaneously: HYPERLIQUID venue (5.1% actual vs 20% target — and the analyst explicitly flagged HL-4H as the only reliable ~2.3yr OHLCV data path for future non-VWAP HL hypotheses), the long_short DIRECTION bucket (12.6% vs 55% cap; long_only is grossly over-represented at 87.4%), and multi-instrument scope. CRITICAL DESIGN CHOICE — this is TREND-FOLLOWING, the structural OPPOSITE of the mean-reversion mechanisms that died 9+ times this session. Trend-following has a NON-INVERTED payoff by construction (cut losers at the ATR/cross-flip stop, let winners ride the trend), directly curing the inverted avg_win<avg_loss signature that the analyst cited as the root cause of every VWAP-excursion-MR abandon. It also avoids: the falling-knife problem (we trade WITH excursions, never fade them), cointegration assumptions (each asset judged against its own trend, no pair ratio), funding/premium/OI/L-S supplementary data (the 2023-cutoff data-engineering blocker that killed the funding-carry and premium-index hypotheses — this uses OHLCV ONLY), and tick-data coverage holes. Distinct from the existing HyperliquidMajorsCrossSectionalMomentumRotationLS sibling: that is CROSS-SECTIONAL rank rotation at weekly/1D cadence; this is ABSOLUTE per-asset trend at 4H. Kept to 3 core parameters to resist the recurring overfit failures.

Hypotheses

Iteration 4 fixes ONLY the reported Layer-3 sandbox_timeout. The prior code recomputed both EMAs and the ATR by looping the entire 500-bar buffer inside calculate_signal()/on_extra_bar() on every bar - roughly 1,500 heavy .as_double() ops per bar event across ~72,000 bar events on five 4H legs, which blew the 300s wall clock. The indicators are now INCREMENTAL: per-instrument state holds the two EMA values, a deque(maxlen=atr_period) of true ranges with a running sum, and the previous close; it is seeded once (a single O(len(buffer)) pass the first time a leg has min_bars) and then advanced in O(1) per new bar, guarded by the bar timestamp so a repeated call for the same bar never double-updates. The ATR remains the same simple mean of the last atr_period true ranges; the EMAs converge to the same values from the one-time seed. Signal logic, entry/exit rules, sizing, config, imports and class structure are otherwise unchanged, so the Layer 1/1.5/2 passes are preserved.

Hypotheses

backtest_failed:data: the full backtest could not run 3 times — test_agent.py", line 424, in _ensure_funding_for_perp_legs raise RuntimeError( RuntimeError: Funding data unavailable for perp leg 'AVAXUSD.HYPERLIQUID' — on-demand collection failed or the venue has no funding history (backtest blocked for accuracy; set SF_DISABLE_ONDEMAND_FUNDING=1 to bypass) Pipeline/data failure, not a verdict on the strategy; revive once the cause is fixed.

Implementation

Long/short absolute-momentum (time-series trend) basket on five liquid Hyperliquid perpetuals (BTCUSD, ETHUSD, SOLUSD, AVAXUSD, BNBUSD) on 4-HOUR bars. Each leg is traded independently: long while the 20-EMA is above the 50-EMA, short while below, exiting on the EMA cross flip or an ATR(14) x 3 adverse move from entry. Legs are sized to equal volatility risk (equity * risk_frac * price / (atr_stop_mult * ATR)), capped at 18% of equity each. OHLCV only - no supplementary data.

Verification Results

If a trailing stop is intended, track the position's peak favorable price and stop when price retraces atr_stop_mult*ATR from that peak, rather than from entry.

Verification Results

Hypothesis title says 'ATR-Trailing' stop, but the exit implements a FIXED ATR stop measured from the fill price (entry - price > atr_stop_mult*ATR), not a trailing high-water stop. The config description accurately calls it an 'ATR(14) x 3 adverse move from entry', so intent is documented. The structural risk controls the hypothesis names (ATR stop + EMA cross-flip exit) are both present, so this is not a blocking mismatch — but a true trailing stop would lock in more of a winning trend and better matches the title.

Verification Results

Verify base_template routes warmup through the override; if not, rename to a strategy-private helper to avoid silent shadowing.

Verification Results

min_bars_required() overrides a framework method (static analyzer base_shadow warning). The override returns a valid int (>= ema_slow + 5) and is used for warmup gating; behavior appears intended and correct, but confirm the base template consumes this override rather than its own computation for the warmup gate.

Verification Results

Sandbox smoke test is fragile-positive: profit_factor 1.002, Sharpe 0.497, win_rate 29%, max_drawdown 45.8%. Regime attribution shows only the 'calm' tercile is profitable (+53%) while 'normal' (-31%) and 'stressed' (-27%) both lose — the edge is concentrated in one vol regime. Capacity is low (capacity_usd ~$109K, impact_cost_pct 95.7%, impact_folded) driven by ATR-sizing five legs up to 18% each (avg_position_pct ~19.6%, exposure ~487%); on thinner majors (AVAX/BNB) impact could erode the thin per-trade edge in a full backtest. avg_trade_return_pct 0.44% clears the fee floor, so it is worth a real backtest — but PF barely above 1 and regime concentration are the first things the analyst should stress on full history.

Analysis

Backtest gate: - metrics unreliable: 636 trades / 956 data days is too little evidence (need >=5 trades over >=10 days) — increase signal frequency or widen the window

Iteration History

HlMajorsDualEmaTrendLongShortBasket

Iteration History

Verification failed (Layer 2 — synthetic scenarios): Parameters used: ['ema_fast', 'ema_slow', 'min_notional', 'atr_trail_mult'] Check that __init__ sets all attributes from self.parameters.get(). - steady_uptrend: TypeError: HlMajorsDualEmaTrendLongShortBasket._ema() takes 2 positional arguments but 3 were given (bar timestamp: 1735692480000) - steady_downtrend: TypeError: HlMajorsDualEmaTrendLongShortBasket._ema() takes 2 positional arguments but 3 were given (bar timestamp: 1735692480000) - flat_ranging: TypeError: HlMajorsDualEmaTrendLongShortBasket._ema() takes 2 positional arguments but 3 were given (bar timestamp: 1735692480000) - volatility_spike: TypeError: HlMajorsDualEmaTrendLongShortBasket._ema() takes 2 positional arguments but 3 were given (bar timestamp: 1735692480000) - zero_volume: TypeError: HlMajorsDualEmaTrendLongShortBasket._ema() takes 2 positional arguments but 3 were given (bar timestamp: 1735692480000) - price_gap: TypeError: HlMajorsDualEmaTrendLongShortBasket._ema() takes 2 positional arguments but 3 were given (bar timestamp: 1735692480000)

Iteration History

Verification failed (Layer 1 — static analysis): - No class extending FactoryStrategy found (line None) - Required method not implemented: should_enter (line None) - Required method not implemented: calculate_signal (line None) - Required method not implemented: position_size (line None) - Required method not implemented: should_exit (line None)

Iteration History

sandbox_timeout: Verification failed (Layer 3 — sandbox backtest): smoke test exceeded the 300s wall-clock limit on a ~20000-bar window (2400 days of BTCUSD.HYPERLIQUID-4-HOUR-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).

Iteration History

Verification failed (Layer 2 — synthetic scenarios): Parameters used: ['ema_fast', 'ema_slow', 'risk_frac', 'atr_period', 'max_leg_frac', 'min_notional', 'atr_stop_mult'] Check that __init__ sets all attributes from self.parameters.get(). - steady_uptrend: TypeError: HyperliquidMajors4hDualEmaTrendLongShortAtrBasket._ema_from_bars() takes 2 positional arguments but 3 were given (bar timestamp: 1735692840000) - steady_downtrend: TypeError: HyperliquidMajors4hDualEmaTrendLongShortAtrBasket._ema_from_bars() takes 2 positional arguments but 3 were given (bar timestamp: 1735692840000) - flat_ranging: TypeError: HyperliquidMajors4hDualEmaTrendLongShortAtrBasket._ema_from_bars() takes 2 positional arguments but 3 were given (bar timestamp: 1735692840000) - volatility_spike: TypeError: HyperliquidMajors4hDualEmaTrendLongShortAtrBasket._ema_from_bars() takes 2 positional arguments but 3 were given (bar timestamp: 1735692840000) - zero_volume: TypeError: HyperliquidMajors4hDualEmaTrendLongShortAtrBasket._ema_from_bars() takes 2 positional arguments but 3 were given (bar timestamp: 1735692840000) - price_gap: TypeError: HyperliquidMajors4hDualEmaTrendLongShortAtrBasket._ema_from_bars() takes 2 positional arguments but 3 were given (bar timestamp: 1735692840000)
Strategy report

Backtest and paper results are hypothetical. Trading involves risk of loss.