Ver en GitHub

Estrategia de Alerta de Línea de Tendencia

Esta estrategia monitorea dos líneas de tendencia definidas por el usuario y reacciona cuando el precio las rompe. Las líneas superior e inferior representan niveles de resistencia y soporte. Cuando el precio de cierre cruza por encima de la línea superior, se abre una posición larga. Cuando el precio cae por debajo de la línea inferior, se abre una posición corta. La lógica opcional de trailing stop protege las posiciones abiertas moviendo el nivel del stop en la dirección del trade.

Parámetros

  • Breakout Points – puntos adicionales añadidos a los niveles de la línea de tendencia para definir el umbral de ruptura.
  • Upper Line – nivel de precio para la ruptura alcista.
  • Lower Line – nivel de precio para la ruptura bajista.
  • Start Hour – hora de inicio del trading en horas.
  • End Hour – hora de fin del trading en horas.
  • Use Trailing Stop – activa la gestión del trailing stop.
  • Trailing Stop Points – distancia en puntos para el trailing stop.
  • Candle Type – marco temporal de velas usado para el análisis.

Cómo Funciona

  1. La estrategia se suscribe a la serie de velas seleccionada.
  2. Para cada vela cerrada verifica que el tiempo esté dentro de la ventana de trading especificada.
  3. Se detecta una ruptura cuando el cierre de la vela cruza por encima de la línea superior o por debajo de la línea inferior, ajustado por el umbral de puntos de ruptura.
  4. Cuando ocurre una ruptura, se envía una orden de mercado en la dirección de la ruptura si no hay posición existente.
  5. Si el trailing stop está activado, el nivel del stop sigue al precio hasta que se activa.

Notas

  • La estrategia es una conversión simplificada del asesor experto original TrendlineAlert de MetaTrader. El dibujo manual de líneas de tendencia es reemplazado por niveles de precio fijos definidos por parámetros.
  • No se realizan órdenes fuera de las horas de trading especificadas.
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 reacts to price breakouts above or below predefined trendlines.
/// </summary>
public class TrendlineAlertStrategy : Strategy
{
	private readonly StrategyParam<int> _breakoutPoints;
	private readonly StrategyParam<int> _startHour;
	private readonly StrategyParam<int> _endHour;
	private readonly StrategyParam<bool> _useTrailingStop;
	private readonly StrategyParam<int> _trailingStopPoints;
	private readonly StrategyParam<decimal> _upperLine;
	private readonly StrategyParam<decimal> _lowerLine;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _lastPrice;
	private decimal _stopPrice;
	private bool _upAlerted;
	private bool _downAlerted;

	/// <summary>
	/// Breakout threshold in points.
	/// </summary>
	public int BreakoutPoints { get => _breakoutPoints.Value; set => _breakoutPoints.Value = value; }

	/// <summary>
	/// Trading start hour (0-23).
	/// </summary>
	public int StartHour { get => _startHour.Value; set => _startHour.Value = value; }

	/// <summary>
	/// Trading end hour (0-24).
	/// </summary>
	public int EndHour { get => _endHour.Value; set => _endHour.Value = value; }

	/// <summary>
	/// Enable trailing stop logic.
	/// </summary>
	public bool UseTrailingStop { get => _useTrailingStop.Value; set => _useTrailingStop.Value = value; }

	/// <summary>
	/// Trailing stop distance in points.
	/// </summary>
	public int TrailingStopPoints { get => _trailingStopPoints.Value; set => _trailingStopPoints.Value = value; }

	/// <summary>
	/// Price level of the upper trendline.
	/// </summary>
	public decimal UpperLine { get => _upperLine.Value; set => _upperLine.Value = value; }

	/// <summary>
	/// Price level of the lower trendline.
	/// </summary>
	public decimal LowerLine { get => _lowerLine.Value; set => _lowerLine.Value = value; }

	/// <summary>
	/// Type of candles to subscribe.
	/// </summary>
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	/// <summary>
	/// Initialize <see cref="TrendlineAlertStrategy"/>.
	/// </summary>
	public TrendlineAlertStrategy()
	{
		_breakoutPoints = Param(nameof(BreakoutPoints), 0)
			.SetDisplay("Breakout Points", "Additional points for breakout", "General");

		_startHour = Param(nameof(StartHour), 0)
			.SetDisplay("Start Hour", "Strategy start hour", "General");

		_endHour = Param(nameof(EndHour), 24)
			.SetDisplay("End Hour", "Strategy end hour", "General");

		_useTrailingStop = Param(nameof(UseTrailingStop), false)
			.SetDisplay("Use Trailing Stop", "Enable trailing stop", "Protection");

		_trailingStopPoints = Param(nameof(TrailingStopPoints), 5)
			.SetDisplay("Trailing Stop Points", "Trailing stop distance", "Protection");

		_upperLine = Param(nameof(UpperLine), 68000m)
			.SetDisplay("Upper Line", "Upper trendline level", "Levels");

		_lowerLine = Param(nameof(LowerLine), 62000m)
			.SetDisplay("Lower Line", "Lower trendline level", "Levels");

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Type of candles", "General");
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_lastPrice = 0;
		_stopPrice = 0;
		_upAlerted = false;
		_downAlerted = false;
	}

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

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

		StartProtection(
			new Unit(2, UnitTypes.Percent),
			new Unit(2, UnitTypes.Percent));
	}

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

		var hour = candle.OpenTime.Hour;
		if (hour < StartHour || hour >= EndHour)
			return;

		var step = Security.PriceStep ?? 1m;
		var threshold = BreakoutPoints * step;

		var upper = UpperLine + threshold;
		var lower = LowerLine - threshold;
		var price = candle.ClosePrice;

		if (!_upAlerted && price > upper && _lastPrice <= upper)
		{
			_upAlerted = true;
			if (Position <= 0)
				BuyMarket();
		}
		else if (!_downAlerted && price < lower && _lastPrice >= lower)
		{
			_downAlerted = true;
			if (Position >= 0)
				SellMarket();
		}

		if (UseTrailingStop)
			UpdateTrailingStop(candle);

		_lastPrice = price;
	}

	private void UpdateTrailingStop(ICandleMessage candle)
	{
		var step = Security.PriceStep ?? 1m;
		var trail = TrailingStopPoints * step;

		if (Position > 0)
		{
			_stopPrice = Math.Max(_stopPrice, candle.ClosePrice - trail);
			if (candle.LowPrice <= _stopPrice)
				SellMarket();
		}
		else if (Position < 0)
		{
			_stopPrice = Math.Min(_stopPrice, candle.ClosePrice + trail);
			if (candle.HighPrice >= _stopPrice)
				BuyMarket();
		}
		else
		{
			_stopPrice = 0;
		}
	}
}