View on GitHub

Manual Trading Lightweight Utility Panel Strategy

Overview

The Manual Trading Lightweight Utility Panel Strategy replicates the behaviour of the MT4 "Manual Trading Lightweight Utility" panel using the StockSharp high level strategy API. It exposes the same interactive controls as strategy parameters so that the operator can toggle between market, limit and stop orders, adjust automatic price calculation, configure volume management and attach risk controls without relying on custom chart objects.

The strategy is designed for discretionary trading. Orders are triggered manually by changing the Send Buy Order or Send Sell Order parameters in the UI. Every command is acknowledged immediately, while the strategy keeps all calculations — such as automatic price suggestions and risk levels — synchronised with real time market data.

Key Features

  • Manual order dispatch for both buy and sell sides with support for market, limit and stop orders.
  • Automatic price suggestion that mirrors the MT4 panel logic, updating the proposed limit or stop price from the latest bid/ask stream.
  • Optional manual price mode that lets the operator type the desired trigger level while respecting instrument step sizes.
  • Volume management with a global lot size and individual buy/sell volumes when the lot control switch is enabled.
  • Integrated stop-loss and take-profit management implemented in the strategy layer to emulate order-attached protections on MT4.
  • Detailed feedback through parameters that always reflect the latest computed entry levels for both sides.

Conversion Notes

  • The MT4 chart objects (buttons, labels and edit boxes) are replaced by strategy parameters grouped in logical sections for easy access in Hydra/Terminal.
  • Protective stops and targets are handled internally by observing the live market price because StockSharp does not embed them into pending orders the same way as MT4.
  • Price offsets expressed in points reuse the instrument metadata (PriceStep and VolumeStep) so that limits and stops always respect exchange constraints.

Usage

  1. Attach the strategy to a security and portfolio in Hydra or Terminal.
  2. Configure the default lot size, risk parameters and price offsets.
  3. Optionally enable Lot Control to maintain independent volumes for the buy and sell buttons.
  4. Pick the order type (market, pending limit or pending stop) and whether the trigger price should follow the market or remain manual.
  5. When ready, toggle Send Buy Order or Send Sell Order to true. The strategy will submit the corresponding order and reset the flag to false once processed.
  6. The protection manager will close open positions at the configured stop-loss or take-profit levels calculated from the executed entry price.

Files

  • CS/ManualTradingLightweightUtilityPanelStrategy.cs – C# implementation of the strategy.
  • README.md – English documentation (this file).
  • README_zh.md – Simplified Chinese documentation.
  • README_ru.md – Russian documentation.
namespace StockSharp.Samples.Strategies;

using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;

/// <summary>
/// Manual Trading Panel strategy: SMA mean reversion with RSI filter.
/// Buys when close is below SMA and RSI is oversold, sells when above and overbought.
/// </summary>
public class ManualTradingLightweightUtilityPanelStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _smaPeriod;
	private readonly StrategyParam<int> _rsiPeriod;
	private readonly StrategyParam<int> _signalCooldownCandles;
	private int _candlesSinceTrade;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int SmaPeriod { get => _smaPeriod.Value; set => _smaPeriod.Value = value; }
	public int RsiPeriod { get => _rsiPeriod.Value; set => _rsiPeriod.Value = value; }
	public int SignalCooldownCandles { get => _signalCooldownCandles.Value; set => _signalCooldownCandles.Value = value; }

	public ManualTradingLightweightUtilityPanelStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
			.SetDisplay("Candle Type", "Candle timeframe", "General");
		_smaPeriod = Param(nameof(SmaPeriod), 20)
			.SetGreaterThanZero()
			.SetDisplay("SMA Period", "SMA period", "Indicators");
		_rsiPeriod = Param(nameof(RsiPeriod), 14)
			.SetGreaterThanZero()
			.SetDisplay("RSI Period", "RSI period", "Indicators");
		_signalCooldownCandles = Param(nameof(SignalCooldownCandles), 8)
			.SetGreaterThanZero()
			.SetDisplay("Signal Cooldown", "Bars to wait between trades", "Trading");
	}

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_candlesSinceTrade = SignalCooldownCandles;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);
		_candlesSinceTrade = SignalCooldownCandles;
		var sma = new SimpleMovingAverage { Length = SmaPeriod };
		var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
		var subscription = SubscribeCandles(CandleType);
		subscription.Bind(sma, rsi, ProcessCandle).Start();
	}

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

		if (_candlesSinceTrade < SignalCooldownCandles)
			_candlesSinceTrade++;

		var close = candle.ClosePrice;

		if (close < sma && rsi < 35 && Position <= 0 && _candlesSinceTrade >= SignalCooldownCandles)
		{
			BuyMarket();
			_candlesSinceTrade = 0;
		}
		else if (close > sma && rsi > 65 && Position >= 0 && _candlesSinceTrade >= SignalCooldownCandles)
		{
			SellMarket();
			_candlesSinceTrade = 0;
		}
	}
}