Ver en GitHub

Estrategia de DeMarker ganando posición Volumen 2

Esta estrategia reproduce el MetaTrader 5 asesor experto "DeMarker ganando posición volumen 2" utilizando el nivel alto de StockSharp API. Analiza una serie de velas configurables con el oscilador DeMarker y reacciona cuando el valor entra en zonas extremas. La implementación mantiene el estilo original de administración de dinero con tamaño de lote fijo, inversión opcional de señales, manejo integrado de stop-loss/take-profit y un filtro de sesión comercial opcional.

Comportamiento experto original

  • Plataforma: MetaTrader 5.
  • Indicador: oscilador DeMarker clásico (DEM), período predeterminado 14.
  • Entradas: abrir posiciones largas cuando DeMarker cae por debajo de un umbral inferior, abrir posiciones cortas cuando sube por encima de un umbral superior.
  • Controles de riesgo: stop-loss/take-profit fijo expresado en puntos, trailing stop opcional con escalón, ventana de tiempo opcional.
  • Gestión de posición: asegúrese de realizar solo una operación por barra y cierre el lado opuesto antes de cambiar de dirección.

La conversión StockSharp sigue los mismos principios. Las órdenes de protección se implementan con StartProtection, por lo que el límite de pérdidas, la toma de ganancias y el seguimiento se administran automáticamente una vez que se abre una posición.

Lógica comercial

  1. Suscríbase al tipo de vela configurado (CandleType, velas de 5 minutos por defecto) y calcule el valor de DeMarker con el período elegido (DeMarkerPeriod).
  2. Cuando se cierra una vela, evalúe el oscilador:
    • Si ReverseSignals es falso (predeterminado):
      • Configuración largaDeMarker <= LowerLevel.
      • Configuración breveDeMarker >= UpperLevel.
    • Si ReverseSignals es verdadero, las reglas largas/cortas se intercambian.
  3. Opere únicamente dentro de la ventana de sesión opcional definida por SessionStart/SessionEnd cuando UseTimeFilter esté habilitado. Se admiten sesiones nocturnas.
  4. Ejecute como máximo una nueva entrada por vela. Antes de abrir una nueva posición, la estrategia cierra cualquier posición opuesta para reflejar la lógica MT5.
  5. Los volúmenes están fijados por el parámetro TradeVolume. Si la estrategia ya está parcialmente en la dirección deseada, se completa hasta el volumen solicitado.

Gestión de riesgos

  • StopLossPoints y TakeProfitPoints (en pasos de precio) se asignan a las distancias de parada y toma de ganancias basadas en puntos del experto.
  • Al habilitar EnableTrailing se cambia la distancia de parada a TrailingStopPoints y se activa el motor de seguimiento integrado usando TrailingStepPoints como paso de ajuste.
  • StartProtection está configurado con useMarketOrders = true para que las órdenes de protección se ejecuten inmediatamente, asemejándose al comportamiento de cierre comercial de MT5.

Parámetros

Parámetro Descripción
DeMarkerPeriod Período promedio del indicador DeMarker.
UpperLevel / LowerLevel Umbrales de sobrecompra/sobreventa que activan posiciones cortas/largas.
ReverseSignals Intercambie condiciones largas y cortas.
StopLossPoints Distancia de parada de protección inicial medida en pasos de precio.
TakeProfitPoints Distancia de obtención de beneficios medida en incrementos de precios.
EnableTrailing Habilita el bloque trailing stop.
TrailingStopPoints Distancia del trailing stop una vez que el trailing está activo.
TrailingStepPoints Movimiento mínimo favorable antes de que se avance el trailing stop.
UseTimeFilter Restringe el comercio a la ventana SessionStartSessionEnd.
SessionStart / SessionEnd Límites de sesión inclusivos/exclusivos (admite la integración).
TradeVolume Cantidad a enviar con cada orden de mercado.
CandleType Serie de velas a analizar (por defecto 5 minutos).

Notas de implementación

  • El experto en MT5 incluyó un umbral de "activación final". La protección de seguimiento estándar de StockSharp no expone el mismo parámetro, por lo tanto, el seguimiento se activa inmediatamente cuando EnableTrailing es verdadero.
  • La infraestructura de StockSharp maneja el manejo de errores para tamaños de lote no válidos, niveles de congelación y lógica de actualización de oferta/demanda, por lo que se omiten de la conversión.
  • El registro se realiza a través de la clase base Strategy (llame a LogInfo/LogError si se requiere seguimiento adicional).
namespace StockSharp.Samples.Strategies;

using System;

using Ecng.Common;

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

/// <summary>
/// Strategy converted from "DeMarker gaining position volume 2" expert advisor.
/// Uses DeMarker oscillator thresholds for entry signals.
/// </summary>
public class DeMarkerGainingPositionVolume2Strategy : Strategy
{
	private readonly StrategyParam<int> _deMarkerPeriod;
	private readonly StrategyParam<decimal> _upperLevel;
	private readonly StrategyParam<decimal> _lowerLevel;
	private readonly StrategyParam<DataType> _candleType;

	private RelativeStrengthIndex _rsi;
	private decimal? _prevOscillator;

	/// <summary>
	/// DeMarker averaging period.
	/// </summary>
	public int DeMarkerPeriod
	{
		get => _deMarkerPeriod.Value;
		set => _deMarkerPeriod.Value = value;
	}

	/// <summary>
	/// Upper DeMarker threshold.
	/// </summary>
	public decimal UpperLevel
	{
		get => _upperLevel.Value;
		set => _upperLevel.Value = value;
	}

	/// <summary>
	/// Lower DeMarker threshold.
	/// </summary>
	public decimal LowerLevel
	{
		get => _lowerLevel.Value;
		set => _lowerLevel.Value = value;
	}

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

	public DeMarkerGainingPositionVolume2Strategy()
	{
		_deMarkerPeriod = Param(nameof(DeMarkerPeriod), 14)
			.SetDisplay("DeMarker Period", "Number of bars used by the oscillator.", "Indicator");

		_upperLevel = Param(nameof(UpperLevel), 0.7m)
			.SetDisplay("Upper Level", "Overbought threshold.", "Indicator");

		_lowerLevel = Param(nameof(LowerLevel), 0.3m)
			.SetDisplay("Lower Level", "Oversold threshold.", "Indicator");

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe for calculations.", "Data");
	}

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

		_prevOscillator = null;
		_rsi = new RelativeStrengthIndex { Length = DeMarkerPeriod };

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

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

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

		if (!_rsi.IsFormed)
		{
			_prevOscillator = rsiValue / 100m;
			return;
		}

		var oscillatorValue = rsiValue / 100m;

		var volume = Volume;
		if (volume <= 0)
			volume = 1;

		// Cross below lower level => oversold => buy
		if (_prevOscillator is decimal prev)
		{
			var crossBelow = prev >= LowerLevel && oscillatorValue < LowerLevel;
			var crossAbove = prev <= UpperLevel && oscillatorValue > UpperLevel;

			if (crossBelow)
			{
				if (Position <= 0)
					BuyMarket(Position < 0 ? Math.Abs(Position) + volume : volume);
			}
			else if (crossAbove)
			{
				if (Position >= 0)
					SellMarket(Position > 0 ? Math.Abs(Position) + volume : volume);
			}
		}

		_prevOscillator = oscillatorValue;
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		_rsi = null;
		_prevOscillator = null;

		base.OnReseted();
	}
}