Открыть на GitHub

Торговая панель (ID 3468)

Описание

TradingPanelStrategy — это помощник для ручного трейдинга, портированный из MQL5-советника EA_TradingPanel. Он предоставляет методы PlaceBuyOrders() и PlaceSellOrders(), которые повторяют действия кнопок BUY/SELL оригинальной панели: за один вызов отправляется серия рыночных заявок, автоматически рассчитываются стоп-лосс и тейк-профит в пипсах, а при необходимости выбирается альтернативный инструмент. Значения по умолчанию полностью совпадают с MQL-версией (1 сделка, стоп 2 пипса, тейк 10 пипсов, объем 0.01).

Вместо встроенного интерфейса MT5 стратегия ориентирована на интеграцию с пользовательскими окнами StockSharp: внешний код управляет панелью, а сама стратегия берет на себя нормализацию объемов, округление цен и постановку защитных ордеров.

Параметры

Имя Назначение Примечания
TradeCount Сколько рыночных заявок отправлять за одно действие. Минимум 0. Значение по умолчанию 1.
StopLossPips Дистанция стоп-лосса в пипсах. 0 отключает стоп. По умолчанию 2.
TakeProfitPips Дистанция тейк-профита в пипсах. 0 отключает тейк. По умолчанию 10.
VolumePerTrade Объем одной рыночной заявки. Нормализуется по VolumeStep. По умолчанию 0.01.
TargetSecurity Необязательный инструмент, отличный от Strategy.Security. Если не задан, используется основной инструмент стратегии.

Все параметры созданы через StrategyParam<T>, поэтому доступны для оптимизации и интерактивного изменения из интерфейса StockSharp.

Как работает стратегия

  1. Определяет активный инструмент (TargetSecurity либо Strategy.Security).
  2. Вычисляет размер пипса: при количестве знаков после запятой ≥ 3 умножает PriceStep на 10, как и в исходном советнике.
  3. Получает актуальную цену (лучший бид/аск либо последнюю сделку) и приводит ее к допустимому шагу через Security.ShrinkPrice.
  4. Рассчитывает желаемый объем TradeCount × VolumePerTrade, учитывает ограничения MinVolume/MaxVolume/VolumeStep и добавляет объем для закрытия противоположной позиции, чтобы одно действие могло развернуть позицию.
  5. Отправляет рыночную заявку BuyMarket или SellMarket.
  6. Создает защитные ордера (стоп и лимит) с учетом выбранных дистанций в пипсах.
  7. При остановке стратегии или смене направления отменяет устаревшие защитные заявки.

Логика защитных ордеров

  • Для лонга ставится SellStop (стоп) и SellLimit (тейк).
  • Для шорта ставится BuyStop (стоп) и BuyLimit (тейк).
  • Объем защитных заявок соответствует объемy, запрошенному панелью.
  • Очистка ссылок выполняется в OnStopped, OnReseted и при переходе на противоположное направление.

Рекомендации по использованию

  • До вызова методов панели назначьте Strategy.Security или заполните TargetSecurity, иначе сделки не будут отправлены.
  • Методы PlaceBuyOrders() и PlaceSellOrders() предназначены для вызова из внешнего интерфейса, который заменяет кнопки MT5.
  • Если нет данных лучшего бида/аска и последней сделки, стратегия запишет ошибку и пропустит отправку.
  • В OnStarted вызывается StartProtection(), чтобы защититься от «зависших» позиций после перезапуска.
  • При отсутствии PriceStep у инструмента используется значение 0.0001. Задайте шаг вручную, если ваш брокер использует другой размер пипса.

Отличия от MQL-версии

  • В стратегии отсутствует встроенный графический интерфейс; его следует реализовать отдельно либо управлять методами напрямую.
  • Стопы и тейки агрегируются на весь объем действия, а не на каждый отдельный тикет MT5, что упрощает реализацию при сохранении конечного результата.
  • Выполняется дополнительная проверка объемов и цен согласно требованиям биржи (VolumeStep, MinVolume, MaxVolume, Security.ShrinkPrice).
  • Для удобства контроля добавлены журнальные сообщения LogInfo и LogError.

Порядок запуска

  1. Создайте стратегию, назначьте портфель и инструмент.
  2. Запустите стратегию, чтобы активировать StartProtection().
  3. Подключите пользовательский интерфейс к методам PlaceBuyOrders() и PlaceSellOrders().
  4. Следите за сообщениями лога и при необходимости реализуйте дополнительные проверки на стороне UI.

Стратегия предлагает детальную и точную адаптацию панели MT5 к высокоуровневой инфраструктуре StockSharp, сохраняя удобство ручного ввода сделок.

namespace StockSharp.Samples.Strategies;

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

/// <summary>
/// Trading Panel strategy: WMA crossover.
/// Buys when fast WMA crosses above slow WMA, sells on cross below.
/// </summary>
public class TradingPanelStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;

	private decimal _prevFast;
	private decimal _prevSlow;
	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 TradingPanelStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
		_fastPeriod = Param(nameof(FastPeriod), 10)
			.SetGreaterThanZero()
			.SetDisplay("Fast WMA", "Fast WMA period", "Indicators");
		_slowPeriod = Param(nameof(SlowPeriod), 30)
			.SetGreaterThanZero()
			.SetDisplay("Slow WMA", "Slow WMA period", "Indicators");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevFast = 0;
		_prevSlow = 0;
		_hasPrev = false;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_prevFast = 0;
		_prevSlow = 0;
		_hasPrev = false;
		var fast = new ExponentialMovingAverage { Length = FastPeriod };
		var slow = new ExponentialMovingAverage { Length = SlowPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(fast, slow, ProcessCandle).Start();
	}

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

		if (_hasPrev)
		{
			if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
				BuyMarket();
			else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
				SellMarket();
		}
		else
		{
			if (fastValue > slowValue && Position <= 0)
				BuyMarket();
			else if (fastValue < slowValue && Position >= 0)
				SellMarket();
		}

		_prevFast = fastValue;
		_prevSlow = slowValue;
		_hasPrev = true;
	}
}