aboutsummaryrefslogtreecommitdiff
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
parent827a53b25c6bd831278697018acf8e15346fb32c (diff)
downloadequity-tracker-ff406d90e2018ae7e5a70dbcd52fea37f298c9da.tar.gz
equity-tracker-ff406d90e2018ae7e5a70dbcd52fea37f298c9da.tar.bz2
equity-tracker-ff406d90e2018ae7e5a70dbcd52fea37f298c9da.zip
Add records mod to represent schema
-rw-r--r--Cargo.lock145
-rw-r--r--Cargo.toml2
-rw-r--r--src/app/store/records.rs150
3 files changed, 297 insertions, 0 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 1ee8226..6f021bd 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -33,6 +33,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
+
+[[package]]
name = "anyhow"
version = "1.0.102"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -84,6 +93,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
+name = "bigdecimal"
+version = "0.4.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4d6867f1565b3aad85681f1015055b087fcfd840d6aeee6eee7f2da317603695"
+dependencies = [
+ "autocfg",
+ "libm",
+ "num-bigint",
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
name = "bit-set"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -163,6 +185,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
[[package]]
+name = "chrono"
+version = "0.4.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0"
+dependencies = [
+ "iana-time-zone",
+ "js-sys",
+ "num-traits",
+ "wasm-bindgen",
+ "windows-link",
+]
+
+[[package]]
name = "compact_str"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -186,6 +221,12 @@ dependencies = [
]
[[package]]
+name = "core-foundation-sys"
+version = "0.8.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
+
+[[package]]
name = "cpufeatures"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -341,6 +382,8 @@ checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
name = "equity-tracker"
version = "0.1.0"
dependencies = [
+ "bigdecimal",
+ "chrono",
"crossterm",
"miette",
"ratatui",
@@ -504,6 +547,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
+name = "iana-time-zone"
+version = "0.1.65"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470"
+dependencies = [
+ "android_system_properties",
+ "core-foundation-sys",
+ "iana-time-zone-haiku",
+ "js-sys",
+ "log",
+ "wasm-bindgen",
+ "windows-core",
+]
+
+[[package]]
+name = "iana-time-zone-haiku"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
+dependencies = [
+ "cc",
+]
+
+[[package]]
name = "id-arena"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -616,6 +683,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112"
[[package]]
+name = "libm"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981"
+
+[[package]]
name = "line-clipping"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -772,6 +845,16 @@ dependencies = [
]
[[package]]
+name = "num-bigint"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
+dependencies = [
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
name = "num-conv"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -789,6 +872,15 @@ dependencies = [
]
[[package]]
+name = "num-integer"
+version = "0.1.46"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
name = "num-traits"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1837,12 +1929,65 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
+name = "windows-core"
+version = "0.62.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
+dependencies = [
+ "windows-implement",
+ "windows-interface",
+ "windows-link",
+ "windows-result",
+ "windows-strings",
+]
+
+[[package]]
+name = "windows-implement"
+version = "0.60.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
+name = "windows-interface"
+version = "0.59.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.117",
+]
+
+[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
+name = "windows-result"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
+dependencies = [
+ "windows-link",
+]
+
+[[package]]
+name = "windows-strings"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
+dependencies = [
+ "windows-link",
+]
+
+[[package]]
name = "windows-sys"
version = "0.60.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index e5e7a5a..0e395e7 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -11,6 +11,8 @@ authors = ["Kevin Hoerr <kjhoerr@submelon.dev>"]
publish = false
[dependencies]
+bigdecimal = "0.4.10"
+chrono = "0.4.44"
crossterm = "0.29.0"
miette = { version = "7.6.0", features = ["fancy"] }
ratatui = "0.30.0"
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