The strategy replicates the MetaTrader OverHedge V2 expert advisor on the StockSharp high-level API. It builds a hedged grid by following the direction of a fast and a slow EMA, then alternates long and short orders inside a dynamic tunnel. Positions are added according to a geometric lot progression and the whole basket is liquidated once the aggregated unrealized profit reaches the configured target.
Trading Logic
Trend filter: An 8-period EMA must diverge from a 21-period EMA by at least MinDistancePips. The filter decides the direction of the first trade in each cycle.
Grid tunnel: The tunnel width equals the current spread multiplied by two plus TunnelWidthPips converted to price units. It defines the opposite-side trigger once the cycle starts.
Order alternation: The first three positions are opened in the trend direction. Afterwards the algorithm alternates side to hedge the exposure using the same tunnel anchors as the reference.
Lot escalation: Each subsequent order multiplies the previous volume by BaseMultiplier starting from StartVolume. The size is aligned to instrument volume constraints.
Cycle exit: When the net unrealized gain per instrument lot is above MinProfitTargetPips and the total basket profit exceeds ProfitTargetPips, the strategy closes all open positions and resets the state.
Manual shutdown: Setting ShutdownGrid to true closes any remaining position and prevents new orders until it is toggled off.
Entry Conditions
Long entries
Trend filter indicates an uptrend (EMA_short - EMA_long > MinDistancePips).
Ask price is greater than or equal to the current buy anchor.
The strategy is not in shutdown mode and the basket has not reached its profit target.
Short entries
Trend filter indicates a downtrend (EMA_long - EMA_short > MinDistancePips).
Ask price is less than or equal to the current sell anchor.
Shutdown flag is false and the basket profit target is not yet hit.
Exit Management
Profit exit: When the unrealized basket profit satisfies ProfitTargetPips with every open side gaining at least MinProfitTargetPips per lot, all positions are closed at market.
Emergency exit: Setting ShutdownGrid to true immediately closes any open exposure.
Indicators and Data
8-period EMA (fast) and 21-period EMA (slow) calculated on the configured candle series.
Level 1 subscription is used to track best bid/ask in order to build the tunnel and compare entry conditions with real-time spreads.
Parameters
Parameter
Description
StartVolume
Initial volume of the first order in a cycle.
BaseMultiplier
Geometric multiplier applied to the volume of each subsequent order.
TunnelWidthPips
Additional tunnel width in pips added to twice the current spread.
ProfitTargetPips
Basket profit target measured in pips converted to price distance.
MinProfitTargetPips
Minimum favorable move per side before the basket can close.
ShortEmaPeriod
Period of the fast EMA used for direction confirmation.
LongEmaPeriod
Period of the slow EMA used for direction confirmation.
MinDistancePips
Minimum EMA separation required to declare a trend.
CandleType
Time frame of the candles feeding the EMAs and the trading loop.
ShutdownGrid
Boolean switch that forces liquidation and blocks new trades.
Practical Notes
The default candle period is one hour; adjust it to match the timeframe used in the original EA.
The strategy relies on best bid/ask data; provide Level 1 quotes during live trading or backtesting.
Because StockSharp maintains a net position per instrument, alternating buys and sells will reduce or flip the net exposure instead of holding independent hedged tickets, but the basket logic still mimics the intended profit capture.
Always verify instrument-specific volume steps and tick sizes so the generated tunnel and lot scaling match the market you trade.
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Grid hedging strategy based on EMA crossover direction.
/// Opens positions on EMA trend, reverses on direction change.
/// </summary>
public class OverHedgeV2Strategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _shortEmaPeriod;
private readonly StrategyParam<int> _longEmaPeriod;
private int _prevSignal;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int ShortEmaPeriod
{
get => _shortEmaPeriod.Value;
set => _shortEmaPeriod.Value = value;
}
public int LongEmaPeriod
{
get => _longEmaPeriod.Value;
set => _longEmaPeriod.Value = value;
}
public OverHedgeV2Strategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_shortEmaPeriod = Param(nameof(ShortEmaPeriod), 8)
.SetGreaterThanZero()
.SetDisplay("Short EMA", "Fast EMA length", "Indicators");
_longEmaPeriod = Param(nameof(LongEmaPeriod), 21)
.SetGreaterThanZero()
.SetDisplay("Long EMA", "Slow EMA length", "Indicators");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevSignal = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevSignal = 0;
var shortEma = new ExponentialMovingAverage { Length = ShortEmaPeriod };
var longEma = new ExponentialMovingAverage { Length = LongEmaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(shortEma, longEma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, shortEma);
DrawIndicator(area, longEma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal shortEma, decimal longEma)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var signal = shortEma > longEma ? 1 : shortEma < longEma ? -1 : _prevSignal;
if (signal == _prevSignal)
return;
var oldSignal = _prevSignal;
_prevSignal = signal;
if (signal == 1 && oldSignal <= 0)
{
if (Position < 0)
BuyMarket();
if (Position <= 0)
BuyMarket();
}
else if (signal == -1 && oldSignal >= 0)
{
if (Position > 0)
SellMarket();
if (Position >= 0)
SellMarket();
}
}
}
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
from StockSharp.Algo.Strategies import Strategy
class over_hedge_v2_strategy(Strategy):
def __init__(self):
super(over_hedge_v2_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._short_ema_period = self.Param("ShortEmaPeriod", 8) \
.SetDisplay("Short EMA", "Fast EMA length", "Indicators")
self._long_ema_period = self.Param("LongEmaPeriod", 21) \
.SetDisplay("Long EMA", "Slow EMA length", "Indicators")
self._prev_signal = 0
@property
def CandleType(self):
return self._candle_type.Value
@property
def ShortEmaPeriod(self):
return self._short_ema_period.Value
@property
def LongEmaPeriod(self):
return self._long_ema_period.Value
def OnReseted(self):
super(over_hedge_v2_strategy, self).OnReseted()
self._prev_signal = 0
def OnStarted2(self, time):
super(over_hedge_v2_strategy, self).OnStarted2(time)
self._prev_signal = 0
short_ema = ExponentialMovingAverage()
short_ema.Length = self.ShortEmaPeriod
long_ema = ExponentialMovingAverage()
long_ema.Length = self.LongEmaPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(short_ema, long_ema, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, short_ema)
self.DrawIndicator(area, long_ema)
self.DrawOwnTrades(area)
def _on_process(self, candle, short_value, long_value):
if candle.State != CandleStates.Finished:
return
sv = float(short_value)
lv = float(long_value)
if sv > lv:
signal = 1
elif sv < lv:
signal = -1
else:
signal = self._prev_signal
if signal == self._prev_signal:
return
old_signal = self._prev_signal
self._prev_signal = signal
if signal == 1 and old_signal <= 0:
if self.Position < 0:
self.BuyMarket()
if self.Position <= 0:
self.BuyMarket()
elif signal == -1 and old_signal >= 0:
if self.Position > 0:
self.SellMarket()
if self.Position >= 0:
self.SellMarket()
def CreateClone(self):
return over_hedge_v2_strategy()