GitHub で見る

Candle Trailing Stop戦略

Candle Trailing Stop戦略は、同名のMetaTraderエキスパートアドバイザーのStockSharpポートです。元のロボットはマルチタイムフレームのトレンドフィルター、モメンタム確認、最近のローソク足の高安値を追うアグレッシブなTrailingストップエンジンを組み合わせていました。C#バージョンは同じワークフローを維持しながら、StockSharpの高レベルコンポーネントに依存し、すべての重要な設定を戦略パラメーターとして公開しています。

コアロジック

  1. データサブスクリプション
    • 取引時間軸がエントリーとTrailingストップの更新を担います。
    • 上位時間軸は線形加重移動平均(LWMA)とモメンタムインジケーターを使用して確認を提供します。
    • 3番目のサブスクリプションは、取引をフィルタリングするためにゆっくりとした時間軸(デフォルトは月次)でMACDラインを計算します。
  2. トレンドアライメント
    • 両方の時間軸で高速・中速・低速LWMAシーケンスが揃っている場合(ロングは強気シーケンス、ショートは弱気)のみ取引が許可されます。
  3. モメンタムゲート
    • モメンタムインジケーターは、最後の3本の上位タイムフレームバーのうち少なくとも1本でニュートラル値100の近くにある必要があります。
  4. MACD確認
    • ロングはMACDラインがシグナルラインより上にあることが必要で、ショートは逆の関係が必要です。
  5. エントリートリガー
    • 現在の時間軸でのLWMA高速線を通じたブレイクアウト(前のバーで触れた後にローソク足が平均より上/下で閉じる)は、設定可能なポジション制限を守りながら新しい取引を開始します。
  6. リスクと出口管理
    • 初期のストップロスとテイクプロフィット距離はpipsで定義され、価格ステップに自動変換されます。
    • ストップはブレークイーブンに移行したり、最近のローソク足の極値の後ろをTrailingしたり、クラシックな固定距離Trailingに戻ることができます。
    • オプションの資産ベース機能は元のEAを反映します:金銭的テイクプロフィット、パーセンテージテイクプロフィット、資産Trailing、ドローダウン保護。

パラメーター

グループ 名前 説明 デフォルト
取引 Volume 注文サイズ(ロット/契約)。 1
MaxTrades Volume * MaxTradesとして表される最大集約エクスポージャー。 10
インジケーター FastCurrentLength 取引時間軸の高速LWMA。 9
MiddleCurrentLength 取引時間軸の中速LWMA。 20
SlowCurrentLength 取引時間軸の低速LWMA。 52
FastHigherLength 上位時間軸の高速LWMA。 9
MiddleHigherLength 上位時間軸の中速LWMA。 20
SlowHigherLength 上位時間軸の低速LWMA。 52
MomentumPeriod 上位時間軸のモメンタム期間。 14
MomentumBuyThreshold ロング取引に許可される100からの最大偏差。 0.3
MomentumSellThreshold ショート取引に許可される100からの最大偏差。 0.3
MacdFastLength MACD確認用の高速EMA長さ。 12
MacdSlowLength MACD確認用の低速EMA長さ。 26
MacdSignalLength MACD確認用のシグナルEMA長さ。 9
リスク StopLossPips pipsでのストップロス距離。 20
TakeProfitPips pipsでのテイクプロフィット距離。 50
UseMoveToBreakEven ブレークイーブンロジックを有効にします。 true
BreakEvenTriggerPips ストップを移動する前に必要なpipsでの利益。 30
BreakEvenOffsetPips ストップをブレークイーブンにシフトする際に追加するオフセット。 30
UseCandleTrail ローソク足ベースのTrailing(true)またはクラシックTrailing(false)を選択。 true
CandleTrailLength Trailing極値の計算に使用する閉じたローソク足の数。 3
PadAmountPips Trailing極値の下/上に追加される余分なバッファ。 10
TrailTriggerPips クラシックTrailingが有効になる前に必要な利益。 40
TrailAmountPips クラシックTrailingが維持する距離。 40
資産ルール UseMoneyTakeProfit 含み益が金銭的目標を超えたらすべてのポジションを決済。 false
MoneyTakeProfit 金銭的利益目標。 40
UsePercentTakeProfit 含み益がパーセンテージ目標を超えたらすべてのポジションを決済。 false
PercentTakeProfit 利益目標として使用する初期資産のパーセンテージ。 10
EnableMoneyTrailing しきい値後に含み益のTrailingを有効化。 true
MoneyTrailTarget 金銭的Trailingロジックをオンにする利益レベル。 40
MoneyTrailStop 目標到達後に許可される最大押し戻し。 10
UseEquityStop 資産ドローダウン保護を有効にします。 true
EquityRiskPercent フラットポジションを強制する前の資産ピークからの最大ドローダウン。 1
データ CurrentCandleType 取引時間軸。 5m
HigherCandleType フィルターに使用する上位時間軸。 30m
MacdCandleType MACD確認の時間軸(デフォルトは月次)。 30d

注意事項と前提

  • pipsは計器のティックサイズを使用して価格ステップに変換されます。1 pipが1 tickと異なるシンボルでは、デフォルトのpip距離を調整する必要がある場合があります。
  • 金銭的機能は(終値 - 平均価格) * ポジションとして近似された含み益に依存します。スワップと手数料の調整はシミュレートされません。
  • 戦略はエントリーと出口に市場注文を使用します。初期のテイクプロフィット注文は取引が開かれると登録され、ストップロス管理は内部で処理され、計算されたレベルが越えられると市場注文で決済されます。
  • コード内のすべてのコメントはプロジェクトガイドラインに従って英語で書かれています。
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 CandleTrailingStopStrategy : 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 CandleTrailingStopStrategy()
	{
		_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;
	}
}