Ver en GitHub

Estrategia Cycle Lines

Descripción general

La estrategia Cycle Lines es la adaptación a StockSharp del asesor experto de MetaTrader "Cycle Lines". El script original combinaba dibujo en el gráfico con botones manuales de trading. Esta versión se centra en la lógica de negociación automatizada que acompañaba a esos controles. La estrategia opera cruces de la línea MACD, mantiene el riesgo bajo control estricto mediante límites absolutos de stop-loss y take-profit, y admite gestión de break-even y trailing stop.

Cuando la línea MACD cruza por encima de su línea de señal, la estrategia abre una posición larga. Cuando la línea cruza por debajo de la línea de señal, abre una posición corta. Las operaciones abiertas se cierran si el indicador cambia a la dirección opuesta o si se activa cualquiera de las reglas de protección (stop-loss, take-profit, break-even o trailing stop).

Reglas de trading

  1. Condiciones de entrada
    • Largo: MACD cruza por encima de la línea de señal en la serie de velas seleccionada.
    • Corto: MACD cruza por debajo de la línea de señal en la serie de velas seleccionada.
    • Las entradas solo se evalúan después de que el indicador esté completamente formado y la estrategia esté conectada y autorizada para operar.
  2. Condiciones de salida
    • Cruce MACD opuesto.
    • Stop-loss alcanzado.
    • Take-profit alcanzado.
    • Nivel de protección break-even tocado.
    • Nivel de trailing stop tocado.

Parámetros

Nombre Descripción Predeterminado Notas
Volume Volumen de orden por operación. 1 Debe ser positivo.
MacdFastPeriod Período de la EMA rápida dentro del cálculo MACD. 12 Optimizable.
MacdSlowPeriod Período de la EMA lenta dentro de MACD. 26 Optimizable.
MacdSignalPeriod Período de la línea de señal MACD. 9 Optimizable.
StopLoss Distancia absoluta de precio para el stop de protección. 0 Se desactiva cuando se establece en 0.
TakeProfit Distancia absoluta de precio para el objetivo de take-profit. 0 Se desactiva cuando se establece en 0.
TrailingOffset Distancia mantenida entre el mejor precio favorable y el trailing stop. 0 Se desactiva cuando se establece en 0.
BreakEvenTrigger Distancia de ganancia necesaria antes de mover el stop a break-even. 0 Se desactiva cuando se establece en 0.
BreakEvenOffset Desplazamiento adicional aplicado al nivel de break-even. 0 Permite asegurar algo de ganancia extra por encima/debajo de la entrada.
CandleType Serie de velas usada para los cálculos de indicadores. Velas de marco temporal de 15 minutos Acepta cualquier DataType compatible con StockSharp.

Gestión de posiciones

  • Stop-loss / take-profit: Ambas comprobaciones evalúan los extremos intrabar (máximo/mínimo) de velas cerradas, asegurando que la salida respete la distancia absoluta configurada desde el precio de entrada.
  • Break-even: Cuando el precio avanza a favor al menos BreakEvenTrigger, la estrategia arma un stop en entry ± BreakEvenOffset. Cualquier retroceso que toque ese nivel cierra la posición.
  • Trailing stop: En operaciones largas se vigila el precio máximo alcanzado. El nivel de stop sigue al máximo menos TrailingOffset. En operaciones cortas, la lógica refleja el comportamiento alrededor del precio mínimo.

Notas de uso

  • La estrategia mantiene una sola posición a la vez. Las señales consecutivas no piramidarán una posición existente.
  • Los parámetros se exponen como objetos StrategyParam<T> y, por lo tanto, admiten optimización dentro de StockSharp.
  • Para reproducir el comportamiento predeterminado del EA original, configure los períodos MACD en 12 / 26 / 9 y ajuste los parámetros de riesgo según los valores en pips deseados.

Diferencias con la versión MQL

  • Las funciones de dibujo en el gráfico y los botones manuales BUY/SELL se omitieron porque StockSharp gestiona la visualización de otra forma.
  • Todas las reglas de protección se reescribieron para operar sobre datos de velas en lugar de eventos tick de MetaTrader, lo que las mantiene compatibles con la API de alto nivel de StockSharp.
  • La lógica de trailing y break-even es simétrica para operaciones largas y cortas, por claridad y robustez.
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 CycleLinesStrategy : 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 CycleLinesStrategy()
	{
		_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;
	}
}