Ver en GitHub

Estrategia Perceptron Mult

Esta estrategia porta el experto Peceptron_Mult.mq5 a la API de alto nivel de StockSharp. Monitorea simultáneamente hasta tres mercados independientes y aplica el oscilador Acceleration/Deceleration (AC) dentro de un modelo de perceptrón. Cada mercado recibe su propia configuración de pesos, dimensionamiento de posición y salidas de protección, de modo que se preserve el comportamiento del asesor original multi-símbolo.

Lógica de Trading

  1. Para cada instrumento configurado, la estrategia se suscribe al mismo tipo de vela (predeterminado: 1 minuto).
  2. En cada vela terminada calcula el oscilador Acceleration/Deceleration de Bill Williams:
    • Calcular el Awesome Oscillator (AO) a partir de los máximos y mínimos de la vela (medias móviles de precio mediano 5/34).
    • Restar una media móvil simple de 5 períodos de AO del valor actual de AO.
  3. Se mantiene un buffer circular con los últimos 22 valores de AC por instrumento.
  4. La señal del perceptrón se forma a partir de cuatro valores retardados de AC usando pesos (w - 100) exactamente como en el código MQL:
    • AC[0], AC[7], AC[14], AC[21] corresponden a la lectura más reciente y tres históricas.
  5. Reglas de entrada:
    • Suma positiva ⇒ abrir posición larga si no existe posición en ese instrumento.
    • Suma negativa ⇒ abrir posición corta si el instrumento está plano.
  6. Reglas de salida:
    • Las distancias de stop-loss y take-profit se expresan en puntos. Se convierten a desplazamientos de precio absolutos usando el paso de precio del instrumento.
    • Las salidas de protección se evalúan en cada vela terminada. Una operación larga se cierra cuando el mínimo de la vela toca el stop o el máximo alcanza el objetivo de ganancia; los cortos usan la lógica espejada.
  7. Las posiciones son mutuamente exclusivas por instrumento. La estrategia ignora nuevas señales mientras la exposición permanece abierta, replicando el comportamiento del asesor original.

Parámetros

Parámetro Descripción
FirstSecurity, SecondSecurity, ThirdSecurity Instrumentos procesados por el perceptrón. Dejar en null para deshabilitar un slot.
FirstOrderVolume, SecondOrderVolume, ThirdOrderVolume Tamaño de orden a mercado para cada instrumento.
FirstWeight1FirstWeight4, etc. Pesos del perceptrón (entradas MQL x1…x12). La estrategia resta internamente 100 de cada valor antes de aplicarlo.
FirstStopLossPoints, SecondStopLossPoints, ThirdStopLossPoints Distancia de stop-loss en puntos de precio para cada instrumento. Establecer en 0 para deshabilitar.
FirstTakeProfitPoints, SecondTakeProfitPoints, ThirdTakeProfitPoints Distancia de take-profit en puntos de precio para cada instrumento. Establecer en 0 para deshabilitar.
CandleType Serie de velas compartida por todos los instrumentos.

Notas de Implementación

  • La estrategia utiliza los indicadores AwesomeOscillator y SimpleMovingAverage de StockSharp para reconstruir el oscilador AC, evitando recálculos manuales.
  • Los buffers circulares se usan solo para emular las entradas del perceptrón de la implementación MQL (índices 0, 7, 14, 21).
  • Los niveles de protección se aplican sin registrar órdenes stop separadas: la estrategia monitorea los extremos de las velas y cierra posiciones con órdenes a mercado cuando se violan los niveles, reflejando el comportamiento del EA original en nuevos ticks.
  • Cada instrumento mantiene un estado de indicador independiente, volumen de orden y configuraciones de riesgo, coincidiendo con la estructura de tres símbolos del asesor fuente.

Consejos de Uso

  1. Asignar hasta tres instrumentos en el panel de parámetros. Cualquier slot no utilizado puede quedar como null.
  2. Ajustar los stops y objetivos basados en puntos para que coincidan con el tamaño de tick de los instrumentos seleccionados.
  3. Ajustar los pesos del perceptrón para enfatizar rezagos específicos del oscilador AC si se requiere optimización.
  4. Dado que todos los instrumentos comparten el mismo tipo de vela, asegurarse de que los datos históricos estén disponibles para cada instrumento 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;
	}
}