Открыть на GitHub

Стратегия TradePad Sample

Обзор

TradePad Sample Strategy — порт примера MetaTrader «TradePad». В исходном советнике на графике строилась таблица кнопок, цвет которых отражал текущее состояние случайного осциллятора (Stochastic). В версии для StockSharp сохранена аналитическая часть — мониторинг нескольких инструментов и классификация тренда — но не воспроизводится визуальная панель MT5. Стратегия подписывается на свечи для всех выбранных инструментов, рассчитывает Stochastic и присваивает каждому статус Uptrend, Downtrend или Flat. При изменении статуса в журнал отправляется сообщение, что соответствует смене цвета в оригинале.

Стратегия не торгует автоматически и предназначена как вспомогательная панель для трейдеров, которые принимают решения вручную.

Как работает

  1. Выбор инструментов. Параметр SymbolList принимает список тикеров, разделённых запятыми. Если он пустой, используется основной инструмент стратегии.
  2. Подписка на свечи. Для всех инструментов применяется один таймфрейм из CandleType.
  3. Расчёт индикатора. Для каждого инструмента создаётся собственный StochasticOscillator, который выдаёт значение %K после закрытия свечи.
  4. Определение тренда. Значение выше UpperLevel соответствует Uptrend, ниже LowerLevelDowntrend, промежуточные значения дают статус Flat. Последнее значение сохраняется в LatestKValues.
  5. Интервал обновления. Параметр TimerPeriodSeconds имитирует таймер из оригинального TradePad — для каждого инструмента статус обновляется не чаще указанного интервала, чтобы избежать лишних сообщений.

Параметры

Параметр Описание
SymbolList Список тикеров через запятую. Пустое значение — использовать основной инструмент.
TimerPeriodSeconds Минимальное время между повторными обновлениями статуса для одного инструмента.
StochasticLength Длина расчёта исходной линии %K.
StochasticKPeriod Период сглаживания %K.
StochasticDPeriod Период сглаживания %D (оставлен для совместимости, хотя стратегия использует только %K).
UpperLevel Порог, при превышении которого фиксируется восходящий тренд.
LowerLevel Порог, при падении ниже которого фиксируется нисходящий тренд.
CandleType Таймфрейм свечей для расчёта индикатора.

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

  • Убедитесь, что все указанные тикеры доступны в подключённом источнике данных. Отсутствующие инструменты выводятся в журнал и игнорируются.
  • Свойство TrendStates публикует текущую классификацию и может использоваться во внешних панелях Designer.
  • В Designer или Runner можно построить собственный интерфейс, реагирующий на сообщения AddInfoLog или данные из словарей.
  • Отсутствие торговых операций делает стратегию безопасной для запуска на реальном подключении в режиме мониторинга.

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

Функция MQL5 Реализация в StockSharp
Таблица кнопок Представлена через журнальные сообщения и публичные словари.
Кнопки BUY/SELL Не реализованы, стратегия остаётся наблюдателем.
Перетаскивание панели Не требуется в StockSharp и опущено.
Обновление цветов Заменено обновлением статуса с периодичностью TimerPeriodSeconds.

Идеи для расширения

  • Подключите словарь TrendStates к визуальным элементам Designer, чтобы восстановить цветовую матрицу.
  • Настройте уведомления при переходе инструмента из Flat в Uptrend или Downtrend.
  • При необходимости можно добавить торговую логику поверх текущих сигналов и реализовать полуавтоматическую панель.
namespace StockSharp.Samples.Strategies;

using System;

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

/// <summary>
/// TradePad Sample strategy.
/// Classifies market state using Stochastic oscillator and trades on state transitions.
/// Buys when stochastic crosses up from oversold, sells when it crosses down from overbought.
/// </summary>
public class TradePadSampleStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _stochasticKPeriod;
	private readonly StrategyParam<int> _stochasticDPeriod;
	private readonly StrategyParam<decimal> _upperLevel;
	private readonly StrategyParam<decimal> _lowerLevel;

	private decimal _prevK;
	private bool _hasPrev;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int StochasticKPeriod { get => _stochasticKPeriod.Value; set => _stochasticKPeriod.Value = value; }
	public int StochasticDPeriod { get => _stochasticDPeriod.Value; set => _stochasticDPeriod.Value = value; }
	public decimal UpperLevel { get => _upperLevel.Value; set => _upperLevel.Value = value; }
	public decimal LowerLevel { get => _lowerLevel.Value; set => _lowerLevel.Value = value; }

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

		_stochasticKPeriod = Param(nameof(StochasticKPeriod), 10)
			.SetDisplay("Stochastic %K", "%K period", "Indicators");

		_stochasticDPeriod = Param(nameof(StochasticDPeriod), 3)
			.SetDisplay("Stochastic %D", "%D period", "Indicators");

		_upperLevel = Param(nameof(UpperLevel), 75m)
			.SetDisplay("Upper Threshold", "Overbought level", "Signals");

		_lowerLevel = Param(nameof(LowerLevel), 25m)
			.SetDisplay("Lower Threshold", "Oversold level", "Signals");
	}

	/// <inheritdoc />
	public override System.Collections.Generic.IEnumerable<(StockSharp.BusinessEntities.Security sec, DataType dt)> GetWorkingSecurities()
		=> [(Security, CandleType)];

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

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_hasPrev = false;

		var stochastic = new StochasticOscillator();
		stochastic.K.Length = StochasticKPeriod;
		stochastic.D.Length = StochasticDPeriod;

		var subscription = SubscribeCandles(CandleType);

		subscription
			.BindEx(stochastic, ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawOwnTrades(area);
		}
	}

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

		if (!stochVal.IsFinal || !stochVal.IsFormed)
			return;

		var stoch = (StochasticOscillatorValue)stochVal;
		if (stoch.K is not decimal kValue)
			return;

		if (!_hasPrev)
		{
			_prevK = kValue;
			_hasPrev = true;
			return;
		}

		// Buy: stochastic crosses up through lower level (leaving oversold)
		var crossUp = _prevK <= LowerLevel && kValue > LowerLevel;
		// Sell: stochastic crosses down through upper level (leaving overbought)
		var crossDown = _prevK >= UpperLevel && kValue < UpperLevel;

		if (crossUp && Position <= 0)
		{
			BuyMarket();
		}
		else if (crossDown && Position >= 0)
		{
			SellMarket();
		}

		_prevK = kValue;
	}
}