Ver no GitHub

EMA 6/12 Crossover Strategy

This strategy replicates the MetaTrader expert advisor that trades the crossover between a fast EMA(6) and a slow EMA(12). It subscribes to hourly candles by default, calculates both moving averages, and waits for a confirmed crossover at the close of a candle before acting.

Trading Logic

  • Entry:
    • A bullish signal appears when EMA(6) crosses above EMA(12). The strategy opens a long position if there is no active position.
    • A bearish signal appears when EMA(6) crosses below EMA(12). The strategy opens a short position if there is no active position.
  • Exit:
    • When UseCloseSignals is enabled (default behaviour), the strategy closes the current position once an opposite crossover is detected. It waits for the next crossover before opening a new trade, mirroring the original expert advisor.
    • Optional take profit and trailing stop protections are managed via StockSharp's built-in StartProtection helper.
  • Position sizing:
    • Orders use the OrderVolume parameter (default 1 lot). Volumes are aligned to the security settings before sending orders.

Risk Management

  • Trailing stop: Converts the original "points" setting into price steps. When greater than zero, the stop automatically trails in the direction of the trade once the position becomes profitable.
  • Take profit: Expressed in price steps. Set to zero to disable.
  • The strategy never averages down or pyramids. Only one position per symbol is allowed.

Parameters

Parameter Description
CandleType Time frame used to build candles and EMAs. Defaults to 1 hour.
OrderVolume Trade size in lots.
ShortEmaLength Period for the fast EMA (default 6).
LongEmaLength Period for the slow EMA (default 12).
UseCloseSignals Close the current position on an opposite crossover (default: enabled).
TrailingStopSteps Trailing distance in price steps. Zero disables trailing.
TakeProfitSteps Take profit distance in price steps. Zero disables it.

Notes

  • Signals are processed only on finished candles to avoid intrabar noise.
  • The previous EMA values are reset whenever the position returns to zero, ensuring clean detection for the next crossover.
  • All code comments are written in English, and indentation uses tabs in accordance with project guidelines.
using System;
using System.Collections.Generic;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// EMA 6/12 crossover strategy.
/// Buys when EMA(6) crosses above EMA(12).
/// Sells when EMA(6) crosses below EMA(12).
/// </summary>
public class Ema612Strategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevFast;
	private decimal _prevSlow;
	private bool _hasPrev;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public Ema612Strategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 6)
			.SetDisplay("Fast EMA", "Fast EMA period", "Indicators");

		_slowPeriod = Param(nameof(SlowPeriod), 12)
			.SetDisplay("Slow EMA", "Slow EMA period", "Indicators");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
	protected override void OnReseted() { base.OnReseted(); _prevFast = 0m; _prevSlow = 0m; _hasPrev = false; }

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_hasPrev = false;

		var fast = new ExponentialMovingAverage { Length = FastPeriod };
		var slow = new ExponentialMovingAverage { Length = SlowPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(fast, slow, ProcessCandle)
			.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!_hasPrev)
		{
			_prevFast = fast;
			_prevSlow = slow;
			_hasPrev = true;
			return;
		}

		if (_prevFast <= _prevSlow && fast > slow && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		else if (_prevFast >= _prevSlow && fast < slow && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}

		_prevFast = fast;
		_prevSlow = slow;
	}
}