Auf GitHub ansehen

Perceptron Mult Strategy

This strategy ports the Peceptron_Mult.mq5 expert advisor to the StockSharp high-level API. It simultaneously monitors up to three independent markets and applies the Acceleration/Deceleration (AC) oscillator inside a perceptron model. Each market receives its own weight configuration, position sizing, and protective exits so the behaviour of the original multi-symbol advisor is preserved.

Trading Logic

  1. For every configured security the strategy subscribes to the same candle type (default: 1-minute).
  2. On each finished candle it calculates the Bill Williams Acceleration/Deceleration oscillator:
    • Compute the Awesome Oscillator (AO) from candle highs and lows (5/34 median price moving averages).
    • Subtract a 5-period simple moving average of AO from the current AO value.
  3. A rolling buffer with the latest 22 AC values is maintained per security.
  4. The perceptron signal is formed from four delayed AC values using weights (w - 100) exactly as in the MQL code:
    • AC[0], AC[7], AC[14], AC[21] correspond to the most recent and three historical readings.
  5. Entry rules:
    • Positive sum ⇒ open a long position if no position exists on that security.
    • Negative sum ⇒ open a short position if the security is flat.
  6. Exit rules:
    • Stop-loss and take-profit distances are expressed in points. They are converted to absolute price offsets using the instrument price step.
    • Protective exits are evaluated on every finished candle. A long trade is closed when the candle low hits the stop or the high reaches the profit target; shorts use the mirrored logic.
  7. Positions are mutually exclusive per security. The strategy ignores new signals while exposure remains open, replicating the original advisor behaviour.

Parameters

Parameter Description
FirstSecurity, SecondSecurity, ThirdSecurity Instruments processed by the perceptron. Leave null to disable a slot.
FirstOrderVolume, SecondOrderVolume, ThirdOrderVolume Market order size for each instrument.
FirstWeight1FirstWeight4, etc. Perceptron weights (MQL inputs x1…x12). The strategy internally subtracts 100 from each value before applying it.
FirstStopLossPoints, SecondStopLossPoints, ThirdStopLossPoints Stop-loss distance in price points for each instrument. Set to 0 to disable.
FirstTakeProfitPoints, SecondTakeProfitPoints, ThirdTakeProfitPoints Take-profit distance in price points for each instrument. Set to 0 to disable.
CandleType Candle series shared by all securities.

Implementation Notes

  • The strategy relies on AwesomeOscillator and SimpleMovingAverage indicators from StockSharp to reconstruct the AC oscillator, avoiding manual recalculations.
  • Rolling buffers are used only to emulate the perceptron inputs from the MQL implementation (indices 0, 7, 14, 21).
  • Protective levels are enforced without registering separate stop orders: the strategy monitors candle extremes and closes positions with market orders when levels are breached, mirroring the behaviour of the original EA on new ticks.
  • Each security maintains independent indicator state, order volume, and risk settings, matching the three-symbol structure of the source advisor.

Usage Tips

  1. Assign up to three securities in the parameter panel. Any unused slot can remain null.
  2. Adjust the point-based stops and targets to match the tick size of the selected instruments.
  3. Tune the perceptron weights to emphasise specific lags of the AC oscillator if optimisation is required.
  4. Because all instruments share the same candle type, ensure historical data is available for every configured security.
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>
/// Perceptron strategy that uses weighted moving average crossover signals
/// to determine trade direction. Simplified from multi-symbol to single security.
/// </summary>
public class PerceptronMultStrategy : 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;

	/// <summary>
	/// Fast EMA period.
	/// </summary>
	public int FastPeriod
	{
		get => _fastPeriod.Value;
		set => _fastPeriod.Value = value;
	}

	/// <summary>
	/// Slow EMA period.
	/// </summary>
	public int SlowPeriod
	{
		get => _slowPeriod.Value;
		set => _slowPeriod.Value = value;
	}

	/// <summary>
	/// Stop-loss distance in price steps.
	/// </summary>
	public int StopLossPoints
	{
		get => _stopLossPoints.Value;
		set => _stopLossPoints.Value = value;
	}

	/// <summary>
	/// Take-profit distance in price steps.
	/// </summary>
	public int TakeProfitPoints
	{
		get => _takeProfitPoints.Value;
		set => _takeProfitPoints.Value = value;
	}

	/// <summary>
	/// Initializes strategy parameters.
	/// </summary>
	public PerceptronMultStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 50)
			.SetGreaterThanZero()
			.SetDisplay("Fast Period", "Fast EMA period", "Indicator");

		_slowPeriod = Param(nameof(SlowPeriod), 200)
			.SetGreaterThanZero()
			.SetDisplay("Slow Period", "Slow EMA period", "Indicator");

		_stopLossPoints = Param(nameof(StopLossPoints), 100)
			.SetNotNegative()
			.SetDisplay("Stop Loss", "Stop-loss distance in price steps", "Risk");

		_takeProfitPoints = Param(nameof(TakeProfitPoints), 200)
			.SetNotNegative()
			.SetDisplay("Take Profit", "Take-profit distance in price steps", "Risk");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_fast = null;
		_slow = null;
		_prevFast = 0;
		_prevSlow = 0;
		_entryPrice = 0;
	}

	/// <inheritdoc />
	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;
		}

		var close = candle.ClosePrice;
		var step = Security?.PriceStep ?? 1m;

		// Check SL/TP
		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step)
			{
				SellMarket();
				_entryPrice = 0;
				_prevFast = fastValue;
				_prevSlow = slowValue;
				return;
			}

			if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step)
			{
				SellMarket();
				_entryPrice = 0;
				_prevFast = fastValue;
				_prevSlow = slowValue;
				return;
			}
		}
		else if (Position < 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step)
			{
				BuyMarket();
				_entryPrice = 0;
				_prevFast = fastValue;
				_prevSlow = slowValue;
				return;
			}

			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step)
			{
				BuyMarket();
				_entryPrice = 0;
				_prevFast = fastValue;
				_prevSlow = slowValue;
				return;
			}
		}

		// Entry: fast crosses slow
		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();

			BuyMarket();
			_entryPrice = close;
		}
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{
			if (Position > 0)
				SellMarket();

			SellMarket();
			_entryPrice = close;
		}

		_prevFast = fastValue;
		_prevSlow = slowValue;
	}
}