GitHub で見る

Vortex インジケーターのブレイクアウト戦略

この戦略は、MetaTrader エキスパート Vortex Indicator System.mq4 を StockSharp の高レベル API に移植します。当初のアイデアは 株式と商品のテクニカル分析 (2010 年 1 月) に掲載されており、Arm Breeze とクロスオーバーした Vortex インジケーターに依存しています。 akout注文はクロスオーバーローソク足の高値/安値で行われます。 StockSharp バージョンでは、同じ意思決定フローが維持されます。クロスオーバーにより、 反対の位置で、クロスオーバーバーの極限でブレイクアウトトリガーを作動させ、そのレベルをブレイクする次のローソク足が実行します。 市場注文。

仕組み

  1. 単一のキャンドル サブスクリプションが CandleType に従ってオープンされます。結果のストリームは 1 つの VortexIndicator インスタにバインドされます Bind を使用しているため、ストラテジーは常に完成したローソク足の同期された VI+ 値と VI- 値を受け取ります。
  2. インジケーターのウォームアップが完了すると、アルゴリズムは以前の VI 値を追跡して、同じクロスオーバー条件を検出します。 MQL エキスパートでの設定: VI+VI- の上を横切る、またはその逆、最後の 2 本の閉じたローソク足の間。
  3. セットアップフェーズ – 強気のクロスオーバーが検出されるとすぐに、開いているショートポジションは直ちに閉じられ、その高値は クロスオーバーキャンドルは保留中のロングトリガーになります。反対側のクロスオーバーは既存のロングポジションを決済し、安値を保管します。 そのバーをショートトリガーとして使用します。
  4. トリガー フェーズ – 後続の終了したローソク足ごとに、戦略は記録されたトリガー価格に触れたかどうかをチェックします (Hi ghPrice ≥ long trigger or LowPrice ≤ ショート トリガー)。そうであれば、残りの相手をフラットにするサイズの成行注文を送信します。 サイト公開(前の注文がまだ完了していない場合)し、TradeVolume で新しいポジションをオープンします。
  5. 命令が発火されると、対応するトリガーはクリアされます。ブレイクアウトが発生しない場合、セットアップは新しいクロスオーバーまでアクティブなままになります。 r はそれをオーバーライドします。
  6. イグジットはクロスオーバー ロジックのみに依存します。反対の信号は現在の位置を即座にフラットにし、新しい b を準備します。 reakout トリガー。MetaTrader 実装をミラーリングします。

信号

  • 強気の設定VI+ が直前の閉じたローソク足で VI- 以下であり、最後のローソク足でそれを上回る場合に発生します 最近のやつ。ロングトリガーはそのローソクの高値に設定されます。
  • 強気約定 – 高値がトリガーに達した次のローソク足が、TradeVolume (および任意の VO) を使用して成行買い注文を送信します。 未解決のショートポジションを閉じるには夜光が必要です)。
  • 弱気セットアップVI- が直前の閉じたローソク足で VI+ 以下であり、最後のローソク足でそれを上回る場合に発生します 最近のやつ。ショートトリガーはローソク足の安値に設定されます。
  • 弱気執行 – 安値がトリガーに接触した次のローソク足が、TradeVolume (プラス VO) を使用して成行売り注文を送信します。 オープンロングポジションをフラットにするために必要な夜光)。

パラメーター

パラメータ デフォルト 説明
VortexLength 14 Vortex インジケーターに適用される期間。
CandleType 1時間 ローソク足とインジケーターの更新に使用される時間枠。
TradeVolume 1 新規エントリーに使用される成行注文サイズ。

実装メモ

  • この戦略は、変換ガイドラインに準拠するために 終了 キャンドルにのみ反応します。バー内ブレイクアウトは、 保存されたトリガーを超えてローソク足が高値/安値で閉じるとすぐに。
  • 保留中のトリガーは OnStopped でクリアされるため、状態を残さずにインスタンスをクリーンに再起動できます。
  • ブレイクアウト注文を実行する際、反対のポジションを保持している場合、アルゴリズムはボリュームを増加させ、同じ利益を達成します。 新しい注文を開く前にアクティブな注文を閉じた MetaTrader のエキスパートとして影響を受けます。
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Vortex Indicator Breakout: Dual EMA crossover breakout with ATR stops.
/// </summary>
public class VortexIndicatorBreakoutStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastEmaLength;
	private readonly StrategyParam<int> _slowEmaLength;
	private readonly StrategyParam<int> _atrLength;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;

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

		_fastEmaLength = Param(nameof(FastEmaLength), 14)
			.SetDisplay("Fast EMA", "Fast EMA period.", "Indicators");

		_slowEmaLength = Param(nameof(SlowEmaLength), 28)
			.SetDisplay("Slow EMA", "Slow EMA period.", "Indicators");

		_atrLength = Param(nameof(AtrLength), 14)
			.SetDisplay("ATR Length", "ATR period.", "Indicators");
	}

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

	public int FastEmaLength
	{
		get => _fastEmaLength.Value;
		set => _fastEmaLength.Value = value;
	}

	public int SlowEmaLength
	{
		get => _slowEmaLength.Value;
		set => _slowEmaLength.Value = value;
	}

	public int AtrLength
	{
		get => _atrLength.Value;
		set => _atrLength.Value = value;
	}

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

		_prevFast = 0;
		_prevSlow = 0;
		_entryPrice = 0;
	}

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

		_prevFast = 0;
		_prevSlow = 0;
		_entryPrice = 0;

		var fastEma = new ExponentialMovingAverage { Length = FastEmaLength };
		var slowEma = new ExponentialMovingAverage { Length = SlowEmaLength };
		var atr = new AverageTrueRange { Length = AtrLength };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(fastEma, slowEma, atr, ProcessCandle)
			.Start();

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

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

		if (_prevFast == 0 || _prevSlow == 0 || atrVal <= 0)
		{
			_prevFast = fastVal;
			_prevSlow = slowVal;
			return;
		}

		var close = candle.ClosePrice;

		if (Position > 0)
		{
			if (fastVal < slowVal && _prevFast >= _prevSlow)
			{
				SellMarket();
				_entryPrice = 0;
			}
			else if (close <= _entryPrice - atrVal * 2m)
			{
				SellMarket();
				_entryPrice = 0;
			}
		}
		else if (Position < 0)
		{
			if (fastVal > slowVal && _prevFast <= _prevSlow)
			{
				BuyMarket();
				_entryPrice = 0;
			}
			else if (close >= _entryPrice + atrVal * 2m)
			{
				BuyMarket();
				_entryPrice = 0;
			}
		}

		if (Position == 0)
		{
			if (fastVal > slowVal && _prevFast <= _prevSlow)
			{
				_entryPrice = close;
				BuyMarket();
			}
			else if (fastVal < slowVal && _prevFast >= _prevSlow)
			{
				_entryPrice = close;
				SellMarket();
			}
		}

		_prevFast = fastVal;
		_prevSlow = slowVal;
	}
}