Открыть на GitHub

WAMI Cloud X2

Стратегия воспроизводит поведение оригинального эксперта MetaTrader «Exp_WAMI_Cloud_X2». Индикатор Warren Momentum Indicator (WAMI) используется на старшем таймфрейме для определения преобладающего направления и на младшем таймфрейме для формирования сигналов. Основная линия WAMI сравнивается с её сигнальной линией на обоих таймфреймах, что полностью повторяет исходную логику MQL.

Концепция

  • Конструкция WAMI – индикатор строится на первой разности закрытий, которая трижды сглаживается последовательностью скользящих средних с настраиваемыми методами (SMA, EMA, SMMA или LWMA). Четвёртая скользящая средняя формирует сигнальную линию. Пользовательский индикатор в стратегии повторяет эту цепочку и возвращает обе линии в одном значении.
  • Фильтр тренда (старший таймфрейм) – по умолчанию используются шестичасовые свечи. Если основная линия WAMI выше сигнальной, направление признаётся бычьим; если ниже – медвежьим. При равенстве значений или недостатке данных направление считается нейтральным.
  • Сигнальный модуль (младший таймфрейм) – стандартные 30-минутные свечи служат для поиска точек входа. Для каждой завершённой свечи сохраняются значения WAMI, после чего анализируется последняя закрытая свеча, задаваемая параметром SignalBar. Пересечения фиксируются сравнением текущего значения (SignalBar) и предыдущего (SignalBar + 1).

Правила торговли

  1. Выходы
    • Длинные позиции закрываются при устойчивом медвежьем сигнале на младшем таймфрейме (previous.Main < previous.Signal), если активирован флаг CloseLongOnSignal.
    • Короткие позиции закрываются при противоположном условии и активном флаге CloseShortOnSignal.
    • При смене направления на старшем таймфрейме соответствующие флаги CloseLongOnTrendFlip или CloseShortOnTrendFlip принудительно закрывают позиции.
  2. Входы
    • Продажи разрешены, когда на старшем таймфрейме медвежий тренд, а на младшем основная линия WAMI пересекает сигнальную снизу вверх (current.Main >= current.Signal вместе с previous.Main < previous.Signal). Это повторяет оригинальную реализацию, продающую при первом проколе сигнальной линии в нисходящем тренде.
    • Покупки – зеркальное условие: старший таймфрейм бычий, а на младшем основная линия пересекает сигнальную сверху вниз (current.Main <= current.Signal и previous.Main > previous.Signal).
    • Переключатели EnableBuyEntries и EnableSellEntries позволяют запретить отдельные направления. При наличии противоположной позиции отправляется рыночная заявка, полностью закрывающая текущий объём и открывающая новую позицию, как и в MQL-функциях.

Параметры

  • WAMI трендаTrendPeriod1/2/3, TrendMethod1/2/3, TrendSignalPeriod, TrendSignalMethod, TrendCandleType.
  • WAMI сигналовSignalPeriod1/2/3, SignalMethod1/2/3, SignalSignalPeriod, SignalSignalMethod, SignalCandleType.
  • Управляющие флагиSignalBar, EnableBuyEntries, EnableSellEntries, CloseLongOnTrendFlip, CloseShortOnTrendFlip, CloseLongOnSignal, CloseShortOnSignal.
  • Объём торговлиTradeVolume задаёт размер рыночной заявки. При реверсе отправляется встречная заявка на суммарный объём (закрытие + новый вход).

Все параметры создаются через StrategyParam<T>, благодаря чему их можно оптимизировать и изменять в пользовательском интерфейсе StockSharp аналогично входным параметрам советника MetaTrader.

Значения по умолчанию

  • Таймфрейм тренда – 6 часов.
  • Таймфрейм сигналов – 30 минут.
  • Методы сглаживания – простое скользящее среднее (SMA) для всех этапов.
  • Длины сглаживания – 4 / 13 / 13 для трёх последовательных средних и 4 для сигнальной линии на обоих таймфреймах.
  • SignalBar – 1 (используется последняя закрытая свеча).
  • TradeVolume – 1 контракт.
  • Все разрешающие флаги – включены.

Дополнительные замечания

  • Стратегия не устанавливает фиксированные стоп-лоссы и тейк-профиты; управление риском следует подключать отдельно.
  • На графике отображаются свечи сигнального таймфрейма, обе линии WAMI и исполненные сделки. Трендовый таймфрейм выводится на отдельной панели для визуального контроля.
  • Реализация использует только высокоуровневые подписки на свечи и не обращается к индикатору напрямую через GetValue, что соответствует требованиям проекта.
using System;
using System.Collections.Generic;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// WAMI Cloud X2 strategy (simplified). Uses RSI momentum with EMA trend
/// filter as a proxy for the original multi-stage WAMI oscillator.
/// </summary>
public class WamiCloudX2Strategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _rsiLength;
	private readonly StrategyParam<int> _emaLength;

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public int RsiLength
	{
		get => _rsiLength.Value;
		set => _rsiLength.Value = value;
	}

	public int EmaLength
	{
		get => _emaLength.Value;
		set => _emaLength.Value = value;
	}

	public WamiCloudX2Strategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Candles", "General");

		_rsiLength = Param(nameof(RsiLength), 14)
			.SetGreaterThanZero()
			.SetDisplay("RSI Length", "RSI period", "Indicators");

		_emaLength = Param(nameof(EmaLength), 50)
			.SetGreaterThanZero()
			.SetDisplay("EMA Length", "Trend EMA", "Indicators");
	}

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

		var rsi = new RelativeStrengthIndex { Length = RsiLength };
		var ema = new ExponentialMovingAverage { Length = EmaLength };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(rsi, ema, (ICandleMessage candle, decimal rsiValue, decimal emaValue) =>
			{
				if (candle.State != CandleStates.Finished)
					return;

				if (!IsFormedAndOnlineAndAllowTrading())
					return;

				var close = candle.ClosePrice;

				// Price above EMA with RSI not overbought => buy
				if (close > emaValue && rsiValue > 55m && Position <= 0)
					BuyMarket();
				// Price below EMA with RSI not oversold => sell
				else if (close < emaValue && rsiValue < 45m && Position >= 0)
					SellMarket();
			})
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawIndicator(area, ema);
			DrawOwnTrades(area);
		}
	}
}