Esta estrategia porta el asesor experto original Gann Grid de MQL/25065/Gann Grid.mq4 a la API de alto nivel de StockSharp. El script original mezclaba objetos de gráfico manuales con filtros de múltiples marcos temporales; la versión en C# mantiene el flujo de trabajo general mientras reemplaza los datos derivados de gráficos con lógica impulsada por indicadores que puede ejecutarse de forma desatendida.
Lógica de trading
Grid de Gann sintético – el máximo más alto y el mínimo más bajo durante AnchorPeriod velas aproximan los niveles de precio que se dibujaban manualmente en MetaTrader. Una ruptura por encima del máximo desencadena configuraciones largas, una ruptura por debajo del mínimo desencadena cortos.
Confirmación de tendencia – las medias móviles ponderadas linealmente rápida y lenta en el marco temporal superior (TrendCandleType) deben estar de acuerdo con la dirección de la ruptura.
Filtro de Momentum – la distancia porcentual entre el indicador de Momentum y el precio actual (también en el marco temporal superior) necesita exceder MomentumThreshold para asegurar que haya suficiente aceleración.
Confirmación MACD – un flujo de velas separado (MacdCandleType) alimenta un MACD (12/26/9 por defecto). La línea MACD debe estar en el mismo lado de cero y de la línea de señal que la dirección del trade.
Gestión de riesgo – se aplican compensaciones simétricas de stop-loss y take-profit desde el precio de entrada. Los módulos opcionales de break-even y trailing reproducen los bloques de protección de capital de la implementación MQL.
Solo se procesan velas terminadas para coincidir con las verificaciones originales de "nueva barra".
Diferencias versus la versión MQL
El código MetaTrader esperaba un objeto GANNGRID dibujado manualmente. El port lo reemplaza con indicadores de máximo/mínimo continuo, haciendo la lógica determinista para pruebas automatizadas.
El Momentum en MetaTrader está centrado alrededor de 100. El Momentum de StockSharp produce una diferencia de precio, por lo tanto la estrategia lo convierte en un porcentaje del cierre actual antes de compararlo con MomentumThreshold.
Las notificaciones (correo electrónico, push) y las operaciones gráficas del script MQL se omiten.
La gestión de riesgo usa salidas de mercado en lugar de modificar órdenes existentes, porque las estrategias de StockSharp gestionan posiciones en lugar de órdenes a nivel de terminal.
Parámetros
Nombre
Tipo
Predeterminado
Descripción
CandleType
DataType
Marco temporal de 5 minutos
Velas primarias que definen las rupturas.
TrendCandleType
DataType
Marco temporal de 15 minutos
Marco temporal superior usado para LWMA y filtros de Momentum.
MacdCandleType
DataType
Marco temporal de 1 día
Flujo de velas que alimenta el filtro de confirmación MACD.
FastMaPeriod
int
6
Longitud LWMA rápida en el marco temporal superior.
SlowMaPeriod
int
85
Longitud LWMA lenta en el marco temporal superior.
MomentumPeriod
int
14
Longitud de retroceso del Momentum.
MomentumThreshold
decimal
0.3
Desviación mínima de Momentum en porcentaje requerida para operar.
AnchorPeriod
int
100
Número de velas primarias que forman el grid de Gann sintético.
TakeProfitOffset
decimal
0.005
Distancia absoluta de take-profit desde el precio de entrada.
StopLossOffset
decimal
0.002
Distancia absoluta de stop-loss desde el precio de entrada.
EnableTrailing
bool
true
Habilita la gestión del trailing stop.
TrailingActivation
decimal
0.003
Beneficio requerido antes de que el trailing stop comience a seguir el precio.
TrailingStep
decimal
0.0015
Distancia entre el máximo local y el trailing stop.
EnableBreakEven
bool
true
Activa la lógica de movimiento al break-even.
BreakEvenTrigger
decimal
0.0025
Beneficio necesario antes de armar el break-even.
BreakEvenOffset
decimal
0.0
Compensación aplicada al precio de entrada al cerrar en break-even.
MacdFastPeriod
int
12
Longitud EMA rápida dentro del MACD.
MacdSlowPeriod
int
26
Longitud EMA lenta dentro del MACD.
MacdSignalPeriod
int
9
Longitud EMA de señal dentro del MACD.
Todas las compensaciones son distancias de precio absolutas. Ajústalas para que coincidan con el tamaño de tick del símbolo (por ejemplo, 0.001 ≈ 10 puntos en una cotización FX de 5 dígitos).
Cómo usar
Adjunte la estrategia a un valor y configure los tipos de velas. Es posible usar el mismo tipo de vela para múltiples filtros si se desea un único marco temporal.
Ajuste AnchorPeriod y las compensaciones de precio para que coincidan con la volatilidad del instrumento.
Habilite o deshabilite el break-even/trailing según su política de riesgo.
Inicie la estrategia; se suscribe automáticamente a los flujos de velas necesarios y gestiona posiciones con órdenes de mercado.
Archivos
CS/GannGridStrategy.cs – implementación de la estrategia.
README.md – esta documentación.
README_ru.md – descripción en ruso.
README_zh.md – descripción en chino.
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 GannGridStrategy : 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 GannGridStrategy()
{
_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 gann_grid_strategy(Strategy):
def __init__(self):
super(gann_grid_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(gann_grid_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(gann_grid_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 gann_grid_strategy()