View on GitHub

MaRobot Strategy

Summary

  • Implements a bar-based moving average crossover system that operates on a configurable intraday timeframe while using daily ADX and RSI filters.
  • Uses StockSharp high-level bindings to calculate two simple moving averages together with Lowest/Highest swing detectors and daily AverageDirectionalIndex plus RelativeStrengthIndex indicators.
  • Recreates the original MT4 protective logic: take-profit by percentage, swing-based stop loss, and an optional break-even stop once a minimum profit is achieved.

Indicators

  • SimpleMovingAverage (fast and slow) on the primary timeframe.
  • Lowest / Highest to capture the extreme prices of the last BackClose candles for stop placement.
  • Daily AverageDirectionalIndex and RelativeStrengthIndex values for trend-strength and momentum filters.

Parameters

  • CandleType – primary timeframe (default: 15-minute candles).
  • FastPeriod, SlowPeriod – lengths of the fast and slow SMA lines.
  • AdxThreshold – maximum allowed value of the daily ADX to enable new entries.
  • RsiThreshold – daily RSI level for long entries (the short entry uses 100 - RsiThreshold).
  • TakeProfitRatio – fractional distance between entry price and the profit target.
  • StopLossPoints – distance of the protective stop (in instrument points) that arms after reaching ProtectThreshold.
  • ProtectThreshold – minimal open profit ratio that activates the protective stop.
  • BackClose – number of completed candles used for swing high/low stop calculation.
  • DailyAdxPeriod, DailyRsiPeriod – lengths of the daily indicators.

Trading Rules

  1. Work only on finished candles to match the MT4 expert advisor.
  2. Wait until all indicators are fully formed and daily values are available.
  3. Entry filters:
    • Reject new positions when the daily ADX exceeds AdxThreshold.
    • Long entry requires the fast SMA crossing above the slow SMA and the daily RSI to be below RsiThreshold.
    • Short entry requires the fast SMA crossing below the slow SMA and the daily RSI to be above 100 - RsiThreshold.
  4. On entry, store the swing extreme (Lowest for longs, Highest for shorts) as the manual stop reference.
  5. Exit logic while a position is active:
    • Close at TakeProfitRatio profit measured from the stored entry price.
    • Close if the candle close breaches the stored swing stop level.
    • Close on an opposite moving-average cross.
    • After the profit exceeds ProtectThreshold, arm a break-even style stop offset by StopLossPoints (rounded to the tick size) and close if the price retraces through it.
  6. Reset all internal state when the net position returns to zero.

Notes

  • All comments in the C# code are kept in English as required by the repository guidelines.
  • The strategy relies solely on StockSharp high-level subscriptions via Bind, avoiding manual indicator buffers.
  • Python translation is intentionally omitted according to the task instructions.
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 MaRobotStrategy : 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 MaRobotStrategy()
	{
		_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;
	}
}