Estrategia Fractal WPR
Esta estrategia utiliza el oscilador Williams %R para generar señales de trading basadas en cruces de los niveles de sobrecompra y sobreventa. Está adaptada de un asesor experto MQL5 y demuestra un sistema simple de reversión de momentum.
Cómo funciona
- Se calcula un indicador Williams %R con período configurable en el marco temporal seleccionado.
- Dos niveles horizontales definen las zonas extremas:
HighLevelmarca la zona de sobrecompra (por defecto −30).LowLevelmarca la zona de sobreventa (por defecto −70).
- Cuando
Trendestá configurado comoDirect:- Cruzar hacia abajo
LowLevelabre una posición larga y cierra cualquier posición corta. - Cruzar hacia arriba
HighLevelabre una posición corta y cierra cualquier posición larga.
- Cruzar hacia abajo
- Cuando
Trendestá configurado comoAgainst, las reacciones a los cruces se invierten. - Los parámetros opcionales permiten habilitar o deshabilitar por separado la apertura y el cierre de posiciones largas o cortas.
- Las distancias de stop‑loss y take‑profit en ticks se aplican mediante la API de protección de alto nivel.
Solo se procesan las velas completadas para evitar reaccionar al ruido intrabarra.
Parámetros
WprPeriod– período de cálculo de Williams %R.HighLevel– umbral para la zona de sobrecompra.LowLevel– umbral para la zona de sobreventa.Trend– modo de trading (DirectoAgainst).BuyPositionOpen– permitir abrir posiciones largas.SellPositionOpen– permitir abrir posiciones cortas.BuyPositionClose– permitir cerrar posiciones largas.SellPositionClose– permitir cerrar posiciones cortas.StopLossTicks– distancia del stop‑loss en ticks.TakeProfitTicks– distancia del take‑profit en ticks.CandleType– marco temporal de velas utilizado para el análisis.
Indicadores
- Williams %R
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 using Williams %R crossings with optional trend direction.
/// </summary>
public class FractalWprStrategy : Strategy
{
private readonly StrategyParam<int> _wprPeriod;
private readonly StrategyParam<decimal> _highLevel;
private readonly StrategyParam<decimal> _lowLevel;
private readonly StrategyParam<TrendModes> _trend;
private readonly StrategyParam<bool> _buyPositionOpen;
private readonly StrategyParam<bool> _sellPositionOpen;
private readonly StrategyParam<bool> _buyPositionClose;
private readonly StrategyParam<bool> _sellPositionClose;
private readonly StrategyParam<int> _stopLossTicks;
private readonly StrategyParam<int> _takeProfitTicks;
private readonly StrategyParam<DataType> _candleType;
private WilliamsR _wpr;
private decimal? _prevWpr;
/// <summary>
/// Williams %R calculation period.
/// </summary>
public int WprPeriod { get => _wprPeriod.Value; set => _wprPeriod.Value = value; }
/// <summary>
/// Overbought threshold.
/// </summary>
public decimal HighLevel { get => _highLevel.Value; set => _highLevel.Value = value; }
/// <summary>
/// Oversold threshold.
/// </summary>
public decimal LowLevel { get => _lowLevel.Value; set => _lowLevel.Value = value; }
/// <summary>
/// Trading direction mode.
/// </summary>
public TrendModes Trend { get => _trend.Value; set => _trend.Value = value; }
/// <summary>
/// Allow opening long positions.
/// </summary>
public bool BuyPositionOpen { get => _buyPositionOpen.Value; set => _buyPositionOpen.Value = value; }
/// <summary>
/// Allow opening short positions.
/// </summary>
public bool SellPositionOpen { get => _sellPositionOpen.Value; set => _sellPositionOpen.Value = value; }
/// <summary>
/// Allow closing long positions.
/// </summary>
public bool BuyPositionClose { get => _buyPositionClose.Value; set => _buyPositionClose.Value = value; }
/// <summary>
/// Allow closing short positions.
/// </summary>
public bool SellPositionClose { get => _sellPositionClose.Value; set => _sellPositionClose.Value = value; }
/// <summary>
/// Stop loss distance in ticks.
/// </summary>
public int StopLossTicks { get => _stopLossTicks.Value; set => _stopLossTicks.Value = value; }
/// <summary>
/// Take profit distance in ticks.
/// </summary>
public int TakeProfitTicks { get => _takeProfitTicks.Value; set => _takeProfitTicks.Value = value; }
/// <summary>
/// Candle type and timeframe.
/// </summary>
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
/// <summary>
/// Initializes a new instance of the <see cref="FractalWprStrategy"/> class.
/// </summary>
public FractalWprStrategy()
{
_wprPeriod = Param(nameof(WprPeriod), 30)
.SetDisplay("WPR Period", "Williams %R calculation period", "Indicators")
.SetGreaterThanZero()
;
_highLevel = Param(nameof(HighLevel), -30m)
.SetDisplay("High Level", "Overbought threshold", "Levels");
_lowLevel = Param(nameof(LowLevel), -70m)
.SetDisplay("Low Level", "Oversold threshold", "Levels");
_trend = Param(nameof(Trend), TrendModes.Direct)
.SetDisplay("Trend Mode", "Trading direction mode", "General");
_buyPositionOpen = Param(nameof(BuyPositionOpen), true)
.SetDisplay("Buy Open", "Allow opening long positions", "Permissions");
_sellPositionOpen = Param(nameof(SellPositionOpen), true)
.SetDisplay("Sell Open", "Allow opening short positions", "Permissions");
_buyPositionClose = Param(nameof(BuyPositionClose), true)
.SetDisplay("Buy Close", "Allow closing long positions", "Permissions");
_sellPositionClose = Param(nameof(SellPositionClose), true)
.SetDisplay("Sell Close", "Allow closing short positions", "Permissions");
_stopLossTicks = Param(nameof(StopLossTicks), 1000)
.SetDisplay("Stop Loss", "Stop loss distance in ticks", "Protection")
.SetGreaterThanZero();
_takeProfitTicks = Param(nameof(TakeProfitTicks), 2000)
.SetDisplay("Take Profit", "Take profit distance in ticks", "Protection")
.SetGreaterThanZero();
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_wpr = null!;
_prevWpr = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_wpr = new WilliamsR { Length = WprPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(_wpr, ProcessCandle)
.Start();
var step = GetSecurityValue<decimal?>(Level1Fields.StepPrice) ?? 1m;
StartProtection(
stopLoss: new Unit(step * StopLossTicks, UnitTypes.Absolute),
takeProfit: new Unit(step * TakeProfitTicks, UnitTypes.Absolute));
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _wpr);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue wprValue)
{
if (candle.State != CandleStates.Finished)
return;
var wpr = wprValue.ToDecimal();
if (_prevWpr.HasValue && IsFormedAndOnlineAndAllowTrading())
{
if (Trend == TrendModes.Direct)
{
if (_prevWpr > LowLevel && wpr <= LowLevel)
{
if (BuyPositionOpen && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
if (SellPositionClose && Position < 0)
BuyMarket(Math.Abs(Position));
}
if (_prevWpr < HighLevel && wpr >= HighLevel)
{
if (SellPositionOpen && Position >= 0)
SellMarket(Volume + Math.Abs(Position));
if (BuyPositionClose && Position > 0)
SellMarket(Position);
}
}
else
{
if (_prevWpr > LowLevel && wpr <= LowLevel)
{
if (SellPositionOpen && Position >= 0)
SellMarket(Volume + Math.Abs(Position));
if (BuyPositionClose && Position > 0)
SellMarket(Position);
}
if (_prevWpr < HighLevel && wpr >= HighLevel)
{
if (BuyPositionOpen && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
if (SellPositionClose && Position < 0)
BuyMarket(Math.Abs(Position));
}
}
}
_prevWpr = wpr;
}
/// <summary>
/// Trend trading modes.
/// </summary>
public enum TrendModes
{
Direct,
Against
}
}
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
from StockSharp.Algo.Strategies import Strategy
class fractal_wpr_strategy(Strategy):
"""
Fractal WPR: Williams %R level crossings with directional mode.
Direct mode: buy on oversold crossing, sell on overbought.
Against mode: reverse signals.
Uses StartProtection for SL/TP.
"""
def __init__(self):
super(fractal_wpr_strategy, self).__init__()
self._wpr_period = self.Param("WprPeriod", 30) \
.SetDisplay("WPR Period", "Williams %R calculation period", "Indicators")
self._high_level = self.Param("HighLevel", -30.0) \
.SetDisplay("High Level", "Overbought threshold", "Levels")
self._low_level = self.Param("LowLevel", -70.0) \
.SetDisplay("Low Level", "Oversold threshold", "Levels")
self._trend = self.Param("Trend", 0) \
.SetDisplay("Trend Mode", "0=Direct, 1=Against", "General")
self._stop_loss_ticks = self.Param("StopLossTicks", 1000) \
.SetDisplay("Stop Loss", "Stop loss distance in ticks", "Protection")
self._take_profit_ticks = self.Param("TakeProfitTicks", 2000) \
.SetDisplay("Take Profit", "Take profit distance in ticks", "Protection")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._prev_wpr = None
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(fractal_wpr_strategy, self).OnReseted()
self._prev_wpr = None
def OnStarted2(self, time):
super(fractal_wpr_strategy, self).OnStarted2(time)
wpr = WilliamsR()
wpr.Length = self._wpr_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(wpr, self._process_candle).Start()
ps = 1.0
if self.Security is not None and self.Security.PriceStep is not None:
ps = float(self.Security.PriceStep)
if ps <= 0:
ps = 1.0
sl_dist = ps * self._stop_loss_ticks.Value
tp_dist = ps * self._take_profit_ticks.Value
self.StartProtection(
Unit(tp_dist, UnitTypes.Absolute),
Unit(sl_dist, UnitTypes.Absolute))
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, wpr)
self.DrawOwnTrades(area)
def _process_candle(self, candle, wpr_value):
if candle.State != CandleStates.Finished:
return
wpr = float(wpr_value)
if self._prev_wpr is not None and self.IsFormedAndOnlineAndAllowTrading():
high_level = self._high_level.Value
low_level = self._low_level.Value
is_direct = self._trend.Value == 0
if is_direct:
if self._prev_wpr > low_level and wpr <= low_level:
if self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
if self._prev_wpr < high_level and wpr >= high_level:
if self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
else:
if self._prev_wpr > low_level and wpr <= low_level:
if self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
if self._prev_wpr < high_level and wpr >= high_level:
if self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._prev_wpr = wpr
def CreateClone(self):
return fractal_wpr_strategy()