Открыть на GitHub

Стратегия SpectrAnalysis WPR

Стратегия создана на основе MQL5 эксперта Exp_i-SpectrAnalysis_WPR. Она анализирует направление индикатора Williams %R и открывает или закрывает позиции при разворотах индикатора.

Логика

  1. Подписка на свечи выбранного таймфрейма.
  2. Расчёт Williams %R с заданным периодом.
  3. Сохранение двух последних значений индикатора для определения направления.
  4. При развороте вверх и разрешённых покупках:
    • При необходимости закрыть короткую позицию.
    • Открыть новую длинную позицию.
  5. При развороте вниз и разрешённых продажах:
    • При необходимости закрыть длинную позицию.
    • Открыть новую короткую позицию.

Обрабатываются только завершённые свечи. Стратегия использует высокоуровневые привязки и не выполняет сложные запросы истории.

Параметры

Название Описание Значение по умолчанию
Candle Type Таймфрейм используемых свечей 4h
WPR Period Период индикатора Williams %R 13
Allow Long Entry Разрешить открытие длинных позиций true
Allow Short Entry Разрешить открытие коротких позиций true
Allow Long Exit Разрешить закрытие длинных позиций true
Allow Short Exit Разрешить закрытие коротких позиций true

Примечания

Оригинальная версия на MQL применяла спектральный анализ к значению Williams %R. В данной C# реализации используется стандартный индикатор Williams %R, а логика сигналов воспроизводится отслеживанием последних значений индикатора.

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>
/// Strategy based on Williams %R trend direction.
/// </summary>
public class SpectrAnalysisWprStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _wprPeriod;
	private readonly StrategyParam<bool> _buyPosOpen;
	private readonly StrategyParam<bool> _sellPosOpen;
	private readonly StrategyParam<bool> _buyPosClose;
	private readonly StrategyParam<bool> _sellPosClose;

	private decimal? _prev;
	private decimal? _prev2;

	/// <summary>
	/// Candle type for calculations.
	/// </summary>
	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	/// <summary>
	/// Williams %R period.
	/// </summary>
	public int WprPeriod
	{
		get => _wprPeriod.Value;
		set => _wprPeriod.Value = value;
	}

	/// <summary>
	/// Allow opening long positions.
	/// </summary>
	public bool BuyPosOpen
	{
		get => _buyPosOpen.Value;
		set => _buyPosOpen.Value = value;
	}

	/// <summary>
	/// Allow opening short positions.
	/// </summary>
	public bool SellPosOpen
	{
		get => _sellPosOpen.Value;
		set => _sellPosOpen.Value = value;
	}

	/// <summary>
	/// Allow closing long positions.
	/// </summary>
	public bool BuyPosClose
	{
		get => _buyPosClose.Value;
		set => _buyPosClose.Value = value;
	}

	/// <summary>
	/// Allow closing short positions.
	/// </summary>
	public bool SellPosClose
	{
		get => _sellPosClose.Value;
		set => _sellPosClose.Value = value;
	}

	/// <summary>
	/// Initializes a new instance of the strategy.
	/// </summary>
	public SpectrAnalysisWprStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe for indicator", "General");
		_wprPeriod = Param(nameof(WprPeriod), 13)
			.SetGreaterThanZero()
			.SetDisplay("WPR Period", "Williams %R period", "Indicator");
		_buyPosOpen = Param(nameof(BuyPosOpen), true)
			.SetDisplay("Allow Long Entry", "Enable long position opening", "Trading");
		_sellPosOpen = Param(nameof(SellPosOpen), true)
			.SetDisplay("Allow Short Entry", "Enable short position opening", "Trading");
		_buyPosClose = Param(nameof(BuyPosClose), true)
			.SetDisplay("Allow Long Exit", "Enable closing of long positions", "Trading");
		_sellPosClose = Param(nameof(SellPosClose), true)
			.SetDisplay("Allow Short Exit", "Enable closing of short positions", "Trading");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, CandleType);
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prev = null;
		_prev2 = null;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_prev = null;
		_prev2 = null;

		var wpr = new WilliamsR { Length = WprPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(wpr, ProcessCandle).Start();

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

	private void ProcessCandle(ICandleMessage candle, decimal wprValue)
	{
		if (candle.State != CandleStates.Finished || !IsFormedAndOnlineAndAllowTrading())
			return;

		if (_prev is null || _prev2 is null)
		{
			_prev2 = _prev;
			_prev = wprValue;
			return;
		}

		// Upward direction detected (WPR was falling, now turning up)
		if (_prev < _prev2 && wprValue >= _prev)
		{
			if (BuyPosOpen && Position <= 0)
				BuyMarket(Position < 0 ? Volume + Math.Abs(Position) : Volume);
			else if (SellPosClose && Position < 0)
				BuyMarket(Math.Abs(Position));
		}
		// Downward direction detected (WPR was rising, now turning down)
		else if (_prev > _prev2 && wprValue <= _prev)
		{
			if (SellPosOpen && Position >= 0)
				SellMarket(Position > 0 ? Volume + Position : Volume);
			else if (BuyPosClose && Position > 0)
				SellMarket(Position);
		}

		_prev2 = _prev;
		_prev = wprValue;
	}
}