A estratégia replica o consultor especialista OverHedge V2 do MetaTrader na API de alto nível do StockSharp. Constrói uma grade com hedge seguindo a direção de uma EMA rápida e uma lenta, alternando então ordens compradas e vendidas dentro de um túnel dinâmico. As posições são adicionadas de acordo com uma progressão geométrica de lotes e toda a cesta é liquidada quando o lucro não realizado agregado atinge o alvo configurado.
Lógica de negociação
Filtro de tendência: Uma EMA de 8 períodos deve divergir de uma EMA de 21 períodos em pelo menos MinDistancePips. O filtro decide a direção da primeira negociação em cada ciclo.
Túnel de grade: A largura do túnel é igual ao spread atual multiplicado por dois mais TunnelWidthPips convertido em unidades de preço. Define o gatilho do lado oposto assim que o ciclo começa.
Alternância de ordens: As primeiras três posições são abertas na direção da tendência. Depois o algoritmo alterna de lado para fazer hedge da exposição usando os mesmos âncoras do túnel como referência.
Escalonamento de lotes: Cada ordem subsequente multiplica o volume anterior por BaseMultiplier começando em StartVolume. O tamanho é alinhado às restrições de volume do instrumento.
Saída do ciclo: Quando o ganho líquido não realizado por lote do instrumento está acima de MinProfitTargetPips e o lucro total da cesta excede ProfitTargetPips, a estratégia fecha todas as posições abertas e redefine o estado.
Desligamento manual: Definir ShutdownGrid como true fecha quaisquer posições restantes e evita novas ordens até que seja desativado.
Condições de entrada
Entradas compradas
O filtro de tendência indica tendência de alta (EMA_short - EMA_long > MinDistancePips).
O preço Ask é maior ou igual à âncora de compra atual.
A estratégia não está em modo de desligamento e a cesta não atingiu seu alvo de lucro.
Entradas vendidas
O filtro de tendência indica tendência de baixa (EMA_long - EMA_short > MinDistancePips).
O preço Ask é menor ou igual à âncora de venda atual.
A flag de desligamento é falsa e o alvo de lucro da cesta ainda não foi atingido.
Gestão de saída
Saída de lucro: Quando o lucro não realizado da cesta satisfaz ProfitTargetPips com cada lado aberto ganhando pelo menos MinProfitTargetPips por lote, todas as posições são fechadas a mercado.
Saída de emergência: Definir ShutdownGrid como true fecha imediatamente qualquer exposição aberta.
Indicadores e dados
EMA de 8 períodos (rápida) e EMA de 21 períodos (lenta) calculadas na série de velas configurada.
A assinatura de Nível 1 é usada para rastrear o melhor bid/ask para construir o túnel e comparar as condições de entrada com os spreads em tempo real.
Parâmetros
Parâmetro
Descrição
StartVolume
Volume inicial da primeira ordem em um ciclo.
BaseMultiplier
Multiplicador geométrico aplicado ao volume de cada ordem subsequente.
TunnelWidthPips
Largura adicional do túnel em pips adicionada ao dobro do spread atual.
ProfitTargetPips
Alvo de lucro da cesta medido em pips convertidos em distância de preço.
MinProfitTargetPips
Movimento mínimo favorável por lado antes que a cesta possa fechar.
ShortEmaPeriod
Período da EMA rápida usada para confirmação de direção.
LongEmaPeriod
Período da EMA lenta usada para confirmação de direção.
MinDistancePips
Separação mínima de EMA necessária para declarar uma tendência.
CandleType
Período de tempo das velas que alimentam as EMAs e o loop de negociação.
ShutdownGrid
Interruptor booleano que força a liquidação e bloqueia novas negociações.
Notas práticas
O período de vela padrão é uma hora; ajuste-o para corresponder ao período de tempo usado no EA original.
A estratégia depende de dados de melhor bid/ask; forneça cotações de Nível 1 durante o trading ao vivo ou backtesting.
Como o StockSharp mantém uma posição líquida por instrumento, compras e vendas alternadas reduzirão ou reverterão a exposição líquida em vez de manter tickets com hedge independentes, mas a lógica da cesta ainda imita a captura de lucro pretendida.
Sempre verifique as etapas de volume específicas do instrumento e os tamanhos de tick para que o túnel gerado e a escala de lotes correspondam ao mercado que você negocia.
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Grid hedging strategy based on EMA crossover direction.
/// Opens positions on EMA trend, reverses on direction change.
/// </summary>
public class OverHedgeV2Strategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _shortEmaPeriod;
private readonly StrategyParam<int> _longEmaPeriod;
private int _prevSignal;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int ShortEmaPeriod
{
get => _shortEmaPeriod.Value;
set => _shortEmaPeriod.Value = value;
}
public int LongEmaPeriod
{
get => _longEmaPeriod.Value;
set => _longEmaPeriod.Value = value;
}
public OverHedgeV2Strategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_shortEmaPeriod = Param(nameof(ShortEmaPeriod), 8)
.SetGreaterThanZero()
.SetDisplay("Short EMA", "Fast EMA length", "Indicators");
_longEmaPeriod = Param(nameof(LongEmaPeriod), 21)
.SetGreaterThanZero()
.SetDisplay("Long EMA", "Slow EMA length", "Indicators");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevSignal = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevSignal = 0;
var shortEma = new ExponentialMovingAverage { Length = ShortEmaPeriod };
var longEma = new ExponentialMovingAverage { Length = LongEmaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(shortEma, longEma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, shortEma);
DrawIndicator(area, longEma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal shortEma, decimal longEma)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var signal = shortEma > longEma ? 1 : shortEma < longEma ? -1 : _prevSignal;
if (signal == _prevSignal)
return;
var oldSignal = _prevSignal;
_prevSignal = signal;
if (signal == 1 && oldSignal <= 0)
{
if (Position < 0)
BuyMarket();
if (Position <= 0)
BuyMarket();
}
else if (signal == -1 && oldSignal >= 0)
{
if (Position > 0)
SellMarket();
if (Position >= 0)
SellMarket();
}
}
}
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 over_hedge_v2_strategy(Strategy):
def __init__(self):
super(over_hedge_v2_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._short_ema_period = self.Param("ShortEmaPeriod", 8) \
.SetDisplay("Short EMA", "Fast EMA length", "Indicators")
self._long_ema_period = self.Param("LongEmaPeriod", 21) \
.SetDisplay("Long EMA", "Slow EMA length", "Indicators")
self._prev_signal = 0
@property
def CandleType(self):
return self._candle_type.Value
@property
def ShortEmaPeriod(self):
return self._short_ema_period.Value
@property
def LongEmaPeriod(self):
return self._long_ema_period.Value
def OnReseted(self):
super(over_hedge_v2_strategy, self).OnReseted()
self._prev_signal = 0
def OnStarted2(self, time):
super(over_hedge_v2_strategy, self).OnStarted2(time)
self._prev_signal = 0
short_ema = ExponentialMovingAverage()
short_ema.Length = self.ShortEmaPeriod
long_ema = ExponentialMovingAverage()
long_ema.Length = self.LongEmaPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(short_ema, long_ema, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, short_ema)
self.DrawIndicator(area, long_ema)
self.DrawOwnTrades(area)
def _on_process(self, candle, short_value, long_value):
if candle.State != CandleStates.Finished:
return
sv = float(short_value)
lv = float(long_value)
if sv > lv:
signal = 1
elif sv < lv:
signal = -1
else:
signal = self._prev_signal
if signal == self._prev_signal:
return
old_signal = self._prev_signal
self._prev_signal = signal
if signal == 1 and old_signal <= 0:
if self.Position < 0:
self.BuyMarket()
if self.Position <= 0:
self.BuyMarket()
elif signal == -1 and old_signal >= 0:
if self.Position > 0:
self.SellMarket()
if self.Position >= 0:
self.SellMarket()
def CreateClone(self):
return over_hedge_v2_strategy()