mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-04-24 06:39:58 -07:00
global: snap
This commit is contained in:
@@ -37,22 +37,25 @@ mod count;
|
||||
mod supply;
|
||||
|
||||
pub use count::{AddrTypeToExposedAddrCount, ExposedAddrCountsVecs};
|
||||
pub use supply::{AddrTypeToExposedAddrSupply, ExposedAddrSupplyVecs};
|
||||
pub use supply::{AddrTypeToExposedSupply, ExposedAddrSupplyVecs, ExposedSupplyShareVecs};
|
||||
|
||||
use brk_cohort::ByAddrType;
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Indexes, Version};
|
||||
use brk_types::{Height, Indexes, Sats, Version};
|
||||
use rayon::prelude::*;
|
||||
use vecdb::{AnyStoredVec, Database, Exit, Rw, StorageMode};
|
||||
use vecdb::{AnyStoredVec, Database, Exit, ReadableVec, Rw, StorageMode};
|
||||
|
||||
use crate::{indexes, prices};
|
||||
use crate::{indexes, internal::RatioSatsBp16, prices};
|
||||
|
||||
/// Top-level container for all exposed address tracking: counts (funded +
|
||||
/// total) plus the funded supply.
|
||||
/// total), the funded supply, and share of supply.
|
||||
#[derive(Traversable)]
|
||||
pub struct ExposedAddrVecs<M: StorageMode = Rw> {
|
||||
pub count: ExposedAddrCountsVecs<M>,
|
||||
pub supply: ExposedAddrSupplyVecs<M>,
|
||||
#[traversable(wrap = "supply", rename = "share")]
|
||||
pub supply_share: ExposedSupplyShareVecs<M>,
|
||||
}
|
||||
|
||||
impl ExposedAddrVecs {
|
||||
@@ -64,6 +67,7 @@ impl ExposedAddrVecs {
|
||||
Ok(Self {
|
||||
count: ExposedAddrCountsVecs::forced_import(db, version, indexes)?,
|
||||
supply: ExposedAddrSupplyVecs::forced_import(db, version, indexes)?,
|
||||
supply_share: ExposedSupplyShareVecs::forced_import(db, version, indexes)?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -84,6 +88,7 @@ impl ExposedAddrVecs {
|
||||
pub(crate) fn reset_height(&mut self) -> Result<()> {
|
||||
self.count.reset_height()?;
|
||||
self.supply.reset_height()?;
|
||||
self.supply_share.reset_height()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -91,11 +96,39 @@ impl ExposedAddrVecs {
|
||||
&mut self,
|
||||
starting_indexes: &Indexes,
|
||||
prices: &prices::Vecs,
|
||||
all_supply_sats: &impl ReadableVec<Height, Sats>,
|
||||
type_supply_sats: &ByAddrType<&impl ReadableVec<Height, Sats>>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.count.compute_rest(starting_indexes, exit)?;
|
||||
self.supply
|
||||
.compute_rest(starting_indexes.height, prices, exit)?;
|
||||
|
||||
let max_from = starting_indexes.height;
|
||||
|
||||
self.supply_share
|
||||
.all
|
||||
.compute_binary::<Sats, Sats, RatioSatsBp16>(
|
||||
max_from,
|
||||
&self.supply.all.sats.height,
|
||||
all_supply_sats,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
for ((_, share), ((_, exposed), (_, denom))) in self
|
||||
.supply_share
|
||||
.by_addr_type
|
||||
.iter_mut()
|
||||
.zip(self.supply.by_addr_type.iter().zip(type_supply_sats.iter()))
|
||||
{
|
||||
share.compute_binary::<Sats, Sats, RatioSatsBp16>(
|
||||
max_from,
|
||||
&exposed.sats.height,
|
||||
*denom,
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,10 @@
|
||||
//! aggregated `all`. See the parent [`super`] module for the definition of
|
||||
//! "exposed" and how it varies by address type.
|
||||
|
||||
mod share;
|
||||
mod state;
|
||||
mod vecs;
|
||||
|
||||
pub use state::AddrTypeToExposedAddrSupply;
|
||||
pub use share::ExposedSupplyShareVecs;
|
||||
pub use state::AddrTypeToExposedSupply;
|
||||
pub use vecs::ExposedAddrSupplyVecs;
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{BasisPoints16, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{Database, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{PercentPerBlock, WithAddrTypes},
|
||||
};
|
||||
|
||||
/// Share of exposed supply relative to total supply.
|
||||
///
|
||||
/// - `all`: exposed_supply / circulating_supply
|
||||
/// - Per-type: type's exposed_supply / type's total supply
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
pub struct ExposedSupplyShareVecs<M: StorageMode = Rw>(
|
||||
#[traversable(flatten)] pub WithAddrTypes<PercentPerBlock<BasisPoints16, M>>,
|
||||
);
|
||||
|
||||
impl ExposedSupplyShareVecs {
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
Ok(Self(
|
||||
WithAddrTypes::<PercentPerBlock<BasisPoints16>>::forced_import(
|
||||
db,
|
||||
"exposed_supply_share",
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
))
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
use brk_cohort::ByAddrType;
|
||||
use brk_types::Height;
|
||||
use brk_types::{Height, Sats};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::ReadableVec;
|
||||
|
||||
@@ -10,22 +10,21 @@ use super::vecs::ExposedAddrSupplyVecs;
|
||||
/// Runtime running counter for the total balance (sats) held by funded
|
||||
/// exposed addresses, per address type.
|
||||
#[derive(Debug, Default, Deref, DerefMut)]
|
||||
pub struct AddrTypeToExposedAddrSupply(ByAddrType<u64>);
|
||||
pub struct AddrTypeToExposedSupply(ByAddrType<Sats>);
|
||||
|
||||
impl AddrTypeToExposedAddrSupply {
|
||||
impl AddrTypeToExposedSupply {
|
||||
#[inline]
|
||||
pub(crate) fn sum(&self) -> u64 {
|
||||
self.0.values().sum()
|
||||
pub(crate) fn sum(&self) -> Sats {
|
||||
self.0.values().copied().sum()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(&ExposedAddrSupplyVecs, Height)> for AddrTypeToExposedAddrSupply {
|
||||
impl From<(&ExposedAddrSupplyVecs, Height)> for AddrTypeToExposedSupply {
|
||||
#[inline]
|
||||
fn from((vecs, starting_height): (&ExposedAddrSupplyVecs, Height)) -> Self {
|
||||
if let Some(prev_height) = starting_height.decremented() {
|
||||
let read = |v: &AmountPerBlock| -> u64 {
|
||||
u64::from(v.sats.height.collect_one(prev_height).unwrap())
|
||||
};
|
||||
let read =
|
||||
|v: &AmountPerBlock| -> Sats { v.sats.height.collect_one(prev_height).unwrap() };
|
||||
Self(ByAddrType {
|
||||
p2pk65: read(&vecs.by_addr_type.p2pk65),
|
||||
p2pk33: read(&vecs.by_addr_type.p2pk33),
|
||||
|
||||
@@ -26,7 +26,7 @@ impl ExposedAddrSupplyVecs {
|
||||
) -> Result<Self> {
|
||||
Ok(Self(WithAddrTypes::<AmountPerBlock>::forced_import(
|
||||
db,
|
||||
"exposed_addr_supply",
|
||||
"exposed_supply",
|
||||
version,
|
||||
indexes,
|
||||
)?))
|
||||
|
||||
@@ -13,13 +13,9 @@ pub use activity::{AddrActivityVecs, AddrTypeToActivityCounts};
|
||||
pub use addr_count::{AddrCountsVecs, AddrTypeToAddrCount};
|
||||
pub use data::AddrsDataVecs;
|
||||
pub use delta::DeltaVecs;
|
||||
pub use exposed::{
|
||||
AddrTypeToExposedAddrCount, AddrTypeToExposedAddrSupply, ExposedAddrVecs,
|
||||
};
|
||||
pub use exposed::{AddrTypeToExposedAddrCount, AddrTypeToExposedSupply, ExposedAddrVecs,};
|
||||
pub use indexes::AnyAddrIndexesVecs;
|
||||
pub use new_addr_count::NewAddrCountVecs;
|
||||
pub use reused::{
|
||||
AddrTypeToReusedAddrCount, AddrTypeToReusedAddrEventCount, ReusedAddrVecs,
|
||||
};
|
||||
pub use reused::{AddrTypeToReusedAddrCount, AddrTypeToReusedAddrEventCount, ReusedAddrVecs};
|
||||
pub use total_addr_count::TotalAddrCountVecs;
|
||||
pub use type_map::{AddrTypeToTypeIndexMap, AddrTypeToVec, HeightToAddrTypeToVec};
|
||||
|
||||
@@ -4,7 +4,7 @@ use rustc_hash::FxHashMap;
|
||||
|
||||
use crate::distribution::{
|
||||
addr::{
|
||||
AddrTypeToActivityCounts, AddrTypeToExposedAddrCount, AddrTypeToExposedAddrSupply,
|
||||
AddrTypeToActivityCounts, AddrTypeToExposedAddrCount, AddrTypeToExposedSupply,
|
||||
AddrTypeToReusedAddrCount, AddrTypeToReusedAddrEventCount, AddrTypeToVec,
|
||||
},
|
||||
cohorts::AddrCohorts,
|
||||
@@ -34,7 +34,7 @@ pub(crate) fn process_received(
|
||||
active_reused_addr_count: &mut AddrTypeToReusedAddrEventCount,
|
||||
exposed_addr_count: &mut AddrTypeToExposedAddrCount,
|
||||
total_exposed_addr_count: &mut AddrTypeToExposedAddrCount,
|
||||
exposed_addr_supply: &mut AddrTypeToExposedAddrSupply,
|
||||
exposed_supply: &mut AddrTypeToExposedSupply,
|
||||
) {
|
||||
let max_type_len = received_data
|
||||
.iter()
|
||||
@@ -56,11 +56,10 @@ pub(crate) fn process_received(
|
||||
let type_reused_count = reused_addr_count.get_mut(output_type).unwrap();
|
||||
let type_total_reused_count = total_reused_addr_count.get_mut(output_type).unwrap();
|
||||
let type_output_to_reused_count = output_to_reused_addr_count.get_mut(output_type).unwrap();
|
||||
let type_active_reused_count =
|
||||
active_reused_addr_count.get_mut(output_type).unwrap();
|
||||
let type_active_reused_count = active_reused_addr_count.get_mut(output_type).unwrap();
|
||||
let type_exposed_count = exposed_addr_count.get_mut(output_type).unwrap();
|
||||
let type_total_exposed_count = total_exposed_addr_count.get_mut(output_type).unwrap();
|
||||
let type_exposed_supply = exposed_addr_supply.get_mut(output_type).unwrap();
|
||||
let type_exposed_supply = exposed_supply.get_mut(output_type).unwrap();
|
||||
|
||||
// Aggregate receives by address - each address processed exactly once
|
||||
for (type_index, value) in vec {
|
||||
@@ -206,15 +205,12 @@ pub(crate) fn process_received(
|
||||
if !was_funded && was_pubkey_exposed {
|
||||
*type_exposed_count += 1;
|
||||
}
|
||||
if output_type.pubkey_exposed_at_funding()
|
||||
&& matches!(status, TrackingStatus::New)
|
||||
{
|
||||
if output_type.pubkey_exposed_at_funding() && matches!(status, TrackingStatus::New) {
|
||||
*type_total_exposed_count += 1;
|
||||
}
|
||||
|
||||
// Update exposed supply via post-receive contribution delta.
|
||||
let exposed_contribution_after =
|
||||
addr_data.exposed_supply_contribution(output_type);
|
||||
let exposed_contribution_after = addr_data.exposed_supply_contribution(output_type);
|
||||
// Receives can only add to balance and membership, so the delta
|
||||
// is always non-negative.
|
||||
*type_exposed_supply += exposed_contribution_after - exposed_contribution_before;
|
||||
|
||||
@@ -6,7 +6,7 @@ use vecdb::VecIndex;
|
||||
|
||||
use crate::distribution::{
|
||||
addr::{
|
||||
AddrTypeToActivityCounts, AddrTypeToExposedAddrCount, AddrTypeToExposedAddrSupply,
|
||||
AddrTypeToActivityCounts, AddrTypeToExposedAddrCount, AddrTypeToExposedSupply,
|
||||
AddrTypeToReusedAddrCount, AddrTypeToReusedAddrEventCount, HeightToAddrTypeToVec,
|
||||
},
|
||||
cohorts::AddrCohorts,
|
||||
@@ -43,7 +43,7 @@ pub(crate) fn process_sent(
|
||||
active_reused_addr_count: &mut AddrTypeToReusedAddrEventCount,
|
||||
exposed_addr_count: &mut AddrTypeToExposedAddrCount,
|
||||
total_exposed_addr_count: &mut AddrTypeToExposedAddrCount,
|
||||
exposed_addr_supply: &mut AddrTypeToExposedAddrSupply,
|
||||
exposed_supply: &mut AddrTypeToExposedSupply,
|
||||
received_addrs: &ByAddrType<FxHashSet<TypeIndex>>,
|
||||
height_to_price: &[Cents],
|
||||
height_to_timestamp: &[Timestamp],
|
||||
@@ -69,11 +69,10 @@ pub(crate) fn process_sent(
|
||||
let type_reused_count = reused_addr_count.get_mut(output_type).unwrap();
|
||||
let type_input_from_reused_count =
|
||||
input_from_reused_addr_count.get_mut(output_type).unwrap();
|
||||
let type_active_reused_count =
|
||||
active_reused_addr_count.get_mut(output_type).unwrap();
|
||||
let type_active_reused_count = active_reused_addr_count.get_mut(output_type).unwrap();
|
||||
let type_exposed_count = exposed_addr_count.get_mut(output_type).unwrap();
|
||||
let type_total_exposed_count = total_exposed_addr_count.get_mut(output_type).unwrap();
|
||||
let type_exposed_supply = exposed_addr_supply.get_mut(output_type).unwrap();
|
||||
let type_exposed_supply = exposed_supply.get_mut(output_type).unwrap();
|
||||
let type_received = received_addrs.get(output_type);
|
||||
let type_seen = seen_senders.get_mut_unwrap(output_type);
|
||||
|
||||
@@ -96,8 +95,7 @@ pub(crate) fn process_sent(
|
||||
if type_seen.insert(type_index) {
|
||||
type_activity.sending += 1;
|
||||
|
||||
let also_received =
|
||||
type_received.is_some_and(|s| s.contains(&type_index));
|
||||
let also_received = type_received.is_some_and(|s| s.contains(&type_index));
|
||||
// Track "bidirectional": addresses that sent AND
|
||||
// received this block.
|
||||
if also_received {
|
||||
@@ -136,12 +134,13 @@ pub(crate) fn process_sent(
|
||||
// addr_data.spent_txo_count is now incremented by 1.
|
||||
|
||||
// Update exposed supply via post-spend contribution delta.
|
||||
let exposed_contribution_after =
|
||||
addr_data.exposed_supply_contribution(output_type);
|
||||
let exposed_contribution_after = addr_data.exposed_supply_contribution(output_type);
|
||||
if exposed_contribution_after >= exposed_contribution_before {
|
||||
*type_exposed_supply += exposed_contribution_after - exposed_contribution_before;
|
||||
*type_exposed_supply +=
|
||||
exposed_contribution_after - exposed_contribution_before;
|
||||
} else {
|
||||
*type_exposed_supply -= exposed_contribution_before - exposed_contribution_after;
|
||||
*type_exposed_supply -=
|
||||
exposed_contribution_before - exposed_contribution_after;
|
||||
}
|
||||
|
||||
// Update exposed counts on first-ever pubkey exposure.
|
||||
|
||||
@@ -830,26 +830,26 @@ impl UTXOCohorts<Rw> {
|
||||
let mut sth_acc = RealizedFullAccum::default();
|
||||
let mut lth_acc = RealizedFullAccum::default();
|
||||
|
||||
let mut all_icap = (0u128, 0u128);
|
||||
let mut sth_icap = (0u128, 0u128);
|
||||
let mut lth_icap = (0u128, 0u128);
|
||||
let mut all_ccap = (0u128, 0u128);
|
||||
let mut sth_ccap = (0u128, 0u128);
|
||||
let mut lth_ccap = (0u128, 0u128);
|
||||
|
||||
for ar in age_range.iter_mut() {
|
||||
if let Some(state) = ar.state.as_mut() {
|
||||
all_acc.add(&state.realized);
|
||||
|
||||
let u = state.compute_unrealized_state(height_price);
|
||||
all_icap.0 += u.investor_cap_in_profit_raw;
|
||||
all_icap.1 += u.investor_cap_in_loss_raw;
|
||||
all_ccap.0 += u.capitalized_cap_in_profit_raw;
|
||||
all_ccap.1 += u.capitalized_cap_in_loss_raw;
|
||||
|
||||
if sth_filter.includes(&ar.metrics.filter) {
|
||||
sth_acc.add(&state.realized);
|
||||
sth_icap.0 += u.investor_cap_in_profit_raw;
|
||||
sth_icap.1 += u.investor_cap_in_loss_raw;
|
||||
sth_ccap.0 += u.capitalized_cap_in_profit_raw;
|
||||
sth_ccap.1 += u.capitalized_cap_in_loss_raw;
|
||||
} else {
|
||||
lth_acc.add(&state.realized);
|
||||
lth_icap.0 += u.investor_cap_in_profit_raw;
|
||||
lth_icap.1 += u.investor_cap_in_loss_raw;
|
||||
lth_ccap.0 += u.capitalized_cap_in_profit_raw;
|
||||
lth_ccap.1 += u.capitalized_cap_in_loss_raw;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -860,28 +860,28 @@ impl UTXOCohorts<Rw> {
|
||||
|
||||
all.metrics
|
||||
.unrealized
|
||||
.investor_cap_in_profit_raw
|
||||
.push(CentsSquaredSats::new(all_icap.0));
|
||||
.capitalized_cap_in_profit_raw
|
||||
.push(CentsSquaredSats::new(all_ccap.0));
|
||||
all.metrics
|
||||
.unrealized
|
||||
.investor_cap_in_loss_raw
|
||||
.push(CentsSquaredSats::new(all_icap.1));
|
||||
.capitalized_cap_in_loss_raw
|
||||
.push(CentsSquaredSats::new(all_ccap.1));
|
||||
sth.metrics
|
||||
.unrealized
|
||||
.investor_cap_in_profit_raw
|
||||
.push(CentsSquaredSats::new(sth_icap.0));
|
||||
.capitalized_cap_in_profit_raw
|
||||
.push(CentsSquaredSats::new(sth_ccap.0));
|
||||
sth.metrics
|
||||
.unrealized
|
||||
.investor_cap_in_loss_raw
|
||||
.push(CentsSquaredSats::new(sth_icap.1));
|
||||
.capitalized_cap_in_loss_raw
|
||||
.push(CentsSquaredSats::new(sth_ccap.1));
|
||||
lth.metrics
|
||||
.unrealized
|
||||
.investor_cap_in_profit_raw
|
||||
.push(CentsSquaredSats::new(lth_icap.0));
|
||||
.capitalized_cap_in_profit_raw
|
||||
.push(CentsSquaredSats::new(lth_ccap.0));
|
||||
lth.metrics
|
||||
.unrealized
|
||||
.investor_cap_in_loss_raw
|
||||
.push(CentsSquaredSats::new(lth_icap.1));
|
||||
.capitalized_cap_in_loss_raw
|
||||
.push(CentsSquaredSats::new(lth_ccap.1));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,8 +13,7 @@ use crate::{
|
||||
distribution::{
|
||||
addr::{
|
||||
AddrTypeToActivityCounts, AddrTypeToAddrCount, AddrTypeToExposedAddrCount,
|
||||
AddrTypeToExposedAddrSupply, AddrTypeToReusedAddrCount,
|
||||
AddrTypeToReusedAddrEventCount,
|
||||
AddrTypeToExposedSupply, AddrTypeToReusedAddrCount, AddrTypeToReusedAddrEventCount,
|
||||
},
|
||||
block::{
|
||||
AddrCache, InputsResult, process_inputs, process_outputs, process_received,
|
||||
@@ -203,7 +202,7 @@ pub(crate) fn process_blocks(
|
||||
mut total_reused_addr_counts,
|
||||
mut exposed_addr_counts,
|
||||
mut total_exposed_addr_counts,
|
||||
mut exposed_addr_supply,
|
||||
mut exposed_supply,
|
||||
) = if starting_height > Height::ZERO {
|
||||
(
|
||||
AddrTypeToAddrCount::from((&vecs.addrs.funded.by_addr_type, starting_height)),
|
||||
@@ -212,7 +211,7 @@ pub(crate) fn process_blocks(
|
||||
AddrTypeToReusedAddrCount::from((&vecs.addrs.reused.count.total, starting_height)),
|
||||
AddrTypeToExposedAddrCount::from((&vecs.addrs.exposed.count.funded, starting_height)),
|
||||
AddrTypeToExposedAddrCount::from((&vecs.addrs.exposed.count.total, starting_height)),
|
||||
AddrTypeToExposedAddrSupply::from((&vecs.addrs.exposed.supply, starting_height)),
|
||||
AddrTypeToExposedSupply::from((&vecs.addrs.exposed.supply, starting_height)),
|
||||
)
|
||||
} else {
|
||||
(
|
||||
@@ -222,7 +221,7 @@ pub(crate) fn process_blocks(
|
||||
AddrTypeToReusedAddrCount::default(),
|
||||
AddrTypeToExposedAddrCount::default(),
|
||||
AddrTypeToExposedAddrCount::default(),
|
||||
AddrTypeToExposedAddrSupply::default(),
|
||||
AddrTypeToExposedSupply::default(),
|
||||
)
|
||||
};
|
||||
debug!("addr_counts recovered");
|
||||
@@ -489,7 +488,7 @@ pub(crate) fn process_blocks(
|
||||
&mut active_reused_addr_counts,
|
||||
&mut exposed_addr_counts,
|
||||
&mut total_exposed_addr_counts,
|
||||
&mut exposed_addr_supply,
|
||||
&mut exposed_supply,
|
||||
);
|
||||
|
||||
// Process sent inputs (addresses sending funds)
|
||||
@@ -507,7 +506,7 @@ pub(crate) fn process_blocks(
|
||||
&mut active_reused_addr_counts,
|
||||
&mut exposed_addr_counts,
|
||||
&mut total_exposed_addr_counts,
|
||||
&mut exposed_addr_supply,
|
||||
&mut exposed_supply,
|
||||
&received_addrs,
|
||||
height_to_price_vec,
|
||||
height_to_timestamp_vec,
|
||||
@@ -539,8 +538,8 @@ pub(crate) fn process_blocks(
|
||||
total_reused_addr_counts.values().copied(),
|
||||
);
|
||||
let activity_totals = activity_counts.totals();
|
||||
let active_addr_count = activity_totals.sending + activity_totals.receiving
|
||||
- activity_totals.bidirectional;
|
||||
let active_addr_count =
|
||||
activity_totals.sending + activity_totals.receiving - activity_totals.bidirectional;
|
||||
let active_reused = u32::try_from(active_reused_addr_counts.sum()).unwrap();
|
||||
vecs.addrs.reused.events.push_height(
|
||||
&output_to_reused_addr_counts,
|
||||
@@ -556,10 +555,10 @@ pub(crate) fn process_blocks(
|
||||
total_exposed_addr_counts.sum(),
|
||||
total_exposed_addr_counts.values().copied(),
|
||||
);
|
||||
vecs.addrs.exposed.supply.push_height(
|
||||
exposed_addr_supply.sum(),
|
||||
exposed_addr_supply.values().copied(),
|
||||
);
|
||||
vecs.addrs
|
||||
.exposed
|
||||
.supply
|
||||
.push_height(exposed_supply.sum(), exposed_supply.values().copied());
|
||||
|
||||
let is_last_of_day = is_last_of_day[offset];
|
||||
let date_opt = is_last_of_day.then(|| Date::from(timestamp));
|
||||
|
||||
@@ -144,8 +144,8 @@ impl AllCohortMetrics {
|
||||
&self.unrealized.invested_capital.in_loss.cents.height,
|
||||
&self.supply.in_profit.sats.height,
|
||||
&self.supply.in_loss.sats.height,
|
||||
&self.unrealized.investor_cap_in_profit_raw,
|
||||
&self.unrealized.investor_cap_in_loss_raw,
|
||||
&self.unrealized.capitalized_cap_in_profit_raw,
|
||||
&self.unrealized.capitalized_cap_in_loss_raw,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
|
||||
@@ -120,8 +120,8 @@ impl ExtendedCohortMetrics {
|
||||
&self.unrealized.invested_capital.in_loss.cents.height,
|
||||
&self.supply.in_profit.sats.height,
|
||||
&self.supply.in_loss.sats.height,
|
||||
&self.unrealized.investor_cap_in_profit_raw,
|
||||
&self.unrealized.investor_cap_in_loss_raw,
|
||||
&self.unrealized.capitalized_cap_in_profit_raw,
|
||||
&self.unrealized.capitalized_cap_in_loss_raw,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
|
||||
@@ -153,8 +153,8 @@ impl CostBasis {
|
||||
invested_cap_in_loss: &impl ReadableVec<Height, Cents>,
|
||||
supply_in_profit_sats: &impl ReadableVec<Height, Sats>,
|
||||
supply_in_loss_sats: &impl ReadableVec<Height, Sats>,
|
||||
investor_cap_in_profit_raw: &impl ReadableVec<Height, CentsSquaredSats>,
|
||||
investor_cap_in_loss_raw: &impl ReadableVec<Height, CentsSquaredSats>,
|
||||
capitalized_cap_in_profit_raw: &impl ReadableVec<Height, CentsSquaredSats>,
|
||||
capitalized_cap_in_loss_raw: &impl ReadableVec<Height, CentsSquaredSats>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.in_profit.per_coin.cents.height.compute_transform3(
|
||||
@@ -193,29 +193,29 @@ impl CostBasis {
|
||||
)?;
|
||||
self.in_profit.per_dollar.cents.height.compute_transform3(
|
||||
starting_indexes.height,
|
||||
investor_cap_in_profit_raw,
|
||||
capitalized_cap_in_profit_raw,
|
||||
invested_cap_in_profit,
|
||||
spot,
|
||||
|(h, investor_cap, invested_cents, spot, ..)| {
|
||||
|(h, capitalized_cap, invested_cents, spot, ..)| {
|
||||
let invested_raw = invested_cents.as_u128() * Sats::ONE_BTC_U128;
|
||||
if invested_raw == 0 {
|
||||
return (h, spot);
|
||||
}
|
||||
(h, Cents::new((investor_cap.inner() / invested_raw) as u64))
|
||||
(h, Cents::new((capitalized_cap.inner() / invested_raw) as u64))
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
self.in_loss.per_dollar.cents.height.compute_transform3(
|
||||
starting_indexes.height,
|
||||
investor_cap_in_loss_raw,
|
||||
capitalized_cap_in_loss_raw,
|
||||
invested_cap_in_loss,
|
||||
spot,
|
||||
|(h, investor_cap, invested_cents, spot, ..)| {
|
||||
|(h, capitalized_cap, invested_cents, spot, ..)| {
|
||||
let invested_raw = invested_cents.as_u128() * Sats::ONE_BTC_U128;
|
||||
if invested_raw == 0 {
|
||||
return (h, spot);
|
||||
}
|
||||
(h, Cents::new((investor_cap.inner() / invested_raw) as u64))
|
||||
(h, Cents::new((capitalized_cap.inner() / invested_raw) as u64))
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
|
||||
@@ -45,7 +45,7 @@ pub struct RealizedPeakRegret<M: StorageMode = Rw> {
|
||||
}
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct RealizedInvestor<M: StorageMode = Rw> {
|
||||
pub struct RealizedCapitalized<M: StorageMode = Rw> {
|
||||
pub price: PriceWithRatioExtendedPerBlock<M>,
|
||||
#[traversable(hidden)]
|
||||
pub cap_raw: M::Stored<BytesVec<Height, CentsSquaredSats>>,
|
||||
@@ -63,7 +63,7 @@ pub struct RealizedFull<M: StorageMode = Rw> {
|
||||
pub net_pnl: RealizedNetPnl<M>,
|
||||
pub sopr: RealizedSopr<M>,
|
||||
pub peak_regret: RealizedPeakRegret<M>,
|
||||
pub investor: RealizedInvestor<M>,
|
||||
pub capitalized: RealizedCapitalized<M>,
|
||||
|
||||
pub profit_to_loss_ratio: RollingWindows<StoredF64, M>,
|
||||
|
||||
@@ -108,10 +108,10 @@ impl RealizedFull {
|
||||
value: cfg.import("realized_peak_regret", Version::new(3))?,
|
||||
};
|
||||
|
||||
// Investor
|
||||
let investor = RealizedInvestor {
|
||||
price: cfg.import("investor_price", v0)?,
|
||||
cap_raw: cfg.import("investor_cap_raw", v0)?,
|
||||
// Capitalized
|
||||
let capitalized = RealizedCapitalized {
|
||||
price: cfg.import("capitalized_price", v0)?,
|
||||
cap_raw: cfg.import("capitalized_cap_raw", v0)?,
|
||||
};
|
||||
|
||||
// Price ratio stats
|
||||
@@ -125,7 +125,7 @@ impl RealizedFull {
|
||||
net_pnl,
|
||||
sopr,
|
||||
peak_regret,
|
||||
investor,
|
||||
capitalized,
|
||||
profit_to_loss_ratio: cfg.import("realized_profit_to_loss_ratio", v1)?,
|
||||
cap_raw: cfg.import("cap_raw", v0)?,
|
||||
cap_to_own_mcap: cfg.import("realized_cap_to_own_mcap", v1)?,
|
||||
@@ -151,13 +151,13 @@ impl RealizedFull {
|
||||
}
|
||||
|
||||
pub(crate) fn min_stateful_len(&self) -> usize {
|
||||
self.investor
|
||||
self.capitalized
|
||||
.price
|
||||
.cents
|
||||
.height
|
||||
.len()
|
||||
.min(self.cap_raw.len())
|
||||
.min(self.investor.cap_raw.len())
|
||||
.min(self.capitalized.cap_raw.len())
|
||||
.min(self.peak_regret.value.block.cents.len())
|
||||
}
|
||||
|
||||
@@ -167,15 +167,15 @@ impl RealizedFull {
|
||||
state: &CohortState<RealizedState, CostBasisData<WithCapital>>,
|
||||
) {
|
||||
self.core.push_state(state);
|
||||
self.investor
|
||||
self.capitalized
|
||||
.price
|
||||
.cents
|
||||
.height
|
||||
.push(state.realized.investor_price());
|
||||
.push(state.realized.capitalized_price());
|
||||
self.cap_raw.push(state.realized.cap_raw());
|
||||
self.investor
|
||||
self.capitalized
|
||||
.cap_raw
|
||||
.push(state.realized.investor_cap_raw());
|
||||
.push(state.realized.capitalized_cap_raw());
|
||||
self.peak_regret
|
||||
.value
|
||||
.block
|
||||
@@ -185,9 +185,9 @@ impl RealizedFull {
|
||||
|
||||
pub(crate) fn collect_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
|
||||
let mut vecs = self.core.collect_vecs_mut();
|
||||
vecs.push(&mut self.investor.price.cents.height);
|
||||
vecs.push(&mut self.capitalized.price.cents.height);
|
||||
vecs.push(&mut self.cap_raw as &mut dyn AnyStoredVec);
|
||||
vecs.push(&mut self.investor.cap_raw as &mut dyn AnyStoredVec);
|
||||
vecs.push(&mut self.capitalized.cap_raw as &mut dyn AnyStoredVec);
|
||||
vecs.push(&mut self.peak_regret.value.block.cents);
|
||||
vecs
|
||||
}
|
||||
@@ -207,17 +207,17 @@ impl RealizedFull {
|
||||
#[inline(always)]
|
||||
pub(crate) fn push_accum(&mut self, accum: &RealizedFullAccum) {
|
||||
self.cap_raw.push(accum.cap_raw);
|
||||
self.investor.cap_raw.push(accum.investor_cap_raw);
|
||||
self.capitalized.cap_raw.push(accum.capitalized_cap_raw);
|
||||
|
||||
let investor_price = {
|
||||
let capitalized_price = {
|
||||
let cap = accum.cap_raw.as_u128();
|
||||
if cap == 0 {
|
||||
Cents::ZERO
|
||||
} else {
|
||||
Cents::new((accum.investor_cap_raw / cap) as u64)
|
||||
Cents::new((accum.capitalized_cap_raw / cap) as u64)
|
||||
}
|
||||
};
|
||||
self.investor.price.cents.height.push(investor_price);
|
||||
self.capitalized.price.cents.height.push(capitalized_price);
|
||||
|
||||
self.peak_regret.value.block.cents.push(accum.peak_regret());
|
||||
}
|
||||
@@ -298,8 +298,8 @@ impl RealizedFull {
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// Investor price ratio, percentiles and bands
|
||||
self.investor
|
||||
// Capitalized price ratio, percentiles and bands
|
||||
self.capitalized
|
||||
.price
|
||||
.compute_rest(prices, starting_indexes, exit)?;
|
||||
|
||||
@@ -374,14 +374,14 @@ impl RealizedFull {
|
||||
#[derive(Default)]
|
||||
pub struct RealizedFullAccum {
|
||||
pub(crate) cap_raw: CentsSats,
|
||||
pub(crate) investor_cap_raw: CentsSquaredSats,
|
||||
pub(crate) capitalized_cap_raw: CentsSquaredSats,
|
||||
peak_regret: CentsSats,
|
||||
}
|
||||
|
||||
impl RealizedFullAccum {
|
||||
pub(crate) fn add(&mut self, state: &RealizedState) {
|
||||
self.cap_raw += state.cap_raw();
|
||||
self.investor_cap_raw += state.investor_cap_raw();
|
||||
self.capitalized_cap_raw += state.capitalized_cap_raw();
|
||||
self.peak_regret += CentsSats::new(state.peak_regret_raw());
|
||||
}
|
||||
|
||||
|
||||
@@ -33,8 +33,8 @@ pub struct UnrealizedFull<M: StorageMode = Rw> {
|
||||
pub gross_pnl: FiatPerBlock<Cents, M>,
|
||||
pub invested_capital: UnrealizedInvestedCapital<M>,
|
||||
|
||||
pub investor_cap_in_profit_raw: M::Stored<BytesVec<Height, CentsSquaredSats>>,
|
||||
pub investor_cap_in_loss_raw: M::Stored<BytesVec<Height, CentsSquaredSats>>,
|
||||
pub capitalized_cap_in_profit_raw: M::Stored<BytesVec<Height, CentsSquaredSats>>,
|
||||
pub capitalized_cap_in_loss_raw: M::Stored<BytesVec<Height, CentsSquaredSats>>,
|
||||
|
||||
pub sentiment: UnrealizedSentiment<M>,
|
||||
}
|
||||
@@ -53,8 +53,8 @@ impl UnrealizedFull {
|
||||
in_loss: cfg.import("invested_capital_in_loss", v1)?,
|
||||
};
|
||||
|
||||
let investor_cap_in_profit_raw = cfg.import("investor_cap_in_profit_raw", v0)?;
|
||||
let investor_cap_in_loss_raw = cfg.import("investor_cap_in_loss_raw", v0)?;
|
||||
let capitalized_cap_in_profit_raw = cfg.import("capitalized_cap_in_profit_raw", v0)?;
|
||||
let capitalized_cap_in_loss_raw = cfg.import("capitalized_cap_in_loss_raw", v0)?;
|
||||
|
||||
let sentiment = UnrealizedSentiment {
|
||||
pain_index: cfg.import("pain_index", v1)?,
|
||||
@@ -66,33 +66,33 @@ impl UnrealizedFull {
|
||||
inner,
|
||||
gross_pnl,
|
||||
invested_capital,
|
||||
investor_cap_in_profit_raw,
|
||||
investor_cap_in_loss_raw,
|
||||
capitalized_cap_in_profit_raw,
|
||||
capitalized_cap_in_loss_raw,
|
||||
sentiment,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn min_stateful_len(&self) -> usize {
|
||||
// Only check per-block pushed vecs (investor_cap_raw).
|
||||
// Only check per-block pushed vecs (capitalized_cap_raw).
|
||||
// Core-level vecs (profit/loss) are aggregated from age_range, not stateful.
|
||||
self.investor_cap_in_profit_raw
|
||||
self.capitalized_cap_in_profit_raw
|
||||
.len()
|
||||
.min(self.investor_cap_in_loss_raw.len())
|
||||
.min(self.capitalized_cap_in_loss_raw.len())
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn push_state_all(&mut self, state: &UnrealizedState) {
|
||||
self.inner.push_state(state);
|
||||
self.investor_cap_in_profit_raw
|
||||
.push(CentsSquaredSats::new(state.investor_cap_in_profit_raw));
|
||||
self.investor_cap_in_loss_raw
|
||||
.push(CentsSquaredSats::new(state.investor_cap_in_loss_raw));
|
||||
self.capitalized_cap_in_profit_raw
|
||||
.push(CentsSquaredSats::new(state.capitalized_cap_in_profit_raw));
|
||||
self.capitalized_cap_in_loss_raw
|
||||
.push(CentsSquaredSats::new(state.capitalized_cap_in_loss_raw));
|
||||
}
|
||||
|
||||
pub(crate) fn collect_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
|
||||
let mut vecs = self.inner.collect_vecs_mut();
|
||||
vecs.push(&mut self.investor_cap_in_profit_raw as &mut dyn AnyStoredVec);
|
||||
vecs.push(&mut self.investor_cap_in_loss_raw as &mut dyn AnyStoredVec);
|
||||
vecs.push(&mut self.capitalized_cap_in_profit_raw as &mut dyn AnyStoredVec);
|
||||
vecs.push(&mut self.capitalized_cap_in_loss_raw as &mut dyn AnyStoredVec);
|
||||
vecs
|
||||
}
|
||||
|
||||
@@ -154,7 +154,7 @@ impl UnrealizedFull {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Compute sentiment using investor_price (original formula).
|
||||
/// Compute sentiment using capitalized_price (original formula).
|
||||
/// Called after cost_basis.in_profit/loss are computed at the cohort level.
|
||||
pub(crate) fn compute_sentiment(
|
||||
&mut self,
|
||||
@@ -162,45 +162,45 @@ impl UnrealizedFull {
|
||||
spot: &impl ReadableVec<Height, Cents>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
// greed = spot - investor_price_winners
|
||||
// investor_price = investor_cap / invested_cap
|
||||
// greed = spot - capitalized_price_winners
|
||||
// capitalized_price = capitalized_cap / invested_cap
|
||||
// invested_cap is in Cents (already / ONE_BTC), multiply back for CentsSats scale
|
||||
self.sentiment.greed_index.cents.height.compute_transform3(
|
||||
starting_indexes.height,
|
||||
&self.investor_cap_in_profit_raw,
|
||||
&self.capitalized_cap_in_profit_raw,
|
||||
&self.invested_capital.in_profit.cents.height,
|
||||
spot,
|
||||
|(h, investor_cap, invested_cap_cents, spot, ..)| {
|
||||
|(h, capitalized_cap, invested_cap_cents, spot, ..)| {
|
||||
let invested_cap_raw = invested_cap_cents.as_u128() * Sats::ONE_BTC_U128;
|
||||
if invested_cap_raw == 0 {
|
||||
return (h, Cents::ZERO);
|
||||
}
|
||||
let investor_price = investor_cap.inner() / invested_cap_raw;
|
||||
let capitalized_price = capitalized_cap.inner() / invested_cap_raw;
|
||||
let spot_u128 = spot.as_u128();
|
||||
(
|
||||
h,
|
||||
Cents::new(spot_u128.saturating_sub(investor_price) as u64),
|
||||
Cents::new(spot_u128.saturating_sub(capitalized_price) as u64),
|
||||
)
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// pain = investor_price_losers - spot
|
||||
// pain = capitalized_price_losers - spot
|
||||
self.sentiment.pain_index.cents.height.compute_transform3(
|
||||
starting_indexes.height,
|
||||
&self.investor_cap_in_loss_raw,
|
||||
&self.capitalized_cap_in_loss_raw,
|
||||
&self.invested_capital.in_loss.cents.height,
|
||||
spot,
|
||||
|(h, investor_cap, invested_cap_cents, spot, ..)| {
|
||||
|(h, capitalized_cap, invested_cap_cents, spot, ..)| {
|
||||
let invested_cap_raw = invested_cap_cents.as_u128() * Sats::ONE_BTC_U128;
|
||||
if invested_cap_raw == 0 {
|
||||
return (h, Cents::ZERO);
|
||||
}
|
||||
let investor_price = investor_cap.inner() / invested_cap_raw;
|
||||
let capitalized_price = capitalized_cap.inner() / invested_cap_raw;
|
||||
let spot_u128 = spot.as_u128();
|
||||
(
|
||||
h,
|
||||
Cents::new(investor_price.saturating_sub(spot_u128) as u64),
|
||||
Cents::new(capitalized_price.saturating_sub(spot_u128) as u64),
|
||||
)
|
||||
},
|
||||
exit,
|
||||
|
||||
@@ -18,7 +18,7 @@ pub struct SendPrecomputed {
|
||||
pub current_ps: CentsSats,
|
||||
pub prev_ps: CentsSats,
|
||||
pub ath_ps: CentsSats,
|
||||
pub prev_investor_cap: CentsSquaredSats,
|
||||
pub prev_capitalized_cap: CentsSquaredSats,
|
||||
}
|
||||
|
||||
impl SendPrecomputed {
|
||||
@@ -42,7 +42,7 @@ impl SendPrecomputed {
|
||||
} else {
|
||||
CentsSats::from_price_sats(ath, sats)
|
||||
};
|
||||
let prev_investor_cap = prev_ps.to_investor_cap(prev_price);
|
||||
let prev_capitalized_cap = prev_ps.to_capitalized_cap(prev_price);
|
||||
Some(Self {
|
||||
sats,
|
||||
prev_price,
|
||||
@@ -50,7 +50,7 @@ impl SendPrecomputed {
|
||||
current_ps,
|
||||
prev_ps,
|
||||
ath_ps,
|
||||
prev_investor_cap,
|
||||
prev_capitalized_cap,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -90,7 +90,7 @@ impl<R: RealizedOps, C: CostBasisOps> CohortState<R, C> {
|
||||
pub(crate) fn restore_realized_cap(&mut self) {
|
||||
self.realized.set_cap_raw(self.cost_basis.cap_raw());
|
||||
self.realized
|
||||
.set_investor_cap_raw(self.cost_basis.investor_cap_raw());
|
||||
.set_capitalized_cap_raw(self.cost_basis.capitalized_cap_raw());
|
||||
}
|
||||
|
||||
pub(crate) fn reset_cost_basis_data_if_needed(&mut self) -> Result<()> {
|
||||
@@ -117,12 +117,12 @@ impl<R: RealizedOps, C: CostBasisOps> CohortState<R, C> {
|
||||
|
||||
if s.supply_state.value > Sats::ZERO {
|
||||
self.realized
|
||||
.increment_snapshot(s.price_sats, s.investor_cap);
|
||||
.increment_snapshot(s.price_sats, s.capitalized_cap_raw);
|
||||
self.cost_basis.increment(
|
||||
s.realized_price,
|
||||
s.supply_state.value,
|
||||
s.price_sats,
|
||||
s.investor_cap,
|
||||
s.capitalized_cap_raw,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -132,12 +132,12 @@ impl<R: RealizedOps, C: CostBasisOps> CohortState<R, C> {
|
||||
|
||||
if s.supply_state.value > Sats::ZERO {
|
||||
self.realized
|
||||
.decrement_snapshot(s.price_sats, s.investor_cap);
|
||||
.decrement_snapshot(s.price_sats, s.capitalized_cap_raw);
|
||||
self.cost_basis.decrement(
|
||||
s.realized_price,
|
||||
s.supply_state.value,
|
||||
s.price_sats,
|
||||
s.investor_cap,
|
||||
s.capitalized_cap_raw,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -162,7 +162,7 @@ impl<R: RealizedOps, C: CostBasisOps> CohortState<R, C> {
|
||||
snapshot.realized_price,
|
||||
supply.value,
|
||||
snapshot.price_sats,
|
||||
snapshot.investor_cap,
|
||||
snapshot.capitalized_cap_raw,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -184,7 +184,7 @@ impl<R: RealizedOps, C: CostBasisOps> CohortState<R, C> {
|
||||
current.realized_price,
|
||||
current.supply_state.value,
|
||||
current.price_sats,
|
||||
current.investor_cap,
|
||||
current.capitalized_cap_raw,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -193,7 +193,7 @@ impl<R: RealizedOps, C: CostBasisOps> CohortState<R, C> {
|
||||
prev.realized_price,
|
||||
prev.supply_state.value,
|
||||
prev.price_sats,
|
||||
prev.investor_cap,
|
||||
prev.capitalized_cap_raw,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -212,11 +212,11 @@ impl<R: RealizedOps, C: CostBasisOps> CohortState<R, C> {
|
||||
pre.current_ps,
|
||||
pre.prev_ps,
|
||||
pre.ath_ps,
|
||||
pre.prev_investor_cap,
|
||||
pre.prev_capitalized_cap,
|
||||
);
|
||||
|
||||
self.cost_basis
|
||||
.decrement(pre.prev_price, pre.sats, pre.prev_ps, pre.prev_investor_cap);
|
||||
.decrement(pre.prev_price, pre.sats, pre.prev_ps, pre.prev_capitalized_cap);
|
||||
}
|
||||
|
||||
pub(crate) fn send_utxo(
|
||||
@@ -265,17 +265,17 @@ impl<R: RealizedOps, C: CostBasisOps> CohortState<R, C> {
|
||||
let current_ps = CentsSats::from_price_sats(current_price, sats);
|
||||
let prev_ps = CentsSats::from_price_sats(prev_price, sats);
|
||||
let ath_ps = CentsSats::from_price_sats(ath, sats);
|
||||
let prev_investor_cap = prev_ps.to_investor_cap(prev_price);
|
||||
let prev_capitalized_cap = prev_ps.to_capitalized_cap(prev_price);
|
||||
|
||||
self.realized
|
||||
.send(sats, current_ps, prev_ps, ath_ps, prev_investor_cap);
|
||||
.send(sats, current_ps, prev_ps, ath_ps, prev_capitalized_cap);
|
||||
|
||||
if current.supply_state.value.is_not_zero() {
|
||||
self.cost_basis.increment(
|
||||
current.realized_price,
|
||||
current.supply_state.value,
|
||||
current.price_sats,
|
||||
current.investor_cap,
|
||||
current.capitalized_cap_raw,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -284,7 +284,7 @@ impl<R: RealizedOps, C: CostBasisOps> CohortState<R, C> {
|
||||
prev.realized_price,
|
||||
prev.supply_state.value,
|
||||
prev.price_sats,
|
||||
prev.investor_cap,
|
||||
prev.capitalized_cap_raw,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ use rustc_hash::FxHashMap;
|
||||
use vecdb::{Bytes, unlikely};
|
||||
|
||||
use super::{Accumulate, CachedUnrealizedState, UnrealizedState};
|
||||
use crate::distribution::state::pending::{PendingCapDelta, PendingDelta, PendingInvestorCapDelta};
|
||||
use crate::distribution::state::pending::{PendingCapDelta, PendingDelta, PendingCapitalizedCapRawDelta};
|
||||
|
||||
/// Type alias for the price-to-sats map used in cost basis data.
|
||||
pub(super) type CostBasisMap = BTreeMap<CentsCompact, Sats>;
|
||||
@@ -27,20 +27,20 @@ pub trait CostBasisOps: Send + Sync + 'static {
|
||||
fn with_price_rounding(self, digits: i32) -> Self;
|
||||
fn import_at_or_before(&mut self, height: Height) -> Result<Height>;
|
||||
fn cap_raw(&self) -> CentsSats;
|
||||
fn investor_cap_raw(&self) -> CentsSquaredSats;
|
||||
fn capitalized_cap_raw(&self) -> CentsSquaredSats;
|
||||
fn increment(
|
||||
&mut self,
|
||||
price: Cents,
|
||||
sats: Sats,
|
||||
price_sats: CentsSats,
|
||||
investor_cap: CentsSquaredSats,
|
||||
capitalized_cap: CentsSquaredSats,
|
||||
);
|
||||
fn decrement(
|
||||
&mut self,
|
||||
price: Cents,
|
||||
sats: Sats,
|
||||
price_sats: CentsSats,
|
||||
investor_cap: CentsSquaredSats,
|
||||
capitalized_cap: CentsSquaredSats,
|
||||
);
|
||||
fn apply_pending(&mut self);
|
||||
fn init(&mut self);
|
||||
@@ -182,7 +182,7 @@ impl CostBasisOps for CostBasisRaw {
|
||||
self.state.as_ref().unwrap().cap_raw
|
||||
}
|
||||
|
||||
fn investor_cap_raw(&self) -> CentsSquaredSats {
|
||||
fn capitalized_cap_raw(&self) -> CentsSquaredSats {
|
||||
CentsSquaredSats::ZERO
|
||||
}
|
||||
|
||||
@@ -192,7 +192,7 @@ impl CostBasisOps for CostBasisRaw {
|
||||
_price: Cents,
|
||||
_sats: Sats,
|
||||
price_sats: CentsSats,
|
||||
_investor_cap: CentsSquaredSats,
|
||||
_capitalized_cap: CentsSquaredSats,
|
||||
) {
|
||||
self.pending_cap.inc += price_sats;
|
||||
}
|
||||
@@ -203,7 +203,7 @@ impl CostBasisOps for CostBasisRaw {
|
||||
_price: Cents,
|
||||
_sats: Sats,
|
||||
price_sats: CentsSats,
|
||||
_investor_cap: CentsSquaredSats,
|
||||
_capitalized_cap: CentsSquaredSats,
|
||||
) {
|
||||
self.pending_cap.dec += price_sats;
|
||||
}
|
||||
@@ -240,7 +240,7 @@ impl CostBasisOps for CostBasisRaw {
|
||||
/// Composes `CostBasisRaw` for scalar tracking, adds map, pending, and cache.
|
||||
///
|
||||
/// Generic over the accumulator `S`:
|
||||
/// - `WithCapital`: tracks all fields including invested capital + investor cap (128 bytes)
|
||||
/// - `WithCapital`: tracks all fields including invested capital + capitalized cap (128 bytes)
|
||||
/// - `WithoutCapital`: tracks only supply + unrealized profit/loss (64 bytes, 1 cache line)
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CostBasisData<S: Accumulate> {
|
||||
@@ -249,8 +249,8 @@ pub struct CostBasisData<S: Accumulate> {
|
||||
pending: FxHashMap<CentsCompact, PendingDelta>,
|
||||
cache: Option<CachedUnrealizedState<S>>,
|
||||
rounding_digits: Option<i32>,
|
||||
investor_cap_raw: CentsSquaredSats,
|
||||
pending_investor_cap: PendingInvestorCapDelta,
|
||||
capitalized_cap_raw: CentsSquaredSats,
|
||||
pending_capitalized_cap: PendingCapitalizedCapRawDelta,
|
||||
}
|
||||
|
||||
impl<S: Accumulate> CostBasisData<S> {
|
||||
@@ -351,8 +351,8 @@ impl<S: Accumulate> CostBasisOps for CostBasisData<S> {
|
||||
pending: FxHashMap::default(),
|
||||
cache: None,
|
||||
rounding_digits: None,
|
||||
investor_cap_raw: CentsSquaredSats::ZERO,
|
||||
pending_investor_cap: PendingInvestorCapDelta::default(),
|
||||
capitalized_cap_raw: CentsSquaredSats::ZERO,
|
||||
pending_capitalized_cap: PendingCapitalizedCapRawDelta::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -375,10 +375,10 @@ impl<S: Accumulate> CostBasisOps for CostBasisData<S> {
|
||||
"CostBasisData state too short: {} bytes",
|
||||
rest.len()
|
||||
);
|
||||
self.investor_cap_raw = CentsSquaredSats::from_bytes(&rest[16..32])?;
|
||||
self.capitalized_cap_raw = CentsSquaredSats::from_bytes(&rest[16..32])?;
|
||||
self.pending.clear();
|
||||
self.raw.pending_cap = PendingCapDelta::default();
|
||||
self.pending_investor_cap = PendingInvestorCapDelta::default();
|
||||
self.pending_capitalized_cap = PendingCapitalizedCapRawDelta::default();
|
||||
self.cache = None;
|
||||
Ok(height)
|
||||
}
|
||||
@@ -387,8 +387,8 @@ impl<S: Accumulate> CostBasisOps for CostBasisData<S> {
|
||||
self.raw.cap_raw()
|
||||
}
|
||||
|
||||
fn investor_cap_raw(&self) -> CentsSquaredSats {
|
||||
self.investor_cap_raw
|
||||
fn capitalized_cap_raw(&self) -> CentsSquaredSats {
|
||||
self.capitalized_cap_raw
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -397,13 +397,13 @@ impl<S: Accumulate> CostBasisOps for CostBasisData<S> {
|
||||
price: Cents,
|
||||
sats: Sats,
|
||||
price_sats: CentsSats,
|
||||
investor_cap: CentsSquaredSats,
|
||||
capitalized_cap: CentsSquaredSats,
|
||||
) {
|
||||
let price = self.round_price(price);
|
||||
self.pending.entry(price.into()).or_default().inc += sats;
|
||||
self.raw.pending_cap.inc += price_sats;
|
||||
if investor_cap != CentsSquaredSats::ZERO {
|
||||
self.pending_investor_cap.inc += investor_cap;
|
||||
if capitalized_cap != CentsSquaredSats::ZERO {
|
||||
self.pending_capitalized_cap.inc += capitalized_cap;
|
||||
}
|
||||
if let Some(cache) = self.cache.as_mut() {
|
||||
cache.on_receive(price, sats);
|
||||
@@ -416,13 +416,13 @@ impl<S: Accumulate> CostBasisOps for CostBasisData<S> {
|
||||
price: Cents,
|
||||
sats: Sats,
|
||||
price_sats: CentsSats,
|
||||
investor_cap: CentsSquaredSats,
|
||||
capitalized_cap: CentsSquaredSats,
|
||||
) {
|
||||
let price = self.round_price(price);
|
||||
self.pending.entry(price.into()).or_default().dec += sats;
|
||||
self.raw.pending_cap.dec += price_sats;
|
||||
if investor_cap != CentsSquaredSats::ZERO {
|
||||
self.pending_investor_cap.dec += investor_cap;
|
||||
if capitalized_cap != CentsSquaredSats::ZERO {
|
||||
self.pending_capitalized_cap.dec += capitalized_cap;
|
||||
}
|
||||
if let Some(cache) = self.cache.as_mut() {
|
||||
cache.on_send(price, sats);
|
||||
@@ -432,19 +432,19 @@ impl<S: Accumulate> CostBasisOps for CostBasisData<S> {
|
||||
fn apply_pending(&mut self) {
|
||||
self.apply_map_pending();
|
||||
self.raw.apply_pending_cap();
|
||||
self.investor_cap_raw += self.pending_investor_cap.inc;
|
||||
self.capitalized_cap_raw += self.pending_capitalized_cap.inc;
|
||||
debug_assert!(
|
||||
self.investor_cap_raw >= self.pending_investor_cap.dec,
|
||||
"CostBasis investor_cap_raw underflow!\n\
|
||||
self.capitalized_cap_raw >= self.pending_capitalized_cap.dec,
|
||||
"CostBasis capitalized_cap_raw underflow!\n\
|
||||
Path: {:?}\n\
|
||||
Current (after increments): {:?}\n\
|
||||
Trying to decrement by: {:?}",
|
||||
self.raw.pathbuf,
|
||||
self.investor_cap_raw,
|
||||
self.pending_investor_cap.dec
|
||||
self.capitalized_cap_raw,
|
||||
self.pending_capitalized_cap.dec
|
||||
);
|
||||
self.investor_cap_raw -= self.pending_investor_cap.dec;
|
||||
self.pending_investor_cap = PendingInvestorCapDelta::default();
|
||||
self.capitalized_cap_raw -= self.pending_capitalized_cap.dec;
|
||||
self.pending_capitalized_cap = PendingCapitalizedCapRawDelta::default();
|
||||
}
|
||||
|
||||
fn init(&mut self) {
|
||||
@@ -452,8 +452,8 @@ impl<S: Accumulate> CostBasisOps for CostBasisData<S> {
|
||||
self.map.replace(CostBasisDistribution::default());
|
||||
self.pending.clear();
|
||||
self.cache = None;
|
||||
self.investor_cap_raw = CentsSquaredSats::ZERO;
|
||||
self.pending_investor_cap = PendingInvestorCapDelta::default();
|
||||
self.capitalized_cap_raw = CentsSquaredSats::ZERO;
|
||||
self.pending_capitalized_cap = PendingCapitalizedCapRawDelta::default();
|
||||
}
|
||||
|
||||
fn clean(&mut self) -> Result<()> {
|
||||
@@ -469,7 +469,7 @@ impl<S: Accumulate> CostBasisOps for CostBasisData<S> {
|
||||
let raw_state = self.raw.state.as_ref().unwrap();
|
||||
let mut buffer = self.map.as_ref().unwrap().serialize()?;
|
||||
buffer.extend(raw_state.cap_raw.to_bytes());
|
||||
buffer.extend(self.investor_cap_raw.to_bytes());
|
||||
buffer.extend(self.capitalized_cap_raw.to_bytes());
|
||||
fs::write(self.raw.path_state(height), buffer)?;
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -18,11 +18,11 @@ pub trait RealizedOps: Default + Clone + Send + Sync + 'static {
|
||||
Sats::ZERO
|
||||
}
|
||||
fn set_cap_raw(&mut self, cap_raw: CentsSats);
|
||||
fn set_investor_cap_raw(&mut self, investor_cap_raw: CentsSquaredSats);
|
||||
fn set_capitalized_cap_raw(&mut self, capitalized_cap_raw: CentsSquaredSats);
|
||||
fn reset_single_iteration_values(&mut self);
|
||||
fn increment(&mut self, price: Cents, sats: Sats);
|
||||
fn increment_snapshot(&mut self, price_sats: CentsSats, investor_cap: CentsSquaredSats);
|
||||
fn decrement_snapshot(&mut self, price_sats: CentsSats, investor_cap: CentsSquaredSats);
|
||||
fn increment_snapshot(&mut self, price_sats: CentsSats, capitalized_cap: CentsSquaredSats);
|
||||
fn decrement_snapshot(&mut self, price_sats: CentsSats, capitalized_cap: CentsSquaredSats);
|
||||
fn receive(&mut self, price: Cents, sats: Sats) {
|
||||
self.increment(price, sats);
|
||||
}
|
||||
@@ -32,7 +32,7 @@ pub trait RealizedOps: Default + Clone + Send + Sync + 'static {
|
||||
current_ps: CentsSats,
|
||||
prev_ps: CentsSats,
|
||||
ath_ps: CentsSats,
|
||||
prev_investor_cap: CentsSquaredSats,
|
||||
prev_capitalized_cap: CentsSquaredSats,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ impl RealizedOps for MinimalRealizedState {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_investor_cap_raw(&mut self, _investor_cap_raw: CentsSquaredSats) {}
|
||||
fn set_capitalized_cap_raw(&mut self, _capitalized_cap_raw: CentsSquaredSats) {}
|
||||
|
||||
#[inline]
|
||||
fn reset_single_iteration_values(&mut self) {
|
||||
@@ -104,12 +104,12 @@ impl RealizedOps for MinimalRealizedState {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn increment_snapshot(&mut self, price_sats: CentsSats, _investor_cap: CentsSquaredSats) {
|
||||
fn increment_snapshot(&mut self, price_sats: CentsSats, _capitalized_cap: CentsSquaredSats) {
|
||||
self.cap_raw += price_sats.as_u128();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn decrement_snapshot(&mut self, price_sats: CentsSats, _investor_cap: CentsSquaredSats) {
|
||||
fn decrement_snapshot(&mut self, price_sats: CentsSats, _capitalized_cap: CentsSquaredSats) {
|
||||
self.cap_raw -= price_sats.as_u128();
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ impl RealizedOps for MinimalRealizedState {
|
||||
current_ps: CentsSats,
|
||||
prev_ps: CentsSats,
|
||||
_ath_ps: CentsSats,
|
||||
_prev_investor_cap: CentsSquaredSats,
|
||||
_prev_capitalized_cap: CentsSquaredSats,
|
||||
) {
|
||||
match current_ps.cmp(&prev_ps) {
|
||||
Ordering::Greater => {
|
||||
@@ -184,7 +184,7 @@ impl RealizedOps for CoreRealizedState {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_investor_cap_raw(&mut self, _investor_cap_raw: CentsSquaredSats) {}
|
||||
fn set_capitalized_cap_raw(&mut self, _capitalized_cap_raw: CentsSquaredSats) {}
|
||||
|
||||
#[inline]
|
||||
fn reset_single_iteration_values(&mut self) {
|
||||
@@ -199,13 +199,13 @@ impl RealizedOps for CoreRealizedState {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn increment_snapshot(&mut self, price_sats: CentsSats, _investor_cap: CentsSquaredSats) {
|
||||
self.minimal.increment_snapshot(price_sats, _investor_cap);
|
||||
fn increment_snapshot(&mut self, price_sats: CentsSats, _capitalized_cap: CentsSquaredSats) {
|
||||
self.minimal.increment_snapshot(price_sats, _capitalized_cap);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn decrement_snapshot(&mut self, price_sats: CentsSats, _investor_cap: CentsSquaredSats) {
|
||||
self.minimal.decrement_snapshot(price_sats, _investor_cap);
|
||||
fn decrement_snapshot(&mut self, price_sats: CentsSats, _capitalized_cap: CentsSquaredSats) {
|
||||
self.minimal.decrement_snapshot(price_sats, _capitalized_cap);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -215,10 +215,10 @@ impl RealizedOps for CoreRealizedState {
|
||||
current_ps: CentsSats,
|
||||
prev_ps: CentsSats,
|
||||
ath_ps: CentsSats,
|
||||
prev_investor_cap: CentsSquaredSats,
|
||||
prev_capitalized_cap: CentsSquaredSats,
|
||||
) {
|
||||
self.minimal
|
||||
.send(sats, current_ps, prev_ps, ath_ps, prev_investor_cap);
|
||||
.send(sats, current_ps, prev_ps, ath_ps, prev_capitalized_cap);
|
||||
match current_ps.cmp(&prev_ps) {
|
||||
Ordering::Greater | Ordering::Equal => {
|
||||
self.sent_in_profit += sats;
|
||||
@@ -242,8 +242,8 @@ impl CoreRealizedState {
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct RealizedState {
|
||||
core: CoreRealizedState,
|
||||
/// Raw investor cap: Σ(price² × sats)
|
||||
investor_cap_raw: CentsSquaredSats,
|
||||
/// Raw capitalized cap: Σ(price² × sats)
|
||||
capitalized_cap_raw: CentsSquaredSats,
|
||||
/// Raw realized peak regret: Σ((peak - sell_price) × sats)
|
||||
peak_regret_raw: u128,
|
||||
}
|
||||
@@ -287,8 +287,8 @@ impl RealizedOps for RealizedState {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_investor_cap_raw(&mut self, investor_cap_raw: CentsSquaredSats) {
|
||||
self.investor_cap_raw = investor_cap_raw;
|
||||
fn set_capitalized_cap_raw(&mut self, capitalized_cap_raw: CentsSquaredSats) {
|
||||
self.capitalized_cap_raw = capitalized_cap_raw;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -301,20 +301,20 @@ impl RealizedOps for RealizedState {
|
||||
fn increment(&mut self, price: Cents, sats: Sats) {
|
||||
self.core.increment(price, sats);
|
||||
if sats.is_not_zero() {
|
||||
self.investor_cap_raw += CentsSats::from_price_sats(price, sats).to_investor_cap(price);
|
||||
self.capitalized_cap_raw += CentsSats::from_price_sats(price, sats).to_capitalized_cap(price);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn increment_snapshot(&mut self, price_sats: CentsSats, investor_cap: CentsSquaredSats) {
|
||||
self.core.increment_snapshot(price_sats, investor_cap);
|
||||
self.investor_cap_raw += investor_cap;
|
||||
fn increment_snapshot(&mut self, price_sats: CentsSats, capitalized_cap: CentsSquaredSats) {
|
||||
self.core.increment_snapshot(price_sats, capitalized_cap);
|
||||
self.capitalized_cap_raw += capitalized_cap;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn decrement_snapshot(&mut self, price_sats: CentsSats, investor_cap: CentsSquaredSats) {
|
||||
self.core.decrement_snapshot(price_sats, investor_cap);
|
||||
self.investor_cap_raw -= investor_cap;
|
||||
fn decrement_snapshot(&mut self, price_sats: CentsSats, capitalized_cap: CentsSquaredSats) {
|
||||
self.core.decrement_snapshot(price_sats, capitalized_cap);
|
||||
self.capitalized_cap_raw -= capitalized_cap;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -324,26 +324,26 @@ impl RealizedOps for RealizedState {
|
||||
current_ps: CentsSats,
|
||||
prev_ps: CentsSats,
|
||||
ath_ps: CentsSats,
|
||||
prev_investor_cap: CentsSquaredSats,
|
||||
prev_capitalized_cap: CentsSquaredSats,
|
||||
) {
|
||||
self.core
|
||||
.send(sats, current_ps, prev_ps, ath_ps, prev_investor_cap);
|
||||
.send(sats, current_ps, prev_ps, ath_ps, prev_capitalized_cap);
|
||||
|
||||
self.peak_regret_raw += (ath_ps - current_ps).as_u128();
|
||||
self.investor_cap_raw -= prev_investor_cap;
|
||||
self.capitalized_cap_raw -= prev_capitalized_cap;
|
||||
}
|
||||
}
|
||||
|
||||
impl RealizedState {
|
||||
/// Get investor price as CentsUnsigned.
|
||||
/// investor_price = Σ(price² × sats) / Σ(price × sats)
|
||||
/// Get capitalized price as CentsUnsigned.
|
||||
/// capitalized_price = Σ(price² × sats) / Σ(price × sats)
|
||||
#[inline]
|
||||
pub(crate) fn investor_price(&self) -> Cents {
|
||||
pub(crate) fn capitalized_price(&self) -> Cents {
|
||||
let cap_raw = self.core.cap_raw_u128();
|
||||
if cap_raw == 0 {
|
||||
return Cents::ZERO;
|
||||
}
|
||||
Cents::new((self.investor_cap_raw / cap_raw) as u64)
|
||||
Cents::new((self.capitalized_cap_raw / cap_raw) as u64)
|
||||
}
|
||||
|
||||
/// Get raw realized cap for aggregation.
|
||||
@@ -352,10 +352,10 @@ impl RealizedState {
|
||||
CentsSats::new(self.core.cap_raw_u128())
|
||||
}
|
||||
|
||||
/// Get raw investor cap for aggregation.
|
||||
/// Get raw capitalized cap for aggregation.
|
||||
#[inline]
|
||||
pub(crate) fn investor_cap_raw(&self) -> CentsSquaredSats {
|
||||
self.investor_cap_raw
|
||||
pub(crate) fn capitalized_cap_raw(&self) -> CentsSquaredSats {
|
||||
self.capitalized_cap_raw
|
||||
}
|
||||
|
||||
/// Get realized peak regret as CentsUnsigned.
|
||||
|
||||
@@ -10,8 +10,8 @@ pub struct UnrealizedState {
|
||||
pub supply_in_loss: Sats,
|
||||
pub unrealized_profit: Cents,
|
||||
pub unrealized_loss: Cents,
|
||||
pub investor_cap_in_profit_raw: u128,
|
||||
pub investor_cap_in_loss_raw: u128,
|
||||
pub capitalized_cap_in_profit_raw: u128,
|
||||
pub capitalized_cap_in_loss_raw: u128,
|
||||
}
|
||||
|
||||
impl UnrealizedState {
|
||||
@@ -20,8 +20,8 @@ impl UnrealizedState {
|
||||
supply_in_loss: Sats::ZERO,
|
||||
unrealized_profit: Cents::ZERO,
|
||||
unrealized_loss: Cents::ZERO,
|
||||
investor_cap_in_profit_raw: 0,
|
||||
investor_cap_in_loss_raw: 0,
|
||||
capitalized_cap_in_profit_raw: 0,
|
||||
capitalized_cap_in_loss_raw: 0,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -34,12 +34,12 @@ pub struct WithoutCapital {
|
||||
pub(crate) unrealized_loss: u128,
|
||||
}
|
||||
|
||||
/// Full cache state: core + investor cap (for sentiment computation).
|
||||
/// Full cache state: core + capitalized cap (for sentiment computation).
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct WithCapital {
|
||||
core: WithoutCapital,
|
||||
investor_cap_in_profit: u128,
|
||||
investor_cap_in_loss: u128,
|
||||
capitalized_cap_in_profit: u128,
|
||||
capitalized_cap_in_loss: u128,
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
@@ -116,8 +116,8 @@ impl Accumulate for WithoutCapital {
|
||||
impl Accumulate for WithCapital {
|
||||
fn to_output(&self) -> UnrealizedState {
|
||||
UnrealizedState {
|
||||
investor_cap_in_profit_raw: self.investor_cap_in_profit,
|
||||
investor_cap_in_loss_raw: self.investor_cap_in_loss,
|
||||
capitalized_cap_in_profit_raw: self.capitalized_cap_in_profit,
|
||||
capitalized_cap_in_loss_raw: self.capitalized_cap_in_loss,
|
||||
..Accumulate::to_output(&self.core)
|
||||
}
|
||||
}
|
||||
@@ -133,25 +133,25 @@ impl Accumulate for WithCapital {
|
||||
fn accumulate_profit(&mut self, price_u128: u128, sats: Sats) {
|
||||
self.core.supply_in_profit += sats;
|
||||
let invested = price_u128 * sats.as_u128();
|
||||
self.investor_cap_in_profit += price_u128 * invested;
|
||||
self.capitalized_cap_in_profit += price_u128 * invested;
|
||||
}
|
||||
#[inline(always)]
|
||||
fn accumulate_loss(&mut self, price_u128: u128, sats: Sats) {
|
||||
self.core.supply_in_loss += sats;
|
||||
let invested = price_u128 * sats.as_u128();
|
||||
self.investor_cap_in_loss += price_u128 * invested;
|
||||
self.capitalized_cap_in_loss += price_u128 * invested;
|
||||
}
|
||||
#[inline(always)]
|
||||
fn deaccumulate_profit(&mut self, price_u128: u128, sats: Sats) {
|
||||
self.core.supply_in_profit -= sats;
|
||||
let invested = price_u128 * sats.as_u128();
|
||||
self.investor_cap_in_profit -= price_u128 * invested;
|
||||
self.capitalized_cap_in_profit -= price_u128 * invested;
|
||||
}
|
||||
#[inline(always)]
|
||||
fn deaccumulate_loss(&mut self, price_u128: u128, sats: Sats) {
|
||||
self.core.supply_in_loss -= sats;
|
||||
let invested = price_u128 * sats.as_u128();
|
||||
self.investor_cap_in_loss -= price_u128 * invested;
|
||||
self.capitalized_cap_in_loss -= price_u128 * invested;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ impl PendingCapDelta {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub(crate) struct PendingInvestorCapDelta {
|
||||
pub(crate) struct PendingCapitalizedCapRawDelta {
|
||||
pub inc: CentsSquaredSats,
|
||||
pub dec: CentsSquaredSats,
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use brk_cohort::{ByAddrType, Filter};
|
||||
use brk_error::Result;
|
||||
use brk_indexer::Indexer;
|
||||
use brk_traversable::Traversable;
|
||||
@@ -476,9 +477,18 @@ impl Vecs {
|
||||
&inputs.by_type,
|
||||
exit,
|
||||
)?;
|
||||
self.addrs
|
||||
.exposed
|
||||
.compute_rest(starting_indexes, prices, exit)?;
|
||||
let t = &self.utxo_cohorts.type_;
|
||||
let type_supply_sats = ByAddrType::new(|filter| {
|
||||
let Filter::Type(ot) = filter else { unreachable!() };
|
||||
&t.get(ot).metrics.supply.total.sats.height
|
||||
});
|
||||
self.addrs.exposed.compute_rest(
|
||||
starting_indexes,
|
||||
prices,
|
||||
&self.utxo_cohorts.all.metrics.supply.total.sats.height,
|
||||
&type_supply_sats,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// 6c. Compute total_addr_count = addr_count + empty_addr_count
|
||||
self.addrs.total.compute(
|
||||
|
||||
@@ -44,15 +44,15 @@ impl RarityMeter {
|
||||
let lth_realized = &distribution.utxo_cohorts.lth.metrics.realized;
|
||||
let spot = &prices.spot.cents.height;
|
||||
|
||||
// Full: all + sth + lth (rp + ip), 6 models
|
||||
// Full: all + sth + lth (rp + cp), 6 models
|
||||
self.full.compute(
|
||||
&[
|
||||
&realized.price_ratio_percentiles,
|
||||
&realized.investor.price.percentiles,
|
||||
&realized.capitalized.price.percentiles,
|
||||
&sth_realized.price_ratio_percentiles,
|
||||
&sth_realized.investor.price.percentiles,
|
||||
&sth_realized.capitalized.price.percentiles,
|
||||
<h_realized.price_ratio_percentiles,
|
||||
<h_realized.investor.price.percentiles,
|
||||
<h_realized.capitalized.price.percentiles,
|
||||
],
|
||||
spot,
|
||||
starting_indexes,
|
||||
@@ -63,7 +63,7 @@ impl RarityMeter {
|
||||
self.local.compute(
|
||||
&[
|
||||
&sth_realized.price_ratio_percentiles,
|
||||
&sth_realized.investor.price.percentiles,
|
||||
&sth_realized.capitalized.price.percentiles,
|
||||
],
|
||||
spot,
|
||||
starting_indexes,
|
||||
@@ -74,9 +74,9 @@ impl RarityMeter {
|
||||
self.cycle.compute(
|
||||
&[
|
||||
&realized.price_ratio_percentiles,
|
||||
&realized.investor.price.percentiles,
|
||||
&realized.capitalized.price.percentiles,
|
||||
<h_realized.price_ratio_percentiles,
|
||||
<h_realized.investor.price.percentiles,
|
||||
<h_realized.capitalized.price.percentiles,
|
||||
],
|
||||
spot,
|
||||
starting_indexes,
|
||||
|
||||
@@ -13,7 +13,8 @@ use vecdb::{AnyStoredVec, AnyVec, Database, EagerVec, Exit, PcoVec, WritableVec}
|
||||
use crate::{indexes, prices};
|
||||
|
||||
use super::{
|
||||
AmountPerBlock, NumericValue, PerBlock, PerBlockCumulativeRolling, WindowStartVec, Windows,
|
||||
AmountPerBlock, BpsType, NumericValue, PerBlock, PerBlockCumulativeRolling, PercentPerBlock,
|
||||
WindowStartVec, Windows,
|
||||
};
|
||||
|
||||
/// `all` aggregate plus per-`AddrType` breakdown.
|
||||
@@ -244,3 +245,26 @@ impl WithAddrTypes<AmountPerBlock> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: BpsType> WithAddrTypes<PercentPerBlock<B>> {
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
let all = PercentPerBlock::forced_import(db, name, version, indexes)?;
|
||||
let by_addr_type = ByAddrType::new_with_name(|type_name| {
|
||||
PercentPerBlock::forced_import(db, &format!("{type_name}_{name}"), version, indexes)
|
||||
})?;
|
||||
Ok(Self { all, by_addr_type })
|
||||
}
|
||||
|
||||
pub(crate) fn reset_height(&mut self) -> Result<()> {
|
||||
self.all.bps.height.reset()?;
|
||||
for v in self.by_addr_type.values_mut() {
|
||||
v.bps.height.reset()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user