use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
#[cfg_attr(feature = "python", pyo3::prelude::pyclass)]
#[cfg_attr(feature = "js", napi_derive::napi(object))]
pub struct StatsResponse {
pub date: String,
#[serde(rename = "type")]
pub data_type: String,
pub exchange: String,
pub market: String,
pub symbol: String,
pub name: String,
#[serde(rename = "openPrice")]
pub open_price: f64,
#[serde(rename = "highPrice")]
pub high_price: f64,
#[serde(rename = "lowPrice")]
pub low_price: f64,
#[serde(rename = "closePrice")]
pub close_price: f64,
pub change: f64,
#[serde(rename = "changePercent", default)]
pub change_percent: Option<f64>,
#[serde(rename = "tradeVolume")]
pub trade_volume: i64,
#[serde(rename = "tradeValue")]
pub trade_value: f64,
#[serde(rename = "previousClose")]
pub previous_close: f64,
#[serde(rename = "week52High")]
pub week52_high: f64,
#[serde(rename = "week52Low")]
pub week52_low: f64,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_stats_response_deserialization() {
let json = r#"{
"date": "2024-01-15",
"type": "EQUITY",
"exchange": "TWSE",
"market": "TSE",
"symbol": "2330",
"name": "台積電",
"openPrice": 580.0,
"highPrice": 590.0,
"lowPrice": 575.0,
"closePrice": 588.0,
"change": 8.0,
"changePercent": 1.38,
"tradeVolume": 50000000,
"tradeValue": 29000000000,
"previousClose": 580.0,
"week52High": 650.0,
"week52Low": 480.0
}"#;
let stats: StatsResponse = serde_json::from_str(json).unwrap();
assert_eq!(stats.symbol, "2330");
assert_eq!(stats.date, "2024-01-15");
assert_eq!(stats.close_price, 588.0);
assert_eq!(stats.change, 8.0);
assert_eq!(stats.change_percent, Some(1.38));
assert_eq!(stats.week52_high, 650.0);
assert_eq!(stats.week52_low, 480.0);
}
#[test]
fn test_stats_response_omits_change_percent() {
let json = r#"{
"date": "2026-04-16", "type": "EQUITY", "exchange": "TWSE",
"market": "TSE", "symbol": "2330", "name": "台積電",
"openPrice": 580.0, "highPrice": 590.0, "lowPrice": 575.0,
"closePrice": 588.0, "change": 8.0, "tradeVolume": 50000000,
"tradeValue": 29000000000, "previousClose": 580.0,
"week52High": 650.0, "week52Low": 480.0
}"#;
let stats: StatsResponse = serde_json::from_str(json).unwrap();
assert_eq!(stats.change_percent, None);
assert_eq!(stats.change, 8.0);
}
#[test]
fn test_stats_response_serialization_roundtrip() {
let stats = StatsResponse {
date: "2024-01-15".to_string(),
data_type: "EQUITY".to_string(),
exchange: "TWSE".to_string(),
market: "TSE".to_string(),
symbol: "2330".to_string(),
name: "台積電".to_string(),
open_price: 580.0,
high_price: 590.0,
low_price: 575.0,
close_price: 588.0,
change: 8.0,
change_percent: Some(1.38),
trade_volume: 50000000,
trade_value: 29000000000.0,
previous_close: 580.0,
week52_high: 650.0,
week52_low: 480.0,
};
let json = serde_json::to_string(&stats).unwrap();
let deserialized: StatsResponse = serde_json::from_str(&json).unwrap();
assert_eq!(stats, deserialized);
}
}