Estrategia de Ruptura Williams %R
Esta estrategia busca ráfagas de momentum observando Williams %R en relación con su promedio histórico. Cuando el oscilador se empuja mucho más allá de las lecturas típicas, puede señalar el inicio de un movimiento fuerte.
Las pruebas indican un rendimiento anual promedio de aproximadamente 91%. Funciona mejor en el mercado de acciones.
Se abre una posición larga cuando %R sube por encima de la media más Multiplier veces una desviación estándar estimada. Se toma una posición corta cuando %R cae por debajo de la media menos el mismo multiplicador. La operación se cierra una vez que %R regresa hacia su media o se alcanza un stop-loss.
El enfoque está orientado a traders de ruptura que desean participar temprano en tendencias emergentes. El riesgo de posición se gestiona con un stop porcentual basado en el precio de entrada.
Detalles
- Criterios de entrada:
- Largo: %R > Avg + Multiplier * StdDev
- Corto: %R < Avg - Multiplier * StdDev
- Largo/Corto: Ambos lados.
- Criterios de salida:
- Largo: Salir cuando %R < Avg
- Corto: Salir cuando %R > Avg
- Stops: Sí, stop-loss porcentual.
- Valores predeterminados:
WilliamsRPeriod= 14AvgPeriod= 20Multiplier= 2.0mCandleType= TimeSpan.FromMinutes(5)
- Filtros:
- Categoría: Ruptura
- Dirección: Ambos
- Indicadores: Williams %R
- Stops: Sí
- Complejidad: Intermedio
- Marco temporal: Intradía
- Estacionalidad: No
- Redes neuronales: No
- Divergencia: No
- Nivel de riesgo: Medio
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()