Ichimoku Stochastic Strategy
Strategy based on Ichimoku Cloud and Stochastic Oscillator indicators. Enters long when price is above Kumo (cloud), Tenkan > Kijun, and Stochastic is oversold (< 20) Enters short when price is below Kumo, Tenkan < Kijun, and Stochastic is overbought (> 80)
Testing indicates an average annual return of about 118%. It performs best in the stocks market.
Ichimoku outlines trend and support levels while Stochastic times the entry on pullbacks. Trades open when the oscillator resets within the prevailing cloud direction.
Traders who favor structured indicators may find it practical. The cloud itself acts as the stop against abrupt reversals.
Details
- Entry Criteria:
- Long:
Price > Cloud && Tenkan > Kijun && StochK < 20 - Short:
Price < Cloud && Tenkan < Kijun && StochK > 80
- Long:
- Long/Short: Both
- Exit Criteria:
- Cloud breakout in opposite direction
- Stops: Uses Ichimoku cloud boundaries
- Default Values:
TenkanPeriod= 9KijunPeriod= 26SenkouPeriod= 52StochPeriod= 14StochK= 3StochOversold= 20StochOverbought= 80CandleType= TimeSpan.FromMinutes(30).TimeFrame() The 20 and 80 in the rules are the defaults of StochOversold and StochOverbought. %K is the stochastic over StochPeriod candles smoothed over StochK candles; %D plays no part. A long closes when price closes below the cloud and a short when it closes above it. An entry signal against an open position reverses it.
- Filters:
- Category: Mean reversion
- Direction: Both
- Indicators: Ichimoku Cloud, Stochastic Oscillator
- Stops: Yes
- Complexity: Intermediate
- Timeframe: Mid-term
- Seasonality: No
- Neural Networks: No
- Divergence: No
- Risk Level: Medium
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Ichimoku Stochastic strategy.
/// A close above the cloud with Tenkan-sen above Kijun-sen and %K below StochOversold goes long, a close below the cloud with Tenkan-sen
/// below Kijun-sen and %K above StochOverbought goes short, reversing an opposite position; %K is the stochastic over StochPeriod candles smoothed over StochK candles. The cloud is the stop:
/// a long closes when price closes below the cloud and a short when it closes above it.
/// </summary>
public class IchimokuStochasticStrategy : Strategy
{
private readonly StrategyParam<int> _tenkanPeriod;
private readonly StrategyParam<int> _kijunPeriod;
private readonly StrategyParam<int> _senkouPeriod;
private readonly StrategyParam<int> _stochPeriod;
private readonly StrategyParam<int> _stochK;
private readonly StrategyParam<decimal> _stochOversold;
private readonly StrategyParam<decimal> _stochOverbought;
private readonly StrategyParam<DataType> _candleType;
/// <summary>
/// Period of Tenkan-sen.
/// </summary>
public int TenkanPeriod
{
get => _tenkanPeriod.Value;
set => _tenkanPeriod.Value = value;
}
/// <summary>
/// Period of Kijun-sen.
/// </summary>
public int KijunPeriod
{
get => _kijunPeriod.Value;
set => _kijunPeriod.Value = value;
}
/// <summary>
/// Period of Senkou Span B.
/// </summary>
public int SenkouPeriod
{
get => _senkouPeriod.Value;
set => _senkouPeriod.Value = value;
}
/// <summary>
/// Lookback period of the raw stochastic.
/// </summary>
public int StochPeriod
{
get => _stochPeriod.Value;
set => _stochPeriod.Value = value;
}
/// <summary>
/// Smoothing period of %K.
/// </summary>
public int StochK
{
get => _stochK.Value;
set => _stochK.Value = value;
}
/// <summary>
/// %K level for longs.
/// </summary>
public decimal StochOversold
{
get => _stochOversold.Value;
set => _stochOversold.Value = value;
}
/// <summary>
/// %K level for shorts.
/// </summary>
public decimal StochOverbought
{
get => _stochOverbought.Value;
set => _stochOverbought.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public IchimokuStochasticStrategy()
{
_tenkanPeriod = Param(nameof(TenkanPeriod), 9)
.SetGreaterThanZero()
.SetDisplay("Tenkan Period", "Period of Tenkan-sen", "Ichimoku");
_kijunPeriod = Param(nameof(KijunPeriod), 26)
.SetGreaterThanZero()
.SetDisplay("Kijun Period", "Period of Kijun-sen", "Ichimoku");
_senkouPeriod = Param(nameof(SenkouPeriod), 52)
.SetGreaterThanZero()
.SetDisplay("Senkou Period", "Period of Senkou Span B", "Ichimoku");
_stochPeriod = Param(nameof(StochPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("Stochastic Period", "Lookback period of the raw stochastic", "Stochastic");
_stochK = Param(nameof(StochK), 3)
.SetGreaterThanZero()
.SetDisplay("Stochastic %K", "Smoothing period of %K", "Stochastic");
_stochOversold = Param(nameof(StochOversold), 20m)
.SetDisplay("Stochastic Oversold", "%K level for longs", "Stochastic");
_stochOverbought = Param(nameof(StochOverbought), 80m)
.SetDisplay("Stochastic Overbought", "%K level for shorts", "Stochastic");
_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 OnStarted2(DateTime time)
{
base.OnStarted2(time);
var ichimoku = new Ichimoku
{
Tenkan = { Length = TenkanPeriod },
Kijun = { Length = KijunPeriod },
SenkouB = { Length = SenkouPeriod }
};
// The D line of the core oscillator is the smoothed %K.
var stochastic = new StochasticOscillator
{
K = { Length = StochPeriod },
D = { Length = StochK },
};
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(ichimoku, stochastic, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ichimoku);
DrawOwnTrades(area);
var oscillators = CreateChartArea();
if (oscillators != null)
{
DrawIndicator(oscillators, stochastic);
}
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue ichimokuValue, IIndicatorValue stochasticValue)
{
if (candle.State != CandleStates.Finished)
return;
if (ichimokuValue is not IIchimokuValue { Tenkan: decimal tenkan, Kijun: decimal kijun, SenkouA: decimal senkouA, SenkouB: decimal senkouB })
return;
if (!stochasticValue.IsFormed || stochasticValue is not IStochasticOscillatorValue { D: decimal k })
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var close = candle.ClosePrice;
var cloudTop = Math.Max(senkouA, senkouB);
var cloudBottom = Math.Min(senkouA, senkouB);
if (close > cloudTop && tenkan > kijun && k < StochOversold && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
else if (close < cloudBottom && tenkan < kijun && k > StochOverbought && Position >= 0)
SellMarket(Volume + Math.Abs(Position));
else if (Position > 0 && close < cloudBottom)
SellMarket(Position);
else if (Position < 0 && close > cloudTop)
BuyMarket(-Position);
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import Ichimoku, StochasticOscillator
from StockSharp.Algo.Strategies import Strategy
class ichimoku_stochastic_strategy(Strategy):
"""
Ichimoku Stochastic strategy.
A close above the cloud with Tenkan-sen above Kijun-sen and %K below StochOversold goes long, a close below the cloud with Tenkan-sen
below Kijun-sen and %K above StochOverbought goes short, reversing an opposite position; %K is the stochastic over StochPeriod candles smoothed over StochK candles. The cloud is the stop:
a long closes when price closes below the cloud and a short when it closes above it.
"""
def __init__(self):
super(ichimoku_stochastic_strategy, self).__init__()
self._tenkan_period = self.Param("TenkanPeriod", 9).SetGreaterThanZero().SetDisplay("Tenkan Period", "Period of Tenkan-sen", "Ichimoku")
self._kijun_period = self.Param("KijunPeriod", 26).SetGreaterThanZero().SetDisplay("Kijun Period", "Period of Kijun-sen", "Ichimoku")
self._senkou_period = self.Param("SenkouPeriod", 52).SetGreaterThanZero().SetDisplay("Senkou Period", "Period of Senkou Span B", "Ichimoku")
self._stoch_period = self.Param("StochPeriod", 14).SetGreaterThanZero().SetDisplay("Stochastic Period", "Lookback period of the raw stochastic", "Stochastic")
self._stoch_k = self.Param("StochK", 3).SetGreaterThanZero().SetDisplay("Stochastic %K", "Smoothing period of %K", "Stochastic")
self._stoch_oversold = self.Param("StochOversold", 20.0).SetDisplay("Stochastic Oversold", "%K level for longs", "Stochastic")
self._stoch_overbought = self.Param("StochOverbought", 80.0).SetDisplay("Stochastic Overbought", "%K level for shorts", "Stochastic")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))).SetDisplay("Candle Type", "Type of candles to use", "General")
@property
def candle_type(self):
return self._candle_type.Value
def OnStarted2(self, time):
super(ichimoku_stochastic_strategy, self).OnStarted2(time)
ichimoku = Ichimoku()
ichimoku.Tenkan.Length = self._tenkan_period.Value
ichimoku.Kijun.Length = self._kijun_period.Value
ichimoku.SenkouB.Length = self._senkou_period.Value
# The D line of the core oscillator is the smoothed %K.
stochastic = StochasticOscillator()
stochastic.K.Length = self._stoch_period.Value
stochastic.D.Length = self._stoch_k.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(ichimoku, stochastic, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ichimoku)
self.DrawOwnTrades(area)
oscillators = self.CreateChartArea()
if oscillators is not None:
self.DrawIndicator(oscillators, stochastic)
def _process_candle(self, candle, ichimoku_value, stochastic_value):
if candle.State != CandleStates.Finished:
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
if not stochastic_value.IsFormed or stochastic_value.D is None:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
k = stochastic_value.D
close = candle.ClosePrice
cloud_top = senkou_a if senkou_a > senkou_b else senkou_b
cloud_bottom = senkou_b if senkou_a > senkou_b else senkou_a
if close > cloud_top and tenkan > kijun and k < Decimal(self._stoch_oversold.Value) and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif close < cloud_bottom and tenkan < kijun and k > Decimal(self._stoch_overbought.Value) and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
elif self.Position > 0 and close < cloud_bottom:
self.SellMarket(self.Position)
elif self.Position < 0 and close > cloud_top:
self.BuyMarket(-self.Position)
def CreateClone(self):
return ichimoku_stochastic_strategy()