This strategy ports the MetaTrader expert Starter.mq5 to StockSharp's high level API. It synchronises three stochastic
oscillators (fast, normal, slow) with matching moving averages calculated on different timeframes. A trade is allowed only
when all filters confirm the same direction and price trades on the correct side of every shifted moving average.
Trading logic
The strategy subscribes to three candle streams:
Fast timeframe (default M5).
Normal timeframe (default M30).
Slow timeframe (default H2).
For each stream it builds a moving average (configurable method, length, and applied price) and a stochastic oscillator
with the same %K, %D, and slowing parameters.
The slow timeframe drives execution. When a slow candle closes, the latest values from all timeframes are compared:
Long setup: every stochastic line has %K > %D, all %K values are below 50, and price is below every shifted
moving average.
Short setup: every stochastic line has %K < %D, all %K values are above 50, and price is above every shifted
moving average.
Signals can optionally be inverted through ReverseSignals. When an entry is taken the strategy either flips the
existing exposure (if CloseOppositePositions = true) or ignores the signal until the opposite position is closed.
After a fill, stop-loss and take-profit levels are simulated in price space. A trailing stop replicates the original MQL
logic by requiring TrailingStopPips + TrailingStepPips of profit before moving the stop by TrailingStopPips.
Risk-based position sizing mirrors MetaTrader's lot/risk switch. When the mode is RiskPercent, the trade volume is
derived from the account value, risk percentage, and the stop-loss distance in pips.
Parameters
Name
Default
Description
StopLossPips
45
Protective stop distance in pips. Set to 0 to disable the fixed stop.
TakeProfitPips
105
Take-profit distance in pips. Set to 0 to disable the target.
TrailingStopPips
5
Trailing stop offset applied after the minimum advance.
TrailingStepPips
5
Minimum profit advance (in pips) required before the trailing stop moves.
MoneyMode
RiskPercent
Selects between fixed lot sizing and percentage risk per trade.
MoneyValue
3
Lot size when using FixedLot, or percentage risk when using RiskPercent.
FastCandleType
M5
Candle type for the fast indicator set.
NormalCandleType
M30
Candle type for the intermediate indicator set.
SlowCandleType
H2
Candle type that triggers signal evaluation and orders.
MaPeriod
20
Length of all moving averages.
MaShift
1
Horizontal shift applied to every moving average (bars back).
MaMethod
Simple
Moving average smoothing: Simple, Exponential, Smoothed, or Weighted.
MaPriceType
Close
Applied price used to feed the moving averages.
StochasticKPeriod
5
%K length for all stochastic oscillators.
StochasticDPeriod
3
%D smoothing length.
StochasticSlowing
3
Final slowing factor for %K.
ReverseSignals
false
Swaps the long and short conditions.
CloseOppositePositions
false
If true, reverses the position in a single order when a signal appears in the opposite direction.
Money management
MoneyMode = FixedLot sends every order with the exact MoneyValue volume.
MoneyMode = RiskPercent reproduces the original behaviour: the risked cash equals AccountValue * MoneyValue / 100. The
trade size is calculated as risked cash / (StopLossPips * pip size). If StopLossPips is zero or the portfolio value is
not available, the strategy refuses to trade.
Protection and trailing
Stop-loss and take-profit levels are tracked internally and compared against candle highs/lows, emulating MetaTrader's
protective orders.
The trailing stop activates only after the unrealised profit exceeds TrailingStopPips + TrailingStepPips pips, matching
the original requirement that both an initial offset and a minimum step must be satisfied before the stop is moved.
Multi-timeframe alignment
All indicators are recalculated on every closed candle of their respective timeframe. The slow timeframe waits for all three
moving averages and stochastics to form and uses the most recent shifted moving average values, mimicking the MetaTrader
iMA shift parameter. This ensures that the StockSharp port triggers trades on the same bar as the original MQL expert.
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class StarterTripleStochasticStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private ExponentialMovingAverage _fast;
private ExponentialMovingAverage _slow;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
private int _cooldown;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }
public StarterTripleStochasticStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
protected override void OnReseted()
{
base.OnReseted();
_fast = null; _slow = null;
_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fast = new ExponentialMovingAverage { Length = FastPeriod };
_slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
subscription.Bind(_fast, _slow, ProcessCandle);
subscription.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished) return;
if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }
var close = candle.ClosePrice;
var step = Security?.PriceStep ?? 1m;
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }
_prevFast = fastValue; _prevSlow = slowValue;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class starter_triple_stochastic_strategy(Strategy):
def __init__(self):
super(starter_triple_stochastic_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 14) \
.SetDisplay("Fast Period", "Fast MA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 50) \
.SetDisplay("Slow Period", "Slow MA period", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200) \
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 400) \
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def stop_loss_points(self):
return self._stop_loss_points.Value
@property
def take_profit_points(self):
return self._take_profit_points.Value
def OnReseted(self):
super(starter_triple_stochastic_strategy, self).OnReseted()
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(starter_triple_stochastic_strategy, self).OnStarted2(time)
self._fast = ExponentialMovingAverage()
self._fast.Length = self.fast_period
self._slow = ExponentialMovingAverage()
self._slow.Length = self.slow_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(5)))
subscription.Bind(self._fast, self._slow, self._process_candle)
subscription.Start()
def _process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_value)
slow_val = float(slow_value)
if not self._fast.IsFormed or not self._slow.IsFormed:
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
if self.Position > 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close <= self._entry_price - self.stop_loss_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close >= self._entry_price + self.take_profit_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close >= self._entry_price + self.stop_loss_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close <= self._entry_price - self.take_profit_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 100
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return starter_triple_stochastic_strategy()