aboutsummaryrefslogtreecommitdiff
path: root/src/app/store
diff options
context:
space:
mode:
authorKevin Hoerr <kjhoerr@submelon.dev>2026-04-16 17:07:14 -0400
committerKevin Hoerr <kjhoerr@submelon.dev>2026-04-16 17:07:14 -0400
commitf6c34724c49de095055f0861128682440af9bd74 (patch)
treef1cc221e946b3619d0dc8df84298a79eb0a2176a /src/app/store
parent223a91e938bc019cad1566753eb6bb61c3cec779 (diff)
downloadequity-tracker-f6c34724c49de095055f0861128682440af9bd74.tar.gz
equity-tracker-f6c34724c49de095055f0861128682440af9bd74.tar.bz2
equity-tracker-f6c34724c49de095055f0861128682440af9bd74.zip
ledger.rs: Compile change events for account + symbol
Diffstat (limited to 'src/app/store')
-rw-r--r--src/app/store/ledger.rs56
-rw-r--r--src/app/store/mod.rs2
2 files changed, 58 insertions, 0 deletions
diff --git a/src/app/store/ledger.rs b/src/app/store/ledger.rs
new file mode 100644
index 0000000..841f7b3
--- /dev/null
+++ b/src/app/store/ledger.rs
@@ -0,0 +1,56 @@
+use bigdecimal::{BigDecimal, Zero};
+use crate::app::store::records::{EquityChangeEvent, EquitySymbol, FiduciaryAccount, ROUNDING_MODE};
+
+/// An estimated ledger is the effective sum of all the equity change events
+/// derived from record for a particular fiduciary account and equity symbol.
+pub struct EstimatedLedger<'a> {
+ pub fiduciary_account: &'a FiduciaryAccount,
+ pub equity_symbol: &'a EquitySymbol,
+ pub equity_change_events: Vec<&'a EquityChangeEvent>,
+ pub account_symbol_quantity: BigDecimal,
+ pub account_symbol_cost_basis_price: BigDecimal,
+ pub account_symbol_cost_basis_total_usd: BigDecimal,
+ pub account_symbol_last_effective_price: BigDecimal,
+ pub account_symbol_last_update_timestamp: chrono::DateTime<chrono::Utc>,
+}
+
+impl EstimatedLedger<'_> {
+
+ /// Takes a list of all change events, a fiduciary account, and an equity
+ /// symbol for which to create a ledger.
+ ///
+ /// All values are taken by reference, so they need to be maintained elsewhere
+ pub fn compile_change_events<'a>(fiduciary_account: &'a FiduciaryAccount, equity_symbol: &'a EquitySymbol, events: Vec<&'a EquityChangeEvent>) -> EstimatedLedger<'a> {
+ let mut acsym_events = vec![];
+ let mut quantity = BigDecimal::zero();
+ let mut cost_basis_total = BigDecimal::zero();
+ let mut last_event: Option<&EquityChangeEvent> = None;
+
+ for event in events {
+ if event.fiduciary_account_id != fiduciary_account.fiduciary_account_id || event.equity_symbol_id != equity_symbol.equity_symbol_id {
+ continue;
+ }
+
+ if last_event.is_none() || last_event.unwrap().equity_change_timestamp < event.equity_change_timestamp {
+ last_event = Some(event);
+ }
+
+ quantity += event.scored_quantity();
+ cost_basis_total += event.scored_cost_basis();
+
+ acsym_events.push(event);
+ }
+
+ EstimatedLedger {
+ fiduciary_account,
+ equity_symbol,
+ equity_change_events: acsym_events,
+ account_symbol_quantity: quantity.clone(),
+ account_symbol_cost_basis_price: (cost_basis_total.clone() / quantity).with_scale_round(2, ROUNDING_MODE),
+ account_symbol_cost_basis_total_usd: cost_basis_total.with_scale_round(2, ROUNDING_MODE),
+ account_symbol_last_effective_price: last_event.map_or_else(|| BigDecimal::zero().with_scale(2), |ev| ev.estimated_price()),
+ account_symbol_last_update_timestamp: last_event.map_or_else(|| chrono::DateTime::<chrono::Utc>::MIN_UTC, |ev| ev.equity_change_timestamp),
+ }
+ }
+
+} \ No newline at end of file
diff --git a/src/app/store/mod.rs b/src/app/store/mod.rs
index d038167..da01fba 100644
--- a/src/app/store/mod.rs
+++ b/src/app/store/mod.rs
@@ -1,4 +1,6 @@
mod sqlite;
+mod records;
+mod ledger;
use sqlite::{SQLiteStore};
use miette::{Diagnostic, Result};