Ver en GitHub

Estrategia Exp Amstell

Descripción general

La Estrategia Exp Amstell es un sistema de comercio en red convertido del MetaTrader 4 asesor experto original exp_Amstell.mq4. Coloca continuamente órdenes de mercado de compra y venta cada vez que el precio se aleja una cantidad configurable de puntos del cumplimiento más reciente. Cada operación individual se gestiona de forma independiente: una vez que el mercado se mueve a la distancia de obtención de beneficios especificada, la estrategia envía una orden de compensación para capturar las ganancias de esa única capa.

A diferencia de los sistemas impulsados por el impulso, Exp Amstell permanece activo en todo momento. No espera confirmaciones de indicadores y, en cambio, acumula posiciones en ambos lados del libro a medida que el mercado oscila. Este comportamiento lo hace muy sensible a las distancias de los puntos elegidos y al tamaño de cada orden.

Lógica de trading

  • Procesamiento basado en ticks. La estrategia se suscribe a cotizaciones de nivel 1 y reacciona a cada cambio en la mejor oferta y la mejor demanda, al igual que la función start() en el código original MQL.
  • Stacks largos y cortos independientes. Se permiten órdenes de compra cuando no hay operaciones largas abiertas o cuando el precio de venta ha bajado al menos la distancia de reingreso desde la última entrada larga. Las órdenes de venta utilizan la condición simétrica en el precio de oferta.
  • Obtención de beneficios por operación. Cada capa abierta se rastrea por separado. Cuando la oferta (para largos) o la demanda (para cortos) avanza por los puntos de toma de ganancias configurados, la estrategia cierra solo esa capa con una orden de mercado. Otras capas permanecen intactas.
  • Emulación FIFO. Las operaciones ejecutadas se registran en orden FIFO para reproducir la contabilidad basada en tickets que MetaTrader aplica a las posiciones cubiertas. Esto garantiza que los rellenos parciales reduzcan primero la capa pendiente más antigua.
  • Conocimiento neto de la cartera. StockSharp mantiene posiciones netas. Si una nueva orden de compra compensa una capa corta abierta, la estrategia elimina esa posición corta de su pila sintética antes de registrar el resto como una nueva posición larga.

Parámetros

Nombre Tipo Predeterminado Descripción
TradeVolume decimal 0.1 Volumen de cada orden de mercado que abre una nueva capa de la grilla.
TakeProfitPoints int 30 Distancia en MetaTrader puntos que debe cubrir el precio antes de cerrar una capa individual.
ReentryDistancePoints int 10 Distancia mínima de puntos desde la última entrada antes de agregar otra orden en el mismo lado.

La estrategia convierte automáticamente puntos en incrementos de precios reales utilizando el PriceStep del instrumento. Las cotizaciones de cinco y tres dígitos reciben el multiplicador específico de MetaTrader, de modo que 1 point sea igual a 0.0001 (o 0.01 para símbolos de estilo JPY).

Notas de implementación

  • Los datos de nivel 1 son suficientes; no se requiere suscripción a velas. La estrategia declara esto anulando GetWorkingSecurities() y solicitando (Security, DataType.Level1).
  • StartProtection() se invoca durante OnStarted para garantizar que el corredor cierre cualquier posición sobrante si la estrategia se detiene inesperadamente.
  • Todos los comentarios dentro del archivo C# permanecen en inglés, cumpliendo con las pautas del proyecto.
  • Debido a que StockSharp utiliza posiciones netas, el puerto no puede mantener abiertas las compras y ventas opuestas simultáneamente. Cuando ambas partes comercian al mismo tiempo, la nueva orden aplanará la exposición existente antes de crear una nueva capa.

Consejos de uso

  1. Calibre las distancias de los puntos. Las distancias más pequeñas crean redes más densas que pueden realizar operaciones excesivas en mercados volátiles. Las distancias más grandes reducen la actividad pero aumentan la reducción por capa.
  2. Tamaño de los pedidos con prudencia. Los sistemas de red acumulan exposición rápidamente. Pruebe volúmenes conservadores en Designer/Backtester antes de cambiar al comercio real.
  3. Considere controles de riesgo manuales. El experto original no tiene un límite de pérdidas global. Combine la estrategia con protecciones a nivel de cartera para limitar el riesgo de cola.
  4. Monitorear la calidad de ejecución. El algoritmo supone que las órdenes de mercado se ejecutan cerca de la mejor oferta/demanda. El deslizamiento afecta directamente las distancias de obtención de beneficios alcanzadas.

Fuente

Convertido de MQL/9027/exp_Amstell.mq4.

using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Exp Amstell: Grid-style strategy that scales into positions
/// on ATR-based price movements and closes on profit targets.
/// </summary>
public class ExpAmstellStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _atrLength;
	private readonly StrategyParam<int> _emaLength;

	private decimal _entryPrice;
	private decimal _prevEma;
	private int _gridCount;

	public ExpAmstellStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe.", "General");

		_atrLength = Param(nameof(AtrLength), 14)
			.SetDisplay("ATR Length", "ATR period for grid distance.", "Indicators");

		_emaLength = Param(nameof(EmaLength), 20)
			.SetDisplay("EMA Length", "EMA period for trend.", "Indicators");
	}

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public int AtrLength
	{
		get => _atrLength.Value;
		set => _atrLength.Value = value;
	}

	public int EmaLength
	{
		get => _emaLength.Value;
		set => _emaLength.Value = value;
	}

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

		_entryPrice = 0;
		_prevEma = 0;
		_gridCount = 0;
	}

		protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_entryPrice = 0;
		_prevEma = 0;
		_gridCount = 0;

		var atr = new AverageTrueRange { Length = AtrLength };
		var ema = new ExponentialMovingAverage { Length = EmaLength };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(atr, ema, ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, ema);
			DrawOwnTrades(area);
		}
	}

	private void ProcessCandle(ICandleMessage candle, decimal atrVal, decimal emaVal)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (atrVal <= 0 || _prevEma == 0)
		{
			_prevEma = emaVal;
			return;
		}

		var close = candle.ClosePrice;

		// Grid exit: take profit at 1.5 ATR or stop at 3 ATR
		if (Position > 0)
		{
			if (close >= _entryPrice + atrVal * 1.5m)
			{
				SellMarket();
				_entryPrice = 0;
				_gridCount = 0;
			}
			else if (close <= _entryPrice - atrVal * 3m)
			{
				SellMarket();
				_entryPrice = 0;
				_gridCount = 0;
			}
			else if (_gridCount < 3 && close <= _entryPrice - atrVal)
			{
				// Scale in: add to position on pullback
				_entryPrice = (_entryPrice + close) / 2m;
				_gridCount++;
				BuyMarket();
			}
		}
		else if (Position < 0)
		{
			if (close <= _entryPrice - atrVal * 1.5m)
			{
				BuyMarket();
				_entryPrice = 0;
				_gridCount = 0;
			}
			else if (close >= _entryPrice + atrVal * 3m)
			{
				BuyMarket();
				_entryPrice = 0;
				_gridCount = 0;
			}
			else if (_gridCount < 3 && close >= _entryPrice + atrVal)
			{
				_entryPrice = (_entryPrice + close) / 2m;
				_gridCount++;
				SellMarket();
			}
		}

		// Entry: EMA direction determines initial direction
		if (Position == 0)
		{
			if (close > emaVal && emaVal > _prevEma)
			{
				_entryPrice = close;
				_gridCount = 0;
				BuyMarket();
			}
			else if (close < emaVal && emaVal < _prevEma)
			{
				_entryPrice = close;
				_gridCount = 0;
				SellMarket();
			}
		}

		_prevEma = emaVal;
	}
}