Estratégia Fractal WPR
Esta estratégia usa o oscilador Williams %R para gerar sinais de trading com base em cruzamentos dos níveis de sobrecompra e sobrevenda. É adaptada de um expert advisor MQL5 e demonstra um sistema simples de reversão de momentum.
Como funciona
- Um indicador Williams %R com período configurável é calculado no período selecionado.
- Dois níveis horizontais definem as zonas extremas:
HighLevelmarca a zona de sobrecompra (padrão −30).LowLevelmarca a zona de sobrevenda (padrão −70).
- Quando
Trendestá configurado comoDirect:- Cruzar para baixo
LowLevelabre uma posição comprada e fecha qualquer posição vendida. - Cruzar para cima
HighLevelabre uma posição vendida e fecha qualquer posição comprada.
- Cruzar para baixo
- Quando
Trendestá configurado comoAgainst, as reações aos cruzamentos são invertidas. - Parâmetros opcionais permitem habilitar ou desabilitar separadamente a abertura e o fechamento de posições compradas ou vendidas.
- As distâncias de stop‑loss e take‑profit em ticks são aplicadas usando a API de proteção de alto nível.
Apenas velas completas são processadas para evitar reagir ao ruído intrabarra.
Parâmetros
WprPeriod– período de cálculo do Williams %R.HighLevel– limiar para a zona de sobrecompra.LowLevel– limiar para a zona de sobrevenda.Trend– modo de trading (DirectouAgainst).BuyPositionOpen– permitir abertura de posições compradas.SellPositionOpen– permitir abertura de posições vendidas.BuyPositionClose– permitir fechamento de posições compradas.SellPositionClose– permitir fechamento de posições vendidas.StopLossTicks– distância do stop‑loss em ticks.TakeProfitTicks– distância do take‑profit em ticks.CandleType– período de velas usado para análise.
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()