Esta estratégia porta o consultor especialista original Gann Grid de MQL/25065/Gann Grid.mq4 para a API de alto nível do StockSharp. O script original misturava objetos de gráfico manuais com filtros de múltiplos períodos; a versão em C# mantém o fluxo de trabalho geral enquanto substitui os dados derivados de gráficos por lógica orientada por indicadores que pode ser executada de forma autônoma.
Lógica de trading
Grid de Gann sintético – A máxima mais alta e a mínima mais baixa ao longo de AnchorPeriod velas aproximam os níveis de preço que eram desenhados manualmente no MetaTrader. Um rompimento acima da máxima aciona configurações compradas, um rompimento abaixo da mínima aciona vendidas.
Confirmação de tendência – Médias móvias ponderadas lineares rápida e lenta no período superior (TrendCandleType) devem concordar com a direção do rompimento.
Filtro de Momentum – A distância percentual entre o indicador de Momentum e o preço atual (também no período superior) precisa exceder MomentumThreshold para garantir aceleração suficiente.
Confirmação MACD – Um fluxo de velas separado (MacdCandleType) alimenta um MACD (12/26/9 por padrão). A linha MACD deve estar no mesmo lado de zero e da linha de sinal que a direção do trade.
Gestão de risco – Offsets simétricos de stop-loss e take-profit são aplicados a partir do preço de entrada. Módulos opcionais de break-even e trailing reproduzem os blocos de proteção de capital da implementação MQL.
Apenas velas concluídas são processadas para corresponder às verificações originais de "nova barra".
Diferenças em relação à versão MQL
O código MetaTrader esperava um objeto GANNGRID desenhado manualmente. O port o substitui por indicadores de máxima/mínima contínua, tornando a lógica determinística para testes automatizados.
O Momentum no MetaTrader é centrado em torno de 100. O Momentum do StockSharp produz uma diferença de preço, portanto a estratégia o converte em porcentagem do fechamento atual antes de comparar com MomentumThreshold.
Notificações (e-mail, push) e operações gráficas do script MQL são omitidas.
A gestão de risco usa saídas de mercado em vez de modificar ordens existentes, pois as estratégias StockSharp gerenciam posições em vez de ordens no nível do terminal.
Parâmetros
Nome
Tipo
Padrão
Descrição
CandleType
DataType
Período de 5 minutos
Velas primárias que definem os rompimentos.
TrendCandleType
DataType
Período de 15 minutos
Período superior usado para LWMA e filtros de Momentum.
MacdCandleType
DataType
Período de 1 dia
Fluxo de velas que alimenta o filtro de confirmação MACD.
FastMaPeriod
int
6
Comprimento LWMA rápida no período superior.
SlowMaPeriod
int
85
Comprimento LWMA lenta no período superior.
MomentumPeriod
int
14
Comprimento de retrocesso do Momentum.
MomentumThreshold
decimal
0.3
Desvio mínimo de Momentum em percentual necessário para operar.
AnchorPeriod
int
100
Número de velas primárias que formam o grid de Gann sintético.
TakeProfitOffset
decimal
0.005
Distância absoluta de take-profit a partir do preço de entrada.
StopLossOffset
decimal
0.002
Distância absoluta de stop-loss a partir do preço de entrada.
EnableTrailing
bool
true
Habilita o gerenciamento do trailing stop.
TrailingActivation
decimal
0.003
Lucro necessário antes do trailing stop começar a seguir o preço.
TrailingStep
decimal
0.0015
Distância entre a máxima local e o trailing stop.
EnableBreakEven
bool
true
Ativa a lógica de mover para break-even.
BreakEvenTrigger
decimal
0.0025
Lucro necessário antes do break-even ser ativado.
BreakEvenOffset
decimal
0.0
Offset aplicado ao preço de entrada ao fechar no break-even.
MacdFastPeriod
int
12
Comprimento EMA rápida dentro do MACD.
MacdSlowPeriod
int
26
Comprimento EMA lenta dentro do MACD.
MacdSignalPeriod
int
9
Comprimento EMA de sinal dentro do MACD.
Todos os offsets são distâncias de preço absolutas. Ajuste-os para corresponder ao tamanho de tick do símbolo (por exemplo, 0.001 ≈ 10 pontos em uma cotação FX de 5 dígitos).
Como usar
Anexe a estratégia a um ativo e configure os tipos de velas. É possível usar o mesmo tipo de vela para múltiplos filtros se um único período for desejado.
Ajuste AnchorPeriod e os offsets de preço para corresponder à volatilidade do instrumento.
Habilite ou desabilite break-even/trailing de acordo com sua política de risco.
Inicie a estratégia; ela assina automaticamente os fluxos de velas necessários e gerencia posições com ordens de mercado.
Arquivos
CS/GannGridStrategy.cs – implementação da estratégia.
README.md – esta documentação.
README_ru.md – descrição em russo.
README_zh.md – descrição em chinês.
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()