Открыть на GitHub

Стратегия GPF TCPivotLimit

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

Стратегия GPF TCPivotLimit переносит советник MetaTrader 4 gpfTCPivotLimit.mq4 в инфраструктуру StockSharp. Торговля ведётся по часовым свечам с использованием классических дневных уровней Pivot. В начале каждого нового дня стратегия пересчитывает уровни Pivot, три сопротивления (R1–R3) и три поддержки (S1–S3) на основании максимума, минимума и цены закрытия прошлого дня. Далее анализируются две последние закрытые свечи, чтобы определить ложный пробой уровней и открыть сделку в противоположную сторону.

Алгоритм работы

  1. Расчёт Pivot – при смене дня сохраняются данные прошлого дня и вычисляются уровни:
    • Pivot = (High + Low + Close) / 3
    • R1 = 2 × Pivot − Low, S1 = 2 × Pivot − High
    • R2 = Pivot + (High − Low), S2 = Pivot − (High − Low)
    • R3 = High + 2 × (Pivot − Low), S3 = Low − 2 × (High − Pivot)
  2. Подтверждение входа – уже в новом дне рассматриваются свечи t-2 и t-1:
    • Продажа открывается, если свеча t-2 сходила выше выбранного сопротивления (high > уровень либо close ≥ уровень), открылась ниже него, а свеча t-1 закрылась ниже уровня.
    • Покупка открывается, если свеча t-2 ушла ниже выбранной поддержки (low < уровень либо close ≤ уровень), открылась выше него, а свеча t-1 закрылась выше уровня.
  3. Наборы целей – входной параметр TargetMode полностью повторяет варианты оригинала. Таблица ниже показывает соответствие.
TargetMode Триггер лонга Стоп лонга Цель лонга Триггер шорта Стоп шорта Цель шорта
1 S1 S2 R1 R1 R2 S1
2 S1 S2 R2 R1 R2 S2
3 S2 S3 R1 R2 R3 S1
4 S2 S3 R2 R2 R3 S2
5 S2 S3 R3 R2 R3 S3
  1. Риск-менеджмент – стоп-лосс и тейк-профит проверяются на каждой закрытой свече. Дополнительно реализован трейлинг-стоп: как только плавающая прибыль превышает заданное расстояние, стоп подтягивается вслед за ценой. Опционально позиция закрывается в 23:00 по времени платформы.

  2. Адаптация объёма – параметр MetaTrader isFloatLots соответствует переключателю UseDynamicVolume. При включении лот уменьшается после серии убыточных сделок с учётом DrawdownFactor и RiskPercentage.

Параметры

Имя Описание По умолчанию
BaseVolume Базовый объём рыночных заявок до применения корректировок по риску. 1
UseDynamicVolume Уменьшает объём после более чем одной убыточной сделки подряд. false
RiskPercentage Эталонная доля риска на сделку (аналог MaxR), масштабирует базовый объём. 0.02
DrawdownFactor Делитель при уменьшении объёма после серии убытков (аналог DcF). 3
TargetMode Выбор комбинации уровней сопротивления/поддержки (аналог TgtProfit). 1
TrailingPoints Дистанция трейлинг-стопа в пунктах инструмента; 0 отключает функцию. 30
CloseAtSessionEnd При значении true позиция закрывается на свече 23:00. false
LogSignals Записывает значения Pivot, входы и выходы в лог стратегии вместо e-mail. false
CandleType Тип свечей для анализа (по умолчанию часовые). TimeFrameCandleMessage(1h)

Дополнительные замечания

  • Заявки всегда рыночные, как и в исходном советнике; отложенные ордера не используются.
  • Выходы по стопу и цели исполняются рыночными заявками для совместимости с любым коннектором StockSharp.
  • Для трейлинг-стопа требуется корректно заполненный PriceStep. При отсутствии шага цены трейлинг автоматически отключается.
  • Флаг отправки e-mail заменён параметром LogSignals, который выводит подробные сообщения в журнал.
using System;



using StockSharp.Algo.Indicators;

using StockSharp.Algo.Strategies;

using StockSharp.BusinessEntities;

using StockSharp.Messages;



namespace StockSharp.Samples.Strategies;



public class GpfTcpPivotLimitStrategy : Strategy

{

	private readonly StrategyParam<int> _channelPeriod;

	private readonly StrategyParam<int> _emaPeriod;

	private readonly StrategyParam<DataType> _candleType;



	private decimal _prevClose; private decimal _prevMid; private bool _hasPrev;

	private int _cooldown;



	public int ChannelPeriod { get => _channelPeriod.Value; set => _channelPeriod.Value = value; }

	public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }

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



	public GpfTcpPivotLimitStrategy()

	{

		_channelPeriod = Param(nameof(ChannelPeriod), 20).SetDisplay("Channel Period", "Pivot lookback", "Indicators");

		_emaPeriod = Param(nameof(EmaPeriod), 14).SetDisplay("EMA Period", "EMA filter", "Indicators");

		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame()).SetDisplay("Candle Type", "Candle timeframe", "General");

	}



	/// <inheritdoc />

	protected override void OnReseted()

	{

		base.OnReseted();

		_prevClose = default;

		_prevMid = default;

		_hasPrev = default;

		_cooldown = default;

	}



	/// <inheritdoc />

	protected override void OnStarted2(DateTime time)

	{

		base.OnStarted2(time);

		_hasPrev = false;

		var highest = new Highest { Length = ChannelPeriod };

		var lowest = new Lowest { Length = ChannelPeriod };

		var subscription = SubscribeCandles(CandleType);

		subscription.Bind(highest, lowest, ProcessCandle).Start();

	}



	private void ProcessCandle(ICandleMessage candle, decimal highest, decimal lowest)

	{

		if (candle.State != CandleStates.Finished) return;

		if (!IsFormedAndOnlineAndAllowTrading()) return;

		var close = candle.ClosePrice;

		var mid = (highest + lowest) / 2;

		if (!_hasPrev) { _prevClose = close; _prevMid = mid; _hasPrev = true; return; }

		if (_cooldown > 0)

		{

			_cooldown--;

			_prevClose = close; _prevMid = mid;

			return;

		}



		if (_prevClose <= _prevMid && close > mid && Position <= 0)

		{

			var volume = Volume + Math.Abs(Position);

			BuyMarket(volume);

			_cooldown = 2;

		}

		else if (_prevClose >= _prevMid && close < mid && Position >= 0)

		{

			var volume = Volume + Math.Abs(Position);

			SellMarket(volume);

			_cooldown = 2;

		}

		_prevClose = close; _prevMid = mid;

	}

}