Estrategia Williams R Ichimoku
Esta configuración combina los extremos de momentum de Williams %R con la estructura de tendencia definida por la Nube Ichimoku. La idea es unirse a movimientos fuertes solo cuando el precio se encuentra en el lado favorable de la nube y las líneas de corto plazo confirman el sesgo.
Las pruebas indican un rendimiento anual promedio de aproximadamente 73%. Funciona mejor en el mercado de criptomonedas.
Una oportunidad larga aparece cuando el oscilador cae por debajo de -80 mientras el precio se mantiene por encima de la nube y Tenkan-sen cruza por encima de Kijun-sen. Una señal corta ocurre cuando %R sube por encima de -20 con el precio bajo la nube y Tenkan-sen por debajo de Kijun-sen. La posición permanece abierta hasta que el precio cruza el lado opuesto de la nube.
Debido a que el método espera varias confirmaciones, es adecuado para traders que prefieren filtros de tendencia claros sobre reversiones rápidas. Los stops dinámicos se establecen alrededor del Kijun-sen para que el riesgo se ajuste con la fuerza de la tendencia subyacente.
Detalles
- Criterios de entrada:
- Largo: %R < -80 && price above Ichimoku cloud and Tenkan-sen > Kijun-sen
- Corto: %R > -20 && price below Ichimoku cloud and Tenkan-sen < Kijun-sen
- Largo/Corto: Ambos lados.
- Criterios de salida:
- Largo: Salir cuando el precio cruce por debajo de la nube
- Corto: Salir cuando el precio cruce por encima de la nube
- Stops: Sí.
- Valores predeterminados:
WilliamsRPeriod= 14TenkanPeriod= 9KijunPeriod= 26SenkouSpanBPeriod= 52CandleType= TimeSpan.FromMinutes(15)
- Filtros:
- Categoría: Mixto
- Dirección: Ambos
- Indicadores: Williams R Ichimoku
- Stops: Sí
- Complejidad: Intermedio
- Marco temporal: Intradía
- Estacionalidad: No
- Redes neuronales: No
- Divergencia: No
- Nivel de riesgo: Medio
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 based on Williams %R and Ichimoku indicators.
/// Enters long when Williams %R is below -80 (oversold) and price is above Ichimoku Cloud with Tenkan-sen > Kijun-sen.
/// Enters short when Williams %R is above -20 (overbought) and price is below Ichimoku Cloud with Tenkan-sen < Kijun-sen.
/// </summary>
public class WilliamsIchimokuStrategy : Strategy
{
private readonly StrategyParam<int> _williamsRPeriod;
private readonly StrategyParam<int> _tenkanPeriod;
private readonly StrategyParam<int> _kijunPeriod;
private readonly StrategyParam<int> _senkouSpanBPeriod;
private readonly StrategyParam<int> _cooldownBars;
private readonly StrategyParam<DataType> _candleType;
private WilliamsR _williamsR;
private Ichimoku _ichimoku;
private decimal? _lastKijun;
private decimal _prevWilliamsR;
private int _cooldown;
/// <summary>
/// Williams %R indicator period.
/// </summary>
public int WilliamsRPeriod
{
get => _williamsRPeriod.Value;
set => _williamsRPeriod.Value = value;
}
/// <summary>
/// Tenkan-sen period (Ichimoku).
/// </summary>
public int TenkanPeriod
{
get => _tenkanPeriod.Value;
set => _tenkanPeriod.Value = value;
}
/// <summary>
/// Kijun-sen period (Ichimoku).
/// </summary>
public int KijunPeriod
{
get => _kijunPeriod.Value;
set => _kijunPeriod.Value = value;
}
/// <summary>
/// Senkou Span B period (Ichimoku).
/// </summary>
public int SenkouSpanBPeriod
{
get => _senkouSpanBPeriod.Value;
set => _senkouSpanBPeriod.Value = value;
}
/// <summary>
/// Bars to wait between trades.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Candle type parameter.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public WilliamsIchimokuStrategy()
{
_williamsRPeriod = Param(nameof(WilliamsRPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("Williams %R Period", "Period for Williams %R calculation", "Indicators")
.SetOptimize(10, 20, 2);
_tenkanPeriod = Param(nameof(TenkanPeriod), 9)
.SetGreaterThanZero()
.SetDisplay("Tenkan-sen Period", "Period for Tenkan-sen line (Ichimoku)", "Indicators")
.SetOptimize(7, 13, 1);
_kijunPeriod = Param(nameof(KijunPeriod), 26)
.SetGreaterThanZero()
.SetDisplay("Kijun-sen Period", "Period for Kijun-sen line (Ichimoku)", "Indicators")
.SetOptimize(20, 30, 2);
_senkouSpanBPeriod = Param(nameof(SenkouSpanBPeriod), 52)
.SetGreaterThanZero()
.SetDisplay("Senkou Span B Period", "Period for Senkou Span B line (Ichimoku)", "Indicators")
.SetOptimize(40, 60, 4);
_cooldownBars = Param(nameof(CooldownBars), 60)
.SetRange(1, 200)
.SetDisplay("Cooldown Bars", "Bars between trades", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).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();
_williamsR = null;
_ichimoku = null;
_lastKijun = null;
_prevWilliamsR = -50m;
_cooldown = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
// Initialize indicators
_williamsR = new WilliamsR
{
Length = WilliamsRPeriod
};
_ichimoku = new Ichimoku
{
Tenkan = { Length = TenkanPeriod },
Kijun = { Length = KijunPeriod },
SenkouB = { Length = SenkouSpanBPeriod }
};
// Create candles subscription
var subscription = SubscribeCandles(CandleType);
// Bind indicators to subscription
subscription
.BindEx(_williamsR, _ichimoku, ProcessCandle)
.Start();
// Setup chart if available
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _williamsR);
DrawIndicator(area, _ichimoku);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue williamsRValue, IIndicatorValue ichimokuValue)
{
// Skip unfinished candles
if (candle.State != CandleStates.Finished)
return;
// Skip if strategy is not ready to trade
if (!IsFormedAndOnlineAndAllowTrading())
return;
// Extract Ichimoku values
var ichimokuTyped = (IchimokuValue)ichimokuValue;
if (ichimokuTyped.Tenkan is not decimal tenkan)
return;
if (ichimokuTyped.Kijun is not decimal kijun)
return;
if (ichimokuTyped.SenkouA is not decimal senkouA)
return;
if (ichimokuTyped.SenkouB is not decimal senkouB)
return;
// Determine if price is above or below the Kumo (cloud)
var kumoTop = Math.Max(senkouA, senkouB);
var kumoBottom = Math.Min(senkouA, senkouB);
var isPriceAboveKumo = candle.ClosePrice > kumoTop;
var isPriceBelowKumo = candle.ClosePrice < kumoBottom;
var williamsRDec = williamsRValue.ToDecimal();
var crossedBelow80 = _prevWilliamsR >= -90m && williamsRDec < -90m;
var crossedAbove20 = _prevWilliamsR <= -10m && williamsRDec > -10m;
_prevWilliamsR = williamsRDec;
if (_cooldown > 0)
_cooldown--;
// Save current Kijun for stop-loss
_lastKijun = kijun;
// Trading logic
if (_cooldown == 0 && crossedBelow80 && candle.ClosePrice > kumoTop * 1.002m && isPriceAboveKumo && tenkan > kijun)
{
// Long signal: %R < -80 (oversold), price above Kumo, Tenkan > Kijun
if (Position <= 0)
{
// Close any existing short position and open long
BuyMarket(Volume + Math.Abs(Position));
_cooldown = CooldownBars;
}
}
else if (_cooldown == 0 && crossedAbove20 && candle.ClosePrice < kumoBottom * 0.998m && isPriceBelowKumo && tenkan < kijun)
{
// Short signal: %R > -20 (overbought), price below Kumo, Tenkan < Kijun
if (Position >= 0)
{
// Close any existing long position and open short
SellMarket(Volume + Math.Abs(Position));
_cooldown = CooldownBars;
}
}
else if ((Position > 0 && candle.ClosePrice < kumoBottom) ||
(Position < 0 && candle.ClosePrice > kumoTop))
{
// Exit positions when price crosses the Kumo
if (Position > 0)
{
SellMarket(Math.Abs(Position));
_cooldown = CooldownBars;
}
else if (Position < 0)
{
BuyMarket(Math.Abs(Position));
_cooldown = CooldownBars;
}
}
}
}
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, CandleStates
from StockSharp.Algo.Indicators import WilliamsR, Ichimoku
from StockSharp.Algo.Strategies import Strategy
from datatype_extensions import *
from indicator_extensions import *
class williams_ichimoku_strategy(Strategy):
"""
Strategy based on Williams %R and Ichimoku indicators.
"""
def __init__(self):
super(williams_ichimoku_strategy, self).__init__()
self._williams_r_period = self.Param("WilliamsRPeriod", 14) \
.SetGreaterThanZero() \
.SetDisplay("Williams %R Period", "Period for Williams %R calculation", "Indicators")
self._tenkan_period = self.Param("TenkanPeriod", 9) \
.SetGreaterThanZero() \
.SetDisplay("Tenkan-sen Period", "Period for Tenkan-sen line (Ichimoku)", "Indicators")
self._kijun_period = self.Param("KijunPeriod", 26) \
.SetGreaterThanZero() \
.SetDisplay("Kijun-sen Period", "Period for Kijun-sen line (Ichimoku)", "Indicators")
self._senkou_span_b_period = self.Param("SenkouSpanBPeriod", 52) \
.SetGreaterThanZero() \
.SetDisplay("Senkou Span B Period", "Period for Senkou Span B line (Ichimoku)", "Indicators")
self._cooldown_bars = self.Param("CooldownBars", 60) \
.SetRange(1, 200) \
.SetDisplay("Cooldown Bars", "Bars between trades", "General")
self._candle_type = self.Param("CandleType", tf(30)) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._prev_williams_r = -50.0
self._cooldown = 0
@property
def CandleType(self):
return self._candle_type.Value
def OnReseted(self):
super(williams_ichimoku_strategy, self).OnReseted()
self._prev_williams_r = -50.0
self._cooldown = 0
def OnStarted2(self, time):
super(williams_ichimoku_strategy, self).OnStarted2(time)
self._prev_williams_r = -50.0
self._cooldown = 0
williams_r = WilliamsR()
williams_r.Length = self._williams_r_period.Value
ichimoku = Ichimoku()
ichimoku.Tenkan.Length = self._tenkan_period.Value
ichimoku.Kijun.Length = self._kijun_period.Value
ichimoku.SenkouB.Length = self._senkou_span_b_period.Value
subscription = self.SubscribeCandles(self.CandleType)
subscription.BindEx(williams_r, ichimoku, self.ProcessCandle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, williams_r)
self.DrawIndicator(area, ichimoku)
self.DrawOwnTrades(area)
def ProcessCandle(self, candle, williams_r_value, ichimoku_value):
if candle.State != CandleStates.Finished:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
tenkan = ichimoku_value.Tenkan
kijun = ichimoku_value.Kijun
senkou_a = ichimoku_value.SenkouA
senkou_b = ichimoku_value.SenkouB
if tenkan is None or kijun is None or senkou_a is None or senkou_b is None:
return
tenkan = float(tenkan)
kijun = float(kijun)
senkou_a = float(senkou_a)
senkou_b = float(senkou_b)
kumo_top = max(senkou_a, senkou_b)
kumo_bottom = min(senkou_a, senkou_b)
price = float(candle.ClosePrice)
is_price_above_kumo = price > kumo_top
is_price_below_kumo = price < kumo_bottom
wr = float(williams_r_value)
crossed_below_90 = self._prev_williams_r >= -90 and wr < -90
crossed_above_10 = self._prev_williams_r <= -10 and wr > -10
self._prev_williams_r = wr
if self._cooldown > 0:
self._cooldown -= 1
cooldown_val = int(self._cooldown_bars.Value)
if self._cooldown == 0 and crossed_below_90 and price > kumo_top * 1.002 and is_price_above_kumo and tenkan > kijun:
if self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
self._cooldown = cooldown_val
elif self._cooldown == 0 and crossed_above_10 and price < kumo_bottom * 0.998 and is_price_below_kumo and tenkan < kijun:
if self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
self._cooldown = cooldown_val
elif (self.Position > 0 and price < kumo_bottom) or (self.Position < 0 and price > kumo_top):
if self.Position > 0:
self.SellMarket(abs(self.Position))
self._cooldown = cooldown_val
elif self.Position < 0:
self.BuyMarket(abs(self.Position))
self._cooldown = cooldown_val
def CreateClone(self):
return williams_ichimoku_strategy()