The List Positions Strategy reproduces the behaviour of the original MetaTrader script by periodically printing the current portfolio positions to the strategy log. It is a monitoring-only helper that never places orders. Instead, it builds a snapshot of the open positions so that the operator can inspect symbol, direction, size, entry price and current profit directly from the Designer or StockSharp logs.
Key Features
Timer-driven position reporting with the first snapshot delivered immediately after the strategy starts.
Optional filtering by the strategy security or by strategy identifier (the analogue of the MetaTrader magic number).
Detailed log output including the position identifier, last change time, side, quantity, average price and profit.
Thread-safe processing that avoids overlapping timer callbacks when the environment is busy.
Parameters
Name
Description
Default
StrategyIdFilter
Strategy identifier to skip. When left empty all positions are reported.
Empty string
SelectionMode
Controls whether positions from every symbol or only from Strategy.Security are reported.
AllSymbols
TimerInterval
Interval between consecutive position snapshots.
6 seconds
How It Works
During OnStarted the strategy verifies that a portfolio is attached and that the timer interval is positive.
A System.Threading.Timer is created with zero delay so the first report is produced immediately and then repeated at the configured interval.
Each timer tick calls ProcessPositions, which iterates over Portfolio.Positions, applies the optional symbol and strategy-id filters, and appends formatted lines to a StringBuilder.
When at least one position passes the filters the assembled table is written to the log with LogInfo. If nothing matches, a concise notification is logged instead.
Timer overlaps are prevented with an interlocked guard so that slow I/O cannot trigger concurrent executions.
Usage Notes
Assign both Portfolio and Connector before starting the strategy. If SelectionMode is set to CurrentSymbol, also set Strategy.Security to the instrument you wish to monitor.
To emulate the MetaTrader magic filter, fill StrategyIdFilter with the string value used as StrategyId when other strategies submit orders. Those positions will be excluded from the report.
The strategy never modifies positions or registers orders, making it safe to run alongside live trading logic as an informational widget.
Log output is grouped under the column header Idx | Symbol | PositionId | LastChange | Side | Quantity | AvgPrice | PnL so it can be easily parsed by external tooling if necessary.
Differences Compared to the MQL Version
MetaTrader uses an unsigned 64-bit magic number. StockSharp positions expose the strategy identifier as a string, therefore the filter accepts textual values.
Instead of writing to the chart comment, this port records the snapshot via LogInfo, which is visible in Designer, Runner or any log listener.
The StockSharp version guards against overlapping timer invocations to stay responsive under heavy load.
Time stamps rely on Position.LastChangeTime, which reflects StockSharp position updates, while the MQL script displayed the ticket creation time.
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Logs the current position on every candle. Simplified from the original multi-position listing.
/// When position changes direction based on candle close, trades accordingly.
/// </summary>
public class ListPositionsStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _logInterval;
private int _candleCount;
private decimal? _prevClose;
/// <summary>
/// Candle type for monitoring.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Number of candles between position log entries.
/// </summary>
public int LogInterval
{
get => _logInterval.Value;
set => _logInterval.Value = value;
}
/// <summary>
/// Initializes strategy parameters.
/// </summary>
public ListPositionsStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe for monitoring", "General");
_logInterval = Param(nameof(LogInterval), 10)
.SetGreaterThanZero()
.SetDisplay("Log Interval", "Log position every N candles", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, CandleType);
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_candleCount = 0;
_prevClose = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
SubscribeCandles(CandleType)
.Bind(ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormed)
return;
_candleCount++;
if (_prevClose != null)
{
if (candle.ClosePrice > _prevClose.Value && Position <= 0)
BuyMarket();
else if (candle.ClosePrice < _prevClose.Value && Position >= 0)
SellMarket();
}
if (_candleCount % LogInterval == 0)
{
LogInfo($"Position: {Position}, Price: {candle.ClosePrice:0.#####}, Equity: {Portfolio?.CurrentValue:0.##}");
}
_prevClose = candle.ClosePrice;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes
from StockSharp.Algo.Strategies import Strategy
class list_positions_strategy(Strategy):
def __init__(self):
super(list_positions_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4)))
self._log_interval = self.Param("LogInterval", 10)
self._candle_count = 0
self._prev_close = None
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def LogInterval(self):
return self._log_interval.Value
@LogInterval.setter
def LogInterval(self, value):
self._log_interval.Value = value
def OnStarted2(self, time):
super(list_positions_strategy, self).OnStarted2(time)
self._candle_count = 0
self._prev_close = None
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self.ProcessCandle).Start()
def ProcessCandle(self, candle):
if candle.State != CandleStates.Finished:
return
if not self.IsFormed:
return
close = float(candle.ClosePrice)
self._candle_count += 1
if self._prev_close is not None:
if close > self._prev_close and self.Position <= 0:
self.BuyMarket()
elif close < self._prev_close and self.Position >= 0:
self.SellMarket()
self._prev_close = close
def OnReseted(self):
super(list_positions_strategy, self).OnReseted()
self._candle_count = 0
self._prev_close = None
def CreateClone(self):
return list_positions_strategy()