Ver en GitHub

Estrategia de Exp Cronex Chaikin

Esta estrategia porta el experto asesor MetaTrader Exp_CronexChaikin.mq5 a la API de alto nivel de StockSharp. El robot original reconstruye el oscilador Chaikin a partir de valores de acumulación/distribución, lo suaviza dos veces con filtros Cronex "XMA" y opera las cruces entre las líneas rápida y lenta. La versión de StockSharp reproduce la misma lógica mientras expone cada etapa como parámetros configurables.

Lógica de trading

  1. Suscribirse a la serie de velas configurada (CandleType).
  2. Recalcular la línea de acumulación/distribución (AD) para cada vela finalizada usando el VolumeSource seleccionado (volumen tick o real).
  3. Aplicar el oscilador Chaikin suavizando la línea AD con dos medias móviles (ChaikinFastPeriod, ChaikinSlowPeriod, ChaikinMethod) y tomando su diferencia.
  4. Suavizar el oscilador resultante dos veces usando los filtros Cronex controlados por SmoothingMethod, FastPeriod, SlowPeriod y Phase. Estos dos valores suavizados corresponden a las líneas "rápida" y "señal" en el indicador original.
  5. Mirar atrás SignalBar velas completadas y comparar ambas líneas Cronex en esa barra y en la anterior.
  6. Cuando la línea rápida está por encima de la lenta, la estrategia opcionalmente cierra posiciones cortas y, si BuyOpenEnabled es verdadero, abre una posición larga si se detectó un cruce ascendente fresco en la barra de retroceso.
  7. Cuando la línea rápida está por debajo de la lenta, se ejecutan las acciones opuestas para operaciones cortas, controladas por SellOpenEnabled y BuyCloseEnabled.
  8. Cada vez que se abre una nueva posición, las órdenes de stop-loss y take-profit (expresadas en puntos) se recalculan con StopLoss y TakeProfit.

Solo se mantiene una única posición neta. Si la dirección de la señal cambia, la estrategia combina el volumen necesario para cerrar la posición actual con el nuevo tamaño de operación para imitar el comportamiento de netting de MetaTrader.

Indicadores y opciones de suavizado

  • Oscilador Chaikin: Construido aplicando el tipo de media móvil ChaikinMethod seleccionado a la línea de acumulación/distribución. Las opciones disponibles incluyen medias simples, exponenciales, suavizadas y linealmente ponderadas.
  • Suavizadores Cronex: El parámetro SmoothingMethod expone la familia Cronex XMA (SMA, EMA, SMMA, LWMA, Jurik JJMA/JurX, Parabolic MA, T3, VIDYA, AMA). El parámetro Phase influye en los filtros basados en Jurik exactamente como en la implementación MQL.

Parámetros

Parámetro Descripción
CandleType Tipo de datos de las velas usadas para calcular el indicador. Por defecto es un marco temporal de cuatro horas.
ChaikinMethod Método de media móvil usado dentro del oscilador Chaikin.
ChaikinFastPeriod / ChaikinSlowPeriod Períodos rápido y lento aplicados a la línea de acumulación/distribución.
SmoothingMethod Algoritmo de suavizado Cronex aplicado a los valores del oscilador Chaikin.
FastPeriod / SlowPeriod Longitudes de las líneas Cronex rápida y lenta.
Phase Parámetro de fase para suavizadores basados en Jurik (rango -100 a +100).
VolumeSource Selecciona volumen tick o real al calcular la línea de acumulación/distribución.
SignalBar Número de barras completadas hacia atrás que debe contener la señal de cruce.
BuyOpenEnabled / SellOpenEnabled Activar o desactivar la apertura de operaciones largas o cortas.
BuyCloseEnabled / SellCloseEnabled Permitir cerrar la posición opuesta cuando aparece una señal inversa.
TakeProfit / StopLoss Distancias de objetivo de beneficio y stop protector en puntos del instrumento aplicadas tras cada entrada.
Volume Tamaño de posición estándar de StockSharp (actúa como tamaño de lote en el experto original).

Diferencias respecto a la versión MQL

  • Las rutinas de gestión monetaria y deslizamiento de TradeAlgorithms.mqh son reemplazadas por los asistentes integrados Volume, SetStopLoss y SetTakeProfit.
  • La implementación de StockSharp recalcula la línea AD solo en velas finalizadas, asegurando un comportamiento determinista para pruebas y trading en vivo.
  • Las opciones de suavizado Cronex se basan en indicadores de StockSharp: los filtros Jurik están respaldados por JurikMovingAverage (con control de fase), mientras que VIDYA y ParMA usan aproximaciones exponenciales consistentes con otras conversiones Cronex.
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;
	}
}