يعد Go شائعًا بشكل متزايد للأدوات المالية إن تجميعه السريع وأدائه المتوقع وأصوله الأولية المتزامنة من الدرجة الأولى تجعله مناسبًا طبيعيًا لخطوط أنابيب البيانات وأتمتة التداول. يمر هذا الدليل بكل ما تحتاجه للاتصال بـ FXMacroData REST API من Go ، من طلب بسيط لمرة واحدة إلى عميل قابل لإعادة الاستخدام يتعامل مع المدة النهائية ، فك تشفير JSON ، وتصفية التاريخ الاختيارية. بحلول النهاية سيكون لديك رمز Go لغوي يسترد إعلانات مؤشر الماكرو ، ويسحب تقويم الإصدار القادم ، ويسأل أسعار الفوركس كل ذلك في أقل من 150 سطر.
ما الذي ستبنيه
عميل HTTP خفيف الوزن يُصادق على API REST FXMacroData باستخدام إصدار أساس API معين ، ويقوم بتفكيك استجابات JSON المنظمة إلى هياكل Go المطبوعة ، ويكشف عن وظائف مساعد قابلة لإعادة الاستخدام لثلاث عائلات النقاط النهائية الأكثر شيوعًا: الإعلانات وتقويم الإصدار ومعدلات الفورية FX.
الشروط المسبقة
- الذهاب 1.21 أو أحدث (go.dev/dl)
- مفتاح FXMacroData API تسجيل في / اشترك لتلقي واحدة
- معرفة أساسية بوحدة غو والمكتبة القياسية
لا حاجة إلى مكتبات HTTP أو JSON من طرف ثالث المعيار net/http و
encoding/json الحزم تتعامل مع كل شيء
الخطوة 1 فهم شكل API
كل نقطة نهاية مؤشر FXMacroData تتبع نمط URL نفسه:
GET https://fxmacrodata.com/api/v1/announcements/{currency}/{indicator}?api_key=YOUR_API_KEY
الرد هو كائن JSON مع مستوى أعلى data كل عنصر يحتوي على
date سلسلة، رقمية valو إختيارياً announcement_datetime
هذا يعطي دقة من المستوى الثاني عندما تم إصدار الرقم رسمياً. على سبيل المثال:
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" }
]
}
تنطبق نفس هيكل المغلف على نقاط النهاية التقويمية والفوركس، مما يجعل من السهل كتابة رمز فك عام واحد وتخصص أنواع نقطة النهاية الواحدة فوقها.
الخطوة 2 بدء تشغيل الوحدة
إنشاء دليل جديد للمشروع وبدء وحدة الذهاب:
mkdir fxmd-go && cd fxmd-go
go mod init fxmd
يمكنك تخزين مفتاح API الخاص بك في متغير البيئة بدلاً من ترميزه بصورة صارمة. يمكنك تصديره للجلسة الحالية للشيل:
export FXMD_API_KEY="your_actual_api_key_here"
نصيحة أمنية
لا تقم أبداً بتشفير مفاتيح API في ملفات المصدر أو إلتزامها بمراقبة الإصدارات. في الإنتاج ، استخدم مدير أسرار أو متغير بيئة CI / CD. ينظر النمط المعروض هنا إلى المفتاح عند بدء التشغيل ويفشل بسرعة إذا كان المتغير مفقودًا.
الخطوة 3 تحديد أنواع الاستجابة
أنشئ ملفًا يدعى fxmd.go. ابدأ بتعريف هيكل Go الذي يرسله إلى أشكال JSON التي تعودها عائلات النقاط النهائية الثلاث التي ستستخدمها:
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"`
}
الخطوة 4 بناء عميل قابلة لإعادة الاستخدام
لفاف رقيق حولها net/http يبقي boilerplate خارج رمز الدعوة. يقرأ العميل مفتاح API مرة واحدة في البناء، ويحقنه كمعلم استفسار في كل طلب، ويفرض وقتًا نهائيًا بحيث لا يمكن أن يؤخر البرنامج الخاص بك إلى أجل غير مسمى:
// 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
}
باستخدام json.NewDecoder على جسم الاستجابة (بدلاً من قراءة الجسم إلى شريحة بايت أولاً) هو النهج المفرد في Go: إنه يسلط فك الترميز مباشرة من اتصال HTTP ، وتجنب تخصيصًا وسيطًا للحمولات الضخمة.
الخطوة 5 إضافة مساعدات مطبوعة لكل عائلة نقطة نهاية
لفّي العقار get طريقة مع مساعدي المكتوب حتى لا يتعين على المتصلين بناء مسارات أو فك شفرة المظروف يدوياً:
// 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
}
الخطوة 6 ضعي كل شيء معاً main
مع العميل والمساعدين في مكانهم، استدعاء واجهة برمجة التطبيقات هو موجزة وآمنة النوع. main
وظيفة إلى fxmd.go التي تمارس كل ثلاث عائلات من النقاط النهائية:
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)
}
}
}
اطلق البرنامج
go run fxmd.go
يجب أن ترى مخرجات مشابهة ل:
=== 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
نصيحة الحملات المشتركة مع الجوروتين
لأن العميل آمن للاستخدام المتزامن، يمكنك نشر طلبات مؤشر متعددة بالتوازي باستخدام الروتينات sync.WaitGroup أو errgroupهذا مفيد بشكل خاص عند بناء لوحة تحكم تسحب عدة عملات في وقت واحد الوقت الساعة الجدار يبقى بالقرب من فترة تأخير بطيئة الطلب الواحد بدلا من مجموع جميع الطلبات.
الخطوة 7 التعامل مع صفحة وتفاصيل التاريخ
بشكل افتراضي، نقطة نهاية الإعلانات تعيد كل التاريخ المتاح.
المعروض النقدي في م2 أو الناتج المحلي الإجمالي سوف ترغب في الحد من نافذة استخدام start و end معايير الاستفسار كلا المساعدين يدعمونها بالفعل. هنا مثال مركز يحصل فقط على الربع الأخير من
العمل بدوام جزئي في الولايات المتحدة البيانات:
// 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)
}
القياس time صيغ الحزمة "2006-01-02" (الوقت المرجعي هو التذكر: 01/02 03:04:05 PM '06 -0700) ، والذي يتطابق مع سلسلة التواريخ ISO-8601 المتوقعة من قبل API.
الخطوة 8 تحليل announcement_datetime كـ time.Time
- ... announcement_datetime المجال هو سلسلة UTC RFC-3339. إذا كان تطبيقك يحتاج إلى حساب العد التنازلي للوقت إلى الإفراج أو فرز الأحداث أو بيانات الدلو حسب الجلسة ، فقم بتحليلها إلى
time.Time القيمة:
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))
}
قائمة الملفات الكاملة
للمرجع، هنا تنفيذ الملف الواحد الكامل الموصوف في الخطوات أعلاه:
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)
}
}
}
ما التالي
لديك الآن عميل Go يعمل يغطي ثلاثة أسرة من نقاط نهاية FXMacroData الأكثر شيوعاً.
- توسيع تعريفات الهيكل لالتقاط حقول إضافية مثل
revisedالقيم من نقطة نهاية التقويم. - إضافة
errgroup-بناء على المروحة لتحقيق عملات متعددة في وقت واحدClientمن الآمن أن تشارك بين الروتينات لأنhttp.Clientهو آمن من التزامن من خلال التصميم. - استمرار النتائج إلى قاعدة بيانات SQLite المحلية باستخدام
database/sqlللاختبار الخلفي الخفيف أو أبحاث الإشارات. - استكشاف الكتالوج الكامل للمؤشرات للدولار الأمريكي عند /api-data-docs/USD/الأجور وبالنسبة للعملات الأخرى عبر لوحة القيادة ابحث عن م2- لا العمل بدوام جزئيو الناتج المحلي الإجمالي كنقاط انطلاق نظراً لإشارات الطلب التي دفعت هذا الدليل.