Ver en GitHub

Lego EA Strategy

The Lego EA Strategy is a direct port of the MetaTrader "Lego EA" expert advisor. It uses a configurable combination of technical filters—Commodity Channel Index, dual moving averages, stochastic oscillator, Accelerator Oscillator, DeMarker and Awesome Oscillator—to validate entries and exits. Each filter can be toggled on or off independently for entries and exits, allowing you to rebuild the original "Lego" block-by-block or experiment with custom setups.

Parameters

  • Volume – base trading volume used when the previous trade was profitable.
  • LotMultiplier – multiplier applied to the last executed volume after a losing trade (martingale-style recovery).
  • StopLossPips – protective stop expressed in pips (converted internally using the symbol’s tick size).
  • TakeProfitPips – profit target in pips.
  • UseCciForEntry / UseCciForExit – enable the CCI filter when opening or closing positions.
  • UseMaForEntry / UseMaForExit – use the fast/slow moving-average crossover for confirmations.
  • UseStochasticForEntry / UseStochasticForExit – require stochastic %K/%D alignment within configured thresholds.
  • UseAcceleratorForEntry / UseAcceleratorForExit – require Accelerator Oscillator acceleration patterns.
  • UseDemarkerForEntry / UseDemarkerForExit – apply DeMarker level checks.
  • UseAwesomeForEntry / UseAwesomeForExit – include Awesome Oscillator momentum confirmation.
  • CciPeriod – period of the Commodity Channel Index.
  • MaFastPeriod / MaSlowPeriod – lookback lengths for the fast and slow moving averages.
  • MaShift – number of completed bars to shift moving-average values back in time, reproducing the MT5 horizontal shift parameter.
  • MaMethod – smoothing method (simple, exponential, smoothed, or weighted).
  • MaPrice – candle price source supplied to both moving averages.
  • StochasticKPeriod, StochasticDPeriod, StochasticSlow – stochastic oscillator configuration.
  • StochasticLevelUp / StochasticLevelDown – overbought/oversold thresholds used for signals.
  • DemarkerPeriod, DemarkerLevelUp, DemarkerLevelDown – DeMarker oscillator settings.
  • CandleType – timeframe of the candle series used by all indicators.

Trading workflow

  1. On every completed candle the strategy collects indicator values from the selected filters.
  2. Each filter computes buy/sell readiness based on the previous fully formed bar (matching the original EA’s iGetArray(..., 1) offset).
  3. A long entry is permitted only when all enabled entry filters agree on a bullish signal. Likewise, a short entry requires unanimous bearish confirmation.
  4. If the account is flat and a valid entry signal appears, a market order is sent using either the base Volume or the last losing trade volume multiplied by LotMultiplier.
  5. When already in position, the enabled exit filters are evaluated the same way. The position is closed only when all exit filters agree on an opposing signal.
  6. Stop-loss and take-profit protection is automatically installed using StartProtection, converting pip inputs into absolute price distances based on the symbol’s tick size.

Money management

  • After a winning trade the next order reverts to the base Volume.
  • After a losing trade the volume is multiplied by LotMultiplier, emulating the original EA’s lot escalation logic.
  • Exchange-imposed volume bounds (step, min and max) are enforced before each order.

Notes and differences vs. MetaTrader version

  • Indicator price sources map to StockSharp equivalents. CCI uses the typical price internally and moving averages use the selected MaPrice source.
  • All indicator calculations rely on fully closed candles. This avoids partially formed data and mimics the EA’s "new bar" processing.
  • Freeze-level checks and manual SL/TP price placement are handled by StockSharp’s StartProtection service.
  • Partial position exits update the loss-tracking state only when the entire position is flat, matching the EA’s DEAL_ENTRY_OUT logic.

Usage tips

  • Start with the original configuration (MA filter enabled, other filters disabled) to reproduce baseline behaviour, then enable additional filters to tighten signal quality.
  • Monitor account exposure when using high LotMultiplier values; risk grows quickly during streaks of losses.
  • Combine the strategy with the Backtester to confirm whether your chosen filter mix aligns with the instruments you plan to trade.

This strategy currently has no Python version.

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>
/// Lego EA multi-indicator trend following strategy using SMA crossover.
/// Buys when fast SMA crosses above slow SMA, sells on reverse.
/// </summary>
public class LegoEaStrategy : Strategy
{
	private readonly StrategyParam<int> _fastPeriod;
	private readonly StrategyParam<int> _slowPeriod;
	private readonly StrategyParam<int> _stopLossPoints;
	private readonly StrategyParam<int> _takeProfitPoints;

	private SimpleMovingAverage _fast;
	private SimpleMovingAverage _slow;

	private decimal _prevFast;
	private decimal _prevSlow;
	private decimal _entryPrice;
	private int _cooldown;

	/// <summary>
	/// Fast SMA period.
	/// </summary>
	public int FastPeriod
	{
		get => _fastPeriod.Value;
		set => _fastPeriod.Value = value;
	}

	/// <summary>
	/// Slow SMA period.
	/// </summary>
	public int SlowPeriod
	{
		get => _slowPeriod.Value;
		set => _slowPeriod.Value = value;
	}

	/// <summary>
	/// Stop-loss distance in price steps.
	/// </summary>
	public int StopLossPoints
	{
		get => _stopLossPoints.Value;
		set => _stopLossPoints.Value = value;
	}

	/// <summary>
	/// Take-profit distance in price steps.
	/// </summary>
	public int TakeProfitPoints
	{
		get => _takeProfitPoints.Value;
		set => _takeProfitPoints.Value = value;
	}

	/// <summary>
	/// Initializes a new instance of the <see cref="LegoEaStrategy"/> class.
	/// </summary>
	public LegoEaStrategy()
	{
		_fastPeriod = Param(nameof(FastPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("Fast Period", "Fast SMA period", "Indicator");

		_slowPeriod = Param(nameof(SlowPeriod), 67)
			.SetGreaterThanZero()
			.SetDisplay("Slow Period", "Slow SMA 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");
	}

	/// <inheritdoc />
	public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
	{
		yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
	}

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

		_fast = null;
		_slow = null;
		_prevFast = 0;
		_prevSlow = 0;
		_entryPrice = 0;
		_cooldown = 0;
	}

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

		_fast = new SimpleMovingAverage { Length = FastPeriod };
		_slow = new SimpleMovingAverage { 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;

		// Check SL/TP
		if (Position > 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step)
			{
				SellMarket();
				_entryPrice = 0;
				_cooldown = 80;
				_prevFast = fastValue;
				_prevSlow = slowValue;
				return;
			}

			if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step)
			{
				SellMarket();
				_entryPrice = 0;
				_cooldown = 80;
				_prevFast = fastValue;
				_prevSlow = slowValue;
				return;
			}
		}
		else if (Position < 0 && _entryPrice > 0)
		{
			if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step)
			{
				BuyMarket();
				_entryPrice = 0;
				_cooldown = 80;
				_prevFast = fastValue;
				_prevSlow = slowValue;
				return;
			}

			if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step)
			{
				BuyMarket();
				_entryPrice = 0;
				_cooldown = 80;
				_prevFast = fastValue;
				_prevSlow = slowValue;
				return;
			}
		}

		// SMA crossover
		if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
		{
			if (Position < 0)
				BuyMarket();

			BuyMarket();
			_entryPrice = close;
			_cooldown = 80;
		}
		else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
		{
			if (Position > 0)
				SellMarket();

			SellMarket();
			_entryPrice = close;
			_cooldown = 80;
		}

		_prevFast = fastValue;
		_prevSlow = slowValue;
	}
}