Ver no GitHub

Cronex DeMarker Crossover Strategy

Overview

The Cronex DeMarker Crossover Strategy reproduces the MetaTrader indicator Cronex DeMarker and transforms it into an automated trading system. The original indicator plots the DeMarker oscillator together with two linear weighted moving averages (LWMAs). The strategy mirrors that setup, evaluates bullish and bearish crossovers between the smoothed oscillator lines, and converts them into market orders. This allows the trading logic to react immediately when momentum shifts from downside to upside pressure (and vice versa) according to the indicator.

Indicator construction

  1. DeMarker oscillator – Measures the relationship between the current candle and the previous candle:
    • If the current high is higher than the previous high, the positive pressure equals the difference of the highs; otherwise it is zero.
    • If the current low is lower than the previous low, the negative pressure equals the distance between the lows; otherwise it is zero.
    • The sums of positive and negative pressure over DeMarkerPeriod bars form the oscillator value deMax / (deMax + deMin).
  2. Fast LWMA – A linear weighted moving average with period FastMaPeriod is applied to the raw DeMarker values in order to emphasise the latest oscillator changes.
  3. Slow LWMA – Another linear weighted moving average with period SlowMaPeriod smooths the same DeMarker stream to build a slower confirmation line.

The strategy feeds every finished candle to this indicator stack, exactly matching the buffer calculations from the original MQ4 file.

Trading logic

  1. Wait until the DeMarker oscillator and both LWMAs are fully formed.
  2. After each completed candle, compute the fresh DeMarker value and update both moving averages.
  3. Detect crossovers between the fast and slow LWMA series:
    • Bullish crossover – The fast LWMA moves from below to above the slow LWMA. The strategy closes any short exposure and opens a long market position.
    • Bearish crossover – The fast LWMA moves from above to below the slow LWMA. The strategy closes any long exposure and opens a short market position.
  4. Orders are skipped while the strategy is not yet formed, while it is offline, or when trading is disabled.

Positions are reversed immediately on opposite signals. Existing exposure is closed by adding the required quantity to the new market order.

Parameters

Parameter Description Default
DeMarkerPeriod Number of candles used to build the DeMarker oscillator. 25
FastMaPeriod Period of the fast linear weighted moving average that reacts to new oscillator values. 14
SlowMaPeriod Period of the slow linear weighted moving average that confirms the direction. 25
CandleType Candle series processed by the strategy (time-frame or other DataType). 1 Hour time-frame

Implementation details

  • Uses the high-level SubscribeCandles API. Indicators are updated only when a candle reaches the Finished state to avoid mid-bar repainting.
  • The strategy relies on the built-in DeMarker and WeightedMovingAverage indicators from StockSharp to faithfully replicate the MQ4 buffers.
  • A chart area is created automatically, plotting the price candles together with the oscillator and both moving averages for visual confirmation.
  • StartProtection() is invoked during startup so that position protection is engaged exactly once, as required by the project guidelines.

Usage

  1. Attach the strategy to the desired security and assign the preferred candle type (for example, 1-hour time-frame candles).
  2. Configure the DeMarker and moving average periods to match the original indicator or tune them for optimisation.
  3. Run the strategy. It will start trading once the indicators are fully formed and trading is allowed.
  4. Monitor the plotted chart to see the DeMarker oscillator and LWMA crossover signals driving the entries.
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();
		}
	}
}