Auf GitHub ansehen

Mean-Reversion-Momentum-Strategie

Überblick

Die Mean Reversion-Strategie ist eine direkte Portierung des MetaTrader-Expertenberaters Mean reversion.mq4. Die StockSharp-Version behält die ursprüngliche Handelsidee bei: Kauf nach einer längeren Serie fallender Schlusskurse und Verkauf nach einem ähnlichen Aufwärtstrend. Einträge werden durch Trendausrichtung mit zwei linear gewichteten gleitenden Durchschnitten, Momentumstärke in einem höheren Zeitrahmen und einem monatlichen MACD-Filter bestätigt.

Sobald die Strategie in Position ist, werden die Money-Management-Regeln der MQL-Version neu erstellt: konfigurierbarer Stop-Loss und Take-Profit in Pips, optionale Break-Even-Verschiebung und ein Trailing-Stop, der Gewinne sichert, wenn sich der Markt zu Gunsten des Handels bewegt.

Handelslogik

  1. Signalzeitrahmen – die Strategie arbeitet mit der ausgewählten Kerzenserie (Standard 15 Minuten).
  2. Erschöpfungserkennung – es werden die letzten BarsToCount Schließungen erfasst. Bei einem Long-Setup muss der letzte Schlusskurs niedriger sein als jeder der vorherigen Schlusskurse, was einen Ausverkauf signalisiert. Ein kurzes Setup erfordert die gegenteilige Bedingung.
  3. Trendfilter – Der schnelle LWMA (Länge FastMaLength) muss für Long-Positionen über dem langsamen LWMA (SlowMaLength) und für Short-Positionen darunter liegen.
  4. Momentum-Filter – der Momentum-Indikator (Periode MomentumLength) wird auf dem MetaTrader-ähnlichen höheren Zeitrahmen berechnet (M15 → H1, H1 → D1 usw.). Mindestens einer der letzten drei Impulswerte muss um mehr als MomentumThreshold von 100 abweichen.
  5. MACD-Bestätigung – bei einem monatlichen MACD (26.12.9) muss die Hauptlinie für Long-Positionen über der Signallinie und für Short-Positionen darunter liegen.

Wenn alle Bedingungen erfüllt sind, eröffnet die Strategie eine Position mit OrderVolume. Gegensätzliche Trades glätten die aktuelle Position, bevor sie sich umkehren.

Positionsmanagement

  • Stop-Loss und Take-Profit – konfiguriert in Pips über StopLossPips und TakeProfitPips.
  • Break-even – wenn aktiviert, wird der Stop auf den Einstiegspreis plus BreakEvenOffsetPips verschoben, nachdem der Preis um BreakEvenTriggerPips gestiegen ist.
  • Trailing Stop – wenn EnableTrailing wahr ist und der nicht realisierte Gewinn TrailingStopPips übersteigt, folgt der Stop dem Preis mit Schritt TrailingStepPips.

Bei allen Preisumrechnungen wird die Pip-Größe des Instruments verwendet, um das Verhalten von MetaTrader abzugleichen.

Parameter

Name Beschreibung Standard
OrderVolume Ordergröße, die für Markteintritte verwendet wird. 1
CandleType Primäre Kerzenserie, die für Signale verwendet wird. M15
BarsToCount Anzahl der vorherigen Abschlüsse, die auf Erschöpfung überprüft wurden. 10
FastMaLength Schnelle LWMA-Periode. 6
SlowMaLength Langsame LWMA-Periode. 85
MomentumLength Momentum-Periode im höheren Zeitrahmen. 14
MomentumThreshold Minimale absolute Abweichung von 100 zur Impulsbestätigung. 0.3
StopLossPips Stop-Loss-Distanz in Pips. 20
TakeProfitPips Take-Profit-Distanz in Pips. 50
UseBreakEven Aktivieren Sie die Stop-Verlagerung zum Break-Even. false
BreakEvenTriggerPips Der Gewinn in Pips ist erforderlich, bevor der Stopp verschoben wird. 30
BreakEvenOffsetPips Beim Erreichen der Gewinnschwelle werden zusätzliche Pips hinzugefügt. 30
EnableTrailing Aktivieren Sie die Trailing-Stop-Verwaltung. true
TrailingStopPips Der Gewinn in Pips ist erforderlich, um mit dem Trailing zu beginnen. 40
TrailingStepPips Vom Trailing Stop aufrechterhaltener Abstand. 40

Notizen

  • Der höhere Zeitrahmen für den Impuls folgt MetaTrader Schritten: M1→M15, M5→M30, M15→H1, M30→H4, H1→D1, H4→W1, D1→MN1, W1→MN1.
  • Die MACD-Bestätigung verwendet immer den monatlichen Zeitrahmen (MN1).
  • Die Strategie erwartet zeitrahmenbasierte Kerzentypen; Tick- oder Range-Kerzen werden nicht unterstützt.
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();
	}
}