Ver no GitHub

Estratégia do Trailing Stop Trigger Manager

Visão geral

A Estratégia Trailing Stop Trigger Manager é uma versão StockSharp do MetaTrader consultor especialista Trailing Sl.mq5. O original EA não abriu negociações por conta própria. Em vez disso, monitorou as posições já abertas com um número mágico correspondente e reforçou seus níveis de stop-loss quando o mercado se moveu na direção desejada. Esta implementação C# reproduz esse comportamento usando A estratégia de alto nível de StockSharp API, oferecendo gerenciamento transparente de trailing-stop que funciona com qualquer instrumento suportado por StockSharp.

Lógica final

  1. Assina a carteira de pedidos para ler as últimas melhores ofertas e melhores cotações de venda.
  2. Detecta se a estratégia mantém atualmente uma posição líquida longa ou curta.
  3. Calcula o lucro flutuante usando o lado apropriado do mercado (melhor oferta para posições compradas, melhor oferta para posições vendidas).
  4. Ativa o modo de rastreamento quando o lucro excede TriggerPoints (convertido em unidades de preço por meio de PriceStep).
  5. Define o trailing stop na distância configurada TrailingPoints da cotação de mercado atual.
  6. Move o trailing stop apenas em direção ao mercado para continuar obtendo lucro adicional.
  7. Envia uma ordem de mercado para nivelar a posição assim que a melhor cotação atinge o nível de trailing stop calculado.

Gerenciamento de pedidos e riscos

  • A estratégia não envia pedidos de entrada iniciais. Gerencia apenas uma posição existente que pode ter sido aberta manualmente ou por outra estratégia.
  • As saídas de mercado são colocadas com BuyMarket/SellMarket, espelhando as chamadas PositionModify do código MetaTrader original.
  • A distância de parada é automaticamente dimensionada com o PriceStep do instrumento, que preserva a configuração baseada em pontos de o EA.
  • Depois que a posição é fechada, o estado final é redefinido para que novas posições comecem do zero.

Parâmetros

Nome Tipo Padrão Descrição
TrailingPoints int 1000 Distância entre o preço atual e o trailing stop, medida em etapas de preço.
TriggerPoints int 1500 Lucro mínimo nas etapas de preço necessário para começar a rastrear a posição.

Notas de uso

  • Anexe a estratégia ao título cuja posição você deseja supervisionar. Ele começará imediatamente a rastrear o existente exposição.
  • Configure o Volume inicial da estratégia para corresponder ao tamanho da sua posição aberta. StockSharp usa posições líquidas, então o estratégia sairá do lote inteiro quando o trailing stop for acionado.
  • Se o corretor fornecer etapas de preços grosseiras, ajuste TrailingPoints e TriggerPoints de acordo para evitar saídas prematuras.
  • A estratégia mantém seu estado inteiramente dentro de StockSharp, podendo ser combinada com qualquer sistema discricionário ou automatizado que deixa a execução real do pedido para StockSharp.

Diferenças do especialista MetaTrader original

  • MetaTrader gerenciava posições separadas por ticket e as filtrava por número mágico. StockSharp trabalha com uma posição líquida por segurança, eliminando a necessidade de filtragem de tickets.
  • As entradas Setloss, TakeProfit e Lots não foram utilizadas no EA original. Eles são, portanto, omitidos no StockSharp versão para manter a configuração focada no comportamento final.
  • As modificações de ordem são substituídas por saídas diretas do mercado, que é a abordagem idiomática para compensação de contas em 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;
	}
}