Estratégia Multi-Fator
A Estratégia Multi-Fator combina MACD, RSI e duas médias móveis para operar com confirmação de tendência. Operações compradas ocorrem quando a linha MACD está acima do seu sinal, o RSI está abaixo de 70, o preço está acima da SMA de 50 períodos e a SMA de 50 está acima da SMA de 200. Operações vendidas usam condições opostas.
Stops e alvos são baseados em múltiplos do ATR.
Detalhes
- Critérios de entrada:
- Comprado:
MACD > Signal&&RSI < 70&&Close > SMA50&&SMA50 > SMA200. - Vendido:
MACD < Signal&&RSI > 30&&Close < SMA50&&SMA50 < SMA200.
- Comprado:
- Comprado/Vendido: Ambas as direções.
- Critérios de saída: Stop loss e take profit baseados em ATR.
- Stops: Sim.
- Valores padrão:
FastLength= 12SlowLength= 26SignalLength= 9RsiLength= 14AtrLength= 14StopAtrMultiplier= 2ProfitAtrMultiplier= 3CandleType= TimeSpan.FromMinutes(15)
- Filtros:
- Categoria: Seguidor de tendência
- Direção: Ambos
- Indicadores: MACD, RSI, SMA, ATR
- Stops: Sim
- Complexidade: Básico
- Período: Médio prazo
- Sazonalidade: Não
- Redes neurais: Não
- Divergência: Não
- Nível de risco: Médio
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>
/// Multi-factor strategy.
/// Goes long when MACD is above its signal, RSI is below 70, the close is above the 50-period SMA and the 50 SMA is above the 200 SMA;
/// goes short on the mirrored conditions (RSI above 30), reversing an opposite position. Each position is closed by a stop loss and a
/// take profit placed StopAtrMultiplier and ProfitAtrMultiplier ATRs away from the entry price.
/// </summary>
public class MultiFactorStrategy : Strategy
{
private const int _trendFastLength = 50;
private const int _trendSlowLength = 200;
private const decimal _rsiUpper = 70m;
private const decimal _rsiLower = 30m;
private readonly StrategyParam<int> _fastLength;
private readonly StrategyParam<int> _slowLength;
private readonly StrategyParam<int> _signalLength;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<int> _atrLength;
private readonly StrategyParam<decimal> _stopAtrMultiplier;
private readonly StrategyParam<decimal> _profitAtrMultiplier;
private readonly StrategyParam<DataType> _candleType;
private decimal? _stopPrice;
private decimal? _takePrice;
/// <summary>
/// MACD fast EMA length.
/// </summary>
public int FastLength
{
get => _fastLength.Value;
set => _fastLength.Value = value;
}
/// <summary>
/// MACD slow EMA length.
/// </summary>
public int SlowLength
{
get => _slowLength.Value;
set => _slowLength.Value = value;
}
/// <summary>
/// MACD signal EMA length.
/// </summary>
public int SignalLength
{
get => _signalLength.Value;
set => _signalLength.Value = value;
}
/// <summary>
/// RSI period.
/// </summary>
public int RsiLength
{
get => _rsiLength.Value;
set => _rsiLength.Value = value;
}
/// <summary>
/// ATR period.
/// </summary>
public int AtrLength
{
get => _atrLength.Value;
set => _atrLength.Value = value;
}
/// <summary>
/// ATR multiple for the stop loss distance.
/// </summary>
public decimal StopAtrMultiplier
{
get => _stopAtrMultiplier.Value;
set => _stopAtrMultiplier.Value = value;
}
/// <summary>
/// ATR multiple for the take profit distance.
/// </summary>
public decimal ProfitAtrMultiplier
{
get => _profitAtrMultiplier.Value;
set => _profitAtrMultiplier.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public MultiFactorStrategy()
{
_fastLength = Param(nameof(FastLength), 12)
.SetGreaterThanZero()
.SetDisplay("MACD Fast", "MACD fast EMA length", "MACD");
_slowLength = Param(nameof(SlowLength), 26)
.SetGreaterThanZero()
.SetDisplay("MACD Slow", "MACD slow EMA length", "MACD");
_signalLength = Param(nameof(SignalLength), 9)
.SetGreaterThanZero()
.SetDisplay("MACD Signal", "MACD signal EMA length", "MACD");
_rsiLength = Param(nameof(RsiLength), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Length", "RSI period", "RSI");
_atrLength = Param(nameof(AtrLength), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Length", "ATR period", "Risk");
_stopAtrMultiplier = Param(nameof(StopAtrMultiplier), 2m)
.SetNotNegative()
.SetDisplay("Stop ATR Mult", "ATR multiple for the stop loss", "Risk");
_profitAtrMultiplier = Param(nameof(ProfitAtrMultiplier), 3m)
.SetNotNegative()
.SetDisplay("Profit ATR Mult", "ATR multiple for the take profit", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_stopPrice = null;
_takePrice = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_stopPrice = null;
_takePrice = null;
var macd = new MovingAverageConvergenceDivergenceSignal
{
Macd =
{
ShortMa = { Length = FastLength },
LongMa = { Length = SlowLength },
},
SignalMa = { Length = SignalLength }
};
var rsi = new RelativeStrengthIndex { Length = RsiLength };
var atr = new AverageTrueRange { Length = AtrLength };
var sma50 = new SimpleMovingAverage { Length = _trendFastLength };
var sma200 = new SimpleMovingAverage { Length = _trendSlowLength };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(macd, rsi, atr, sma50, sma200, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, sma50);
DrawIndicator(area, sma200);
DrawOwnTrades(area);
var oscillators = CreateChartArea();
if (oscillators != null)
{
DrawIndicator(oscillators, macd);
DrawIndicator(oscillators, rsi);
}
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue macdValue, IIndicatorValue rsiValue, IIndicatorValue atrValue, IIndicatorValue sma50Value, IIndicatorValue sma200Value)
{
if (candle.State != CandleStates.Finished)
return;
// The ATR levels are checked against the candle range, so exits happen before new signals.
if (Position > 0 && _stopPrice is decimal longStop && _takePrice is decimal longTake)
{
if (candle.LowPrice <= longStop || candle.HighPrice >= longTake)
{
SellMarket(Position);
_stopPrice = null;
_takePrice = null;
return;
}
}
else if (Position < 0 && _stopPrice is decimal shortStop && _takePrice is decimal shortTake)
{
if (candle.HighPrice >= shortStop || candle.LowPrice <= shortTake)
{
BuyMarket(-Position);
_stopPrice = null;
_takePrice = null;
return;
}
}
if (!macdValue.IsFormed || !rsiValue.IsFormed || !atrValue.IsFormed || !sma50Value.IsFormed || !sma200Value.IsFormed)
return;
if (macdValue is not IMovingAverageConvergenceDivergenceSignalValue { Macd: decimal macdLine, Signal: decimal signalLine })
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var rsi = rsiValue.GetValue<decimal>();
var atr = atrValue.GetValue<decimal>();
var sma50 = sma50Value.GetValue<decimal>();
var sma200 = sma200Value.GetValue<decimal>();
var close = candle.ClosePrice;
var longSignal = macdLine > signalLine && rsi < _rsiUpper && close > sma50 && sma50 > sma200;
var shortSignal = macdLine < signalLine && rsi > _rsiLower && close < sma50 && sma50 < sma200;
if (longSignal && Position <= 0)
{
BuyMarket(Volume + Math.Abs(Position));
SetLevels(close, atr, true);
}
else if (shortSignal && Position >= 0)
{
SellMarket(Volume + Math.Abs(Position));
SetLevels(close, atr, false);
}
}
private void SetLevels(decimal entry, decimal atr, bool isLong)
{
var stop = atr * StopAtrMultiplier;
var take = atr * ProfitAtrMultiplier;
// A zero multiplier disables that level.
_stopPrice = StopAtrMultiplier > 0 ? (isLong ? entry - stop : entry + stop) : (isLong ? decimal.MinValue : decimal.MaxValue);
_takePrice = ProfitAtrMultiplier > 0 ? (isLong ? entry + take : entry - take) : (isLong ? decimal.MaxValue : decimal.MinValue);
}
}
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
from StockSharp.Algo.Indicators import MovingAverageConvergenceDivergenceSignal, RelativeStrengthIndex, AverageTrueRange, SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
TREND_FAST_LENGTH = 50
TREND_SLOW_LENGTH = 200
RSI_UPPER = 70
RSI_LOWER = 30
class multi_factor_strategy(Strategy):
"""
Multi-factor strategy.
Goes long when MACD is above its signal, RSI is below 70, the close is above the 50-period SMA and the 50 SMA is above the 200 SMA;
goes short on the mirrored conditions (RSI above 30), reversing an opposite position. Each position is closed by a stop loss and a
take profit placed StopAtrMultiplier and ProfitAtrMultiplier ATRs away from the entry price.
"""
def __init__(self):
super(multi_factor_strategy, self).__init__()
self._fast_length = self.Param("FastLength", 12).SetGreaterThanZero().SetDisplay("MACD Fast", "MACD fast EMA length", "MACD")
self._slow_length = self.Param("SlowLength", 26).SetGreaterThanZero().SetDisplay("MACD Slow", "MACD slow EMA length", "MACD")
self._signal_length = self.Param("SignalLength", 9).SetGreaterThanZero().SetDisplay("MACD Signal", "MACD signal EMA length", "MACD")
self._rsi_length = self.Param("RsiLength", 14).SetGreaterThanZero().SetDisplay("RSI Length", "RSI period", "RSI")
self._atr_length = self.Param("AtrLength", 14).SetGreaterThanZero().SetDisplay("ATR Length", "ATR period", "Risk")
self._stop_atr_multiplier = self.Param("StopAtrMultiplier", 2.0).SetNotNegative().SetDisplay("Stop ATR Mult", "ATR multiple for the stop loss", "Risk")
self._profit_atr_multiplier = self.Param("ProfitAtrMultiplier", 3.0).SetNotNegative().SetDisplay("Profit ATR Mult", "ATR multiple for the take profit", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._reset_state()
@property
def candle_type(self):
return self._candle_type.Value
def _reset_state(self):
self._stop_price = None
self._take_price = None
def OnReseted(self):
super(multi_factor_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(multi_factor_strategy, self).OnStarted2(time)
self._reset_state()
macd = MovingAverageConvergenceDivergenceSignal()
macd.Macd.ShortMa.Length = self._fast_length.Value
macd.Macd.LongMa.Length = self._slow_length.Value
macd.SignalMa.Length = self._signal_length.Value
rsi = RelativeStrengthIndex()
rsi.Length = self._rsi_length.Value
atr = AverageTrueRange()
atr.Length = self._atr_length.Value
sma50 = SimpleMovingAverage()
sma50.Length = TREND_FAST_LENGTH
sma200 = SimpleMovingAverage()
sma200.Length = TREND_SLOW_LENGTH
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(macd, rsi, atr, sma50, sma200, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, sma50)
self.DrawIndicator(area, sma200)
self.DrawOwnTrades(area)
oscillators = self.CreateChartArea()
if oscillators is not None:
self.DrawIndicator(oscillators, macd)
self.DrawIndicator(oscillators, rsi)
def _process_candle(self, candle, macd_value, rsi_value, atr_value, sma50_value, sma200_value):
if candle.State != CandleStates.Finished:
return
# The ATR levels are checked against the candle range, so exits happen before new signals.
if self.Position > 0 and self._stop_price is not None and self._take_price is not None:
if candle.LowPrice <= self._stop_price or candle.HighPrice >= self._take_price:
self.SellMarket(self.Position)
self._reset_state()
return
elif self.Position < 0 and self._stop_price is not None and self._take_price is not None:
if candle.HighPrice >= self._stop_price or candle.LowPrice <= self._take_price:
self.BuyMarket(-self.Position)
self._reset_state()
return
if not macd_value.IsFormed or not rsi_value.IsFormed or not atr_value.IsFormed or not sma50_value.IsFormed or not sma200_value.IsFormed:
return
if macd_value.Macd is None or macd_value.Signal is None:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
macd_line = macd_value.Macd
signal_line = macd_value.Signal
rsi = rsi_value.GetValue[Decimal](None)
atr = atr_value.GetValue[Decimal](None)
sma50 = sma50_value.GetValue[Decimal](None)
sma200 = sma200_value.GetValue[Decimal](None)
close = candle.ClosePrice
long_signal = macd_line > signal_line and rsi < Decimal(RSI_UPPER) and close > sma50 and sma50 > sma200
short_signal = macd_line < signal_line and rsi > Decimal(RSI_LOWER) and close < sma50 and sma50 < sma200
if long_signal and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
self._set_levels(close, atr, True)
elif short_signal and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
self._set_levels(close, atr, False)
def _set_levels(self, entry, atr, is_long):
stop_mult = Decimal(self._stop_atr_multiplier.Value)
take_mult = Decimal(self._profit_atr_multiplier.Value)
stop = atr * stop_mult
take = atr * take_mult
# A zero multiplier disables that level.
if stop_mult > Decimal(0):
self._stop_price = entry - stop if is_long else entry + stop
else:
self._stop_price = Decimal.MinValue if is_long else Decimal.MaxValue
if take_mult > Decimal(0):
self._take_price = entry + take if is_long else entry - take
else:
self._take_price = Decimal.MaxValue if is_long else Decimal.MinValue
def CreateClone(self):
return multi_factor_strategy()