View on GitHub

HistoryInfoEaStrategy

Overview

The HistoryInfoEaStrategy replicates the MT4 "HistoryInfo" utility on top of StockSharp. Instead of drawing text on the MetaTrader chart, the strategy listens to the OnNewMyTrade stream and aggregates statistics for trades that match a chosen filter. The aggregated values are exposed through the LastSnapshot property and mirrored in the strategy log so that a GUI or automation script can display the summary in any preferred form.

The strategy never registers its own orders. It is designed to run alongside other automated or manual strategies while they submit orders to the broker. Every filled trade that satisfies the filter contributes to the totals.

Parameters

Parameter Description
FilterType Selection mode that determines how trades are matched. Supported values: CountByUserOrderId, CountByComment, CountBySecurity.
MagicNumber Expected Order.UserOrderId. Used only when FilterType equals CountByUserOrderId. Leave empty to disable this filter.
OrderComment Prefix that must match Order.Comment. Only relevant for the CountByComment mode. The default value ("OrdersComment") mimics the MT4 script placeholder and usually does not match any order until replaced.
SecurityId Identifier of the instrument (Security.Id) that must match when FilterType equals CountBySecurity. The default ("OrdersSymbol") is a placeholder.

Aggregated metrics

LastSnapshot is updated after every matching trade. It contains:

  • FirstTrade / LastTrade – timestamps of the earliest and latest processed trades.
  • TotalVolume – cumulative filled volume expressed in the trade's volume units (lots, contracts, etc.).
  • TotalProfit – sum of MyTrade.PnL minus reported commission, giving the realised profit in account currency.
  • TotalPips – profit converted into pips using Security.PriceStep, Security.StepPrice and MT4-like digit handling (5/3 digits multiply the point by 10).
  • TradeCount – number of trades that passed the filter.

The same information is printed to the strategy log in a single line, emulating the MT4 Comment() output for quick inspection.

Usage

  1. Attach the strategy to the same portfolio and security that other strategies use for order submission.
  2. Pick the desired FilterType and fill the associated parameter (magic number, comment prefix, or security identifier).
  3. Start the strategy. As soon as the first trade that matches the criteria is filled, the totals become available through LastSnapshot and the log.
  4. The counters reset automatically on every strategy restart or manual reset.

Note: To compute pip totals the strategy relies on correct instrument metadata. Ensure that Security.PriceStep and Security.StepPrice are configured in the board definition. If either value is missing the pip counter stays at zero while the profit value continues to accumulate.

Conversion notes

  • The MT4 code iterated over OrdersHistoryTotal() on every tick. In StockSharp the strategy reacts to real-time MyTrade notifications, so there is no polling and the calculations update immediately when a fill arrives.
  • MT4 stored profit as OrderProfit + OrderCommission + OrderSwap. StockSharp delivers realised profit via MyTrade.PnL and commission separately; swap is usually already included in the PnL. The port subtracts commission from PnL to keep consistency with the original report.
  • The string placeholders ("OrdersComment", "OrdersSymbol") are preserved to resemble the original defaults. Replace them with actual values before starting the strategy if you expect matches.
  • Visual chart output from MT4 is replaced by structured data (LastSnapshot) and log lines so that integrators can decide how to render the information.
  • The strategy purposefully avoids creating any new orders, so it can be launched in read-only mode to analyse third-party trade streams without interfering with them.

Extensibility ideas

  • Subscribe to the LastSnapshot updates and forward the information to a dashboard or telemetry collector.
  • Extend the class with additional filters (for example by portfolio or custom strategy tags) if the connector provides the relevant metadata.
  • Combine the strategy with a periodic timer to export historical summaries to a CSV/JSON report.
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 HistoryInfoEaStrategy : 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 HistoryInfoEaStrategy()
	{
		_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;
	}
}