GitHub で見る
Bollinger バンドのセッションの反転
この戦略は、MetaTrader エキスパート アドバイザー BollingerBandsEA (ver. 3.0) の C# ポートです。アクティブな取引セッション中に価格が Bollinger バンドを超えた後に発生する平均回帰セットアップを取引します。
取引ロジック
- 主要な日中ローソク足シリーズ (デフォルトでは 15 分足ローソク足) と、トレンド フィルターの構築に使用される日次ローソク足シリーズを購読します。
- 日中シリーズの Bollinger バンド (長さ 20、幅 2.0) と日次終値の 100 期間 SMA を計算します。
- 現在および前日の高値/安値を追跡し、信号評価のために以前の Bollinger バンド値を保持します。
- 取引セッションウィンドウ内でのみエントリーを許可します。取引日開始後の
SessionStartOffsetMinutes から取引日終了前の SessionEndOffsetMinutes までです。
- その日の累積損益がプラスになったら取引をスキップし、EA の毎日のストップを模倣します。
- 前のローソク足が弱気で、上部バンドより上で終了し、現在の終値がそのバンドより上に留まり、バンド幅が十分に広く、価格が日次 SMA を下回り、価格が現在または以前の日次高値を上回って取引されている場合、ショートにエントリーします。
- 前のローソク足が強気で、下限バンドを下回って終了し、現在の終値がそのバンドを下回ったままで、バンド幅が十分に広く、価格が日次 SMA を上回り、価格が現在または以前の日次安値を下回って取引されている場合に、ロングに入ります。
- ポジションサイズは、構成された固定ボリュームまたはポイント単位のストップロスまでの距離を使用するリスクベースのサイジングのいずれかによって決定されます。
- 終了は、ストップロス、テイクプロフィット、オプションのミドルバンドでのクローズ、オプションのトレーリングストップ、オプションの損益分岐点ロジックをチェックすることによって実行されます。負けた取引は、設定可能な保持時間の経過後に清算することもできます。
パラメーター
| パラメータ |
説明 |
CandleType |
取引に使用される日中ローソク足シリーズ。 |
BollingerLength |
Bollinger バンドの移動平均の期間。 |
BollingerWidth |
Bollinger バンドの幅の乗数。 |
DailyMaLength |
日次の SMA フィルターの長さ。 |
StopLossPoints |
インスツルメントポイントで表されるストップロス距離。 |
UseRiskVolume |
リスクベースのポジションサイジングを可能にします。 |
RiskPercent |
リスクベースのサイジングに使用されるアカウントの割合。 |
FixedVolume |
リスクサイジングが無効または不可能な場合、固定ボリュームをフォールバックします。 |
SessionStartOffsetMinutes |
セッション開始後、エントリーが許可されるまでの数分。 |
SessionEndOffsetMinutes |
エントリがブロックされるセッション終了の数分前。 |
CloseOnMiddleBand |
価格がBollingerのミドルバンドを横切ったときにポジションを終了します。 |
EnableTrailing |
トレーリングストップ調整を有効にします。 |
TrailingFactor |
ストップを追跡するまでに必要な距離の乗数。 |
EnableBreakEven |
ストップをエントリー価格に移動できるようにします。 |
BreakEvenFactor |
ストップを損益分岐点に移動するには利益倍数が必要です。 |
CloseLosingAfterMinutes |
指定された分間保持した後、負けた取引を閉じます。 |
注意事項
- 保護的なストップロス注文と利食い注文は、更新ごとにローソク足の極値をチェックすることによってシミュレートされます。取引所側の保護命令が必要な場合は、このセクションを調整します。
- リスクベースのサイジングは、
Security.Step と Security.StepPrice に依存します。これらの値が欠落している場合、戦略は固定ボリュームに戻ります。
- 毎日のプロフィットストップは損益戦略を使用するため、実現損益と変動損益はポートフォリオと同じ通貨である必要があります。
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Trades reversals from Bollinger Bands.
/// Buys when price closes below lower band and sells when price closes above upper band.
/// </summary>
public class BollingerBandsSessionReversalStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _bollingerLength;
private readonly StrategyParam<decimal> _bollingerWidth;
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Length of the Bollinger Bands moving average.
/// </summary>
public int BollingerLength
{
get => _bollingerLength.Value;
set => _bollingerLength.Value = value;
}
/// <summary>
/// Width multiplier for Bollinger Bands.
/// </summary>
public decimal BollingerWidth
{
get => _bollingerWidth.Value;
set => _bollingerWidth.Value = value;
}
/// <summary>
/// Initializes strategy parameters.
/// </summary>
public BollingerBandsSessionReversalStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Primary candle series", "General");
_bollingerLength = Param(nameof(BollingerLength), 20)
.SetGreaterThanZero()
.SetDisplay("Bollinger Length", "MA period for Bollinger Bands", "Indicators");
_bollingerWidth = Param(nameof(BollingerWidth), 2.0m)
.SetGreaterThanZero()
.SetDisplay("Bollinger Width", "Band width multiplier", "Indicators");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var bollinger = new BollingerBands
{
Length = BollingerLength,
Width = BollingerWidth
};
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(bollinger, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, bollinger);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue bbValue)
{
if (candle.State != CandleStates.Finished)
return;
if (bbValue is not IBollingerBandsValue bb)
return;
var middle = bb.MovingAverage ?? 0m;
var upper = bb.UpBand ?? 0m;
var lower = bb.LowBand ?? 0m;
if (middle == 0m || upper == 0m || lower == 0m)
return;
var price = candle.ClosePrice;
// Reversal: buy when price falls below lower band
if (price < lower && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
}
// Reversal: sell when price rises above upper band
else if (price > upper && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
}
// Exit at middle band
else if (Position > 0 && price >= middle)
{
SellMarket();
}
else if (Position < 0 && price <= middle)
{
BuyMarket();
}
}
}
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 BollingerBands
from StockSharp.Algo.Strategies import Strategy
class bollinger_bands_session_reversal_strategy(Strategy):
def __init__(self):
super(bollinger_bands_session_reversal_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Primary candle series", "General")
self._bollinger_length = self.Param("BollingerLength", 20) \
.SetGreaterThanZero() \
.SetDisplay("Bollinger Length", "MA period for Bollinger Bands", "Indicators")
self._bollinger_width = self.Param("BollingerWidth", 2.0) \
.SetGreaterThanZero() \
.SetDisplay("Bollinger Width", "Band width multiplier", "Indicators")
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def BollingerLength(self):
return self._bollinger_length.Value
@BollingerLength.setter
def BollingerLength(self, value):
self._bollinger_length.Value = value
@property
def BollingerWidth(self):
return self._bollinger_width.Value
@BollingerWidth.setter
def BollingerWidth(self, value):
self._bollinger_width.Value = value
def OnStarted2(self, time):
super(bollinger_bands_session_reversal_strategy, self).OnStarted2(time)
bollinger = BollingerBands()
bollinger.Length = self.BollingerLength
bollinger.Width = self.BollingerWidth
subscription = self.SubscribeCandles(self.CandleType)
subscription.BindEx(bollinger, self._process_candle).Start()
def _process_candle(self, candle, bb_value):
if candle.State != CandleStates.Finished:
return
upper = bb_value.UpBand
lower = bb_value.LowBand
middle = bb_value.MovingAverage
if upper is None or lower is None or middle is None:
return
up = float(upper)
lo = float(lower)
mid = float(middle)
if up == 0 or lo == 0 or mid == 0:
return
price = float(candle.ClosePrice)
if price < lo and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif price > up and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
elif self.Position > 0 and price >= mid:
self.SellMarket()
elif self.Position < 0 and price <= mid:
self.BuyMarket()
def CreateClone(self):
return bollinger_bands_session_reversal_strategy()