From 31e7683d127b61b9b14846d1abd03517bf567b75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A2=81=E7=AB=A0=E6=B4=AA?= Date: Wed, 10 Jun 2026 17:50:17 +0800 Subject: [PATCH 01/25] feat(macrodata): add country filter, count field, offset param - MacrodataIndicators: add country parameter (MacrodataCountry enum) - MacrodataIndicators: return MacrodataIndicatorListResponse with count - Macrodata: add offset parameter for pagination - Macrodata: response includes count field - Add MacrodataImportance enum (1=Low, 2=Medium, 3=High) Co-Authored-By: Claude Sonnet 4.6 (1M context) --- fundamental/context.go | 28 +++++++++--------- fundamental/jsontypes/types.go | 8 ++++-- fundamental/types.go | 52 +++++++++++++++++++++++++++++----- 3 files changed, 65 insertions(+), 23 deletions(-) diff --git a/fundamental/context.go b/fundamental/context.go index facfc82..fec1577 100644 --- a/fundamental/context.go +++ b/fundamental/context.go @@ -1616,16 +1616,20 @@ func convertFinancialReportSnapshot(j *jsontypes.FinancialReportSnapshot) *Finan // MacrodataIndicators fetches the list of available macroeconomic indicators. // -// Pass offset and limit as nil to use the API defaults (offset=0, limit=100). -// To fetch all ~619 indicators in one call pass limit=1000. +// Pass country to filter by country code (e.g. MacrodataCountryUS). +// Pass nil for all countries. // // Path: GET /v1/quote/macrodata func (c *FundamentalContext) MacrodataIndicators( ctx context.Context, + country *MacrodataCountry, offset *int32, limit *int32, -) ([]MacrodataIndicator, error) { +) (*MacrodataIndicatorListResponse, error) { q := url.Values{} + if country != nil { + q.Set("country", string(*country)) + } if offset != nil { q.Set("offset", fmt.Sprintf("%d", *offset)) } @@ -1640,16 +1644,9 @@ func (c *FundamentalContext) MacrodataIndicators( for _, item := range resp.Data { out = append(out, convertMacrodataIndicator(&item)) } - return out, nil + return &MacrodataIndicatorListResponse{Data: out, Count: resp.Count}, nil } -// Macrodata fetches historical data for a specific macroeconomic -// indicator. -// -// startTime and endTime are Unix timestamps in seconds; pass nil to omit. -// limit defaults to 100 (max 100) when nil. -// -// Path: GET /v1/quote/macrodata/{indicator_code} // Macrodata fetches historical data for a specific macroeconomic indicator. // // startDate and endDate are date strings in "YYYY-MM-DD" format. @@ -1661,6 +1658,7 @@ func (c *FundamentalContext) Macrodata( indicatorCode string, startDate *string, endDate *string, + offset *int32, limit *int32, ) (*MacrodataResponse, error) { q := url.Values{} @@ -1670,6 +1668,9 @@ func (c *FundamentalContext) Macrodata( if endDate != nil { q.Set("end_time", *endDate+"T23:59:59Z") } + if offset != nil { + q.Set("offset", fmt.Sprintf("%d", *offset)) + } if limit != nil { q.Set("limit", fmt.Sprintf("%d", *limit)) } @@ -1683,8 +1684,9 @@ func (c *FundamentalContext) Macrodata( data = append(data, convertMacrodata(&d)) } return &MacrodataResponse{ - Info: convertMacrodataIndicator(&resp.Info), - Data: data, + Info: convertMacrodataIndicator(&resp.Info), + Data: data, + Count: resp.Count, }, nil } diff --git a/fundamental/jsontypes/types.go b/fundamental/jsontypes/types.go index 997f676..4831847 100644 --- a/fundamental/jsontypes/types.go +++ b/fundamental/jsontypes/types.go @@ -822,7 +822,8 @@ type MacrodataIndicator struct { // MacrodataIndicatorListResponse is the raw response for GET /v1/quote/macrodata. type MacrodataIndicatorListResponse struct { - Data []MacrodataIndicator `json:"list"` + Data []MacrodataIndicator `json:"list"` + Count int32 `json:"count"` } // Macrodata is one historical data point for a macroeconomic @@ -842,6 +843,7 @@ type Macrodata struct { // MacrodataResponse is the raw response for // GET /v1/quote/macrodata/{indicator_code}. type MacrodataResponse struct { - Info MacrodataIndicator `json:"info"` - Data []Macrodata `json:"data"` + Info MacrodataIndicator `json:"info"` + Data []Macrodata `json:"data"` + Count int32 `json:"count"` } diff --git a/fundamental/types.go b/fundamental/types.go index 251cda2..b056234 100644 --- a/fundamental/types.go +++ b/fundamental/types.go @@ -1363,9 +1363,46 @@ type MultiLanguageText struct { TraditionalChinese string } +// MacrodataCountry is a country code for filtering macroeconomic indicators. +type MacrodataCountry string + +const ( + MacrodataCountryUS MacrodataCountry = "US" // United States + MacrodataCountryCN MacrodataCountry = "CN" // China + MacrodataCountryEU MacrodataCountry = "EU" // Euro Zone + MacrodataCountryJP MacrodataCountry = "JP" // Japan + MacrodataCountryUK MacrodataCountry = "UK" // United Kingdom + MacrodataCountryDE MacrodataCountry = "DE" // Germany + MacrodataCountryFR MacrodataCountry = "FR" // France + MacrodataCountryAU MacrodataCountry = "AU" // Australia + MacrodataCountryCA MacrodataCountry = "CA" // Canada + MacrodataCountryKR MacrodataCountry = "KR" // South Korea + MacrodataCountryIN MacrodataCountry = "IN" // India + MacrodataCountryBR MacrodataCountry = "BR" // Brazil + MacrodataCountryHK MacrodataCountry = "HK" // Hong Kong + MacrodataCountrySG MacrodataCountry = "SG" // Singapore +) + +// MacrodataImportance is the importance level of a macroeconomic indicator. +type MacrodataImportance int32 + +const ( + MacrodataImportanceLow MacrodataImportance = 1 + MacrodataImportanceMedium MacrodataImportance = 2 + MacrodataImportanceHigh MacrodataImportance = 3 +) + +// MacrodataIndicatorListResponse is the response for FundamentalContext.MacrodataIndicators. +type MacrodataIndicatorListResponse struct { + // Data is the list of indicators. + Data []MacrodataIndicator + // Count is the total number of indicators matching the query. + Count int32 +} + // MacrodataIndicator is the metadata for one macroeconomic indicator. type MacrodataIndicator struct { - // IndicatorCode is the external vendor code (input to EconomicIndicator). + // IndicatorCode is the external vendor code (input to Macrodata). IndicatorCode string SourceOrg string Country string @@ -1375,14 +1412,13 @@ type MacrodataIndicator struct { Periodicity string Category string Describe MultiLanguageText - // Importance — higher is more important. + // Importance: 1=Low, 2=Medium, 3=High. Importance int32 // StartDate is the start date of data coverage; nil if unset. StartDate *time.Time } -// Macrodata is one historical data point for a macroeconomic -// indicator. +// Macrodata is one historical data point for a macroeconomic indicator. type Macrodata struct { // Period is the statistical period (e.g. "2024-Q1", "2024-03"). Period string @@ -1396,8 +1432,10 @@ type Macrodata struct { UnitPrefix MultiLanguageText } -// MacrodataResponse is the response for FundamentalContext.EconomicIndicator. +// MacrodataResponse is the response for FundamentalContext.Macrodata. type MacrodataResponse struct { - Info MacrodataIndicator - Data []Macrodata + Info MacrodataIndicator + Data []Macrodata + // Count is the total number of historical data points. + Count int32 } From 97b68fd5515b06c5af735e91f4af2fa0114f9ee2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A2=81=E7=AB=A0=E6=B4=AA?= Date: Wed, 10 Jun 2026 17:53:06 +0800 Subject: [PATCH 02/25] fix: trim MacrodataCountry to 6 supported countries (HK/CN/US/EU/JP/SG) Co-Authored-By: Claude Sonnet 4.6 (1M context) --- fundamental/types.go | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/fundamental/types.go b/fundamental/types.go index b056234..10f46f8 100644 --- a/fundamental/types.go +++ b/fundamental/types.go @@ -1367,19 +1367,11 @@ type MultiLanguageText struct { type MacrodataCountry string const ( + MacrodataCountryHK MacrodataCountry = "HK" // Hong Kong SAR China + MacrodataCountryCN MacrodataCountry = "CN" // China (Mainland) MacrodataCountryUS MacrodataCountry = "US" // United States - MacrodataCountryCN MacrodataCountry = "CN" // China MacrodataCountryEU MacrodataCountry = "EU" // Euro Zone MacrodataCountryJP MacrodataCountry = "JP" // Japan - MacrodataCountryUK MacrodataCountry = "UK" // United Kingdom - MacrodataCountryDE MacrodataCountry = "DE" // Germany - MacrodataCountryFR MacrodataCountry = "FR" // France - MacrodataCountryAU MacrodataCountry = "AU" // Australia - MacrodataCountryCA MacrodataCountry = "CA" // Canada - MacrodataCountryKR MacrodataCountry = "KR" // South Korea - MacrodataCountryIN MacrodataCountry = "IN" // India - MacrodataCountryBR MacrodataCountry = "BR" // Brazil - MacrodataCountryHK MacrodataCountry = "HK" // Hong Kong MacrodataCountrySG MacrodataCountry = "SG" // Singapore ) From c94a4c05518708f6d7ae8aed17d36b4c261ac48d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A2=81=E7=AB=A0=E6=B4=AA?= Date: Wed, 10 Jun 2026 17:55:25 +0800 Subject: [PATCH 03/25] fix: convert MacrodataCountry code to API full name before request SDK accepts HK/CN/US/EU/JP/SG; converts to 'Hong Kong SAR China'/'China (Mainland)'/etc. when sending to API. Co-Authored-By: Claude Sonnet 4.6 (1M context) --- fundamental/context.go | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/fundamental/context.go b/fundamental/context.go index fec1577..c6687c5 100644 --- a/fundamental/context.go +++ b/fundamental/context.go @@ -1628,7 +1628,7 @@ func (c *FundamentalContext) MacrodataIndicators( ) (*MacrodataIndicatorListResponse, error) { q := url.Values{} if country != nil { - q.Set("country", string(*country)) + q.Set("country", macrodataCountryToAPIValue(*country)) } if offset != nil { q.Set("offset", fmt.Sprintf("%d", *offset)) @@ -1738,3 +1738,22 @@ func convertMacrodata(j *jsontypes.Macrodata) Macrodata { UnitPrefix: convertMultiLanguageText(j.UnitPrefix), } } + +func macrodataCountryToAPIValue(c MacrodataCountry) string { + switch c { + case MacrodataCountryHK: + return "Hong Kong SAR China" + case MacrodataCountryCN: + return "China (Mainland)" + case MacrodataCountryUS: + return "United States" + case MacrodataCountryEU: + return "Euro Zone" + case MacrodataCountryJP: + return "Japan" + case MacrodataCountrySG: + return "Singapore" + default: + return string(c) + } +} From c480822829f0f289162a05932ba814af24bc9540 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A2=81=E7=AB=A0=E6=B4=AA?= Date: Thu, 11 Jun 2026 10:01:50 +0800 Subject: [PATCH 04/25] refactor: rename macrodata -> macroeconomic in all interfaces and types Co-Authored-By: Claude Sonnet 4.6 (1M context) --- fundamental/context.go | 44 +++++++++++++++++----------------- fundamental/jsontypes/types.go | 16 ++++++------- fundamental/types.go | 42 ++++++++++++++++---------------- 3 files changed, 51 insertions(+), 51 deletions(-) diff --git a/fundamental/context.go b/fundamental/context.go index c6687c5..3ef1e16 100644 --- a/fundamental/context.go +++ b/fundamental/context.go @@ -1614,18 +1614,18 @@ func convertFinancialReportSnapshot(j *jsontypes.FinancialReportSnapshot) *Finan // ─── Macrodata ──────────────────────────────────────────────────── -// MacrodataIndicators fetches the list of available macroeconomic indicators. +// MacroeconomicIndicators fetches the list of available macroeconomic indicators. // -// Pass country to filter by country code (e.g. MacrodataCountryUS). +// Pass country to filter by country code (e.g. MacroeconomicCountryUS). // Pass nil for all countries. // // Path: GET /v1/quote/macrodata -func (c *FundamentalContext) MacrodataIndicators( +func (c *FundamentalContext) MacroeconomicIndicators( ctx context.Context, - country *MacrodataCountry, + country *MacroeconomicCountry, offset *int32, limit *int32, -) (*MacrodataIndicatorListResponse, error) { +) (*MacroeconomicIndicatorListResponse, error) { q := url.Values{} if country != nil { q.Set("country", macrodataCountryToAPIValue(*country)) @@ -1636,15 +1636,15 @@ func (c *FundamentalContext) MacrodataIndicators( if limit != nil { q.Set("limit", fmt.Sprintf("%d", *limit)) } - var resp jsontypes.MacrodataIndicatorListResponse + var resp jsontypes.MacroeconomicIndicatorListResponse if err := c.httpClient.Get(ctx, "/v1/quote/macrodata", q, &resp); err != nil { return nil, err } - out := make([]MacrodataIndicator, 0, len(resp.Data)) + out := make([]MacroeconomicIndicator, 0, len(resp.Data)) for _, item := range resp.Data { - out = append(out, convertMacrodataIndicator(&item)) + out = append(out, convertMacroeconomicIndicator(&item)) } - return &MacrodataIndicatorListResponse{Data: out, Count: resp.Count}, nil + return &MacroeconomicIndicatorListResponse{Data: out, Count: resp.Count}, nil } // Macrodata fetches historical data for a specific macroeconomic indicator. @@ -1660,7 +1660,7 @@ func (c *FundamentalContext) Macrodata( endDate *string, offset *int32, limit *int32, -) (*MacrodataResponse, error) { +) (*MacroeconomicResponse, error) { q := url.Values{} if startDate != nil { q.Set("start_time", *startDate+"T00:00:00Z") @@ -1674,7 +1674,7 @@ func (c *FundamentalContext) Macrodata( if limit != nil { q.Set("limit", fmt.Sprintf("%d", *limit)) } - var resp jsontypes.MacrodataResponse + var resp jsontypes.MacroeconomicResponse path := "/v1/quote/macrodata/" + indicatorCode if err := c.httpClient.Get(ctx, path, q, &resp); err != nil { return nil, err @@ -1683,8 +1683,8 @@ func (c *FundamentalContext) Macrodata( for _, d := range resp.Data { data = append(data, convertMacrodata(&d)) } - return &MacrodataResponse{ - Info: convertMacrodataIndicator(&resp.Info), + return &MacroeconomicResponse{ + Info: convertMacroeconomicIndicator(&resp.Info), Data: data, Count: resp.Count, }, nil @@ -1710,8 +1710,8 @@ func parseOptionalRFC3339(s string) *time.Time { return &t } -func convertMacrodataIndicator(j *jsontypes.MacrodataIndicator) MacrodataIndicator { - return MacrodataIndicator{ +func convertMacroeconomicIndicator(j *jsontypes.MacroeconomicIndicator) MacroeconomicIndicator { + return MacroeconomicIndicator{ IndicatorCode: j.IndicatorCode, SourceOrg: j.SourceOrg, Country: j.Country, @@ -1739,19 +1739,19 @@ func convertMacrodata(j *jsontypes.Macrodata) Macrodata { } } -func macrodataCountryToAPIValue(c MacrodataCountry) string { +func macrodataCountryToAPIValue(c MacroeconomicCountry) string { switch c { - case MacrodataCountryHK: + case MacroeconomicCountryHK: return "Hong Kong SAR China" - case MacrodataCountryCN: + case MacroeconomicCountryCN: return "China (Mainland)" - case MacrodataCountryUS: + case MacroeconomicCountryUS: return "United States" - case MacrodataCountryEU: + case MacroeconomicCountryEU: return "Euro Zone" - case MacrodataCountryJP: + case MacroeconomicCountryJP: return "Japan" - case MacrodataCountrySG: + case MacroeconomicCountrySG: return "Singapore" default: return string(c) diff --git a/fundamental/jsontypes/types.go b/fundamental/jsontypes/types.go index 4831847..d7c63f8 100644 --- a/fundamental/jsontypes/types.go +++ b/fundamental/jsontypes/types.go @@ -806,8 +806,8 @@ type MultiLanguageText struct { TraditionalChinese string `json:"traditional_chinese"` } -// MacrodataIndicator is the metadata for one macroeconomic indicator. -type MacrodataIndicator struct { +// MacroeconomicIndicator is the metadata for one macroeconomic indicator. +type MacroeconomicIndicator struct { IndicatorCode string `json:"indicator_code"` SourceOrg string `json:"source_org"` Country string `json:"country"` @@ -820,9 +820,9 @@ type MacrodataIndicator struct { StartDate string `json:"start_date"` } -// MacrodataIndicatorListResponse is the raw response for GET /v1/quote/macrodata. -type MacrodataIndicatorListResponse struct { - Data []MacrodataIndicator `json:"list"` +// MacroeconomicIndicatorListResponse is the raw response for GET /v1/quote/macrodata. +type MacroeconomicIndicatorListResponse struct { + Data []MacroeconomicIndicator `json:"list"` Count int32 `json:"count"` } @@ -840,10 +840,10 @@ type Macrodata struct { UnitPrefix MultiLanguageText `json:"unit_prefix"` } -// MacrodataResponse is the raw response for +// MacroeconomicResponse is the raw response for // GET /v1/quote/macrodata/{indicator_code}. -type MacrodataResponse struct { - Info MacrodataIndicator `json:"info"` +type MacroeconomicResponse struct { + Info MacroeconomicIndicator `json:"info"` Data []Macrodata `json:"data"` Count int32 `json:"count"` } diff --git a/fundamental/types.go b/fundamental/types.go index 10f46f8..9fc5a2d 100644 --- a/fundamental/types.go +++ b/fundamental/types.go @@ -1363,37 +1363,37 @@ type MultiLanguageText struct { TraditionalChinese string } -// MacrodataCountry is a country code for filtering macroeconomic indicators. -type MacrodataCountry string +// MacroeconomicCountry is a country code for filtering macroeconomic indicators. +type MacroeconomicCountry string const ( - MacrodataCountryHK MacrodataCountry = "HK" // Hong Kong SAR China - MacrodataCountryCN MacrodataCountry = "CN" // China (Mainland) - MacrodataCountryUS MacrodataCountry = "US" // United States - MacrodataCountryEU MacrodataCountry = "EU" // Euro Zone - MacrodataCountryJP MacrodataCountry = "JP" // Japan - MacrodataCountrySG MacrodataCountry = "SG" // Singapore + MacroeconomicCountryHK MacroeconomicCountry = "HK" // Hong Kong SAR China + MacroeconomicCountryCN MacroeconomicCountry = "CN" // China (Mainland) + MacroeconomicCountryUS MacroeconomicCountry = "US" // United States + MacroeconomicCountryEU MacroeconomicCountry = "EU" // Euro Zone + MacroeconomicCountryJP MacroeconomicCountry = "JP" // Japan + MacroeconomicCountrySG MacroeconomicCountry = "SG" // Singapore ) -// MacrodataImportance is the importance level of a macroeconomic indicator. -type MacrodataImportance int32 +// MacroeconomicImportance is the importance level of a macroeconomic indicator. +type MacroeconomicImportance int32 const ( - MacrodataImportanceLow MacrodataImportance = 1 - MacrodataImportanceMedium MacrodataImportance = 2 - MacrodataImportanceHigh MacrodataImportance = 3 + MacroeconomicImportanceLow MacroeconomicImportance = 1 + MacroeconomicImportanceMedium MacroeconomicImportance = 2 + MacroeconomicImportanceHigh MacroeconomicImportance = 3 ) -// MacrodataIndicatorListResponse is the response for FundamentalContext.MacrodataIndicators. -type MacrodataIndicatorListResponse struct { +// MacroeconomicIndicatorListResponse is the response for FundamentalContext.MacroeconomicIndicators. +type MacroeconomicIndicatorListResponse struct { // Data is the list of indicators. - Data []MacrodataIndicator + Data []MacroeconomicIndicator // Count is the total number of indicators matching the query. Count int32 } -// MacrodataIndicator is the metadata for one macroeconomic indicator. -type MacrodataIndicator struct { +// MacroeconomicIndicator is the metadata for one macroeconomic indicator. +type MacroeconomicIndicator struct { // IndicatorCode is the external vendor code (input to Macrodata). IndicatorCode string SourceOrg string @@ -1424,9 +1424,9 @@ type Macrodata struct { UnitPrefix MultiLanguageText } -// MacrodataResponse is the response for FundamentalContext.Macrodata. -type MacrodataResponse struct { - Info MacrodataIndicator +// MacroeconomicResponse is the response for FundamentalContext.Macrodata. +type MacroeconomicResponse struct { + Info MacroeconomicIndicator Data []Macrodata // Count is the total number of historical data points. Count int32 From 090f4b5ce553242e48a0a53d4b0bdb027267dbc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A2=81=E7=AB=A0=E6=B4=AA?= Date: Thu, 11 Jun 2026 10:05:42 +0800 Subject: [PATCH 05/25] refactor: rename Macrodata (data point type) to Macroeconomic Co-Authored-By: Claude Sonnet 4.6 (1M context) --- fundamental/context.go | 12 ++++++------ fundamental/jsontypes/types.go | 6 +++--- fundamental/types.go | 10 +++++----- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/fundamental/context.go b/fundamental/context.go index 3ef1e16..1c29ac6 100644 --- a/fundamental/context.go +++ b/fundamental/context.go @@ -1612,7 +1612,7 @@ func convertFinancialReportSnapshot(j *jsontypes.FinancialReportSnapshot) *Finan } } -// ─── Macrodata ──────────────────────────────────────────────────── +// ─── Macroeconomic ──────────────────────────────────────────────────── // MacroeconomicIndicators fetches the list of available macroeconomic indicators. // @@ -1647,13 +1647,13 @@ func (c *FundamentalContext) MacroeconomicIndicators( return &MacroeconomicIndicatorListResponse{Data: out, Count: resp.Count}, nil } -// Macrodata fetches historical data for a specific macroeconomic indicator. +// Macroeconomic fetches historical data for a specific macroeconomic indicator. // // startDate and endDate are date strings in "YYYY-MM-DD" format. // startDate is sent as YYYY-MM-DDT00:00:00Z; endDate is sent as YYYY-MM-DDT23:59:59Z. // // Path: GET /v1/quote/macrodata/{indicator_code} -func (c *FundamentalContext) Macrodata( +func (c *FundamentalContext) Macroeconomic( ctx context.Context, indicatorCode string, startDate *string, @@ -1679,7 +1679,7 @@ func (c *FundamentalContext) Macrodata( if err := c.httpClient.Get(ctx, path, q, &resp); err != nil { return nil, err } - data := make([]Macrodata, 0, len(resp.Data)) + data := make([]Macroeconomic, 0, len(resp.Data)) for _, d := range resp.Data { data = append(data, convertMacrodata(&d)) } @@ -1725,8 +1725,8 @@ func convertMacroeconomicIndicator(j *jsontypes.MacroeconomicIndicator) Macroeco } } -func convertMacrodata(j *jsontypes.Macrodata) Macrodata { - return Macrodata{ +func convertMacrodata(j *jsontypes.Macroeconomic) Macroeconomic { + return Macroeconomic{ Period: j.Period, ReleaseAt: parseOptionalRFC3339(j.ReleaseAt), ActualValue: j.ActualValue, diff --git a/fundamental/jsontypes/types.go b/fundamental/jsontypes/types.go index d7c63f8..26aa33b 100644 --- a/fundamental/jsontypes/types.go +++ b/fundamental/jsontypes/types.go @@ -826,9 +826,9 @@ type MacroeconomicIndicatorListResponse struct { Count int32 `json:"count"` } -// Macrodata is one historical data point for a macroeconomic +// Macroeconomic is one historical data point for a macroeconomic // indicator. -type Macrodata struct { +type Macroeconomic struct { Period string `json:"period"` ReleaseAt string `json:"release_at"` ActualValue string `json:"actual_value"` @@ -844,6 +844,6 @@ type Macrodata struct { // GET /v1/quote/macrodata/{indicator_code}. type MacroeconomicResponse struct { Info MacroeconomicIndicator `json:"info"` - Data []Macrodata `json:"data"` + Data []Macroeconomic `json:"data"` Count int32 `json:"count"` } diff --git a/fundamental/types.go b/fundamental/types.go index 9fc5a2d..ef68789 100644 --- a/fundamental/types.go +++ b/fundamental/types.go @@ -1394,7 +1394,7 @@ type MacroeconomicIndicatorListResponse struct { // MacroeconomicIndicator is the metadata for one macroeconomic indicator. type MacroeconomicIndicator struct { - // IndicatorCode is the external vendor code (input to Macrodata). + // IndicatorCode is the external vendor code (input to Macroeconomic). IndicatorCode string SourceOrg string Country string @@ -1410,8 +1410,8 @@ type MacroeconomicIndicator struct { StartDate *time.Time } -// Macrodata is one historical data point for a macroeconomic indicator. -type Macrodata struct { +// Macroeconomic is one historical data point for a macroeconomic indicator. +type Macroeconomic struct { // Period is the statistical period (e.g. "2024-Q1", "2024-03"). Period string ReleaseAt *time.Time @@ -1424,10 +1424,10 @@ type Macrodata struct { UnitPrefix MultiLanguageText } -// MacroeconomicResponse is the response for FundamentalContext.Macrodata. +// MacroeconomicResponse is the response for FundamentalContext.Macroeconomic. type MacroeconomicResponse struct { Info MacroeconomicIndicator - Data []Macrodata + Data []Macroeconomic // Count is the total number of historical data points. Count int32 } From c2a97c9090995481bc92431277faf2912b08fe14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A2=81=E7=AB=A0=E6=B4=AA?= Date: Thu, 11 Jun 2026 10:07:40 +0800 Subject: [PATCH 06/25] refactor: rename macrodataCountryToAPIValue to macroeconomicCountryToAPIValue Co-Authored-By: Claude Sonnet 4.6 (1M context) --- fundamental/context.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fundamental/context.go b/fundamental/context.go index 1c29ac6..e200a71 100644 --- a/fundamental/context.go +++ b/fundamental/context.go @@ -1628,7 +1628,7 @@ func (c *FundamentalContext) MacroeconomicIndicators( ) (*MacroeconomicIndicatorListResponse, error) { q := url.Values{} if country != nil { - q.Set("country", macrodataCountryToAPIValue(*country)) + q.Set("country", macroeconomicCountryToAPIValue(*country)) } if offset != nil { q.Set("offset", fmt.Sprintf("%d", *offset)) @@ -1739,7 +1739,7 @@ func convertMacrodata(j *jsontypes.Macroeconomic) Macroeconomic { } } -func macrodataCountryToAPIValue(c MacroeconomicCountry) string { +func macroeconomicCountryToAPIValue(c MacroeconomicCountry) string { switch c { case MacroeconomicCountryHK: return "Hong Kong SAR China" From 9b6c28996fcdd34ed5b19930eb4246103b190b3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A2=81=E7=AB=A0=E6=B4=AA?= Date: Thu, 11 Jun 2026 10:09:51 +0800 Subject: [PATCH 07/25] refactor: rename all remaining macrodata -> macroeconomic Co-Authored-By: Claude Sonnet 4.6 (1M context) --- fundamental/context.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fundamental/context.go b/fundamental/context.go index e200a71..9373da2 100644 --- a/fundamental/context.go +++ b/fundamental/context.go @@ -1681,7 +1681,7 @@ func (c *FundamentalContext) Macroeconomic( } data := make([]Macroeconomic, 0, len(resp.Data)) for _, d := range resp.Data { - data = append(data, convertMacrodata(&d)) + data = append(data, convertMacroeconomic(&d)) } return &MacroeconomicResponse{ Info: convertMacroeconomicIndicator(&resp.Info), @@ -1725,7 +1725,7 @@ func convertMacroeconomicIndicator(j *jsontypes.MacroeconomicIndicator) Macroeco } } -func convertMacrodata(j *jsontypes.Macroeconomic) Macroeconomic { +func convertMacroeconomic(j *jsontypes.Macroeconomic) Macroeconomic { return Macroeconomic{ Period: j.Period, ReleaseAt: parseOptionalRFC3339(j.ReleaseAt), From 6d1f6009113983b1b5e28eabafd5e67a32d9bba7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A2=81=E7=AB=A0=E6=B4=AA?= Date: Thu, 11 Jun 2026 10:35:40 +0800 Subject: [PATCH 08/25] docs: update CHANGELOG with correct macroeconomic naming --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 85a88e7..5fa9287 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,9 +13,9 @@ - `QuoteContext.SymbolToCounterIds` — batch convert symbols to counter IDs via `POST /v1/quote/symbol-to-counter-ids` - `QuoteContext.ResolveCounterIds` — local-first counter ID resolution with batched remote fallback and automatic caching - `FundamentalContext.EtfAssetAllocation` — ETF asset allocation (holdings / regional / asset class / industry) via `GET /v1/quote/etf-asset-allocation` -- `FundamentalContext.MacrodataIndicators` — list macroeconomic indicators via `GET /v1/quote/macrodata` -- `FundamentalContext.Macrodata` — historical data for a specific indicator via `GET /v1/quote/macrodata/{indicator_code}`; `startDate` / `endDate` accept `"YYYY-MM-DD"` strings -- New types: `MultiLanguageText`, `MacrodataIndicator`, `Macrodata`, `MacrodataResponse` +- `FundamentalContext.MacroeconomicIndicators(country, offset, limit)` — list macroeconomic indicators via `GET /v1/quote/macrodata`; filter by `MacroeconomicCountry` (HK/CN/US/EU/JP/SG); response includes `Count` +- `FundamentalContext.Macroeconomic(indicatorCode, startDate, endDate, offset, limit)` — historical data for a specific indicator via `GET /v1/quote/macrodata/{indicator_code}`; `startDate` / `endDate` accept `"YYYY-MM-DD"` strings; response includes `Count` +- New types: `MultiLanguageText`, `MacroeconomicCountry`, `MacroeconomicImportance`, `MacroeconomicIndicator`, `MacroeconomicIndicatorListResponse`, `Macroeconomic`, `MacroeconomicResponse` ## [v0.24.2] - 2026-06-02 From db4335c8af10eb4c1fd03d89a6f448460510cf8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A2=81=E7=AB=A0=E6=B4=AA?= Date: Fri, 12 Jun 2026 13:54:35 +0800 Subject: [PATCH 09/25] feat: switch macroeconomic APIs to v2 endpoints - macroeconomic_indicators / MacroeconomicIndicatorsV2: GET /v2/quote/macrodata - market defaults to 'ALL' when country is nil - new keyword param for fuzzy name filter - maps V2MacroIndicator to existing MacroeconomicIndicator type - macroeconomic / MacroeconomicV2: GET /v2/quote/macrodata/{id} - new sort param (asc/desc) - maps V2MacroIndicatorDetail to existing MacroeconomicResponse type - original method signatures unchanged; _v2 variants add new params Co-Authored-By: Claude Sonnet 4.6 (1M context) --- fundamental/context.go | 122 ++++++++++++++++++++++++--------- fundamental/jsontypes/types.go | 40 ++++++++++- 2 files changed, 130 insertions(+), 32 deletions(-) diff --git a/fundamental/context.go b/fundamental/context.go index 9373da2..c16c4ca 100644 --- a/fundamental/context.go +++ b/fundamental/context.go @@ -1616,43 +1616,61 @@ func convertFinancialReportSnapshot(j *jsontypes.FinancialReportSnapshot) *Finan // MacroeconomicIndicators fetches the list of available macroeconomic indicators. // -// Pass country to filter by country code (e.g. MacroeconomicCountryUS). -// Pass nil for all countries. +// Pass country to filter by country code (e.g. MacroeconomicCountryUS), nil for all. +// offset and limit are kept for backward compatibility but ignored by v2. // -// Path: GET /v1/quote/macrodata +// Path: GET /v2/quote/macrodata func (c *FundamentalContext) MacroeconomicIndicators( ctx context.Context, country *MacroeconomicCountry, offset *int32, limit *int32, ) (*MacroeconomicIndicatorListResponse, error) { - q := url.Values{} + return c.MacroeconomicIndicatorsV2(ctx, country, nil, offset, limit) +} + +// MacroeconomicIndicatorsV2 fetches macroeconomic indicators with optional keyword filter. +// +// keyword fuzzy-filters indicators by name (case-insensitive). +// +// Path: GET /v2/quote/macrodata +func (c *FundamentalContext) MacroeconomicIndicatorsV2( + ctx context.Context, + country *MacroeconomicCountry, + keyword *string, + offset *int32, + limit *int32, +) (*MacroeconomicIndicatorListResponse, error) { + market := "ALL" if country != nil { - q.Set("country", macroeconomicCountryToAPIValue(*country)) - } - if offset != nil { - q.Set("offset", fmt.Sprintf("%d", *offset)) + market = string(*country) } - if limit != nil { - q.Set("limit", fmt.Sprintf("%d", *limit)) + q := url.Values{} + q.Set("market", market) + if keyword != nil && *keyword != "" { + q.Set("keyword", *keyword) } - var resp jsontypes.MacroeconomicIndicatorListResponse - if err := c.httpClient.Get(ctx, "/v1/quote/macrodata", q, &resp); err != nil { + var resp jsontypes.V2MacroeconomicIndicatorListResponse + if err := c.httpClient.Get(ctx, "/v2/quote/macrodata", q, &resp); err != nil { return nil, err } - out := make([]MacroeconomicIndicator, 0, len(resp.Data)) - for _, item := range resp.Data { - out = append(out, convertMacroeconomicIndicator(&item)) + out := make([]MacroeconomicIndicator, 0, len(resp.IndicatorList)) + for _, item := range resp.IndicatorList { + out = append(out, MacroeconomicIndicator{ + IndicatorCode: fmt.Sprintf("%d", item.IndicatorID), + Country: item.Market, + Name: MultiLanguageText{English: item.IndicatorName}, + }) } - return &MacroeconomicIndicatorListResponse{Data: out, Count: resp.Count}, nil + return &MacroeconomicIndicatorListResponse{Data: out, Count: int32(len(out))}, nil } // Macroeconomic fetches historical data for a specific macroeconomic indicator. // -// startDate and endDate are date strings in "YYYY-MM-DD" format. -// startDate is sent as YYYY-MM-DDT00:00:00Z; endDate is sent as YYYY-MM-DDT23:59:59Z. +// indicatorCode is the indicator ID. startDate / endDate are "YYYY-MM-DD" strings. +// offset is kept for backward compatibility but ignored by v2. // -// Path: GET /v1/quote/macrodata/{indicator_code} +// Path: GET /v2/quote/macrodata/{indicator_id} func (c *FundamentalContext) Macroeconomic( ctx context.Context, indicatorCode string, @@ -1660,33 +1678,75 @@ func (c *FundamentalContext) Macroeconomic( endDate *string, offset *int32, limit *int32, +) (*MacroeconomicResponse, error) { + return c.MacroeconomicV2(ctx, indicatorCode, startDate, endDate, offset, limit, nil) +} + +// MacroeconomicV2 fetches historical data with sort support. +// +// sort can be "asc" or "desc". +// +// Path: GET /v2/quote/macrodata/{indicator_id} +func (c *FundamentalContext) MacroeconomicV2( + ctx context.Context, + indicatorCode string, + startDate *string, + endDate *string, + offset *int32, + limit *int32, + sort *string, ) (*MacroeconomicResponse, error) { q := url.Values{} if startDate != nil { - q.Set("start_time", *startDate+"T00:00:00Z") + q.Set("start_date", *startDate) } if endDate != nil { - q.Set("end_time", *endDate+"T23:59:59Z") - } - if offset != nil { - q.Set("offset", fmt.Sprintf("%d", *offset)) + q.Set("end_date", *endDate) } if limit != nil { q.Set("limit", fmt.Sprintf("%d", *limit)) } - var resp jsontypes.MacroeconomicResponse - path := "/v1/quote/macrodata/" + indicatorCode + if sort != nil { + q.Set("sort", *sort) + } + var resp jsontypes.V2MacroeconomicResponse + path := "/v2/quote/macrodata/" + indicatorCode if err := c.httpClient.Get(ctx, path, q, &resp); err != nil { return nil, err } - data := make([]Macroeconomic, 0, len(resp.Data)) - for _, d := range resp.Data { - data = append(data, convertMacroeconomic(&d)) + // Take first item (single-indicator query) + if len(resp.IndicatorDataList) == 0 { + return &MacroeconomicResponse{}, nil + } + detail := resp.IndicatorDataList[0] + data := make([]Macroeconomic, 0, len(detail.IndicatorData)) + for _, d := range detail.IndicatorData { + var releaseAt *time.Time + if d.PublishedTime != "" { + if t, err := time.Parse("2006-01-02T15:04:05", d.PublishedTime); err == nil { + ut := t.UTC() + releaseAt = &ut + } + } + data = append(data, Macroeconomic{ + Period: d.ObservationDate, + ReleaseAt: releaseAt, + ActualValue: d.ActualData, + PreviousValue: d.PreviousData, + ForecastValue: d.EstimatedData, + Unit: MultiLanguageText{English: detail.Unit}, + }) } + count := int32(len(data)) return &MacroeconomicResponse{ - Info: convertMacroeconomicIndicator(&resp.Info), + Info: MacroeconomicIndicator{ + IndicatorCode: fmt.Sprintf("%d", detail.IndicatorID), + Country: detail.Market, + Name: MultiLanguageText{English: detail.IndicatorName}, + Describe: MultiLanguageText{English: detail.Description}, + }, Data: data, - Count: resp.Count, + Count: count, }, nil } diff --git a/fundamental/jsontypes/types.go b/fundamental/jsontypes/types.go index 26aa33b..53383e3 100644 --- a/fundamental/jsontypes/types.go +++ b/fundamental/jsontypes/types.go @@ -845,5 +845,43 @@ type Macroeconomic struct { type MacroeconomicResponse struct { Info MacroeconomicIndicator `json:"info"` Data []Macroeconomic `json:"data"` - Count int32 `json:"count"` + Count int32 `json:"count"` +} + +// ── v2 wire types ───────────────────────────────────────────────── + +// V2MacroeconomicIndicator is one indicator from GET /v2/quote/macrodata. +type V2MacroeconomicIndicator struct { + IndicatorID int32 `json:"indicator_id"` + IndicatorName string `json:"indicator_name"` + Market string `json:"market"` +} + +// V2MacroeconomicIndicatorListResponse is the response for GET /v2/quote/macrodata. +type V2MacroeconomicIndicatorListResponse struct { + IndicatorList []V2MacroeconomicIndicator `json:"indicator_list"` +} + +// V2IndicatorDataDetail is one data point from GET /v2/quote/macrodata/:id. +type V2IndicatorDataDetail struct { + ActualData string `json:"actual_data"` + PreviousData string `json:"previous_data"` + EstimatedData string `json:"estimated_data"` + PublishedTime string `json:"published_time"` + ObservationDate string `json:"observation_date"` +} + +// V2MacroeconomicDetail is one indicator with data from GET /v2/quote/macrodata/:id. +type V2MacroeconomicDetail struct { + IndicatorID int32 `json:"indicator_id"` + IndicatorName string `json:"indicator_name"` + Unit string `json:"unit"` + Description string `json:"description"` + Market string `json:"market"` + IndicatorData []V2IndicatorDataDetail `json:"indicator_data"` +} + +// V2MacroeconomicResponse is the response for GET /v2/quote/macrodata/:id. +type V2MacroeconomicResponse struct { + IndicatorDataList []V2MacroeconomicDetail `json:"indicator_data_list"` } From bdfddeb055b0ebd5790f241abb0918a25f5b3bd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A2=81=E7=AB=A0=E6=B4=AA?= Date: Fri, 12 Jun 2026 14:01:20 +0800 Subject: [PATCH 10/25] feat: pass offset/limit to v2 and handle total from pagination response Co-Authored-By: Claude Sonnet 4.6 (1M context) --- fundamental/context.go | 20 ++++++++++++++++++-- fundamental/jsontypes/types.go | 2 ++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/fundamental/context.go b/fundamental/context.go index c16c4ca..96cbda2 100644 --- a/fundamental/context.go +++ b/fundamental/context.go @@ -1650,6 +1650,12 @@ func (c *FundamentalContext) MacroeconomicIndicatorsV2( if keyword != nil && *keyword != "" { q.Set("keyword", *keyword) } + if offset != nil { + q.Set("offset", fmt.Sprintf("%d", *offset)) + } + if limit != nil { + q.Set("limit", fmt.Sprintf("%d", *limit)) + } var resp jsontypes.V2MacroeconomicIndicatorListResponse if err := c.httpClient.Get(ctx, "/v2/quote/macrodata", q, &resp); err != nil { return nil, err @@ -1662,7 +1668,11 @@ func (c *FundamentalContext) MacroeconomicIndicatorsV2( Name: MultiLanguageText{English: item.IndicatorName}, }) } - return &MacroeconomicIndicatorListResponse{Data: out, Count: int32(len(out))}, nil + count := resp.Total + if count == 0 { + count = int32(len(out)) + } + return &MacroeconomicIndicatorListResponse{Data: out, Count: count}, nil } // Macroeconomic fetches historical data for a specific macroeconomic indicator. @@ -1703,6 +1713,9 @@ func (c *FundamentalContext) MacroeconomicV2( if endDate != nil { q.Set("end_date", *endDate) } + if offset != nil { + q.Set("offset", fmt.Sprintf("%d", *offset)) + } if limit != nil { q.Set("limit", fmt.Sprintf("%d", *limit)) } @@ -1737,7 +1750,10 @@ func (c *FundamentalContext) MacroeconomicV2( Unit: MultiLanguageText{English: detail.Unit}, }) } - count := int32(len(data)) + count := resp.Total + if count == 0 { + count = int32(len(data)) + } return &MacroeconomicResponse{ Info: MacroeconomicIndicator{ IndicatorCode: fmt.Sprintf("%d", detail.IndicatorID), diff --git a/fundamental/jsontypes/types.go b/fundamental/jsontypes/types.go index 53383e3..d3f2dee 100644 --- a/fundamental/jsontypes/types.go +++ b/fundamental/jsontypes/types.go @@ -860,6 +860,7 @@ type V2MacroeconomicIndicator struct { // V2MacroeconomicIndicatorListResponse is the response for GET /v2/quote/macrodata. type V2MacroeconomicIndicatorListResponse struct { IndicatorList []V2MacroeconomicIndicator `json:"indicator_list"` + Total int32 `json:"total"` } // V2IndicatorDataDetail is one data point from GET /v2/quote/macrodata/:id. @@ -884,4 +885,5 @@ type V2MacroeconomicDetail struct { // V2MacroeconomicResponse is the response for GET /v2/quote/macrodata/:id. type V2MacroeconomicResponse struct { IndicatorDataList []V2MacroeconomicDetail `json:"indicator_data_list"` + Total int32 `json:"total"` } From 7686d4b7c2d6d57b4186e514b4c86d7cfb70b026 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A2=81=E7=AB=A0=E6=B4=AA?= Date: Fri, 12 Jun 2026 14:40:55 +0800 Subject: [PATCH 11/25] fix: update v2 detail response to use indicator (single) instead of indicator_data_list GetMacroIndicatorHistoryResp returns: indicator: MacroIndicatorDetail (single object) total: int32 Also confirm list endpoint has offset/limit/total. Co-Authored-By: Claude Sonnet 4.6 (1M context) --- fundamental/context.go | 6 +----- fundamental/jsontypes/types.go | 6 +++--- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/fundamental/context.go b/fundamental/context.go index 96cbda2..8596a82 100644 --- a/fundamental/context.go +++ b/fundamental/context.go @@ -1727,11 +1727,7 @@ func (c *FundamentalContext) MacroeconomicV2( if err := c.httpClient.Get(ctx, path, q, &resp); err != nil { return nil, err } - // Take first item (single-indicator query) - if len(resp.IndicatorDataList) == 0 { - return &MacroeconomicResponse{}, nil - } - detail := resp.IndicatorDataList[0] + detail := resp.Indicator data := make([]Macroeconomic, 0, len(detail.IndicatorData)) for _, d := range detail.IndicatorData { var releaseAt *time.Time diff --git a/fundamental/jsontypes/types.go b/fundamental/jsontypes/types.go index d3f2dee..f969ed6 100644 --- a/fundamental/jsontypes/types.go +++ b/fundamental/jsontypes/types.go @@ -882,8 +882,8 @@ type V2MacroeconomicDetail struct { IndicatorData []V2IndicatorDataDetail `json:"indicator_data"` } -// V2MacroeconomicResponse is the response for GET /v2/quote/macrodata/:id. +// V2MacroeconomicResponse is the response for GET /v2/quote/macrodata/:id (GetMacroIndicatorHistoryResp). type V2MacroeconomicResponse struct { - IndicatorDataList []V2MacroeconomicDetail `json:"indicator_data_list"` - Total int32 `json:"total"` + Indicator V2MacroeconomicDetail `json:"indicator"` + Total int32 `json:"total"` } From 1a1563d3183a37b91697e556d48d42eb220ddd84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A2=81=E7=AB=A0=E6=B4=AA?= Date: Fri, 12 Jun 2026 15:26:53 +0800 Subject: [PATCH 12/25] fix: support both indicator/indicator_data_list response formats; fix published_time RFC3339 parsing Co-Authored-By: Claude Sonnet 4.6 (1M context) --- fundamental/context.go | 17 +++++++++++++---- fundamental/jsontypes/types.go | 8 +++++--- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/fundamental/context.go b/fundamental/context.go index 8596a82..4ef81ff 100644 --- a/fundamental/context.go +++ b/fundamental/context.go @@ -1727,14 +1727,23 @@ func (c *FundamentalContext) MacroeconomicV2( if err := c.httpClient.Get(ctx, path, q, &resp); err != nil { return nil, err } - detail := resp.Indicator + // Support both new format (indicator) and old format (indicator_data_list) + var detail jsontypes.V2MacroeconomicDetail + if resp.Indicator.IndicatorID != 0 { + detail = resp.Indicator + } else if len(resp.IndicatorDataList) > 0 { + detail = resp.IndicatorDataList[0] + } data := make([]Macroeconomic, 0, len(detail.IndicatorData)) for _, d := range detail.IndicatorData { var releaseAt *time.Time if d.PublishedTime != "" { - if t, err := time.Parse("2006-01-02T15:04:05", d.PublishedTime); err == nil { - ut := t.UTC() - releaseAt = &ut + for _, layout := range []string{time.RFC3339, "2006-01-02T15:04:05Z", "2006-01-02T15:04:05"} { + if t, err := time.Parse(layout, d.PublishedTime); err == nil { + ut := t.UTC() + releaseAt = &ut + break + } } } data = append(data, Macroeconomic{ diff --git a/fundamental/jsontypes/types.go b/fundamental/jsontypes/types.go index f969ed6..b651e10 100644 --- a/fundamental/jsontypes/types.go +++ b/fundamental/jsontypes/types.go @@ -882,8 +882,10 @@ type V2MacroeconomicDetail struct { IndicatorData []V2IndicatorDataDetail `json:"indicator_data"` } -// V2MacroeconomicResponse is the response for GET /v2/quote/macrodata/:id (GetMacroIndicatorHistoryResp). +// V2MacroeconomicResponse is the response for GET /v2/quote/macrodata/:id. +// Supports both new format (indicator) and old format (indicator_data_list). type V2MacroeconomicResponse struct { - Indicator V2MacroeconomicDetail `json:"indicator"` - Total int32 `json:"total"` + Indicator V2MacroeconomicDetail `json:"indicator"` + IndicatorDataList []V2MacroeconomicDetail `json:"indicator_data_list"` + Total int32 `json:"total"` } From 37e8e99e33ae2a3603755ace0f40b4ec6305bc23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A2=81=E7=AB=A0=E6=B4=AA?= Date: Fri, 12 Jun 2026 15:35:11 +0800 Subject: [PATCH 13/25] refactor: remove indicator_data_list compatibility, use indicator only Co-Authored-By: Claude Sonnet 4.6 (1M context) --- fundamental/context.go | 8 +------- fundamental/jsontypes/types.go | 8 +++----- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/fundamental/context.go b/fundamental/context.go index 4ef81ff..fc52694 100644 --- a/fundamental/context.go +++ b/fundamental/context.go @@ -1727,13 +1727,7 @@ func (c *FundamentalContext) MacroeconomicV2( if err := c.httpClient.Get(ctx, path, q, &resp); err != nil { return nil, err } - // Support both new format (indicator) and old format (indicator_data_list) - var detail jsontypes.V2MacroeconomicDetail - if resp.Indicator.IndicatorID != 0 { - detail = resp.Indicator - } else if len(resp.IndicatorDataList) > 0 { - detail = resp.IndicatorDataList[0] - } + detail := resp.Indicator data := make([]Macroeconomic, 0, len(detail.IndicatorData)) for _, d := range detail.IndicatorData { var releaseAt *time.Time diff --git a/fundamental/jsontypes/types.go b/fundamental/jsontypes/types.go index b651e10..f969ed6 100644 --- a/fundamental/jsontypes/types.go +++ b/fundamental/jsontypes/types.go @@ -882,10 +882,8 @@ type V2MacroeconomicDetail struct { IndicatorData []V2IndicatorDataDetail `json:"indicator_data"` } -// V2MacroeconomicResponse is the response for GET /v2/quote/macrodata/:id. -// Supports both new format (indicator) and old format (indicator_data_list). +// V2MacroeconomicResponse is the response for GET /v2/quote/macrodata/:id (GetMacroIndicatorHistoryResp). type V2MacroeconomicResponse struct { - Indicator V2MacroeconomicDetail `json:"indicator"` - IndicatorDataList []V2MacroeconomicDetail `json:"indicator_data_list"` - Total int32 `json:"total"` + Indicator V2MacroeconomicDetail `json:"indicator"` + Total int32 `json:"total"` } From 50c150bfca75c881fb5c41f6175e61d525c0056a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A2=81=E7=AB=A0=E6=B4=AA?= Date: Fri, 12 Jun 2026 17:08:01 +0800 Subject: [PATCH 14/25] feat: add periodicity/description/importance to v2 MacroeconomicIndicator list mapping Co-Authored-By: Claude Sonnet 4.6 (1M context) --- fundamental/context.go | 3 +++ fundamental/jsontypes/types.go | 3 +++ 2 files changed, 6 insertions(+) diff --git a/fundamental/context.go b/fundamental/context.go index fc52694..88c1408 100644 --- a/fundamental/context.go +++ b/fundamental/context.go @@ -1666,6 +1666,9 @@ func (c *FundamentalContext) MacroeconomicIndicatorsV2( IndicatorCode: fmt.Sprintf("%d", item.IndicatorID), Country: item.Market, Name: MultiLanguageText{English: item.IndicatorName}, + Periodicity: item.Periodicity, + Describe: MultiLanguageText{English: item.Description}, + Importance: item.Importance, }) } count := resp.Total diff --git a/fundamental/jsontypes/types.go b/fundamental/jsontypes/types.go index f969ed6..49374e9 100644 --- a/fundamental/jsontypes/types.go +++ b/fundamental/jsontypes/types.go @@ -855,6 +855,9 @@ type V2MacroeconomicIndicator struct { IndicatorID int32 `json:"indicator_id"` IndicatorName string `json:"indicator_name"` Market string `json:"market"` + Periodicity string `json:"periodicity"` + Description string `json:"description"` + Importance int32 `json:"importance"` } // V2MacroeconomicIndicatorListResponse is the response for GET /v2/quote/macrodata. From 62e11ac8d9c1943483e599ca7bfd1b2efa7b6a03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A2=81=E7=AB=A0=E6=B4=AA?= Date: Fri, 12 Jun 2026 17:17:03 +0800 Subject: [PATCH 15/25] fix: use frequence (not periodicity) as field name from v2 proto MacroIndicatorItem.frequence maps to MacroeconomicIndicator.Periodicity Co-Authored-By: Claude Sonnet 4.6 (1M context) --- fundamental/context.go | 2 +- fundamental/jsontypes/types.go | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/fundamental/context.go b/fundamental/context.go index 88c1408..46715f8 100644 --- a/fundamental/context.go +++ b/fundamental/context.go @@ -1666,7 +1666,7 @@ func (c *FundamentalContext) MacroeconomicIndicatorsV2( IndicatorCode: fmt.Sprintf("%d", item.IndicatorID), Country: item.Market, Name: MultiLanguageText{English: item.IndicatorName}, - Periodicity: item.Periodicity, + Periodicity: item.Frequence, Describe: MultiLanguageText{English: item.Description}, Importance: item.Importance, }) diff --git a/fundamental/jsontypes/types.go b/fundamental/jsontypes/types.go index 49374e9..cf1083f 100644 --- a/fundamental/jsontypes/types.go +++ b/fundamental/jsontypes/types.go @@ -855,9 +855,10 @@ type V2MacroeconomicIndicator struct { IndicatorID int32 `json:"indicator_id"` IndicatorName string `json:"indicator_name"` Market string `json:"market"` - Periodicity string `json:"periodicity"` - Description string `json:"description"` Importance int32 `json:"importance"` + Description string `json:"description"` + // Frequence: day/week/month/quarter/half_year/year + Frequence string `json:"frequence"` } // V2MacroeconomicIndicatorListResponse is the response for GET /v2/quote/macrodata. From 490986a61c6a88a926ea669f6988618e2fe79f74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A2=81=E7=AB=A0=E6=B4=AA?= Date: Fri, 12 Jun 2026 17:27:13 +0800 Subject: [PATCH 16/25] refactor: change MacroeconomicIndicator.Name/Describe from MultiLanguageText to string Co-Authored-By: Claude Sonnet 4.6 (1M context) --- fundamental/context.go | 12 ++++++------ fundamental/types.go | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/fundamental/context.go b/fundamental/context.go index 46715f8..ad537f5 100644 --- a/fundamental/context.go +++ b/fundamental/context.go @@ -1665,9 +1665,9 @@ func (c *FundamentalContext) MacroeconomicIndicatorsV2( out = append(out, MacroeconomicIndicator{ IndicatorCode: fmt.Sprintf("%d", item.IndicatorID), Country: item.Market, - Name: MultiLanguageText{English: item.IndicatorName}, + Name: item.IndicatorName, Periodicity: item.Frequence, - Describe: MultiLanguageText{English: item.Description}, + Describe: item.Description, Importance: item.Importance, }) } @@ -1760,8 +1760,8 @@ func (c *FundamentalContext) MacroeconomicV2( Info: MacroeconomicIndicator{ IndicatorCode: fmt.Sprintf("%d", detail.IndicatorID), Country: detail.Market, - Name: MultiLanguageText{English: detail.IndicatorName}, - Describe: MultiLanguageText{English: detail.Description}, + Name: detail.IndicatorName, + Describe: detail.Description, }, Data: data, Count: count, @@ -1793,11 +1793,11 @@ func convertMacroeconomicIndicator(j *jsontypes.MacroeconomicIndicator) Macroeco IndicatorCode: j.IndicatorCode, SourceOrg: j.SourceOrg, Country: j.Country, - Name: convertMultiLanguageText(j.Name), + Name: j.Name.English, AdjustmentFactor: j.AdjustmentFactor, Periodicity: j.Periodicity, Category: j.Category, - Describe: convertMultiLanguageText(j.Describe), + Describe: j.Describe.English, Importance: j.Importance, StartDate: parseOptionalRFC3339(j.StartDate), } diff --git a/fundamental/types.go b/fundamental/types.go index ef68789..1b93ab3 100644 --- a/fundamental/types.go +++ b/fundamental/types.go @@ -1398,12 +1398,12 @@ type MacroeconomicIndicator struct { IndicatorCode string SourceOrg string Country string - Name MultiLanguageText + Name string AdjustmentFactor string // Periodicity is the release periodicity (e.g. monthly / quarterly). Periodicity string Category string - Describe MultiLanguageText + Describe string // Importance: 1=Low, 2=Medium, 3=High. Importance int32 // StartDate is the start date of data coverage; nil if unset. From 0cd877e8fef8e564e940facab5aaf1b4fe6bd448 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A2=81=E7=AB=A0=E6=B4=AA?= Date: Fri, 12 Jun 2026 17:29:49 +0800 Subject: [PATCH 17/25] refactor: change Macroeconomic.Unit/UnitPrefix from MultiLanguageText to string Co-Authored-By: Claude Sonnet 4.6 (1M context) --- fundamental/context.go | 6 +++--- fundamental/types.go | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/fundamental/context.go b/fundamental/context.go index ad537f5..b316018 100644 --- a/fundamental/context.go +++ b/fundamental/context.go @@ -1749,7 +1749,7 @@ func (c *FundamentalContext) MacroeconomicV2( ActualValue: d.ActualData, PreviousValue: d.PreviousData, ForecastValue: d.EstimatedData, - Unit: MultiLanguageText{English: detail.Unit}, + Unit: detail.Unit, }) } count := resp.Total @@ -1812,8 +1812,8 @@ func convertMacroeconomic(j *jsontypes.Macroeconomic) Macroeconomic { ForecastValue: j.ForecastValue, RevisedValue: j.RevisedValue, NextReleaseAt: parseOptionalRFC3339(j.NextReleaseAt), - Unit: convertMultiLanguageText(j.Unit), - UnitPrefix: convertMultiLanguageText(j.UnitPrefix), + Unit: j.Unit.English, + UnitPrefix: j.UnitPrefix.English, } } diff --git a/fundamental/types.go b/fundamental/types.go index 1b93ab3..15f53c5 100644 --- a/fundamental/types.go +++ b/fundamental/types.go @@ -1420,8 +1420,8 @@ type Macroeconomic struct { ForecastValue string RevisedValue string NextReleaseAt *time.Time - Unit MultiLanguageText - UnitPrefix MultiLanguageText + Unit string + UnitPrefix string } // MacroeconomicResponse is the response for FundamentalContext.Macroeconomic. From 4247332a6b72e445b3fef620838a370dae9b4b94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A2=81=E7=AB=A0=E6=B4=AA?= Date: Fri, 12 Jun 2026 17:36:17 +0800 Subject: [PATCH 18/25] refactor: remove unused helper functions Remove: convertMultiLanguageText, convertMacroeconomicIndicator, convertMacroeconomic, macroeconomicCountryToAPIValue Co-Authored-By: Claude Sonnet 4.6 (1M context) --- fundamental/context.go | 55 ------------------------------------------ 1 file changed, 55 deletions(-) diff --git a/fundamental/context.go b/fundamental/context.go index b316018..dce757d 100644 --- a/fundamental/context.go +++ b/fundamental/context.go @@ -1768,14 +1768,6 @@ func (c *FundamentalContext) MacroeconomicV2( }, nil } -func convertMultiLanguageText(j jsontypes.MultiLanguageText) MultiLanguageText { - return MultiLanguageText{ - English: j.English, - SimplifiedChinese: j.SimplifiedChinese, - TraditionalChinese: j.TraditionalChinese, - } -} - func parseOptionalRFC3339(s string) *time.Time { if s == "" { return nil @@ -1788,50 +1780,3 @@ func parseOptionalRFC3339(s string) *time.Time { return &t } -func convertMacroeconomicIndicator(j *jsontypes.MacroeconomicIndicator) MacroeconomicIndicator { - return MacroeconomicIndicator{ - IndicatorCode: j.IndicatorCode, - SourceOrg: j.SourceOrg, - Country: j.Country, - Name: j.Name.English, - AdjustmentFactor: j.AdjustmentFactor, - Periodicity: j.Periodicity, - Category: j.Category, - Describe: j.Describe.English, - Importance: j.Importance, - StartDate: parseOptionalRFC3339(j.StartDate), - } -} - -func convertMacroeconomic(j *jsontypes.Macroeconomic) Macroeconomic { - return Macroeconomic{ - Period: j.Period, - ReleaseAt: parseOptionalRFC3339(j.ReleaseAt), - ActualValue: j.ActualValue, - PreviousValue: j.PreviousValue, - ForecastValue: j.ForecastValue, - RevisedValue: j.RevisedValue, - NextReleaseAt: parseOptionalRFC3339(j.NextReleaseAt), - Unit: j.Unit.English, - UnitPrefix: j.UnitPrefix.English, - } -} - -func macroeconomicCountryToAPIValue(c MacroeconomicCountry) string { - switch c { - case MacroeconomicCountryHK: - return "Hong Kong SAR China" - case MacroeconomicCountryCN: - return "China (Mainland)" - case MacroeconomicCountryUS: - return "United States" - case MacroeconomicCountryEU: - return "Euro Zone" - case MacroeconomicCountryJP: - return "Japan" - case MacroeconomicCountrySG: - return "Singapore" - default: - return string(c) - } -} From 61abd166434f749b94d6b63b2533cfb490595b21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A2=81=E7=AB=A0=E6=B4=AA?= Date: Fri, 12 Jun 2026 17:53:16 +0800 Subject: [PATCH 19/25] feat: default sort to desc in MacroeconomicV2 Co-Authored-By: Claude Sonnet 4.6 (1M context) --- fundamental/context.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fundamental/context.go b/fundamental/context.go index dce757d..fc239d5 100644 --- a/fundamental/context.go +++ b/fundamental/context.go @@ -1722,8 +1722,10 @@ func (c *FundamentalContext) MacroeconomicV2( if limit != nil { q.Set("limit", fmt.Sprintf("%d", *limit)) } - if sort != nil { + if sort != nil && *sort != "" { q.Set("sort", *sort) + } else { + q.Set("sort", "desc") } var resp jsontypes.V2MacroeconomicResponse path := "/v2/quote/macrodata/" + indicatorCode From ff12e4d1d3332b8fade024dde37cdea6a4858678 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A2=81=E7=AB=A0=E6=B4=AA?= Date: Fri, 12 Jun 2026 18:29:07 +0800 Subject: [PATCH 20/25] feat: add keyword param to MacroeconomicIndicators; hide _v2 variants Co-Authored-By: Claude Sonnet 4.6 (1M context) --- fundamental/context.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fundamental/context.go b/fundamental/context.go index fc239d5..d14f005 100644 --- a/fundamental/context.go +++ b/fundamental/context.go @@ -1617,16 +1617,17 @@ func convertFinancialReportSnapshot(j *jsontypes.FinancialReportSnapshot) *Finan // MacroeconomicIndicators fetches the list of available macroeconomic indicators. // // Pass country to filter by country code (e.g. MacroeconomicCountryUS), nil for all. -// offset and limit are kept for backward compatibility but ignored by v2. +// keyword optionally fuzzy-filters by name (case-insensitive), pass nil to skip. // // Path: GET /v2/quote/macrodata func (c *FundamentalContext) MacroeconomicIndicators( ctx context.Context, country *MacroeconomicCountry, + keyword *string, offset *int32, limit *int32, ) (*MacroeconomicIndicatorListResponse, error) { - return c.MacroeconomicIndicatorsV2(ctx, country, nil, offset, limit) + return c.MacroeconomicIndicatorsV2(ctx, country, keyword, offset, limit) } // MacroeconomicIndicatorsV2 fetches macroeconomic indicators with optional keyword filter. From f3385e18e0bc933276a7eb1477e61e60ccd0d698 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A2=81=E7=AB=A0=E6=B4=AA?= Date: Fri, 12 Jun 2026 19:05:08 +0800 Subject: [PATCH 21/25] chore: add v0.26.0 CHANGELOG entry for macroeconomic v2 changes Co-Authored-By: Claude Sonnet 4.6 (1M context) --- CHANGELOG.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5fa9287..3d1114c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## [v0.26.0] - 2026-06-13 + +### Added + +- `FundamentalContext.MacroeconomicIndicators` gains `keyword` parameter for fuzzy name filtering +- `FundamentalContext.Macroeconomic` switches to `GET /v2/quote/macrodata/{id}`, defaults to `sort=desc` +- `MacroeconomicIndicators` returns `Periodicity`, `Describe`, `Importance` from v2 API + +### Changed + +- `MacroeconomicIndicator.Name` / `.Describe`: `MultiLanguageText` → `string` +- `Macroeconomic.Unit` / `.UnitPrefix`: `MultiLanguageText` → `string` + ## [v0.25.0] - 2026-06-10 ### Added From 9b3c080887f90a8eea72fa56794477a9a694ba0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A2=81=E7=AB=A0=E6=B4=AA?= Date: Fri, 12 Jun 2026 19:08:27 +0800 Subject: [PATCH 22/25] chore: rename v0.26.0 to v0.25.1 in CHANGELOG --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d1114c..db4ef1d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## [v0.26.0] - 2026-06-13 +## [v0.25.1] - 2026-06-13 ### Added From fbe9598476831fa409e03ef4f9994873e3ad486e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A2=81=E7=AB=A0=E6=B4=AA?= Date: Fri, 12 Jun 2026 19:13:41 +0800 Subject: [PATCH 23/25] docs: update v0.25.1 CHANGELOG content --- CHANGELOG.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index db4ef1d..8909da0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,14 +4,13 @@ ### Added -- `FundamentalContext.MacroeconomicIndicators` gains `keyword` parameter for fuzzy name filtering -- `FundamentalContext.Macroeconomic` switches to `GET /v2/quote/macrodata/{id}`, defaults to `sort=desc` -- `MacroeconomicIndicators` returns `Periodicity`, `Describe`, `Importance` from v2 API +- **All languages:** `macroeconomic_indicators` gains `keyword` parameter for fuzzy name filtering +- **All languages:** `macroeconomic` switches to `GET /v2/quote/macrodata/{id}`, defaults to `sort=desc` ### Changed -- `MacroeconomicIndicator.Name` / `.Describe`: `MultiLanguageText` → `string` -- `Macroeconomic.Unit` / `.UnitPrefix`: `MultiLanguageText` → `string` +- `MacroeconomicIndicator.name` / `.describe`: `MultiLanguageText` → `string` +- `Macroeconomic.unit` / `.unit_prefix`: `MultiLanguageText` → `string` ## [v0.25.0] - 2026-06-10 From abdfd57751278c3deadda22d7ba842a435e126ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A2=81=E7=AB=A0=E6=B4=AA?= Date: Fri, 12 Jun 2026 19:53:45 +0800 Subject: [PATCH 24/25] feat(macroeconomic): switch to v2 endpoints MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - MacroeconomicIndicators: GET /v1 → /v2/quote/macrodata, adds keyword param, uses market code directly - Macroeconomic: GET /v1 → /v2/quote/macrodata/{id}, defaults sort=desc - Add convertV2MacroeconomicIndicator/Detail/Macroeconomic converters - Remove unused v1 converters: convertMacroeconomicIndicator, convertMacroeconomic, macroeconomicCountryToAPIValue, convertMultiLanguageText Co-Authored-By: Claude Sonnet 4.6 (1M context) --- fundamental/context.go | 115 +++++++++++++++++------------------------ 1 file changed, 48 insertions(+), 67 deletions(-) diff --git a/fundamental/context.go b/fundamental/context.go index 218f91d..ed068b9 100644 --- a/fundamental/context.go +++ b/fundamental/context.go @@ -1616,19 +1616,23 @@ func convertFinancialReportSnapshot(j *jsontypes.FinancialReportSnapshot) *Finan // MacroeconomicIndicators fetches the list of available macroeconomic indicators. // -// Pass country to filter by country code (e.g. MacroeconomicCountryUS). -// Pass nil for all countries. +// Pass country to filter by country code (e.g. MacroeconomicCountryUS); nil for all. +// Pass keyword for fuzzy name filtering; nil for no filter. // -// Path: GET /v1/quote/macrodata +// Path: GET /v2/quote/macrodata func (c *FundamentalContext) MacroeconomicIndicators( ctx context.Context, country *MacroeconomicCountry, + keyword *string, offset *int32, limit *int32, ) (*MacroeconomicIndicatorListResponse, error) { q := url.Values{} if country != nil { - q.Set("country", macroeconomicCountryToAPIValue(*country)) + q.Set("market", string(*country)) + } + if keyword != nil { + q.Set("keyword", *keyword) } if offset != nil { q.Set("offset", fmt.Sprintf("%d", *offset)) @@ -1636,23 +1640,24 @@ func (c *FundamentalContext) MacroeconomicIndicators( if limit != nil { q.Set("limit", fmt.Sprintf("%d", *limit)) } - var resp jsontypes.MacroeconomicIndicatorListResponse - if err := c.httpClient.Get(ctx, "/v1/quote/macrodata", q, &resp); err != nil { + var resp jsontypes.V2MacroeconomicIndicatorListResponse + if err := c.httpClient.Get(ctx, "/v2/quote/macrodata", q, &resp); err != nil { return nil, err } - out := make([]MacroeconomicIndicator, 0, len(resp.Data)) - for _, item := range resp.Data { - out = append(out, convertMacroeconomicIndicator(&item)) + out := make([]MacroeconomicIndicator, 0, len(resp.IndicatorList)) + for _, item := range resp.IndicatorList { + out = append(out, convertV2MacroeconomicIndicator(&item)) } - return &MacroeconomicIndicatorListResponse{Data: out, Count: resp.Count}, nil + return &MacroeconomicIndicatorListResponse{Data: out, Count: resp.Total}, nil } // Macroeconomic fetches historical data for a specific macroeconomic indicator. // +// indicatorCode is the IndicatorCode returned by MacroeconomicIndicators. // startDate and endDate are date strings in "YYYY-MM-DD" format. -// startDate is sent as YYYY-MM-DDT00:00:00Z; endDate is sent as YYYY-MM-DDT23:59:59Z. +// Results are sorted descending (newest first) by default. // -// Path: GET /v1/quote/macrodata/{indicator_code} +// Path: GET /v2/quote/macrodata/{indicator_id} func (c *FundamentalContext) Macroeconomic( ctx context.Context, indicatorCode string, @@ -1662,6 +1667,7 @@ func (c *FundamentalContext) Macroeconomic( limit *int32, ) (*MacroeconomicResponse, error) { q := url.Values{} + q.Set("sort", "desc") if startDate != nil { q.Set("start_time", *startDate+"T00:00:00Z") } @@ -1674,30 +1680,22 @@ func (c *FundamentalContext) Macroeconomic( if limit != nil { q.Set("limit", fmt.Sprintf("%d", *limit)) } - var resp jsontypes.MacroeconomicResponse - path := "/v1/quote/macrodata/" + indicatorCode + var resp jsontypes.V2MacroeconomicResponse + path := "/v2/quote/macrodata/" + indicatorCode if err := c.httpClient.Get(ctx, path, q, &resp); err != nil { return nil, err } - data := make([]Macroeconomic, 0, len(resp.Data)) - for _, d := range resp.Data { - data = append(data, convertMacroeconomic(&d)) + data := make([]Macroeconomic, 0, len(resp.Indicator.IndicatorData)) + for _, d := range resp.Indicator.IndicatorData { + data = append(data, convertV2Macroeconomic(&d, resp.Indicator.Unit)) } return &MacroeconomicResponse{ - Info: convertMacroeconomicIndicator(&resp.Info), + Info: convertV2MacroeconomicDetail(&resp.Indicator), Data: data, - Count: resp.Count, + Count: resp.Total, }, nil } -func convertMultiLanguageText(j jsontypes.MultiLanguageText) MultiLanguageText { - return MultiLanguageText{ - English: j.English, - SimplifiedChinese: j.SimplifiedChinese, - TraditionalChinese: j.TraditionalChinese, - } -} - func parseOptionalRFC3339(s string) *time.Time { if s == "" { return nil @@ -1710,50 +1708,33 @@ func parseOptionalRFC3339(s string) *time.Time { return &t } -func convertMacroeconomicIndicator(j *jsontypes.MacroeconomicIndicator) MacroeconomicIndicator { +func convertV2MacroeconomicIndicator(j *jsontypes.V2MacroeconomicIndicator) MacroeconomicIndicator { return MacroeconomicIndicator{ - IndicatorCode: j.IndicatorCode, - SourceOrg: j.SourceOrg, - Country: j.Country, - Name: j.Name.English, - AdjustmentFactor: j.AdjustmentFactor, - Periodicity: j.Periodicity, - Category: j.Category, - Describe: j.Describe.English, - Importance: j.Importance, - StartDate: parseOptionalRFC3339(j.StartDate), + IndicatorCode: strconv.FormatInt(int64(j.IndicatorID), 10), + Country: j.Market, + Name: j.IndicatorName, + Describe: j.Description, + Importance: j.Importance, + Periodicity: j.Frequence, } } -func convertMacroeconomic(j *jsontypes.Macroeconomic) Macroeconomic { +func convertV2MacroeconomicDetail(j *jsontypes.V2MacroeconomicDetail) MacroeconomicIndicator { + return MacroeconomicIndicator{ + IndicatorCode: strconv.FormatInt(int64(j.IndicatorID), 10), + Country: j.Market, + Name: j.IndicatorName, + Describe: j.Description, + } +} + +func convertV2Macroeconomic(j *jsontypes.V2IndicatorDataDetail, unit string) Macroeconomic { return Macroeconomic{ - Period: j.Period, - ReleaseAt: parseOptionalRFC3339(j.ReleaseAt), - ActualValue: j.ActualValue, - PreviousValue: j.PreviousValue, - ForecastValue: j.ForecastValue, - RevisedValue: j.RevisedValue, - NextReleaseAt: parseOptionalRFC3339(j.NextReleaseAt), - Unit: j.Unit.English, - UnitPrefix: j.UnitPrefix.English, - } -} - -func macroeconomicCountryToAPIValue(c MacroeconomicCountry) string { - switch c { - case MacroeconomicCountryHK: - return "Hong Kong SAR China" - case MacroeconomicCountryCN: - return "China (Mainland)" - case MacroeconomicCountryUS: - return "United States" - case MacroeconomicCountryEU: - return "Euro Zone" - case MacroeconomicCountryJP: - return "Japan" - case MacroeconomicCountrySG: - return "Singapore" - default: - return string(c) + Period: j.ObservationDate, + ReleaseAt: parseOptionalRFC3339(j.PublishedTime), + ActualValue: j.ActualData, + PreviousValue: j.PreviousData, + ForecastValue: j.EstimatedData, + Unit: unit, } } From 7d197b3f85579524bb1f950d43e60cae0d9b13ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=A2=81=E7=AB=A0=E6=B4=AA?= Date: Fri, 12 Jun 2026 20:04:08 +0800 Subject: [PATCH 25/25] fix(macroeconomic): align v2 request params and count fallback with Rust SDK MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - start_time/end_time (RFC3339) → start_date/end_date (YYYY-MM-DD) per proto spec - Add count fallback: if total==0 use len(data), matching Rust behavior Co-Authored-By: Claude Sonnet 4.6 (1M context) --- fundamental/context.go | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/fundamental/context.go b/fundamental/context.go index ed068b9..49f0a36 100644 --- a/fundamental/context.go +++ b/fundamental/context.go @@ -1648,7 +1648,11 @@ func (c *FundamentalContext) MacroeconomicIndicators( for _, item := range resp.IndicatorList { out = append(out, convertV2MacroeconomicIndicator(&item)) } - return &MacroeconomicIndicatorListResponse{Data: out, Count: resp.Total}, nil + count := resp.Total + if count == 0 { + count = int32(len(out)) + } + return &MacroeconomicIndicatorListResponse{Data: out, Count: count}, nil } // Macroeconomic fetches historical data for a specific macroeconomic indicator. @@ -1669,10 +1673,10 @@ func (c *FundamentalContext) Macroeconomic( q := url.Values{} q.Set("sort", "desc") if startDate != nil { - q.Set("start_time", *startDate+"T00:00:00Z") + q.Set("start_date", *startDate) } if endDate != nil { - q.Set("end_time", *endDate+"T23:59:59Z") + q.Set("end_date", *endDate) } if offset != nil { q.Set("offset", fmt.Sprintf("%d", *offset)) @@ -1689,10 +1693,14 @@ func (c *FundamentalContext) Macroeconomic( for _, d := range resp.Indicator.IndicatorData { data = append(data, convertV2Macroeconomic(&d, resp.Indicator.Unit)) } + count := resp.Total + if count == 0 { + count = int32(len(data)) + } return &MacroeconomicResponse{ Info: convertV2MacroeconomicDetail(&resp.Indicator), Data: data, - Count: resp.Total, + Count: count, }, nil }