Calendar REST & cBot API
Το οικονομικό ημερολόγιο εκτίθεται ως ένα versioned, JWT-secured, rate-limited REST API — η
ναυαρχίδα επιφάνεια ενσωμάτωσης. Κάθε εξωτερική υπηρεσία, dashboard ή cBot ενσωματώνεται με αυτό
ως προϊόν. Έχει feature parity με το FXStreet Calendar API και το ξεπερνά: point-in-time asOf,
πλήρεις αλυσίδες αναθεωρήσεων, ντετερμινιστική αιτιολόγηση αντίκτυπου, surprise analytics,
αντιστοίχιση χώρα→σύμβολο, και blackout math που άλλα ημερολόγια API δεν εκθέτουν.
Κατάσταση. Η ασφάλεια JWT (έκδοση client + ανταλλαγή token), η πύλη, και τα βασικά read endpoints —
token,events,events/{id},history,series,surprises,next,blackout,affected-symbols,health— είναι υλοποιημένα και integration-tested (auth, scope enforcement, feature/white-label 404), συνevents/batch(bounded multiplex) και ένα ανακαλύψιμο/openapi.jsonέγγραφο,ETag/If-None-Match304 στις event/history reads, και keyset cursor pagination (Link: rel="next"), την SSEstream(ζωντανόevent: releasepush, poll-backed), HMAC-signed webhooks (X-CMind-Signature: sha256=…, owner-registered, delivered by a config-gated worker off a persisted watermark), και τον typed client (CmindCalendarClient). Η πλήρης δημόσια επιφάνεια API είναι υλοποιημένη.
Ασφάλεια — JWT
Το API επαναχρησιμοποιεί την υπάρχουσα μηχανή HS256 token (το ίδιο μοτίβο που χρησιμοποιούν οι CtraderCliNode agents), όχι ένα νέο σχήμα:
- Ένας app admin εκδίδει ένα Calendar API client (όνομα + scopes + λήξη). Ο client ανταλλάσσει
το id και το secret του στο
POST /api/calendar/v1/tokenγια ένα βραχύβιο HS256 JWT (iss=cmind-calendar,aud=calendar-api,exp~15 λεπτά, scope claim). Μόνο το βραχύ JWT μεταφέρεται σε αιτήματα (Authorization: Bearer <jwt>). - Το client secret αποθηκεύεται κρυπτογραφημένο μέσω
ISecretProtector— ποτέ plaintext, ποτέ logged. - Scopes (ελάχιστο προνόμιο):
calendar:read,calendar:blackout,calendar:surprises,calendar:stream. Ένα cBot token τυπικά παίρνει μόνοread+blackout. - Τυπική
JwtBearerεπικύρωση (issuer, audience, lifetime, signing key·alg=noneαπορρίπτεται· σφιχτό clock skew). Per-client token-bucket rate limit + global limiter·429μεRetry-After. Όλες οι αποτυχίες auth ελέγχονται. - Η απενεργοποίηση του client σταματά αμέσως τη μελλοντική έκδοση token· το βραχύ JWT lifetime
οριοθετεί ένα διαρρεύσαν token. Όλο το
/api/calendar/**tree404s όταν το feature είναι απενεργοποιημένο.
Συμβάσεις
- Base path & versioning:
/api/calendar/v1/...(URL-versioned· αθροιστικές αλλαγές δεν bump). - Format: JSON· RFC 3339 UTC instants συν explicit
sourceTimeZone· προαιρετικόtz=αποδίδει μια τοπική ώρα χωρίς να χάσει το UTC anchor. - Pagination: cursor-based (
cursor,limit≤ 1000)·nextcursor στο body καιLinkheader. - Caching:
ETag+If-None-Match· ιστορικά ranges έχουν μεγάλο TTL, επερχόμενα μικρό. - Errors: RFC 7807
problem+json, ποτέ ένα γυμνό500. - Degraded reads: ένα source/DB fault επιστρέφει
200με τα πιο γνωστά δεδομένα συνX-Calendar-Freshness/stale=trueσήμα (ή503 Retry-Afterμόνο αν πραγματικά τίποτα δεν είναι γνωστό) — το cBot αποφασίζει.
Endpoints
| Method & path | Σκοπός | Βασικές παράμετροι |
|---|---|---|
POST /v1/token | Ανταλλαγή client id+secret → βραχύ JWT | body: clientId, clientSecret |
GET /v1/events | Events σε ένα παράθυρο (επερχόμενα ή ιστορικά) | from,to,countries,currencies,series,minImpact,category,q,asOf,cursor,limit,tz |
GET /v1/events/{id} | Ένα event: πλήρης αλυσίδα αναθεωρήσεων, surprise, αιτιολόγηση αντίκτυπου, επηρεασμένα σύμβολα | watchlist?,asOf? |
GET /v1/events/{id}/revisions | Διατεταγμένο ιστορικό αναθεωρήσεων | — |
GET /v1/history | Βαθιά ιστορική ανάκτηση για μια σειρά (≥10y) | series,from,to,asOf,cursor,limit |
GET /v1/series | Κατάλογος παρακολουθούμενων δεικτών + cadences + πηγή | countries,currencies,q |
GET /v1/surprises | Ιστορική σειρά actual/forecast/surprise z-score | series,count/from,to |
GET /v1/next | Επόμενο σχετικό release για ένα σύμβολο (χώρα→σύμβολο mapped) | symbol,minImpact |
GET /v1/blackout | Είναι ένα σύμβολο μέσα σε ένα παράθυρο υψηλού αντίκτυπου τώρα/στη T | symbol,at?,minImpact,before,after |
GET /v1/affected-symbols | Επίλυση event → σύμβολα σε μια watchlist | eventId,watchlist |
POST /v1/events:batch | Multiplex αρκετών queries σε μία κλήση | body: array of queries |
GET /v1/stream (SSE) | Ζωντανό push: releases/revisions/window-enter | currencies,minImpact (scope calendar:stream) |
POST /v1/webhooks | Καταχώρηση HMAC-signed callback για release/revision/blackout | body: url, filters, secret |
GET /v1/health | Freshness + coverage ανά πηγή | — |
Blackout — το cBot news filter
GET /v1/blackout επιστρέφει { inBlackout, event, startsAt, endsAt, stale }. Σε αβεβαιότητα
προεπιλέγει τη διαμορφωμένη συντηρητική απάντηση (fail-closed εξ ορισμού: "υποθέστε in-blackout"
για risk-off bots), συν ένα stale flag — ένα κενό δεδομένων ποτέ δεν πρασινίζει τις συναλλαγές
μέσω ενός high-impact release. Το endpoint είναι ένα καθαρό DB/cache read με hard server timeout·
δεν υπάρχει σύγχρονη origin fetch στο hot path.
Ένας shipped typed client (Infrastructure.Calendar.CmindCalendarClient) τυλίγει αυτό: στοχεύστε
το HttpClient του στη ρίζα του API, καλέστε GetTokenAsync(clientId, clientSecret) μία φορά, και
μετά GetBlackoutAsync(token, symbol) πριν από κάθε εντολή — είναι fail-safe by construction
(οποιοδήποτε non-success ή parse error επιστρέφει InBlackout = true, Stale = true, οπότε ένα
κενό δεδομένων ποτέ δεν πρασινίζει τις συναλλαγές). Ένα cBot παύει γύρω από ειδήσεις έτσι:
// Pseudocode για cTrader cBot χρησιμοποιώντας WebRequest + Calendar API client token.
var jwt = CalendarApi.GetToken(clientId, clientSecret); // POST /v1/token
var res = CalendarApi.Blackout(jwt, symbol: SymbolName, // GET /v1/blackout
minImpact: "High", before: 15, after: 15);
if (res.InBlackout || res.Stale) // fail-safe: stale ⇒ treat as blackout
return; // skip new entries in the news window
// ...otherwise proceed to place the order
Point-in-time για backtests
Περάστε asOf σε οποιοδήποτε read για να πάρετε το ημερολόγιο ακριβώς όπως ήταν σε μια
παρελθούσα στιγμή — τα actuals, τα forecasts και οι αναθεωρήσεις όπως ήταν τότε. Επειδή
τα asOf reads είναι καθαρά και cacheable, ένα backtest που χτυπά το ιστορικό παίρνει
τα ίδια bytes κάθε φορά, και ένα backtested news rule συμπεριφέρεται ακριβώς όπως το live
(χωρίς look-ahead από αναθεωρημένες τιμές).
Ανθεκτικότητα για algo callers
Το API βρίσκεται σε ένα trading hot path, οπότε ποτέ δεν ρίχνει σε ένα live bot: κάθε
διαδρομή επιστρέφει well-formed problem+json ή ένα typed degraded body. Επαναχρησιμοποιεί
τα resilience primitives της αντιγραφής συναλλαγών — το τυπικό HTTP resilience handler σε
κάθε source client, ένα domain circuit breaker ανά source, ένα lease-guarded singleton
ingestion worker με startup reconciliation, και health checks συνδεδεμένα στο /health. Το
shipped typed client snippet έρχεται με retry + timeout + circuit-breaker προδιαμορφωμένα ώστε
οι bot authors να κληρονομούν resilience.
Sibling: AI currency-strength (market:read)
Το read model AI macro currency-strength χρησιμοποιεί την ίδια
μηχανή JWT — ένα σχήμα, ένα μυστικό υπογραφής, ένας rate-limiter — προσθέτοντας μόνο ένα
market:read scope. Καταχωρήστε ένα API client με αυτό το scope, ανταλλάξτε το για token
ακριβώς όπως παραπάνω, και καλέστε:
GET /api/market/v1/currency-strength/latest?horizon=3M&tier=Majors
GET /api/market/v1/currency-strength/history?days=30
GET /api/market/v1/currency-strength/pair/EUR/USD?horizon=3M
// λάβετε token μέσω POST /api/calendar/v1/token όπως παραπάνω, μετά:
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var view = await http.GetFromJsonAsync<JsonElement>(
baseUrl + "/api/market/v1/currency-strength/latest?horizon=3M");
// view.ranking[], view.forecasts[], view.pairs[] (bias/conviction), view.narrative
Ένα token που λείπει market:read παίρνει 403· ένα expired/tampered token παίρνει 401.
Τα endpoints είναι gated στο AI feature flag και σερβίρονται κάτω από /api/market/v1
ώστε να παραμένουν ανεξάρτητα από το calendar feature gate. Κατά την αποστολή run/backtest
μια deployment μπορεί να εισάγει CMIND_API_BASEURL + ένα βραχύβιο market:read token ώστε
ένα cBot να καλέσει με μηδενική εγγραφή client.