GitHub で見る

JS Signal Baes Strategy

Overview

This strategy is a StockSharp port of the MetaTrader expert advisor "JS Signal Baes". It evaluates six different timeframes simultaneously (M1, M5, M15, M30, H1, H4 by default) and waits until all monitored indicators agree on the same market direction before opening a position. Signals can be inverted through the Reverse parameter for users who want to trade counter to the detected trend.

Indicators and Confirmations

The following indicators are calculated on each of the six timeframes:

  • Two Moving Averages using the selected smoothing method (simple, exponential, smoothed or linear weighted).
  • MACD (Moving Average Convergence Divergence) using configurable fast, slow and signal lengths.
  • RSI (Relative Strength Index) with a dedicated period parameter.
  • CCI (Commodity Channel Index) with its own lookback length.
  • Stochastic Oscillator defined by K, D and smoothing periods.

A timeframe is considered bullish when:

  1. Fast MA > Slow MA.
  2. MACD main line > MACD signal line.
  3. RSI > 50.
  4. CCI > 0.
  5. Stochastic %K > 40.

A timeframe is considered bearish when:

  1. Fast MA < Slow MA.
  2. MACD main line < MACD signal line.
  3. RSI < 50.
  4. CCI < 0.
  5. Stochastic %K < 60.

Trading Rules

A new netted position is opened only when the primary timeframe (default M1) closes and all six timeframes are simultaneously bullish or bearish:

  • Long entry: every timeframe is bullish. If Reverse is enabled the signal becomes a short entry instead.
  • Short entry: every timeframe is bearish. If Reverse is enabled the signal becomes a long entry instead.

Positions are not pyramided. The strategy waits until the existing position is closed externally before acting on a new signal. There are no automatic exits beyond the opposite signal logic from the original expert advisor.

Parameters

Parameter Default Description
CciPeriod 13 Lookback length for the Commodity Channel Index.
FastMaPeriod 5 Length of the fast moving average.
SlowMaPeriod 9 Length of the slow moving average.
MaMethod LinearWeighted Moving average smoothing type applied to both averages.
MacdFastPeriod 8 Fast EMA length used by MACD.
MacdSlowPeriod 17 Slow EMA length used by MACD.
MacdSignalPeriod 9 Signal line length used by MACD.
StochasticKPeriod 5 K period for the stochastic oscillator.
StochasticDPeriod 3 D period for the stochastic oscillator.
StochasticSmoothing 3 Smoothing factor for the stochastic oscillator.
RsiPeriod 9 RSI lookback length.
ReverseSignals false Invert the direction of every trading signal.
TimeFrame1..6 M1, M5, M15, M30, H1, H4 Candle series assigned to each timeframe.

Notes

  • The default parameters replicate the configuration embedded in the MetaTrader version.
  • Money management, stop-loss, take-profit and trailing logic from the original code are not reproduced; use portfolio-level risk controls if required.
  • Ensure that historical data are available for every selected timeframe so the indicators can warm up before trading.
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 JsSignalBaesStrategy : 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 JsSignalBaesStrategy()
	{
		_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;
	}
}