GitHub で見る

NextBar モメンタム戦略

この戦略は、最近完成したバーが古い参照バーから遠く離れてクローズした場合に発生するブレイクアウトを取引します。MetaTraderのエキスパートアドバイザー「Nextbar」からインスパイアされ、pipsベースのストップ、トレーリングロジック、制限されたポジション有効期間などの元のマネー管理機能を維持しています。

デフォルト設定は15分足の高速移動するFXまたは指数先物チャートを対象としていますが、定期的なローソク足を提供する任意のシンボルでロジックが機能します。全注文は設定されたポジションサイズで成行で送信されます。

取引ロジック

  • シグナル検出
    • 新しいバーが完成すると、アルゴリズムは前のバーのクローズと SignalBar バー前に発生したクローズを比較します。
    • 前のクローズが遠いクローズより MinDistancePips 以上高い場合、ロングセットアップが生成されます。
    • 前のクローズが遠いクローズより MinDistancePips 以上低い場合、ショートセットアップが現れます。
    • ReverseSignals スイッチは各セットアップの方向を反転し、逆張りワークフローに対応します。
  • 注文処理
    • ポジションが開いている間は注文を無視します。元のエキスパートアドバイザーと同様に、戦略は一度に1つのポジションのみを保持します。
    • 各約定はエントリー価格を保存し、価格単位でストップロスとテイクプロフィットレベルを事前計算します。pipsベースの値は証券の価格ステップを使用して変換されます(5桁の銘柄はMetaTraderのpipサイズに合わせるために自動的に10×乗数を使用します)。

エグジットルール

  • ストップロス / テイクプロフィット – 両方のレベルはオプションです。ゼロの値は対応する保護を無効にします。戦略はローソク足の高値と安値を監視して、レベルが横切られたときにエグジットをトリガーします。
  • トレーリングストップ – 有効な場合(TrailingStopPips > 0)、利益が TrailingStopPips + TrailingStepPips を超えると、ストップが現在の価格に近づきます。価格からストップへの距離が縮まることはなく、単調なトレーリング動作を保証します。
  • ポジション有効期間LifetimeBars 完成したローソク足の間市場に留まった後、利益に関係なく次のバー開始時にポジションがクローズされます。これは元の「N バー後に期限切れ」メカニズムを再現します。

パラメーター

  • CandleType – シグナル評価に使用する時間軸。デフォルトは15分足ローソク足。
  • OrderVolume – 各成行注文で送信される数量。
  • StopLossPips – エントリー価格から保護ストップまでの距離(pips)。
  • TakeProfitPips – エントリー価格から利益目標までの距離(pips)。
  • TrailingStopPips – トレーリングストップが維持する距離。トレーリングロジックを無効にするにはゼロに設定。
  • TrailingStepPips – トレーリングストップが再び進む前に必要な追加利益。トレーリングが無効な場合は無視されます。
  • SignalBar – 比較クローズ間のバー数。現在のバーの参照を避けるために少なくとも2である必要があります。
  • MinDistancePips – シグナルが受け入れられる前の比較されたクローズ間の最小pip距離。
  • LifetimeBars – ポジションが開いたままでいられる最大完成ローソク足数。タイマーを無効にするにはゼロに設定。
  • ReverseSignals – 有効にするとロング/ショートシグナルを反転します。

実装ノート

  • 戦略は重い履歴構造の代わりに前のクローズの短いローリングリストに依存しており、シグナル計算を軽量に保ちます。
  • pipsは証券の価格ステップを使用して価格単位に変換されます。3桁または5桁の小数で引用される銘柄は自動的に伝統的なpip定義にマッピングされます。
  • 全てのリスクコントロールは完成したローソク足で適用されます。バー内保護が必要な場合は、プラットフォーム設定を通じてエクスチェンジネイティブのストップ注文と戦略を組み合わせてください。
  • このサンプルには自動化テストは付属していません。本番で使用する前に履歴データで検証してください。
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>
/// Next Bar Momentum strategy. Compares close with a reference bar for momentum breakout.
/// </summary>
public class NextBarMomentumStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _momentumPeriod;

	private decimal? _prevMom;

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

	public int MomentumPeriod
	{
		get => _momentumPeriod.Value;
		set => _momentumPeriod.Value = value;
	}

	public NextBarMomentumStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe", "General");

		_momentumPeriod = Param(nameof(MomentumPeriod), 10)
			.SetGreaterThanZero()
			.SetDisplay("Momentum Period", "Rate of change lookback", "Indicators");
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevMom = null;
	}

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

		_prevMom = null;

		var momentum = new Momentum { Length = MomentumPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(momentum, ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			var momArea = CreateChartArea();
			if (momArea != null)
				DrawIndicator(momArea, momentum);
			DrawOwnTrades(area);
		}
	}

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

		if (!IsFormedAndOnlineAndAllowTrading())
		{
			_prevMom = momVal;
			return;
		}

		if (_prevMom == null)
		{
			_prevMom = momVal;
			return;
		}

		// Momentum crosses above zero → buy
		if (_prevMom.Value <= 100m && momVal > 100m && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		// Momentum crosses below zero → sell
		else if (_prevMom.Value >= 100m && momVal < 100m && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}

		_prevMom = momVal;
	}
}