GitHub で見る

N Candles v3戦略

概要

この戦略は最新の完成ローソク足をスキャンし、最後のN本のバーが同じ方向(すべて強気またはすべて弱気)を共有するシーケンスを探します。そのような連続が現れると、一度に開けるポジション数の上限を尊重しながらシーケンスの方向にエントリーします。この実装は元のMetaTrader 5エキスパートアドバイザーをStockSharpの高レベルAPIに移行したものです。

トレードロジック

  • エンジンは設定されたローソク足タイプにサブスクライブし、完成したバーのみを処理する。
  • 完成した各ローソク足のボディ方向を評価する:強気、弱気、または中立(十字足)。
  • 十字足は内部カウンターをリセットする。それ以外の場合、現在のローソク足が前のものと同じ方向を持つとカウンターが増加する。カウンターがIdentical Candlesパラメーターに達すると、戦略は新しい注文を発行する。
  • ロングシグナルはまず既存のショートエクスポージャーを閉じ、買いの総ボリュームがMax Positions * Volumeを下回る間はロングユニットを追加する。
  • ショートシグナルは弱気連続に対して対称的に機能する。

リスク管理

  • 各約定後、戦略はアクティブポジションの平均エントリー価格に基づいて新しい保護的なストップロスとテイクプロフィット注文を設定する。
  • 距離は銘柄の価格刻みで測定される:Take Profit Pointsは刻みを掛けてエントリーより上(ロング)または下(ショート)のターゲットを計算し、Stop Loss Pointsは保護ストップに同じ考えを使用する。
  • 段階的なトレーリングストップは、価格がポジションに有利な方向にTrailing Stop Points分動いた後、最初のストップと置き換えることができる。ストップは価格が前のトレーリングレベルを少なくともTrailing Step Points超えて前進した場合にのみ移動する。

パラメーター

  • Candle Type – 分析するための時間軸またはローソク足ソース。
  • Identical Candles – エントリーを発動するために必要な同じ方向の連続ローソク足の数。
  • Volume – 銘柄単位での各新規エントリーの注文サイズ。
  • Max Positions – 同じ方向に同時にオープンできるエントリーユニットの最大数。
  • Take Profit Points – 銘柄価格刻みの倍数でのテイクプロフィット距離。
  • Stop Loss Points – 銘柄価格刻みの倍数でのストップロス距離。
  • Trailing Stop Points – トレーリングストップを有効化・維持するために使用する現在価格からの距離。トレーリングを無効にするにはゼロに設定。
  • Trailing Step Points – トレーリングストップを再び移動させる前にカバーしなければならない追加の価格刻み距離。

追加の注意事項

  • 戦略はネッティング方式で動作する:反対方向のシグナルが現れると、新しいポジションを追加する前に反対側の既存エクスポージャーが閉じられる。
  • オープンポジションサイズと同期を保つため、すべての保護注文は各約定後に再作成される。
  • 銘柄が非ゼロのPriceStepを提供していることを確認する;そうでない場合はデフォルトのステップ値1が使用される。
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 a sequence of candles with the same direction.
/// Uses StartProtection for take profit and stop loss management.
/// </summary>
public class NCandlesV3Strategy : Strategy
{
	private readonly StrategyParam<int> _identicalCandles;
	private readonly StrategyParam<decimal> _takeProfitPoints;
	private readonly StrategyParam<decimal> _stopLossPoints;
	private readonly StrategyParam<DataType> _candleType;

	private int _sequenceDirection;
	private int _sequenceCount;

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

	/// <summary>
	/// Number of consecutive candles required before entering a trade.
	/// </summary>
	public int IdenticalCandles
	{
		get => _identicalCandles.Value;
		set => _identicalCandles.Value = value;
	}

	/// <summary>
	/// Take profit distance expressed in price steps.
	/// </summary>
	public decimal TakeProfitPoints
	{
		get => _takeProfitPoints.Value;
		set => _takeProfitPoints.Value = value;
	}

	/// <summary>
	/// Stop loss distance expressed in price steps.
	/// </summary>
	public decimal StopLossPoints
	{
		get => _stopLossPoints.Value;
		set => _stopLossPoints.Value = value;
	}

	/// <summary>
	/// Initializes a new instance of the strategy.
	/// </summary>
	public NCandlesV3Strategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles analysed by the strategy", "General");

		_identicalCandles = Param(nameof(IdenticalCandles), 3)
			.SetRange(1, 10)
			.SetDisplay("Identical Candles", "Required number of equal candles", "Pattern");

		_takeProfitPoints = Param(nameof(TakeProfitPoints), 50m)
			.SetRange(0m, 500m)
			.SetDisplay("Take Profit Points", "Take profit distance in price steps", "Risk Management");

		_stopLossPoints = Param(nameof(StopLossPoints), 50m)
			.SetRange(0m, 500m)
			.SetDisplay("Stop Loss Points", "Stop loss distance in price steps", "Risk Management");
	}

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

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

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

		_sequenceDirection = 0;
		_sequenceCount = 0;

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

		var step = Security?.PriceStep ?? 1m;
		var takeProfit = TakeProfitPoints > 0 ? TakeProfitPoints * step : (decimal?)null;
		var stopLoss = StopLossPoints > 0 ? StopLossPoints * step : (decimal?)null;

		if (takeProfit != null || stopLoss != null)
			StartProtection(takeProfit ?? 0, stopLoss ?? 0);

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

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

		var direction = GetDirection(candle);
		UpdateSequence(direction);

		if (_sequenceCount < IdenticalCandles)
			return;

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

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

	private void UpdateSequence(int direction)
	{
		if (direction == 0)
		{
			_sequenceDirection = 0;
			_sequenceCount = 0;
			return;
		}

		if (_sequenceDirection == direction)
		{
			_sequenceCount++;
		}
		else
		{
			_sequenceDirection = direction;
			_sequenceCount = 1;
		}
	}
}