Ver en GitHub

Estrategia OsMaSter V0

Descripción general

  • Convertida del experto de MetaTrader 5 "OsMaSter v0" (archivo MQL OsMaSter v0.mq5).
  • Usa un patrón de histograma MACD (OsMA) para identificar reversiones de momentum después de una breve consolidación.
  • Diseñada para operar en un único instrumento y marco temporal seleccionado por el usuario a través del parámetro CandleType.
  • Convierte automáticamente la configuración de riesgo basada en pips (stop-loss y take-profit) a offsets de precio absolutos usando el paso de precio del instrumento y la precisión decimal.

Parámetros

Nombre Predeterminado Descripción
FastPeriod 9 Longitud de la EMA rápida para el histograma MACD.
SlowPeriod 26 Longitud de la EMA lenta para el histograma MACD.
SignalPeriod 5 Longitud de la EMA de señal usada para suavizar el histograma.
StopLossPips 30 Distancia al stop de protección en pips. Poner en 0 para deshabilitar.
TakeProfitPips 50 Distancia al objetivo de ganancia en pips. Poner en 0 para deshabilitar.
TradeVolume 1 Volumen de orden (lotes) usado para entradas de mercado.
CandleType Velas de 15 minutos Marco temporal usado para los cálculos del indicador.

Lógica de señales

  1. La estrategia mantiene los últimos cuatro valores del histograma MACD (hist0 = actual, hist1 = anterior, ..., hist3 = hace tres velas).
  2. Entrada larga cuando hist3 > hist2, hist2 < hist1, y hist1 < hist0 — una secuencia ascendente después de un mínimo local.
  3. Entrada corta cuando hist3 < hist2, hist2 > hist1, y hist1 > hist0 — una secuencia descendente después de un máximo local.
  4. Solo una posición puede estar abierta a la vez. La estrategia ignora nuevas señales mientras una operación está activa.

Gestión de posición

  • Las órdenes se envían con BuyMarket() o SellMarket() usando el TradeVolume configurado.
  • StartProtection adjunta offsets de stop-loss y take-profit basados en las entradas de pips. El tamaño del pip sigue la convención forex (paso de precio × 10 para instrumentos de 3/5 decimales, de lo contrario el propio paso de precio).
  • No hay reglas de salida adicionales; las posiciones se gestionan exclusivamente por las órdenes de protección o por intervención manual.

Notas

  • Asegúrese de que el Security tiene valores correctos de PriceStep y Decimals para que la conversión de pips coincida con la especificación del broker.
  • Ajuste el marco temporal de velas y el volumen para que coincidan con el horizonte de trading del mercado objetivo.
  • Debido a que la estrategia espera la ejecución del stop o del objetivo, las señales consecutivas en la misma dirección se omiten mientras una posición permanece abierta.
using System;
using System.Linq;
using System.Collections.Generic;

using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// OsMA histogram pattern strategy converted from the "OsMaSter v0" MQL expert.
/// Enters on four-bar momentum reversals identified by the MACD histogram.
/// </summary>
public class OsMaSterV0Strategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _signalPeriod;
	private readonly StrategyParam<int> _stopLossPips;
	private readonly StrategyParam<int> _takeProfitPips;
	private readonly StrategyParam<decimal> _tradeVolume;
	private readonly StrategyParam<DataType> _candleType;

	private decimal? _histCurrent;
	private decimal? _histPrev1;
	private decimal? _histPrev2;
	private decimal? _histPrev3;

	/// <summary>
	/// Initializes a new instance of the strategy.
	/// </summary>
	public OsMaSterV0Strategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 9)
			.SetDisplay("Fast EMA", "Fast EMA period for MACD histogram", "Indicators")
			.SetGreaterThanZero();

		_slowPeriod = Param(nameof(SlowPeriod), 26)
			.SetDisplay("Slow EMA", "Slow EMA period for MACD histogram", "Indicators")
			.SetGreaterThanZero();

		_signalPeriod = Param(nameof(SignalPeriod), 5)
			.SetDisplay("Signal Smoothing", "Signal moving average period", "Indicators")
			.SetGreaterThanZero();

		_stopLossPips = Param(nameof(StopLossPips), 500)
			.SetDisplay("Stop Loss (pips)", "Stop loss distance in pips", "Risk");

		_takeProfitPips = Param(nameof(TakeProfitPips), 1000)
			.SetDisplay("Take Profit (pips)", "Take profit distance in pips", "Risk");

		_tradeVolume = Param(nameof(TradeVolume), 1m)
			.SetDisplay("Trade Volume", "Order volume in lots", "Trading")
			.SetGreaterThanZero();

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles for calculations", "General");

		Volume = TradeVolume;
	}

	/// <summary>
	/// Fast EMA period used in MACD histogram calculation.
	/// </summary>
	public int FastPeriod
	{
		get => _fastPeriod.Value;
		set => _fastPeriod.Value = value;
	}

	/// <summary>
	/// Slow EMA period used in MACD histogram calculation.
	/// </summary>
	public int SlowPeriod
	{
		get => _slowPeriod.Value;
		set => _slowPeriod.Value = value;
	}

	/// <summary>
	/// Signal moving average period.
	/// </summary>
	public int SignalPeriod
	{
		get => _signalPeriod.Value;
		set => _signalPeriod.Value = value;
	}

	/// <summary>
	/// Stop loss size expressed in pips.
	/// </summary>
	public int StopLossPips
	{
		get => _stopLossPips.Value;
		set => _stopLossPips.Value = value;
	}

	/// <summary>
	/// Take profit size expressed in pips.
	/// </summary>
	public int TakeProfitPips
	{
		get => _takeProfitPips.Value;
		set => _takeProfitPips.Value = value;
	}

	/// <summary>
	/// Trading volume used for market orders.
	/// </summary>
	public decimal TradeVolume
	{
		get => _tradeVolume.Value;
		set
		{
			_tradeVolume.Value = value;
			Volume = value;
		}
	}

	/// <summary>
	/// Candle type used for indicator calculations.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

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

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

		_histCurrent = null;
		_histPrev1 = null;
		_histPrev2 = null;
		_histPrev3 = null;

		Volume = TradeVolume;
	}

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

		Volume = TradeVolume;

		var macd = new MovingAverageConvergenceDivergenceHistogram
		{
			Macd =
			{
				ShortMa = { Length = FastPeriod },
				LongMa = { Length = SlowPeriod }
			},
			SignalMa = { Length = SignalPeriod }
		};

		var subscription = SubscribeCandles(CandleType);
		subscription
			.BindEx(macd, ProcessCandle)
			.Start();

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

		var step = Security?.PriceStep ?? 1m;
		var decimals = Security?.Decimals ?? 0;
		var pipSize = (decimals == 3 || decimals == 5) ? step * 10m : step;

		// Convert pip-based risk controls into absolute price offsets.
		StartProtection(
			takeProfit: TakeProfitPips > 0 ? new Unit(TakeProfitPips * pipSize, UnitTypes.Absolute) : null,
			stopLoss: StopLossPips > 0 ? new Unit(StopLossPips * pipSize, UnitTypes.Absolute) : null);
	}

	private void ProcessCandle(ICandleMessage candle, IIndicatorValue macdValue)
	{
		// Ignore incomplete candles because signals rely on closed data.
		if (candle.State != CandleStates.Finished)
			return;

		// Ensure the strategy is synchronized with the market and permitted to trade.
		if (!macdValue.IsFormed)
			return;

		var macdTyped = (MovingAverageConvergenceDivergenceHistogramValue)macdValue;
		if (macdTyped.Macd is not decimal histogram)
			return;

		// Shift stored histogram values to maintain the last four observations.
		_histPrev3 = _histPrev2;
		_histPrev2 = _histPrev1;
		_histPrev1 = _histCurrent;
		_histCurrent = histogram;

		if (_histCurrent is not decimal hist0 ||
			_histPrev1 is not decimal hist1 ||
			_histPrev2 is not decimal hist2 ||
			_histPrev3 is not decimal hist3)
		{
			return;
		}

		// Detect the specific four-bar rising pattern used for long entries.
		var bullishPattern = hist3 > hist2 && hist2 < hist1 && hist1 < hist0;

		// Detect the mirrored falling pattern used for short entries.
		var bearishPattern = hist3 < hist2 && hist2 > hist1 && hist1 > hist0;

		// The original expert opens a position only when no trades are active.
		if (Position != 0)
			return;

		if (bullishPattern)
		{
			// Enter long when the histogram forms a higher-low sequence.
			BuyMarket();
		}
		else if (bearishPattern)
		{
			// Enter short when the histogram forms a lower-high sequence.
			SellMarket();
		}
	}
}