Открыть на GitHub

Стратегия Fifteen Minute Scalper

Стратегия переносит советник 15M Scalper из MetaTrader на высокоуровневый API StockSharp. Реализована та же комбинация фильтров (взвешенные средние, стохастик, Parabolic SAR, многофреймовый Momentum и месячный MACD) и полный набор защит (тейк-профит в пунктах и деньгах, трейлинг, перевод в безубыток, стоп по просадке капитала). Алгоритм работает по закрытию свечей, как и оригинал, а параметры полностью повторяют входные настройки MQL.

Логика работы

  1. Трендовый фильтр – на рабочем таймфрейме (по умолчанию 15 минут) рассчитываются две LWMA по типичной цене ((High + Low + Close) / 3). Для длинного сигнала быстрая средняя должна быть выше медленной, для короткого – наоборот.
  2. Реверс стохастика – стохастик 5/3/3 анализируется по двум последним закрытым барам. Длинная сделка открывается при возврате %K выше 20, короткая – при пробое 80 вниз (аналог условий Stoc1/Stoc2).
  3. Подтверждение SAR – значение Parabolic SAR на завершённой свече должно располагаться ниже открытия предыдущего бара (для покупок) или выше (для продаж), что повторяет проверку SAR < Open[1] / SAR > Open[1].
  4. Momentum старшего таймфрейма – индикатор Momentum с периодом 14 на настраиваемом старшем таймфрейме (по умолчанию 1 час) должен отклониться от уровня 100 на величину, не меньшую порога для покупок/продаж, хотя бы на одном из трёх последних баров.
  5. Месячный MACD – MACD, построенный по месячным свечам (в версии StockSharp – по 30-дневным барам), требует, чтобы основная линия находилась выше сигнальной для лонга и ниже для шорта. Этот же MACD используется и как выходной фильтр при включённом параметре UseExitByMacd.
  6. Обработка сделок – при появлении противоположного сигнала стратегия сначала закрывает текущую позицию и только на следующем баре рассматривает вход в новую сторону. Размер лота масштабируется по LotExponent, а IncreaseFactor увеличивает базовый объём при серии убытков.

Управление рисками

  • Стоп-лосс / тейк-профит – задаются в "пунктах" MetaTrader и пересчитываются в абсолютное расстояние через Security.PriceStep. Для инструментов с дробным шагом (5/3 знака) шаг умножается на 10, что имитирует работу MQL.
  • Перевод в безубыток – после прохождения расстояния BreakEvenTriggerSteps стоп переносится к цене входа плюс BreakEvenOffsetSteps. Если цена вернулась обратно – позиция закрывается рыночной заявкой.
  • Трейлинг-стоп – отслеживает максимум/минимум после входа. Как только откат превышает TrailingStopSteps, позиция закрывается (полный аналог OrderModify из оригинала).
  • Денежные цели – параметры UseProfitTargetMoney, UseProfitTargetPercent и EnableMoneyTrailing оперируют плавающей прибылью, рассчитанной как количество шагов цены × StepPrice. Поддерживаются фиксированный тейк-профит, процентная цель и трейлинг по деньгам (MoneyTrailingStop).
  • Стоп по капиталуUseEquityStop отслеживает максимум суммы (начальный капитал + реализованная прибыль + плавающий результат). При просадке больше TotalEquityRisk процентов вся позиция закрывается – так же, как в функции AccountEquityHigh().
  • Мартингейл – каждый дополнительный вход умножает объём на LotExponent. Если подряд получено более одной убыточной сделки, IncreaseFactor добавляет к базовому объёму надбавку, пропорциональную числу убытков.

Параметры

Параметр Описание
CandleType Рабочий таймфрейм (по умолчанию 15-минутные свечи).
MomentumCandleType Таймфрейм для фильтра Momentum (по умолчанию часовые свечи).
MacdCandleType Таймфрейм для MACD (по умолчанию 30-дневные свечи).
FastMaPeriod, SlowMaPeriod Периоды LWMA, определяющих направление тренда.
MomentumPeriod Период Momentum на старшем таймфрейме.
MomentumBuyThreshold, MomentumSellThreshold Минимальное отклонение от 100 для допуска сделок в Long/Short.
StopLossSteps, TakeProfitSteps Дистанции стоп-лосса и тейк-профита в шагах цены (0 – отключить).
TrailingStopSteps Размер трейлинг-стопа в шагах цены.
UseMoveToBreakeven, BreakEvenTriggerSteps, BreakEvenOffsetSteps Включение перевода в безубыток, расстояние и смещение.
UseProfitTargetMoney, ProfitTargetMoney Денежный тейк-профит и флаг его использования.
UseProfitTargetPercent, ProfitTargetPercent Процентный тейк-профит от стартового капитала.
EnableMoneyTrailing, MoneyTrailingTakeProfit, MoneyTrailingStop Параметры денежного трейлинг-стопа.
UseEquityStop, TotalEquityRisk Включение стопа по капиталу и допустимая просадка от максимума.
BaseVolume, LotExponent, IncreaseFactor, MaxTrades Настройки мартингейла: базовый лот, мультипликатор, надбавка после убытков, максимум доборов.
UseExitByMacd Закрывать позиции при обратном пересечении MACD.

Как использовать

  1. Привяжите стратегию к инструменту и убедитесь, что заданы Security.PriceStep и Security.StepPrice. Без этих значений денежные цели работать не будут.
  2. При необходимости измените CandleType, MomentumCandleType и MacdCandleType, чтобы адаптировать стратегию под другие таймфреймы. Значения по умолчанию соответствуют оригинальному советнику (M15/H1/Monthly).
  3. Настройте параметры, выраженные в пунктах (StopLossSteps, TakeProfitSteps, трейлинг, безубыток) с учётом волатильности и шага цены инструмента.
  4. Определите правила управления капиталом: фиксированные или процентные тейк-профиты, денежный трейлинг, стоп по просадке.
  5. Запустите стратегию – необходимые подписки на свечи создаются автоматически, индикаторы отображаются на графике при его наличии, сделки начинают исполняться после формирования всех индикаторов.

Особенности и отличия от MQL-версии

  • В StockSharp используется агрегированная позиция. При смене сигнала текущая позиция закрывается и новая оценивается со следующей свечи – это избавляет от наложения разнонаправленных ордеров.
  • Денежные расчёты зависят от Security.PriceStep и Security.StepPrice. Если значения недоступны, плавающая прибыль считается нулевой, а денежные цели фактически отключаются (как и в MetaTrader при отсутствии данных).
  • IncreaseFactor в переносе увеличивает базовый объём на IncreaseFactor × количество_убытков, поскольку в тестовой среде нет информации о свободной марже. Поведение остаётся аналогичным задумке автора.
  • Все решения принимаются только по закрытым свечам – как и в оригинальном коде, где использовались сдвиги iMA/iStochastic по историческим барам.
  • Если доступен график, стратегия выводит на него те же индикаторы, что облегчает визуальное сравнение с MetaTrader.

Перед реальной торговлей обязательно проверьте шаг цены, стоимость шага и ограничения по объёму для выбранного инструмента. Эти величины напрямую влияют на преобразование пунктов и расчёт денежных целей внутри стратегии.

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;

/// <summary>
/// 15-minute scalper strategy using WMA crossover with ParabolicSar confirmation.
/// Buys when fast WMA crosses above slow WMA and SAR is below price.
/// Sells on reverse conditions.
/// </summary>
public class FifteenMinuteScalperStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private WeightedMovingAverage _fast;
	private WeightedMovingAverage _slow;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;
	private int _cooldown;

	/// <summary>
	/// Fast WMA period.
	/// </summary>
	public int FastPeriod
	{
		get => _fastPeriod.Value;
		set => _fastPeriod.Value = value;
	}

	/// <summary>
	/// Slow WMA period.
	/// </summary>
	public int SlowPeriod
	{
		get => _slowPeriod.Value;
		set => _slowPeriod.Value = value;
	}

	/// <summary>
	/// Stop-loss distance in price steps.
	/// </summary>
	public int StopLossPoints
	{
		get => _stopLossPoints.Value;
		set => _stopLossPoints.Value = value;
	}

	/// <summary>
	/// Take-profit distance in price steps.
	/// </summary>
	public int TakeProfitPoints
	{
		get => _takeProfitPoints.Value;
		set => _takeProfitPoints.Value = value;
	}

	/// <summary>
	/// Initializes strategy parameters.
	/// </summary>
	public FifteenMinuteScalperStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 20)
			.SetGreaterThanZero()
			.SetDisplay("Fast Period", "Fast WMA period", "Indicator");

		_slowPeriod = Param(nameof(SlowPeriod), 85)
			.SetGreaterThanZero()
			.SetDisplay("Slow Period", "Slow WMA period", "Indicator");

		_stopLossPoints = Param(nameof(StopLossPoints), 200)
			.SetNotNegative()
			.SetDisplay("Stop Loss", "Stop-loss distance in price steps", "Risk");

		_takeProfitPoints = Param(nameof(TakeProfitPoints), 400)
			.SetNotNegative()
			.SetDisplay("Take Profit", "Take-profit distance in price steps", "Risk");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_fast = null;
		_slow = null;
		_prevFast = 0;
		_prevSlow = 0;
		_entryPrice = 0;
		_cooldown = 0;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_fast = new WeightedMovingAverage { Length = FastPeriod };
		_slow = new WeightedMovingAverage { 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;

		// Check SL/TP
		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step)
			{
				SellMarket();
				_entryPrice = 0;
				_cooldown = 80;
				_prevFast = fastValue;
				_prevSlow = slowValue;
				return;
			}

			if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step)
			{
				SellMarket();
				_entryPrice = 0;
				_cooldown = 80;
				_prevFast = fastValue;
				_prevSlow = slowValue;
				return;
			}
		}
		else if (Position < 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step)
			{
				BuyMarket();
				_entryPrice = 0;
				_cooldown = 80;
				_prevFast = fastValue;
				_prevSlow = slowValue;
				return;
			}

			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step)
			{
				BuyMarket();
				_entryPrice = 0;
				_cooldown = 80;
				_prevFast = fastValue;
				_prevSlow = slowValue;
				return;
			}
		}

		// WMA crossover
		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();

			BuyMarket();
			_entryPrice = close;
			_cooldown = 80;
		}
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{
			if (Position > 0)
				SellMarket();

			SellMarket();
			_entryPrice = close;
			_cooldown = 80;
		}

		_prevFast = fastValue;
		_prevSlow = slowValue;
	}
}