51 lines
1.5 KiB
Rust
51 lines
1.5 KiB
Rust
// src/corporate/fx.rs
|
|
use std::collections::HashMap;
|
|
use reqwest;
|
|
use serde_json::Value;
|
|
use tokio::fs;
|
|
use std::path::Path;
|
|
|
|
static FX_CACHE_PATH: &str = "fx_rates.json";
|
|
|
|
pub async fn get_usd_rate(currency: &str) -> anyhow::Result<f64> {
|
|
if currency == "USD" {
|
|
return Ok(1.0);
|
|
}
|
|
|
|
let mut cache: HashMap<String, (f64, String)> = if Path::new(FX_CACHE_PATH).exists() {
|
|
let content = fs::read_to_string(FX_CACHE_PATH).await?;
|
|
serde_json::from_str(&content).unwrap_or_default()
|
|
} else {
|
|
HashMap::new()
|
|
};
|
|
|
|
let today = chrono::Local::now().format("%Y-%m-%d").to_string();
|
|
if let Some((rate, date)) = cache.get(currency) {
|
|
if date == &today {
|
|
return Ok(*rate);
|
|
}
|
|
}
|
|
|
|
let symbol = format!("{}USD=X", currency);
|
|
let url = format!("https://query1.finance.yahoo.com/v8/finance/chart/{}?range=1d&interval=1d", symbol);
|
|
|
|
let json: Value = reqwest::Client::new()
|
|
.get(&url)
|
|
.header("User-Agent", "Mozilla/5.0")
|
|
.send()
|
|
.await?
|
|
.json()
|
|
.await?;
|
|
|
|
let close = json["chart"]["result"][0]["meta"]["regularMarketPrice"]
|
|
.as_f64()
|
|
.or_else(|| json["chart"]["result"][0]["indicators"]["quote"][0]["close"][0].as_f64())
|
|
.unwrap_or(1.0);
|
|
|
|
let rate = if currency == "JPY" || currency == "KRW" { close } else { 1.0 / close }; // inverse pairs
|
|
|
|
cache.insert(currency.to_string(), (rate, today.clone()));
|
|
let _ = fs::write(FX_CACHE_PATH, serde_json::to_string_pretty(&cache)?).await;
|
|
|
|
Ok(rate)
|
|
} |