Skip to content
All hypotheses

Hypotheses

Faithful port of geraked NWERSIASF v1.4 (Nadaraya-Watson Envelope + RSI(5) + ATR-SL-Finder reversal, 1.1x martingale grid up to 20 levels, 50% trailing stop), 3-symbol basket on OKX USDT swaps, 2H, long-short

Line-for-line translation of the MIT-licensed MQL5 expert advisor 'NWERSIASF' v1.4 by Geraked (https://github.com/geraked/metatrader5, Experts/NWERSIASF.mq5, commit d3eb29c; helper library Include/EAUtils.mqh and…

Hypothesis

Line-for-line translation of the MIT-licensed MQL5 expert advisor 'NWERSIASF' v1.4 by Geraked (https://github.com/geraked/metatrader5, Experts/NWERSIASF.mq5, commit d3eb29c; helper library Include/EAUtils.mqh and indicators Indicators/NadarayaWatsonEnvelope.mq5 and Indicators/AtrSlFinder.mq5 at the same commit). Credit: the strategy is by Geraked. The original trades USDCAD, AUDUSD and EURCHF on 2H bars. We keep its structure: 2H bars, three symbols evaluated independently in one strategy, every input at its……Show moreShow less

Line-for-line translation of the MIT-licensed MQL5 expert advisor 'NWERSIASF' v1.4 by Geraked (https://github.com/geraked/metatrader5, Experts/NWERSIASF.mq5, commit d3eb29c; helper library Include/EAUtils.mqh and indicators Indicators/NadarayaWatsonEnvelope.mq5 and Indicators/AtrSlFinder.mq5 at the same commit). Credit: the strategy is by Geraked. The original trades USDCAD, AUDUSD and EURCHF on 2H bars. We keep its structure: 2H bars, three symbols evaluated independently in one strategy, every input at its default value. The symbols are mapped to OKX USDT perpetual swaps. OKX is 0.3% of the corpus, carries pure OHLCV with multi-year 2H history, and needs no supplementary feed (L160). The three names are chosen by a stated rule, not by hand: the three OKX USDT swaps with the oldest 2H history in the catalog, BTC excluded because it is over-concentrated in the corpus. That gives BCHUSDT.OKX (from 2019-12), ADAUSDT.OKX (2020-03) and ATOMUSDT.OKX (2020-05). The primary is BCHUSDT.OKX; the other two go in extra_instruments with their 2H bar types. Performance claims in the source ('2019-2023 on FX') are unverified; our fee-inclusive backtest decides. INDICATORS (reproduce exactly; bar-index notation as in MQL5, where index 0 is the forming bar, 1 the last closed bar and 2 the one before it; on our platform, at the close of bar t, index 1 = t and index 2 = t-1): (1) Nadaraya-Watson Envelope (NweBandWidth=8.0, NweMultiplier=3.0, NweWindowSize=500). This is the causal/endpoint form: it looks only at the current bar and older bars. yhat[t] = sum_{k=0..499} close[t-k]*g(k) / sum_{k=0..499} g(k), with g(k)=exp(-k^2/(2*8^2)) (k=0 is the current bar; truncate if fewer than 500 bars exist). resid[t] = |close[t]-yhat[t]|. mae[t] = (sum_{k=0..499} resid[t-k]) / 500 * 3.0. Note: it divides by 500 even when fewer are available, so require full warm-up. upper[t]=yhat[t]+mae[t] (buffer 0), lower[t]=yhat[t]-mae[t] (buffer 1). Precompute the 500 weights once, keep close and resid in fixed-length deques/numpy ring buffers, compute yhat as one dot product and mae as a running sum. That keeps per-bar cost O(500) and flat, avoiding the sandbox_timeout that killed recent runs. Warm-up is 1000 bars (~83 days). (2) RSI: MT5 iRSI, length 5, on close, Wilder smoothing. (3) ATR Stop-Loss Finder (AsfLength=14, AsfMultiplier=0.75): TR[t]=max(high-low, |high-close[t-1]|, |low-close[t-1]|). ma = SIMPLE mean of the last 14 TR (not Wilder). asfUp[t]=high[t]+0.75*ma, asfDn[t]=low[t]-0.75*ma. SIGNAL (CheckForSignal, per symbol, at each 2H bar close): BUY bc = low[2] < lower[2] AND close[1] > open[1] AND ask < lower[1] + 0.5*(upper[1]-lower[1]) AND RSI[2] < 30. SELL sc = high[2] > upper[2] AND close[1] < open[1] AND bid > upper[1] - 0.5*(upper[1]-lower[1]) AND RSI[2] > 70. Use the close of bar 1 for ask and bid. RSI[2] is the RSI value of the bar BEFORE the last closed bar; do not 'fix' this. BUY is checked first (else-if). Reverse=false. GATES (in source order): OpenNewPos=true. MarginLimit=4000: if any position is open anywhere and margin level (equity / used margin * 100, used margin = total open notional / leverage) < 4000%, no new signal entries on ANY symbol. Grid additions and exits are not gated by this. MultipleOpenPos=true. Skip a symbol if it already has an open position/basket (OPTotal>0). MinPosInterval=4: skip a symbol if a CLOSING fill (exit, not entry) happened on it within the last 4 bars (8h). SpreadLimit=-1 disabled. News=false. EquityDrawdownLimit=0 disabled. ENTRY AND SIZING (GerEA BuyOpen/SellOpen, RISK_DEFAULT, Risk=1.2): entry at market. Long: sl = asfDn[1], pd = |entry - sl|. Short: sl = asfUp[1]. Quantity = 0.012 * account balance / pd, so a move of pd costs 1.2% of balance. TP = entry +/- 1.5*pd is computed but IgnoreTP=true, so no TP is placed. With Grid=true the initial SL is NOT placed as a resting stop. pd is stored per basket as the grid step and trail unit. leverage=5 (OKX cap is higher). If an order would exceed available margin it is rejected and skipped, as the broker would do. There is no other size cap. GRID (checkForGrid; Grid=true, GridVolMult=1.1, GridMaxLvl=20, GridTrailingStopLevel=0). Per symbol basket with n open legs (1 <= n < 20). Long: lastPrice = lowest leg entry. Next level lvl = firstEntry - n*pd; if |lastPrice - pd - lvl| > 0.5*pd then lvl = lastPrice - pd. When price <= lvl, add a leg of qty = 1.1 * largest existing leg qty. Then set the basket TP (one reduce-only take-profit for the whole basket) at the price where the aggregate basket PnL equals target_prof. target_prof = (first-leg qty * pd, the original 1.2% risk amount) + accumulated basket cost (fees) if positive. Short is mirrored (highest entry, lvl = firstEntry + n*pd). Implement the level as a resting limit order so it fills intrabar, approximating the source's 120s timer polling. The basket closes only at the basket TP. GridTrailingStopLevel=0 means no grid trailing. At 20 legs nothing more is added, and there is NO stop loss: the basket is held until TP or until the engine's margin/liquidation logic intervenes. This is faithful to the source (IgnoreSL false, but grid mode suppresses the broker SL; EquityDrawdownLimit=0). TRAILING (checkForTrail, TrailingStopLevel=50 -> 0.5; applies while the basket has exactly one leg). Long: if bid > entry, d = bid - entry, candidate sl = max(entry, breakeven_incl_fees) + d - 0.5*pd. Apply only if candidate >= entry and it raises the existing stop (ratchet). In effect, once price is >= ~0.5*pd in profit, a stop trails 0.5*pd behind price, floored at breakeven. If the candidate stop is within 30 points (use 30 * price increment) of price and the position is net profitable after fees, close at market. Short is mirrored. Place the trail as a stop-market order updated at each bar close so it fills intrabar. Once a basket has >1 leg only the basket TP applies. EXITS: single leg -> trailing stop only (no TP, no SL). Multi-leg -> basket TP only. Flat state: a symbol is flat whenever it has no basket. The expected portfolio is flat ~55-70% of the time, because entries need a 3x-MAE envelope pierce plus an RSI(5) extreme plus a reversal candle.

Analysis

NWERSIASF

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