The TradePad Sample Strategy is a port of the MetaTrader "TradePad" example. The original expert adviser rendered a grid of
buttons that displayed the short-term trend for multiple symbols by coloring each cell with the current Stochastic oscillator
reading. This StockSharp implementation keeps the analytical core of the sample and focuses on monitoring a list of instruments
without replicating the on-chart user interface. The strategy subscribes to candle data for every configured symbol, calculates a
Stochastic oscillator, and classifies each instrument into Uptrend, Downtrend, or Flat states. Every time the class changes,
the strategy writes a log message similar to the color change performed by the original TradePad.
The strategy does not place orders. Its goal is to help discretionary traders keep track of several markets at once and spot
momentum changes that require manual actions (for example, switching charts or preparing trades).
How It Works
Symbol discovery – the SymbolList parameter accepts a comma-separated list of tickers. If no list is supplied, the
strategy falls back to the main Security assigned in the runner.
Candle subscription – each symbol uses the same timeframe configured through CandleType.
Indicator processing – a dedicated StochasticOscillator instance is bound to the candle stream. When the candle is
finished the indicator produces the %K value used for trend classification.
Trend classification – a reading above UpperLevel maps to Uptrend, a reading below LowerLevel maps to Downtrend,
everything in between is Flat. The last oscillator value is stored in LatestKValues.
Refresh interval – the strategy mimics the timer behaviour of the original TradePad. A change is logged at most once per
TimerPeriodSeconds for each symbol even if multiple candles arrive within the interval.
Parameters
Parameter
Description
SymbolList
Comma-separated list of instruments to monitor. Empty string means "use the main security".
TimerPeriodSeconds
Minimum number of seconds between state updates per symbol. Prevents log spam when candles are very short.
StochasticLength
Lookback period used to compute the raw %K line.
StochasticKPeriod
Smoothing period applied to the %K line.
StochasticDPeriod
Smoothing period applied to the %D line (kept for completeness although the strategy only reads %K).
UpperLevel
Threshold above which the symbol is considered to be in an uptrend.
LowerLevel
Threshold below which the symbol is considered to be in a downtrend.
CandleType
Timeframe of the candles used for indicator calculation.
Usage Notes
Ensure the specified tickers are available from the connector; missing symbols are reported in the log and skipped.
The TrendStates property exposes the latest classification for external dashboards or Designer blocks.
Use the strategy inside Designer or Runner to attach your own visuals (dashboards, charts) that react to the AddInfoLog
messages or the public dictionaries.
Because no orders are sent, the strategy is safe to run on live data providers purely for monitoring purposes.
Original MQL Behavior vs. StockSharp Version
MQL5 Feature
StockSharp Implementation
Graphical grid of buttons
Exposed as log entries and public dictionaries so that custom UI can be built in Designer.
Manual BUY/SELL buttons
Not implemented; the strategy intentionally stays passive.
Chart dragging logic
Not applicable in StockSharp and omitted.
Trend color updates
Replaced with trend state changes triggered every TimerPeriodSeconds per symbol.
Extending the Strategy
Connect the TrendStates dictionary to Designer widgets to rebuild the colored pad using XAML controls.
Add alerts or notifications when a symbol transitions from Flat to Uptrend or Downtrend.
Combine the classification with order logic if you want to automate entries after identifying strong momentum.
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()