ColorMaRsi Trigger Strategy
This strategy is a StockSharp port of the original MQL5 expert exp_colormarsi-trigger.mq5. It compares fast and slow EMAs and fast and slow RSI values. The combined signal takes values -1, 0 or +1. A position is opened when the previous signal has the opposite sign to the current one.
How it works
- When the signal turns from positive to zero or negative, a long position is opened and any short position is closed.
- When the signal turns from negative to zero or positive, a short position is opened and any long position is closed.
Parameters
- Fast EMA – period for the fast exponential moving average.
- Slow EMA – period for the slow exponential moving average.
- Fast RSI – period for the fast RSI.
- Slow RSI – period for the slow RSI.
- Candle Type – timeframe of the candles used for calculation.
Indicators
- Exponential Moving Average (fast and slow)
- Relative Strength Index (fast and slow)
Only finished candles are processed. Orders are placed using BuyMarket and SellMarket.
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>
/// ColorMaRsi Trigger strategy.
/// Combines fast and slow EMA crossover with RSI crossover to generate trading signals.
/// </summary>
public class ColorMaRsiTriggerStrategy : Strategy
{
private readonly StrategyParam<int> _emaFastLength;
private readonly StrategyParam<int> _emaSlowLength;
private readonly StrategyParam<int> _rsiFastLength;
private readonly StrategyParam<int> _rsiSlowLength;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevSignal;
public int EmaFastLength { get => _emaFastLength.Value; set => _emaFastLength.Value = value; }
public int EmaSlowLength { get => _emaSlowLength.Value; set => _emaSlowLength.Value = value; }
public int RsiFastLength { get => _rsiFastLength.Value; set => _rsiFastLength.Value = value; }
public int RsiSlowLength { get => _rsiSlowLength.Value; set => _rsiSlowLength.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public ColorMaRsiTriggerStrategy()
{
_emaFastLength = Param(nameof(EmaFastLength), 5)
.SetGreaterThanZero()
.SetDisplay("Fast EMA", "Fast EMA period", "General");
_emaSlowLength = Param(nameof(EmaSlowLength), 10)
.SetGreaterThanZero()
.SetDisplay("Slow EMA", "Slow EMA period", "General");
_rsiFastLength = Param(nameof(RsiFastLength), 3)
.SetGreaterThanZero()
.SetDisplay("Fast RSI", "Fast RSI period", "General");
_rsiSlowLength = Param(nameof(RsiSlowLength), 13)
.SetGreaterThanZero()
.SetDisplay("Slow RSI", "Slow RSI period", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevSignal = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var emaFast = new ExponentialMovingAverage { Length = EmaFastLength };
var emaSlow = new ExponentialMovingAverage { Length = EmaSlowLength };
var rsiFast = new RelativeStrengthIndex { Length = RsiFastLength };
var rsiSlow = new RelativeStrengthIndex { Length = RsiSlowLength };
SubscribeCandles(CandleType)
.Bind(emaFast, emaSlow, rsiFast, rsiSlow, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal emaFastValue, decimal emaSlowValue, decimal rsiFastValue, decimal rsiSlowValue)
{
if (candle.State != CandleStates.Finished)
return;
// Compute composite signal from EMA and RSI crossovers
var signal = 0m;
if (emaFastValue > emaSlowValue)
signal += 1;
if (emaFastValue < emaSlowValue)
signal -= 1;
if (rsiFastValue > rsiSlowValue)
signal += 1;
if (rsiFastValue < rsiSlowValue)
signal -= 1;
// Clamp signal to [-1, 1]
signal = Math.Clamp(signal, -1, 1);
// Detect signal change for entry/exit
if (_prevSignal <= 0 && signal > 0 && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
}
else if (_prevSignal >= 0 && signal < 0 && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
}
_prevSignal = signal;
}
}
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 color_ma_rsi_trigger_strategy(Strategy):
"""
ColorMaRsi Trigger strategy.
Combines fast and slow EMA crossover with RSI crossover to generate trading signals.
"""
def __init__(self):
super(color_ma_rsi_trigger_strategy, self).__init__()
self._ema_fast_length = self.Param("EmaFastLength", 5) \
.SetDisplay("Fast EMA", "Fast EMA period", "General")
self._ema_slow_length = self.Param("EmaSlowLength", 10) \
.SetDisplay("Slow EMA", "Slow EMA period", "General")
self._rsi_fast_length = self.Param("RsiFastLength", 3) \
.SetDisplay("Fast RSI", "Fast RSI period", "General")
self._rsi_slow_length = self.Param("RsiSlowLength", 13) \
.SetDisplay("Slow RSI", "Slow RSI period", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._prev_signal = 0.0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(color_ma_rsi_trigger_strategy, self).OnReseted()
self._prev_signal = 0.0
def OnStarted2(self, time):
super(color_ma_rsi_trigger_strategy, self).OnStarted2(time)
ema_fast = ExponentialMovingAverage()
ema_fast.Length = self._ema_fast_length.Value
ema_slow = ExponentialMovingAverage()
ema_slow.Length = self._ema_slow_length.Value
rsi_fast = RelativeStrengthIndex()
rsi_fast.Length = self._rsi_fast_length.Value
rsi_slow = RelativeStrengthIndex()
rsi_slow.Length = self._rsi_slow_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(ema_fast, ema_slow, rsi_fast, rsi_slow, self.on_process).Start()
def on_process(self, candle, ema_fast_val, ema_slow_val, rsi_fast_val, rsi_slow_val):
if candle.State != CandleStates.Finished:
return
signal = 0.0
if ema_fast_val > ema_slow_val:
signal += 1
if ema_fast_val < ema_slow_val:
signal -= 1
if rsi_fast_val > rsi_slow_val:
signal += 1
if rsi_fast_val < rsi_slow_val:
signal -= 1
signal = max(-1.0, min(1.0, signal))
if self._prev_signal <= 0 and signal > 0 and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif self._prev_signal >= 0 and signal < 0 and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_signal = signal
def CreateClone(self):
return color_ma_rsi_trigger_strategy()