Estratégia Omar MMR
Método baseado em momentum que combina RSI, três médias móveis exponenciais e um cruzamento de MACD. Operações compradas ocorrem quando o preço está acima da EMA lenta, a EMA rápida supera a EMA média, MACD cruza de forma altista e RSI está em uma zona neutra entre 29 e 70.
Os percentuais de take-profit e stop-loss são aplicados através do módulo de proteção do motor. A configuração foca em alinhar momentum e tendência enquanto evita leituras de RSI sobreextendidas.
Detalhes
- Critérios de entrada:
- Comprado: Fechamento acima de EMA C, EMA A > EMA B, a linha MACD cruza acima do sinal, RSI entre 29 e 70.
- Critérios de saída:
- Gerenciado via take-profit ou stop-loss; sem saída explícita por indicador.
- Indicadores:
- RSI (comprimento 14)
- EMA A/B/C (períodos 20/50/200)
- MACD (12,26,9)
- Stops: Take-profit baseado em percentual 1.5% e stop-loss 2% por padrão.
- Valores padrão:
RsiLength= 14EmaALength= 20EmaBLength= 50EmaCLength= 200MacdFastLength= 12MacdSlowLength= 26MacdSignalLength= 9TakeProfitPercent= 1.5StopLossPercent= 2.0
- Filtros:
- Continuação de tendência
- Período único
- Indicadores: RSI, EMA, MACD
- Stops: Sim
- Complexidade: Moderado
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()