This strategy ports the ENGULFING MetaTrader expert advisor to the StockSharp high-level API. It combines a bullish/bearish engulfing pattern on the working timeframe with higher-timeframe momentum confirmation and a monthly MACD trend filter. Risk management reproduces the original break-even and trailing behaviour using stop distances measured in instrument steps.
How it Works
Candlestick Pattern – the latest finished candle must engulf the previous bar in the direction of the trade. The strategy also checks that the bar two periods ago overlaps the prior bar, mirroring the original fractal-based confirmation.
Trend Filter – fast and slow weighted moving averages (LWMA analogue) gate entries. Long trades require the fast average to trade above the slow average and vice versa for shorts.
Momentum Filter – a 14-period momentum indicator calculated on a higher timeframe must deviate from the neutral level (100) by at least the configured threshold on any of the last three values. This reproduces the MomLevelB/MomLevelS checks from the MQL code.
MACD Filter – a monthly (30-day) MACD series must show the main line above the signal line for longs and below for shorts, just like the MacdMAIN0 vs MacdSIGNAL0 comparison in the EA.
Order Handling – the strategy always flips the position when an opposite signal appears. Protective logic closes trades whenever stop, target, break-even or trailing rules fire.
Risk Management
Stop Loss / Take Profit – distances are configured in instrument steps (ticks). They mirror the Stop_Loss and Take_Profit inputs of the original EA.
Trailing Stop – optional trailing measured in steps. The stop follows the best price achieved after entry.
Break-Even Move – once price advances by BreakEvenTriggerSteps, the stop is moved to the entry plus BreakEvenOffsetSteps, reproducing the "no loss" feature (USEMOVETOBREAKEVEN).
Money-based targets from the MQL script (Use_TP_In_Money, Take_Profit_In_percent) are intentionally omitted to keep the logic consistent with StockSharp's unit system. Percentage or currency-based exits can be recreated by adjusting the stop/take parameters.
Parameters
Parameter
Description
FastMaPeriod / SlowMaPeriod
Lengths of the weighted moving averages used for trend confirmation.
MomentumPeriod
Momentum length on the higher timeframe.
MomentumBuyThreshold / MomentumSellThreshold
Minimum absolute deviation from 100 required for the momentum filter.
MacdFastLength, MacdSlowLength, MacdSignalLength
MACD configuration applied to MacdCandleType.
StopLossSteps, TakeProfitSteps
Protective stop and target distances in price steps. Set to zero to disable.
Distance required before moving the stop to break-even and the offset applied.
CandleType
Primary timeframe where engulfing patterns are evaluated.
HigherCandleType
Higher timeframe used for the momentum filter (defaults to 1 hour).
MacdCandleType
Timeframe for the MACD trend filter (defaults to 30 days ≈ monthly).
Usage
Attach the strategy to a security and set CandleType, HigherCandleType, and MacdCandleType to match your preferred timeframes.
Adjust the MA and momentum parameters if you want to align with a different market structure.
Configure stop, take profit, trailing and break-even distances in price steps that correspond to your instrument's tick size.
Start the strategy; it will subscribe to all necessary candle feeds automatically and begin evaluating signals once indicators are formed.
Notes & Differences from the Original EA
Weighted moving averages replicate the LWMA calculations used in MQL without manually iterating over prices.
Break-even and trailing logic is applied on completed candles, matching the bar-by-bar approach of the EA while leveraging StockSharp protection helpers.
Money-based trailing and percentage-based exits are not ported because StockSharp operates on instrument units; equivalent behaviour can be achieved by calibrating the step-based parameters.
The strategy assumes one position at a time, which is the common usage scenario of the source EA even though it exposed a Max_Trades input.
Tune the thresholds and timeframes to match the asset you are trading. Higher-volatility instruments often require larger step distances or wider momentum thresholds to avoid premature exits.
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class EngulfingMomentumStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private decimal? _prevFast, _prevSlow;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public EngulfingMomentumStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame()).SetDisplay("Candle Type", "Timeframe", "General");
_fastPeriod = Param(nameof(FastPeriod), 7).SetGreaterThanZero().SetDisplay("Fast EMA", "Fast EMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 20).SetGreaterThanZero().SetDisplay("Slow EMA", "Slow EMA period", "Indicators");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevFast = null;
_prevSlow = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevFast = null; _prevSlow = null;
var fast = new ExponentialMovingAverage { Length = FastPeriod };
var slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(fast, slow, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null) { DrawCandles(area, subscription); DrawIndicator(area, fast); DrawIndicator(area, slow); DrawOwnTrades(area); }
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
{
if (candle.State != CandleStates.Finished) return;
if (!IsFormedAndOnlineAndAllowTrading()) { _prevFast = fast; _prevSlow = slow; return; }
if (_prevFast == null || _prevSlow == null) { _prevFast = fast; _prevSlow = slow; return; }
var prevAbove = _prevFast.Value > _prevSlow.Value;
var currAbove = fast > slow;
_prevFast = fast; _prevSlow = slow;
if (!prevAbove && currAbove && Position <= 0) { if (Position < 0) BuyMarket(); BuyMarket(); }
else if (prevAbove && !currAbove && Position >= 0) { if (Position > 0) SellMarket(); SellMarket(); }
}
}
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
from StockSharp.Algo.Strategies import Strategy
class engulfing_momentum_strategy(Strategy):
def __init__(self):
super(engulfing_momentum_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._fast_period = self.Param("FastPeriod", 7) \
.SetDisplay("Fast EMA", "Fast EMA period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 20) \
.SetDisplay("Slow EMA", "Slow EMA period", "Indicators")
self._prev_fast = None
self._prev_slow = None
@property
def CandleType(self):
return self._candle_type.Value
@property
def FastPeriod(self):
return self._fast_period.Value
@property
def SlowPeriod(self):
return self._slow_period.Value
def OnReseted(self):
super(engulfing_momentum_strategy, self).OnReseted()
self._prev_fast = None
self._prev_slow = None
def OnStarted2(self, time):
super(engulfing_momentum_strategy, self).OnStarted2(time)
self._prev_fast = None
self._prev_slow = None
fast = ExponentialMovingAverage()
fast.Length = self.FastPeriod
slow = ExponentialMovingAverage()
slow.Length = self.SlowPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(fast, slow, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast)
self.DrawIndicator(area, slow)
self.DrawOwnTrades(area)
def _on_process(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fv = float(fast_value)
sv = float(slow_value)
if self._prev_fast is None or self._prev_slow is None:
self._prev_fast = fv
self._prev_slow = sv
return
prev_above = self._prev_fast > self._prev_slow
curr_above = fv > sv
self._prev_fast = fv
self._prev_slow = sv
if not prev_above and curr_above and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif prev_above and not curr_above and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
def CreateClone(self):
return engulfing_momentum_strategy()