Ver no GitHub

Estratégia Perceptron Mult

Esta estratégia porta o consultor especialista Peceptron_Mult.mq5 para a API de alto nível do StockSharp. Ela monitora simultaneamente até três mercados independentes e aplica o oscilador Acceleration/Deceleration (AC) dentro de um modelo de perceptron. Cada mercado recebe sua própria configuração de pesos, dimensionamento de posição e saídas de proteção, de modo que o comportamento do consultor original multi-símbolo seja preservado.

Lógica de Negociação

  1. Para cada ativo configurado, a estratégia assina o mesmo tipo de vela (padrão: 1 minuto).
  2. Em cada vela concluída, calcula o oscilador Acceleration/Deceleration de Bill Williams:
    • Calcular o Awesome Oscillator (AO) a partir dos máximos e mínimos da vela (médias móveis do preço mediano 5/34).
    • Subtrair uma média móvel simples de 5 períodos de AO do valor atual de AO.
  3. Um buffer circular com os últimos 22 valores de AC é mantido por ativo.
  4. O sinal do perceptron é formado a partir de quatro valores atrasados de AC usando pesos (w - 100) exatamente como no código MQL:
    • AC[0], AC[7], AC[14], AC[21] correspondem à leitura mais recente e três históricas.
  5. Regras de entrada:
    • Soma positiva ⇒ abrir posição comprada se nenhuma posição existir naquele ativo.
    • Soma negativa ⇒ abrir posição vendida se o ativo estiver plano.
  6. Regras de saída:
    • As distâncias de stop-loss e take-profit são expressas em pontos. São convertidas em deslocamentos de preço absolutos usando o passo de preço do instrumento.
    • As saídas de proteção são avaliadas em cada vela concluída. Uma negociação comprada é fechada quando a mínima da vela atinge o stop ou a máxima alcança o alvo de lucro; vendidas usam a lógica espelhada.
  7. As posições são mutuamente exclusivas por ativo. A estratégia ignora novos sinais enquanto a exposição permanece aberta, replicando o comportamento do consultor original.

Parâmetros

Parâmetro Descrição
FirstSecurity, SecondSecurity, ThirdSecurity Instrumentos processados pelo perceptron. Deixar em null para desativar um slot.
FirstOrderVolume, SecondOrderVolume, ThirdOrderVolume Tamanho da ordem a mercado para cada instrumento.
FirstWeight1FirstWeight4, etc. Pesos do perceptron (entradas MQL x1…x12). A estratégia subtrai internamente 100 de cada valor antes de aplicá-lo.
FirstStopLossPoints, SecondStopLossPoints, ThirdStopLossPoints Distância do stop-loss em pontos de preço para cada instrumento. Definir como 0 para desativar.
FirstTakeProfitPoints, SecondTakeProfitPoints, ThirdTakeProfitPoints Distância do take-profit em pontos de preço para cada instrumento. Definir como 0 para desativar.
CandleType Série de velas compartilhada por todos os ativos.

Notas de Implementação

  • A estratégia depende dos indicadores AwesomeOscillator e SimpleMovingAverage do StockSharp para reconstruir o oscilador AC, evitando recálculos manuais.
  • Os buffers circulares são usados apenas para emular as entradas do perceptron da implementação MQL (índices 0, 7, 14, 21).
  • Os níveis de proteção são aplicados sem registrar ordens stop separadas: a estratégia monitora os extremos das velas e fecha posições com ordens a mercado quando os níveis são violados, refletindo o comportamento do EA original em novos ticks.
  • Cada ativo mantém estado de indicador independente, volume de ordem e configurações de risco, correspondendo à estrutura de três símbolos do consultor de origem.

Dicas de Uso

  1. Atribuir até três ativos no painel de parâmetros. Qualquer slot não utilizado pode permanecer como null.
  2. Ajustar os stops e alvos baseados em pontos para corresponder ao tamanho de tick dos instrumentos selecionados.
  3. Ajustar os pesos do perceptron para enfatizar atrasos específicos do oscilador AC se a otimização for necessária.
  4. Como todos os instrumentos compartilham o mesmo tipo de vela, garantir que dados históricos estejam disponíveis para cada ativo configurado.
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;
	}
}