Omar MMR-Strategie
Momentum-basierte Methode, die RSI, drei exponentielle gleitende Durchschnitte und eine MACD-Kreuzung kombiniert. Long-Trades entstehen, wenn der Kurs über der langsamen EMA liegt, die schnelle EMA die mittlere EMA übersteigt, MACD bullisch kreuzt und RSI in einer neutralen Zone zwischen 29 und 70 liegt.
Take-Profit- und Stop-Loss-Prozentsätze werden über das Schutzmodul des Motors angewendet. Das Setup konzentriert sich auf die Ausrichtung von Momentum und Trend und vermeidet dabei überdehnte RSI-Werte.
Details
- Einstiegskriterien:
- Long: Schluss über EMA C, EMA A > EMA B, MACD-Linie kreuzt über das Signal, RSI zwischen 29 und 70.
- Ausstiegskriterien:
- Verwaltung über Take-Profit oder Stop-Loss; kein expliziter Indikator-Ausstieg.
- Indikatoren:
- RSI (Länge 14)
- EMA A/B/C (Perioden 20/50/200)
- MACD (12,26,9)
- Stops: Prozentbasierter Take-Profit 1,5% und Stop-Loss 2% standardmäßig.
- Standardwerte:
RsiLength= 14EmaALength= 20EmaBLength= 50EmaCLength= 200MacdFastLength= 12MacdSlowLength= 26MacdSignalLength= 9TakeProfitPercent= 1.5StopLossPercent= 2.0
- Filter:
- Trendfortsetzung
- Einzelner Zeitrahmen
- Indikatoren: RSI, EMA, MACD
- Stops: Ja
- Komplexität: Moderat
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>
/// Omar MMR strategy (long only).
/// Buys when the close is above EMA C, EMA A is above EMA B, the MACD line crosses above its signal line and RSI is
/// between 29 and 70. Positions are closed only by the percent take-profit and stop-loss.
/// </summary>
public class OmarMmrStrategy : Strategy
{
private const decimal _rsiLower = 29m;
private const decimal _rsiUpper = 70m;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<int> _emaALength;
private readonly StrategyParam<int> _emaBLength;
private readonly StrategyParam<int> _emaCLength;
private readonly StrategyParam<int> _macdFastLength;
private readonly StrategyParam<int> _macdSlowLength;
private readonly StrategyParam<int> _macdSignalLength;
private readonly StrategyParam<decimal> _takeProfitPercent;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<DataType> _candleType;
private decimal? _prevMacd;
private decimal? _prevSignal;
/// <summary>
/// RSI period.
/// </summary>
public int RsiLength
{
get => _rsiLength.Value;
set => _rsiLength.Value = value;
}
/// <summary>
/// EMA A period.
/// </summary>
public int EmaALength
{
get => _emaALength.Value;
set => _emaALength.Value = value;
}
/// <summary>
/// EMA B period.
/// </summary>
public int EmaBLength
{
get => _emaBLength.Value;
set => _emaBLength.Value = value;
}
/// <summary>
/// EMA C period.
/// </summary>
public int EmaCLength
{
get => _emaCLength.Value;
set => _emaCLength.Value = value;
}
/// <summary>
/// MACD fast EMA period.
/// </summary>
public int MacdFastLength
{
get => _macdFastLength.Value;
set => _macdFastLength.Value = value;
}
/// <summary>
/// MACD slow EMA period.
/// </summary>
public int MacdSlowLength
{
get => _macdSlowLength.Value;
set => _macdSlowLength.Value = value;
}
/// <summary>
/// MACD signal line period.
/// </summary>
public int MacdSignalLength
{
get => _macdSignalLength.Value;
set => _macdSignalLength.Value = value;
}
/// <summary>
/// Take-profit percentage. 0 disables it.
/// </summary>
public decimal TakeProfitPercent
{
get => _takeProfitPercent.Value;
set => _takeProfitPercent.Value = value;
}
/// <summary>
/// Stop-loss percentage. 0 disables it.
/// </summary>
public decimal StopLossPercent
{
get => _stopLossPercent.Value;
set => _stopLossPercent.Value = value;
}
/// <summary>
/// Candle type for strategy calculation.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public OmarMmrStrategy()
{
_rsiLength = Param(nameof(RsiLength), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Length", "RSI period", "RSI");
_emaALength = Param(nameof(EmaALength), 20)
.SetGreaterThanZero()
.SetDisplay("EMA A Length", "Fast EMA period", "Moving Averages");
_emaBLength = Param(nameof(EmaBLength), 50)
.SetGreaterThanZero()
.SetDisplay("EMA B Length", "Medium EMA period", "Moving Averages");
_emaCLength = Param(nameof(EmaCLength), 200)
.SetGreaterThanZero()
.SetDisplay("EMA C Length", "Slow EMA period", "Moving Averages");
_macdFastLength = Param(nameof(MacdFastLength), 12)
.SetGreaterThanZero()
.SetDisplay("MACD Fast", "MACD fast EMA period", "MACD");
_macdSlowLength = Param(nameof(MacdSlowLength), 26)
.SetGreaterThanZero()
.SetDisplay("MACD Slow", "MACD slow EMA period", "MACD");
_macdSignalLength = Param(nameof(MacdSignalLength), 9)
.SetGreaterThanZero()
.SetDisplay("MACD Signal", "MACD signal line period", "MACD");
_takeProfitPercent = Param(nameof(TakeProfitPercent), 1.5m)
.SetNotNegative()
.SetDisplay("Take Profit %", "Take-profit percentage", "Risk");
_stopLossPercent = Param(nameof(StopLossPercent), 2.0m)
.SetNotNegative()
.SetDisplay("Stop Loss %", "Stop-loss percentage", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle type", "Candle type for strategy calculation", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevMacd = null;
_prevSignal = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevMacd = null;
_prevSignal = null;
var rsi = new RelativeStrengthIndex { Length = RsiLength };
var emaA = new ExponentialMovingAverage { Length = EmaALength };
var emaB = new ExponentialMovingAverage { Length = EmaBLength };
var emaC = new ExponentialMovingAverage { Length = EmaCLength };
var macd = new MovingAverageConvergenceDivergenceSignal
{
Macd =
{
ShortMa = { Length = MacdFastLength },
LongMa = { Length = MacdSlowLength },
},
SignalMa = { Length = MacdSignalLength },
};
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(rsi, emaA, emaB, emaC, macd, ProcessCandle)
.Start();
StartProtection(
TakeProfitPercent > 0 ? new Unit(TakeProfitPercent, UnitTypes.Percent) : new Unit(),
StopLossPercent > 0 ? new Unit(StopLossPercent, UnitTypes.Percent) : new Unit(),
useMarketOrders: true);
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, emaA);
DrawIndicator(area, emaB);
DrawIndicator(area, emaC);
DrawOwnTrades(area);
var oscillators = CreateChartArea();
if (oscillators != null)
{
DrawIndicator(oscillators, macd);
DrawIndicator(oscillators, rsi);
}
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue rsiValue, IIndicatorValue emaAValue, IIndicatorValue emaBValue, IIndicatorValue emaCValue, IIndicatorValue macdValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!macdValue.IsFormed)
return;
var macdTyped = (MovingAverageConvergenceDivergenceSignalValue)macdValue;
if (macdTyped.Macd is not decimal macd || macdTyped.Signal is not decimal signal)
return;
var prevMacd = _prevMacd;
var prevSignal = _prevSignal;
_prevMacd = macd;
_prevSignal = signal;
if (!rsiValue.IsFormed || !emaAValue.IsFormed || !emaBValue.IsFormed || !emaCValue.IsFormed)
return;
if (prevMacd is not decimal lastMacd || prevSignal is not decimal lastSignal)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var rsi = rsiValue.ToDecimal();
var emaA = emaAValue.ToDecimal();
var emaB = emaBValue.ToDecimal();
var emaC = emaCValue.ToDecimal();
var macdCrossUp = lastMacd <= lastSignal && macd > signal;
if (Position == 0
&& candle.ClosePrice > emaC
&& emaA > emaB
&& macdCrossUp
&& rsi > _rsiLower && rsi < _rsiUpper)
{
BuyMarket(Volume);
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes
from StockSharp.Algo.Indicators import RelativeStrengthIndex, ExponentialMovingAverage, MovingAverageConvergenceDivergenceSignal
from StockSharp.Algo.Strategies import Strategy
RSI_LOWER = 29.0
RSI_UPPER = 70.0
class omar_mmr_strategy(Strategy):
"""
Omar MMR strategy (long only).
Buys when the close is above EMA C, EMA A is above EMA B, the MACD line crosses above its signal line and RSI is
between 29 and 70. Positions are closed only by the percent take-profit and stop-loss.
"""
def __init__(self):
super(omar_mmr_strategy, self).__init__()
self._rsi_length = self.Param("RsiLength", 14) \
.SetGreaterThanZero() \
.SetDisplay("RSI Length", "RSI period", "RSI")
self._ema_a_length = self.Param("EmaALength", 20) \
.SetGreaterThanZero() \
.SetDisplay("EMA A Length", "Fast EMA period", "Moving Averages")
self._ema_b_length = self.Param("EmaBLength", 50) \
.SetGreaterThanZero() \
.SetDisplay("EMA B Length", "Medium EMA period", "Moving Averages")
self._ema_c_length = self.Param("EmaCLength", 200) \
.SetGreaterThanZero() \
.SetDisplay("EMA C Length", "Slow EMA period", "Moving Averages")
self._macd_fast_length = self.Param("MacdFastLength", 12) \
.SetGreaterThanZero() \
.SetDisplay("MACD Fast", "MACD fast EMA period", "MACD")
self._macd_slow_length = self.Param("MacdSlowLength", 26) \
.SetGreaterThanZero() \
.SetDisplay("MACD Slow", "MACD slow EMA period", "MACD")
self._macd_signal_length = self.Param("MacdSignalLength", 9) \
.SetGreaterThanZero() \
.SetDisplay("MACD Signal", "MACD signal line period", "MACD")
self._take_profit_percent = self.Param("TakeProfitPercent", 1.5) \
.SetNotNegative() \
.SetDisplay("Take Profit %", "Take-profit percentage", "Risk")
self._stop_loss_percent = self.Param("StopLossPercent", 2.0) \
.SetNotNegative() \
.SetDisplay("Stop Loss %", "Stop-loss percentage", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))) \
.SetDisplay("Candle type", "Candle type for strategy calculation", "General")
self._prev_macd = None
self._prev_signal = None
@property
def CandleType(self):
return self._candle_type.Value
def GetWorkingSecurities(self):
return [(self.Security, self.CandleType)]
def OnReseted(self):
super(omar_mmr_strategy, self).OnReseted()
self._prev_macd = None
self._prev_signal = None
def OnStarted2(self, time):
super(omar_mmr_strategy, self).OnStarted2(time)
self._prev_macd = None
self._prev_signal = None
rsi = RelativeStrengthIndex()
rsi.Length = self._rsi_length.Value
ema_a = ExponentialMovingAverage()
ema_a.Length = self._ema_a_length.Value
ema_b = ExponentialMovingAverage()
ema_b.Length = self._ema_b_length.Value
ema_c = ExponentialMovingAverage()
ema_c.Length = self._ema_c_length.Value
macd = MovingAverageConvergenceDivergenceSignal()
macd.Macd.ShortMa.Length = self._macd_fast_length.Value
macd.Macd.LongMa.Length = self._macd_slow_length.Value
macd.SignalMa.Length = self._macd_signal_length.Value
subscription = self.SubscribeCandles(self.CandleType)
subscription.BindEx(rsi, ema_a, ema_b, ema_c, macd, self._process_candle).Start()
tp = float(self._take_profit_percent.Value)
sl = float(self._stop_loss_percent.Value)
self.StartProtection(
Unit(Decimal(tp), UnitTypes.Percent) if tp > 0 else Unit(),
Unit(Decimal(sl), UnitTypes.Percent) if sl > 0 else Unit(),
useMarketOrders=True)
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ema_a)
self.DrawIndicator(area, ema_b)
self.DrawIndicator(area, ema_c)
self.DrawOwnTrades(area)
oscillators = self.CreateChartArea()
if oscillators is not None:
self.DrawIndicator(oscillators, macd)
self.DrawIndicator(oscillators, rsi)
def _process_candle(self, candle, rsi_value, ema_a_value, ema_b_value, ema_c_value, macd_value):
if candle.State != CandleStates.Finished:
return
if not macd_value.IsFormed:
return
if macd_value.Macd is None or macd_value.Signal is None:
return
macd = float(macd_value.Macd)
signal = float(macd_value.Signal)
prev_macd = self._prev_macd
prev_signal = self._prev_signal
self._prev_macd = macd
self._prev_signal = signal
if not rsi_value.IsFormed or not ema_a_value.IsFormed or not ema_b_value.IsFormed or not ema_c_value.IsFormed:
return
if prev_macd is None or prev_signal is None:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
rsi = float(rsi_value.GetValue[Decimal](None))
ema_a = float(ema_a_value.GetValue[Decimal](None))
ema_b = float(ema_b_value.GetValue[Decimal](None))
ema_c = float(ema_c_value.GetValue[Decimal](None))
macd_cross_up = prev_macd <= prev_signal and macd > signal
if self.Position == 0 \
and float(candle.ClosePrice) > ema_c \
and ema_a > ema_b \
and macd_cross_up \
and RSI_LOWER < rsi < RSI_UPPER:
self.BuyMarket(self.Volume)
def CreateClone(self):
return omar_mmr_strategy()