Открыть на GitHub

Стратегия Disaster (MQL №7704)

Общее описание

Советник MetaTrader disaster.mq4 выставляет отложенные стоп-заявки вокруг очень длинной простой скользящей средней (SMA). Он ждёт, пока цена уйдёт от средней на заданное количество пунктов, после чего размещает встречные стопы в расчёте на возврат к базовой линии. Каждая новая минутная свеча пересчитывает SMA и двигает ожидающие заявки вслед за средним. После исполнения позиция защищается фиксированным стоп-лоссом и адаптивным тейк-профитом, который сокращается вдвое, если предыдущая сделка того же направления закрылась с убытком.

Особенности портирования

  • Источник данных — оригинал использует минутные бары iMA(PERIOD_M1, 590). Версия на StockSharp подписывается на настраиваемую серию свечей (по умолчанию 1 минута) и подаёт их в индикатор SMA той же длины.
  • Условия срабатывания — в MQL сравниваются котировки Bid/Ask и SMA, требуется разрыв в 20 пунктов. В C# эта дистанция переводится в абсолютное значение через PriceStep/MinPriceStep с десятикратным множителем для инструментов с 3/5 знаками после запятой.
  • Типы заявок — MetaTrader вызывает OrderSend с типами OP_BUYSTOP/OP_SELLSTOP. Порт использует высокоуровневые методы BuyStop и SellStop, сохраняя независимость двух отложенных ордеров.
  • Перенос заявок — каждая новая свеча в MT4 вызывает OrderModify. В StockSharp активные заявки перенастраиваются через ReRegisterOrder, что позволяет избежать лишних отмен.
  • Стоп-уровни брокера — в оригинале проверяется MODE_STOPLEVEL. Порт округляет цены к шагу инструмента и пропускает обновление, если уровень некорректен (≤ 0); проверку реальных ограничений выполняет адаптер брокера.
  • Защитные ордера — в MT4 стоп-лосс и тейк-профит прикреплены к отложенной заявке. В StockSharp они создаются отдельными стоп/лимит ордерами сразу после входа.
  • Адаптивный тейк-профит — после убыточной сделки соответствующего направления тейк-профит для следующей заявки сокращается вдвое. Флаги _lastBuyWasLoss / _lastSellWasLoss повторяют эту логику.
  • Управление объёмом — оригинал рассчитывает лот из свободной маржи. В портированной версии объём задаётся параметром Volume и автоматически выравнивается по VolumeStep, MinVolume, MaxVolume инструмента.

Параметры

Параметр Значение по умолчанию Описание
Volume 0.1 Торговый объём, выравниваемый по шагу объёма.
MaPeriod 590 Длина SMA, задающая базовую линию.
StopLossPips 30 Дистанция от точки входа до защитного стопа.
TakeProfitPips 70 Базовая дистанция тейк-профита. После убыточной сделки сокращается вдвое.
TriggerDistancePips 20 Минимальное удаление цены от SMA для постановки отложенных стопов.
CandleType 1 минута Свечная серия для расчёта SMA.

Все параметры в пунктах переводятся в абсолютное значение через PriceStep/MinPriceStep. Для валютных пар с точностью 3 или 5 знаков шаг умножается на 10 — это эквивалентно Point в MetaTrader.

Алгоритм работы

  1. Подписаться на поток котировок Level1 и минутные свечи.
  2. Обновлять сохранённые Bid/Ask при каждом сообщении Level1.
  3. После завершения свечи пересчитать SMA и перенастроить активные отложенные заявки на новое значение среднего.
  4. Если позиция отсутствует и разрыв между ценой и SMA больше пороговой дистанции, разместить соответствующую стоп-заявку (sell-stop выше SMA при перекупленности, buy-stop ниже при перепроданности).
  5. При исполнении стопа немедленно выставить стоп-лосс и тейк-профит на заданных расстояниях, обновить флаги результатов для адаптивного тейка.
  6. При остановке стратегии отменить все отложенные и защитные ордера.

Отличия от MQL-реализации

  • Защитные уровни реализованы отдельными ордерами, а не встроенными полями SL/TP в отложенной заявке.
  • Проверка стоп-уровня брокера возлагается на адаптер: стратегия округляет цену и не отправляет отрицательные значения, что повторяет общую логику MT4.
  • Расчёт объёма от свободной маржи заменён явным параметром, чтобы добиться предсказуемого поведения на разных площадках.

Требования

  • Инструмент должен предоставлять PriceStep или MinPriceStep. При их отсутствии используется запасное значение 0.0001.
  • Для корректной работы условий необходимы котировки Bid/Ask. Если они отсутствуют, стратегия использует цену закрытия свечи, что может снизить точность триггера.
  • Нужна поддержка стоп- и лимитных ордеров на стороне брокера для постановки защитных заявок.

Практические советы

  • Тестируйте на демо-счёте с минимальными объёмами (0.01), чтобы проверить корректность пересчёта пунктов.
  • Подбирайте TriggerDistancePips и TakeProfitPips совместно: уменьшение порога приводит к росту количества сделок.
  • Отслеживайте флаги _lastBuyWasLoss / _lastSellWasLoss в логах, чтобы убедиться, что адаптивная логика совпадает с историей MetaTrader.
using System;
using System.Collections.Generic;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Disaster strategy - SMA with momentum breakout.
/// Buys when close is above SMA and momentum crosses above zero.
/// Sells when close is below SMA and momentum crosses below zero.
/// </summary>
public class DisasterStrategy : Strategy
{
	private readonly StrategyParam<int> _smaPeriod;
	private readonly StrategyParam<int> _momentumPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevMom;
	private bool _hasPrev;

	public int SmaPeriod { get => _smaPeriod.Value; set => _smaPeriod.Value = value; }
	public int MomentumPeriod { get => _momentumPeriod.Value; set => _momentumPeriod.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public DisasterStrategy()
	{
		_smaPeriod = Param(nameof(SmaPeriod), 50)
			.SetDisplay("SMA Period", "SMA lookback", "Indicators");

		_momentumPeriod = Param(nameof(MomentumPeriod), 14)
			.SetDisplay("Momentum Period", "Momentum lookback", "Indicators");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
	}

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
	protected override void OnReseted() { base.OnReseted(); _prevMom = 0m; _hasPrev = false; }

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_hasPrev = false;

		var sma = new SimpleMovingAverage { Length = SmaPeriod };
		var mom = new Momentum { Length = MomentumPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(sma, mom, ProcessCandle)
			.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal sma, decimal mom)
	{
		if (candle.State != CandleStates.Finished)
			return;

		var close = candle.ClosePrice;

		if (!_hasPrev)
		{
			_prevMom = mom;
			_hasPrev = true;
			return;
		}

		// Momentum crosses above zero + above SMA = buy
		if (_prevMom <= 0 && mom > 0 && close > sma && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		// Momentum crosses below zero + below SMA = sell
		else if (_prevMom >= 0 && mom < 0 && close < sma && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}

		_prevMom = mom;
	}
}