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) は、ロングの場合は正、ショートの場合は負でなければなりません。
エントリールール
ロング取引は次の場合に実行されます。
- トレンド検出により上昇傾向が報告され、GMMA の傾き投票が利用可能なラインの半分を超えています。
- ラゲール トリガーは以前に準備されており、現在の値は
LaguerreOversold を超えて戻ります。
- 高速の EMA は、以前は低速の EMA を下回っていましたが、上回っています。
- 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;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage, MovingAverageConvergenceDivergence
from StockSharp.Algo.Strategies import Strategy
class ft_trend_follower_strategy(Strategy):
"""
FT Trend Follower: EMA crossover with MACD confirmation.
Buys when fast EMA crosses above slow EMA and MACD > 0.
Sells when fast EMA crosses below slow EMA and MACD < 0.
"""
def __init__(self):
super(ft_trend_follower_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Candle type for strategy", "General")
self._fast_period = self.Param("FastPeriod", 12) \
.SetDisplay("Fast EMA", "Fast EMA period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 26) \
.SetDisplay("Slow EMA", "Slow EMA period", "Indicators")
self._prev_fast = 0.0
self._prev_slow = 0.0
self._prev_macd = 0.0
self._is_ready = False
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(ft_trend_follower_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._prev_macd = 0.0
self._is_ready = False
def OnStarted2(self, time):
super(ft_trend_follower_strategy, self).OnStarted2(time)
self._prev_fast = 0.0
self._prev_slow = 0.0
self._prev_macd = 0.0
self._is_ready = False
fast = ExponentialMovingAverage()
fast.Length = self._fast_period.Value
slow = ExponentialMovingAverage()
slow.Length = self._slow_period.Value
macd = MovingAverageConvergenceDivergence()
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(fast, slow, macd, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, fast)
self.DrawIndicator(area, slow)
self.DrawOwnTrades(area)
def _process_candle(self, candle, fast_val, slow_val, macd_val):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_val)
slow_val = float(slow_val)
macd_val = float(macd_val)
if not self._is_ready:
self._prev_fast = fast_val
self._prev_slow = slow_val
self._prev_macd = macd_val
self._is_ready = True
return
if self._prev_fast <= self._prev_slow and fast_val > slow_val and macd_val > 0:
if self.Position < 0:
self.BuyMarket()
if self.Position <= 0:
self.BuyMarket()
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and macd_val < 0:
if self.Position > 0:
self.SellMarket()
if self.Position >= 0:
self.SellMarket()
self._prev_fast = fast_val
self._prev_slow = slow_val
self._prev_macd = macd_val
def CreateClone(self):
return ft_trend_follower_strategy()