use crate::error::{Result, YfError};
pub const MIC_TO_YAHOO_SUFFIX: &[(&str, &str)] = &[
("XCBT", "CBT"),
("XCME", "CME"),
("IFUS", "NYB"),
("CECS", "CMX"),
("XNYM", "NYM"),
("XNYS", ""),
("XNAS", ""),
("XBUE", "BA"),
("XVIE", "VI"),
("XASX", "AX"),
("XAUS", "XA"),
("XBRU", "BR"),
("BVMF", "SA"),
("CNSX", "CN"),
("NEOE", "NE"),
("XTSE", "TO"),
("XTSX", "V"),
("XSGO", "SN"),
("XSHG", "SS"),
("XSHE", "SZ"),
("XBOG", "CL"),
("XPRA", "PR"),
("XCSE", "CO"),
("XCAI", "CA"),
("XTAL", "TL"),
("CEUX", "XD"),
("XEUR", "NX"),
("XHEL", "HE"),
("XPAR", "PA"),
("XBER", "BE"),
("XBMS", "BM"),
("XDUS", "DU"),
("XFRA", "F"),
("XHAM", "HM"),
("XHAN", "HA"),
("XMUN", "MU"),
("XSTU", "SG"),
("XETR", "DE"),
("XATH", "AT"),
("XHKG", "HK"),
("XBUD", "BD"),
("XICE", "IC"),
("XBOM", "BO"),
("XNSE", "NS"),
("XIDX", "JK"),
("XDUB", "IR"),
("XTAE", "TA"),
("MTAA", "MI"),
("EUTL", "TI"),
("XTKS", "T"),
("XKFE", "KW"),
("XRIS", "RG"),
("XVIL", "VS"),
("XKLS", "KL"),
("XMEX", "MX"),
("XAMS", "AS"),
("XNZE", "NZ"),
("XOSL", "OL"),
("XPHS", "PS"),
("XWAR", "WA"),
("XLIS", "LS"),
("XQAT", "QA"),
("XBSE", "RO"),
("XSES", "SI"),
("XJSE", "JO"),
("XKRX", "KS"),
("KQKS", "KQ"),
("BMEX", "MC"),
("XSAU", "SR"),
("XSTO", "ST"),
("XSWX", "SW"),
("ROCO", "TWO"),
("XTAI", "TW"),
("XBKK", "BK"),
("XIST", "IS"),
("XDFM", "AE"),
("AQXE", "AQ"),
("XCHI", "XC"),
("XLON", "L"),
("ILSE", "IL"),
("XCAR", "CR"),
("XSTC", "VN"),
];
pub fn mic_to_suffix(mic: &str) -> Option<&'static str> {
let mic = mic.strip_prefix('.').unwrap_or(mic);
MIC_TO_YAHOO_SUFFIX
.iter()
.find(|(m, _)| m.eq_ignore_ascii_case(mic))
.map(|(_, s)| *s)
}
pub fn is_isin(s: &str) -> bool {
let b = s.as_bytes();
if b.len() != 12 {
return false;
}
b[0].is_ascii_alphabetic()
&& b[1].is_ascii_alphabetic()
&& (2..11).all(|i| b[i].is_ascii_alphanumeric())
&& b[11].is_ascii_digit()
}
pub fn resolve_symbol(symbol: &str, mic: &str) -> Result<String> {
let mic = mic.strip_prefix('.').unwrap_or(mic);
let sfx =
mic_to_suffix(mic).ok_or_else(|| YfError::msg(format!("Unknown MIC code: '{mic}'")))?;
let symbol = symbol.to_ascii_uppercase();
Ok(if sfx.is_empty() {
symbol
} else {
format!("{symbol}.{sfx}")
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn isin_detection() {
assert!(is_isin("US0378331005")); assert!(is_isin("GB0002634946")); assert!(!is_isin("AAPL"));
assert!(!is_isin("US037833100")); assert!(!is_isin("US037833100X")); assert!(!is_isin("12378331005")); }
#[test]
fn mic_suffix_lookup() {
assert_eq!(mic_to_suffix("XPAR"), Some("PA"));
assert_eq!(mic_to_suffix("xpar"), Some("PA")); assert_eq!(mic_to_suffix("XNYS"), Some("")); assert_eq!(mic_to_suffix(".PA"), None); assert_eq!(mic_to_suffix("ZZZZ"), None);
}
#[test]
fn resolve_symbol_pairs() {
assert_eq!(resolve_symbol("OR", "XPAR").unwrap(), "OR.PA");
assert_eq!(resolve_symbol("AAPL", "XNAS").unwrap(), "AAPL");
assert!(resolve_symbol("AAPL", "ZZZZ").is_err());
assert_eq!(resolve_symbol("mc", "XLON").unwrap(), "MC.L");
}
}