Skip to main content

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-Match 304 στις event/history reads, και keyset cursor pagination (Link: rel="next"), την SSE stream (ζωντανό event: release push, 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/** tree 404s όταν το 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)· next cursor στο body και Link header.
  • 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 → βραχύ JWTbody: clientId, clientSecret
GET /v1/eventsEvents σε ένα παράθυρο (επερχόμενα ή ιστορικά)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-scoreseries,count/from,to
GET /v1/nextΕπόμενο σχετικό release για ένα σύμβολο (χώρα→σύμβολο mapped)symbol,minImpact
GET /v1/blackoutΕίναι ένα σύμβολο μέσα σε ένα παράθυρο υψηλού αντίκτυπου τώρα/στη Tsymbol,at?,minImpact,before,after
GET /v1/affected-symbolsΕπίλυση event → σύμβολα σε μια watchlisteventId,watchlist
POST /v1/events:batchMultiplex αρκετών queries σε μία κλήσηbody: array of queries
GET /v1/stream (SSE)Ζωντανό push: releases/revisions/window-entercurrencies,minImpact (scope calendar:stream)
POST /v1/webhooksΚαταχώρηση HMAC-signed callback για release/revision/blackoutbody: url, filters, secret
GET /v1/healthFreshness + 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.