use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Quote {
pub symbol: String,
pub name: String,
pub price: f64,
pub change: f64,
pub change_percent: f64,
pub previous_close: f64,
pub open: f64,
pub day_high: f64,
pub day_low: f64,
pub year_high: f64,
pub year_low: f64,
pub volume: u64,
pub avg_volume: u64,
pub market_cap: Option<u64>,
pub currency: String,
pub exchange: String,
pub quote_type: QuoteType,
pub market_state: MarketState,
pub timestamp: DateTime<Utc>,
}
impl Default for Quote {
fn default() -> Self {
Self {
symbol: String::new(),
name: String::new(),
price: 0.0,
change: 0.0,
change_percent: 0.0,
previous_close: 0.0,
open: 0.0,
day_high: 0.0,
day_low: 0.0,
year_high: 0.0,
year_low: 0.0,
volume: 0,
avg_volume: 0,
market_cap: None,
currency: "USD".to_string(),
exchange: String::new(),
quote_type: QuoteType::Equity,
market_state: MarketState::Closed,
timestamp: Utc::now(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
pub enum QuoteType {
#[default]
Equity,
Cryptocurrency,
Etf,
MutualFund,
Index,
Currency,
Future,
Option,
}
impl std::fmt::Display for QuoteType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
QuoteType::Equity => write!(f, "Stock"),
QuoteType::Cryptocurrency => write!(f, "Crypto"),
QuoteType::Etf => write!(f, "ETF"),
QuoteType::MutualFund => write!(f, "Fund"),
QuoteType::Index => write!(f, "Index"),
QuoteType::Currency => write!(f, "Forex"),
QuoteType::Future => write!(f, "Future"),
QuoteType::Option => write!(f, "Option"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
pub enum MarketState {
Pre,
Regular,
Post,
#[default]
Closed,
}
impl std::fmt::Display for MarketState {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
MarketState::Pre => write!(f, "Pre"),
MarketState::Regular => write!(f, "Open"),
MarketState::Post => write!(f, "Post"),
MarketState::Closed => write!(f, "Closed"),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Holding {
pub symbol: String,
pub quantity: f64,
pub cost_basis: f64,
}
impl Holding {
pub fn total_cost(&self) -> f64 {
self.quantity * self.cost_basis
}
pub fn current_value(&self, price: f64) -> f64 {
self.quantity * price
}
pub fn profit_loss(&self, price: f64) -> f64 {
self.current_value(price) - self.total_cost()
}
pub fn profit_loss_percent(&self, price: f64) -> f64 {
if self.total_cost() == 0.0 {
0.0
} else {
(self.profit_loss(price) / self.total_cost()) * 100.0
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
pub enum SortOrder {
#[default]
Symbol,
Name,
Price,
Change,
ChangePercent,
Volume,
MarketCap,
}
impl SortOrder {
pub fn next(self) -> Self {
match self {
SortOrder::Symbol => SortOrder::Name,
SortOrder::Name => SortOrder::Price,
SortOrder::Price => SortOrder::Change,
SortOrder::Change => SortOrder::ChangePercent,
SortOrder::ChangePercent => SortOrder::Volume,
SortOrder::Volume => SortOrder::MarketCap,
SortOrder::MarketCap => SortOrder::Symbol,
}
}
pub fn header(&self) -> &'static str {
match self {
SortOrder::Symbol => "SYMBOL",
SortOrder::Name => "NAME",
SortOrder::Price => "PRICE",
SortOrder::Change => "CHANGE",
SortOrder::ChangePercent => "CHG%",
SortOrder::Volume => "VOLUME",
SortOrder::MarketCap => "MKT CAP",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum SortDirection {
#[default]
Ascending,
Descending,
}
impl SortDirection {
pub fn toggle(self) -> Self {
match self {
SortDirection::Ascending => SortDirection::Descending,
SortDirection::Descending => SortDirection::Ascending,
}
}
}