Auf GitHub ansehen

Trend Line Strategy

Summary

The Trend Line strategy replicates the core trade management logic of the original MetaTrader expert by combining a fast and slow linear weighted moving average, a momentum filter and a MACD confirmation. The conversion focuses on high-level StockSharp components and keeps the same systematic approach that waits for momentum bursts in the direction of the trend before entering. Protective stops, profit targets and an optional trailing stop in price steps provide risk management similar to the source expert.

Trading Logic

  1. Subscribe to the configured candle series and calculate the following indicators:
    • Fast linear weighted moving average (LWMA) with the configurable FastMaPeriod.
    • Slow LWMA with the configurable SlowMaPeriod.
    • Momentum indicator with period MomentumPeriod. The most recent three momentum readings are tracked to emulate the multi-bar momentum check present in the MQL version.
    • Moving Average Convergence Divergence (MACD) indicator with standard fast/slow/signal lengths. The strategy stores the MACD and signal values for later use.
  2. Enter long when:
    • The fast LWMA is above the slow LWMA.
    • At least one of the last three momentum values is greater than or equal to MomentumBuyThreshold.
    • The MACD main line is above the MACD signal line.
    • No open short position exists (short exposure is flattened before opening a long position).
  3. Enter short when:
    • The fast LWMA is below the slow LWMA.
    • At least one of the last three momentum values is less than or equal to MomentumSellThreshold (threshold should be negative to detect downward acceleration).
    • The MACD main line is below the MACD signal line.
    • No open long position exists (long exposure is flattened before opening a short position).
  4. After each entry the strategy places protective stop-loss and take-profit orders by distance in price steps. Both orders are recalculated every time the position changes.
  5. A trailing stop can be activated with TrailingStopSteps and TrailingTriggerSteps. Once the open position gains at least the trigger distance, the stop-loss is moved to TrailingStopSteps away from the current close price of the processed candle.

Parameters

  • CandleType – data type for the candle series used by every indicator (default 1-hour timeframe).
  • FastMaPeriod – period of the fast LWMA (default 6).
  • SlowMaPeriod – period of the slow LWMA (default 85).
  • MomentumPeriod – number of candles for the momentum calculation (default 14).
  • MomentumBuyThreshold – minimum positive momentum needed to allow new long positions (default 0.3).
  • MomentumSellThreshold – maximum (negative) momentum allowed before opening new short positions (default -0.3).
  • MacdFastLength – fast EMA length of the MACD (default 12).
  • MacdSlowLength – slow EMA length of the MACD (default 26).
  • MacdSignalLength – signal EMA length of the MACD (default 9).
  • StopLossSteps – protective stop distance expressed in instrument steps (default 20).
  • TakeProfitSteps – protective profit target distance in steps (default 50).
  • TrailingStopSteps – trailing stop distance in steps (default 40, disabled when zero).
  • TrailingTriggerSteps – profit in steps required before the trailing stop becomes active (default 40).

Notes

  • Indicator bindings rely on finished candles only; unfinished data is ignored to avoid premature signals.
  • SetStopLoss and SetTakeProfit work with distances in price steps, which keeps the behaviour consistent on instruments with different tick sizes.
  • When MomentumSellThreshold is kept positive, the default comparison (<= threshold) expects that value to be negative. Adjust the sign when optimising the strategy.
  • The trailing stop works in end-of-bar mode because it is updated when each finished candle is processed, mirroring the original script that recalculated stops on new bars.
  • The conversion intentionally omits manual trend-line drawing and equity-based liquidation rules because they relied on interactive terminal features unavailable in StockSharp. All other core entry and risk rules are preserved.
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 TrendLineStrategy : 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 TrendLineStrategy()
	{
		_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;
	}
}