Открыть на GitHub

Стратегия пересечения MA по MA

Стратегия реализует двойное сглаживание скользящей средней. Ценовой ряд сначала сглаживается быстрой экспоненциальной скользящей средней (EMA). Затем результат быстрой EMA дополнительно сглаживается более медленной EMA. Полученные серии сравниваются для генерации сигналов:

  • Длинная позиция открывается, когда быстрая EMA пересекает медленную EMA снизу вверх.
  • Короткая позиция открывается, когда быстрая EMA пересекает медленную EMA сверху вниз. Любая противоположная позиция закрывается при пересечении.

Стратегия работает на любой временной шкале свечей.

Параметры

  • FastLength – период быстрой EMA.
  • SlowLength – период медленной EMA, применяемой к выходу быстрой EMA.
  • EnableLong – разрешить открытие длинных позиций.
  • EnableShort – разрешить открытие коротких позиций.
  • CandleType – тип свечей для расчёта.

Детали

  • Условия входа:
    • Покупка: быстрая EMA пересекает медленную снизу вверх.
    • Продажа: быстрая EMA пересекает медленную сверху вниз.
  • Длинные/короткие: поддерживаются оба направления.
  • Условия выхода:
    • Противоположное пересечение закрывает существующую позицию.
  • Стопы: явные стоп-лосс и тейк-профит не используются.
  • Значения по умолчанию:
    • FastLength = 7
    • SlowLength = 7
    • EnableLong = true
    • EnableShort = true
    • CandleType = таймфрейм 12 часов
  • Фильтры:
    • Категория: Следование тренду
    • Направление: Оба
    • Индикаторы: Скользящие средние
    • Стопы: Нет
    • Сложность: Базовая
    • Таймфрейм: Любой
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>
/// Double smoothed moving average crossover strategy.
/// </summary>
public class MaByMaStrategy : Strategy
{
	private readonly StrategyParam<int> _fastLength;
	private readonly StrategyParam<int> _slowLength;
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<decimal> _minSpreadPercent;
	private readonly StrategyParam<int> _cooldownBars;

	private bool _isInitialized;
	private bool _wasFastBelowSlow;
	private int _cooldownRemaining;

	public int FastLength { get => _fastLength.Value; set => _fastLength.Value = value; }
	public int SlowLength { get => _slowLength.Value; set => _slowLength.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public decimal MinSpreadPercent { get => _minSpreadPercent.Value; set => _minSpreadPercent.Value = value; }
	public int CooldownBars { get => _cooldownBars.Value; set => _cooldownBars.Value = value; }

	public MaByMaStrategy()
	{
		_fastLength = Param(nameof(FastLength), 7)
			.SetGreaterThanZero()
			.SetDisplay("Fast EMA Length", "Period for fast EMA", "Indicator");

		_slowLength = Param(nameof(SlowLength), 21)
			.SetGreaterThanZero()
			.SetDisplay("Slow EMA Length", "Period for slow EMA", "Indicator");

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

		_minSpreadPercent = Param(nameof(MinSpreadPercent), 0.003m)
			.SetDisplay("Minimum Spread %", "Minimum normalized spread between fast and slow EMA values", "Filters");

		_cooldownBars = Param(nameof(CooldownBars), 6)
			.SetDisplay("Cooldown Bars", "Completed candles to wait after a position change", "Trading");
	}

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_isInitialized = false;
		_wasFastBelowSlow = false;
		_cooldownRemaining = 0;
	}

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

		var fastMa = new ExponentialMovingAverage { Length = FastLength };
		var slowMa = new ExponentialMovingAverage { Length = SlowLength };

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

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

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

		if (_cooldownRemaining > 0)
			_cooldownRemaining--;

		var spreadPercent = slowValue != 0m ? Math.Abs(fastValue - slowValue) / slowValue : 0m;
		if (!_isInitialized)
		{
			_wasFastBelowSlow = fastValue < slowValue;
			_isInitialized = true;
			return;
		}

		var isFastBelowSlow = fastValue < slowValue;
		if (_cooldownRemaining == 0 && spreadPercent >= MinSpreadPercent)
		{
			if (_wasFastBelowSlow && !isFastBelowSlow && Position <= 0)
			{
				if (Position < 0)
					BuyMarket();

				BuyMarket();
				_cooldownRemaining = CooldownBars;
			}
			else if (!_wasFastBelowSlow && isFastBelowSlow && Position >= 0)
			{
				if (Position > 0)
					SellMarket();

				SellMarket();
				_cooldownRemaining = CooldownBars;
			}
		}

		_wasFastBelowSlow = isFastBelowSlow;
	}
}