Ichimoku RSI MACD 策略
结合 Ichimoku 云、RSI 和 MACD 交叉信号的趋势跟随策略。
细节
- 入场条件:价格位于 Ichimoku 云之上/之下,配合 RSI 过滤和 MACD 线穿越信号线。
- 多空方向:双向。
- 出场条件:相反的 MACD 交叉。
- 止损:无。
- 默认值:
TenkanPeriod= 9KijunPeriod= 26SenkouSpanBPeriod= 52RsiLength= 14RsiOverbought= 70RsiOversold= 30MacdFast= 12MacdSlow= 26MacdSignal= 9CandleType= TimeSpan.FromHours(1)
- 过滤器:
- 类别:Trend Following
- 方向:双向
- 指标:Ichimoku、RSI、MACD
- 止损:无
- 复杂度:初级
- 时间框架:日内 (1 小时)
- 季节性:无
- 神经网络:无
- 背离:无
- 风险等级:中
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 RSI MACD strategy.
/// A long opens when the close is above the Ichimoku cloud, RSI is below RsiOverbought and the MACD line crosses above its signal line;
/// a short opens when the close is below the cloud, RSI is above RsiOversold and MACD crosses below its signal line.
/// The opposite MACD crossover closes the position (or reverses it when the opposite entry conditions also hold). There are no stops.
/// </summary>
public class IchimokuRsiMacdStrategy : Strategy
{
private readonly StrategyParam<int> _tenkanPeriod;
private readonly StrategyParam<int> _kijunPeriod;
private readonly StrategyParam<int> _senkouSpanBPeriod;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<decimal> _rsiOverbought;
private readonly StrategyParam<decimal> _rsiOversold;
private readonly StrategyParam<int> _macdFast;
private readonly StrategyParam<int> _macdSlow;
private readonly StrategyParam<int> _macdSignal;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevMacd;
private decimal? _prevSignal;
/// <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>
/// RSI period.
/// </summary>
public int RsiLength
{
get => _rsiLength.Value;
set => _rsiLength.Value = value;
}
/// <summary>
/// RSI level above which longs are not opened.
/// </summary>
public decimal RsiOverbought
{
get => _rsiOverbought.Value;
set => _rsiOverbought.Value = value;
}
/// <summary>
/// RSI level below which shorts are not opened.
/// </summary>
public decimal RsiOversold
{
get => _rsiOversold.Value;
set => _rsiOversold.Value = value;
}
/// <summary>
/// Fast EMA period of MACD.
/// </summary>
public int MacdFast
{
get => _macdFast.Value;
set => _macdFast.Value = value;
}
/// <summary>
/// Slow EMA period of MACD.
/// </summary>
public int MacdSlow
{
get => _macdSlow.Value;
set => _macdSlow.Value = value;
}
/// <summary>
/// Signal line period of MACD.
/// </summary>
public int MacdSignal
{
get => _macdSignal.Value;
set => _macdSignal.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public IchimokuRsiMacdStrategy()
{
_tenkanPeriod = Param(nameof(TenkanPeriod), 9)
.SetGreaterThanZero()
.SetDisplay("Tenkan Period", "Tenkan-sen period", "Ichimoku");
_kijunPeriod = Param(nameof(KijunPeriod), 26)
.SetGreaterThanZero()
.SetDisplay("Kijun Period", "Kijun-sen period", "Ichimoku");
_senkouSpanBPeriod = Param(nameof(SenkouSpanBPeriod), 52)
.SetGreaterThanZero()
.SetDisplay("Senkou Span B Period", "Senkou Span B period", "Ichimoku");
_rsiLength = Param(nameof(RsiLength), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Length", "RSI period", "RSI");
_rsiOverbought = Param(nameof(RsiOverbought), 70m)
.SetDisplay("RSI Overbought", "RSI level above which longs are not opened", "RSI");
_rsiOversold = Param(nameof(RsiOversold), 30m)
.SetDisplay("RSI Oversold", "RSI level below which shorts are not opened", "RSI");
_macdFast = Param(nameof(MacdFast), 12)
.SetGreaterThanZero()
.SetDisplay("MACD Fast", "Fast EMA period of MACD", "MACD");
_macdSlow = Param(nameof(MacdSlow), 26)
.SetGreaterThanZero()
.SetDisplay("MACD Slow", "Slow EMA period of MACD", "MACD");
_macdSignal = Param(nameof(MacdSignal), 9)
.SetGreaterThanZero()
.SetDisplay("MACD Signal", "Signal line period of MACD", "MACD");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).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();
_prevMacd = null;
_prevSignal = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevMacd = null;
_prevSignal = null;
var ichimoku = new Ichimoku
{
Tenkan = { Length = TenkanPeriod },
Kijun = { Length = KijunPeriod },
SenkouB = { Length = SenkouSpanBPeriod },
};
var rsi = new RelativeStrengthIndex { Length = RsiLength };
var macd = new MovingAverageConvergenceDivergenceSignal
{
Macd =
{
ShortMa = { Length = MacdFast },
LongMa = { Length = MacdSlow },
},
SignalMa = { Length = MacdSignal }
};
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(ichimoku, rsi, macd, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ichimoku);
DrawOwnTrades(area);
var oscillators = CreateChartArea();
if (oscillators != null)
{
DrawIndicator(oscillators, rsi);
DrawIndicator(oscillators, macd);
}
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue ichimokuValue, IIndicatorValue rsiValue, IIndicatorValue macdValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!macdValue.IsFormed || macdValue is not MovingAverageConvergenceDivergenceSignalValue { Macd: decimal macd, Signal: decimal signal })
return;
var prevMacd = _prevMacd;
var prevSignal = _prevSignal;
_prevMacd = macd;
_prevSignal = signal;
if (prevMacd is not decimal pm || prevSignal is not decimal ps)
return;
if (!ichimokuValue.IsFormed || ichimokuValue is not IchimokuValue { SenkouA: decimal senkouA, SenkouB: decimal senkouB })
return;
if (!rsiValue.IsFormed)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var rsi = rsiValue.GetValue<decimal>();
var close = candle.ClosePrice;
var cloudTop = Math.Max(senkouA, senkouB);
var cloudBottom = Math.Min(senkouA, senkouB);
var crossUp = pm <= ps && macd > signal;
var crossDown = pm >= ps && macd < signal;
if (crossUp)
{
if (close > cloudTop && rsi < RsiOverbought && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
else if (Position < 0)
BuyMarket(-Position);
}
else if (crossDown)
{
if (close < cloudBottom && rsi > RsiOversold && Position >= 0)
SellMarket(Volume + Math.Abs(Position));
else if (Position > 0)
SellMarket(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, RelativeStrengthIndex, MovingAverageConvergenceDivergenceSignal
from StockSharp.Algo.Strategies import Strategy
class ichimoku_rsi_macd_strategy(Strategy):
"""
Ichimoku RSI MACD strategy.
A long opens when the close is above the Ichimoku cloud, RSI is below RsiOverbought and the MACD line crosses above its signal line;
a short opens when the close is below the cloud, RSI is above RsiOversold and MACD crosses below its signal line.
The opposite MACD crossover closes the position (or reverses it when the opposite entry conditions also hold). There are no stops.
"""
def __init__(self):
super(ichimoku_rsi_macd_strategy, self).__init__()
self._tenkan_period = self.Param("TenkanPeriod", 9).SetGreaterThanZero().SetDisplay("Tenkan Period", "Tenkan-sen period", "Ichimoku")
self._kijun_period = self.Param("KijunPeriod", 26).SetGreaterThanZero().SetDisplay("Kijun Period", "Kijun-sen period", "Ichimoku")
self._senkou_span_b_period = self.Param("SenkouSpanBPeriod", 52).SetGreaterThanZero().SetDisplay("Senkou Span B Period", "Senkou Span B period", "Ichimoku")
self._rsi_length = self.Param("RsiLength", 14).SetGreaterThanZero().SetDisplay("RSI Length", "RSI period", "RSI")
self._rsi_overbought = self.Param("RsiOverbought", 70.0).SetDisplay("RSI Overbought", "RSI level above which longs are not opened", "RSI")
self._rsi_oversold = self.Param("RsiOversold", 30.0).SetDisplay("RSI Oversold", "RSI level below which shorts are not opened", "RSI")
self._macd_fast = self.Param("MacdFast", 12).SetGreaterThanZero().SetDisplay("MACD Fast", "Fast EMA period of MACD", "MACD")
self._macd_slow = self.Param("MacdSlow", 26).SetGreaterThanZero().SetDisplay("MACD Slow", "Slow EMA period of MACD", "MACD")
self._macd_signal = self.Param("MacdSignal", 9).SetGreaterThanZero().SetDisplay("MACD Signal", "Signal line period of MACD", "MACD")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._reset_state()
@property
def candle_type(self):
return self._candle_type.Value
def _reset_state(self):
self._prev_macd = None
self._prev_signal = None
def OnReseted(self):
super(ichimoku_rsi_macd_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(ichimoku_rsi_macd_strategy, self).OnStarted2(time)
self._reset_state()
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
rsi = RelativeStrengthIndex()
rsi.Length = self._rsi_length.Value
macd = MovingAverageConvergenceDivergenceSignal()
macd.Macd.ShortMa.Length = self._macd_fast.Value
macd.Macd.LongMa.Length = self._macd_slow.Value
macd.SignalMa.Length = self._macd_signal.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(ichimoku, rsi, macd, 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, rsi)
self.DrawIndicator(oscillators, macd)
def _process_candle(self, candle, ichimoku_value, rsi_value, macd_value):
if candle.State != CandleStates.Finished:
return
if not macd_value.IsFormed or macd_value.Macd is None or macd_value.Signal is None:
return
macd = macd_value.Macd
signal = macd_value.Signal
prev_macd = self._prev_macd
prev_signal = self._prev_signal
self._prev_macd = macd
self._prev_signal = signal
if prev_macd is None or prev_signal is None:
return
if not ichimoku_value.IsFormed or ichimoku_value.SenkouA is None or ichimoku_value.SenkouB is None:
return
if not rsi_value.IsFormed:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
rsi = rsi_value.GetValue[Decimal](None)
close = candle.ClosePrice
senkou_a = ichimoku_value.SenkouA
senkou_b = ichimoku_value.SenkouB
cloud_top = max(senkou_a, senkou_b)
cloud_bottom = min(senkou_a, senkou_b)
cross_up = prev_macd <= prev_signal and macd > signal
cross_down = prev_macd >= prev_signal and macd < signal
if cross_up:
if close > cloud_top and rsi < Decimal(self._rsi_overbought.Value) and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif self.Position < 0:
self.BuyMarket(-self.Position)
elif cross_down:
if close < cloud_bottom and rsi > Decimal(self._rsi_oversold.Value) and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
elif self.Position > 0:
self.SellMarket(self.Position)
def CreateClone(self):
return ichimoku_rsi_macd_strategy()