Ver en GitHub

Estrategia de Harami

Descripción general

HaramiStrategy convierte el asesor experto "Harami" de MetaTrader en la API de alto nivel de StockSharp. La estrategia combina un patrón Harami alcista/bajista detectado en un marco temporal superior con expansión de momentum y un filtro MACD de largo plazo. Solo se procesan velas completadas y toda la gestión de operaciones se realiza a través del motor de protección integrado de StockSharp.

Datos e indicadores

  • Marco temporal base: configurable (velas de 15 minutos por defecto) para la detección de tendencia mediante medias móviles.
  • Marco temporal superior: configurable (por defecto una hora) para el reconocimiento de patrones y la confirmación de momentum.
  • Marco temporal MACD: configurable (por defecto velas de 30 días) para emular el filtro MACD mensual original.
  • Indicadores:
    • Media Móvil Ponderada Linealmente (FastMaLength) en el marco temporal base.
    • Media Móvil Exponencial (SlowMaLength) en el marco temporal base.
    • Momentum (MomentumPeriod) en el marco temporal superior. La estrategia utiliza la distancia absoluta desde el valor neutral (100) para las últimas tres barras del marco temporal superior.
    • Convergencia/Divergencia de Medias Móviles (12/26/9) en el marco temporal MACD.

Configuración larga

  1. La EMA lenta está por encima de la LWMA rápida en el marco temporal base, señalando una tendencia alcista.
  2. El marco temporal superior forma una secuencia Harami alcista: hace dos velas fue bajista, la vela anterior fue alcista y su cuerpo es más pequeño que el cuerpo bajista anterior.
  3. Cualquiera de las últimas tres desviaciones de momentum del marco temporal superior supera MomentumBuyThreshold.
  4. La línea principal MACD está por encima de la línea de señal en el marco temporal MACD.
  5. No hay posición larga abierta (Position <= 0).
  6. La estrategia envía una orden de compra a mercado dimensionada para revertir cualquier exposición corta y añadir Volume lotes.

Configuración corta

  1. La EMA lenta está por debajo de la LWMA rápida en el marco temporal base.
  2. El marco temporal superior forma un Harami bajista: hace dos velas fue alcista, la vela anterior fue bajista y el último cuerpo es más pequeño.
  3. Cualquiera de las últimas tres desviaciones de momentum del marco temporal superior supera MomentumSellThreshold.
  4. La línea principal MACD está por debajo de la línea de señal.
  5. No hay exposición corta abierta (Position >= 0).
  6. La estrategia envía una orden de venta a mercado suficientemente grande para cerrar posiciones largas y abrir una nueva posición corta de tamaño Volume.

Gestión de riesgo

StartProtection instala niveles de stop-loss y take-profit (expresados en puntos). Las características adicionales de trailing, break-even y gestión monetaria del EA original se omiten intencionalmente para mantener la versión StockSharp concisa. Los cambios de dirección de operación aplanan automáticamente la exposición opuesta.

Parámetros

Nombre Descripción Valor predeterminado
CandleType Marco temporal primario para medias móviles y ejecución de señales. Velas de 15 minutos
HigherCandleType Marco temporal utilizado para la confirmación de Harami y momentum. Velas de 1 hora
MacdCandleType Marco temporal para el filtro de tendencia MACD. Velas de 30 días
FastMaLength Longitud de la MA ponderada linealmente rápida. 6
SlowMaLength Longitud de la MA exponencial lenta. 85
MomentumPeriod Período de retroceso del Momentum en el marco temporal superior. 14
MomentumBuyThreshold Desviación mínima de momentum para confirmación larga. 0.3
MomentumSellThreshold Desviación mínima de momentum para confirmación corta. 0.3
StopLossPoints Distancia de stop-loss en puntos. 40
TakeProfitPoints Distancia de take-profit en puntos. 100

Consejos de uso

  • Alinear CandleType, HigherCandleType y MacdCandleType con los datos históricos disponibles; asegurarse de que el marco temporal superior sea más largo que el marco temporal base.
  • Ajustar los umbrales de momentum para que coincidan con la volatilidad del instrumento negociado.
  • Usar el optimizador de StockSharp a través de los rangos de parámetros proporcionados para ajustar las longitudes de MA y los umbrales de momentum.
  • Siempre realizar backtesting con configuraciones realistas de comisión/latencia antes de implementar en vivo.
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 HaramiStrategy : 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 HaramiStrategy()
	{
		_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;
	}
}