Estrategia Exp XWPR Histograma Vol Directo
Descripción general
Esta estrategia es un port de StockSharp del asesor experto de MetaTrader Exp_XWPR_Histogram_Vol_Direct. Reproduce el enfoque
original de ponderar los valores de Williams %R por volumen, suavizar el resultado, y abrir operaciones cuando la pendiente del histograma cambia
de color. Las órdenes se activan en velas completamente formadas y usan stop-loss y take-profit protectores opcionales medidos en pasos de precio.
Lógica central
- Calcular Williams %R en el marco temporal seleccionado.
- Desplazar el oscilador en +50, multiplicarlo por la fuente de volumen elegida (tick o real), y suavizar el flujo con una media móvil
configurable.
- Suavizar el volumen bruto con la misma media móvil para reconstruir las bandas del indicador (HighLevel2, HighLevel1, LowLevel1, LowLevel2).
- Rastrear el color de la pendiente del histograma: azul (
0) cuando el valor suavizado sube, magenta (1) cuando cae. La estrategia
mantiene un buffer de historial corto para comparar los últimos dos colores completados respetando el parámetro SignalShift.
- Ejecutar acciones cuando el color anterior cambia:
- Transición de color
0 → 1: cerrar cortos (si está habilitado) y opcionalmente abrir una nueva posición larga.
- Transición de color
1 → 0: cerrar largos (si está habilitado) y opcionalmente abrir una nueva posición corta.
La clasificación de zona (Neutral/Alcista/Bajista/Extrema) se registra por contexto pero no bloquea las operaciones, coincidiendo con el comportamiento del
asesor original que solo lee el buffer de color.
Parámetros
| Parámetro |
Descripción |
WilliamsPeriod |
Longitud de retrospección para Williams %R. |
HighLevel2, HighLevel1, LowLevel1, LowLevel2 |
Multiplicadores aplicados al volumen suavizado para reconstruir las bandas del indicador. |
SmoothingType |
Familia de media móvil usada tanto para el valor ponderado como para los flujos de volumen (SMA, EMA, SMMA, WMA, Hull, VWMA, DEMA, TEMA). |
SmoothingLength |
Longitud de la media móvil de suavizado. |
SignalShift |
Cuántas barras atrás leer el buffer de color (1 reproduce el predeterminado de MetaTrader). |
EnableLongEntries / EnableShortEntries |
Permitir o bloquear la apertura de posiciones largas/cortas. |
EnableLongExits / EnableShortExits |
Permitir o bloquear el cierre de posiciones largas/cortas. |
VolumeSource |
Elegir entre conteo de ticks o volumen real para la ponderación. |
StopLossPoints / TakeProfitPoints |
Objetivos protectores opcionales expresados en pasos de precio. |
CandleType |
Tipo de vela y marco temporal usado para análisis y trading. |
Use la propiedad base Volume de la estrategia para definir el tamaño de entrada. La reversión de posición se maneja enviando la cantidad absoluta
de posición más el tamaño de lote configurado, similar al asesor experto MQL.
Notas de uso
- La fase de suavizado (
MA_Phase en MetaTrader) no es compatible porque las medias móviles de StockSharp no exponen ese parámetro.
- Asegure que haya suficiente historial cargado para el marco temporal elegido para que las medias móviles estén completamente formadas antes de que comience el trading.
- La estrategia funciona en cualquier instrumento compatible con StockSharp; establezca
CandleType en la resolución deseada (por ejemplo,
marco temporal de 4 horas para coincidir con los valores predeterminados originales).
- La ponderación por volumen de tick requiere fuentes de datos que proporcionen conteos de tick dentro de los mensajes de vela. De lo contrario, cambie a volumen real.
Registro y visualización
La estrategia dibuja velas y el indicador Williams %R en el área de gráfico predeterminada. Las acciones de trading registran la zona detectada y el
valor del histograma suavizado para ayudar en la depuración y comparación con la implementación de referencia de MetaTrader.
namespace StockSharp.Samples.Strategies;
using System;
using System.Collections.Generic;
using StockSharp.Algo;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
/// <summary>
/// Direct Williams %R histogram strategy with volume-weighted smoothing.
/// Trades only on strong bullish and bearish zone flips.
/// </summary>
public class ExpXwprHistogramVolDirectStrategy : Strategy
{
private readonly StrategyParam<int> _williamsPeriod;
private readonly StrategyParam<int> _highLevel1;
private readonly StrategyParam<int> _lowLevel1;
private readonly StrategyParam<MovingAverageKinds> _smoothingType;
private readonly StrategyParam<int> _smoothingLength;
private readonly StrategyParam<bool> _enableLongEntries;
private readonly StrategyParam<bool> _enableShortEntries;
private readonly StrategyParam<bool> _enableLongExits;
private readonly StrategyParam<bool> _enableShortExits;
private readonly StrategyParam<VolumeSources> _volumeSource;
private readonly StrategyParam<int> _signalCooldownBars;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private readonly StrategyParam<DataType> _candleType;
private WilliamsR _williams;
private DecimalLengthIndicator _valueSmoother;
private DecimalLengthIndicator _volumeSmoother;
private int? _previousZone;
private int _cooldownRemaining;
public int WilliamsPeriod { get => _williamsPeriod.Value; set => _williamsPeriod.Value = value; }
public int HighLevel1 { get => _highLevel1.Value; set => _highLevel1.Value = value; }
public int LowLevel1 { get => _lowLevel1.Value; set => _lowLevel1.Value = value; }
public MovingAverageKinds SmoothingType { get => _smoothingType.Value; set => _smoothingType.Value = value; }
public int SmoothingLength { get => _smoothingLength.Value; set => _smoothingLength.Value = value; }
public bool EnableLongEntries { get => _enableLongEntries.Value; set => _enableLongEntries.Value = value; }
public bool EnableShortEntries { get => _enableShortEntries.Value; set => _enableShortEntries.Value = value; }
public bool EnableLongExits { get => _enableLongExits.Value; set => _enableLongExits.Value = value; }
public bool EnableShortExits { get => _enableShortExits.Value; set => _enableShortExits.Value = value; }
public VolumeSources VolumeSource { get => _volumeSource.Value; set => _volumeSource.Value = value; }
public int SignalCooldownBars { get => _signalCooldownBars.Value; set => _signalCooldownBars.Value = value; }
public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public ExpXwprHistogramVolDirectStrategy()
{
_williamsPeriod = Param(nameof(WilliamsPeriod), 14)
.SetRange(5, 200)
.SetDisplay("Williams %R Period", "Lookback for the Williams %R oscillator", "Indicator");
_highLevel1 = Param(nameof(HighLevel1), 1)
.SetRange(-200, 200)
.SetDisplay("High Level 1", "Bullish threshold", "Indicator");
_lowLevel1 = Param(nameof(LowLevel1), -1)
.SetRange(-200, 200)
.SetDisplay("Low Level 1", "Bearish threshold", "Indicator");
_smoothingType = Param(nameof(SmoothingType), MovingAverageKinds.Simple)
.SetDisplay("Smoothing Type", "Moving average type used for smoothing", "Indicator");
_smoothingLength = Param(nameof(SmoothingLength), 12)
.SetRange(2, 200)
.SetDisplay("Smoothing Length", "Moving average length", "Indicator");
_enableLongEntries = Param(nameof(EnableLongEntries), true)
.SetDisplay("Enable Long Entries", "Allow the strategy to open long positions", "Trading Rules");
_enableShortEntries = Param(nameof(EnableShortEntries), true)
.SetDisplay("Enable Short Entries", "Allow the strategy to open short positions", "Trading Rules");
_enableLongExits = Param(nameof(EnableLongExits), true)
.SetDisplay("Enable Long Exits", "Allow the strategy to close long positions", "Trading Rules");
_enableShortExits = Param(nameof(EnableShortExits), true)
.SetDisplay("Enable Short Exits", "Allow the strategy to close short positions", "Trading Rules");
_volumeSource = Param(nameof(VolumeSource), VolumeSources.Tick)
.SetDisplay("Volume Source", "Type of volume used for weighting", "Indicator");
_signalCooldownBars = Param(nameof(SignalCooldownBars), 5)
.SetRange(1, 200)
.SetDisplay("Signal Cooldown", "Bars to wait between new entries", "Trading Rules");
_stopLossPoints = Param(nameof(StopLossPoints), 1000)
.SetRange(0, 10000)
.SetDisplay("Stop Loss (ticks)", "Protective stop distance in price steps", "Risk Management");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 2000)
.SetRange(0, 10000)
.SetDisplay("Take Profit (ticks)", "Profit target distance in price steps", "Risk Management");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Candle type used for analysis", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_williams = null;
_valueSmoother = null;
_volumeSmoother = null;
_previousZone = null;
_cooldownRemaining = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_williams = new WilliamsR { Length = WilliamsPeriod };
_valueSmoother = CreateMovingAverage(SmoothingType, SmoothingLength);
_volumeSmoother = CreateMovingAverage(SmoothingType, SmoothingLength);
_previousZone = null;
_cooldownRemaining = 0;
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ProcessCandle).Start();
StartProtection(
takeProfit: new Unit(2, UnitTypes.Percent),
stopLoss: new Unit(1, UnitTypes.Percent)
);
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
if (_cooldownRemaining > 0)
_cooldownRemaining--;
var williamsValue = _williams.Process(candle);
if (!_williams.IsFormed)
return;
var wprValue = williamsValue.ToDecimal();
// Williams %R ranges from -100 to 0; shift to 0..100
var normalized = wprValue + 100m;
var bullishLevel = 80m;
var bearishLevel = 20m;
var zone = normalized >= bullishLevel ? 1 : normalized <= bearishLevel ? -1 : 0;
if (_previousZone == null)
{
_previousZone = zone;
return;
}
if (_previousZone.Value != zone && _cooldownRemaining == 0 && Position == 0)
{
if (zone > 0 && EnableLongEntries)
{
BuyMarket();
_cooldownRemaining = SignalCooldownBars;
}
else if (zone < 0 && EnableShortEntries)
{
SellMarket();
_cooldownRemaining = SignalCooldownBars;
}
}
_previousZone = zone;
}
private decimal GetWeightedVolume(ICandleMessage candle)
{
if (VolumeSource == VolumeSources.Tick && candle.TotalTicks is int ticks && ticks > 0)
return ticks;
return candle.TotalVolume > 0m ? candle.TotalVolume : 1m;
}
private static DecimalLengthIndicator CreateMovingAverage(MovingAverageKinds type, int length)
{
return type switch
{
MovingAverageKinds.Simple => new SMA { Length = length },
MovingAverageKinds.Exponential => new EMA { Length = length },
MovingAverageKinds.Smoothed => new SmoothedMovingAverage { Length = length },
MovingAverageKinds.Weighted => new WeightedMovingAverage { Length = length },
MovingAverageKinds.Hull => new HullMovingAverage { Length = length },
MovingAverageKinds.VolumeWeighted => new VolumeWeightedMovingAverage { Length = length },
MovingAverageKinds.DoubleExponential => new DoubleExponentialMovingAverage { Length = length },
MovingAverageKinds.TripleExponential => new TripleExponentialMovingAverage { Length = length },
_ => new SMA { Length = length },
};
}
public enum MovingAverageKinds
{
Simple,
Exponential,
Smoothed,
Weighted,
Hull,
VolumeWeighted,
DoubleExponential,
TripleExponential,
}
public enum VolumeSources
{
Tick,
Real,
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes
from StockSharp.Algo.Indicators import WilliamsR, CandleIndicatorValue
from StockSharp.Algo.Strategies import Strategy
class exp_xwpr_histogram_vol_direct_strategy(Strategy):
def __init__(self):
super(exp_xwpr_histogram_vol_direct_strategy, self).__init__()
self._williams_period = self.Param("WilliamsPeriod", 14) \
.SetDisplay("Williams %R Period", "Lookback for the Williams %R oscillator", "Indicator")
self._signal_cooldown_bars = self.Param("SignalCooldownBars", 5) \
.SetDisplay("Signal Cooldown", "Bars to wait between new entries", "Trading Rules")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Candle type used for analysis", "General")
self._williams = None
self._previous_zone = None
self._cooldown_remaining = 0
@property
def CandleType(self):
return self._candle_type.Value
@property
def WilliamsPeriod(self):
return self._williams_period.Value
@property
def SignalCooldownBars(self):
return self._signal_cooldown_bars.Value
def OnReseted(self):
super(exp_xwpr_histogram_vol_direct_strategy, self).OnReseted()
self._williams = None
self._previous_zone = None
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(exp_xwpr_histogram_vol_direct_strategy, self).OnStarted2(time)
self._williams = WilliamsR()
self._williams.Length = self.WilliamsPeriod
self._previous_zone = None
self._cooldown_remaining = 0
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._on_process).Start()
self.StartProtection(Unit(2, UnitTypes.Percent), Unit(1, UnitTypes.Percent))
def _on_process(self, candle):
if candle.State != CandleStates.Finished:
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
williams_value = self._williams.Process(CandleIndicatorValue(self._williams, candle))
if not self._williams.IsFormed:
return
wpr_value = float(williams_value)
normalized = wpr_value + 100.0
bullish_level = 80.0
bearish_level = 20.0
if normalized >= bullish_level:
zone = 1
elif normalized <= bearish_level:
zone = -1
else:
zone = 0
if self._previous_zone is None:
self._previous_zone = zone
return
if not self.IsFormedAndOnlineAndAllowTrading():
self._previous_zone = zone
return
if self._previous_zone != zone and self._cooldown_remaining == 0 and self.Position == 0:
if zone > 0:
self.BuyMarket()
self._cooldown_remaining = self.SignalCooldownBars
elif zone < 0:
self.SellMarket()
self._cooldown_remaining = self.SignalCooldownBars
self._previous_zone = zone
def CreateClone(self):
return exp_xwpr_histogram_vol_direct_strategy()