GitHub で見る

TP SL Trailing戦略

概要

この戦略はMetaTrader 5エキスパートアドバイザー「TP SL Trailing」の直接変換です。戦略自体はエントリーを生成しません。代わりに、保護的なストップロスとテイクプロフィットを設定し、トレードが利益を生み出すとストップをトレーリングすることで既存のポジションを管理します。ピップベースの設定は元のスクリプトのパラメーターに一致し、StockSharpでサポートされるあらゆるシンボルにロジックを付属させることができます。

取引ロジック

  • 新しいポジションが現れると、戦略は設定されたピップ距離を使用して初期ストップロスとテイクプロフィットをオプションで設定できます。この動作は元のエキスパートアドバイザーと同様にOnly Zero Valuesフラグで制御されます。
  • ロングポジションの場合、未実現利益がトレーリングストップとトレーリングステップの合計を超えると、戦略はストップロスを上向きに移動します。ストップは現在価格 - トレーリングストップに移動し、最小限の利益が確保されることを保証します。
  • ショートポジションの場合、戦略は同じアイデアを反映し、利益がトレーリングしきい値を超えるとストップを下向きに移動します。
  • トレーリングストップとトレーリングステップの両方がゼロの場合、戦略はストップロスをそのままにします。
  • テイクプロフィットレベルはトレーリングされません。Only Zero Valuesが有効な場合の初期配置フェーズ中にのみ設定され、MQLの動作を完全に再現します。

パラメーター

名前 説明
CandleType 価格動作の追跡に使用されるローソク足の時間軸。より速い時間軸はトレーリングの精度を向上させます。
StopLossPips エントリー価格と初期ストップロスの間のピップ距離。Only Zero Valuesが有効な場合にのみ適用されます。
TakeProfitPips エントリー価格と初期テイクプロフィットの間のピップ距離。Only Zero Valuesが有効な場合にのみ適用されます。
TrailingStopPips コアトレーリング距離(ピップ)。起動後に現在価格からどれだけ離れてストップを維持するかを定義します。
TrailingStepPips ストップが再び動く前に超える必要がある追加のピップバッファー。ストップの更新が頻繁になりすぎるのを防ぎます。
OnlyZeroValues 元のEAフラグに対応します。有効にすると、初期保護注文は現在ストップロスやテイクプロフィットが割り当てられていないポジションにのみ作成されます。

変換上の注意事項

  • ピップ距離は証券のPriceStepを使用して価格単位に変換されます。これによりロジックはインストゥルメントに依存せず、MLQバージョンの3/5桁調整を反映します。
  • トレーリングロジックがストップロスを動かすたびに保護注文は再登録されます。前のポジションからのアクティブな注文はポジションサイズがゼロに戻ったときに自動的にキャンセルされます。
  • すべてのコードコメントは英語で書かれており、この文書はポーティングプロセス中に行われたすべての決定を再現するために意図的に詳細になっています。
using System;
using System.Collections.Generic;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// TP SL Trailing strategy. Uses EMA with price crossover for entries.
/// </summary>
public class TpSlTrailingStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _emaPeriod;
	private decimal? _prevClose;
	private decimal? _prevEma;

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

	public TpSlTrailingStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame()).SetDisplay("Candle Type", "Timeframe", "General");
		_emaPeriod = Param(nameof(EmaPeriod), 20).SetGreaterThanZero().SetDisplay("EMA Period", "EMA lookback", "Indicators");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];

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

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevClose = null; _prevEma = null;
		var ema = new ExponentialMovingAverage { Length = EmaPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(ema, ProcessCandle).Start();
		var area = CreateChartArea();
		if (area != null) { DrawCandles(area, subscription); DrawIndicator(area, ema); DrawOwnTrades(area); }
	}

	private void ProcessCandle(ICandleMessage candle, decimal emaVal)
	{
		if (candle.State != CandleStates.Finished) return;
		var close = candle.ClosePrice;
		if (!IsFormedAndOnlineAndAllowTrading()) { _prevClose = close; _prevEma = emaVal; return; }
		if (_prevClose == null || _prevEma == null) { _prevClose = close; _prevEma = emaVal; return; }
		if (_prevClose.Value < _prevEma.Value && close >= emaVal && Position <= 0) { if (Position < 0) BuyMarket(); BuyMarket(); }
		else if (_prevClose.Value > _prevEma.Value && close <= emaVal && Position >= 0) { if (Position > 0) SellMarket(); SellMarket(); }
		_prevClose = close; _prevEma = emaVal;
	}
}