GitHub で見る

トレイラーv戦略

概要

TRAYLERv 戦略 は、MetaTrader 4 エキスパート アドバイザー TRAYLERv を直接変換したものです。元のコードはシグナルジェネレーターではなく、自動取引マネージャーとして機能しました。 Bill Williams フラクタルを使用して既存のポジションの保護注文を継続的に調整し、トレーダーが未処理の未決注文をクリーンアップできるようにしました。この StockSharp ポートは、注文管理とキャンドル サブスクリプションに高レベルの API を活用しながら、同じ動作を維持します。

この戦略は、それ自体でポジションをオープンしません。取引が手動または別の戦略によって作成されることを期待し、以下のロジックに従ってストップとテイクプロフィットを維持する仕事を引き継ぎます。すべてのコメントと構成名は従来の EA に従っており、経験豊富なユーザーが動作をすばやくマッピングできます。

取引ロジック

  1. 設定されたローソク足シリーズ (デフォルトでは 1 分間のローソク足) をサブスクライブし、完成した各バーを記録します。 5 本のローソク足が利用可能になるとフラクタルの高値と安値が検出され、標準の MT4 フラクタル定義が再現されます。
  2. 偶数分間に新しいローソク足が閉じるたびに、ストラテジーは現在のネットポジションをチェックします。
    • ロング ポジション: StopFractalDepth バー (デフォルトは 7) 以内の最新のダウン フラクタルを検索します。見つかった場合は、フラクタル安値から現在のスプレッドと 2 ポイント バッファーを差し引いた値の下に売りストップを配置または移動します。有効なフラクタルが存在しない場合は、ローソク足の 3 バー前の安値から 2 ポイントを引いた値を使用します。ロングポジションに利益があり、テイクプロフィットが有効になっている場合、TakeProfitFractalDepth バー(デフォルトは 21)以内の最新の上昇フラクタルを探し、MetaTrader の実装に一致するようにそのレベルよりわずかに低い売り制限を設定します。
    • ショート ポジション: トレーリング バイ ストップにアップ フラクタルを使用し、テイクプロフィット ターゲットにダウン フラクタルを使用するロジックを反映します。早期停止を避けるために、フラクタル高値の上にバッファが追加されます。
  3. DeleteAllPendingOrders が有効な場合、ストラテジーは確認できるすべてのアクティブな未決注文をキャンセルします。あるいは、DeleteOwnPendingOrders は、現在のシンボルに属する未決注文のみを削除します。どちらのオプションも、元の EA の手動クリーンアップ スイッチを複製します。
  4. オープンなポジションがない場合は、オーダーブックを整理するために、ストラテジーによって登録されたすべての保護注文がキャンセルされます。

リスク管理

  • 保護注文は成行注文の対応物 (SellStopBuyStopSellLimitBuyLimit) を使用して作成されます。保護注文のボリュームは常に絶対ネットポジションサイズと一致します。
  • トレーリングストップとテイクプロフィットはオプションです。テイクプロフィットパラメータを無効にすると、既存の指値注文は削除されますが、トレーリングロジックはそのまま残ります。
  • スプレッド情報は、利用可能な場合は最良の買値/売値ペアから取得されます。スプレッドを測定できない場合、コードは商品の最小価格増分に戻り、現在の価格で直接注文することを回避します。
  • すべての価格レベルは商品のティックサイズに正規化されているため、結果として得られる注文は為替要件に準拠します。

パラメーター

パラメータ 説明 デフォルト
OrderVolume 手動エントリに推奨されるデフォルトのボリューム。これは、元の EA との互換性のために保持されており、内部では使用されません。 0.1
DeleteAllPendingOrders true の場合、各ローソク足の後に接続上のすべてのアクティブな未決注文をキャンセルします。 false
DeleteOwnPendingOrders true の場合、現在のシンボルの未決注文のみをキャンセルします。 false
UseTakeProfit フラクタルベースのテイクプロフィット計算を有効にします。無効にすると、既存のテイクプロフィット注文はすべて削除されます。 true
EnableSound MT4 からの保存されたレガシー フラグ。完全を期すために提供されていますが、StockSharp では使用されません。 true
ShowCommentary MT4のオンチャート解説に相当するレガシースイッチ。これは設定画面で使用できますが、ポートには影響しません。 true
StopFractalDepth トレーリングストップのフラクタルを見つけるために検査されたバーの数。 7
TakeProfitFractalDepth テイクプロフィットのフラクタルを見つけるために検査されたバーの数。 21
CandleType プライマリローソク足シリーズに使用されるデータタイプ。デフォルトの時間枠は 1 分です。 1 minute 時間枠

実装メモ

  • この戦略では、高レベルの SubscribeCandles().Bind(...) ワークフローを使用し、完了したローソク足のみを処理し、MT4 ティックベースのループをミラーリングしながら、時期尚早な更新を回避します。
  • フラクタル検出は、ローソク足のスナップショットのローリング リストを使用して手動で実装されます。これにより、追加の StockSharp インジケーターに依存せずに、MT4 iFractals インジケーターの動作が再現されます。
  • 注文価格は最も近い有効なティックに四捨五入され、取引量は取引所の互換性を保証するために VolumeStepMinVolume、および MaxVolume の制約を尊重します。
  • Python の翻訳は含まれていません。 PY ディレクトリは、変換ガイドラインの要件に従って、意図的に存在しません。
using System;

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

namespace StockSharp.Samples.Strategies;

public class TraylerStrategy : Strategy
{
	private readonly StrategyParam<int> _emaPeriod;
	private readonly StrategyParam<int> _cooldownCandles;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevClose;
	private decimal _prevEma;
	private bool _hasPrev;
	private int _cooldownRemaining;

	public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
	public int CooldownCandles { get => _cooldownCandles.Value; set => _cooldownCandles.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public TraylerStrategy()
	{
		_emaPeriod = Param(nameof(EmaPeriod), 50).SetDisplay("EMA Period", "EMA lookback", "Indicators");
		_cooldownCandles = Param(nameof(CooldownCandles), 200).SetDisplay("Cooldown", "Candles between signals", "General");
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame()).SetDisplay("Candle Type", "Candle timeframe", "General");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevClose = default;
		_prevEma = default;
		_hasPrev = default;
		_cooldownRemaining = default;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevClose = 0;
		_prevEma = 0;
		_hasPrev = false;
		_cooldownRemaining = 0;

		var ema = new ExponentialMovingAverage { Length = EmaPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(ema, ProcessCandle).Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal ema)
	{
		if (candle.State != CandleStates.Finished) return;
		var close = candle.ClosePrice;
		if (!_hasPrev) { _prevClose = close; _prevEma = ema; _hasPrev = true; return; }

		if (_cooldownRemaining > 0)
		{
			_cooldownRemaining--;
			_prevClose = close;
			_prevEma = ema;
			return;
		}

		if (_prevClose <= _prevEma && close > ema && Position <= 0)
		{
			if (Position < 0) BuyMarket();
			BuyMarket();
			_cooldownRemaining = CooldownCandles;
		}
		else if (_prevClose >= _prevEma && close < ema && Position >= 0)
		{
			if (Position > 0) SellMarket();
			SellMarket();
			_cooldownRemaining = CooldownCandles;
		}
		_prevClose = close;
		_prevEma = ema;
	}
}