GitHub で見る

Starter V6 Mod E

Starter V6 Mod E is a high-level StockSharp conversion of the MetaTrader 4 expert advisor Starter_v6mod_e_www_forex-instruments_info.mq4. The port keeps the original combination of Laguerre oscillator extremes, dual EMA momentum confirmation, CCI filtering, and EMA-angle gating while adapting execution to StockSharp's event-driven architecture.

Trading logic

  • Trend gate: a 34-period EMA slope is measured between configurable start/end shifts. The slope is expressed in pip units; only positive slopes allow long trades, only negative slopes allow shorts, and flat readings block new entries.
  • Laguerre extremes: a handcrafted Laguerre RSI (gamma = 0.7 by default) tracks oversold/overbought states on the 0–1 scale. Longs require both current and previous values to stay below the Laguerre Oversold level, shorts require both values above Laguerre Overbought.
  • EMA momentum filter: 120- and 40-period EMAs (median price) must both rise for longs and both fall for shorts, mirroring the original MA filter.
  • CCI confirmation: a 14-period CCI must be below -CCI Threshold for longs and above +CCI Threshold for shorts, replicating the Alpha filter from MQL.
  • Friday safety: new trades are blocked after Friday Block Hour, and any remaining positions are liquidated once Friday Exit Hour is reached.

Risk management

  • Configurable stop-loss, take-profit, and trailing-stop distances (in pips) emulate the expert's money management block.
  • Trailing stops follow the best favorable price after entry and close the trade when retracement exceeds the configured distance.
  • Manual position closing is executed through SellMarket/BuyMarket, ensuring high-level API compliance.

Parameters

Parameter Description
Volume Order volume for each market entry.
StopLossPips Protective stop distance in pips.
TakeProfitPips Profit target in pips.
TrailingStopPips Trailing stop distance in pips (0 disables trailing).
SlowEmaPeriod Period of the slow EMA calculated on PRICE_MEDIAN.
FastEmaPeriod Period of the fast EMA calculated on PRICE_MEDIAN.
AngleEmaPeriod EMA period used for the angle detector.
AngleStartShift / AngleEndShift Bar shifts used to compute EMA slope.
AngleThreshold Minimum slope (in pip units) required to allow trading.
CciPeriod / CciThreshold Period and absolute threshold for the CCI filter.
LaguerreGamma Gamma parameter for the Laguerre oscillator.
LaguerreOversold / LaguerreOverbought Entry thresholds on the 0–1 Laguerre scale.
CandleType Candle data type (default 1-minute).
FridayBlockHour / FridayExitHour Hours (local instrument time) controlling Friday risk limits.

Conversion notes

  • The Laguerre oscillator is implemented directly from the original recursive formula, keeping the 0–1 output range and gamma smoothing.
  • EMA slope replaces the MQL angle helper by computing pip-normalized differences between historical EMA points.
  • Money management features such as equity cut-off and grid stacking are intentionally omitted because the MT4 variant being converted disabled them by default and StockSharp encourages explicit portfolio control.
  • Orders are sent through BuyMarket/SellMarket and rely on OnNewMyTrade to track fill prices for trailing logic.
namespace StockSharp.Samples.Strategies;

using System;
using System.Collections.Generic;

using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;

/// <summary>
/// Starter V6 Mod E strategy using dual EMA crossover with Laguerre RSI filter.
/// Buy when fast EMA crosses above slow EMA and Laguerre is oversold.
/// Sell when fast EMA crosses below slow EMA and Laguerre is overbought.
/// </summary>
public class StarterV6ModEStrategy : Strategy
{
	private readonly StrategyParam<int> _slowEmaPeriod;
	private readonly StrategyParam<int> _fastEmaPeriod;
	private readonly StrategyParam<decimal> _laguerreGamma;
	private readonly StrategyParam<decimal> _laguerreOversold;
	private readonly StrategyParam<decimal> _laguerreOverbought;
	private readonly StrategyParam<DataType> _candleType;

	private decimal _lagL0;
	private decimal _lagL1;
	private decimal _lagL2;
	private decimal _lagL3;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _prevLaguerre;
	private bool _hasPrev;

	public int SlowEmaPeriod
	{
		get => _slowEmaPeriod.Value;
		set => _slowEmaPeriod.Value = value;
	}

	public int FastEmaPeriod
	{
		get => _fastEmaPeriod.Value;
		set => _fastEmaPeriod.Value = value;
	}

	public decimal LaguerreGamma
	{
		get => _laguerreGamma.Value;
		set => _laguerreGamma.Value = value;
	}

	public decimal LaguerreOversold
	{
		get => _laguerreOversold.Value;
		set => _laguerreOversold.Value = value;
	}

	public decimal LaguerreOverbought
	{
		get => _laguerreOverbought.Value;
		set => _laguerreOverbought.Value = value;
	}

	public DataType CandleType
	{
		get => _candleType.Value;
		set => _candleType.Value = value;
	}

	public StarterV6ModEStrategy()
	{
		_slowEmaPeriod = Param(nameof(SlowEmaPeriod), 26)
			.SetDisplay("Slow EMA", "Slow EMA period", "Indicators");

		_fastEmaPeriod = Param(nameof(FastEmaPeriod), 12)
			.SetDisplay("Fast EMA", "Fast EMA period", "Indicators");

		_laguerreGamma = Param(nameof(LaguerreGamma), 0.7m)
			.SetDisplay("Laguerre Gamma", "Smoothing factor for Laguerre RSI", "Indicators");

		_laguerreOversold = Param(nameof(LaguerreOversold), 0.5m)
			.SetDisplay("Laguerre Oversold", "Oversold level (0-1)", "Indicators");

		_laguerreOverbought = Param(nameof(LaguerreOverbought), 0.5m)
			.SetDisplay("Laguerre Overbought", "Overbought level (0-1)", "Indicators");

		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		return [(Security, CandleType)];
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();

		_lagL0 = 0m;
		_lagL1 = 0m;
		_lagL2 = 0m;
		_lagL3 = 0m;
		_prevFast = 0m;
		_prevSlow = 0m;
		_prevLaguerre = 0m;
		_hasPrev = false;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_hasPrev = false;
		_lagL0 = _lagL1 = _lagL2 = _lagL3 = 0m;

		var fastEma = new ExponentialMovingAverage { Length = FastEmaPeriod };
		var slowEma = new ExponentialMovingAverage { Length = SlowEmaPeriod };

		var subscription = SubscribeCandles(CandleType);
		subscription
			.Bind(fastEma, slowEma, ProcessCandle)
			.Start();
	}

	private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
	{
		if (candle.State != CandleStates.Finished)
			return;

		var laguerre = CalculateLaguerre(candle.ClosePrice);

		if (!_hasPrev)
		{
			_prevFast = fast;
			_prevSlow = slow;
			_prevLaguerre = laguerre;
			_hasPrev = true;
			return;
		}

		// EMA crossover signals
		var bullishCross = _prevFast <= _prevSlow && fast > slow;
		var bearishCross = _prevFast >= _prevSlow && fast < slow;

		// Long: fast EMA crosses above slow + Laguerre was oversold
		if (Position <= 0 && bullishCross && _prevLaguerre <= LaguerreOversold)
		{
			if (Position < 0)
				BuyMarket();
			BuyMarket();
		}
		// Short: fast EMA crosses below slow + Laguerre was overbought
		else if (Position >= 0 && bearishCross && _prevLaguerre >= LaguerreOverbought)
		{
			if (Position > 0)
				SellMarket();
			SellMarket();
		}

		_prevFast = fast;
		_prevSlow = slow;
		_prevLaguerre = laguerre;
	}

	private decimal CalculateLaguerre(decimal price)
	{
		var gamma = LaguerreGamma;

		var l0Prev = _lagL0;
		var l1Prev = _lagL1;
		var l2Prev = _lagL2;
		var l3Prev = _lagL3;

		_lagL0 = (1m - gamma) * price + gamma * l0Prev;
		_lagL1 = -gamma * _lagL0 + l0Prev + gamma * l1Prev;
		_lagL2 = -gamma * _lagL1 + l1Prev + gamma * l2Prev;
		_lagL3 = -gamma * _lagL2 + l2Prev + gamma * l3Prev;

		decimal cu = 0m;
		decimal cd = 0m;

		if (_lagL0 >= _lagL1) cu = _lagL0 - _lagL1; else cd = _lagL1 - _lagL0;
		if (_lagL1 >= _lagL2) cu += _lagL1 - _lagL2; else cd += _lagL2 - _lagL1;
		if (_lagL2 >= _lagL3) cu += _lagL2 - _lagL3; else cd += _lagL3 - _lagL2;

		var denom = cu + cd;
		return denom == 0m ? 0m : cu / denom;
	}
}