Bollinger Winner Pro 戦略
Bollinger Winner Pro は、Lite版にモジュール式フィルターとリスク管理を追加して 拡張したものです。引き続きBollinger Bandsの外側で終値を付ける価格を探しますが、 オプションの確認が一致する場合にのみ取引が実行されます。
トレーダーはRSI、Aroon、移動平均フィルターを有効にして、モメンタムとトレンドの 方向を確認できます。統合されたストップロスも有効にしてリスクを制限することが できます。この柔軟性により、戦略はさまざまな市場やテストニーズに適応できます。
このアプローチは平均回帰を目標としています:価格がバンドに再び入るか反対側に 触れると、ポジションがクローズされるかストップが発動します。複数のフィルターを 重ねられるため、シグナルは少なくなりますが、質が高くなります。
詳細
- データ: 価格ローソク足。
- エントリー条件: ローソク足がバンドの外側で終値を付け、有効なすべてのフィルターが一致する。
- エグジット条件: 中央/反対側バンドへの回帰、または
UseSLが真の場合はストップロス。 - ストップ:
UseSLで制御されるオプションのストップロス。 - デフォルト値:
UseRSI= TrueUseAroon= FalseUseMA= TrueUseSL= True
- フィルター:
- カテゴリ: 確認付き平均回帰
- 方向: ロング/ショート
- インジケーター: Bollinger Bands, RSI, Aroon, Moving Average
- 複雑さ: 上級
- リスクレベル: 中
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>
/// Bollinger Winner Pro strategy.
/// Goes long when a candle closes below the lower band and short when it closes above the upper band, provided every
/// enabled filter agrees: RSI beyond its oversold/overbought level, Aroon up/down dominance in the trade direction and
/// the middle band on the trade side of the moving average, which confirms the trend direction. A position closes when price returns to the middle band (or reaches
/// the opposite one), and an optional percent stop-loss caps the risk.
/// </summary>
public class BollingerWinnerProStrategy : Strategy
{
private readonly StrategyParam<int> _bbLength;
private readonly StrategyParam<decimal> _bbMultiplier;
private readonly StrategyParam<bool> _useRsi;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<decimal> _rsiOversold;
private readonly StrategyParam<decimal> _rsiOverbought;
private readonly StrategyParam<bool> _useAroon;
private readonly StrategyParam<int> _aroonLength;
private readonly StrategyParam<bool> _useMa;
private readonly StrategyParam<int> _maLength;
private readonly StrategyParam<bool> _useSl;
private readonly StrategyParam<decimal> _stopLossPercent;
private readonly StrategyParam<DataType> _candleType;
/// <summary>
/// Bollinger Bands period.
/// </summary>
public int BBLength
{
get => _bbLength.Value;
set => _bbLength.Value = value;
}
/// <summary>
/// Bollinger Bands standard deviation multiplier.
/// </summary>
public decimal BBMultiplier
{
get => _bbMultiplier.Value;
set => _bbMultiplier.Value = value;
}
/// <summary>
/// Enable the RSI filter.
/// </summary>
public bool UseRSI
{
get => _useRsi.Value;
set => _useRsi.Value = value;
}
/// <summary>
/// RSI period.
/// </summary>
public int RSILength
{
get => _rsiLength.Value;
set => _rsiLength.Value = value;
}
/// <summary>
/// RSI level below which longs are allowed.
/// </summary>
public decimal RSIOversold
{
get => _rsiOversold.Value;
set => _rsiOversold.Value = value;
}
/// <summary>
/// RSI level above which shorts are allowed.
/// </summary>
public decimal RSIOverbought
{
get => _rsiOverbought.Value;
set => _rsiOverbought.Value = value;
}
/// <summary>
/// Enable the Aroon filter.
/// </summary>
public bool UseAroon
{
get => _useAroon.Value;
set => _useAroon.Value = value;
}
/// <summary>
/// Aroon period.
/// </summary>
public int AroonLength
{
get => _aroonLength.Value;
set => _aroonLength.Value = value;
}
/// <summary>
/// Enable the moving average filter.
/// </summary>
public bool UseMA
{
get => _useMa.Value;
set => _useMa.Value = value;
}
/// <summary>
/// Moving average period.
/// </summary>
public int MALength
{
get => _maLength.Value;
set => _maLength.Value = value;
}
/// <summary>
/// Enable the stop-loss.
/// </summary>
public bool UseSL
{
get => _useSl.Value;
set => _useSl.Value = value;
}
/// <summary>
/// Stop-loss percentage from the entry price.
/// </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 BollingerWinnerProStrategy()
{
_bbLength = Param(nameof(BBLength), 20)
.SetGreaterThanZero()
.SetDisplay("BB Period", "Bollinger Bands period", "Bollinger Bands");
_bbMultiplier = Param(nameof(BBMultiplier), 1.5m)
.SetGreaterThanZero()
.SetDisplay("BB StdDev", "Bollinger Bands standard deviation multiplier", "Bollinger Bands");
_useRsi = Param(nameof(UseRSI), true)
.SetDisplay("Use RSI", "Require RSI confirmation", "RSI Filter");
_rsiLength = Param(nameof(RSILength), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Length", "RSI period", "RSI Filter");
_rsiOversold = Param(nameof(RSIOversold), 40m)
.SetDisplay("RSI Oversold", "RSI level below which longs are allowed", "RSI Filter");
_rsiOverbought = Param(nameof(RSIOverbought), 60m)
.SetDisplay("RSI Overbought", "RSI level above which shorts are allowed", "RSI Filter");
_useAroon = Param(nameof(UseAroon), false)
.SetDisplay("Use Aroon", "Require Aroon confirmation", "Aroon Filter");
_aroonLength = Param(nameof(AroonLength), 14)
.SetGreaterThanZero()
.SetDisplay("Aroon Length", "Aroon period", "Aroon Filter");
_useMa = Param(nameof(UseMA), true)
.SetDisplay("Use MA", "Require the middle band on the trade side of the moving average", "Moving Average");
_maLength = Param(nameof(MALength), 50)
.SetGreaterThanZero()
.SetDisplay("MA Length", "Moving average period", "Moving Average");
_useSl = Param(nameof(UseSL), true)
.SetDisplay("Use Stop Loss", "Enable the percent stop-loss", "Risk");
_stopLossPercent = Param(nameof(StopLossPercent), 2m)
.SetGreaterThanZero()
.SetDisplay("Stop Loss %", "Stop-loss percentage from the entry price", "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 OnStarted2(DateTime time)
{
base.OnStarted2(time);
var bollinger = new BollingerBands
{
Length = BBLength,
Width = BBMultiplier
};
var rsi = new RelativeStrengthIndex { Length = RSILength };
var aroon = new Aroon { Length = AroonLength };
var ma = new ExponentialMovingAverage { Length = MALength };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(bollinger, rsi, aroon, ma, ProcessCandle)
.Start();
if (UseSL)
StartProtection(new Unit(), new Unit(StopLossPercent, UnitTypes.Percent), useMarketOrders: true);
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, bollinger);
DrawIndicator(area, ma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue bollingerValue, IIndicatorValue rsiValue, IIndicatorValue aroonValue, IIndicatorValue maValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!bollingerValue.IsFormed || !rsiValue.IsFormed || !aroonValue.IsFormed || !maValue.IsFormed)
return;
var bb = (BollingerBandsValue)bollingerValue;
if (bb.UpBand is not decimal upper ||
bb.LowBand is not decimal lower ||
bb.MovingAverage is not decimal middle)
return;
var aroon = (AroonValue)aroonValue;
if (aroon.Up is not decimal aroonUp || aroon.Down is not decimal aroonDown)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var rsi = rsiValue.ToDecimal();
var ma = maValue.ToDecimal();
var close = candle.ClosePrice;
var longSignal = close < lower
&& (!UseRSI || rsi < RSIOversold)
&& (!UseAroon || aroonUp > aroonDown)
&& (!UseMA || middle > ma);
var shortSignal = close > upper
&& (!UseRSI || rsi > RSIOverbought)
&& (!UseAroon || aroonDown > aroonUp)
&& (!UseMA || middle < ma);
if (longSignal && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
else if (shortSignal && Position >= 0)
SellMarket(Volume + Math.Abs(Position));
else if (Position > 0 && close >= middle)
SellMarket(Position);
else if (Position < 0 && close <= middle)
BuyMarket(-Position);
}
}
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 BollingerBands, RelativeStrengthIndex, ExponentialMovingAverage, Aroon
from StockSharp.Algo.Strategies import Strategy
class bollinger_winner_pro_strategy(Strategy):
"""
Bollinger Winner Pro strategy.
Goes long when a candle closes below the lower band and short when it closes above the upper band, provided every
enabled filter agrees: RSI beyond its oversold/overbought level, Aroon up/down dominance in the trade direction and
the middle band on the trade side of the moving average, which confirms the trend direction. A position closes when price returns to the middle band (or reaches
the opposite one), and an optional percent stop-loss caps the risk.
"""
def __init__(self):
super(bollinger_winner_pro_strategy, self).__init__()
self._bb_length = self.Param("BBLength", 20) \
.SetGreaterThanZero() \
.SetDisplay("BB Period", "Bollinger Bands period", "Bollinger Bands")
self._bb_multiplier = self.Param("BBMultiplier", 1.5) \
.SetGreaterThanZero() \
.SetDisplay("BB StdDev", "Bollinger Bands standard deviation multiplier", "Bollinger Bands")
self._use_rsi = self.Param("UseRSI", True) \
.SetDisplay("Use RSI", "Require RSI confirmation", "RSI Filter")
self._rsi_length = self.Param("RSILength", 14) \
.SetGreaterThanZero() \
.SetDisplay("RSI Length", "RSI period", "RSI Filter")
self._rsi_oversold = self.Param("RSIOversold", 40.0) \
.SetDisplay("RSI Oversold", "RSI level below which longs are allowed", "RSI Filter")
self._rsi_overbought = self.Param("RSIOverbought", 60.0) \
.SetDisplay("RSI Overbought", "RSI level above which shorts are allowed", "RSI Filter")
self._use_aroon = self.Param("UseAroon", False) \
.SetDisplay("Use Aroon", "Require Aroon confirmation", "Aroon Filter")
self._aroon_length = self.Param("AroonLength", 14) \
.SetGreaterThanZero() \
.SetDisplay("Aroon Length", "Aroon period", "Aroon Filter")
self._use_ma = self.Param("UseMA", True) \
.SetDisplay("Use MA", "Require the middle band on the trade side of the moving average", "Moving Average")
self._ma_length = self.Param("MALength", 50) \
.SetGreaterThanZero() \
.SetDisplay("MA Length", "Moving average period", "Moving Average")
self._use_sl = self.Param("UseSL", True) \
.SetDisplay("Use Stop Loss", "Enable the percent stop-loss", "Risk")
self._stop_loss_percent = self.Param("StopLossPercent", 2.0) \
.SetGreaterThanZero() \
.SetDisplay("Stop Loss %", "Stop-loss percentage from the entry price", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))) \
.SetDisplay("Candle type", "Candle type for strategy calculation", "General")
@property
def CandleType(self):
return self._candle_type.Value
def GetWorkingSecurities(self):
return [(self.Security, self.CandleType)]
def OnStarted2(self, time):
super(bollinger_winner_pro_strategy, self).OnStarted2(time)
bollinger = BollingerBands()
bollinger.Length = self._bb_length.Value
bollinger.Width = Decimal(self._bb_multiplier.Value)
rsi = RelativeStrengthIndex()
rsi.Length = self._rsi_length.Value
aroon = Aroon()
aroon.Length = self._aroon_length.Value
ma = ExponentialMovingAverage()
ma.Length = self._ma_length.Value
subscription = self.SubscribeCandles(self.CandleType)
subscription.BindEx(bollinger, rsi, aroon, ma, self._process_candle).Start()
if self._use_sl.Value:
self.StartProtection(Unit(), Unit(Decimal(self._stop_loss_percent.Value), UnitTypes.Percent), useMarketOrders=True)
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, bollinger)
self.DrawIndicator(area, ma)
self.DrawOwnTrades(area)
def _process_candle(self, candle, bollinger_value, rsi_value, aroon_value, ma_value):
if candle.State != CandleStates.Finished:
return
if not bollinger_value.IsFormed or not rsi_value.IsFormed or not aroon_value.IsFormed or not ma_value.IsFormed:
return
if bollinger_value.UpBand is None or bollinger_value.LowBand is None or bollinger_value.MovingAverage is None:
return
if aroon_value.Up is None or aroon_value.Down is None:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
upper = float(bollinger_value.UpBand)
lower = float(bollinger_value.LowBand)
middle = float(bollinger_value.MovingAverage)
aroon_up = float(aroon_value.Up)
aroon_down = float(aroon_value.Down)
rsi = float(rsi_value.GetValue[Decimal](None))
ma = float(ma_value.GetValue[Decimal](None))
close = float(candle.ClosePrice)
use_rsi = self._use_rsi.Value
use_aroon = self._use_aroon.Value
use_ma = self._use_ma.Value
long_signal = close < lower \
and (not use_rsi or rsi < float(self._rsi_oversold.Value)) \
and (not use_aroon or aroon_up > aroon_down) \
and (not use_ma or middle > ma)
short_signal = close > upper \
and (not use_rsi or rsi > float(self._rsi_overbought.Value)) \
and (not use_aroon or aroon_down > aroon_up) \
and (not use_ma or middle < ma)
if long_signal and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif short_signal and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
elif self.Position > 0 and close >= middle:
self.SellMarket(self.Position)
elif self.Position < 0 and close <= middle:
self.BuyMarket(-self.Position)
def CreateClone(self):
return bollinger_winner_pro_strategy()