Ver en GitHub

FXF Safe Trend Scalp V1 (C#)

The FXF Safe Trend Scalp V1 strategy trades breakouts from ZigZag-based trendlines and mirrors the behaviour of the original MetaTrader 4 expert advisor. It observes the distance between the current price and dynamic resistance/support lines constructed from recent ZigZag pivots and aligns trades with a pair of simple moving averages. Protective stop-loss, take-profit and a floating-profit exit reproduce the money management rules from the source code.

Trading Logic

  1. ZigZag Trendlines
    • A manual ZigZag detector searches for alternating swing highs and lows using the configurable depth, deviation and backstep parameters.
    • The last four swing highs define the active resistance line, while the last four swing lows define the active support line. The strategy continuously extrapolates those lines into the current bar.
    • An entry signal is prepared when the closing price approaches a line within a fixed offset (10 points by default).
  2. Moving Average Filter
    • A fast simple moving average (length 2) and a slow simple moving average (length 50) filter the trend.
    • Short positions require the fast MA below the slow MA, whereas long positions require the fast MA above the slow MA.
  3. Order Execution
    • Signals are stored and activated on the next finished candle, matching the “new bar” logic of the MetaTrader version.
    • Before opening a position, the strategy verifies that the spread does not exceed the configured maximum and that no position is currently open.
  4. Risk Management
    • Stop-loss and take-profit distances are expressed in points and applied immediately after the order is filled.
    • A floating-profit target closes the position once unrealised profit (in price units times volume) exceeds the configured reward per lot.

Parameters

Name Description
Candle Type Time frame used for signal generation.
Volume Trade volume submitted with each entry.
ZigZag Depth Minimum number of bars between confirmed pivots.
ZigZag Deviation (pts) Minimum price move in points before the direction changes.
ZigZag Backstep Bars required before accepting an opposite pivot.
Trend Offset (pts) Distance from the trendline that triggers a signal.
Fast MA Length Length of the fast simple moving average.
Slow MA Length Length of the slow simple moving average.
Max Spread (pts) Maximum allowed spread, expressed in points.
Stop Loss (pts) Protective stop distance measured from the entry price.
Take Profit (pts) Profit target distance measured from the entry price.
Profit Target per Lot Floating profit required (price units × volume) to close the position.

Notes

  • Only one position is held at a time. Signals are ignored while a trade is open.
  • The spread filter relies on best bid/ask quotes, so the strategy should be connected to a data source providing level 1 information.
  • The Python version of the strategy is intentionally omitted as requested.

Files

  • CS/FXFSafeTrendScalpV1Strategy.cs – StockSharp implementation of the expert advisor.
namespace StockSharp.Samples.Strategies;

using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;

/// <summary>
/// FXF Safe Trend Scalp V1 strategy: SMA crossover with ATR volatility filter.
/// Enters long when fast SMA crosses above slow SMA and ATR confirms volatility.
/// Enters short when fast SMA crosses below slow SMA.
/// </summary>
public class FxfSafeTrendScalpV1Strategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _atrPeriod;
	private readonly StrategyParam<int> _signalCooldownCandles;

	private decimal _prevFast;
	private decimal _prevSlow;
	private bool _hasPrev;
	private int _candlesSinceTrade;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int AtrPeriod { get => _atrPeriod.Value; set => _atrPeriod.Value = value; }
	public int SignalCooldownCandles { get => _signalCooldownCandles.Value; set => _signalCooldownCandles.Value = value; }

	public FxfSafeTrendScalpV1Strategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
		_fastPeriod = Param(nameof(FastPeriod), 5)
			.SetGreaterThanZero()
			.SetDisplay("Fast Period", "Fast SMA period", "Indicators");
		_slowPeriod = Param(nameof(SlowPeriod), 20)
			.SetGreaterThanZero()
			.SetDisplay("Slow Period", "Slow SMA period", "Indicators");
		_atrPeriod = Param(nameof(AtrPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("ATR Period", "ATR period", "Indicators");
		_signalCooldownCandles = Param(nameof(SignalCooldownCandles), 3)
			.SetGreaterThanZero()
			.SetDisplay("Signal Cooldown", "Bars to wait between entries", "Trading");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevFast = 0m;
		_prevSlow = 0m;
		_hasPrev = false;
		_candlesSinceTrade = SignalCooldownCandles;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_hasPrev = false;
		_candlesSinceTrade = SignalCooldownCandles;
		var fast = new SimpleMovingAverage { Length = FastPeriod };
		var slow = new SimpleMovingAverage { Length = SlowPeriod };
		var atr = new AverageTrueRange { Length = AtrPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(fast, slow, atr, ProcessCandle).Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow, decimal atr)
	{
		if (candle.State != CandleStates.Finished) return;

		if (_candlesSinceTrade < SignalCooldownCandles)
			_candlesSinceTrade++;

		if (_hasPrev)
		{
			var close = candle.ClosePrice;
			var longSignal = _prevFast <= _prevSlow && fast > slow && close > slow + atr * 0.25m;
			var shortSignal = _prevFast >= _prevSlow && fast < slow && close < slow - atr * 0.25m;

			if (_candlesSinceTrade >= SignalCooldownCandles)
			{
				if (longSignal && Position <= 0)
				{
					BuyMarket();
					_candlesSinceTrade = 0;
				}
				else if (shortSignal && Position >= 0)
				{
					SellMarket();
					_candlesSinceTrade = 0;
				}
			}
		}

		_prevFast = fast;
		_prevSlow = slow;
		_hasPrev = true;
	}
}