Ver no GitHub

Starter Triple Stochastic Strategy

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

  1. The strategy subscribes to three candle streams:
    • Fast timeframe (default M5).
    • Normal timeframe (default M30).
    • Slow timeframe (default H2).
  2. 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.
  3. 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.
  4. 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.
  5. 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.
  6. 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;
	}
}