Go ist zunehmend beliebt für Finanzwerkzeuge seine schnelle Kompilierung, vorhersehbare Leistung und erstklassige Gleichzeitigkeit Primitive machen es zu einem natürlichen Passform für Datenpipelines und Handelsautomation. Dieser Leitfaden geht durch alles, was Sie benötigen, um die FXMacroData REST API von Go aufzurufen, von einer einfachen einmaligen Anfrage bis zu einem wiederverwendbaren Client, der Timeouts, JSON-Decodierung und optionales Datum Filtern verarbeitet. Am Ende haben Sie idiomatischen Go-Code, der Makro-Indikatorankündigungen abruft, einen Kalender für die bevorstehende Veröffentlichung zieht und FX-Spotraten in weniger als 150 Zeilen abfragt.
Was du bauen wirst
Ein leichter Go HTTP-Client, der sich mit der FXMacroData REST API mit Hilfe des Abfrageparameters API-Key-Auth authentifiziert, strukturierte JSON-Antworten in getypte Go-Strukturen dekodiert und wiederverwendbare Helferfunktionen für die drei häufigsten Endpunktfamilien zeigt: Ankündigungen, Release-Kalender und FX-Spotraten.
Voraussetzungen
- Die Daten sind in der Tabelle 1.21 oder höher (Die Daten werden in der Tabelle aufgeführt.)
- Ein FXMacroData API-Schlüssel melden Sie sich an /abonnieren eine zu erhalten
- Grundlegende Kenntnisse der Go-Module und der Standardbibliothek
Es werden keine HTTP- oder JSON-Bibliotheken von Drittanbietern benötigt der Standard net/http Und ...
encoding/json Die Pakete regeln alles.
Schritt 1 Die API-Form verstehen
Jeder Endpunkt des FXMacroData-Indikators folgt demselben URL-Muster:
GET https://fxmacrodata.com/api/v1/announcements/{currency}/{indicator}?api_key=YOUR_API_KEY
Die Antwort ist ein JSON-Objekt mit einem oberen Level data Jedes Element enthält eine
date Eine Zeichenfolge, eine Zahl. val, und optional eine announcement_datetime
Das gibt eine zweite Präzision für das Datum, an dem die Zahl offiziell veröffentlicht wurde.
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" }
]
}
Die gleiche Umschlagstruktur gilt für die Kalender- und Forex-Endpunkte, wodurch es einfach ist, einen einzelnen generischen Decoder zu schreiben und spezialisierte Endpunkte zu schreibt.
Schritt 2 Ihr Modul initialisieren
Erstellen Sie ein neues Verzeichnis für das Projekt und initialisieren Sie ein Go-Modul:
mkdir fxmd-go && cd fxmd-go
go mod init fxmd
Speichern Sie Ihren API-Schlüssel in einer Umgebungsvariable, anstatt ihn hart zu kodieren.
export FXMD_API_KEY="your_actual_api_key_here"
Sicherheitstipp
Verwenden Sie niemals Hardcode-API-Schlüssel in Quelldateien oder verpflichten Sie sie zur Versionskontrolle. In der Produktion verwenden Sie einen Geheimnismanager oder eine CI / CD-Umgebungsvariable. Das hier gezeigte Muster liest den Schlüssel beim Starten und versagt schnell, wenn die Variable fehlt.
Schritt 3 Definition von Reaktionsarten
Erstellen Sie eine Datei namens fxmd.go. Beginnen Sie mit der Definition der Go-Strukturen , die die von den drei verwendeten Endpunktfamilien zurückgegebenen JSON-Formen abbilden:
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"`
}
Schritt 4 Erstellen Sie einen wiederverwendbaren Client
Eine dünne Umhüllung . net/http Der Client liest den API-Schlüssel einmal beim Bau, injiziert ihn als Abfrageparameter bei jeder Anfrage und erzwingt einen Timeout, so dass ein langsamer Upstream Ihr Programm nie unbegrenzt stoppen kann:
// 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
}
- Ich benutze ... json.NewDecoder Die Verwendung von "Dicoding" auf dem Antwortkörper (anstatt den Körper zuerst in ein Byte-Slice zu lesen) ist der idiomatische Ansatz in Go: Es streamt die Entschlüsselung direkt von der HTTP-Verbindung aus und vermeidet eine Zwischenzuweisung für große Nutzlasten.
Schritt 5 Für jede Endpunktfamilie Typ-Hilfen hinzufügen
Verpacken Sie das Generikum . get Methode mit eingegebenem Helfer, so dass Anrufer niemals manuell Wege oder Decodierung von Umschlägen erstellen müssen:
// 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
}
Schritt 6 Wirf alles zusammen main
Mit dem Client und Helfern ist der Anruf der API prägnant und typschutzsicher. main
Funktion zu fxmd.go die alle drei Endpunktfamilien ausübt:
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)
}
}
}
Führen Sie das Programm aus:
go run fxmd.go
Sie sollten eine Ausgabe sehen, die ähnlich ist wie:
=== 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
Tipp Gleichzeitige Abholungen mit Goroutines
Da der Client sicher für gleichzeitige Verwendung ist, können Sie mehrere Indikator-Anfragen parallel mit goroutines und sync.WaitGroup Oder ... errgroup. Dies ist besonders nützlich, wenn Sie ein Dashboard erstellen, das mehrere Währungen gleichzeitig zieht die Wanduhrzeit bleibt in der Nähe der Latenzzeit der langsamsten einzelnen Anfrage und nicht der Summe aller Anfragen.
Schritt 7 Verarbeiten von Seitenbezeichnung und Datumsbereichen
Standardmäßig gibt der Endpunkt für Ankündigungen alle verfügbaren Daten zurück.
M2 Geldmenge Oder ... BIP Sie werden oft wollen, das Fenster zu begrenzen, indem Sie start Und ... end Hier ist ein fokussiertes Beispiel, das nur das jüngste Quartal von
Teilzeitbeschäftigung in den USA Daten:
// 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)
}
Das ist Go's Standard. time Daten zu den Paketformaten "2006-01-02" (Die Referenzzeit ist eine Mnemonik: 01/02 03:04:05 PM '06 -0700), die mit den von der API erwarteten ISO-8601-Datenzfolgen übereinstimmt.
Schritt 8 Parsieren announcement_datetime als time.Time
Die ... announcement_datetime Das Feld ist eine UTC RFC-3339-String. Wenn Ihre Anwendung Zeit-zu-Veröffentlichung Countdowns berechnen, Ereignisse sortieren oder Bucket-Daten nach Sitzung benötigt, analysieren Sie es in eine
time.Time Wert:
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))
}
Vollständige Datei-Liste
Hier ist die vollständige Einfachdatei-Implementierung, die in den oben genannten Schritten beschrieben wird:
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)
}
}
}
Was kommt als Nächstes?
Sie haben nun einen funktionierenden Go-Client, der die drei häufigsten FXMacroData-Endpunktfamilien abdeckt.
- Erweiterung der Strukturdefinitionen um zusätzliche Felder wie
revisedWerte ab dem Kalenderendpunkt. - Fügen Sie ein
errgroup- auf der Grundlage von Fan-Out - um mehrere Währungen gleichzeitig abzurufen dieClientist sicher, über goroutines zu teilen, weilhttp.Clientist gleichzeitig sicher. - Folgen Sie den Ergebnissen in einer lokalen SQLite-Datenbank verwenden
database/sqlfür leichte Rückprüfung oder Signalforschung. - Siehe vollständiger Indikatorkatalog für USD bei /api-data-docs/USD/Löhne und für andere Währungen auf dem Dashboard suchen Sie nach M2- Ich weiß . TeilzeitbeschäftigungUnd ... BIP Die Kommission hat die Kommission aufgefordert, die in diesem Leitfaden beschriebenen Anwendungsbedingungen für die