Ver en GitHub

Estrategia de Four Hour Swing

Descripción general

La Estrategia de Four Hour Swing porta el asesor experto "4H swing" de MetaTrader a la API de alto nivel de StockSharp. El sistema original combina seguimiento de tendencia y confirmaciones de oscilador tomadas de marcos temporales superiores. Esta versión en C# se suscribe a tres marcos temporales (entrada, confirmación y filtro macro) y recrea la pila de indicadores con componentes de StockSharp.

Lógica de negociación

  • El filtro de tendencia principal utiliza tres medias móviles exponenciales calculadas sobre el precio típico de las velas de entrada. Una configuración larga requiere Fast EMA > Medium EMA > Slow EMA; una configuración corta refleja la condición de forma inversa.
  • Los valores del oscilador Stochastic se evalúan en el marco temporal de confirmación superior. La línea %K debe mantenerse por encima de %D para largos y por debajo para cortos.
  • El Momentum se muestrea de las mismas velas de confirmación y se convierte a la proporción de estilo MetaTrader alrededor de 100. Una operación solo se permite si al menos una de las últimas tres lecturas de momentum está más lejos que el umbral configurado.
  • Los valores MACD mensuales (o definidos por el usuario) proporcionan el filtro de dirección macro. Una compra requiere que la línea MACD esté por encima de su señal, mientras que una venta verifica la relación opuesta.

Una posición se abre en la siguiente vela base una vez que todas las confirmaciones están alineadas y la cuenta está plana o posicionada en dirección opuesta (en ese caso la orden de mercado cierra y revierte).

Gestión de riesgo

  • Las distancias fijas de stop-loss y take-profit (expresadas en pips) se aplican inmediatamente después de la entrada.
  • Un trailing stop opcional sigue el precio extremo alcanzado después de la entrada.
  • La protección de break-even puede mover el stop al precio de entrada más un desplazamiento una vez que se alcanza la distancia de activación configurada.
  • Una salida MACD opcional cierra las operaciones abiertas cuando el filtro macro cambia.

Parámetros

Nombre Descripción Valor predeterminado
TradeVolume Volumen de orden de mercado predeterminado. 0.01
CandleType Tipo de vela de entrada (p. ej., velas de 4 horas). 4H
SignalCandleType Tipo de vela de confirmación para Stochastic y Momentum. 7D (semanal)
MacdCandleType Tipo de vela de filtro macro. 30D
FastEmaPeriod, MediumEmaPeriod, SlowEmaPeriod Longitudes de EMA calculadas sobre el precio típico. 4, 14, 50
StochasticKPeriod, StochasticDPeriod, StochasticSmoothPeriod Configuración del oscilador Stochastic. 13, 5, 5
MomentumPeriod Período de retrovisión utilizado por el indicador de Momentum. 14
MomentumThreshold Distancia mínima desde 100 requerida para validar el Momentum. 0.3
StopLossPips, TakeProfitPips Órdenes de protección en pips. 20, 50
TrailingStopPips Distancia del trailing stop en pips. Establecer en cero para desactivar. 40
UseBreakEven Activa la protección de break-even. true
BreakEvenTriggerPips, BreakEvenOffsetPips Activador y desplazamiento para el movimiento de break-even. 30, 30
UseMacdExit Cerrar posiciones cuando el MACD macro cambia. false

Notas

  • Las características de gestión monetaria (stops de capital, objetivos de moneda) del experto original se omiten intencionalmente para mantener la implementación compacta.
  • La estrategia procesa solo velas terminadas, correspondiendo a la evaluación barra por barra de MetaTrader.
  • Los marcos temporales predeterminados reproducen la configuración común de 4 horas (confirmación semanal y filtro mensual), pero cada parámetro DataType puede reconfigurarse para ejecutarse en períodos alternativos.
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 FourHourSwingStrategy : 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 FourHourSwingStrategy()
	{
		_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;
	}
}