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
For every configured security the strategy subscribes to the same candle type (default: 1-minute).
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.
A rolling buffer with the latest 22 AC values is maintained per security.
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.
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.
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.
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.
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
Assign up to three securities in the parameter panel. Any unused slot can remain null.
Adjust the point-based stops and targets to match the tick size of the selected instruments.
Tune the perceptron weights to emphasise specific lags of the AC oscillator if optimisation is required.
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;
}
}
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
from datatype_extensions import *
from indicator_extensions import *
class perceptron_mult_strategy(Strategy):
def __init__(self):
super(perceptron_mult_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 50).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 200).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator")
self._sl_points = self.Param("StopLossPoints", 100).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._tp_points = self.Param("TakeProfitPoints", 200).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Candle timeframe", "General")
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, value): self._candle_type.Value = value
def OnReseted(self):
super(perceptron_mult_strategy, self).OnReseted()
self._prev_fast = 0
self._prev_slow = 0
self._entry_price = 0
def OnStarted2(self, time):
super(perceptron_mult_strategy, self).OnStarted2(time)
self._prev_fast = 0
self._prev_slow = 0
self._entry_price = 0
fast = ExponentialMovingAverage()
fast.Length = self._fast_period.Value
slow = ExponentialMovingAverage()
slow.Length = self._slow_period.Value
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(fast, slow, self.OnProcess).Start()
def _get_step(self):
if self.Security is not None and self.Security.PriceStep is not None and self.Security.PriceStep > 0:
return float(self.Security.PriceStep)
return 1.0
def OnProcess(self, candle, fast_val, slow_val):
if candle.State != CandleStates.Finished:
return
if self._prev_fast == 0 or self._prev_slow == 0:
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = candle.ClosePrice
step = self._get_step()
if self.Position > 0 and self._entry_price > 0:
if self._sl_points.Value > 0 and close <= self._entry_price - self._sl_points.Value * step:
self.SellMarket()
self._entry_price = 0
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._tp_points.Value > 0 and close >= self._entry_price + self._tp_points.Value * step:
self.SellMarket()
self._entry_price = 0
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if self._sl_points.Value > 0 and close >= self._entry_price + self._sl_points.Value * step:
self.BuyMarket()
self._entry_price = 0
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._tp_points.Value > 0 and close <= self._entry_price - self._tp_points.Value * step:
self.BuyMarket()
self._entry_price = 0
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
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._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return perceptron_mult_strategy()