La Estrategia de umbral %R de Vlado Williams es una conversión directa del MetaTrader 4 asesor experto Vlado_www_forex-instruments_info.mq4. El robot original intercambia un único oscilador Williams %R y cambia su exposición al mercado cada vez que el indicador cruza un nivel definido por el usuario. Este puerto StockSharp reproduce el mismo comportamiento de cambio de régimen al tiempo que expone cada valor ajustable como un parámetro de estrategia para la optimización y el control de la interfaz de usuario.
Conceptos clave
Intercambia la dirección del oscilador Williams %R en relación con un umbral (predeterminado -50).
Mantiene como máximo una posición de mercado a la vez y se revierte solo después de cerrar la operación anterior.
Tamaño de posición opcional basado en el riesgo que imita la MetaTrader fórmula de administración de dinero AccountFreeMargin * MaximumRisk / price.
Funciona con cualquier período de vela a través del parámetro CandleType (barras predeterminadas de 15 minutos).
Lógica de trading
Suscríbase al flujo de velas configurado y calcule un Williams %R de longitud WprLength (predeterminado 100).
Cuando Williams %R sube por encima de WprLevel, la estrategia marca un sesgo alcista:
Si no hay ninguna posición abierta y la operación anterior no fue larga, envíe una orden de compra de mercado.
Si existe una posición corta, se cierra inmediatamente; Se consideran nuevas posiciones largas en la siguiente vela después de que la posición sea plana.
Cuando el Williams %R cae por debajo de WprLevel, el sesgo cambia a bajista:
Si no hay ninguna posición abierta y la operación anterior no fue corta, envíe una orden de venta de mercado.
Si existe una posición larga, se aplana de inmediato.
El tamaño de la posición está determinado por CalculateOrderVolume:
Cuando UseRiskMoneyManagement es verdadero, la estrategia estima el volumen negociable a partir del valor actual de la cartera: Portfolio.CurrentValue × MaximumRiskPercent ÷ 100 ÷ ClosePrice.
De lo contrario, se utiliza la base Strategy.Volume.
Los lotes resultantes se alinean con el instrumento VolumeStep y se sujetan con MinVolume/MaxVolume si estos límites están disponibles.
La estrategia evita intencionalmente abrir una posición de reversión en la misma vela que desencadenó la salida, coincidiendo con el flujo EA original (CheckForClose se ejecuta antes de CheckForOpen).
Notas de conversión
Los valores predeterminados de administración del dinero siguen el script MT4: MaximumRiskPercent comienza en 10, coincidiendo con la constante MaximumRisk = 10 original que apuntaba aproximadamente a un mini lote por operación.
El parámetro MetaTrader shift (cambio de indicador) siempre es cero en el archivo fuente; por lo tanto fue omitido.
Los argumentos de color MT4 (por ejemplo, Red, Blue) no tienen equivalente StockSharp y se ignoran.
No se requieren entradas de deslizamiento porque StockSharp órdenes de mercado ya utilizan el mejor precio actual.
Parámetros
Parámetro
Tipo
Predeterminado
Descripción
CandleType
DataType
plazo de 15 minutos
Plazo tanto para el cálculo de señales como para la activación de órdenes.
WprLength
int
100
Período retrospectivo del oscilador Williams %R.
WprLevel
decimal
-50
Umbral que separa los regímenes alcistas y bajistas.
UseRiskMoneyManagement
bool
false
Alterna el tamaño de la posición basado en el riesgo.
MaximumRiskPercent
decimal
10
Porcentaje de capital de la cartera desplegado por operación cuando la gestión de riesgos está activada.
Consejo: Combine la estrategia con StartProtection() o controles de riesgo externos si necesita un manejo automático de stop-loss. El EA original también se basaba en la supervisión manual y no definía paradas bruscas.
Pautas de uso
Adjunte la estrategia a un valor que exponga límites precisos de PriceStep, StepPrice, VolumeStep y de volumen para que el asistente de dimensionamiento de posiciones pueda normalizar los pedidos correctamente.
Establezca Volume en el tamaño de lote alternativo que desee. Se utilizará siempre que el capital de la cartera no esté disponible o UseRiskMoneyManagement esté deshabilitado.
Optimice WprLevel y WprLength para adaptar el sistema a diferentes mercados. Los niveles estrechos (por ejemplo, -20 / -80) hacen que la estrategia sea más selectiva, mientras que los umbrales amplios (-50) garantizan que casi siempre se invierta.
La estrategia sigue la tendencia: se revertirá con frecuencia en condiciones variables. Considere combinarlo con filtros como comprobaciones de tendencias con períodos de tiempo más altos o umbrales de volatilidad cuando sea necesario.
Diferencias frente a la versión MetaTrader
Utiliza suscripciones de velas y enlaces de indicadores dla API de alto nivel de StockSharp; no hay bucle de pedidos manual ni escaneo del historial.
El tamaño del riesgo depende de Portfolio.CurrentValue. Cuando falta la valoración de la cuenta, la lógica vuelve al Volume estático, que coincide con el comportamiento de MT4 donde mm=0 forzó un tamaño de lote fijo.
Todos los comentarios y descripciones de parámetros están en inglés para mantener la coherencia con las pautas del repositorio.
Lista de verificación de validación
✅ Archivo de estrategia compilado con las convenciones de plantilla de estrategia StockSharp (pestañas, espacio de nombres con ámbito de archivo, documento heredado XML).
✅ Parámetros creados a través de Param() y marcados para optimización cuando corresponda.
✅ Williams Valores %R consumidos a través de Bind, sin ningún acceso directo a GetValue().
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>
/// Port of the "Vlado" MetaTrader expert advisor that trades Williams %R level breakouts.
/// </summary>
public class VladoWilliamsPercentRangeStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _wprLength;
private readonly StrategyParam<decimal> _wprLevel;
private readonly StrategyParam<bool> _useRiskMoneyManagement;
private readonly StrategyParam<decimal> _maximumRiskPercent;
private bool _buySignal;
private bool _sellSignal;
private int _lastSignal;
private WilliamsR _williamsR;
/// <summary>
/// Initializes a new instance of the <see cref="VladoWilliamsPercentRangeStrategy"/> class.
/// </summary>
public VladoWilliamsPercentRangeStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Primary timeframe for the strategy", "General");
_wprLength = Param(nameof(WprLength), 100)
.SetGreaterThanZero()
.SetDisplay("Williams %R Period", "Lookback period for Williams %R", "Indicators")
;
_wprLevel = Param(nameof(WprLevel), -50m)
.SetDisplay("Williams %R Level", "Threshold that flips the bias", "Signals")
;
_useRiskMoneyManagement = Param(nameof(UseRiskMoneyManagement), false)
.SetDisplay("Risk Money Management", "Recalculate volume from equity before entries", "Risk")
;
_maximumRiskPercent = Param(nameof(MaximumRiskPercent), 10m)
.SetDisplay("Maximum Risk Percent", "Equity percentage used when sizing orders", "Risk")
;
}
/// <summary>
/// Candle type processed by the strategy.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Williams %R lookback length.
/// </summary>
public int WprLength
{
get => _wprLength.Value;
set => _wprLength.Value = value;
}
/// <summary>
/// Threshold that toggles bullish or bearish bias.
/// </summary>
public decimal WprLevel
{
get => _wprLevel.Value;
set => _wprLevel.Value = value;
}
/// <summary>
/// Enables risk based volume sizing similar to the MetaTrader version.
/// </summary>
public bool UseRiskMoneyManagement
{
get => _useRiskMoneyManagement.Value;
set => _useRiskMoneyManagement.Value = value;
}
/// <summary>
/// Fraction of the current equity used to size entries when risk management is enabled.
/// </summary>
public decimal MaximumRiskPercent
{
get => _maximumRiskPercent.Value;
set => _maximumRiskPercent.Value = value;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_williamsR = null;
_buySignal = false;
_sellSignal = false;
_lastSignal = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_williamsR = new WilliamsR
{
Length = WprLength
};
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_williamsR, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _williamsR);
}
}
private void ProcessCandle(ICandleMessage candle, decimal wprValue)
{
if (candle.State != CandleStates.Finished)
return;
UpdateSignals(wprValue);
if (Position != 0)
{
if (Position > 0 && _sellSignal)
{
// Exit long positions when the bearish Williams %R regime appears.
if (Position > 0) SellMarket(); else BuyMarket();
return;
}
if (Position < 0 && _buySignal)
{
// Exit short positions when the bullish Williams %R regime appears.
if (Position > 0) SellMarket(); else BuyMarket();
return;
}
return;
}
// No open position - evaluate fresh entries.
var volume = CalculateOrderVolume(candle.ClosePrice);
if (volume <= 0m)
return;
if (_sellSignal && _lastSignal != -1)
{
// Enter short once Williams %R falls below the chosen level.
SellMarket();
_lastSignal = -1;
return;
}
if (_buySignal && _lastSignal != 1)
{
// Enter long once Williams %R rises above the chosen level.
BuyMarket();
_lastSignal = 1;
}
}
private void UpdateSignals(decimal wprValue)
{
// Williams %R values are negative: less negative indicates bullish momentum.
if (wprValue > WprLevel)
{
_buySignal = true;
_sellSignal = false;
}
else if (wprValue < WprLevel)
{
_sellSignal = true;
_buySignal = false;
}
}
private decimal CalculateOrderVolume(decimal referencePrice)
{
var volume = Volume;
if (UseRiskMoneyManagement && MaximumRiskPercent > 0m && referencePrice > 0m)
{
var equity = Portfolio?.CurrentValue ?? 0m;
if (equity > 0m)
{
// Convert risk capital to volume using the latest close price as approximation.
volume = equity * (MaximumRiskPercent / 100m) / referencePrice;
}
}
return NormalizeVolume(volume);
}
private decimal NormalizeVolume(decimal volume)
{
var normalized = volume;
if (Security?.VolumeStep is decimal step && step > 0m)
{
var steps = decimal.Floor(normalized / step);
normalized = steps * step;
if (normalized <= 0m)
normalized = step;
}
if (Security?.MinVolume is decimal minVolume && minVolume > 0m && normalized < minVolume)
normalized = minVolume;
if (Security?.MaxVolume is decimal maxVolume && maxVolume > 0m && normalized > maxVolume)
normalized = maxVolume;
if (normalized <= 0m && volume > 0m)
normalized = volume;
return normalized;
}
}
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_williams_percent_range_strategy(Strategy):
"""Vlado Williams %R strategy. Trades level breakouts: goes long when WPR rises
above the threshold, short when it falls below. Exits on opposite signal."""
def __init__(self):
super(vlado_williams_percent_range_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Primary timeframe for the strategy", "General")
self._wpr_length = self.Param("WprLength", 100) \
.SetGreaterThanZero() \
.SetDisplay("Williams %R Period", "Lookback period for Williams %R", "Indicators")
self._wpr_level = self.Param("WprLevel", -50.0) \
.SetDisplay("Williams %R Level", "Threshold that flips the bias", "Signals")
self._buy_signal = False
self._sell_signal = False
self._last_signal = 0
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def WprLength(self):
return self._wpr_length.Value
@property
def WprLevel(self):
return self._wpr_level.Value
def OnReseted(self):
super(vlado_williams_percent_range_strategy, self).OnReseted()
self._buy_signal = False
self._sell_signal = False
self._last_signal = 0
def OnStarted2(self, time):
super(vlado_williams_percent_range_strategy, self).OnStarted2(time)
williams_r = WilliamsR()
williams_r.Length = self.WprLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(williams_r, self._process_candle).Start()
def _process_candle(self, candle, wpr_value):
if candle.State != CandleStates.Finished:
return
wpr = float(wpr_value)
wpr_level = float(self.WprLevel)
# Update signals based on Williams %R relative to threshold
if wpr > wpr_level:
self._buy_signal = True
self._sell_signal = False
elif wpr < wpr_level:
self._sell_signal = True
self._buy_signal = False
if self.Position != 0:
if self.Position > 0 and self._sell_signal:
# Exit long on bearish regime
self.SellMarket()
return
if self.Position < 0 and self._buy_signal:
# Exit short on bullish regime
self.BuyMarket()
return
return
# No open position - evaluate entries
if self._sell_signal and self._last_signal != -1:
self.SellMarket()
self._last_signal = -1
return
if self._buy_signal and self._last_signal != 1:
self.BuyMarket()
self._last_signal = 1
def CreateClone(self):
return vlado_williams_percent_range_strategy()