Un ordine dall'esito ignoto non viene più abbandonato: entra in data/state/pending_orders.json prima della chiamata HTTP, l'esito si legge per orderId (il server non registra il referenceId degli ordini v2) e in mancanza si ricostruisce dalla posizione comparsa sul conto. Una gamba senza esito porta il basket in PendingA/PendingB invece di rifiutarlo; alla risoluzione parte la gamba B, ridimensionata sulle unità eseguite, oppure la gamba A viene richiusa. Ogni posizione del conto è classificata basket / orfana-bot / esterna: le orfane del bot vengono adottate e chiuse, le esterne contate e mai toccate. Il picco di equity ignora i movimenti di cassa. Bonifica da headless, orders.jsonl, contatori in dashboard, bandit che propone e non applica. ADR-0009, test (m)-(q). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
11 KiB
Fonti dei dati
Aggiornato: 2026-09-16. Ogni fonte è stata verificata alla data indicata; se un feed cambia o sparisce, la voce va aggiornata e la decisione annotata in docs/QUESTIONS.md.
1. Mercato
| Fonte | Cosa | Formato | Frequenza | Limiti | Fallback |
|---|---|---|---|---|---|
eToro Public API — GET /api/v2/market-data/rates?instrumentIds=… |
bid/ask di tutti gli strumenti in una chiamata | JSON {results:[{instrumentId,bid,ask,date,quoteType}]} |
polling ogni run.pollSeconds (3 s) |
quota condivisa 120/min con le altre rotte di market data; il bot ne usa ~20/min | nessuno: senza quote il bot non decide |
eToro Public API — GET /api/v1/market-data/instruments/{id}/history/candles/asc/FifteenMinutes/1000 |
ultime 1000 candele M15 (mid, senza spread) | JSON | all'avvio, per il riscaldamento e il delta | non pagina: niente data di partenza, al massimo ~10 giorni | le barre locali salvate a ogni chiusura |
eToro Public API — GET /api/v2/market-data/instruments?symbols=…, POST /trading/info/{demo/}eligibility |
id, nome, esposizione minima, leve, limiti di stop | JSON | all'avvio, scritti in instruments.json |
120/min e 20/min dedicate | valori prudenti incorporati (esposizione minima 1000 USD, leve 1-20) |
eToro Public API — POST /trading/info/{demo/}costs |
markup, spread di mercato, commissione, overnight e weekend per un ordine ipotetico | JSON {costs:[{costType, currency, value}]} — il campo è value (verificato il 2026-09-16: EURUSD 10 000 unità → markup 0, marketSpread 0,1 USD, overnightFee 0,91 USD/giorno) |
ogni 15 minuti per strumento | 20/min dedicate | markup 0 e overnight da strategy.json |
Tick MetaTrader 5 — A:\Download\Trading\<SYMBOL>_<da>_<a>.csv |
tick bid/ask 2018-12-12 → 2026-09-15, UTC (verificato sui fine settimana: chiusura venerdì 20:53 estate / 21:57 inverno, riapertura domenica 21:05 / 22:05) | tab-separato <DATE> <TIME> <BID> <ASK> <LAST> <VOLUME> <FLAGS>; flag 2 = solo bid, 4 = solo ask, 6 = entrambi |
una tantum, backtest ticks |
EURAUD copre solo parte del 2018, del 2021 e del 2026 (39 615 barre contro ~192 000 delle altre): il basket EURAUD/AUDCAD è misurabile solo su quei tratti | — |
Barre M15 derivate — Documenti\Encelado\data\market\candles_<SYMBOL>_M15.csv |
OHLC bid e ask, spread medio, numero di tick, provenienza | CSV ; (schema in docs/LEDGER_SCHEMA.md) |
scritte dallo strumento e aggiornate dal bot a ogni barra chiusa | — | — |
Qualità (data/market/data_quality.csv, generato da backtest ticks, e reports/data_quality.csv dal bot): buchi > 1 h nei giorni feriali, salti > 2 % fra barre, duplicati. Una barra sospetta sospende le decisioni sul basket coinvolto per quella barra.
1.1 Ordini e posizioni (rotte verificate il 2026-09-23 sull'OpenAPI v1.379.0 e con chiamate reali sul conto demo)
| Rotta | Uso | Note verificate |
|---|---|---|
POST api/v2/trading/execution/{demo/}orders |
invio dell'ordine (action open, transaction buy/sellShort, orderType mkt, units, leverage, stopLossRate) |
risponde 200 con orderId; il server lavora l'ordine in modo asincrono. Non registra l'x-request-id come riferimento: la lettura per orderId di un ordine del bot mostra referenceID = 00000000-0000-0000-0000-000000000000. Quota 20/min condivisa con chiusure e cancellazioni. |
GET api/v2/trading/info/{demo/}orders:lookup?orderId=<id> |
esito dell'ordine, con le posizioni prodotte (positionExecutions[].positionId, openingData.avgPrice, units, executionTime, fees) |
è la chiave usata dal bot. requestedUnits/requestedAmount possono differire dalle unità inviate: il 2026-09-16 il server ha ridotto gli ordini a 2 000 USD di margine (frozenAmount 2000, unità a sei decimali ricalcolate). Quota 60/min condivisa con close-orders/{id} e orders/{id}. |
GET api/v2/trading/info/{demo/}orders:lookup?referenceId=<x-request-id> |
ripiego quando la risposta al POST è andata persa |
404 per gli ordini v2 del bot (vedi sopra). |
GET api/v1/trading/info/{demo/}orders/{orderId} |
ripiego per orderId con la risposta v1 (statusID, errorCode, positions[] {positionID, rate, units, occurred, isOpen}) |
verificato con l'ordine 381739181. |
DELETE api/v2/trading/execution/{demo/}orders/{orderId} |
cancellazione di un ordine non ancora eseguito (kill-switch) | 200 = richiesta accettata, non annullamento avvenuto: confermare con il lookup (7 o 9 = annullato, 6 = in corso). Idempotente su ordini già chiusi. |
GET api/v1/trading/info/{demo/}pnl |
conto e posizioni in una chiamata: clientPortfolio.credit, bonusCredit, unrealizedPnL, positions[] {positionID, instrumentID, isBuy, units, openRate, openDateTime, amount (margine), leverage, unrealizedPnL.pnL, totalFees} |
equity = credit + bonus + Σ amount + unrealized; available = credit + bonus; usedMargin = Σ amount. Il conto demo non compare in api/v1/balances (solo i conti reali). |
GET api/v1/trading/info/trade/{demo/}history?minDate=…&page=…&pageSize=200 |
posizioni chiuse: positionId, orderId, openRate, closeRate, openTime, closeTime, netProfit, fees, investment |
netProfit non include fees. Fonte del realizzato della scheda Storico e della distinzione fra chiusure e movimenti di cassa. |
Stati dell'ordine (status.id / statusID): 1 Received, 2 Placed, 3 Filled, 4 Rejected, 5 PartiallyFilled, 6 PendingCancel, 7 Canceled, 8 Expired, 9 CanceledPartiallyFilled, 10 RejectedPartiallyFilled, 11 WaitingForMarket, 12 PendingTriggeredRate. Il bot tratta 3 e 5 come eseguito, 4, 7, 8, 9, 10 come rifiutato/annullato, 1, 2, 6, 11, 12 come in corso; in assenza di risposta lo stato è Unknown e l'ordine resta nel registro. Esiste anche POST api/v3/trading/execution/{demo/}orders (202, stessa semantica, settlementType obbligatorio): non usato, annotato per il futuro.
2. Calendario economico
| Fonte | URL | Formato | Aggiornamento | Note |
|---|---|---|---|---|
| Forex Factory via FairEconomy | https://nfs.faireconomy.media/ff_calendar_thisweek.json |
JSON [{title,country,date,impact,forecast,previous,actual}], date con offset (ora di New York) |
il feed cambia più volte al giorno; il bot lo rilegge ogni 10 minuti, mai più di una richiesta al minuto | country è già il codice valuta (USD, EUR, GBP, JPY, AUD, NZD, CAD, CHF, CNY, All); impact ∈ {High, Medium, Low, Holiday} |
| variante XML | https://nfs.faireconomy.media/ff_calendar_thisweek.xml |
<weeklyevents><event> con date MM-DD-YYYY e time 8:15am in UTC (verificato contro il JSON: "10:30pm" del 09-13 = "18:30-04:00") |
idem | usata solo come riserva |
Archivio: data/calendar/events.jsonl (append-only, una riga per evento, dedup per title+date+country; un actual che arriva dopo la pubblicazione è una riga nuova). Feature derivate per ogni valuta: minutesToNextHigh, minutesSinceLastHigh, surpriseLast = (actual − forecast)/|forecast|.
Limite: il feed copre la settimana corrente. Non esiste uno storico gratuito: il backtest non applica il blackout né le feature di calendario, e lo dice (docs/STRATEGY.md).
3. Notizie (RSS)
Tutte lette con User-Agent: Encelado/4.0 (+correlation baskets; contact: operator), al massimo una richiesta al minuto per fonte, con backoff esponenziale sugli errori e rispetto di robots.txt (gruppo User-agent: *). Verifica del 2026-09-16:
| Fonte | URL | Formato | Esito |
|---|---|---|---|
| FXStreet | https://www.fxstreet.com/rss/news |
RSS 2.0 | 200 |
| ForexLive | https://www.forexlive.com/feed/ |
RSS 2.0 | 200 |
| Federal Reserve | https://www.federalreserve.gov/feeds/press_all.xml |
RSS 2.0 | 200 con lo User-Agent del bot; con uno User-Agent minimale risponde con una pagina HTML "not found" |
| BCE | https://www.ecb.europa.eu/rss/press.html |
RSS 2.0 | 200 |
| Bank of England | https://www.bankofengland.co.uk/rss/news |
RSS 2.0 | 200 |
| RBA | https://www.rba.gov.au/rss/rss-cb-media-releases.xml |
RSS 1.0 (RDF) | 200 alla prima verifica, poi "Access Denied" (Akamai) a richieste successive: tenuta con backoff, coperta anche da Google News "Reserve Bank of Australia" |
| Bank of Canada | https://www.bankofcanada.ca/content_type/press-releases/feed/ |
RSS 1.0 (RDF) | 200 |
| SNB | https://www.snb.ch/en/rss/press-releases |
— | 404: omessa (D-08); coperta da Google News "Swiss National Bank" |
| RBNZ | https://www.rbnz.govt.nz/rss/news |
— | 403 "website unavailable": omessa (D-08); coperta da Google News RBNZ |
| Google News | https://news.google.com/rss/search?q=<query>&hl=en-US&gl=US&ceid=US:en per EURUSD, "Swiss National Bank", RBNZ, forex dollar |
RSS 2.0 | 200 |
Archivio: data/news/news_YYYYMM.jsonl (append-only, una riga per item, dedup per hash(link)), con published, source, title, summary, link, currencies, scores{net, hawkish, riskOff}.
Sentiment senza librerie (Encelado.Core/News/SentimentLexicon.cs, SentimentEngine.cs): lessico incorporato in tre dimensioni (tono positivo/negativo ~180 termini ciascuno, hawkish/dovish ~80, risk-on/risk-off ~50), negazione a finestra di tre parole, attribuzione alle valute per entità (Fed, Powell, FOMC → USD; ECB, Lagarde → EUR; BoJ → JPY; RBA → AUD; RBNZ → NZD; BoC → CAD; SNB → CHF; BoE → GBP), parole-paese e nomi di coppia. Per ogni valuta e finestra (1 h, 4 h, 24 h): netSentiment, hawkishScore, riskOff (globale), newsCount, con decadimento esponenziale a emivita 2 h. Le feature di un basket sono le differenze fra le sue due valute non comuni.
Copie dei feed usate dai test: tests/fixtures/ (scaricate il 2026-09-16).
4. Schema dei file in Documenti\Encelado
encelado.json, strategy.json, instruments.json
data/market/candles_<SYMBOL>_M15.csv timeUtc;bidOpen;bidHigh;bidLow;bidClose;askOpen;askHigh;askLow;askClose;spreadMean;ticks;motivazione
data/market/data_quality.csv simbolo;tick_letti;tick_scartati;barre;prima_barra;ultima_barra;buchi_feriali_oltre_1h;barre_spike;spread_mediano_pip;motivazione
data/calendar/events.jsonl {title,country,date,impact,forecast,previous,actual}
data/news/news_YYYYMM.jsonl {hash,published,source,title,summary,link,currencies[],scores{net,hawkish,riskOff}}
data/cache/<fonte>.xml|json ultimo corpo buono di ogni feed
data/ledger/decisions.jsonl vedi docs/LEDGER_SCHEMA.md (rotazione mensile in decisions_YYYYMM.jsonl)
data/ledger/baskets.csv vedi docs/LEDGER_SCHEMA.md
data/ledger/orders.jsonl una riga per ordine inviato e per cambio di stato (5.0)
data/state/baskets_state.json posizioni aperte, ingressi in attesa, picco di equity al netto dei movimenti di cassa, blocchi
data/state/pending_orders.json il registro degli ordini (5.0)
data/state/paper_state.json il conto del simulatore (solo Paper)
data/models/*.json modelli (livelli 1-3) e stato del bandit
knowledge/*.csv, *.md calibrazione, proposte, registri, insight settimanali
reports/*.csv qualità dati, falsificazione, bonifica_YYYYMMDD (5.0)
logs/encelado.log log applicativo (;)