This strategy is a modular system translated from the MetaTrader script "exp_Lego_4_Beta". It combines several common technical indicators and allows enabling or disabling each component through parameters.
Algorithm
Moving Average Cross – A fast and a slow moving average are calculated. A long position opens when the fast average crosses above the slow average. A short position opens on the opposite cross.
Stochastic Oscillator Filter – When enabled, long entries require the Stochastic %K value to be below the oversold level, and short entries require %K to be above the overbought level.
RSI Exit – When enabled, existing long positions are closed if RSI rises above the high threshold. Short positions are closed when RSI drops below the low threshold.
Parameters
UseMaOpen – activate moving average cross signals.
FastMaLength / SlowMaLength – lengths of the fast and slow moving averages.
MaType – type of moving average (SMA, EMA, WMA).
UseStochasticOpen – enable Stochastic filter for entries.
StochLength – main period for Stochastic calculation.
StochKPeriod / StochDPeriod – smoothing periods for %K and %D lines.
StochBuyLevel / StochSellLevel – oversold and overbought thresholds.
UseRsiClose – enable RSI-based exits.
RsiPeriod – RSI calculation length.
RsiHigh / RsiLow – RSI thresholds for closing positions.
CandleType – candle type to subscribe.
Notes
The strategy uses high level SubscribeCandles with BindEx to process indicator values and follows the StockSharp recommended style. Only market orders are used for entry and exit.
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;
/// <summary>
/// Modular strategy combining EMA crossover with RSI filter.
/// Enters on EMA cross with RSI confirmation, exits on RSI extremes or opposite cross.
/// </summary>
public class Lego4BetaStrategy : Strategy
{
private readonly StrategyParam<int> _fastMaLength;
private readonly StrategyParam<int> _slowMaLength;
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevFast;
private decimal _prevSlow;
private bool _hasPrev;
public int FastMaLength { get => _fastMaLength.Value; set => _fastMaLength.Value = value; }
public int SlowMaLength { get => _slowMaLength.Value; set => _slowMaLength.Value = value; }
public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public Lego4BetaStrategy()
{
_fastMaLength = Param(nameof(FastMaLength), 5)
.SetGreaterThanZero()
.SetDisplay("Fast EMA", "Fast EMA length", "Indicators");
_slowMaLength = Param(nameof(SlowMaLength), 20)
.SetGreaterThanZero()
.SetDisplay("Slow EMA", "Slow EMA length", "Indicators");
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Period", "RSI period", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
protected override void OnReseted()
{
base.OnReseted();
_prevFast = 0; _prevSlow = 0; _hasPrev = false;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var fast = new ExponentialMovingAverage { Length = FastMaLength };
var slow = new ExponentialMovingAverage { Length = SlowMaLength };
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
SubscribeCandles(CandleType).Bind(fast, slow, rsi, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow, decimal rsi)
{
if (candle.State != CandleStates.Finished) return;
if (!_hasPrev) { _prevFast = fast; _prevSlow = slow; _hasPrev = true; return; }
// EMA cross up + RSI not overbought => long
if (_prevFast <= _prevSlow && fast > slow && rsi < 70)
{
if (Position < 0) BuyMarket();
if (Position <= 0) BuyMarket();
}
// EMA cross down + RSI not oversold => short
else if (_prevFast >= _prevSlow && fast < slow && rsi > 30)
{
if (Position > 0) SellMarket();
if (Position >= 0) SellMarket();
}
// RSI exit: overbought close long
else if (Position > 0 && rsi > 75)
{
SellMarket();
}
// RSI exit: oversold close short
else if (Position < 0 && rsi < 25)
{
BuyMarket();
}
_prevFast = fast; _prevSlow = slow;
}
}
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, RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class lego4_beta_strategy(Strategy):
def __init__(self):
super(lego4_beta_strategy, self).__init__()
self._fast_ma_length = self.Param("FastMaLength", 5) \
.SetDisplay("Fast EMA", "Fast EMA length", "Indicators")
self._slow_ma_length = self.Param("SlowMaLength", 20) \
.SetDisplay("Slow EMA", "Slow EMA length", "Indicators")
self._rsi_period = self.Param("RsiPeriod", 14) \
.SetDisplay("RSI Period", "RSI period", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
@property
def fast_ma_length(self):
return self._fast_ma_length.Value
@property
def slow_ma_length(self):
return self._slow_ma_length.Value
@property
def rsi_period(self):
return self._rsi_period.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(lego4_beta_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(lego4_beta_strategy, self).OnStarted2(time)
fast = ExponentialMovingAverage()
fast.Length = self.fast_ma_length
slow = ExponentialMovingAverage()
slow.Length = self.slow_ma_length
rsi = RelativeStrengthIndex()
rsi.Length = self.rsi_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(fast, slow, rsi, self.on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def on_process(self, candle, fast, slow, rsi):
if candle.State != CandleStates.Finished:
return
if not self._has_prev:
self._prev_fast = fast
self._prev_slow = slow
self._has_prev = True
return
# EMA cross up + RSI not overbought => long
if self._prev_fast <= self._prev_slow and fast > slow and rsi < 70:
if self.Position < 0:
self.BuyMarket()
if self.Position <= 0:
self.BuyMarket()
# EMA cross down + RSI not oversold => short
elif self._prev_fast >= self._prev_slow and fast < slow and rsi > 30:
if self.Position > 0:
self.SellMarket()
if self.Position >= 0:
self.SellMarket()
# RSI exit: overbought close long
elif self.Position > 0 and rsi > 75:
self.SellMarket()
# RSI exit: oversold close short
elif self.Position < 0 and rsi < 25:
self.BuyMarket()
self._prev_fast = fast
self._prev_slow = slow
def CreateClone(self):
return lego4_beta_strategy()