Convertida del experto de MetaTrader 5 "MA Envelopes". La estrategia busca retrocesos de precio hacia una media móvil envuelta por un canal de envelopes. Cuando una vela completada cierra entre la media móvil y una de las bandas del envelope durante la ventana de trading configurada, la estrategia coloca entradas limitadas en la media móvil con órdenes de salida protectoras derivadas del envelope.
Lógica de trading
Se calcula una media móvil con el método, fuente de precio y período seleccionados. El mismo valor se usa para construir bandas simétricas de envelope usando el parámetro de desviación.
Cuando una vela terminada cierra por encima de la media móvil pero por debajo de la banda superior del envelope y el precio ask actual permanece por encima de la media móvil, se prepara una secuencia escalonada de órdenes de compra limitadas en el precio de la media móvil.
Cada compra limitada usa el envelope inferior como nivel de stop-loss y el envelope superior más un offset adicional en pips como take-profit.
Se gestionan hasta tres órdenes independientes, cada una con su propio offset de take-profit (parámetros SL/TP de First, Second, Third).
Cuando una vela terminada cierra por debajo de la media móvil pero por encima de la banda inferior del envelope y el precio bid actual permanece por debajo de la media móvil, la lógica se refleja para órdenes de venta limitadas.
La ventana de trading está controlada por StartHour y EndHour (hora del terminal). Después de la hora de fin, todas las órdenes de entrada aún activas se cancelan.
El riesgo por operación se estima a través de MaximumRisk y se reduce después de pérdidas consecutivas usando DecreaseFactor. El volumen de la orden se alinea con el paso de volumen y los límites del instrumento.
Una vez que una orden de entrada está completamente ejecutada, las órdenes de stop-loss y take-profit protectoras se registran inmediatamente. Si se activa una orden de salida, la orden contrapartida se cancela y, si hay volumen de posición restante, se emiten nuevas órdenes protectoras para el resto.
Parámetros
Parámetro
Descripción
MaximumRisk
Fracción del capital disponible arriesgada por posición.
DecreaseFactor
Reduce el tamaño de la posición después de operaciones perdedoras consecutivas.
First/Second/ThirdStopTakeProfitPips
Distancias en pips añadidas a las bandas del envelope para las tres órdenes escalonadas.
StartHour, EndHour
Límites de la sesión de trading en hora del terminal (0–23).
MaPeriod, MaShift, MaMethodType, AppliedPrice
Configuración de la media móvil.
EnvelopeDeviation
Ancho del canal de envelope en porcentaje.
CandleType
Marco temporal de las velas usadas para los cálculos.
Notas
Las órdenes protectoras se recrean siempre que solo parte de una posición está cerrada, manteniendo cubierto el tamaño restante.
Las órdenes de entrada pendientes se cancelan al final de la sesión; las posiciones abiertas siguen siendo gestionadas por sus órdenes protectoras.
La estrategia depende de las actualizaciones del libro de órdenes para capturar los últimos precios bid/ask; los valores de cierre de las velas se usan como alternativa cuando los datos del libro de órdenes no están disponibles.
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class MaEnvelopesStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _period;
private decimal? _prevHigh, _prevLow;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int Period { get => _period.Value; set => _period.Value = value; }
public MaEnvelopesStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame()).SetDisplay("Candle Type", "Timeframe", "General");
_period = Param(nameof(Period), 15).SetGreaterThanZero().SetDisplay("Channel Period", "Channel lookback", "Indicators");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevHigh = null;
_prevLow = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevHigh = null; _prevLow = null;
var highest = new Highest { Length = Period };
var lowest = new Lowest { Length = Period };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(highest, lowest, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null) { DrawCandles(area, subscription); DrawOwnTrades(area); }
}
private void ProcessCandle(ICandleMessage candle, decimal high, decimal low)
{
if (candle.State != CandleStates.Finished) return;
if (!IsFormedAndOnlineAndAllowTrading()) { _prevHigh = high; _prevLow = low; return; }
if (_prevHigh == null || _prevLow == null) { _prevHigh = high; _prevLow = low; return; }
if (candle.ClosePrice > _prevHigh.Value && Position <= 0) { if (Position < 0) BuyMarket(); BuyMarket(); }
else if (candle.ClosePrice < _prevLow.Value && Position >= 0) { if (Position > 0) SellMarket(); SellMarket(); }
_prevHigh = high; _prevLow = low;
}
}
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 Highest, Lowest
from StockSharp.Algo.Strategies import Strategy
class ma_envelopes_strategy(Strategy):
def __init__(self):
super(ma_envelopes_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._period = self.Param("Period", 15) \
.SetDisplay("Channel Period", "Channel lookback", "Indicators")
self._prev_high = None
self._prev_low = None
@property
def CandleType(self):
return self._candle_type.Value
@property
def Period(self):
return self._period.Value
def OnReseted(self):
super(ma_envelopes_strategy, self).OnReseted()
self._prev_high = None
self._prev_low = None
def OnStarted2(self, time):
super(ma_envelopes_strategy, self).OnStarted2(time)
self._prev_high = None
self._prev_low = None
highest = Highest()
highest.Length = self.Period
lowest = Lowest()
lowest.Length = self.Period
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(highest, lowest, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _on_process(self, candle, high_value, low_value):
if candle.State != CandleStates.Finished:
return
hv = float(high_value)
lv = float(low_value)
if self._prev_high is None or self._prev_low is None:
self._prev_high = hv
self._prev_low = lv
return
close = float(candle.ClosePrice)
if close > self._prev_high and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif close < self._prev_low and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_high = hv
self._prev_low = lv
def CreateClone(self):
return ma_envelopes_strategy()