Skip to content
All strategies

Strategies

AFAOSMD (geraked, MIT) on ETHUSDT.OKX 30m: Average Force zero-cross + Andean Oscillator + MACD(100,200) signal, REVERSED (contrarian), swing-distance 50% trailing stop, 1.1x averaging grid up to 20 levels

Outcome: Abandoned

AfaosmdEthOkxReverseGrid

Outcome Summary

This hypothesis ported geraked's AFAOSMD expert from NZDCAD FX to ETHUSDT.OKX 30m as a contrarian signal strategy with a trailing stop and a 1.1x averaging grid of up to 20 levels. A partial NautilusTrader implementation, AfaosmdEthOkxReverseGrid, was drafted. After 2 iterations the coding stage timed out three times and the run was abandoned with reason 'coding_stuck_3_timeouts'. No backtest, optimization, analyst or risk review took place, so the edge was never tested.

Hypothesis

A faithful statement of geraked's MT5 expert 'AFAOSMD' v1.5 (https://github.com/geraked/metatrader5, Experts/AFAOSMD.mq5 + Include/EAUtils.mqh + Indicators/AverageForce.mq5 + Indicators/AndeanOscillator.mq5, MIT licence; credit: geraked). The original trades NZDCAD 30M FX, which our platform does not carry, so it is mapped to ETHUSDT.OKX (OKX USDT perpetual swap, 30-MINUTE bars, long+short, taker 0.05%, ~0.10% RT). OKX is chosen because it is 0.3% of the corpus while BINANCE is 67%. The mechanism is pure OHLCV,……Show moreShow less

A faithful statement of geraked's MT5 expert 'AFAOSMD' v1.5 (https://github.com/geraked/metatrader5, Experts/AFAOSMD.mq5 + Include/EAUtils.mqh + Indicators/AverageForce.mq5 + Indicators/AndeanOscillator.mq5, MIT licence; credit: geraked). The original trades NZDCAD 30M FX, which our platform does not carry, so it is mapped to ETHUSDT.OKX (OKX USDT perpetual swap, 30-MINUTE bars, long+short, taker 0.05%, ~0.10% RT). OKX is chosen because it is 0.3% of the corpus while BINANCE is 67%. The mechanism is pure OHLCV, so it needs no supplementary feed (lesson 160). All performance the author claims is unverified; our fee-inclusive backtest decides. INDICATORS (all on closed 30m bars; index 1 = last closed bar, index 2 = the bar before): (1) Average Force AF(period P=20, smooth S=9): C1[i] = (close[i] - lowest_low(P bars ending at i)) / (highest_high(P) - lowest_low(P)) - 0.5 (0 if the range is 0); AF[i] = simple mean of C1 over the last S=9 bars. Buffer 0. (2) Andean Oscillator (Length=50, SignalLength=9): alpha = 2/(Length+1). Recursively: up1 = max(max(close,open), up1_prev - (up1_prev - close)*alpha); up2 = max(max(close^2,open^2), up2_prev - (up2_prev - close^2)*alpha); dn1 = min(min(close,open), dn1_prev + (close - dn1_prev)*alpha); dn2 = min(min(close^2,open^2), dn2_prev + (close^2 - dn2_prev)*alpha). Each state is seeded with close (or close^2) when it is 0. Bull = sqrt(dn2 - dn1^2); Bear = sqrt(up2 - up1^2); Signal = EMA9(max(Bull,Bear)), which the rules do not use. Compute on raw prices (squared ETH prices fit in float64) and guard sqrt of tiny negatives with max(0,.). (3) MACD main line MD = EMA(close,100) - EMA(close,200) (iMACD with signal period 1; only the main line is read). RAW SIGNALS (evaluated once per new bar, at each 30m bar close): BuySignal: Bull[1] > Bear[1] AND AF[2] < 0 AND AF[1] > 0 AND MD[2] > 0 AND MD[1] > 0 AND MD[2] < MD[1]. SellSignal: Bull[1] < Bear[1] AND AF[2] > 0 AND AF[1] < 0 AND MD[2] < 0 AND MD[1] < 0 AND MD[2] > MD[1]. BuySignal is checked first; SellSignal is checked only if BuySignal did not fire. REVERSE = true (default): the EA trades the OPPOSITE side. A BuySignal opens a SHORT and a SellSignal opens a LONG. The strategy therefore fades a fresh AF zero-cross that agrees with the Andean regime and a rising or falling MACD trend. GATES before signals are evaluated (OnTick, new bar only): OpenNewPos=true; SpreadLimit=-1 (off); MarginLimit=300%, meaning skip new entries when positions are open and the margin level is below 300% (translate as: skip new entries when margin used > equity/3). Because Grid=true, there is NO new signal entry while any position (any grid level) is open. News=false; EquityDrawdownLimit=0 (off). SWING DISTANCE pd (stored per basket; the EA writes it into the order comment): for a BuySignal, swing = lowest low of bars 1..7 minus SLDev; for a SellSignal, swing = highest high of bars 1..7 plus SLDev. SLDev=60 points on a 5-digit FX quote is about 0.07% of price, so use 0.07% of entry price. pd = |entry - swing|. TPCoef=1.0, so the original TP distance is also pd. STOPS AND TARGETS ON THE FIRST ORDER: with Reverse=true the EA swaps SL and TP and also swaps the IgnoreSL and IgnoreTP flags. With Grid=true it forces isl=true before the swap. With the defaults (IgnoreSL=false, IgnoreTP=true, Grid=true), the first order therefore has NO stop-loss and NO take-profit. Exits come only from the trailing logic and the grid basket TP below. POSITION SIZE (RISK_DEFAULT, Risk=1%): volume = 1% of min(balance, free margin) / |entry - SLparam|. After the reverse swap SLparam is the original TP price, so the distance is TPCoef*pd = pd. Level-1 notional = 0.01*equity*entry/pd (e.g. pd = 1.2% gives notional of about 0.83x equity). TRAILING (Trail=true, TrailingStopLevel=50%): MT5 checks this every 30s; we check at each 30m bar close and hold the stop as a resting stop order, so it can trigger intrabar. It applies while the basket has exactly one position. For a long: once price is above entry by d, candidate stop = max(entry, breakeven including costs) + d - 0.5*pd. The stop is only set if it is >= entry and tighter than the existing stop, so it activates after a favourable move of 0.5*pd and then trails 0.5*pd behind the best price. If price is within 30 points (~0.035%) of the candidate stop and the trade is net profitable, close at market. Shorts mirror this. GridTrailingStopLevel=0, so there is no trailing once the grid has 2+ levels. GRID (Grid=true, GridVolMult=1.1, GridMaxLvl=20): while n (open levels) < 20, check whether price has moved ADVERSELY to level lvl = first_entry -/+ n*pd (long: price <= first_entry - n*pd; short: price >= first_entry + n*pd). If |last_level_price -/+ pd - lvl| > 0.5*pd, use last_level_price -/+ pd as the level instead. If the level is reached and price is at least 30 points from the last level price, add a position in the same direction with volume = largest existing volume * 1.1. Then set ONE basket take-profit on all levels at the price where total basket PnL = level-1 volume * pd + accumulated costs, at least 30 points from price. The whole basket closes when that TP is hit. The source has no basket stop. PLATFORM ADAPTATION (only where the source cannot run as written): NautilusTrader holds one net position per instrument, so grid levels are tracked internally as a list of (price, qty), and the net position is increased by market order. Basket breakeven and TP are computed from that list. Leverage config = 10x (the engine clamps at 20x). An add-on that would take gross notional above 10x equity is skipped, which mirrors MT5's 'no money' rejection. Flat state: flat between baskets, expected ~55-65% of the time. PRE-REGISTERED SECOND VARIANT (scored separately; Grid=false, all else default): no averaging. With Reverse and IgnoreTP=true/IgnoreSL=false, the first order gets NO stop and a TAKE-PROFIT at the original swing level (distance pd in the trade's favour), plus the same 50% trail. Report both; the primary claim is the faithful default.

A faithful port of the defaults: the indicator formulas, signal conjunction, Buy-before-Sell ordering, reverse swap (with Grid, the first order has no SL/TP), RISK_DEFAULT sizing against the swapped SL distance (= pd), the 50% trail, and the 1.1x/20-level grid with its basket TP formula. Platform adaptations: (1) the EA checks trail and grid on a 30s timer; here they run at each 30m bar close and are held as resting orders (reduce-only STOP_MARKET trail, LIMIT grid add, reduce-only LIMIT basket TP), so they fill……Show moreShow less

A faithful port of the defaults: the indicator formulas, signal conjunction, Buy-before-Sell ordering, reverse swap (with Grid, the first order has no SL/TP), RISK_DEFAULT sizing against the swapped SL distance (= pd), the 50% trail, and the 1.1x/20-level grid with its basket TP formula. Platform adaptations: (1) the EA checks trail and grid on a 30s timer; here they run at each 30m bar close and are held as resting orders (reduce-only STOP_MARKET trail, LIMIT grid add, reduce-only LIMIT basket TP), so they fill intrabar at their levels. The trail's best price uses bar highs/lows. (2) NautilusTrader nets to one position, so the grid levels are kept as scalar aggregates (count, total qty, notional sum, first/last price, largest level qty). These are reconciled each bar against the position's quantity and avg_px_open, and a grid fill price is derived from the change in the average. A position whose ts_opened does not match the basket (a grid limit that filled after the basket TP or stop in the same bar) is closed as an orphan. (3) The order management runs inside calculate_signal because the base template skips should_exit while non-contingent orders rest. It is skipped during warmup and the replay freeze. (4) SLDev 60 points is mapped to 0.07% of price and the 30-point slippage/near distance to 0.035%. The MarginLimit gate is implied because Grid blocks new entries while any level is open. (5) Leverage is set to 10x and the grid stops adding past 10x gross, which stands in for MT5's no-money rejection. Risk note: the source has no basket stop, so a sustained trend against a filled grid is exposed up to 10x equity. That martingale tail is the hypothesis's claim to test, not something this port adds.

coding_stuck_3_timeouts

Implementation

Stratmill translation of geraked's MT5 expert AFAOSMD v1.5 (MIT) on ETHUSDT.OKX 30m. Raw BuySignal = Andean Bull>Bear, Average Force(20,9) crosses up through 0, MACD(100,200) main line >0 and rising; SellSignal mirrors it. Reverse=true, so a BuySignal opens a SHORT and a SellSignal a LONG. Swing distance pd = |entry - (7-bar low - 0.07%)| (or 7-bar high + 0.07%); level-1 size = 1% equity / pd. No stop or TP on the first order. While one level is open, a resting reduce-only stop trails 50% of pd behind the best price once price has moved 0.5*pd in favour (breakeven includes fees), with a market close when price is within 0.035% of the stop and net profitable. Averaging grid: resting limit at first -/+ n*pd (or last -/+ pd), size = largest level * 1.1, up to 20 levels, skipped once gross notional would exceed 10x equity. From level 2 on, one reduce-only basket TP sits where basket PnL = level-1 qty * pd + round-trip fees. No basket stop. grid_enabled=false gives the pre-registered variant: TP at pd plus the 50% trail, no averaging.

Iteration History

sandbox_timeout: Verification failed (Layer 3 — sandbox backtest): smoke test exceeded the 300s wall-clock limit on a ~20000-bar window (416 days of ETHUSDT.OKX-30-MINUTE-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

A long, stateful translation of this kind (three custom indicators, reverse-swapped stops, a trailing stop and a 20-level grid basket tracked by hand on a netting engine) can exceed the coding time budget, so a future attempt should first code a simpler core, such as the pre-registered no-grid variant, and add the grid afterwards.

The run was abandoned in the coding stage with reason 'coding_stuck_3_timeouts' after 2 iterations. The backtest-review, analyst and risk-officer stages were never reached.

A faithful port of geraked's MIT-licensed MT5 expert AFAOSMD v1.5 to ETHUSDT.OKX 30m bars. It faded (traded against) Average Force zero-crosses that agreed with the Andean Oscillator regime and a MACD(100,200) trend, exiting through a 50% swing-distance trailing stop and a 1.1x averaging grid of up to 20 levels with a single basket take-profit and no basket stop.

There are no performance figures. The backtest report is empty and no optimization report exists, so return, Sharpe and trade count were never measured.

Analysis

AFAOSMD

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