Market Trend Levels Non-Repainting
EMA crossover strategy that optionally filters trades using RSI. Long positions open when the fast EMA crosses above the slow EMA, while short trades trigger on the opposite cross. When ApplyExitFilters is enabled and the RSI filter is active, positions close if the RSI leaves the allowed zone.
Details
- Entry Conditions:
- Long:
Fast EMAcrosses aboveSlow EMAandRSI > RsiLongThresholdwhen enabled - Short:
Fast EMAcrosses belowSlow EMAandRSI < RsiShortThresholdwhen enabled
- Long:
- Exit Conditions: Opposite crossover or RSI filter failing when
ApplyExitFiltersis true - Type: Trend-following
- Indicators: EMA, RSI
- Timeframe: 5 minutes (default)
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;
public class MarketTrendLevelsNonRepaintingStrategy : Strategy
{
private readonly StrategyParam<int> _fastLength;
private readonly StrategyParam<int> _slowLength;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<int> _cooldownBars;
private readonly StrategyParam<DataType> _candleType;
private ExponentialMovingAverage _emaFast;
private ExponentialMovingAverage _emaSlow;
private RelativeStrengthIndex _rsi;
private decimal? _prevDiff;
private int _barsFromSignal;
public int FastLength { get => _fastLength.Value; set => _fastLength.Value = value; }
public int SlowLength { get => _slowLength.Value; set => _slowLength.Value = value; }
public int RsiLength { get => _rsiLength.Value; set => _rsiLength.Value = value; }
public int CooldownBars { get => _cooldownBars.Value; set => _cooldownBars.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public MarketTrendLevelsNonRepaintingStrategy()
{
_fastLength = Param(nameof(FastLength), 12);
_slowLength = Param(nameof(SlowLength), 25);
_rsiLength = Param(nameof(RsiLength), 14);
_cooldownBars = Param(nameof(CooldownBars), 3);
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(10).TimeFrame());
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_emaFast = null;
_emaSlow = null;
_rsi = null;
_prevDiff = null;
_barsFromSignal = CooldownBars;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_emaFast = new ExponentialMovingAverage { Length = FastLength };
_emaSlow = new ExponentialMovingAverage { Length = SlowLength };
_rsi = new RelativeStrengthIndex { Length = RsiLength };
_prevDiff = null;
_barsFromSignal = CooldownBars;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_emaFast, _emaSlow, _rsi, Process)
.Start();
}
private void Process(ICandleMessage candle, decimal fast, decimal slow, decimal rsiValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_emaFast.IsFormed || !_emaSlow.IsFormed || !_rsi.IsFormed)
{
_prevDiff = fast - slow;
return;
}
var diff = fast - slow;
var crossUp = _prevDiff.HasValue && _prevDiff <= 0 && diff > 0;
var crossDown = _prevDiff.HasValue && _prevDiff >= 0 && diff < 0;
_prevDiff = diff;
var filterLong = rsiValue > 52;
var filterShort = rsiValue < 48;
if (crossUp && Position <= 0 && filterLong)
BuyMarket();
if (crossDown && Position >= 0 && filterShort)
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
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage, RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class market_trend_levels_non_repainting_strategy(Strategy):
"""
Market Trend Levels: EMA crossover with RSI filter.
"""
def __init__(self):
super(market_trend_levels_non_repainting_strategy, self).__init__()
self._fast_length = self.Param("FastLength", 12).SetDisplay("Fast", "Fast EMA", "Indicators")
self._slow_length = self.Param("SlowLength", 25).SetDisplay("Slow", "Slow EMA", "Indicators")
self._rsi_length = self.Param("RsiLength", 14).SetDisplay("RSI", "RSI period", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(10))).SetDisplay("Candle Type", "Candles", "General")
self._prev_diff = None
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(market_trend_levels_non_repainting_strategy, self).OnReseted()
self._prev_diff = None
def OnStarted2(self, time):
super(market_trend_levels_non_repainting_strategy, self).OnStarted2(time)
self._prev_diff = None
self._ema_fast = ExponentialMovingAverage()
self._ema_fast.Length = self._fast_length.Value
self._ema_slow = ExponentialMovingAverage()
self._ema_slow.Length = self._slow_length.Value
self._rsi = RelativeStrengthIndex()
self._rsi.Length = self._rsi_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._ema_fast, self._ema_slow, self._rsi, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, self._ema_fast)
self.DrawIndicator(area, self._ema_slow)
self.DrawOwnTrades(area)
def _process_candle(self, candle, fast_val, slow_val, rsi_val):
if candle.State != CandleStates.Finished:
return
fast = float(fast_val)
slow = float(slow_val)
rsi = float(rsi_val)
diff = fast - slow
if not self._ema_fast.IsFormed or not self._ema_slow.IsFormed or not self._rsi.IsFormed:
self._prev_diff = diff
return
if self._prev_diff is None:
self._prev_diff = diff
return
cross_up = self._prev_diff <= 0 and diff > 0
cross_down = self._prev_diff >= 0 and diff < 0
self._prev_diff = diff
if cross_up and self.Position <= 0 and rsi > 52:
self.BuyMarket()
if cross_down and self.Position >= 0 and rsi < 48:
self.SellMarket()
def CreateClone(self):
return market_trend_levels_non_repainting_strategy()