GitHub で見る

Bollinger バンド保留ストップ戦略

概要

このサンプルは、元の MQL「Bb_0_1」エキスパート アドバイザを StockSharp の高レベル API に変換します。この戦略は 1 つのローソク足サブスクリプションをリッスンし、Bollinger バンドを使用して現在の価格をブラケットします。市場が上限バンドと下限バンドの間にある場合、アルゴリズムは価格より上に 3 層の買いストップ注文を配置し、価格より下に 3 層の売りストップ注文を配置します。各レイヤーは、反対側のバンドから取得した同じストップリファレンスを共有しながら、個別のテイクプロフィットディスタンスで構成されます。

取引ロジック

  • 構成されたタイムフレームをサブスクライブし、要求された期間と偏差を使用して Bollinger バンドを計算します。
  • 取引ウィンドウ内 (StartHour < 時間 < EndHour) で、価格がバンドの間にある間に未決注文を出します。
    • 現在のアッパーバンドレベルで 3 つの買いストップがあり、テイクプロフィットはエントリーより FirstTakeProfitSecondTakeProfit、および ThirdTakeProfit の価格ステップだけ置き換えられます。
    • 現在の下位バンドレベルで 3 つの売りストップがあり、エントリーの下にミラーリングされたテイクプロフィットがあります。
    • すべてのエントリは、最初の保護停止として反対のバンドを継承します。
  • 未決注文は、注文がインジケーターのエンベロープに従うように、バンドが価格に近づくたびに自動的に再登録されます。
  • ストップ注文が実行されると、ストラテジーは約定したボリュームに対して明示的なストップロス注文とテイクプロフィット注文を登録します。
  • トレーリング保護はオプションです。UseBandTrailingStop はトレーリング用に反対側のバンドを選択します。それ以外の場合は、中間バンド (EMA) が使用されます。ストップは、終値がエントリー価格を超え、インジケーターの値がより良いレベルを提供する場合にのみ追跡します。

パラメーター

名前 説明
CandleType Bollinger バンドの計算に使用される時間枠。
BandPeriod バンドが使用するキャンドルの数。
BandDeviation バンドの標準偏差乗数。
Volume 各保留中のレイヤーのボリューム。
StartHour / EndHour 時間ごとの取引ウィンドウ (排他的範囲)。
FirstTakeProfit, SecondTakeProfit, ThirdTakeProfit 各レイヤーの価格ステップで表されるテイクプロフィット距離。
UseBandTrailingStop 末尾の参照を選択します: 反対側のバンド (true) または Bollinger の中央のライン (false)。

実装メモ

  • 注文量は、静的なサイズ (Volume) を使用して元のエキスパート アドバイザーを反映します。 StockSharp サンプル環境では口座履歴が提供されないため、MQL コードによるリスクベースのポジションサイジングは実装されていません。
  • 高レベルの API がすでに現在のローソクに合わせた値を提供しているため、MQL スクリプトからのインジケーター シフト パラメーターは公開されません。
  • プロテクション注文は、バンドベースのトレーリング条件がストップレベルを改善するたびに更新される通常のストップ注文および指値注文です。
using System;
using System.Collections.Generic;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Bollinger Band breakout strategy.
/// Buys when price closes above upper band, sells when price closes below lower band.
/// Exits at middle band.
/// </summary>
public class BollingerBandPendingStopsStrategy : Strategy
{
	private readonly StrategyParam<int> _bandPeriod;
	private readonly StrategyParam<decimal> _bandWidth;
	private readonly StrategyParam<DataType> _candleType;

	public int BandPeriod { get => _bandPeriod.Value; set => _bandPeriod.Value = value; }
	public decimal BandWidth { get => _bandWidth.Value; set => _bandWidth.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public BollingerBandPendingStopsStrategy()
	{
		_bandPeriod = Param(nameof(BandPeriod), 20)
			.SetDisplay("Band Period", "Bollinger bands period", "Indicators");

		_bandWidth = Param(nameof(BandWidth), 1m)
			.SetDisplay("Band Width", "Bollinger bands deviation", "Indicators");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
	protected override void OnReseted() { base.OnReseted(); }

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		var bb = new BollingerBands { Length = BandPeriod, Width = BandWidth };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.BindEx(bb, ProcessCandle)
			.Start();
	}

	private void ProcessCandle(ICandleMessage candle, IIndicatorValue value)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!value.IsFinal || value.IsEmpty)
			return;

		var bbVal = value.IsEmpty ? null : value as BollingerBandsValue;
		if (bbVal == null)
			return;

		var upper = bbVal.UpBand;
		var lower = bbVal.LowBand;
		var middle = bbVal.MovingAverage;

		if (upper == null || lower == null || middle == null)
			return;

		var close = candle.ClosePrice;

		// Breakout above upper band - buy
		if (close > upper.Value && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		// Breakout below lower band - sell
		else if (close < lower.Value && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}
		// Exit at middle band
		else if (Position > 0 && close < middle.Value)
		{
			SellMarket();
		}
		else if (Position < 0 && close > middle.Value)
		{
			BuyMarket();
		}
	}
}