use crate::core::types::Symbol;
#[derive(Debug, Clone)]
pub struct TwelvedataUrls {
pub rest: &'static str,
pub ws: &'static str,
}
impl TwelvedataUrls {
pub const PRODUCTION: Self = Self {
rest: "https://api.twelvedata.com",
ws: "wss://ws.twelvedata.com",
};
}
impl Default for TwelvedataUrls {
fn default() -> Self {
Self::PRODUCTION
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum TwelvedataEndpoint {
Price,
Quote,
TimeSeries,
Eod,
ExchangeRate,
Stocks,
ForexPairs,
Cryptocurrencies,
Etf,
Commodities,
Indices,
SymbolSearch,
EarliestTimestamp,
Exchanges,
MarketState,
Rsi,
Macd,
BBands,
Sma,
Ema,
Logo,
Profile,
Statistics,
RealTimePrice,
ComplexData,
MutualFundsList,
BondsList,
}
impl TwelvedataEndpoint {
pub fn path(&self) -> &'static str {
match self {
Self::Price => "/price",
Self::Quote => "/quote",
Self::TimeSeries => "/time_series",
Self::Eod => "/eod",
Self::ExchangeRate => "/exchange_rate",
Self::Stocks => "/stocks",
Self::ForexPairs => "/forex_pairs",
Self::Cryptocurrencies => "/cryptocurrencies",
Self::Etf => "/etf",
Self::Commodities => "/commodities",
Self::Indices => "/indices",
Self::SymbolSearch => "/symbol_search",
Self::EarliestTimestamp => "/earliest_timestamp",
Self::Exchanges => "/exchanges",
Self::MarketState => "/market_state",
Self::Rsi => "/rsi",
Self::Macd => "/macd",
Self::BBands => "/bbands",
Self::Sma => "/sma",
Self::Ema => "/ema",
Self::Logo => "/logo",
Self::Profile => "/profile",
Self::Statistics => "/statistics",
Self::RealTimePrice => "/price",
Self::ComplexData => "/complex_data",
Self::MutualFundsList => "/mutual_funds/list",
Self::BondsList => "/bonds/list",
}
}
pub fn requires_auth(&self) -> bool {
match self {
Self::Stocks
| Self::ForexPairs
| Self::Cryptocurrencies
| Self::Etf
| Self::Commodities
| Self::Indices
| Self::Exchanges
| Self::MutualFundsList
| Self::BondsList => false,
_ => true,
}
}
pub fn method(&self) -> &'static str {
"GET"
}
pub fn credit_cost(&self) -> u32 {
match self {
Self::Price
| Self::Quote
| Self::TimeSeries
| Self::Eod
| Self::ExchangeRate
| Self::SymbolSearch
| Self::EarliestTimestamp
| Self::MarketState
| Self::Logo => 1,
Self::Stocks
| Self::ForexPairs
| Self::Cryptocurrencies
| Self::Etf
| Self::Commodities
| Self::Indices
| Self::Exchanges => 1,
Self::Rsi | Self::Macd | Self::BBands | Self::Sma | Self::Ema => 1,
Self::Profile => 10,
Self::Statistics => 5,
Self::RealTimePrice => 1,
Self::ComplexData => 1,
Self::MutualFundsList | Self::BondsList => 1,
}
}
}
pub fn format_symbol(symbol: &Symbol) -> String {
if symbol.quote.is_empty() {
return symbol.base.to_uppercase();
}
if symbol.base.len() == 3 {
format!("{}/{}", symbol.base, symbol.quote)
} else {
symbol.base.to_uppercase()
}
}
pub fn _parse_symbol(api_symbol: &str) -> Symbol {
if let Some((base, quote)) = api_symbol.split_once('/') {
Symbol::new(base, quote)
} else if let Some((ticker, _exchange)) = api_symbol.split_once(':') {
Symbol::new(ticker, "USD")
} else {
Symbol::new(api_symbol, "USD")
}
}
pub fn map_interval(interval: &str) -> &'static str {
match interval {
"1m" => "1min",
"3m" => "3min",
"5m" => "5min",
"15m" => "15min",
"30m" => "30min",
"45m" => "45min",
"1h" => "1h",
"2h" => "2h",
"4h" => "4h",
"1d" => "1day",
"1w" => "1week",
"1M" => "1month",
_ => "1h", }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_format_symbol_stock() {
let symbol = Symbol::new("AAPL", "USD");
assert_eq!(format_symbol(&symbol), "AAPL");
}
#[test]
fn test_format_symbol_crypto() {
let symbol = Symbol::new("BTC", "USDT");
assert_eq!(format_symbol(&symbol), "BTC/USDT");
}
#[test]
fn test_format_symbol_forex() {
let symbol = Symbol::new("EUR", "USD");
assert_eq!(format_symbol(&symbol), "EUR/USD");
}
#[test]
fn test_parse_symbol_stock() {
let symbol = _parse_symbol("AAPL");
assert_eq!(symbol.base, "AAPL");
assert_eq!(symbol.quote, "USD");
}
#[test]
fn test_parse_symbol_crypto() {
let symbol = _parse_symbol("BTC/USD");
assert_eq!(symbol.base, "BTC");
assert_eq!(symbol.quote, "USD");
}
#[test]
fn test_parse_symbol_exchange_qualified() {
let symbol = _parse_symbol("AAPL:NASDAQ");
assert_eq!(symbol.base, "AAPL");
assert_eq!(symbol.quote, "USD");
}
#[test]
fn test_map_interval() {
assert_eq!(map_interval("1m"), "1min");
assert_eq!(map_interval("5m"), "5min");
assert_eq!(map_interval("1h"), "1h");
assert_eq!(map_interval("1d"), "1day");
assert_eq!(map_interval("1w"), "1week");
assert_eq!(map_interval("unknown"), "1h");
}
}