GitHub で見る

トレーリングストップトリガーマネージャー戦略

概要

トレーリング ストップ トリガー マネージャー戦略は、MetaTrader エキスパート アドバイザー Trailing Sl.mq5 の StockSharp 移植です。オリジナルの EA 独自に取引を開始しませんでした。代わりに、すでにオープンしているポジションを一致する マジックナンバー で監視し、そのポジションを厳格化しました。 市場が望ましい方向に動いたときのストップロスレベル。この C# 実装では、次を使用してその動作を再現します。 StockSharp のハイレベル戦略 API は、サポートされているあらゆる金融商品で機能する透過的なトレーリングストップ管理を提供します。 StockSharp。

トレーリングロジック

  1. 最新の最高買値と最高売値を読むために注文ブックを購読します。
  2. ストラテジーが現在ロングネットポジションを保持しているかショートネットポジションを保持しているかを検出します。
  3. 市場の適切なサイド (ロングの最適な買い値、ショートの最適な売り値) を使用して変動利益を計算します。
  4. 利益が TriggerPoints (PriceStep を介して価格単位に変換) を超えると、トレーリング モードがアクティブになります。
  5. 現在の市場相場から設定された距離 TrailingPoints にトレーリング ストップを設定します。
  6. トレーリングストップを市場方向にのみ移動して、追加の利益を確保し続けます。
  7. 最良相場が計算されたトレーリングストップレベルに達するとすぐに、ポジションをフラット化するために成行注文を送信します。

注文とリスクの管理

  • この戦略では、最初のエントリー注文は送信されません。手動でオープンされた可能性のある既存のポジションのみを管理します または別の戦略によって。
  • マーケットの出口は、元の MetaTrader コードからの PositionModify 呼び出しをミラーリングして、BuyMarket/SellMarket で配置されます。
  • 停止距離は機器の PriceStep に応じて自動的に調整され、ポイントベースの構成が維持されます。 EA。
  • ポジションがクローズされると、トレーリング状態がリセットされ、新しいポジションが白紙の状態から開始されます。

パラメーター

名前 種類 デフォルト 説明
TrailingPoints int 1000 現在の価格とトレーリングストップの間の距離。価格ステップで測定されます。
TriggerPoints int 1500 ポジションのトレーリングを開始するために必要な価格ステップにおける最小利益。

使用上の注意

  • ポジションを監視したい証券にストラテジーを添付します。すぐに既存の追跡を開始します 露出。
  • オープンポジションのサイズに一致するように戦略の最初の Volume を設定します。 StockSharp はネット ポジションを使用するため、 トレーリングストップがトリガーされると、ストラテジーはロット全体を終了します。
  • ブローカーが大まかな価格ステップを提供する場合は、早期終了を避けるために、それに応じて TrailingPointsTriggerPoints を調整します。
  • この戦略はその状態を完全に StockSharp 内に保持するため、任意の任意システムまたは自動システムと組み合わせることができます。 実際の注文執行は StockSharp に任せられます。

元の MetaTrader 専門家との違い

  • MetaTrader はチケットごとに個別のポジションを管理し、マジックナンバーでフィルターしました。 StockSharp はネット ポジションごとに機能します セキュリティが向上し、チケットのフィルタリングが不要になります。
  • SetlossTakeProfit、および Lots 入力は、元の EA では使用されませんでした。したがって、これらは StockSharp では省略されています。 バージョンを変更して、トレーリング動作に焦点を当てた構成を維持します。
  • 注文の変更は直接市場からの撤退に置き換えられます。これは、StockSharp の口座をネッティングするための慣用的なアプローチです。
using System;
using System.Linq;
using System.Collections.Generic;

using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Strategy that mirrors the MetaTrader "Trailing Sl" expert by managing trailing stops for existing positions.
/// Adds SMA crossover entries for backtesting.
/// </summary>
public class TrailingStopTriggerManagerStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _trailingPoints;
	private readonly StrategyParam<int> _triggerPoints;

	private decimal _lastEntryPrice;
	private decimal? _activeStopPrice;
	private bool _trailingEnabled;
	private decimal _trailingDistance;
	private decimal _triggerDistance;
	private decimal _prevFast;
	private decimal _prevSlow;
	private bool _hasPrev;

	/// <summary>
	/// Candle type.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Trailing stop distance in price steps.
	/// </summary>
	public int TrailingPoints
	{
		get => _trailingPoints.Value;
		set => _trailingPoints.Value = value;
	}

	/// <summary>
	/// Profit distance that activates trailing stop.
	/// </summary>
	public int TriggerPoints
	{
		get => _triggerPoints.Value;
		set => _triggerPoints.Value = value;
	}

	/// <summary>
	/// Initializes a new instance of the strategy.
	/// </summary>
	public TrailingStopTriggerManagerStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe", "General");

		_trailingPoints = Param(nameof(TrailingPoints), 1000)
			.SetGreaterThanZero()
			.SetDisplay("Trailing Points", "Trailing stop distance", "Trailing Management");

		_triggerPoints = Param(nameof(TriggerPoints), 1500)
			.SetGreaterThanZero()
			.SetDisplay("Trigger Points", "Profit to activate trailing", "Trailing Management");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_lastEntryPrice = 0m;
		_activeStopPrice = null;
		_trailingEnabled = false;
		_trailingDistance = 0m;
		_triggerDistance = 0m;
		_prevFast = 0m;
		_prevSlow = 0m;
		_hasPrev = false;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		var step = Security?.PriceStep ?? 1m;
		if (step <= 0m) step = 1m;
		_trailingDistance = step * TrailingPoints;
		_triggerDistance = step * TriggerPoints;

		var smaFast = new SimpleMovingAverage { Length = 10 };
		var smaSlow = new SimpleMovingAverage { Length = 30 };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(smaFast, smaSlow, ProcessCandle)
			.Start();

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

	/// <inheritdoc />
	protected override void OnOwnTradeReceived(MyTrade trade)
	{
		base.OnOwnTradeReceived(trade);
		_lastEntryPrice = trade.Trade.Price;
	}

	private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
	{
		if (candle.State != CandleStates.Finished)
			return;

		var price = candle.ClosePrice;

		// Trailing stop management
		if (Position > 0 && _lastEntryPrice > 0)
		{
			var profit = price - _lastEntryPrice;
			if (!_trailingEnabled && profit >= _triggerDistance)
			{
				_trailingEnabled = true;
				_activeStopPrice = price - _trailingDistance;
			}
			else if (_trailingEnabled)
			{
				var desiredStop = price - _trailingDistance;
				if (!_activeStopPrice.HasValue || desiredStop > _activeStopPrice.Value)
					_activeStopPrice = desiredStop;
			}

			if (_trailingEnabled && _activeStopPrice.HasValue && price <= _activeStopPrice.Value)
			{
				SellMarket();
				ResetTrailingState();
				return;
			}
		}
		else if (Position < 0 && _lastEntryPrice > 0)
		{
			var profit = _lastEntryPrice - price;
			if (!_trailingEnabled && profit >= _triggerDistance)
			{
				_trailingEnabled = true;
				_activeStopPrice = price + _trailingDistance;
			}
			else if (_trailingEnabled)
			{
				var desiredStop = price + _trailingDistance;
				if (!_activeStopPrice.HasValue || desiredStop < _activeStopPrice.Value)
					_activeStopPrice = desiredStop;
			}

			if (_trailingEnabled && _activeStopPrice.HasValue && price >= _activeStopPrice.Value)
			{
				BuyMarket();
				ResetTrailingState();
				return;
			}
		}

		// SMA crossover entries
		if (_hasPrev)
		{
			var crossUp = _prevFast <= _prevSlow && fast > slow;
			var crossDown = _prevFast >= _prevSlow && fast < slow;

			if (crossUp && Position <= 0)
			{
				if (Position < 0)
					BuyMarket();
				BuyMarket();
				ResetTrailingState();
			}
			else if (crossDown && Position >= 0)
			{
				if (Position > 0)
					SellMarket();
				SellMarket();
				ResetTrailingState();
			}
		}

		_prevFast = fast;
		_prevSlow = slow;
		_hasPrev = true;
	}

	private void ResetTrailingState()
	{
		_trailingEnabled = false;
		_activeStopPrice = null;
	}
}