Открыть на GitHub

Стратегия Up3x1 Premium 2vM

Обзор

Стратегия представляет собой прямую конверсию советника MetaTrader 4 up3x1_Premium_2vM. Она работает с одним инструментом и никогда не удерживает более одной позиции одновременно. Для входа используются сглаженные скользящие средние, мощные свечные диапазоны и фильтр пробоя дневного бара в полночь. Управление рисками выполняется фиксированными уровнями тейк-профита и стоп-лосса в ценовых пунктах, а опциональный трейлинг-стоп повторяет оригинальный алгоритм, который подтягивает защитный уровень по мере движения цены в прибыль.

Логика работы

  1. Основной таймфрейм настраивается пользователем; по умолчанию он соответствует таймфрейму графика в MT4. На этой серии свечей рассчитываются две сглаженные скользящие (SMMA) с периодами 12 и 26 и ценой Typical.
  2. Дополнительная подписка на дневные свечи воссоздаёт поток PERIOD_D1, использовавшийся в MQL, и питает 10-периодную дневную SMA.
  3. При отсутствии позиции анализируются две последние завершённые свечи и сохранённые значения SMMA:
    • Длинный сигнал: быстарая SMMA пересекает медленную снизу вверх при растущих ценах открытия; либо последняя свеча удовлетворяет порогам диапазона и тела; либо предыдущий дневной бар бычий и имеет достаточный размах. Оригинальный код также сравнивал дневную SMA с ценой Ask. Условие всегда истинно, поэтому для совместимости оно сохранено.
    • Короткий сигнал: зеркальные условия для шорта, основанные на пересечении вниз и медвежьем диапазоне.
    • Если выполняется хоть одно длинное условие, отправляется рыночная покупка. Иначе, при выполнении любого короткого условия отправляется рыночная продажа. Перед отправкой объём приводится к шагу объёма инструмента.
  4. При открытой позиции стратегия отслеживает предыдущие значения SMMA. Когда их разница становится меньше ConvergenceTolerance, позиция закрывается, что повторяет сравнение на равенство в советнике.
  5. Модуль трейлинг-стопа отслеживает среднюю цену входа. После прохождения ценой дистанции трейлинга стоп переносится, сохраняя заданный отступ. При касании уровня позиция закрывается сразу, имитируя повторные вызовы OrderModify в MQL.

Параметры

Имя Значение по умолчанию Описание
CandleType TimeFrame(1h) Основной таймфрейм.
FastMaPeriod 12 Период быстрой SMMA по типичной цене.
SlowMaPeriod 26 Период медленной SMMA по типичной цене.
RangeThreshold 0.0060 Минимальный диапазон свечи для фильтра импульса.
BodyThreshold 0.0050 Минимальный размер тела свечи в фильтре диапазона.
DailyRangeThreshold 0.0060 Минимальная разница между открытием и закрытием последнего дневного бара.
TakeProfitPoints 150 Дистанция тейк-профита в ценовых пунктах. Ноль отключает.
StopLossPoints 100 Дистанция стоп-лосса в ценовых пунктах. Ноль отключает.
TrailingStopPoints 10 Отступ трейлинг-стопа. Ноль отключает сопровождение.
TradeVolume 0.05 Базовый объём рыночных заявок до нормализации.
ConvergenceTolerance 0.00001 Допустимая разница между 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;
	}
}