La Estrategia corta Envelope MA es un puerto C# del asesor experto MetaTrader EnvelopeMA.mq4 (ID 9533). Recrea la lógica de ruptura original de solo cortos en velas de 15 minutos combinando una envolvente de promedio móvil exponencial con dos EMA adicionales y un trío de filtros Parabolic SAR. La estrategia busca retrocesos del precio y el EMA rápido en la mitad inferior del sobre, luego arma una orden de venta pendiente en el límite inferior del sobre. Cuando se ejecuta la orden, gestiona la posición corta con niveles fijos de stop-loss y take-profit, así como reglas de salida basadas en indicadores.
Indicadores y señales
Base del sobre: Promedio móvil exponencial de máximos de velas (EnvelopePeriod, predeterminado 280). La banda inferior es el disparador de entrada y se calcula con un porcentaje de desviación (EnvelopeDeviation, predeterminado 0,08%).
EMA rápida: Promedio móvil exponencial de los mínimos de las velas (FastMaPeriod, predeterminado 6) utilizado para confirmar el impulso antes de armar la entrada corta.
EMA lenta (desplazado): Promedio móvil exponencial de los mínimos de las velas con un retraso de una barra (SlowMaPeriod, predeterminado 18). El valor retrasado refleja el parámetro de cambio iMA de MetaTrader y se utiliza tanto para la confirmación de entrada como para las decisiones de salida.
Parabolic SAR trío: Tres Parabolic SAR instancias con diferentes factores de aceleración (0.03/0.5, 0.015/0.6 y 0.02/0.2) que deben ubicarse por encima del precio actual antes de que la estrategia permita una salida basada en indicadores.
La estrategia espera a que se completen las velas. Cuando el EMA rápido, el } lento desplazado y el cierre de la vela permanecen entre los límites del sobre (por encima de la banda inferior y por debajo de la banda superior), envía una orden de stop de venta en la banda del sobre inferior. Las órdenes pendientes caducan después de aproximadamente cinco intervalos de velas si permanecen sin ejecutar.
Gestión comercial
Niveles de protección: Al ingresar, la estrategia coloca objetivos internos de stop-loss y take-profit derivados de las distancias de pips configuradas. Los movimientos de precios fuera del rango de la vela se aproximan utilizando los valores máximos y mínimos de cada barra terminada.
Salida del indicador: Una posición corta se cierra anticipadamente cuando tanto las EMA como el cierre se sitúan por debajo del precio de entrada, los tres valores de SAR permanecen por encima del precio y el EMA rápido vuelve a cruzar por encima del EMA lento retrasado, imitando el comportamiento de MetaTrader.
Ajuste final: Después de al menos cuatro barras, si el máximo de vela más alto desde la entrada se ha movido al menos tres pasos de precio por debajo del precio de entrada y el cierre se negocia por debajo de la banda inferior del sobre, el stop-loss se ajusta a esa banda inferior.
Controles de riesgo
Protección de capital: El parámetro LiquidityThreshold cierra cualquier posición corta abierta y cancela las paradas de venta pendientes si la relación entre el capital de la cartera y el saldo inicial cae por debajo del valor configurado (predeterminado 0,58).
Caducidad de la orden: Las órdenes pendientes no completadas se cancelan automáticamente una vez que transcurre su vida útil de cinco barras para evitar señales obsoletas.
Parámetros
Nombre
Descripción
Predeterminado
CandleType
Tipo de vela/plazo de tiempo procesado por la estrategia.
plazo de 15 minutos
EnvelopePeriod
EMA longitud utilizada como base del sobre.
280
EnvelopeDeviation
Ancho del sobre expresado en porcentaje.
0,08
FastMaPeriod
Periodo EMA rápida calculado sobre mínimos.
6
SlowMaPeriod
Periodo EMA lento (evaluado con un retraso de una barra).
18
StopLossPips
Distancia del stop-loss en pips desde el precio de entrada.
25
TakeProfitPips
Distancia de toma de ganancias en pips desde el precio de entrada.
25
TradeVolume
Volumen utilizado para órdenes pendientes y de mercado.
1
LiquidityThreshold
Relación mínima entre capital y saldo; los cortos se liquidan cuando se incumplen.
0,58
Notas de conversión
El tamaño del lote MetaTrader basado en el saldo, el margen o los contrapips se reemplazó con un parámetro directo TradeVolume para ajustarse al modelo de ejecución StockSharp.
La marca de tiempo de vencimiento de las órdenes pendientes se maneja dentro del ciclo de estrategia porque las órdenes StockSharp no exponen el mismo campo de vencimiento que MetaTrader.
Los niveles de stop-loss y take-profit se evalúan frente a los máximos y mínimos de las velas para aproximarse a los activadores dentro de la barra, coincidiendo con el comportamiento del experto MQL que monitoreó los precios en las barras completadas.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Envelope MA Short: EMA band reversion with ATR stops.
/// </summary>
public class EnvelopeMaShortStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _emaLength;
private readonly StrategyParam<int> _atrLength;
private readonly StrategyParam<decimal> _bandPercent;
private decimal _entryPrice;
public EnvelopeMaShortStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_emaLength = Param(nameof(EmaLength), 20)
.SetDisplay("EMA Length", "EMA period.", "Indicators");
_atrLength = Param(nameof(AtrLength), 14)
.SetDisplay("ATR Length", "ATR period.", "Indicators");
_bandPercent = Param(nameof(BandPercent), 1.0m)
.SetDisplay("Band %", "Band width percent.", "Indicators");
}
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int EmaLength { get => _emaLength.Value; set => _emaLength.Value = value; }
public int AtrLength { get => _atrLength.Value; set => _atrLength.Value = value; }
public decimal BandPercent { get => _bandPercent.Value; set => _bandPercent.Value = value; }
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_entryPrice = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_entryPrice = 0;
var ema = new ExponentialMovingAverage { Length = EmaLength };
var atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ema, atr, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null) { DrawCandles(area, subscription); DrawIndicator(area, ema); DrawOwnTrades(area); }
}
private void ProcessCandle(ICandleMessage candle, decimal emaVal, decimal atrVal)
{
if (candle.State != CandleStates.Finished) return;
if (atrVal <= 0 || emaVal <= 0) return;
var close = candle.ClosePrice;
var factor = BandPercent / 100m;
var upper = emaVal * (1m + factor);
var lower = emaVal * (1m - factor);
if (Position > 0)
{
if (close >= emaVal || close <= _entryPrice - atrVal * 1.5m) { SellMarket(); _entryPrice = 0; }
}
else if (Position < 0)
{
if (close <= emaVal || close >= _entryPrice + atrVal * 1.5m) { BuyMarket(); _entryPrice = 0; }
}
if (Position == 0)
{
if (close < lower) { _entryPrice = close; BuyMarket(); }
else if (close > upper) { _entryPrice = close; 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.Strategies import Strategy
from StockSharp.Algo.Indicators import ExponentialMovingAverage, AverageTrueRange
class envelope_ma_short_strategy(Strategy):
def __init__(self):
super(envelope_ma_short_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe.", "General")
self._ema_length = self.Param("EmaLength", 20) \
.SetDisplay("EMA Length", "EMA period.", "Indicators")
self._atr_length = self.Param("AtrLength", 14) \
.SetDisplay("ATR Length", "ATR period.", "Indicators")
self._band_percent = self.Param("BandPercent", 1.0) \
.SetDisplay("Band %", "Band width percent.", "Indicators")
self._entry_price = 0.0
@property
def CandleType(self):
return self._candle_type.Value
@property
def EmaLength(self):
return self._ema_length.Value
@property
def AtrLength(self):
return self._atr_length.Value
@property
def BandPercent(self):
return self._band_percent.Value
def OnStarted2(self, time):
super(envelope_ma_short_strategy, self).OnStarted2(time)
self._entry_price = 0.0
self._ema = ExponentialMovingAverage()
self._ema.Length = self.EmaLength
self._atr = AverageTrueRange()
self._atr.Length = self.AtrLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._ema, self._atr, self.ProcessCandle).Start()
def ProcessCandle(self, candle, ema_val, atr_val):
if candle.State != CandleStates.Finished:
return
ev = float(ema_val)
av = float(atr_val)
if av <= 0 or ev <= 0:
return
close = float(candle.ClosePrice)
factor = float(self.BandPercent) / 100.0
upper = ev * (1.0 + factor)
lower = ev * (1.0 - factor)
if self.Position > 0:
if close >= ev or close <= self._entry_price - av * 1.5:
self.SellMarket()
self._entry_price = 0.0
elif self.Position < 0:
if close <= ev or close >= self._entry_price + av * 1.5:
self.BuyMarket()
self._entry_price = 0.0
if self.Position == 0:
if close < lower:
self._entry_price = close
self.BuyMarket()
elif close > upper:
self._entry_price = close
self.SellMarket()
def OnReseted(self):
super(envelope_ma_short_strategy, self).OnReseted()
self._entry_price = 0.0
def CreateClone(self):
return envelope_ma_short_strategy()