Стратегия Day Trading – это порт оригинального советника MQL/24298/Day Trading.mq4 на платформу StockSharp. Алгоритм ищет сделки по тренду после отката к средней, комбинируя индикаторы EMA, Momentum и подтверждение по MACD на старшем таймфрейме. Все ключевые настройки вынесены в параметры стратегии и могут подстраиваться под инструмент.
Торговля ведётся по одной бумаге и по типу свечей, который задаётся пользователем. Ордера выставляются по рынку только после закрытия свечи, когда все фильтры выполняются. После входа немедленно рассчитываются фиксированные уровни стоп-лосса и тейк-профита.
Логика входа
Подтверждение тренда – последние TrendConfirmationCount свечей должны находиться по правильную сторону 100-периодной EMA. Для покупок минимумы свечей обязаны располагаться выше EMA, для продаж – максимумы ниже. Это повторяет работу функции candles() из исходника.
Откат к EMA20 – хотя бы одна из трёх предыдущих свечей должна протестировать 20-периодную EMA. Для длинных позиций низ свечи должен проколоть среднюю, для коротких – низ остаётся выше EMA (в оригинальном коде использовалось сравнение Low > EMA20, и этот критерий сохранён).
Фильтр Momentum – индикатор Momentum с периодом MomentumPeriod должен отклониться от базового уровня 100 больше, чем на MomentumThreshold, на любой из трёх последних свечей. Используется абсолютное отклонение abs(momentum - 100).
MACD на месяце – сделки разрешены только когда основная линия MACD на старшем таймфрейме выше сигнальной (для покупок) или ниже (для продаж). MACD считается на подписке MacdCandleType, по умолчанию это месячные свечи, с настройками 12/26/9.
Размер позиции – каждый новый ордер имеет объём Volume. Совокупная позиция не превышает Volume * MaxPositions. При смене сигнала открытая позиция переворачивается одним рыночным ордером.
Управление рисками – сразу после входа сохраняются значения стопа и цели, рассчитанные из параметров 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 ограничивает суммарный объём, а не количество отдельных сделок.
Как использовать
Подключите стратегию к источнику данных, который предоставляет свечи для рабочего таймфрейма и для подтверждения MACD.
Настройте параметры под инструмент. Повышение TrendConfirmationCount или MomentumThreshold сделает сигналы более редкими.
Запустите стратегию – сделки будут открываться автоматически после закрытия свечи, когда все фильтры дают разрешение.
Состав
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;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class day_trading_trend_pullback_strategy(Strategy):
def __init__(self):
super(day_trading_trend_pullback_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 14) \
.SetDisplay("Fast Period", "Fast MA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 50) \
.SetDisplay("Slow Period", "Slow MA period", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200) \
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 400) \
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def stop_loss_points(self):
return self._stop_loss_points.Value
@property
def take_profit_points(self):
return self._take_profit_points.Value
def OnReseted(self):
super(day_trading_trend_pullback_strategy, self).OnReseted()
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(day_trading_trend_pullback_strategy, self).OnStarted2(time)
self._fast = ExponentialMovingAverage()
self._fast.Length = self.fast_period
self._slow = ExponentialMovingAverage()
self._slow.Length = self.slow_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(5)))
subscription.Bind(self._fast, self._slow, self._process_candle)
subscription.Start()
def _process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_value)
slow_val = float(slow_value)
if not self._fast.IsFormed or not self._slow.IsFormed:
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
if self.Position > 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close <= self._entry_price - self.stop_loss_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close >= self._entry_price + self.take_profit_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close >= self._entry_price + self.stop_loss_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close <= self._entry_price - self.take_profit_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 100
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return day_trading_trend_pullback_strategy()