GitHub で見る

1H Bollinger Bands戦略

概要

1H Bollinger Bands戦略は、MetaTraderの専門家「1H Bolinger Bands」をStockSharpの高水準APIに適応させたものです。日次のBollinger Bandsからのバウンスを取引しながら、時間足のトレンドが一致し、長期月次MACDが方向を確認するという考えです。この戦略はデフォルトでH1タイムフレームで機能し、確認のために追加の高い時間軸データストリームに依存します。

トレーディングロジック

  • トレンドフィルター: ベースタイムフレームの2つの線形加重移動平均(LWMA 250および500)により、支配的な方向に一致した取引のみが許可されます。
  • トリガーパターン: 高い時間軸(デフォルトは日次)では、ローソク足の安値が下部Bollinger Bandを突き抜け、次のローソク足がその上で始値をつけるパターンを監視します(上部バンドでのショートには逆)。これは元のバウンス条件を再現します。
  • モメンタム確認: モメンタム(期間14)は高い時間軸で計算されます。直近3つのモメンタム偏差のうち少なくとも1つが100から設定された閾値(デフォルト0.3)を超える必要があります。
  • MACDフィルター: 月次MACD(12/26/9)がシグナルに一致する必要があります。ロングトレードではMACDラインがシグナルラインの上に、ショートでは下にある必要があります。
  • エントリー: すべてのフィルターが一致すると、戦略はマーケット注文を開きます。反対のポジションが開いている場合、要求されたボリュームは既存のエクスポージャーを中和して方向を反転させます。

ポジション管理

リスク管理はSecurity.PriceStepを通じて変換されるpipベースの距離を使用して戦略内で直接実装されています:

  • ストップロス: 価格が設定されたpip数だけエントリーに逆行するとポジションを閉じます。
  • テイクプロフィット: 価格が設定されたpipターゲットに達すると利益を確定します。
  • トレーリングストップ(オプション): 有効で動きがトレーリング距離を超えると、内部トレーリングレベルが価格に続きます。そのレベルを突き抜けるバーがトレードを閉じます。
  • ブレークイーブン(オプション): 価格がトリガー距離だけ進んだ後、ストップレベルはエントリー価格に設定されたオフセットを加えた値(ショートでは引いた値)に移動されます。そのレベルへの引き戻しがポジションを終了させます。

元の専門家のマネーベースの利益管理は再作成されていません。StockSharpバージョンは取引所に依存しないために価格ベースの制御に焦点を当てています。

パラメーター

パラメーター 説明 デフォルト
CandleType シグナル評価のベースタイムフレーム。 1時間足
HigherTimeFrame Bollinger Bandsとモメンタムに使用するタイムフレーム。 1日足
MacdTimeFrame 確認MACDのタイムフレーム。 30日足
FastMaPeriod / SlowMaPeriod ベースタイムフレームの高速/低速LWMA長。 6 / 85
TrendFastPeriod / TrendSlowPeriod 長期LWMAトレンドフィルター。 250 / 500
MomentumPeriod 高い時間軸でのモメンタムルックバック。 14
MomentumThreshold モメンタムの100からの最小絶対偏差。 0.3
BollingerPeriod / BollingerWidth 日次Bollinger Bands設定。 20 / 2.0
TradeVolume 各新規ポジションのベースボリューム。 1
StopLossPips / TakeProfitPips pipでの保護ストップとターゲット。 20 / 50
EnableTrailing / TrailingStopPips トレーリングストップのトグルと距離。 true / 40
EnableBreakEven / BreakEvenTriggerPips / BreakEvenOffsetPips ブレークイーブンのトグル、トリガー距離、オフセット。 true / 30 / 30

すべての数値パラメーターはStrategyParam<T>を通じて公開され、Designer/Runnerで最適化できます。

実装上の注意

  • 戦略は同時に3つのローソク足ストリームを購読します:ベースタイムフレーム、Bollinger/Momentum用の高い時間軸、MACDタイムフレーム。
  • モメンタムは標準StockSharpのMomentumインジケーターを使用し、MQLロジックを模倣するために最後の3つの偏差を保存します。
  • 取引ボリュームとpip距離はSecurity.PriceStepが正しく設定されていることを前提とします。そうでなければ保護ロジックはトリガーされません。
  • StockSharpは単一のネットポジションを維持します。元のスクリプトの「Max_Trades」スケーリング動作はこのポートでは単一の集計ポジションに簡略化されています。
  • MQLバージョンのエクイティベースのストップアウトとマネートレーリング機能は、実装を取引所中立に保つために意図的に省略されています。

使用方法

  1. 時間足、日足、月足を提供するインストゥルメントに戦略を接続します(またはパラメーターを適切に調整します)。
  2. pip距離が価格オフセットに変換されるよう、インストゥルメントがPriceStepを公開していることを確認します。
  3. 戦略を開始する前に、UIまたはコードで希望のボリュームとリスクパラメーターを設定します。
  4. 戦略を開始すると、必要なデータを自動的に購読し、クローズしたローソク足でシグナルを評価し、設定された保護ルールでポジションを管理します。

MQL専門家との既知の違い

  • マネーベースのトレーリングと合計エクイティストップは実装されていません。価格ベースの制御のみが保持されます。
  • MLQコードのアラート、メール、プッシュ通知は省略されています。
  • 注文のスタッキングはStockSharpの単一ネットポジションモデルに置き換えられています。

これらの調整により、元の専門家の中核的な取引アイデアを保持しながら、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;

public class OneHBollingerBandsStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private ExponentialMovingAverage _fast;
	private ExponentialMovingAverage _slow;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;
	private int _cooldown;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
	public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }

	public OneHBollingerBandsStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
		_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
		_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
		_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
	}

	protected override void OnReseted()
	{
		base.OnReseted();
		_fast = null; _slow = null;
		_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_fast = new ExponentialMovingAverage { Length = FastPeriod };
		_slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
		subscription.Bind(_fast, _slow, ProcessCandle);
		subscription.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
		if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }

		var close = candle.ClosePrice;
		var step = Security?.PriceStep ?? 1m;

		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}
		else if (Position < 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}

		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }

		_prevFast = fastValue; _prevSlow = slowValue;
	}
}