Ver no GitHub

Exp Cronex Chaikin Strategy

This strategy ports the MetaTrader expert advisor Exp_CronexChaikin.mq5 to the StockSharp high-level API. The original robot rebuilds the Chaikin oscillator from accumulation/distribution values, smooths it twice with Cronex "XMA" filters, and trades crossovers between the fast and slow lines. The StockSharp version reproduces the same logic while exposing each stage as configurable parameters.

Trading logic

  1. Subscribe to the configured candle series (CandleType).
  2. Recalculate the accumulation/distribution (AD) line for every finished candle using the selected VolumeSource (tick or real volume).
  3. Apply the Chaikin oscillator by smoothing the AD line with two moving averages (ChaikinFastPeriod, ChaikinSlowPeriod, ChaikinMethod) and taking their difference.
  4. Smooth the resulting oscillator twice using the Cronex filters controlled by SmoothingMethod, FastPeriod, SlowPeriod, and Phase. These two smoothed values correspond to the "fast" and "signal" lines in the original indicator.
  5. Look back SignalBar completed candles and compare both Cronex lines on that bar and on the previous one.
  6. When the fast line is above the slow line, the strategy optionally closes short positions and, if BuyOpenEnabled is true, opens a long position if a fresh upward cross was detected on the lookback bar.
  7. When the fast line is below the slow line, the opposite actions are executed for short trades, controlled by SellOpenEnabled and BuyCloseEnabled.
  8. Whenever a new position is opened, stop-loss and take-profit orders (expressed in points) are recalculated with StopLoss and TakeProfit.

Only a single net position is maintained. If the signal direction changes, the strategy combines the volume required to close the current position with the new trade size to mimic MetaTrader's netting behaviour.

Indicators and smoothing options

  • Chaikin oscillator: Built by applying the selected ChaikinMethod moving average type to the accumulation/distribution line. Available options include simple, exponential, smoothed, and linear-weighted averages.
  • Cronex smoothers: The SmoothingMethod parameter exposes the Cronex XMA family (SMA, EMA, SMMA, LWMA, Jurik JJMA/JurX, Parabolic MA, T3, VIDYA, AMA). The Phase parameter influences Jurik-based filters exactly like in the MQL implementation.

Parameters

Parameter Description
CandleType Data type of the candles used to compute the indicator. Default is a four-hour timeframe.
ChaikinMethod Moving-average method used inside the Chaikin oscillator.
ChaikinFastPeriod / ChaikinSlowPeriod Fast and slow periods applied to the accumulation/distribution line.
SmoothingMethod Cronex smoothing algorithm applied to the Chaikin oscillator values.
FastPeriod / SlowPeriod Lengths of the fast and slow Cronex lines.
Phase Phase parameter for Jurik-based smoothers (range -100 to +100).
VolumeSource Selects tick or real volume when calculating the accumulation/distribution line.
SignalBar Number of completed bars back that must contain the crossover signal.
BuyOpenEnabled / SellOpenEnabled Enable or disable opening of long or short trades.
BuyCloseEnabled / SellCloseEnabled Allow closing the opposite position when an inverse signal appears.
TakeProfit / StopLoss Profit target and protective stop distances in instrument points applied after each entry.
Volume Standard StockSharp position size (acts as the lot size in the original expert).

Differences from the MQL version

  • Money-management and slippage routines from TradeAlgorithms.mqh are replaced by the built-in Volume, SetStopLoss, and SetTakeProfit helpers.
  • The StockSharp implementation recomputes the AD line on finished candles only, ensuring deterministic behaviour for testing and live trading.
  • Cronex smoothing options rely on StockSharp indicators: Jurik filters are backed by JurikMovingAverage (with phase control), while VIDYA and ParMA use exponential approximations consistent with other Cronex conversions.
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 ExpCronexChaikinStrategy : 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 ExpCronexChaikinStrategy()
	{
		_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;
	}
}