TradePad Sample Strategy — порт примера MetaTrader «TradePad». В исходном советнике на графике строилась таблица кнопок,
цвет которых отражал текущее состояние случайного осциллятора (Stochastic). В версии для StockSharp сохранена аналитическая
часть — мониторинг нескольких инструментов и классификация тренда — но не воспроизводится визуальная панель MT5. Стратегия
подписывается на свечи для всех выбранных инструментов, рассчитывает Stochastic и присваивает каждому статус Uptrend,
Downtrend или Flat. При изменении статуса в журнал отправляется сообщение, что соответствует смене цвета в оригинале.
Стратегия не торгует автоматически и предназначена как вспомогательная панель для трейдеров, которые принимают решения вручную.
Как работает
Выбор инструментов. Параметр SymbolList принимает список тикеров, разделённых запятыми. Если он пустой, используется
основной инструмент стратегии.
Подписка на свечи. Для всех инструментов применяется один таймфрейм из CandleType.
Расчёт индикатора. Для каждого инструмента создаётся собственный StochasticOscillator, который выдаёт значение %K
после закрытия свечи.
Определение тренда. Значение выше UpperLevel соответствует Uptrend, ниже LowerLevel — Downtrend, промежуточные
значения дают статус Flat. Последнее значение сохраняется в LatestKValues.
Интервал обновления. Параметр 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;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import StochasticOscillator
from StockSharp.Algo.Strategies import Strategy
class trade_pad_sample_strategy(Strategy):
"""Stochastic crossover strategy. Buys when %K crosses up from oversold, sells when it crosses down from overbought."""
def __init__(self):
super(trade_pad_sample_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._stochastic_k_period = self.Param("StochasticKPeriod", 10) \
.SetDisplay("Stochastic %K", "%K period", "Indicators")
self._stochastic_d_period = self.Param("StochasticDPeriod", 3) \
.SetDisplay("Stochastic %D", "%D period", "Indicators")
self._upper_level = self.Param("UpperLevel", 75.0) \
.SetDisplay("Upper Threshold", "Overbought level", "Signals")
self._lower_level = self.Param("LowerLevel", 25.0) \
.SetDisplay("Lower Threshold", "Oversold level", "Signals")
self._prev_k = 0.0
self._has_prev = False
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def StochasticKPeriod(self):
return self._stochastic_k_period.Value
@property
def StochasticDPeriod(self):
return self._stochastic_d_period.Value
@property
def UpperLevel(self):
return self._upper_level.Value
@property
def LowerLevel(self):
return self._lower_level.Value
def OnReseted(self):
super(trade_pad_sample_strategy, self).OnReseted()
self._prev_k = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(trade_pad_sample_strategy, self).OnStarted2(time)
self._has_prev = False
stochastic = StochasticOscillator()
stochastic.K.Length = self.StochasticKPeriod
stochastic.D.Length = self.StochasticDPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.BindEx(stochastic, self._process_candle).Start()
def _process_candle(self, candle, stoch_val):
if candle.State != CandleStates.Finished:
return
if not stoch_val.IsFinal or not stoch_val.IsFormed:
return
k_raw = stoch_val.K
if k_raw is None:
return
k_value = float(k_raw)
if not self._has_prev:
self._prev_k = k_value
self._has_prev = True
return
lower = float(self.LowerLevel)
upper = float(self.UpperLevel)
cross_up = self._prev_k <= lower and k_value > lower
cross_down = self._prev_k >= upper and k_value < upper
if cross_up and self.Position <= 0:
self.BuyMarket()
elif cross_down and self.Position >= 0:
self.SellMarket()
self._prev_k = k_value
def CreateClone(self):
return trade_pad_sample_strategy()