View on GitHub

Cronex DeMarker

The Cronex DeMarker Strategy reproduces the classic Cronex expert advisor that combines the DeMarker oscillator with a double smoothing stack. First, the DeMarker values are smoothed by a fast simple moving average, then the result is smoothed once more by a slower average. The distance and relative order of these two lines provide reversal-style entry signals.

The original MQL5 implementation allows trade direction toggles and works on higher timeframes. This StockSharp port keeps the same philosophy: it reacts when the fast line crosses through the slow one and immediately closes any opposite position. Because the system is contrarian, a cross below the slow line opens a long position, while a cross above opens a short. Both directions can be disabled independently through parameters, making the strategy flexible for different portfolio allocations.

How it works

  1. Request candles for the selected timeframe (4H by default).
  2. Calculate the DeMarker oscillator and smooth it with a fast SMA (default 14 bars).
  3. Apply a second SMA (default 25 bars) on top of the fast line to obtain the signal line.
  4. When the fast line was above the slow line on the previous candle and now drops below it, the strategy buys (contrarian reversal). Any existing short position is flattened.
  5. When the fast line was below the slow line on the previous candle and now climbs above it, the strategy sells and closes any open long.
  6. Position size is defined by the Volume property; reversals use the absolute position to flip immediately.

This logic allows the expert to capture short-term exhaustion moves after strong momentum pushes, making it a mean-reversion tool that prefers ranging or choppy markets.

Default parameters

Parameter Default Description
DeMarkerPeriod 25 Number of bars used by the DeMarker oscillator.
FastPeriod 14 Length of the first smoothing SMA applied to DeMarker values.
SlowPeriod 25 Length of the signal SMA applied to the fast line.
CandleType 4 hour Candle series used for indicator calculations.
EnableLongEntry true Allow contrarian long entries when the fast line crosses below the slow line.
EnableShortEntry true Allow short entries when the fast line crosses above the slow line.
EnableLongExit true Close existing long positions when bearish conditions appear.
EnableShortExit true Close existing short positions when bullish conditions appear.

Filters & tags

  • Category: Mean Reversion, Oscillator based
  • Direction: Long & Short (configurable)
  • Indicators: DeMarker, Simple Moving Average (double smoothing)
  • Stops: None (fully signal driven)
  • Timeframe: Swing trading (H4 by default, adjustable)
  • Complexity: Intermediate due to sequential indicator chain
  • Risk Profile: Medium — contrarian entries can face extended trends
  • Automation: Fully automated via high-level StockSharp API

Usage notes

  • The strategy only processes finished candles to avoid repainting issues.
  • Reversal orders reuse the absolute position size, guaranteeing immediate flattening before entering the new direction.
  • Chart output draws the two smoothed lines and trade markers, helping with discretionary validation.
  • For portfolios that only allow one direction, disable the unwanted entries and exits through the provided parameters.
  • Consider adding external risk controls (stop-loss, trailing exit) when deploying on volatile assets.
using System;
using System.Collections.Generic;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

public class CronexDeMarkerStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private ExponentialMovingAverage _fast;
	private ExponentialMovingAverage _slow;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;
	private int _cooldown;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
	public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }

	public CronexDeMarkerStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
		_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
		_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
		_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
	}

	protected override void OnReseted()
	{
		base.OnReseted();
		_fast = null; _slow = null;
		_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_fast = new ExponentialMovingAverage { Length = FastPeriod };
		_slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
		subscription.Bind(_fast, _slow, ProcessCandle);
		subscription.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
		if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }

		var close = candle.ClosePrice;
		var step = Security?.PriceStep ?? 1m;

		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}
		else if (Position < 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}

		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }

		_prevFast = fastValue; _prevSlow = slowValue;
	}
}