mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-04-24 14:49:58 -07:00
global: snapshot
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
use brk_cohort::ByAddressType;
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Indexes, StoredF64, StoredU64, Version};
|
||||
use brk_types::{Height, Indexes, StoredU64, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use rayon::prelude::*;
|
||||
use vecdb::{
|
||||
@@ -9,15 +9,12 @@ use vecdb::{
|
||||
WritableVec,
|
||||
};
|
||||
|
||||
use crate::{blocks, indexes, internal::ComputedFromHeight};
|
||||
use crate::{indexes, internal::ComputedFromHeight};
|
||||
|
||||
/// Address count with 1m change metric for a single type.
|
||||
#[derive(Traversable)]
|
||||
pub struct AddrCountVecs<M: StorageMode = Rw> {
|
||||
#[traversable(flatten)]
|
||||
pub count: ComputedFromHeight<StoredU64, M>,
|
||||
pub change_1m: ComputedFromHeight<StoredF64, M>,
|
||||
}
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
pub struct AddrCountVecs<M: StorageMode = Rw>(
|
||||
#[traversable(flatten)] pub ComputedFromHeight<StoredU64, M>,
|
||||
);
|
||||
|
||||
impl AddrCountVecs {
|
||||
pub(crate) fn forced_import(
|
||||
@@ -26,31 +23,9 @@ impl AddrCountVecs {
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
count: ComputedFromHeight::forced_import(db, name, version, indexes)?,
|
||||
change_1m: ComputedFromHeight::forced_import(
|
||||
db,
|
||||
&format!("{name}_change_1m"),
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn compute_rest(
|
||||
&mut self,
|
||||
blocks: &blocks::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.change_1m.height.compute_rolling_change(
|
||||
starting_indexes.height,
|
||||
&blocks.count.height_1m_ago,
|
||||
&self.count.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
Ok(Self(ComputedFromHeight::forced_import(
|
||||
db, name, version, indexes,
|
||||
)?))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,56 +47,48 @@ impl From<(&AddressTypeToAddrCountVecs, Height)> for AddressTypeToAddressCount {
|
||||
Self(ByAddressType {
|
||||
p2pk65: groups
|
||||
.p2pk65
|
||||
.count
|
||||
.height
|
||||
.collect_one(prev_height)
|
||||
.unwrap()
|
||||
.into(),
|
||||
p2pk33: groups
|
||||
.p2pk33
|
||||
.count
|
||||
.height
|
||||
.collect_one(prev_height)
|
||||
.unwrap()
|
||||
.into(),
|
||||
p2pkh: groups
|
||||
.p2pkh
|
||||
.count
|
||||
.height
|
||||
.collect_one(prev_height)
|
||||
.unwrap()
|
||||
.into(),
|
||||
p2sh: groups
|
||||
.p2sh
|
||||
.count
|
||||
.height
|
||||
.collect_one(prev_height)
|
||||
.unwrap()
|
||||
.into(),
|
||||
p2wpkh: groups
|
||||
.p2wpkh
|
||||
.count
|
||||
.height
|
||||
.collect_one(prev_height)
|
||||
.unwrap()
|
||||
.into(),
|
||||
p2wsh: groups
|
||||
.p2wsh
|
||||
.count
|
||||
.height
|
||||
.collect_one(prev_height)
|
||||
.unwrap()
|
||||
.into(),
|
||||
p2tr: groups
|
||||
.p2tr
|
||||
.count
|
||||
.height
|
||||
.collect_one(prev_height)
|
||||
.unwrap()
|
||||
.into(),
|
||||
p2a: groups
|
||||
.p2a
|
||||
.count
|
||||
.height
|
||||
.collect_one(prev_height)
|
||||
.unwrap()
|
||||
@@ -133,7 +100,7 @@ impl From<(&AddressTypeToAddrCountVecs, Height)> for AddressTypeToAddressCount {
|
||||
}
|
||||
}
|
||||
|
||||
/// Address count per address type, with height + derived indexes + 1m change.
|
||||
/// Address count per address type, with height + derived indexes.
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
pub struct AddressTypeToAddrCountVecs<M: StorageMode = Rw>(ByAddressType<AddrCountVecs<M>>);
|
||||
|
||||
@@ -159,7 +126,7 @@ impl AddressTypeToAddrCountVecs {
|
||||
}
|
||||
|
||||
pub(crate) fn min_stateful_height(&self) -> usize {
|
||||
self.0.values().map(|v| v.count.height.len()).min().unwrap()
|
||||
self.0.values().map(|v| v.height.len()).min().unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn par_iter_height_mut(
|
||||
@@ -167,7 +134,7 @@ impl AddressTypeToAddrCountVecs {
|
||||
) -> impl ParallelIterator<Item = &mut dyn AnyStoredVec> {
|
||||
self.0
|
||||
.par_values_mut()
|
||||
.map(|v| &mut v.count.height as &mut dyn AnyStoredVec)
|
||||
.map(|v| &mut v.height as &mut dyn AnyStoredVec)
|
||||
}
|
||||
|
||||
pub(crate) fn truncate_push_height(
|
||||
@@ -176,7 +143,7 @@ impl AddressTypeToAddrCountVecs {
|
||||
addr_counts: &AddressTypeToAddressCount,
|
||||
) -> Result<()> {
|
||||
for (vecs, &count) in self.0.values_mut().zip(addr_counts.values()) {
|
||||
vecs.count.height.truncate_push(height, count.into())?;
|
||||
vecs.height.truncate_push(height, count.into())?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -184,25 +151,13 @@ impl AddressTypeToAddrCountVecs {
|
||||
pub(crate) fn reset_height(&mut self) -> Result<()> {
|
||||
use vecdb::WritableVec;
|
||||
for v in self.0.values_mut() {
|
||||
v.count.height.reset()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn compute_rest(
|
||||
&mut self,
|
||||
blocks: &blocks::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
for v in self.0.values_mut() {
|
||||
v.compute_rest(blocks, starting_indexes, exit)?;
|
||||
v.height.reset()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn by_height(&self) -> Vec<&EagerVec<PcoVec<Height, StoredU64>>> {
|
||||
self.0.values().map(|v| &v.count.height).collect()
|
||||
self.0.values().map(|v| &v.height).collect()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,22 +182,18 @@ impl AddrCountsVecs {
|
||||
}
|
||||
|
||||
pub(crate) fn min_stateful_height(&self) -> usize {
|
||||
self.all
|
||||
.count
|
||||
.height
|
||||
.len()
|
||||
.min(self.by_addresstype.min_stateful_height())
|
||||
self.all.height.len().min(self.by_addresstype.min_stateful_height())
|
||||
}
|
||||
|
||||
pub(crate) fn par_iter_height_mut(
|
||||
&mut self,
|
||||
) -> impl ParallelIterator<Item = &mut dyn AnyStoredVec> {
|
||||
rayon::iter::once(&mut self.all.count.height as &mut dyn AnyStoredVec)
|
||||
rayon::iter::once(&mut self.all.height as &mut dyn AnyStoredVec)
|
||||
.chain(self.by_addresstype.par_iter_height_mut())
|
||||
}
|
||||
|
||||
pub(crate) fn reset_height(&mut self) -> Result<()> {
|
||||
self.all.count.height.reset()?;
|
||||
self.all.height.reset()?;
|
||||
self.by_addresstype.reset_height()?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -253,7 +204,7 @@ impl AddrCountsVecs {
|
||||
total: u64,
|
||||
addr_counts: &AddressTypeToAddressCount,
|
||||
) -> Result<()> {
|
||||
self.all.count.height.truncate_push(height, total.into())?;
|
||||
self.all.height.truncate_push(height, total.into())?;
|
||||
self.by_addresstype
|
||||
.truncate_push_height(height, addr_counts)?;
|
||||
Ok(())
|
||||
@@ -261,26 +212,13 @@ impl AddrCountsVecs {
|
||||
|
||||
pub(crate) fn compute_rest(
|
||||
&mut self,
|
||||
blocks: &blocks::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.by_addresstype
|
||||
.compute_rest(blocks, starting_indexes, exit)?;
|
||||
|
||||
let sources = self.by_addresstype.by_height();
|
||||
self.all
|
||||
.count
|
||||
.height
|
||||
.compute_sum_of_others(starting_indexes.height, &sources, exit)?;
|
||||
|
||||
self.all.change_1m.height.compute_rolling_change(
|
||||
starting_indexes.height,
|
||||
&blocks.count.height_1m_ago,
|
||||
&self.all.count.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
61
crates/brk_computer/src/distribution/address/delta.rs
Normal file
61
crates/brk_computer/src/distribution/address/delta.rs
Normal file
@@ -0,0 +1,61 @@
|
||||
use brk_cohort::ByAddressType;
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, StoredI64, StoredU64, Version};
|
||||
use vecdb::{Database, Exit, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{WindowStarts, RollingDelta},
|
||||
};
|
||||
|
||||
use super::AddrCountsVecs;
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct DeltaVecs<M: StorageMode = Rw> {
|
||||
pub all: RollingDelta<StoredU64, StoredI64, M>,
|
||||
#[traversable(flatten)]
|
||||
pub by_addresstype: ByAddressType<RollingDelta<StoredU64, StoredI64, M>>,
|
||||
}
|
||||
|
||||
impl DeltaVecs {
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
let version = version + Version::ONE;
|
||||
|
||||
let all = RollingDelta::forced_import(db, "addr_count", version, indexes)?;
|
||||
|
||||
let by_addresstype = ByAddressType::new_with_name(|name| {
|
||||
RollingDelta::forced_import(db, &format!("{name}_addr_count"), version, indexes)
|
||||
})?;
|
||||
|
||||
Ok(Self {
|
||||
all,
|
||||
by_addresstype,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
windows: &WindowStarts<'_>,
|
||||
addr_count: &AddrCountsVecs,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.all
|
||||
.compute(max_from, windows, &addr_count.all.height, exit)?;
|
||||
|
||||
for ((_, growth), (_, addr)) in self
|
||||
.by_addresstype
|
||||
.iter_mut()
|
||||
.zip(addr_count.by_addresstype.iter())
|
||||
{
|
||||
growth.compute(max_from, windows, &addr.height, exit)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
use brk_cohort::ByAddressType;
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{BasisPoints16, Height, StoredU64, Version};
|
||||
use vecdb::{Database, EagerVec, Exit, PcoVec, ReadableVec, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{PercentFromHeightDistribution, WindowStarts},
|
||||
};
|
||||
|
||||
use super::{AddrCountsVecs, NewAddrCountVecs};
|
||||
|
||||
/// Growth rate: new_addr_count / addr_count (global + per-type)
|
||||
#[derive(Traversable)]
|
||||
pub struct GrowthRateVecs<M: StorageMode = Rw> {
|
||||
pub all: PercentFromHeightDistribution<BasisPoints16, M>,
|
||||
#[traversable(flatten)]
|
||||
pub by_addresstype: ByAddressType<PercentFromHeightDistribution<BasisPoints16, M>>,
|
||||
}
|
||||
|
||||
impl GrowthRateVecs {
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
let all =
|
||||
PercentFromHeightDistribution::forced_import(db, "growth_rate", version, indexes)?;
|
||||
|
||||
let by_addresstype = ByAddressType::new_with_name(|name| {
|
||||
PercentFromHeightDistribution::forced_import(
|
||||
db,
|
||||
&format!("{name}_growth_rate"),
|
||||
version,
|
||||
indexes,
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok(Self {
|
||||
all,
|
||||
by_addresstype,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
windows: &WindowStarts<'_>,
|
||||
new_addr_count: &NewAddrCountVecs,
|
||||
addr_count: &AddrCountsVecs,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.all.compute(max_from, windows, exit, |target| {
|
||||
compute_ratio(
|
||||
target,
|
||||
max_from,
|
||||
&new_addr_count.all.height,
|
||||
&addr_count.all.count.height,
|
||||
exit,
|
||||
)
|
||||
})?;
|
||||
|
||||
for ((_, growth), ((_, new), (_, addr))) in self.by_addresstype.iter_mut().zip(
|
||||
new_addr_count
|
||||
.by_addresstype
|
||||
.iter()
|
||||
.zip(addr_count.by_addresstype.iter()),
|
||||
) {
|
||||
growth.compute(max_from, windows, exit, |target| {
|
||||
compute_ratio(target, max_from, &new.height, &addr.count.height, exit)
|
||||
})?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn compute_ratio(
|
||||
target: &mut EagerVec<PcoVec<Height, BasisPoints16>>,
|
||||
max_from: Height,
|
||||
numerator: &impl ReadableVec<Height, StoredU64>,
|
||||
denominator: &impl ReadableVec<Height, StoredU64>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
target.compute_transform2(
|
||||
max_from,
|
||||
numerator,
|
||||
denominator,
|
||||
|(h, num, den, ..)| {
|
||||
let n = *num as f64;
|
||||
let d = *den as f64;
|
||||
let ratio = if d == 0.0 { 0.0 } else { n / d };
|
||||
(h, BasisPoints16::from(ratio))
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
mod activity;
|
||||
mod address_count;
|
||||
mod data;
|
||||
mod growth_rate;
|
||||
mod delta;
|
||||
mod indexes;
|
||||
mod new_addr_count;
|
||||
mod total_addr_count;
|
||||
@@ -10,7 +10,7 @@ mod type_map;
|
||||
pub use activity::{AddressActivityVecs, AddressTypeToActivityCounts};
|
||||
pub use address_count::{AddrCountsVecs, AddressTypeToAddressCount};
|
||||
pub use data::AddressesDataVecs;
|
||||
pub use growth_rate::GrowthRateVecs;
|
||||
pub use delta::DeltaVecs;
|
||||
pub use indexes::AnyAddressIndexesVecs;
|
||||
pub use new_addr_count::NewAddrCountVecs;
|
||||
pub use total_addr_count::TotalAddrCountVecs;
|
||||
|
||||
@@ -50,8 +50,8 @@ impl TotalAddrCountVecs {
|
||||
) -> Result<()> {
|
||||
self.all.height.compute_add(
|
||||
max_from,
|
||||
&addr_count.all.count.height,
|
||||
&empty_addr_count.all.count.height,
|
||||
&addr_count.all.height,
|
||||
&empty_addr_count.all.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
@@ -63,7 +63,7 @@ impl TotalAddrCountVecs {
|
||||
) {
|
||||
total
|
||||
.height
|
||||
.compute_add(max_from, &addr.count.height, &empty.count.height, exit)?;
|
||||
.compute_add(max_from, &addr.height, &empty.height, exit)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -378,46 +378,6 @@ impl UTXOCohorts<Rw> {
|
||||
.try_for_each(|v| v.compute_rest_part1(blocks, prices, starting_indexes, exit))?;
|
||||
}
|
||||
|
||||
// 2. Compute net_sentiment.height for aggregate cohorts (weighted average).
|
||||
// Separate cohorts already computed net_sentiment in step 1 (inside compute_rest_part1).
|
||||
// Note: min_age, max_age, epoch, class are Core tier — no net_sentiment.
|
||||
// Note: ge_amount, lt_amount, amount_range are Minimal tier — no net_sentiment.
|
||||
{
|
||||
let Self {
|
||||
all,
|
||||
sth,
|
||||
lth,
|
||||
age_range,
|
||||
..
|
||||
} = self;
|
||||
|
||||
let ar = &*age_range;
|
||||
let si = starting_indexes;
|
||||
|
||||
let tasks: Vec<Box<dyn FnOnce() -> Result<()> + Send + '_>> = vec![
|
||||
Box::new(|| {
|
||||
let sources = filter_sources_from(ar.iter(), None);
|
||||
all.metrics
|
||||
.compute_net_sentiment_from_others(si, &sources, exit)
|
||||
}),
|
||||
Box::new(|| {
|
||||
let sources = filter_sources_from(ar.iter(), Some(sth.metrics.filter()));
|
||||
sth.metrics
|
||||
.compute_net_sentiment_from_others(si, &sources, exit)
|
||||
}),
|
||||
Box::new(|| {
|
||||
let sources = filter_sources_from(ar.iter(), Some(lth.metrics.filter()));
|
||||
lth.metrics
|
||||
.compute_net_sentiment_from_others(si, &sources, exit)
|
||||
}),
|
||||
];
|
||||
|
||||
tasks
|
||||
.into_par_iter()
|
||||
.map(|f| f())
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ use brk_types::{Bitcoin, Height, Indexes, Sats, StoredF64, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{AnyStoredVec, AnyVec, Exit, Rw, StorageMode, WritableVec};
|
||||
|
||||
use crate::internal::ComputedFromHeightCumulativeSum;
|
||||
use crate::internal::{ComputedFromHeightCumulative, ComputedFromHeightCumulativeSum};
|
||||
|
||||
use crate::{blocks, distribution::metrics::ImportConfig, prices};
|
||||
|
||||
@@ -17,7 +17,7 @@ pub struct ActivityFull<M: StorageMode = Rw> {
|
||||
#[traversable(flatten)]
|
||||
pub base: ActivityBase<M>,
|
||||
|
||||
pub coinblocks_destroyed: ComputedFromHeightCumulativeSum<StoredF64, M>,
|
||||
pub coinblocks_destroyed: ComputedFromHeightCumulative<StoredF64, M>,
|
||||
pub coindays_destroyed: ComputedFromHeightCumulativeSum<StoredF64, M>,
|
||||
}
|
||||
|
||||
@@ -94,11 +94,10 @@ impl ActivityFull {
|
||||
self.base
|
||||
.compute_rest_part1(blocks, prices, starting_indexes, exit)?;
|
||||
|
||||
let window_starts = blocks.count.window_starts();
|
||||
|
||||
self.coinblocks_destroyed
|
||||
.compute_rest(starting_indexes.height, &window_starts, exit)?;
|
||||
.compute_rest(starting_indexes.height, exit)?;
|
||||
|
||||
let window_starts = blocks.count.window_starts();
|
||||
self.coindays_destroyed
|
||||
.compute_rest(starting_indexes.height, &window_starts, exit)?;
|
||||
|
||||
|
||||
@@ -35,6 +35,7 @@ pub struct AllCohortMetrics<M: StorageMode = Rw> {
|
||||
|
||||
impl CohortMetricsBase for AllCohortMetrics {
|
||||
type RealizedVecs = RealizedFull;
|
||||
type UnrealizedVecs = UnrealizedFull;
|
||||
type CostBasisVecs = CostBasisWithExtended;
|
||||
|
||||
impl_cohort_accessors!();
|
||||
|
||||
@@ -8,7 +8,7 @@ use crate::{blocks, prices};
|
||||
|
||||
use crate::distribution::metrics::{
|
||||
ActivityFull, CohortMetricsBase, CostBasisBase, ImportConfig, OutputsMetrics, RealizedBase,
|
||||
RelativeWithRelToAll, SupplyMetrics, UnrealizedFull,
|
||||
RelativeWithRelToAll, SupplyMetrics, UnrealizedBase,
|
||||
};
|
||||
|
||||
/// Basic cohort metrics: no extensions, with relative (rel_to_all).
|
||||
@@ -22,12 +22,13 @@ pub struct BasicCohortMetrics<M: StorageMode = Rw> {
|
||||
pub activity: Box<ActivityFull<M>>,
|
||||
pub realized: Box<RealizedBase<M>>,
|
||||
pub cost_basis: Box<CostBasisBase<M>>,
|
||||
pub unrealized: Box<UnrealizedFull<M>>,
|
||||
pub unrealized: Box<UnrealizedBase<M>>,
|
||||
pub relative: Box<RelativeWithRelToAll<M>>,
|
||||
}
|
||||
|
||||
impl CohortMetricsBase for BasicCohortMetrics {
|
||||
type RealizedVecs = RealizedBase;
|
||||
type UnrealizedVecs = UnrealizedBase;
|
||||
type CostBasisVecs = CostBasisBase;
|
||||
|
||||
impl_cohort_accessors!();
|
||||
@@ -47,7 +48,7 @@ impl CohortMetricsBase for BasicCohortMetrics {
|
||||
impl BasicCohortMetrics {
|
||||
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
|
||||
let supply = SupplyMetrics::forced_import(cfg)?;
|
||||
let unrealized = UnrealizedFull::forced_import(cfg)?;
|
||||
let unrealized = UnrealizedBase::forced_import(cfg)?;
|
||||
let realized = RealizedBase::forced_import(cfg)?;
|
||||
|
||||
let relative = RelativeWithRelToAll::forced_import(cfg)?;
|
||||
|
||||
@@ -8,7 +8,7 @@ use crate::{blocks, prices};
|
||||
|
||||
use crate::distribution::metrics::{
|
||||
ActivityBase, CohortMetricsBase, RealizedBase, ImportConfig, OutputsMetrics,
|
||||
RelativeBaseWithRelToAll, SupplyMetrics, UnrealizedBase,
|
||||
RelativeBaseWithRelToAll, SupplyMetrics, UnrealizedCore,
|
||||
};
|
||||
|
||||
#[derive(Traversable)]
|
||||
@@ -19,7 +19,7 @@ pub struct CoreCohortMetrics<M: StorageMode = Rw> {
|
||||
pub outputs: Box<OutputsMetrics<M>>,
|
||||
pub activity: Box<ActivityBase<M>>,
|
||||
pub realized: Box<RealizedBase<M>>,
|
||||
pub unrealized: Box<UnrealizedBase<M>>,
|
||||
pub unrealized: Box<UnrealizedCore<M>>,
|
||||
pub relative: Box<RelativeBaseWithRelToAll<M>>,
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ impl CoreCohortMetrics {
|
||||
outputs: Box::new(OutputsMetrics::forced_import(cfg)?),
|
||||
activity: Box::new(ActivityBase::forced_import(cfg)?),
|
||||
realized: Box::new(RealizedBase::forced_import(cfg)?),
|
||||
unrealized: Box::new(UnrealizedBase::forced_import(cfg)?),
|
||||
unrealized: Box::new(UnrealizedCore::forced_import(cfg)?),
|
||||
relative: Box::new(RelativeBaseWithRelToAll::forced_import(cfg)?),
|
||||
})
|
||||
}
|
||||
@@ -90,7 +90,7 @@ impl CoreCohortMetrics {
|
||||
)?;
|
||||
self.unrealized.compute_from_stateful(
|
||||
starting_indexes,
|
||||
&others.iter().map(|v| &v.unrealized_full().base).collect::<Vec<_>>(),
|
||||
&others.iter().map(|v| &v.unrealized_base().core).collect::<Vec<_>>(),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ pub struct ExtendedCohortMetrics<M: StorageMode = Rw> {
|
||||
|
||||
impl CohortMetricsBase for ExtendedCohortMetrics {
|
||||
type RealizedVecs = RealizedFull;
|
||||
type UnrealizedVecs = UnrealizedFull;
|
||||
type CostBasisVecs = CostBasisWithExtended;
|
||||
|
||||
impl_cohort_accessors!();
|
||||
|
||||
@@ -8,6 +8,7 @@ use crate::{blocks, prices};
|
||||
|
||||
use crate::distribution::metrics::{
|
||||
CohortMetricsBase, CostBasisWithExtended, ImportConfig, RealizedAdjusted, RealizedFull,
|
||||
UnrealizedFull,
|
||||
};
|
||||
|
||||
use super::ExtendedCohortMetrics;
|
||||
@@ -27,6 +28,7 @@ pub struct ExtendedAdjustedCohortMetrics<M: StorageMode = Rw> {
|
||||
|
||||
impl CohortMetricsBase for ExtendedAdjustedCohortMetrics {
|
||||
type RealizedVecs = RealizedFull;
|
||||
type UnrealizedVecs = UnrealizedFull;
|
||||
type CostBasisVecs = CostBasisWithExtended;
|
||||
|
||||
impl_cohort_accessors!();
|
||||
|
||||
@@ -27,8 +27,8 @@ macro_rules! impl_cohort_accessors {
|
||||
fn activity_mut(&mut self) -> &mut $crate::distribution::metrics::ActivityFull { &mut self.activity }
|
||||
fn realized(&self) -> &Self::RealizedVecs { &self.realized }
|
||||
fn realized_mut(&mut self) -> &mut Self::RealizedVecs { &mut self.realized }
|
||||
fn unrealized_full(&self) -> &$crate::distribution::metrics::UnrealizedFull { &self.unrealized }
|
||||
fn unrealized_full_mut(&mut self) -> &mut $crate::distribution::metrics::UnrealizedFull { &mut self.unrealized }
|
||||
fn unrealized(&self) -> &Self::UnrealizedVecs { &self.unrealized }
|
||||
fn unrealized_mut(&mut self) -> &mut Self::UnrealizedVecs { &mut self.unrealized }
|
||||
fn cost_basis(&self) -> &Self::CostBasisVecs { &self.cost_basis }
|
||||
fn cost_basis_mut(&mut self) -> &mut Self::CostBasisVecs { &mut self.cost_basis }
|
||||
};
|
||||
@@ -58,7 +58,7 @@ pub use relative::{
|
||||
RelativeBaseWithRelToAll, RelativeForAll, RelativeWithExtended, RelativeWithRelToAll,
|
||||
};
|
||||
pub use supply::SupplyMetrics;
|
||||
pub use unrealized::{UnrealizedBase, UnrealizedFull};
|
||||
pub use unrealized::{UnrealizedBase, UnrealizedCore, UnrealizedFull, UnrealizedLike};
|
||||
|
||||
use brk_cohort::Filter;
|
||||
use brk_error::Result;
|
||||
@@ -92,6 +92,7 @@ impl<M: StorageMode> CohortMetricsState for AllCohortMetrics<M> {
|
||||
|
||||
pub trait CohortMetricsBase: CohortMetricsState<Realized = RealizedState> + Send + Sync {
|
||||
type RealizedVecs: RealizedLike;
|
||||
type UnrealizedVecs: UnrealizedLike;
|
||||
type CostBasisVecs: CostBasisLike;
|
||||
|
||||
fn filter(&self) -> &Filter;
|
||||
@@ -103,8 +104,8 @@ pub trait CohortMetricsBase: CohortMetricsState<Realized = RealizedState> + Send
|
||||
fn activity_mut(&mut self) -> &mut ActivityFull;
|
||||
fn realized(&self) -> &Self::RealizedVecs;
|
||||
fn realized_mut(&mut self) -> &mut Self::RealizedVecs;
|
||||
fn unrealized_full(&self) -> &UnrealizedFull;
|
||||
fn unrealized_full_mut(&mut self) -> &mut UnrealizedFull;
|
||||
fn unrealized(&self) -> &Self::UnrealizedVecs;
|
||||
fn unrealized_mut(&mut self) -> &mut Self::UnrealizedVecs;
|
||||
fn cost_basis(&self) -> &Self::CostBasisVecs;
|
||||
fn cost_basis_mut(&mut self) -> &mut Self::CostBasisVecs;
|
||||
|
||||
@@ -112,6 +113,10 @@ pub trait CohortMetricsBase: CohortMetricsState<Realized = RealizedState> + Send
|
||||
fn realized_base(&self) -> &RealizedBase { self.realized().as_base() }
|
||||
fn realized_base_mut(&mut self) -> &mut RealizedBase { self.realized_mut().as_base_mut() }
|
||||
|
||||
/// Convenience: access unrealized as `&UnrealizedBase` (via `UnrealizedLike::as_base`).
|
||||
fn unrealized_base(&self) -> &UnrealizedBase { self.unrealized().as_base() }
|
||||
fn unrealized_base_mut(&mut self) -> &mut UnrealizedBase { self.unrealized_mut().as_base_mut() }
|
||||
|
||||
/// Convenience: access cost basis as `&CostBasisBase` (via `CostBasisLike::as_base`).
|
||||
fn cost_basis_base(&self) -> &CostBasisBase { self.cost_basis().as_base() }
|
||||
fn cost_basis_base_mut(&mut self) -> &mut CostBasisBase { self.cost_basis_mut().as_base_mut() }
|
||||
@@ -134,7 +139,7 @@ pub trait CohortMetricsBase: CohortMetricsState<Realized = RealizedState> + Send
|
||||
self.cost_basis_base_mut()
|
||||
.truncate_push_minmax(height, state)?;
|
||||
let unrealized_state = state.compute_unrealized_state(height_price);
|
||||
self.unrealized_full_mut()
|
||||
self.unrealized_mut()
|
||||
.truncate_push(height, &unrealized_state)?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -160,7 +165,7 @@ pub trait CohortMetricsBase: CohortMetricsState<Realized = RealizedState> + Send
|
||||
.min(self.outputs().min_len())
|
||||
.min(self.activity().min_len())
|
||||
.min(self.realized().min_stateful_height_len())
|
||||
.min(self.unrealized_full().min_stateful_height_len())
|
||||
.min(self.unrealized().min_stateful_height_len())
|
||||
.min(self.cost_basis_base().min_stateful_height_len())
|
||||
}
|
||||
|
||||
@@ -180,31 +185,6 @@ pub trait CohortMetricsBase: CohortMetricsState<Realized = RealizedState> + Send
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Compute net_sentiment.height as capital-weighted average of component cohorts.
|
||||
fn compute_net_sentiment_from_others<T: CohortMetricsBase>(
|
||||
&mut self,
|
||||
starting_indexes: &Indexes,
|
||||
others: &[&T],
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
let weights: Vec<_> = others
|
||||
.iter()
|
||||
.map(|o| &o.realized_base().realized_cap.height)
|
||||
.collect();
|
||||
let values: Vec<_> = others
|
||||
.iter()
|
||||
.map(|o| &o.unrealized_full().net_sentiment.cents.height)
|
||||
.collect();
|
||||
|
||||
self.unrealized_full_mut()
|
||||
.net_sentiment
|
||||
.cents
|
||||
.height
|
||||
.compute_weighted_average_of_others(starting_indexes.height, &weights, &values, exit)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// First phase of computed metrics (indexes from height).
|
||||
fn compute_rest_part1(
|
||||
&mut self,
|
||||
@@ -234,19 +214,18 @@ pub trait CohortMetricsBase: CohortMetricsState<Realized = RealizedState> + Send
|
||||
self.realized_mut()
|
||||
.compute_rest_part1(starting_indexes, exit)?;
|
||||
|
||||
self.unrealized_full_mut()
|
||||
self.unrealized_mut()
|
||||
.compute_rest(prices, starting_indexes, exit)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Compute net_sentiment.height for separate cohorts (greed - pain).
|
||||
fn compute_net_sentiment_height(
|
||||
&mut self,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.unrealized_full_mut()
|
||||
self.unrealized_mut()
|
||||
.compute_net_sentiment_height(starting_indexes, exit)?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -278,9 +257,9 @@ pub trait CohortMetricsBase: CohortMetricsState<Realized = RealizedState> + Send
|
||||
&others.iter().map(|v| v.realized_base()).collect::<Vec<_>>(),
|
||||
exit,
|
||||
)?;
|
||||
self.unrealized_full_mut().compute_from_stateful(
|
||||
self.unrealized_base_mut().compute_from_stateful(
|
||||
starting_indexes,
|
||||
&others.iter().map(|v| v.unrealized_full()).collect::<Vec<_>>(),
|
||||
&others.iter().map(|v| v.unrealized_base()).collect::<Vec<_>>(),
|
||||
exit,
|
||||
)?;
|
||||
self.cost_basis_base_mut().compute_from_stateful(
|
||||
|
||||
@@ -10,9 +10,8 @@ use crate::{
|
||||
blocks,
|
||||
distribution::state::RealizedOps,
|
||||
internal::{
|
||||
ComputedFromHeight, ComputedFromHeightCumulative,
|
||||
LazyFromHeight, NegCentsUnsignedToDollars, RatioCents64,
|
||||
RollingWindows, ValueFromHeightCumulative,
|
||||
ByUnit, ComputedFromHeight, ComputedFromHeightCumulative, LazyFromHeight,
|
||||
NegCentsUnsignedToDollars, RatioCents64, RollingWindows, SatsToCents,
|
||||
},
|
||||
prices,
|
||||
};
|
||||
|
||||
@@ -5,7 +5,7 @@ use vecdb::{Exit, ReadableVec, Rw, StorageMode};
|
||||
|
||||
use crate::internal::{PercentFromHeight, RatioSatsBp16};
|
||||
|
||||
use crate::distribution::metrics::{ImportConfig, UnrealizedBase};
|
||||
use crate::distribution::metrics::{ImportConfig, UnrealizedCore};
|
||||
|
||||
/// Relative metrics for the Complete tier.
|
||||
#[derive(Traversable)]
|
||||
@@ -29,7 +29,7 @@ impl RelativeBase {
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
unrealized: &UnrealizedBase,
|
||||
unrealized: &UnrealizedCore,
|
||||
supply_total_sats: &impl ReadableVec<Height, Sats>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
|
||||
@@ -5,7 +5,7 @@ use vecdb::{Exit, ReadableVec, Rw, StorageMode};
|
||||
|
||||
use crate::internal::{PercentFromHeight, RatioDollarsBp16, RatioDollarsBp32, RatioDollarsBps32};
|
||||
|
||||
use crate::distribution::metrics::{ImportConfig, UnrealizedFull};
|
||||
use crate::distribution::metrics::{ImportConfig, UnrealizedBase};
|
||||
|
||||
/// Extended relative metrics for own market cap (extended && rel_to_all).
|
||||
#[derive(Traversable)]
|
||||
@@ -32,7 +32,7 @@ impl RelativeExtendedOwnMarketCap {
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
unrealized: &UnrealizedFull,
|
||||
unrealized: &UnrealizedBase,
|
||||
own_market_cap: &impl ReadableVec<Height, Dollars>,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
|
||||
@@ -5,7 +5,7 @@ use vecdb::{Exit, Rw, StorageMode};
|
||||
|
||||
use crate::internal::{PercentFromHeight, RatioDollarsBp16, RatioDollarsBps32};
|
||||
|
||||
use crate::distribution::metrics::{ImportConfig, UnrealizedFull};
|
||||
use crate::distribution::metrics::{ImportConfig, UnrealizedBase};
|
||||
|
||||
/// Extended relative metrics for own total unrealized PnL (extended only).
|
||||
#[derive(Traversable)]
|
||||
@@ -32,7 +32,7 @@ impl RelativeExtendedOwnPnl {
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
unrealized: &UnrealizedFull,
|
||||
unrealized: &UnrealizedBase,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.unrealized_profit_rel_to_own_gross_pnl
|
||||
|
||||
@@ -4,7 +4,7 @@ use brk_types::{Dollars, Height, Sats};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{Exit, ReadableVec, Rw, StorageMode};
|
||||
|
||||
use crate::distribution::metrics::{ImportConfig, RealizedBase, UnrealizedFull};
|
||||
use crate::distribution::metrics::{ImportConfig, RealizedBase, UnrealizedBase};
|
||||
|
||||
use super::{RelativeFull, RelativeExtendedOwnPnl};
|
||||
|
||||
@@ -30,7 +30,7 @@ impl RelativeForAll {
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
unrealized: &UnrealizedFull,
|
||||
unrealized: &UnrealizedBase,
|
||||
realized: &RealizedBase,
|
||||
supply_total_sats: &impl ReadableVec<Height, Sats>,
|
||||
market_cap: &impl ReadableVec<Height, Dollars>,
|
||||
|
||||
@@ -10,7 +10,7 @@ use crate::internal::{
|
||||
Bps32ToFloat, LazyFromHeight, PercentFromHeight, RatioDollarsBp16, RatioDollarsBps32,
|
||||
};
|
||||
|
||||
use crate::distribution::metrics::{ImportConfig, RealizedBase, UnrealizedFull};
|
||||
use crate::distribution::metrics::{ImportConfig, RealizedBase, UnrealizedBase};
|
||||
|
||||
use super::RelativeBase;
|
||||
|
||||
@@ -73,7 +73,7 @@ impl RelativeFull {
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
unrealized: &UnrealizedFull,
|
||||
unrealized: &UnrealizedBase,
|
||||
realized: &RealizedBase,
|
||||
supply_total_sats: &impl ReadableVec<Height, Sats>,
|
||||
market_cap: &impl ReadableVec<Height, Dollars>,
|
||||
@@ -81,7 +81,7 @@ impl RelativeFull {
|
||||
) -> Result<()> {
|
||||
self.base.compute(
|
||||
max_from,
|
||||
&unrealized.base,
|
||||
&unrealized.core,
|
||||
supply_total_sats,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
@@ -4,7 +4,7 @@ use brk_types::{Dollars, Height, Sats};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{Exit, ReadableVec, Rw, StorageMode};
|
||||
|
||||
use crate::distribution::metrics::{ImportConfig, RealizedBase, UnrealizedFull};
|
||||
use crate::distribution::metrics::{ImportConfig, RealizedBase, UnrealizedBase};
|
||||
|
||||
use super::{RelativeFull, RelativeExtendedOwnMarketCap, RelativeExtendedOwnPnl, RelativeToAll};
|
||||
|
||||
@@ -38,7 +38,7 @@ impl RelativeWithExtended {
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
unrealized: &UnrealizedFull,
|
||||
unrealized: &UnrealizedBase,
|
||||
realized: &RealizedBase,
|
||||
supply_total_sats: &impl ReadableVec<Height, Sats>,
|
||||
market_cap: &impl ReadableVec<Height, Dollars>,
|
||||
|
||||
@@ -4,7 +4,7 @@ use brk_types::{Dollars, Height, Sats};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{Exit, ReadableVec, Rw, StorageMode};
|
||||
|
||||
use crate::distribution::metrics::{ImportConfig, RealizedBase, UnrealizedFull};
|
||||
use crate::distribution::metrics::{ImportConfig, RealizedBase, UnrealizedBase};
|
||||
|
||||
use super::{RelativeFull, RelativeToAll};
|
||||
|
||||
@@ -32,7 +32,7 @@ impl RelativeWithRelToAll {
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
unrealized: &UnrealizedFull,
|
||||
unrealized: &UnrealizedBase,
|
||||
realized: &RealizedBase,
|
||||
supply_total_sats: &impl ReadableVec<Height, Sats>,
|
||||
market_cap: &impl ReadableVec<Height, Dollars>,
|
||||
|
||||
@@ -4,7 +4,7 @@ use brk_types::{Height, Sats};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{Exit, ReadableVec, Rw, StorageMode};
|
||||
|
||||
use crate::distribution::metrics::{ImportConfig, UnrealizedBase};
|
||||
use crate::distribution::metrics::{ImportConfig, UnrealizedCore};
|
||||
|
||||
use super::{RelativeBase, RelativeToAll};
|
||||
|
||||
@@ -31,7 +31,7 @@ impl RelativeBaseWithRelToAll {
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
unrealized: &UnrealizedBase,
|
||||
unrealized: &UnrealizedCore,
|
||||
supply_total_sats: &impl ReadableVec<Height, Sats>,
|
||||
all_supply_sats: &impl ReadableVec<Height, Sats>,
|
||||
exit: &Exit,
|
||||
|
||||
@@ -1,73 +1,75 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Cents, CentsSigned, Height, Indexes, Version};
|
||||
use vecdb::{AnyStoredVec, AnyVec, Exit, ReadableCloneableVec, Rw, StorageMode, WritableVec};
|
||||
use brk_types::{Cents, CentsSats, CentsSquaredSats, Height, Indexes, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{AnyStoredVec, AnyVec, BytesVec, Exit, ReadableVec, Rw, StorageMode, WritableVec};
|
||||
|
||||
use crate::{
|
||||
distribution::state::UnrealizedState,
|
||||
internal::{
|
||||
CentsSubtractToCentsSigned, FiatFromHeight, LazyFromHeight, NegCentsUnsignedToDollars,
|
||||
ValueFromHeight,
|
||||
},
|
||||
internal::FiatFromHeight,
|
||||
prices,
|
||||
};
|
||||
|
||||
use brk_types::Dollars;
|
||||
|
||||
use crate::distribution::metrics::ImportConfig;
|
||||
|
||||
/// Unrealized metrics for the Complete tier (~6 fields).
|
||||
///
|
||||
/// Excludes source-only fields (invested_capital, raw BytesVecs)
|
||||
/// and extended-only fields (pain_index, greed_index, net_sentiment).
|
||||
#[derive(Traversable)]
|
||||
use super::UnrealizedCore;
|
||||
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
pub struct UnrealizedBase<M: StorageMode = Rw> {
|
||||
pub supply_in_profit: ValueFromHeight<M>,
|
||||
pub supply_in_loss: ValueFromHeight<M>,
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
#[traversable(flatten)]
|
||||
pub core: UnrealizedCore<M>,
|
||||
|
||||
pub unrealized_profit: FiatFromHeight<Cents, M>,
|
||||
pub unrealized_loss: FiatFromHeight<Cents, M>,
|
||||
pub gross_pnl: FiatFromHeight<Cents, M>,
|
||||
|
||||
pub neg_unrealized_loss: LazyFromHeight<Dollars, Cents>,
|
||||
pub invested_capital_in_profit: FiatFromHeight<Cents, M>,
|
||||
pub invested_capital_in_loss: FiatFromHeight<Cents, M>,
|
||||
|
||||
pub net_unrealized_pnl: FiatFromHeight<CentsSigned, M>,
|
||||
pub invested_capital_in_profit_raw: M::Stored<BytesVec<Height, CentsSats>>,
|
||||
pub invested_capital_in_loss_raw: M::Stored<BytesVec<Height, CentsSats>>,
|
||||
pub investor_cap_in_profit_raw: M::Stored<BytesVec<Height, CentsSquaredSats>>,
|
||||
pub investor_cap_in_loss_raw: M::Stored<BytesVec<Height, CentsSquaredSats>>,
|
||||
}
|
||||
|
||||
impl UnrealizedBase {
|
||||
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
|
||||
let v0 = Version::ZERO;
|
||||
let supply_in_profit = cfg.import("supply_in_profit", v0)?;
|
||||
let supply_in_loss = cfg.import("supply_in_loss", v0)?;
|
||||
|
||||
let unrealized_profit = cfg.import("unrealized_profit", v0)?;
|
||||
let unrealized_loss: FiatFromHeight<Cents> = cfg.import("unrealized_loss", v0)?;
|
||||
let core = UnrealizedCore::forced_import(cfg)?;
|
||||
|
||||
let neg_unrealized_loss = LazyFromHeight::from_computed::<NegCentsUnsignedToDollars>(
|
||||
&cfg.name("neg_unrealized_loss"),
|
||||
cfg.version,
|
||||
unrealized_loss.cents.height.read_only_boxed_clone(),
|
||||
&unrealized_loss.cents,
|
||||
);
|
||||
let gross_pnl = cfg.import("unrealized_gross_pnl", v0)?;
|
||||
|
||||
let net_unrealized_pnl = cfg.import("net_unrealized_pnl", v0)?;
|
||||
let invested_capital_in_profit = cfg.import("invested_capital_in_profit", v0)?;
|
||||
let invested_capital_in_loss = cfg.import("invested_capital_in_loss", v0)?;
|
||||
|
||||
let invested_capital_in_profit_raw =
|
||||
cfg.import("invested_capital_in_profit_raw", v0)?;
|
||||
let invested_capital_in_loss_raw = cfg.import("invested_capital_in_loss_raw", v0)?;
|
||||
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)?;
|
||||
|
||||
Ok(Self {
|
||||
supply_in_profit,
|
||||
supply_in_loss,
|
||||
unrealized_profit,
|
||||
unrealized_loss,
|
||||
neg_unrealized_loss,
|
||||
net_unrealized_pnl,
|
||||
core,
|
||||
gross_pnl,
|
||||
invested_capital_in_profit,
|
||||
invested_capital_in_loss,
|
||||
invested_capital_in_profit_raw,
|
||||
invested_capital_in_loss_raw,
|
||||
investor_cap_in_profit_raw,
|
||||
investor_cap_in_loss_raw,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn min_stateful_height_len(&self) -> usize {
|
||||
self.supply_in_profit
|
||||
.sats
|
||||
.height
|
||||
.len()
|
||||
.min(self.supply_in_loss.sats.height.len())
|
||||
.min(self.unrealized_profit.cents.height.len())
|
||||
.min(self.unrealized_loss.cents.height.len())
|
||||
self.core
|
||||
.min_stateful_height_len()
|
||||
.min(self.invested_capital_in_profit.cents.height.len())
|
||||
.min(self.invested_capital_in_loss.cents.height.len())
|
||||
.min(self.invested_capital_in_profit_raw.len())
|
||||
.min(self.invested_capital_in_loss_raw.len())
|
||||
.min(self.investor_cap_in_profit_raw.len())
|
||||
.min(self.investor_cap_in_loss_raw.len())
|
||||
}
|
||||
|
||||
pub(crate) fn truncate_push(
|
||||
@@ -75,35 +77,46 @@ impl UnrealizedBase {
|
||||
height: Height,
|
||||
height_state: &UnrealizedState,
|
||||
) -> Result<()> {
|
||||
self.supply_in_profit
|
||||
.sats
|
||||
.height
|
||||
.truncate_push(height, height_state.supply_in_profit)?;
|
||||
self.supply_in_loss
|
||||
.sats
|
||||
.height
|
||||
.truncate_push(height, height_state.supply_in_loss)?;
|
||||
self.unrealized_profit
|
||||
self.core.truncate_push(height, height_state)?;
|
||||
|
||||
self.invested_capital_in_profit
|
||||
.cents
|
||||
.height
|
||||
.truncate_push(height, height_state.unrealized_profit)?;
|
||||
self.unrealized_loss
|
||||
.truncate_push(height, height_state.invested_capital_in_profit)?;
|
||||
self.invested_capital_in_loss
|
||||
.cents
|
||||
.height
|
||||
.truncate_push(height, height_state.unrealized_loss)?;
|
||||
.truncate_push(height, height_state.invested_capital_in_loss)?;
|
||||
|
||||
self.invested_capital_in_profit_raw.truncate_push(
|
||||
height,
|
||||
CentsSats::new(height_state.invested_capital_in_profit_raw),
|
||||
)?;
|
||||
self.invested_capital_in_loss_raw.truncate_push(
|
||||
height,
|
||||
CentsSats::new(height_state.invested_capital_in_loss_raw),
|
||||
)?;
|
||||
self.investor_cap_in_profit_raw.truncate_push(
|
||||
height,
|
||||
CentsSquaredSats::new(height_state.investor_cap_in_profit_raw),
|
||||
)?;
|
||||
self.investor_cap_in_loss_raw.truncate_push(
|
||||
height,
|
||||
CentsSquaredSats::new(height_state.investor_cap_in_loss_raw),
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn collect_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
|
||||
vec![
|
||||
&mut self.supply_in_profit.base.sats.height as &mut dyn AnyStoredVec,
|
||||
&mut self.supply_in_profit.base.cents.height as &mut dyn AnyStoredVec,
|
||||
&mut self.supply_in_loss.base.sats.height as &mut dyn AnyStoredVec,
|
||||
&mut self.supply_in_loss.base.cents.height as &mut dyn AnyStoredVec,
|
||||
&mut self.unrealized_profit.cents.height,
|
||||
&mut self.unrealized_loss.cents.height,
|
||||
]
|
||||
let mut vecs = self.core.collect_vecs_mut();
|
||||
vecs.push(&mut self.invested_capital_in_profit.cents.height as &mut dyn AnyStoredVec);
|
||||
vecs.push(&mut self.invested_capital_in_loss.cents.height as &mut dyn AnyStoredVec);
|
||||
vecs.push(&mut self.invested_capital_in_profit_raw as &mut dyn AnyStoredVec);
|
||||
vecs.push(&mut self.invested_capital_in_loss_raw as &mut dyn AnyStoredVec);
|
||||
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
|
||||
}
|
||||
|
||||
pub(crate) fn compute_from_stateful(
|
||||
@@ -112,29 +125,89 @@ impl UnrealizedBase {
|
||||
others: &[&Self],
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
sum_others!(self, starting_indexes, others, exit; supply_in_profit.sats.height);
|
||||
sum_others!(self, starting_indexes, others, exit; supply_in_loss.sats.height);
|
||||
sum_others!(self, starting_indexes, others, exit; unrealized_profit.cents.height);
|
||||
sum_others!(self, starting_indexes, others, exit; unrealized_loss.cents.height);
|
||||
let core_refs: Vec<&UnrealizedCore> =
|
||||
others.iter().map(|o| &o.core).collect();
|
||||
self.core
|
||||
.compute_from_stateful(starting_indexes, &core_refs, exit)?;
|
||||
|
||||
sum_others!(self, starting_indexes, others, exit; invested_capital_in_profit.cents.height);
|
||||
sum_others!(self, starting_indexes, others, exit; invested_capital_in_loss.cents.height);
|
||||
|
||||
let start = self
|
||||
.invested_capital_in_profit_raw
|
||||
.len()
|
||||
.min(self.invested_capital_in_loss_raw.len())
|
||||
.min(self.investor_cap_in_profit_raw.len())
|
||||
.min(self.investor_cap_in_loss_raw.len());
|
||||
let end = others
|
||||
.iter()
|
||||
.map(|o| o.invested_capital_in_profit_raw.len())
|
||||
.min()
|
||||
.unwrap_or(0);
|
||||
|
||||
let invested_profit_ranges: Vec<Vec<CentsSats>> = others
|
||||
.iter()
|
||||
.map(|o| {
|
||||
o.invested_capital_in_profit_raw
|
||||
.collect_range_at(start, end)
|
||||
})
|
||||
.collect();
|
||||
let invested_loss_ranges: Vec<Vec<CentsSats>> = others
|
||||
.iter()
|
||||
.map(|o| o.invested_capital_in_loss_raw.collect_range_at(start, end))
|
||||
.collect();
|
||||
let investor_profit_ranges: Vec<Vec<CentsSquaredSats>> = others
|
||||
.iter()
|
||||
.map(|o| o.investor_cap_in_profit_raw.collect_range_at(start, end))
|
||||
.collect();
|
||||
let investor_loss_ranges: Vec<Vec<CentsSquaredSats>> = others
|
||||
.iter()
|
||||
.map(|o| o.investor_cap_in_loss_raw.collect_range_at(start, end))
|
||||
.collect();
|
||||
|
||||
for i in start..end {
|
||||
let height = Height::from(i);
|
||||
let local_i = i - start;
|
||||
|
||||
let mut sum_invested_profit = CentsSats::ZERO;
|
||||
let mut sum_invested_loss = CentsSats::ZERO;
|
||||
let mut sum_investor_profit = CentsSquaredSats::ZERO;
|
||||
let mut sum_investor_loss = CentsSquaredSats::ZERO;
|
||||
|
||||
for idx in 0..others.len() {
|
||||
sum_invested_profit += invested_profit_ranges[idx][local_i];
|
||||
sum_invested_loss += invested_loss_ranges[idx][local_i];
|
||||
sum_investor_profit += investor_profit_ranges[idx][local_i];
|
||||
sum_investor_loss += investor_loss_ranges[idx][local_i];
|
||||
}
|
||||
|
||||
self.invested_capital_in_profit_raw
|
||||
.truncate_push(height, sum_invested_profit)?;
|
||||
self.invested_capital_in_loss_raw
|
||||
.truncate_push(height, sum_invested_loss)?;
|
||||
self.investor_cap_in_profit_raw
|
||||
.truncate_push(height, sum_investor_profit)?;
|
||||
self.investor_cap_in_loss_raw
|
||||
.truncate_push(height, sum_investor_loss)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Compute derived metrics from stored values.
|
||||
pub(crate) fn compute_rest(
|
||||
&mut self,
|
||||
_prices: &prices::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.net_unrealized_pnl
|
||||
.cents
|
||||
.height
|
||||
.compute_binary::<Cents, Cents, CentsSubtractToCentsSigned>(
|
||||
starting_indexes.height,
|
||||
&self.unrealized_profit.cents.height,
|
||||
&self.unrealized_loss.cents.height,
|
||||
exit,
|
||||
)?;
|
||||
self.core.compute_rest(starting_indexes, exit)?;
|
||||
|
||||
self.gross_pnl.cents.height.compute_add(
|
||||
starting_indexes.height,
|
||||
&self.core.unrealized_profit.cents.height,
|
||||
&self.core.unrealized_loss.cents.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
137
crates/brk_computer/src/distribution/metrics/unrealized/core.rs
Normal file
137
crates/brk_computer/src/distribution/metrics/unrealized/core.rs
Normal file
@@ -0,0 +1,137 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Cents, CentsSigned, Height, Indexes, Version};
|
||||
use vecdb::{AnyStoredVec, AnyVec, Exit, ReadableCloneableVec, Rw, StorageMode, WritableVec};
|
||||
|
||||
use crate::{
|
||||
distribution::state::UnrealizedState,
|
||||
internal::{
|
||||
CentsSubtractToCentsSigned, FiatFromHeight, LazyFromHeight, NegCentsUnsignedToDollars,
|
||||
ValueFromHeight,
|
||||
},
|
||||
};
|
||||
|
||||
use brk_types::Dollars;
|
||||
|
||||
use crate::distribution::metrics::ImportConfig;
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct UnrealizedCore<M: StorageMode = Rw> {
|
||||
pub supply_in_profit: ValueFromHeight<M>,
|
||||
pub supply_in_loss: ValueFromHeight<M>,
|
||||
|
||||
pub unrealized_profit: FiatFromHeight<Cents, M>,
|
||||
pub unrealized_loss: FiatFromHeight<Cents, M>,
|
||||
|
||||
pub neg_unrealized_loss: LazyFromHeight<Dollars, Cents>,
|
||||
|
||||
pub net_unrealized_pnl: FiatFromHeight<CentsSigned, M>,
|
||||
}
|
||||
|
||||
impl UnrealizedCore {
|
||||
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
|
||||
let v0 = Version::ZERO;
|
||||
let supply_in_profit = cfg.import("supply_in_profit", v0)?;
|
||||
let supply_in_loss = cfg.import("supply_in_loss", v0)?;
|
||||
|
||||
let unrealized_profit = cfg.import("unrealized_profit", v0)?;
|
||||
let unrealized_loss: FiatFromHeight<Cents> = cfg.import("unrealized_loss", v0)?;
|
||||
|
||||
let neg_unrealized_loss = LazyFromHeight::from_computed::<NegCentsUnsignedToDollars>(
|
||||
&cfg.name("neg_unrealized_loss"),
|
||||
cfg.version,
|
||||
unrealized_loss.cents.height.read_only_boxed_clone(),
|
||||
&unrealized_loss.cents,
|
||||
);
|
||||
|
||||
let net_unrealized_pnl = cfg.import("net_unrealized_pnl", v0)?;
|
||||
|
||||
Ok(Self {
|
||||
supply_in_profit,
|
||||
supply_in_loss,
|
||||
unrealized_profit,
|
||||
unrealized_loss,
|
||||
neg_unrealized_loss,
|
||||
net_unrealized_pnl,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn min_stateful_height_len(&self) -> usize {
|
||||
self.supply_in_profit
|
||||
.sats
|
||||
.height
|
||||
.len()
|
||||
.min(self.supply_in_loss.sats.height.len())
|
||||
.min(self.unrealized_profit.cents.height.len())
|
||||
.min(self.unrealized_loss.cents.height.len())
|
||||
}
|
||||
|
||||
pub(crate) fn truncate_push(
|
||||
&mut self,
|
||||
height: Height,
|
||||
height_state: &UnrealizedState,
|
||||
) -> Result<()> {
|
||||
self.supply_in_profit
|
||||
.sats
|
||||
.height
|
||||
.truncate_push(height, height_state.supply_in_profit)?;
|
||||
self.supply_in_loss
|
||||
.sats
|
||||
.height
|
||||
.truncate_push(height, height_state.supply_in_loss)?;
|
||||
self.unrealized_profit
|
||||
.cents
|
||||
.height
|
||||
.truncate_push(height, height_state.unrealized_profit)?;
|
||||
self.unrealized_loss
|
||||
.cents
|
||||
.height
|
||||
.truncate_push(height, height_state.unrealized_loss)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn collect_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
|
||||
vec![
|
||||
&mut self.supply_in_profit.base.sats.height as &mut dyn AnyStoredVec,
|
||||
&mut self.supply_in_profit.base.cents.height as &mut dyn AnyStoredVec,
|
||||
&mut self.supply_in_loss.base.sats.height as &mut dyn AnyStoredVec,
|
||||
&mut self.supply_in_loss.base.cents.height as &mut dyn AnyStoredVec,
|
||||
&mut self.unrealized_profit.cents.height,
|
||||
&mut self.unrealized_loss.cents.height,
|
||||
]
|
||||
}
|
||||
|
||||
pub(crate) fn compute_from_stateful(
|
||||
&mut self,
|
||||
starting_indexes: &Indexes,
|
||||
others: &[&Self],
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
sum_others!(self, starting_indexes, others, exit; supply_in_profit.sats.height);
|
||||
sum_others!(self, starting_indexes, others, exit; supply_in_loss.sats.height);
|
||||
sum_others!(self, starting_indexes, others, exit; unrealized_profit.cents.height);
|
||||
sum_others!(self, starting_indexes, others, exit; unrealized_loss.cents.height);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Compute derived metrics from stored values.
|
||||
pub(crate) fn compute_rest(
|
||||
&mut self,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.net_unrealized_pnl
|
||||
.cents
|
||||
.height
|
||||
.compute_binary::<Cents, Cents, CentsSubtractToCentsSigned>(
|
||||
starting_indexes.height,
|
||||
&self.unrealized_profit.cents.height,
|
||||
&self.unrealized_loss.cents.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,42 +1,23 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Cents, CentsSats, CentsSigned, CentsSquaredSats, Height, Indexes, Version};
|
||||
use brk_types::{Cents, CentsSigned, Indexes, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{AnyStoredVec, AnyVec, BytesVec, Exit, ReadableVec, Rw, StorageMode, WritableVec};
|
||||
use vecdb::{Exit, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
distribution::state::UnrealizedState,
|
||||
internal::{CentsSubtractToCentsSigned, FiatFromHeight},
|
||||
prices,
|
||||
};
|
||||
use crate::internal::{CentsSubtractToCentsSigned, FiatFromHeight};
|
||||
use crate::prices;
|
||||
|
||||
use crate::distribution::metrics::ImportConfig;
|
||||
|
||||
use super::UnrealizedBase;
|
||||
|
||||
/// Full unrealized metrics (Source/Extended tier).
|
||||
///
|
||||
/// Contains all Complete-tier fields (via Deref to UnrealizedBase) plus:
|
||||
/// - Source-only: invested_capital, raw BytesVecs
|
||||
/// - Extended-only: pain_index, greed_index, net_sentiment
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
pub struct UnrealizedFull<M: StorageMode = Rw> {
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
#[traversable(flatten)]
|
||||
pub base: UnrealizedBase<M>,
|
||||
pub inner: UnrealizedBase<M>,
|
||||
|
||||
pub gross_pnl: FiatFromHeight<Cents, M>,
|
||||
|
||||
pub invested_capital_in_profit: FiatFromHeight<Cents, M>,
|
||||
pub invested_capital_in_loss: FiatFromHeight<Cents, M>,
|
||||
|
||||
pub invested_capital_in_profit_raw: M::Stored<BytesVec<Height, CentsSats>>,
|
||||
pub invested_capital_in_loss_raw: M::Stored<BytesVec<Height, CentsSats>>,
|
||||
pub investor_cap_in_profit_raw: M::Stored<BytesVec<Height, CentsSquaredSats>>,
|
||||
pub investor_cap_in_loss_raw: M::Stored<BytesVec<Height, CentsSquaredSats>>,
|
||||
|
||||
// --- Extended-only fields ---
|
||||
pub pain_index: FiatFromHeight<Cents, M>,
|
||||
pub greed_index: FiatFromHeight<Cents, M>,
|
||||
pub net_sentiment: FiatFromHeight<CentsSigned, M>,
|
||||
@@ -44,198 +25,32 @@ pub struct UnrealizedFull<M: StorageMode = Rw> {
|
||||
|
||||
impl UnrealizedFull {
|
||||
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
|
||||
let v0 = Version::ZERO;
|
||||
let inner = UnrealizedBase::forced_import(cfg)?;
|
||||
|
||||
let base = UnrealizedBase::forced_import(cfg)?;
|
||||
|
||||
let gross_pnl = cfg.import("unrealized_gross_pnl", v0)?;
|
||||
|
||||
let invested_capital_in_profit = cfg.import("invested_capital_in_profit", v0)?;
|
||||
let invested_capital_in_loss = cfg.import("invested_capital_in_loss", v0)?;
|
||||
|
||||
let invested_capital_in_profit_raw =
|
||||
cfg.import("invested_capital_in_profit_raw", v0)?;
|
||||
let invested_capital_in_loss_raw = cfg.import("invested_capital_in_loss_raw", v0)?;
|
||||
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 pain_index = cfg.import("pain_index", v0)?;
|
||||
let greed_index = cfg.import("greed_index", v0)?;
|
||||
let pain_index = cfg.import("pain_index", Version::ZERO)?;
|
||||
let greed_index = cfg.import("greed_index", Version::ZERO)?;
|
||||
let net_sentiment = cfg.import("net_sentiment", Version::ONE)?;
|
||||
|
||||
Ok(Self {
|
||||
base,
|
||||
gross_pnl,
|
||||
invested_capital_in_profit,
|
||||
invested_capital_in_loss,
|
||||
invested_capital_in_profit_raw,
|
||||
invested_capital_in_loss_raw,
|
||||
investor_cap_in_profit_raw,
|
||||
investor_cap_in_loss_raw,
|
||||
inner,
|
||||
pain_index,
|
||||
greed_index,
|
||||
net_sentiment,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn min_stateful_height_len(&self) -> usize {
|
||||
self.base
|
||||
.min_stateful_height_len()
|
||||
.min(self.invested_capital_in_profit.cents.height.len())
|
||||
.min(self.invested_capital_in_loss.cents.height.len())
|
||||
.min(self.invested_capital_in_profit_raw.len())
|
||||
.min(self.invested_capital_in_loss_raw.len())
|
||||
.min(self.investor_cap_in_profit_raw.len())
|
||||
.min(self.investor_cap_in_loss_raw.len())
|
||||
}
|
||||
|
||||
pub(crate) fn truncate_push(
|
||||
&mut self,
|
||||
height: Height,
|
||||
height_state: &UnrealizedState,
|
||||
) -> Result<()> {
|
||||
self.base.truncate_push(height, height_state)?;
|
||||
|
||||
self.invested_capital_in_profit
|
||||
.cents
|
||||
.height
|
||||
.truncate_push(height, height_state.invested_capital_in_profit)?;
|
||||
self.invested_capital_in_loss
|
||||
.cents
|
||||
.height
|
||||
.truncate_push(height, height_state.invested_capital_in_loss)?;
|
||||
|
||||
self.invested_capital_in_profit_raw.truncate_push(
|
||||
height,
|
||||
CentsSats::new(height_state.invested_capital_in_profit_raw),
|
||||
)?;
|
||||
self.invested_capital_in_loss_raw.truncate_push(
|
||||
height,
|
||||
CentsSats::new(height_state.invested_capital_in_loss_raw),
|
||||
)?;
|
||||
self.investor_cap_in_profit_raw.truncate_push(
|
||||
height,
|
||||
CentsSquaredSats::new(height_state.investor_cap_in_profit_raw),
|
||||
)?;
|
||||
self.investor_cap_in_loss_raw.truncate_push(
|
||||
height,
|
||||
CentsSquaredSats::new(height_state.investor_cap_in_loss_raw),
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn collect_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
|
||||
let mut vecs = self.base.collect_vecs_mut();
|
||||
vecs.push(&mut self.invested_capital_in_profit.cents.height as &mut dyn AnyStoredVec);
|
||||
vecs.push(&mut self.invested_capital_in_loss.cents.height as &mut dyn AnyStoredVec);
|
||||
vecs.push(&mut self.invested_capital_in_profit_raw as &mut dyn AnyStoredVec);
|
||||
vecs.push(&mut self.invested_capital_in_loss_raw as &mut dyn AnyStoredVec);
|
||||
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
|
||||
}
|
||||
|
||||
pub(crate) fn compute_from_stateful(
|
||||
&mut self,
|
||||
starting_indexes: &Indexes,
|
||||
others: &[&Self],
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
// Delegate Complete-tier aggregation
|
||||
let base_refs: Vec<&UnrealizedBase> =
|
||||
others.iter().map(|o| &o.base).collect();
|
||||
self.base
|
||||
.compute_from_stateful(starting_indexes, &base_refs, exit)?;
|
||||
|
||||
// Source-only: invested_capital
|
||||
sum_others!(self, starting_indexes, others, exit; invested_capital_in_profit.cents.height);
|
||||
sum_others!(self, starting_indexes, others, exit; invested_capital_in_loss.cents.height);
|
||||
|
||||
// Source-only: raw BytesVec aggregation
|
||||
let start = self
|
||||
.invested_capital_in_profit_raw
|
||||
.len()
|
||||
.min(self.invested_capital_in_loss_raw.len())
|
||||
.min(self.investor_cap_in_profit_raw.len())
|
||||
.min(self.investor_cap_in_loss_raw.len());
|
||||
let end = others
|
||||
.iter()
|
||||
.map(|o| o.invested_capital_in_profit_raw.len())
|
||||
.min()
|
||||
.unwrap_or(0);
|
||||
|
||||
// Pre-collect all cohort data to avoid per-element BytesVec reads in nested loop
|
||||
let invested_profit_ranges: Vec<Vec<CentsSats>> = others
|
||||
.iter()
|
||||
.map(|o| {
|
||||
o.invested_capital_in_profit_raw
|
||||
.collect_range_at(start, end)
|
||||
})
|
||||
.collect();
|
||||
let invested_loss_ranges: Vec<Vec<CentsSats>> = others
|
||||
.iter()
|
||||
.map(|o| o.invested_capital_in_loss_raw.collect_range_at(start, end))
|
||||
.collect();
|
||||
let investor_profit_ranges: Vec<Vec<CentsSquaredSats>> = others
|
||||
.iter()
|
||||
.map(|o| o.investor_cap_in_profit_raw.collect_range_at(start, end))
|
||||
.collect();
|
||||
let investor_loss_ranges: Vec<Vec<CentsSquaredSats>> = others
|
||||
.iter()
|
||||
.map(|o| o.investor_cap_in_loss_raw.collect_range_at(start, end))
|
||||
.collect();
|
||||
|
||||
for i in start..end {
|
||||
let height = Height::from(i);
|
||||
let local_i = i - start;
|
||||
|
||||
let mut sum_invested_profit = CentsSats::ZERO;
|
||||
let mut sum_invested_loss = CentsSats::ZERO;
|
||||
let mut sum_investor_profit = CentsSquaredSats::ZERO;
|
||||
let mut sum_investor_loss = CentsSquaredSats::ZERO;
|
||||
|
||||
for idx in 0..others.len() {
|
||||
sum_invested_profit += invested_profit_ranges[idx][local_i];
|
||||
sum_invested_loss += invested_loss_ranges[idx][local_i];
|
||||
sum_investor_profit += investor_profit_ranges[idx][local_i];
|
||||
sum_investor_loss += investor_loss_ranges[idx][local_i];
|
||||
}
|
||||
|
||||
self.invested_capital_in_profit_raw
|
||||
.truncate_push(height, sum_invested_profit)?;
|
||||
self.invested_capital_in_loss_raw
|
||||
.truncate_push(height, sum_invested_loss)?;
|
||||
self.investor_cap_in_profit_raw
|
||||
.truncate_push(height, sum_investor_profit)?;
|
||||
self.investor_cap_in_loss_raw
|
||||
.truncate_push(height, sum_investor_loss)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Compute derived metrics from stored values + price.
|
||||
pub(crate) fn compute_rest(
|
||||
&mut self,
|
||||
prices: &prices::Vecs,
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.base.compute_rest(starting_indexes, exit)?;
|
||||
self.inner.compute_rest(prices, starting_indexes, exit)?;
|
||||
|
||||
self.gross_pnl.cents.height.compute_add(
|
||||
starting_indexes.height,
|
||||
&self.base.unrealized_profit.cents.height,
|
||||
&self.base.unrealized_loss.cents.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// Pain index (investor_price_of_losers - spot)
|
||||
self.pain_index.cents.height.compute_transform3(
|
||||
starting_indexes.height,
|
||||
&self.investor_cap_in_loss_raw,
|
||||
&self.invested_capital_in_loss_raw,
|
||||
&self.inner.investor_cap_in_loss_raw,
|
||||
&self.inner.invested_capital_in_loss_raw,
|
||||
&prices.price.cents.height,
|
||||
|(h, investor_cap, invested_cap, spot, ..)| {
|
||||
if invested_cap.inner() == 0 {
|
||||
@@ -248,11 +63,10 @@ impl UnrealizedFull {
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// Extended-only: Greed index (spot - investor_price_of_winners)
|
||||
self.greed_index.cents.height.compute_transform3(
|
||||
starting_indexes.height,
|
||||
&self.investor_cap_in_profit_raw,
|
||||
&self.invested_capital_in_profit_raw,
|
||||
&self.inner.investor_cap_in_profit_raw,
|
||||
&self.inner.invested_capital_in_profit_raw,
|
||||
&prices.price.cents.height,
|
||||
|(h, investor_cap, invested_cap, spot, ..)| {
|
||||
if invested_cap.inner() == 0 {
|
||||
@@ -268,7 +82,6 @@ impl UnrealizedFull {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Compute net_sentiment.height for separate cohorts (greed - pain).
|
||||
pub(crate) fn compute_net_sentiment_height(
|
||||
&mut self,
|
||||
starting_indexes: &Indexes,
|
||||
|
||||
@@ -1,5 +1,52 @@
|
||||
mod base;
|
||||
mod core;
|
||||
mod full;
|
||||
|
||||
pub use base::UnrealizedBase;
|
||||
pub use self::core::UnrealizedCore;
|
||||
pub use full::UnrealizedFull;
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_types::{Height, Indexes};
|
||||
use vecdb::Exit;
|
||||
|
||||
use crate::{distribution::state::UnrealizedState, prices};
|
||||
|
||||
pub trait UnrealizedLike: Send + Sync {
|
||||
fn as_base(&self) -> &UnrealizedBase;
|
||||
fn as_base_mut(&mut self) -> &mut UnrealizedBase;
|
||||
fn min_stateful_height_len(&self) -> usize;
|
||||
fn truncate_push(&mut self, height: Height, state: &UnrealizedState) -> Result<()>;
|
||||
fn compute_rest(&mut self, prices: &prices::Vecs, starting_indexes: &Indexes, exit: &Exit) -> Result<()>;
|
||||
fn compute_net_sentiment_height(&mut self, starting_indexes: &Indexes, exit: &Exit) -> Result<()>;
|
||||
}
|
||||
|
||||
impl UnrealizedLike for UnrealizedBase {
|
||||
fn as_base(&self) -> &UnrealizedBase { self }
|
||||
fn as_base_mut(&mut self) -> &mut UnrealizedBase { self }
|
||||
fn min_stateful_height_len(&self) -> usize { self.min_stateful_height_len() }
|
||||
fn truncate_push(&mut self, height: Height, state: &UnrealizedState) -> Result<()> {
|
||||
self.truncate_push(height, state)
|
||||
}
|
||||
fn compute_rest(&mut self, prices: &prices::Vecs, starting_indexes: &Indexes, exit: &Exit) -> Result<()> {
|
||||
self.compute_rest(prices, starting_indexes, exit)
|
||||
}
|
||||
fn compute_net_sentiment_height(&mut self, _starting_indexes: &Indexes, _exit: &Exit) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl UnrealizedLike for UnrealizedFull {
|
||||
fn as_base(&self) -> &UnrealizedBase { &self.inner }
|
||||
fn as_base_mut(&mut self) -> &mut UnrealizedBase { &mut self.inner }
|
||||
fn min_stateful_height_len(&self) -> usize { self.inner.min_stateful_height_len() }
|
||||
fn truncate_push(&mut self, height: Height, state: &UnrealizedState) -> Result<()> {
|
||||
self.inner.truncate_push(height, state)
|
||||
}
|
||||
fn compute_rest(&mut self, prices: &prices::Vecs, starting_indexes: &Indexes, exit: &Exit) -> Result<()> {
|
||||
self.compute_rest(prices, starting_indexes, exit)
|
||||
}
|
||||
fn compute_net_sentiment_height(&mut self, starting_indexes: &Indexes, exit: &Exit) -> Result<()> {
|
||||
self.compute_net_sentiment_height(starting_indexes, exit)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ use crate::{
|
||||
use super::{
|
||||
AddressCohorts, AddressesDataVecs, AnyAddressIndexesVecs, RangeMap, UTXOCohorts,
|
||||
address::{
|
||||
AddrCountsVecs, AddressActivityVecs, GrowthRateVecs, NewAddrCountVecs, TotalAddrCountVecs,
|
||||
AddrCountsVecs, AddressActivityVecs, DeltaVecs, NewAddrCountVecs, TotalAddrCountVecs,
|
||||
},
|
||||
compute::aggregates,
|
||||
};
|
||||
@@ -57,8 +57,8 @@ pub struct Vecs<M: StorageMode = Rw> {
|
||||
pub total_addr_count: TotalAddrCountVecs<M>,
|
||||
/// New addresses per block (delta of total) - stored height + cumulative + rolling, global + per-type
|
||||
pub new_addr_count: NewAddrCountVecs<M>,
|
||||
/// Growth rate (new / addr_count) - stored ratio with distribution stats, global + per-type
|
||||
pub growth_rate: GrowthRateVecs<M>,
|
||||
/// Windowed change + growth rate for addr_count, global + per-type
|
||||
pub delta: DeltaVecs<M>,
|
||||
|
||||
pub fundedaddressindex:
|
||||
LazyVecFrom1<FundedAddressIndex, FundedAddressIndex, FundedAddressIndex, FundedAddressData>,
|
||||
@@ -141,7 +141,7 @@ impl Vecs {
|
||||
let new_addr_count = NewAddrCountVecs::forced_import(&db, version, indexes)?;
|
||||
|
||||
// Growth rate: new / addr_count (global + per-type)
|
||||
let growth_rate = GrowthRateVecs::forced_import(&db, version, indexes)?;
|
||||
let delta = DeltaVecs::forced_import(&db, version, indexes)?;
|
||||
|
||||
let this = Self {
|
||||
supply_state: BytesVec::forced_import_with(
|
||||
@@ -154,7 +154,7 @@ impl Vecs {
|
||||
address_activity,
|
||||
total_addr_count,
|
||||
new_addr_count,
|
||||
growth_rate,
|
||||
delta,
|
||||
|
||||
utxo_cohorts,
|
||||
address_cohorts,
|
||||
@@ -400,11 +400,11 @@ impl Vecs {
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// 6b. Compute address count day1 vecs (by addresstype + all)
|
||||
// 6b. Compute address count sum (by addresstype → all)
|
||||
self.addr_count
|
||||
.compute_rest(blocks, starting_indexes, exit)?;
|
||||
.compute_rest(starting_indexes, exit)?;
|
||||
self.empty_addr_count
|
||||
.compute_rest(blocks, starting_indexes, exit)?;
|
||||
.compute_rest(starting_indexes, exit)?;
|
||||
|
||||
// 6c. Compute total_addr_count = addr_count + empty_addr_count
|
||||
self.total_addr_count.compute(
|
||||
@@ -425,11 +425,9 @@ impl Vecs {
|
||||
exit,
|
||||
)?;
|
||||
|
||||
// 6e. Compute growth_rate = new_addr_count / addr_count
|
||||
self.growth_rate.compute(
|
||||
self.delta.compute(
|
||||
starting_indexes.height,
|
||||
&window_starts,
|
||||
&self.new_addr_count,
|
||||
&self.addr_count,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
107
crates/brk_computer/src/internal/from_height/computed/delta.rs
Normal file
107
crates/brk_computer/src/internal/from_height/computed/delta.rs
Normal file
@@ -0,0 +1,107 @@
|
||||
//! RollingDelta - raw change + growth rate (%) across 4 time windows.
|
||||
//!
|
||||
//! For a monotonic source (e.g., cumulative address count):
|
||||
//! - `change._24h` = count_now - count_24h_ago
|
||||
//! - `rate._24h` = (count_now - count_24h_ago) / count_24h_ago in BPS
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{BasisPoints16, Height, Version};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{Database, Exit, ReadableVec, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{NumericValue, PercentRollingWindows, RollingWindows, WindowStarts},
|
||||
};
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct RollingDelta<S, C = S, M: StorageMode = Rw>
|
||||
where
|
||||
S: NumericValue + JsonSchema,
|
||||
C: NumericValue + JsonSchema,
|
||||
{
|
||||
pub change: RollingWindows<C, M>,
|
||||
pub rate: PercentRollingWindows<BasisPoints16, M>,
|
||||
_phantom: std::marker::PhantomData<S>,
|
||||
}
|
||||
|
||||
impl<S, C> RollingDelta<S, C>
|
||||
where
|
||||
S: NumericValue + JsonSchema,
|
||||
C: NumericValue + JsonSchema,
|
||||
{
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
change: RollingWindows::forced_import(
|
||||
db,
|
||||
&format!("{name}_change"),
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
rate: PercentRollingWindows::forced_import(
|
||||
db,
|
||||
&format!("{name}_rate"),
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
_phantom: std::marker::PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
windows: &WindowStarts<'_>,
|
||||
source: &impl ReadableVec<Height, S>,
|
||||
exit: &Exit,
|
||||
) -> Result<()>
|
||||
where
|
||||
S: Default,
|
||||
{
|
||||
// Step 1: change = current - ago
|
||||
for (change_w, starts) in self.change.0.as_mut_array().into_iter().zip(windows.as_array())
|
||||
{
|
||||
change_w.height.compute_transform(
|
||||
max_from,
|
||||
*starts,
|
||||
|(h, ago_h, ..)| {
|
||||
let current: f64 = source.collect_one(h).unwrap_or_default().into();
|
||||
let ago: f64 = source.collect_one(ago_h).unwrap_or_default().into();
|
||||
(h, C::from(current - ago))
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
|
||||
// Step 2: rate = change / ago = change / (current - change)
|
||||
for (growth_w, change_w) in self
|
||||
.rate
|
||||
.0
|
||||
.as_mut_array()
|
||||
.into_iter()
|
||||
.zip(self.change.0.as_array())
|
||||
{
|
||||
growth_w.bps.height.compute_transform2(
|
||||
max_from,
|
||||
source,
|
||||
&change_w.height,
|
||||
|(h, current, change, ..)| {
|
||||
let current_f: f64 = current.into();
|
||||
let change_f: f64 = change.into();
|
||||
let ago = current_f - change_f;
|
||||
let rate = if ago == 0.0 { 0.0 } else { change_f / ago };
|
||||
(h, BasisPoints16::from(rate))
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
//! ComputedFromHeight using Distribution aggregation (no sum/cumulative).
|
||||
//!
|
||||
//! Stored height data + LazyAggVec index views + rolling distribution windows.
|
||||
//! Use for block-based metrics where sum/cumulative would be misleading
|
||||
//! (e.g., activity counts that can't be deduplicated across blocks).
|
||||
|
||||
use brk_error::Result;
|
||||
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Version};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{Database, EagerVec, Exit, ImportableVec, PcoVec, Rw, StorageMode};
|
||||
|
||||
use crate::indexes;
|
||||
|
||||
use crate::internal::{ComputedVecValue, NumericValue, RollingDistribution, WindowStarts};
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct ComputedFromHeightDistribution<T, M: StorageMode = Rw>
|
||||
where
|
||||
T: ComputedVecValue + PartialOrd + JsonSchema,
|
||||
{
|
||||
pub height: M::Stored<EagerVec<PcoVec<Height, T>>>,
|
||||
#[traversable(flatten)]
|
||||
pub rolling: RollingDistribution<T, M>,
|
||||
}
|
||||
|
||||
impl<T> ComputedFromHeightDistribution<T>
|
||||
where
|
||||
T: NumericValue + JsonSchema,
|
||||
{
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
let height: EagerVec<PcoVec<Height, T>> = EagerVec::forced_import(db, name, version)?;
|
||||
let rolling = RollingDistribution::forced_import(db, name, version, indexes)?;
|
||||
|
||||
Ok(Self { height, rolling })
|
||||
}
|
||||
|
||||
/// Compute height data via closure, then rolling distribution.
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
windows: &WindowStarts<'_>,
|
||||
exit: &Exit,
|
||||
compute_height: impl FnOnce(&mut EagerVec<PcoVec<Height, T>>) -> Result<()>,
|
||||
) -> Result<()>
|
||||
where
|
||||
T: Copy + Ord + From<f64> + Default,
|
||||
f64: From<T>,
|
||||
{
|
||||
compute_height(&mut self.height)?;
|
||||
self.compute_rest(max_from, windows, exit)
|
||||
}
|
||||
|
||||
/// Compute rolling distribution from already-populated height data.
|
||||
pub(crate) fn compute_rest(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
windows: &WindowStarts<'_>,
|
||||
exit: &Exit,
|
||||
) -> Result<()>
|
||||
where
|
||||
T: Copy + Ord + From<f64> + Default,
|
||||
f64: From<T>,
|
||||
{
|
||||
self.rolling
|
||||
.compute_distribution(max_from, windows, &self.height, exit)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
mod aggregated;
|
||||
mod cumulative;
|
||||
mod cumulative_sum;
|
||||
mod distribution;
|
||||
mod full;
|
||||
mod delta;
|
||||
mod rolling_average;
|
||||
|
||||
pub use aggregated::*;
|
||||
pub use cumulative::*;
|
||||
pub use cumulative_sum::*;
|
||||
pub use distribution::*;
|
||||
pub use full::*;
|
||||
pub use delta::*;
|
||||
pub use rolling_average::*;
|
||||
|
||||
@@ -37,7 +37,7 @@ where
|
||||
) -> Result<Self> {
|
||||
let height: EagerVec<PcoVec<Height, T>> = EagerVec::forced_import(db, name, version)?;
|
||||
let average =
|
||||
RollingWindows::forced_import(db, &format!("{name}_average"), version, indexes)?;
|
||||
RollingWindows::forced_import(db, &format!("{name}_average"), version + Version::ONE, indexes)?;
|
||||
|
||||
Ok(Self { height, average })
|
||||
}
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, StoredF32, Version};
|
||||
use vecdb::{Database, EagerVec, Exit, PcoVec, ReadableCloneableVec, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{BpsType, WindowStarts},
|
||||
};
|
||||
|
||||
use crate::internal::{ComputedFromHeightDistribution, LazyFromHeight};
|
||||
|
||||
/// Like PercentFromHeight but with rolling distribution stats on the bps data.
|
||||
#[derive(Traversable)]
|
||||
pub struct PercentFromHeightDistribution<B: BpsType, M: StorageMode = Rw> {
|
||||
pub bps: ComputedFromHeightDistribution<B, M>,
|
||||
pub ratio: LazyFromHeight<StoredF32, B>,
|
||||
pub percent: LazyFromHeight<StoredF32, B>,
|
||||
}
|
||||
|
||||
impl<B: BpsType> PercentFromHeightDistribution<B> {
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
let bps = ComputedFromHeightDistribution::forced_import(
|
||||
db,
|
||||
&format!("{name}_bps"),
|
||||
version,
|
||||
indexes,
|
||||
)?;
|
||||
|
||||
let ratio = LazyFromHeight::from_height_source::<B::ToRatio>(
|
||||
&format!("{name}_ratio"),
|
||||
version,
|
||||
bps.height.read_only_boxed_clone(),
|
||||
indexes,
|
||||
);
|
||||
|
||||
let percent = LazyFromHeight::from_height_source::<B::ToPercent>(
|
||||
name,
|
||||
version,
|
||||
bps.height.read_only_boxed_clone(),
|
||||
indexes,
|
||||
);
|
||||
|
||||
Ok(Self {
|
||||
bps,
|
||||
ratio,
|
||||
percent,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
windows: &WindowStarts<'_>,
|
||||
exit: &Exit,
|
||||
compute_height: impl FnOnce(&mut EagerVec<PcoVec<Height, B>>) -> Result<()>,
|
||||
) -> Result<()>
|
||||
where
|
||||
B: Copy + Ord + From<f64> + Default,
|
||||
f64: From<B>,
|
||||
{
|
||||
self.bps.compute(max_from, windows, exit, compute_height)
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
mod base;
|
||||
mod distribution;
|
||||
mod rolling_average;
|
||||
|
||||
pub use base::*;
|
||||
pub use distribution::*;
|
||||
pub use rolling_average::*;
|
||||
|
||||
Reference in New Issue
Block a user