Стратегия MACD + Bollinger Bands + RSI
Эта комбинированная схема ищет откаты против текущего импульса MACD, выходящие за пределы полос Боллинджера. Когда MACD положителен, но цена закрывается ниже нижней полосы вместе с перепроданным RSI, стратегия покупает в ожидании продолжения тренда. Для коротких позиций применяются зеркальные условия.
Детали
- Критерии входа:
- Длинная позиция:
MACD > 0иClose < LowerBandиRSI < 30 - Короткая позиция:
MACD < 0иClose > UpperBandиRSI > 70
- Длинная позиция:
- Длинные/короткие: обе стороны
- Критерии выхода: Противоположный сигнал
- Стопы: нет
- Значения по умолчанию:
MacdFastLength= 12MacdSlowLength= 26MacdSignalLength= 9BBLength= 20BBMultiplier= 2.0RSILength= 14
- Фильтры:
- Категория: Следование тренду
- Направление: Оба
- Индикаторы: MACD, полосы Боллинджера, RSI
- Стопы: Нет
- Сложность: Средняя
- Таймфрейм: Краткосрочный
- Сезонность: Нет
- Нейросети: Нет
- Дивергенция: Да
- Уровень риска: Средний
namespace StockSharp.Samples.Strategies;
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
/// <summary>
/// MACD + Bollinger Bands + RSI Strategy.
/// Uses MACD for momentum, BB for volatility levels, RSI for confirmation.
/// Buys when MACD bullish + price near lower BB + RSI oversold.
/// Sells when MACD bearish + price near upper BB + RSI overbought.
/// </summary>
public class MacdBbRsiStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleTypeParam;
private readonly StrategyParam<int> _bbLength;
private readonly StrategyParam<decimal> _bbWidth;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<int> _cooldownBars;
private MovingAverageConvergenceDivergence _macd;
private BollingerBands _bollinger;
private RelativeStrengthIndex _rsi;
private decimal _prevMacd;
private int _cooldownRemaining;
public MacdBbRsiStrategy()
{
_candleTypeParam = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle type", "Candle type for strategy calculation.", "General");
_bbLength = Param(nameof(BBLength), 20)
.SetGreaterThanZero()
.SetDisplay("BB Length", "Bollinger Bands period", "Bollinger Bands");
_bbWidth = Param(nameof(BBWidth), 1.5m)
.SetDisplay("BB Width", "BB standard deviation multiplier", "Bollinger Bands");
_rsiLength = Param(nameof(RSILength), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Length", "RSI period", "RSI");
_cooldownBars = Param(nameof(CooldownBars), 50)
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "Risk");
}
public DataType CandleType
{
get => _candleTypeParam.Value;
set => _candleTypeParam.Value = value;
}
public int BBLength
{
get => _bbLength.Value;
set => _bbLength.Value = value;
}
public decimal BBWidth
{
get => _bbWidth.Value;
set => _bbWidth.Value = value;
}
public int RSILength
{
get => _rsiLength.Value;
set => _rsiLength.Value = value;
}
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_macd = null;
_bollinger = null;
_rsi = null;
_prevMacd = 0;
_cooldownRemaining = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_macd = new MovingAverageConvergenceDivergence();
_bollinger = new BollingerBands { Length = BBLength, Width = BBWidth };
_rsi = new RelativeStrengthIndex { Length = RSILength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_rsi, OnProcess)
.Start();
StartProtection(
takeProfit: new Unit(2, UnitTypes.Percent),
stopLoss: new Unit(1, UnitTypes.Percent)
);
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _bollinger);
DrawOwnTrades(area);
}
}
private void OnProcess(ICandleMessage candle, decimal rsi)
{
if (candle.State != CandleStates.Finished)
return;
// Process MACD and BB manually
var macdResult = _macd.Process(candle);
var bbResult = _bollinger.Process(candle);
if (!_macd.IsFormed || !_bollinger.IsFormed)
return;
var macdVal = macdResult.ToDecimal();
var bb = (BollingerBandsValue)bbResult;
if (bb.UpBand is not decimal upper ||
bb.LowBand is not decimal lower ||
bb.MovingAverage is not decimal middle)
return;
if (_cooldownRemaining > 0)
{
_cooldownRemaining--;
_prevMacd = macdVal;
return;
}
var close = candle.ClosePrice;
// Buy: price below lower BB + RSI oversold + MACD positive
if (close <= lower && rsi < 30 && Position == 0)
{
BuyMarket();
_cooldownRemaining = CooldownBars;
}
// Sell: price above upper BB + RSI overbought + MACD negative
else if (close >= upper && rsi > 70 && Position == 0)
{
SellMarket();
_cooldownRemaining = CooldownBars;
}
_prevMacd = macdVal;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes
from StockSharp.Algo.Indicators import MovingAverageConvergenceDivergence, BollingerBands, RelativeStrengthIndex, IndicatorHelper, CandleIndicatorValue
from StockSharp.Algo.Strategies import Strategy
class macd_bb_rsi_strategy(Strategy):
"""MACD + Bollinger Bands + RSI Strategy."""
def __init__(self):
super(macd_bb_rsi_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle type", "Candle type for strategy calculation.", "General")
self._bb_length = self.Param("BBLength", 20) \
.SetDisplay("BB Length", "Bollinger Bands period", "Bollinger Bands")
self._bb_width = self.Param("BBWidth", 1.5) \
.SetDisplay("BB Width", "BB standard deviation multiplier", "Bollinger Bands")
self._rsi_length = self.Param("RSILength", 14) \
.SetDisplay("RSI Length", "RSI period", "RSI")
self._cooldown_bars = self.Param("CooldownBars", 50) \
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "Risk")
self._macd = None
self._bollinger = None
self._rsi = None
self._prev_macd = 0.0
self._cooldown_remaining = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(macd_bb_rsi_strategy, self).OnReseted()
self._macd = None
self._bollinger = None
self._rsi = None
self._prev_macd = 0.0
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(macd_bb_rsi_strategy, self).OnStarted2(time)
self._macd = MovingAverageConvergenceDivergence()
self._bollinger = BollingerBands()
self._bollinger.Length = int(self._bb_length.Value)
self._bollinger.Width = float(self._bb_width.Value)
self._rsi = RelativeStrengthIndex()
self._rsi.Length = int(self._rsi_length.Value)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._rsi, self._on_process).Start()
self.StartProtection(
Unit(2, UnitTypes.Percent),
Unit(1, UnitTypes.Percent)
)
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, self._bollinger)
self.DrawOwnTrades(area)
def _on_process(self, candle, rsi_val):
if candle.State != CandleStates.Finished:
return
# Process MACD and BB manually (need CandleIndicatorValue wrapper for Python)
civ_macd = CandleIndicatorValue(self._macd, candle)
civ_macd.IsFinal = True
macd_result = self._macd.Process(civ_macd)
civ_bb = CandleIndicatorValue(self._bollinger, candle)
civ_bb.IsFinal = True
bb_result = self._bollinger.Process(civ_bb)
if not self._macd.IsFormed or not self._bollinger.IsFormed:
return
macd_val = float(IndicatorHelper.ToDecimal(macd_result))
if bb_result.UpBand is None or bb_result.LowBand is None or bb_result.MovingAverage is None:
return
upper = float(bb_result.UpBand)
lower = float(bb_result.LowBand)
middle = float(bb_result.MovingAverage)
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
self._prev_macd = macd_val
return
close = float(candle.ClosePrice)
rsi = float(rsi_val)
cooldown = int(self._cooldown_bars.Value)
# Buy: price below lower BB + RSI oversold
if close <= lower and rsi < 30 and self.Position == 0:
self.BuyMarket()
self._cooldown_remaining = cooldown
# Sell: price above upper BB + RSI overbought
elif close >= upper and rsi > 70 and self.Position == 0:
self.SellMarket()
self._cooldown_remaining = cooldown
self._prev_macd = macd_val
def CreateClone(self):
return macd_bb_rsi_strategy()