Auf GitHub ansehen

Order Expert Strategy (1916)

This strategy opens a market position when the instrument price reaches predefined levels. It mimics behaviour of the original MQL expert that managed orders via chart lines.

How it works

  • Subscribes to candles of a configurable timeframe.
  • When the closing price crosses the BuyLevel or SellLevel thresholds it opens a long or short market position.
  • Stop-loss and take-profit values are calculated from the entry price using StopLossPip and TakeProfitPip.
  • Optional trailing stop moves the stop-loss towards the current price as it moves in a favorable direction.

Parameters

  • TakeProfitPip – distance from entry price to take profit in pips.
  • StopLossPip – distance from entry price to stop loss in pips.
  • EnableTrailingStop – enable or disable trailing stop logic.
  • CandleType – candle type used for calculations.
  • BuyLevel – price level that triggers long entry (0 disables).
  • SellLevel – price level that triggers short entry (0 disables).

Notes

  • Strategy uses high-level API and processes only finished candles.
  • Protection subsystem is activated on start to avoid accidental large positions.
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>
/// Order expert strategy that uses EMA crossover to enter positions
/// and manages them with stop-loss, take-profit and trailing stop.
/// </summary>
public class OrderExpertStrategy : Strategy
{
	private readonly StrategyParam<decimal> _takeProfitPct;
	private readonly StrategyParam<decimal> _stopLossPct;
	private readonly StrategyParam<int> _fastEmaPeriod;
	private readonly StrategyParam<int> _slowEmaPeriod;
	private readonly StrategyParam<int> _cooldownBars;
	private readonly StrategyParam<DataType> _candleType;

	private ExponentialMovingAverage _slowEma;
	private decimal _prevFast;
	private decimal _prevSlow;
	private int _barsSinceTrade;
	private bool _isFirst = true;

	public decimal TakeProfitPct { get => _takeProfitPct.Value; set => _takeProfitPct.Value = value; }
	public decimal StopLossPct { get => _stopLossPct.Value; set => _stopLossPct.Value = value; }
	public int FastEmaPeriod { get => _fastEmaPeriod.Value; set => _fastEmaPeriod.Value = value; }
	public int SlowEmaPeriod { get => _slowEmaPeriod.Value; set => _slowEmaPeriod.Value = value; }
	public int CooldownBars { get => _cooldownBars.Value; set => _cooldownBars.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public OrderExpertStrategy()
	{
		_takeProfitPct = Param(nameof(TakeProfitPct), 3m)
			.SetDisplay("Take Profit %", "Take profit percentage", "Risk");

		_stopLossPct = Param(nameof(StopLossPct), 2m)
			.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk");

		_fastEmaPeriod = Param(nameof(FastEmaPeriod), 12)
			.SetGreaterThanZero()
			.SetDisplay("Fast EMA", "Fast EMA period", "Indicators");

		_slowEmaPeriod = Param(nameof(SlowEmaPeriod), 26)
			.SetGreaterThanZero()
			.SetDisplay("Slow EMA", "Slow EMA period", "Indicators");

		_cooldownBars = Param(nameof(CooldownBars), 6)
			.SetGreaterThanZero()
			.SetDisplay("Cooldown Bars", "Bars between trades", "Trading");

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles", "General");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevFast = 0;
		_prevSlow = 0;
		_barsSinceTrade = CooldownBars;
		_isFirst = true;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		var fastEma = new ExponentialMovingAverage { Length = FastEmaPeriod };
		_slowEma = new ExponentialMovingAverage { Length = SlowEmaPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(fastEma, ProcessCandle)
			.Start();

		StartProtection(
			stopLoss: new Unit(StopLossPct, UnitTypes.Percent),
			takeProfit: new Unit(TakeProfitPct, UnitTypes.Percent)
		);

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, fastEma);
			DrawIndicator(area, _slowEma);
			DrawOwnTrades(area);
		}
	}

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

		_barsSinceTrade++;

		var slowResult = _slowEma.Process(candle.ClosePrice, candle.OpenTime, true);
		if (!slowResult.IsFormed)
			return;

		var slow = slowResult.ToDecimal();

		if (_isFirst)
		{
			_prevFast = fast;
			_prevSlow = slow;
			_isFirst = false;
			return;
		}

		// EMA cross up -> buy
		if (_prevFast <= _prevSlow && fast > slow && Position == 0 && _barsSinceTrade >= CooldownBars)
		{
			BuyMarket();
			_barsSinceTrade = 0;
		}
		// EMA cross down -> sell
		else if (_prevFast >= _prevSlow && fast < slow && Position == 0 && _barsSinceTrade >= CooldownBars)
		{
			SellMarket();
			_barsSinceTrade = 0;
		}

		_prevFast = fast;
		_prevSlow = slow;
	}
}