Ver en GitHub

Simple EA MA plus MACD

Overview

This strategy ports the MetaTrader 5 expert adviser Simple EA MA plus MACD to the StockSharp high-level API. It searches for a breakout from a “signal bar” that satisfies two conditions: a shifted moving average sits below/above the bar’s highs, and the MACD histogram has just crossed the zero line. When the next candle closes beyond the signal bar’s extremum the strategy enters in the breakout direction.

The implementation keeps the original behaviour of the EA:

  1. Signal detection – on every finished candle the strategy inspects the previous bar. A configurable moving average (default LWMA) calculated on the chosen applied price must be lower than both the previous and current candle highs for longs (higher for shorts). Simultaneously the MACD main line must have crossed zero between the two preceding bars.
  2. Signal confirmation – once a signal bar is stored, the strategy waits for the next completed candle. A close above the stored high triggers a long breakout; a close below the stored low triggers a short breakout. If price invalidates the signal by closing back inside the signal bar, the setup is cancelled.
  3. Position management – newly opened trades inherit stop-loss, take-profit and trailing-stop distances expressed in pips. Protective levels are converted to absolute prices using the security PriceStep. Instruments with three or five decimals receive the classic forex adjustment (step × 10) to mimic MetaTrader pip definitions.

Risk management

  • Stop-loss / take-profit – optional distances defined in pips are evaluated on every candle close. When the market prints beyond the corresponding level the strategy exits with a market order.
  • Trailing stop – when profit exceeds TrailingStopPips + TrailingStepPips, a trailing reference is moved behind the best price reached. If price pulls back to the trailing level the position is closed. A trailing step of zero re-arms the stop on every new extreme.
  • Flatten on reversal – if an opposite breakout appears while an opposite position is open, the strategy sends a single market order large enough to close the existing exposure and open the new trade in one shot.

Implementation notes

  • The moving average supports the same smoothing methods and applied price options as MetaTrader (Simple, Exponential, Smoothed, LinearWeighted and Close/Open/High/Low/Median/Typical/Weighted prices).
  • MaShift reproduces the horizontal offset of the MetaTrader indicator by reading values from earlier bars before evaluating the breakout rules.
  • MACD uses the built-in MovingAverageConvergenceDivergence indicator. Only the histogram (difference between fast and slow EMAs) is required; the signal line period is retained to stay faithful to the EA settings.
  • Candle subscriptions and indicator processing rely exclusively on the StockSharp high-level API. No manual tick handling or indicator buffers are used.

Parameters

Parameter Default Description
Volume 1 Order size for every breakout entry.
TakeProfitPips 50 Profit target distance expressed in pips (converted to absolute price using the security price step). Set to 0 to disable.
StopLossPips 50 Protective stop distance in pips. Set to 0 to disable.
TrailingStopPips 5 Trailing stop distance in pips that is locked in once price advances sufficiently.
TrailingStepPips 5 Minimum additional progress (in pips) before the trailing stop is advanced again.
MaPeriod 100 Length of the moving average used to validate the signal bar.
MaShift 0 Horizontal shift applied to the moving average, emulating the MetaTrader ma_shift parameter.
MaMethod LinearWeighted Moving average smoothing method (Simple, Exponential, Smoothed, LinearWeighted).
MaAppliedPrice Weighted Price source fed into the moving average (Close, Open, High, Low, Median, Typical, Weighted).
MacdFastPeriod 12 Fast EMA period used in the MACD calculation.
MacdSlowPeriod 26 Slow EMA period used in the MACD calculation.
MacdSignalPeriod 9 Signal line smoothing period retained for parity with the original EA.
MacdAppliedPrice Weighted Applied price used when feeding values into MACD.
CandleType 1 hour time frame Primary candle series analysed for signals and trade management.

Usage tips

  • Tune the pip-based protections to match the tick size of the selected instrument; incorrect PriceStep values on the connector side will distort pip conversions.
  • For highly volatile markets consider increasing TrailingStepPips to reduce premature exits, or decrease it to tighten trailing behaviour.
  • Because trades are executed on closed candles, the breakout must persist until the bar completes; enabling smaller timeframes increases trading frequency but may introduce more noise.
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;

/// <summary>
/// Simple EA MA plus MACD strategy using EMA crossover.
/// Buys when fast EMA crosses above slow EMA, sells on reverse.
/// </summary>
public class SimpleEaMaPlusMacdStrategy : 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 SimpleEaMaPlusMacdStrategy()
	{
		_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;
	}
}