kb.erickguedes.com
C#: Matching Engine — Projeto Mercado Financeiro

LINQ, Delegates e Eventos no Trading

Aula 5 de 7

Delegates — Callbacks

// Delegate personalizado
public delegate void TradeHandler(Trade trade);

public class TradeNotifier
{
    private TradeHandler? _handlers;

    public void Subscribe(TradeHandler handler)
        => _handlers += handler;

    public void Unsubscribe(TradeHandler handler)
        => _handlers -= handler;

    public void Notify(Trade trade)
        => _handlers?.Invoke(trade);
}

Events — Notificação Padrão

public class MatchingEngine
{
    // Evento com EventHandler<T> padrão .NET
    public event EventHandler<Trade>? TradeExecutado;
    public event EventHandler<Ordem>? OrdemRejeitada;
    public event EventHandler<MarketData>? BookAtualizado;

    private void OnTradeExecutado(Trade trade)
    {
        TradeExecutado?.Invoke(this, trade);
    }

    public void ProcessarOrdem(Ordem ordem)
    {
        if (ordem.Quantidade <= 0)
        {
            OrdemRejeitada?.Invoke(this, ordem);
            return;
        }
        // ... processamento ...
        OnTradeExecutado(new Trade(/* ... */));
    }
}

// Uso
engine.TradeExecutado += (sender, trade) =>
{
    Console.WriteLine($"Trade: {trade.Quantidade} @ {trade.Preco}");
};

Func/Action — Delegates Genéricos

public class OrderValidator
{
    // Func<entrada, retorno>
    private readonly Func<Ordem, bool> _validateOrdem;
    private readonly Func<Ordem, decimal?> _calcularTaxa;

    // Action<entrada> — sem retorno
    private readonly Action<Ordem> _onRejeicao;

    public OrderValidator(
        Func<Ordem, bool> validateOrdem,
        Func<Ordem, decimal?> calcularTaxa,
        Action<Ordem> onRejeicao)
    {
        _validateOrdem = validateOrdem;
        _calcularTaxa = calcularTaxa;
        _onRejeicao = onRejeicao;
    }

    public bool Validar(Ordem ordem)
    {
        if (!_validateOrdem(ordem))
        {
            _onRejeicao(ordem);
            return false;
        }
        var taxa = _calcularTaxa(ordem);
        return true;
    }
}

LINQ — Análise de Dados

public class TradeAnalyzer
{
    private readonly IEnumerable<Trade> _trades;

    public TradeAnalyzer(IEnumerable<Trade> trades)
    {
        _trades = trades;
    }

    public IEnumerable<(string Simbolo, decimal Volume)> TopSimbolos(int n)
        => _trades
            .GroupBy(t => t.Simbolo)
            .Select(g => (Simbolo: g.Key,
                          Volume: g.Sum(t => t.Quantidade * t.Preco)))
            .OrderByDescending(x => x.Volume)
            .Take(n);

    public decimal PrecoMedioPonderado(string simbolo)
        => _trades
            .Where(t => t.Simbolo == simbolo)
            .Aggregate(
                (QtdTotal: 0m, ValorTotal: 0m),
                (acc, t) => (
                    acc.QtdTotal + t.Quantidade,
                    acc.ValorTotal + t.Quantidade * t.Preco
                ),
                acc => acc.QtdTotal > 0
                    ? acc.ValorTotal / acc.QtdTotal
                    : 0m
            );

    public Dictionary<string, decimal> VolatilidadePorSimbolo()
        => _trades
            .GroupBy(t => t.Simbolo)
            .ToDictionary(
                g => g.Key,
                g => {
                    var precos = g.Select(t => t.Preco);
                    var media = precos.Average();
                    var variancia = precos
                        .Select(p => Math.Pow((double)(p - media), 2))
                        .Average();
                    return (decimal)Math.Sqrt(variancia);
                });
}

LINQ em Tempo Real com MoreLINQ

// Janela deslizante de trades para VWAP
public class VwapCalculator
{
    private readonly Queue<Trade> _janela = new();
    private readonly int _tamanhoJanela;

    public VwapCalculator(int tamanhoJanela = 100)
    {
        _tamanhoJanela = tamanhoJanela;
    }

    public decimal AdicionarTrade(Trade trade)
    {
        _janela.Enqueue(trade);
        while (_janela.Count > _tamanhoJanela)
            _janela.Dequeue();

        return _janela.Sum(t => t.Quantidade * t.Preco)
             / _janela.Sum(t => t.Quantidade);
    }
}

Eventos são o padrão de notificação no .NET. LINQ transforma consultas complexas em código declarativo. Use Func/Action para comportamentos injetáveis.