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
Signalzeitrahmen – die Strategie arbeitet mit der ausgewählten Kerzenserie (Standard 15 Minuten).
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.
Trendfilter – Der schnelle LWMA (Länge FastMaLength) muss für Long-Positionen über dem langsamen LWMA (SlowMaLength) und für Short-Positionen darunter liegen.
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.
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();
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class mean_reversion_momentum_strategy(Strategy):
def __init__(self):
super(mean_reversion_momentum_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5)))
self._bars_to_count = self.Param("BarsToCount", 5)
self._rsi_period = self.Param("RsiPeriod", 14)
self._rsi_overbought = self.Param("RsiOverbought", 70.0)
self._rsi_oversold = self.Param("RsiOversold", 30.0)
self._close_history = []
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def BarsToCount(self):
return self._bars_to_count.Value
@BarsToCount.setter
def BarsToCount(self, value):
self._bars_to_count.Value = value
@property
def RsiPeriod(self):
return self._rsi_period.Value
@RsiPeriod.setter
def RsiPeriod(self, value):
self._rsi_period.Value = value
@property
def RsiOverbought(self):
return self._rsi_overbought.Value
@RsiOverbought.setter
def RsiOverbought(self, value):
self._rsi_overbought.Value = value
@property
def RsiOversold(self):
return self._rsi_oversold.Value
@RsiOversold.setter
def RsiOversold(self, value):
self._rsi_oversold.Value = value
def OnReseted(self):
super(mean_reversion_momentum_strategy, self).OnReseted()
self._close_history = []
def OnStarted2(self, time):
super(mean_reversion_momentum_strategy, self).OnStarted2(time)
self._close_history = []
rsi = RelativeStrengthIndex()
rsi.Length = self.RsiPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(rsi, self._process_candle).Start()
def _process_candle(self, candle, rsi_value):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
rsi_val = float(rsi_value)
bars_to_count = self.BarsToCount
self._close_history.append(close)
while len(self._close_history) > bars_to_count + 1:
self._close_history.pop(0)
if len(self._close_history) < bars_to_count + 1:
return
# Count consecutive down bars
down_count = 0
for i in range(len(self._close_history) - 1, 0, -1):
if self._close_history[i] < self._close_history[i - 1]:
down_count += 1
else:
break
# Count consecutive up bars
up_count = 0
for i in range(len(self._close_history) - 1, 0, -1):
if self._close_history[i] > self._close_history[i - 1]:
up_count += 1
else:
break
# Multi-bar sell-off + RSI oversold -> mean reversion buy
if down_count >= bars_to_count and rsi_val < float(self.RsiOversold):
if self.Position <= 0:
self.BuyMarket()
# Multi-bar rally + RSI overbought -> mean reversion sell
elif up_count >= bars_to_count and rsi_val > float(self.RsiOverbought):
if self.Position >= 0:
self.SellMarket()
def CreateClone(self):
return mean_reversion_momentum_strategy()