aboutsummaryrefslogtreecommitdiff
path: root/src/app/store
diff options
context:
space:
mode:
authorKevin Hoerr <kjhoerr@submelon.dev>2026-04-16 16:14:33 -0400
committerKevin Hoerr <kjhoerr@submelon.dev>2026-04-16 16:14:33 -0400
commitff406d90e2018ae7e5a70dbcd52fea37f298c9da (patch)
tree1f2ec4de61a4aa8f0348ec3f15bb50b686149b10 /src/app/store
parent827a53b25c6bd831278697018acf8e15346fb32c (diff)
downloadequity-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.rs150
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