Diese Strategie ist eine C#-Konvertierung des MetaTrader-Experten Exp_XWPR_Histogram_Vol. Sie handelt auf den Farbänderungen des
benutzerdefinierten Indikators XWPR Histogramm Vol, der den Williams %R-Oszillator mit dem Kerzenvolumen multipliziert und das Ergebnis glättet. Der
Port behält das ursprüngliche Zwei-Slot-Geldmanagement-Schema (primäres und sekundäres Volumen) bei und reproduziert dieselben farbgesteuerten
Einstiegs- und Ausstiegsregeln unter Verwendung der StockSharp-High-Level-API.
Der Algorithmus verarbeitet nur abgeschlossene Kerzen. Bei jeder neuen Kerze prüft er die Histogrammfarbe eine konfigurierbare Anzahl von Kerzen
zurück in der Vergangenheit und reagiert, wenn die Farbübergänge die bullischen oder bärischen Schwellenwerte des Indikators überschreiten.
Indikatorlogik
Williams %R (WprPeriod) wird um +50 verschoben und mit dem ausgewählten Kerzenvolumen (VolumeMode) multipliziert.
Sowohl der gewichtete Williams %R als auch das Rohvolumen durchlaufen identische Glättungsfilter (SmoothingMethod,
SmoothingLength, SmoothingPhase).
Vier dynamische Niveaus werden aus dem geglätteten Volumen abgeleitet: HighLevel2, HighLevel1, LowLevel1 und LowLevel2.
Histogrammfarben entsprechen den durch diese Niveaus definierten Zonen:
0 – Histogramm über HighLevel2 (stark bullisch).
1 – Histogramm zwischen HighLevel1 und HighLevel2 (moderat bullisch).
2 – Histogramm zwischen LowLevel1 und HighLevel1 (neutral).
3 – Histogramm zwischen LowLevel2 und LowLevel1 (moderat bärisch).
4 – Histogramm unter LowLevel2 (stark bärisch).
Signalregeln
Die Strategie liest zwei historische Farben pro Auswertung: Balken SignalBar + 1 (älter) und Balken SignalBar (neuer).
Primäres Long öffnen (Volumen = PrimaryVolume) wenn die ältere Balkenfarbe 1 ist und die neuere Balkenfarbe sich zu 2, 3 oder
4 bewegt. Die Bewegung fordert gleichzeitig das Schließen von Short-Positionen an.
Sekundäres Long öffnen (Volumen = SecondaryVolume) wenn die ältere Balkenfarbe 0 ist und die neuere Balkenfarbe zu etwas
anderem als 0 wird. Das gleiche Signal schließt auch Shorts.
Primäres Short öffnen (Volumen = PrimaryVolume) wenn die ältere Balkenfarbe 3 ist und die neuere Balkenfarbe auf 0, 1
oder 2 steigt, während auch Longs geschlossen werden.
Sekundäres Short öffnen (Volumen = SecondaryVolume) wenn die ältere Balkenfarbe 4 ist und die neuere Balkenfarbe zu
0, 1, 2 oder 3 wird, wobei erneut Long-Exits erzwungen werden.
Longs schließen immer wenn die ältere Farbe 3 oder 4 (bärische Zone) ist.
Shorts schließen immer wenn die ältere Farbe 0 oder 1 (bullische Zone) ist.
Für jede Richtung werden zwei unabhängige Positionsslots geführt. Ein Signal löst nur eine Order aus, wenn der entsprechende Slot
derzeit inaktiv ist und das relevante Einstiegs-Flag (AllowLongEntry, AllowShortEntry) es erlaubt.
Risikomanagement
StopLossSteps und TakeProfitSteps werden über StartProtection in StockSharp-Schutzorders übersetzt. Die Werte werden
in Instrumenten-Preisschritten ausgedrückt.
DeviationSteps wird für Kompatibilität mit der MQL-Eingabeliste beibehalten. StockSharp-Marktorders verwenden es nicht.
Parameter
Name
Beschreibung
CandleType
Zeitrahmen für die dem Indikator zugeführten Kerzen.
PrimaryVolume, SecondaryVolume
Von den Level-1- und Level-2-Slots angewendete Volumina.
AllowLongEntry, AllowShortEntry
Öffnen neuer Long- oder Short-Positionen aktivieren.
AllowLongExit, AllowShortExit
Schließen von Long- oder Short-Exposure bei Ausstiegssignalen aktivieren.
StopLossSteps, TakeProfitSteps
Optionale Schutzabstände in Preisschritten (0 deaktiviert den jeweiligen Schutz).
DeviationSteps
Für Kompatibilität reserviert; hat keinen Einfluss auf StockSharp-Orders.
SignalBar
Anzahl geschlossener Kerzen zur Verschiebung der Signalauswertung (0 = letzte abgeschlossene Kerze).
WprPeriod
Rückschauperiode für die Williams %R-Berechnung.
VolumeMode
Wählt zwischen Tick-Anzahl (Tick) oder Realvolumen (Real) im Histogramm.
HighLevel2, HighLevel1
Multiplikatoren, die die oberen bullischen Schwellenwerte definieren.
LowLevel1, LowLevel2
Multiplikatoren, die die unteren bärischen Schwellenwerte definieren.
SmoothingMethod
Moving-Average-Typ für sowohl das Histogramm als auch das Basisvolumen.
SmoothingLength
Länge der Glättungsfilter.
SmoothingPhase
Phase für Jurik-basierte Glätter (von anderen Methoden ignoriert).
Verwendungshinweise
Die Strategie handelt ein einzelnes Wertpapier, das von GetWorkingSecurities() zurückgegeben wird, und verwendet Marktorders für alle Aktionen.
Signale werden einmal pro abgeschlossener Kerze ausgewertet. Der zusätzliche Historiebuffer verhindert doppelte Orders auf demselben Balken.
Die beiden Einstiegs-Slots agieren unabhängig. Deaktivieren Sie einen Slot, indem Sie das entsprechende Volumen auf 0 setzen oder das
Allow*Entry-Flag deaktivieren.
Die Konvertierung repliziert keine MetaTrader-Magic-Numbers oder Margin-Modi. Die Portfolio-Größenbestimmung wird vollständig durch die
Parameter PrimaryVolume und SecondaryVolume gesteuert.
namespace StockSharp.Samples.Strategies;
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
/// <summary>
/// Strategy converted from the MetaTrader expert Exp_XWPR_Histogram_Vol.
/// Computes a volume-weighted Williams %R histogram inline and trades on strong colour transitions.
/// </summary>
public class ExpXwprHistogramVolStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _wprPeriod;
private readonly StrategyParam<int> _smoothingLength;
private readonly StrategyParam<decimal> _highLevel2;
private readonly StrategyParam<decimal> _lowLevel2;
private readonly StrategyParam<int> _signalCooldownBars;
private WilliamsR _wpr;
private SimpleMovingAverage _histSma;
private SimpleMovingAverage _volSma;
private int? _prevColor;
private int _cooldownRemaining;
private DateTimeOffset? _lastEntryTime;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int WprPeriod { get => _wprPeriod.Value; set => _wprPeriod.Value = value; }
public int SmoothingLength { get => _smoothingLength.Value; set => _smoothingLength.Value = value; }
public decimal HighLevel2 { get => _highLevel2.Value; set => _highLevel2.Value = value; }
public decimal LowLevel2 { get => _lowLevel2.Value; set => _lowLevel2.Value = value; }
public int SignalCooldownBars { get => _signalCooldownBars.Value; set => _signalCooldownBars.Value = value; }
public ExpXwprHistogramVolStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Timeframe", "General");
_wprPeriod = Param(nameof(WprPeriod), 7)
.SetGreaterThanZero()
.SetDisplay("WPR Period", "Williams %R lookback", "Indicator");
_smoothingLength = Param(nameof(SmoothingLength), 5)
.SetGreaterThanZero()
.SetDisplay("Smoothing", "Smoothing length", "Indicator");
_highLevel2 = Param(nameof(HighLevel2), 17m)
.SetDisplay("High Level 2", "Strong bullish zone", "Indicator");
_lowLevel2 = Param(nameof(LowLevel2), -17m)
.SetDisplay("Low Level 2", "Strong bearish zone", "Indicator");
_signalCooldownBars = Param(nameof(SignalCooldownBars), 48)
.SetGreaterThanZero()
.SetDisplay("Signal Cooldown", "Bars to wait after a new entry", "Trading");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_wpr = null;
_histSma = null;
_volSma = null;
_prevColor = null;
_cooldownRemaining = 0;
_lastEntryTime = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevColor = null;
_cooldownRemaining = 0;
_lastEntryTime = null;
_wpr = new WilliamsR { Length = WprPeriod };
_histSma = new SimpleMovingAverage { Length = SmoothingLength };
_volSma = new SimpleMovingAverage { Length = SmoothingLength };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ProcessCandle).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
if (_cooldownRemaining > 0)
_cooldownRemaining--;
var wprValue = _wpr.Process(candle);
if (!wprValue.IsFormed)
return;
var wpr = wprValue.ToDecimal();
var volume = candle.TotalVolume > 0m ? candle.TotalVolume : 1m;
var histRaw = (wpr + 50m) * volume;
var histSmoothed = _histSma.Process(new DecimalIndicatorValue(_histSma, histRaw, candle.OpenTime) { IsFinal = true });
var volSmoothed = _volSma.Process(new DecimalIndicatorValue(_volSma, volume, candle.OpenTime) { IsFinal = true });
if (!histSmoothed.IsFormed || !volSmoothed.IsFormed)
return;
var baseline = volSmoothed.ToDecimal();
if (baseline == 0m)
return;
var hist = histSmoothed.ToDecimal();
var strongBullLevel = HighLevel2 * baseline;
var strongBearLevel = LowLevel2 * baseline;
var color = hist >= strongBullLevel ? 0 : hist <= strongBearLevel ? 4 : 2;
if (_prevColor == null)
{
_prevColor = color;
return;
}
var previousColor = _prevColor.Value;
_prevColor = color;
if (_cooldownRemaining > 0 || HasRecentEntry(candle))
return;
if (previousColor != 0 && color == 0 && Position <= 0)
{
var volumeToBuy = Volume + Math.Abs(Position);
BuyMarket(volumeToBuy);
_cooldownRemaining = SignalCooldownBars;
_lastEntryTime = candle.CloseTime != default ? candle.CloseTime : candle.OpenTime;
}
else if (previousColor != 4 && color == 4 && Position >= 0)
{
var volumeToSell = Volume + Math.Abs(Position);
SellMarket(volumeToSell);
_cooldownRemaining = SignalCooldownBars;
_lastEntryTime = candle.CloseTime != default ? candle.CloseTime : candle.OpenTime;
}
}
private bool HasRecentEntry(ICandleMessage candle)
{
if (!_lastEntryTime.HasValue)
return false;
var candleTime = candle.CloseTime != default ? candle.CloseTime : candle.OpenTime;
return candleTime.Date == _lastEntryTime.Value.Date;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import CandleIndicatorValue, SimpleMovingAverage, WilliamsR
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class exp_xwpr_histogram_vol_strategy(Strategy):
def __init__(self):
super(exp_xwpr_histogram_vol_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._wpr_period = self.Param("WprPeriod", 7) \
.SetDisplay("WPR Period", "Williams %R lookback", "Indicator")
self._smoothing_length = self.Param("SmoothingLength", 5) \
.SetDisplay("Smoothing", "Smoothing length", "Indicator")
self._high_level2 = self.Param("HighLevel2", Decimal(17)) \
.SetDisplay("High Level 2", "Strong bullish zone", "Indicator")
self._low_level2 = self.Param("LowLevel2", Decimal(-17)) \
.SetDisplay("Low Level 2", "Strong bearish zone", "Indicator")
self._signal_cooldown_bars = self.Param("SignalCooldownBars", 48) \
.SetDisplay("Signal Cooldown", "Bars to wait after a new entry", "Trading")
self._wpr = None
self._hist_sma = None
self._vol_sma = None
self._prev_color = None
self._cooldown_remaining = 0
@property
def CandleType(self):
return self._candle_type.Value
@property
def WprPeriod(self):
return self._wpr_period.Value
@property
def SmoothingLength(self):
return self._smoothing_length.Value
@property
def HighLevel2(self):
return self._high_level2.Value
@property
def LowLevel2(self):
return self._low_level2.Value
@property
def SignalCooldownBars(self):
return self._signal_cooldown_bars.Value
def OnReseted(self):
super(exp_xwpr_histogram_vol_strategy, self).OnReseted()
self._wpr = None
self._hist_sma = None
self._vol_sma = None
self._prev_color = None
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(exp_xwpr_histogram_vol_strategy, self).OnStarted2(time)
self._prev_color = None
self._cooldown_remaining = 0
self._wpr = WilliamsR()
self._wpr.Length = self.WprPeriod
self._hist_sma = SimpleMovingAverage()
self._hist_sma.Length = self.SmoothingLength
self._vol_sma = SimpleMovingAverage()
self._vol_sma.Length = self.SmoothingLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _on_process(self, candle):
if candle.State != CandleStates.Finished:
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
wpr_result = self._wpr.Process(CandleIndicatorValue(self._wpr, candle))
if not wpr_result.IsFormed:
return
wpr = wpr_result.Value
volume = candle.TotalVolume if candle.TotalVolume > Decimal(0) else Decimal(1)
hist_raw = (wpr + Decimal(50)) * volume
hist_smoothed = process_float(self._hist_sma, hist_raw, candle.OpenTime, True)
vol_smoothed = process_float(self._vol_sma, volume, candle.OpenTime, True)
if not hist_smoothed.IsFormed or not vol_smoothed.IsFormed:
return
baseline = vol_smoothed.Value
if baseline == Decimal(0):
return
hist = hist_smoothed.Value
strong_bull_level = self.HighLevel2 * baseline
strong_bear_level = self.LowLevel2 * baseline
if hist >= strong_bull_level:
color = 0
elif hist <= strong_bear_level:
color = 4
else:
color = 2
if self._prev_color is None:
self._prev_color = color
return
previous_color = self._prev_color
self._prev_color = color
if self._cooldown_remaining > 0:
return
if previous_color != 0 and color == 0 and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._cooldown_remaining = self.SignalCooldownBars
elif previous_color != 4 and color == 4 and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._cooldown_remaining = self.SignalCooldownBars
def CreateClone(self):
return exp_xwpr_histogram_vol_strategy()