Ver no GitHub

Estratégia de cruzamento Cronex DeMarker

Visão geral

A estratégia cruzada Cronex DeMarker reproduz o indicador MetaTrader Cronex DeMarker e o transforma em um sistema de negociação automatizado. O indicador original representa graficamente o oscilador DeMarker junto com duas médias móveis lineares ponderadas (LWMAs). A estratégia reflete essa configuração, avalia cruzamentos de alta e baixa entre as linhas suavizadas do oscilador e os converte em ordens de mercado. Isso permite que a lógica de negociação reaja imediatamente quando o momentum muda de pressão negativa para pressão positiva (e vice-versa) de acordo com o indicador.

Construção do indicador

  1. Oscilador DeMarker – Mede a relação entre a vela atual e a vela anterior:
    • Se o máximo atual for superior ao máximo anterior, a pressão positiva é igual à diferença dos máximos; caso contrário, é zero.
    • Se o mínimo atual for inferior ao mínimo anterior, a pressão negativa será igual à distância entre os mínimos; caso contrário, é zero.
    • As somas das pressões positivas e negativas em DeMarkerPeriod barras formam o valor do oscilador deMax / (deMax + deMin).
  2. LWMA rápido – Uma média móvel ponderada linear com período FastMaPeriod é aplicada aos valores brutos do DeMarker para enfatizar as últimas alterações do oscilador.
  3. LWMA lento – Outra média móvel ponderada linear com período SlowMaPeriod suaviza o mesmo fluxo DeMarker para construir uma linha de confirmação mais lenta.

A estratégia alimenta cada vela finalizada para esta pilha de indicadores, correspondendo exatamente aos cálculos do buffer do arquivo MQ4 original.

Lógica de negociação

  1. Aguarde até que o oscilador DeMarker e ambos os LWMAs estejam totalmente formados.
  2. Após cada vela concluída, calcule o novo valor do DeMarker e atualize ambas as médias móveis.
  3. Detecte cruzamentos entre as séries LWMA rápida e lenta:
    • Cruzamento de alta – O LWMA rápido se move de baixo para cima do LWMA lento. A estratégia fecha qualquer exposição curta e abre uma posição longa no mercado.
    • Cruzamento de baixa – O LWMA rápido se move de cima para baixo do LWMA lento. A estratégia fecha qualquer exposição longa e abre uma posição curta no mercado.
  4. As ordens são ignoradas enquanto a estratégia ainda não está formada, enquanto está offline ou quando a negociação está desativada.

As posições são invertidas imediatamente em sinais opostos. A exposição existente é encerrada adicionando a quantidade necessária à nova ordem de mercado.

Parâmetros

Parâmetro Descrição Padrão
DeMarkerPeriod Número de velas usadas para construir o oscilador DeMarker. 25
FastMaPeriod Período da média móvel ponderada linear rápida que reage a novos valores do oscilador. 14
SlowMaPeriod Período da média móvel ponderada linear lenta que confirma a direção. 25
CandleType Série de velas processadas pela estratégia (período ou outro DataType). 1 Hour período de tempo

Detalhes de implementação

  • Usa o SubscribeCandles API de alto nível. Os indicadores são atualizados somente quando uma vela atinge o estado Finished para evitar a repintura da barra intermediária.
  • A estratégia depende dos indicadores DeMarker e WeightedMovingAverage integrados de StockSharp para replicar fielmente os buffers MQ4.
  • Uma área do gráfico é criada automaticamente, traçando as velas de preço junto com o oscilador e ambas as médias móveis para confirmação visual.
  • StartProtection() é invocado durante a inicialização para que a proteção de posição seja acionada exatamente uma vez, conforme exigido pelas diretrizes do projeto.

Uso

  1. Anexe a estratégia ao título desejado e atribua o tipo de vela preferido (por exemplo, velas com período de 1 hora).
  2. Configure o DeMarker e os períodos de média móvel para corresponder ao indicador original ou ajuste-os para otimização.
  3. Execute a estratégia. Ele começará a ser negociado assim que os indicadores estiverem totalmente formados e a negociação for permitida.
  4. Monitore o gráfico plotado para ver o oscilador DeMarker e os sinais de cruzamento LWMA conduzindo as entradas.
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>
/// Strategy that replicates the Cronex DeMarker indicator setup and trades crossovers of its smoothed values.
/// </summary>
public class CronexDeMarkerCrossoverStrategy : Strategy
{
	private readonly StrategyParam<int> _deMarkerPeriod;
	private readonly StrategyParam<int> _fastMaPeriod;
	private readonly StrategyParam<int> _slowMaPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private DeMarker _deMarker;
	private WeightedMovingAverage _fastMa;
	private WeightedMovingAverage _slowMa;

	private decimal? _previousFast;
	private decimal? _previousSlow;

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

	/// <summary>
	/// Fast linear weighted moving average period.
	/// </summary>
	public int FastMaPeriod
	{
		get => _fastMaPeriod.Value;
		set => _fastMaPeriod.Value = value;
	}

	/// <summary>
	/// Slow linear weighted moving average period.
	/// </summary>
	public int SlowMaPeriod
	{
		get => _slowMaPeriod.Value;
		set => _slowMaPeriod.Value = value;
	}

	/// <summary>
	/// Candle type processed by the strategy.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Initializes a new instance of the <see cref="CronexDeMarkerCrossoverStrategy"/>.
	/// </summary>
	public CronexDeMarkerCrossoverStrategy()
	{
		_deMarkerPeriod = Param(nameof(DeMarkerPeriod), 25)
			.SetRange(2, 150)
			.SetDisplay("DeMarker Period", "Length of the DeMarker oscillator", "Indicators")
			;

		_fastMaPeriod = Param(nameof(FastMaPeriod), 14)
			.SetRange(2, 100)
			.SetDisplay("Fast LWMA Period", "Length of the fast linear weighted moving average", "Indicators")
			;

		_slowMaPeriod = Param(nameof(SlowMaPeriod), 25)
			.SetRange(2, 150)
			.SetDisplay("Slow LWMA Period", "Length of the slow linear weighted moving average", "Indicators")
			;

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Time frame of processed candles", "General");
	}

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

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

		_deMarker = null;
		_fastMa = null;
		_slowMa = null;
		_previousFast = null;
		_previousSlow = null;
	}

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

		// Instantiate indicators matching the original MetaTrader logic.
		_deMarker = new DeMarker
		{
			Length = DeMarkerPeriod
		};

		_fastMa = new WeightedMovingAverage
		{
			Length = FastMaPeriod
		};

		_slowMa = new WeightedMovingAverage
		{
			Length = SlowMaPeriod
		};

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

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

		StartProtection(null, null);
	}

	private void ProcessCandle(ICandleMessage candle)
	{
		// Only act on completed candles to avoid repainting effects.
		if (candle.State != CandleStates.Finished)
			return;

		if (_deMarker is null || _fastMa is null || _slowMa is null)
			return;

		// Update the DeMarker oscillator with the full candle data.
		var deMarkerResult = _deMarker.Process(new CandleIndicatorValue(_deMarker, candle));
		if (deMarkerResult.IsEmpty)
		{
			return;
		}
		var deMarkerValue = deMarkerResult.GetValue<decimal>();

		// Smooth the oscillator with linear weighted moving averages.
		var fastResult = _fastMa.Process(new DecimalIndicatorValue(_fastMa, deMarkerValue, candle.OpenTime) { IsFinal = true });
		if (fastResult.IsEmpty) return;
		var fastValue = fastResult.GetValue<decimal>();
		var slowResult = _slowMa.Process(new DecimalIndicatorValue(_slowMa, deMarkerValue, candle.OpenTime) { IsFinal = true });
		if (slowResult.IsEmpty) return;
		var slowValue = slowResult.GetValue<decimal>();

		// Ensure all indicators accumulated enough samples.
		if (!_deMarker.IsFormed || !_fastMa.IsFormed || !_slowMa.IsFormed)
		{
			_previousFast = fastValue;
			_previousSlow = slowValue;
			return;
		}

		var previousFast = _previousFast;
		var previousSlow = _previousSlow;

		_previousFast = fastValue;
		_previousSlow = slowValue;

		if (!previousFast.HasValue || !previousSlow.HasValue)
			return;

		// Check readiness and trading permissions before sending orders.
		// indicators formed check removed

		var crossUp = previousFast.Value <= previousSlow.Value && fastValue > slowValue;
		var crossDown = previousFast.Value >= previousSlow.Value && fastValue < slowValue;

		if (crossUp)
		{
			// Close short exposure and establish a long position.
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		else if (crossDown)
		{
			// Close long exposure and establish a short position.
			if (Position > 0)
				SellMarket();
			SellMarket();
		}
	}
}