Адаптация советника MT4 "5MIN SCALPING" (MQL ID 22828) на инфраструктуру StockSharp с использованием высокоуровневого API. Стратегия ищет быстрые пробойные сетапы на базовом таймфрейме, подтверждает их импульсом старшего горизонта и направлением месячного MACD, а затем открывает сделку.
Категория: Скальпинг / пробой с подтверждением импульсом
Оригинальная платформа: MetaTrader 4
Требования к данным: Поток тиков или свечей для всех используемых таймфреймов (по умолчанию 5 минут, 30 минут, 1 месяц)
Логика торговли
Фильтр тренда. Две линейно-взвешенные скользящие средние (LWMA) с настраиваемыми периодами (по умолчанию 6 и 85) определяют направление тренда. Для покупок требуется, чтобы быстрая LWMA была выше медленной, для продаж — наоборот.
Паттерн из нескольких свечей. Внутренний набор LWMA с периодами 8, 13 и 21 анализирует последние 20 завершённых свечей, повторяя логику функции scalper() из MQL:
Для лонга каждая свеча в окне должна удовлетворять условиям LWMA8 > LWMA13 > LWMA21, минимум свечи откатывается в область средних, а текущая цена закрытия пробивает максимум предыдущих 5 свечей.
Для шорта используется зеркальная проверка: максимум свечи касается диапазона средних, а текущее закрытие опускается ниже минимума предыдущих 5 свечей.
Защита от ложных всплесков. Дополнительное условие перекрытия (Low[2] < High[1] для покупок, Low[1] < High[2] для продаж) исключает одиночные шпильки без структуры.
Импульсное подтверждение. Индикатор Momentum на старшем таймфрейме (по умолчанию 30-минутные свечи, период 14) должен показать, что хотя бы одно из трёх последних значений отклоняется от базового уровня 100 больше, чем заданный порог (0.3 по умолчанию).
Месячный MACD. Рассчитывается MACD(12, 26, 9) на месячных свечах. Для входа в лонг линия MACD должна находиться выше сигнальной, для шорта — ниже.
Агрегация позиции. Переход из одной стороны в другую автоматически закрывает текущую позицию и открывает новую с указанным объёмом.
Управление рисками
Фиксированные цели. Необязательные уровни тейк-профита и стоп-лосса, задаваемые в пунктах (конвертируются через PriceStep).
Переход в безубыток. При включении стоп переносится на уровень входа ± смещение, как только цена проходит заданное расстояние.
Трейлинг-стоп. Необязательный трейлинг, следующий за ценой на фиксированное число пунктов.
Ручные выходы. Все закрытия выполняются внутри стратегии без постановки защитных заявок, что повторяет поведение оригинального эксперта.
Параметры
Параметр
Значение по умолчанию
Описание
CandleType
Таймфрейм 5 минут
Основной таймфрейм, на котором ищется пробой.
MomentumCandleType
Таймфрейм 30 минут
Свечи для импульсного подтверждения.
MacroMacdCandleType
Таймфрейм 1 месяц
Свечи для расчёта долгосрочного MACD.
FastMaLength
6
Период быстрой LWMA.
SlowMaLength
85
Период медленной LWMA.
MomentumLength
14
Длина окна индикатора Momentum.
MomentumBuyThreshold
0.3
Минимальное отклонение
Momentum-100
для подтверждения покупок.
MomentumSellThreshold
0.3
Минимальное отклонение
Momentum-100
для подтверждения продаж.
TakeProfitPips
50
Дистанция тейк-профита в пунктах (0 — отключить).
StopLossPips
20
Дистанция стоп-лосса в пунктах (0 — отключить).
TrailingStopPips
40
Дистанция трейлинг-стопа в пунктах (работает при включённом EnableTrailing).
EnableTrailing
true
Включает или отключает трейлинг-стоп.
EnableBreakEven
true
Включает автоматический переход в безубыток.
BreakEvenTriggerPips
30
Прибыль в пунктах, после которой активируется безубыток.
BreakEvenOffsetPips
30
Дополнительный буфер (в пунктах) при переносе стопа в безубыток.
TradeVolume
1
Объём заявки при входе.
Использование
Добавьте стратегию в проект StockSharp и выберите нужный инструмент.
Убедитесь, что история по всем требуемым таймфреймам загружена до запуска.
Настройте объём, таймфреймы и пороги под волатильность выбранного инструмента.
Запустите стратегию: она подпишется на нужные серии свечей, построит индикаторы (при наличии графика) и будет автоматически управлять сделками.
Отличия от исходного советника
Денежные модули (Take_Profit_In_Money, TRAIL_PROFIT_IN_MONEY2) и блок стопа по эквити не перенесены — управление рисками выполняется через пунктовые расстояния.
Мартингейл-увеличение лота (Lots * MathPow(LotExponent, CountTrades())) не реализовано. При необходимости корректируйте TradeVolume вручную.
Почтовые и push-уведомления из оригинального кода опущены. При необходимости используйте уведомления StockSharp.
Конвертация пунктов опирается на PriceStep инструмента. Проверьте корректность параметров инструмента в вашем соединении.
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>
/// 5-minute scalping strategy using fast/slow WMA crossover with momentum confirmation.
/// Buys on bullish crossover, sells on bearish crossover.
/// </summary>
public class FiveMinScalpingStrategy : 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 a new instance of the <see cref="FiveMinScalpingStrategy"/> class.
/// </summary>
public FiveMinScalpingStrategy()
{
_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), 500)
.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 = 60;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step)
{
SellMarket();
_entryPrice = 0;
_cooldown = 60;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step)
{
BuyMarket();
_entryPrice = 0;
_cooldown = 60;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step)
{
BuyMarket();
_entryPrice = 0;
_cooldown = 60;
_prevFast = fastValue;
_prevSlow = slowValue;
return;
}
}
// WMA crossover buy
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
_entryPrice = close;
_cooldown = 60;
}
// WMA crossover sell
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
_entryPrice = close;
_cooldown = 60;
}
_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 WeightedMovingAverage
from StockSharp.Algo.Strategies import Strategy
class five_min_scalping_strategy(Strategy):
"""
5-minute scalping using fast/slow WMA crossover with SL/TP and cooldown.
Buys on bullish crossover, sells on bearish crossover.
"""
def __init__(self):
super(five_min_scalping_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 20) \
.SetDisplay("Fast Period", "Fast WMA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 85) \
.SetDisplay("Slow Period", "Slow WMA period", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200) \
.SetDisplay("Stop Loss", "Stop-loss distance in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 500) \
.SetDisplay("Take Profit", "Take-profit distance in price steps", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Trading timeframe", "General")
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(five_min_scalping_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(five_min_scalping_strategy, self).OnStarted2(time)
fast = WeightedMovingAverage()
fast.Length = self._fast_period.Value
slow = WeightedMovingAverage()
slow.Length = self._slow_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(fast, slow, self._process_candle).Start()
def _process_candle(self, candle, fast_val, slow_val):
if candle.State != CandleStates.Finished:
return
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_fast = float(fast_val)
self._prev_slow = float(slow_val)
return
fast_val = float(fast_val)
slow_val = float(slow_val)
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
step = 1.0
if self.Security is not None and self.Security.PriceStep is not None:
step = float(self.Security.PriceStep)
if step <= 0:
step = 1.0
sl_pts = self._stop_loss_points.Value
tp_pts = self._take_profit_points.Value
if self.Position > 0 and self._entry_price > 0:
if sl_pts > 0 and close <= self._entry_price - sl_pts * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 60
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if tp_pts > 0 and close >= self._entry_price + tp_pts * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 60
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if sl_pts > 0 and close >= self._entry_price + sl_pts * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 60
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if tp_pts > 0 and close <= self._entry_price - tp_pts * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 60
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 = 60
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 = 60
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return five_min_scalping_strategy()