BB戦略戦略
概要
BBStrategy は、MetaTrader エキスパート アドバイザー「BBStrategy」から変換された Bollinger バンド ブレイクアウト システムです。この戦略は、周期は同じだが偏差乗数が異なる 2 セットの Bollinger バンドを追跡します。価格が外側のバンドを突き抜けると、アルゴリズムは取引を準備しますが、実際のエントリーは、価格が内側のバンドに戻るまで延期されます。この行動は、ボラティリティ拡大後の継続の動きを捉えながら、過度に延長されたブレイクアウトの購入や深く売られすぎた状態での売りを回避しようとします。
中核ロジック
- ローソク足を購読し、2 つの Bollinger バンドを計算します。
- アウターバンドは、設定可能な偏差乗数 (デフォルトは 3.0) を使用します。
- 内側のバンドでは、より低い偏差が使用されます (デフォルトは 2.0)。
- 終値が外側のバンドの外側で終了するときを検出します。
- 上部外側のバンドアームの上に長いセットアップがあります。
- 下部外側バンドアームの下に短いセットアップがあります。
- 次に完了したローソク足がブレイクアウトの方向にある内側のバンドの内側で閉じた場合にのみエントリーします。価格が再参入するのを待っている間、戦略は対応する方向に対して「待機」状態に留まります。
- 条件が一致し、オープンポジションやアクティブな注文がない場合に、単一の成行注文を送信します。既存の反対ポジションは、成行注文の量を増やすことによって決済されます。
- オプションのテイクプロフィットとストップロスの距離 (ポイントで表現) は、絶対価格オフセットに変換され、組み込みの保護ヘルパーを介して管理されます。
パラメーター
| 名前 | 説明 |
|---|---|
| 注文量 | 各ポジションの取引サイズ。 |
| Bollinger 期間 | 両方の Bollinger バンドの計算に使用されるローソク足の数。 |
| 内部偏差 | プルバックを検証する内側バンドの偏差乗数。 |
| 外側の偏差 | ブレイクアウトを検出する外側バンドの偏差乗数。 |
| ストップロスポイント | 保護停止距離 (ポイント単位) (0 は停止を無効にします)。 |
| 利益確定ポイント | テイクプロフィット距離 (ポイント単位) (0 はターゲットを無効にします)。 |
| キャンドルタイプ | 計算用のローソク足の時間枠。 |
注意事項
- この戦略は一度に 1 つのポジションを取引し、注文がアクティブである間は新しいシグナルを無視します。
- リスク管理のために、ヘルパーは商品ティック サイズに基づいて MetaTrader の「ポイント」を実際の価格増分に変換します。
- チャートの描画には、視覚的なデバッグを容易にするために、ローソク足、Bollinger バンドと戦略自体のトレードの両方が含まれています。
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Bollinger Bands breakout strategy.
/// When price breaks above outer band, waits for re-entry into inner band then goes long.
/// When price breaks below outer band, waits for re-entry into inner band then goes short.
/// </summary>
public class BBStrategy : Strategy
{
private readonly StrategyParam<int> _bollingerPeriod;
private readonly StrategyParam<decimal> _innerDeviation;
private readonly StrategyParam<decimal> _outerDeviation;
private readonly StrategyParam<DataType> _candleType;
public int BollingerPeriod
{
get => _bollingerPeriod.Value;
set => _bollingerPeriod.Value = value;
}
public decimal InnerDeviation
{
get => _innerDeviation.Value;
set => _innerDeviation.Value = value;
}
public decimal OuterDeviation
{
get => _outerDeviation.Value;
set => _outerDeviation.Value = value;
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public BBStrategy()
{
_bollingerPeriod = Param(nameof(BollingerPeriod), 20)
.SetDisplay("BB Period", "Bollinger Bands period", "Indicators");
_innerDeviation = Param(nameof(InnerDeviation), 2m)
.SetDisplay("Inner Dev", "Inner band deviations", "Indicators");
_outerDeviation = Param(nameof(OuterDeviation), 3m)
.SetDisplay("Outer Dev", "Outer band deviations", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Candle series", "General");
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var innerBand = new BollingerBands
{
Length = BollingerPeriod,
Width = InnerDeviation
};
var outerBand = new BollingerBands
{
Length = BollingerPeriod,
Width = OuterDeviation
};
var waitDirection = 0;
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(innerBand, outerBand, (candle, innerVal, outerVal) =>
{
if (candle.State != CandleStates.Finished)
return;
if (!innerBand.IsFormed || !outerBand.IsFormed)
return;
if (innerVal.IsEmpty || outerVal.IsEmpty)
return;
var innerBb = innerVal as IBollingerBandsValue;
var outerBb = outerVal as IBollingerBandsValue;
if (innerBb == null || outerBb == null)
return;
var innerUpper = innerBb.UpBand ?? 0;
var innerLower = innerBb.LowBand ?? 0;
var outerUpper = outerBb.UpBand ?? 0;
var outerLower = outerBb.LowBand ?? 0;
if (innerUpper == 0 || innerLower == 0 || outerUpper == 0 || outerLower == 0)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var price = candle.ClosePrice;
var signal = 0;
// Detect outer band breakout
if (price > outerUpper)
waitDirection = 1;
else if (price < outerLower)
waitDirection = -1;
// Check re-entry into inner band
if (waitDirection > 0 && price < innerUpper && price > innerLower)
{
signal = 1;
waitDirection = 0;
}
else if (waitDirection < 0 && price > innerLower && price < innerUpper)
{
signal = -1;
waitDirection = 0;
}
if (signal == 1 && Position <= 0)
BuyMarket();
else if (signal == -1 && Position >= 0)
SellMarket();
})
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, innerBand);
DrawIndicator(area, outerBand);
DrawOwnTrades(area);
}
}
}
import clr
import math
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 SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class bb_strategy(Strategy):
def __init__(self):
super(bb_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5)))
self._bollinger_period = self.Param("BollingerPeriod", 20)
self._inner_deviation = self.Param("InnerDeviation", 2.0)
self._outer_deviation = self.Param("OuterDeviation", 3.0)
self._closes = []
self._wait_direction = 0
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def BollingerPeriod(self):
return self._bollinger_period.Value
@BollingerPeriod.setter
def BollingerPeriod(self, value):
self._bollinger_period.Value = value
@property
def InnerDeviation(self):
return self._inner_deviation.Value
@InnerDeviation.setter
def InnerDeviation(self, value):
self._inner_deviation.Value = value
@property
def OuterDeviation(self):
return self._outer_deviation.Value
@OuterDeviation.setter
def OuterDeviation(self, value):
self._outer_deviation.Value = value
def OnReseted(self):
super(bb_strategy, self).OnReseted()
self._closes = []
self._wait_direction = 0
def OnStarted2(self, time):
super(bb_strategy, self).OnStarted2(time)
self._closes = []
self._wait_direction = 0
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._process_candle).Start()
def _compute_bands(self, period, deviation):
if len(self._closes) < period:
return None, None, None
window = self._closes[-period:]
mean = sum(window) / period
variance = sum((x - mean) ** 2 for x in window) / period
std = math.sqrt(variance)
upper = mean + deviation * std
lower = mean - deviation * std
return mean, upper, lower
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
period = self.BollingerPeriod
inner_dev = float(self.InnerDeviation)
outer_dev = float(self.OuterDeviation)
self._closes.append(close)
while len(self._closes) > period + 10:
self._closes.pop(0)
if len(self._closes) < period:
return
_, inner_upper, inner_lower = self._compute_bands(period, inner_dev)
_, outer_upper, outer_lower = self._compute_bands(period, outer_dev)
if inner_upper is None or outer_upper is None:
return
# Detect outer band breakout
if close > outer_upper:
self._wait_direction = 1
elif close < outer_lower:
self._wait_direction = -1
signal = 0
# Check re-entry into inner band
if self._wait_direction > 0 and close < inner_upper and close > inner_lower:
signal = 1
self._wait_direction = 0
elif self._wait_direction < 0 and close > inner_lower and close < inner_upper:
signal = -1
self._wait_direction = 0
if signal == 1 and self.Position <= 0:
self.BuyMarket()
elif signal == -1 and self.Position >= 0:
self.SellMarket()
def CreateClone(self):
return bb_strategy()