QC DD-DATA-FLOW-PRINCIPLES

Scopo: Definire come i dati devono attraversare i vari strati dell’applicazione (Engine Worker UI) senza subire perdite accidentali.


1. Il Problema del “Collo di Bottiglia Silenzioso”

In un’architettura a strati come quella di quantum-core, i dati generati dalla logica di business (es. BacktestEngine) devono attraversare diversi componenti intermedi (Worker, Signals, Handlers) prima di essere visualizzati.

Un errore comune è trattare i componenti intermedi come Filtri (o “Buttafuori”):

“Prendo solo quello che conosco, il resto lo butto.”

Questo approccio causa bug critici: quando l’Engine evolve e produce nuovi dati, i componenti intermedi li bloccano silenziosamente perché non sono stati aggiornati per riconoscerli.

2. Il Principio del “Postino” (The Mailman Principle)

Un Worker deve comportarsi come un Postino, non come un Censore. Il suo compito è consegnare il pacco (il dizionario dati) intatto, aggiungendo eventualmente timbri o ricevute (metriche calcolate), ma senza mai aprire il pacco per togliere contenuti che non capisce.

2.1. La Regola del Dictionary Merging

Quando un Worker riceve un output complesso da un Engine:

  1. NON creare un nuovo dizionario vuoto riempiendolo manualmente.
  2. USA sempre .copy() sull’output originale.
  3. USA .update() (o l’operatore |) per aggiungere i dati elaborati dal Worker stesso.

3. Esempio Pratico (Case Study: BacktestWorker)

Ecco come abbiamo corretto il bug della perdita dei colori di sfondo (background_events).

❌ Prima (Vulnerabile)

Il Worker decideva arbitrariamente quali chiavi salvare.

# L'engine ritornava {'trades': [...], 'background_events': [...]}
engine_output = engine.run()
 
# Il Worker creava un NUOVO dizionario, dimenticandosi 'background_events'
results = {
    "stats": calculate_stats(),
    "trades": engine_output.get("trades")
}
# RISULTATO: 'background_events' perso per sempre.

✅ Dopo (Robusto - Passthrough)

Il Worker preserva tutto ciò che arriva dall’Engine.

engine_output = engine.run()
 
# 1. Copia tutto (incluso 'background_events' che il Worker non conosce nemmeno!)
results = engine_output.copy()
 
# 2. Aggiungi/Sovrascrivi solo ciò che serve
results.update({
    "stats": calculate_stats(),
    "equity_curve": calculate_equity()
})
 
# RISULTATO: Il payload finale contiene TUTTO.

4. Checklist per Nuove Feature Dati

Quando aggiungi un nuovo tipo di dato (es. una nuova lista di segnali o overlay):

  1. Producer (Engine): Assicurati che il dato sia nel return dictionary.
  2. Transport (Worker): Verifica che il Worker usi il pattern copy().update(). Se non lo fa, rifattorizzalo.
  3. Transport (Handler): Verifica che l’Handler UI non filtri le chiavi prima di passarle al Manager.
  4. Consumer (UI Manager): Implementa la logica di visualizzazione.