Открыть на GitHub

FXF Safe Trend Scalp V1 (C#)

Стратегия FXF Safe Trend Scalp V1 воспроизводит оригинального советника MetaTrader 4 и торгует от динамических линий тренда, построенных по крайним точкам ZigZag. Когда цена вплотную подходит к сопротивлению или поддержке и направление подтверждается парой скользящих средних, на следующей завершенной свече открывается позиция. Сразу после входа выставляются стоп‑лосс, тейк‑профит и контроль общей плавающей прибыли, как и в исходном коде.

Логика торговли

  1. Трендовые линии ZigZag
    • Ручной детектор ZigZag с параметрами глубины, отклонения и шага назад ищет чередующиеся экстремумы.
    • Последние четыре максимума формируют текущую линию сопротивления, последние четыре минимума — линию поддержки. Линии экстраполируются в текущую свечу.
    • Сигнал фиксируется, когда цена оказывается ближе заданного отступа (по умолчанию 10 пунктов) от соответствующей линии.
  2. Фильтр скользящих средних
    • Быстрая SMA с периодом 2 и медленная SMA с периодом 50 задают направление сделок.
    • Для продаж требуется, чтобы быстрая SMA была ниже медленной, для покупок — выше.
  3. Исполнение ордеров
    • Сигналы активируются на следующей завершенной свече, что соответствует функции isNewBar() из MQL.
    • Перед входом проверяется отсутствие открытых позиций и соответствие текущего спрэда допустимому максимуму.
  4. Управление рисками
    • Стоп‑лосс и тейк‑профит задаются в пунктах и устанавливаются сразу после сделки.
    • Позиция закрывается, если плавающая прибыль (разница цены × объем) превышает настроенное значение на один лот.

Параметры

Название Описание
Candle Type Таймфрейм, используемый для расчетов.
Volume Объем каждой сделки.
ZigZag Depth Минимальное число свечей между экстремумами.
ZigZag Deviation (pts) Минимальное изменение цены (в пунктах) перед сменой направления.
ZigZag Backstep Число свечей, которые нужно подождать перед противоположным экстремумом.
Trend Offset (pts) Допустимое расстояние до трендовой линии для формирования сигнала.
Fast MA Length Период быстрой SMA.
Slow MA Length Период медленной SMA.
Max Spread (pts) Максимально разрешенный спрэд в пунктах.
Stop Loss (pts) Размер стоп‑лосса от цены входа.
Take Profit (pts) Размер тейк‑профита от цены входа.
Profit Target per Lot Требуемая плавающая прибыль на один лот для досрочного закрытия.

Особенности

  • Открывается не более одной позиции одновременно; новые сигналы игнорируются, пока позиция не закрыта.
  • Для работы фильтра по спрэду необходимы котировки уровня Level 1 (лучшая цена Bid/Ask).
  • По заданию Python-версия не создается, папка PY отсутствует.

Файлы

  • CS/FXFSafeTrendScalpV1Strategy.cs — реализация стратегии на StockSharp.
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;
	}
}