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
Suscribirse a la serie de velas configurada (CandleType).
Recalcular la línea de acumulación/distribución (AD) para cada vela finalizada usando el VolumeSource seleccionado (volumen tick o real).
Aplicar el oscilador Chaikin suavizando la línea AD con dos medias móviles (ChaikinFastPeriod, ChaikinSlowPeriod, ChaikinMethod) y tomando su diferencia.
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.
Mirar atrás SignalBar velas completadas y comparar ambas líneas Cronex en esa barra y en la anterior.
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.
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.
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;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class exp_cronex_chaikin_strategy(Strategy):
def __init__(self):
super(exp_cronex_chaikin_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 14) \
.SetDisplay("Fast Period", "Fast MA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 50) \
.SetDisplay("Slow Period", "Slow MA period", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200) \
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 400) \
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def stop_loss_points(self):
return self._stop_loss_points.Value
@property
def take_profit_points(self):
return self._take_profit_points.Value
def OnReseted(self):
super(exp_cronex_chaikin_strategy, self).OnReseted()
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(exp_cronex_chaikin_strategy, self).OnStarted2(time)
self._fast = ExponentialMovingAverage()
self._fast.Length = self.fast_period
self._slow = ExponentialMovingAverage()
self._slow.Length = self.slow_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(5)))
subscription.Bind(self._fast, self._slow, self._process_candle)
subscription.Start()
def _process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_value)
slow_val = float(slow_value)
if not self._fast.IsFormed or not self._slow.IsFormed:
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
if self.Position > 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close <= self._entry_price - self.stop_loss_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close >= self._entry_price + self.take_profit_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close >= self._entry_price + self.stop_loss_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close <= self._entry_price - self.take_profit_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 100
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return exp_cronex_chaikin_strategy()