Esta estratégia é uma porta direta do consultor especialista MetaTrader 4 "Canais" incluído na biblioteca pública de Gordago. Ele combina uma média móvel exponencial muito rápida (EMA) com três envelopes baseados em EMA para detectar momentos em que o preço escapa das zonas comprimidas. Uma vez aberta uma única posição, a estratégia depende de ordens de stop e trailing stops opcionais para gerenciar saídas, assim como a implementação original MQL.
Lógica de negociação
A estratégia assina velas horárias por padrão e calcula:
Um EMA rápido (comprimento 2) usando preços de fechamento de velas.
Um segundo EMA rápido (comprimento 2) usando preços de vela aberto, exigido pelas regras de entrada curta do consultor especialista.
Um EMA lenta (comprimento 220) nos fechamentos que serve de base para três desvios de envelope: ±1,0%, ±0,7% e ±0,3%.
Uma posição longa é aberta quando o fechamento EMA rápida satisfaz qualquer uma das seis verificações cruzadas históricas:
Ele cruza para cima através do envelope externo 1% inferior.
Ele cruza para cima através do envelope inferior de 0,7%.
Ele passa duas barras consecutivas abaixo do envelope inferior de 0,3% (condição de sobrevenda).
Ele cruza para cima através do próprio EMA lento.
Ele cruza para cima através do envelope superior de 0,3%.
Ele cruza para cima através do envelope superior de 0,7%.
Uma posição curta é aberta quando o rápido baseado em abertura EMA aciona qualquer uma das regras curtas simétricas:
Ele cruza para baixo através do envelope superior externo de 1%.
Cruza para baixo através do envelope superior de 0,7%.
Ele cruza para baixo o envelope superior de 0,3%.
Ele cruza para baixo através do EMA lenta.
Ele cruza para baixo através do envelope inferior de 0,3%.
Ele cruza para baixo através do envelope inferior de 0,7%.
Apenas uma posição de mercado pode existir por vez. Um novo sinal é ignorado enquanto uma negociação está ativa, correspondendo ao comportamento do especialista MetaTrader.
Gestão de risco
As distâncias individuais de stop-loss e take-profit podem ser configuradas para negociações longas e curtas. Quando definidas como zero, essas ordens de proteção são ignoradas, o que replica o estado desabilitado por padrão da fonte original.
Os trailing stops opcionais apertam a ordem de proteção quando o preço se move a favor da posição em mais do que a distância final medida em pontos.
Todas as ordens de proteção são canceladas automaticamente quando a posição é achatada ou a estratégia é interrompida.
Parâmetros
Nome
Descrição
Candle Type
Prazo utilizado para análise de preços (padrão: 1 hora).
Volume
Tamanho do pedido usado para todas as entradas.
Fast EMA / Slow EMA
Períodos para EMAs rápidos e lentos.
Envelope 1%, Envelope 0.7%, Envelope 0.3%
Largura percentual das três faixas do envelope.
Buy Stop-Loss, Sell Stop-Loss
Distância em pontos entre o preço de entrada e o stop loss inicial para negociações longas ou curtas.
Buy Take-Profit, Sell Take-Profit
Distância em pontos para os níveis opcionais de take-profit fixo.
Buy Trailing, Sell Trailing
Distância do trailing stop em pontos para posições longas ou curtas.
Use Trading Hours
Ativa o filtro de janela de tempo.
From Hour, To Hour
Limites de horas do dia inclusivos para abertura de novas posições. A janela termina por volta da meia-noite se From for maior que To.
Notas de uso
Como as distâncias de parada são definidas em pontos, elas são multiplicadas pela segurança PriceStep internamente. Certifique-se de que esta etapa corresponda ao instrumento usado para negociação.
O comprimento EMA rápida é intencionalmente muito curto para espelhar o especialista MT4. Aumentá-lo mudará drasticamente a frequência do sinal.
O consultor original também permitiu listas de permissões de contas e alertas sonoros. Eles foram omitidos porque são específicos da plataforma e não afetam a lógica do pedido.
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()