Esta estrategia es un puerto directo del asesor experto MetaTrader 4 "Canales" incluido en la biblioteca pública de Gordago. Combina una media móvil exponencial muy rápida (EMA) con tres envolventes basadas en EMA para detectar momentos en los que el precio escapa de las zonas comprimidas. Una vez que se abre una sola posición, la estrategia se basa en órdenes de parada y paradas dinámicas opcionales para gestionar las salidas, al igual que la implementación original MQL.
Lógica comercial
La estrategia se suscribe a velas horarias de forma predeterminada y calcula:
Un EMA rápido (longitud 2) utilizando precios de cierre de velas.
Un segundo EMA rápido (longitud 2) utilizando precios de vela apertura, requeridos por las reglas de entrada corta del asesor experto.
Un EMA lento (longitud 220) en cierres que sirve como base para tres desviaciones de envolvente: ±1,0%, ±0,7% y ±0,3%.
Se abre una posición larga cuando el cierre EMA rápida satisface cualquiera de las seis verificaciones cruzadas históricas:
Cruza hacia arriba a través de la envoltura exterior inferior del 1%.
Cruza hacia arriba a través de la envolvente inferior del 0,7%.
Pasa dos barras consecutivas por debajo del límite inferior del 0,3% (condición de sobreventa).
Cruza hacia arriba a través del EMA lenta.
Cruza hacia arriba a través de la envolvente superior del 0,3%.
Cruza hacia arriba a través de la envolvente superior del 0,7%.
Se abre una posición corta cuando el EMA rápido basado en apertura activa cualquiera de las reglas cortas simétricas:
Cruza hacia abajo a través de la envoltura superior exterior del 1%.
Cruza hacia abajo a través de la envolvente superior del 0,7%.
Cruza hacia abajo a través de la envolvente superior del 0,3%.
Cruza hacia abajo por el EMA lenta.
Cruza hacia abajo a través de la envolvente inferior del 0,3%.
Cruza hacia abajo a través de la envolvente inferior del 0,7%.
Sólo puede existir una posición de mercado a la vez. Una nueva señal se ignora mientras una operación está activa, coincidiendo con el comportamiento del experto MetaTrader.
Gestión de riesgos
Se pueden configurar distancias individuales de stop-loss y take-profit para operaciones largas y cortas. Cuando se establece en cero, esas órdenes de protección se omiten, lo que replica el estado deshabilitado por defecto de la fuente original.
Los trailingstops opcionales refuerzan la orden de protección una vez que el precio se mueve a favor de la posición en más que la distancia de seguimiento medida en puntos.
Todas las órdenes de protección se cancelan automáticamente cuando la posición se aplana o la estrategia se detiene.
Parámetros
Nombre
Descripción
Candle Type
Plazo utilizado para el análisis de precios (predeterminado: 1 hora).
Volume
Tamaño del pedido utilizado para todas las entradas.
Fast EMA / Slow EMA
Períodos para las EMA rápidas y lentas.
Envelope 1%, Envelope 0.7%, Envelope 0.3%
Ancho porcentual de las tres bandas envolventes.
Buy Stop-Loss, Sell Stop-Loss
Distancia en puntos entre el precio de entrada y el stop-loss inicial para operaciones largas o cortas.
Buy Take-Profit, Sell Take-Profit
Distancia en puntos para los niveles de toma de ganancias fijos opcionales.
Buy Trailing, Sell Trailing
Distancia del trailing stop en puntos para posiciones largas o cortas.
Use Trading Hours
Habilita el filtro de ventana de tiempo.
From Hour, To Hour
Límites horarios inclusivos para la apertura de nuevas posiciones. La ventana cierra alrededor de la medianoche si From es mayor que To.
Notas de uso
Debido a que las distancias de parada se definen en puntos, se multiplican internamente por la seguridad PriceStep. Asegúrese de que este paso coincida con el instrumento utilizado para operar.
La longitud rápida EMA es intencionalmente muy corta para reflejar al experto en MT4. Aumentarlo cambiará drásticamente la frecuencia de la señal.
El asesor original también permitía la inclusión en listas blancas de cuentas y alertas sonoras. Se omitieron porque son específicos de la plataforma y no afectan la lógica de los pedidos.
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>
/// Channels strategy - EMA envelope breakout.
/// Buys when price breaks above fast EMA + offset, sells when it breaks below.
/// Uses slow EMA as trend filter.
/// </summary>
public class ChannelsStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevFast;
private decimal _prevSlow;
private bool _hasPrev;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public ChannelsStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 10)
.SetDisplay("Fast EMA", "Fast EMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 30)
.SetDisplay("Slow EMA", "Slow 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(); _prevFast = 0m; _prevSlow = 0m; _hasPrev = false; }
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_hasPrev = false;
var fast = new ExponentialMovingAverage { Length = FastPeriod };
var slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fast, slow, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
{
if (candle.State != CandleStates.Finished)
return;
if (!_hasPrev)
{
_prevFast = fast;
_prevSlow = slow;
_hasPrev = true;
return;
}
var close = candle.ClosePrice;
// Buy: price crosses above fast EMA, fast EMA above slow EMA (uptrend)
if (close > fast && fast > slow && _prevFast <= _prevSlow && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
}
// Sell: price crosses below fast EMA, fast EMA below slow EMA (downtrend)
else if (close < fast && fast < slow && _prevFast >= _prevSlow && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
}
// Exit long when fast crosses below slow
else if (Position > 0 && fast < slow && _prevFast >= _prevSlow)
{
SellMarket();
}
// Exit short when fast crosses above slow
else if (Position < 0 && fast > slow && _prevFast <= _prevSlow)
{
BuyMarket();
}
_prevFast = fast;
_prevSlow = slow;
}
}
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 channels_strategy(Strategy):
def __init__(self):
super(channels_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 10) \
.SetDisplay("Fast EMA", "Fast EMA period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 30) \
.SetDisplay("Slow EMA", "Slow EMA period", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(channels_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(channels_strategy, self).OnStarted2(time)
self._has_prev = False
fast = ExponentialMovingAverage()
fast.Length = self.fast_period
slow = ExponentialMovingAverage()
slow.Length = self.slow_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(fast, slow, self.process_candle).Start()
def process_candle(self, candle, fast, slow):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast)
slow_val = float(slow)
if not self._has_prev:
self._prev_fast = fast_val
self._prev_slow = slow_val
self._has_prev = True
return
close = float(candle.ClosePrice)
if close > fast_val and fast_val > slow_val and self._prev_fast <= self._prev_slow and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif close < fast_val and fast_val < slow_val and self._prev_fast >= self._prev_slow and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
elif self.Position > 0 and fast_val < slow_val and self._prev_fast >= self._prev_slow:
self.SellMarket()
elif self.Position < 0 and fast_val > slow_val and self._prev_fast <= self._prev_slow:
self.BuyMarket()
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return channels_strategy()