Открыть на GitHub

Twenty200 Time Breakout

Стратегия представляет собой порт советника MetaTrader 20/200 expert v4.2 (AntS). Каждый день в заданный час она сравнивает два значения цены открытия прошлых часовых свечей (по умолчанию 6 и 2 бара назад). Если более дальняя свеча открылась выше ближней на величину, превышающую Short Delta пунктов, открывается короткая позиция. Обратное соотношение, превышающее Long Delta, приводит к покупке.

Логика торговли

  • Подписка ведётся на часовые свечи (тип задаётся параметром Candle Type).
  • В сутки допускается только одна сделка. Заявки отправляются, когда активируется свеча с часом, равным Trade Hour.
  • Сигналы используют цену открытия свечей LookbackFar и LookbackNear баров назад.
    • Продажа: Open[t1] - Open[t2] > Short Delta × пункт.
    • Покупка: Open[t2] - Open[t1] > Long Delta × пункт.
  • После появления сигнала выставляется рыночный ордер вычисленного объёма. Стоп-лосс и тейк-профит копируют значения советника и задаются в пунктах; стратегия переводит их в цену через Security.PriceStep.
  • Пока позиция открыта, новых входов не происходит. На следующей календарной дате торговля возобновляется.

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

  • Стоп-лосс и тейк-профит контролируются на каждом обновлении свечи по её максимуму и минимуму.
  • Параметр Max Open Hours принудительно закрывает позицию по рынку, если её жизнь превышает заданное количество часов (по умолчанию 504). Нулевое значение отключает ограничение.

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

  • Fixed Volume — базовый объём сделки, используемый при выключенном авто-лоте или отсутствии данных о балансе.
  • При включённом Use Auto Lot размер позиции повторяет огромную таблицу из MT4. В StockSharp она аппроксимирована формулой volume = round(balance × Auto Lot Factor, 2) с коэффициентом 0.000038, что даёт ту же лесенку лотов в диапазоне от 300 до 270 000+ USD.
  • Если текущая стоимость портфеля опускается ниже последнего зафиксированного значения, следующий вход умножается на Big Lot Multiplier, что соответствует режиму «Big Lot» оригинального советника.
  • Объёмы приводятся к Security.VolumeStep и ограничиваются диапазоном MinVolume/MaxVolume, если он задан.

Отличия от оригинала

  • В MT4 вручную перечислено более тысячи порогов для авто-лота. В порте используется линейный коэффициент (Auto Lot Factor), воспроизводящий ту же ступенчатую зависимость. При необходимости точного совпадения для другого брокера скорректируйте коэффициент.
  • Стоп и тейк реализованы как рыночные закрытия при достижении уровней свечой, что обеспечивает одинаковое поведение в тестах и на реале без биржевых стоп-заявок.
  • Глобальные переменные globalBalans и globalPosic заменены на состояние в памяти, дополнительная инфраструктура не требуется.

Параметры

Параметр Описание
Long/Short Take Profit Дистанция тейк-профита в пунктах.
Long/Short Stop Loss Дистанция стоп-лосса в пунктах.
Trade Hour Час (0–23), когда разрешены входы.
Far/Near Lookback Количество баров для выборки двух цен открытия.
Long/Short Delta Требуемый разрыв в пунктах для входа.
Max Open Hours Максимальная продолжительность позиции в часах (0 отключает).
Fixed Volume Базовый объём при выключенном авто-лоте.
Use Auto Lot Включение авто-расчёта объёма по балансу.
Auto Lot Factor Множитель для аппроксимации MT4-таблицы лотов.
Big Lot Multiplier Множитель объёма после просадки счёта.
Candle Type Таймфрейм свечей для сигналов.
using System;

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

namespace StockSharp.Samples.Strategies;

public class Twenty200TimeBreakoutStrategy : Strategy
{
	private readonly StrategyParam<int> _shortPeriod;
	private readonly StrategyParam<int> _longPeriod;
	private readonly StrategyParam<int> _cooldownCandles;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _prevShort;
	private decimal _prevLong;
	private bool _hasPrev;
	private int _cooldownRemaining;

	public int ShortPeriod { get => _shortPeriod.Value; set => _shortPeriod.Value = value; }
	public int LongPeriod { get => _longPeriod.Value; set => _longPeriod.Value = value; }
	public int CooldownCandles { get => _cooldownCandles.Value; set => _cooldownCandles.Value = value; }
	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }

	public Twenty200TimeBreakoutStrategy()
	{
		_shortPeriod = Param(nameof(ShortPeriod), 20).SetDisplay("Short SMA", "Short SMA period", "Indicators");
		_longPeriod = Param(nameof(LongPeriod), 200).SetDisplay("Long SMA", "Long SMA period", "Indicators");
		_cooldownCandles = Param(nameof(CooldownCandles), 100).SetDisplay("Cooldown", "Candles between signals", "General");
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame()).SetDisplay("Candle Type", "Candle timeframe", "General");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevShort = default;
		_prevLong = default;
		_hasPrev = default;
		_cooldownRemaining = default;
	}

	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevShort = 0;
		_prevLong = 0;
		_hasPrev = false;
		_cooldownRemaining = 0;

		var shortSma = new SimpleMovingAverage { Length = ShortPeriod };
		var longSma = new SimpleMovingAverage { Length = LongPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(shortSma, longSma, ProcessCandle).Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal shortSma, decimal longSma)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!_hasPrev) { _prevShort = shortSma; _prevLong = longSma; _hasPrev = true; return; }

		if (_cooldownRemaining > 0)
		{
			_cooldownRemaining--;
			_prevShort = shortSma;
			_prevLong = longSma;
			return;
		}

		if (_prevShort <= _prevLong && shortSma > longSma && Position <= 0)
		{
			if (Position < 0) BuyMarket();
			BuyMarket();
			_cooldownRemaining = CooldownCandles;
		}
		else if (_prevShort >= _prevLong && shortSma < longSma && Position >= 0)
		{
			if (Position > 0) SellMarket();
			SellMarket();
			_cooldownRemaining = CooldownCandles;
		}
		_prevShort = shortSma;
		_prevLong = longSma;
	}
}