Стратегия представляет собой прямую конверсию советника MetaTrader 4 up3x1_Premium_2vM. Она работает с одним инструментом и никогда не удерживает более одной позиции одновременно. Для входа используются сглаженные скользящие средние, мощные свечные диапазоны и фильтр пробоя дневного бара в полночь. Управление рисками выполняется фиксированными уровнями тейк-профита и стоп-лосса в ценовых пунктах, а опциональный трейлинг-стоп повторяет оригинальный алгоритм, который подтягивает защитный уровень по мере движения цены в прибыль.
Логика работы
Основной таймфрейм настраивается пользователем; по умолчанию он соответствует таймфрейму графика в MT4. На этой серии свечей рассчитываются две сглаженные скользящие (SMMA) с периодами 12 и 26 и ценой Typical.
Дополнительная подписка на дневные свечи воссоздаёт поток PERIOD_D1, использовавшийся в MQL, и питает 10-периодную дневную SMA.
При отсутствии позиции анализируются две последние завершённые свечи и сохранённые значения SMMA:
Длинный сигнал: быстарая SMMA пересекает медленную снизу вверх при растущих ценах открытия; либо последняя свеча удовлетворяет порогам диапазона и тела; либо предыдущий дневной бар бычий и имеет достаточный размах. Оригинальный код также сравнивал дневную SMA с ценой Ask. Условие всегда истинно, поэтому для совместимости оно сохранено.
Короткий сигнал: зеркальные условия для шорта, основанные на пересечении вниз и медвежьем диапазоне.
Если выполняется хоть одно длинное условие, отправляется рыночная покупка. Иначе, при выполнении любого короткого условия отправляется рыночная продажа. Перед отправкой объём приводится к шагу объёма инструмента.
При открытой позиции стратегия отслеживает предыдущие значения SMMA. Когда их разница становится меньше ConvergenceTolerance, позиция закрывается, что повторяет сравнение на равенство в советнике.
Модуль трейлинг-стопа отслеживает среднюю цену входа. После прохождения ценой дистанции трейлинга стоп переносится, сохраняя заданный отступ. При касании уровня позиция закрывается сразу, имитируя повторные вызовы OrderModify в MQL.
Параметры
Имя
Значение по умолчанию
Описание
CandleType
TimeFrame(1h)
Основной таймфрейм.
FastMaPeriod
12
Период быстрой SMMA по типичной цене.
SlowMaPeriod
26
Период медленной SMMA по типичной цене.
RangeThreshold
0.0060
Минимальный диапазон свечи для фильтра импульса.
BodyThreshold
0.0050
Минимальный размер тела свечи в фильтре диапазона.
DailyRangeThreshold
0.0060
Минимальная разница между открытием и закрытием последнего дневного бара.
TakeProfitPoints
150
Дистанция тейк-профита в ценовых пунктах. Ноль отключает.
StopLossPoints
100
Дистанция стоп-лосса в ценовых пунктах. Ноль отключает.
Допустимая разница между SMMA для принудительного выхода.
Примечания
Сохранена особенность оригинального советника: условие сравнения дневной SMA с ценой Ask всегда истинно, что обеспечивает идентичность поведения.
Уровни стоп-лосса и тейк-профита создаются через StartProtection, поэтому автоматически учитывают шаг цены инструмента.
Трейлинг-стоп активируется только при положительном значении TrailingStopPoints и наличии Security.PriceStep. При отсутствии данных об отступе сопровождение отключается.
Нормализация объёма подчиняется биржевым ограничениям (VolumeStep, VolumeMin, VolumeMax). При необходимости процентного мани-менеджмента можно расширить логику и использовать отрицательные значения параметра.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class Up3x1Premium2VmStrategy : 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 Up3x1Premium2VmStrategy()
{
_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;
}
}