GitHub で見る

Hedger Drawdown Strategy

StockSharp port of the MetaTrader 5 expert advisor hedger.mq5 (MQL #23511). The original system opens a protective hedge in the opposite direction when an existing position draws down by a specified number of pips. Once price retraces by a smaller amount, the hedge is closed even at a loss, allowing the original trade to recover. This conversion reproduces the behaviour with the StockSharp high level API and adapts the mechanics to the platform's net position model.

Trading logic

  1. The strategy monitors the close of every candle from the configured timeframe.
  2. For each non-hedge long position it checks whether the distance between the entry price and the current close is greater than or equal to DrawdownOpenPips. If no short hedge is active, it opens one with the same volume.
  3. For each non-hedge short position it applies the symmetrical rule, opening a long hedge after the loss reaches the open threshold.
  4. Active hedge legs are closed when their floating loss reaches DrawdownClosePips, mirroring the MetaTrader logic of releasing the protection after a partial recovery.
  5. When the account is flat and StartWithLong is enabled, the algorithm opens a seed long position to kick off the cycle.

Because StockSharp tracks net positions, the strategy keeps internal ledgers of long and short entries (including which ones are hedges). Each market order updates the ledgers so that hedges can be opened and closed independently even if the broker collapses positions.

Parameters

Parameter Description
DrawdownOpenPips Drawdown in pips that triggers opening the opposite hedge.
DrawdownClosePips Drawdown in pips that forces the hedge to be closed.
InitialVolume Volume of the initial trade when seeding the cycle.
StartWithLong If enabled, opens the initial long position when flat.
EnableVerboseLogging Writes hedging actions to the strategy log for debugging.
CandleType Candle series used for monitoring drawdowns.

Differences from the MetaTrader version

  • The expert advisor relied on ticket comments (hedge_buy / hedge_sell) to distinguish hedge positions. The conversion stores this state in memory because StockSharp uses netting.
  • Margin checks and slippage settings are omitted; order placement uses the high level BuyMarket / SellMarket helpers.
  • The strategy exposes optimisation ranges for the pip thresholds and volume so they can be tuned with StockSharp optimisers.

Usage notes

  1. Attach the strategy to the desired symbol and portfolio.
  2. Adjust the pip thresholds to match the instrument's volatility.
  3. Enable verbose logging when validating the conversion—the log records every hedge creation and removal with pip statistics.
  4. Deploy on timeframes that deliver meaningful candle closes (e.g. M15 to H1) to avoid overtrading.
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 HedgerDrawdownStrategy : 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 HedgerDrawdownStrategy()
	{
		_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;
	}
}