Ver no GitHub

Estratégia de Heikin Ashi Trader

Esta estratégia porta o especialista do MetaTrader 4 "Heikin Ashi Trader" para o StockSharp. Mantém a lógica de confirmação multi-indicador do robô original e a implementa com a API de subscrição de velas de alto nível para que cada decisão seja baseada apenas em barras finalizadas.

Detalhes

  • Indicadores:
    • Velas Heikin-Ashi calculadas a partir do período de trabalho.
    • Duas médias móveis ponderadas lineares (LWMA) usando o preço típico da vela ((high + low + close) / 3).
    • Um oscilador estocástico (os períodos %K/%D/Smooth são configuráveis pelo usuário).
    • Momentum (distância do nível neutro 100).
    • Convergência/Divergência de Médias Móveis (MACD).
  • Critérios de entrada:
    • Comprado: A última vela Heikin-Ashi deve ser altista, pelo menos um dos últimos três valores estocásticos deve estar acima do nível de sobrecompra, a LWMA rápida deve estar acima da LWMA lenta, a distância de momentum desde 100 deve exceder o limiar de compra, e a linha MACD deve estar acima do seu sinal.
    • Vendido: Condições espelhadas — vela Heikin-Ashi baixista, estocástico abaixo do nível de sobrevenda, LWMA rápida abaixo da LWMA lenta, distância de momentum acima do limiar de venda, e linha MACD abaixo do seu sinal.
    • Opcionalmente achatar a exposição oposta antes de entrar no novo trade (CloseOppositePositions).
  • Gestão de posições:
    • Stop-loss e take-profit fixos em pips (derivados do passo de preço do ativo).
    • Trailing stop opcional que segue o fechamento assim que o trade avança TrailingStopPips.
    • Lógica de break-even que move o stop para Entry ± BreakEvenOffsetPips após o preço avançar BreakEvenTriggerPips a favor da posição.
    • Interruptor de saída manual (ForceExit) para achatar tudo na próxima vela.
  • Diferenças vs. versão MT4:
    • O EA original avaliava momentum em um período superior. Este porto mantém os mesmos períodos de indicadores mas os lê do fluxo de velas primário para permanecer dentro da API de alto nível do StockSharp. Os parâmetros permitem ajustar os limiares para recriar a sensibilidade original.
    • As regras de stop baseadas em dinheiro do código MT4 não estão incluídas. O risco é gerenciado através de stops baseados em preço e o módulo de break-even.

Parâmetros

Nome Descrição
CandleType Período (ou qualquer outro tipo de vela) usado para todos os indicadores e decisões de trading.
FastMaPeriod, SlowMaPeriod Períodos das médias móveis ponderadas lineares rápida e lenta (preço típico).
StochasticKPeriod, StochasticDPeriod, StochasticSlowing Comprimentos %K/%D e fator de suavização do oscilador estocástico.
StochasticOverbought, StochasticOversold Limiares estocásticos que devem ser cruzados durante os últimos três valores concluídos.
MomentumPeriod Comprimento do indicador Momentum.
MomentumBuyThreshold, MomentumSellThreshold Distância absoluta mínima da linha 100 necessária para trades comprados/vendidos.
MacdFastPeriod, MacdSlowPeriod, MacdSignalPeriod Configuração MACD.
CloseOppositePositions Fechar o lado oposto antes de entrar em um novo trade.
MaxPositions Exposição líquida máxima por direção (0 = ilimitado).
TradeVolume Volume de cada nova ordem; também atribuído ao Volume da estratégia.
UseStopLoss, StopLossPips Habilitar e dimensionar o stop protetor em pips.
UseTakeProfit, TakeProfitPips Habilitar e dimensionar o take-profit em pips.
UseTrailingStop, TrailingStopPips Habilitar a lógica de trailing stop e definir sua distância.
UseBreakEven, BreakEvenTriggerPips, BreakEvenOffsetPips Distância de ativação do break-even e o offset bloqueado.
ForceExit Quando true, todas as posições são fechadas na próxima vela processada.

Notas de implementação

  • A estratégia subscreve velas através de SubscribeCandles().BindEx(...) para que os indicadores recebam valores finalizados e o código nunca chame GetValue() diretamente.
  • A conversão de pips usa o PriceStep do instrumento; se o mercado cotizar pips fracionários, configurar o passo do ativo adequadamente.
  • Atualizações de trailing e break-even apenas movem o stop na direção favorável. A lógica de redefinição limpa os valores de stop/alvo em cache sempre que um trade é fechado, para que novas posições comecem com configurações de risco novas.
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 HeikinAshiTraderStrategy : 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 HeikinAshiTraderStrategy()
	{
		_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;
	}
}