Ver en GitHub

Estrategia de Experto en Órdenes (1916)

Esta estrategia abre una posición de mercado cuando el precio del instrumento alcanza niveles predefinidos. Imita el comportamiento del experto MQL original que gestionaba órdenes mediante líneas en el gráfico.

Cómo funciona

  • Se suscribe a velas de un marco temporal configurable.
  • Cuando el precio de cierre cruza los umbrales BuyLevel o SellLevel, abre una posición larga o corta de mercado.
  • Los valores de stop-loss y take-profit se calculan desde el precio de entrada usando StopLossPip y TakeProfitPip.
  • Un trailing stop opcional mueve el stop-loss hacia el precio actual a medida que se mueve en una dirección favorable.

Parámetros

  • TakeProfitPip – distancia desde el precio de entrada al take profit en pips.
  • StopLossPip – distancia desde el precio de entrada al stop loss en pips.
  • EnableTrailingStop – habilitar o deshabilitar la lógica de trailing stop.
  • CandleType – tipo de vela utilizado para cálculos.
  • BuyLevel – nivel de precio que activa la entrada larga (0 deshabilita).
  • SellLevel – nivel de precio que activa la entrada corta (0 deshabilita).

Notas

  • La estrategia utiliza API de alto nivel y procesa solo velas terminadas.
  • El subsistema de protección se activa al inicio para evitar posiciones grandes accidentales.
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;
	}
}