The EES Hedger strategy mirrors the behaviour of the classic MetaTrader expert advisor that automatically hedges positions created by another trading system or by manual traders. Whenever the monitored account opens a position that matches the configured filter, the strategy immediately opens an opposite position with its own parameters. By doing so it neutralises directional exposure while still allowing the original trade to run.
The algorithm is built on the high level StockSharp API. It listens to account trades, opens hedge positions, and manages protective orders through stop-loss, take-profit and trailing stop logic. Trailing management follows the original implementation closely, only advancing the stop when price movement exceeds both the stop distance and the trailing increment.
Parameters
Name
Description
HedgeVolume
Fixed volume for the hedge order. It does not depend on the external trade size.
StopLossPips
Distance in pips for the protective stop-loss of the hedge. Set to zero to skip the initial stop.
TakeProfitPips
Distance in pips for the take-profit order. Set to zero to omit the target.
TrailingStopPips
Distance in pips used for trailing once price moves favourably.
TrailingStepPips
Minimum pip movement required before moving the trailing stop again. Must be positive when trailing is active.
OriginalOrderComment
Optional comment filter. Only trades whose comment matches this value (case-insensitive) will be hedged. Leave empty to react to every trade.
HedgerOrderComment
Optional comment used to recognise the strategy's own hedge trades. When supplied, trades carrying the same comment are ignored to prevent re-hedging.
Behaviour
Trade detection – the strategy subscribes to NewMyTrade events of the connector. Each trade that comes from the selected security and passes the comment filters is treated as an external entry signal.
Hedge execution – as soon as a qualifying trade is seen, the strategy submits a market order in the opposite direction using HedgeVolume.
Protection setup – after each own fill the algorithm cancels existing protective orders and registers new stop-loss and take-profit orders according to the current average position price.
Trailing stop – every incoming trade tick is used to evaluate the trailing rules. Once the price has moved by at least TrailingStopPips + TrailingStepPips in favour of the hedge, the stop is moved closer to price. For long positions the stop trails below the market, for shorts above it.
Position reset – when the hedge position is fully closed (for example by stop or target), the strategy automatically cancels the remaining protective orders and waits for the next external trade.
Usage Notes
The strategy assumes that the account connector reports all account trades, including those generated by other systems.
Pip calculation adapts to the instrument price step and multiplies by ten for 3- or 5-digit quotes, imitating the MQL point adjustment.
Set OriginalOrderComment to match the comment of the primary system if only specific trades should be mirrored. When hedging manual trades, leave it empty.
Ensure that TrailingStepPips remains greater than zero whenever trailing is enabled to avoid premature termination at start-up.
Because the hedger always uses a fixed volume, you may wish to tune HedgeVolume so that the hedge covers the average exposure generated by the primary system.
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()