Estrategia convertida del experto MetaTrader Exp_AverageChangeCandle. Recrea la lógica original dentro de StockSharp suavizando ratios de velas relativos a una media móvil de referencia dinámica y reaccionando a las transiciones de color alcista/bajista.
Idea principal
Calcular una media móvil de referencia (MaMethod1, Length1) sobre el precio aplicado seleccionado.
Expresar el precio de apertura y cierre de la vela actual como ratios respecto a la referencia y elevarlos a la potencia Power.
Suavizar los valores transformados de apertura y cierre con una segunda media móvil (MaMethod2, Length2).
Clasificar el color de la vela: alcista cuando el cierre suavizado > apertura suavizada, bajista cuando el cierre suavizado < apertura suavizada.
Generar señales de trading cuando el color cambia después del retraso SignalBar configurado.
Solo se procesan velas terminadas. La estrategia abre posiciones de mercado en la dirección del nuevo color y opcionalmente cierra el lado contrario.
Parámetros
Parámetro
Predeterminado
Descripción
OrderVolume
1
Volumen usado al abrir una nueva posición.
MaMethod1
Lwma
Suavizado aplicado al ratio de referencia (subconjunto de SMA/EMA/SMMA/LWMA/JJMA/AMA). Los tipos no soportados usan EMA.
Length1
12
Período de la media móvil de referencia.
Phase1
15
Parámetro de fase Jurik para la referencia (mantenido por compatibilidad).
PriceSource
Median
Precio aplicado antes de calcular la referencia.
MaMethod2
Jjma
Suavizado aplicado a los ratios transformados.
Length2
5
Período de la media móvil de señal.
Phase2
100
Parámetro de fase Jurik para el suavizado de señal.
Power
5
Exponente usado al elevar los ratios de apertura/cierre.
SignalBar
1
Cuántas velas cerradas esperar antes de actuar en un cambio de color.
BuyOpenEnabled
true
Permitir abrir posiciones largas.
SellOpenEnabled
true
Permitir abrir posiciones cortas.
BuyCloseEnabled
true
Cerrar largos cuando aparece una señal bajista.
SellCloseEnabled
true
Cerrar cortos cuando aparece una señal alcista.
StopLossPoints
0
Distancia absoluta de stop-loss. 0 desactiva el stop.
TakeProfitPoints
0
Distancia absoluta de take-profit. 0 desactiva el objetivo.
CandleType
Marco temporal H4
Serie de velas procesada por la estrategia.
Reglas de trading
Transición alcista (color cambia a 2): cerrar cortos activos (si se permite) y abrir una posición larga cuando Position <= 0 y BuyOpenEnabled es verdadero.
Transición bajista (color cambia a 0): cerrar largos activos (si se permite) y abrir una posición corta cuando Position >= 0 y SellOpenEnabled es verdadero.
Color 1 (neutral) no activa operaciones.
Las señales se evalúan usando la barra ubicada SignalBar pasos detrás de la vela terminada más reciente para imitar el timing original de MetaTrader.
Gestión de riesgos
StopLossPoints y TakeProfitPoints configuran StartProtection con distancias absolutas. Cuando cualquiera de los valores es cero, la protección respectiva se desactiva.
Notas
Solo se implementan directamente los métodos de suavizado disponibles en StockSharp. JurX, ParMA, T3 y VIDYA del código original se mapean a EMA como alternativa funcional.
Los parámetros de fase se mantienen por compatibilidad pero solo afectan a las medias basadas en Jurik/Kaufman.
La estrategia usa órdenes de mercado igual que el asesor experto original. La gestión de slippage de la versión MQL no se reproduce porque StockSharp maneja la ejecución mediante conectores.
namespace StockSharp.Samples.Strategies;
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// Average Change Candle strategy (simplified).
/// Compares smoothed open vs close ratios to detect bullish/bearish candle patterns.
/// Uses EMA of open and close to determine trend direction.
/// </summary>
public class AverageChangeCandleStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _emaPeriod;
private decimal _prevSmoothedOpen;
private decimal _prevSmoothedClose;
private bool _initialized;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int EmaPeriod
{
get => _emaPeriod.Value;
set => _emaPeriod.Value = value;
}
public AverageChangeCandleStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Source candles", "General");
_emaPeriod = Param(nameof(EmaPeriod), 12)
.SetGreaterThanZero()
.SetDisplay("EMA Period", "EMA smoothing period", "Indicators");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevSmoothedOpen = 0m;
_prevSmoothedClose = 0m;
_initialized = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevSmoothedOpen = 0;
_prevSmoothedClose = 0;
_initialized = false;
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ema, (ICandleMessage candle, decimal emaValue) =>
{
if (candle.State != CandleStates.Finished)
return;
// Simple exponential smoothing of open and close
var alpha = 2m / (EmaPeriod + 1m);
if (!_initialized)
{
_prevSmoothedOpen = candle.OpenPrice;
_prevSmoothedClose = candle.ClosePrice;
_initialized = true;
return;
}
var smoothedOpen = alpha * candle.OpenPrice + (1m - alpha) * _prevSmoothedOpen;
var smoothedClose = alpha * candle.ClosePrice + (1m - alpha) * _prevSmoothedClose;
var prevBullish = _prevSmoothedClose > _prevSmoothedOpen;
var currBullish = smoothedClose > smoothedOpen;
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevSmoothedOpen = smoothedOpen;
_prevSmoothedClose = smoothedClose;
return;
}
// Buy on transition to bullish smoothed candle
if (currBullish && !prevBullish && candle.ClosePrice > emaValue && Position <= 0)
{
BuyMarket();
}
// Sell on transition to bearish smoothed candle
else if (!currBullish && prevBullish && candle.ClosePrice < emaValue && Position >= 0)
{
SellMarket();
}
_prevSmoothedOpen = smoothedOpen;
_prevSmoothedClose = smoothedClose;
})
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ema);
DrawOwnTrades(area);
}
}
}
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 average_change_candle_strategy(Strategy):
def __init__(self):
super(average_change_candle_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Source candles", "General")
self._ema_period = self.Param("EmaPeriod", 12) \
.SetDisplay("EMA Period", "EMA smoothing period", "Indicators")
self._prev_smoothed_open = 0.0
self._prev_smoothed_close = 0.0
self._initialized = False
@property
def CandleType(self):
return self._candle_type.Value
@property
def EmaPeriod(self):
return self._ema_period.Value
def OnReseted(self):
super(average_change_candle_strategy, self).OnReseted()
self._prev_smoothed_open = 0.0
self._prev_smoothed_close = 0.0
self._initialized = False
def OnStarted2(self, time):
super(average_change_candle_strategy, self).OnStarted2(time)
self._prev_smoothed_open = 0.0
self._prev_smoothed_close = 0.0
self._initialized = False
ema = ExponentialMovingAverage()
ema.Length = self.EmaPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription \
.Bind(ema, self._on_process) \
.Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ema)
self.DrawOwnTrades(area)
def _on_process(self, candle, ema_value):
if candle.State != CandleStates.Finished:
return
ev = float(ema_value)
o = float(candle.OpenPrice)
c = float(candle.ClosePrice)
alpha = 2.0 / (self.EmaPeriod + 1.0)
if not self._initialized:
self._prev_smoothed_open = o
self._prev_smoothed_close = c
self._initialized = True
return
smoothed_open = alpha * o + (1.0 - alpha) * self._prev_smoothed_open
smoothed_close = alpha * c + (1.0 - alpha) * self._prev_smoothed_close
prev_bullish = self._prev_smoothed_close > self._prev_smoothed_open
curr_bullish = smoothed_close > smoothed_open
if curr_bullish and not prev_bullish and c > ev and self.Position <= 0:
self.BuyMarket()
elif not curr_bullish and prev_bullish and c < ev and self.Position >= 0:
self.SellMarket()
self._prev_smoothed_open = smoothed_open
self._prev_smoothed_close = smoothed_close
def CreateClone(self):
return average_change_candle_strategy()