The MACD Power strategy is a multi-timeframe momentum system converted from the original MetaTrader expert advisor. The logic combines a pair of linear weighted moving averages (LWMA) calculated on the primary timeframe, two MACD variations, a higher timeframe momentum filter, and a monthly MACD bias. The strategy attempts to participate in impulsive moves once momentum and higher timeframe trend conditions align.
Core logic
Primary moving averages – A fast and a slow LWMA of the candle typical price (((High + Low + Close) / 3)). The strategy requires the fast average to trade below the slow average before any signal is considered, mirroring the original code that waits for pullbacks inside a dominant bearish slope before entering in the direction of the monthly bias.
Dual MACD confirmation – Two MACD indicators with parameters (12, 26, 1) and (6, 13, 1) must both be above zero for long trades or below zero for short trades. These values reproduce the MQL expert's MacdMAIN1 and MacdMAIN2 conditions that measure short-term acceleration.
Momentum filter – Momentum (length 14) is computed on a higher timeframe derived from the primary candle size (e.g., 15‑minute base -> 1‑hour momentum). The absolute distance from 100 is monitored over the three latest momentum readings; at least one of them must exceed the configured threshold to confirm that price is moving decisively.
Monthly MACD bias – A monthly (12, 26, 9) MACD (identical to MacdMAIN0/MacdSIGNAL0 in the EA) must have its main line above the signal line for long trades and below the signal line for shorts. This guards against trading against the dominant macro trend.
Trade management
Entry sizing – The OrderVolume parameter defines the base order size. When a reversal of position is required, the engine automatically adds the magnitude of the opposite position so that the net volume is flipped in a single market order.
Take profit / stop loss – Absolute distances are expressed in instrument points and converted to price using Security.PriceStep (with a safe fallback to 1).
Trailing stop – Once price moves in favour by TrailingActivationPoints, the stop tracks the highest (long) or lowest (short) price with an offset defined by TrailingOffsetPoints.
Break-even – When price reaches BreakEvenTriggerPoints, a synthetic break-even stop is armed at Entry ± BreakEvenOffsetPoints. If price retreats back to that level the position is closed.
Trade limit – MaxTrades limits the number of position initiations per run; once the threshold is reached, no new entries are issued.
Parameters
Name
Description
Default
CandleType
Primary timeframe for signal generation.
15-minute candles
FastMaLength
Length of the fast LWMA (typical price).
6
SlowMaLength
Length of the slow LWMA (typical price).
85
MomentumLength
Momentum lookback on the higher timeframe.
14
MomentumBuyThreshold
Minimum absolute distance from 100 required for bullish momentum.
0.3
MomentumSellThreshold
Minimum absolute distance from 100 required for bearish momentum.
0.3
TakeProfitPoints
Take-profit distance in instrument points.
50
StopLossPoints
Stop-loss distance in instrument points.
20
TrailingActivationPoints
Profit (points) required before trailing activates.
40
TrailingOffsetPoints
Gap (points) between trailing stop and extreme price.
40
BreakEvenTriggerPoints
Profit (points) that enables break-even protection.
30
BreakEvenOffsetPoints
Offset (points) applied when moving the stop to break-even.
30
MaxTrades
Maximum number of trades allowed per session.
10
OrderVolume
Base order volume.
1
Differences versus the MQL expert
The strategy uses the StockSharp high-level API (SubscribeCandles + Bind/BindEx) instead of direct tick polling. Indicator values are processed only after candles are finished.
Money-based trailing and equity stop blocks from the original code are not ported because account-level money management is normally handled by the StockSharp risk framework. Instead, point-based trailing and break-even remain and can be configured to emulate the EA's behaviour.
Alerts, notifications, and manual order modification helpers from MQL are omitted; the StockSharp engine handles orders directly via market calls.
Usage notes
Choose the primary timeframe by setting CandleType. Higher timeframe momentum and the monthly MACD are derived automatically according to the mapping implemented in GetMomentumCandleType().
Align TakeProfitPoints, StopLossPoints, and the trailing/break-even parameters with the instrument's tick size. The defaults reflect the EA's 5-digit Forex settings but can be adapted for other markets.
Monitor the MaxTrades counter when running automated backtests; set it to a large number if the original EA's martingale-like stacking behaviour is desired.
For visual analysis, enable charting in the GUI – the implementation draws candles and the two LWMA curves by default.
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class MacdPowerStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private ExponentialMovingAverage _fast;
private ExponentialMovingAverage _slow;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
private int _cooldown;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }
public MacdPowerStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 12).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
protected override void OnReseted()
{
base.OnReseted();
_fast = null; _slow = null;
_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fast = new ExponentialMovingAverage { Length = FastPeriod };
_slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
subscription.Bind(_fast, _slow, ProcessCandle);
subscription.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished) return;
if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }
var close = candle.ClosePrice;
var step = Security?.PriceStep ?? 1m;
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }
_prevFast = fastValue; _prevSlow = slowValue;
}
}
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 macd_power_strategy(Strategy):
"""
MACD Power: EMA crossover with manual SL/TP via price steps.
"""
def __init__(self):
super(macd_power_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 12).SetDisplay("Fast Period", "Fast EMA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 50).SetDisplay("Slow Period", "Slow EMA period", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200).SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 400).SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Candles", "General")
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(macd_power_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(macd_power_strategy, self).OnStarted2(time)
self._fast_ind = ExponentialMovingAverage()
self._fast_ind.Length = self._fast_period.Value
self._slow_ind = ExponentialMovingAverage()
self._slow_ind.Length = self._slow_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._fast_ind, self._slow_ind, self._process_candle).Start()
def _process_candle(self, candle, fast_val, slow_val):
if candle.State != CandleStates.Finished:
return
fast = float(fast_val)
slow = float(slow_val)
if not self._fast_ind.IsFormed or not self._slow_ind.IsFormed:
self._prev_fast = fast
self._prev_slow = slow
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast
self._prev_slow = slow
return
close = float(candle.ClosePrice)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
if self.Position > 0 and self._entry_price > 0:
if self._stop_loss_points.Value > 0 and close <= self._entry_price - self._stop_loss_points.Value * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast
self._prev_slow = slow
return
if self._take_profit_points.Value > 0 and close >= self._entry_price + self._take_profit_points.Value * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast
self._prev_slow = slow
return
elif self.Position < 0 and self._entry_price > 0:
if self._stop_loss_points.Value > 0 and close >= self._entry_price + self._stop_loss_points.Value * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast
self._prev_slow = slow
return
if self._take_profit_points.Value > 0 and close <= self._entry_price - self._take_profit_points.Value * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast
self._prev_slow = slow
return
if self._prev_fast <= self._prev_slow and fast > slow and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 100
elif self._prev_fast >= self._prev_slow and fast < slow and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 100
self._prev_fast = fast
self._prev_slow = slow
def CreateClone(self):
return macd_power_strategy()