diff options
| author | Kevin Hoerr <kjhoerr@submelon.dev> | 2026-04-16 16:14:33 -0400 |
|---|---|---|
| committer | Kevin Hoerr <kjhoerr@submelon.dev> | 2026-04-16 16:14:33 -0400 |
| commit | ff406d90e2018ae7e5a70dbcd52fea37f298c9da (patch) | |
| tree | 1f2ec4de61a4aa8f0348ec3f15bb50b686149b10 /src/app/store | |
| parent | 827a53b25c6bd831278697018acf8e15346fb32c (diff) | |
| download | equity-tracker-ff406d90e2018ae7e5a70dbcd52fea37f298c9da.tar.gz equity-tracker-ff406d90e2018ae7e5a70dbcd52fea37f298c9da.tar.bz2 equity-tracker-ff406d90e2018ae7e5a70dbcd52fea37f298c9da.zip | |
Add records mod to represent schema
Diffstat (limited to 'src/app/store')
| -rw-r--r-- | src/app/store/records.rs | 150 |
1 files changed, 150 insertions, 0 deletions
diff --git a/src/app/store/records.rs b/src/app/store/records.rs new file mode 100644 index 0000000..3f73765 --- /dev/null +++ b/src/app/store/records.rs @@ -0,0 +1,150 @@ +use bigdecimal::{BigDecimal, Context, RoundingMode}; +use std::sync::LazyLock; + +static ROUNDING_MODE: RoundingMode = RoundingMode::HalfEven; +/// Will use default precision (either 100 or ${RUST_BIGDECIMAL_DEFAULT_PRECISION}) +static DECIMAL_CONTEXT: LazyLock<Context> = std::sync::LazyLock::new(|| Context::default().with_rounding_mode(ROUNDING_MODE)); + +/// Represents an account that holds equity. An account may hold equity with +/// multiple equity symbols. +pub struct FiduciaryAccount { + pub fiduciary_account_id: u8, + pub fiduciary_account_number: usize, + pub fiduciary_account_name: String, + pub fiduciary_account_description: String, + pub fiduciary_account_created_timestamp: chrono::DateTime<chrono::Utc>, +} + +/// An atomic enum to describe for what element of equity a symbol tracks +/// (stock, fund, etc.) +pub enum EquityType { + STOCK, + ETF, + CRYPTO +} + +impl std::fmt::Display for EquityType { + + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", match self { + EquityType::STOCK => "STOCK", + EquityType::ETF => "ETF", + EquityType::CRYPTO => "CRYPTO", + }) + } + +} + +/// Represents a symbol used to ascribe speculative value +pub struct EquitySymbol { + pub equity_symbol_id: usize, + pub equity_symbol_name: String, + pub equity_symbol_type: EquityType, + pub equity_symbol_managing_company: String, + pub equity_symbol_created_timestamp: chrono::DateTime<chrono::Utc>, +} + +/// An atomic enum to describe the basic action of an equity change event +pub enum ChangeEventType { + BUY, + SELL +} + +impl ChangeEventType { + + /// Relativizes the effect of a change event for numeration + pub fn score(&self) -> i8 { + match self { + ChangeEventType::BUY => 1, + ChangeEventType::SELL => -1, + } + } + +} + +impl std::fmt::Display for ChangeEventType { + + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", match self { + ChangeEventType::BUY => "BUY", + ChangeEventType::SELL => "SELL", + }) + } + +} + +/// An immutable record describing a quantative change for a symbolized +/// equity attributed to an account. +pub struct EquityChangeEvent { + pub equity_change_event_id: usize, + pub fiduciary_account_id: usize, + pub equity_symbol_id: usize, + pub equity_change_quantity: BigDecimal, + pub equity_change_cost_basis_usd: BigDecimal, + pub equity_change_type: ChangeEventType, + pub equity_change_timestamp: chrono::DateTime<chrono::Utc>, +} + +impl EquityChangeEvent { + + /// Provides an estimate of the numerated change based on the cost basis + /// of the equity and the quantity + pub fn estimated_price(&self) -> BigDecimal { + DECIMAL_CONTEXT.multiply(&self.equity_change_cost_basis_usd, &self.equity_change_quantity) + } + +} + +mod tests { + use super::*; + use std::str::FromStr; + + fn nat(num: BigDecimal) -> BigDecimal { + num.with_scale_round(2_i64, ROUNDING_MODE) + } + + #[test] + fn event_type_score_buy() { + let eventType = ChangeEventType::BUY; + + assert_eq!(eventType.score(), 1_i8); + } + + #[test] + fn event_type_score_sell() { + let eventType = ChangeEventType::SELL; + + assert_eq!(eventType.score(), -1_i8); + } + + #[test] + fn change_event_estimated_price() { + let event = EquityChangeEvent { + equity_change_event_id: 0, + fiduciary_account_id: 0, + equity_symbol_id: 0, + equity_change_quantity: BigDecimal::from_str("0.125").unwrap(), + equity_change_cost_basis_usd: BigDecimal::from_str("82.80").unwrap(), + equity_change_type: ChangeEventType::BUY, + equity_change_timestamp: chrono::Local::now().to_utc(), + }; + + assert_eq!(nat(event.estimated_price()).to_plain_string(), "10.35"); + } + + #[test] + fn change_event_estimated_price_rounded() { + let event = EquityChangeEvent { + equity_change_event_id: 0, + fiduciary_account_id: 0, + equity_symbol_id: 0, + equity_change_quantity: BigDecimal::from_str("87.12808").unwrap(), + equity_change_cost_basis_usd: BigDecimal::from_str("14.91").unwrap(), + equity_change_type: ChangeEventType::BUY, + equity_change_timestamp: chrono::Local::now().to_utc(), + }; + + // actual: 1299.0796728 + assert_eq!(nat(event.estimated_price()).to_plain_string(), "1299.08"); + } +}
\ No newline at end of file |
