GitHub で見る

Three Breaky戦略

Three Breaky戦略は、MetaTrader 4エキスパートアドバイザーThreeBreaky_v1.mq4の完全変換版です。StockSharpバージョンは元のブレイクアウトサブシステムのトリオを維持し、ローソク足ベースのロジックを高レベルAPIに変換し、各モジュールに明確なポジション管理を追加します。戦略は単一の設定可能な時間軸で動作し、他のサブシステムに影響を与えずに任意のサブシステムを有効または無効にできます。

取引モジュール

  1. システム1 – ATR拡張ブレイクアウト

    • 前のローソク足のみを使用します。
    • 前のローソク足が強気で、その高値-安値のレンジが72期間の平均真のレンジの4倍を超える場合にロングします。
    • 前のローソク足が弱気で同じレンジ条件が満たされる場合にショートします。
  2. システム2 – Ichimoku雲フリップ

    • デフォルト期間9/26/52の雲の境界(先行スパンAと先行スパンB)を観察します。
    • 2本前のローソク足が両スパンの下で閉じ、最新のローソク足が両スパンの上で閉じた場合(雲を通じた強気フリップ)にロングシグナルが発火します。
    • 2本前のローソク足が両スパンの上で閉じ、最新のローソク足が両スパンの下で閉じた場合にショートシグナルが発火します。
  3. システム3 – 例外的なボディブレイクアウト

    • 過去20本の完了したローソク足のボディサイズを追跡します。
    • ロングセットアップは、前のローソク足が強気で、そのボディが20本のローソク足履歴で観察された最大ボディの3倍以上であることを必要とします。
    • ショートセットアップは弱気ボディに対して条件をミラーリングします。

各サブシステムは専用の仮想ポジションを取引します。注文タイムスタンプは、元のbuyTagsellTagロジックと同様に、モジュールが1本のローソク足あたり最大1回の取引を開けるように保存されます。

出口ロジック

  • パラボリックSAR反転: すべてのオープンポジションはパラボリックSAR(0.005/0.2)出口を共有します。価格が前2本のローソク足間でSARを超えると、影響を受けたポジションが決済されます。
  • リスク管理: オプションのストップロスとテイクプロフィット距離(pips)は各完了したローソク足で評価されます。設定されたしきい値を超えると、関連するポジションがすぐに決済されます。

使用インジケーター

  • 平均真のレンジ(期間72)による平均ボラティリティベースライン。
  • Ichimoku Kinko Hyo(9、26、52)による雲フリップフィルター。
  • パラボリックSAR(加速0.005、最大0.2)による出口とTrailingロジック。
  • MQL最大ボディ比較を再現するためのローリングボディサイズバッファ(20ローソク足)。

パラメーター

パラメーター 説明
UseSystem1 ATR拡張ブレイクアウトモジュールを有効にします。
UseSystem2 Ichimoku雲フリップモジュールを有効にします。
UseSystem3 大きなボディブレイクアウトモジュールを有効にします。
OrderVolume 任意のモジュールが生成する各市場注文に使用するボリューム。
StopLossPips pipsでの保護ストップ距離。ゼロに設定すると無効になります。
TakeProfitPips pipsでのテイクプロフィット距離。ゼロに設定すると無効になります。
CandleType 作業ローソク足の時間軸(デフォルト1時間)。

ワークフロー概要

  1. 設定されたローソク足シリーズをサブスクライブし、完成したローソク足のみを処理します。
  2. ATR、Ichimoku、パラボリックSARインジケーターをローリングボディ履歴とともに更新します。
  3. ストップ、ターゲット、またはパラボリックSAR反転に達したポジションを決済します。
  4. 取引が許可されている場合、各サブシステムを独立して評価し、それぞれの条件がすべて満たされたら市場注文を発行します。
  5. 次のローソク足が元のMQL実装と同じ履歴値にアクセスできるように、最新のインジケーター出力を保存します。

注意事項

  • 戦略は計器価格ステップに基づいてpip値を仮定します。5桁と3桁のFX価格はそれぞれ4桁と2桁小数のpipサイズに正規化されます。
  • サブシステムは同時に実行できます。各サブシステムは独自のエントリー価格、ポジション方向、最新シグナルタイムスタンプを維持し、ソースEAのMagicNumber+N分離を反映します。
  • StockSharp実装は、ローソク足開始時刻を使用して単一バー内の重複注文をブロックすることで「1バーに1回」の実行パターンを維持します。
using System;
using System.Collections.Generic;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

public class ThreeBreakyStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private ExponentialMovingAverage _fast;
	private ExponentialMovingAverage _slow;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;
	private int _cooldown;

	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
	public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }

	public ThreeBreakyStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 15).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
		_slowPeriod = Param(nameof(SlowPeriod), 60).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
		_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
		_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
	}

	protected override void OnReseted()
	{
		base.OnReseted();
		_fast = null; _slow = null;
		_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_fast = new ExponentialMovingAverage { Length = FastPeriod };
		_slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
		subscription.Bind(_fast, _slow, ProcessCandle);
		subscription.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
		if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }

		var close = candle.ClosePrice;
		var step = Security?.PriceStep ?? 1m;

		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}
		else if (Position < 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
		}

		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }

		_prevFast = fastValue; _prevSlow = slowValue;
	}
}