View on GitHub

Cronex AC

The Cronex AC strategy recreates the classic Cronex Acceleration/Deceleration (AC) expert advisor using the StockSharp high-level API. It smooths the Accelerator Oscillator with two consecutive moving averages and reacts when the fast line crosses the slow line. Bullish crossovers open long positions and close shorts, while bearish crossovers open shorts and close longs.

Trading logic

  1. Build Accelerator Oscillator (AO-AC) values from the selected candle series.
  2. Smooth the AC with the chosen moving-average type twice: the first smoothing produces the "fast" line and the second smoothing produces the "signal" line.
  3. Evaluate the two lines on the bar defined by the SignalBar parameter. The strategy also looks one bar further back to confirm a crossover.
  4. When the fast line crosses above the signal line, the strategy closes existing short positions (if enabled) and opens a new long position (if enabled).
  5. When the fast line crosses below the signal line, the strategy closes existing long positions (if enabled) and opens a new short position (if enabled).
  6. Position size equals the configured Volume plus the absolute value of the current position, allowing reversals in a single market order.

The logic mirrors the MQL5 expert by only acting on fully completed candles and by separating permissions for entries and exits in both directions.

Parameters

Name Type Default Description
SmoothingType CronexMovingAverageType Simple Moving-average algorithm applied to the Accelerator Oscillator. Options: Simple, Exponential, Smoothed, Weighted.
FastPeriod int 14 Lookback of the first smoothing (fast line).
SlowPeriod int 25 Lookback of the second smoothing (signal line).
SignalBar int 1 Number of finished bars to look back when reading the signal. A value of 1 replicates the default Cronex behavior.
CandleType DataType TimeFrame(8h) Candle series used for calculations.
EnableLongEntry bool true Allow opening long positions after a bullish crossover.
EnableShortEntry bool true Allow opening short positions after a bearish crossover.
EnableLongExit bool true Allow closing long positions when the fast line drops below the slow line.
EnableShortExit bool true Allow closing short positions when the fast line rises above the slow line.
Volume decimal strategy default Order size used for entries. The strategy automatically adds the absolute value of the open position to reverse in a single trade.

Charting

When a chart area is available the strategy plots:

  • source candles for the selected timeframe,
  • Accelerator Oscillator values,
  • fast and signal moving averages,
  • the strategy's own trades for visual validation.

Notes

  • All calculations rely on completed candles (CandleStates.Finished) to avoid repainting.
  • The smoothing buffers keep just enough historical values to evaluate the requested SignalBar shift, matching the original MQL expert.
  • Money-management features from the MQL version (stop-loss, take-profit, deviation) are intentionally omitted so that position management can be handled externally through StockSharp's risk controls.
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 CronexAcStrategy : 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 CronexAcStrategy()
	{
		_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;
	}
}