Открыть на GitHub

Стратегия Avalanche

Описание

Avalanche — это сеточная стратегия возврата к среднему, основанная на оригинальном советнике Avalanche v1.2 для MetaTrader. В основе лежит анализ расположения цены относительно опорной средней (ERP), рассчитываемой как простая скользящая на более высоком таймфрейме. Если цена находится ниже ERP, стратегия ожидает возврат вверх и наращивает длинные позиции. Когда цена поднимается выше ERP, система набирает короткие позиции. Каждое добавление позиции происходит через заданный шаг и сопровождается индивидуальными уровнями стоп-лосса и тейк-профита.

В данной реализации StockSharp воспроизводится «toward»-ветка оригинального алгоритма. «Away»-ордера, направленные против ERP, не реализованы, так как стратегии в StockSharp оперируют одной чистой позицией. Тем не менее логика сеточного накопления, буферов и раздельного сопровождения сделок полностью сохранена.

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

  1. Подписка на два потока свечей: рабочий таймфрейм для торговли и отдельный таймфрейм для вычисления ERP.
  2. Расчёт ERP с помощью индикатора SMA и определение, находится ли цена выше или ниже средней. Параметр ErpChangeBuffer предотвращает ложные переключения направления.
  3. При смене положения относительно ERP существующая сетка закрывается и ожидание начинается заново.
  4. Если разрешён параметр OpenStartingOrders, первая позиция открывается сразу после появления сигнала (покупка при цене ниже ERP, продажа — при цене выше).
  5. При движении цены в прибыльную сторону ещё на IntervalToward пунктов выполняется дополнительная покупка/продажа.
  6. При неблагоприятном движении на IntervalToward + StackBufferToward пунктов добавляется усредняющая сделка.
  7. Каждая позиция сопровождается собственными уровнями TakeProfitToward и StopLossToward, что позволяет закрывать прибыльные части сетки независимо от остальных.

Настройки

Параметр Назначение
BaseVolume Базовый объём заявки до применения множителей.
TowardMultiplier Множитель объёма для стандартных сделок по направлению к ERP.
TowardInterestMultiplier Множитель объёма при положительном свопе в торговом направлении.
IntervalToward Шаг (в пунктах) между последовательными сделками при движении в прибыльную сторону.
StackBufferToward Дополнительный буфер (в пунктах) при усреднении против движения.
TakeProfitToward Дистанция тейк-профита для каждой сделки; 0 отключает цель.
StopLossToward Дистанция стоп-лосса для каждой сделки; 0 отключает защиту.
ErpPeriod Период SMA, используемой как ERP.
ErpChangeBuffer Буфер вокруг ERP перед сменой направления.
CandleType Таймфрейм, по которому принимаются торговые решения.
ErpCandleType Таймфрейм для расчёта ERP.
OpenStartingOrders Автоматически открывать стартовую позицию после появления сигнала.

Отличия от оригинального эксперта

  • Не используются встречные «away»-ордера — стратегия работает только в сторону предполагаемого возврата к ERP.
  • Сделки выполняются рыночными заявками, а не отложенными стоп-ордерами.
  • Сохраняется логика определения направления положительного свопа для выбора множителя объёма.

Рекомендации по применению

  • Подбирайте значения IntervalToward и StackBufferToward в зависимости от волатильности инструмента, чтобы контролировать скорость набора сетки.
  • Следите за величиной совокупной позиции: сеточные стратегии способны быстро накапливать риск.
  • Используйте дополнительные фильтры — ограничения по времени торгов, лимиты по просадке, внешние стопы по капиталу.
using System;
using System.Collections.Generic;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Avalanche grid strategy - mean reversion around EMA.
/// Buys when price drops below EMA, sells when above.
/// Uses RSI to confirm oversold/overbought conditions.
/// </summary>
public class AvalancheStrategy : Strategy
{
	private readonly StrategyParam<int> _emaPeriod;
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<decimal> _rsiOversold;
	private readonly StrategyParam<decimal> _rsiOverbought;
	private readonly StrategyParam<DataType> _candleType;

	public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
	public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
	public decimal RsiOversold { get => _rsiOversold.Value; set => _rsiOversold.Value = value; }
	public decimal RsiOverbought { get => _rsiOverbought.Value; set => _rsiOverbought.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public AvalancheStrategy()
	{
		_emaPeriod = Param(nameof(EmaPeriod), 20)
			.SetDisplay("EMA Period", "EMA period for equilibrium", "Indicators");

		_rsiPeriod = Param(nameof(RsiPeriod), 14)
			.SetDisplay("RSI Period", "RSI period", "Indicators");

		_rsiOversold = Param(nameof(RsiOversold), 35m)
			.SetDisplay("RSI Oversold", "RSI oversold level", "Indicators");

		_rsiOverbought = Param(nameof(RsiOverbought), 65m)
			.SetDisplay("RSI Overbought", "RSI overbought level", "Indicators");

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

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

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
	}

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

		var ema = new ExponentialMovingAverage { Length = EmaPeriod };
		var rsi = new RelativeStrengthIndex { Length = RsiPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(ema, rsi, ProcessCandle)
			.Start();
	}

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

		var close = candle.ClosePrice;

		// Below EMA and oversold - buy
		if (close < emaValue && rsiValue <= RsiOversold && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		// Above EMA and overbought - sell
		else if (close > emaValue && rsiValue >= RsiOverbought && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}
	}
}