Estrategia de reversión de momentum basada en el clásico oscilador Williams %R de Larry Williams. El sistema espera a que el oscilador alcance lecturas extremas de sobreventa o sobrecompra y luego revierte la posición en la siguiente barra completada. El port de StockSharp mantiene el carácter discrecional de la implementación original de MetaTrader, exponiendo cada ajuste importante como un parámetro.
Visión general
Categoría: Estrategia de oscilador de reversión a la media.
Mercado: Cualquier instrumento líquido que ofrezca datos de velas estables (pares de forex, futuros de índices, pares spot de criptomonedas).
Marco temporal: Configurable mediante CandleType. Por defecto velas de 1 hora, coincidiendo con el ejemplo de uso original.
Dirección: Largo y corto. El motor siempre mantiene como máximo una posición y gira cuando aparece la señal opuesta.
Indicador: Williams %R con longitud de lookback y niveles de umbral configurables.
Cómo Funciona
Se suscribe al feed de velas seleccionado y calcula Williams %R en cada vela terminada.
Usa el nivel de sobreventa predeterminado de -75 y el nivel de sobrecompra de -25 (los valores son negativos debido a la escala del oscilador).
Cuando %R cae por debajo del nivel de sobreventa, la estrategia entra o revierte a una posición larga.
Cuando %R sube por encima del nivel de sobrecompra, la estrategia entra o revierte a una posición corta.
Las órdenes se dimensionan con Volume + Math.Abs(Position) de modo que una reversión cierra la posición existente y abre la nueva en una única orden de mercado.
No se utiliza stop-loss ni take-profit explícito. El riesgo se controla mediante los niveles del indicador y el marco temporal elegido.
Cada acción se registra mediante LogInfo, lo que facilita auditar las operaciones en la GUI de StockSharp o en los archivos de log.
Parámetros
WilliamsPeriod: Número de velas utilizadas para calcular el oscilador. Valores más altos suavizan la señal, valores más bajos reaccionan más rápido.
OverboughtLevel: Umbral que define cuándo se considera que el mercado está sobrecomprado (por defecto -25). Se puede optimizar.
OversoldLevel: Umbral que define cuándo se considera que el mercado está sobrevendido (por defecto -75). Se puede optimizar.
CandleType: Tipo de vela y marco temporal aplicado a todos los cálculos. Funciona con marcos de tiempo, velas de volumen o barras de rango.
Volume (heredado de Strategy): Define el tamaño base de la orden. Ajustar según el tamaño de la cuenta y el apetito de riesgo.
Reglas de Trading
Entrada larga: Se activa cuando %R <= OversoldLevel y la posición actual es plana o corta.
Entrada corta: Se activa cuando %R >= OverboughtLevel y la posición actual es plana o larga.
Salida: Realizada implícitamente por la orden de reversión cuando aparece una señal opuesta.
Gestión de posición: Siempre una única posición abierta. El algoritmo no hace piramidación ni escala de salida.
Notas Adicionales
Funciona mejor en mercados laterales o de tendencia lenta donde los osciladores pueden oscilar entre extremos.
Se recomienda combinar la estrategia con controles de riesgo externos (stops de patrimonio, filtros de sesión) para el trading en vivo.
La implementación incluye renderizado de gráficos: el área principal muestra velas y operaciones, mientras que un panel secundario traza Williams %R.
Diseñada para mayor investigación: cada parámetro admite optimización dentro de los optimizadores de StockSharp.
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()