HistoryInfoEaStrategy replica la utilidad MT4 "HistoryInfo" sobre StockSharp. En lugar de dibujar texto en el gráfico de MetaTrader, la estrategia escucha el flujo OnNewMyTrade y agrega estadísticas de operaciones que coinciden con un filtro elegido. Los valores agregados se exponen mediante la propiedad LastSnapshot y se reflejan en el log de la estrategia para que una GUI o un script de automatización pueda mostrar el resumen en el formato preferido.
La estrategia nunca registra sus propias órdenes. Está diseñada para ejecutarse junto con otras estrategias automáticas o manuales mientras estas envían órdenes al bróker. Cada operación ejecutada que satisface el filtro contribuye a los totales.
Parámetros
Parámetro
Descripción
FilterType
Modo de selección que determina cómo se emparejan las operaciones. Valores admitidos: CountByUserOrderId, CountByComment, CountBySecurity.
MagicNumber
Order.UserOrderId esperado. Se usa solo cuando FilterType es igual a CountByUserOrderId. Dejar vacío para desactivar este filtro.
OrderComment
Prefijo que debe coincidir con Order.Comment. Solo es relevante para el modo CountByComment. El valor predeterminado (\"OrdersComment\") imita el placeholder del script MT4 y normalmente no coincide con ninguna orden hasta reemplazarlo.
SecurityId
Identificador del instrumento (Security.Id) que debe coincidir cuando FilterType es igual a CountBySecurity. El valor predeterminado (\"OrdersSymbol\") es un placeholder.
Métricas agregadas
LastSnapshot se actualiza después de cada operación coincidente. Contiene:
FirstTrade / LastTrade - marcas temporales de las operaciones procesadas más antigua y más reciente.
TotalVolume - volumen ejecutado acumulado expresado en las unidades de volumen de la operación (lotes, contratos, etc.).
TotalProfit - suma de MyTrade.PnL menos la comisión reportada, lo que da la ganancia realizada en la divisa de la cuenta.
TotalPips - ganancia convertida a pips usando Security.PriceStep, Security.StepPrice y manejo de dígitos similar a MT4 (5/3 dígitos multiplican el punto por 10).
TradeCount - número de operaciones que pasaron el filtro.
La misma información se imprime en el log de la estrategia en una sola línea, emulando la salida Comment() de MT4 para inspección rápida.
Uso
Adjunte la estrategia a la misma cartera y valor que otras estrategias usan para enviar órdenes.
Elija el FilterType deseado y complete el parámetro asociado (magic number, prefijo de comentario o identificador de valor).
Inicie la estrategia. Tan pronto como se ejecute la primera operación que coincide con los criterios, los totales estarán disponibles mediante LastSnapshot y el log.
Los contadores se reinician automáticamente en cada reinicio de estrategia o reinicio manual.
Nota: Para calcular totales de pips, la estrategia depende de metadatos correctos del instrumento. Asegúrese de que Security.PriceStep y Security.StepPrice estén configurados en la definición del board. Si falta cualquiera de los valores, el contador de pips permanece en cero mientras la ganancia sigue acumulándose.
Notas de conversión
El código MT4 iteraba sobre OrdersHistoryTotal() en cada tick. En StockSharp, la estrategia reacciona a notificaciones MyTrade en tiempo real, por lo que no hay polling y los cálculos se actualizan inmediatamente cuando llega una ejecución.
MT4 guardaba la ganancia como OrderProfit + OrderCommission + OrderSwap. StockSharp entrega la ganancia realizada mediante MyTrade.PnL y la comisión por separado; el swap normalmente ya está incluido en el PnL. La adaptación resta la comisión de PnL para mantener consistencia con el reporte original.
Los placeholders de cadena (\"OrdersComment\", \"OrdersSymbol\") se conservan para parecerse a los valores predeterminados originales. Reemplácelos por valores reales antes de iniciar la estrategia si espera coincidencias.
La salida visual de gráfico de MT4 se sustituye por datos estructurados (LastSnapshot) y líneas de log para que los integradores decidan cómo representar la información.
La estrategia evita deliberadamente crear órdenes nuevas, por lo que puede iniciarse en modo solo lectura para analizar flujos de operaciones de terceros sin interferir con ellos.
Ideas de extensibilidad
Suscríbase a las actualizaciones de LastSnapshot y reenvíe la información a un dashboard o colector de telemetría.
Amplíe la clase con filtros adicionales (por ejemplo, por cartera o etiquetas de estrategia personalizadas) si el conector proporciona los metadatos relevantes.
Combine la estrategia con un temporizador periódico para exportar resúmenes históricos a un reporte CSV/JSON.
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;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class history_info_ea_strategy(Strategy):
def __init__(self):
super(history_info_ea_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 14) \
.SetDisplay("Fast Period", "Fast MA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 50) \
.SetDisplay("Slow Period", "Slow MA period", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200) \
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 400) \
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def stop_loss_points(self):
return self._stop_loss_points.Value
@property
def take_profit_points(self):
return self._take_profit_points.Value
def OnReseted(self):
super(history_info_ea_strategy, self).OnReseted()
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(history_info_ea_strategy, self).OnStarted2(time)
self._fast = ExponentialMovingAverage()
self._fast.Length = self.fast_period
self._slow = ExponentialMovingAverage()
self._slow.Length = self.slow_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(5)))
subscription.Bind(self._fast, self._slow, self._process_candle)
subscription.Start()
def _process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_value)
slow_val = float(slow_value)
if not self._fast.IsFormed or not self._slow.IsFormed:
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
if self.Position > 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close <= self._entry_price - self.stop_loss_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close >= self._entry_price + self.take_profit_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close >= self._entry_price + self.stop_loss_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close <= self._entry_price - self.take_profit_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 100
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return history_info_ea_strategy()