GitHub で見る

ColorMaRsi Trigger MMRec Duplex Strategy

Overview

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

  1. 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).
  2. 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.
  3. 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.
  4. 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.

  5. 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;
	}
}