Открыть на GitHub

Стратегия Exp Candles XSmoothed

Стратегия отслеживает максимумы и минимумы свечей, сглаженные взвешенным скользящим средним (WMA). Когда цена закрытия пробивает сглаженный максимум плюс настраиваемый буфер, открывается длинная позиция и закрывается существующий шорт. Если цена опускается ниже сглаженного минимума минус буфер, открывается короткая позиция и закрывается существующий лонг.

Параметры

  • MA Length – количество периодов для WMA по максимумам и минимумам.
  • Level – размер буфера в пунктах, добавляемый к сглаженному максимуму и вычитаемый из сглаженного минимума.
  • Candle Type – таймфрейм свечей для анализа.
  • Buy Open / Sell Open – разрешения на открытие длинных или коротких позиций.
  • Buy Close / Sell Close – разрешения на закрытие позиций при противоположном пробое.

Стратегия отображает линии сглаженных максимумов и минимумов на графике и при запуске активирует встроенную защиту позиций.

using System;
using System.Linq;
using System.Collections.Generic;

using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Breakout strategy based on smoothed candle highs and lows.
/// Applies a weighted moving average to high and low prices and
/// enters on breakouts beyond these smoothed levels plus a buffer.
/// </summary>
public class ExpCandlesXSmoothedStrategy : Strategy
{
	private readonly StrategyParam<int> _maLength;
	private readonly StrategyParam<decimal> _level;
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<bool> _buyPosOpen;
	private readonly StrategyParam<bool> _sellPosOpen;
	private readonly StrategyParam<bool> _buyPosClose;
	private readonly StrategyParam<bool> _sellPosClose;

	private WeightedMovingAverage _highMa;
	private WeightedMovingAverage _lowMa;

	/// <summary>
	/// Moving average period length.
	/// </summary>
	public int MaLength
	{
		get => _maLength.Value;
		set => _maLength.Value = value;
	}

	/// <summary>
	/// Breakout level in points.
	/// </summary>
	public decimal Level
	{
		get => _level.Value;
		set => _level.Value = value;
	}

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

	/// <summary>
	/// Permission to open long positions.
	/// </summary>
	public bool BuyPosOpen
	{
		get => _buyPosOpen.Value;
		set => _buyPosOpen.Value = value;
	}

	/// <summary>
	/// Permission to open short positions.
	/// </summary>
	public bool SellPosOpen
	{
		get => _sellPosOpen.Value;
		set => _sellPosOpen.Value = value;
	}

	/// <summary>
	/// Permission to close long positions.
	/// </summary>
	public bool BuyPosClose
	{
		get => _buyPosClose.Value;
		set => _buyPosClose.Value = value;
	}

	/// <summary>
	/// Permission to close short positions.
	/// </summary>
	public bool SellPosClose
	{
		get => _sellPosClose.Value;
		set => _sellPosClose.Value = value;
	}

	/// <summary>
	/// Initializes a new instance of <see cref="ExpCandlesXSmoothedStrategy"/>.
	/// </summary>
	public ExpCandlesXSmoothedStrategy()
	{
		_maLength = Param(nameof(MaLength), 30)
			.SetGreaterThanZero()
			.SetDisplay("MA Length", "Smoothing period", "Indicators");

		_level = Param(nameof(Level), 30m)
			.SetGreaterThanZero()
			.SetDisplay("Level", "Breakout level in points", "General");

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

		_buyPosOpen = Param(nameof(BuyPosOpen), true)
			.SetDisplay("Buy Open", "Allow opening long positions", "Trading");

		_sellPosOpen = Param(nameof(SellPosOpen), true)
			.SetDisplay("Sell Open", "Allow opening short positions", "Trading");

		_buyPosClose = Param(nameof(BuyPosClose), true)
			.SetDisplay("Buy Close", "Allow closing long positions", "Trading");

		_sellPosClose = Param(nameof(SellPosClose), true)
			.SetDisplay("Sell Close", "Allow closing short positions", "Trading");
	}

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

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

		_highMa = new WeightedMovingAverage { Length = MaLength };
		_lowMa = new WeightedMovingAverage { Length = MaLength };

		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(ProcessCandle).Start();

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

		StartProtection(null, null);
	}

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

		var highVal = _highMa.Process(candle.HighPrice, candle.OpenTime, true);
		var lowVal = _lowMa.Process(candle.LowPrice, candle.OpenTime, true);

		if (!highVal.IsFormed || !lowVal.IsFormed)
			return;

		var level = Level * (Security.PriceStep ?? 1m);
		var smoothedHigh = highVal.ToDecimal();
		var smoothedLow = lowVal.ToDecimal();

		var breakUp = candle.ClosePrice > smoothedHigh + level;
		var breakDown = candle.ClosePrice < smoothedLow - level;

		if (breakUp)
		{
			if (SellPosClose && Position < 0)
				BuyMarket();

			if (BuyPosOpen && Position <= 0)
				BuyMarket();
		}
		else if (breakDown)
		{
			if (BuyPosClose && Position > 0)
				SellMarket();

			if (SellPosOpen && Position >= 0)
				SellMarket();
		}
	}
}