Ver en GitHub

Estrategia de Gann Fan

Esta estrategia de StockSharp reproduce el experto MetaTrader GANN_FAN usando la API de alto nivel. Combina filtros de tendencia de medias móviles ponderadas linealmente con confirmación de Momentum, una puerta de dirección MACD y una reconstrucción basada en fractales del Gann Fan para determinar el sesgo alcista o bajista. La gestión de riesgos refleja el robot original con entradas apiladas de estilo martingala, stops fijos, protección de trailing y movimientos de punto de equilibrio opcionales.

Lógica de trading

  1. Filtro de tendencia – Dos medias móviles ponderadas linealmente (LWMA) construidas sobre el precio típico (H+L+C)/3 definen la tendencia rápida y lenta. Los trades largos requieren que la LWMA rápida permanezca por encima de la LWMA lenta; los trades cortos necesitan el cruce inverso.
  2. Confirmación de Momentum – La estrategia calcula el oscilador de Momentum clásico como 100 * Close / Close(n) y evalúa la desviación del nivel neutro 100 durante los últimos tres velas cerradas. Al menos una desviación debe superar el umbral configurado para confirmar la fortaleza en la dirección del trade.
  3. Dirección MACD – Una señal MACD configurable (períodos de EMA rápida, lenta y de señal) debe estar de acuerdo con la tendencia. Las entradas largas requieren que la línea MACD sea mayor que la línea de señal, mientras que los cortos necesitan que la línea MACD permanezca por debajo de la línea de señal.
  4. Orientación del Gann Fan – Los fractales confirmados de Bill Williams reconstruyen los rayos del Gann Fan alcista y bajista. Los dos fractales descendentes más recientes forman el rayo alcista; su pendiente debe ser positiva para permitir largos. Los dos últimos fractales ascendentes definen el rayo bajista; su pendiente debe ser negativa para autorizar ventas en corto.
  5. Apilamiento de posiciones – Cuando llega una nueva señal, la estrategia puede añadir a una posición existente hasta el máximo configurado. Cada orden adicional aumenta el volumen multiplicando el lote base por el exponente de lote, emulando el dimensionamiento de martingala utilizado en la versión MQL.

Gestión de riesgos

  • Stop-loss y take-profit fijos – Expresados en pasos de precio del instrumento, convertidos automáticamente por la estrategia usando Security.PriceStep.
  • Control de punto de equilibrio – Cuando está habilitado, una vez que el beneficio alcanza la distancia de disparo, el stop se adelanta a la entrada más/menos el offset configurado.
  • Stop de seguimiento – Se activa después de alcanzar la distancia de disparo. El stop puede seguir al mercado ya sea por una distancia fija del cierre o bloqueando el valor más bajo (para largos) / más alto (para cortos) de las velas más recientes más un factor de margen.
  • Interruptor de salida forzada – Establecer Force Exit en true liquida inmediatamente cualquier exposición abierta en la próxima vela terminada.

Parámetros

Parámetro Descripción
Volume Tamaño base de la orden utilizado para la primera entrada.
Fast LWMA / Slow LWMA Períodos de las medias móviles ponderadas linealmente utilizadas para el filtro de tendencia.
Momentum Period / Threshold Retrospectiva del cálculo de Momentum y desviación mínima de 100 requerida para operar.
MACD Fast / Slow / Signal Períodos de EMA para el filtro de confirmación MACD.
Fractal History Número máximo de puntos de fractal confirmados almacenados para construir los rayos del Gann Fan.
Max Trades Número máximo de entradas apiladas permitidas en una sola dirección.
Lot Exponent Multiplicador aplicado al volumen base para cada entrada adicional.
Stop Loss / Take Profit Distancias de protección en pasos de precio.
Enable Trailing Habilita la gestión del stop de seguimiento.
Trail Trigger / Distance / Padding Disparador de beneficio, distancia de trailing y margen extra (en pasos de precio) utilizado al hacer trailing mediante extremos de vela.
Use Candle Trail Habilita el trailing basado en velas además del trailing de distancia fija.
Trailing Candles Número de velas terminadas recientes consideradas al calcular los niveles de trailing basados en velas.
Enable Break-even Activa o desactiva la lógica de punto de equilibrio.
Break-even Trigger / Offset Disparador de beneficio y offset (en pasos de precio) para mover el stop al punto de equilibrio.
Use Gann Filter Impone la orientación alcista/bajista del Gann Fan para las entradas.
Force Exit Obliga a la estrategia a cerrar todas las posiciones en la siguiente barra.
Candle Type Series de velas utilizadas para cálculos y generación de órdenes.

Notas

  • Todos los cálculos de indicadores funcionan exclusivamente en velas terminadas proporcionadas por SubscribeCandles y Bind para cumplir con las mejores prácticas de la API de alto nivel de StockSharp.
  • Las distancias de trailing y punto de equilibrio se adaptan automáticamente al tamaño del tick del instrumento. Cuando PriceStep no está disponible, las características de protección permanecen inactivas hasta que el conector lo proporcione.
  • La estrategia mantiene estados separados para posiciones largas y cortas, asegurando que los niveles de trailing y punto de equilibrio se reinicien cuando la exposición cambia de dirección.
  • Para imitar el experto MetaTrader de cerca, las alertas, notificaciones y objetos de gráfico explícitos del código original son reemplazados por la reconstrucción nativa del Gann Fan de StockSharp usando fractales.
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;

public class GannFanStrategy : 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;
	private int _cooldown;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
	public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }

	public GannFanStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
		_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
		_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
		_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
	}

	protected override void OnReseted()
	{
		base.OnReseted();
		_fast = null; _slow = null;
		_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
	}

	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; }
		if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }

		var close = candle.ClosePrice;
		var step = Security?.PriceStep ?? 1m;

		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}
		else if (Position < 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}

		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }

		_prevFast = fastValue; _prevSlow = slowValue;
	}
}