Ver en GitHub

Estrategia STO M5xM15xM30

Descripción general

Esta estrategia es una conversión fiel de C# del MetaTrader 4 asesor experto "STO_m5xm15xm30". Utiliza tres osciladores estocásticos calculados en los marcos temporales M5, M15 y M30 para identificar cambios de impulso sincronizados. La implementación StockSharp mantiene la estructura de entrada/salida original, reemplaza la gestión manual de pedidos con el nivel alto API y expone cada clave constante como un StrategyParam configurable.

Lógica de trading

  1. Confirmación de múltiples períodos
    • El estocástico primario (M5 predeterminado) debe mostrar un cruce alcista (%K cruza por encima de %D).
    • Los valores estocásticos medio (M15 predeterminado) y lento (M30 predeterminado) ya deben ser alcistas (%K por encima de %D).
    • Una configuración bajista requiere condiciones reflejadas (%K debajo de %D).
  2. Filtro de cambio
    • El estocástico primario también verifica el estado ShiftBars velas antes. Una señal de compra requiere que el %K histórico esté por debajo de %D, lo que garantiza un nuevo cruce. Las señales de venta requieren lo contrario.
  3. Filtro de impulso de precios
    • El último cierre debe ser mayor (para compras) o menor (para ventas) que el cierre de la vela anterior. Esto refleja la regla Close[0] > Close[1] del script MT4.
  4. Reglas de entrada
    • Si no hay ninguna posición abierta y se cumplen los criterios alcistas, la estrategia abre una orden de mercado larga con el TradeVolume configurado.
    • Si existe una posición corta cuando llega una señal alcista, primero se aplana y luego se abre una posición larga. Lo contrario ocurre con las señales bajistas.
  5. Reglas de salida
    • Un estocástico M5 dedicado con período ExitKPeriod verifica la vela anterior (shift = 1). Una posición larga se cierra cuando %K cae por debajo de %D; un corto se cierra cuando %K sube por encima de %D.
    • Después de que se activa una salida, la estrategia omite el reingreso inmediato en la misma vela, replicando el comportamiento del bucle de órdenes MT4.

Indicadores y Suscripciones de Datos

  • Velas primarias: período de tiempo predeterminado de 5 minutos (CandleType).
  • Velas de confirmación intermedia: período de tiempo predeterminado de 15 minutos (MiddleCandleType).
  • Velas de confirmación lentas: período de tiempo predeterminado de 30 minutos (SlowCandleType).
  • Osciladores Stochastic: todos usan suavizado %K = 3 y suavizado %D = 3, coincidiendo con los parámetros originales.

Parámetros

Nombre Predeterminado Descripción
CandleType velas de 5 minutos Horario de trabajo para entradas y salidas.
MiddleCandleType velas de 15 minutos Plazo de confirmación #1.
SlowCandleType velas de 30 minutos Plazo de confirmación #2.
FastKPeriod 5 Período %K para el estocástico primario.
MiddleKPeriod 5 Período %K para el estocástico medio.
SlowKPeriod 5 Período %K para el estocástico lento.
ExitKPeriod 5 Periodo %K para el estocástico de salida que opera en la barra anterior.
ShiftBars 3 Número de barras entre el cruce de referencia y la barra actual.
TakeProfitPoints 30 Distancia protectora de toma de ganancias (puntos).
StopLossPoints 10 Distancia de protección stop-loss (puntos).
TradeVolume 0.1 Volumen de pedidos utilizado para nuevas entradas.

Todos los parámetros se exponen a través de StrategyParam<T>, lo que los hace disponibles para su optimización dentro de StockSharp Designer.

Gestión del riesgo

StartProtection() traduce las entradas MT4 TP y SL en StockSharp órdenes de protección. Ambos se pueden desactivar poniendo el parámetro correspondiente a cero.

Notas de implementación

  • Los valores de los indicadores se obtienen exclusivamente a través de SubscribeCandles(...).BindEx(...), cumpliendo con las directrices de alto nivel API y evitando la recopilación manual de indicadores.
  • El asistente StochasticShiftBuffer imita el argumento MT4 shift sin llamar a GetValue, manteniendo solo el historial de barras necesario.
  • El procesamiento de entrada ocurre una vez por vela completada. La evaluación de salida ocurre antes de la lógica de entrada, coincidiendo con el orden de procesamiento del EA original.
  • Los comentarios en línea explican cada paso del procesamiento y aclaran cómo la lógica MQL se asigna al código StockSharp.

Uso

  1. Agregue la estrategia a un esquema StockSharp o proyecto de diseñador.
  2. Configure el símbolo deseado y asegúrese de que los datos históricos de las velas M5, M15 y M30 estén disponibles.
  3. Ajuste los parámetros para adaptarlos al mercado objetivo o al escenario de optimización.
  4. Iniciar la estrategia; Los niveles protectores de stop-loss/take-profit se registran automáticamente para cada posición.
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// STO M5xM15xM30: RSI momentum with dual EMA confirmation.
/// Uses RSI crossover of 50 level with fast/slow EMA alignment.
/// </summary>
public class StoM5xM15xM30Strategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _rsiLength;
	private readonly StrategyParam<int> _fastLength;
	private readonly StrategyParam<int> _slowLength;
	private readonly StrategyParam<int> _atrLength;

	private decimal _prevRsi;
	private decimal _entryPrice;

	public StoM5xM15xM30Strategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe.", "General");

		_rsiLength = Param(nameof(RsiLength), 14)
			.SetDisplay("RSI Length", "RSI period.", "Indicators");

		_fastLength = Param(nameof(FastLength), 10)
			.SetDisplay("Fast EMA", "Fast EMA period.", "Indicators");

		_slowLength = Param(nameof(SlowLength), 30)
			.SetDisplay("Slow EMA", "Slow EMA period.", "Indicators");

		_atrLength = Param(nameof(AtrLength), 14)
			.SetDisplay("ATR Length", "ATR period for stops.", "Indicators");
	}

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public int RsiLength
	{
		get => _rsiLength.Value;
		set => _rsiLength.Value = value;
	}

	public int FastLength
	{
		get => _fastLength.Value;
		set => _fastLength.Value = value;
	}

	public int SlowLength
	{
		get => _slowLength.Value;
		set => _slowLength.Value = value;
	}

	public int AtrLength
	{
		get => _atrLength.Value;
		set => _atrLength.Value = value;
	}

	/// <inheritdoc />
	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_prevRsi = 0;
		_entryPrice = 0;
	}

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

		_prevRsi = 0;
		_entryPrice = 0;

		var rsi = new RelativeStrengthIndex { Length = RsiLength };
		var fast = new ExponentialMovingAverage { Length = FastLength };
		var slow = new ExponentialMovingAverage { Length = SlowLength };
		var atr = new AverageTrueRange { Length = AtrLength };

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

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

	private void ProcessCandle(ICandleMessage candle, decimal rsiVal, decimal fastVal, decimal slowVal, decimal atrVal)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (_prevRsi == 0 || atrVal <= 0)
		{
			_prevRsi = rsiVal;
			return;
		}

		var close = candle.ClosePrice;

		// Exit management
		if (Position > 0)
		{
			if (close <= _entryPrice - atrVal * 2m || close >= _entryPrice + atrVal * 3m)
			{
				SellMarket();
				_entryPrice = 0;
			}
			else if (rsiVal < 40 && fastVal < slowVal)
			{
				SellMarket();
				_entryPrice = 0;
			}
		}
		else if (Position < 0)
		{
			if (close >= _entryPrice + atrVal * 2m || close <= _entryPrice - atrVal * 3m)
			{
				BuyMarket();
				_entryPrice = 0;
			}
			else if (rsiVal > 60 && fastVal > slowVal)
			{
				BuyMarket();
				_entryPrice = 0;
			}
		}

		// Entry: RSI crosses 50 with EMA alignment
		if (Position == 0)
		{
			if (_prevRsi <= 50 && rsiVal > 50 && fastVal > slowVal)
			{
				_entryPrice = close;
				BuyMarket();
			}
			else if (_prevRsi >= 50 && rsiVal < 50 && fastVal < slowVal)
			{
				_entryPrice = close;
				SellMarket();
			}
		}

		_prevRsi = rsiVal;
	}
}