Ver no GitHub

Estratégia reversa média

Visão geral

A estratégia Mean Reverse replica o consultor especialista "MeanReversionTrendEA". Ele combina um módulo de tendência de cruzamento de média móvel com uma sobreposição de reversão à média impulsionada por bandas de volatilidade Average True Range (ATR). A ideia é abrir uma posição quando o preço confirmar uma mudança de tendência de alta ou baixa ou se afastar muito da média móvel mais lenta por uma distância ajustada pela volatilidade.

Lógica de negociação

  • Componente de tendência: uma configuração longa aparece quando a média móvel simples rápida (SMA) cruza acima da lenta SMA. Uma configuração curta é acionada quando o SMA rápido cruza abaixo do SMA lento.
  • Componente de reversão à média: uma configuração longa é ativada sempre que o preço de fechamento cai abaixo do lento SMA em mais de ATR × Multiplier. Uma configuração curta aparece quando o preço sobe acima do lento SMA por mais do que a mesma distância.
  • Combinação de sinais: se o módulo de tendência ou o módulo de reversão à média sinalizar uma posição comprada (curta) enquanto nenhuma posição estiver aberta, a estratégia entra em uma posição comprada (curta) com o volume configurado.

Gestão Comercial

  • Stop-loss: imediatamente após a entrada a estratégia coloca um nível de preço em entry − StopLossPoints × Step para posições longas ou entry + StopLossPoints × Step para posições curtas. Quando os extremos da vela tocam este nível, a posição é fechada.
  • Take-profit: uma meta de lucro é colocada em entry + TakeProfitPoints × Step para negociações longas ou entry − TakeProfitPoints × Step para negociações curtas. Um toque na respectiva máxima ou mínima da vela fecha a posição.
  • Restrição de posição única: o algoritmo mantém no máximo uma posição aberta. Novos sinais são ignorados até que a negociação atual seja fechada.
  • Módulo de segurança: a chamada StartProtection() integrada espelha a camada de validação de negociação de segurança do consultor especialista original e protege contra estados de posição inesperados.

Indicadores

  • Média Móvel Simples (SMA) com período FastMaPeriod.
  • Média Móvel Simples (SMA) com período SlowMaPeriod.
  • Intervalo verdadeiro médio (ATR) com período AtrPeriod.

Todos os indicadores são atualizados a partir da mesma assinatura de vela definida por CandleType.

Parâmetros

Nome Descrição Padrão
FastMaPeriod Lookback do SMA rápido usado na detecção de tendências e nas bandas de reversão à média. 20
SlowMaPeriod Lookback do lento SMA que representa a média de equilíbrio. 50
AtrPeriod Número de velas para cálculo de volatilidade ATR. 14
AtrMultiplier Multiplicador aplicado a ATR para verificações de distância. 2,0
StopLossPoints Distância de stop-loss medida em Security.Step unidades. 500
TakeProfitPoints Distância de realização de lucro medida em Security.Step unidades. 1000
TradeVolume Volume enviado com cada ordem de mercado. 1
CandleType Tipo de dados Candle que alimenta os indicadores. Período de 1 hora

Notas

  • O tamanho padrão da vela é de uma hora para refletir a lógica do "período atual" da versão MetaTrader. Ajuste-o para corresponder ao período original do gráfico.
  • Os envelopes baseados em ATR usam o fechamento da vela como preço de referência, refletindo o ponto médio original entre o lance e o pedido.
  • Use os sinalizadores de otimização anexados aos parâmetros para calibrar o sistema para diferentes mercados.
namespace StockSharp.Samples.Strategies;

using System;
using System.Linq;
using System.Collections.Generic;

using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

/// <summary>
/// Mean reversion and trend following hybrid that reacts to moving average crossovers and ATR deviations.
/// </summary>
public class MeanReverseStrategy : Strategy
{
	private readonly StrategyParam<int> _fastMaPeriod;
	private readonly StrategyParam<int> _slowMaPeriod;
	private readonly StrategyParam<int> _atrPeriod;
	private readonly StrategyParam<decimal> _atrMultiplier;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;
	private readonly StrategyParam<decimal> _tradeVolume;
	private readonly StrategyParam<DataType> _candleType;

	private SimpleMovingAverage _fastMa = null!;
	private SimpleMovingAverage _slowMa = null!;
	private AverageTrueRange _atr = null!;

	private decimal? _prevFastMa;
	private decimal? _prevSlowMa;
	private decimal _stopLossPrice;
	private decimal _takeProfitPrice;

	/// <summary>
	/// Fast simple moving average period.
	/// </summary>
	public int FastMaPeriod
	{
		get => _fastMaPeriod.Value;
		set => _fastMaPeriod.Value = value;
	}

	/// <summary>
	/// Slow simple moving average period.
	/// </summary>
	public int SlowMaPeriod
	{
		get => _slowMaPeriod.Value;
		set => _slowMaPeriod.Value = value;
	}

	/// <summary>
	/// ATR lookback period.
	/// </summary>
	public int AtrPeriod
	{
		get => _atrPeriod.Value;
		set => _atrPeriod.Value = value;
	}

	/// <summary>
	/// Multiplier applied to ATR for mean reversion envelopes.
	/// </summary>
	public decimal AtrMultiplier
	{
		get => _atrMultiplier.Value;
		set => _atrMultiplier.Value = value;
	}

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

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

	/// <summary>
	/// Volume used for a new position.
	/// </summary>
	public decimal TradeVolume
	{
		get => _tradeVolume.Value;
		set => _tradeVolume.Value = value;
	}

	/// <summary>
	/// Candle type processed by the strategy.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Initializes a new instance of the strategy.
	/// </summary>
	public MeanReverseStrategy()
	{
		_fastMaPeriod = Param(nameof(FastMaPeriod), 20)
			.SetRange(5, 200)
			.SetDisplay("Fast MA Period", "Length of the fast simple moving average", "Indicators")
			;

		_slowMaPeriod = Param(nameof(SlowMaPeriod), 50)
			.SetRange(5, 300)
			.SetDisplay("Slow MA Period", "Length of the slow simple moving average", "Indicators")
			;

		_atrPeriod = Param(nameof(AtrPeriod), 14)
			.SetRange(5, 100)
			.SetDisplay("ATR Period", "Number of candles used for ATR calculation", "Indicators")
			;

		_atrMultiplier = Param(nameof(AtrMultiplier), 2m)
			.SetRange(0.5m, 5m)
			.SetDisplay("ATR Multiplier", "Multiplier applied to ATR for deviation bands", "Indicators")
			;

		_stopLossPoints = Param(nameof(StopLossPoints), 500)
			.SetRange(10, 5000)
			.SetDisplay("Stop Loss Points", "Stop-loss distance in security steps", "Risk Management")
			;

		_takeProfitPoints = Param(nameof(TakeProfitPoints), 1000)
			.SetRange(10, 10000)
			.SetDisplay("Take Profit Points", "Take-profit distance in security steps", "Risk Management")
			;

		_tradeVolume = Param(nameof(TradeVolume), 1m)
			.SetRange(0.01m, 100m)
			.SetDisplay("Trade Volume", "Volume opened with a new signal", "Execution")
			;

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Type of market data processed by the strategy", "General");
	}

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

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

		_prevFastMa = null;
		_prevSlowMa = null;
		_stopLossPrice = default;
		_takeProfitPrice = default;
	}

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

		_fastMa = new SimpleMovingAverage { Length = FastMaPeriod };
		_slowMa = new SimpleMovingAverage { Length = SlowMaPeriod };
		_atr = new AverageTrueRange { Length = AtrPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(_fastMa, _slowMa, _atr, ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, _fastMa);
			DrawIndicator(area, _slowMa);
			DrawOwnTrades(area);
		}
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastMa, decimal slowMa, decimal atr)
	{
		if (candle.State != CandleStates.Finished)
		return;

		// indicators checked via Bind

		var step = Security?.PriceStep ?? 1m;

		if (_prevFastMa is null || _prevSlowMa is null)
		{
			_prevFastMa = fastMa;
			_prevSlowMa = slowMa;
			return;
		}

		var prevFast = _prevFastMa.Value;
		var prevSlow = _prevSlowMa.Value;

		var trendBuySignal = prevFast <= prevSlow && fastMa > slowMa;
		var trendSellSignal = prevFast >= prevSlow && fastMa < slowMa;

		var deviation = atr * AtrMultiplier;
		var reversionBuySignal = candle.ClosePrice < slowMa - deviation;
		var reversionSellSignal = candle.ClosePrice > slowMa + deviation;

		var buySignal = trendBuySignal || reversionBuySignal;
		var sellSignal = trendSellSignal || reversionSellSignal;

		if (Position == 0)
		{
			if (buySignal)
			{
				EnterLong(candle.ClosePrice, step);
			}
			else if (sellSignal)
			{
				EnterShort(candle.ClosePrice, step);
			}
		}
		else if (Position > 0)
		{
			ManageLongPosition(candle);
		}
		else
		{
			ManageShortPosition(candle);
		}

		_prevFastMa = fastMa;
		_prevSlowMa = slowMa;
	}

	private void EnterLong(decimal entryPrice, decimal step)
	{
		// Open a new long position and pre-calculate risk levels.
		BuyMarket(TradeVolume);

		_stopLossPrice = entryPrice - StopLossPoints * step;
		_takeProfitPrice = entryPrice + TakeProfitPoints * step;
	}

	private void EnterShort(decimal entryPrice, decimal step)
	{
		// Open a new short position and pre-calculate risk levels.
		SellMarket(TradeVolume);

		_stopLossPrice = entryPrice + StopLossPoints * step;
		_takeProfitPrice = entryPrice - TakeProfitPoints * step;
	}

	private void ManageLongPosition(ICandleMessage candle)
	{
		// Exit logic for the long position.
		if (candle.LowPrice <= _stopLossPrice)
		{
			SellMarket(Position);
			ClearRiskLevels();
			return;
		}

		if (_takeProfitPrice != default && candle.HighPrice >= _takeProfitPrice)
		{
			SellMarket(Position);
			ClearRiskLevels();
		}
	}

	private void ManageShortPosition(ICandleMessage candle)
	{
		// Exit logic for the short position.
		if (candle.HighPrice >= _stopLossPrice)
		{
			BuyMarket(Math.Abs(Position));
			ClearRiskLevels();
			return;
		}

		if (_takeProfitPrice != default && candle.LowPrice <= _takeProfitPrice)
		{
			BuyMarket(Math.Abs(Position));
			ClearRiskLevels();
		}
	}

	private void ClearRiskLevels()
	{
		// Reset stored risk levels after an exit.
		_stopLossPrice = default;
		_takeProfitPrice = default;
	}
}