GitHub で見る

前ローソク足ブレイクアウト戦略

この戦略はSoubra2003によるMetaTraderのクラシックな「BreakOut」エキスパートアドバイザーを再現します。最近完成した ローソク足の高値と安値を監視し、現在の終値がそれらの参照レベルを突破するたびに反応します。アプローチは完全に 対称的です:強気のブレイクアウトでロングポジションを開き、弱気のブレイクダウンでショートポジションを開きます。 価格単位で表されるオプションのストップロスとテイクプロフィットのバッファにより、ユーザーはリスクを制限したり 利益を確保したりできます。

概要

  • 単一のローソク足シリーズをサブスクライブします(デフォルトは1時間時間軸)。
  • ブレイクアウトトリガーとして機能させるために、前のローソク足の高値と安値を保存します。
  • 元のティックベースのロジックをバー内データに依存せずに反映するため、ローソク足のクローズ時のみ取引します。
  • ロングとショートの両方の取引をサポートし、ブレイクアウト条件がアクティブでない場合は常にフラットを維持します。

取引ルール

  1. ブレイクアウトエントリー / リバーサル
    • 現在の完成したローソク足のクローズが前のローソク足の高値を厳密に上回る場合:
      • 開いているショートポジションは成行で決済されます。
      • すぐ後に新しいロングポジションが開かれます(リバーサルは同じローソク足処理ステップ内で発生します)。
    • クローズが前のローソク足の安値を厳密に下回る場合:
      • 開いているロングポジションは成行で決済されます。
      • その後、新しいショートポジションが開かれます。
  2. 保護的なエグジット(オプション)
    • ストップロスオフセットが設定されている場合(> 0)、戦略はクローズがエントリー価格よりoffset単位下落すると ロングを終了し、またはクローズがエントリー価格よりoffset単位上昇するとショートを終了します。
    • テイクプロフィットオフセットが設定されている場合(> 0)、戦略はクローズがエントリー価格よりoffset単位上昇 するとロングを終了し、またはクローズがエントリー価格よりoffset単位下落するとショートを終了します。
  3. 状態リセット
    • 各ローソク足が処理された後、最新の高値と安値が新しいブレイクアウト参照レベルになります。

パラメーター

  • Candle Type – サブスクリプションに使用されるデータタイプ(デフォルトは時間足時間軸)。元のエキスパートに対して MetaTraderで使用されたチャートと一致するバーサイズに設定してください。
  • Stop Loss – エントリー価格と保護的なストップの間の絶対価格単位の距離。ストップロス処理を無効にするには0のままにしてください。
  • Take Profit – エントリー価格と利益目標の間の絶対価格単位の距離。テイクプロフィット処理を無効にするには0のままにしてください。

注意事項

  • ストップロスとテイクプロフィットの計算はローソク足のクローズ価格で実行されます。元のMQL4バージョンはSL/TPレベルを 注文に静的に付加しました。StockSharpではしきい値が満たされると成行注文を送信することでエグジットをシミュレートします。
  • オフセットを設定する際はインストゥルメント固有の価格増分を使用してください。例えば、インストゥルメントのティックサイズが 0.01で20ティックのストップが欲しい場合は、ストップロスパラメーターを0.20に設定します。
  • ロジックは常に直前のローソク足を参照するため、ブレイクアウトが意味を持つトレンドインストゥルメントや高ボラティリティ セッション中に最も効果的です。

出典

using System;
using System.Linq;
using System.Collections.Generic;

using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Breakout strategy that trades when the close crosses the previous candle's high or low.
/// Ported from the BreakOut.mq4 expert by Soubra2003.
/// </summary>
public class PreviousCandleBreakoutStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<decimal> _stopLossOffset;
	private readonly StrategyParam<decimal> _takeProfitOffset;

	private decimal? _previousHigh;
	private decimal? _previousLow;
	private decimal _entryPrice;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public decimal StopLossOffset { get => _stopLossOffset.Value; set => _stopLossOffset.Value = value; }
	public decimal TakeProfitOffset { get => _takeProfitOffset.Value; set => _takeProfitOffset.Value = value; }

	public PreviousCandleBreakoutStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromDays(1).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe for candle subscription", "General");

		_stopLossOffset = Param(nameof(StopLossOffset), 1000m)
			.SetDisplay("Stop Loss", "Price distance for the stop-loss. Set 0 to disable.", "Risk")
			;

		_takeProfitOffset = Param(nameof(TakeProfitOffset), 1500m)
			.SetDisplay("Take Profit", "Price distance for the take-profit. Set 0 to disable.", "Risk")
			;
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_previousHigh = null;
		_previousLow = null;
		_entryPrice = 0m;
	}

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

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

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

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

		if (_previousHigh is null || _previousLow is null)
		{
			// Store the first finished candle to obtain reference high/low levels.
			_previousHigh = candle.HighPrice;
			_previousLow = candle.LowPrice;
			return;
		}

		var previousHigh = _previousHigh.Value;
		var previousLow = _previousLow.Value;
		var close = candle.ClosePrice;

		var breakoutAbove = close > previousHigh;
		var breakoutBelow = close < previousLow;

		// Manage protective exits while a position is open.
		if (Position > 0)
		{
			if (StopLossOffset > 0m && close <= _entryPrice - StopLossOffset)
			{
				if (Position > 0) SellMarket(); else if (Position < 0) BuyMarket();
				_entryPrice = 0m;
			}
			else if (TakeProfitOffset > 0m && close >= _entryPrice + TakeProfitOffset)
			{
				if (Position > 0) SellMarket(); else if (Position < 0) BuyMarket();
				_entryPrice = 0m;
			}
		}
		else if (Position < 0)
		{
			if (StopLossOffset > 0m && close >= _entryPrice + StopLossOffset)
			{
				if (Position > 0) SellMarket(); else if (Position < 0) BuyMarket();
				_entryPrice = 0m;
			}
			else if (TakeProfitOffset > 0m && close <= _entryPrice - TakeProfitOffset)
			{
				if (Position > 0) SellMarket(); else if (Position < 0) BuyMarket();
				_entryPrice = 0m;
			}
		}

		// Breakout above the previous high opens or reverses into a long position.
		if (breakoutAbove)
		{
			if (Position < 0)
			{
				if (Position > 0) SellMarket(); else if (Position < 0) BuyMarket();
				_entryPrice = 0m;
			}

			if (Position <= 0)
			{
				BuyMarket();
				_entryPrice = close;
			}
		}
		else if (breakoutBelow)
		{
			// Breakout below the previous low opens or reverses into a short position.
			if (Position > 0)
			{
				if (Position > 0) SellMarket(); else if (Position < 0) BuyMarket();
				_entryPrice = 0m;
			}

			if (Position >= 0)
			{
				SellMarket();
				_entryPrice = close;
			}
		}

		_previousHigh = candle.HighPrice;
		_previousLow = candle.LowPrice;
	}
}