Ver en GitHub

Estrategia Harami alcista y bajista Stochastic

La Estrategia Harami alcista y bajista Stochastic es el puerto StockSharp del Expert Advisor de MetaTrader expert_abh_bh_stoch.mq5 de la carpeta MQL/310. El experto original utiliza el reconocimiento de patrones de velas para configuraciones Bullish Harami y Bearish Harami y requiere una confirmación del oscilador estocástico. La versión C# mantiene la misma lógica usando StockSharp API de alto nivel y agrega registros detallados y resultados de gráficos para facilitar el monitoreo.

Ideas centrales

  • Detecte patrones de velas Harami alcistas y Harami bajistas utilizando las dos velas completadas anteriores.
  • Confirme configuraciones alcistas con la línea estocástica %D por debajo de un umbral de sobreventa y configuraciones bajistas con %D por encima de un umbral de sobrecompra.
  • Cierre las posiciones cortas cuando la línea estocástica %D rebote por encima de los umbrales de salida inferior o superior, y cierre las posiciones largas cuando %D caiga por debajo de esos umbrales.

Parámetros

Parámetro Descripción Predeterminado
CandleType Periodo de tiempo de la serie de velas utilizada para el reconocimiento de patrones. 1 Hour
StochasticKPeriod Período retrospectivo para el cálculo estocástico de %K. 47
StochasticDPeriod Período de suavizado para la línea %D. 9
StochasticSlowing Suavizado adicional aplicado a %K (“desaceleración” MT5). 13
MovingAveragePeriod Número de velas utilizadas para promediar el tamaño corporal para la validación del patrón. 5
OversoldLevel Stochastic %D umbral para confirmar señales alcistas. 30
OverboughtLevel Stochastic %D umbral para confirmar señales bajistas. 70
ExitLowerLevel Nivel estocástico inferior que desencadena salidas. 20
ExitUpperLevel Nivel estocástico superior que desencadena salidas. 80

Reglas de trading

Entrada larga

  1. Se detecta un patrón Harami alcista en las dos velas completadas más recientes (una pequeña vela alcista envuelta por una vela bajista más larga en una tendencia bajista).
  2. La línea estocástica %D de la vela de confirmación está en OversoldLevel o menos.
  3. Actualmente no hay ninguna posición larga abierta (Position <= 0).
  4. La estrategia compra en el mercado por el Volume configurado, agregando cualquier exposición corta para invertir la posición si es necesario.

Entrada corta

  1. Se detecta un patrón Harami bajista (pequeña vela bajista dentro de una vela alcista larga durante una tendencia alcista).
  2. El valor estocástico %D es igual o superior a OverboughtLevel.
  3. No existe ninguna exposición corta (Position >= 0).
  4. La estrategia vende en el mercado, cubriendo primero cualquier posición larga si es necesario.

Salidas

  • Cubrir cortos: Cuando el estocástico %D cruza hacia arriba a través de ExitLowerLevel o ExitUpperLevel, el algoritmo cubre toda la posición corta.
  • Cerrar posiciones largas: Cuando el estocástico %D cruza hacia abajo a través de ExitUpperLevel o ExitLowerLevel, la posición larga se cierra.

Archivos

  • CS/BullishBearishHaramiStochasticStrategy.cs: implementación StockSharp de alto nivel de la estrategia.
  • README.md — Documentación en inglés (este archivo).
  • README_ru.md — Documentación rusa.
  • README_zh.md — Documentación china.

Nota: La versión de Python no está incluida según las instrucciones de conversión.

namespace StockSharp.Samples.Strategies;

using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;

/// <summary>
/// Harami + Stochastic strategy: Bullish/bearish harami patterns with stochastic confirmation.
/// </summary>
public class BullishBearishHaramiStochasticStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _stochPeriod;
	private readonly StrategyParam<decimal> _oversold;
	private readonly StrategyParam<decimal> _overbought;
	private readonly StrategyParam<int> _signalCooldownCandles;

	private readonly List<ICandleMessage> _candles = new();
	private decimal _prevK;
	private bool _hasPrevK;
	private int _candlesSinceTrade;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int StochPeriod { get => _stochPeriod.Value; set => _stochPeriod.Value = value; }
	public decimal Oversold { get => _oversold.Value; set => _oversold.Value = value; }
	public decimal Overbought { get => _overbought.Value; set => _overbought.Value = value; }
	public int SignalCooldownCandles { get => _signalCooldownCandles.Value; set => _signalCooldownCandles.Value = value; }

	public BullishBearishHaramiStochasticStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
		_stochPeriod = Param(nameof(StochPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("Stoch Period", "Stochastic K period", "Indicators");
		_oversold = Param(nameof(Oversold), 30m)
			.SetDisplay("Oversold", "Stochastic oversold level", "Signals");
		_overbought = Param(nameof(Overbought), 70m)
			.SetDisplay("Overbought", "Stochastic overbought level", "Signals");
		_signalCooldownCandles = Param(nameof(SignalCooldownCandles), 6)
			.SetGreaterThanZero()
			.SetDisplay("Signal Cooldown", "Bars to wait between trades", "Trading");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_candles.Clear();
		_prevK = 0m;
		_hasPrevK = false;
		_candlesSinceTrade = SignalCooldownCandles;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_candles.Clear();
		_hasPrevK = false;
		_candlesSinceTrade = SignalCooldownCandles;
		var stoch = new StochasticOscillator { K = { Length = StochPeriod }, D = { Length = 3 } };
		var subscription = SubscribeCandles(CandleType);
		subscription.BindEx(stoch, ProcessCandle).Start();
	}

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

		if (_candlesSinceTrade < SignalCooldownCandles)
			_candlesSinceTrade++;

		var stochTyped = stochValue as StochasticOscillatorValue;
		if (stochTyped?.K is not decimal kValue) return;

		_candles.Add(candle);
		if (_candles.Count > 5) _candles.RemoveAt(0);

		if (_candles.Count >= 2)
		{
			var curr = _candles[^1];
			var prev = _candles[^2];

			// Bullish harami: prev bearish, curr bullish, curr body inside prev body
			var bullishHarami = prev.OpenPrice > prev.ClosePrice
				&& curr.ClosePrice > curr.OpenPrice
				&& curr.OpenPrice > prev.ClosePrice
				&& curr.ClosePrice < prev.OpenPrice;

			// Bearish harami: prev bullish, curr bearish, curr body inside prev body
			var bearishHarami = prev.ClosePrice > prev.OpenPrice
				&& curr.OpenPrice > curr.ClosePrice
				&& curr.ClosePrice > prev.OpenPrice
				&& curr.OpenPrice < prev.ClosePrice;

			if (bullishHarami && kValue < Oversold && Position <= 0 && _candlesSinceTrade >= SignalCooldownCandles)
			{
				BuyMarket();
				_candlesSinceTrade = 0;
			}
			else if (bearishHarami && kValue > Overbought && Position >= 0 && _candlesSinceTrade >= SignalCooldownCandles)
			{
				SellMarket();
				_candlesSinceTrade = 0;
			}
		}

		if (_hasPrevK)
		{
			if (Position > 0 && _prevK >= Overbought && kValue < Overbought && _candlesSinceTrade >= SignalCooldownCandles)
			{
				SellMarket();
				_candlesSinceTrade = 0;
			}
			else if (Position < 0 && _prevK <= Oversold && kValue > Oversold && _candlesSinceTrade >= SignalCooldownCandles)
			{
				BuyMarket();
				_candlesSinceTrade = 0;
			}
		}

		_prevK = kValue;
		_hasPrevK = true;
	}
}