Ichimoku 云宽度突破策略
本策略监测 Ichimoku 云层宽度的快速扩张。当宽度显著超出常态时,价格往往会开始新的走势。
测试表明年均收益约为 64%,该策略在外汇市场表现最佳。
云宽突破依据历史数据和倍数设定的界限时开仓,可多可空,并设置止损。宽度回到平均水平后平仓。
适合动量交易者把握趋势起步阶段。
详细信息
- 入场条件: Indicator exceeds average by deviation multiplier.
- Long/Short: 双向 directions.
- 退出条件: Indicator reverts to average.
- 止损: 是
- 默认值:
TenkanPeriod= 9KijunPeriod= 26SenkouSpanBPeriod= 52AvgPeriod= 20Multiplier= 2.0mCandleType= TimeSpan.FromMinutes(5)StopLoss= 2.0m
- 筛选条件:
- 类别: 突破
- 方向: 双向
- 指标: Ichimoku
- 止损: 是
- 复杂度: 中等
- 时间框架: 短期
- 季节性: 否
- 神经网络: 否
- 背离: 否
- 风险等级: 中等
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 that trades on Ichimoku Cloud width breakouts.
/// When Ichimoku Cloud width increases significantly above its average,
/// it enters position in the direction determined by price location relative to the cloud.
/// </summary>
public class IchimokuWidthBreakoutStrategy : Strategy
{
private readonly StrategyParam<int> _tenkanPeriod;
private readonly StrategyParam<int> _kijunPeriod;
private readonly StrategyParam<int> _senkouSpanBPeriod;
private readonly StrategyParam<int> _avgPeriod;
private readonly StrategyParam<decimal> _multiplier;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<decimal> _stopLoss;
private Ichimoku _ichimoku;
private SimpleMovingAverage _widthAverage;
/// <summary>
/// Tenkan-sen period for Ichimoku.
/// </summary>
public int TenkanPeriod
{
get => _tenkanPeriod.Value;
set => _tenkanPeriod.Value = value;
}
/// <summary>
/// Kijun-sen period for Ichimoku.
/// </summary>
public int KijunPeriod
{
get => _kijunPeriod.Value;
set => _kijunPeriod.Value = value;
}
/// <summary>
/// Senkou Span B period for Ichimoku.
/// </summary>
public int SenkouSpanBPeriod
{
get => _senkouSpanBPeriod.Value;
set => _senkouSpanBPeriod.Value = value;
}
/// <summary>
/// Period for width average calculation.
/// </summary>
public int AvgPeriod
{
get => _avgPeriod.Value;
set => _avgPeriod.Value = value;
}
/// <summary>
/// Standard deviation multiplier for breakout detection.
/// </summary>
public decimal Multiplier
{
get => _multiplier.Value;
set => _multiplier.Value = value;
}
/// <summary>
/// Candle type for strategy.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Stop-loss percentage.
/// </summary>
public decimal StopLoss
{
get => _stopLoss.Value;
set => _stopLoss.Value = value;
}
/// <summary>
/// Initialize <see cref="IchimokuWidthBreakoutStrategy"/>.
/// </summary>
public IchimokuWidthBreakoutStrategy()
{
_tenkanPeriod = Param(nameof(TenkanPeriod), 9)
.SetGreaterThanZero()
.SetDisplay("Tenkan Period", "Period for Tenkan-sen line", "Indicators")
.SetOptimize(5, 20, 1);
_kijunPeriod = Param(nameof(KijunPeriod), 26)
.SetGreaterThanZero()
.SetDisplay("Kijun Period", "Period for Kijun-sen line", "Indicators")
.SetOptimize(20, 40, 2);
_senkouSpanBPeriod = Param(nameof(SenkouSpanBPeriod), 52)
.SetGreaterThanZero()
.SetDisplay("Senkou Span B Period", "Period for Senkou Span B line", "Indicators")
.SetOptimize(40, 80, 4);
_avgPeriod = Param(nameof(AvgPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("Average Period", "Period for cloud width average calculation", "Indicators")
.SetOptimize(10, 50, 5);
_multiplier = Param(nameof(Multiplier), 1.0m)
.SetGreaterThanZero()
.SetDisplay("Multiplier", "Standard deviation multiplier for breakout detection", "Indicators")
.SetOptimize(1.0m, 3.0m, 0.5m);
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_stopLoss = Param(nameof(StopLoss), 2.0m)
.SetGreaterThanZero()
.SetDisplay("Stop Loss %", "Stop Loss percentage", "Risk Management")
.SetOptimize(1.0m, 5.0m, 0.5m);
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
// Create indicators
_ichimoku = new Ichimoku
{
Tenkan = { Length = TenkanPeriod },
Kijun = { Length = KijunPeriod },
SenkouB = { Length = SenkouSpanBPeriod }
};
_widthAverage = new SMA { Length = AvgPeriod };
// Create subscription
var subscription = SubscribeCandles(CandleType);
// Bind Ichimoku to the candle subscription
subscription
.BindEx(_ichimoku, ProcessIchimoku)
.Start();
// Enable stop loss protection
StartProtection(
takeProfit: new Unit(2, UnitTypes.Percent),
stopLoss: new Unit(StopLoss, UnitTypes.Percent)
);
// Create chart area for visualization
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _ichimoku);
DrawOwnTrades(area);
}
}
private void ProcessIchimoku(ICandleMessage candle, IIndicatorValue ichimokuValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!ichimokuValue.IsFinal)
return;
// Get current Ichimoku values
// The structure of values depends on the implementation, this is just an example
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 senkouSpanA)
return;
if (ichimokuTyped.SenkouB is not decimal senkouSpanB)
return;
// Calculate Cloud width (absolute difference between Senkou lines)
var width = Math.Abs(senkouSpanA - senkouSpanB);
// Process width through average
var widthAvgValue = _widthAverage.Process(new DecimalIndicatorValue(_widthAverage, width, candle.ServerTime) { IsFinal = true });
var avgWidth = widthAvgValue.ToDecimal();
// Skip if indicators are not formed yet
if (!_ichimoku.IsFormed || !_widthAverage.IsFormed)
{
return;
}
// Cloud width breakout detection
if (width > avgWidth * Multiplier && Position == 0)
{
// Determine trade direction based on price relative to cloud
var upperCloud = Math.Max(senkouSpanA, senkouSpanB);
var lowerCloud = Math.Min(senkouSpanA, senkouSpanB);
var bullish = candle.ClosePrice > upperCloud;
var bearish = candle.ClosePrice < lowerCloud;
if (bullish)
{
BuyMarket();
}
else if (bearish)
{
SellMarket();
}
}
}
}
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 Ichimoku, SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
from datatype_extensions import *
from indicator_extensions import *
class ichimoku_width_breakout_strategy(Strategy):
"""
Strategy that trades on Ichimoku Cloud width breakouts.
When Ichimoku Cloud width increases significantly above its average,
it enters position in the direction determined by price location relative to the cloud.
"""
def __init__(self):
super(ichimoku_width_breakout_strategy, self).__init__()
self._tenkanPeriod = self.Param("TenkanPeriod", 9) \
.SetGreaterThanZero() \
.SetDisplay("Tenkan Period", "Period for Tenkan-sen line", "Indicators") \
.SetCanOptimize(True) \
.SetOptimize(5, 20, 1)
self._kijunPeriod = self.Param("KijunPeriod", 26) \
.SetGreaterThanZero() \
.SetDisplay("Kijun Period", "Period for Kijun-sen line", "Indicators") \
.SetCanOptimize(True) \
.SetOptimize(20, 40, 2)
self._senkouSpanBPeriod = self.Param("SenkouSpanBPeriod", 52) \
.SetGreaterThanZero() \
.SetDisplay("Senkou Span B Period", "Period for Senkou Span B line", "Indicators") \
.SetCanOptimize(True) \
.SetOptimize(40, 80, 4)
self._avgPeriod = self.Param("AvgPeriod", 20) \
.SetGreaterThanZero() \
.SetDisplay("Average Period", "Period for cloud width average calculation", "Indicators") \
.SetCanOptimize(True) \
.SetOptimize(10, 50, 5)
self._multiplier = self.Param("Multiplier", 1.0) \
.SetGreaterThanZero() \
.SetDisplay("Multiplier", "Standard deviation multiplier for breakout detection", "Indicators") \
.SetCanOptimize(True) \
.SetOptimize(1.0, 3.0, 0.5)
self._candleType = self.Param("CandleType", tf(5)) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._stopLoss = self.Param("StopLoss", 2.0) \
.SetGreaterThanZero() \
.SetDisplay("Stop Loss %", "Stop Loss percentage", "Risk Management") \
.SetCanOptimize(True) \
.SetOptimize(1.0, 5.0, 0.5)
self._ichimoku = None
self._widthAverage = None
@property
def TenkanPeriod(self):
return self._tenkanPeriod.Value
@TenkanPeriod.setter
def TenkanPeriod(self, value):
self._tenkanPeriod.Value = value
@property
def KijunPeriod(self):
return self._kijunPeriod.Value
@KijunPeriod.setter
def KijunPeriod(self, value):
self._kijunPeriod.Value = value
@property
def SenkouSpanBPeriod(self):
return self._senkouSpanBPeriod.Value
@SenkouSpanBPeriod.setter
def SenkouSpanBPeriod(self, value):
self._senkouSpanBPeriod.Value = value
@property
def AvgPeriod(self):
return self._avgPeriod.Value
@AvgPeriod.setter
def AvgPeriod(self, value):
self._avgPeriod.Value = value
@property
def Multiplier(self):
return self._multiplier.Value
@Multiplier.setter
def Multiplier(self, value):
self._multiplier.Value = value
@property
def CandleType(self):
return self._candleType.Value
@CandleType.setter
def CandleType(self, value):
self._candleType.Value = value
@property
def StopLoss(self):
return self._stopLoss.Value
@StopLoss.setter
def StopLoss(self, value):
self._stopLoss.Value = value
def GetWorkingSecurities(self):
return [(self.Security, self.CandleType)]
def OnReseted(self):
super(ichimoku_width_breakout_strategy, self).OnReseted()
def OnStarted2(self, time):
super(ichimoku_width_breakout_strategy, self).OnStarted2(time)
# Create indicators
self._ichimoku = Ichimoku()
self._ichimoku.Tenkan.Length = self.TenkanPeriod
self._ichimoku.Kijun.Length = self.KijunPeriod
self._ichimoku.SenkouB.Length = self.SenkouSpanBPeriod
self._widthAverage = SimpleMovingAverage()
self._widthAverage.Length = self.AvgPeriod
# Create subscription
subscription = self.SubscribeCandles(self.CandleType)
# Bind Ichimoku
subscription.BindEx(self._ichimoku, self.ProcessIchimoku).Start()
# Enable stop loss protection
self.StartProtection(
takeProfit=Unit(2, UnitTypes.Percent),
stopLoss=Unit(self.StopLoss, UnitTypes.Percent)
)
# Create chart area for visualization
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, self._ichimoku)
self.DrawOwnTrades(area)
def ProcessIchimoku(self, candle, ichimoku_value):
if candle.State != CandleStates.Finished:
return
if not ichimoku_value.IsFinal:
return
# Get Ichimoku values
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)
# Calculate Cloud width
width = abs(senkou_a - senkou_b)
# Process width through average
avg_result = process_float(self._widthAverage, width, candle.ServerTime, True)
avg_width = float(avg_result)
# Skip if indicators are not formed yet
if not self._ichimoku.IsFormed or not self._widthAverage.IsFormed:
return
# Cloud width breakout detection
if width > avg_width * self.Multiplier and self.Position == 0:
# Determine trade direction based on price relative to cloud
upper_cloud = max(senkou_a, senkou_b)
lower_cloud = min(senkou_a, senkou_b)
close_price = float(candle.ClosePrice)
bullish = close_price > upper_cloud
bearish = close_price < lower_cloud
if bullish:
self.BuyMarket()
elif bearish:
self.SellMarket()
def CreateClone(self):
return ichimoku_width_breakout_strategy()