Williams %R-Ausbruch-Strategie
Diese Strategie sucht nach Momentum-Schüben, indem sie Williams %R im Verhältnis zu seinem historischen Durchschnitt beobachtet. Wenn der Oszillator weit über die typischen Werte hinausgeht, kann dies den Beginn einer starken Bewegung signalisieren.
Tests zeigen eine durchschnittliche jährliche Rendite von etwa 91%. Die Strategie funktioniert am besten am Aktienmarkt.
Eine Long-Position wird eröffnet, wenn %R über den Durchschnitt plus Multiplier mal eine geschätzte Standardabweichung steigt. Eine Short-Position wird eingegangen, wenn %R unter den Durchschnitt minus denselben Multiplikator fällt. Der Trade schließt, sobald %R in Richtung seines Durchschnitts zurückkehrt oder ein Stop-Loss getroffen wird.
Der Ansatz richtet sich an Ausbruchstrader, die früh an entstehenden Trends teilnehmen möchten. Das Positionsrisiko wird mit einem Prozentstopp auf Basis des Einstiegspreises gesteuert.
Details
- Einstiegskriterien:
- Long: %R > Avg + Multiplier * StdDev
- Short: %R < Avg - Multiplier * StdDev
- Long/Short: Beide Seiten.
- Ausstiegskriterien:
- Long: Ausstieg wenn %R < Avg
- Short: Ausstieg wenn %R > Avg
- Stops: Ja, prozentualer Stop-Loss.
- Standardwerte:
WilliamsRPeriod= 14AvgPeriod= 20Multiplier= 2.0mCandleType= TimeSpan.FromMinutes(5)
- Filter:
- Kategorie: Ausbruch
- Richtung: Beide
- Indikatoren: Williams %R
- Stops: Ja
- Komplexität: Mittel
- Zeitrahmen: Intraday
- Saisonalität: Nein
- Neuronale Netze: Nein
- Divergenz: Nein
- Risikolevel: Mittel
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy that trades on Williams %R breakouts.
/// When Williams %R crosses above the overbought level or below the oversold level,
/// it enters position in the corresponding direction. Exits when Williams %R
/// crosses back through its moving average.
/// </summary>
public class WilliamsRBreakoutStrategy : Strategy
{
private readonly StrategyParam<int> _williamsRPeriod;
private readonly StrategyParam<int> _avgPeriod;
private readonly StrategyParam<decimal> _overboughtLevel;
private readonly StrategyParam<decimal> _oversoldLevel;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<decimal> _stopLoss;
private WilliamsR _williamsR;
private SimpleMovingAverage _williamsRAverage;
private bool _prevInitialized;
private decimal _prevWilliamsRValue;
private decimal _prevWilliamsRAvgValue;
private int _cooldown;
/// <summary>
/// Williams %R period.
/// </summary>
public int WilliamsRPeriod
{
get => _williamsRPeriod.Value;
set => _williamsRPeriod.Value = value;
}
/// <summary>
/// Period for Williams %R average calculation.
/// </summary>
public int AvgPeriod
{
get => _avgPeriod.Value;
set => _avgPeriod.Value = value;
}
/// <summary>
/// Overbought level for Williams %R (e.g. -10).
/// </summary>
public decimal OverboughtLevel
{
get => _overboughtLevel.Value;
set => _overboughtLevel.Value = value;
}
/// <summary>
/// Oversold level for Williams %R (e.g. -90).
/// </summary>
public decimal OversoldLevel
{
get => _oversoldLevel.Value;
set => _oversoldLevel.Value = value;
}
/// <summary>
/// Candle type for strategy.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Stop-loss percentage.
/// </summary>
public decimal StopLoss
{
get => _stopLoss.Value;
set => _stopLoss.Value = value;
}
/// <summary>
/// Initialize <see cref="WilliamsRBreakoutStrategy"/>.
/// </summary>
public WilliamsRBreakoutStrategy()
{
_williamsRPeriod = Param(nameof(WilliamsRPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("Williams %R Period", "Period for Williams %R indicator", "Indicators")
.SetOptimize(10, 30, 2);
_avgPeriod = Param(nameof(AvgPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("Average Period", "Period for Williams %R average calculation", "Indicators")
.SetOptimize(10, 50, 5);
_overboughtLevel = Param(nameof(OverboughtLevel), -10m)
.SetDisplay("Overbought Level", "Williams %R overbought threshold", "Indicators");
_oversoldLevel = Param(nameof(OversoldLevel), -90m)
.SetDisplay("Oversold Level", "Williams %R oversold threshold", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_stopLoss = Param(nameof(StopLoss), 2.0m)
.SetGreaterThanZero()
.SetDisplay("Stop Loss %", "Stop Loss percentage", "Risk Management")
.SetOptimize(1.0m, 5.0m, 0.5m);
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevInitialized = false;
_prevWilliamsRValue = 0;
_prevWilliamsRAvgValue = 0;
_cooldown = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
// Create indicators
_williamsR = new WilliamsR { Length = WilliamsRPeriod };
_williamsRAverage = new SimpleMovingAverage { Length = AvgPeriod };
// Create subscription and bind Williams %R
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_williamsR, ProcessCandle)
.Start();
// Enable stop loss protection
StartProtection(
takeProfit: new Unit(0, UnitTypes.Absolute),
stopLoss: new Unit(StopLoss, UnitTypes.Percent)
);
// Create chart area for visualization
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _williamsR);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal wrValue)
{
if (candle.State != CandleStates.Finished)
return;
// Feed WR value through SMA to get the average (must set IsFinal for buffer to accumulate)
var input = new DecimalIndicatorValue(_williamsRAverage, wrValue, candle.ServerTime) { IsFinal = true };
var avgResult = _williamsRAverage.Process(input);
if (!_williamsRAverage.IsFormed)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var currentWilliamsRAvg = avgResult.ToDecimal();
if (!_prevInitialized)
{
_prevWilliamsRValue = wrValue;
_prevWilliamsRAvgValue = currentWilliamsRAvg;
_prevInitialized = true;
return;
}
// Cooldown between trades (minimum bars between signals)
if (_cooldown > 0)
{
_cooldown--;
_prevWilliamsRValue = wrValue;
_prevWilliamsRAvgValue = currentWilliamsRAvg;
return;
}
const int cooldownBars = 100;
// Williams %R breakout detection using crossover of extreme levels
// Williams %R crossing above overbought level from below = bullish breakout
if (_prevWilliamsRValue <= OverboughtLevel && wrValue > OverboughtLevel && Position <= 0)
{
BuyMarket(Volume + Math.Abs(Position));
_cooldown = cooldownBars;
}
// Williams %R crossing below oversold level from above = bearish breakout
else if (_prevWilliamsRValue >= OversoldLevel && wrValue < OversoldLevel && Position >= 0)
{
SellMarket(Volume + Math.Abs(Position));
_cooldown = cooldownBars;
}
// Exit long when Williams %R drops below the midpoint (-50)
else if (Position > 0 && _prevWilliamsRValue >= -50m && wrValue < -50m)
{
SellMarket(Math.Abs(Position));
_cooldown = cooldownBars;
}
// Exit short when Williams %R rises above the midpoint (-50)
else if (Position < 0 && _prevWilliamsRValue <= -50m && wrValue > -50m)
{
BuyMarket(Math.Abs(Position));
_cooldown = cooldownBars;
}
// Update previous values
_prevWilliamsRValue = wrValue;
_prevWilliamsRAvgValue = currentWilliamsRAvg;
}
}
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, Unit, UnitTypes, CandleStates
from StockSharp.Algo.Indicators import WilliamsR, SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
from datatype_extensions import *
from indicator_extensions import *
class williams_r_breakout_strategy(Strategy):
"""
Strategy that trades on Williams %R breakouts.
When Williams %R crosses above the overbought level or below the oversold level,
it enters position in the corresponding direction. Exits when Williams %R
crosses back through its moving average.
"""
def __init__(self):
super(williams_r_breakout_strategy, self).__init__()
self._williamsRPeriod = self.Param("WilliamsRPeriod", 14) \
.SetGreaterThanZero() \
.SetDisplay("Williams %R Period", "Period for Williams %R indicator", "Indicators") \
.SetCanOptimize(True) \
.SetOptimize(10, 30, 2)
self._avgPeriod = self.Param("AvgPeriod", 20) \
.SetGreaterThanZero() \
.SetDisplay("Average Period", "Period for Williams %R average calculation", "Indicators") \
.SetCanOptimize(True) \
.SetOptimize(10, 50, 5)
self._overboughtLevel = self.Param("OverboughtLevel", -10.0) \
.SetDisplay("Overbought Level", "Williams %R overbought threshold", "Indicators")
self._oversoldLevel = self.Param("OversoldLevel", -90.0) \
.SetDisplay("Oversold Level", "Williams %R oversold threshold", "Indicators")
self._candleType = self.Param("CandleType", tf(5)) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._stopLoss = self.Param("StopLoss", 2.0) \
.SetGreaterThanZero() \
.SetDisplay("Stop Loss %", "Stop Loss percentage", "Risk Management") \
.SetCanOptimize(True) \
.SetOptimize(1.0, 5.0, 0.5)
self._prevInitialized = False
self._prevWilliamsRValue = 0
self._prevWilliamsRAvgValue = 0
self._cooldown = 0
self._williamsR = None
self._williamsRAverage = None
@property
def WilliamsRPeriod(self):
return self._williamsRPeriod.Value
@WilliamsRPeriod.setter
def WilliamsRPeriod(self, value):
self._williamsRPeriod.Value = value
@property
def AvgPeriod(self):
return self._avgPeriod.Value
@AvgPeriod.setter
def AvgPeriod(self, value):
self._avgPeriod.Value = value
@property
def OverboughtLevel(self):
return self._overboughtLevel.Value
@OverboughtLevel.setter
def OverboughtLevel(self, value):
self._overboughtLevel.Value = value
@property
def OversoldLevel(self):
return self._oversoldLevel.Value
@OversoldLevel.setter
def OversoldLevel(self, value):
self._oversoldLevel.Value = value
@property
def CandleType(self):
return self._candleType.Value
@CandleType.setter
def CandleType(self, value):
self._candleType.Value = value
@property
def StopLoss(self):
return self._stopLoss.Value
@StopLoss.setter
def StopLoss(self, value):
self._stopLoss.Value = value
def GetWorkingSecurities(self):
return [(self.Security, self.CandleType)]
def OnReseted(self):
super(williams_r_breakout_strategy, self).OnReseted()
self._prevInitialized = False
self._prevWilliamsRValue = 0
self._prevWilliamsRAvgValue = 0
self._cooldown = 0
def OnStarted2(self, time):
super(williams_r_breakout_strategy, self).OnStarted2(time)
# Create indicators
self._williamsR = WilliamsR()
self._williamsR.Length = self.WilliamsRPeriod
self._williamsRAverage = SimpleMovingAverage()
self._williamsRAverage.Length = self.AvgPeriod
# Create subscription and bind Williams %R
subscription = self.SubscribeCandles(self.CandleType)
subscription.BindEx(self._williamsR, self.ProcessCandle).Start()
# Enable stop loss protection
self.StartProtection(
takeProfit=Unit(0, UnitTypes.Absolute),
stopLoss=Unit(self.StopLoss, UnitTypes.Percent)
)
# Create chart area for visualization
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, self._williamsR)
self.DrawOwnTrades(area)
def ProcessCandle(self, candle, wrValue):
if candle.State != CandleStates.Finished:
return
if not wrValue.IsFinal:
return
wrVal = float(wrValue)
# Feed WR value through SMA to get the average (must set IsFinal for buffer to accumulate)
avgResult = process_float(self._williamsRAverage, wrVal, candle.ServerTime, True)
if not self._williamsRAverage.IsFormed:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
currentWilliamsRAvg = float(avgResult)
if not self._prevInitialized:
self._prevWilliamsRValue = wrVal
self._prevWilliamsRAvgValue = currentWilliamsRAvg
self._prevInitialized = True
return
# Cooldown between trades (minimum bars between signals)
if self._cooldown > 0:
self._cooldown -= 1
self._prevWilliamsRValue = wrVal
self._prevWilliamsRAvgValue = currentWilliamsRAvg
return
cooldownBars = 100
# Williams %R breakout detection using crossover of extreme levels
# Williams %R crossing above overbought level from below = bullish breakout
if self._prevWilliamsRValue <= self.OverboughtLevel and wrVal > self.OverboughtLevel and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
self._cooldown = cooldownBars
# Williams %R crossing below oversold level from above = bearish breakout
elif self._prevWilliamsRValue >= self.OversoldLevel and wrVal < self.OversoldLevel and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
self._cooldown = cooldownBars
# Exit long when Williams %R drops below the midpoint (-50)
elif self.Position > 0 and self._prevWilliamsRValue >= -50.0 and wrVal < -50.0:
self.SellMarket(abs(self.Position))
self._cooldown = cooldownBars
# Exit short when Williams %R rises above the midpoint (-50)
elif self.Position < 0 and self._prevWilliamsRValue <= -50.0 and wrVal > -50.0:
self.BuyMarket(abs(self.Position))
self._cooldown = cooldownBars
# Update previous values
self._prevWilliamsRValue = wrVal
self._prevWilliamsRAvgValue = currentWilliamsRAvg
def CreateClone(self):
return williams_r_breakout_strategy()