Открыть на GitHub

For Max V2

Обзор

For Max V2 — порт советника MetaTrader 4 for_max_v2.mq4. Стратегия отслеживает два варианта свечного паттерна «поглощение» и после его появления ставит симметричные отложенные ордера Buy Stop и Sell Stop вокруг последней закрывшейся свечи. Как только пробой срабатывает, противоположный ордер удаляется, а позиция сопровождается фиксированным стопом, необязательным тейк-профитом и трейлингом, который сначала переносит защиту в зону небольшого профита, а затем следует за ценой.

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

Обнаружение паттерна

В оригинальном советнике реализовано два блока входов, оба сохранены:

  • Сценарий типа 1 — просматривает завершённые свечи в количестве Max Search (без текущей). Если минимум или максимум диапазона пришёлся на свечу два бара назад и эта свеча поглотила следующую (более высокий максимум и более низкий минимум), формируется страддл вокруг последней свечи.
  • Сценарий типа 2 — аналогично сканирует Max Search свечей, но требует, чтобы экстремум появился на предыдущем баре, который также поглощает ещё более раннюю свечу. После этого ордера выставляются относительно последней свечи. Оба сценария могут существовать параллельно и ведут собственные ордера и время жизни.

Размещение отложенных ордеров

  • Цены входа — Buy Stop ставится на максимум предыдущей свечи плюс Gap Points, Sell Stop — на минимум минус Gap Points.
  • Стоп-лоссы — для типа 1 лонг защищается минимумом свечи два бара назад (минус зазор), шорт — максимумом той же свечи (плюс зазор). Тип 2 использует экстремумы предыдущей свечи.
  • Тейк-профиты — опционально. Лонговая цель добавляет Gap Points + Buy Take Profit Points к предыдущему максимуму, шортовая вычитает Gap Points + Sell Take Profit Points из предыдущего минимума. Значение 0 отключает тейк-профит.
  • Срок действия — каждая пара ордеров получает время истечения: Order Expiry (bars) умножается на выбранный таймфрейм свечи. После истечения оба ордера удаляются, если ещё не исполнены.

Сопровождение позиции

  • После исполнения Buy Stop все остающиеся Sell Stop (из обоих сценариев) отменяются; при открытии шорта действует зеркальное правило.
  • Стопы и тейки контролируются на закрытых свечах. Если минимум свечи достигает стоп-лосса по лонгу (или максимум касается стопа по шорту), позиция закрывается рыночным ордером. Для тейк-профитов применяется та же логика.
  • Блок безубыточности (Break-even Trigger и Break-even Offset) переносит стоп на уровень входа плюс/минус заданное смещение, когда прибыль достигает порога.
  • Трейлинг удерживает стоп на расстоянии Long/Short Trailing Buffer пунктов от лучшего движения и, при необходимости, активируется только после выхода позиции в прибыль. Параметр Trailing Step задаёт минимальный шаг улучшения, без которого стоп не двигается.

Параметры

  • Volume — объём ордера для каждой пары отложенных заявок.
  • Buy Take Profit (points) — расстояние до тейк-профита для лонгов (0 — отключить).
  • Sell Take Profit (points) — расстояние до тейк-профита для шортов (0 — отключить).
  • Gap (points) — дополнительный зазор, прибавляемый к экстремумам и участвующий в расчёте тейк-профита.
  • Search Depth — количество завершённых свечей, используемых для поиска паттерна типов 1 и 2.
  • Order Expiry (bars) — число свечей, в течение которых страддл остаётся активным.
  • Break-even Trigger (points) — прибыль, необходимая для перевода стопа в безубыток.
  • Break-even Offset (points) — смещение стопа относительно цены входа при переходе в безубыток.
  • Long Trailing Buffer (points) — расстояние трейлинг-стопа от максимума движения в лонге.
  • Short Trailing Buffer (points) — расстояние трейлинг-стопа от минимума движения в шорте.
  • Trailing Step (points) — минимальный шаг улучшения, позволяющий подтянуть трейлинг-стоп.
  • Trail Only After Profit — если включено, трейлинг активируется только после появления прибыли выше буфера.
  • Candle Type — таймфрейм свечей, используемых для анализа, истечения ордеров и контроля выходов.

Дополнительная информация

  • Все значения в пунктах опираются на PriceStep инструмента. Для пятого и третьего знака после запятой преобразование совпадает с логикой MetaTrader.
  • Стопы и тейки исполняются рыночными заявками внутри стратегии, что повторяет поведение оригинального советника, работавшего по закрытым свечам.
  • Неиспользуемая функция vhod_3 из исходника не переносилась — реализованы только два задействованных блока входа.
  • В набор входит только реализация на C#, версия на Python не поставляется.
using System;

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

namespace StockSharp.Samples.Strategies;

/// <summary>
/// For Max V2: N-bar engulfing breakout with EMA filter and ATR stops.
/// </summary>
public class ForMaxV2Strategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _emaLength;
	private readonly StrategyParam<int> _atrLength;
	private readonly StrategyParam<int> _lookback;

	private decimal _entryPrice;
	private decimal _prevHigh;
	private decimal _prevLow;
	private readonly decimal[] _highs = new decimal[10];
	private readonly decimal[] _lows = new decimal[10];
	private int _barCount;

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

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

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

		_lookback = Param(nameof(Lookback), 10)
			.SetDisplay("Lookback", "N-bar channel lookback.", "Indicators");
	}

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

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

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

	public int Lookback
	{
		get => _lookback.Value;
		set => _lookback.Value = value;
	}

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

		_entryPrice = 0;
		_barCount = 0;
		_prevHigh = 0;
		_prevLow = 0;
		Array.Clear(_highs, 0, _highs.Length);
		Array.Clear(_lows, 0, _lows.Length);
	}

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

		_entryPrice = 0;
		_barCount = 0;
		_prevHigh = 0;
		_prevLow = 0;

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

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(ema, 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 atrVal)
	{
		if (candle.State != CandleStates.Finished)
			return;

		var len = Math.Min(Lookback, _highs.Length);
		var idx = _barCount % len;
		_highs[idx] = candle.HighPrice;
		_lows[idx] = candle.LowPrice;
		_barCount++;

		if (_barCount < len || atrVal <= 0)
			return;

		var high = decimal.MinValue;
		var low = decimal.MaxValue;
		for (var i = 0; i < len; i++)
		{
			if (_highs[i] > high) high = _highs[i];
			if (_lows[i] < low) low = _lows[i];
		}

		var close = candle.ClosePrice;

		if (_prevHigh == 0 || _prevLow == 0)
		{
			_prevHigh = high;
			_prevLow = low;
			return;
		}

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

		if (Position == 0)
		{
			if (close > _prevHigh && close > emaVal)
			{
				_entryPrice = close;
				BuyMarket();
			}
			else if (close < _prevLow && close < emaVal)
			{
				_entryPrice = close;
				SellMarket();
			}
		}

		_prevHigh = high;
		_prevLow = low;
	}
}