Esta estratégia é um port StockSharp do especialista MetaTrader Standard Deviation Channel. Ela traça um canal de volatilidade baseado em uma média móvel ponderada linear (LWMA) e opera rompimentos alinhados com a tendência predominante. As entradas são filtradas pela força do Momentum e uma confirmação do MACD, enquanto as saídas combinam alvos fixos, saltos de break-even e proteção por trailing.
Indicadores e sinais
Canal de desvio padrão construído a partir de uma linha base LWMA e um multiplicador de desvio configurável. Configurações longas exigem que a banda superior suba; configurações curtas exigem que a banda inferior desça.
Filtro de tendência: LWMA rápida e lenta calculadas nas mesmas velas. Posições longas exigem LWMA_fast > LWMA_slow; posições curtas requerem o oposto.
Filtro de Momentum: um indicador de Momentum de 14 períodos. Pelo menos uma das últimas três leituras deve desviar do nível neutro de 100 pelo limiar configurado.
Filtro MACD: configuração clássica 12/26/9. Entradas longas precisam de MACD ≥ signal, enquanto entradas curtas requerem MACD ≤ signal.
Gerenciamento de operações
Dimensionamento de posição: usa o parâmetro TradeVolume. Reversões fecham automaticamente a exposição oposta antes de abrir o novo lado.
Take-profit e stop-loss: expressos em pips e avaliados contra o PriceStep do instrumento. A estratégia emite saídas de mercado quando o intervalo da vela toca o preço alvo ou de stop.
Salto de break-even: quando o lucro não realizado atinge BreakEvenTriggerPips, o stop é movido para a entrada mais BreakEvenOffsetPips (ou menos para vendidos).
Trailing stop: após atingir TrailingStartPips, o stop segue o preço por TrailingStepPips, assegurando ganhos em ambos os lados.
Saída por rejeição do canal: se o preço fechar de volta dentro do canal e a inclinação achatar contra a posição, a operação é fechada antecipadamente.
Parâmetros
Nome
Descrição
CandleType
Período principal utilizado para todos os cálculos.
TradeVolume
Tamanho base da ordem.
TrendLength
Período de retrocesso LWMA que define a linha base do canal.
DeviationMultiplier
Multiplicador de desvio padrão para a largura do canal.
FastMaLength / SlowMaLength
Comprimentos LWMA para o filtro de tendência.
MomentumPeriod
Período de retrocesso para o filtro de Momentum.
MomentumThreshold
Desvio mínimo de 100 exigido em qualquer um dos últimos três valores de Momentum.
TakeProfitPips / StopLossPips
Distância dos níveis de saída fixos (convertidos usando PriceStep).
BreakEvenTriggerPips / BreakEvenOffsetPips
Controla quando e como o stop de break-even é ativado.
TrailingStartPips / TrailingStepPips
Ativa e dimensiona o trailing stop.
MacdFastPeriod, MacdSlowPeriod, MacdSignalPeriod
Configuração do MACD.
MaxPositionUnits
Posição líquida absoluta máxima; previne alavancagem excessiva.
Notas de uso
Anexe a estratégia a um ativo que exponha um PriceStep válido. Os pips são convertidos multiplicando este valor de passo.
Use TrendLength e DeviationMultiplier para adaptar o canal a diferentes mercados.
Os filtros de Momentum e MACD podem ser relaxados (limiar menor, períodos mais curtos) para aumentar a frequência de operações.
A lógica de trailing funciona em fechamentos de velas; picos intrabarra que não terminam além dos limiares são ignorados.
Diferenças em relação ao Expert Advisor original
A versão MetaTrader depende de objetos gráficos para ler a inclinação do canal e usa vários ramos de gerenciamento de dinheiro (dimensionamento martingale, proteção de capital). Este port mantém a verificação de inclinação, mas simplifica o controle de risco para operações de tamanho fixo limitadas por MaxPositionUnits.
Todas as saídas são tratadas com ordens de mercado no fechamento da vela, pois as estratégias StockSharp não espelham diretamente as APIs de modificação de ordens do MT4.
Notificações por e-mail e push são substituídas por mensagens AddInfoLog para manter a conversão autocontida.
Cortes de conta baseados em capital foram omitidos; em vez disso, o foco é posto nas funcionalidades de proteção por posição.
Aviso legal
Esta amostra destina-se a uso educacional. Sempre faça testes prospectivos e valide a configuração antes de implantá-la em uma conta real.
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class StandardDeviationChannelStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private ExponentialMovingAverage _fast;
private ExponentialMovingAverage _slow;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
private int _cooldown;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }
public StandardDeviationChannelStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
protected override void OnReseted()
{
base.OnReseted();
_fast = null; _slow = null;
_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fast = new ExponentialMovingAverage { Length = FastPeriod };
_slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
subscription.Bind(_fast, _slow, ProcessCandle);
subscription.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished) return;
if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }
var close = candle.ClosePrice;
var step = Security?.PriceStep ?? 1m;
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }
_prevFast = fastValue; _prevSlow = slowValue;
}
}
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 standard_deviation_channel_strategy(Strategy):
def __init__(self):
super(standard_deviation_channel_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 14) \
.SetDisplay("Fast Period", "Fast MA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 50) \
.SetDisplay("Slow Period", "Slow MA period", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200) \
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 400) \
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def stop_loss_points(self):
return self._stop_loss_points.Value
@property
def take_profit_points(self):
return self._take_profit_points.Value
def OnReseted(self):
super(standard_deviation_channel_strategy, self).OnReseted()
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(standard_deviation_channel_strategy, self).OnStarted2(time)
self._fast = ExponentialMovingAverage()
self._fast.Length = self.fast_period
self._slow = ExponentialMovingAverage()
self._slow.Length = self.slow_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(5)))
subscription.Bind(self._fast, self._slow, self._process_candle)
subscription.Start()
def _process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_value)
slow_val = float(slow_value)
if not self._fast.IsFormed or not self._slow.IsFormed:
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
if self.Position > 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close <= self._entry_price - self.stop_loss_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close >= self._entry_price + self.take_profit_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close >= self._entry_price + self.stop_loss_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close <= self._entry_price - self.take_profit_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 100
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return standard_deviation_channel_strategy()