Go est de plus en plus populaire pour les outils financiers sa compilation rapide, ses performances prévisibles et ses primitifs de concurrence de première classe en font un ajustement naturel pour les pipelines de données et l'automatisation du trading. Ce guide vous explique tout ce dont vous avez besoin pour appeler l'API REST FXMacroData de Go, d'une simple demande ponctuelle à un client réutilisable qui gère les temps d'arrêt, le décodage JSON et le filtrage de date optionnel. À la fin, vous aurez un code Go idiomatique qui récupère les annonces d'indicateurs macro, tire un calendrier de sortie à venir et interroge les taux spot FX , le tout en moins de 150 lignes.
Ce que vous allez construire
Un client HTTP Go léger qui s'authentifie contre l'API REST FXMacroData en utilisant l'authorisation de la clé API du paramètre de requête, décode les réponses JSON structurées en structures Go typées et expose des fonctions d'assistant réutilisables pour les trois familles de terminaux les plus courantes: annonces, calendrier de sortie et taux de change au comptant.
Pré-requis
- Go 1.21 ou version ultérieure installée (le code de l'appareil est le même)
- Une clé d'API FXMacroData inscrivez-vous à / souscrivez Pour en recevoir un
- Familiarité de base avec les modules Go et la bibliothèque standard
Aucune bibliothèque HTTP ou JSON tierce partie n'est nécessaire la norme net/http Je suis désolé .
encoding/json Les paquets s'occupent de tout.
Étape 1 Comprendre la forme de l'API
Chaque indicateur FXMacroData suit le même schéma d'URL:
GET https://fxmacrodata.com/api/v1/announcements/{currency}/{indicator}?api_key=YOUR_API_KEY
La réponse est un objet JSON avec un niveau supérieur data Chaque élément contient un
date Une chaîne, un chiffre. val, et optionnellement un announcement_datetime
qui donne une précision de deuxième niveau pour quand le chiffre a été officiellement publié.
curl "https://fxmacrodata.com/api/v1/announcements/usd/wages?api_key=YOUR_API_KEY&start=2024-01-01"
{
"data": [
{ "date": "2025-03-14", "val": 4.0, "announcement_datetime": "2025-03-14T12:30:00Z" },
{ "date": "2025-02-07", "val": 4.1, "announcement_datetime": "2025-02-07T13:30:00Z" },
{ "date": "2025-01-10", "val": 3.9, "announcement_datetime": "2025-01-10T13:30:00Z" }
]
}
La même structure d'enveloppe s'applique aux terminaisons calendrier et forex, ce qui facilite l'écriture d'un seul décodeur générique et des types spécialisés par terminaisonnel.
Étape 2 Initialisez votre module
Créer un nouveau répertoire pour le projet et initialiser un module Go:
mkdir fxmd-go && cd fxmd-go
go mod init fxmd
Vous pouvez l'exporter pour la session de shell en cours:
export FXMD_API_KEY="your_actual_api_key_here"
Astuce de sécurité
Ne jamais coder les clés API dans les fichiers sources ou les engager au contrôle de version. Dans la production, utilisez un gestionnaire de secrets ou une variable d'environnement CI / CD. Le modèle montré ici lit la clé au démarrage et échoue rapidement si la variable manque.
Étape 3 Définir les types de réponse
Créez un fichier appelé fxmd.go. Commencez par définir les structures Go qui correspondent aux formes JSON renvoyées par les trois familles de points de terminaison que vous utiliserez:
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"os"
"time"
)
const baseURL = "https://fxmacrodata.com/api/v1"
// DataPoint represents a single indicator observation.
type DataPoint struct {
Date string `json:"date"`
Val float64 `json:"val"`
AnnouncementDatetime string `json:"announcement_datetime,omitempty"`
}
// AnnouncementsResponse is the envelope for indicator announcement endpoints.
type AnnouncementsResponse struct {
Data []DataPoint `json:"data"`
}
// CalendarEvent represents one upcoming or recent release on the calendar.
type CalendarEvent struct {
Date string `json:"date"`
Indicator string `json:"indicator"`
Expected float64 `json:"expected,omitempty"`
Prior float64 `json:"prior,omitempty"`
Actual float64 `json:"actual,omitempty"`
}
// CalendarResponse is the envelope for the release calendar endpoint.
type CalendarResponse struct {
Data []CalendarEvent `json:"data"`
}
// ForexPoint represents a single FX spot-rate observation.
type ForexPoint struct {
Date string `json:"date"`
Rate float64 `json:"rate"`
}
// ForexResponse is the envelope for the FX spot-rate endpoint.
type ForexResponse struct {
Data []ForexPoint `json:"data"`
}
Étape 4 Construire un client réutilisable
Une épaisse enveloppe . net/http Le client lit la clé API une fois lors de la construction, l'injecte comme paramètre de requête à chaque demande, et impose un temps d'arrêt afin qu'un upstream lent ne puisse jamais bloquer votre programme indéfiniment:
// Client is a lightweight FXMacroData API client.
type Client struct {
apiKey string
httpClient *http.Client
}
// NewClient creates a Client that reads the API key from the FXMD_API_KEY environment variable.
// It panics at startup if the variable is not set — fail-fast is safer than silent empty results.
func NewClient() *Client {
key := os.Getenv("FXMD_API_KEY")
if key == "" {
panic("FXMD_API_KEY environment variable is not set")
}
return &Client{
apiKey: key,
httpClient: &http.Client{
Timeout: 15 * time.Second,
},
}
}
// get performs a GET request to the given path with optional query parameters.
// It decodes the JSON body into dest.
func (c *Client) get(path string, params url.Values, dest any) error {
if params == nil {
params = url.Values{}
}
params.Set("api_key", c.apiKey)
u := baseURL + path + "?" + params.Encode()
resp, err := c.httpClient.Get(u)
if err != nil {
return fmt.Errorf("http get %s: %w", path, err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return fmt.Errorf("api error %d on %s: %s", resp.StatusCode, path, body)
}
if err := json.NewDecoder(resp.Body).Decode(dest); err != nil {
return fmt.Errorf("json decode %s: %w", path, err)
}
return nil
}
En utilisant json.NewDecoder sur le corps de la réponse (plutôt que de lire le corps dans une tranche d'octet d'abord) est l'approche idiomatique dans Go: il diffuse le décodage directement à partir de la connexion HTTP, évitant une allocation intermédiaire pour de grandes charges utiles.
Étape 5 Ajouter des aides à la dactylographie pour chaque famille de points de terminaison
Enveloppe le générique . get méthode avec des aides tapées afin que les appelants n'aient jamais à construire des chemins ou à décoder des enveloppes manuellement:
// GetAnnouncements retrieves indicator announcement data for the given currency and indicator slug.
// start and end are optional date strings in "YYYY-MM-DD" format; pass "" to omit them.
//
// Example: client.GetAnnouncements("usd", "wages", "2024-01-01", "")
// Full indicator catalogue: https://fxmacrodata.com/api-data-docs/usd/wages
func (c *Client) GetAnnouncements(currency, indicator, start, end string) (*AnnouncementsResponse, error) {
params := url.Values{}
if start != "" {
params.Set("start", start)
}
if end != "" {
params.Set("end", end)
}
var out AnnouncementsResponse
err := c.get("/announcements/"+currency+"/"+indicator, params, &out)
return &out, err
}
// GetCalendar retrieves the upcoming release calendar for the given currency code.
//
// Example: client.GetCalendar("usd")
func (c *Client) GetCalendar(currency string) (*CalendarResponse, error) {
var out CalendarResponse
err := c.get("/calendar/"+currency, nil, &out)
return &out, err
}
// GetForex retrieves FX spot-rate history for a currency pair.
// start and end are optional date strings in "YYYY-MM-DD" format.
//
// Example: client.GetForex("eur", "usd", "2024-01-01", "")
func (c *Client) GetForex(base, quote, start, end string) (*ForexResponse, error) {
params := url.Values{}
if start != "" {
params.Set("start", start)
}
if end != "" {
params.Set("end", end)
}
var out ForexResponse
err := c.get("/forex/"+base+"/"+quote, params, &out)
return &out, err
}
Étape 6 Connectez-le tout ensemble main
Avec le client et les assistants en place, appeler l'API est concis et sécurisé par type. main
fonctionner à fxmd.go qui exerce les trois familles de paramètres:
func main() {
client := NewClient()
// --- Announcements: US wages ---
fmt.Println("=== US Wages (last 5 releases) ===")
wages, err := client.GetAnnouncements("usd", "wages", "2024-01-01", "")
if err != nil {
fmt.Println("error:", err)
} else {
for i, pt := range wages.Data {
if i >= 5 {
break
}
fmt.Printf(" %s val=%.2f released=%s\n", pt.Date, pt.Val, pt.AnnouncementDatetime)
}
}
// --- Release calendar: upcoming USD events ---
fmt.Println("\n=== Upcoming USD Releases ===")
cal, err := client.GetCalendar("usd")
if err != nil {
fmt.Println("error:", err)
} else {
for i, ev := range cal.Data {
if i >= 5 {
break
}
fmt.Printf(" %s %s prior=%.2f expected=%.2f\n",
ev.Date, ev.Indicator, ev.Prior, ev.Expected)
}
}
// --- FX spot rates: EUR/USD ---
fmt.Println("\n=== EUR/USD Spot Rate (last 5 days) ===")
fx, err := client.GetForex("eur", "usd", "2025-01-01", "")
if err != nil {
fmt.Println("error:", err)
} else {
for i, pt := range fx.Data {
if i >= 5 {
break
}
fmt.Printf(" %s rate=%.5f\n", pt.Date, pt.Rate)
}
}
}
Exécutez le programme:
go run fxmd.go
Vous devriez voir une sortie similaire à:
=== US Wages (last 5 releases) ===
2025-03-14 val=4.00 released=2025-03-14T12:30:00Z
2025-02-07 val=4.10 released=2025-02-07T13:30:00Z
2025-01-10 val=3.90 released=2025-01-10T13:30:00Z
2024-12-06 val=4.00 released=2024-12-06T13:30:00Z
2024-11-01 val=4.00 released=2024-11-01T12:30:00Z
=== Upcoming USD Releases ===
2025-04-25 gdp prior=2.30 expected=0.40
2025-05-02 non_farm_payrolls prior=228.00 expected=135.00
2025-05-13 inflation prior=2.40 expected=2.30
=== EUR/USD Spot Rate (last 5 days) ===
2025-04-17 rate=1.13452
2025-04-16 rate=1.13680
2025-04-15 rate=1.13590
2025-04-14 rate=1.13320
2025-04-11 rate=1.13580
Conseil Apportez en même temps avec des goroutines
Parce que le client est sûr pour une utilisation simultanée, vous pouvez diffuser plusieurs demandes d'indicateur en parallèle en utilisant des goroutines et sync.WaitGroup ou ... errgroup. Ceci est particulièrement utile lors de la construction d'un tableau de bord qui tire plusieurs devises à la fois temps de l'horloge murale reste proche de la latence de la demande unique la plus lente plutôt que la somme de toutes les demandes.
Étape 7 Gérer la pagination et les plages de dates
Par défaut, le point de terminaison des annonces renvoie tout l'historique disponible.
La masse monétaire M2 ou ... Le PIB Vous voudrez souvent limiter la fenêtre en utilisant start Je suis désolé . end Les paramètres de requête les deux assistants les prennent déjà en charge.
Emploi à temps partiel aux États-Unis données:
// Fetch US part-time employment for the last 90 days
end := time.Now().Format("2006-01-02")
start := time.Now().AddDate(0, 0, -90).Format("2006-01-02")
pt, err := client.GetAnnouncements("usd", "part_time_employment", start, end)
if err != nil {
log.Fatal(err)
}
for _, dp := range pt.Data {
fmt.Printf("%s %.0f\n", dp.Date, dp.Val)
}
Le standard du Go. time Les dates de mise en forme des paquets à partir de "2006-01-02" (l'heure de référence est une mnémonique: 01/02 03:04:05 PM '06 -0700), qui correspond aux chaînes de dates ISO-8601 attendues par l'API.
Étape 8 Parse announcement_datetime comme un time.Time
Le announcement_datetime Si votre application a besoin de calculer des compte à rebours de temps de sortie, trier des événements ou des données de seau par session, analysez-le en un
time.Time valeur:
const rfc3339 = time.RFC3339
for _, dp := range wages.Data {
if dp.AnnouncementDatetime == "" {
continue
}
t, err := time.Parse(rfc3339, dp.AnnouncementDatetime)
if err != nil {
log.Printf("bad datetime %q: %v", dp.AnnouncementDatetime, err)
continue
}
until := time.Until(t)
fmt.Printf("%s val=%.2f in %s\n", dp.Date, dp.Val, until.Round(time.Minute))
}
Liste complète des fichiers
Pour référence, voici la mise en œuvre complète du fichier unique décrite dans les étapes ci-dessus:
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"os"
"time"
)
const baseURL = "https://fxmacrodata.com/api/v1"
type DataPoint struct {
Date string `json:"date"`
Val float64 `json:"val"`
AnnouncementDatetime string `json:"announcement_datetime,omitempty"`
}
type AnnouncementsResponse struct {
Data []DataPoint `json:"data"`
}
type CalendarEvent struct {
Date string `json:"date"`
Indicator string `json:"indicator"`
Expected float64 `json:"expected,omitempty"`
Prior float64 `json:"prior,omitempty"`
Actual float64 `json:"actual,omitempty"`
}
type CalendarResponse struct {
Data []CalendarEvent `json:"data"`
}
type ForexPoint struct {
Date string `json:"date"`
Rate float64 `json:"rate"`
}
type ForexResponse struct {
Data []ForexPoint `json:"data"`
}
type Client struct {
apiKey string
httpClient *http.Client
}
func NewClient() *Client {
key := os.Getenv("FXMD_API_KEY")
if key == "" {
panic("FXMD_API_KEY environment variable is not set")
}
return &Client{
apiKey: key,
httpClient: &http.Client{Timeout: 15 * time.Second},
}
}
func (c *Client) get(path string, params url.Values, dest any) error {
if params == nil {
params = url.Values{}
}
params.Set("api_key", c.apiKey)
u := baseURL + path + "?" + params.Encode()
resp, err := c.httpClient.Get(u)
if err != nil {
return fmt.Errorf("http get %s: %w", path, err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return fmt.Errorf("api error %d on %s: %s", resp.StatusCode, path, body)
}
return json.NewDecoder(resp.Body).Decode(dest)
}
func (c *Client) GetAnnouncements(currency, indicator, start, end string) (*AnnouncementsResponse, error) {
params := url.Values{}
if start != "" { params.Set("start", start) }
if end != "" { params.Set("end", end) }
var out AnnouncementsResponse
return &out, c.get("/announcements/"+currency+"/"+indicator, params, &out)
}
func (c *Client) GetCalendar(currency string) (*CalendarResponse, error) {
var out CalendarResponse
return &out, c.get("/calendar/"+currency, nil, &out)
}
func (c *Client) GetForex(base, quote, start, end string) (*ForexResponse, error) {
params := url.Values{}
if start != "" { params.Set("start", start) }
if end != "" { params.Set("end", end) }
var out ForexResponse
return &out, c.get("/forex/"+base+"/"+quote, params, &out)
}
func main() {
client := NewClient()
wages, err := client.GetAnnouncements("usd", "wages", "2024-01-01", "")
if err != nil {
fmt.Println("wages error:", err)
} else {
fmt.Println("=== US Wages ===")
for i, pt := range wages.Data {
if i >= 5 { break }
fmt.Printf(" %s %.2f\n", pt.Date, pt.Val)
}
}
cal, err := client.GetCalendar("usd")
if err != nil {
fmt.Println("calendar error:", err)
} else {
fmt.Println("=== Upcoming USD Releases ===")
for i, ev := range cal.Data {
if i >= 5 { break }
fmt.Printf(" %s %s\n", ev.Date, ev.Indicator)
}
}
fx, err := client.GetForex("eur", "usd", "2025-01-01", "")
if err != nil {
fmt.Println("forex error:", err)
} else {
fmt.Println("=== EUR/USD ===")
for i, pt := range fx.Data {
if i >= 5 { break }
fmt.Printf(" %s %.5f\n", pt.Date, pt.Rate)
}
}
}
Ce qui suit
Vous avez maintenant un client Go fonctionnel qui couvre les trois familles de points de terminaison FXMacroData les plus courantes.
- Élargir les définitions de la structure pour capturer des champs supplémentaires tels que
revisedles valeurs à partir du point final du calendrier. - Ajoutez un
errgroup- à base de ventilateur Pour obtenir plusieurs devises simultanément leClientest sûr de partager à travers les routines parce quehttp.Clientest protégé contre les concurrences par conception. - Persister à envoyer les résultats dans une base de données SQLite locale en utilisant
database/sqlpour les tests de rétroaction ou la recherche de signaux légers. - Consultez le catalogue complet des indicateurs pour USD à /api-data-docs/usd/salaires Les salaires sont calculés en dollars américains et pour les autres devises sur le tableau de bord recherchez M2Je suis désolé . emploi à temps partiel, et Le PIB Les résultats de cette étude sont les suivants: