Открыть на GitHub

Стратегия Peter Panel

Peter Panel Strategy переносит ручную панель MetaTrader 5 «Peter Panel» в экосистему StockSharp. В оригинальном советнике на графике находились три горизонтальные линии (вход, тейк-профит, стоп-лосс) и блок кнопок. Торговец нажимал нужную кнопку — и заявка отправлялась на основе текущих линий. Перевод на C# сохраняет ту же логику, но вместо интерфейсных кнопок использует параметры стратегии. Как и в исходной панели, установка булевого параметра в значение true сразу выполняет действие, после чего параметр автоматически возвращается в false.

Основные идеи

  1. Ассистент для ручной торговли – стратегия не генерирует собственных сигналов; решение принимает трейдер, переключая параметры.
  2. Общие ценовые линии – бирюзовая линия входа, зелёная линия тейк-профита и красная линия стоп-лосса представлены тремя числовыми параметрами. Их можно задать вручную или пересчитать относительно текущей середины спрэда с помощью ResetCommand.
  3. Полный набор ордеров – реализованы все шесть операций из панели: покупка и продажа по рынку, buy stop, buy limit, sell stop и sell limit. После каждой сделки выставляются защитные ордера, имитирующие заполнение полей TP/SL в MetaTrader.
  4. Массовая модификация – параметр ModifyCommand повторно применяет текущие уровни ко всем активным отложенным ордерам и защитным стопам/тейкам открытой позиции.
  5. Одно нажатие – полный выходCloseCommand отменяет отложенные ордера, снимает защитные заявки и закрывает совокупную позицию по рынку.

Сравнение с оригинальным советником

Возможность MetaTrader 5 Peter Panel StockSharp Peter Panel Strategy
Интерфейс Графический диалог с кнопками и полями ввода Параметры стратегии в менеджере S#
Управление линиями Перетаскивание линий или кнопка «Reset» Редактирование параметров или ResetCommand
Отправка заявок Кнопка вызывает OrderSend Переключатель вызывает метод Buy/Sell и сохраняет ссылку на ордер
Тейк/стоп Заполняются в MqlTradeRequest.tp/sl Выставляются отдельными стоп- и лимит-заявками после сделки
Модификация Выбор тикета и кнопка «Modify» ModifyCommand отменяет и пересоздаёт все отложенные заявки, обновляя защиту
Закрытие Кнопка «Close» для выбранного тикета CloseCommand закрывает всю позицию и очищает ордеры
Список ордеров Таблица на панели Состояние отслеживается через журналы и стандартные отчёты StockSharp

Важно: в MetaTrader трейдер выбирал конкретный тикет. В параметрах стратегии подобный выбор невозможен, поэтому все операции применяются ко всем ордерам, созданным стратегией.

Параметры

Параметр Описание
Volume Объём сделки в лотах. Проверяется на соответствие минимальному объёму и шагу инструмента.
EntryLevel Цена для отложенных ордеров (бирюзовая линия).
TakeProfitLevel Зелёная линия. Для длинных позиций – тейк-профит, для коротких – уровень защитного стопа.
StopLossLevel Красная линия. Для длинных – стоп-лосс, для коротких – целевой тейк.
BuyMarketCommand Отправляет рыночный ордер на покупку и сбрасывается.
BuyStopCommand Размещает buy stop на EntryLevel.
BuyLimitCommand Размещает buy limit на EntryLevel.
SellMarketCommand Отправляет рыночный ордер на продажу.
SellStopCommand Размещает sell stop на EntryLevel.
SellLimitCommand Размещает sell limit на EntryLevel.
ModifyCommand Повторно применяет уровни ко всем отложенным заявкам и защитным ордерам.
CloseCommand Снимает отложенные заявки, удаляет защиту и закрывает позицию.
ResetCommand Пересчитывает уровни вокруг текущего mid-цены (среднее между bid и ask).

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

  1. Подключите инструмент и портфель, затем запустите стратегию. При старте оформляется подписка на Level1, чтобы ResetCommand получал актуальный спред.
  2. Установите цены линий вручную или воспользуйтесь ResetCommand, который центрирует уровни вокруг текущего рынка.
  3. Активируйте нужный торговый параметр. После выполнения действие параметр автоматически сбрасывается в false.
  4. После исполнения ордера стратегия выставляет защитные заявки: для длинной позиции это sell stop на красной линии и sell limit на зелёной; для короткой – зеркальная комбинация.
  5. Измените уровни и нажмите ModifyCommand, чтобы обновить активные заявки без перезапуска стратегии.
  6. По завершении торговли включите CloseCommand для полного выхода и очистки очереди ордеров.

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

  • Нет визуального списка тикетов. Информацию о состоянии можно получить через логи, отчёты и собственные пользовательские панели.
  • StopLoss/TakeProfit реализованы как отдельные заявки, поскольку StockSharp не позволяет передать их в базовом ордере, но итоговое поведение идентично.
  • При модификации используется схема «снять и заново выставить», что упрощает поддержку разных торговых шлюзов.

Рекомендации по эксплуатации

  • Свяжите параметры стратегии с пользовательской панелью или горячими клавишами, чтобы повторить оригинальный опыт взаимодействия.
  • Стратегия не ставит действия в очередь. Если нужно выполнить серию команд, дождитесь, когда предыдущий параметр снова станет false.
  • Защитные заявки создаются только при ненулевой позиции. Если была выставлена отложенная заявка и она исполнилась позже, нажмите ModifyCommand, чтобы гарантированно обновить стопы и тейки.

Меры предосторожности

  • Перед отправкой ордеров убедитесь, что заданы портфель и инструмент, а также известен шаг цены; при отсутствии данных стратегия выдаёт предупреждение.
  • Если объём после приведения к шагу оказывается равен нулю, заявка не отправляется, а в журнале появляется предупреждение.
  • При выполнении CloseCommand порядок действий следующий: сначала снимаются защитные заявки, затем отложенные ордера, после чего позиция закрывается по рынку. Это повторяет консервативную схему из оригинального советника.
namespace StockSharp.Samples.Strategies;

using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;

/// <summary>
/// Peter Panel strategy: MACD histogram crossover.
/// Buys when MACD histogram turns positive, sells when it turns negative.
/// </summary>
public class PeterPanelStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _signalPeriod;

	private decimal _prevMacd;
	private decimal _prevSignal;
	private bool _hasPrev;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
	public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
	public int SignalPeriod { get => _signalPeriod.Value; set => _signalPeriod.Value = value; }

	public PeterPanelStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
		_fastPeriod = Param(nameof(FastPeriod), 12)
			.SetGreaterThanZero()
			.SetDisplay("Fast Period", "MACD fast period", "Indicators");
		_slowPeriod = Param(nameof(SlowPeriod), 26)
			.SetGreaterThanZero()
			.SetDisplay("Slow Period", "MACD slow period", "Indicators");
		_signalPeriod = Param(nameof(SignalPeriod), 9)
			.SetGreaterThanZero()
			.SetDisplay("Signal Period", "MACD signal period", "Indicators");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevMacd = 0m;
		_prevSignal = 0m;
		_hasPrev = false;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_hasPrev = false;
		var macd = new MovingAverageConvergenceDivergenceSignal
		{
			Macd =
			{
				ShortMa = { Length = FastPeriod },
				LongMa = { Length = SlowPeriod },
			},
			SignalMa = { Length = SignalPeriod }
		};
		var subscription = SubscribeCandles(CandleType);
		subscription.BindEx(macd, ProcessCandle).Start();
	}

	private void ProcessCandle(ICandleMessage candle, IIndicatorValue macdValue)
	{
		if (candle.State != CandleStates.Finished) return;
		if (!macdValue.IsFinal) return;

		var macdTyped = (MovingAverageConvergenceDivergenceSignalValue)macdValue;
		if (macdTyped.Macd is not decimal macdLine || macdTyped.Signal is not decimal signal) return;

		var histogram = macdLine - signal;

		if (_hasPrev)
		{
			var prevHist = _prevMacd - _prevSignal;
			if (prevHist <= 0 && histogram > 0 && Position <= 0)
				BuyMarket();
			else if (prevHist >= 0 && histogram < 0 && Position >= 0)
				SellMarket();
		}

		_prevMacd = macdLine;
		_prevSignal = signal;
		_hasPrev = true;
	}
}