Открыть на GitHub

AIS5 Trade Machine

Обзор

AIS5 Trade Machine переносит советника MetaTrader 4 AIS5TM.mq4 на высокоуровневый API StockSharp. Исходная программа строила профили рынка на двух таймфреймах и предоставляла полуавтоматическую панель управления. Версия для StockSharp сохраняет идею выделения сильных и слабых ценовых зон с помощью накопления тикового объёма и превращает её в полностью автоматическую стратегию пробоя с адаптивным управлением риском на основе ATR.

Стратегия подписывается на две ленты свечей:

  • Таймфрейм профиля (по умолчанию 15 минут) – аккумулирует объёмы и определяет сильные/слабые зоны.
  • Торговый таймфрейм (по умолчанию 1 минута) – отслеживает пробои вдали от обнаруженных зон с подтверждением объёмом.

Позиции защищаются стопами, привязанными к ATR, и масштабируемыми целями. Сжатие объёма приводит к раннему выходу из сделки, что повторяет дисциплину мониторинга в исходном коде MT4.

Логика стратегии

Определение зон (таймфрейм профиля)

  • Каждая завершённая свеча старшего таймфрейма обновляет простые скользящие средние (SMA) тикового объёма.
  • Свеча отмечается как сильная зона, если её объём превышает средний, умноженный на параметр Strong Volume Mult. Цена закрытия сохраняется как последняя сильная отметка.
  • Свеча отмечается как слабая зона, если её объём ниже среднего, делённого на Weak Volume Divider. Цена закрытия становится последней слабой отметкой.
  • В расчёте участвуют только закрытые свечи. Пока SMA профиля не сформирована, сигналы игнорируются, чтобы избежать шума на старте.

Входы на пробой (торговый таймфрейм)

  • Свечи младшего таймфрейма ожидают формирования своей SMA объёма и индикатора ATR.
  • Для входа в лонг цена закрытия должна превысить последнюю сильную отметку на величину суммы Zone Base Points и Zone Step Points (пересчитанных через шаг цены инструмента). Одновременно текущий объём должен превышать среднее значение.
  • Для входа в шорт условия зеркальные: требуется пробой слабой зоны вниз на ту же комбинированную дистанцию и подтверждение ростом объёма.
  • Оригинальный советник поддерживал ручные команды и сетку ордеров. Порт на StockSharp использует одиночную позицию и открывает сделку только при отсутствии текущего экспозиции.

Управление выходом

  • При открытии позиции фиксируется цена входа, рассчитывается защитный стоп (ATR, умноженный на ATR Multiplier и ограниченный базовым буфером) и целевая отметка равная стоп-дистанции, умноженной на делитель слабого объёма. Это выравнивает риск и потенциал прибыли относительно структуры объёма.
  • Пока позиция открыта, каждая завершённая свеча младшего таймфрейма проверяет:
    • Достигнута ли цель или сработал ли стоп – в этом случае позиция закрывается сразу.
    • Снизился ли объём ниже слабого порога до срабатывания уровней – тогда сделка закрывается досрочно, чтобы не оставаться в «пустой» зоне.
  • После возврата позиции к нулю внутреннее состояние сбрасывается и стратегия готова к следующему пробою.

Параметры

  • Profile Candle – тип свечей, используемых для построения профиля объёма (по умолчанию 15 минут).
  • Trading Candle – низкий таймфрейм для сигналов и выхода (по умолчанию 1 минута).
  • Volume Lookback – длина окна для SMA объёма и периода ATR.
  • Strong Volume Mult – множитель среднего объёма для пометки сильных зон (соответствует Parameter.1 в MQL).
  • Weak Volume Divider – делитель среднего объёма для слабых зон и расчёта цели (соответствует Parameter.2).
  • ATR Multiplier – коэффициент, применяемый к ATR при определении дистанции стопа (соответствует Parameter.3).
  • Zone Base Points – минимальный буфер в пунктах перед проверкой пробоя (соответствует ZoneBasePoints).
  • Zone Step Points – дополнительный буфер пробоя, требующий большего удаления цены от зоны (соответствует ZoneStepPoints).
  • Volume – параметр базового класса Strategy, задающий объём рыночных заявок.

Дополнительные замечания

  • Если у инструмента не задан шаг цены, стратегия использует значение 0.0001, чтобы параметры в пунктах работали для большинства валютных пар.
  • Все индикаторы используют только закрытые свечи, что соответствует оригинальному советнику MT4.
  • В портированной версии нет ручной панели и загрузки профилей из файлов – зоны пересчитываются только по текущим данным, благодаря чему стратегия полностью автономна.
  • Python-реализация отсутствует.
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// AIS5 Trade Machine: EMA breakout with RSI filter and ATR stops.
/// </summary>
public class Ais5TradeMachineStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _emaLength;
	private readonly StrategyParam<int> _rsiLength;
	private readonly StrategyParam<int> _atrLength;

	private decimal _prevClose;
	private decimal _entryPrice;

	public Ais5TradeMachineStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(8).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe.", "General");

		_emaLength = Param(nameof(EmaLength), 20)
			.SetDisplay("EMA Length", "Trend filter.", "Indicators");

		_rsiLength = Param(nameof(RsiLength), 14)
			.SetDisplay("RSI Length", "RSI period.", "Indicators");

		_atrLength = Param(nameof(AtrLength), 14)
			.SetDisplay("ATR Length", "ATR period.", "Indicators");
	}

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public int EmaLength
	{
		get => _emaLength.Value;
		set => _emaLength.Value = value;
	}

	public int RsiLength
	{
		get => _rsiLength.Value;
		set => _rsiLength.Value = value;
	}

	public int AtrLength
	{
		get => _atrLength.Value;
		set => _atrLength.Value = value;
	}

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

		_prevClose = 0;
		_entryPrice = 0;
	}

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

		_prevClose = 0;
		_entryPrice = 0;

		var ema = new ExponentialMovingAverage { Length = EmaLength };
		var rsi = new RelativeStrengthIndex { Length = RsiLength };
		var atr = new AverageTrueRange { Length = AtrLength };

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

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

	private void ProcessCandle(ICandleMessage candle, decimal emaVal, decimal rsiVal, decimal atrVal)
	{
		if (candle.State != CandleStates.Finished)
			return;

		var close = candle.ClosePrice;

		if (_prevClose == 0 || atrVal <= 0)
		{
			_prevClose = close;
			return;
		}

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

		if (Position == 0)
		{
			if (close > emaVal && _prevClose <= emaVal && rsiVal > 50)
			{
				_entryPrice = close;
				BuyMarket();
			}
			else if (close < emaVal && _prevClose >= emaVal && rsiVal < 50)
			{
				_entryPrice = close;
				SellMarket();
			}
		}

		_prevClose = close;
	}
}