Ver no GitHub

Estratégia Exp i-KlPrice Vol

Visão Geral

Esta estratégia é uma conversão em C# do especialista MetaTrader Exp_i-KlPrice_Vol.mq5. Reconstrói o oscilador KlPrice que mede a distância entre o preço e uma banda de volatilidade, multiplica o oscilador pelo volume da candle e rastreia transições de cor geradas por limiares adaptativos. Dois slots de posição independentes são emulados para cada direção, espelhando o comportamento dual-magic do consultor especialista original.

Lógica do Indicador

  • O preço é transformado usando o modo AppliedPrice selecionado (close, open, median, Demark, etc.).
  • O preço transformado é suavizado pelo método de média móvel definido em PriceMaMethod e PriceMaLength.
  • O intervalo da candle (High - Low) é suavizado com RangeMaMethod/RangeMaLength. O intervalo age como largura dinâmica de banda.
  • O oscilador KlPrice base é calculado como 100 * (Price - (MA - RangeMA)) / (2 * RangeMA) - 50.
  • O oscilador é multiplicado pela fonte de volume selecionada (AppliedVolume.Tick ou AppliedVolume.Real).
  • Um suavizador Jurik de comprimento SmoothingLength é aplicado tanto ao oscilador quanto ao volume bruto, criando duas séries adaptativas.
  • Limiares adaptativos são obtidos multiplicando o volume suavizado por HighLevel2, HighLevel1, LowLevel1 e LowLevel2.
  • A cor atual do oscilador é determinada comparando o valor do oscilador suavizado com os limiares adaptativos:
    • 4 – acima de HighLevel2 * volume (pressão altista extrema).
    • 3 – entre HighLevel1 * volume e o nível extremo.
    • 2 – entre os limiares altista e baixista.
    • 1 – entre o limiar inferior e a linha neutra.
    • 0 – abaixo de LowLevel2 * volume (pressão baixista extrema).

Regras de Trading

  1. Avaliar a cor em SignalBar (geralmente a candle completada anterior) e a cor antes dela.
  2. Entradas compradas:
    • O Slot 1 abre quando a cor muda de 4 para qualquer valor abaixo de 4 e AllowLongEntry é true.
    • O Slot 2 abre quando a cor muda de 3 para abaixo de 3.
  3. Entradas vendidas:
    • O Slot 1 abre quando a cor sobe de 0 para acima de 0 e AllowShortEntry é true.
    • O Slot 2 abre quando a cor sobe de 1 para acima de 1.
  4. Saídas compradas ocorrem quando a cor anterior era 0 ou 1 e AllowLongExit está habilitado.
  5. Saídas vendidas ocorrem quando a cor anterior era 4 ou 3 e AllowShortExit está habilitado.
  6. Cada slot rastreia o tempo do último sinal para evitar ordens duplicadas na mesma candle. Stops de proteção são opcionais e são tratados através de StartProtection quando StopLossPoints ou TakeProfitPoints são maiores que zero.

Parâmetros

Nome Tipo Padrão Descrição
PrimaryVolume decimal 0.1 Volume usado pelo primeiro slot comprado/vendido.
SecondaryVolume decimal 0.2 Volume usado pelo segundo slot.
StopLossPoints int 1000 Distância de stop de proteção opcional em passos de preço.
TakeProfitPoints int 2000 Distância de take-profit opcional em passos de preço.
AllowLongEntry bool true Habilitar abertura de posições compradas.
AllowShortEntry bool true Habilitar abertura de posições vendidas.
AllowLongExit bool true Fechar posições compradas quando aparecem cores baixistas.
AllowShortExit bool true Fechar posições vendidas quando aparecem cores altistas.
CandleType DataType H8 Período de candle para cálculos.
PriceMaMethod SmoothMethod Sma Tipo de média móvel usada no preço aplicado.
PriceMaLength int 100 Comprimento do suavizador de preço.
PriceMaPhase int 15 Parâmetro de fase para filtros baseados em Jurik.
RangeMaMethod SmoothMethod Jjma Tipo de média móvel usada no intervalo da candle.
RangeMaLength int 20 Comprimento do suavizador de intervalo.
RangeMaPhase int 100 Parâmetro de fase para o suavizador de intervalo.
SmoothingLength int 20 Comprimento de suavização Jurik aplicado ao oscilador e volume.
AppliedPrice AppliedPrice Close Fonte de preço usada nos cálculos do oscilador.
VolumeType AppliedVolume Tick Fonte de volume multiplicada pelo oscilador.
HighLevel2 int 150 Multiplicador extremo superior para o limiar adaptativo.
HighLevel1 int 20 Multiplicador moderado superior.
LowLevel1 int -20 Multiplicador moderado inferior.
LowLevel2 int -150 Multiplicador extremo inferior.
SignalBar int 1 Offset histórico usado para ler transições de cor.

Notas de Uso

  • Conecte a estratégia a um instrumento que forneça informações de preço e volume; quando apenas o volume de ticks está disponível, o contador de ticks é usado como proxy.
  • Os dois volumes de slot podem ser ajustados independentemente para emular a configuração de gestão de dinheiro dual do EA original.
  • Ajuste SignalBar ao trabalhar com candles parcialmente formadas ou ao ressincronizar dados históricos.
  • Os métodos de suavização suportam filtros Jurik através de reflexão para replicar o comportamento da biblioteca MQL SmoothAlgorithms.
  • Como StartProtection é invocado apenas quando as distâncias de stop ou alvo são positivas, deixe-as em zero para desabilitar as ordens de proteção.
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;

/// <summary>
/// KlPrice-Vol strategy using EMA crossover with volume confirmation.
/// Buys on fast EMA crossing above slow EMA with rising volume.
/// Sells on reverse crossover.
/// </summary>
public class ExpIKlPriceVolStrategy : 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;

	/// <summary>
	/// Fast EMA period.
	/// </summary>
	public int FastPeriod
	{
		get => _fastPeriod.Value;
		set => _fastPeriod.Value = value;
	}

	/// <summary>
	/// Slow EMA period.
	/// </summary>
	public int SlowPeriod
	{
		get => _slowPeriod.Value;
		set => _slowPeriod.Value = value;
	}

	/// <summary>
	/// Stop-loss distance in price steps.
	/// </summary>
	public int StopLossPoints
	{
		get => _stopLossPoints.Value;
		set => _stopLossPoints.Value = value;
	}

	/// <summary>
	/// Take-profit distance in price steps.
	/// </summary>
	public int TakeProfitPoints
	{
		get => _takeProfitPoints.Value;
		set => _takeProfitPoints.Value = value;
	}

	/// <summary>
	/// Initializes strategy parameters.
	/// </summary>
	public ExpIKlPriceVolStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 50)
			.SetGreaterThanZero()
			.SetDisplay("Fast Period", "Fast EMA period", "Indicator");

		_slowPeriod = Param(nameof(SlowPeriod), 200)
			.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");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_fast = null;
		_slow = null;
		_prevFast = 0;
		_prevSlow = 0;
		_entryPrice = 0;
		_cooldown = 0;
	}

	/// <inheritdoc />
	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;

		// Check SL/TP
		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step)
			{
				SellMarket();
				_entryPrice = 0;
				_cooldown = 60;
				_prevFast = fastValue;
				_prevSlow = slowValue;
				return;
			}

			if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step)
			{
				SellMarket();
				_entryPrice = 0;
				_cooldown = 60;
				_prevFast = fastValue;
				_prevSlow = slowValue;
				return;
			}
		}
		else if (Position < 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step)
			{
				BuyMarket();
				_entryPrice = 0;
				_cooldown = 60;
				_prevFast = fastValue;
				_prevSlow = slowValue;
				return;
			}

			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step)
			{
				BuyMarket();
				_entryPrice = 0;
				_cooldown = 60;
				_prevFast = fastValue;
				_prevSlow = slowValue;
				return;
			}
		}

		// EMA crossover
		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();

			BuyMarket();
			_entryPrice = close;
			_cooldown = 60;
		}
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{
			if (Position > 0)
				SellMarket();

			SellMarket();
			_entryPrice = close;
			_cooldown = 60;
		}

		_prevFast = fastValue;
		_prevSlow = slowValue;
	}
}