chstrategy.xyz
Piattaforma personale per gestire in un unico posto strategia di investimento, patrimonio e operatività crypto. Una homepage semplice con tre bottoni porta a tre sezioni indipendenti, ognuna con la propria dashboard, come tre piattaforme a sé stanti: Strategia, Assets e Trading.
Un concetto attraversa tutte le sezioni: il portafoglio. Ogni portafoglio (il
proprio privato, quello di una cliente) è un'entità separata nel database, con le
sue piattaforme, le sue posizioni e il suo track record. L'accesso alle tre sezioni
richiede il login (Clerk, pagine /accedi e /registrati in italiano e con il tema
del sito): al clic su una sezione dalla home si apre un selettore minimale al centro
della pagina con i portafogli dell'utente loggato, dove se ne sceglie uno o se ne
crea uno nuovo, e solo allora si entra. Il portafoglio attivo resta visibile
nell'intestazione della sezione, da cui si può cambiare, e chi entra in una sezione
da un link diretto senza selezione trova lo stesso selettore ad attenderlo.
I dati su Convex seguono la stessa gerarchia a tre livelli: prima l'utente
loggato (il subject del JWT Clerk), poi il portafoglio selezionato, poi la o le
piattaforme scelte (per esempio l'account con la sua venue nel chatbot di
trading). Ogni dato applicativo nasce etichettato così. La sezione Trading è
riservata agli amministratori elencati, separati da virgola, in
NEXT_PUBLIC_ADMIN_EMAIL: gli altri utenti loggati vedono solo Strategia e
Assets e la voce Trading non compare in home. L'accesso in sola lettura per i
clienti al proprio portafoglio in Strategia e Assets resta il passo successivo,
ora che l'identità c'è.
Direzione a lungo termine
L'obiettivo è un unico chatbot da cui aprire e chiudere posizioni su qualsiasi piattaforma tra le più usate: exchange centralizzati e decentralizzati, protocolli DeFi e aggregatori come 1inch e NEAR Intents, ognuno integrato con i suoi strumenti ufficiali tenuti all'ultima versione (vedi "Manutenzione dello stack"). La difficoltà principale del piano è tenere ordine nella crescita: ogni piattaforma in più porta simboli, quote, credenziali e regole sue, e senza una forma comune la codebase degenera in casi speciali.
La regola è quindi mettere ogni cosa al posto giusto e restare minimali,
classificando ogni singola chiamata verso una piattaforma, non la piattaforma
nel suo insieme: la stessa piattaforma può comparire in più punti con ruoli
diversi (Hyperliquid, per esempio, è sia una venue sia una fonte dati). Due
domande in sequenza decidono dove va il codice. Primo: la chiamata firma
qualcosa o muta uno stato reale (un conto, un wallet)? Se no, è layer dati:
entra come provider in convex/providers dietro l'interfaccia di ingestione
comune, alimenta le tabelle Convex (marketLatest/marketSeries per i prezzi,
accountSnapshots per saldi e posizioni) e la chat legge da lì invece che in
diretta, riservando il fetch live ai soli momenti pre-trade. Se sì, seconda
domanda: lo stato risultante vive in un conto tenuto dalla piattaforma stessa
(margine, order book, posizioni gestite da lei) o in token dentro il wallet
dell'utente? Nel primo caso è layer esecuzione, comparto venue: un
VenueAdapter registrato in src/lib/execution/venues, con le sue capacità
dichiarate e nessuna credenziale conservata, diviso a sua volta in una faccia
VenueReader (sola lettura, usata ovunque) e una faccia VenueExecutor (firma,
usata solo dalla route execute e dall'anteprima dell'ordine). Nel secondo caso
è layer esecuzione, comparto DeFi: un modulo in src/lib/execution/defi
con client fetch-only e logica pura testabile senza rete, come
src/lib/execution/defi/nearIntents (oggi solo quote in sola lettura: il primo
swap davvero eseguito è ciò che darà forma al primo WalletCredentials
usato per davvero). Le integrazioni DeFi devono conservare tutte le capacità
tecniche delle API; l'autorizzazione cambia in base al valore, e sopra 50 USD
richiede sempre una firma Ledger. Una piattaforma nuova si aggiunge implementando
l'interfaccia del comparto giusto e registrandosi, senza toccare motore di
rischio, flusso di conferma o chat; se un'integrazione non risponde in modo
netto a queste due domande, prima si decide la forma nuova e poi si scrive il
codice. Gli SDK ufficiali si adottano solo dove fanno risparmiare lavoro vero,
tipicamente la firma degli ordini; le letture restano fetch diretti, bundlabili
sia in Next sia in Convex. Con la crescita dei tool della chat, il dispatch oggi
concentrato nella route parse andrà spezzato in moduli per tool, mantenendo
la route come solo smistamento.
Strategia
Definisce l'allocazione ideale del capitale, distinguendo per tipologia di asset e per piattaforma usata per investirci. È il punto di riferimento contro cui confrontare la fotografia reale della sezione Assets (il confronto resta a occhio: le sezioni non si parlano). I contenuti sono separati per portafoglio nel backend Convex: un portafoglio senza strategia mostra uno stato vuoto. Il primo caso reale è VK, con la proposta del 18 giugno 2026: 600'000 CHF divisi in parti uguali tra comparto a medio e ad alto rischio, con componenti, piattaforme e regole di rischio mostrate in sola lettura. Gli asset concreti assegnati a una componente hanno uno stato binario: idea (rosso) o executed (verde); il prezzo d'ingresso resta un campo opzionale a parte.
L'evoluzione prevista aggiunge due forme equivalenti di modifica: editing inline sui singoli blocchi della pagina e un chatbox che applica le modifiche descritte in linguaggio naturale. Entrambi scriveranno le stesse tabelle, l'ultimo salvataggio vincerà e Convex conserverà la storia delle revisioni.
Assets
Risponde a due domande: "dove sono i soldi?" oggi e "come sono andati?" nel tempo. In cima mostra due grafici affiancati del portafoglio attivo: il capitale ripartito tra gli asset delle posizioni e la liquidità restante, e lo stesso totale ripartito per venue. Sotto resta il track record, che aggrega le operazioni realizzate archiviate da Hyperliquid, OKX e Lighter, incluse le vendite spot OKX con costo medio ponderato mobile. La tabella elenca lo storico completo del portafoglio, non un estratto. Mostra inoltre le posizioni Hyperliquid ancora aperte, il risultato netto, la percentuale di operazioni vincenti, il profit factor, il rendimento sul margine, commissioni, funding, durata media, curva cumulata e max drawdown.
Quali conti appartengono a quale portafoglio lo si decide in integrazione (env + assegnazione Convex), non dalla UI: niente click «collega». I badge Trading mostrano le venue attive su quel portafoglio. Un portafoglio senza conti non vede i conti altrui, e un errore nella lettura della mappa non ricade mai sui conti degli altri portafogli. Un job Convex aggiorna ogni ora lo storico di entrambe le venue e il pulsante aggiorna permette di rileggerlo subito. La fotografia patrimoniale completa, con liquidità fiat e stablecoin esterne alle venue collegate, e l'inserimento manuale via chat restano da realizzare.
Trading
Operatività multipiattaforma in linguaggio naturale. Il chatbot è un agente con strumenti: legge mercati, conti, storico, dati aggregati e fonti web, poi prepara ordini, protezioni su posizioni aperte, cambi leva e cancellazioni senza affidare al modello i numeri reali o i controlli di rischio.
La sottopagina /trading/markets classifica per volume nelle ultime 24 ore i
mercati delle venue collegate al portafoglio. Permette di passare dalle singole
coppie agli asset aggregati e mantiene spot e perpetual separati; nello spot il
confronto include solo quote USD o stablecoin, così i volumi restano omogenei.
L'interfaccia permette di scegliere il modello OpenRouter e il livello di
ragionamento; la ricerca web usa una chiamata dedicata e restituisce le fonti.
Ogni conversazione mostra nell'header il costo OpenRouter cumulativo in dollari:
il server somma tutte le chiamate del giro agentico, inclusa la ricerca web,
mentre token e dettaglio dei costi per modello restano salvati ma nascosti.
Ogni risposta generata mostra accanto a «chstrategy» il modello effettivo
restituito da OpenRouter. Le
conversazioni precedenti all'attivazione del conteggio non vengono stimate.
La cronologia resta associata al portafoglio selezionato e permette di riaprire,
rinominare inline ed eliminare le conversazioni salvate.
L'esecuzione reale è collegata a Hyperliquid, OKX, KuCoin e Lighter. Ogni azione passa dalla
validazione deterministica, produce un'anteprima e richiede un consenso finale:
l'utente può rispondere in linguaggio naturale, per esempio «sì», «esatto» o
«procedi», oppure usare il pulsante. Il consenso instrada un token firmato
monouso, non lascia al modello la decisione di eseguire. Le connessioni operative
possono leggere e fare trading. I trasferimenti di capitale vivono in un
sottosistema separato, con destinazioni verificate e un gate spento per default. Il
motore Python indipendente in engine/ conserva LIVE_TRADING
come seconda conferma locale per la modalità --live. Le regole complete sono
in SECURITY.md.
La venue non è fissata quando si apre la chat. Ogni richiesta viene instradata dal server verso la piattaforma nominata dall'utente; se manca, il server cerca il mercato nei conti operativi collegati e chiede una scelta soltanto quando lo stesso prodotto è disponibile su più venue. Lo stesso flusso copre perpetual, spot cash e spot margin cross/isolated dove supportato. OKX espone tutte queste modalità; Hyperliquid espone perpetual e spot cash. Il piano normalizzato, la venue e il conto risolti vengono firmati insieme nel token monouso, poi riletti e rivalidati prima dell'invio.
Le letture private riguardano invece un solo conto per volta. Se l'utente nomina
una posizione con ticker e direzione, il server la cerca nei conti collegati e
seleziona l'unico riscontro reale; chiede il conto soltanto quando la posizione
compare in più conti o il contesto verificato della piattaforma e della
conversazione non basta a identificarla. Una volta risolto,
lo stato comprende tutti i saldi e comparti esposti, posizioni aperte, ordini
standard e condizionali; ogni sezione non leggibile resta
esplicitamente incompleta con il bottleneck restituito dall'API. Lo storico
normalizzato copre ordini ed esecuzioni: su OKX legge l'archivio API di tre mesi
per spot, margin, swap e futures, inclusi gli ordini algo; su Hyperliquid vale il
limite API di 2.000 righe. Gli adapter convertono sempre le size dei derivati
nell'asset sottostante. Le posizioni chiuse includono anche il margine iniziale
calcolabile e restano disponibili nell'archivio permanente closedPositions di
Convex.
Le domande sul capitale aggiunto mentre una posizione era aperta usano una lettura storica dedicata: il server identifica la posizione chiusa in tutti i conti collegati, applica l'intervallo esatto tra apertura e chiusura e classifica le esecuzioni avvenute dentro quella finestra. Il totale esclude il margine iniziale della posizione che definisce l'intervallo e le operazioni di chiusura; somma gli esborsi spot cash e il margine dei soli aumenti perp. Il notional resta separato. Quando manca la leva storica ma la posizione è ancora aperta, la leva attuale produce una stima dichiarata; senza nessuna leva il totale resta esplicitamente parziale.
Tech stack
Il sito: TypeScript, React, Tailwind, Convex per backend e dati, Clerk per l'autenticazione, deploy su Vercel, Bun come runtime e package manager. Il motore della sezione Trading: Python 3.11+, SDK ufficiale Hyperliquid e ccxt per leggere le posizioni sugli altri exchange.
Struttura del repository
Alla radice vive l'app web Next.js (src/app/ con homepage e le tre sezioni,
src/components/ per i componenti condivisi). In engine/ vive il motore Python
della sezione Trading, con i suoi src/, tests/ e plans/.
Backend dati (Convex)
Il backend dati vive in convex/. Lo schema (schema.ts) copre i portafogli per
utente (portfolios), le connessioni di piattaforma per portafoglio
(connections), le conversazioni della chat di trading (conversations e
messages, con tracce d'audit su cosa ha prodotto ogni turno e la partizione per
utente e portafoglio), una cache della directory dei dex builder (builders, chi
ha deployato cosa), le tabelle di market intelligence descritte sotto,
accountSnapshots e il catalogo tool/docs (toolDocs + coda toolDocsQueue).
Account snapshots e sync account
accountSnapshots tiene saldi, posizioni, storico ordini e fill per (venue,
account, kind, scope), popolata ogni 5 minuti da convex/accountIngest.ts con lo
stesso collettore fetch-only dei provider di mercato. La chat legge da qui invece
di interrogare la venue in diretta a ogni domanda; se il dato manca o ha più di
10 minuti (src/lib/marketData/freshness.ts) ricade sul fetch live di sempre,
senza mai mescolare un pezzo fresco con uno stantio nella stessa risposta. L'elenco
account da fotografare non è una variabile d'ambiente Convex da tenere allineata
a mano: la rotta parse lo sincronizza su trackedAccounts a ogni turno di chat
(best-effort, mai bloccante), a partire dallo stesso TRADING_ACCOUNTS già
letto lato Vercel, che resta l'unica fonte di verità (indirizzi e dex builder
insieme). Le connessioni sono la mappa di quali account di trading appartengono a
quale portafoglio: le credenziali restano solo nelle variabili d'ambiente del
server, la rotta accounts filtra gli account configurati sulle connessioni del
portafoglio attivo e un portafoglio senza connessioni registrate li vede tutti,
così un portafoglio nuovo non nasce cieco.
Quando l'utente chiede una lezione su un trade già chiuso, la chat non cerca la posizione tra quelle aperte. Il server identifica prima l'ultima posizione archiviata con quel ticker in tutti i conti collegati al portafoglio, fissa venue e conto, legge le candele pubbliche del mercato sulla stessa venue e passa al modello un blocco obbligatorio con trade, percorso del prezzo e punto pertinente del Regolamento Interno. Una risposta generica che salta questi riscontri viene rigenerata. Il massimo profitto aperto e il tipo esatto dell'ordine di uscita restano non verificati finché non compaiono nello storico ordini o fill.
Storico operazioni realizzate (track record)
closedPositions è l'esatto contrario di accountSnapshots: accumula invece
di sovrascrivere. Serve perché le venue conservano lo storico per una finestra
limitata, circa tre mesi su OKX e circa 10.000 fill su Hyperliquid. La forma è
neutra rispetto alla venue e contiene strumento, verso o tipo spot, prezzi medi
di apertura e chiusura, PnL lordo, commissioni, funding e PnL netto. Le
statistiche del portafoglio sono quindi aggregate senza uno strato di unione.
Due job orari archiviano lo storico. closedPositions:ingestOkx legge
positions-history, dove OKX restituisce già i derivati aggregati, e
fills-history per lo spot. Ogni vendita spot realizza il PnL contro il costo
medio ponderato mobile degli acquisti precedenti. Il ledger conserva ogni fill,
mentre statistiche e lista accorpano quelli dello stesso ordine OKX in una sola
operazione; se l'id ordine manca usano una finestra di un'ora per strumento.
Fill elaborati e inventario aperto restano in Convex, quindi la finestra di tre
mesi serve solo al bootstrap e al recupero. Una vendita senza costo iniziale
noto non viene valorizzata a costo zero: resta diagnosticabile come non
abbinata e Assets mostra quali asset hanno ancora una base di costo incompleta.
closedPositions:ingestHyperliquid ricompone invece i cicli di apertura e
chiusura dai fill degli account tracciati. Poiché i fill storici non contengono
la leva, il job legge la configurazione dell'asset da activeAssetData alla
prima archiviazione e la congela sulla riga: per lo storico precedente al primo
sync è la configurazione disponibile al momento del recupero, non una prova di
eventuali variazioni avvenute mentre la posizione era aperta. Il pulsante
aggiorna in Assets esegue entrambi senza attendere il giro successivo.
Il client OKX del job Convex (convex/lib/okx.ts) e quello usato da Next per le
letture live (src/lib/execution/venues/okx/) sono separati perché girano in
runtime diversi e leggono le proprie variabili d'ambiente. Due trappole già
coperte: l'account EEA risponde solo su eea.okx.com tramite OKX_API_BASE e
OKX riusa il posId tra posizioni successive sullo stesso strumento. La chiave
logica è quindi (venue, account, positionId, closedAt).
closedPositions:stats calcola il track record sul PnL netto: totale,
percentuale di vincenti, vincita e perdita media, profit factor, rendimento sul
margine, curva cumulata giornaliera, massima discesa dal picco e suddivisioni
per asset, verso e mese. Il drawdown è misurato sul PnL realizzato, non sul
patrimonio: senza una serie storica del capitale è l'approssimazione onesta.
La query è riservata agli amministratori; probeOkx è una diagnostica di
collegamento in sola lettura.
Catalogo tool/docs (backend condiviso, UI solo admin)
Obiettivo: tenere sotto traccia le pagine ufficiali di documentazione dei tool
usati e dei candidati trovati navigando sul web, per aggiornare lo stack, per
valutare connettività genetica prima di integrare, perché gli agenti IDE
possano chiedere "cosa non è ancora implementato che potrebbe servire?"
(toolDocs:listOpportunities), e per monitorare le nuove capacità agentiche
dell'AI nel tempo: i docs incollati vanno letti in profondità (browser/llms.txt/
OpenAPI/guides), non solo classificati, così emergono idee di implementazione
per la sezione trading. Il catalogo alimenta le connessioni agentiche del trading
avanzato: a valle serve a tutti gli account, ma nessun utente non-admin lo vede
o ci accede in UI.
- Tabelle:
toolDocsQueue(coda di valutazione) etoolDocs(catalogo ammesso). - UI
/trading/tools-docs: solo amministratori (stesso controllo di/trading/*). Dump URL, coda, classify, seed, refresh ↻ (merge su fornitore già noto), toggle implemented, status, note. Se il dump è un nuovo path dello stesso fornitore già in catalogo → stessa pipeline refresh (skillupdate-tool.md), non una seconda entry. - UI
/api-status: integrazioni raggruppate per categoria Tool docs (venue_cex,venue_dex,defi,market_data, …): collegamento portafoglio, credenziali runtime, presenza dei nomi env su Vercel Production (viaVERCEL_API_TOKEN) e presenza delle chiavi attese sul deployment Convex (queryenvPresence, senza valori), scadenza sempre dichiarata —illimitatao data in<VENUE>_API_KEY_EXPIRES_AT— e ping live delle API a ogni apertura. Per ogni venue implementata: elenco delle funzioni con nome nativo della piattaforma e switch attivo/non attivo (gate prodotto chstrategy). I trasferimenti restano spenti per default e possono essere attivati solo se implementati. Modifica switch: solo admin. Stesso sblocco password di/trading. Catalogo host attesi:SERVICE_ENV_CHECKS/expectedEnvKeysForVenueinsrc/lib/apiStatus.ts; regola agenti:.cursor/rules/env-hosts.mdc. - Ciclo di vita (
status):watch→candidate→integrated; se un tool viene sostituito da uno migliore →deprecated(resta in catalogo come storico; non confondere concategoryprodotto tipovenue_cex/broker_api/market_data).archived= fuori scope. - Lettura client Convex (
listCatalog, coda):requireAdmin. - Lettura e accodamento server / agenti (
listCatalogForServer,listOpportunities,enqueueUrlsForServer+CHAT_WRITE_SECRET): usati dalle route Next e dagli agenti IDE senza esporre la pagina catalogo. - Flusso admin: dump URL (anche candidati web) → coda →
POST /api/trading/tools-docs/classify(1 URL per richiesta) → gate fit+tier e gate categoria (3 domande sì/no; categorie built-in o slug nuovi) → catalogo. Preferenza URL: homepage docs o getting started / for developers. Se un URL in coda/catalogo è morto:web_searchper lo slug docs ufficiale aggiornato, poi nuovodocsRootUrl(skill classify + rule Cursor). Ogni doc hacategory(identità),integrationRoles(usi chstrategy),capabilities(funzioni API) eserviceTargetscon il tipo di accesso per retail, professionisti e istituzioni. Le note amministrative diventano il brief vincolante del refresh successivo. - Seed iniziale:
seedImplementedCatalog(admin) oseedImplementedCatalogServer(secret); lista inconvex/toolDocsSeed.tse skillsrc/lib/toolsDocs/skills/codebase-context.md. Regola Cursor:.cursor/rules/tool-docs-catalog.mdc. - Fase 2 (parziale): refresh manuale (bottone ↻ in catalogo) e auto-refresh quando
il dump URL appartiene a un fornitore già classificato (skill
update-tool.md, merge sutargetToolDocId). Restano: refresh periodico, score DeFiLlama, storico check.
Auth sulle funzioni
Le funzioni che leggono o scrivono le conversazioni restano protette da un segreto
condiviso (CHAT_WRITE_SECRET): il loro chiamante legittimo è il server Next,
che aggiunge ai turni salvati l'identità Clerk verificata dal middleware, mentre
un client del browser non conosce il segreto e non può toccare quei dati. Questo
vale doppio qui, dato che è la stessa app che firma ordini reali. Le funzioni dei
portafogli (portfolios.ts) usano invece direttamente l'identità del JWT Clerk
validato da Convex: ogni utente vede solo i suoi. Il catalogo toolDocs è
dato di backend gestito solo dall'admin in UI, ma leggibile dal server (secret)
per alimentare tool agentici a beneficio di tutti gli account.
Lo schema crescerà con le tabelle di Strategia e Assets, che nasceranno già agganciate al portafoglio giusto.
Autenticazione (Clerk)
L'autenticazione è su tre livelli, tutti in src/proxy.ts. Il primo è il
login Clerk, che protegge le tre sezioni e rende disponibile l'identità su tutta
l'app. Il secondo riserva /trading e /api/trading/* agli amministratori: il
middleware confronta il claim email del token di sessione Clerk con l'elenco
separato da virgola in NEXT_PUBLIC_ADMIN_EMAIL (vedi src/lib/admin.ts); gli
altri utenti vengono rimandati in home e non vedono la voce Trading. Il terzo
è lo sblocco a cookie preesistente, che resta a guardia della sola esecuzione
anche per l'amministratore, perché lì passano ordini reali.
Il ponte con Convex passa dal template JWT convex creato nella dashboard Clerk:
il suo issuer va nella variabile CLERK_JWT_ISSUER_DOMAIN del deployment Convex
(vedi convex/auth.config.ts), mentre le chiavi pubblica e segreta stanno nelle
variabili d'ambiente di Next (vedi .env.example). Il template JWT convex deve
includere il claim email ({{user.primary_email_address}}), altrimenti
requireAdmin lato Convex non può confrontare l'email (fallback:
ADMIN_CLERK_SUBJECT). Nel layout radice ConvexProviderWithClerk fa viaggiare
il JWT con ogni query reattiva del browser. Il menu utente mostra nome e foto e
apre un pannello per modificarli o disconnettere la sessione.
Market intelligence
In convex/ vive anche il layer di raccolta dati di mercato: cron di ingestione
(crons.ts, ingest.ts) che leggono periodicamente da Hyperliquid, Coinalyze
(aggregati cross-exchange: open interest, funding, liquidazioni) e Coin Metrics
(metriche on-chain), tramite i provider in convex/providers/. L'ultimo valore
per fonte, asset e metrica sta in marketLatest (upsert, dimensione fissa), lo
storico orario e giornaliero in marketSeries con pruning per età, le run in
ingestRuns per l'osservabilità (inclusa diagnostica di degrado parziale senza
marcare tutta la run come fallita). È dato pubblico di mercato, volutamente
globale e non partizionato per utente o portafoglio. Lo legge il chatbot della
sezione Trading con l'azione market_intel, instradata dal MarketDataRouter
di src/lib/marketData (etichette e formati dei numeri unificati in
format.ts), che per i dati aggregati attinge solo allo storico raccolto e
dichiara sempre fonte ed età di ogni numero. Il pannello MarketIntelPanel
esiste ma al momento non è montato: la pagina Trading mostra solo chat e
posizioni. Gli asset tracciati e la retention si regolano con le
variabili documentate in .env.example (TRACKED_ASSETS, RETENTION_1H_DAYS,
COINALYZE_API_KEY).
In sospeso (luglio 2026): il watcher di spike di volume (convex/volumeWatch.ts)
dipende oggi in modo rigido da Coinalyze, la stessa fonte di OI/funding/liquidazioni
sopra: senza COINALYZE_API_KEY la run si salta silenziosamente. Dopo l'attivazione
di una chiave nuova le chiamate hanno preso 429 in modo persistente per oltre 14 ore
nonostante il traffico minimo (solo il cron ogni 5-15 minuti), ben sotto il limite
dichiarato di 40 richieste/minuto: sospetto legato al burst di chiamate di verifica
fatto subito dopo l'attivazione, o a una limitazione a monte non visibile dal
pannello utente (che non mostra contatori d'uso). Prossimo passo non ancora
eseguito: rigenerare la chiave su coinalyze.net/account/api-key/ e testarla SOLO con
il cron naturale, senza nessuna chiamata manuale di verifica. Se il blocco si
ripresenta identico, convex/providers/laevitas.ts è già pronto come fallback
per il solo volume watcher (stesso tipo VenueCandles[], buy/sell volume
confermato a 5 minuti su Binance/OKX/Bybit/Deribit/Hyperliquid contro la
documentazione Laevitas), ma non è collegato a nessun cron né attivato: prima di
attivarlo vanno verificati contro l'API live con una chiave reale sia l'euristica
che riconosce i perpetual nel catalogo (isPerpetualInstrument, oggi validata solo
sugli esempi della documentazione) sia il prefisso /api/v1 nel path di base. Non
sostituirebbe comunque il job di OI/funding/liquidazioni aggregati, che resta su
Coinalyze finché non si verificano gli endpoint Laevitas equivalenti.
Lo scaffolding (dipendenza, schema, funzioni) è in repository. Il collegamento a
Convex avviene tramite l'integrazione Convex su Vercel: quella mette da sola su
Vercel la chiave di deploy in Production, e il buildCommand in vercel.json
(convex deploy --cmd 'bun run build') fa sì che ogni build di produzione
pubblichi prima il backend Convex (schema e funzioni, generando convex/_generated)
e poi costruisca il sito, con NEXT_PUBLIC_CONVEX_URL già disponibile. Sulle
Preview, se CONVEX_DEPLOY_KEY manca, la build fa solo bun run build (niente
deploy Convex da branch): lo schema live resta quello di produzione, aggiornato
al merge su main. Non serve il
login locale: quel comando lo si usa solo per sviluppare Convex sul proprio Mac
(bunx --bun convex dev).
main è il ramo di produzione: ogni push avvia automaticamente un deploy Vercel
in produzione, compreso il deploy Convex eseguito dal comando di build.
Le operazioni destinate a chstrategy.xyz, inclusi catalogo tool/docs, code e
dati Trading, usano sempre il deployment Convex di produzione; quello di
sviluppo si usa solo su richiesta esplicita per prove locali.
Attenzione all'ordine: il primo deploy con questo buildCommand funziona solo dopo
che l'integrazione Convex ha messo la sua chiave su Vercel, altrimenti la build
fallisce cercandola. Il segreto CHAT_WRITE_SECRET va impostato in due posti con lo
stesso valore, sulle variabili d'ambiente di Vercel e su quelle del deployment
Convex. Imposta anche ADMIN_EMAIL e, come ripiego opzionale,
ADMIN_CLERK_SUBJECT sul deployment Convex. Entrambe accettano più valori
separati da virgola; ADMIN_EMAIL deve restare allineata con
NEXT_PUBLIC_ADMIN_EMAIL. Servono a requireAdmin per coda e classify di
/trading/tools-docs. La route
src/app/api/trading/parse/route.ts salva ogni turno via le funzioni
conversations/messages come scrittura best-effort, che non fa mai fallire la
chat né il percorso di invio ordini, e allega a ogni conversazione l'utente Clerk
e il portafoglio selezionato. Il conto della conversazione diventa contesto per
le letture successive solo dopo che il server lo ha verificato da testo, dati o
tool; il primo conto configurato non vale come scelta implicita dell'utente.
Manutenzione dello stack e canali da monitorare
L'infrastruttura del trading dipende da componenti esterni che evolvono in fretta,
quindi conviene sapere dove guardare quando qualcosa cambia. La fonte di verità
dei link ufficiali da tenere sotto traccia è il catalogo Convex toolDocs
(pagina admin /trading/tools-docs, seed in convex/toolDocsSeed.ts): homepage
o getting started di Hyperliquid, NEAR Intents, Coinalyze, Coin Metrics, OpenRouter,
Finnhub, Convex, Clerk, CCXT, Laevitas (fallback), ecc. Quando si integra un tool
nuovo, il link docs entra in coda, passa il classify, e se ammesso resta nel
catalogo con implemented e note di fit.
L'SDK Hyperliquid TypeScript che firma e legge tutto è
@nktkas/hyperliquid: le sue
release su GitHub e la
pagina npm dicono quando esce
una versione nuova, da confrontare con quella fissata in package.json e nel
lockfile. Le regole di mercato, gli endpoint e il comportamento dei dex builder
vivono nelle Hyperliquid docs,
in particolare le pagine su
HIP-3
e sulle HIP-3 deployer actions,
che è anche dove verificare periodicamente le note curate in BUILDER_NOTES
(chi c'è dietro un deployer). Questa regola vale per ogni componente futuro,
non solo per Hyperliquid: ogni nuova venue dietro VenueAdapter (Aster, Lighter,
Uniswap su Unichain, ...) porta con sé le proprie fonti, SDK con release e pagina
npm, docs ufficiali, e le aggiunge al catalogo toolDocs con la stessa logica,
versione pubblicata contro versione in uso. Lo stesso se l'agent wallet passerà
da un MCP dedicato: il repository di quel server entra nella lista.
Perché il controllo non dipenda dalla memoria di qualcuno, è prevista una
routine schedulata settimanale (stack-watch, vedi la skill /schedule) che
farebbe una ricerca web su queste fonti (a partire dal catalogo), confronterebbe
le versioni pubblicate con quelle del lockfile, eseguirebbe bun audit per le
vulnerabilità note e riporterebbe solo i delta, con i link diretti. È ancora
una bozza in pausa, da rivedere e aggiornare insieme al resto del codice in
lavorazione prima di riattivarla; quando sarà attiva, l'aggiornamento resterà
comunque un intervento manuale (bun add, lettura del changelog, test), mai
automatico su codice che firma ordini reali.
Contro i pacchetti npm compromessi il repository ha due difese committate:
bunfig.toml impone minimumReleaseAge di 7 giorni, così un'installazione
rifiuta versioni appena pubblicate (quasi tutti gli attacchi supply-chain
vengono scoperti e rimossi entro pochi giorni), e trustedDependencies in
package.json limita gli script postinstall ai soli pacchetti che ne hanno
davvero bisogno; tutto il resto viene installato senza poter eseguire codice.
Stato attuale
Il motore Python del trading è costruito, testato e utilizzabile da terminale:
anteprima dry-run, esecuzione reale con triplo controllo, lettura di margine e
ordini aperti, indicatore OBV e posizioni su altri exchange via ccxt. L'app web
ha la homepage, le tre dashboard e, nella sezione Trading, un agente OpenRouter
che usa gli strumenti server per leggere dati e preparare azioni. Le azioni
confermate nella web app possono partire realmente quando le credenziali di firma
della venue sono configurate; LIVE_TRADING resta solo la seconda conferma del
motore Python indipendente avviato con --live. Le piattaforme collegate sono
Hyperliquid, OKX, KuCoin e Lighter,
tutte in lettura ed esecuzione,
dietro l'interfaccia VenueAdapter in src/lib/execution/venues: ogni
piattaforma espone risoluzione asset, dati di mercato, stato del conto ed esecuzione
dietro la stessa forma (divisa in VenueReader e VenueExecutor, credenziali come
unione discriminata per venue), e l'account porta con sé la sua venue nell'elenco
degli account attivi, nel token di autorizzazione e nelle conversazioni salvate.
L'intenzione è aggiungere Aster e Uniswap su Unichain come nuovi adapter,
riusando immutati il motore di rischio e il flusso anteprima più consenso.
Aggiungere una venue. L'id del conto non ha più forma imposta: è una
stringa e a giudicarla è l'adapter (isValidAccountId), quindi convivono un
indirizzo 0x di Hyperliquid e un'etichetta di conto come main su OKX/KuCoin. I
conti si dichiarano una venue per volta, <VENUE>_ACCOUNTS (HYPERLIQUID_ACCOUNTS,
OKX_ACCOUNTS, KUCOIN_ACCOUNTS, LIGHTER_ACCOUNTS), così collegare una piattaforma non obbliga mai a riscrivere la
configurazione di un'altra; TRADING_ACCOUNTS, lista unica di voci
venue:label:id, resta come scorciatoia e ha la precedenza quando c'è. KuCoin
usa api.kucoin.com (conto global); le function Vercel girano su fra1 per
evitare il blocco geografico 400302. Override host con KUCOIN_API_BASE /
KUCOIN_FUTURES_API_BASE solo se il conto è sul sito EU. Le
piattaforme che si autenticano con chiave API partono da
src/lib/execution/venues/keyedRest.ts, che risolve le credenziali dalle env per
convenzione (<VENUE>_API_KEY, _API_SECRET, _API_PASSPHRASE, _API_BASE),
firma in HMAC-SHA256, costruisce la query e normalizza gli errori: è la famiglia
più numerosa del catalogo tools docs (QFEX, Variational, Propr, Robinhood,
Alpaca). Lighter usa invece le API ufficiali per le letture e un servizio Python
isolato basato sull'SDK ufficiale per firmare ordini. Chi firma con un wallet (Blink, Nado, RISEx,
tradeXYZ) è un'altra famiglia e avrà una base propria.
Gli spostamenti di capitale sono due caratteri distinti, non due facce dello stesso adapter.
Intravenue (stesso conto, sezioni interne tipo Funding ↔ Trading): API nativa tipo asset/transfer, capability in src/lib/execution/transfers/internal.ts (canInternalTransfer). Oggi solo OKX. La chat usa propose_internal_transfer e non deve fare if (venue === "okx").
Intervenue (esce dalla venue): API nativa tipo withdrawal, rotte con indirizzi già verificati, cooldown, limiti e Ledger sopra 50 USD. Registry in src/lib/execution/transfers/registry.ts, chat propose_capital_transfer.
Lighter espone un conto unified: total_asset_value è l'equity corrente già
comprensiva del PnL non realizzato, available_balance è la liquidità operativa
e cross_initial_margin_requirement è l'IMR cross. L'adapter usa questi campi
una sola volta e aggiunge allocated_margin soltanto per le posizioni isolate;
collateral non va sommato di nuovo a total_asset_value.
Tre differenze di modello che l'adapter deve assorbire, e che OKX mostra tutte.
Su un CEX anche leggere il conto è autenticato (l'adapter risolve da solo le
chiavi di lettura dalle env, le credenziali di firma restano fuori dal tipo
VenueReader). Le size dei perpetual si contano in contratti, non nell'asset,
quindi la conversione va fatta dentro la cartella della venue perché fuori i
numeri restino confrontabili tra piattaforme; nel senso opposto, un piano
approvato dal motore di rischio si converte in contratti troncando sempre per
difetto, perché arrotondare per eccesso manderebbe a mercato una size diversa
da quella confermata. E gli id degli ordini hanno forma propria per venue
(numeri su Hyperliquid, stringhe di 18 cifre su OKX, che un numero JavaScript
non rappresenta esattamente): a normalizzarli è parseOrderId dell'adapter,
come isValidAccountId fa per gli id dei conti.
Chi può firmare. Il conto operativo è uno per piattaforma
(operativeAccountFor), non uno solo in assoluto: le credenziali di una venue
non autorizzano nulla sull'altra. Su Hyperliquid resta HYPERLIQUID_ACCOUNT_ADDRESS;
sulle altre è il conto configurato, ma solo se ce n'è esattamente uno, perché
con due conti dietro le stesse chiavi non si potrebbe stabilire dove atterrerebbe
l'ordine. Tutti gli altri account restano di sola lettura, e la route execute
verifica che la venue del token coincida con quella del conto operativo che porta
quell'id.
Le connessioni operative usano chiavi con permessi di lettura e trading. Per OKX la stessa chiave può ricevere anche i permessi Transfer e Withdraw e servire il sottosistema trasferimenti; una chiave separata Read + Withdraw resta supportata come override più isolato. In entrambi i casi i prelievi restano confinati alle rotte verificate.
Trasferimenti di capitale
La pagina /trading/transfers e il chatbot possono spostare esplicitamente USDC
fra OKX Funding e OKX Trading, in entrambe le direzioni. L'azione usa
POST /api/v5/asset/transfer con tipo main-account, controlla il saldo della
sezione di origine, richiede una conferma monouso e legge poi lo stato nativo del
trasferimento. Non viene mai concatenata automaticamente a un ordine o a un
prelievo. Il gate asset_transfer resta spento per default. Sopra 50 USD serve
una firma Ledger e quindi almeno un wallet verificato nel portafoglio.
La pagina /trading/transfers gestisce la prima rotta: OKX Funding → Rabby/Ledger
verificato oppure OKX Funding → Lighter, in USDC su Arbitrum. Non esiste un
campo indirizzo libero. Ogni destinazione nasce da una firma del wallet, resta in
cooldown per 24 ore e viene richiamata solo tramite id interno. Lighter deriva
l'indirizzo di deposito dall'API ufficiale e lo lega allo stesso wallet L1.
La rotta account-to-account OKX Funding → Lighter e' disponibile anche dal chatbot: l'utente indica origine, destinazione e importo, poi autorizza la proposta nel normale blocco di conferma. Entrambi gli estremi vengono risolti contro i conti collegati al portafoglio e la chat non accetta indirizzi o identificativi esterni. Le altre coppie di venue restano bloccate finche' non esiste un provider e una rotta verificata specifici.
Ogni anteprima include rete, fee e indirizzo finale, viene firmata in un token
monouso e viene riletta prima dell'invio. Fino a 50 USD basta la conferma API;
sopra 50 USD serve anche una firma Ledger sul piano esatto. I limiti predefiniti
sono 50 USD per trasferimento, 100 USD al giorno e 10 minuti tra invii ripetuti.
Il gate nativo OKX asset_withdrawal resta spento per default. Il dry-run non
invia fondi. Le direzioni inverse e le rotte Hyperliquid restano in roadmap.
Configurazione Next/Vercel:
OKX_API_KEY
OKX_API_SECRET
OKX_API_PASSPHRASE
LIGHTER_ACCOUNTS
LIGHTER_L1_ADDRESS
LIGHTER_API_PRIVATE_KEY
LIGHTER_ACCOUNT_INDEX
LIGHTER_API_KEY_INDEX
LIGHTER_SIGNER_SECRET
La chiave operativa OKX deve avere Read, Trade e Transfer per gli spostamenti
Funding ↔ Trading, e anche Withdraw quando viene riusata per i prelievi esterni.
In alternativa OKX_WITHDRAWAL_API_KEY,
OKX_WITHDRAWAL_API_SECRET e OKX_WITHDRAWAL_API_PASSPHRASE definiscono un
override separato Read + Withdraw, preferito automaticamente quando presente.
La chiave attiva va limitata agli IP del runtime quando OKX lo permette. Il
signer Lighter gira come Python Function nello stesso progetto Vercel e usa
LIGHTER_API_KEY_INDEX tra 4 e 254.
LIGHTER_SIGNER_URL è un override locale opzionale: in produzione l'endpoint
deriva automaticamente da VERCEL_URL. Il signer non usa né chiede la chiave
del wallet L1 o del Ledger. Avvio locale:
cd engine
source .venv/bin/activate
python -m src.lighter_signer_service
La route /api/lighter_signer richiede il bearer secret e accetta soltanto
token di lettura, leva, creazione, modifica e cancellazione ordini. L'API key
Lighter può tecnicamente firmare anche prelievi, ma la route non espone né
inoltra depositi, prelievi o trasferimenti. Le quattro variabili privilegiate
restano solo su Vercel e non vanno duplicate su Convex.
Il consenso in chat accetta risposte naturali brevi soltanto quando esiste una
proposta attiva; negazioni e frasi che chiedono modifiche tornano invece al
chatbot. Il token firmato resta monouso e l'esito viene salvato nello stesso
thread Convex. Gli id ordine viaggiano nel tool come testo, così le 18 o 19 cifre
di OKX non vengono arrotondate da JavaScript. Gli helper OpenRouter
(src/lib/agent/) sono condivisi tra chat trading e classificatore tools-docs.
I 400 generici di un provider OpenRouter ricevono un retry limitato e i log
estraggono la causa reale da metadata.raw; anche l'eventuale risposta di errore
viene salvata nel thread, così la cronologia non nasconde i turni falliti.
Le correzioni interne di una risposta restano istruzioni di sistema anche sui
modelli Anthropic: non vengono aggiunte alla conversazione come falsi messaggi
dell'utente.
Per una nuova entrata perpetual, stop loss e take profit sono opzionali. Entrano nel piano firmato e vengono inviati alla venue soltanto se l'utente li ha chiesti o ha accettato esplicitamente quei livelli. L'anteprima rende visibile quando lo stop non è specificato e non calcola una perdita allo stop o un rapporto R:R con dati assenti. La regola 2 del Regolamento Interno resta un riferimento da citare, non un ordine di protezione implicito.
Stop loss e take profit su una posizione già aperta hanno un flusso dedicato: il server rilegge posizione, size, mark e ordini condizionali, blocca duplicati e livelli già oltre il mark, poi firma soltanto uscite reduce-only. Questa azione non crea una nuova entrata e non cambia la leva.
Quando una richiesta ridimensiona o riprezza più ordini limite già aperti, la chat prepara un unico piano coordinato fino a 20 righe e chiede un solo consenso. Il server ricava dal messaggio l'eventuale obiettivo monetario, rifiuta se il modello lo omette o lo cambia, rilegge l'intero gruppo prima della prima modifica e impedisce aumenti sia della quantità sia del notional. Size e prezzo vengono allineati ai passi reali della venue prima dell'anteprima e del token. OKX e Hyperliquid usano la modifica nativa dell'ordine, compresi gli ordini spot OKX, senza passare dalla coppia fragile cancellazione più reinserimento. Se una venue rifiuta una singola riga, l'esito distingue quelle riuscite da quelle fallite, calcola la liquidità stimata sulle sole righe riuscite e riporta a parte la variazione osservata del capitale disponibile dopo l'operazione.
L'identità del chatbot vive in src/lib/tradingAgent/chatbot-soul.md, le regole
di scrittura in src/lib/tradingAgent/style.md e il Regolamento Interno completo
in src/lib/tradingAgent/regolamento-interno.md. Il prompt contiene sempre
identità, stile e indice delle dieci regole; lo strumento read_regolamento
carica solo la regola pertinente quando serve. soul.md alla radice descrive
invece il progetto e non entra nel prompt. Sui modelli Anthropic il prefisso
stabile usa il prompt caching, cioè il riuso della parte invariata, per ridurre
il costo dei turni con più strumenti.
Il login Clerk è attivo: le tre sezioni richiedono l'accesso, la sezione Trading è riservata agli amministratori, ogni utente ha i suoi portafogli su Convex e li sceglie dal selettore all'ingresso delle sezioni, e le conversazioni della chat nascono partizionate per utente e portafoglio. I conti di trading del portafoglio attivo restano tutti visibili insieme, senza selettore. Il pannello può aggregarli, mentre ogni lettura privata del chatbot risolve un solo conto dichiarato dall'utente e chiede una scelta quando il contesto è ambiguo. L'esecuzione reale resta confinata all'account operativo configurato sul server.
Il pannello unisce tutte le venue in due tabelle, una per le posizioni e una per gli ordini aperti. In cima mostra liquidità disponibile, valore complessivo e margine impiegato; un clic sulla cifra apre per tutti e tre la ripartizione per venue e comparto. Ogni riga riporta venue, tipo di strumento e prezzo mark. Nelle posizioni, la direzione include la leva e una colonna separata mostra il margine impiegato in USD. Gli ordini mostrano anche il proprio controvalore in USD. Entrambe le tabelle partono dal controvalore maggiore e permettono di scegliere altri ordinamenti pertinenti. Gli ordini condizionati sono descritti come take profit, stop loss o entrata e mostrano la distanza dal prezzo corrente. La stessa classificazione nativa viene passata al chatbot; quando la venue espone solo un trigger generico, la chat lo descrive come uscita condizionale senza indovinare se sia take profit o stop loss. La piccola X in fondo a destra di ogni riga permette di annullare l'ordine dopo un controllo esplicito dell'utente; il server verifica di nuovo portafoglio, account operativo e ordine aperto prima di firmare la cancellazione. Hyperliquid si aggiorna anche via websocket, mentre le altre venue usano la lettura REST e il pulsante comune.
Ogni adapter dichiara gli scope del conto con id ed etichetta e restituisce uno snapshot normalizzato con valore, liquidità, margine e sezioni patrimoniali. La costruzione dei tre indicatori è comune: una nuova venue registrata entra automaticamente nei totali e nei popup senza condizioni specifiche nella route. I comparti che richiedono trasferimento o riscatto contano nel valore, ma non nella liquidità immediatamente utilizzabile.
Sopra il chatbot i badge delle trading platform, oggi DEX e CEX e in futuro
anche broker, sono raggruppati per stato: verde se account e firma sono pronti,
giallo per sola lettura o configurazione parziale, rosso se il collegamento è
ancora previsto. La rotta legge dal catalogo Tool docs tutte le CEX, DEX,
integrazioni DeFi e gli aggregatori con un ruolo di esecuzione; le voci
PLANNED_VENUES restano la roadmap locale e il ripiego se il catalogo non è
disponibile. Il link Tools docs sta nell'header della sezione.
Le conversazioni passate vivono in una barra laterale a sinistra della chat, come
nelle interfacce LLM correnti: un clic riapre la conversazione e la si prosegue da
lì, mentre il cestino elimina la conversazione e i suoi messaggi. Un comando
nell'intestazione espande la chat a tutto schermo e permette di tornare alla
vista normale, anche con Esc. I tabulati tecnici prodotti dagli strumenti non
vengono mostrati: restano nel contesto della conversazione per permettere al
chatbot di sintetizzarli e rispondere alle domande successive. I cron di raccolta
dati di mercato e account snapshots girano; il chatbot risponde sui dati
aggregati e on-chain storicizzati con market_intel e sulle letture di conto da
Convex con paracadute live. La pagina Trading mostra la chat, i suggerimenti
rapidi e il pannello di posizioni e ordini.
Per avviare l'app web in locale:
bun install
cp .env.example .env.local # compila OPENROUTER_API_KEY, ENGINE_DIR, ENGINE_PYTHON, le chiavi Clerk e NEXT_PUBLIC_ADMIN_EMAIL
bun dev
Per provare il motore di trading:
cd engine
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
python -m src.main --plan plans/spcx_short.example.yaml --offline
pytest