A HistoryInfoEaStrategy replica o utilitário MT4 "HistoryInfo" sobre o StockSharp. Em vez de desenhar texto no gráfico do MetaTrader, a estratégia escuta o fluxo OnNewMyTrade e agrega estatísticas de operações que correspondem a um filtro escolhido. Os valores agregados são expostos pela propriedade LastSnapshot e espelhados no log da estratégia para que uma GUI ou script de automação possa exibir o resumo no formato preferido.
A estratégia nunca registra suas próprias ordens. Ela foi projetada para rodar ao lado de outras estratégias automáticas ou manuais enquanto elas enviam ordens à corretora. Cada operação executada que satisfaz o filtro contribui para os totais.
Parâmetros
Parâmetro
Descrição
FilterType
Modo de seleção que determina como operações são correspondidas. Valores suportados: CountByUserOrderId, CountByComment, CountBySecurity.
MagicNumber
Order.UserOrderId esperado. Usado apenas quando FilterType é igual a CountByUserOrderId. Deixe vazio para desabilitar este filtro.
OrderComment
Prefixo que deve corresponder a Order.Comment. Relevante apenas para o modo CountByComment. O valor padrão (\"OrdersComment\") imita o placeholder do script MT4 e normalmente não corresponde a nenhuma ordem até ser substituído.
SecurityId
Identificador do instrumento (Security.Id) que deve corresponder quando FilterType é igual a CountBySecurity. O padrão (\"OrdersSymbol\") é um placeholder.
Métricas agregadas
LastSnapshot é atualizado após cada operação correspondente. Ele contém:
FirstTrade / LastTrade - timestamps da operação mais antiga e mais recente processadas.
TotalVolume - volume preenchido acumulado expresso nas unidades de volume da operação (lotes, contratos etc.).
TotalProfit - soma de MyTrade.PnL menos comissão reportada, fornecendo o lucro realizado na moeda da conta.
TotalPips - lucro convertido para pips usando Security.PriceStep, Security.StepPrice e tratamento de dígitos semelhante ao MT4 (5/3 dígitos multiplicam o ponto por 10).
TradeCount - número de operações que passaram pelo filtro.
A mesma informação é impressa no log da estratégia em uma única linha, emulando a saída Comment() do MT4 para inspeção rápida.
Uso
Anexe a estratégia ao mesmo portfólio e ativo que outras estratégias usam para envio de ordens.
Escolha o FilterType desejado e preencha o parâmetro associado (magic number, prefixo de comentário ou identificador de ativo).
Inicie a estratégia. Assim que a primeira operação que corresponde aos critérios for preenchida, os totais ficam disponíveis por LastSnapshot e pelo log.
Os contadores são redefinidos automaticamente em cada reinício da estratégia ou reset manual.
Observação: Para calcular totais em pips, a estratégia depende de metadados corretos do instrumento. Garanta que Security.PriceStep e Security.StepPrice estejam configurados na definição do board. Se qualquer valor estiver ausente, o contador de pips permanece em zero enquanto o valor de lucro continua acumulando.
Notas de conversão
O código MT4 iterava sobre OrdersHistoryTotal() em cada tick. No StockSharp, a estratégia reage a notificações MyTrade em tempo real, portanto não há polling e os cálculos atualizam imediatamente quando um fill chega.
O MT4 armazenava lucro como OrderProfit + OrderCommission + OrderSwap. O StockSharp entrega lucro realizado via MyTrade.PnL e comissão separadamente; swap geralmente já está incluído no PnL. A versão subtrai comissão de PnL para manter consistência com o relatório original.
Os placeholders de string (\"OrdersComment\", \"OrdersSymbol\") são preservados para lembrar os padrões originais. Substitua-os por valores reais antes de iniciar a estratégia se espera correspondências.
A saída visual de gráfico do MT4 é substituída por dados estruturados (LastSnapshot) e linhas de log, para que integradores decidam como renderizar a informação.
A estratégia evita propositalmente criar novas ordens, podendo ser iniciada em modo somente leitura para analisar fluxos de operações de terceiros sem interferir neles.
Ideias de extensibilidade
Assine as atualizações de LastSnapshot e encaminhe a informação para um dashboard ou coletor de telemetria.
Estenda a classe com filtros adicionais (por exemplo, por portfólio ou tags de estratégia customizadas) se o conector fornecer os metadados relevantes.
Combine a estratégia com um temporizador periódico para exportar resumos históricos para um relatório 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()