Открыть на GitHub

Стратегия Flat Trend

Обзор

Flat Trend Strategy переносит логику оригинального советника Flat Trend, объединяя многоуровневые трендовые фильтры, подтверждение силой тренда по ADX и волатильностный фильтр на основе стандартного отклонения («juice»). Цель стратегии — распознать момент выхода цены из флэта и присоединиться к зарождающемуся движению с помощью продуманного риск-менеджмента.

Логика торговли

  1. Трендовые фильтры – три экспоненциальные скользящие средние (EMA) с настраиваемыми периодами: триггер, первый и второй фильтр. На основе положения цены и наклона каждой EMA определяется состояние:
    • Сильный бычий сигнал – цена выше EMA, EMA растет.
    • Умеренный бычий сигнал – цена выше EMA, наклон нейтрален.
    • Сильный медвежий сигнал – цена ниже EMA, EMA падает.
    • Умеренный медвежий сигнал – цена ниже EMA, наклон нейтрален.
  2. Условия входа
    • Покупки выполняются, когда триггер и первый фильтр дают бычьи состояния; второй фильтр можно отключить.
    • Продажи симметричны условиям для покупок.
    • При включении фильтра ADX средний направленный индекс должен превышать порог, а опция UseDirectionalFilter дополнительно требует согласованности линий +DI и −DI с направлением сделки.
    • Фильтр «juice» проверяет, что стандартное отклонение превышает заданный уровень, тем самым отсекая периоды низкой волатильности.
    • Доступен фильтр торговых часов, ограничивающий работу стратегии определенным интервалом суток.
  3. Условия выхода
    • Противоположный сигнал на триггерной EMA закрывает позицию; в строгом режиме ожидается только сильный противоположный сигнал.
    • Динамический стоп сопровождает сделку и закрывает позицию при касании уровня.

Управление рисками

  • Начальный стоп – задается фиксированным числом пунктов или вычисляется по ATR, что приближает логику к исходному ADR-стопу.
  • Трейлинг-стоп – тянется за максимальной/минимальной ценой с использованием произведения ATR и коэффициента.
  • Переход в безубыток – после достижения заданной прибыли стоп переносится за цену входа с фиксацией небольшой доли профита.

Параметры

Параметр Описание
TriggerLength Период EMA-триггера.
FilterLength1 Период первой EMA-фильтра.
FilterLength2 Период второй EMA-фильтра.
UseOnlyPrimaryIndicators Использовать только триггер и первый фильтр.
IgnoreModerateForEntry Для входа требуются только сильные сигналы.
IgnoreModerateForExit Для выхода требуются только сильные встречные сигналы.
UseTradingHours Включить фильтр торговых часов.
TradingHourBegin / TradingHourEnd Начало и конец торгового окна.
UseJuiceFilter, JuicePeriod, JuiceThreshold Настройки фильтра стандартного отклонения.
UseAdxFilter, AdxPeriod, AdxThreshold, UseDirectionalFilter Параметры ADX и фильтра направлений.
UseAdrForStop, StopLossPips Конфигурация стартового стопа.
TrailingDivisor Коэффициент ATR для трейлинг-стопа.
BreakEvenPips, BreakEvenLockPips Дистанция до безубытка и величина фиксируемой прибыли.
AtrPeriod Период ATR для оценки волатильности.
CandleType Тип свечей, по которым ведутся расчеты.

Набор индикаторов

  • EMA – три скользящие для оценки тренда на разных «скоростях».
  • Standard Deviation – фильтр волатильности, аналог индикатора Juice.
  • Average True Range – определяет дистанцию стопов и трейлинга.
  • Average Directional Index – подтверждает силу и направление тренда.

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

  1. Убедитесь, что у инструмента задан шаг цены (PriceStep). При его отсутствии стратегия применит значение по умолчанию 0.0001.
  2. Входы осуществляются рыночными заявками (BuyMarket, SellMarket); при смене направления стратегия автоматически закрывает противоположные позиции.
  3. Трейлинг и безубыток реализованы через внутренний контроль цены: как только рынок пересекает виртуальный стоп, позиция закрывается рыночной заявкой.
  4. Комбинируйте фильтр торговых часов со строгим режимом входа, чтобы сосредоточиться на ликвидных сессиях и снизить влияние флэтовых участков.
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>
/// Flat Trend strategy - breakout from low volatility using ATR and EMA.
/// Buys when ATR expands and price is above EMA.
/// Sells when ATR expands and price is below EMA.
/// </summary>
public class FlatTrendStrategy : Strategy
{
	private readonly StrategyParam<int> _emaPeriod;
	private readonly StrategyParam<int> _atrPeriod;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevAtr;
	private decimal _prevClose;
	private decimal _prevEma;
	private bool _hasPrev;

	public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
	public int AtrPeriod { get => _atrPeriod.Value; set => _atrPeriod.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public FlatTrendStrategy()
	{
		_emaPeriod = Param(nameof(EmaPeriod), 20)
			.SetDisplay("EMA Period", "EMA trend filter", "Indicators");

		_atrPeriod = Param(nameof(AtrPeriod), 14)
			.SetDisplay("ATR Period", "ATR volatility period", "Indicators");

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

	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
	protected override void OnReseted() { base.OnReseted(); _prevAtr = 0m; _prevClose = 0m; _prevEma = 0m; _hasPrev = false; }

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

		_hasPrev = false;

		var ema = new ExponentialMovingAverage { Length = EmaPeriod };
		var atr = new AverageTrueRange { Length = AtrPeriod };

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

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

		var close = candle.ClosePrice;

		if (!_hasPrev)
		{
			_prevAtr = atr;
			_prevClose = close;
			_prevEma = ema;
			_hasPrev = true;
			return;
		}

		// Volatility expansion: ATR increasing
		var atrExpanding = atr > _prevAtr;

		// Breakout above EMA with expanding volatility
		if (atrExpanding && _prevClose <= _prevEma && close > ema && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		// Breakout below EMA with expanding volatility
		else if (atrExpanding && _prevClose >= _prevEma && close < ema && Position >= 0)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}

		_prevAtr = atr;
		_prevClose = close;
		_prevEma = ema;
	}
}