GitHub で見る

BBブレイクアウト戦略

概要

BBブレイクアウト戦略は、MetaTraderエキスパートアドバイザーBreakthrough_BBをStockSharpの高レベルAPIで再現します。このシステムはボリンジャーバンドと高速な単純移動平均を組み合わせて、価格がバンド境界付近で圧縮された後に発生する爆発的なブレイクアウトを捉えます。シグナルを確定的に保ち、元のMQL5の動作を反映するために、取引は完成したロウソク足でのみ生成されます。

トレードロジック

  • トレンドフィルター: 設定可能な期間の単純移動平均(SMA)がトレンド方向を検証します。戦略は最新のSMA値を4バー前のSMA値と比較します。ロング取引にはSMAの上向き傾斜が必要で、ショートには下向き傾斜が必要です。
  • ボリンジャーバンドブレイクアウト: 戦略は4バー前の終値がボリンジャー上限または下限バンドとどのように相互作用したかを観察し、最新の終値と比較します。有効なブレイクアウトは、2つのタイムスタンプ間でバンド内から外へ価格が動いたときに発生します。
  • シングルポジションモデル: アルゴリズムは最大1つのオープンポジションを保持します。重複したエクスポージャーを防ぐため、新規エントリーを評価する前にオープン取引をクローズします。

エントリー条件

ロング設定

  1. 4本前の完成したロウソク足の終値がボリンジャー上限バンドより低かった。
  2. 最新の終値が現在のボリンジャー上限バンドより高く終了した。
  3. 最新ロウソク足で計算されたSMA値が4本前のSMA値より大きい(正の傾斜)。
  4. 現在オープンなポジションがない。

ショート設定

  1. 4本前の完成したロウソク足の終値がボリンジャー下限バンドより高かった。
  2. 最新の終値が現在のボリンジャー下限バンドより低く終了した。
  3. 最新ロウソク足で計算されたSMA値が4本前のSMA値より小さい(負の傾斜)。
  4. 現在オープンなポジションがない。

エントリー条件が満たされると、戦略は設定されたボリュームパラメーターを使用して成行注文を送信します。

エグジットルール

  • ロングポジションエグジット: ロング取引がアクティブで最新の終値がボリンジャー中央線を下回った場合、ポジションは即座に成行売り注文でクローズされます。
  • ショートポジションエグジット: ショート取引がオープンで最新の終値がボリンジャー中央線を上回った場合、ポジションは成行買い注文でカバーされます。

これらのエグジットルールは、市場がバンド中央線内に戻るたびに取引を削除した元のエキスパートアドバイザーを模倣します。

インジケーター

  • 単純移動平均(SMA): 方向バイアスを定義し、4ロウソク足インターバルにわたる傾斜比較を提供します。
  • ボリンジャーバンド: ブレイクアウトエントリーの検出とエグジット管理に使用される上限、中央、下限エンベロープを提供します。

パラメーター

名前 説明 デフォルト 最適化可能
MaPeriod トレンドフィルターに使用するSMAの長さ。 9
BandsPeriod ボリンジャーバンド計算のルックバック長。 28
Deviation ボリンジャーバンドに適用される標準偏差乗数。 1.6
Volume 注文サイズ(インストゥルメントに応じてロットまたは枚)。 1
CandleType 戦略が処理するロウソク足集計タイプ。 1時間の時間軸

すべてのパラメーターはStockSharpのStrategyParamメタデータを公開しており、UIで調整したりデザイナーで最適化したりできます。

データ要件

  • 選択したCandleTypeと互換性のあるロウソク足データを提供するあらゆるインストゥルメントで機能します。
  • シグナルは完成したロウソク足のみで評価されます。未完成のロウソク足はロジックを確定的に保つために無視されます。
  • デフォルト設定は時間足ロウソク足を使用しますが、データソースがサポートするあらゆる時間軸を提供できます。

追加の注記

  • アルゴリズムはインジケーター履歴の検索を避け、代わりに終値とSMA値のための4バーのローリングキャッシュを維持し、プロジェクトガイドラインを遵守しています。
  • ストップロスやテイクプロフィットなどの保護機能は必要に応じてStartProtectionで追加できます。元のMQL実装には含まれていないため、ここでは省略されています。
  • 戦略は成行注文を発行するため、スリッページを最小化するために選択したインストゥルメントに十分な流動性があることを確認してください。
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>
/// Breakout strategy that trades Bollinger Bands breakouts with a moving average trend filter.
/// </summary>
public class BreakthroughBbStrategy : Strategy
{
	private readonly StrategyParam<int> _maPeriod;
	private readonly StrategyParam<int> _bandsPeriod;
	private readonly StrategyParam<decimal> _deviation;
	private readonly StrategyParam<DataType> _candleType;

	private SimpleMovingAverage _sma;
	private BollingerBands _bollingerBands;

	private decimal? _closeLag0;
	private decimal? _closeLag1;
	private decimal? _closeLag2;
	private decimal? _closeLag3;

	private decimal? _maLag0;
	private decimal? _maLag1;
	private decimal? _maLag2;
	private decimal? _maLag3;

	/// <summary>
	/// Moving average period that defines the long term trend.
	/// </summary>
	public int MaPeriod
	{
		get => _maPeriod.Value;
		set => _maPeriod.Value = value;
	}

	/// <summary>
	/// Bollinger Bands lookback period.
	/// </summary>
	public int BandsPeriod
	{
		get => _bandsPeriod.Value;
		set => _bandsPeriod.Value = value;
	}

	/// <summary>
	/// Bollinger Bands width measured in standard deviations.
	/// </summary>
	public decimal Deviation
	{
		get => _deviation.Value;
		set => _deviation.Value = value;
	}


	/// <summary>
	/// Candle type processed by the strategy.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Initializes a new instance of <see cref="BreakthroughBbStrategy"/>.
	/// </summary>
	public BreakthroughBbStrategy()
	{
		_maPeriod = Param(nameof(MaPeriod), 9)
			.SetGreaterThanZero()
			.SetDisplay("MA Period", "Simple moving average length", "Parameters")
			;

		_bandsPeriod = Param(nameof(BandsPeriod), 28)
			.SetGreaterThanZero()
			.SetDisplay("Bands Period", "Bollinger Bands lookback", "Parameters")
			;

		_deviation = Param(nameof(Deviation), 1.6m)
			.SetGreaterThanZero()
			.SetDisplay("Deviation", "Bollinger Bands width in deviations", "Parameters")
			;


		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Candle series processed by the strategy", "General");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_sma = default;
		_bollingerBands = default;

		_closeLag0 = null;
		_closeLag1 = null;
		_closeLag2 = null;
		_closeLag3 = null;

		_maLag0 = null;
		_maLag1 = null;
		_maLag2 = null;
		_maLag3 = null;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_sma = new SimpleMovingAverage { Length = MaPeriod };
		_bollingerBands = new BollingerBands
		{
			Length = BandsPeriod,
			Width = Deviation
		};

		var subscription = SubscribeCandles(CandleType);

		subscription
			.BindEx(_sma, _bollingerBands, ProcessCandle)
			.Start();
	}

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

		var smaValue = smaIndValue.IsFormed ? smaIndValue.ToDecimal() : 0m;
		var bb = bbValue as IBollingerBandsValue;
		var middleBand = bb?.MovingAverage ?? 0m;
		var upperBand = bb?.UpBand ?? 0m;
		var lowerBand = bb?.LowBand ?? 0m;

		var close = candle.ClosePrice;
		var maPrev4 = _maLag2;
		var closePrev4 = _closeLag2;

		if (_sma is null || _bollingerBands is null)
		{
			UpdateHistory(close, smaValue);
			return;
		}

		if (!_sma.IsFormed || !_bollingerBands.IsFormed)
		{
			UpdateHistory(close, smaValue);
			return;
		}

		if (Position > 0 && close < middleBand)
		{
			SellMarket();
			UpdateHistory(close, smaValue);
			return;
		}

		if (Position < 0 && close > middleBand)
		{
			BuyMarket();
			UpdateHistory(close, smaValue);
			return;
		}

		if (maPrev4 is null || closePrev4 is null)
		{
			UpdateHistory(close, smaValue);
			return;
		}

		if (Position == 0)
		{
			if (closePrev4.Value < upperBand && close > upperBand && smaValue > maPrev4.Value)
			{
				BuyMarket();
				UpdateHistory(close, smaValue);
				return;
			}

			if (closePrev4.Value > lowerBand && close < lowerBand && smaValue < maPrev4.Value)
			{
				SellMarket();
				UpdateHistory(close, smaValue);
				return;
			}
		}

		UpdateHistory(close, smaValue);
	}

	private void UpdateHistory(decimal close, decimal maValue)
	{
		_maLag3 = _maLag2;
		_maLag2 = _maLag1;
		_maLag1 = _maLag0;
		_maLag0 = maValue;

		_closeLag3 = _closeLag2;
		_closeLag2 = _closeLag1;
		_closeLag1 = _closeLag0;
		_closeLag0 = close;
	}
}