Открыть на GitHub

Стратегия Order Expert (1916)

Стратегия открывает позицию, когда цена достигает заранее заданного уровня. Поведение аналогично исходному MQL-советнику, управляющему ордерами через линии на графике.

Принцип работы

  • Подписка на свечи выбранного таймфрейма.
  • При пересечении ценой уровней BuyLevel или SellLevel открывается соответствующая рыночная позиция.
  • Значения стоп-лосса и тейк-профита рассчитываются от цены входа с использованием StopLossPip и TakeProfitPip.
  • Опциональный трейлинг-стоп сдвигает стоп по мере движения цены в прибыльную сторону.

Параметры

  • TakeProfitPip – расстояние до тейк-профита в пунктах.
  • StopLossPip – расстояние до стоп-лосса в пунктах.
  • EnableTrailingStop – включение или отключение трейлинг-стопа.
  • CandleType – тип свечей для расчётов.
  • BuyLevel – уровень, при достижении которого открывается покупка (0 отключает).
  • SellLevel – уровень, при достижении которого открывается продажа (0 отключает).

Примечания

  • Стратегия использует высокоуровневый API и обрабатывает только завершённые свечи.
  • При запуске активируется система защиты для предотвращения случайных крупных позиций.
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;
	}
}