一目均衡表 Tenkan/Kijun 交叉策略
一目均衡表是一套完整的趋势跟随系统。本策略关注 Tenkan 线与 Kijun 线的交叉,并结合价格相对于云层的位置。Tenkan 在云层上方上穿 Kijun 表示趋势可能继续上行;在云层下方下穿则表明疲弱。
测试表明年均收益约为 142%,该策略在股票市场表现最佳。
运行中,策略在每根K线上计算一目均衡表组件。当 Tenkan 上穿 Kijun 且价格位于云层上方时开多仓,止损设在 Kijun 附近。反之,在云层下方的向下交叉则做空,止损同样靠近 Kijun。
系统持仓直至止损被触发或再次交叉,力求捕捉沿云层方向的持续行情。
细节
- 入场条件:Tenkan/Kijun 交叉并结合价格相对云层的位置。
- 多/空:双向。
- 退出条件:止损或相反交叉。
- 止损:有,在 Kijun 附近。
- 默认值:
TenkanPeriod= 9KijunPeriod= 26SenkouSpanBPeriod= 52CandleType= 30 分钟
- 过滤条件:
- 类别: 趋势跟随
- 方向: 双向
- 指标: 一目均衡表
- 止损: 有
- 复杂度: 中等
- 时间框架: 波段
- 季节性: 无
- 神经网络: 无
- 背离: 无
- 风险级别: 中等
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 Tenkan/Kijun Cross strategy.
/// Tenkan-sen crossing above Kijun-sen while the close is above the cloud opens a long position, the opposite cross below the
/// cloud a short one. The stop is the Kijun-sen at entry; an opposite cross closes the position, reversing it when the close
/// is on the other side of the cloud.
/// </summary>
public class IchimokuTenkanKijunStrategy : Strategy
{
private readonly StrategyParam<int> _tenkanPeriod;
private readonly StrategyParam<int> _kijunPeriod;
private readonly StrategyParam<int> _senkouSpanBPeriod;
private readonly StrategyParam<DataType> _candleType;
private bool? _prevTenkanAbove;
private decimal _stopPrice;
/// <summary>
/// Tenkan-sen period.
/// </summary>
public int TenkanPeriod
{
get => _tenkanPeriod.Value;
set => _tenkanPeriod.Value = value;
}
/// <summary>
/// Kijun-sen period.
/// </summary>
public int KijunPeriod
{
get => _kijunPeriod.Value;
set => _kijunPeriod.Value = value;
}
/// <summary>
/// Senkou Span B period.
/// </summary>
public int SenkouSpanBPeriod
{
get => _senkouSpanBPeriod.Value;
set => _senkouSpanBPeriod.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public IchimokuTenkanKijunStrategy()
{
_tenkanPeriod = Param(nameof(TenkanPeriod), 9)
.SetGreaterThanZero()
.SetDisplay("Tenkan Period", "Period for Tenkan-sen", "Ichimoku");
_kijunPeriod = Param(nameof(KijunPeriod), 26)
.SetGreaterThanZero()
.SetDisplay("Kijun Period", "Period for Kijun-sen", "Ichimoku");
_senkouSpanBPeriod = Param(nameof(SenkouSpanBPeriod), 52)
.SetGreaterThanZero()
.SetDisplay("Senkou Span B Period", "Period for Senkou Span B", "Ichimoku");
_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();
_prevTenkanAbove = null;
_stopPrice = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevTenkanAbove = null;
_stopPrice = default;
var ichimoku = new Ichimoku
{
Tenkan = { Length = TenkanPeriod },
Kijun = { Length = KijunPeriod },
SenkouB = { Length = SenkouSpanBPeriod }
};
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(ichimoku, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ichimoku);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue ichimokuIv)
{
if (candle.State != CandleStates.Finished)
return;
if (ichimokuIv is not IIchimokuValue { Tenkan: decimal tenkan, Kijun: decimal kijun, SenkouA: decimal senkouA, SenkouB: decimal senkouB })
return;
// Equal lines are no cross; the previous side holds.
var wasAbove = _prevTenkanAbove;
if (tenkan != kijun)
_prevTenkanAbove = tenkan > kijun;
if (wasAbove is not bool previous || !IsFormedAndOnlineAndAllowTrading())
return;
var bullishCross = !previous && tenkan > kijun;
var bearishCross = previous && tenkan < kijun;
var close = candle.ClosePrice;
var upperKumo = Math.Max(senkouA, senkouB);
var lowerKumo = Math.Min(senkouA, senkouB);
if (Position > 0)
{
if (bearishCross && close < lowerKumo)
{
SellMarket(Volume + Position);
_stopPrice = kijun;
}
else if (bearishCross || close <= _stopPrice)
{
SellMarket(Position);
}
}
else if (Position < 0)
{
if (bullishCross && close > upperKumo)
{
BuyMarket(Volume - Position);
_stopPrice = kijun;
}
else if (bullishCross || close >= _stopPrice)
{
BuyMarket(-Position);
}
}
else if (bullishCross && close > upperKumo)
{
BuyMarket(Volume);
_stopPrice = kijun;
}
else if (bearishCross && close < lowerKumo)
{
SellMarket(Volume);
_stopPrice = kijun;
}
}
}
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, Decimal
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import Ichimoku
from StockSharp.Algo.Strategies import Strategy
class ichimoku_tenkan_kijun_strategy(Strategy):
"""
Ichimoku Tenkan/Kijun Cross strategy.
Tenkan-sen crossing above Kijun-sen while the close is above the cloud opens a long position, the opposite cross below the
cloud a short one. The stop is the Kijun-sen at entry; an opposite cross closes the position, reversing it when the close
is on the other side of the cloud.
"""
def __init__(self):
super(ichimoku_tenkan_kijun_strategy, self).__init__()
self._tenkan_period = self.Param("TenkanPeriod", 9).SetGreaterThanZero().SetDisplay("Tenkan Period", "Period for Tenkan-sen", "Ichimoku")
self._kijun_period = self.Param("KijunPeriod", 26).SetGreaterThanZero().SetDisplay("Kijun Period", "Period for Kijun-sen", "Ichimoku")
self._senkou_span_b_period = self.Param("SenkouSpanBPeriod", 52).SetGreaterThanZero().SetDisplay("Senkou Span B Period", "Period for Senkou Span B", "Ichimoku")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._prev_tenkan_above = None
self._stop_price = Decimal(0)
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(ichimoku_tenkan_kijun_strategy, self).OnReseted()
self._prev_tenkan_above = None
self._stop_price = Decimal(0)
def OnStarted2(self, time):
super(ichimoku_tenkan_kijun_strategy, self).OnStarted2(time)
self._prev_tenkan_above = None
self._stop_price = Decimal(0)
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.candle_type)
subscription.BindEx(ichimoku, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ichimoku)
self.DrawOwnTrades(area)
def _process_candle(self, candle, ichimoku_iv):
if candle.State != CandleStates.Finished:
return
tenkan = ichimoku_iv.Tenkan
kijun = ichimoku_iv.Kijun
senkou_a = ichimoku_iv.SenkouA
senkou_b = ichimoku_iv.SenkouB
if tenkan is None or kijun is None or senkou_a is None or senkou_b is None:
return
# Equal lines are no cross; the previous side holds.
was_above = self._prev_tenkan_above
if tenkan != kijun:
self._prev_tenkan_above = tenkan > kijun
if was_above is None or not self.IsFormedAndOnlineAndAllowTrading():
return
bullish_cross = not was_above and tenkan > kijun
bearish_cross = was_above and tenkan < kijun
close = candle.ClosePrice
upper_kumo = Math.Max(senkou_a, senkou_b)
lower_kumo = Math.Min(senkou_a, senkou_b)
if self.Position > 0:
if bearish_cross and close < lower_kumo:
self.SellMarket(self.Volume + self.Position)
self._stop_price = kijun
elif bearish_cross or close <= self._stop_price:
self.SellMarket(self.Position)
elif self.Position < 0:
if bullish_cross and close > upper_kumo:
self.BuyMarket(self.Volume - self.Position)
self._stop_price = kijun
elif bullish_cross or close >= self._stop_price:
self.BuyMarket(-self.Position)
elif bullish_cross and close > upper_kumo:
self.BuyMarket(self.Volume)
self._stop_price = kijun
elif bearish_cross and close < lower_kumo:
self.SellMarket(self.Volume)
self._stop_price = kijun
def CreateClone(self):
return ichimoku_tenkan_kijun_strategy()