Esta estrategia porta el asesor experto MetaTrader 5 Exp_StochasticCGOscillator a StockSharp. La conversión mantiene la lógica original del oscilador Stochastic Center of Gravity, reconstruye el suavizado de la línea de disparo y ejecuta operaciones usando la API de estrategia de alto nivel de StockSharp.
Cómo funciona
Pipeline de indicadores – cada vela finalizada de CandleType alimenta el oscilador Stochastic CG personalizado. Los precios medios impulsan un bucle center-of-gravity, los valores se normalizan sobre las últimas Length barras, y una ventana deslizante ponderada produce la línea del oscilador. La línea de disparo se recrea mediante el mismo suavizado 0.96 * (previous + 0.02) que aplica el EA.
Muestreo de señal – la estrategia inspecciona dos lecturas históricas separadas por SignalBar. Se prepara una compra cuando la lectura más antigua (desplazamiento SignalBar + 1) está por encima del trigger mientras la más reciente (desplazamiento SignalBar) cruza por debajo. Los cortos reflejan la lógica en dirección opuesta.
Gestión de posición – las posiciones largas se cierran en cuanto la lectura más antigua cae por debajo del trigger, mientras que las posiciones cortas salen cuando la lectura más antigua sube por encima de él. Cuando aparece una nueva entrada en el lado opuesto, la posición actual se aplana antes de enviar la orden de reversión.
Manejo de riesgo – las distancias opcionales de stop-loss y take-profit se expresan en pasos del instrumento y se evalúan sobre el precio de cierre de cada vela procesada. Reflejan los inputs protectores del EA sin depender de órdenes pendientes.
Control de calentamiento – la estrategia espera hasta que el indicador esté completamente inicializado (suficiente historial para el bucle CG y el buffer de suavizado de cuatro valores) antes de emitir señales, garantizando backtests deterministas.
Gestión de riesgo y dimensionamiento de posición
Stops/objetivos – StopLossPoints y TakeProfitPoints se traducen en distancias absolutas usando Security.PriceStep. Un valor de 0 deshabilita el límite respectivo.
Posición activa única – el algoritmo nunca mantiene exposición larga y corta al mismo tiempo. Las señales opuestas activan un cierre explícito antes de entrar en la nueva dirección.
Dimensionamiento de posición – SizingMode = FixedVolume siempre opera con FixedVolume. SizingMode = PortfolioShare convierte DepositShare del valor del portafolio en contratos usando el último cierre y Security.VolumeStep.
Parámetros
Parámetro
Descripción
CandleType
Marco temporal suscrito para velas y cálculos de indicadores.
Length
Período del oscilador Stochastic CG (afecta las ventanas CG y de normalización).
SignalBar
Número de velas cerradas atrás usadas para evaluar señales (1 reproduce el valor predeterminado del EA).
AllowLongEntry / AllowShortEntry
Activa/desactiva entradas largas/cortas.
AllowLongExit / AllowShortExit
Activa/desactiva salidas automáticas para posiciones largas/cortas.
StopLossPoints / TakeProfitPoints
Distancias protectoras en pasos de precio. Establezca en 0 para deshabilitar.
FixedVolume
Tamaño de orden cuando el modo de dimensionamiento es volumen fijo.
DepositShare
Fracción del portafolio usada en el dimensionamiento basado en participación.
SizingMode
Elige entre volumen fijo y dimensionamiento de posición basado en participación.
Notas de uso
Alinee CandleType con el marco temporal usado por el indicador original (H8 en la versión MQL). Valores de SignalBar más grandes requieren un calentamiento más largo porque el buffer de historial del indicador debe cubrir el desplazamiento.
Los stops y objetivos actúan sobre los cierres de velas; no son órdenes intrabarra. Ajuste los valores de puntos para adaptarse al tamaño del tick del instrumento.
Cuando el dimensionamiento PortfolioShare está habilitado, asegúrese de que la valoración del portafolio esté disponible; de lo contrario la estrategia recurre al volumen fijo.
El indicador produce valores en el rango [-1, 1] como la implementación original, permitiendo reutilizar filtros familiares basados en umbrales si se desea.
Diferencias con el EA original
Las órdenes de mercado se envían inmediatamente sin el parámetro Deviation_; el manejo del deslizamiento se delega a la capa de ejecución de StockSharp.
La gestión de dinero se simplifica a dos modos (FixedVolume y PortfolioShare). Las opciones de dimensionamiento adicionales basadas en margen del EA no se reproducen.
Las marcas de tiempo de órdenes pendientes (UpSignalTime / DnSignalTime) son innecesarias porque las estrategias de StockSharp trabajan en velas completadas y se ejecutan sincrónicamente.
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;
/// <summary>
/// Stochastic CG Oscillator strategy using EMA crossover.
/// Buys when fast EMA crosses above slow EMA, sells on reverse.
/// </summary>
public class StochasticCgOscillatorStrategy : 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 StochasticCgOscillatorStrategy()
{
_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 stochastic_cg_oscillator_strategy(Strategy):
def __init__(self):
super(stochastic_cg_oscillator_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(stochastic_cg_oscillator_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(stochastic_cg_oscillator_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 stochastic_cg_oscillator_strategy()