GitHub で見る

FTトレンドフォロワー

概要

FT トレンド フォロワーは、MetaTrader 4 エキスパート アドバイザー FT_TrendFollower.mq4 の StockSharp 移植です。この戦略は、Guppy Multiple Moving Average (GMMA) ファンとラゲール発振器トリガー、高速/低速の EMA クロスオーバー、および MACD フィルターをスタックすることで、中期的なトレンドに乗ります。エントリーは、市場がGMMAバンドルに落ち込み、ラゲール極端から反発し、GMMAラインの大部分が再び取引方向に傾いた後にのみ発動します。利益管理はオリジナルの EA を反映しており、オプションのスイングベースのストップ、固定距離のストップ、および毎日のピボット レベルまたはチャネル平均によって駆動される 3 つの相互に排他的な段階的出口モジュールです。

戦略ロジック

GMMA 構造とトレンド検出

  • GMMA ファンは StartGmmaPeriod から EndGmmaPeriod までの範囲にあります。ピリオドは、それぞれが BandsPerGroup 行の 5 つのグループに分散され、元の CountLine ロジックを複製します。
  • トレンドの方向は、より遅い GMMA グループ (最後からのインデックス CountLine + CountLine) とより速い長期グループ (最後からのインデックス CountLine) を比較します。長期平均の上昇は上昇傾向を定義します。下落するものは下降トレンドを定義します。
  • 傾きの確認では、前のバーと比較して短期、中期、長期の GMMA ラインが何本増加または減少したかをカウントします。取引では、MetaTrader の controlvverh/controlvverhS のしきい値を模倣して、上り (または下り) スロープ数が合計 GMMA ラインの半分を超える必要があります。

シグナルプライミング

  • クローズリセット – 前のローソク足が最も遅いGMMAラインの下で閉じると、長いモジュールがアームされます。最も遅いラインを超えて閉じると、短いモジュールがアームされます。最速の GMMA より上 (または下) に戻ると、元の CloseOk ロジックと同様に、アーミング フラグがクリアされます。
  • ラゲール トリガー – ラゲール フィルター (LaguerreGamma) は、ローソク足がまだ長期 GMMA を尊重している間に、まず LaguerreOversold を下回るか (長期セットアップ)、または LaguerreOverbought を上回る (短期セットアップ) 必要があります。発振器がしきい値を通って後退した後でのみ、エントリーが発火できます。
  • EMA クロスオーバー – 高速の EMA (FastSignalLength) は低速の EMA (SlowSignalLength) を下回って長いモジュールを準備し、その後、その上にクロスしてエントリーを解放する必要があります。ショートは不平等を逆転させます。
  • MACD フィルター – MACD メインライン (EA の 5/35/5) は、ロングの場合は正、ショートの場合は負でなければなりません。

エントリールール

ロング取引は次の場合に実行されます。

  1. トレンド検出により上昇傾向が報告され、GMMA の傾き投票が利用可能なラインの半分を超えています。
  2. ラゲール トリガーは以前に準備されており、現在の値は LaguerreOversold を超えて戻ります。
  3. 高速の EMA は、以前は低速の EMA を下回っていましたが、上回っています。
  4. MACD はゼロより大きいです。

ショートエントリーには、オシレーターが LaguerreOverbought を下回り、MACD がマイナスになる対称条件が必要です。既存のポジションを反転する場合、注文サイズは自動的に前のエクスポージャーをオフセットするため、最終的なネットポジションは Volume と等しくなります。

リスク管理と出口

  • ストップ – 前のローソク足より SwingStopPips ポイント下(上)に位置するスイング ストップ(UseSwingStop)、または FixedStopPips ポイントオフセットされた固定距離ストップ(UseFixedStop)のいずれかを選択します。両方を同時に有効にすると、戦略は開始時に中止され、EA 検証ルールが再現されます。
  • ピボット終了モジュール (終了) – 有効にすると、価格が前日の未実現利益のある R1/S1 ピボットを超えると、最初の部分的な終値 (Volume の 50%) がトリガーされます。残りは、ハル MA が有効な値を生成し、MetaTrader からの hma1 バッファ チェックと一致するとすぐに閉じます。
  • ピボット範囲終了モジュール (Quit1) – 最初の部分的なクローズは依然として R1/S1 で発生します。取引が利益を上げ続けると、残りは R2/S2 で終了します。
  • チャネル終了モジュール (Quit2) – 最初の部分的なクローズが R1/S1 で発生します。この戦略は、ローソク足がロングの場合は安値の SMA チャネル (ChannelPeriod) を下回るか、ショートの場合は高値の SMA チャネルを上回って再び開くと、残りを閉じます。これは元のボラティリティ フィルターを反映しています。

EA のパラメータ検証と同様に、一度にアクティブにできる終了モジュールは 1 つだけです。

パラメーター

  • 出来高 – 新規取引の注文サイズ。
  • StartGmmaPeriod / EndGmmaPeriod – GMMA ファンの境界。
  • BandsPerGroup – グループごとにサンプリングされた GMMA ラインの数 (MT4 では CountLine)。
  • FastSignalLength / SlowSignalLength – クロスオーバー確認に使用される EMA の長さ。
  • TradeShift – 互換性のために保持されています。実装は完成したローソク足で動作するため、0 または 1 以外の値は拒否されます。
  • UseSwingStop / SwingStopPips – スイングベースの保護停止を有効にして構成します。
  • UseFixedStop /FixedStopPips – 価格ポイントで測定される固定距離ストップを有効にします。
  • EnablePivotExit / EnablePivotRangeExit / EnableChannelExit – 相互に排他的な段階的終了モジュール。
  • LaguerreOverSold / LaguerreOverbought / LaguerreGamma – ラゲール トリガーしきい値と平滑化係数。
  • HmaPeriod – ピボット出口モジュールによって使用される船体の MA 長さ。
  • ChannelPeriod – Quit2 の高/低 SMA チャネルの長さ。
  • CandleType – 戦略の計算を行う時間枠 (デフォルト: 1 時間のローソク足)。

追加の注意事項

  • 日次ピボット レベルは、セカンダリ サブスクリプションによって提供される、最後に終了した日次ローソク足から計算されます。
  • 価格ポイントとピップ変換は証券の PriceStep に依存します。異なる目盛りサイズのシンボルは自動的に適応されます。
  • この戦略は、プロジェクトの高レベルの API ガイドラインに準拠して、高レベルのインジケーターのみをサブスクライブし、バッファーの直接読み取りを回避します。
  • このパッケージでは Python 実装は提供されません。
using System;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Trend following strategy using EMA crossover with MACD confirmation.
/// Buys when fast EMA crosses above slow EMA and MACD histogram is positive.
/// Sells when fast EMA crosses below slow EMA and MACD histogram is negative.
/// </summary>
public class FtTrendFollowerStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _prevMacd;
	private bool _isReady;

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

	public int FastPeriod
	{
		get => _fastPeriod.Value;
		set => _fastPeriod.Value = value;
	}

	public int SlowPeriod
	{
		get => _slowPeriod.Value;
		set => _slowPeriod.Value = value;
	}

	public FtTrendFollowerStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Candle type for strategy", "General");

		_fastPeriod = Param(nameof(FastPeriod), 12)
			.SetDisplay("Fast EMA", "Fast EMA period", "Indicators");

		_slowPeriod = Param(nameof(SlowPeriod), 26)
			.SetDisplay("Slow EMA", "Slow EMA period", "Indicators");
	}

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

		_prevFast = 0;
		_prevSlow = 0;
		_prevMacd = 0;
		_isReady = false;
	}

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

		_prevFast = 0;
		_prevSlow = 0;
		_prevMacd = 0;
		_isReady = false;

		var fast = new EMA { Length = FastPeriod };
		var slow = new EMA { Length = SlowPeriod };
		var macd = new MovingAverageConvergenceDivergence();

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(fast, slow, macd, OnProcess)
			.Start();

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

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

		if (!_isReady)
		{
			_prevFast = fastVal;
			_prevSlow = slowVal;
			_prevMacd = macdVal;
			_isReady = true;
			return;
		}

		// Buy: fast crosses above slow + MACD positive
		if (_prevFast <= _prevSlow && fastVal > slowVal && macdVal > 0)
		{
			if (Position < 0)
				BuyMarket();
			if (Position <= 0)
				BuyMarket();
		}
		// Sell: fast crosses below slow + MACD negative
		else if (_prevFast >= _prevSlow && fastVal < slowVal && macdVal < 0)
		{
			if (Position > 0)
				SellMarket();
			if (Position >= 0)
				SellMarket();
		}

		_prevFast = fastVal;
		_prevSlow = slowVal;
		_prevMacd = macdVal;
	}
}