This strategy is a StockSharp high level API port of the MetaTrader expert Exp_ColorMaRsi-Trigger_MMRec_Duplex.mq5. It runs
two independent MaRsi-Trigger blocks – one for long opportunities and another for short opportunities. Each block evaluates a
composite signal generated by comparing a fast and a slow moving average together with a fast and a slow RSI. The composite valu
e is clamped to the range [-1, 1], reproducing the behaviour of the original indicator: +1 marks bullish alignment, -1 mar
ks bearish alignment, and 0 indicates mixed conditions.
A money-management “MMRec” module monitors the latest trades for each direction. When a configurable number of losses appears wit
hin a moving window, the next trade switches to a reduced volume until performance recovers. This reproduces the adaptive positio
n sizing logic of the MetaTrader library TradeAlgorithms.mqh used by the expert.
Trading Logic
Indicator pipeline (per block):
Compute a fast moving average (MA_fast) and a slow moving average (MA_slow) on the selected applied price and timeframe.
Compute a fast RSI (RSI_fast) and a slow RSI (RSI_slow) on possibly different applied prices.
Build a color score: start at 0, add +1 if MA_fast > MA_slow or -1 otherwise, then add +1 if RSI_fast > RSI_slow
or -1 otherwise. Clamp the result to [-1, 1].
Store the score history and read it with the configured SignalBar shift (the default matches the MetaTrader implementation).
Long block:
Entry: allowed when no long position is open (shorts are covered first). The previous colour (SignalBar + 1) must be + 1 while the current colour (SignalBar) is ≤ 0, indicating the bullish block has just neutralised.
Exit: when the previous colour turns negative (-1) and exits are enabled.
Short block:
Entry: allowed when no short position is open (longs are closed first). The previous colour must be -1 while the curren
t colour is ≥ 0, signalling a fresh bearish-to-neutral transition.
Exit: when the previous colour turns positive and exits are enabled.
Stops and targets: optional stop-loss and take-profit distances are expressed in price steps and re-evaluated on every fini
shed candle. Crossing either boundary closes the respective position immediately.
Money management: the strategy stores the result of each completed trade (per direction) and counts the number of losses in
the latest HistoryDepth trades. If the loss count reaches LossTrigger, the next order uses the reduced volume. Otherwise, the
normal volume is used.
Parameters
Group
Name
Description
Default
Long Block
LongCandleType
Timeframe that feeds the long MaRsi-Trigger block.
H4
LongAllowOpen / LongAllowClose
Enable opening / closing long positions.
true
LongStopLossPoints / LongTakeProfitPoints
Protective distances in instrument points. Set to 0 to disable.
1000 / 2000
LongSignalBar
Number of completed bars to shift when sampling the indicator buffers.
1
LongRsiPeriod / LongRsiLongPeriod
Fast and slow RSI lengths.
3 / 13
LongMaPeriod / LongMaLongPeriod
Fast and slow moving average lengths.
5 / 10
LongRsiPrice / LongRsiLongPrice
Applied price for fast / slow RSI (Close, Open, High, Low, Median, Typical, Weighted).
Weighted / Median
LongMaPrice / LongMaLongPrice
Applied price for fast / slow MA.
Close / Close
LongMaType / LongMaLongType
Moving average algorithms (Simple, Exponential, Smoothed, Weighted).
Exponential / Exponential
Money Management
LongNormalVolume / LongReducedVolume
Standard and reduced long trade volume.
0.1 / 0.01
LongHistoryDepth
Number of recent long trades observed by the money management filter.
5
LongLossTrigger
Minimum count of losses inside the window to switch to reduced long volume.
3
Group
Name
Description
Default
Short Block
ShortCandleType
Timeframe that feeds the short MaRsi-Trigger block.
H4
ShortAllowOpen / ShortAllowClose
Enable opening / closing short positions.
true
ShortStopLossPoints / ShortTakeProfitPoints
Protective distances in instrument points. Set to 0 to disable.
1000 / 2000
ShortSignalBar
Number of completed bars to shift when sampling the indicator buffers.
1
ShortRsiPeriod / ShortRsiLongPeriod
Fast and slow RSI lengths.
3 / 13
ShortMaPeriod / ShortMaLongPeriod
Fast and slow moving average lengths.
5 / 10
ShortRsiPrice / ShortRsiLongPrice
Applied price for fast / slow RSI.
Weighted / Median
ShortMaPrice / ShortMaLongPrice
Applied price for fast / slow MA.
Close / Close
ShortMaType / ShortMaLongType
Moving average algorithms (Simple, Exponential, Smoothed, Weighted).
Exponential / Exponential
Money Management
ShortNormalVolume / ShortReducedVolume
Standard and reduced short trade volume.
0.1 / 0.01
ShortHistoryDepth
Number of recent short trades observed by the money management filter.
5
ShortLossTrigger
Minimum count of losses inside the window to switch to reduced short volume.
3
Notes
Applied price options follow MetaTrader semantics. For example, Weighted equals (High + Low + 2 * Close) / 4 and Typical e
quals (High + Low + Close) / 3.
When the long and short blocks share the same timeframe (default), a single candle subscription feeds both calculators.
Setting the loss trigger to 0 forces the reduced volume immediately, mirroring the behaviour of the original money management
helper.
The strategy uses market orders; the MetaTrader Deviation parameter is therefore not required.
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 ColorMaRsiTriggerMmRecDuplexStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private ExponentialMovingAverage _fast;
private ExponentialMovingAverage _slow;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
private int _cooldown;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }
public ColorMaRsiTriggerMmRecDuplexStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
protected override void OnReseted()
{
base.OnReseted();
_fast = null; _slow = null;
_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fast = new ExponentialMovingAverage { Length = FastPeriod };
_slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
subscription.Bind(_fast, _slow, ProcessCandle);
subscription.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished) return;
if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }
var close = candle.ClosePrice;
var step = Security?.PriceStep ?? 1m;
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }
_prevFast = fastValue; _prevSlow = slowValue;
}
}
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
from StockSharp.Algo.Strategies import Strategy
class color_ma_rsi_trigger_mm_rec_duplex_strategy(Strategy):
def __init__(self):
super(color_ma_rsi_trigger_mm_rec_duplex_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 14) \
.SetDisplay("Fast Period", "Fast MA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 50) \
.SetDisplay("Slow Period", "Slow MA period", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200) \
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 400) \
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def stop_loss_points(self):
return self._stop_loss_points.Value
@property
def take_profit_points(self):
return self._take_profit_points.Value
def OnReseted(self):
super(color_ma_rsi_trigger_mm_rec_duplex_strategy, self).OnReseted()
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(color_ma_rsi_trigger_mm_rec_duplex_strategy, self).OnStarted2(time)
self._fast = ExponentialMovingAverage()
self._fast.Length = self.fast_period
self._slow = ExponentialMovingAverage()
self._slow.Length = self.slow_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(5)))
subscription.Bind(self._fast, self._slow, self._process_candle)
subscription.Start()
def _process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_value)
slow_val = float(slow_value)
if not self._fast.IsFormed or not self._slow.IsFormed:
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
if self.Position > 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close <= self._entry_price - self.stop_loss_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close >= self._entry_price + self.take_profit_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close >= self._entry_price + self.stop_loss_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close <= self._entry_price - self.take_profit_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 100
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return color_ma_rsi_trigger_mm_rec_duplex_strategy()