EMA RSIスイング・トレンドフィルター戦略
この戦略は、EMA200のトレンドフィルターの方向に従ってEMA20とEMA50のクロスオーバーを取引します。 オプションのRSIフィルターにより、RSIが買われすぎの場合はロングエントリーを、売られすぎの場合はショートエントリーを制限します。
詳細
- エントリー条件: EMA20がEMA50をクロスし、EMA200に対する価格の位置とオプションのRSIフィルターを確認。
- ロング/ショート: 両方向。
- エグジット条件: 逆EMAクロスでのオプションのエグジット。
- ストップ: なし。
- デフォルト値:
EmaFastPeriod= 20EmaSlowPeriod= 50EmaTrendPeriod= 200RsiLength= 14UseRsiFilter= trueRsiMaxLong= 70RsiMinShort= 30RequireCloseConfirm= trueExitOnOpposite= trueCandleType= TimeSpan.FromMinutes(5)
- フィルター:
- カテゴリ: トレンド
- 方向: 両方
- インジケーター: EMA, RSI
- ストップ: いいえ
- 複雑さ: 基本
- 時間軸: イントラデイ
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 中
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>
/// EMA RSI swing trend filter strategy.
/// The fast EMA crossing above the slow EMA with the close above the trend EMA goes long, the bearish cross with the close below
/// it goes short. With UseRsiFilter longs need RSI below RsiMaxLong and shorts RSI above RsiMinShort. With ExitOnOpposite an
/// opposite EMA cross closes the position even when the entry filters reject a new trade.
/// </summary>
public class EmaRsiSwingTrendFilterStrategy : Strategy
{
private readonly StrategyParam<int> _emaFastPeriod;
private readonly StrategyParam<int> _emaSlowPeriod;
private readonly StrategyParam<int> _emaTrendPeriod;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<bool> _useRsiFilter;
private readonly StrategyParam<decimal> _rsiMaxLong;
private readonly StrategyParam<decimal> _rsiMinShort;
private readonly StrategyParam<bool> _requireCloseConfirm;
private readonly StrategyParam<bool> _exitOnOpposite;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevFast;
private decimal? _prevSlow;
/// <summary>
/// Fast EMA period.
/// </summary>
public int EmaFastPeriod
{
get => _emaFastPeriod.Value;
set => _emaFastPeriod.Value = value;
}
/// <summary>
/// Slow EMA period.
/// </summary>
public int EmaSlowPeriod
{
get => _emaSlowPeriod.Value;
set => _emaSlowPeriod.Value = value;
}
/// <summary>
/// Trend EMA period.
/// </summary>
public int EmaTrendPeriod
{
get => _emaTrendPeriod.Value;
set => _emaTrendPeriod.Value = value;
}
/// <summary>
/// RSI length.
/// </summary>
public int RsiLength
{
get => _rsiLength.Value;
set => _rsiLength.Value = value;
}
/// <summary>
/// Use the RSI filter.
/// </summary>
public bool UseRsiFilter
{
get => _useRsiFilter.Value;
set => _useRsiFilter.Value = value;
}
/// <summary>
/// RSI below which longs are allowed.
/// </summary>
public decimal RsiMaxLong
{
get => _rsiMaxLong.Value;
set => _rsiMaxLong.Value = value;
}
/// <summary>
/// RSI above which shorts are allowed.
/// </summary>
public decimal RsiMinShort
{
get => _rsiMinShort.Value;
set => _rsiMinShort.Value = value;
}
/// <summary>
/// Act only on closed candles.
/// </summary>
public bool RequireCloseConfirm
{
get => _requireCloseConfirm.Value;
set => _requireCloseConfirm.Value = value;
}
/// <summary>
/// Close the position on an opposite EMA cross.
/// </summary>
public bool ExitOnOpposite
{
get => _exitOnOpposite.Value;
set => _exitOnOpposite.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public EmaRsiSwingTrendFilterStrategy()
{
_emaFastPeriod = Param(nameof(EmaFastPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("Fast EMA", "Fast EMA period", "Indicators");
_emaSlowPeriod = Param(nameof(EmaSlowPeriod), 50)
.SetGreaterThanZero()
.SetDisplay("Slow EMA", "Slow EMA period", "Indicators");
_emaTrendPeriod = Param(nameof(EmaTrendPeriod), 200)
.SetGreaterThanZero()
.SetDisplay("Trend EMA", "Trend EMA period", "Indicators");
_rsiLength = Param(nameof(RsiLength), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Length", "RSI length", "Indicators");
_useRsiFilter = Param(nameof(UseRsiFilter), true)
.SetDisplay("Use RSI Filter", "Use the RSI filter", "Filters");
_rsiMaxLong = Param(nameof(RsiMaxLong), 70m)
.SetDisplay("RSI Max Long", "RSI below which longs are allowed", "Filters");
_rsiMinShort = Param(nameof(RsiMinShort), 30m)
.SetDisplay("RSI Min Short", "RSI above which shorts are allowed", "Filters");
_requireCloseConfirm = Param(nameof(RequireCloseConfirm), true)
.SetDisplay("Close Confirm", "Act only on closed candles", "Filters");
_exitOnOpposite = Param(nameof(ExitOnOpposite), true)
.SetDisplay("Exit On Opposite", "Close the position on an opposite EMA cross", "Exits");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).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();
_prevFast = null;
_prevSlow = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevFast = null;
_prevSlow = null;
var fast = new ExponentialMovingAverage { Length = EmaFastPeriod };
var slow = new ExponentialMovingAverage { Length = EmaSlowPeriod };
var trend = new ExponentialMovingAverage { Length = EmaTrendPeriod };
var rsi = new RelativeStrengthIndex { Length = RsiLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fast, slow, trend, rsi, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fast);
DrawIndicator(area, slow);
DrawIndicator(area, trend);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow, decimal trend, decimal rsi)
{
// Signals are always evaluated on closed candles, which is what RequireCloseConfirm asks for.
if (candle.State != CandleStates.Finished)
return;
var prevFast = _prevFast;
var prevSlow = _prevSlow;
_prevFast = fast;
_prevSlow = slow;
if (prevFast is not decimal pf || prevSlow is not decimal ps)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var crossUp = pf <= ps && fast > slow;
var crossDown = pf >= ps && fast < slow;
var close = candle.ClosePrice;
var longOk = crossUp && close > trend && (!UseRsiFilter || rsi < RsiMaxLong);
var shortOk = crossDown && close < trend && (!UseRsiFilter || rsi > RsiMinShort);
if (longOk && Position <= 0)
{
BuyMarket(Volume + Math.Abs(Position));
}
else if (shortOk && Position >= 0)
{
SellMarket(Volume + Math.Abs(Position));
}
else if (ExitOnOpposite)
{
if (Position > 0 && crossDown)
SellMarket(Position);
else if (Position < 0 && crossUp)
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
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage, RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class ema_rsi_swing_trend_filter_strategy(Strategy):
"""
EMA RSI swing trend filter strategy.
The fast EMA crossing above the slow EMA with the close above the trend EMA goes long, the bearish cross with the close below
it goes short. With UseRsiFilter longs need RSI below RsiMaxLong and shorts RSI above RsiMinShort. With ExitOnOpposite an
opposite EMA cross closes the position even when the entry filters reject a new trade.
"""
def __init__(self):
super(ema_rsi_swing_trend_filter_strategy, self).__init__()
self._ema_fast_period = self.Param("EmaFastPeriod", 20).SetGreaterThanZero().SetDisplay("Fast EMA", "Fast EMA period", "Indicators")
self._ema_slow_period = self.Param("EmaSlowPeriod", 50).SetGreaterThanZero().SetDisplay("Slow EMA", "Slow EMA period", "Indicators")
self._ema_trend_period = self.Param("EmaTrendPeriod", 200).SetGreaterThanZero().SetDisplay("Trend EMA", "Trend EMA period", "Indicators")
self._rsi_length = self.Param("RsiLength", 14).SetGreaterThanZero().SetDisplay("RSI Length", "RSI length", "Indicators")
self._use_rsi_filter = self.Param("UseRsiFilter", True).SetDisplay("Use RSI Filter", "Use the RSI filter", "Filters")
self._rsi_max_long = self.Param("RsiMaxLong", 70.0).SetDisplay("RSI Max Long", "RSI below which longs are allowed", "Filters")
self._rsi_min_short = self.Param("RsiMinShort", 30.0).SetDisplay("RSI Min Short", "RSI above which shorts are allowed", "Filters")
self._require_close_confirm = self.Param("RequireCloseConfirm", True).SetDisplay("Close Confirm", "Act only on closed candles", "Filters")
self._exit_on_opposite = self.Param("ExitOnOpposite", True).SetDisplay("Exit On Opposite", "Close the position on an opposite EMA cross", "Exits")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).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_fast = None
self._prev_slow = None
def OnReseted(self):
super(ema_rsi_swing_trend_filter_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(ema_rsi_swing_trend_filter_strategy, self).OnStarted2(time)
self._reset_state()
fast = ExponentialMovingAverage()
fast.Length = self._ema_fast_period.Value
slow = ExponentialMovingAverage()
slow.Length = self._ema_slow_period.Value
trend = ExponentialMovingAverage()
trend.Length = self._ema_trend_period.Value
rsi = RelativeStrengthIndex()
rsi.Length = self._rsi_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(fast, slow, trend, rsi, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast)
self.DrawIndicator(area, slow)
self.DrawIndicator(area, trend)
self.DrawOwnTrades(area)
def _process_candle(self, candle, fast_value, slow_value, trend_value, rsi_value):
# Signals are always evaluated on closed candles, which is what RequireCloseConfirm asks for.
if candle.State != CandleStates.Finished:
return
fast = float(fast_value)
slow = float(slow_value)
trend = float(trend_value)
rsi = float(rsi_value)
prev_fast = self._prev_fast
prev_slow = self._prev_slow
self._prev_fast = fast
self._prev_slow = slow
if prev_fast is None or prev_slow is None:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
cross_up = prev_fast <= prev_slow and fast > slow
cross_down = prev_fast >= prev_slow and fast < slow
close = float(candle.ClosePrice)
use_rsi = self._use_rsi_filter.Value
long_ok = cross_up and close > trend and (not use_rsi or rsi < float(self._rsi_max_long.Value))
short_ok = cross_down and close < trend and (not use_rsi or rsi > float(self._rsi_min_short.Value))
if long_ok and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif short_ok and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
elif self._exit_on_opposite.Value:
if self.Position > 0 and cross_down:
self.SellMarket(self.Position)
elif self.Position < 0 and cross_up:
self.BuyMarket(-self.Position)
def CreateClone(self):
return ema_rsi_swing_trend_filter_strategy()