diff --git a/CHANGELOG.md b/CHANGELOG.md index a1329525e..9d74445a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,18 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [4.3.2] - 2026-06-13 + +### Added + +- **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` / `.unit_prefix`: `MultiLanguageText` → `string` + ## [4.3.1] - 2026-06-12 ### Added diff --git a/Cargo.toml b/Cargo.toml index b21cdf2dc..8606b5cb6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ resolver = "3" members = ["rust", "python", "nodejs", "java", "c"] [workspace.package] -version = "4.3.1" +version = "4.3.2" edition = "2024" [profile.release] diff --git a/java/javasrc/src/main/java/com/longbridge/SdkNative.java b/java/javasrc/src/main/java/com/longbridge/SdkNative.java index dadca70c6..7beea04c5 100644 --- a/java/javasrc/src/main/java/com/longbridge/SdkNative.java +++ b/java/javasrc/src/main/java/com/longbridge/SdkNative.java @@ -452,7 +452,7 @@ public static native void fundamentalContextGetFinancialReportSnapshot(long cont AsyncCallback callback); public static native void fundamentalContextMacroeconomicIndicators(long context, - Object country, Object offset, Object limit, + Object country, Object keyword, Object offset, Object limit, AsyncCallback callback); public static native void fundamentalContextMacroeconomic(long context, diff --git a/java/javasrc/src/main/java/com/longbridge/fundamental/FundamentalContext.java b/java/javasrc/src/main/java/com/longbridge/fundamental/FundamentalContext.java index 4c22e256a..a3a224656 100644 --- a/java/javasrc/src/main/java/com/longbridge/fundamental/FundamentalContext.java +++ b/java/javasrc/src/main/java/com/longbridge/fundamental/FundamentalContext.java @@ -339,9 +339,9 @@ public CompletableFuture getValuationComparison(Val * List macroeconomic indicators. * country: ISO country code string (e.g. "US", "CN", "EU"); pass null for all countries. */ - public CompletableFuture getMacroeconomicIndicators(String country, Integer offset, Integer limit) throws OpenApiException { + public CompletableFuture getMacroeconomicIndicators(String country, String keyword, Integer offset, Integer limit) throws OpenApiException { return AsyncCallback.executeTask((callback) -> { - SdkNative.fundamentalContextMacroeconomicIndicators(raw, country, offset, limit, callback); + SdkNative.fundamentalContextMacroeconomicIndicators(raw, country, keyword, offset, limit, callback); }); } @@ -354,4 +354,10 @@ public CompletableFuture getMacroeconomic(String indicato SdkNative.fundamentalContextMacroeconomic(raw, indicatorCode, startDate, endDate, offset, limit, callback); }); } + + /** + * List macroeconomic indicators (v2) with optional keyword filter. + * country: "HK","CN","US","EU","JP","SG" or null for ALL. + * keyword: optional fuzzy filter on indicator name. + */ } diff --git a/java/javasrc/src/main/java/com/longbridge/fundamental/Macroeconomic.java b/java/javasrc/src/main/java/com/longbridge/fundamental/Macroeconomic.java index caca0ccda..c817f54b5 100644 --- a/java/javasrc/src/main/java/com/longbridge/fundamental/Macroeconomic.java +++ b/java/javasrc/src/main/java/com/longbridge/fundamental/Macroeconomic.java @@ -10,6 +10,6 @@ public class Macroeconomic { public String forecastValue; public String revisedValue; public String nextReleaseAt; - public MultiLanguageText unit; - public MultiLanguageText unitPrefix; + public String unit; + public String unitPrefix; } diff --git a/java/javasrc/src/main/java/com/longbridge/fundamental/MacroeconomicIndicator.java b/java/javasrc/src/main/java/com/longbridge/fundamental/MacroeconomicIndicator.java index c83d4fd5a..d46e9e072 100644 --- a/java/javasrc/src/main/java/com/longbridge/fundamental/MacroeconomicIndicator.java +++ b/java/javasrc/src/main/java/com/longbridge/fundamental/MacroeconomicIndicator.java @@ -6,12 +6,12 @@ public class MacroeconomicIndicator { public String indicatorCode; public String sourceOrg; public String country; - public MultiLanguageText name; + public String name; public String adjustmentFactor; /** Release periodicity (e.g. monthly / quarterly). */ public String periodicity; public String category; - public MultiLanguageText describe; + public String describe; /** Importance — higher is more important. */ public int importance; /** Start date of data coverage (unix timestamp string). */ diff --git a/java/src/fundamental_context.rs b/java/src/fundamental_context.rs index 163b2fd45..bfd2d8b37 100644 --- a/java/src/fundamental_context.rs +++ b/java/src/fundamental_context.rs @@ -269,6 +269,7 @@ pub unsafe extern "system" fn Java_com_longbridge_SdkNative_fundamentalContextMa _class: JClass, context: i64, country: JObject, + keyword: JObject, offset: JObject, limit: JObject, callback: JObject, @@ -288,12 +289,13 @@ pub unsafe extern "system" fn Java_com_longbridge_SdkNative_fundamentalContextMa _ => None, } }); + let keyword: Option = FromJValue::from_jvalue(env, keyword.into())?; let offset: Option = FromJValue::from_jvalue(env, offset.into())?; let limit: Option = FromJValue::from_jvalue(env, limit.into())?; async_util::execute(env, callback, async move { Ok(context .ctx - .macroeconomic_indicators(country, offset, limit) + .macroeconomic_indicators(country, keyword, offset, limit) .await?) })?; Ok(()) diff --git a/nodejs/index.d.ts b/nodejs/index.d.ts index 56b157e18..d564d1d5f 100644 --- a/nodejs/index.d.ts +++ b/nodejs/index.d.ts @@ -622,6 +622,10 @@ export declare class FundamentalContext { macroeconomicIndicators(country?: MacroeconomicCountry | undefined | null, offset?: number | undefined | null, limit?: number | undefined | null): Promise /** Get historical data for a macroeconomic indicator */ macroeconomic(indicatorCode: string, startDate?: string | undefined | null, endDate?: string | undefined | null, offset?: number | undefined | null, limit?: number | undefined | null): Promise + /** List macroeconomic indicators (v2) with optional keyword filter */ + macroeconomicIndicatorsV2(country?: MacroeconomicCountry | undefined | null, keyword?: string | undefined | null, offset?: number | undefined | null, limit?: number | undefined | null): Promise + /** Get historical data for a macroeconomic indicator (v2) with sort support */ + macroeconomicV2(indicatorCode: string, startDate?: string | undefined | null, endDate?: string | undefined | null, offset?: number | undefined | null, limit?: number | undefined | null, sort?: string | undefined | null): Promise } /** Fund position */ @@ -4488,8 +4492,8 @@ export interface Macroeconomic { revisedValue: string /** Next release datetime (unix timestamp in seconds; null if unset) */ nextReleaseAt?: number - unit: MultiLanguageText - unitPrefix: MultiLanguageText + unit: string + unitPrefix: string } /** Country code for filtering macroeconomic indicators */ @@ -4513,11 +4517,11 @@ export interface MacroeconomicIndicator { indicatorCode: string sourceOrg: string country: string - name: MultiLanguageText + name: string adjustmentFactor: string periodicity: string category: string - describe: MultiLanguageText + describe: string importance: number /** Start date of data coverage (unix timestamp in seconds; null if unset) */ startDate?: number diff --git a/nodejs/index.js b/nodejs/index.js index fa943da93..25721d294 100644 --- a/nodejs/index.js +++ b/nodejs/index.js @@ -689,7 +689,7 @@ module.exports.FlowDirection = nativeBinding.FlowDirection module.exports.Granularity = nativeBinding.Granularity module.exports.InstitutionRecommend = nativeBinding.InstitutionRecommend module.exports.Language = nativeBinding.Language -module.exports.MacrodataCountry = nativeBinding.MacrodataCountry +module.exports.MacroeconomicCountry = nativeBinding.MacroeconomicCountry module.exports.Market = nativeBinding.Market module.exports.OptionDirection = nativeBinding.OptionDirection module.exports.OptionType = nativeBinding.OptionType diff --git a/nodejs/src/fundamental/context.rs b/nodejs/src/fundamental/context.rs index 3fdc33587..78ae9285f 100644 --- a/nodejs/src/fundamental/context.rs +++ b/nodejs/src/fundamental/context.rs @@ -293,12 +293,13 @@ impl FundamentalContext { pub async fn macroeconomic_indicators( &self, country: Option, + keyword: Option, offset: Option, limit: Option, ) -> Result { Ok(self .ctx - .macroeconomic_indicators(country.map(Into::into), offset, limit) + .macroeconomic_indicators(country.map(Into::into), keyword, offset, limit) .await .map_err(ErrorNewType)? .into()) diff --git a/nodejs/src/fundamental/types.rs b/nodejs/src/fundamental/types.rs index e66916ff5..0e8526c87 100644 --- a/nodejs/src/fundamental/types.rs +++ b/nodejs/src/fundamental/types.rs @@ -1979,11 +1979,11 @@ pub struct MacroeconomicIndicator { pub indicator_code: String, pub source_org: String, pub country: String, - pub name: MultiLanguageText, + pub name: String, pub adjustment_factor: String, pub periodicity: String, pub category: String, - pub describe: MultiLanguageText, + pub describe: String, pub importance: i32, /// Start date of data coverage (unix timestamp in seconds; null if unset) pub start_date: Option, @@ -1995,11 +1995,11 @@ impl From for MacroeconomicIndicator { indicator_code: v.indicator_code, source_org: v.source_org, country: v.country, - name: v.name.into(), + name: v.name, adjustment_factor: v.adjustment_factor, periodicity: v.periodicity, category: v.category, - describe: v.describe.into(), + describe: v.describe, importance: v.importance, start_date: v.start_date.map(|dt| dt.unix_timestamp()), } @@ -2019,8 +2019,8 @@ pub struct Macroeconomic { pub revised_value: String, /// Next release datetime (unix timestamp in seconds; null if unset) pub next_release_at: Option, - pub unit: MultiLanguageText, - pub unit_prefix: MultiLanguageText, + pub unit: String, + pub unit_prefix: String, } impl From for Macroeconomic { @@ -2033,8 +2033,8 @@ impl From for Macroeconomic { forecast_value: v.forecast_value, revised_value: v.revised_value, next_release_at: v.next_release_at.map(|dt| dt.unix_timestamp()), - unit: v.unit.into(), - unit_prefix: v.unit_prefix.into(), + unit: v.unit, + unit_prefix: v.unit_prefix, } } } diff --git a/python/pysrc/longbridge/openapi.pyi b/python/pysrc/longbridge/openapi.pyi index 8186b1c8f..bef3ba940 100644 --- a/python/pysrc/longbridge/openapi.pyi +++ b/python/pysrc/longbridge/openapi.pyi @@ -10118,12 +10118,12 @@ class MacroeconomicIndicator: """External vendor code (input to macroeconomic)""" source_org: str country: str - name: MultiLanguageText + name: str adjustment_factor: str periodicity: str """Release periodicity (e.g. monthly / quarterly)""" category: str - describe: MultiLanguageText + describe: str importance: int start_date: datetime | None """Start date of data coverage""" @@ -10140,8 +10140,8 @@ class Macroeconomic: forecast_value: str revised_value: str next_release_at: datetime | None - unit: MultiLanguageText - unit_prefix: MultiLanguageText + unit: str + unit_prefix: str class MacroeconomicResponse: diff --git a/python/src/fundamental/context.rs b/python/src/fundamental/context.rs index 5d4dd8639..1804b1919 100644 --- a/python/src/fundamental/context.rs +++ b/python/src/fundamental/context.rs @@ -213,12 +213,13 @@ impl FundamentalContext { fn macroeconomic_indicators( &self, country: Option, + keyword: Option, offset: Option, limit: Option, ) -> PyResult { Ok(self .ctx - .macroeconomic_indicators(country.map(Into::into), offset, limit) + .macroeconomic_indicators(country.map(Into::into), keyword, offset, limit) .map_err(ErrorNewType)? .into()) } diff --git a/python/src/fundamental/context_async.rs b/python/src/fundamental/context_async.rs index 1fe4f9d5c..d0ef8151a 100644 --- a/python/src/fundamental/context_async.rs +++ b/python/src/fundamental/context_async.rs @@ -323,13 +323,14 @@ impl AsyncFundamentalContext { &self, py: Python<'_>, country: Option, + keyword: Option, offset: Option, limit: Option, ) -> PyResult> { let ctx = self.ctx.clone(); pyo3_async_runtimes::tokio::future_into_py(py, async move { Ok(MacroeconomicIndicatorListResponse::from( - ctx.macroeconomic_indicators(country.map(Into::into), offset, limit) + ctx.macroeconomic_indicators(country.map(Into::into), keyword, offset, limit) .await .map_err(ErrorNewType)?, )) diff --git a/python/src/fundamental/types.rs b/python/src/fundamental/types.rs index c541cb7c9..ea842a54b 100644 --- a/python/src/fundamental/types.rs +++ b/python/src/fundamental/types.rs @@ -2036,11 +2036,11 @@ pub(crate) struct MacroeconomicIndicator { pub indicator_code: String, pub source_org: String, pub country: String, - pub name: MultiLanguageText, + pub name: String, pub adjustment_factor: String, pub periodicity: String, pub category: String, - pub describe: MultiLanguageText, + pub describe: String, pub importance: i32, pub start_date: Option, } @@ -2051,11 +2051,11 @@ impl From for MacroeconomicIndicator { indicator_code: v.indicator_code, source_org: v.source_org, country: v.country, - name: v.name.into(), + name: v.name, adjustment_factor: v.adjustment_factor, periodicity: v.periodicity, category: v.category, - describe: v.describe.into(), + describe: v.describe, importance: v.importance, start_date: v.start_date.map(crate::time::PyOffsetDateTimeWrapper), } @@ -2073,8 +2073,8 @@ pub(crate) struct Macroeconomic { pub forecast_value: String, pub revised_value: String, pub next_release_at: Option, - pub unit: MultiLanguageText, - pub unit_prefix: MultiLanguageText, + pub unit: String, + pub unit_prefix: String, } impl From for Macroeconomic { @@ -2087,8 +2087,8 @@ impl From for Macroeconomic { forecast_value: v.forecast_value, revised_value: v.revised_value, next_release_at: v.next_release_at.map(crate::time::PyOffsetDateTimeWrapper), - unit: v.unit.into(), - unit_prefix: v.unit_prefix.into(), + unit: v.unit, + unit_prefix: v.unit_prefix, } } } diff --git a/rust/src/blocking/fundamental.rs b/rust/src/blocking/fundamental.rs index d580a0484..532c09bc1 100644 --- a/rust/src/blocking/fundamental.rs +++ b/rust/src/blocking/fundamental.rs @@ -295,11 +295,13 @@ impl FundamentalContextSync { pub fn macroeconomic_indicators( &self, country: Option, + keyword: Option + Send + 'static>, offset: Option, limit: Option, ) -> Result { self.rt.call(move |ctx| async move { - ctx.macroeconomic_indicators(country, offset, limit).await + ctx.macroeconomic_indicators(country, keyword, offset, limit) + .await }) } @@ -317,4 +319,34 @@ impl FundamentalContextSync { .await }) } + + /// List macroeconomic indicators (v2) with optional keyword filter + pub(crate) fn macroeconomic_indicators_v2( + &self, + country: Option, + keyword: Option + Send + 'static>, + offset: Option, + limit: Option, + ) -> Result { + self.rt.call(move |ctx| async move { + ctx.macroeconomic_indicators_v2(country, keyword, offset, limit) + .await + }) + } + + /// Get historical data for a macroeconomic indicator (v2) with sort support + pub(crate) fn macroeconomic_v2( + &self, + indicator_code: impl Into + Send + 'static, + start_date: Option + Send + 'static>, + end_date: Option + Send + 'static>, + offset: Option, + limit: Option, + sort: Option + Send + 'static>, + ) -> Result { + self.rt.call(move |ctx| async move { + ctx.macroeconomic_v2(indicator_code, start_date, end_date, offset, limit, sort) + .await + }) + } } diff --git a/rust/src/fundamental/context.rs b/rust/src/fundamental/context.rs index be0212c9c..450848147 100644 --- a/rust/src/fundamental/context.rs +++ b/rust/src/fundamental/context.rs @@ -834,54 +834,92 @@ impl FundamentalContext { /// List macroeconomic indicators. /// - /// Pass `country` to filter by country code (e.g. - /// `MacroeconomicCountry::UnitedStates`). + /// `country` accepts a market code string (e.g. `"US"`, `"HK"`, `"ALL"`). + /// `keyword` optionally filters indicators by name (fuzzy, + /// case-insensitive). `offset` and `limit` are kept for backward + /// compatibility but ignored by v2. /// - /// Path: `GET /v1/quote/macrodata` + /// Path: `GET /v2/quote/macrodata` pub async fn macroeconomic_indicators( &self, country: Option, + keyword: Option>, + offset: Option, + limit: Option, + ) -> Result { + self.macroeconomic_indicators_v2(country, keyword, offset, limit) + .await + } + + /// List macroeconomic indicators (v2) with optional keyword filter. + /// + /// Path: `GET /v2/quote/macrodata` + pub(crate) async fn macroeconomic_indicators_v2( + &self, + country: Option, + keyword: Option>, offset: Option, limit: Option, ) -> Result { #[derive(Serialize)] struct Query { + market: String, #[serde(skip_serializing_if = "Option::is_none")] - country: Option, + keyword: Option, #[serde(skip_serializing_if = "Option::is_none")] offset: Option, #[serde(skip_serializing_if = "Option::is_none")] limit: Option, } - let country_str = country.map(|c| { - match c { - MacroeconomicCountry::HongKong => "Hong Kong SAR China", - MacroeconomicCountry::China => "China (Mainland)", - MacroeconomicCountry::UnitedStates => "United States", - MacroeconomicCountry::EuroZone => "Euro Zone", - MacroeconomicCountry::Japan => "Japan", - MacroeconomicCountry::Singapore => "Singapore", - } - .to_string() - }); - self.get( - "/v1/quote/macrodata", - Query { - country: country_str, - offset, - limit, - }, - ) - .await + let market = country + .map(|c| match c { + MacroeconomicCountry::HongKong => "HK", + MacroeconomicCountry::China => "CN", + MacroeconomicCountry::UnitedStates => "US", + MacroeconomicCountry::EuroZone => "EU", + MacroeconomicCountry::Japan => "JP", + MacroeconomicCountry::Singapore => "SG", + }) + .unwrap_or("ALL") + .to_string(); + + let raw: V2MacroIndicatorListResponse = self + .get( + "/v2/quote/macrodata", + Query { + market, + keyword: keyword.map(|k| k.into()), + offset, + limit, + }, + ) + .await?; + + let total = raw.total; + let data = raw + .indicator_list + .into_iter() + .map(|ind| MacroeconomicIndicator { + indicator_code: ind.indicator_id.to_string(), + country: ind.market, + name: ind.indicator_name, + periodicity: ind.frequence, + describe: ind.description, + importance: ind.importance, + ..Default::default() + }) + .collect::>(); + let count = if total > 0 { total } else { data.len() as i32 }; + Ok(MacroeconomicIndicatorListResponse { data, count }) } /// Get historical data for a macroeconomic indicator. /// - /// `start_date` and `end_date` are date strings in `"YYYY-MM-DD"` format. - /// `start_date` is sent as `YYYY-MM-DDT00:00:00Z`; `end_date` is sent as - /// `YYYY-MM-DDT23:59:59Z`. + /// `indicator_code` is the indicator ID (integer as string in v2). + /// `start_date` and `end_date` are `"YYYY-MM-DD"` format. + /// `sort` can be `"asc"` or `"desc"` (new in v2). /// - /// Path: `GET /v1/quote/macrodata/{indicator_code}` + /// Path: `GET /v2/quote/macrodata/{indicator_id}` pub async fn macroeconomic( &self, indicator_code: impl Into, @@ -889,33 +927,106 @@ impl FundamentalContext { end_date: Option>, offset: Option, limit: Option, + ) -> Result { + self.macroeconomic_v2( + indicator_code, + start_date, + end_date, + offset, + limit, + None::, + ) + .await + } + + /// Get historical data for a macroeconomic indicator (v2) with sort + /// support. + /// + /// Path: `GET /v2/quote/macrodata/{indicator_id}` + pub(crate) async fn macroeconomic_v2( + &self, + indicator_code: impl Into, + start_date: Option>, + end_date: Option>, + offset: Option, + limit: Option, + sort: Option>, ) -> Result { #[derive(Serialize)] struct Query { #[serde(skip_serializing_if = "Option::is_none")] - start_time: Option, + start_date: Option, #[serde(skip_serializing_if = "Option::is_none")] - end_time: Option, + end_date: Option, #[serde(skip_serializing_if = "Option::is_none")] offset: Option, #[serde(skip_serializing_if = "Option::is_none")] limit: Option, + #[serde(skip_serializing_if = "Option::is_none")] + sort: Option, } - let path = format!("/v1/quote/macrodata/{}", indicator_code.into()); - Ok(self + let path = format!("/v2/quote/macrodata/{}", indicator_code.into()); + let raw: V2MacroIndicatorDataResponse = self .0 .http_cli .request(Method::GET, path) .query_params(Query { - start_time: start_date.map(|d| format!("{}T00:00:00Z", d.into())), - end_time: end_date.map(|d| format!("{}T23:59:59Z", d.into())), + start_date: start_date.map(|d| d.into()), + end_date: end_date.map(|d| d.into()), offset, limit, + sort: Some(sort.map(|s| s.into()).unwrap_or_else(|| "desc".to_string())), }) - .response::>() + .response::>() .send() .with_subscriber(self.0.log_subscriber.clone()) .await? - .0) + .0; + + let total = raw.total; + let detail = raw.indicator; + let unit_english = detail.unit.clone(); + let count = detail.indicator_data.len() as i32; + + let info = MacroeconomicIndicator { + indicator_code: detail.indicator_id.to_string(), + country: detail.market, + name: detail.indicator_name, + describe: detail.description, + ..Default::default() + }; + + let data = detail + .indicator_data + .into_iter() + .map(|d| { + use time::format_description::well_known::Rfc3339; + let release_at = time::OffsetDateTime::parse(&d.published_time, &Rfc3339) + .ok() + .or_else(|| { + // Try without timezone suffix + time::PrimitiveDateTime::parse( + &d.published_time, + &time::macros::format_description!( + "[year]-[month]-[day]T[hour]:[minute]:[second]" + ), + ) + .ok() + .map(|dt| dt.assume_utc()) + }); + Macroeconomic { + period: d.observation_date, + release_at, + actual_value: d.actual_data, + previous_value: d.previous_data, + forecast_value: d.estimated_data, + unit: unit_english.clone(), + ..Default::default() + } + }) + .collect(); + + let count = if total > 0 { total } else { count }; + Ok(MacroeconomicResponse { info, data, count }) } } diff --git a/rust/src/fundamental/types.rs b/rust/src/fundamental/types.rs index 9bee91e22..fa127e9c4 100644 --- a/rust/src/fundamental/types.rs +++ b/rust/src/fundamental/types.rs @@ -1636,9 +1636,9 @@ pub struct MacroeconomicIndicator { /// Country #[serde(default)] pub country: String, - /// Indicator name (multilingual) - #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")] - pub name: MultiLanguageText, + /// Indicator name + #[serde(default)] + pub name: String, /// Adjustment factor #[serde(default)] pub adjustment_factor: String, @@ -1648,9 +1648,9 @@ pub struct MacroeconomicIndicator { /// Indicator category #[serde(default)] pub category: String, - /// Description (multilingual) - #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")] - pub describe: MultiLanguageText, + /// Description + #[serde(default)] + pub describe: String, /// Importance — higher is more important #[serde(default)] pub importance: i32, @@ -1698,12 +1698,12 @@ pub struct Macroeconomic { /// Next release datetime #[serde(default, with = "crate::serde_utils::rfc3339_opt")] pub next_release_at: Option, - /// Unit (multilingual) - #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")] - pub unit: MultiLanguageText, - /// Unit prefix / data scale (multilingual, e.g. millions / billions) - #[serde(default, deserialize_with = "crate::serde_utils::null_as_default")] - pub unit_prefix: MultiLanguageText, + /// Unit + #[serde(default)] + pub unit: String, + /// Unit prefix / data scale (e.g. millions / billions) + #[serde(default)] + pub unit_prefix: String, } /// Response for [`crate::FundamentalContext::macroeconomic`] @@ -1719,3 +1719,77 @@ pub struct MacroeconomicResponse { #[serde(default)] pub count: i32, } + +// ── v2 wire types (internal, used for mapping to existing public types) ────── + +/// v2 wire: one indicator from GET /v2/quote/macrodata +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(crate) struct V2MacroIndicator { + #[serde(default)] + pub indicator_id: i32, + #[serde(default)] + pub indicator_name: String, + #[serde(default)] + pub market: String, + #[serde(default)] + pub importance: i32, + #[serde(default)] + pub description: String, + /// Update frequency: day/week/month/quarter/half_year/year + #[serde(default)] + pub frequence: String, +} + +/// v2 wire: response from GET /v2/quote/macrodata +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(crate) struct V2MacroIndicatorListResponse { + #[serde(default)] + pub indicator_list: Vec, + /// Total count for pagination + #[serde(default)] + pub total: i32, +} + +/// v2 wire: one data point from GET /v2/quote/macrodata/:id +#[derive(Debug, Clone, Serialize, Deserialize)] +pub(crate) struct V2IndicatorDataDetail { + #[serde(default)] + pub actual_data: String, + #[serde(default)] + pub previous_data: String, + #[serde(default)] + pub estimated_data: String, + #[serde(default)] + pub published_time: String, + #[serde(default)] + pub observation_date: String, +} + +/// v2 wire: one indicator with data from GET /v2/quote/macrodata/:id +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub(crate) struct V2MacroIndicatorDetail { + #[serde(default)] + pub indicator_id: i32, + #[serde(default)] + pub indicator_name: String, + #[serde(default)] + pub unit: String, + #[serde(default)] + pub description: String, + #[serde(default)] + pub market: String, + #[serde(default)] + pub indicator_data: Vec, +} + +/// v2 wire: response from GET /v2/quote/macrodata/:id +/// (GetMacroIndicatorHistoryResp) +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub(crate) struct V2MacroIndicatorDataResponse { + /// Single indicator with paginated data points + #[serde(default)] + pub indicator: V2MacroIndicatorDetail, + /// Total data points matching the query (for pagination) + #[serde(default)] + pub total: i32, +}