Открыть на GitHub

AIS2 Trading Robot

Общая информация

AIS2 Trading Robot — это стратегия пробоя, перенесённая из оригинального советника MetaTrader 5. Алгоритм анализирует свечи старшего таймфрейма (по умолчанию 15 минут) для определения направления движения и использует младший таймфрейм (по умолчанию 1 минута) для построения адаптивного трейлинг-стопа. Управление позициями, расчёт рисков и логика модификации ордеров повторяют код MQ5, но реализованы поверх высокоуровневого API StockSharp.

Логика входа

  • Базовая свеча: по каждой завершённой свече основного таймфрейма сохраняются максимум, минимум, закрытие, середина диапазона и сам диапазон.
  • Условия для покупки:
    • Закрытие предыдущей свечи выше середины диапазона.
    • Текущая цена ask должна быть выше предыдущего максимума с учётом текущего спреда.
    • Цена входа — текущий ask. Стоп рассчитывается как high + spread − (range × StopFactor), тейк — ask + (range × TakeFactor).
    • Выполняются проверки на минимальное расстояние до стопа/тейка, чтобы не нарушать ограничения биржи.
  • Условия для продажи:
    • Закрытие ниже середины диапазона.
    • Текущий bid ниже минимума предыдущей свечи.
    • Цена входа — текущий bid. Стоп low + (range × StopFactor), тейк bid − (range × TakeFactor).
  • Работа с позицией: сделка открывается только при отсутствии позиции или при наличии встречной позиции — объём заявки автоматически перекрывает открытую позицию и создаёт новую в нужном направлении.

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

  • Трейлинг-стоп: диапазон свечи младшего таймфрейма умножается на TrailFactor. Для длинной позиции стоп подтягивается к bid − trailDistance, для короткой — к ask + trailDistance. Обновления игнорируются, если сделка не в прибыли либо предлагаемое изменение меньше минимального шага и заморозки.
  • Выход по стопу/тейку: как только bid (для лонга) или ask (для шорта) пересекает установленный уровень, позиция закрывается рыночным ордером.
  • Поток заявок: подписка на стакан цен обеспечивает доступ к актуальным bid/ask и позволяет воспроизвести логику SymbolInfo.Bid/Ask из MQ5.

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

  • Резерв счёта (AccountReserve): часть капитала исключается из торговли и служит подушкой безопасности, как в оригинальном советнике.
  • Лимит на сделку (OrderReserve): оставшийся капитал ограничивает максимальный риск на одну операцию.
  • Проверки:
    • Если объём резерва меньше расчётного лимита (Equity × OrderReserve), новые сделки не открываются.
    • Размер позиции рассчитывается по формуле riskBudget / |entry - stop| и приводится к шагу объёма инструмента. При отсутствии данных по портфелю используется параметр BaseVolume.

Параметры

Параметр Описание
AccountReserve Доля капитала, исключаемая из торговли (0–0.95).
OrderReserve Доля доступного капитала, выделяемая под риск одной сделки (0–1).
PrimaryCandleType Таймфрейм для расчёта сигналов (по умолчанию 15 минут).
SecondaryCandleType Таймфрейм для трейлинг-стопа (по умолчанию 1 минута).
TakeFactor Множитель диапазона основной свечи для тейк-профита.
StopFactor Множитель диапазона для стоп-лосса.
TrailFactor Множитель диапазона вторичной свечи для трейлинг-стопа.
BaseVolume Резервный объём, когда расчёт риска недоступен.
StopBufferTicks Дополнительный зазор к минимальной дистанции стопа (в тиках).
FreezeBufferTicks Дополнительный зазор, ограничивающий частые модификации стопа (в тиках).
TrailStepMultiplier Множитель спреда, задающий минимальный шаг подтягивания стопа.

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

  • Подайте в стратегию одновременно потоки свечей обоих таймфреймов и стакан заявок, иначе часть логики не активируется.
  • Поскольку сигналы используют bid/ask, тестирование только по последней цене может отличаться от результатов на реальном рынке.
  • При запуске автоматически включается защита позиции, как и в исходной реализации MQ5.
namespace StockSharp.Samples.Strategies;

using System;

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

/// <summary>
/// AIS2 Trading Robot strategy (simplified).
/// Breakout strategy using candle range with ATR filter.
/// Buys when close is near high of candle and ATR shows volatility.
/// Sells when close is near low of candle.
/// </summary>
public class Ais2TradingRobotStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _atrPeriod;
	private readonly StrategyParam<decimal> _breakoutThreshold;

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

	public int AtrPeriod
	{
		get => _atrPeriod.Value;
		set => _atrPeriod.Value = value;
	}

	public decimal BreakoutThreshold
	{
		get => _breakoutThreshold.Value;
		set => _breakoutThreshold.Value = value;
	}

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

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

		_breakoutThreshold = Param(nameof(BreakoutThreshold), 0.85m)
			.SetDisplay("Breakout Threshold", "Candle body ratio threshold (0-1)", "Signals");
	}

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

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind((ICandleMessage candle) =>
			{
				if (candle.State != CandleStates.Finished)
					return;

				if (!IsFormedAndOnlineAndAllowTrading())
					return;

				var range = candle.HighPrice - candle.LowPrice;
				if (range <= 0)
					return;

				var bodyRatio = (candle.ClosePrice - candle.LowPrice) / range;

				// Buy on strong bullish candle (close near high)
				if (bodyRatio > BreakoutThreshold && candle.ClosePrice > candle.OpenPrice && Position <= 0)
				{
					BuyMarket();
				}
				// Sell on strong bearish candle (close near low)
				else if (bodyRatio < (1m - BreakoutThreshold) && candle.ClosePrice < candle.OpenPrice && Position >= 0)
				{
					SellMarket();
				}
			})
			.Start();

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