Ver en GitHub

Estrategia DLMv FX Fish Grid

Descripción general

La Estrategia DLMv FX Fish Grid replica el comportamiento del asesor experto original de MetaTrader construido alrededor del oscilador "FX Fish 2MA". La estrategia evalúa la Transformada de Fisher del precio, la suaviza con una media móvil y abre posiciones cuando el oscilador cruza su línea de base suavizada en el lado apropiado de cero. La gestión de posiciones imita el comportamiento en cuadrícula del EA fuente: las entradas adicionales están espaciadas por una distancia configurable, las órdenes límite pendientes pueden estratificarse y la automatización protectora maneja los controles de riesgo.

Lógica de trading

  1. Cálculo del indicador
    • Los precios más altos y más bajos durante las velas CalculatePeriod definen el rango rodante.
    • Una Transformada de Fisher se aplica al precio seleccionado (AppliedPrice), usando el mismo factor de suavizado 0.67 que el indicador MT5.
    • Una media móvil simple (MaPeriod) del valor de Fisher proporciona la línea de base de la señal.
  2. Generación de señales
    • Señal larga: los valores actuales y anteriores de Fisher están por debajo de cero mientras el oscilador cruza por encima de su media móvil (valor anterior por debajo de la media, valor actual por encima).
    • Señal corta: los valores actuales y anteriores de Fisher están por encima de cero mientras el oscilador cruza por debajo de la media móvil (valor anterior por encima de la media, valor actual por debajo).
    • Las señales pueden invertirse habilitando ReverseSignals.
  3. Ejecución de órdenes
    • Cuando aparece una señal de compra (o venta), la estrategia puede opcionalmente cerrar la exposición opuesta existente (CloseOpposite).
    • Se permiten entradas adicionales hasta que el conteo total alcance MaxTrades. Cada nueva entrada debe respetar el espaciado mínimo dado por DistancePips desde la última operación completada.
    • Las órdenes límite opcionales (SetLimitOrders) colocan ofertas/demandas en reposo al espaciado configurado, replicando la cuadrícula escalonada del EA original.
  4. Gestión de riesgos
    • Los valores fijos de stop-loss, take-profit y trailing stop se aplican mediante StartProtection, todos definidos en pips.
    • TimeLiveSeconds cierra toda la exposición cuando una operación ha estado abierta más tiempo del permitido.
    • El trading puede deshabilitarse los viernes (TradeOnFriday = false). Cuando está deshabilitado, la estrategia cierra posiciones y cancela órdenes pendientes tan pronto como llega una vela de viernes.

Parámetros

Parámetro Descripción
OrderVolume Tamaño de orden para cada entrada (lotes).
StopLossPips Distancia del stop-loss protector desde la entrada. Establecer en 0 para deshabilitar.
TakeProfitPips Distancia del nivel de take-profit. Establecer en 0 para deshabilitar.
TrailingStopPips Distancia del trailing stop (0 deshabilita el trailing).
TrailingStepPips Paso por el cual se ajusta el trailing stop.
MaxTrades Número máximo de operaciones simultáneas por dirección. 0 elimina el límite.
DistancePips Distancia mínima entre entradas consecutivas y para las órdenes de cuadrícula opcionales.
TradeOnFriday Cuando es false, la estrategia deja de operar los viernes y liquida la exposición.
TimeLiveSeconds Tiempo máximo (segundos) que las posiciones pueden permanecer abiertas antes de ser cerradas forzosamente.
ReverseSignals Invertir condiciones largo/corto.
SetLimitOrders Habilitar órdenes límite adicionales en reposo en DistancePips.
CloseOpposite Cerrar la exposición opuesta antes de entrar en un nuevo trade.
CalculatePeriod Lookback para el rango de la Transformada de Fisher.
MaPeriod Periodo de la media móvil aplicada al valor de Fisher.
AppliedPrice Fuente de precio usada en la Transformada de Fisher (close, open, high, low, median, typical, weighted).
CandleType Tipo de datos/marco temporal de las velas procesadas por la estrategia.

Notas

  • Las distancias de stop-loss, take-profit y trailing stop se convierten de pips a offsets de precio absolutos usando Security.PriceStep * 10, coincidiendo con la lógica de pip de cinco dígitos de la versión MQL.
  • Las órdenes límite se cancelan automáticamente cuando las señales cambian, el trading se pausa o se activan las protecciones de tiempo/viernes.
  • La Transformada de Fisher evita búsquedas repetidas de valores, en cambio almacena las lecturas anteriores del oscilador y de la línea de base para una detección precisa de cruces.
using System;
using System.Collections.Generic;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// DLMv FX Fish Grid strategy. Uses Highest/Lowest range with Fisher transform crossover.
/// </summary>
public class DlmvFxFishGridStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _period;

	private decimal? _prevFish;
	private decimal _prevValue;

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

	public int Period
	{
		get => _period.Value;
		set => _period.Value = value;
	}

	public DlmvFxFishGridStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe", "General");

		_period = Param(nameof(Period), 20)
			.SetGreaterThanZero()
			.SetDisplay("Period", "Lookback period for high/low range", "Indicators");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevFish = null;
		_prevValue = 0m;
	}

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

		_prevFish = null;
		_prevValue = 0m;

		var highest = new Highest { Length = Period };
		var lowest = new Lowest { Length = Period };

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

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

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

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		var range = high - low;
		var midPrice = (candle.HighPrice + candle.LowPrice) / 2m;

		var normalized = range != 0m ? (midPrice - low) / range : 0.5m;
		var value = 0.66m * (normalized - 0.5m) + 0.67m * _prevValue;
		value = Math.Min(Math.Max(value, -0.999m), 0.999m);

		var ratio = (double)((1m + value) / (1m - value));
		var fish = 0.5m * (decimal)Math.Log(ratio);

		_prevValue = value;

		if (_prevFish == null)
		{
			_prevFish = fish;
			return;
		}

		// Fisher crosses zero from below → buy
		if (_prevFish.Value < 0m && fish >= 0m && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		// Fisher crosses zero from above → sell
		else if (_prevFish.Value > 0m && fish <= 0m && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}

		_prevFish = fish;
	}
}