Открыть на GitHub

Стратегия Hans123Trader (StockSharp)

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

Стратегия Hans123Trader повторяет логику советника MetaTrader "Hans123Trader v1" на высокоуровневом API StockSharp. Алгоритм дважды в день выставляет стоп-заявки на пробой диапазона, сформированного последними 80 пятиминутными свечами. Решение ориентировано на валютные инструменты, где шаг цены соответствует дробной величине пункта. В начале каждого нового торгового дня все отложенные ордера обновляются, а открытые позиции принудительно закрываются.

Последовательность работы

  1. Отслеживание диапазона. Индикаторы Highest и Lowest ведут скользящее окно из 80 свечей таймфрейма 5 минут. Максимум и минимум определяют уровни потенциального пробоя.
  2. Планирование сессий. Торговые окна задаются параметрами EndSession1 и EndSession2. Как только наступает соответствующий час (при минуте 00), стратегия пересчитывает и выставляет новые стоп-заявки.
  3. Постановка ордеров. Бай-стоп размещается на 5 пунктов выше диапазона, селл-стоп — на 5 пунктов ниже. При смене календарного дня заявки снимаются, имитируя истечение срока действия в MetaTrader в 23:59.
  4. Ведение позиции. После входа применяются стартовый стоп-лосс, опциональный тейк-профит и трейлинг-стоп. Все защитные уровни задаются в пунктах и переводятся в цену через PriceStep инструмента.
  5. Ежедневная очистка. При переходе на новый день открытая позиция закрывается рыночной сделкой. Отложенные ордера прошлого дня отменяются прежде, чем будут подготовлены новые.

Правила торговли

  • Условия входа
    • Две попытки входа в сутки: первая в час EndSession1, вторая — в час EndSession2 (по времени сервера брокера).
    • Цена бай-стопа = High диапазона + 5 пунктов. Цена селл-стопа = Low диапазона − 5 пунктов.
    • Объём ордеров берётся из параметра Volume (по умолчанию 1).
    • Если объём ≤ 0, заявки не выставляются.
  • Условия выхода
    • Начальный стоп-лосс = цена входа ± InitialStopLoss пунктов (ниже для покупок, выше для продаж).
    • Тейк-профит = цена входа ± TakeProfit пунктов (выше для покупок, ниже для продаж).
    • Трейлинг-стоп подтягивает защитный уровень, когда цена закрытия уходит в прибыль минимум на TrailingStop пунктов.
    • Позиция, оставшаяся до следующего дня, закрывается немедленно рыночным ордером.
  • Обслуживание ордеров
    • Отложенные стоп-заявки отменяются в начале каждого календарного дня.
    • Как только заявка исполняется, отменяется или завершается с ошибкой, ссылка на неё очищается.

Параметры

Параметр Назначение
BeginSession1 / BeginSession2 Сохранены для совместимости (подсказка начала окна); текущая реализация использует конечные часы.
EndSession1 / EndSession2 Часы (0–23), в которые выставляются новые стоп-заявки. Минуты должны равняться нулю.
TrailingStop Дистанция трейлинг-стопа в пунктах. 0 отключает трейлинг.
TakeProfit Дистанция тейк-профита в пунктах. 0 отключает тейк-профит.
InitialStopLoss Начальный стоп-лосс в пунктах. 0 оставляет сделку без стопа (пока не сработает трейлинг).
CandleType Серия свечей для расчёта диапазона (по умолчанию 5 минут).
Volume Базовый объём стратегии.

Особенности конверсии

  • Функция MetaTrader OrderSendExtended и блокировка через глобальные переменные не потребовались: управление очередями заявок берет на себя StockSharp.
  • Вместо magic-номеров используются явные поля _session*, которые очищаются при завершении жизненного цикла ордера.
  • Истечение отложенных заявок в 23:59 заменено отменой при наступлении нового дня.
  • Для трейлинга используются цены закрытия свечей как приближение котировок Bid/Ask из MetaTrader.
  • Параметры в пунктах переводятся в абсолютную цену путём умножения на Security.PriceStep. Если шаг цены не задан, значения трактуются как абсолютные.

Рекомендации по применению

  • Назначайте инструменты с корректно настроенными PriceStep, StepPrice и VolumeStep, иначе пересчёт пунктов и объёмов будет некорректен.
  • Убедитесь, что доступна история пятиминутных свечей: пробойные уровни строятся по последним 80 барам.
  • Настраивайте EndSession1/EndSession2 под нужные торговые сессии (например, перед началом Лондона и Нью-Йорка).
  • Перед запуском протестируйте и оптимизируйте InitialStopLoss, TakeProfit и TrailingStop в Designer или Runner для конкретного инструмента.
  • Дополнительно подключайте риск-менеджмент StockSharp, если несколько стратегий работают на одном портфеле.
using System;
using System.Collections.Generic;

using Ecng.Common;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// Hans123 breakout strategy. Builds a range from recent highest/lowest prices
/// and enters on breakout above range high or below range low.
/// </summary>
public class Hans123TraderRangeBreakoutStrategy : Strategy
{
	private readonly StrategyParam<int> _rangeLength;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _entryPrice;
	private decimal _prevHighest;
	private decimal _prevLowest;

	public Hans123TraderRangeBreakoutStrategy()
	{
		_rangeLength = Param(nameof(RangeLength), 20)
			.SetDisplay("Range Length", "Number of candles used to compute the breakout range.", "Breakout");

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
			.SetDisplay("Candle Type", "Candle series for range detection.", "General");
	}

	public int RangeLength
	{
		get => _rangeLength.Value;
		set => _rangeLength.Value = value;
	}

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

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

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

		_entryPrice = 0;
		_prevHighest = 0;
		_prevLowest = 0;
	}

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

		_entryPrice = 0;
		_prevHighest = 0;
		_prevLowest = 0;

		var highest = new Highest { Length = RangeLength };
		var lowest = new Lowest { Length = RangeLength };

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

		StartProtection(
			takeProfit: new Unit(2, UnitTypes.Percent),
			stopLoss: new Unit(1, UnitTypes.Percent)
		);

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

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

		if (highestValue <= 0 || lowestValue <= 0)
		{
			_prevHighest = highestValue;
			_prevLowest = lowestValue;
			return;
		}

		// Entry on breakout using previous levels
		if (Position == 0 && _prevHighest > 0 && _prevLowest > 0)
		{
			if (candle.ClosePrice > _prevHighest)
			{
				BuyMarket();
				_entryPrice = candle.ClosePrice;
			}
			else if (candle.ClosePrice < _prevLowest)
			{
				SellMarket();
				_entryPrice = candle.ClosePrice;
			}
		}

		_prevHighest = highestValue;
		_prevLowest = lowestValue;
	}
}