GitHub で見る

N Candles シーケンス・エントリー戦略

コンセプト

N Candles戦略は、すべて同じ方向に閉じる連続したローソク足をマーケットでスキャンします。設定可能な数の強気または弱気のローソク足が現れると、戦略はシーケンスの方向にエントリーします。この実装はMetaTrader「N Candles v4」エキスパートアドバイザーの直接変換であり、StockSharpの高レベルAPIの中でリスクコントロール、pipsベースの設定、オプションのトレーリングストップ動作を維持します。

エントリー条件

  • 完成した各ローソク足を一度評価する。
  • 上方に閉じるローソク足は強気としてカウント、下方に閉じるローソク足は弱気としてカウント、十字足はシーケンスをリセットする。
  • ConsecutiveCandles本の強気(または弱気)ローソク足が連続して現れた場合、戦略は動きの方向に成行注文を送信する。
  • 選択されたAccountingModeに応じてヘッジスタイルのスタッキングまたはネッティングスタイルのエクスポージャー上限が適用される。

エグジット管理

  • StopLossPipsTakeProfitPipsはアクティブポジションの平均エントリー価格からpipsで測定した静的な出口レベルを定義する。
  • TrailingStopPipsがゼロより大きい場合、ストップレベルは最も有利な価格に追随する:
    • 固定ストップが存在しない場合(例えばStopLossPipsがゼロの場合)、戦略はブレイクイーブンストップを設定する前に価格がトレードに有利な方向にTrailingStopPips動くまで待つ。
    • ストップが設定されると、価格とストップ間の距離がTrailingStopPips + TrailingStepPipsを超えるとき市場に向かって移動する。
  • 保護レベルはポジションサイズが変化するたびに再計算され、各完成ローソク足に対してチェックされ、ストップロスまたはテイクプロフィットイベントが即座にトレードを閉じることを保証する。

パラメーター

名前 説明 デフォルト値
ConsecutiveCandles エントリーを発動するために必要な同一ローソク足の数。 3
TakeProfitPips pipsでのテイクプロフィット距離。ターゲットを無効にするにはゼロを使用。 50
StopLossPips pipsでのストップロス距離。ストップを無効にするにはゼロを使用。 50
TrailingStopPips pipsでのトレーリングストップ距離。ゼロはトレーリングを無効にする。 10
TrailingStepPips トレーリングストップが前進する前に必要な追加の動き。 4
MaxPositionsPerDirection ヘッジングでの方向ごとのスタックされたエントリーの最大数。 2
MaxNetVolume ネッティングモードで動作するときの最大絶対ネットポジションサイズ。 2
AccountingMode Netting(ボリューム上限)とHedging(エントリー数上限)を切り替え。 Netting
CandleType パターン検出に使用するローソク足集計。 1分足

すべてのpipsベースのパラメーターは銘柄のティックサイズを使用して価格オフセットに変換されます。銘柄に3または5の小数点桁数がある場合、MetaTraderの定義を反映するためにpipサイズは10倍に拡張されます。

実装上の注意

  • 戦略はStockSharpの高レベルローソク足サブスクリプション(SubscribeCandles)に依存し、手動の履歴バッファを避けます。
  • 保護ロジックはオリジナルのトレーリング動作をエミュレートするためにエントリー後に見られた最高値(ロング用)または最安値(ショート用)を追跡します。
  • ポジション制限は戦略のベースVolumeに自動的に適応します。Volumeを増加させるとストップとテイクプロフィット注文サイズが比例して拡大します。
  • 保護出口(ストップまたはテイクプロフィット)がポジションを閉じるたびにログメッセージが発せられ、バックテスト中の明確さを提供します。

使用上のヒント

  • 方向ごとに複数のチケットを許可するプラットフォームをシミュレートするときはHedgingモードを選択し、単一ポジション口座を反映するにはNettingにとどまる。
  • 市場がTrailingStopPips前進するたびに移動するクラシックなトレーリングストップにはTrailingStepPipsをゼロに設定する。
  • 出口は完成したローソク足で評価されるため、バー内精度が重要な場合はより短いローソク足間隔を検討する。
using System;
using System.Collections.Generic;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Strategy that opens positions after detecting N identical candles in a row.
/// Enters in the direction of the candle streak.
/// </summary>
public class NCandlesSequenceStrategy : Strategy
{
	private readonly StrategyParam<int> _consecutiveCandles;
	private readonly StrategyParam<DataType> _candleType;

	private int _consecutiveDirection;
	private int _consecutiveCount;

	/// <summary>
	/// Number of identical candles required before entering a trade.
	/// </summary>
	public int ConsecutiveCandles
	{
		get => _consecutiveCandles.Value;
		set => _consecutiveCandles.Value = value;
	}

	/// <summary>
	/// Candle type used for pattern detection.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Initializes a new instance of the strategy.
	/// </summary>
	public NCandlesSequenceStrategy()
	{
		_consecutiveCandles = Param(nameof(ConsecutiveCandles), 3)
			.SetGreaterThanZero()
			.SetDisplay("Consecutive Candles", "Number of identical candles in a row", "Entry")
			.SetOptimize(2, 6, 1);

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

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_consecutiveDirection = 0;
		_consecutiveCount = 0;
	}

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

		_consecutiveDirection = 0;
		_consecutiveCount = 0;

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

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

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

		var direction = GetCandleDirection(candle);

		if (direction == 0)
		{
			_consecutiveDirection = 0;
			_consecutiveCount = 0;
			return;
		}

		if (direction == _consecutiveDirection)
		{
			_consecutiveCount++;
		}
		else
		{
			_consecutiveDirection = direction;
			_consecutiveCount = 1;
		}

		if (_consecutiveCount < ConsecutiveCandles)
			return;

		if (direction > 0 && Position <= 0)
		{
			BuyMarket();
		}
		else if (direction < 0 && Position >= 0)
		{
			SellMarket();
		}
	}

	private static int GetCandleDirection(ICandleMessage candle)
	{
		if (candle.ClosePrice > candle.OpenPrice)
			return 1;
		if (candle.ClosePrice < candle.OpenPrice)
			return -1;
		return 0;
	}
}