View on GitHub

EES Hedger (advanced)

Overview

The strategy mirrors the behaviour of the classic MetaTrader "EES Hedger" expert advisor. Whenever an external trader, discretionary operator, or another automated system opens a position on the same account, the strategy immediately creates an opposite hedge using a configurable volume. It then manages the hedge with stop-loss, take-profit, break-even, and trailing-stop rules so that exposure is neutralised while profits on the hedge are protected.

Unlike traditional signal-driven strategies, this module assumes that entries are produced elsewhere. Its sole responsibility is to observe account trades, react to matching tickets, and protect the hedge position until it is closed either by the protective orders or manually.

Trading logic

  1. Detection of external trades – the connector stream of account trades is monitored. Trades whose comment matches OriginalOrderComment (or all trades when the field is empty) are treated as the source that must be hedged. Trades produced by the strategy itself are filtered by storing their transaction identifiers.
  2. Mirroring orders – once a qualifying trade is received, the strategy submits an immediate market order in the opposite direction with volume HedgeVolume. An optional HedgerOrderComment helps back-office tools separate the hedge orders from other activity.
  3. Risk management – after the hedge is filled the strategy places stop-loss and take-profit orders at distances defined by the pip parameters. When break-even conditions are met, the stop is moved to the entry price plus one pip. If trailing is enabled, the stop is further advanced as the market continues to move in favour of the hedge.
  4. State cleanup – when the position reaches zero (for example, after a manual close) all protective orders are cancelled and internal flags are reset so that the next external trade can be hedged from scratch.

Parameters

Parameter Description
HedgeVolume Volume used to open the opposite hedge position.
StopLossPips Distance from the entry price to the protective stop-loss order.
TakeProfitPips Distance from the entry price to the take-profit order.
TrailingStopPips Distance maintained by the trailing stop once the activation threshold is exceeded. Set to zero to disable trailing.
TrailingActivationPips Minimum profit (in pips) required before the trailing stop begins to move.
BreakEvenPips Profit threshold (in pips) after which the stop-loss is moved to the entry price plus one pip.
OriginalOrderComment Optional comment filter that selects which external trades should be hedged. Leave empty to hedge all trades on the instrument.
HedgerOrderComment Comment attached to hedge orders and protective stops generated by the strategy.

Practical notes

  • Assign the same portfolio/account to the strategy as the external trader. All positions created on that account will be visible to the connector and can therefore be hedged.
  • When used with MetaTrader bridges, configure the expert adviser or bridge to copy the original order comment so that filtering works as expected.
  • The pip size is derived from the instrument price step. For five-digit FX symbols the distance automatically translates the specified pip values into correct price offsets.
  • Break-even and trailing logic never move the stop further away from the entry price. Only improvements are applied, ensuring that once break-even is reached the stop never goes back to a loss-making level.
  • The strategy does not manage the original position. Closing or modifying it remains the responsibility of the primary trading system.

Usage workflow

  1. Configure the strategy parameters, paying special attention to the comment filters and the volume of the hedge.
  2. Start the strategy and confirm that it is connected to the broker feed. It will remain idle until an external trade arrives.
  3. As soon as a qualifying trade appears, observe how the hedge order is created and how protective orders are placed in the DOM.
  4. Monitor the break-even and trailing behaviour to ensure that the configured pip distances match the broker's contract specifications.
  5. Stop the strategy when hedging is no longer required. All working protective orders are cancelled during shutdown.

Limitations

  • The module assumes access to the account's trade stream. It cannot hedge trades that are completely invisible to the connector.
  • Volume rounding rules are broker-specific. Ensure that the configured HedgeVolume is compatible with the instrument's lot step.
  • Because the strategy places market orders immediately, slippage in fast markets may result in imperfect hedges. Increase stop-loss distances to account for this when necessary.
using System;
using System.Linq;
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;

/// <summary>
/// Adapted from the MetaTrader "EES Hedger" expert advisor.
/// Uses EMA crossover signals with break-even and trailing stop risk management.
/// </summary>
public class EesHedgerAdvancedStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _stopLossPips;
	private readonly StrategyParam<int> _takeProfitPips;
	private readonly StrategyParam<DataType> _candleType;

	/// <summary>
	/// Fast EMA period.
	/// </summary>
	public int FastPeriod
	{
		get => _fastPeriod.Value;
		set => _fastPeriod.Value = value;
	}

	/// <summary>
	/// Slow EMA period.
	/// </summary>
	public int SlowPeriod
	{
		get => _slowPeriod.Value;
		set => _slowPeriod.Value = value;
	}

	/// <summary>
	/// Stop-loss distance in price steps.
	/// </summary>
	public int StopLossPips
	{
		get => _stopLossPips.Value;
		set => _stopLossPips.Value = value;
	}

	/// <summary>
	/// Take-profit distance in price steps.
	/// </summary>
	public int TakeProfitPips
	{
		get => _takeProfitPips.Value;
		set => _takeProfitPips.Value = value;
	}

	/// <summary>
	/// Candle type used by the strategy.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Initializes default parameters.
	/// </summary>
	public EesHedgerAdvancedStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 10)
			.SetGreaterThanZero()
			.SetDisplay("Fast EMA", "Fast EMA period", "Indicators");

		_slowPeriod = Param(nameof(SlowPeriod), 30)
			.SetGreaterThanZero()
			.SetDisplay("Slow EMA", "Slow EMA period", "Indicators");

		_stopLossPips = Param(nameof(StopLossPips), 500)
			.SetDisplay("Stop Loss", "Stop-loss distance", "Risk Management");

		_takeProfitPips = Param(nameof(TakeProfitPips), 500)
			.SetDisplay("Take Profit", "Take-profit distance", "Risk Management");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Candles used for calculations", "Market Data");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return new[] { (Security, CandleType) };
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		var fastEma = new EMA { Length = FastPeriod };
		var slowEma = new EMA { Length = SlowPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(fastEma, slowEma, ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, fastEma);
			DrawIndicator(area, slowEma);
			DrawOwnTrades(area);
		}

		// Use StartProtection for SL/TP
		var tp = TakeProfitPips > 0 ? new Unit(TakeProfitPips, UnitTypes.Absolute) : null;
		var sl = StopLossPips > 0 ? new Unit(StopLossPips, UnitTypes.Absolute) : null;
		StartProtection(tp, sl);

		base.OnStarted2(time);
	}

	private decimal _prevFast;
	private decimal _prevSlow;
	private bool _hasPrev;

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevFast = 0;
		_prevSlow = 0;
		_hasPrev = false;
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!IsFormedAndOnlineAndAllowTrading())
		{
			_prevFast = fastValue;
			_prevSlow = slowValue;
			_hasPrev = true;
			return;
		}

		if (!_hasPrev)
		{
			_prevFast = fastValue;
			_prevSlow = slowValue;
			_hasPrev = true;
			return;
		}

		// EMA crossover detection
		var crossedUp = _prevFast <= _prevSlow && fastValue > slowValue;
		var crossedDown = _prevFast >= _prevSlow && fastValue < slowValue;

		if (crossedUp)
		{
			// Close short if any, then go long
			if (Position < 0)
				BuyMarket(Math.Abs(Position));
			if (Position <= 0)
				BuyMarket(Volume);
		}
		else if (crossedDown)
		{
			// Close long if any, then go short
			if (Position > 0)
				SellMarket(Position);
			if (Position >= 0)
				SellMarket(Volume);
		}

		_prevFast = fastValue;
		_prevSlow = slowValue;
	}
}