Открыть на GitHub

Flat 001a — стратегия торговли во флэте

Общее описание

Flat 001a — скальперская система для часового графика EURUSD. Она анализирует три последние завершённые свечи и измеряет расстояние между максимальным максимумом и минимальным минимумом. Если диапазон укладывается в заданное количество пунктов, стратегия предполагает, что рынок остаётся во флэте, и ищет сделки против крайних движений внутри этого канала. Входы происходят, когда цена закрытия оказывается в верхней или нижней четверти диапазона, после чего сразу выставляются защитные уровни.

Исходный советник MQL4 работал только с EURUSD на таймфрейме H1 и отказывался торговать при несоответствии символа или периода. В портированной версии сохранены эти настройки по умолчанию (EURUSD, 60-минутные свечи), а логика входов, стоп-лоссов, тейк-профитов и трейлинг-стопов полностью воспроизведена на StockSharp.

Данные и индикаторы

  • Индикаторы Highest и Lowest с периодом 3 отслеживают верхнюю и нижнюю границы трёх последних свечей.
  • Параметр типа свечей по умолчанию задаёт 60-минутные бары, что повторяет требование исходного эксперта.
  • Дополнительные индикаторы не используются: стратегия опирается только на экстремумы цены.

Логика входа

  1. Стратегия обрабатывает только завершённые свечи подписки.
  2. Проверяется соответствие кода инструмента параметру SecurityCode (по умолчанию EURUSD). При несовпадении стратегия остаётся в режиме ожидания.
  3. Применяется опциональный фильтр торговых часов. По умолчанию входы разрешены в двухчасовом окне, начиная с полуночи (часы 0 и 1). Фильтр можно отключить.
  4. Рассчитывается диапазон трёх свечей range = highest - lowest и переводится в пункты с помощью PriceStep инструмента.
  5. Продолжение возможно только при условии, что диапазон находится между DiffMinPoints и DiffMaxPoints.
  6. Если цена закрытия попадает в нижнюю четверть диапазона и позиций нет, открывается длинная позиция.
  7. Если цена закрытия попадает в верхнюю четверть диапазона и позиций нет, открывается короткая позиция.

Управление позицией

  • Начальный стоп-лосс
    • Для покупок: lowest - range / 3.
    • Для продаж: highest + range / 3.
  • Тейк-профит
    • Для покупок: цена входа + TakeProfitPoints * PriceStep.
    • Для продаж: цена входа − TakeProfitPoints * PriceStep.
  • Трейлинг-стоп
    • Как только нереализованная прибыль превышает TrailingStopPoints * PriceStep, стоп-лосс подтягивается по закрытиям свечей.
    • Для покупок стоп переносится на closePrice - TrailingDistance, если новое значение выше текущего.
    • Для продаж стоп переносится на closePrice + TrailingDistance, если новое значение ниже текущего.
  • Выходы выполняются рыночными ордерами. Как только очередная свеча пробивает стоп или тейк, позиция закрывается полностью.

Параметры

Группа Имя Описание Значение по умолчанию
Общие CandleType Тип свечей для расчётов. Для повторения оригинала используйте 60-минутные бары. TimeFrame(60m)
Общие SecurityCode Ожидаемый код инструмента. Оставьте пустым, чтобы торговать любым инструментом. EURUSD
Фильтр диапазона DiffMinPoints Минимальный диапазон трёх свечей в пунктах, допускающий торговлю. 18
Фильтр диапазона DiffMaxPoints Максимальный диапазон трёх свечей в пунктах, при превышении которого сделки запрещены. 28
Торговое окно EnableTimeFilter Включение/отключение фильтра по часам. true
Торговое окно OpenHour Начальный час (0–23) торгового окна. Стратегия также допускает следующий час. 0
Риск-менеджмент TakeProfitPoints Дистанция тейк-профита в пунктах. Ноль отключает тейк. 8
Риск-менеджмент TrailingStopPoints Дистанция трейлинг-стопа в пунктах. Ноль отключает подтягивание. 6

Практические рекомендации

  • Объём сделок задаётся свойством Strategy.Volume. Убедитесь, что он соответствует контракту брокера.
  • Инструмент должен предоставлять корректный PriceStep. При его отсутствии стратегия использует значение 1 и записывает предупреждение в лог.
  • В оригинале присутствовал простой мани-менеджмент, рассчитывающий лоты от свободной маржи. В версии для StockSharp объём фиксированный; при необходимости добавьте собственную логику.
  • Перед запуском на реальном счёте протестируйте стратегию в симуляторе. При резких движениях возможен проскальзывание относительно рассчитанных уровней защиты.
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Flat 001a: RSI reversal with EMA filter and ATR stops.
/// </summary>
public class Flat001aStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _rsiLength;
	private readonly StrategyParam<int> _emaLength;
	private readonly StrategyParam<int> _atrLength;

	private decimal _prevRsi;
	private decimal _entryPrice;

	public Flat001aStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe.", "General");
		_rsiLength = Param(nameof(RsiLength), 14)
			.SetDisplay("RSI Length", "RSI period.", "Indicators");
		_emaLength = Param(nameof(EmaLength), 20)
			.SetDisplay("EMA Length", "Trend filter.", "Indicators");
		_atrLength = Param(nameof(AtrLength), 14)
			.SetDisplay("ATR Length", "ATR period.", "Indicators");
	}

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int RsiLength { get => _rsiLength.Value; set => _rsiLength.Value = value; }
	public int EmaLength { get => _emaLength.Value; set => _emaLength.Value = value; }
	public int AtrLength { get => _atrLength.Value; set => _atrLength.Value = value; }

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

		_prevRsi = 0; _entryPrice = 0;
	}

		protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevRsi = 0; _entryPrice = 0;
		var rsi = new RelativeStrengthIndex { Length = RsiLength };
		var ema = new ExponentialMovingAverage { Length = EmaLength };
		var atr = new AverageTrueRange { Length = AtrLength };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(rsi, ema, atr, ProcessCandle).Start();
		var area = CreateChartArea();
		if (area != null) { DrawCandles(area, subscription); DrawIndicator(area, ema); DrawOwnTrades(area); }
	}

	private void ProcessCandle(ICandleMessage candle, decimal rsiVal, decimal emaVal, decimal atrVal)
	{
		if (candle.State != CandleStates.Finished) return;
		if (_prevRsi == 0 || atrVal <= 0) { _prevRsi = rsiVal; return; }
		var close = candle.ClosePrice;

		if (Position > 0)
		{
			if (close >= _entryPrice + atrVal * 2.5m || close <= _entryPrice - atrVal * 1.5m || rsiVal > 70) { SellMarket(); _entryPrice = 0; }
		}
		else if (Position < 0)
		{
			if (close <= _entryPrice - atrVal * 2.5m || close >= _entryPrice + atrVal * 1.5m || rsiVal < 30) { BuyMarket(); _entryPrice = 0; }
		}

		if (Position == 0)
		{
			if (rsiVal > 55 && _prevRsi <= 55 && close > emaVal) { _entryPrice = close; BuyMarket(); }
			else if (rsiVal < 45 && _prevRsi >= 45 && close < emaVal) { _entryPrice = close; SellMarket(); }
		}
		_prevRsi = rsiVal;
	}
}