Auf GitHub ansehen

Trading Criteria Strategy

Overview

Trading Criteria Strategy is a multi-timeframe trend-following approach converted from the original MQL4 "Trading Criteria" expert advisor. The port relies on linear weighted moving averages, momentum deviation filters, and MACD confirmations drawn from both trend and monthly timeframes. Risk management features include trailing stops, break-even protection, and configurable stop-loss/ take-profit targets.

Entry Logic

  1. Primary timeframe: Uses a fast and slow linear weighted moving average (LWMA). Long signals require the fast MA to stay above the slow MA; short signals require the opposite.
  2. Momentum filter: Calculates momentum deviation (|Momentum-100|) on the trend timeframe and checks the three most recent values against bullish or bearish thresholds.
  3. Trend MACD filter: Evaluates the MACD main line relative to its signal line on the same trend timeframe. Signals only trigger when the current relationship aligns with the previous bar to avoid rapid flip-flopping.
  4. Monthly MACD filter: Confirms the larger directional bias using MACD on a monthly (or user-specified slow) timeframe.
  5. Position exposure: Limits the maximum net position size to MaxPositions * Volume. If a new signal appears while holding an opposite position, the strategy will first neutralize the exposure by buying or selling enough volume.

Exit and Risk Management

  • Stop Loss / Take Profit: Set via StopLossPoints and TakeProfitPoints, converted into actual price offsets using the normalized pip size of the instrument.
  • Trailing stop: Enabled with EnableTrailing and TrailingStopPoints. For longs, the stop tracks the highest price minus the trailing distance once the move exceeds the threshold; shorts mirror the logic using the lowest price.
  • Break-even move: When enabled (EnableBreakEven), the stop migrates to the entry price plus an optional offset once the close price reaches BreakEvenTriggerPoints distance in favor of the open position.
  • Manual protective exits: If the candle touches the calculated stop or target levels, the strategy closes the entire net position on that bar.

Parameters

Parameter Description
CandleType Base timeframe for signal generation and moving averages.
TrendCandleType Timeframe used for momentum and MACD filters.
MonthlyCandleType Slow timeframe providing long-term MACD confirmation.
FastMaPeriod / SlowMaPeriod Lengths of the fast and slow LWMAs on the entry timeframe.
MomentumPeriod Momentum lookback period on the trend timeframe.
MomentumBuyThreshold / MomentumSellThreshold Minimum deviation from 100 required for long or short entries.
MaxPositions Maximum number of base lots that can remain open simultaneously.
StopLossPoints / TakeProfitPoints Distances, in points, for protective stops and profit targets.
EnableTrailing / TrailingStopPoints Activates trailing stops and sets their distance.
EnableBreakEven Toggles break-even behavior.
BreakEvenTriggerPoints / BreakEvenOffsetPoints Controls how far price must move before the stop shifts to break-even and how much offset to apply.

Usage Notes

  • Attach the strategy to an instrument with proper candle series support for the selected timeframes.
  • Ensure the security provides an accurate PriceStep; the implementation adjusts fractional pip instruments (3 or 5 decimal places) to match MQL conventions.
  • Trailing and break-even protections operate on completed candles. In fast markets, protective levels may execute on the following bar when a gap occurs.
  • The default parameter set mirrors the published MQL inputs, but they can be optimized via the built-in parameter metadata.
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 TradingCriteriaStrategy : 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 TradingCriteriaStrategy()
	{
		_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;
	}
}