Estrategia Exp XWPR Histograma Vol
Descripción general
Esta estrategia es una conversión en C# del experto de MetaTrader Exp_XWPR_Histogram_Vol. Opera en los cambios de color del
indicador personalizado XWPR Histograma Vol, que multiplica el oscilador Williams %R por el volumen de la vela y suaviza el resultado. El
port mantiene el esquema original de gestión de dinero de dos slots (volumen primario y secundario) y reproduce las mismas reglas de
entrada y salida basadas en color usando la API de alto nivel de StockSharp.
El algoritmo procesa solo velas finalizadas. En cada nueva barra, inspecciona el color del histograma un número configurable de barras
en el pasado y reacciona cuando las transiciones de color cruzan los umbrales alcistas o bajistas definidos por el indicador.
Lógica del indicador
- Williams %R (
WprPeriod) se desplaza en +50 y se multiplica por el volumen de vela seleccionado (VolumeMode).
- Tanto el Williams %R ponderado como el volumen bruto pasan por filtros de suavizado idénticos (
SmoothingMethod,
SmoothingLength, SmoothingPhase).
- Se derivan cuatro niveles dinámicos del volumen suavizado:
HighLevel2, HighLevel1, LowLevel1 y LowLevel2.
- Los colores del histograma corresponden a las zonas definidas por esos niveles:
- 0 – histograma por encima de
HighLevel2 (alcista fuerte).
- 1 – histograma entre
HighLevel1 y HighLevel2 (alcista moderado).
- 2 – histograma entre
LowLevel1 y HighLevel1 (neutral).
- 3 – histograma entre
LowLevel2 y LowLevel1 (bajista moderado).
- 4 – histograma por debajo de
LowLevel2 (bajista fuerte).
Reglas de señal
La estrategia lee dos colores históricos por evaluación: barra SignalBar + 1 (más antigua) y barra SignalBar (más reciente).
- Abrir largo primario (volumen =
PrimaryVolume) cuando el color de la barra más antigua es 1 y el color de la barra más nueva se mueve a 2, 3 o
4. El movimiento simultáneamente solicita el cierre de posiciones cortas.
- Abrir largo secundario (volumen =
SecondaryVolume) cuando el color de la barra más antigua es 0 y el color de la barra más nueva se convierte en
cualquier cosa distinta de 0. La misma señal también cierra cortos.
- Abrir corto primario (volumen =
PrimaryVolume) cuando el color de la barra más antigua es 3 y el color de la barra más nueva sube a 0, 1
o 2, mientras también cierra largos.
- Abrir corto secundario (volumen =
SecondaryVolume) cuando el color de la barra más antigua es 4 y el color de la barra más nueva se convierte en
0, 1, 2 o 3, nuevamente forzando salidas largas.
- Cerrar largos cuando el color más antiguo es
3 o 4 (zona bajista).
- Cerrar cortos cuando el color más antiguo es
0 o 1 (zona alcista).
Se mantienen dos slots de posición independientes para cada dirección. Una señal solo activa una orden si el slot correspondiente está
actualmente inactivo y el indicador de entrada relevante (AllowLongEntry, AllowShortEntry) lo permite.
Gestión de riesgo
StopLossSteps y TakeProfitSteps se traducen a órdenes protectoras de StockSharp a través de StartProtection. Los valores se
expresan en pasos de precio del instrumento.
DeviationSteps se conserva para compatibilidad con la lista de entradas MQL. Las órdenes de mercado de StockSharp no lo usan.
Parámetros
| Nombre |
Descripción |
CandleType |
Marco temporal usado para construir las velas suministradas al indicador. |
PrimaryVolume, SecondaryVolume |
Volúmenes aplicados por los slots de nivel uno y nivel dos. |
AllowLongEntry, AllowShortEntry |
Habilitar apertura de nuevas posiciones largas o cortas. |
AllowLongExit, AllowShortExit |
Habilitar cierre de exposición larga o corta cuando aparezcan señales de salida. |
StopLossSteps, TakeProfitSteps |
Distancias protectoras opcionales en pasos de precio (0 deshabilita la protección respectiva). |
DeviationSteps |
Reservado para compatibilidad; no tiene efecto en las órdenes de StockSharp. |
SignalBar |
Número de velas cerradas para desplazar la evaluación de señal (0 = última vela finalizada). |
WprPeriod |
Período de retrospección para el cálculo de Williams %R. |
VolumeMode |
Selecciona entre conteo de ticks (Tick) o volumen real (Real) en el histograma. |
HighLevel2, HighLevel1 |
Multiplicadores que definen los umbrales alcistas superiores. |
LowLevel1, LowLevel2 |
Multiplicadores que definen los umbrales bajistas inferiores. |
SmoothingMethod |
Tipo de media móvil usada tanto para el histograma como para el volumen de referencia. |
SmoothingLength |
Longitud de los filtros de suavizado. |
SmoothingPhase |
Fase enviada a suavizadores basados en Jurik (ignorada por otros métodos). |
Notas de uso
- La estrategia opera en un único valor devuelto por
GetWorkingSecurities() y usa órdenes de mercado para todas las acciones.
- Las señales se evalúan una vez por vela finalizada. El buffer de historial adicional evita órdenes duplicadas en la misma barra.
- Los dos slots de entrada actúan de forma independiente. Deshabilite un slot estableciendo el volumen correspondiente en
0 o deshabilitando el
indicador Allow*Entry.
- La conversión no replica los números mágicos de MetaTrader ni los modos de margen. El dimensionamiento de la cartera está completamente controlado por los
parámetros
PrimaryVolume y SecondaryVolume.
namespace StockSharp.Samples.Strategies;
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
/// <summary>
/// Strategy converted from the MetaTrader expert Exp_XWPR_Histogram_Vol.
/// Computes a volume-weighted Williams %R histogram inline and trades on strong colour transitions.
/// </summary>
public class ExpXwprHistogramVolStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _wprPeriod;
private readonly StrategyParam<int> _smoothingLength;
private readonly StrategyParam<decimal> _highLevel2;
private readonly StrategyParam<decimal> _lowLevel2;
private readonly StrategyParam<int> _signalCooldownBars;
private WilliamsR _wpr;
private SimpleMovingAverage _histSma;
private SimpleMovingAverage _volSma;
private int? _prevColor;
private int _cooldownRemaining;
private DateTimeOffset? _lastEntryTime;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int WprPeriod { get => _wprPeriod.Value; set => _wprPeriod.Value = value; }
public int SmoothingLength { get => _smoothingLength.Value; set => _smoothingLength.Value = value; }
public decimal HighLevel2 { get => _highLevel2.Value; set => _highLevel2.Value = value; }
public decimal LowLevel2 { get => _lowLevel2.Value; set => _lowLevel2.Value = value; }
public int SignalCooldownBars { get => _signalCooldownBars.Value; set => _signalCooldownBars.Value = value; }
public ExpXwprHistogramVolStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Timeframe", "General");
_wprPeriod = Param(nameof(WprPeriod), 7)
.SetGreaterThanZero()
.SetDisplay("WPR Period", "Williams %R lookback", "Indicator");
_smoothingLength = Param(nameof(SmoothingLength), 5)
.SetGreaterThanZero()
.SetDisplay("Smoothing", "Smoothing length", "Indicator");
_highLevel2 = Param(nameof(HighLevel2), 17m)
.SetDisplay("High Level 2", "Strong bullish zone", "Indicator");
_lowLevel2 = Param(nameof(LowLevel2), -17m)
.SetDisplay("Low Level 2", "Strong bearish zone", "Indicator");
_signalCooldownBars = Param(nameof(SignalCooldownBars), 48)
.SetGreaterThanZero()
.SetDisplay("Signal Cooldown", "Bars to wait after a new entry", "Trading");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_wpr = null;
_histSma = null;
_volSma = null;
_prevColor = null;
_cooldownRemaining = 0;
_lastEntryTime = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevColor = null;
_cooldownRemaining = 0;
_lastEntryTime = null;
_wpr = new WilliamsR { Length = WprPeriod };
_histSma = new SimpleMovingAverage { Length = SmoothingLength };
_volSma = new SimpleMovingAverage { Length = SmoothingLength };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ProcessCandle).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
if (_cooldownRemaining > 0)
_cooldownRemaining--;
var wprValue = _wpr.Process(candle);
if (!wprValue.IsFormed)
return;
var wpr = wprValue.ToDecimal();
var volume = candle.TotalVolume > 0m ? candle.TotalVolume : 1m;
var histRaw = (wpr + 50m) * volume;
var histSmoothed = _histSma.Process(new DecimalIndicatorValue(_histSma, histRaw, candle.OpenTime) { IsFinal = true });
var volSmoothed = _volSma.Process(new DecimalIndicatorValue(_volSma, volume, candle.OpenTime) { IsFinal = true });
if (!histSmoothed.IsFormed || !volSmoothed.IsFormed)
return;
var baseline = volSmoothed.ToDecimal();
if (baseline == 0m)
return;
var hist = histSmoothed.ToDecimal();
var strongBullLevel = HighLevel2 * baseline;
var strongBearLevel = LowLevel2 * baseline;
var color = hist >= strongBullLevel ? 0 : hist <= strongBearLevel ? 4 : 2;
if (_prevColor == null)
{
_prevColor = color;
return;
}
var previousColor = _prevColor.Value;
_prevColor = color;
if (_cooldownRemaining > 0 || HasRecentEntry(candle))
return;
if (previousColor != 0 && color == 0 && Position <= 0)
{
var volumeToBuy = Volume + Math.Abs(Position);
BuyMarket(volumeToBuy);
_cooldownRemaining = SignalCooldownBars;
_lastEntryTime = candle.CloseTime != default ? candle.CloseTime : candle.OpenTime;
}
else if (previousColor != 4 && color == 4 && Position >= 0)
{
var volumeToSell = Volume + Math.Abs(Position);
SellMarket(volumeToSell);
_cooldownRemaining = SignalCooldownBars;
_lastEntryTime = candle.CloseTime != default ? candle.CloseTime : candle.OpenTime;
}
}
private bool HasRecentEntry(ICandleMessage candle)
{
if (!_lastEntryTime.HasValue)
return false;
var candleTime = candle.CloseTime != default ? candle.CloseTime : candle.OpenTime;
return candleTime.Date == _lastEntryTime.Value.Date;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import CandleIndicatorValue, SimpleMovingAverage, WilliamsR
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class exp_xwpr_histogram_vol_strategy(Strategy):
def __init__(self):
super(exp_xwpr_histogram_vol_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._wpr_period = self.Param("WprPeriod", 7) \
.SetDisplay("WPR Period", "Williams %R lookback", "Indicator")
self._smoothing_length = self.Param("SmoothingLength", 5) \
.SetDisplay("Smoothing", "Smoothing length", "Indicator")
self._high_level2 = self.Param("HighLevel2", Decimal(17)) \
.SetDisplay("High Level 2", "Strong bullish zone", "Indicator")
self._low_level2 = self.Param("LowLevel2", Decimal(-17)) \
.SetDisplay("Low Level 2", "Strong bearish zone", "Indicator")
self._signal_cooldown_bars = self.Param("SignalCooldownBars", 48) \
.SetDisplay("Signal Cooldown", "Bars to wait after a new entry", "Trading")
self._wpr = None
self._hist_sma = None
self._vol_sma = None
self._prev_color = None
self._cooldown_remaining = 0
@property
def CandleType(self):
return self._candle_type.Value
@property
def WprPeriod(self):
return self._wpr_period.Value
@property
def SmoothingLength(self):
return self._smoothing_length.Value
@property
def HighLevel2(self):
return self._high_level2.Value
@property
def LowLevel2(self):
return self._low_level2.Value
@property
def SignalCooldownBars(self):
return self._signal_cooldown_bars.Value
def OnReseted(self):
super(exp_xwpr_histogram_vol_strategy, self).OnReseted()
self._wpr = None
self._hist_sma = None
self._vol_sma = None
self._prev_color = None
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(exp_xwpr_histogram_vol_strategy, self).OnStarted2(time)
self._prev_color = None
self._cooldown_remaining = 0
self._wpr = WilliamsR()
self._wpr.Length = self.WprPeriod
self._hist_sma = SimpleMovingAverage()
self._hist_sma.Length = self.SmoothingLength
self._vol_sma = SimpleMovingAverage()
self._vol_sma.Length = self.SmoothingLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _on_process(self, candle):
if candle.State != CandleStates.Finished:
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
wpr_result = self._wpr.Process(CandleIndicatorValue(self._wpr, candle))
if not wpr_result.IsFormed:
return
wpr = wpr_result.Value
volume = candle.TotalVolume if candle.TotalVolume > Decimal(0) else Decimal(1)
hist_raw = (wpr + Decimal(50)) * volume
hist_smoothed = process_float(self._hist_sma, hist_raw, candle.OpenTime, True)
vol_smoothed = process_float(self._vol_sma, volume, candle.OpenTime, True)
if not hist_smoothed.IsFormed or not vol_smoothed.IsFormed:
return
baseline = vol_smoothed.Value
if baseline == Decimal(0):
return
hist = hist_smoothed.Value
strong_bull_level = self.HighLevel2 * baseline
strong_bear_level = self.LowLevel2 * baseline
if hist >= strong_bull_level:
color = 0
elif hist <= strong_bear_level:
color = 4
else:
color = 2
if self._prev_color is None:
self._prev_color = color
return
previous_color = self._prev_color
self._prev_color = color
if self._cooldown_remaining > 0:
return
if previous_color != 0 and color == 0 and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._cooldown_remaining = self.SignalCooldownBars
elif previous_color != 4 and color == 4 and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._cooldown_remaining = self.SignalCooldownBars
def CreateClone(self):
return exp_xwpr_histogram_vol_strategy()