Die ATR-Normalisiertes-Histogramm-Strategie reproduziert das Verhalten des MetaTrader-Experten Exp_ATR_Normalize_Histogram innerhalb von StockSharp. Das System beobachtet das normalisierte Verhältnis zwischen dem geglätteten Abstand vom Schlusskurs zum Tief und dem geglätteten True Range. Farbänderungen des Histogramms steuern sowohl Ein- als auch Ausstiege und emulieren die Multi-Buffer-Logik der ursprünglichen MQL5-Implementierung.
Indikatorberechnung
Für jede abgeschlossene Kerze berechnet die Strategie:
diff = Close − Low.
range = max(High, vorheriger Close) − min(Low, vorheriger Close).
Jede Reihe wird unabhängig mit den gewählten Methoden und Längen geglättet. Fünf Methoden sind verfügbar: Simple, Exponential, Smoothed (RMA), Weighted und Jurik. Nicht unterstützte MQL-Methoden (JurX, Parabolic, T3, VIDYA, AMA) fallen auf den einfachen gleitenden Durchschnitt zurück.
Der normalisierte Histogrammwert wird berechnet als
Schwellenwerte unterteilen das Histogramm in fünf Bänder. Übergänge zwischen Bändern spiegeln den Farb-Buffer wider, der vom MQL-Indikator erzeugt wird.
Signallogik
Einstiegsfilter – SignalBar wählt aus, welcher historische Balken ausgewertet werden soll (Standard 1, der zuletzt geschlossene Balken). Die Strategie vergleicht die Farbe dieses Balkens mit dem vorherigen:
Ein Übergang vom bullischen Extrem (Farbe 0) zu einer anderen Farbe eröffnet eine Long-Position, wenn Long-Trades aktiviert sind.
Ein Übergang vom bärischen Extrem (Farbe 4) zu einer anderen Farbe eröffnet eine Short-Position, wenn Short-Trades aktiviert sind.
Ausstiegsfilter – die Farbe des vorherigen Balkens allein reicht aus, um Positionen zu schließen:
Farbe 0 schließt Short-Positionen, wenn Short-Exits aktiviert sind.
Farbe 4 schließt Long-Positionen, wenn Long-Exits aktiviert sind.
Ausstiege werden vor neuen Einstiegen verarbeitet, damit die Strategie niemals überlappende Trades hält.
Risikomanagement
Die Strategie verfolgt den letzten Ausführungspreis und erzwingt optional Schutz-Stops und Ziele in Instrumentpunkten. Die Konvertierung verwendet Security.PriceStep und entspricht damit dem "Punkte"-Konzept des ursprünglichen Experten. Wenn eines der Limits innerhalb der Kerze erreicht wird, wird die Position sofort geschlossen und die Handelsrichtung kann beim folgenden Signal wechseln.
Parameter
CandleType – Zeitrahmen für die Berechnung.
FirstSmoothingMethod / SecondSmoothingMethod – Glättungstyp für diff- und range-Streams.
FirstLength / SecondLength – Perioden für die Glätter.
SignalBar – Versatz für die Buffer-Auswertung (Minimum 1).
EnableBuyEntries, EnableSellEntries, EnableBuyExits, EnableSellExits – Schalter zur Verwaltung der vier Handelsrichtungen.
TradeVolume – Basis-Ordergröße. Die Strategie gleicht vorhandene Exposition automatisch aus, wenn die Richtung gewechselt wird.
StopLossPoints, TakeProfitPoints – optionale Schutzabstände in Punkten; auf null setzen, um zu deaktivieren.
Hinweise und Unterschiede zur MQL-Version
Beide Glättungsstufen sind unabhängig konfigurierbar, aber nur die fünf StockSharp-Implementierungen für gleitende Durchschnitte sind verfügbar. Wenn eine andere MQL-Methode gewählt wird, verwendet die Strategie den einfachen gleitenden Durchschnitt mit unveränderter Länge.
Die SignalBar-Logik folgt dem Buffer-Versatz aus CopyBuffer, sodass größere Versatze den gewählten Balken weiterhin mit seinem unmittelbaren Vorgänger vergleichen.
Die Geldmanagement-Parameter des ursprünglichen Experten (MM, MMMode, Deviation) werden auf einen einzigen TradeVolume-Parameter vereinfacht. Die Orderausführung erfolgt zum Marktpreis mit optionalem Stop/Ziel-Monitoring.
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()