Ver no GitHub

Cycle Lines Strategy

Overview

Cycle Lines Strategy is the StockSharp port of the MetaTrader expert advisor "Cycle Lines". The original script combined chart drawing with manual trade buttons. This port focuses on the automated trading logic that accompanied those controls. The strategy trades MACD line crossovers, keeps risk tightly controlled via absolute stop-loss and take-profit limits, and supports break-even as well as trailing stop management.

When the MACD line crosses above its signal line, the strategy opens a long position. When the line crosses below the signal line, it opens a short position. Open trades are closed if the indicator flips to the opposite direction or any of the protective rules (stop-loss, take-profit, break-even or trailing stop) are triggered.

Trading Rules

  1. Entry conditions
    • Long: MACD crosses above the signal line on the selected candle series.
    • Short: MACD crosses below the signal line on the selected candle series.
    • Entries are only evaluated after the indicator is fully formed and the strategy is connected and allowed to trade.
  2. Exit conditions
    • Opposite MACD crossover.
    • Stop-loss reached.
    • Take-profit reached.
    • Break-even protection level touched.
    • Trailing stop level touched.

Parameters

Name Description Default Notes
Volume Order volume per trade. 1 Must be positive.
MacdFastPeriod Fast EMA period inside the MACD calculation. 12 Optimizable.
MacdSlowPeriod Slow EMA period inside MACD. 26 Optimizable.
MacdSignalPeriod MACD signal-line period. 9 Optimizable.
StopLoss Absolute price distance for the protective stop. 0 Disabled when set to 0.
TakeProfit Absolute price distance for the take-profit target. 0 Disabled when set to 0.
TrailingOffset Distance kept between the best favorable price and the trailing stop. 0 Disabled when set to 0.
BreakEvenTrigger Profit distance required before moving the stop to break-even. 0 Disabled when set to 0.
BreakEvenOffset Additional offset applied to the break-even level. 0 Allows locking some extra profit above/below entry.
CandleType Candle series used for indicator calculations. 15 minute time-frame candles Accepts any DataType supported by StockSharp.

Position Management

  • Stop-loss / take-profit: Both checks evaluate intrabar extremes (high/low) for finished candles, ensuring the exit respects the configured absolute distance from the entry price.
  • Break-even: Once the price moves in favor by at least BreakEvenTrigger, the strategy arms a stop at entry ± BreakEvenOffset. Any retracement that touches this level closes the position.
  • Trailing stop: For long trades the highest reached price is monitored. The stop level follows the high minus TrailingOffset. For short trades the logic mirrors the behavior around the lowest price.

Usage Notes

  • The strategy trades a single position at a time. Consecutive signals will not pyramid an existing position.
  • Parameters are exposed as StrategyParam<T> objects and therefore support optimization inside StockSharp.
  • To reproduce the original EA's default behavior, configure the MACD periods to 12 / 26 / 9 and set the risk parameters according to your desired pip values.

Differences from the MQL Version

  • Chart drawing features and manual BUY/SELL buttons were omitted because StockSharp handles visualization differently.
  • All protective rules were rewritten to operate on candle data instead of MetaTrader tick events, which keeps them compatible with StockSharp's high-level API.
  • Trailing and break-even logic is symmetric for long and short trades for clarity and robustness.
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;
	}
}