A estratégia de Histograma ATR Normalizado reproduz o comportamento do especialista MetaTrader Exp_ATR_Normalize_Histogram dentro do StockSharp. O sistema observa a proporção normalizada entre a distância suavizada de fechamento ao mínimo e o range verdadeiro suavizado. Mudanças de cor do histograma impulsionam tanto as entradas quanto as saídas, emulando a lógica multi-buffer utilizada na implementação MQL5 original.
Cálculo do indicador
Para cada vela concluída, a estratégia calcula:
diff = Close − Low.
range = max(High, fechamento anterior) − min(Low, fechamento anterior).
Cada série é suavizada independentemente com os métodos e comprimentos selecionados. Cinco métodos estão disponíveis: Simple, Exponential, Smoothed (RMA), Weighted e Jurik. Métodos MQL não suportados (JurX, Parabolic, T3, VIDYA, AMA) recorrem à média móvel simples.
O valor normalizado do histograma é calculado como
Os limiares dividem o histograma em cinco bandas. A transição entre bandas espelha o buffer de cor produzido pelo indicador MQL.
Lógica de sinais
Filtro de entrada – SignalBar seleciona qual barra histórica deve ser avaliada (padrão 1, a última barra fechada). A estratégia compara a cor dessa barra com a anterior:
Uma transição do extremo altista (cor 0) para qualquer outra cor abre uma posição comprada quando operações compradas estão habilitadas.
Uma transição do extremo baixista (cor 4) para qualquer outra cor abre uma posição vendida quando operações vendidas estão habilitadas.
Filtro de saída – a cor da barra anterior por si só é suficiente para fechar posições:
A cor 0 fecha posições vendidas se as saídas vendidas estiverem habilitadas.
A cor 4 fecha posições compradas se as saídas compradas estiverem habilitadas.
As saídas são processadas antes de qualquer nova entrada para que a estratégia nunca mantenha operações sobrepostas.
Gestão de risco
A estratégia mantém o registro do último preço de execução e opcionalmente aplica stops de proteção e metas medidos em pontos do instrumento. A conversão usa Security.PriceStep, correspondendo ao conceito de "pontos" do especialista original. Quando qualquer limite é atingido dentro da barra, a posição é fechada imediatamente e a direção da operação pode mudar no sinal seguinte.
Parâmetros
CandleType – período usado para o cálculo.
FirstSmoothingMethod / SecondSmoothingMethod – tipo de suavização para os fluxos diff e range.
FirstLength / SecondLength – períodos para os suavizadores.
HighLevel, MiddleLevel, LowLevel – limiares do histograma (padrão 60/50/40).
SignalBar – deslocamento para avaliação do buffer (mínimo 1).
EnableBuyEntries, EnableSellEntries, EnableBuyExits, EnableSellExits – interruptores para gerenciar as quatro direções de operação.
TradeVolume – tamanho de ordem base. A estratégia compensa automaticamente a exposição existente ao mudar de direção.
StopLossPoints, TakeProfitPoints – distâncias de proteção opcionais em pontos; definir como zero para desabilitar.
Notas e diferenças em relação à versão MQL
Ambos os estágios de suavização são configuráveis independentemente, mas apenas as cinco implementações de média móvel do StockSharp estão disponíveis. Quando outro método MQL é selecionado, a estratégia usa a média móvel simples mantendo o comprimento.
A lógica de SignalBar segue o deslocamento de buffer usado em CopyBuffer, portanto deslocamentos maiores ainda comparam a barra escolhida com seu predecessor imediato.
Os parâmetros de gestão de dinheiro do especialista original (MM, MMMode, Deviation) são simplificados para um único parâmetro TradeVolume. A execução de ordens ocorre a mercado com monitoramento opcional de stop/alvo.
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;
/// <summary>
/// Strategy based on normalized ATR histogram transitions (simplified).
/// Uses ATR to measure volatility and trades on regime changes.
/// </summary>
public class AtrNormalizeHistogramStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _atrLength;
private readonly StrategyParam<int> _emaLength;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int AtrLength
{
get => _atrLength.Value;
set => _atrLength.Value = value;
}
public int EmaLength
{
get => _emaLength.Value;
set => _emaLength.Value = value;
}
public AtrNormalizeHistogramStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe", "General");
_atrLength = Param(nameof(AtrLength), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Length", "ATR period", "Indicators");
_emaLength = Param(nameof(EmaLength), 20)
.SetGreaterThanZero()
.SetDisplay("EMA Length", "EMA period for trend", "Indicators");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var atr = new AverageTrueRange { Length = AtrLength };
var ema = new ExponentialMovingAverage { Length = EmaLength };
decimal prevAtr = 0;
bool hasPrev = false;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(atr, ema, (ICandleMessage candle, decimal atrValue, decimal emaValue) =>
{
if (candle.State != CandleStates.Finished)
return;
if (!hasPrev)
{
prevAtr = atrValue;
hasPrev = true;
return;
}
if (!IsFormedAndOnlineAndAllowTrading())
{
prevAtr = atrValue;
return;
}
var price = candle.ClosePrice;
// ATR expanding + price above EMA = bullish breakout
if (atrValue > prevAtr * 1.1m && price > emaValue && Position <= 0)
{
BuyMarket();
}
// ATR expanding + price below EMA = bearish breakout
else if (atrValue > prevAtr * 1.1m && price < emaValue && Position >= 0)
{
SellMarket();
}
prevAtr = atrValue;
})
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ema);
DrawOwnTrades(area);
}
}
}
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 AverageTrueRange, ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class atr_normalize_histogram_strategy(Strategy):
def __init__(self):
super(atr_normalize_histogram_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._atr_length = self.Param("AtrLength", 14) \
.SetDisplay("ATR Length", "ATR period", "Indicators")
self._ema_length = self.Param("EmaLength", 20) \
.SetDisplay("EMA Length", "EMA period for trend", "Indicators")
self._prev_atr = 0.0
self._has_prev = False
@property
def CandleType(self):
return self._candle_type.Value
@property
def AtrLength(self):
return self._atr_length.Value
@property
def EmaLength(self):
return self._ema_length.Value
def OnReseted(self):
super(atr_normalize_histogram_strategy, self).OnReseted()
self._prev_atr = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(atr_normalize_histogram_strategy, self).OnStarted2(time)
self._prev_atr = 0.0
self._has_prev = False
atr = AverageTrueRange()
atr.Length = self.AtrLength
ema = ExponentialMovingAverage()
ema.Length = self.EmaLength
subscription = self.SubscribeCandles(self.CandleType)
subscription \
.Bind(atr, ema, self._on_process) \
.Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ema)
self.DrawOwnTrades(area)
def _on_process(self, candle, atr_value, ema_value):
if candle.State != CandleStates.Finished:
return
av = float(atr_value)
ev = float(ema_value)
if not self._has_prev:
self._prev_atr = av
self._has_prev = True
return
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_atr = av
return
close = float(candle.ClosePrice)
if av > self._prev_atr * 1.1 and close > ev and self.Position <= 0:
self.BuyMarket()
elif av > self._prev_atr * 1.1 and close < ev and self.Position >= 0:
self.SellMarket()
self._prev_atr = av
def CreateClone(self):
return atr_normalize_histogram_strategy()