Ver no GitHub

Estratégia de Reversão KlPrice

Esta estratégia é uma conversão em C# do especialista MQL5 original exp_i-KlPrice.mq5. Implementa um sistema de reversão baseado em um oscilador de preço normalizado. O oscilador compara o preço atual com uma banda de preço suavizada derivada de uma média móvel e o intervalo verdadeiro médio (ATR). Cruzar os limites predefinidos gera sinais de trading.

Como Funciona

  1. Uma média móvel simples (SMA) suaviza o preço de fechamento.

  2. Um Intervalo Verdadeiro Médio (ATR) estima a volatilidade do mercado.

  3. O oscilador é calculado como:

    jres = 100 * (Close - (SMA - ATR)) / (2 * ATR) - 50

  4. O valor do oscilador é mapeado para cinco zonas de cor:

    • 4 – acima do nível superior
    • 3 – entre zero e o nível superior
    • 2 – entre os níveis superior e inferior
    • 1 – entre o nível inferior e zero
    • 0 – abaixo do nível inferior
  5. Uma posição comprada é aberta quando o oscilador sai da zona 4. Uma posição vendida é aberta quando sai da zona 0. As posições existentes fecham quando o oscilador cruza zero.

Parâmetros

Nome Descrição
CandleType Período para dados de preço.
PriceMaLength Período de SMA para suavização de preço.
AtrLength Período de ATR para calcular a banda de preço.
UpLevel Limiar superior do oscilador.
DownLevel Limiar inferior do oscilador.
EnableBuy Permitir abertura de posições compradas.
EnableSell Permitir abertura de posições vendidas.

Uso

  1. Criar uma instância de KlPriceReversalStrategy.
  2. Definir os parâmetros desejados.
  3. Anexar a estratégia a um portfólio e ativo.
  4. Iniciar a estratégia para receber sinais e colocar ordens.

A estratégia usa ordens a mercado via BuyMarket e SellMarket. A proteção de posição é ativada através de StartProtection().

Notas

  • A implementação aproxima o indicador MQL original usando indicadores integrados do StockSharp (SimpleMovingAverage e AverageTrueRange).
  • Todos os cálculos são realizados apenas em velas concluídas.
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 reversal strategy.
/// Calculates a normalized oscillator based on price position relative to EMA and ATR.
/// Buys when leaving overbought zone, sells when leaving oversold zone.
/// </summary>
public class KlPriceReversalStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _priceMaLength;
	private readonly StrategyParam<int> _atrLength;
	private readonly StrategyParam<decimal> _upLevel;
	private readonly StrategyParam<decimal> _downLevel;

	private decimal _prevColor;
	private bool _isFirst;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int PriceMaLength { get => _priceMaLength.Value; set => _priceMaLength.Value = value; }
	public int AtrLength { get => _atrLength.Value; set => _atrLength.Value = value; }
	public decimal UpLevel { get => _upLevel.Value; set => _upLevel.Value = value; }
	public decimal DownLevel { get => _downLevel.Value; set => _downLevel.Value = value; }

	public KlPriceReversalStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Time frame for calculations", "General");

		_priceMaLength = Param(nameof(PriceMaLength), 100)
			.SetGreaterThanZero()
			.SetDisplay("Price MA Length", "EMA period for price smoothing", "Parameters");

		_atrLength = Param(nameof(AtrLength), 20)
			.SetGreaterThanZero()
			.SetDisplay("ATR Length", "ATR period for range estimation", "Parameters");

		_upLevel = Param(nameof(UpLevel), 50m)
			.SetDisplay("Upper Level", "Upper threshold for signals", "Parameters");

		_downLevel = Param(nameof(DownLevel), -50m)
			.SetDisplay("Lower Level", "Lower threshold for signals", "Parameters");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevColor = 2m;
		_isFirst = true;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_isFirst = true;
		_prevColor = 2m;

		var priceMa = new ExponentialMovingAverage { Length = PriceMaLength };
		var atr = new AverageTrueRange { Length = AtrLength };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.BindEx(priceMa, atr, ProcessCandle)
			.Start();
	}

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

		if (!maValue.IsFormed || !atrValue.IsFormed)
			return;

		if (!IsFormedAndOnlineAndAllowTrading())
			return;

		var ma = maValue.ToDecimal();
		var tr = atrValue.ToDecimal();
		if (tr == 0m)
			return;

		var dwband = ma - tr;
		var jres = 100m * (candle.ClosePrice - dwband) / (2m * tr) - 50m;

		var color = 2m;
		if (jres > UpLevel)
			color = 4m;
		else if (jres > 0m)
			color = 3m;

		if (jres < DownLevel)
			color = 0m;
		else if (jres < 0m)
			color = 1m;

		if (!_isFirst)
		{
			// Buy: leaving overbought (was 4, now < 4)
			if (_prevColor == 4m && color < 4m && Position <= 0)
			{
				if (Position < 0) BuyMarket();
				BuyMarket();
			}
			// Sell: leaving oversold (was 0, now > 0)
			else if (_prevColor == 0m && color > 0m && Position >= 0)
			{
				if (Position > 0) SellMarket();
				SellMarket();
			}
		}

		_prevColor = color;
		_isFirst = false;
	}
}