Открыть на GitHub

Стратегия Wedge Pattern

Обзор

Wedge Pattern Strategy — это перенос советника MetaTrader Wedge pattern.mq4 на высокоуровневый API StockSharp. Стратегия находит симметричные «клины», построенные по фракталам Билла Вильямса, и торгует их пробой при совпадении трендовых и моментум-фильтров.

Высокоуровневая реализация сохраняет логику принятия решений, но использует стандартные механизмы StockSharp:

  • Трендовый фильтр — сравнение быстрой и медленной LWMA по типичной цене.
  • Фильтр моментума — проверка абсолютного отклонения 14-периодного индикатора Momentum от уровня 100; из трёх последних значений хотя бы одно должно превышать порог.
  • MACD-подтверждение — для покупок линия MACD должна быть выше сигнальной, для продаж ниже.
  • Определение клина по фракталам — последние два верхних и нижних фрактала образуют сходящиеся трендовые линии; сигнал появляется при закрытии свечи за пределами линий плюс буфер подтверждения.
  • Риск-менеджмент — фиксированные стоп и тейк, автоматический переход в безубыток и последующий трейлинг стопа.

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

  1. Подписка на свечи с периодом, указанным в параметре CandleType.
  2. После закрытия каждой свечи пересчитываются индикаторы и обновляются буферы максимумов/минимумов для поиска новых фракталов.
  3. Из двух последних фракталов по максимумам и минимумам строятся линии клина. Учитываются только сходящиеся конструкции (понижающиеся хай и повышающийся лой).
  4. Покупка выполняется, если:
    • Быстрая LWMA выше медленной.
    • Линия MACD выше сигнальной.
    • Хотя бы одно из трёх последних значений Momentum превышает порог.
    • Закрытие свечи выше верхней линии клина на величину буфера.
  5. Условия для продаж зеркальны.
  6. После входа выставляются стоп-лосс и тейк-профит; далее стоп может быть переведён в безубыток и подтягивается трейлингом.

Параметры

Параметр Описание
CandleType Используемый таймфрейм.
FastMaPeriod / SlowMaPeriod Периоды быстрых и медленных LWMA.
MomentumPeriod Период индикатора Momentum.
MomentumThreshold Минимальное требуемое отклонение Momentuм от 100.
MacdFastPeriod / MacdSlowPeriod / MacdSignalPeriod Настройки MACD.
FractalDepth Количество баров по обе стороны для подтверждения фрактала.
StopLossPips / TakeProfitPips Размеры стопа и тейка в пунктах.
UseBreakeven, BreakevenTriggerPips, BreakevenOffsetPips Параметры перевода в безубыток.
UseTrailing, TrailingActivationPips, TrailingDistancePips, TrailingStepPips Настройки трейлинг-стопа.
BreakoutBufferPips Дополнительный буфер для подтверждения пробоя.

Все значения в пунктах автоматически переводятся в ценовые расстояния с учётом шага цены инструмента, включая дробные котировки (3 или 5 знаков).

Рекомендации по использованию

  1. Выберите инструмент и задайте соответствующий таймфрейм через CandleType (например, 15-минутные свечи).
  2. Настройте размер позиции через свойство Strategy.Volume.
  3. При необходимости адаптируйте фильтры и уровни риска под волатильность инструмента.
  4. Запустите стратегию — она подпишется на данные, отрисует график и начнёт торговать пробои клиньев.

Отличия от версии MetaTrader

  • Используются методы SubscribeCandles и привязка индикаторов вместо ручной обработки тиков.
  • Управление защитными ордерами реализовано через SetStopLoss/SetTakeProfit, что упрощает интеграцию с защитными механизмами StockSharp.
  • Поддерживается только одна совокупная позиция; в MQL-версии допускалось наращивание серии сделок.
  • Убраны сервисные уведомления (Alert, Email, Push); при необходимости их можно реализовать отдельно.

В остальном логика входов и сопровождения позиций соответствует оригинальному советнику, но реализована средствами StockSharp.

using System;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Wedge Pattern strategy: WMA crossover + Momentum.
/// Buys when fast WMA crosses above slow WMA and momentum &gt; 100.
/// Sells on cross below with momentum &lt; 100.
/// </summary>
public class WedgePatternStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _momPeriod;

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

	public int FastPeriod
	{
		get => _fastPeriod.Value;
		set => _fastPeriod.Value = value;
	}

	public int SlowPeriod
	{
		get => _slowPeriod.Value;
		set => _slowPeriod.Value = value;
	}

	public int MomPeriod
	{
		get => _momPeriod.Value;
		set => _momPeriod.Value = value;
	}

	public WedgePatternStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");

		_fastPeriod = Param(nameof(FastPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("Fast WMA", "Fast WMA period", "Indicators");

		_slowPeriod = Param(nameof(SlowPeriod), 40)
			.SetGreaterThanZero()
			.SetDisplay("Slow WMA", "Slow WMA period", "Indicators");

		_momPeriod = Param(nameof(MomPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("Momentum", "Momentum period", "Indicators");
	}

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

		var fast = new WeightedMovingAverage { Length = FastPeriod };
		var slow = new WeightedMovingAverage { Length = SlowPeriod };
		var mom = new Momentum { Length = MomPeriod };

		decimal? prevFast = null;
		decimal? prevSlow = null;

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(fast, slow, mom, (candle, fastVal, slowVal, momVal) =>
			{
				if (candle.State != CandleStates.Finished)
					return;

				if (!IsFormedAndOnlineAndAllowTrading())
					return;

				if (prevFast.HasValue && prevSlow.HasValue)
				{
					var crossUp = prevFast.Value <= prevSlow.Value && fastVal > slowVal;
					var crossDown = prevFast.Value >= prevSlow.Value && fastVal < slowVal;

					if (crossUp && momVal > 100m && Position <= 0)
						BuyMarket();
					else if (crossDown && momVal < 100m && Position >= 0)
						SellMarket();
				}

				prevFast = fastVal;
				prevSlow = slowVal;
			})
			.Start();

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