Ver en GitHub

Combo Right Perceptron Strategy

This strategy is a faithful StockSharp port of the MetaTrader expert advisor Combo_Right.mq4. It mixes a base Commodity Channel Index (CCI) momentum filter with three perceptrons that analyse open-price momentum over configurable bar strides. Depending on the PassMode the perceptrons can override the CCI signal and instruct the supervisor to open long or short positions with their dedicated risk parameters.

Trading Logic

  1. Subscribe to the configured candle type and calculate the CCI on open prices. The last completed candle provides both the close price and the historical open values for perceptron inputs.
  2. Maintain a circular buffer of open prices so that the perceptrons can access the open of period, 2*period, 3*period and 4*period bars ago without relying on indicator history getters.
  3. When a finished candle arrives:
    • Evaluate the CCI value. This acts as the default signal (> 0 = long, < 0 = short) with the base protective distances (TakeProfit1 / StopLoss1).
    • Depending on PassMode, compute one or several perceptrons. Each perceptron uses weights derived from the original MQL inputs (X** - 100) and the differences between the most recent close and historical opens.
    • If a perceptron condition is satisfied it overrides the default signal and assigns its own stop-loss / take-profit distances before any order is sent.
  4. Cancel working orders, flatten opposite exposure and open the new position using the configured TradeVolume. After the market order is sent, call SetTakeProfit and SetStopLoss with the calculated offsets so the protective orders reflect the active perceptron branch.

Pass modes

  • Pass 1 – only the CCI value is considered. The signal is proportional to the latest indicator value.
  • Pass 2 – if the first perceptron (Perceptron1Period, X12…X42) produces a negative output the strategy immediately opens a short trade with the second risk profile. Otherwise it falls back to the CCI outcome.
  • Pass 3 – if the second perceptron is positive the strategy opens a long trade with the third risk profile. Otherwise it relies on the CCI output.
  • Pass 4 – first check the third perceptron. A positive value requires the second perceptron to be positive as well to permit a long entry with the bullish risk profile. If the third perceptron is negative and the first perceptron is below zero, the supervisor opens a short with the bearish risk profile. If neither branch triggers, the CCI output is used.

In all modes the strategy ignores signals until enough candles are collected to feed the deepest perceptron stride.

Risk Management

Every entry calculates fresh price offsets based on the symbol PriceStep. If the instrument does not provide a step the raw point distance is used as-is. SetTakeProfit and SetStopLoss receive the desired offsets together with the resulting net position so the protective brackets stay in sync with the current exposure.

Parameters

Name Type Default Description
TakeProfit1, StopLoss1 decimal 50 / 50 Profit and loss distances (in points) when the CCI output is used.
CciPeriod int 10 Period of the CCI calculated on open prices.
X12, X22, X32, X42 int 100 Raw weights for the bearish perceptron; the strategy internally subtracts 100 as in the original code.
TakeProfit2, StopLoss2 decimal 50 / 50 Risk distances (points) applied when the bearish perceptron triggers.
Perceptron1Period int 20 Stride between samples for the bearish perceptron (in bars).
X13, X23, X33, X43 int 100 Raw weights for the bullish perceptron.
TakeProfit3, StopLoss3 decimal 50 / 50 Risk distances (points) applied when the bullish perceptron triggers.
Perceptron2Period int 20 Stride between samples for the bullish perceptron (in bars).
X14, X24, X34, X44 int 100 Raw weights for the confirmation perceptron used in PassMode = 4.
Perceptron3Period int 20 Stride between samples for the confirmation perceptron (in bars).
PassMode int 1 Supervisor mode (1–4) that reproduces the branching logic of the MQL expert.
TradeVolume decimal 0.01 Volume used for new market entries. Opposite exposure is closed before entering.
CandleType DataType M1 Candle series feeding the CCI and perceptron inputs.

Notes

  • The implementation intentionally waits until all perceptrons have enough historical open prices before trading, preventing array-bound issues that were implicit in MetaTrader.
  • Indicator values are never retrieved through random access. Instead, the required history is stored in a circular buffer compliant with the project guidelines.
  • All comments and documentation are kept in English to match the repository requirements.
using System;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

public class ComboRightPerceptronStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _cooldownCandles;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevFast;
	private decimal _prevSlow;
	private bool _hasPrev;
	private int _cooldownRemaining;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int CooldownCandles { get => _cooldownCandles.Value; set => _cooldownCandles.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public ComboRightPerceptronStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 20).SetDisplay("Fast EMA", "Fast EMA period", "Indicators");
		_slowPeriod = Param(nameof(SlowPeriod), 80).SetDisplay("Slow EMA", "Slow EMA period", "Indicators");
		_cooldownCandles = Param(nameof(CooldownCandles), 100).SetDisplay("Cooldown", "Candles between signals", "General");
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame()).SetDisplay("Candle Type", "Candle timeframe", "General");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevFast = default;
		_prevSlow = default;
		_hasPrev = default;
		_cooldownRemaining = default;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevFast = 0;
		_prevSlow = 0;
		_hasPrev = false;
		_cooldownRemaining = 0;

		var fast = new ExponentialMovingAverage { Length = FastPeriod };
		var slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(fast, slow, ProcessCandle).Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!_hasPrev) { _prevFast = fast; _prevSlow = slow; _hasPrev = true; return; }

		if (_cooldownRemaining > 0)
		{
			_cooldownRemaining--;
			_prevFast = fast;
			_prevSlow = slow;
			return;
		}

		if (_prevFast <= _prevSlow && fast > slow && Position <= 0)
		{
			if (Position < 0) BuyMarket();
			BuyMarket();
			_cooldownRemaining = CooldownCandles;
		}
		else if (_prevFast >= _prevSlow && fast < slow && Position >= 0)
		{
			if (Position > 0) SellMarket();
			SellMarket();
			_cooldownRemaining = CooldownCandles;
		}
		_prevFast = fast;
		_prevSlow = slow;
	}
}