Открыть на GitHub

AIS2 Trading Robot 20005 (порт для StockSharp)

Обзор

AIS2 Trading Robot 20005 — это внутридневная стратегия пробоя диапазона, изначально созданная для MetaTrader 4. Порт переносит её многофреймовую логику на высокоуровневый API StockSharp и воспроизводит ключевые элементы оригинала:

  • анализ завершённых свечей старшего таймфрейма (по умолчанию 15 минут) и расчёт целевых дистанций по амплитуде свечи;
  • входы по рынку при пробое середины и экстремума предыдущей свечи с учётом спрэда и брокерских ограничений;
  • сопровождение позиции по виртуальному трейлинг-стопу, шаг которого задаётся диапазоном более быстрого таймфрейма (по умолчанию 1 минута).

Риск-менеджмент повторяет концепцию резервов из MQL-версии: часть капитала блокируется (AccountReserve), оставшаяся доля (OrderReserve) используется для расчёта объёма сделки. Дополнительно реализована пауза между операциями, чтобы исключить чрезмерную торговую активность.

Логика работы

  1. Свеча старшего таймфрейма — вычисляются середина диапазона, расстояния до стоп-лосса/тейк-профита (умножение диапазона на StopFactor и TakeFactor), минимальный шаг трейлинга и буферы стоп/фриз уровней.
  2. Условия входа
    • Лонг открывается, если закрытие выше середины, а ask пробивает максимум предыдущей свечи + спрэд.
    • Шорт открывается, если закрытие ниже середины, а bid пробивает минимум предыдущей свечи.
    • Сделка отклоняется, если рассчитанные стоп/тейк ближе допустимых брокером уровней.
  3. Размер позиции — рассчитывается по текущей стоимости портфеля. При недостатке капитала или несоблюдении ограничений объём обнуляется и сигнал игнорируется.
  4. Сопровождение — вторичный таймфрейм обновляет расстояние трейлинга. Стоп подтягивается только при устойчивом движении цены и соблюдении минимального шага.
  5. ЗащитаTradingPauseSeconds задаёт паузу между операциями. Для оценки спрэда стратегия подписывается на стакан, но при его отсутствии использует цены закрытия свечей.

Параметры

Параметр Описание Значение по умолчанию
PrimaryCandleType Таймфрейм для поиска сигналов. 15-минутные свечи
SecondaryCandleType Таймфрейм для расчёта трейлинга. 1-минутные свечи
TakeFactor Множитель диапазона для тейк-профита. 1.7
StopFactor Множитель диапазона для стоп-лосса. 1.7
TrailFactor Множитель диапазона для трейлинг-стопа. 0.5
AccountReserve Доля капитала, зарезервированная от торговли. 0.20
OrderReserve Доля капитала на одну сделку. 0.04
BaseVolume Резервный объём при невозможности рассчитать риск. 1 лот
StopBufferTicks Дополнительные тики для проверки стоп-уровней. 0
FreezeBufferTicks Буфер для предотвращения частых переносов стопа. 0
TrailStepMultiplier Множитель спрэда при проверке шага трейлинга. 1
TradingPauseSeconds Пауза между сделками. 5 секунд

Большинство числовых параметров подготовлены для оптимизации через SetCanOptimize().

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

  • Перед запуском убедитесь, что по инструменту доступны данные Level1/стакана, иначе расчёт спрэда и стоп-уровней будет консервативным.
  • Настройте PrimaryCandleType и SecondaryCandleType в соответствии с доступными таймфреймами источника данных. Подписка выполняется через SubscribeCandles.
  • Трейлинг-стоп реализован внутри стратегии — фактические защитные ордера не выставляются. При необходимости доработайте код и регистрируйте стоп-заявки после входа.
  • Метод StartProtection() активирован при старте стратегии и помогает закрыть чужие позиции, которые могли остаться в портфеле.

Отличия от MQL-реализации

  • Вместо глобальных переменных MetaTrader параметры инкапсулированы в StrategyParam, что упрощает изменение настроек и оптимизацию.
  • Вместо OrderModify применяются рыночные операции закрытия — это логичнее в рамках StockSharp и обеспечивает мгновенную реакцию на условия выхода.
  • Расчёт капитала и риск-параметров основан на данных портфеля StockSharp, а не на балансе счёта в MT4.

Содержимое

  • CS/Ais2TradingRobot20005Strategy.cs — исходный код стратегии.
  • README.md — описание на английском языке.
  • README_zh.md — описание на китайском языке.
  • README_ru.md — описание на русском языке (этот файл).
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// AIS2 Trading Robot: range breakout strategy.
/// Enters on close above/below previous candle range midpoint,
/// uses ATR for trailing stop management.
/// </summary>
public class Ais2TradingRobot20005Strategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _atrLength;
	private readonly StrategyParam<decimal> _takeFactor;
	private readonly StrategyParam<decimal> _stopFactor;

	private decimal _prevHigh;
	private decimal _prevLow;
	private decimal _prevMid;
	private decimal _entryPrice;
	private decimal _stopPrice;

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

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

		_takeFactor = Param(nameof(TakeFactor), 1.7m)
			.SetDisplay("Take Factor", "ATR multiplier for take profit.", "Risk");

		_stopFactor = Param(nameof(StopFactor), 1.0m)
			.SetDisplay("Stop Factor", "ATR multiplier for stop loss.", "Risk");
	}

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

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

	public decimal TakeFactor
	{
		get => _takeFactor.Value;
		set => _takeFactor.Value = value;
	}

	public decimal StopFactor
	{
		get => _stopFactor.Value;
		set => _stopFactor.Value = value;
	}

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

		_prevHigh = 0;
		_prevLow = 0;
		_prevMid = 0;
		_entryPrice = 0;
		_stopPrice = 0;
	}

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

		_prevHigh = 0;
		_prevLow = 0;
		_prevMid = 0;
		_entryPrice = 0;
		_stopPrice = 0;

		var atr = new AverageTrueRange { Length = AtrLength };

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

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

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

		if (_prevHigh == 0 || atrVal <= 0)
		{
			_prevHigh = candle.HighPrice;
			_prevLow = candle.LowPrice;
			_prevMid = (candle.HighPrice + candle.LowPrice) / 2m;
			return;
		}

		var close = candle.ClosePrice;
		var takeDistance = atrVal * TakeFactor;
		var stopDistance = atrVal * StopFactor;

		// Manage open position
		if (Position > 0)
		{
			// Take profit
			if (close - _entryPrice >= takeDistance)
			{
				SellMarket();
				_entryPrice = 0;
				_stopPrice = 0;
			}
			// Stop loss
			else if (_stopPrice > 0 && close <= _stopPrice)
			{
				SellMarket();
				_entryPrice = 0;
				_stopPrice = 0;
			}
			// Trail stop
			else
			{
				var newStop = close - stopDistance;
				if (newStop > _stopPrice)
					_stopPrice = newStop;
			}
		}
		else if (Position < 0)
		{
			if (_entryPrice - close >= takeDistance)
			{
				BuyMarket();
				_entryPrice = 0;
				_stopPrice = 0;
			}
			else if (_stopPrice > 0 && close >= _stopPrice)
			{
				BuyMarket();
				_entryPrice = 0;
				_stopPrice = 0;
			}
			else
			{
				var newStop = close + stopDistance;
				if (newStop < _stopPrice || _stopPrice == 0)
					_stopPrice = newStop;
			}
		}

		// New entry: breakout above previous high with close above midpoint
		if (Position == 0)
		{
			if (close > _prevHigh && close > _prevMid)
			{
				_entryPrice = close;
				_stopPrice = close - stopDistance;
				BuyMarket();
			}
			else if (close < _prevLow && close < _prevMid)
			{
				_entryPrice = close;
				_stopPrice = close + stopDistance;
				SellMarket();
			}
		}

		_prevHigh = candle.HighPrice;
		_prevLow = candle.LowPrice;
		_prevMid = (candle.HighPrice + candle.LowPrice) / 2m;
	}
}