A estratégia EES Hedger espelha o comportamento do assessor especializado clássico do MetaTrader que cobre automaticamente posições criadas por outro sistema de trading ou por traders manuais. Sempre que a conta monitorada abre uma posição que corresponde ao filtro configurado, a estratégia abre imediatamente uma posição oposta com seus próprios parâmetros. Dessa forma, neutraliza a exposição direcional enquanto ainda permite que a operação original continue.
O algoritmo é construído sobre a API de alto nível do StockSharp. Ele escuta as operações da conta, abre posições de hedge e gerencia ordens de proteção por meio de lógica de stop-loss, take-profit e trailing stop. O gerenciamento do trailing segue de perto a implementação original, avançando o stop somente quando o movimento do preço supera tanto a distância do stop quanto o incremento do trailing.
Parâmetros
Nome
Descrição
HedgeVolume
Volume fixo para a ordem de hedge. Não depende do tamanho da operação externa.
StopLossPips
Distância em pips para o stop-loss de proteção do hedge. Definir como zero para pular o stop inicial.
TakeProfitPips
Distância em pips para a ordem de take-profit. Definir como zero para omitir o alvo.
TrailingStopPips
Distância em pips usada para o trailing assim que o preço se move favoravelmente.
TrailingStepPips
Movimento mínimo em pips necessário antes de mover novamente o trailing stop. Deve ser positivo quando o trailing estiver ativo.
OriginalOrderComment
Filtro de comentário opcional. Apenas operações cujo comentário corresponda a este valor (sem distinção de maiúsculas/minúsculas) serão cobertas. Deixar vazio para reagir a todas as operações.
HedgerOrderComment
Comentário opcional usado para reconhecer as próprias operações de hedge da estratégia. Quando fornecido, operações com o mesmo comentário são ignoradas para evitar novo hedge.
Comportamento
Detecção de operações – a estratégia se subscreve aos eventos NewMyTrade do conector. Cada operação que vem do instrumento selecionado e passa pelos filtros de comentário é tratada como um sinal de entrada externo.
Execução do hedge – assim que uma operação qualificada é vista, a estratégia envia uma ordem de mercado na direção oposta usando HedgeVolume.
Configuração de proteção – após cada preenchimento próprio, o algoritmo cancela as ordens de proteção existentes e registra novas ordens de stop-loss e take-profit de acordo com o preço médio da posição atual.
Trailing stop – cada tick de operação recebido é usado para avaliar as regras de trailing. Assim que o preço se moveu pelo menos TrailingStopPips + TrailingStepPips em favor do hedge, o stop é aproximado do preço. Para posições compradas o stop segue abaixo do mercado, para vendidas acima.
Redefinição de posição – quando a posição de hedge está totalmente fechada (por exemplo, por stop ou alvo), a estratégia cancela automaticamente as ordens de proteção restantes e aguarda a próxima operação externa.
Notas de uso
A estratégia assume que o conector de conta reporta todas as operações da conta, incluindo as geradas por outros sistemas.
O cálculo de pips se adapta ao passo de preço do instrumento e multiplica por dez para cotações de 3 ou 5 dígitos, imitando o ajuste de ponto MQL.
Definir OriginalOrderComment para corresponder ao comentário do sistema primário se apenas operações específicas devem ser espelhadas. Ao cobrir operações manuais, deixar vazio.
Garantir que TrailingStepPips permaneça maior que zero sempre que o trailing estiver habilitado para evitar encerramento prematuro na inicialização.
Como o hedge sempre usa um volume fixo, pode ser conveniente ajustar HedgeVolume para que o hedge cubra a exposição média gerada pelo sistema primário.
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
using System.Globalization;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Mirrors trades by opening an opposite hedge position with trailing stop management.
/// Simplified from the EES Hedger expert advisor.
/// </summary>
public class EesHedgerStrategy : Strategy
{
private readonly StrategyParam<decimal> _hedgeVolume;
private readonly StrategyParam<int> _stopLossPips;
private readonly StrategyParam<int> _takeProfitPips;
private readonly StrategyParam<int> _trailingStopPips;
private readonly StrategyParam<int> _trailingStepPips;
private readonly StrategyParam<DataType> _candleType;
private decimal _entryPrice;
private decimal? _stopPrice;
private decimal _pipSize;
/// <summary>
/// Hedge position volume.
/// </summary>
public decimal HedgeVolume
{
get => _hedgeVolume.Value;
set => _hedgeVolume.Value = value;
}
/// <summary>
/// Stop-loss distance in pips.
/// </summary>
public int StopLossPips
{
get => _stopLossPips.Value;
set => _stopLossPips.Value = value;
}
/// <summary>
/// Take-profit distance in pips.
/// </summary>
public int TakeProfitPips
{
get => _takeProfitPips.Value;
set => _takeProfitPips.Value = value;
}
/// <summary>
/// Trailing stop distance in pips.
/// </summary>
public int TrailingStopPips
{
get => _trailingStopPips.Value;
set => _trailingStopPips.Value = value;
}
/// <summary>
/// Minimum step between trailing stop updates in pips.
/// </summary>
public int TrailingStepPips
{
get => _trailingStepPips.Value;
set => _trailingStepPips.Value = value;
}
/// <summary>
/// Initializes strategy parameters.
/// </summary>
public EesHedgerStrategy()
{
_hedgeVolume = Param(nameof(HedgeVolume), 0.1m)
.SetDisplay("Hedge Volume", "Volume used for hedge orders", "General");
_stopLossPips = Param(nameof(StopLossPips), 50)
.SetDisplay("Stop Loss (pips)", "Stop-loss distance per hedge", "Risk Management");
_takeProfitPips = Param(nameof(TakeProfitPips), 50)
.SetDisplay("Take Profit (pips)", "Take-profit distance per hedge", "Risk Management");
_trailingStopPips = Param(nameof(TrailingStopPips), 25)
.SetDisplay("Trailing Stop (pips)", "Trailing stop distance", "Risk Management");
_trailingStepPips = Param(nameof(TrailingStepPips), 5)
.SetDisplay("Trailing Step (pips)", "Minimum trailing stop increment", "Risk Management");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Time frame for processing", "General");
}
/// <summary>
/// Candle type used for processing.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, CandleType);
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_entryPrice = 0m;
_stopPrice = null;
_pipSize = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_pipSize = CalculatePipSize();
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var price = candle.ClosePrice;
if (price <= 0m)
return;
// Entry: if no position, open based on tick direction
if (Position == 0 && _entryPrice == 0m)
{
var volume = HedgeVolume > 0m ? HedgeVolume : Volume;
if (volume <= 0m)
return;
BuyMarket();
_entryPrice = price;
_stopPrice = null;
return;
}
if (Position != 0 && _entryPrice == 0m)
_entryPrice = price;
// Check stop loss
if (Position != 0 && StopLossPips > 0 && _pipSize > 0m)
{
var stopDistance = StopLossPips * _pipSize;
if (Position > 0 && price <= _entryPrice - stopDistance)
{
SellMarket();
_entryPrice = 0m;
_stopPrice = null;
return;
}
else if (Position < 0 && price >= _entryPrice + stopDistance)
{
BuyMarket();
_entryPrice = 0m;
_stopPrice = null;
return;
}
}
// Check take profit
if (Position != 0 && TakeProfitPips > 0 && _pipSize > 0m)
{
var takeDistance = TakeProfitPips * _pipSize;
if (Position > 0 && price >= _entryPrice + takeDistance)
{
SellMarket();
_entryPrice = 0m;
_stopPrice = null;
return;
}
else if (Position < 0 && price <= _entryPrice - takeDistance)
{
BuyMarket();
_entryPrice = 0m;
_stopPrice = null;
return;
}
}
// Trailing stop
if (Position != 0 && TrailingStopPips > 0 && _pipSize > 0m)
{
var trailingDistance = TrailingStopPips * _pipSize;
var trailingStep = TrailingStepPips * _pipSize;
if (Position > 0)
{
var newStop = price - trailingDistance;
if (newStop > _entryPrice && (!_stopPrice.HasValue || newStop > _stopPrice.Value + trailingStep))
_stopPrice = newStop;
if (_stopPrice.HasValue && price <= _stopPrice.Value)
{
SellMarket();
_entryPrice = 0m;
_stopPrice = null;
}
}
else if (Position < 0)
{
var newStop = price + trailingDistance;
if (newStop < _entryPrice && (!_stopPrice.HasValue || newStop < _stopPrice.Value - trailingStep))
_stopPrice = newStop;
if (_stopPrice.HasValue && price >= _stopPrice.Value)
{
BuyMarket();
_entryPrice = 0m;
_stopPrice = null;
}
}
}
}
private decimal CalculatePipSize()
{
var step = Security?.PriceStep ?? 0m;
if (step <= 0m)
return 1m;
var decimals = GetDecimalPlaces(step);
return decimals == 3 || decimals == 5 ? step * 10m : step;
}
private static int GetDecimalPlaces(decimal value)
{
var text = Math.Abs(value).ToString(CultureInfo.InvariantCulture);
var index = text.IndexOf('.');
return index >= 0 ? text.Length - index - 1 : 0;
}
}
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.Strategies import Strategy
class ees_hedger_strategy(Strategy):
"""Hedge strategy with trailing stop management."""
def __init__(self):
super(ees_hedger_strategy, self).__init__()
self._hedge_volume = self.Param("HedgeVolume", 0.1) \
.SetDisplay("Hedge Volume", "Volume used for hedge orders", "General")
self._stop_loss_pips = self.Param("StopLossPips", 50) \
.SetDisplay("Stop Loss (pips)", "Stop-loss distance per hedge", "Risk Management")
self._take_profit_pips = self.Param("TakeProfitPips", 50) \
.SetDisplay("Take Profit (pips)", "Take-profit distance per hedge", "Risk Management")
self._trailing_stop_pips = self.Param("TrailingStopPips", 25) \
.SetDisplay("Trailing Stop (pips)", "Trailing stop distance", "Risk Management")
self._trailing_step_pips = self.Param("TrailingStepPips", 5) \
.SetDisplay("Trailing Step (pips)", "Minimum trailing stop increment", "Risk Management")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Time frame for processing", "General")
self._entry_price = 0.0
self._stop_price = None
self._pip_size = 1.0
@property
def HedgeVolume(self):
return self._hedge_volume.Value
@property
def StopLossPips(self):
return self._stop_loss_pips.Value
@property
def TakeProfitPips(self):
return self._take_profit_pips.Value
@property
def TrailingStopPips(self):
return self._trailing_stop_pips.Value
@property
def TrailingStepPips(self):
return self._trailing_step_pips.Value
@property
def CandleType(self):
return self._candle_type.Value
def _calc_pip_size(self):
sec = self.Security
if sec is None or sec.PriceStep is None or float(sec.PriceStep) <= 0:
return 1.0
step = float(sec.PriceStep)
decimals = sec.Decimals if sec.Decimals is not None else 0
if decimals == 3 or decimals == 5:
return step * 10.0
return step
def OnStarted2(self, time):
super(ees_hedger_strategy, self).OnStarted2(time)
self._pip_size = self._calc_pip_size()
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self.process_candle).Start()
def process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
price = float(candle.ClosePrice)
if price <= 0:
return
# Entry: if no position, open buy
if self.Position == 0 and self._entry_price == 0:
self.BuyMarket()
self._entry_price = price
self._stop_price = None
return
if self.Position != 0 and self._entry_price == 0:
self._entry_price = price
# Stop loss check
if self.Position != 0 and self.StopLossPips > 0 and self._pip_size > 0:
stop_dist = self.StopLossPips * self._pip_size
if self.Position > 0 and price <= self._entry_price - stop_dist:
self.SellMarket()
self._entry_price = 0.0
self._stop_price = None
return
elif self.Position < 0 and price >= self._entry_price + stop_dist:
self.BuyMarket()
self._entry_price = 0.0
self._stop_price = None
return
# Take profit check
if self.Position != 0 and self.TakeProfitPips > 0 and self._pip_size > 0:
take_dist = self.TakeProfitPips * self._pip_size
if self.Position > 0 and price >= self._entry_price + take_dist:
self.SellMarket()
self._entry_price = 0.0
self._stop_price = None
return
elif self.Position < 0 and price <= self._entry_price - take_dist:
self.BuyMarket()
self._entry_price = 0.0
self._stop_price = None
return
# Trailing stop
if self.Position != 0 and self.TrailingStopPips > 0 and self._pip_size > 0:
trail_dist = self.TrailingStopPips * self._pip_size
trail_step = self.TrailingStepPips * self._pip_size
if self.Position > 0:
new_stop = price - trail_dist
if new_stop > self._entry_price and (self._stop_price is None or new_stop > self._stop_price + trail_step):
self._stop_price = new_stop
if self._stop_price is not None and price <= self._stop_price:
self.SellMarket()
self._entry_price = 0.0
self._stop_price = None
elif self.Position < 0:
new_stop = price + trail_dist
if new_stop < self._entry_price and (self._stop_price is None or new_stop < self._stop_price - trail_step):
self._stop_price = new_stop
if self._stop_price is not None and price >= self._stop_price:
self.BuyMarket()
self._entry_price = 0.0
self._stop_price = None
def OnReseted(self):
super(ees_hedger_strategy, self).OnReseted()
self._entry_price = 0.0
self._stop_price = None
self._pip_size = 1.0
def CreateClone(self):
return ees_hedger_strategy()