Expert MACD EURUSD 1 Hour Strategy
Overview
This strategy is a C# translation of the MetaTrader 5 expert advisor Expert MACD EURUSD 1 Hour. It trades on one-hour candles using the MACD indicator with short, long, and signal periods of 5 / 15 / 3. The strategy looks for a strong momentum shift where the MACD main line crosses above or below the zero level while the signal line confirms the move. A trailing stop is used to protect open positions, and trades are closed when the MACD slope turns against the current position.
Parameters
FastLength – fast EMA period for MACD (default: 5).
SlowLength – slow EMA period for MACD (default: 15).
SignalLength – signal line period for MACD (default: 3).
TrailingPoints – trailing stop distance in price points (default: 25).
CandleType – timeframe of candles (default: 1 hour).
- Strategy
Volume property controls the order size.
Trading Logic
Long Entry
- Signal line values:
mac8 > mac7 > mac6 and mac6 < mac5 (rising signal line).
- Main line values:
mac4 > mac3 < mac2 < mac1 (main line rising after a dip).
mac2 < -0.00020, mac4 < 0 and mac1 > 0.00020 – main line crosses above zero.
- If all conditions hold and no long position is open, buy at market.
Short Entry
- Signal line values:
mac8 < mac7 < mac6 and mac6 > mac5 (falling signal line).
- Main line values:
mac4 < mac3 > mac2 > mac1 (main line falling after a peak).
mac2 > 0.00020, mac4 > 0 and mac1 < -0.00035 – main line crosses below zero.
- If all conditions hold and no short position is open, sell at market.
Exit Rules
- Close a long when the current main value is below the previous one.
- Close a short when the current main value is above the previous one.
- Trailing stop updates on every candle and exits if price crosses the stop level.
Notes
This example demonstrates using the high-level StockSharp API with indicator binding and manual trailing stop management. It is intended for educational purposes and does not include money management beyond the fixed Volume parameter.
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// MACD based strategy converted from MetaTrader 5 Expert Advisor.
/// Uses MACD indicator with custom pattern checks on recent values and optional trailing stop.
/// </summary>
public class ExpertMacdEurusd1HourStrategy : Strategy
{
private readonly StrategyParam<int> _fastLength;
private readonly StrategyParam<int> _slowLength;
private readonly StrategyParam<int> _signalLength;
private readonly StrategyParam<decimal> _trailingPoints;
private readonly StrategyParam<DataType> _candleType;
private ExponentialMovingAverage _fastEma;
private ExponentialMovingAverage _slowEma;
private ExponentialMovingAverage _signalEma;
private decimal _main0, _main1;
private decimal _signal0, _signal1;
private int _counter;
/// <summary>
/// Fast EMA length for MACD.
/// </summary>
public int FastLength { get => _fastLength.Value; set => _fastLength.Value = value; }
/// <summary>
/// Slow EMA length for MACD.
/// </summary>
public int SlowLength { get => _slowLength.Value; set => _slowLength.Value = value; }
/// <summary>
/// Signal line length for MACD.
/// </summary>
public int SignalLength { get => _signalLength.Value; set => _signalLength.Value = value; }
/// <summary>
/// Trailing stop distance in price points.
/// </summary>
public decimal TrailingPoints { get => _trailingPoints.Value; set => _trailingPoints.Value = value; }
/// <summary>
/// Candle type for analysis.
/// </summary>
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
/// <summary>
/// Initialize strategy with default parameters.
/// </summary>
public ExpertMacdEurusd1HourStrategy()
{
_fastLength = Param(nameof(FastLength), 12)
.SetDisplay("Fast Length", "Fast EMA length for MACD", "Parameters")
;
_slowLength = Param(nameof(SlowLength), 26)
.SetDisplay("Slow Length", "Slow EMA length for MACD", "Parameters")
;
_signalLength = Param(nameof(SignalLength), 9)
.SetDisplay("Signal Length", "Signal length for MACD", "Parameters")
;
_trailingPoints = Param(nameof(TrailingPoints), 25m)
.SetDisplay("Trailing Points", "Trailing stop distance in points", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Working timeframe", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_main0 = _main1 = 0m;
_signal0 = _signal1 = 0m;
_counter = 0;
_fastEma = null;
_slowEma = null;
_signalEma = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fastEma = new ExponentialMovingAverage { Length = FastLength };
_slowEma = new ExponentialMovingAverage { Length = SlowLength };
_signalEma = new ExponentialMovingAverage { Length = SignalLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
StartProtection(
new Unit(2000m, UnitTypes.Absolute),
new Unit(1000m, UnitTypes.Absolute));
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var fast = _fastEma.Process(candle.ClosePrice, candle.CloseTime, true).ToDecimal();
var slow = _slowEma.Process(candle.ClosePrice, candle.CloseTime, true).ToDecimal();
if (!_fastEma.IsFormed || !_slowEma.IsFormed)
return;
var main = fast - slow;
var signal = _signalEma.Process(main, candle.CloseTime, true).ToDecimal();
if (!_signalEma.IsFormed)
return;
// shift stored values
_main1 = _main0;
_main0 = main;
_signal1 = _signal0;
_signal0 = signal;
if (_counter < 3)
{
_counter++;
return;
}
var buySignal = _main1 <= _signal1 && _main0 > _signal0 && _main0 < 0m;
var sellSignal = _main1 >= _signal1 && _main0 < _signal0 && _main0 > 0m;
if (buySignal && Position <= 0)
BuyMarket();
else if (sellSignal && Position >= 0)
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, Unit, UnitTypes
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class expert_macd_eurusd1_hour_strategy(Strategy):
def __init__(self):
super(expert_macd_eurusd1_hour_strategy, self).__init__()
self._fast_length = self.Param("FastLength", 12)
self._slow_length = self.Param("SlowLength", 26)
self._signal_length = self.Param("SignalLength", 9)
self._trailing_points = self.Param("TrailingPoints", 25.0)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1)))
self._fast_ema = None
self._slow_ema = None
self._signal_ema = None
self._main0 = 0.0
self._main1 = 0.0
self._signal0 = 0.0
self._signal1 = 0.0
self._counter = 0
@property
def FastLength(self):
return self._fast_length.Value
@FastLength.setter
def FastLength(self, value):
self._fast_length.Value = value
@property
def SlowLength(self):
return self._slow_length.Value
@SlowLength.setter
def SlowLength(self, value):
self._slow_length.Value = value
@property
def SignalLength(self):
return self._signal_length.Value
@SignalLength.setter
def SignalLength(self, value):
self._signal_length.Value = value
@property
def TrailingPoints(self):
return self._trailing_points.Value
@TrailingPoints.setter
def TrailingPoints(self, value):
self._trailing_points.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
super(expert_macd_eurusd1_hour_strategy, self).OnStarted2(time)
self._main0 = 0.0
self._main1 = 0.0
self._signal0 = 0.0
self._signal1 = 0.0
self._counter = 0
self._fast_ema = ExponentialMovingAverage()
self._fast_ema.Length = self.FastLength
self._slow_ema = ExponentialMovingAverage()
self._slow_ema.Length = self.SlowLength
self._signal_ema = ExponentialMovingAverage()
self._signal_ema.Length = self.SignalLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self.ProcessCandle).Start()
self.StartProtection(
Unit(2000.0, UnitTypes.Absolute),
Unit(1000.0, UnitTypes.Absolute))
def ProcessCandle(self, candle):
if candle.State != CandleStates.Finished:
return
t = candle.OpenTime
fast_result = process_float(self._fast_ema, candle.ClosePrice, t, True)
slow_result = process_float(self._slow_ema, candle.ClosePrice, t, True)
if not self._fast_ema.IsFormed or not self._slow_ema.IsFormed:
return
fast_val = float(fast_result)
slow_val = float(slow_result)
main = fast_val - slow_val
signal_result = process_float(self._signal_ema, main, t, True)
if not self._signal_ema.IsFormed:
return
signal = float(signal_result)
self._main1 = self._main0
self._main0 = main
self._signal1 = self._signal0
self._signal0 = signal
if self._counter < 3:
self._counter += 1
return
buy_signal = self._main1 <= self._signal1 and self._main0 > self._signal0 and self._main0 < 0.0
sell_signal = self._main1 >= self._signal1 and self._main0 < self._signal0 and self._main0 > 0.0
if buy_signal and self.Position <= 0:
self.BuyMarket()
elif sell_signal and self.Position >= 0:
self.SellMarket()
def OnReseted(self):
super(expert_macd_eurusd1_hour_strategy, self).OnReseted()
self._main0 = 0.0
self._main1 = 0.0
self._signal0 = 0.0
self._signal1 = 0.0
self._counter = 0
self._fast_ema = None
self._slow_ema = None
self._signal_ema = None
def CreateClone(self):
return expert_macd_eurusd1_hour_strategy()