Fix portfolio, account and order reads on the trading port - #2
Conversation
get_portfolio() sent GETPORTFOLIO and get_account_info() sent GETACCTINFO.
Darwin rejects both with ERR;<command>;1004, so neither call could ever
return data. Verified against Darwin 2.5.1:
> GETPORTFOLIO ERR;GETPORTFOLIO;1004
> INFOSTOCKS STOCK;<ticker>;<ora>;... (one line per position)
> GETACCTINFO ERR;GETACCTINFO;1004
> INFOACCOUNT INFOACCOUNT;<ora>;<conto>;...
The parsers were already written for the correct commands — the docstrings
of parse_portfolio_response and parse_account_info_response name INFOSTOCKS
and INFOACCOUNT respectively — so only the commands sent were wrong.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
TradingConnection.send_command returned only the first line matching the expected prefix, discarding the rest of a multi-line response. Against a live account this meant ORDERLIST returned 4 orders on the socket and get_orders() returned 1, and a 15-position portfolio arrived as a single position. Nothing reported that data had been dropped. Two further problems fed into the same failure: - The read loop stopped at the first chunk containing a newline, so a list spanning several TCP reads was cut wherever the segment boundary fell. - Darwin pushes unsolicited traffic on this port: a full portfolio and order snapshot on connect, then position and order updates as they happen. Those lines were read as if they answered the command in flight, and pushed STOCK lines are indistinguishable from INFOSTOCKS rows. The response is now read line by line, with the leftover bytes buffered across calls, until the response is actually complete. Completion is determined by an end marker rather than by silence: connect() enables FLOWPOINT, so Darwin wraps list responses in BEGIN/END STOCKLIST and BEGIN/END ORDERLIST. If Darwin declines, the reader falls back to an idle gap and logs a warning. Lines are then selected by prefix, and a list command returns every matching row. The connect-time push is drained into TradingConnection.pushed_lines instead of being read as a response — it is a genuine snapshot, so it is kept rather than dropped. ERR codes 1024-1028 announce trading/datafeed link events and arrive unprompted; they no longer fail whichever command happens to be in flight. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Covers the reads against a stand-in for Darwin's socket, so they can run without the platform: complete multi-line responses, lines split across TCP reads, the connect-time push not being mistaken for a response, link notifications not failing a command, and the two command names Darwin accepts. Response shapes follow a live Darwin 2.5.1; values are synthetic. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Grazie Simone, mergiata. Segnalazione fatta come si deve: il protocollo verificato contro Darwin reale, i tre problemi separati in tre commit, e la tabella "sul socket / restituito da Prima di mergiare ho girato i 12 test nuovi contro il codice vecchio: falliscono tutti e dodici, quindi fissano davvero il comportamento rotto e non solo quello nuovo. Suite completa verde (20 test) e CI ok su 3.9/3.10/3.11. Ho apprezzato anche il fatto che i parser fossero già scritti per Sul percorso ordini hai ragione a lasciarlo fuori — conto reale, giusto non inviare nulla. Confermo la tua osservazione: Se il server MCP verso Darwin che stai costruendo diventa pubblico, linkalo pure — mi fa piacere sapere che la libreria è servita a qualcosa. |
Ciao! Ho usato questa libreria per costruire un server MCP verso Darwin e, testandola contro una piattaforma reale, ho trovato tre problemi sul percorso di lettura della porta di trading (10002) che la rendevano inutilizzabile per portafoglio, saldo e ordini. Tutto verificato su Darwin 2.5.1 (build 04/02/2025).
Il più insidioso non dà errore: perde dati in silenzio.
1. Due comandi che Darwin rifiuta
get_portfolio()inviaGETPORTFOLIOeget_account_info()inviaGETACCTINFO. Darwin rispondeERR;<comando>;1004a entrambi, quindi nessuna delle due chiamate può restituire dati:Curiosamente i parser erano già scritti per i comandi giusti: i docstring di
parse_portfolio_responseeparse_account_info_responsenominano proprioINFOSTOCKSeINFOACCOUNT. Era solo l'invio a essere sbagliato.Ho verificato anche che non fosse un problema di sinonimi:
GETACCOUNT,GETAVAILABILITY,INFOSTOCK(singolare),PORTFOLIO,INFOPOSITION,GETPENDINGORDERSeTABLEDESCRIPTIONrispondono tutti1004. FunzionanoINFOACCOUNT,INFOAVAILABILITY,INFOSTOCKS,GETPOSITION <sym>,ORDERLIST,ORDERLISTPENDING,DARWINSTATUS.2. Le risposte multi-riga venivano troncate alla prima riga
TradingConnection.send_commandrestituiva solo la prima riga corrispondente al prefisso atteso, scartando il resto. Misurato dal vivo su un conto con 15 posizioni e 4 record di ordine:send_commandORDERLISTINFOSTOCKSNessun errore, nessun warning: il chiamante riceve un portafoglio da una posizione e non ha modo di sapere che ne mancano 14. Su una libreria di trading mi è sembrato il problema più grave dei tre.
Due cause secondarie contribuivano allo stesso guasto:
recv()veniva tagliata dove cadeva il confine del segmento TCP.STOCKpushata è indistinguibile da una riga di risposta aINFOSTOCKS.3.
FLOWPOINT, per sapere dove finisce una listaSenza marker, una lista è solo una sequenza di righe e l'unico modo per sapere di averle tutte è aspettare il silenzio e sperare.
FLOWPOINT TRUErisolve: Darwin avvolge le liste inBEGIN/END.connect()ora lo abilita, così la lettura termina su un marker invece che su un timeout, e le righe pushate a metà risposta cadono fuori dai marker. Se Darwin rifiuta, si torna al fallback a intervallo di inattività con un warning nel log — nessuna regressione per chi ha una build che non lo supporta.Cosa cambia nel codice
TradingConnection.pushed_linesanziché essere letto come risposta. È uno snapshot legittimo del conto, quindi lo conservo invece di scartarlo.ERR1024-1028 segnalano eventi del collegamento trading/datafeed e arrivano non richiesti: non fanno più fallire il comando in volo.send_commandrestituisce ancora una stringa, e per i comandi a riga singola restituisce ancora una riga singola —parse_account_info_response, che fasplit(';')sull'intera risposta, continua a funzionare invariato.Test
Ho aggiunto
tests/test_trading_connection.py: 12 test contro un finto Darwin su socket, quindi girano in CI senza la piattaforma. Coprono le risposte complete, le righe spezzate tra letture TCP (chunk_size=4), il push iniziale non confuso con una risposta, le notifiche di link che non fanno fallire un comando, i due nomi di comando, e il fallback senzaFLOWPOINT. Le forme delle risposte seguono quelle reali; i valori sono sintetici.I 20 test (12 nuovi + 8 preesistenti di simulazione) passano.
Verificato anche end-to-end contro Darwin reale con la libreria patchata:
get_account_infoeget_portfoliotornano a restituire dati (prima:ERR 1004entrambi), eget_ordersrestituisce tutti i record invece di uno solo.Note
send_commandlegge fino aEND CANDLES/END TBTe non ha lo stesso difetto. Non ho comunque potuto vedere dati storici reali perché il conto usato non ha le quotazioni abilitate — ogni comando su 10003 risponde1032, coerentemente con il flag datafeedFALSEinDARWIN_STATUS.send_commandgestisca bene anche le risposteTRADOK/TRADERR/TRADCONFIRM, che con il mio fix passano dal ramo "prefisso non noto".FLOWPOINT— dimmelo e riorganizzo volentieri. Ho inclusoFLOWPOINTperché senza i marker le righe pushate restano indistinguibili da quelle di risposta, quindi il fix del troncamento da solo mi sembrava a metà.Grazie per aver pubblicato la libreria — avere il protocollo già mappato in Python mi ha fatto risparmiare un bel po' di lavoro.