GitHub で見る

Exp Highs Lows Signal戦略

概要

Exp Highs Lows Signal は MetaTrader 5 エキスパートアドバイザー Exp_HighsLowsSignal の直接ポートです。この戦略は、より高い高値とより高い安値(強気シーケンス)またはより低い高値とより低い安値(弱気シーケンス)を印刷する設定可能な数の連続したローソク足を検索するパターン検出器に依存します。シーケンスが見つかると、戦略は設定された数の閉じたバーだけ反応を遅らせ、逆のエクスポージャーを閉じ、オプションで検出された方向のポジションを開きます。保護ストップは、元のロボットが使用するポイントベースのマネーマネジメントを反映するために価格ステップで表現されます。

戦略ロジック

Highs/Lows シーケンス検出器

  • 検出器は選択した時間軸で各完成したローソク足を評価します。
  • 強気シグナルは、現在の高値と現在の安値の両方が前のバーより厳密に大きい SequenceLength 個の連続した比較を必要とします。
  • 弱気シグナルは、現在の高値と現在の安値の両方が前のバーより厳密に小さい SequenceLength 個の連続した比較を必要とします。
  • シグナルはキューに入れられ、MQL 実装の SignalBar 設定に合わせて SignalBarDelay 個の閉じたローソク足の後に解放されます。

エントリールール

  • ロングエントリー
    • 強気シーケンスがアクティブになり AllowLongEntry が有効な場合にトリガーされます。
    • 既存のショートポジションがまず閉じられ(AllowShortExit が真の場合)、次にショートをカバーして希望のロングサイズを確立するために OrderVolume + |Position| の出来高で成行買い注文が送られます。
  • ショートエントリー
    • 弱気シーケンスがアクティブになり AllowShortEntry が有効な場合にトリガーされます。
    • 既存のロングポジションがまず閉じられ(AllowLongExit が真の場合)、次にロングをカバーして希望のショートサイズを確立するために OrderVolume + |Position| の出来高で成行売り注文が送られます。

エグジットルール

  • 強気シーケンスは常にオープンショートを閉じるために AllowShortExit を要求します。
  • 弱気シーケンスは常にオープンロングを閉じるために AllowLongExit を要求します。
  • 関連するフラグが無効な場合、逆のエクスポージャーはそのまま残り、ユーザーは一方向のみで取引したり、検出器を「アラートのみ」モードで実行したりできます。

リスク管理

  • StopLossTicksTakeProfitTicks は価格ステップ(ポイント)の距離を表します。値 0 は対応する保護注文を無効にし、元の EA の動作を再現します。
  • StartProtection はこれらの距離を絶対価格オフセットに変換し、すべての成行エントリーが自動的に対応するストップロスとテイクプロフィット注文を受け取るようにします。

パラメーター

  • OrderVolume – 新しい取引が開かれるときに使用する基本注文出来高。
  • AllowLongEntry / AllowShortEntry – それぞれのシグナルでロングまたはショートエントリーを有効にするトグル。
  • AllowLongExit / AllowShortExit – 逆張りシグナルが現れたときに戦略が逆のポジションを均すことを許可するトグル。
  • StopLossTicks / TakeProfitTicks – 価格ステップ単位の保護距離;無効にするには 0 に設定。
  • SequenceLength – 強気または弱気シーケンスを認定するために必要な連続した比較の数(MT5 の HowManyCandles に相当)。
  • SignalBarDelay – シグナルに基づいて行動する前に待つ閉じたローソク足の数(SignalBar 入力に相当)。
  • CandleType – Highs/Lows 検出器の構築に使用する時間軸(デフォルト: 4 時間足ローソク)。

追加ノート

  • 戦略は検出器に必要な最小限のローソク足履歴のみを保存し、MT5 カスタムインジケーターと同一の動作を維持します。
  • すべての注文管理が StartProtection を通じて行われるため、バックテストとライブ取引は追加コードなしに自動的に対応するストップとテイクプロフィット注文を受け取ります。
  • 対応する Allow フラグを無効にして、戦略を方向性フィルターまたは純粋なシグナリングツールに変換します。
  • Python 翻訳は提供されていません;このパッケージでは C# バージョンのみが利用可能です。
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;

/// <summary>
/// Highs Lows Signal strategy using EMA crossover.
/// Buys when fast EMA crosses above slow EMA, sells on reverse.
/// </summary>
public class ExpHighsLowsSignalStrategy : 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 ExpHighsLowsSignalStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
		_slowPeriod = Param(nameof(SlowPeriod), 50).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;
	}
}