La Estrategia de Lista de Posiciones reproduce el comportamiento del script original de MetaTrader imprimiendo periódicamente las posiciones actuales del portafolio en el log de la estrategia. Es un helper de solo monitoreo que nunca coloca órdenes. En cambio, construye un snapshot de las posiciones abiertas para que el operador pueda inspeccionar símbolo, dirección, tamaño, precio de entrada y ganancia actual directamente desde Designer o los logs de StockSharp.
Características clave
Reporte de posiciones impulsado por temporizador con el primer snapshot entregado inmediatamente después de que la estrategia comienza.
Filtrado opcional por el instrumento de la estrategia o por identificador de estrategia (el análogo del número mágico de MetaTrader).
Salida de log detallada que incluye el identificador de posición, último tiempo de cambio, lado, cantidad, precio promedio y ganancia.
Procesamiento seguro para subprocesos que evita superposiciones de callbacks del temporizador cuando el entorno está ocupado.
Parámetros
Nombre
Descripción
Predeterminado
StrategyIdFilter
Identificador de estrategia a omitir. Cuando se deja vacío, se reportan todas las posiciones.
Cadena vacía
SelectionMode
Controla si se reportan posiciones de cada símbolo o solo de Strategy.Security.
AllSymbols
TimerInterval
Intervalo entre snapshots consecutivos de posiciones.
6 segundos
Cómo funciona
Durante OnStarted, la estrategia verifica que haya un portafolio adjunto y que el intervalo del temporizador sea positivo.
Se crea un System.Threading.Timer con retraso cero para que el primer reporte se produzca inmediatamente y luego se repita en el intervalo configurado.
Cada tick del temporizador llama a ProcessPositions, que itera sobre Portfolio.Positions, aplica los filtros opcionales de símbolo e identificador de estrategia, y adjunta líneas formateadas a un StringBuilder.
Cuando al menos una posición pasa los filtros, la tabla ensamblada se escribe en el log con LogInfo. Si nada coincide, en su lugar se registra una notificación concisa.
Las superposiciones del temporizador se previenen con un guarda interlocked para que la I/O lenta no pueda desencadenar ejecuciones concurrentes.
Notas de uso
Asigne tanto Portfolio como Connector antes de iniciar la estrategia. Si SelectionMode está configurado en CurrentSymbol, también configure Strategy.Security al instrumento que desea monitorear.
Para emular el filtro magic de MetaTrader, llene StrategyIdFilter con el valor de cadena usado como StrategyId cuando otras estrategias envían órdenes. Esas posiciones se excluirán del reporte.
La estrategia nunca modifica posiciones ni registra órdenes, lo que la hace segura para ejecutar junto con lógica de trading en vivo como un widget informacional.
La salida del log se agrupa bajo el encabezado de columna Idx | Symbol | PositionId | LastChange | Side | Quantity | AvgPrice | PnL para que pueda ser fácilmente analizado por herramientas externas si es necesario.
Diferencias con la versión MQL
MetaTrader usa un número magic de 64 bits sin signo. Las posiciones de StockSharp exponen el identificador de estrategia como una cadena, por lo tanto el filtro acepta valores textuales.
En lugar de escribir en el comentario del gráfico, este port registra el snapshot mediante LogInfo, que es visible en Designer, Runner o cualquier listener de log.
La versión de StockSharp protege contra invocaciones solapadas del temporizador para mantenerse responsiva bajo carga pesada.
Las marcas de tiempo dependen de Position.LastChangeTime, que refleja las actualizaciones de posición de StockSharp, mientras que el script MQL mostraba el tiempo de creación del ticket.
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()