GitHub で見る

BBスクイーズ戦略

この戦略はボリンジャーバンドの収縮と拡張を監視してボラティリティブレイクアウトを活用します。スクイーズとは、上部バンドと下部バンドの距離が中央バンドに対して狭くなる期間として定義されます。スクイーズ後にボラティリティが拡大し、価格がバンドの外側で終値を付けると、システムはブレイクアウトの方向にエントリーします。

ポジションは成行注文で建てられます。スクイーズ後に価格が上部バンドより上で終値を付けるとロングポジションが作られ、価格が下部バンドより下で終値を付けるとショートポジションが建てられます。完成したローソク足のみが処理され、形成中の早期シグナルを防ぎます。

アルゴリズムはローソク足の完全な履歴を保存せずにバンド幅の変化を追跡します。現在の幅と前の幅を比較することで、注文を出す前に本当の拡大が発生していることを確認します。これにより、ブレイクアウトが発生しない長期的な低ボラティリティ局面へのエントリーを避けます。

デフォルトパラメーターは幅の乗数2の20期間ボリンジャーバンドを使用します。スクイーズのしきい値は0.05に設定されており、低ボラティリティを記録するためにはバンドが中央ラインの5%以内に収まる必要があります。ローソク足の時間軸とすべての数値は完全に設定可能で、StockSharp環境での最適化をサポートします。

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 Bands breakout strategy.
/// Enters on breakout above/below bands, exits at middle band.
/// </summary>
public class BbSqueezeStrategy : Strategy
{
	private readonly StrategyParam<int> _bollingerPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevClose;
	private decimal _prevUpper;
	private decimal _prevLower;
	private bool _hasPrev;

	public int BollingerPeriod { get => _bollingerPeriod.Value; set => _bollingerPeriod.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public BbSqueezeStrategy()
	{
		_bollingerPeriod = Param(nameof(BollingerPeriod), 20)
			.SetGreaterThanZero()
			.SetDisplay("Bollinger Period", "Period of Bollinger Bands", "Parameters");

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

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
		=> [(Security, CandleType)];

	protected override void OnReseted()
	{
		base.OnReseted();
		_prevClose = 0;
		_prevUpper = 0;
		_prevLower = 0;
		_hasPrev = false;
	}

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

		var bb = new BollingerBands { Length = BollingerPeriod, Width = 2m };
		SubscribeCandles(CandleType).BindEx(bb, ProcessCandle).Start();
	}

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

		var bbVal = (BollingerBandsValue)value;
		if (bbVal.UpBand is not decimal upper ||
			bbVal.LowBand is not decimal lower ||
			bbVal.MovingAverage is not decimal middle)
			return;

		var close = candle.ClosePrice;

		if (!_hasPrev)
		{
			_prevClose = close;
			_prevUpper = upper;
			_prevLower = lower;
			_hasPrev = true;
			return;
		}

		// Cross above upper band => buy
		if (_prevClose <= _prevUpper && close > upper && Position <= 0)
		{
			if (Position < 0) BuyMarket();
			BuyMarket();
		}
		// Cross below lower band => sell
		else if (_prevClose >= _prevLower && close < lower && Position >= 0)
		{
			if (Position > 0) SellMarket();
			SellMarket();
		}
		// Exit long at middle
		else if (Position > 0 && close < middle)
		{
			SellMarket();
		}
		// Exit short at middle
		else if (Position < 0 && close > middle)
		{
			BuyMarket();
		}

		_prevClose = close;
		_prevUpper = upper;
		_prevLower = lower;
	}
}