Momentum-Umkehrstrategie basierend auf dem klassischen Williams %R Oszillator von Larry Williams. Das System wartet, bis der Oszillator extreme Überverkauft- oder Überkauft-Lesungen erreicht, und kehrt dann die Position bei der nächsten abgeschlossenen Kerze um. Der StockSharp-Port behält den diskretionären Charakter der ursprünglichen MetaTrader-Implementierung bei und legt alle wichtigen Einstellungen als Parameter offen.
Zeitrahmen: Konfigurierbar über CandleType. Standard: 1-Stunden-Kerzen, passend zum ursprünglichen Anwendungsbeispiel.
Richtung: Long und Short. Die Engine hält stets höchstens eine Position und dreht um, wenn das entgegengesetzte Signal erscheint.
Indikator: Williams %R mit konfigurierbarer Lookback-Länge und Schwellenwerten.
Funktionsweise
Abonniert den ausgewählten Kerzendaten-Feed und berechnet Williams %R für jede abgeschlossene Kerze.
Verwendet den Standard-Überverkauft-Level von -75 und den Überkauft-Level von -25 (Werte sind negativ aufgrund der Oszillatorskala).
Wenn %R unter den Überverkauft-Level fällt, eröffnet die Strategie eine Long-Position oder dreht dahin um.
Wenn %R über den Überkauft-Level steigt, eröffnet die Strategie eine Short-Position oder dreht dahin um.
Orders werden mit Volume + Math.Abs(Position) bemessen, sodass eine Umkehr die bestehende Position schließt und die neue in einer einzigen Market-Order eröffnet.
Es werden kein expliziter Stop-Loss oder Take-Profit verwendet. Das Risiko wird durch die Indikatorlevels und den gewählten Zeitrahmen kontrolliert.
Jede Aktion wird über LogInfo protokolliert, was die Prüfung von Trades in der StockSharp-GUI oder in Log-Dateien erleichtert.
Parameter
WilliamsPeriod: Anzahl der Kerzen zur Berechnung des Oszillators. Höhere Werte glätten das Signal, niedrigere reagieren schneller.
OverboughtLevel: Schwellenwert, ab dem der Markt als überkauft gilt (Standard -25). Kann optimiert werden.
OversoldLevel: Schwellenwert, ab dem der Markt als überverkauft gilt (Standard -75). Kann optimiert werden.
CandleType: Kerzentyp und Zeitrahmen für alle Berechnungen. Funktioniert mit Zeitrahmen, Volumenkerzen oder Range-Bars.
Volume (von Strategy geerbt): Definiert die Basis-Ordergröße. An Kontogröße und Risikobereitschaft anpassen.
Handelsregeln
Long-Einstieg: Ausgelöst, wenn %R <= OversoldLevel und die aktuelle Position flat oder short ist.
Short-Einstieg: Ausgelöst, wenn %R >= OverboughtLevel und die aktuelle Position flat oder long ist.
Ausstieg: Implizit durch die Umkehrorder, wenn ein entgegengesetztes Signal erscheint.
Positionsmanagement: Immer eine offene Position. Der Algorithmus führt keine Pyramidisierung oder gestaffelte Ausstiege durch.
Zusätzliche Hinweise
Funktioniert am besten in seitwärtslaufenden oder langsam trendenden Märkten, in denen Oszillatoren zwischen Extremen pendeln können.
Es wird empfohlen, die Strategie mit externen Risikokontrollen (Eigenkapital-Stops, Session-Filter) für den Livehandel zu kombinieren.
Die Implementierung enthält Chart-Rendering: der Hauptbereich zeigt Kerzen und Trades, während ein zweites Panel Williams %R darstellt.
Für weitere Forschung konzipiert: Jeder Parameter unterstützt die Optimierung innerhalb der StockSharp-Optimierer.
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>
/// Williams %R reversal strategy.
/// </summary>
public class VladoStrategy : Strategy
{
private readonly StrategyParam<int> _williamsPeriod;
private readonly StrategyParam<decimal> _overboughtLevel;
private readonly StrategyParam<decimal> _oversoldLevel;
private readonly StrategyParam<DataType> _candleType;
private WilliamsR _williamsR;
public int WilliamsPeriod { get => _williamsPeriod.Value; set => _williamsPeriod.Value = value; }
public decimal OverboughtLevel { get => _overboughtLevel.Value; set => _overboughtLevel.Value = value; }
public decimal OversoldLevel { get => _oversoldLevel.Value; set => _oversoldLevel.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public VladoStrategy()
{
_williamsPeriod = Param(nameof(WilliamsPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("Williams Period", "Williams %R lookback.", "Indicator");
_overboughtLevel = Param(nameof(OverboughtLevel), -25m)
.SetDisplay("Overbought Level", "Williams %R short threshold.", "Signal");
_oversoldLevel = Param(nameof(OversoldLevel), -75m)
.SetDisplay("Oversold Level", "Williams %R long threshold.", "Signal");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe.", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_williamsR = new WilliamsR { Length = WilliamsPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(_williamsR, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
var williamsArea = CreateChartArea();
if (williamsArea != null)
DrawIndicator(williamsArea, _williamsR);
}
}
private void ProcessCandle(ICandleMessage candle, decimal value)
{
if (candle.State != CandleStates.Finished || !_williamsR.IsFormed)
return;
var signal = GetSignal(value, OversoldLevel, OverboughtLevel);
if (signal > 0 && Position <= 0m)
{
LogInfo("Williams %R {0}: LONG", value);
BuyMarket(Volume + Math.Abs(Position));
}
else if (signal < 0 && Position >= 0m)
{
LogInfo("Williams %R {0}: SHORT", value);
SellMarket(Volume + Math.Abs(Position));
}
}
internal static int GetSignal(decimal williamsR, decimal oversoldLevel, decimal overboughtLevel)
{
if (williamsR <= oversoldLevel)
return 1;
if (williamsR >= overboughtLevel)
return -1;
return 0;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import WilliamsR
from StockSharp.Algo.Strategies import Strategy
class vlado_strategy(Strategy):
def __init__(self):
super(vlado_strategy, self).__init__()
self._williams_period = self.Param("WilliamsPeriod", 14).SetGreaterThanZero()
self._overbought_level = self.Param("OverboughtLevel", -25.0)
self._oversold_level = self.Param("OversoldLevel", -75.0)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1)))
self._williams_r = None
def GetWorkingSecurities(self):
return [(self.Security, self._candle_type.Value)]
def OnStarted2(self, time):
super(vlado_strategy, self).OnStarted2(time)
self._williams_r = WilliamsR()
self._williams_r.Length = int(self._williams_period.Value)
subscription = self.SubscribeCandles(self._candle_type.Value)
subscription.Bind(self._williams_r, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
williams_area = self.CreateChartArea()
if williams_area is not None:
self.DrawIndicator(williams_area, self._williams_r)
def _process_candle(self, candle, williams_value):
if candle.State != CandleStates.Finished or not self._williams_r.IsFormed:
return
value = float(williams_value)
signal = self.get_signal(value, float(self._oversold_level.Value), float(self._overbought_level.Value))
if signal > 0 and self.Position <= 0:
self.LogInfo("Williams %R {0}: LONG", value)
self.BuyMarket(self.Volume + Math.Abs(self.Position))
elif signal < 0 and self.Position >= 0:
self.LogInfo("Williams %R {0}: SHORT", value)
self.SellMarket(self.Volume + Math.Abs(self.Position))
@staticmethod
def get_signal(williams_r, oversold_level, overbought_level):
if williams_r <= oversold_level:
return 1
if williams_r >= overbought_level:
return -1
return 0
def CreateClone(self):
return vlado_strategy()