Ver en GitHub

Adaptive Grid MT4 (puerto StockSharp)

Descripción general

La estrategia recrea el asesor experto "Adaptive Grid Mt4" para el nivel alto de StockSharp API. Deja caer una rejilla simétrica de comprar órdenes stop y vender órdenes stop alrededor del cierre de la vela actual. Las distancias de la cuadrícula se derivan del rango verdadero promedio (ATR) y Por lo tanto, se adaptan a la volatilidad del mercado. Cada orden pendiente vence después de un número configurable de velas, manteniendo la Libro de pedidos ordenado en mercados laterales.

Cuando se ejecuta una orden de entrada, la estrategia registra inmediatamente las órdenes coincidentes de toma de ganancias y de limitación de pérdidas a los precios calculados. de la instantánea ATR que produjo la cuadrícula. Las órdenes de protección son uno a uno con la entrada completa y persisten hasta que se ejecutan. o cancelado manualmente.

Parámetros

Parámetro Descripción
GridLevels Número de órdenes stop por encima y por debajo del mercado. Equivalente a la entrada nGrid del EA.
TimerBars Número de velas terminadas tras las cuales se cancela cualquier entrada pendiente (MT4 nBars).
PriceOffsetMultiplier multiplicador ATR aplicado a la compensación inicial del precio actual (Poffset).
GridStepMultiplier multiplicador ATR utilizado para el espacio entre niveles de cuadrícula consecutivos (Pstep).
StopLossMultiplier multiplicador ATR que define la distancia del stop-loss adjunto a cada orden (StopLoss).
TakeProfitMultiplier multiplicador ATR que define la distancia de la toma de ganancias (TakeProfit).
AtrPeriod periodo ATR promedio. Refleja el valor codificado de 14 del script.
OrderVolume Volumen utilizado para todas las órdenes pendientes (MT4 Lot).
CandleType Periodo de tiempo que impulsa el recálculo de la cuadrícula (Wtf).

Lógica de trading

  1. Suscríbete a las velas del CandleType configurado y alimenta un ATR(14).
  2. En cada vela terminada:
    • Avance la barra de la barra interna y cancele los pedidos de cuadrícula pendientes que excedieron TimerBars.
    • Omita el procesamiento adicional si el ATR no está formado, alguna orden de cuadrícula aún está activa o la estrategia ya mantiene una posición.
    • Calcule el desplazamiento de ruptura, el espaciado de la cuadrícula, las distancias de parada de pérdidas y toma de ganancias como valores ATR * multiplier.
    • Coloque GridLevels pares de órdenes stop de compra y de venta alrededor del cierre de la vela, normalizando los precios con Security.ShrinkPrice para respetar el tamaño del tick del instrumento.
  3. Cuando se complete una entrada, elimínela de la lista de cuadrícula rastreada y genere las órdenes de protección correspondientes:
    • Las entradas largas reciben un límite de pérdidas SellStop y una toma de ganancias SellLimit.
    • Las entradas cortas reciben un límite de pérdidas BuyStop y una toma de ganancias BuyLimit.
  4. Las órdenes de protección se monitorean a través de OnOrderChanged para que las entradas completadas o canceladas se eliminen del seguimiento. listas.

Notas

  • La cuadrícula solo se reconstruye cuando no hay posiciones abiertas y todas las órdenes de la cuadrícula existentes expiraron, coincidiendo con la lógica What() de el EA original.
  • Los precios se calculan a partir del cierre de la vela en lugar del tick sin procesar Bid/Ask. Esto mantiene la implementación impulsada por velas. mientras produce el mismo diseño simétrico en todo el mercado.
  • La instantánea ATR utilizada para la cuadrícula también se utiliza para que las órdenes de protección imiten la parada por boleto y la toma de ganancias de MetaTrader valores.
  • Aún no existe ninguna traducción de Python que coincida con la solicitud.
using System;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Adaptive grid strategy using ATR-based breakout levels.
/// Simplified from the "Adaptive Grid Mt4" expert advisor to use market orders.
/// </summary>
public class AdaptiveGridMt4Strategy : Strategy
{
	private readonly StrategyParam<int> _atrPeriod;
	private readonly StrategyParam<decimal> _breakoutMultiplier;
	private readonly StrategyParam<DataType> _candleType;

	private AverageTrueRange _atr;
	private decimal? _prevClose;
	private decimal? _prevAtr;
	private decimal _stopPrice;
	private decimal _takeProfitPrice;

	/// <summary>
	/// ATR averaging period.
	/// </summary>
	public int AtrPeriod
	{
		get => _atrPeriod.Value;
		set => _atrPeriod.Value = value;
	}

	/// <summary>
	/// Breakout threshold in ATR multiples.
	/// </summary>
	public decimal BreakoutMultiplier
	{
		get => _breakoutMultiplier.Value;
		set => _breakoutMultiplier.Value = value;
	}

	/// <summary>
	/// Candle type used to drive calculations.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public AdaptiveGridMt4Strategy()
	{
		_atrPeriod = Param(nameof(AtrPeriod), 20)
		.SetGreaterThanZero()
		.SetDisplay("ATR Period", "Number of candles used for ATR smoothing", "Indicators");

		_breakoutMultiplier = Param(nameof(BreakoutMultiplier), 2.5m)
		.SetGreaterThanZero()
		.SetDisplay("Breakout Multiplier", "ATR multiplier for breakout threshold", "Grid");

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
		.SetDisplay("Candle Type", "Candle type used to trigger grid recalculation", "General");
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_prevClose = null;
		_prevAtr = null;
		_stopPrice = 0;
		_takeProfitPrice = 0;

		_atr = new AverageTrueRange { Length = AtrPeriod };

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

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

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

		if (!_atr.IsFormed || atrValue <= 0)
		{
			_prevClose = candle.ClosePrice;
			_prevAtr = atrValue;
			return;
		}

		// Check protective stops
		if (Position > 0)
		{
			if (_stopPrice > 0 && candle.LowPrice <= _stopPrice)
			{
				SellMarket(Math.Abs(Position));
				_stopPrice = 0;
				_takeProfitPrice = 0;
			}
			else if (_takeProfitPrice > 0 && candle.HighPrice >= _takeProfitPrice)
			{
				SellMarket(Math.Abs(Position));
				_stopPrice = 0;
				_takeProfitPrice = 0;
			}
		}
		else if (Position < 0)
		{
			if (_stopPrice > 0 && candle.HighPrice >= _stopPrice)
			{
				BuyMarket(Math.Abs(Position));
				_stopPrice = 0;
				_takeProfitPrice = 0;
			}
			else if (_takeProfitPrice > 0 && candle.LowPrice <= _takeProfitPrice)
			{
				BuyMarket(Math.Abs(Position));
				_stopPrice = 0;
				_takeProfitPrice = 0;
			}
		}

		if (_prevClose is not decimal prevClose || _prevAtr is not decimal prevAtr || prevAtr <= 0)
		{
			_prevClose = candle.ClosePrice;
			_prevAtr = atrValue;
			return;
		}

		var threshold = prevAtr * BreakoutMultiplier;
		var volume = Volume;
		if (volume <= 0)
			volume = 1;

		// Breakout up
		if (candle.ClosePrice > prevClose + threshold && Position <= 0)
		{
			BuyMarket(Position < 0 ? Math.Abs(Position) + volume : volume);
			_stopPrice = candle.ClosePrice - atrValue * 3;
			_takeProfitPrice = candle.ClosePrice + atrValue * 4;
		}
		// Breakout down
		else if (candle.ClosePrice < prevClose - threshold && Position >= 0)
		{
			SellMarket(Position > 0 ? Math.Abs(Position) + volume : volume);
			_stopPrice = candle.ClosePrice + atrValue * 3;
			_takeProfitPrice = candle.ClosePrice - atrValue * 4;
		}

		_prevClose = candle.ClosePrice;
		_prevAtr = atrValue;
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		_atr = null;
		_prevClose = null;
		_prevAtr = null;
		_stopPrice = 0;
		_takeProfitPrice = 0;

		base.OnReseted();
	}
}