Стратегия реализует эксперта MetaTrader 4 «MoStAsHaR15 FoReX - Pivot Line» на высокоуровневом API StockSharp. Сохраняется классическая сетка дневных уровней поддержки/сопротивления, усиленная индикаторами ADX, спредом между EMA по ценам закрытия и открытия, а также гистограммой MACD (OsMA). Торговые решения принимаются по часовым свечам, а параллельная подписка на дневной таймфрейм каждый раз пересчитывает сетку pivot до обработки очередной свечи.
Логика торговли
Расчёт pivot – максимумы, минимумы и закрытие предыдущего дня формируют классический pivot (P), уровни сопротивления R1–R3, уровни поддержки S1–S3 и промежуточные отметки M0–M5. Текущее закрытие сравнивается с этой лестницей для определения актуального диапазона. Сохраняется оригинальная особенность, когда диапазон между M5 и R3 сопоставляется с парой S3/M0.
Фильтр расстояния – сделка открывается только при расстоянии до целевого уровня больше MinimumDistancePips (по умолчанию 14 пунктов), что соответствует проверкам dif1/dif2 в исходном коде.
Условия для покупки:
Линия ADX выше порога AdxThreshold (20), +DI растёт и превосходит −DI.
EMA по закрытиям минимум на EmaSpreadPips (5 пунктов) выше EMA по открытиям, и на предыдущей свече соблюдался такой же бычий порядок.
Гистограмма MACD увеличилась относительно предыдущей свечи.
Условия для продажи – зеркальное отражение бычьих правил: усиление −DI, медвежий спред EMA и падающая гистограмма MACD.
Одновременно разрешён только один чистый объём. Сделки открываются рыночными приказами BuyMarket()/SellMarket().
Управление позицией
Стоп-лосс – опциональный, располагается на расстоянии StopLossPips от цены входа. Значение 0 отключает исходный стоп, как и в EA.
Тейк-профит – фиксируется на границе диапазона (поддержка или сопротивление), в котором находилась цена при входе.
Трейлинг-стоп – после прохождения ценой расстояния TrailingStopPips + TrailingStepPips стоп подтягивается, сохраняя расстояние TrailingStopPips. При включении трейлинга параметр шага обязан быть положительным.
При касании стопа, трейлинга или цели в пределах свечи позиция закрывается в обработке этой свечи.
Параметры стратегии
Параметр
Описание
Значение по умолчанию
HourlyCandleType
Часовой поток свечей для торговой логики.
1 час
DailyCandleType
Дневной поток свечей для расчёта pivot.
1 день
StopLossPips
Размер стартового стоп-лосса в пунктах (0 — отключить).
20
TrailingStopPips
Размер трейлинг-стопа в пунктах.
10
TrailingStepPips
Минимальное движение в пунктах для обновления трейлинга (должно быть > 0 при включении).
5
MinimumDistancePips
Минимальное расстояние до целевого уровня перед входом.
14
EmaSpreadPips
Требуемый спред между EMA по закрытиям и открытиям.
5
AdxThreshold
Минимальное значение ADX для активации сигнала.
20
AdxPeriod
Период индикатора ADX.
14
EmaClosePeriod
Период EMA по ценам закрытия.
5
EmaOpenPeriod
Период EMA по ценам открытия.
8
MacdFastPeriod
Быстрый период EMA в MACD (числитель OsMA).
12
MacdSlowPeriod
Медленный период EMA в MACD.
26
MacdSignalPeriod
Период сигнальной EMA в MACD.
9
Примечания по конвертации
Все индикаторы рассчитываются только на завершённых свечах; коллекции не создаются, состояние хранится в полях, как требует руководство репозитория.
Размер пункта выводится из PriceStep и количества знаков у инструмента. Для котировок с 3 или 5 знаками применяется «мини-пункт», как в MetaTrader.
Специфическое сопоставление диапазона M5→R3 с парой S3/M0 сохранено для точного соответствия оригинальному эксперту.
Комментарии в коде выполнены на английском языке согласно проектным правилам.
Рекомендации по использованию
Подбирайте таймфреймы под торговый режим инструмента, особенно если дневной клиринг отличается от стандартного.
Поскольку проверка стопов происходит по закрытию свечи, в быстрых рынках возможно большее проскальзывание по сравнению с тиковым исполнением в MetaTrader.
Для инструментов с другой волатильностью подстраивайте фильтры MinimumDistancePips и EmaSpreadPips.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// MoStAsHaR15 Pivot Line strategy - EMA crossover with ADX trend filter.
/// Buys when fast EMA crosses above slow EMA and ADX confirms trend.
/// Sells when fast EMA crosses below slow EMA and ADX confirms trend.
/// </summary>
public class MoStAsHaR15PivotLineStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _adxPeriod;
private readonly StrategyParam<decimal> _adxThreshold;
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 AdxPeriod { get => _adxPeriod.Value; set => _adxPeriod.Value = value; }
public decimal AdxThreshold { get => _adxThreshold.Value; set => _adxThreshold.Value = value; }
public int CooldownCandles { get => _cooldownCandles.Value; set => _cooldownCandles.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public MoStAsHaR15PivotLineStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 20)
.SetDisplay("Fast EMA", "Fast EMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 100)
.SetDisplay("Slow EMA", "Slow EMA period", "Indicators");
_adxPeriod = Param(nameof(AdxPeriod), 14)
.SetDisplay("ADX Period", "ADX lookback", "Indicators");
_adxThreshold = Param(nameof(AdxThreshold), 20m)
.SetDisplay("ADX Threshold", "Min ADX for trending", "Levels");
_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;
}
}