Go é cada vez mais popular para ferramentas financeiras sua rápida compilação, desempenho previsível e primitivos de simultâneo de primeira classe tornam-no um ajuste natural para pipelines de dados e automação de negociação. Este guia percorre tudo o que você precisa para chamar a API REST FXMacroData do Go, desde uma simples solicitação única até um cliente reutilizável que lida com timeouts, decodificação JSON e filtragem de data opcional. No final, você terá código idiomático Go que recupera anúncios de indicadores macro, puxa um calendário de lançamento próximo e consulta taxas spot FX tudo em menos de 150 linhas.
O que você vai construir
Um cliente HTTP Go leve que autentica contra a API REST FXMacroData usando a autenticação do parâmetro de consulta API-key, decodifica respostas JSON estruturadas em estruturas Go digitadas e expõe funções auxiliares reutilizáveis para as três famílias de endpoints mais comuns: anúncios, calendário de lançamento e taxas spot FX.
Requisitos prévios
- Go 1.21 ou posterior (Go.dev/dl)
- Uma chave da API do FXMacroData registe-se em / subscrever para receber uma
- Familiarização básica com os módulos Go e com a biblioteca padrão
Não são necessárias bibliotecas HTTP ou JSON de terceiros o padrão net/http E ...
encoding/json Os pacotes tratam de tudo.
Passo 1 Compreender a forma da API
Cada ponto final do indicador FXMacroData segue o mesmo padrão de URL:
GET https://fxmacrodata.com/api/v1/announcements/{currency}/{indicator}?api_key=YOUR_API_KEY
A resposta é um objeto JSON com um nível superior data Cada elemento contém um
date Uma corda numérica. val, e opcionalmente um announcement_datetime
que dá precisão de segundo nível para quando o número foi oficialmente lançado.
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" }
]
}
A mesma estrutura de envelope se aplica aos endpoints de calendário e forex, tornando mais fácil escrever um decodificador genérico único e tipos especializados por endpoint em cima dele.
Passo 2 Iniciar o seu módulo
Crie um novo diretório para o projeto e inicie um módulo Go:
mkdir fxmd-go && cd fxmd-go
go mod init fxmd
Você pode exportá-lo para a sessão do shell atual:
export FXMD_API_KEY="your_actual_api_key_here"
Dica de segurança
Nunca codifique as chaves da API em arquivos de origem ou as comprometa ao controle de versão. Na produção, use um gerenciador de segredos ou uma variável de ambiente CI / CD. O padrão mostrado aqui lê a chave no início e falha rapidamente se a variável estiver faltando.
Passo 3 Definir tipos de resposta
Crie um arquivo chamado fxmd.go. Comece definindo as estruturas Go que mapeam as formas JSON retornadas pelas três famílias de endpoints que você usará:
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"`
}
Passo 4 Construir um cliente reutilizável
Um envoltório fino à volta . net/http O cliente lê a chave API uma vez na construção, injeta-a como um parâmetro de consulta em cada solicitação e impõe um timeout para que um upstream lento nunca possa bloquear o seu programa indefinidamente:
// 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
}
Usando json.NewDecoder no corpo da resposta (em vez de ler o corpo em uma fatia de byte primeiro) é a abordagem idiomática em Go: transmite a decodificação diretamente da conexão HTTP, evitando uma alocação intermediária para grandes cargas úteis.
Passo 5 Adicionar auxiliares de digitação para cada família de pontos finais
Enrole o genérico . get método com ajudantes digitados para que os chamadores nunca tenham que construir caminhos ou decodificar envelopes manualmente:
// 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
}
Passo 6 Junta tudo main
Com o cliente e auxiliares no lugar, chamando a API é concisa e tipo-seguro. main
Função para fxmd.go que exerce as três famílias de endpoints:
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)
}
}
}
Execute o programa:
go run fxmd.go
Você deve ver uma saída semelhante a:
=== 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
Dica Acompanhamento de buscas com rotinas
Como o cliente é seguro para uso simultâneo, você pode distribuir várias solicitações de indicadores em paralelo usando goroutines e sync.WaitGroup Ou ... errgroup. Isto é especialmente útil quando se constrói um painel que puxa várias moedas de uma só vez o tempo do relógio de parede fica perto da latência da solicitação mais lenta em vez da soma de todas as solicitações.
Passo 7 Lidar com a paginação e os intervalos de datas
Por padrão, o ponto final de anúncios retorna todo o histórico disponível.
M2 oferta monetária Ou ... PIB muitas vezes você vai querer limitar a janela usando start E ... end Para o exemplo acima, a função de "query" é a de "request" e "question" ambos os auxiliares já os suportam.
Emprego a tempo parcial nos EUA dados:
// 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)
}
É o padrão do Go. time formatos de embalagem data de "2006-01-02" (a hora de referência é uma mnemônica: 01/02 03:04:05 PM '06 -0700), que corresponde às cadeias de datas ISO-8601 esperadas pela API.
Passo 8 Analisar announcement_datetime Como um time.Time
O ... announcement_datetime campo é uma string UTC RFC-3339. Se o seu aplicativo precisa calcular contagens regressivas de tempo de lançamento, ordenar eventos ou dados de balde por sessão, analise-o em um
time.Time Valor:
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))
}
Lista completa de arquivos
Para referência, aqui está a implementação completa de um único arquivo descrita nas etapas acima:
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)
}
}
}
O que vem a seguir?
Agora você tem um cliente Go que cobre as três famílias de endpoints FXMacroData mais comuns.
- Aumentar as definições de estruturas para capturar campos adicionais como
revisedvalores a partir do ponto final do calendário. - Adicione um
errgroup- baseado em ventilador para obter várias moedas simultaneamente oClientÉ seguro compartilhar entre as rotinas porquehttp.Clienté segura de simultâneo por conceção. - Perseverar resultados para um banco de dados SQLite local Usando
database/sqlpara backtesting leve ou pesquisa de sinais. - Explorar o catálogo completo dos indicadores para USD em /api-data-docs/usd/salários e para outras moedas no painel de instrumentos procure M2- Não . trabalho a tempo parcialE ... PIB A Comissão considera que a utilização de um sistema de informação de base é uma das principais razões para a criação de um "sistema de informação baseado em dados".