Get trend Stochastic Strategy
The strategy is a StockSharp high-level port of the MetaTrader 4 expert advisor Get trend.mq4. It evaluates the M15 chart for
entries, validates the broader trend on H1, and relies on two smoothed moving averages together with a pair of stochastic
oscillators to detect mean-reversion breakouts near the longer-term trend. The implementation keeps the original money management
rules based on fixed take-profit, stop-loss, and trailing-stop distances expressed in price points.
Trading logic
- Indicators and data
- M15 candles feed a smoothed moving average (SMMA, median price) with period
M15MaPeriod and two stochastic oscillators.
- H1 candles feed another SMMA (median price) with period
H1MaPeriod.
- The fast stochastic (
FastStochasticPeriod, 3, 3) supplies the %K line and its previous value. The slow stochastic (SlowStochasticPeriod, 3, 3) supplies the %D signal line.
- Long setup
- The current M15 close is below its SMMA and the H1 close is below its own SMMA.
- The distance between the M15 SMMA and the close is within
ThresholdPoints price steps.
- Both stochastic lines are below 20. The fast line crosses above the slow line during the last candle (
fast > slow while the previous fast value was below slow).
- If a short position exists, the strategy first buys enough volume to flatten it and then opens a new long with
TradeVolume.
- Short setup mirrors the long logic:
- Both closes lie above their SMMAs, the distance is within
ThresholdPoints, the stochastic values are above 80, and the fast
line crosses below the slow line. The strategy sells, closing an existing long if necessary.
- Risk management
- After every entry, protective orders are placed at
StopLossPoints and TakeProfitPoints (converted into absolute price
distances using the instrument's price step).
- A trailing stop re-aligns the stop-loss order once the trade gains at least
TrailingStopPoints points. The new stop is
positioned at the current close minus/plus the trailing distance for longs/shorts respectively.
- When the position returns to flat, all protective orders are cancelled.
Differences versus the original EA
- MetaTrader's SMMA uses an indicator shift of eight bars; StockSharp indicators do not expose a direct shift setting. The port
evaluates the most recent finished value instead. This keeps the crossover timing while avoiding additional custom buffers.
- The original EA used MQL's bid/ask quotes for trailing. The port uses the finished candle close that triggered the trailing
update, which is the closest analogue available in the high-level API.
- Money management relies on StockSharp's order registration helpers (
BuyMarket, SellMarket, SellStop, etc.) instead of
OrderSend and OrderModify.
Parameters
| Group |
Name |
Description |
Default |
| Data |
M15 Candle Type |
Candle type/timeframe used for the main calculations. |
M15 time frame |
| Data |
H1 Candle Type |
Candle type/timeframe used for confirmation. |
H1 time frame |
| Indicators |
M15 SMMA Period |
Length of the smoothed moving average on the M15 series. |
200 |
| Indicators |
H1 SMMA Period |
Length of the smoothed moving average on the H1 series. |
200 |
| Indicators |
Slow Stochastic Period |
%K length for the slow stochastic oscillator that provides the %D line. |
14 |
| Indicators |
Fast Stochastic Period |
%K length for the fast stochastic oscillator that provides the main %K line. |
14 |
| Signals |
Threshold (points) |
Maximum distance between the M15 SMMA and the current close to allow entries. |
50 |
| Risk |
Take Profit (points) |
Take-profit distance expressed in price steps. |
570 |
| Risk |
Stop Loss (points) |
Stop-loss distance expressed in price steps. |
30 |
| Risk |
Trailing Stop (points) |
Trailing-stop distance expressed in price steps. |
200 |
| Trading |
Trade Volume |
Volume sent with each market order. |
0.1 |
Notes for usage
- Ensure the traded security exposes
PriceStep; otherwise the point-based distances fall back to 1, which can lead to large
protective orders on instruments quoted in fractional units.
- The strategy cancels and recreates stop orders as soon as a better trailing level is detected. Brokers that disallow frequent
modifications might require throttling.
- Because the port operates on finished candles only, the system is designed for backtests and end-of-bar execution. Running it on
live tick data requires matching the candle building settings between the terminal and StockSharp.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class GetTrendStochasticStrategy : Strategy
{
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<decimal> _oversold;
private readonly StrategyParam<decimal> _overbought;
private readonly StrategyParam<int> _cooldownCandles;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevRsi;
private bool _hasPrev;
private int _cooldownRemaining;
public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
public decimal Oversold { get => _oversold.Value; set => _oversold.Value = value; }
public decimal Overbought { get => _overbought.Value; set => _overbought.Value = value; }
public int CooldownCandles { get => _cooldownCandles.Value; set => _cooldownCandles.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public GetTrendStochasticStrategy()
{
_rsiPeriod = Param(nameof(RsiPeriod), 14).SetDisplay("RSI Period", "RSI lookback", "Indicators");
_emaPeriod = Param(nameof(EmaPeriod), 20).SetDisplay("EMA Period", "EMA filter", "Indicators");
_oversold = Param(nameof(Oversold), 35m).SetDisplay("Oversold", "RSI oversold level", "Levels");
_overbought = Param(nameof(Overbought), 65m).SetDisplay("Overbought", "RSI overbought level", "Levels");
_cooldownCandles = Param(nameof(CooldownCandles), 30).SetDisplay("Cooldown", "Candles between signals", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame()).SetDisplay("Candle Type", "Candle timeframe", "General");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevRsi = default;
_hasPrev = default;
_cooldownRemaining = default;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevRsi = 0;
_hasPrev = false;
_cooldownRemaining = 0;
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(rsi, ema, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal rsi, decimal ema)
{
if (candle.State != CandleStates.Finished) return;
var close = candle.ClosePrice;
if (!_hasPrev) { _prevRsi = rsi; _hasPrev = true; return; }
if (_cooldownRemaining > 0)
{
_cooldownRemaining--;
_prevRsi = rsi;
return;
}
if (_prevRsi <= Oversold && rsi > Oversold && close > ema && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
_cooldownRemaining = CooldownCandles;
}
else if (_prevRsi >= Overbought && rsi < Overbought && close < ema && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
_cooldownRemaining = CooldownCandles;
}
_prevRsi = rsi;
}
}
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 RelativeStrengthIndex, ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class get_trend_stochastic_strategy(Strategy):
def __init__(self):
super(get_trend_stochastic_strategy, self).__init__()
self._rsi_period = self.Param("RsiPeriod", 14).SetDisplay("RSI Period", "RSI lookback", "Indicators")
self._ema_period = self.Param("EmaPeriod", 20).SetDisplay("EMA Period", "EMA filter", "Indicators")
self._oversold = self.Param("Oversold", 35.0).SetDisplay("Oversold", "RSI oversold level", "Levels")
self._overbought = self.Param("Overbought", 65.0).SetDisplay("Overbought", "RSI overbought level", "Levels")
self._cooldown_candles = self.Param("CooldownCandles", 30).SetDisplay("Cooldown", "Candles between signals", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_rsi = 0.0
self._has_prev = False
self._cooldown_remaining = 0
@property
def rsi_period(self): return self._rsi_period.Value
@property
def ema_period(self): return self._ema_period.Value
@property
def oversold(self): return self._oversold.Value
@property
def overbought(self): return self._overbought.Value
@property
def cooldown_candles(self): return self._cooldown_candles.Value
@property
def candle_type(self): return self._candle_type.Value
def OnReseted(self):
super(get_trend_stochastic_strategy, self).OnReseted()
self._prev_rsi = 0.0
self._has_prev = False
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(get_trend_stochastic_strategy, self).OnStarted2(time)
self._prev_rsi = 0.0
self._has_prev = False
self._cooldown_remaining = 0
rsi = RelativeStrengthIndex()
rsi.Length = self.rsi_period
ema = ExponentialMovingAverage()
ema.Length = self.ema_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(rsi, ema, self.process_candle).Start()
def process_candle(self, candle, rsi, ema):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
rsi_val = float(rsi)
ema_val = float(ema)
if not self._has_prev:
self._prev_rsi = rsi_val
self._has_prev = True
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
self._prev_rsi = rsi_val
return
oversold = float(self.oversold)
overbought = float(self.overbought)
if self._prev_rsi <= oversold and rsi_val > oversold and close > ema_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._cooldown_remaining = self.cooldown_candles
elif self._prev_rsi >= overbought and rsi_val < overbought and close < ema_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._cooldown_remaining = self.cooldown_candles
self._prev_rsi = rsi_val
def CreateClone(self):
return get_trend_stochastic_strategy()