From 004bcead54164f62150df07abb5aa984389d5563 Mon Sep 17 00:00:00 2001 From: Robert Tidball Date: Thu, 9 Jul 2026 12:50:00 +1000 Subject: [PATCH] Add FXMacroData macro data integration --- .../src/services/FXMacroDataService.tsx | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 react/opentp-client/src/services/FXMacroDataService.tsx diff --git a/react/opentp-client/src/services/FXMacroDataService.tsx b/react/opentp-client/src/services/FXMacroDataService.tsx new file mode 100644 index 00000000..3c6a4e4a --- /dev/null +++ b/react/opentp-client/src/services/FXMacroDataService.tsx @@ -0,0 +1,92 @@ +export type FXMacroDataQuery = Record; + +export default class FXMacroDataService { + constructor( + private readonly apiKey?: string, + private readonly baseUrl = 'https://api.fxmacrodata.com/v1', + ) {} + + dataCatalogue(currency: string) { + return this.get(`/data_catalogue/${normalize(currency)}`); + } + + announcements(currency: string, indicator: string) { + return this.get(`/announcements/${normalize(currency)}/${indicator}`); + } + + calendar(currency: string) { + return this.get(`/calendar/${normalize(currency)}`); + } + + predictions(currency: string, indicator: string) { + return this.get(`/predictions/${normalize(currency)}/${indicator}`); + } + + forex(base: string, quote: string) { + return this.get(`/forex/${normalize(base)}/${normalize(quote)}`); + } + + cot(currency: string) { + return this.get(`/cot/${normalize(currency)}`); + } + + commoditiesLatest() { + return this.get('/commodities/latest'); + } + + commodity(indicator: string) { + return this.get(`/commodities/${indicator}`); + } + + curves(currency: string) { + return this.get(`/curves/${normalize(currency)}`); + } + + curveProxies(currency: string) { + return this.get(`/curve_proxies/${normalize(currency)}`); + } + + forwardCurves(currency: string) { + return this.get(`/forward_curves/${normalize(currency)}`); + } + + marketSessions() { + return this.get('/market_sessions'); + } + + riskSentiment() { + return this.get('/risk_sentiment'); + } + + news(currency: string) { + return this.get(`/news/${normalize(currency)}`); + } + + pressReleases(currency: string) { + return this.get(`/press-releases/${normalize(currency)}`); + } + + centralBankers(currency: string) { + return this.get(`/central_bankers/${normalize(currency)}`); + } + + async get(path: string, query: FXMacroDataQuery = {}) { + const response = await fetch(this.url(path, query)); + if (!response.ok) throw new Error(`FXMacroData request failed: ${response.status}`); + return response.json(); + } + + url(path: string, query: FXMacroDataQuery = {}) { + const params = new URLSearchParams(); + if (this.apiKey) params.set('api_key', this.apiKey); + for (const [key, value] of Object.entries(query)) { + if (value !== undefined && value !== null) params.set(key, String(value)); + } + const suffix = params.toString(); + return `${this.baseUrl.replace(/\/$/, '')}${path}${suffix ? `?${suffix}` : ''}`; + } +} + +function normalize(value: string) { + return value.trim().toLowerCase(); +}