Skip to main content

stock_rust/stocks/base/
mod.rs

1use anyhow::{anyhow, Result};
2use async_trait::async_trait;
3
4use crate::types::{Stock, StockApi};
5
6pub const COMMON_SZ: &str = "SZ";
7pub const COMMON_SH: &str = "SH";
8pub const COMMON_HK: &str = "HK";
9pub const COMMON_US: &str = "US";
10
11#[derive(Clone, Copy)]
12pub struct BaseApi;
13
14#[async_trait]
15impl StockApi for BaseApi {
16    async fn get_stock(&self, _code: &str) -> Result<Stock> {
17        Err(anyhow!("未实现获取股票数据"))
18    }
19
20    async fn get_stocks(&self, _codes: &[String]) -> Result<Vec<Stock>> {
21        Err(anyhow!("未实现获取股票数据组"))
22    }
23
24    async fn search_stocks(&self, _key: &str) -> Result<Vec<Stock>> {
25        Err(anyhow!("未实现搜索股票代码"))
26    }
27}
28
29pub fn dedup_codes(codes: &[String]) -> Vec<String> {
30    let mut out: Vec<String> = Vec::new();
31    for code in codes {
32        if !code.is_empty() && !out.contains(code) {
33            out.push(code.clone());
34        }
35    }
36    out
37}
38
39pub fn code_market(code: &str) -> &str {
40    if code.len() >= 2 {
41        &code[..2]
42    } else {
43        ""
44    }
45}
46
47pub fn parse_num(s: Option<&str>) -> f64 {
48    s.unwrap_or("0").parse::<f64>().unwrap_or(0.0)
49}
50
51pub fn as_f64(v: Option<&serde_json::Value>) -> f64 {
52    match v {
53        Some(serde_json::Value::Number(n)) => n.as_f64().unwrap_or(0.0),
54        Some(serde_json::Value::String(s)) => s.parse::<f64>().unwrap_or(0.0),
55        _ => 0.0,
56    }
57}
58
59pub fn as_string(v: Option<&serde_json::Value>) -> String {
60    match v {
61        Some(serde_json::Value::String(s)) => s.clone(),
62        Some(other) => other.to_string(),
63        None => "---".to_string(),
64    }
65}
66
67pub fn percent(now: f64, yesterday: f64) -> f64 {
68    if now == 0.0 || yesterday == 0.0 {
69        0.0
70    } else {
71        now / yesterday - 1.0
72    }
73}
74
75#[cfg(test)]
76mod tests {
77    use super::*;
78
79    #[test]
80    fn test_dedup_codes() {
81        let codes = vec![
82            "SH510500".to_string(),
83            "".to_string(),
84            "SH510500".to_string(),
85            "SZ000001".to_string(),
86        ];
87        assert_eq!(dedup_codes(&codes), vec!["SH510500", "SZ000001"]);
88    }
89
90    #[test]
91    fn test_percent() {
92        let p = percent(7.224, 7.149);
93        assert!((p - (7.224 / 7.149 - 1.0)).abs() < 1e-12);
94        assert_eq!(percent(0.0, 7.149), 0.0);
95    }
96}