pub struct TwelvedataWebSocket {
api_key: String,
}
impl TwelvedataWebSocket {
pub fn new(api_key: impl Into<String>) -> Self {
Self {
api_key: api_key.into(),
}
}
pub fn ws_url(&self) -> String {
format!("wss://ws.twelvedata.com/v1/quotes/price?apikey={}", self.api_key)
}
pub fn subscribe_message(&self, symbols: &[String]) -> String {
let symbols_str = symbols.join(",");
format!(
r#"{{"action":"subscribe","params":{{"symbols":"{}"}}}}"#,
symbols_str
)
}
pub fn unsubscribe_message(&self, symbols: &[String]) -> String {
let symbols_str = symbols.join(",");
format!(
r#"{{"action":"unsubscribe","params":{{"symbols":"{}"}}}}"#,
symbols_str
)
}
pub fn heartbeat_message(&self) -> String {
r#"{"action":"heartbeat"}"#.to_string()
}
pub fn reset_message(&self) -> String {
r#"{"action":"reset"}"#.to_string()
}
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct TwelvedataPriceEvent {
pub symbol: String,
pub exchange: String,
pub asset_type: String,
pub currency: String,
pub price: f64,
pub bid: Option<f64>,
pub ask: Option<f64>,
pub day_volume: Option<f64>,
pub timestamp: i64,
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub enum TwelvedataWsEvent {
Price(TwelvedataPriceEvent),
Connected,
Subscribed { symbols: Vec<String> },
Unsubscribed { symbols: Vec<String> },
HeartbeatAck,
Error { code: i32, message: String },
Closed,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ws_url() {
let ws = TwelvedataWebSocket::new("test_key");
let url = ws.ws_url();
assert!(url.starts_with("wss://ws.twelvedata.com"));
assert!(url.contains("apikey=test_key"));
}
#[test]
fn test_subscribe_message() {
let ws = TwelvedataWebSocket::new("test_key");
let msg = ws.subscribe_message(&["AAPL".to_string(), "TSLA".to_string()]);
assert!(msg.contains(r#""action":"subscribe""#));
assert!(msg.contains("AAPL,TSLA"));
}
#[test]
fn test_heartbeat_message() {
let ws = TwelvedataWebSocket::new("test_key");
let msg = ws.heartbeat_message();
assert_eq!(msg, r#"{"action":"heartbeat"}"#);
}
}