Утренняя стратегия отката в ценовом коридоре воспроизводит работу эксперта MetaTrader 4 «3_Otkat_Sys_v1_2». Она анализирует импульс, возникший за ночь, и торгует только в первые минуты утренней сессии, оценивая коррекцию относительно коридора, построенного по свечам с интервалом в 29 баров. Для длинных и коротких позиций задаются разные уровни фиксации прибыли.
Торговая логика
Фильтр по времени — сигналы рассматриваются лишь в заданный час (по умолчанию 05:00 по времени терминала) и только в первые минуты этого часа. Как и в оригинале, понедельник и пятница исключаются.
Расчёт ценового коридора — стратегия хранит скользящее окно последних свечей и сравнивает:
открытие 29 баров назад с закрытием предыдущей свечи (Open[29] - Close[1]),
закрытие предыдущей свечи с открытием 29 баров назад (Close[1] - Open[29]),
расстояние от предыдущего закрытия до минимального минимума за последние 29 баров,
расстояние от максимума за тот же период до предыдущего закрытия.
Условия входа — если ночной импульс превосходит порог CorridorOpenClosePoints, а текущий откат попадает в диапазон PullbackPoints ± CorridorPullbackPoints, открывается рыночная позиция:
Для покупок требуется либо ночное падение с неглубокой коррекцией, либо продолжение роста выше коридора.
Для продаж используется зеркальный набор условий.
Управление позицией — при открытии сделки устанавливаются:
стоп-лосс на расстоянии StopLossPoints * PriceStep от цены входа,
тейк-профит на уровне TakeProfitPoints * PriceStep для коротких и (TakeProfitPoints + LongTakeProfitExtraPoints) * PriceStep для длинных позиций.
Закрытие в конце дня — если к моменту, превышающему заданный порог (по умолчанию после 22:45), позиция остаётся открытой, она закрывается принудительно.
Параметры
Параметр
Описание
TakeProfitPoints
Базовая дистанция тейк-профита в пунктах. Для покупок добавляется LongTakeProfitExtraPoints.
StopLossPoints
Дистанция защитного стопа в пунктах.
PullbackPoints
Целевая глубина отката, вокруг которой анализируются условия входа.
CorridorOpenClosePoints
Минимальная разница между ценами, разделёнными 29 барами, подтверждающая ночной импульс.
CorridorPullbackPoints
Допуск, добавляемый к целевому откату для формирования коридора.
LongTakeProfitExtraPoints
Дополнительные пункты тейк-профита для длинных позиций.
TradeHour
Час (0–23), в течение которого разрешено открывать новые позиции.
TradeMinuteLimit
Максимальная минута внутри торгового часа, допускающая появление сигнала.
CloseHour
Час, начиная с которого проверяется условие принудительного закрытия.
CloseMinuteThreshold
Минута внутри CloseHour, после которой позиция закрывается.
CandleType
Используемый таймфрейм свечей (по умолчанию 1 минута).
Особенности реализации
Для перевода точечных значений в абсолютные цены используется Security.PriceStep. При отсутствии корректного шага цена по умолчанию принимается равной 1.0.
Стоп-лосс и тейк-профит контролируются на каждой завершённой свече: при пробое уровня сделка закрывается рыночным ордером.
Список хранит последние 60 свечей, что покрывает требуемые 29 баров и имитирует функции Lowest/Highest из MQL.
При наличии графической области настраиваемый компонент автоматически рисует свечи и сделки стратегии.
Рекомендации по использованию
Перед запуском задайте величину Volume — советник не меняет размер позиции динамически.
Согласуйте часовой пояс данных с тем, который использовался в оригинальной системе, чтобы временные фильтры срабатывали корректно.
Пороговые значения, выраженные в пунктах, необходимо оптимизировать для инструментов с другой волатильностью.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class MorningPullbackCorridorStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _cooldownCandles;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevFast;
private decimal _prevSlow;
private bool _hasPrev;
private int _cooldownRemaining;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public int CooldownCandles { get => _cooldownCandles.Value; set => _cooldownCandles.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public MorningPullbackCorridorStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 20).SetDisplay("Fast EMA", "Fast EMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 80).SetDisplay("Slow EMA", "Slow EMA period", "Indicators");
_cooldownCandles = Param(nameof(CooldownCandles), 100).SetDisplay("Cooldown", "Candles between signals", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame()).SetDisplay("Candle Type", "Candle timeframe", "General");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevFast = default;
_prevSlow = default;
_hasPrev = default;
_cooldownRemaining = default;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevFast = 0;
_prevSlow = 0;
_hasPrev = false;
_cooldownRemaining = 0;
var fast = new ExponentialMovingAverage { Length = FastPeriod };
var slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(fast, slow, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
{
if (candle.State != CandleStates.Finished) return;
if (!_hasPrev) { _prevFast = fast; _prevSlow = slow; _hasPrev = true; return; }
if (_cooldownRemaining > 0)
{
_cooldownRemaining--;
_prevFast = fast;
_prevSlow = slow;
return;
}
if (_prevFast <= _prevSlow && fast > slow && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
_cooldownRemaining = CooldownCandles;
}
else if (_prevFast >= _prevSlow && fast < slow && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
_cooldownRemaining = CooldownCandles;
}
_prevFast = fast;
_prevSlow = slow;
}
}