Ver no GitHub

Estratégia de Momento de Reversão à Média

Visão geral

A estratégia Mean Reversion é uma porta direta do consultor especialista MetaTrader Mean reversion.mq4. A versão StockSharp mantém a ideia comercial original: comprar após uma longa série de fechamentos decrescentes e vender após uma corrida de alta semelhante. As entradas são confirmadas pelo alinhamento de tendências usando duas médias móveis lineares ponderadas, força de impulso em um período de tempo mais alto e um filtro mensal MACD.

Uma vez posicionada, a estratégia recria as regras de gerenciamento de dinheiro da versão MQL: stop-loss e take-profit configuráveis em pips, realocação opcional do ponto de equilíbrio e um trailing stop que bloqueia os lucros à medida que o mercado se move a favor da negociação.

Lógica de negociação

  1. Período de sinal – a estratégia opera na série de velas selecionada (padrão 15 minutos).
  2. Detecção de exaustão – coleta os últimos BarsToCount fechamentos. Uma configuração longa exige que o fechamento mais recente seja inferior a cada um dos fechamentos anteriores, sinalizando uma liquidação. Uma configuração curta precisa da condição oposta.
  3. Filtro de tendência – LWMA rápido (comprimento FastMaLength) deve estar acima do LWMA lento (SlowMaLength) para posições compradas e abaixo para posições vendidas.
  4. Filtro de impulso – o indicador de impulso (período MomentumLength) é calculado no período de tempo superior do estilo MetaTrader (M15 → H1, H1 → D1, etc.). Pelo menos uma das últimas três leituras de momento deve divergir de 100 em mais de MomentumThreshold.
  5. MACD confirmação – um MACD mensal (26/12/9) deve ter a linha principal acima da linha de sinal para posições compradas e abaixo para posições vendidas.

Se todas as condições forem satisfeitas, a estratégia abre uma posição usando OrderVolume. As negociações opostas achatam a posição atual antes de reverter.

Gerenciamento de posição

  • Stop-loss e take-profit – configurados em pips via StopLossPips e TakeProfitPips.
  • Ponto de equilíbrio – quando ativado, o stop é movido para o preço de entrada mais BreakEvenOffsetPips após o preço avançar em BreakEvenTriggerPips.
  • Trailing Stop – se EnableTrailing for verdadeiro e o lucro não realizado exceder TrailingStopPips, o stop segue o preço com a etapa TrailingStepPips.

Todas as conversões de preço usam o tamanho do pip do instrumento para corresponder ao comportamento MetaTrader.

Parâmetros

Nome Descrição Padrão
OrderVolume Tamanho do pedido usado para entradas no mercado. 1
CandleType Série de velas primárias usadas para sinais. M15
BarsToCount Número de fechamentos anteriores verificados quanto à exaustão. 10
FastMaLength Período LWMA rápido. 6
SlowMaLength Período LWMA lento. 85
MomentumLength Período de impulso no período de tempo superior. 14
MomentumThreshold Desvio absoluto mínimo de 100 para confirmação do impulso. 0.3
StopLossPips Distância de stop-loss em pips. 20
TakeProfitPips Distância de lucro em pips. 50
UseBreakEven Habilite a relocação de parada para atingir o ponto de equilíbrio. false
BreakEvenTriggerPips Lucro em pips necessário antes de mover o stop. 30
BreakEvenOffsetPips Pips extras adicionados ao passar para o ponto de equilíbrio. 30
EnableTrailing Ative o gerenciamento de trailing stop. true
TrailingStopPips Lucro em pips necessário para iniciar o trailing. 40
TrailingStepPips Distância mantida pelo trailing stop. 40

Notas

  • O prazo mais alto para o impulso segue MetaTrader etapas: M1→M15, M5→M30, M15→H1, M30→H4, H1→D1, H4→W1, D1→MN1, W1→MN1.
  • A confirmação de MACD sempre usa o período mensal (MN1).
  • A estratégia espera tipos de velas baseadas em prazos; velas de tick ou intervalo não são suportadas.
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>
/// Simplified from "Mean Reversion" MetaTrader expert.
/// Buys after multi-bar sell-off when RSI is oversold, sells after multi-bar rally when RSI is overbought.
/// Uses consecutive bar count for exhaustion detection with RSI confirmation.
/// </summary>
public class MeanReversionMomentumStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _barsToCount;
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<decimal> _rsiOverbought;
	private readonly StrategyParam<decimal> _rsiOversold;

	private RelativeStrengthIndex _rsi;
	private readonly List<decimal> _closeHistory = new();

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public int BarsToCount
	{
		get => _barsToCount.Value;
		set => _barsToCount.Value = value;
	}

	public int RsiPeriod
	{
		get => _rsiPeriod.Value;
		set => _rsiPeriod.Value = value;
	}

	public decimal RsiOverbought
	{
		get => _rsiOverbought.Value;
		set => _rsiOverbought.Value = value;
	}

	public decimal RsiOversold
	{
		get => _rsiOversold.Value;
		set => _rsiOversold.Value = value;
	}

	public MeanReversionMomentumStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Primary timeframe", "General");

		_barsToCount = Param(nameof(BarsToCount), 5)
			.SetGreaterThanZero()
			.SetDisplay("Bars To Count", "Number of consecutive bars for exhaustion detection", "Signal");

		_rsiPeriod = Param(nameof(RsiPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("RSI Period", "RSI period for confirmation", "Indicators");

		_rsiOverbought = Param(nameof(RsiOverbought), 70m)
			.SetDisplay("RSI Overbought", "RSI level for sell signal", "Signals");

		_rsiOversold = Param(nameof(RsiOversold), 30m)
			.SetDisplay("RSI Oversold", "RSI level for buy signal", "Signals");
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_closeHistory.Clear();
		_rsi = new RelativeStrengthIndex { Length = RsiPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(_rsi, ProcessCandle)
			.Start();

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

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

		_closeHistory.Add(candle.ClosePrice);
		if (_closeHistory.Count > BarsToCount + 1)
			_closeHistory.RemoveAt(0);

		if (!_rsi.IsFormed || _closeHistory.Count < BarsToCount + 1)
			return;

		var volume = Volume;
		if (volume <= 0)
			volume = 1;

		// Count consecutive down bars
		var downCount = 0;
		for (int i = _closeHistory.Count - 1; i >= 1; i--)
		{
			if (_closeHistory[i] < _closeHistory[i - 1])
				downCount++;
			else
				break;
		}

		// Count consecutive up bars
		var upCount = 0;
		for (int i = _closeHistory.Count - 1; i >= 1; i--)
		{
			if (_closeHistory[i] > _closeHistory[i - 1])
				upCount++;
			else
				break;
		}

		// Multi-bar sell-off + RSI oversold -> mean reversion buy
		if (downCount >= BarsToCount && rsiValue < RsiOversold)
		{
			if (Position < 0)
				BuyMarket(Math.Abs(Position));

			if (Position <= 0)
				BuyMarket(volume);
		}
		// Multi-bar rally + RSI overbought -> mean reversion sell
		else if (upCount >= BarsToCount && rsiValue > RsiOverbought)
		{
			if (Position > 0)
				SellMarket(Position);

			if (Position >= 0)
				SellMarket(volume);
		}
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		_closeHistory.Clear();
		_rsi = null;

		base.OnReseted();
	}
}