Multi-Factor Strategy
Multi-Factor Strategy combines MACD, RSI, and two moving averages to trade with trend confirmation. Long trades occur when MACD line is above its signal, RSI is below 70, price above the 50-period SMA, and the 50 SMA is above the 200 SMA. Short trades use opposite conditions.
Stops and targets are based on ATR multiples.
Details
- Entry Criteria:
- Long:
MACD > Signal&&RSI < 70&&Close > SMA50&&SMA50 > SMA200. - Short:
MACD < Signal&&RSI > 30&&Close < SMA50&&SMA50 < SMA200.
- Long:
- Long/Short: Both directions.
- Exit Criteria: ATR-based stop loss and take profit.
- Stops: Yes.
- Default Values:
FastLength= 12SlowLength= 26SignalLength= 9RsiLength= 14AtrLength= 14StopAtrMultiplier= 2ProfitAtrMultiplier= 3CandleType= TimeSpan.FromMinutes(15)
- Filters:
- Category: Trend following
- Direction: Both
- Indicators: MACD, RSI, SMA, ATR
- Stops: Yes
- Complexity: Basic
- Timeframe: Medium-term
- Seasonality: No
- Neural networks: No
- Divergence: No
- Risk Level: Medium
namespace StockSharp.Samples.Strategies;
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
/// <summary>
/// Multi-factor strategy combining MACD, RSI, ATR, and trend filters.
/// Opens long positions only on bullish MACD crossovers confirmed by RSI and long-term trend.
/// </summary>
public class MultiFactorStrategy : Strategy
{
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<int> _signalCooldownBars;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevDiff;
private bool _hasPrevDiff;
private int _cooldownRemaining;
private MovingAverageConvergenceDivergenceSignal _macd;
private RelativeStrengthIndex _rsi;
private AverageTrueRange _atr;
private SMA _sma50;
private SMA _sma200;
public int FastLength { get => _fastLength.Value; set => _fastLength.Value = value; }
public int SlowLength { get => _slowLength.Value; set => _slowLength.Value = value; }
public int SignalLength { get => _signalLength.Value; set => _signalLength.Value = value; }
public int RsiLength { get => _rsiLength.Value; set => _rsiLength.Value = value; }
public int AtrLength { get => _atrLength.Value; set => _atrLength.Value = value; }
public decimal StopAtrMultiplier { get => _stopAtrMultiplier.Value; set => _stopAtrMultiplier.Value = value; }
public decimal ProfitAtrMultiplier { get => _profitAtrMultiplier.Value; set => _profitAtrMultiplier.Value = value; }
public int SignalCooldownBars { get => _signalCooldownBars.Value; set => _signalCooldownBars.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
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", "ATR");
_stopAtrMultiplier = Param(nameof(StopAtrMultiplier), 2m)
.SetGreaterThanZero()
.SetDisplay("Stop ATR Mult", "ATR multiplier for stop", "Risk");
_profitAtrMultiplier = Param(nameof(ProfitAtrMultiplier), 3m)
.SetGreaterThanZero()
.SetDisplay("Profit ATR Mult", "ATR multiplier for take profit", "Risk");
_signalCooldownBars = Param(nameof(SignalCooldownBars), 50)
.SetGreaterThanZero()
.SetDisplay("Signal Cooldown", "Bars to wait after entries and exits", "Trading");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevDiff = 0m;
_hasPrevDiff = false;
_cooldownRemaining = 0;
_macd = null;
_rsi = null;
_atr = null;
_sma50 = null;
_sma200 = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_macd = new()
{
Macd =
{
ShortMa = { Length = FastLength },
LongMa = { Length = SlowLength },
},
SignalMa = { Length = SignalLength }
};
_rsi = new() { Length = RsiLength };
_atr = new() { Length = AtrLength };
_sma50 = new() { Length = 50 };
_sma200 = new() { Length = 200 };
_prevDiff = 0m;
_hasPrevDiff = false;
_cooldownRemaining = 0;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
StartProtection(
takeProfit: new Unit(3, UnitTypes.Percent),
stopLoss: new Unit(2, UnitTypes.Percent));
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var macdValue = _macd.Process(candle);
var rsiValue = _rsi.Process(candle);
var atrValue = _atr.Process(candle);
var sma50Value = _sma50.Process(new DecimalIndicatorValue(_sma50, candle.ClosePrice, candle.ServerTime) { IsFinal = true });
var sma200Value = _sma200.Process(new DecimalIndicatorValue(_sma200, candle.ClosePrice, candle.ServerTime) { IsFinal = true });
if (!_macd.IsFormed || !_rsi.IsFormed || !_atr.IsFormed || !_sma50.IsFormed || !_sma200.IsFormed)
return;
if (_cooldownRemaining > 0)
_cooldownRemaining--;
var macdData = (MovingAverageConvergenceDivergenceSignalValue)macdValue;
if (macdData.Macd is not decimal macdLine || macdData.Signal is not decimal signalLine)
return;
var diff = macdLine - signalLine;
var rsi = rsiValue.ToDecimal();
var atr = atrValue.ToDecimal();
var sma50 = sma50Value.ToDecimal();
var sma200 = sma200Value.ToDecimal();
if (!_hasPrevDiff)
{
_prevDiff = diff;
_hasPrevDiff = true;
return;
}
if (_cooldownRemaining == 0 && Position == 0)
{
var bullishCross = _prevDiff <= 0m && diff > 0m;
var bearishCross = _prevDiff >= 0m && diff < 0m;
if (bullishCross && candle.ClosePrice > sma50)
{
BuyMarket();
_cooldownRemaining = SignalCooldownBars;
}
else if (bearishCross && candle.ClosePrice < sma50)
{
SellMarket();
_cooldownRemaining = SignalCooldownBars;
}
}
_prevDiff = diff;
}
}
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 MovingAverageConvergenceDivergenceSignal, RelativeStrengthIndex, AverageTrueRange, SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class multi_factor_strategy(Strategy):
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._signal_cooldown_bars = self.Param("SignalCooldownBars", 50) \
.SetGreaterThanZero() \
.SetDisplay("Signal Cooldown", "Bars to wait after entries and exits", "Trading")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._prev_diff = 0.0
self._has_prev_diff = False
self._cooldown_remaining = 0
@property
def candle_type(self):
return self._candle_type.Value
@candle_type.setter
def candle_type(self, value):
self._candle_type.Value = value
def OnReseted(self):
super(multi_factor_strategy, self).OnReseted()
self._prev_diff = 0.0
self._has_prev_diff = False
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(multi_factor_strategy, self).OnStarted2(time)
self._prev_diff = 0.0
self._has_prev_diff = False
self._cooldown_remaining = 0
self._macd = MovingAverageConvergenceDivergenceSignal()
self._macd.Macd.ShortMa.Length = self._fast_length.Value
self._macd.Macd.LongMa.Length = self._slow_length.Value
self._macd.SignalMa.Length = self._signal_length.Value
self._sma50 = SimpleMovingAverage()
self._sma50.Length = 50
self._dummy = SimpleMovingAverage()
self._dummy.Length = 2
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._dummy, self.OnProcess).Start()
def OnProcess(self, candle, dummy_value):
if candle.State != CandleStates.Finished:
return
macd_result = process_float(self._macd, candle.ClosePrice, candle.ServerTime, True)
sma50_result = process_float(self._sma50, candle.ClosePrice, candle.ServerTime, True)
if not self._macd.IsFormed or not self._sma50.IsFormed:
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
macd_line = macd_result.Macd
signal_line = macd_result.Signal
if macd_line is None or signal_line is None:
return
diff = float(macd_line) - float(signal_line)
sma50 = float(sma50_result)
close = float(candle.ClosePrice)
if not self._has_prev_diff:
self._prev_diff = diff
self._has_prev_diff = True
return
if self._cooldown_remaining == 0 and self.Position == 0:
bullish_cross = self._prev_diff <= 0.0 and diff > 0.0
bearish_cross = self._prev_diff >= 0.0 and diff < 0.0
if bullish_cross and close > sma50:
self.BuyMarket()
self._cooldown_remaining = self._signal_cooldown_bars.Value
elif bearish_cross and close < sma50:
self.SellMarket()
self._cooldown_remaining = self._signal_cooldown_bars.Value
self._prev_diff = diff
def CreateClone(self):
return multi_factor_strategy()