The Hedge Any Positions Strategy is a direct conversion of the original Hedge any positions (barabashkakvn's edition) MQL5 expert. The StockSharp version keeps the core idea intact: it monitors every open leg created by the strategy and, once a leg loses a defined number of pips, immediately opens an opposite position with an amplified lot size. The implementation relies on the high-level StockSharp API, so hedge orders are placed through market orders and position tracking is handled internally without custom order-routing code.
The strategy can optionally place an initial trade when it starts. Afterwards it simply reacts to adverse price moves and builds a ladder of hedging trades, marking each leg as hedged so the same position cannot trigger multiple opposite entries.
Hedging Workflow
Candle feed – a configurable CandleType drives the strategy. Only finished candles are processed.
Loss calculation – on each candle close the strategy checks whether the close price moved against any open leg by at least LosingPips multiplied by the computed pip size.
Hedge execution – if a losing leg is found, a market order in the opposite direction is sent. The order volume equals the original leg volume multiplied by LotCoefficient, rounded to the instrument volume step and clipped to the allowed minimum/maximum volume.
State update – once an opposite order is dispatched, the original leg is flagged as hedged and the newly opened trade is stored as a fresh leg that can itself be hedged later if price reverses again.
Parameters
Parameter
Description
Default
CandleType
Timeframe used to evaluate price movements and trigger hedges.
1-minute candles
LosingPips
Number of pips the price must move against a leg before a hedge is opened.
5
LotCoefficient
Multiplier applied to the original volume when submitting the hedge order.
2.0
AutoPlaceInitialTrade
When enabled the strategy sends the first trade automatically on start.
Disabled
InitialVolume
Order size used by the optional initial trade. Rounded to the instrument volume step.
0.10
InitialDirection
Side (buy or sell) used for the optional initial trade.
Buy
Note: Set the Strategy.Volume property to the base order size you want the strategy to use. The parameters above only control hedging-specific behaviour.
Usage Guidelines
Assign a Security, Portfolio, and desired base Volume before starting the strategy.
Adjust LosingPips and LotCoefficient to reflect the volatility and risk tolerance for the selected instrument.
Enable AutoPlaceInitialTrade if you want the StockSharp version to create the very first position automatically; otherwise, manually open an initial leg or let another component do it.
Because the StockSharp high-level API works with net positions, the internal leg list is used to emulate the hedged structure. Monitor account exposure when running on netting accounts.
Review execution reports: every hedge is placed with a market order (BuyMarket or SellMarket).
Differences from the Original Expert
Margin validation, slippage checks, and verbose result logging were removed; StockSharp already reports execution problems through strategy events.
The conversion uses finished candles instead of tick-by-tick data. Choose a sufficiently small timeframe if you need faster reaction times.
Lot rounding now relies on Security.VolumeStep, Security.MinVolume, and Security.MaxVolume to stay compliant with the instrument's trading rules.
Alerts, notifications, and the tester-only random initial trade from the MQL version were intentionally omitted. The optional automatic entry parameter replaces that behaviour.
Recommended Enhancements
Combine the hedging module with a separate entry strategy that defines when the first position should be created.
Add equity-based shutdown rules or maximum-depth limits to prevent unbounded hedging chains.
Integrate portfolio-level monitoring to ensure margin requirements remain within acceptable limits.
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;
/// <summary>
/// Hedge Any Positions strategy using ATR-based mean reversion.
/// Enters long when price drops below EMA by ATR threshold, enters short on rally above.
/// Uses stop-loss and take-profit for risk management.
/// </summary>
public class HedgeAnyPositionsStrategy : Strategy
{
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<decimal> _atrMultiplier;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private ExponentialMovingAverage _ema;
private AverageTrueRange _atr;
private decimal _entryPrice;
private int _cooldown;
/// <summary>
/// EMA period.
/// </summary>
public int EmaPeriod
{
get => _emaPeriod.Value;
set => _emaPeriod.Value = value;
}
/// <summary>
/// ATR period.
/// </summary>
public int AtrPeriod
{
get => _atrPeriod.Value;
set => _atrPeriod.Value = value;
}
/// <summary>
/// Multiplier applied to ATR for entry threshold.
/// </summary>
public decimal AtrMultiplier
{
get => _atrMultiplier.Value;
set => _atrMultiplier.Value = value;
}
/// <summary>
/// Stop-loss distance in price steps.
/// </summary>
public int StopLossPoints
{
get => _stopLossPoints.Value;
set => _stopLossPoints.Value = value;
}
/// <summary>
/// Take-profit distance in price steps.
/// </summary>
public int TakeProfitPoints
{
get => _takeProfitPoints.Value;
set => _takeProfitPoints.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="HedgeAnyPositionsStrategy"/> class.
/// </summary>
public HedgeAnyPositionsStrategy()
{
_emaPeriod = Param(nameof(EmaPeriod), 100)
.SetGreaterThanZero()
.SetDisplay("EMA Period", "EMA period for mean price", "Indicator");
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Period", "ATR calculation period", "Indicator");
_atrMultiplier = Param(nameof(AtrMultiplier), 2m)
.SetGreaterThanZero()
.SetDisplay("ATR Multiplier", "Multiplier for entry distance", "Indicator");
_stopLossPoints = Param(nameof(StopLossPoints), 200)
.SetNotNegative()
.SetDisplay("Stop Loss", "Stop-loss distance in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 300)
.SetNotNegative()
.SetDisplay("Take Profit", "Take-profit distance in price steps", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_ema = null;
_atr = null;
_entryPrice = 0;
_cooldown = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_ema = new ExponentialMovingAverage { Length = EmaPeriod };
_atr = new AverageTrueRange { Length = AtrPeriod };
var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
subscription.Bind(_ema, _atr, ProcessCandle);
subscription.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal emaValue, decimal atrValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_ema.IsFormed || !_atr.IsFormed)
return;
if (_cooldown > 0)
{
_cooldown--;
return;
}
var close = candle.ClosePrice;
var step = Security?.PriceStep ?? 1m;
var threshold = atrValue * AtrMultiplier;
// Check SL/TP
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step)
{
SellMarket();
_entryPrice = 0;
_cooldown = 80;
return;
}
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step)
{
SellMarket();
_entryPrice = 0;
_cooldown = 80;
return;
}
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step)
{
BuyMarket();
_entryPrice = 0;
_cooldown = 80;
return;
}
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step)
{
BuyMarket();
_entryPrice = 0;
_cooldown = 80;
return;
}
}
// Mean reversion: buy when price drops below EMA by threshold
if (close < emaValue - threshold && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
_entryPrice = close;
_cooldown = 80;
}
// Mean reversion: sell when price rallies above EMA by threshold
else if (close > emaValue + threshold && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
_entryPrice = close;
_cooldown = 80;
}
}
}
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, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class hedge_any_positions_strategy(Strategy):
"""
Hedge Any Positions: ATR-based mean reversion.
Enters long when price drops below EMA by ATR*multiplier.
Enters short when price rallies above EMA by ATR*multiplier.
Uses SL/TP for risk management with cooldown.
"""
def __init__(self):
super(hedge_any_positions_strategy, self).__init__()
self._ema_period = self.Param("EmaPeriod", 100) \
.SetDisplay("EMA Period", "EMA period for mean price", "Indicator")
self._atr_period = self.Param("AtrPeriod", 14) \
.SetDisplay("ATR Period", "ATR calculation period", "Indicator")
self._atr_multiplier = self.Param("AtrMultiplier", 2.0) \
.SetDisplay("ATR Multiplier", "Multiplier for entry distance", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200) \
.SetDisplay("Stop Loss", "Stop-loss distance in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 300) \
.SetDisplay("Take Profit", "Take-profit distance in price steps", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._entry_price = 0.0
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(hedge_any_positions_strategy, self).OnReseted()
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(hedge_any_positions_strategy, self).OnStarted2(time)
ema = ExponentialMovingAverage()
ema.Length = self._ema_period.Value
atr = AverageTrueRange()
atr.Length = self._atr_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(ema, atr, self._process_candle).Start()
def _process_candle(self, candle, ema_val, atr_val):
if candle.State != CandleStates.Finished:
return
if self._cooldown > 0:
self._cooldown -= 1
return
close = float(candle.ClosePrice)
ema = float(ema_val)
atr = float(atr_val)
step = 1.0
if self.Security is not None and self.Security.PriceStep is not None:
step = float(self.Security.PriceStep)
if step <= 0:
step = 1.0
threshold = atr * self._atr_multiplier.Value
if self.Position > 0 and self._entry_price > 0:
sl_pts = self._stop_loss_points.Value
tp_pts = self._take_profit_points.Value
if sl_pts > 0 and close <= self._entry_price - sl_pts * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 80
return
if tp_pts > 0 and close >= self._entry_price + tp_pts * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 80
return
elif self.Position < 0 and self._entry_price > 0:
sl_pts = self._stop_loss_points.Value
tp_pts = self._take_profit_points.Value
if sl_pts > 0 and close >= self._entry_price + sl_pts * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 80
return
if tp_pts > 0 and close <= self._entry_price - tp_pts * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 80
return
if close < ema - threshold and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 80
elif close > ema + threshold and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 80
def CreateClone(self):
return hedge_any_positions_strategy()