The FX-CHAOS Scalp MT4 strategy is a direct port of the MetaTrader 4 expert advisor that combines an Awesome Oscillator filter with ZigZag levels built on fractals. The StockSharp version keeps the multi-timeframe design of the original system: hourly candles generate trade signals while daily candles provide a higher-timeframe bias. Two embedded trackers reconstruct the "ZigZag on Fractals" indicator by scanning five-candle patterns and recording alternating swing highs and lows.
Trading Workflow
Data collection
Hourly candles feed the primary execution logic and risk controls.
Daily candles update the long-term ZigZag swing used as a trend filter.
The Awesome Oscillator (5, 34) is evaluated on the hourly series through the high-level indicator API.
ZigZag reconstruction
Every finished candle is stored in a sliding five-element window.
When the middle candle forms an up fractal, the tracker saves the candle high as the latest swing and switches direction to "up"; a down fractal does the same for lows.
Consecutive swings in the same direction are only replaced if the new extreme is more pronounced, mimicking the buffer logic of the MT4 indicator.
Signal detection
The breakout buffer adds two price-step offsets to the previous hour high/low, mirroring the 2*Point padding found in the original code.
For long entries the candle must open below the buffered high, close above it, remain below the most recent hourly ZigZag swing, close above the latest daily swing, and keep the Awesome Oscillator negative.
Short entries mirror the conditions using the buffered low, upper ZigZag level, and positive oscillator values.
Order execution and conflict resolution
Opposite positions are closed before a new order is sent so the strategy never keeps simultaneous long and short trades.
The executed close price is stored to derive stop-loss and take-profit distances in subsequent candles.
Risk Management
Stop-loss and take-profit thresholds are optional; a value of 0 disables the corresponding rule.
At the end of each finished candle the strategy checks whether the candle range touched the configured stop or target and closes the position if the level was breached.
When an opposite breakout appears the position is liquidated first, then the new trade is sent on the same candle to preserve the single-position rule.
Parameters
Name
Description
Volume
Trade volume in lots applied to every market order.
Stop Loss (pts)
Distance in points for the protective stop. Multiplied by the security price step. Set to 0 to disable.
Take Profit (pts)
Distance in points for the profit target. Multiplied by the price step. Set to 0 to disable.
Breakout Buffer
Additional points added to the previous candle extremum before testing breakouts. Default value reproduces the 2*Point cushion used in MT4.
Spread (pts)
Average spread in points that is added to the breakout threshold on buy signals so the entry mirrors 2*Point + spread from MT4.
Trading Candle
Primary timeframe used for entries (defaults to one hour).
Daily Candle
Higher timeframe used for the ZigZag filter (defaults to one day).
Implementation Notes
The strategy relies on the high-level SubscribeCandles API and BindEx to avoid working with indicator buffers directly, respecting the repository guidelines.
The price step retrieved from Security.PriceStep is used to convert parameter values expressed in points into absolute price distances. If the instrument lacks a step the code falls back to 1.
Both ZigZag trackers reset on OnReseted and pause trading until they accumulate enough candles to determine the first swing. This prevents premature entries when historical context is missing.
Chart rendering draws the hourly candles, the Awesome Oscillator, and the strategy trades to help compare the StockSharp implementation with the MT4 template.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class FxChaosScalpMt4Strategy : Strategy
{
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<int> _momentumPeriod;
private readonly StrategyParam<int> _cooldownCandles;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevMom;
private bool _hasPrev;
private int _cooldownRemaining;
public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
public int MomentumPeriod { get => _momentumPeriod.Value; set => _momentumPeriod.Value = value; }
public int CooldownCandles { get => _cooldownCandles.Value; set => _cooldownCandles.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public FxChaosScalpMt4Strategy()
{
_emaPeriod = Param(nameof(EmaPeriod), 14).SetDisplay("EMA Period", "EMA filter", "Indicators");
_momentumPeriod = Param(nameof(MomentumPeriod), 10).SetDisplay("Momentum", "Momentum period", "Indicators");
_cooldownCandles = Param(nameof(CooldownCandles), 200).SetDisplay("Cooldown", "Candles between signals", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame()).SetDisplay("Candle Type", "Candle timeframe", "General");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevMom = default;
_hasPrev = default;
_cooldownRemaining = default;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevMom = 0;
_hasPrev = false;
_cooldownRemaining = 0;
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
var mom = new Momentum { Length = MomentumPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ema, mom, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal ema, decimal mom)
{
if (candle.State != CandleStates.Finished) return;
var close = candle.ClosePrice;
if (!_hasPrev) { _prevMom = mom; _hasPrev = true; return; }
if (_cooldownRemaining > 0)
{
_cooldownRemaining--;
_prevMom = mom;
return;
}
if (close > ema && _prevMom <= 0 && mom > 0 && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
_cooldownRemaining = CooldownCandles;
}
else if (close < ema && _prevMom >= 0 && mom < 0 && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
_cooldownRemaining = CooldownCandles;
}
_prevMom = mom;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage, Momentum
from StockSharp.Algo.Strategies import Strategy
class fx_chaos_scalp_mt4_strategy(Strategy):
def __init__(self):
super(fx_chaos_scalp_mt4_strategy, self).__init__()
self._ema_period = self.Param("EmaPeriod", 14).SetDisplay("EMA Period", "EMA filter", "Indicators")
self._momentum_period = self.Param("MomentumPeriod", 10).SetDisplay("Momentum", "Momentum period", "Indicators")
self._cooldown_candles = self.Param("CooldownCandles", 200).SetDisplay("Cooldown", "Candles between signals", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_mom = 0.0
self._has_prev = False
self._cooldown_remaining = 0
@property
def ema_period(self): return self._ema_period.Value
@property
def momentum_period(self): return self._momentum_period.Value
@property
def cooldown_candles(self): return self._cooldown_candles.Value
@property
def candle_type(self): return self._candle_type.Value
def OnReseted(self):
super(fx_chaos_scalp_mt4_strategy, self).OnReseted()
self._prev_mom = 0.0
self._has_prev = False
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(fx_chaos_scalp_mt4_strategy, self).OnStarted2(time)
self._prev_mom = 0.0
self._has_prev = False
self._cooldown_remaining = 0
ema = ExponentialMovingAverage()
ema.Length = self.ema_period
mom = Momentum()
mom.Length = self.momentum_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(ema, mom, self.process_candle).Start()
def process_candle(self, candle, ema, mom):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
ema_val = float(ema)
mom_val = float(mom)
if not self._has_prev:
self._prev_mom = mom_val
self._has_prev = True
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
self._prev_mom = mom_val
return
if close > ema_val and self._prev_mom <= 0 and mom_val > 0 and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._cooldown_remaining = self.cooldown_candles
elif close < ema_val and self._prev_mom >= 0 and mom_val < 0 and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._cooldown_remaining = self.cooldown_candles
self._prev_mom = mom_val
def CreateClone(self):
return fx_chaos_scalp_mt4_strategy()