Открыть на GitHub

Стратегия Day Trading Trend Pullback

Общее описание

Стратегия Day Trading – это порт оригинального советника MQL/24298/Day Trading.mq4 на платформу StockSharp. Алгоритм ищет сделки по тренду после отката к средней, комбинируя индикаторы EMA, Momentum и подтверждение по MACD на старшем таймфрейме. Все ключевые настройки вынесены в параметры стратегии и могут подстраиваться под инструмент.

Торговля ведётся по одной бумаге и по типу свечей, который задаётся пользователем. Ордера выставляются по рынку только после закрытия свечи, когда все фильтры выполняются. После входа немедленно рассчитываются фиксированные уровни стоп-лосса и тейк-профита.

Логика входа

  1. Подтверждение тренда – последние TrendConfirmationCount свечей должны находиться по правильную сторону 100-периодной EMA. Для покупок минимумы свечей обязаны располагаться выше EMA, для продаж – максимумы ниже. Это повторяет работу функции candles() из исходника.
  2. Откат к EMA20 – хотя бы одна из трёх предыдущих свечей должна протестировать 20-периодную EMA. Для длинных позиций низ свечи должен проколоть среднюю, для коротких – низ остаётся выше EMA (в оригинальном коде использовалось сравнение Low > EMA20, и этот критерий сохранён).
  3. Фильтр Momentum – индикатор Momentum с периодом MomentumPeriod должен отклониться от базового уровня 100 больше, чем на MomentumThreshold, на любой из трёх последних свечей. Используется абсолютное отклонение abs(momentum - 100).
  4. MACD на месяце – сделки разрешены только когда основная линия MACD на старшем таймфрейме выше сигнальной (для покупок) или ниже (для продаж). MACD считается на подписке MacdCandleType, по умолчанию это месячные свечи, с настройками 12/26/9.
  5. Размер позиции – каждый новый ордер имеет объём Volume. Совокупная позиция не превышает Volume * MaxPositions. При смене сигнала открытая позиция переворачивается одним рыночным ордером.
  6. Управление рисками – сразу после входа сохраняются значения стопа и цели, рассчитанные из параметров StopLossPips и TakeProfitPips. На каждой закрывшейся свече проверяется, не были ли достигнуты уровни, и при необходимости позиция закрывается.

Параметры

Параметр Описание Значение по умолчанию
Volume Базовый торговый объём. Нормализуется по шагу объёма инструмента. 1
CandleType Рабочий таймфрейм для расчётов. TimeSpan.FromMinutes(15).TimeFrame()
MacdCandleType Таймфрейм для подтверждения MACD. TimeSpan.FromDays(30).TimeFrame()
TrendConfirmationCount Число свечей, которые должны удерживаться по одну сторону EMA100. Аналог параметра Count. 10
MomentumPeriod Период индикатора Momentum. 14
MomentumThreshold Минимальное отклонение Momentum от уровня 100. 0.3
StopLossPips Размер стоп-лосса в пунктах. 20
TakeProfitPips Размер тейк-профита в пунктах. 50
MaxPositions Максимальное количество базовых лотов в одном направлении. 10

Особенности реализации

  • Используется высокоуровневое API: основная подписка привязывает EMA20/60/100 и Momentum, а отдельная – MACD по старшему таймфрейму через BindEx.
  • Для имитации обращений к истории, присутствовавших в MQL, применяются кольцевые очереди. Это исключает прямой доступ к значениям индикаторов по индексам.
  • Функция перевода пунктов в цену подбирает размер пункта из PriceStep, воспроизводя расчёт переменной pips в оригинале.
  • Метод StartProtection() запускается при старте, чтобы активировать встроенные защитные механизмы фреймворка.

Отличия от исходника

  • Финансовые блоки (контроль общей прибыли/убытка, перевод в безубыток, пользовательский трейлинг) не перенесены. При необходимости их можно добавить поверх реализованной логики.
  • Удалены уведомления по почте/Push и элементы оформления на графике.
  • В StockSharp позиции агрегируются, поэтому MaxPositions ограничивает суммарный объём, а не количество отдельных сделок.

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

  1. Подключите стратегию к источнику данных, который предоставляет свечи для рабочего таймфрейма и для подтверждения MACD.
  2. Настройте параметры под инструмент. Повышение TrendConfirmationCount или MomentumThreshold сделает сигналы более редкими.
  3. Запустите стратегию – сделки будут открываться автоматически после закрытия свечи, когда все фильтры дают разрешение.

Состав

  • CS/DayTradingStrategy.cs – реализация на C#.
  • README.md – описание на английском языке.
  • README_zh.md – описание на китайском языке.
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 DayTradingTrendPullbackStrategy : 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 DayTradingTrendPullbackStrategy()
	{
		_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;
	}
}