Inicio
/
Ejemplos de estrategias
Ver en GitHub
EMA Cross Contest Hedged Strategy
This strategy reproduces the MetaTrader expert advisor EMA_CROSS_CONTEST_HEDGED inside StockSharp. The robot looks for a bullish/bearish crossover between a fast and a slow exponential moving average (EMA) and optionally checks the MACD histogram as a trend confirmation. When a signal appears, the strategy immediately opens a market position and places a ladder of stop orders that hedge the trade by adding more exposure if price keeps trending.
Trading Logic
Calculate a short EMA and a long EMA on the configured candle series. Signals can be taken either from the previous completed bar (default) or from the current bar once the candle closes.
Detect a bullish crossover when the short EMA rises above the long EMA and a bearish crossover when it falls below the long EMA.
Optionally require the MACD line to be above zero for long trades and below zero for short trades, replicating the MQL filter.
When the bullish condition is satisfied, buy at market, attach stop-loss and take-profit targets, and queue four buy-stop pending orders spaced by the hedge distance.
When the bearish condition is satisfied, sell at market, attach risk targets, and queue four sell-stop pending orders below price.
Pending orders are cancelled after their expiration time if they are not triggered.
Trailing stops tighten as open profits grow, and opposite crossovers can force early exits when Use Close is enabled.
Parameters
Candle Type – timeframe used for all calculations.
Order Volume – trade volume for the initial position and each hedge order.
Take Profit (pips) – take-profit distance in pips.
Stop Loss (pips) – stop-loss distance in pips.
Trailing Stop (pips) – trailing stop distance (0 disables trailing).
Hedge Level (pips) – spacing between the hedging pending orders.
Use Close – close existing positions when an opposite crossover happens.
Use MACD – require MACD confirmation for trade entries.
Expiration (s) – lifetime for pending hedge orders.
Short EMA – length of the fast EMA.
Long EMA – length of the slow EMA (must be greater than the fast EMA).
Signal Bar – choose whether to evaluate signals on the current bar (0) or the previous bar (1).
Notes
All comments in the code are provided in English as requested.
The pending hedge structure follows the behaviour from the original MQL expert advisor, placing four orders at equal distance steps.
Price conversions from pips take the symbol’s PriceStep and Decimals into account to match MetaTrader point calculations.
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>
/// EMA Cross Contest strategy - dual EMA crossover.
/// Buys when short EMA crosses above long EMA.
/// Sells when short EMA crosses below long EMA.
/// </summary>
public class EmaCrossContestHedgedLadderStrategy : Strategy
{
private readonly StrategyParam<int> _shortPeriod;
private readonly StrategyParam<int> _longPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevShort;
private decimal _prevLong;
private bool _hasPrev;
public int ShortPeriod { get => _shortPeriod.Value; set => _shortPeriod.Value = value; }
public int LongPeriod { get => _longPeriod.Value; set => _longPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public EmaCrossContestHedgedLadderStrategy()
{
_shortPeriod = Param(nameof(ShortPeriod), 9)
.SetDisplay("Short EMA", "Short EMA period", "Indicators");
_longPeriod = Param(nameof(LongPeriod), 21)
.SetDisplay("Long EMA", "Long EMA period", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
protected override void OnReseted() { base.OnReseted(); _prevShort = 0m; _prevLong = 0m; _hasPrev = false; }
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_hasPrev = false;
var shortEma = new ExponentialMovingAverage { Length = ShortPeriod };
var longEma = new ExponentialMovingAverage { Length = LongPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(shortEma, longEma, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal shortEma, decimal longEma)
{
if (candle.State != CandleStates.Finished)
return;
if (!_hasPrev)
{
_prevShort = shortEma;
_prevLong = longEma;
_hasPrev = true;
return;
}
if (_prevShort <= _prevLong && shortEma > longEma && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
}
else if (_prevShort >= _prevLong && shortEma < longEma && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
}
_prevShort = shortEma;
_prevLong = longEma;
}
}
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 ema_cross_contest_hedged_ladder_strategy(Strategy):
def __init__(self):
super(ema_cross_contest_hedged_ladder_strategy, self).__init__()
self._short_period = self.Param("ShortPeriod", 9).SetDisplay("Short EMA", "Short EMA period", "Indicators")
self._long_period = self.Param("LongPeriod", 21).SetDisplay("Long EMA", "Long EMA period", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))).SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_short = 0.0
self._prev_long = 0.0
self._has_prev = False
@property
def short_period(self): return self._short_period.Value
@property
def long_period(self): return self._long_period.Value
@property
def candle_type(self): return self._candle_type.Value
def OnReseted(self):
super(ema_cross_contest_hedged_ladder_strategy, self).OnReseted()
self._prev_short = 0.0
self._prev_long = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(ema_cross_contest_hedged_ladder_strategy, self).OnStarted2(time)
self._has_prev = False
short_ema = ExponentialMovingAverage()
short_ema.Length = self.short_period
long_ema = ExponentialMovingAverage()
long_ema.Length = self.long_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(short_ema, long_ema, self.process_candle).Start()
def process_candle(self, candle, short_ema, long_ema):
if candle.State != CandleStates.Finished:
return
short_val = float(short_ema)
long_val = float(long_ema)
if not self._has_prev:
self._prev_short = short_val
self._prev_long = long_val
self._has_prev = True
return
if self._prev_short <= self._prev_long and short_val > long_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif self._prev_short >= self._prev_long and short_val < long_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_short = short_val
self._prev_long = long_val
def CreateClone(self):
return ema_cross_contest_hedged_ladder_strategy()