Estrategia de Cruce Ultra WPR
Esta estrategia aplica un oscilador Williams %R suavizado por dos medias móviles. El cruce de las líneas rápida y lenta suavizadas genera señales de trading. Una posición larga se abre cuando la línea rápida sube por encima de la línea lenta, y una posición corta se abre cuando la línea rápida cae por debajo de la línea lenta.
El enfoque busca seguir el momentum emergente mientras limita el riesgo con niveles de take-profit y stop-loss configurables.
Detalles
- Criterios de entrada:
- Largo: La línea rápida cruza por encima de la línea lenta
- Corto: La línea rápida cruza por debajo de la línea lenta
- Largo/Corto: Ambos
- Criterios de salida:
- Largo: Salida cuando la línea rápida cruza por debajo de la línea lenta
- Corto: Salida cuando la línea rápida cruza por encima de la línea lenta
- Stops: Sí, take-profit y stop-loss basados en precio
- Valores predeterminados:
CandleType= TimeSpan.FromHours(4)WprPeriod= 13FastLength= 3SlowLength= 53TakeProfit= 0.2mStopLoss= 0.1m
- Filtros:
- Categoría: Seguimiento de tendencia
- Dirección: Ambos
- Indicadores: Williams %R, Moving Average
- Stops: Sí
- Complejidad: Básico
- Marco temporal: H4
- Estacionalidad: No
- Redes neuronales: No
- Divergencia: No
- Nivel de riesgo: Medio
using System;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy based on the crossover of fast and slow smoothed Williams %R lines.
/// </summary>
public class UltraWprCrossStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _wprPeriod;
private readonly StrategyParam<int> _fastLength;
private readonly StrategyParam<int> _slowLength;
private readonly StrategyParam<decimal> _takeProfit;
private readonly StrategyParam<decimal> _stopLoss;
private readonly StrategyParam<int> _cooldownBars;
private readonly SimpleMovingAverage _fastMa = new();
private readonly SimpleMovingAverage _slowMa = new();
private decimal _prevFast;
private decimal _prevSlow;
private bool _isInitialized;
private int _barsSinceTrade;
/// <summary>
/// Candle type for strategy calculation.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Williams %R period.
/// </summary>
public int WprPeriod
{
get => _wprPeriod.Value;
set => _wprPeriod.Value = value;
}
/// <summary>
/// Fast smoothing length.
/// </summary>
public int FastLength
{
get => _fastLength.Value;
set => _fastLength.Value = value;
}
/// <summary>
/// Slow smoothing length.
/// </summary>
public int SlowLength
{
get => _slowLength.Value;
set => _slowLength.Value = value;
}
/// <summary>
/// Take profit in price.
/// </summary>
public decimal TakeProfit
{
get => _takeProfit.Value;
set => _takeProfit.Value = value;
}
/// <summary>
/// Stop loss in price.
/// </summary>
public decimal StopLoss
{
get => _stopLoss.Value;
set => _stopLoss.Value = value;
}
/// <summary>
/// Bars to wait after a completed trade.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Initializes a new instance of the strategy.
/// </summary>
public UltraWprCrossStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
_wprPeriod = Param(nameof(WprPeriod), 13)
.SetGreaterThanZero()
.SetDisplay("WPR Period", "Williams %R period", "Indicators")
.SetOptimize(5, 40, 1);
_fastLength = Param(nameof(FastLength), 3)
.SetGreaterThanZero()
.SetDisplay("Fast Length", "Fast smoothing length", "Indicators")
.SetOptimize(1, 20, 1);
_slowLength = Param(nameof(SlowLength), 53)
.SetGreaterThanZero()
.SetDisplay("Slow Length", "Slow smoothing length", "Indicators")
.SetOptimize(10, 100, 5);
_takeProfit = Param(nameof(TakeProfit), 900m)
.SetGreaterThanZero()
.SetDisplay("Take Profit", "Take profit in price", "Risk")
.SetOptimize(300m, 1500m, 100m);
_stopLoss = Param(nameof(StopLoss), 450m)
.SetGreaterThanZero()
.SetDisplay("Stop Loss", "Stop loss in price", "Risk")
.SetOptimize(200m, 900m, 50m);
_cooldownBars = Param(nameof(CooldownBars), 1)
.SetDisplay("Cooldown Bars", "Bars to wait after a completed trade", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_fastMa.Length = FastLength;
_slowMa.Length = SlowLength;
_fastMa.Reset();
_slowMa.Reset();
_prevFast = 0m;
_prevSlow = 0m;
_isInitialized = false;
_barsSinceTrade = CooldownBars;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fastMa.Length = FastLength;
_slowMa.Length = SlowLength;
var wpr = new WilliamsR { Length = WprPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(wpr, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _fastMa);
DrawIndicator(area, _slowMa);
DrawOwnTrades(area);
}
StartProtection(new Unit(TakeProfit, UnitTypes.Absolute), new Unit(StopLoss, UnitTypes.Absolute));
}
private void ProcessCandle(ICandleMessage candle, decimal wprValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var fast = _fastMa.Process(new DecimalIndicatorValue(_fastMa, wprValue, candle.OpenTime) { IsFinal = true }).ToDecimal();
var slow = _slowMa.Process(new DecimalIndicatorValue(_slowMa, wprValue, candle.OpenTime) { IsFinal = true }).ToDecimal();
if (!_fastMa.IsFormed || !_slowMa.IsFormed)
return;
if (_barsSinceTrade < CooldownBars)
_barsSinceTrade++;
if (!_isInitialized)
{
_prevFast = fast;
_prevSlow = slow;
_isInitialized = true;
return;
}
var crossUp = _prevFast <= _prevSlow && fast > slow && fast < -55m;
var crossDown = _prevFast >= _prevSlow && fast < slow && fast > -45m;
if (_barsSinceTrade >= CooldownBars)
{
if (crossUp && Position <= 0)
{
BuyMarket(Volume + Math.Abs(Position));
_barsSinceTrade = 0;
}
else if (crossDown && Position >= 0)
{
SellMarket(Volume + Math.Abs(Position));
_barsSinceTrade = 0;
}
}
_prevFast = fast;
_prevSlow = slow;
}
}
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, Unit, UnitTypes, CandleStates
from StockSharp.Algo.Indicators import SimpleMovingAverage, WilliamsR
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class ultra_wpr_cross_strategy(Strategy):
def __init__(self):
super(ultra_wpr_cross_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._wpr_period = self.Param("WprPeriod", 13) \
.SetDisplay("WPR Period", "Williams %R period", "Indicators")
self._fast_length = self.Param("FastLength", 3) \
.SetDisplay("Fast Length", "Fast smoothing length", "Indicators")
self._slow_length = self.Param("SlowLength", 53) \
.SetDisplay("Slow Length", "Slow smoothing length", "Indicators")
self._take_profit = self.Param("TakeProfit", 900.0) \
.SetDisplay("Take Profit", "Take profit in price", "Risk")
self._stop_loss = self.Param("StopLoss", 450.0) \
.SetDisplay("Stop Loss", "Stop loss in price", "Risk")
self._cooldown_bars = self.Param("CooldownBars", 1) \
.SetDisplay("Cooldown Bars", "Bars to wait after a completed trade", "Risk")
self._fast_ma = SimpleMovingAverage()
self._slow_ma = SimpleMovingAverage()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._is_initialized = False
self._bars_since_trade = 0
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def WprPeriod(self):
return self._wpr_period.Value
@WprPeriod.setter
def WprPeriod(self, value):
self._wpr_period.Value = value
@property
def FastLength(self):
return self._fast_length.Value
@FastLength.setter
def FastLength(self, value):
self._fast_length.Value = value
@property
def SlowLength(self):
return self._slow_length.Value
@SlowLength.setter
def SlowLength(self, value):
self._slow_length.Value = value
@property
def TakeProfit(self):
return self._take_profit.Value
@TakeProfit.setter
def TakeProfit(self, value):
self._take_profit.Value = value
@property
def StopLoss(self):
return self._stop_loss.Value
@StopLoss.setter
def StopLoss(self, value):
self._stop_loss.Value = value
@property
def CooldownBars(self):
return self._cooldown_bars.Value
@CooldownBars.setter
def CooldownBars(self, value):
self._cooldown_bars.Value = value
def OnStarted2(self, time):
super(ultra_wpr_cross_strategy, self).OnStarted2(time)
self._fast_ma.Length = self.FastLength
self._slow_ma.Length = self.SlowLength
wpr = WilliamsR()
wpr.Length = self.WprPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(wpr, self.ProcessCandle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, self._fast_ma)
self.DrawIndicator(area, self._slow_ma)
self.DrawOwnTrades(area)
self.StartProtection(
Unit(self.TakeProfit, UnitTypes.Absolute),
Unit(self.StopLoss, UnitTypes.Absolute))
def ProcessCandle(self, candle, wpr_value):
if candle.State != CandleStates.Finished:
return
wpr_val = float(wpr_value)
fast = float(process_float(self._fast_ma, wpr_val, candle.OpenTime, True))
slow = float(process_float(self._slow_ma, wpr_val, candle.OpenTime, True))
if not self._fast_ma.IsFormed or not self._slow_ma.IsFormed:
return
if self._bars_since_trade < self.CooldownBars:
self._bars_since_trade += 1
if not self._is_initialized:
self._prev_fast = fast
self._prev_slow = slow
self._is_initialized = True
return
cross_up = self._prev_fast <= self._prev_slow and fast > slow and fast < -55.0
cross_down = self._prev_fast >= self._prev_slow and fast < slow and fast > -45.0
if self._bars_since_trade >= self.CooldownBars:
pos = self.Position
if cross_up and pos <= 0:
self.BuyMarket(self.Volume + abs(pos))
self._bars_since_trade = 0
elif cross_down and pos >= 0:
self.SellMarket(self.Volume + abs(pos))
self._bars_since_trade = 0
self._prev_fast = fast
self._prev_slow = slow
def OnReseted(self):
super(ultra_wpr_cross_strategy, self).OnReseted()
self._fast_ma.Length = self.FastLength
self._slow_ma.Length = self.SlowLength
self._fast_ma.Reset()
self._slow_ma.Reset()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._is_initialized = False
self._bars_since_trade = self.CooldownBars
def CreateClone(self):
return ultra_wpr_cross_strategy()