Ver no GitHub

Estratégia Harami de alta e baixa Stochastic

A Estratégia Harami Stochastic de alta e baixa é a versão StockSharp do MetaTrader Expert Advisor expert_abh_bh_stoch.mq5 da pasta MQL/310. O especialista original usa reconhecimento de padrão de velas para configurações Bullish Harami e Bearish Harami e requer uma confirmação do oscilador estocástico. A versão C# mantém a mesma lógica usando o StockSharp API de alto nível e adiciona registro detalhado e saída de gráfico para facilitar o monitoramento.

Ideias Centrais

  • Detecte os padrões de velas Bullish Harami e Bearish Harami usando as duas velas concluídas anteriores.
  • Confirme as configurações de alta com a linha estocástica %D abaixo de um limite de sobrevenda e as configurações de baixa com %D acima de um limite de sobrecompra.
  • Feche as posições curtas quando a linha estocástica %D subir acima dos limites de saída inferior ou superior e feche as posições longas quando %D cair abaixo desses limites.

Parâmetros

Parâmetro Descrição Padrão
CandleType Prazo da série de velas usada para reconhecimento de padrões. 1 Hour
StochasticKPeriod Período de lookback para o cálculo estocástico de %K. 47
StochasticDPeriod Período de suavização para a linha %D. 9
StochasticSlowing Suavização adicional aplicada a %K (MT5 “desaceleração”). 13
MovingAveragePeriod Número de velas usadas para calcular a média do tamanho do corpo para validação do padrão. 5
OversoldLevel Limite de Stochastic%D para confirmar sinais de alta. 30
OverboughtLevel Limite de Stochastic%D para confirmar sinais de baixa. 70
ExitLowerLevel Nível estocástico inferior que aciona saídas. 20
ExitUpperLevel Nível estocástico superior que aciona saídas. 80

Regras de negociação

Entrada longa

  1. Um padrão Harami de alta é detectado nas duas velas concluídas mais recentemente (uma pequena vela de alta engolfada por uma vela de baixa mais longa em uma tendência de baixa).
  2. A linha %D estocástica da vela de confirmação está igual ou inferior a OversoldLevel.
  3. Nenhuma posição longa está aberta no momento (Position <= 0).
  4. A estratégia compra a mercado para o Volume configurado, adicionando qualquer exposição curta para inverter a posição, se necessário.

Entrada curta

  1. Um padrão Bearish Harami é detectado (pequena vela de baixa dentro de uma longa vela de alta durante uma tendência de alta).
  2. O valor %D estocástico é igual ou superior a OverboughtLevel.
  3. Não existe exposição curta (Position >= 0).
  4. A estratégia vende no mercado, cobrindo primeiro qualquer posição longa, se necessário.

Saídas

  • Cover Shorts: Quando o %D estocástico cruza para cima através de ExitLowerLevel ou ExitUpperLevel, o algoritmo cobre toda a posição curta.
  • Fechar posições compradas: Quando o estocástico %D cruza para baixo através de ExitUpperLevel ou ExitLowerLevel, a posição longa é fechada.

Arquivos

  • CS/BullishBearishHaramiStochasticStrategy.cs — implementação StockSharp de alto nível da estratégia.
  • README.md — Documentação em inglês (este arquivo).
  • README_ru.md — Documentação russa.
  • README_zh.md — Documentação chinesa.

Observação: A versão Python não está incluída nas instruções de conversão.

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;
	}
}