Ver en GitHub

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 = 14
    • TenkanPeriod = 9
    • KijunPeriod = 26
    • SenkouSpanBPeriod = 52
    • CandleType = 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;
			}
		}
	}
}