mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-05-01 01:50:00 -07:00
global: snapshot
This commit is contained in:
@@ -8,7 +8,7 @@ use vecdb::{
|
||||
AnyStoredVec, AnyVec, Database, EagerVec, Exit, GenericStoredVec, PcoVec, TypedVecIterator,
|
||||
};
|
||||
|
||||
use crate::{ComputeIndexes, indexes, internal::ComputedBlockLast};
|
||||
use crate::{ComputeIndexes, indexes, internal::ComputedFromHeightLast};
|
||||
|
||||
/// Address count per address type (runtime state).
|
||||
#[derive(Debug, Default, Deref, DerefMut)]
|
||||
@@ -78,11 +78,11 @@ impl From<(&AddressTypeToAddrCountVecs, Height)> for AddressTypeToAddressCount {
|
||||
|
||||
/// Address count per address type, with height + derived indexes.
|
||||
#[derive(Clone, Deref, DerefMut, Traversable)]
|
||||
pub struct AddressTypeToAddrCountVecs(ByAddressType<ComputedBlockLast<StoredU64>>);
|
||||
pub struct AddressTypeToAddrCountVecs(ByAddressType<ComputedFromHeightLast<StoredU64>>);
|
||||
|
||||
impl From<ByAddressType<ComputedBlockLast<StoredU64>>> for AddressTypeToAddrCountVecs {
|
||||
impl From<ByAddressType<ComputedFromHeightLast<StoredU64>>> for AddressTypeToAddrCountVecs {
|
||||
#[inline]
|
||||
fn from(value: ByAddressType<ComputedBlockLast<StoredU64>>) -> Self {
|
||||
fn from(value: ByAddressType<ComputedFromHeightLast<StoredU64>>) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
@@ -95,8 +95,8 @@ impl AddressTypeToAddrCountVecs {
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
Ok(Self::from(
|
||||
ByAddressType::<ComputedBlockLast<StoredU64>>::new_with_name(|type_name| {
|
||||
ComputedBlockLast::forced_import(
|
||||
ByAddressType::<ComputedFromHeightLast<StoredU64>>::new_with_name(|type_name| {
|
||||
ComputedFromHeightLast::forced_import(
|
||||
db,
|
||||
&format!("{type_name}_{name}"),
|
||||
version,
|
||||
@@ -224,7 +224,7 @@ impl AddressTypeToAddrCountVecs {
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct AddrCountVecs {
|
||||
pub all: ComputedBlockLast<StoredU64>,
|
||||
pub all: ComputedFromHeightLast<StoredU64>,
|
||||
#[traversable(flatten)]
|
||||
pub by_addresstype: AddressTypeToAddrCountVecs,
|
||||
}
|
||||
@@ -237,7 +237,7 @@ impl AddrCountVecs {
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
all: ComputedBlockLast::forced_import(db, name, version, indexes)?,
|
||||
all: ComputedFromHeightLast::forced_import(db, name, version, indexes)?,
|
||||
by_addresstype: AddressTypeToAddrCountVecs::forced_import(db, name, version, indexes)?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
use std::thread;
|
||||
|
||||
use brk_cohort::ByAddressType;
|
||||
use brk_error::{Error, Result};
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{
|
||||
@@ -6,10 +9,14 @@ use brk_types::{
|
||||
TypeIndex, Version,
|
||||
};
|
||||
use rayon::prelude::*;
|
||||
use rustc_hash::FxHashMap;
|
||||
use vecdb::{
|
||||
AnyStoredVec, BytesVec, Database, GenericStoredVec, ImportOptions, ImportableVec, Reader, Stamp,
|
||||
AnyStoredVec, AnyVec, BytesVec, Database, GenericStoredVec, ImportOptions, ImportableVec,
|
||||
Reader, Stamp,
|
||||
};
|
||||
|
||||
use super::super::AddressTypeToTypeIndexMap;
|
||||
|
||||
const SAVED_STAMPED_CHANGES: u16 = 10;
|
||||
|
||||
/// Macro to define AnyAddressIndexesVecs and its methods.
|
||||
@@ -75,6 +82,31 @@ macro_rules! define_any_address_indexes_vecs {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get length for a given address type.
|
||||
pub fn len_of(&self, address_type: OutputType) -> usize {
|
||||
match address_type {
|
||||
$(OutputType::$variant => self.$field.len(),)*
|
||||
_ => unreachable!("Invalid address type: {:?}", address_type),
|
||||
}
|
||||
}
|
||||
|
||||
/// Update existing entry (must be within bounds).
|
||||
pub fn update(&mut self, address_type: OutputType, typeindex: TypeIndex, index: AnyAddressIndex) -> Result<()> {
|
||||
match address_type {
|
||||
$(OutputType::$variant => self.$field.update(typeindex.into(), index)?,)*
|
||||
_ => unreachable!("Invalid address type: {:?}", address_type),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Push new entry (must be at exactly len position).
|
||||
pub fn push(&mut self, address_type: OutputType, index: AnyAddressIndex) {
|
||||
match address_type {
|
||||
$(OutputType::$variant => self.$field.push(index),)*
|
||||
_ => unreachable!("Invalid address type: {:?}", address_type),
|
||||
}
|
||||
}
|
||||
|
||||
/// Write all address types with stamp.
|
||||
pub fn write(&mut self, stamp: Stamp, with_changes: bool) -> Result<()> {
|
||||
$(self.$field.stamped_write_maybe_with_changes(stamp, with_changes)?;)*
|
||||
@@ -100,3 +132,102 @@ define_any_address_indexes_vecs!(
|
||||
(p2wpkh, P2WPKH, P2WPKHAddressIndex),
|
||||
(p2wsh, P2WSH, P2WSHAddressIndex),
|
||||
);
|
||||
|
||||
impl AnyAddressIndexesVecs {
|
||||
/// Process index updates in parallel by address type.
|
||||
/// Accepts two maps (e.g. from empty and loaded processing) and merges per-thread.
|
||||
/// Updates existing entries and pushes new ones (sorted).
|
||||
/// Returns (update_count, push_count).
|
||||
pub fn par_batch_update(
|
||||
&mut self,
|
||||
updates1: AddressTypeToTypeIndexMap<AnyAddressIndex>,
|
||||
updates2: AddressTypeToTypeIndexMap<AnyAddressIndex>,
|
||||
) -> Result<(usize, usize)> {
|
||||
let ByAddressType {
|
||||
p2a: u1_p2a,
|
||||
p2pk33: u1_p2pk33,
|
||||
p2pk65: u1_p2pk65,
|
||||
p2pkh: u1_p2pkh,
|
||||
p2sh: u1_p2sh,
|
||||
p2tr: u1_p2tr,
|
||||
p2wpkh: u1_p2wpkh,
|
||||
p2wsh: u1_p2wsh,
|
||||
} = updates1.into_inner();
|
||||
|
||||
let ByAddressType {
|
||||
p2a: u2_p2a,
|
||||
p2pk33: u2_p2pk33,
|
||||
p2pk65: u2_p2pk65,
|
||||
p2pkh: u2_p2pkh,
|
||||
p2sh: u2_p2sh,
|
||||
p2tr: u2_p2tr,
|
||||
p2wpkh: u2_p2wpkh,
|
||||
p2wsh: u2_p2wsh,
|
||||
} = updates2.into_inner();
|
||||
|
||||
let Self {
|
||||
p2a,
|
||||
p2pk33,
|
||||
p2pk65,
|
||||
p2pkh,
|
||||
p2sh,
|
||||
p2tr,
|
||||
p2wpkh,
|
||||
p2wsh,
|
||||
} = self;
|
||||
|
||||
thread::scope(|s| {
|
||||
let h_p2a = s.spawn(|| process_single_type_merged(p2a, u1_p2a, u2_p2a));
|
||||
let h_p2pk33 = s.spawn(|| process_single_type_merged(p2pk33, u1_p2pk33, u2_p2pk33));
|
||||
let h_p2pk65 = s.spawn(|| process_single_type_merged(p2pk65, u1_p2pk65, u2_p2pk65));
|
||||
let h_p2pkh = s.spawn(|| process_single_type_merged(p2pkh, u1_p2pkh, u2_p2pkh));
|
||||
let h_p2sh = s.spawn(|| process_single_type_merged(p2sh, u1_p2sh, u2_p2sh));
|
||||
let h_p2tr = s.spawn(|| process_single_type_merged(p2tr, u1_p2tr, u2_p2tr));
|
||||
let h_p2wpkh = s.spawn(|| process_single_type_merged(p2wpkh, u1_p2wpkh, u2_p2wpkh));
|
||||
let h_p2wsh = s.spawn(|| process_single_type_merged(p2wsh, u1_p2wsh, u2_p2wsh));
|
||||
|
||||
let mut total_updates = 0usize;
|
||||
let mut total_pushes = 0usize;
|
||||
|
||||
for h in [
|
||||
h_p2a, h_p2pk33, h_p2pk65, h_p2pkh, h_p2sh, h_p2tr, h_p2wpkh, h_p2wsh,
|
||||
] {
|
||||
let (updates, pushes) = h.join().unwrap()?;
|
||||
total_updates += updates;
|
||||
total_pushes += pushes;
|
||||
}
|
||||
|
||||
Ok((total_updates, total_pushes))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Process updates for a single address type's BytesVec, merging two maps.
|
||||
fn process_single_type_merged<I: vecdb::VecIndex>(
|
||||
vec: &mut BytesVec<I, AnyAddressIndex>,
|
||||
map1: FxHashMap<TypeIndex, AnyAddressIndex>,
|
||||
map2: FxHashMap<TypeIndex, AnyAddressIndex>,
|
||||
) -> Result<(usize, usize)> {
|
||||
let current_len = vec.len();
|
||||
let mut pushes = Vec::with_capacity(map1.len() + map2.len());
|
||||
let mut update_count = 0usize;
|
||||
|
||||
for (typeindex, any_index) in map1.into_iter().chain(map2) {
|
||||
if usize::from(typeindex) < current_len {
|
||||
vec.update(I::from(usize::from(typeindex)), any_index)?;
|
||||
update_count += 1;
|
||||
} else {
|
||||
pushes.push((typeindex, any_index));
|
||||
}
|
||||
}
|
||||
|
||||
let push_count = pushes.len();
|
||||
if !pushes.is_empty() {
|
||||
pushes.sort_unstable_by_key(|(typeindex, _)| *typeindex);
|
||||
for (_, any_index) in pushes {
|
||||
vec.push(any_index);
|
||||
}
|
||||
}
|
||||
|
||||
Ok((update_count, push_count))
|
||||
}
|
||||
|
||||
@@ -92,6 +92,11 @@ impl<T> AddressTypeToTypeIndexMap<T> {
|
||||
self.0.into_iter()
|
||||
}
|
||||
|
||||
/// Consume and return the inner ByAddressType.
|
||||
pub fn into_inner(self) -> ByAddressType<FxHashMap<TypeIndex, T>> {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Iterate mutably over entries by address type.
|
||||
pub fn iter_mut(&mut self) -> impl Iterator<Item = (OutputType, &mut FxHashMap<TypeIndex, T>)> {
|
||||
self.0.iter_mut()
|
||||
|
||||
@@ -3,6 +3,7 @@ use brk_types::{
|
||||
AnyAddressIndex, EmptyAddressData, EmptyAddressIndex, LoadedAddressData, LoadedAddressIndex,
|
||||
OutputType, TypeIndex,
|
||||
};
|
||||
use vecdb::AnyVec;
|
||||
|
||||
use crate::distribution::{AddressTypeToTypeIndexMap, AddressesDataVecs};
|
||||
|
||||
@@ -51,9 +52,12 @@ pub fn process_loaded_addresses(
|
||||
addresses_data.loaded.update(index, data)?;
|
||||
}
|
||||
|
||||
// Phase 3: Pushes (fills holes, then grows)
|
||||
let mut result = AddressTypeToTypeIndexMap::default();
|
||||
for (address_type, typeindex, data) in pushes {
|
||||
// Phase 3: Pushes (fill holes first, then pure pushes)
|
||||
let mut result = AddressTypeToTypeIndexMap::with_capacity(pushes.len() / 4);
|
||||
let holes_count = addresses_data.loaded.holes().len();
|
||||
let mut pushes_iter = pushes.into_iter();
|
||||
|
||||
for (address_type, typeindex, data) in pushes_iter.by_ref().take(holes_count) {
|
||||
let index = addresses_data.loaded.fill_first_hole_or_push(data)?;
|
||||
result
|
||||
.get_mut(address_type)
|
||||
@@ -61,6 +65,18 @@ pub fn process_loaded_addresses(
|
||||
.insert(typeindex, AnyAddressIndex::from(index));
|
||||
}
|
||||
|
||||
// Pure pushes - no holes remain
|
||||
addresses_data.loaded.reserve_pushed(pushes_iter.len());
|
||||
let mut next_index = addresses_data.loaded.len();
|
||||
for (address_type, typeindex, data) in pushes_iter {
|
||||
addresses_data.loaded.push(data);
|
||||
result
|
||||
.get_mut(address_type)
|
||||
.unwrap()
|
||||
.insert(typeindex, AnyAddressIndex::from(LoadedAddressIndex::from(next_index)));
|
||||
next_index += 1;
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
@@ -107,9 +123,12 @@ pub fn process_empty_addresses(
|
||||
addresses_data.empty.update(index, data)?;
|
||||
}
|
||||
|
||||
// Phase 3: Pushes (fills holes, then grows)
|
||||
let mut result = AddressTypeToTypeIndexMap::default();
|
||||
for (address_type, typeindex, data) in pushes {
|
||||
// Phase 3: Pushes (fill holes first, then pure pushes)
|
||||
let mut result = AddressTypeToTypeIndexMap::with_capacity(pushes.len() / 4);
|
||||
let holes_count = addresses_data.empty.holes().len();
|
||||
let mut pushes_iter = pushes.into_iter();
|
||||
|
||||
for (address_type, typeindex, data) in pushes_iter.by_ref().take(holes_count) {
|
||||
let index = addresses_data.empty.fill_first_hole_or_push(data)?;
|
||||
result
|
||||
.get_mut(address_type)
|
||||
@@ -117,5 +136,17 @@ pub fn process_empty_addresses(
|
||||
.insert(typeindex, AnyAddressIndex::from(index));
|
||||
}
|
||||
|
||||
// Pure pushes - no holes remain
|
||||
addresses_data.empty.reserve_pushed(pushes_iter.len());
|
||||
let mut next_index = addresses_data.empty.len();
|
||||
for (address_type, typeindex, data) in pushes_iter {
|
||||
addresses_data.empty.push(data);
|
||||
result
|
||||
.get_mut(address_type)
|
||||
.unwrap()
|
||||
.insert(typeindex, AnyAddressIndex::from(EmptyAddressIndex::from(next_index)));
|
||||
next_index += 1;
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ use rayon::prelude::*;
|
||||
use vecdb::{AnyStoredVec, AnyVec, Database, Exit, GenericStoredVec, IterableVec};
|
||||
|
||||
use crate::{
|
||||
ComputeIndexes, distribution::state::AddressCohortState, indexes, internal::ComputedBlockLast,
|
||||
ComputeIndexes, distribution::state::AddressCohortState, indexes, internal::ComputedFromHeightLast,
|
||||
price,
|
||||
};
|
||||
|
||||
@@ -32,7 +32,7 @@ pub struct AddressCohortVecs {
|
||||
#[traversable(flatten)]
|
||||
pub metrics: CohortMetrics,
|
||||
|
||||
pub addr_count: ComputedBlockLast<StoredU64>,
|
||||
pub addr_count: ComputedFromHeightLast<StoredU64>,
|
||||
}
|
||||
|
||||
impl AddressCohortVecs {
|
||||
@@ -73,7 +73,7 @@ impl AddressCohortVecs {
|
||||
|
||||
metrics: CohortMetrics::forced_import(&cfg, all_supply)?,
|
||||
|
||||
addr_count: ComputedBlockLast::forced_import(
|
||||
addr_count: ComputedFromHeightLast::forced_import(
|
||||
db,
|
||||
&cfg.name("addr_count"),
|
||||
version + VERSION,
|
||||
|
||||
@@ -33,15 +33,12 @@ pub fn process_address_updates(
|
||||
) -> Result<()> {
|
||||
info!("Processing address updates...");
|
||||
|
||||
let i = Instant::now();
|
||||
let empty_result = process_empty_addresses(addresses_data, empty_updates)?;
|
||||
let loaded_result = process_loaded_addresses(addresses_data, loaded_updates)?;
|
||||
let all_updates = empty_result.merge(loaded_result);
|
||||
address_indexes.par_batch_update(empty_result, loaded_result)?;
|
||||
|
||||
for (address_type, sorted) in all_updates.into_sorted_iter() {
|
||||
for (typeindex, any_index) in sorted {
|
||||
address_indexes.update_or_push(address_type, typeindex, any_index)?;
|
||||
}
|
||||
}
|
||||
info!("Processed address updates in {:?}", i.elapsed());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ use vecdb::{AnyStoredVec, AnyVec, EagerVec, Exit, GenericStoredVec, ImportableVe
|
||||
|
||||
use crate::{
|
||||
ComputeIndexes, indexes,
|
||||
internal::{ComputedBlockSumCum, LazyComputedValueBlockSumCum},
|
||||
internal::{ComputedFromHeightSumCum, LazyComputedValueFromHeightSumCum},
|
||||
};
|
||||
|
||||
use super::ImportConfig;
|
||||
@@ -15,7 +15,7 @@ use super::ImportConfig;
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct ActivityMetrics {
|
||||
/// Total satoshis sent at each height + derived indexes
|
||||
pub sent: LazyComputedValueBlockSumCum,
|
||||
pub sent: LazyComputedValueFromHeightSumCum,
|
||||
|
||||
/// Satoshi-blocks destroyed (supply * blocks_old when spent)
|
||||
pub satblocks_destroyed: EagerVec<PcoVec<Height, Sats>>,
|
||||
@@ -24,17 +24,17 @@ pub struct ActivityMetrics {
|
||||
pub satdays_destroyed: EagerVec<PcoVec<Height, Sats>>,
|
||||
|
||||
/// Coin-blocks destroyed (in BTC rather than sats)
|
||||
pub coinblocks_destroyed: ComputedBlockSumCum<StoredF64>,
|
||||
pub coinblocks_destroyed: ComputedFromHeightSumCum<StoredF64>,
|
||||
|
||||
/// Coin-days destroyed (in BTC rather than sats)
|
||||
pub coindays_destroyed: ComputedBlockSumCum<StoredF64>,
|
||||
pub coindays_destroyed: ComputedFromHeightSumCum<StoredF64>,
|
||||
}
|
||||
|
||||
impl ActivityMetrics {
|
||||
/// Import activity metrics from database.
|
||||
pub fn forced_import(cfg: &ImportConfig) -> Result<Self> {
|
||||
Ok(Self {
|
||||
sent: LazyComputedValueBlockSumCum::forced_import(
|
||||
sent: LazyComputedValueFromHeightSumCum::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("sent"),
|
||||
cfg.version,
|
||||
@@ -54,14 +54,14 @@ impl ActivityMetrics {
|
||||
cfg.version,
|
||||
)?,
|
||||
|
||||
coinblocks_destroyed: ComputedBlockSumCum::forced_import(
|
||||
coinblocks_destroyed: ComputedFromHeightSumCum::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("coinblocks_destroyed"),
|
||||
cfg.version,
|
||||
cfg.indexes,
|
||||
)?,
|
||||
|
||||
coindays_destroyed: ComputedBlockSumCum::forced_import(
|
||||
coindays_destroyed: ComputedFromHeightSumCum::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("coindays_destroyed"),
|
||||
cfg.version,
|
||||
|
||||
@@ -8,7 +8,7 @@ use crate::{
|
||||
ComputeIndexes,
|
||||
distribution::state::CohortState,
|
||||
indexes,
|
||||
internal::{ComputedBlockLast, CostBasisPercentiles},
|
||||
internal::{ComputedFromHeightLast, CostBasisPercentiles},
|
||||
};
|
||||
|
||||
use super::ImportConfig;
|
||||
@@ -17,10 +17,10 @@ use super::ImportConfig;
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct CostBasisMetrics {
|
||||
/// Minimum cost basis for any UTXO at this height
|
||||
pub min: ComputedBlockLast<Dollars>,
|
||||
pub min: ComputedFromHeightLast<Dollars>,
|
||||
|
||||
/// Maximum cost basis for any UTXO at this height
|
||||
pub max: ComputedBlockLast<Dollars>,
|
||||
pub max: ComputedFromHeightLast<Dollars>,
|
||||
|
||||
/// Cost basis distribution percentiles (median, quartiles, etc.)
|
||||
pub percentiles: Option<CostBasisPercentiles>,
|
||||
@@ -32,13 +32,13 @@ impl CostBasisMetrics {
|
||||
let extended = cfg.extended();
|
||||
|
||||
Ok(Self {
|
||||
min: ComputedBlockLast::forced_import(
|
||||
min: ComputedFromHeightLast::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("min_cost_basis"),
|
||||
cfg.version,
|
||||
cfg.indexes,
|
||||
)?,
|
||||
max: ComputedBlockLast::forced_import(
|
||||
max: ComputedFromHeightLast::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("max_cost_basis"),
|
||||
cfg.version,
|
||||
|
||||
@@ -313,7 +313,7 @@ impl CohortMetrics {
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.supply
|
||||
.compute_rest_part1(indexes, price, starting_indexes, exit)?;
|
||||
.compute_rest_part1(indexes, starting_indexes, exit)?;
|
||||
self.outputs.compute_rest(indexes, starting_indexes, exit)?;
|
||||
self.activity
|
||||
.compute_rest_part1(indexes, starting_indexes, exit)?;
|
||||
|
||||
@@ -4,21 +4,21 @@ use brk_types::{Height, StoredU64};
|
||||
use rayon::prelude::*;
|
||||
use vecdb::{AnyStoredVec, AnyVec, Exit, GenericStoredVec};
|
||||
|
||||
use crate::{ComputeIndexes, indexes, internal::ComputedBlockLast};
|
||||
use crate::{ComputeIndexes, indexes, internal::ComputedFromHeightLast};
|
||||
|
||||
use super::ImportConfig;
|
||||
|
||||
/// Output metrics for a cohort.
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct OutputsMetrics {
|
||||
pub utxo_count: ComputedBlockLast<StoredU64>,
|
||||
pub utxo_count: ComputedFromHeightLast<StoredU64>,
|
||||
}
|
||||
|
||||
impl OutputsMetrics {
|
||||
/// Import output metrics from database.
|
||||
pub fn forced_import(cfg: &ImportConfig) -> Result<Self> {
|
||||
Ok(Self {
|
||||
utxo_count: ComputedBlockLast::forced_import(
|
||||
utxo_count: ComputedFromHeightLast::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("utxo_count"),
|
||||
cfg.version,
|
||||
|
||||
@@ -12,9 +12,9 @@ use crate::{
|
||||
distribution::state::RealizedState,
|
||||
indexes,
|
||||
internal::{
|
||||
ComputedBlockLast, ComputedBlockSum, ComputedBlockSumCum, ComputedDateLast,
|
||||
ComputedRatioVecsDate, DollarsMinus, LazyBinaryBlockSum, LazyBinaryBlockSumCum,
|
||||
LazyBlockSum, LazyBlockSumCum, LazyDateLast, PercentageDollarsF32, StoredF32Identity,
|
||||
ComputedFromHeightLast, ComputedFromHeightSum, ComputedFromHeightSumCum, ComputedFromDateLast,
|
||||
ComputedFromDateRatio, DollarsMinus, LazyBinaryFromHeightSum, LazyBinaryFromHeightSumCum,
|
||||
LazyFromHeightSum, LazyFromHeightSumCum, LazyFromDateLast, PercentageDollarsF32, StoredF32Identity,
|
||||
},
|
||||
price,
|
||||
};
|
||||
@@ -25,39 +25,39 @@ use super::ImportConfig;
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct RealizedMetrics {
|
||||
// === Realized Cap ===
|
||||
pub realized_cap: ComputedBlockLast<Dollars>,
|
||||
pub realized_price: ComputedBlockLast<Dollars>,
|
||||
pub realized_price_extra: ComputedRatioVecsDate,
|
||||
pub realized_cap_rel_to_own_market_cap: Option<ComputedBlockLast<StoredF32>>,
|
||||
pub realized_cap_30d_delta: ComputedDateLast<Dollars>,
|
||||
pub realized_cap: ComputedFromHeightLast<Dollars>,
|
||||
pub realized_price: ComputedFromHeightLast<Dollars>,
|
||||
pub realized_price_extra: ComputedFromDateRatio,
|
||||
pub realized_cap_rel_to_own_market_cap: Option<ComputedFromHeightLast<StoredF32>>,
|
||||
pub realized_cap_30d_delta: ComputedFromDateLast<Dollars>,
|
||||
|
||||
// === MVRV (Market Value to Realized Value) ===
|
||||
// Proxy for realized_price_extra.ratio (close / realized_price = market_cap / realized_cap)
|
||||
pub mvrv: LazyDateLast<StoredF32>,
|
||||
pub mvrv: LazyFromDateLast<StoredF32>,
|
||||
|
||||
// === Realized Profit/Loss ===
|
||||
pub realized_profit: ComputedBlockSumCum<Dollars>,
|
||||
pub realized_loss: ComputedBlockSumCum<Dollars>,
|
||||
pub neg_realized_loss: LazyBlockSumCum<Dollars>,
|
||||
pub net_realized_pnl: ComputedBlockSumCum<Dollars>,
|
||||
pub realized_value: ComputedBlockSum<Dollars>,
|
||||
pub realized_profit: ComputedFromHeightSumCum<Dollars>,
|
||||
pub realized_loss: ComputedFromHeightSumCum<Dollars>,
|
||||
pub neg_realized_loss: LazyFromHeightSumCum<Dollars>,
|
||||
pub net_realized_pnl: ComputedFromHeightSumCum<Dollars>,
|
||||
pub realized_value: ComputedFromHeightSum<Dollars>,
|
||||
|
||||
// === Realized vs Realized Cap Ratios (lazy) ===
|
||||
pub realized_profit_rel_to_realized_cap: LazyBinaryBlockSumCum<StoredF32, Dollars, Dollars>,
|
||||
pub realized_loss_rel_to_realized_cap: LazyBinaryBlockSumCum<StoredF32, Dollars, Dollars>,
|
||||
pub net_realized_pnl_rel_to_realized_cap: LazyBinaryBlockSumCum<StoredF32, Dollars, Dollars>,
|
||||
pub realized_profit_rel_to_realized_cap: LazyBinaryFromHeightSumCum<StoredF32, Dollars, Dollars>,
|
||||
pub realized_loss_rel_to_realized_cap: LazyBinaryFromHeightSumCum<StoredF32, Dollars, Dollars>,
|
||||
pub net_realized_pnl_rel_to_realized_cap: LazyBinaryFromHeightSumCum<StoredF32, Dollars, Dollars>,
|
||||
|
||||
// === Total Realized PnL ===
|
||||
pub total_realized_pnl: LazyBlockSum<Dollars>,
|
||||
pub total_realized_pnl: LazyFromHeightSum<Dollars>,
|
||||
pub realized_profit_to_loss_ratio: Option<EagerVec<PcoVec<DateIndex, StoredF64>>>,
|
||||
|
||||
// === Value Created/Destroyed ===
|
||||
pub value_created: ComputedBlockSum<Dollars>,
|
||||
pub value_destroyed: ComputedBlockSum<Dollars>,
|
||||
pub value_created: ComputedFromHeightSum<Dollars>,
|
||||
pub value_destroyed: ComputedFromHeightSum<Dollars>,
|
||||
|
||||
// === Adjusted Value (lazy: cohort - up_to_1h) ===
|
||||
pub adjusted_value_created: Option<LazyBinaryBlockSum<Dollars, Dollars, Dollars>>,
|
||||
pub adjusted_value_destroyed: Option<LazyBinaryBlockSum<Dollars, Dollars, Dollars>>,
|
||||
pub adjusted_value_created: Option<LazyBinaryFromHeightSum<Dollars, Dollars, Dollars>>,
|
||||
pub adjusted_value_destroyed: Option<LazyBinaryFromHeightSum<Dollars, Dollars, Dollars>>,
|
||||
|
||||
// === SOPR (Spent Output Profit Ratio) ===
|
||||
pub sopr: EagerVec<PcoVec<DateIndex, StoredF64>>,
|
||||
@@ -73,9 +73,9 @@ pub struct RealizedMetrics {
|
||||
pub sell_side_risk_ratio_30d_ema: EagerVec<PcoVec<DateIndex, StoredF32>>,
|
||||
|
||||
// === Net Realized PnL Deltas ===
|
||||
pub net_realized_pnl_cumulative_30d_delta: ComputedDateLast<Dollars>,
|
||||
pub net_realized_pnl_cumulative_30d_delta_rel_to_realized_cap: ComputedDateLast<StoredF32>,
|
||||
pub net_realized_pnl_cumulative_30d_delta_rel_to_market_cap: ComputedDateLast<StoredF32>,
|
||||
pub net_realized_pnl_cumulative_30d_delta: ComputedFromDateLast<Dollars>,
|
||||
pub net_realized_pnl_cumulative_30d_delta_rel_to_realized_cap: ComputedFromDateLast<StoredF32>,
|
||||
pub net_realized_pnl_cumulative_30d_delta_rel_to_market_cap: ComputedFromDateLast<StoredF32>,
|
||||
}
|
||||
|
||||
impl RealizedMetrics {
|
||||
@@ -87,35 +87,35 @@ impl RealizedMetrics {
|
||||
let compute_adjusted = cfg.compute_adjusted();
|
||||
|
||||
// Import combined types using forced_import which handles height + derived
|
||||
let realized_cap = ComputedBlockLast::forced_import(
|
||||
let realized_cap = ComputedFromHeightLast::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("realized_cap"),
|
||||
cfg.version,
|
||||
cfg.indexes,
|
||||
)?;
|
||||
|
||||
let realized_profit = ComputedBlockSumCum::forced_import(
|
||||
let realized_profit = ComputedFromHeightSumCum::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("realized_profit"),
|
||||
cfg.version,
|
||||
cfg.indexes,
|
||||
)?;
|
||||
|
||||
let realized_loss = ComputedBlockSumCum::forced_import(
|
||||
let realized_loss = ComputedFromHeightSumCum::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("realized_loss"),
|
||||
cfg.version,
|
||||
cfg.indexes,
|
||||
)?;
|
||||
|
||||
let neg_realized_loss = LazyBlockSumCum::from_computed::<Negate>(
|
||||
let neg_realized_loss = LazyFromHeightSumCum::from_computed::<Negate>(
|
||||
&cfg.name("neg_realized_loss"),
|
||||
cfg.version + v1,
|
||||
realized_loss.height.boxed_clone(),
|
||||
&realized_loss,
|
||||
);
|
||||
|
||||
let net_realized_pnl = ComputedBlockSumCum::forced_import(
|
||||
let net_realized_pnl = ComputedFromHeightSumCum::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("net_realized_pnl"),
|
||||
cfg.version,
|
||||
@@ -123,7 +123,7 @@ impl RealizedMetrics {
|
||||
)?;
|
||||
|
||||
// realized_value is the source for total_realized_pnl (they're identical)
|
||||
let realized_value = ComputedBlockSum::forced_import(
|
||||
let realized_value = ComputedFromHeightSum::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("realized_value"),
|
||||
cfg.version,
|
||||
@@ -131,7 +131,7 @@ impl RealizedMetrics {
|
||||
)?;
|
||||
|
||||
// total_realized_pnl is a lazy alias to realized_value
|
||||
let total_realized_pnl = LazyBlockSum::from_computed::<Ident>(
|
||||
let total_realized_pnl = LazyFromHeightSum::from_computed::<Ident>(
|
||||
&cfg.name("total_realized_pnl"),
|
||||
cfg.version + v1,
|
||||
realized_value.height.boxed_clone(),
|
||||
@@ -140,7 +140,7 @@ impl RealizedMetrics {
|
||||
|
||||
// Construct lazy ratio vecs
|
||||
let realized_profit_rel_to_realized_cap =
|
||||
LazyBinaryBlockSumCum::from_computed_last::<PercentageDollarsF32>(
|
||||
LazyBinaryFromHeightSumCum::from_computed_last::<PercentageDollarsF32>(
|
||||
&cfg.name("realized_profit_rel_to_realized_cap"),
|
||||
cfg.version + v1,
|
||||
realized_profit.height.boxed_clone(),
|
||||
@@ -150,7 +150,7 @@ impl RealizedMetrics {
|
||||
);
|
||||
|
||||
let realized_loss_rel_to_realized_cap =
|
||||
LazyBinaryBlockSumCum::from_computed_last::<PercentageDollarsF32>(
|
||||
LazyBinaryFromHeightSumCum::from_computed_last::<PercentageDollarsF32>(
|
||||
&cfg.name("realized_loss_rel_to_realized_cap"),
|
||||
cfg.version + v1,
|
||||
realized_loss.height.boxed_clone(),
|
||||
@@ -160,7 +160,7 @@ impl RealizedMetrics {
|
||||
);
|
||||
|
||||
let net_realized_pnl_rel_to_realized_cap =
|
||||
LazyBinaryBlockSumCum::from_computed_last::<PercentageDollarsF32>(
|
||||
LazyBinaryFromHeightSumCum::from_computed_last::<PercentageDollarsF32>(
|
||||
&cfg.name("net_realized_pnl_rel_to_realized_cap"),
|
||||
cfg.version + v1,
|
||||
net_realized_pnl.height.boxed_clone(),
|
||||
@@ -169,21 +169,21 @@ impl RealizedMetrics {
|
||||
&realized_cap,
|
||||
);
|
||||
|
||||
let realized_price = ComputedBlockLast::forced_import(
|
||||
let realized_price = ComputedFromHeightLast::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("realized_price"),
|
||||
cfg.version + v1,
|
||||
cfg.indexes,
|
||||
)?;
|
||||
|
||||
let value_created = ComputedBlockSum::forced_import(
|
||||
let value_created = ComputedFromHeightSum::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("value_created"),
|
||||
cfg.version,
|
||||
cfg.indexes,
|
||||
)?;
|
||||
|
||||
let value_destroyed = ComputedBlockSum::forced_import(
|
||||
let value_destroyed = ComputedFromHeightSum::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("value_destroyed"),
|
||||
cfg.version,
|
||||
@@ -194,7 +194,7 @@ impl RealizedMetrics {
|
||||
let adjusted_value_created =
|
||||
(compute_adjusted && cfg.up_to_1h_realized.is_some()).then(|| {
|
||||
let up_to_1h = cfg.up_to_1h_realized.unwrap();
|
||||
LazyBinaryBlockSum::from_computed::<DollarsMinus>(
|
||||
LazyBinaryFromHeightSum::from_computed::<DollarsMinus>(
|
||||
&cfg.name("adjusted_value_created"),
|
||||
cfg.version,
|
||||
&value_created,
|
||||
@@ -204,7 +204,7 @@ impl RealizedMetrics {
|
||||
let adjusted_value_destroyed =
|
||||
(compute_adjusted && cfg.up_to_1h_realized.is_some()).then(|| {
|
||||
let up_to_1h = cfg.up_to_1h_realized.unwrap();
|
||||
LazyBinaryBlockSum::from_computed::<DollarsMinus>(
|
||||
LazyBinaryFromHeightSum::from_computed::<DollarsMinus>(
|
||||
&cfg.name("adjusted_value_destroyed"),
|
||||
cfg.version,
|
||||
&value_destroyed,
|
||||
@@ -213,7 +213,7 @@ impl RealizedMetrics {
|
||||
});
|
||||
|
||||
// Create realized_price_extra first so we can reference its ratio for MVRV proxy
|
||||
let realized_price_extra = ComputedRatioVecsDate::forced_import(
|
||||
let realized_price_extra = ComputedFromDateRatio::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("realized_price"),
|
||||
Some(&realized_price),
|
||||
@@ -225,7 +225,7 @@ impl RealizedMetrics {
|
||||
|
||||
// MVRV is a lazy proxy for realized_price_extra.ratio
|
||||
// ratio = close / realized_price = market_cap / realized_cap = MVRV
|
||||
let mvrv = LazyDateLast::from_source::<StoredF32Identity>(
|
||||
let mvrv = LazyFromDateLast::from_source::<StoredF32Identity>(
|
||||
&cfg.name("mvrv"),
|
||||
cfg.version,
|
||||
&realized_price_extra.ratio,
|
||||
@@ -238,7 +238,7 @@ impl RealizedMetrics {
|
||||
realized_price_extra,
|
||||
realized_cap_rel_to_own_market_cap: extended
|
||||
.then(|| {
|
||||
ComputedBlockLast::forced_import(
|
||||
ComputedFromHeightLast::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("realized_cap_rel_to_own_market_cap"),
|
||||
cfg.version,
|
||||
@@ -246,7 +246,7 @@ impl RealizedMetrics {
|
||||
)
|
||||
})
|
||||
.transpose()?,
|
||||
realized_cap_30d_delta: ComputedDateLast::forced_import(
|
||||
realized_cap_30d_delta: ComputedFromDateLast::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("realized_cap_30d_delta"),
|
||||
cfg.version,
|
||||
@@ -338,21 +338,21 @@ impl RealizedMetrics {
|
||||
)?,
|
||||
|
||||
// === Net Realized PnL Deltas ===
|
||||
net_realized_pnl_cumulative_30d_delta: ComputedDateLast::forced_import(
|
||||
net_realized_pnl_cumulative_30d_delta: ComputedFromDateLast::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("net_realized_pnl_cumulative_30d_delta"),
|
||||
cfg.version + v3,
|
||||
cfg.indexes,
|
||||
)?,
|
||||
net_realized_pnl_cumulative_30d_delta_rel_to_realized_cap:
|
||||
ComputedDateLast::forced_import(
|
||||
ComputedFromDateLast::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("net_realized_pnl_cumulative_30d_delta_rel_to_realized_cap"),
|
||||
cfg.version + v3,
|
||||
cfg.indexes,
|
||||
)?,
|
||||
net_realized_pnl_cumulative_30d_delta_rel_to_market_cap:
|
||||
ComputedDateLast::forced_import(
|
||||
ComputedFromDateLast::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("net_realized_pnl_cumulative_30d_delta_rel_to_market_cap"),
|
||||
cfg.version + v3,
|
||||
|
||||
@@ -4,7 +4,7 @@ use brk_types::{Dollars, Sats, StoredF32, StoredF64, Version};
|
||||
use vecdb::IterableCloneableVec;
|
||||
|
||||
use crate::internal::{
|
||||
LazyBinaryBlockLast, LazyBinaryDateLast, NegPercentageDollarsF32, NegRatio32,
|
||||
LazyBinaryFromHeightLast, LazyBinaryFromDateLast, NegPercentageDollarsF32, NegRatio32,
|
||||
PercentageDollarsF32, PercentageSatsF64, Ratio32,
|
||||
};
|
||||
|
||||
@@ -15,49 +15,49 @@ use super::{ImportConfig, SupplyMetrics, UnrealizedMetrics};
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct RelativeMetrics {
|
||||
// === Supply Relative to Circulating Supply (lazy from global supply) ===
|
||||
pub supply_rel_to_circulating_supply: Option<LazyBinaryDateLast<StoredF64, Sats, Sats>>,
|
||||
pub supply_rel_to_circulating_supply: Option<LazyBinaryFromDateLast<StoredF64, Sats, Sats>>,
|
||||
|
||||
// === Supply in Profit/Loss Relative to Own Supply (lazy) ===
|
||||
pub supply_in_profit_rel_to_own_supply: LazyBinaryBlockLast<StoredF64, Sats, Sats>,
|
||||
pub supply_in_loss_rel_to_own_supply: LazyBinaryBlockLast<StoredF64, Sats, Sats>,
|
||||
pub supply_in_profit_rel_to_own_supply: LazyBinaryFromHeightLast<StoredF64, Sats, Sats>,
|
||||
pub supply_in_loss_rel_to_own_supply: LazyBinaryFromHeightLast<StoredF64, Sats, Sats>,
|
||||
|
||||
// === Supply in Profit/Loss Relative to Circulating Supply (lazy from global supply) ===
|
||||
pub supply_in_profit_rel_to_circulating_supply:
|
||||
Option<LazyBinaryBlockLast<StoredF64, Sats, Sats>>,
|
||||
Option<LazyBinaryFromHeightLast<StoredF64, Sats, Sats>>,
|
||||
pub supply_in_loss_rel_to_circulating_supply:
|
||||
Option<LazyBinaryBlockLast<StoredF64, Sats, Sats>>,
|
||||
Option<LazyBinaryFromHeightLast<StoredF64, Sats, Sats>>,
|
||||
|
||||
// === Unrealized vs Market Cap (lazy from global market cap) ===
|
||||
pub unrealized_profit_rel_to_market_cap:
|
||||
Option<LazyBinaryBlockLast<StoredF32, Dollars, Dollars>>,
|
||||
pub unrealized_loss_rel_to_market_cap: Option<LazyBinaryBlockLast<StoredF32, Dollars, Dollars>>,
|
||||
Option<LazyBinaryFromHeightLast<StoredF32, Dollars, Dollars>>,
|
||||
pub unrealized_loss_rel_to_market_cap: Option<LazyBinaryFromHeightLast<StoredF32, Dollars, Dollars>>,
|
||||
pub neg_unrealized_loss_rel_to_market_cap:
|
||||
Option<LazyBinaryBlockLast<StoredF32, Dollars, Dollars>>,
|
||||
Option<LazyBinaryFromHeightLast<StoredF32, Dollars, Dollars>>,
|
||||
pub net_unrealized_pnl_rel_to_market_cap:
|
||||
Option<LazyBinaryBlockLast<StoredF32, Dollars, Dollars>>,
|
||||
Option<LazyBinaryFromHeightLast<StoredF32, Dollars, Dollars>>,
|
||||
|
||||
// === NUPL (Net Unrealized Profit/Loss) ===
|
||||
pub nupl: Option<LazyBinaryBlockLast<StoredF32, Dollars, Dollars>>,
|
||||
pub nupl: Option<LazyBinaryFromHeightLast<StoredF32, Dollars, Dollars>>,
|
||||
|
||||
// === Unrealized vs Own Market Cap (lazy) ===
|
||||
pub unrealized_profit_rel_to_own_market_cap:
|
||||
Option<LazyBinaryBlockLast<StoredF32, Dollars, Dollars>>,
|
||||
Option<LazyBinaryFromHeightLast<StoredF32, Dollars, Dollars>>,
|
||||
pub unrealized_loss_rel_to_own_market_cap:
|
||||
Option<LazyBinaryBlockLast<StoredF32, Dollars, Dollars>>,
|
||||
Option<LazyBinaryFromHeightLast<StoredF32, Dollars, Dollars>>,
|
||||
pub neg_unrealized_loss_rel_to_own_market_cap:
|
||||
Option<LazyBinaryBlockLast<StoredF32, Dollars, Dollars>>,
|
||||
Option<LazyBinaryFromHeightLast<StoredF32, Dollars, Dollars>>,
|
||||
pub net_unrealized_pnl_rel_to_own_market_cap:
|
||||
Option<LazyBinaryBlockLast<StoredF32, Dollars, Dollars>>,
|
||||
Option<LazyBinaryFromHeightLast<StoredF32, Dollars, Dollars>>,
|
||||
|
||||
// === Unrealized vs Own Total Unrealized PnL (lazy) ===
|
||||
pub unrealized_profit_rel_to_own_total_unrealized_pnl:
|
||||
Option<LazyBinaryBlockLast<StoredF32, Dollars, Dollars>>,
|
||||
Option<LazyBinaryFromHeightLast<StoredF32, Dollars, Dollars>>,
|
||||
pub unrealized_loss_rel_to_own_total_unrealized_pnl:
|
||||
Option<LazyBinaryBlockLast<StoredF32, Dollars, Dollars>>,
|
||||
Option<LazyBinaryFromHeightLast<StoredF32, Dollars, Dollars>>,
|
||||
pub neg_unrealized_loss_rel_to_own_total_unrealized_pnl:
|
||||
Option<LazyBinaryBlockLast<StoredF32, Dollars, Dollars>>,
|
||||
Option<LazyBinaryFromHeightLast<StoredF32, Dollars, Dollars>>,
|
||||
pub net_unrealized_pnl_rel_to_own_total_unrealized_pnl:
|
||||
Option<LazyBinaryBlockLast<StoredF32, Dollars, Dollars>>,
|
||||
Option<LazyBinaryFromHeightLast<StoredF32, Dollars, Dollars>>,
|
||||
}
|
||||
|
||||
impl RelativeMetrics {
|
||||
@@ -91,7 +91,7 @@ impl RelativeMetrics {
|
||||
supply_rel_to_circulating_supply: (compute_rel_to_all
|
||||
&& global_supply_sats_dates.is_some())
|
||||
.then(|| {
|
||||
LazyBinaryDateLast::from_both_derived_last::<PercentageSatsF64>(
|
||||
LazyBinaryFromDateLast::from_both_derived_last::<PercentageSatsF64>(
|
||||
&cfg.name("supply_rel_to_circulating_supply"),
|
||||
cfg.version + v1,
|
||||
supply.total.sats.rest.dateindex.boxed_clone(),
|
||||
@@ -103,7 +103,7 @@ impl RelativeMetrics {
|
||||
|
||||
// === Supply in Profit/Loss Relative to Own Supply (lazy) ===
|
||||
supply_in_profit_rel_to_own_supply:
|
||||
LazyBinaryBlockLast::from_height_difficultyepoch_dates::<PercentageSatsF64>(
|
||||
LazyBinaryFromHeightLast::from_height_difficultyepoch_dates::<PercentageSatsF64>(
|
||||
&cfg.name("supply_in_profit_rel_to_own_supply"),
|
||||
cfg.version + v1,
|
||||
unrealized.supply_in_profit.height.boxed_clone(),
|
||||
@@ -120,7 +120,7 @@ impl RelativeMetrics {
|
||||
&supply.total.sats.rest.dates,
|
||||
),
|
||||
supply_in_loss_rel_to_own_supply:
|
||||
LazyBinaryBlockLast::from_height_difficultyepoch_dates::<PercentageSatsF64>(
|
||||
LazyBinaryFromHeightLast::from_height_difficultyepoch_dates::<PercentageSatsF64>(
|
||||
&cfg.name("supply_in_loss_rel_to_own_supply"),
|
||||
cfg.version + v1,
|
||||
unrealized.supply_in_loss.height.boxed_clone(),
|
||||
@@ -141,7 +141,7 @@ impl RelativeMetrics {
|
||||
supply_in_profit_rel_to_circulating_supply: (compute_rel_to_all
|
||||
&& global_supply_sats_height.is_some())
|
||||
.then(|| {
|
||||
LazyBinaryBlockLast::from_height_difficultyepoch_dates::<PercentageSatsF64>(
|
||||
LazyBinaryFromHeightLast::from_height_difficultyepoch_dates::<PercentageSatsF64>(
|
||||
&cfg.name("supply_in_profit_rel_to_circulating_supply"),
|
||||
cfg.version + v1,
|
||||
unrealized.supply_in_profit.height.boxed_clone(),
|
||||
@@ -161,7 +161,7 @@ impl RelativeMetrics {
|
||||
supply_in_loss_rel_to_circulating_supply: (compute_rel_to_all
|
||||
&& global_supply_sats_height.is_some())
|
||||
.then(|| {
|
||||
LazyBinaryBlockLast::from_height_difficultyepoch_dates::<PercentageSatsF64>(
|
||||
LazyBinaryFromHeightLast::from_height_difficultyepoch_dates::<PercentageSatsF64>(
|
||||
&cfg.name("supply_in_loss_rel_to_circulating_supply"),
|
||||
cfg.version + v1,
|
||||
unrealized.supply_in_loss.height.boxed_clone(),
|
||||
@@ -182,8 +182,10 @@ impl RelativeMetrics {
|
||||
// === Unrealized vs Market Cap (lazy from global market cap) ===
|
||||
unrealized_profit_rel_to_market_cap:
|
||||
global_market_cap.map(|mc| {
|
||||
LazyBinaryBlockLast::from_computed_height_date_and_block_last::<
|
||||
LazyBinaryFromHeightLast::from_computed_height_date_and_lazy_binary_block_last::<
|
||||
PercentageDollarsF32,
|
||||
_,
|
||||
_,
|
||||
>(
|
||||
&cfg.name("unrealized_profit_rel_to_market_cap"),
|
||||
cfg.version + v2,
|
||||
@@ -193,8 +195,10 @@ impl RelativeMetrics {
|
||||
}),
|
||||
unrealized_loss_rel_to_market_cap:
|
||||
global_market_cap.map(|mc| {
|
||||
LazyBinaryBlockLast::from_computed_height_date_and_block_last::<
|
||||
LazyBinaryFromHeightLast::from_computed_height_date_and_lazy_binary_block_last::<
|
||||
PercentageDollarsF32,
|
||||
_,
|
||||
_,
|
||||
>(
|
||||
&cfg.name("unrealized_loss_rel_to_market_cap"),
|
||||
cfg.version + v2,
|
||||
@@ -203,8 +207,10 @@ impl RelativeMetrics {
|
||||
)
|
||||
}),
|
||||
neg_unrealized_loss_rel_to_market_cap: global_market_cap.map(|mc| {
|
||||
LazyBinaryBlockLast::from_computed_height_date_and_block_last::<
|
||||
LazyBinaryFromHeightLast::from_computed_height_date_and_lazy_binary_block_last::<
|
||||
NegPercentageDollarsF32,
|
||||
_,
|
||||
_,
|
||||
>(
|
||||
&cfg.name("neg_unrealized_loss_rel_to_market_cap"),
|
||||
cfg.version + v2,
|
||||
@@ -213,10 +219,12 @@ impl RelativeMetrics {
|
||||
)
|
||||
}),
|
||||
net_unrealized_pnl_rel_to_market_cap: global_market_cap.map(|mc| {
|
||||
LazyBinaryBlockLast::from_binary_block_and_computed_block_last::<
|
||||
LazyBinaryFromHeightLast::from_binary_block_and_lazy_binary_block_last::<
|
||||
PercentageDollarsF32,
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
>(
|
||||
&cfg.name("net_unrealized_pnl_rel_to_market_cap"),
|
||||
cfg.version + v2,
|
||||
@@ -227,10 +235,12 @@ impl RelativeMetrics {
|
||||
|
||||
// NUPL is a proxy for net_unrealized_pnl_rel_to_market_cap
|
||||
nupl: global_market_cap.map(|mc| {
|
||||
LazyBinaryBlockLast::from_binary_block_and_computed_block_last::<
|
||||
LazyBinaryFromHeightLast::from_binary_block_and_lazy_binary_block_last::<
|
||||
PercentageDollarsF32,
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
>(
|
||||
&cfg.name("nupl"),
|
||||
cfg.version + v2,
|
||||
@@ -243,8 +253,10 @@ impl RelativeMetrics {
|
||||
unrealized_profit_rel_to_own_market_cap: (extended && compute_rel_to_all)
|
||||
.then(|| {
|
||||
own_market_cap.map(|mc| {
|
||||
LazyBinaryBlockLast::from_computed_height_date_and_block_last::<
|
||||
LazyBinaryFromHeightLast::from_computed_height_date_and_lazy_binary_block_last::<
|
||||
PercentageDollarsF32,
|
||||
_,
|
||||
_,
|
||||
>(
|
||||
&cfg.name("unrealized_profit_rel_to_own_market_cap"),
|
||||
cfg.version + v2,
|
||||
@@ -257,8 +269,10 @@ impl RelativeMetrics {
|
||||
unrealized_loss_rel_to_own_market_cap: (extended && compute_rel_to_all)
|
||||
.then(|| {
|
||||
own_market_cap.map(|mc| {
|
||||
LazyBinaryBlockLast::from_computed_height_date_and_block_last::<
|
||||
LazyBinaryFromHeightLast::from_computed_height_date_and_lazy_binary_block_last::<
|
||||
PercentageDollarsF32,
|
||||
_,
|
||||
_,
|
||||
>(
|
||||
&cfg.name("unrealized_loss_rel_to_own_market_cap"),
|
||||
cfg.version + v2,
|
||||
@@ -271,8 +285,10 @@ impl RelativeMetrics {
|
||||
neg_unrealized_loss_rel_to_own_market_cap: (extended && compute_rel_to_all)
|
||||
.then(|| {
|
||||
own_market_cap.map(|mc| {
|
||||
LazyBinaryBlockLast::from_computed_height_date_and_block_last::<
|
||||
LazyBinaryFromHeightLast::from_computed_height_date_and_lazy_binary_block_last::<
|
||||
NegPercentageDollarsF32,
|
||||
_,
|
||||
_,
|
||||
>(
|
||||
&cfg.name("neg_unrealized_loss_rel_to_own_market_cap"),
|
||||
cfg.version + v2,
|
||||
@@ -285,10 +301,12 @@ impl RelativeMetrics {
|
||||
net_unrealized_pnl_rel_to_own_market_cap: (extended && compute_rel_to_all)
|
||||
.then(|| {
|
||||
own_market_cap.map(|mc| {
|
||||
LazyBinaryBlockLast::from_binary_block_and_computed_block_last::<
|
||||
LazyBinaryFromHeightLast::from_binary_block_and_lazy_binary_block_last::<
|
||||
PercentageDollarsF32,
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
_,
|
||||
>(
|
||||
&cfg.name("net_unrealized_pnl_rel_to_own_market_cap"),
|
||||
cfg.version + v2,
|
||||
@@ -301,7 +319,7 @@ impl RelativeMetrics {
|
||||
|
||||
// === Unrealized vs Own Total Unrealized PnL (lazy, optional) ===
|
||||
unrealized_profit_rel_to_own_total_unrealized_pnl: extended.then(|| {
|
||||
LazyBinaryBlockLast::from_computed_height_date_and_binary_block::<Ratio32, _, _>(
|
||||
LazyBinaryFromHeightLast::from_computed_height_date_and_binary_block::<Ratio32, _, _>(
|
||||
&cfg.name("unrealized_profit_rel_to_own_total_unrealized_pnl"),
|
||||
cfg.version,
|
||||
&unrealized.unrealized_profit,
|
||||
@@ -309,7 +327,7 @@ impl RelativeMetrics {
|
||||
)
|
||||
}),
|
||||
unrealized_loss_rel_to_own_total_unrealized_pnl: extended.then(|| {
|
||||
LazyBinaryBlockLast::from_computed_height_date_and_binary_block::<Ratio32, _, _>(
|
||||
LazyBinaryFromHeightLast::from_computed_height_date_and_binary_block::<Ratio32, _, _>(
|
||||
&cfg.name("unrealized_loss_rel_to_own_total_unrealized_pnl"),
|
||||
cfg.version,
|
||||
&unrealized.unrealized_loss,
|
||||
@@ -317,7 +335,7 @@ impl RelativeMetrics {
|
||||
)
|
||||
}),
|
||||
neg_unrealized_loss_rel_to_own_total_unrealized_pnl: extended.then(|| {
|
||||
LazyBinaryBlockLast::from_computed_height_date_and_binary_block::<NegRatio32, _, _>(
|
||||
LazyBinaryFromHeightLast::from_computed_height_date_and_binary_block::<NegRatio32, _, _>(
|
||||
&cfg.name("neg_unrealized_loss_rel_to_own_total_unrealized_pnl"),
|
||||
cfg.version,
|
||||
&unrealized.unrealized_loss,
|
||||
@@ -325,7 +343,7 @@ impl RelativeMetrics {
|
||||
)
|
||||
}),
|
||||
net_unrealized_pnl_rel_to_own_total_unrealized_pnl: extended.then(|| {
|
||||
LazyBinaryBlockLast::from_both_binary_block::<Ratio32, _, _, _, _>(
|
||||
LazyBinaryFromHeightLast::from_both_binary_block::<Ratio32, _, _, _, _>(
|
||||
&cfg.name("net_unrealized_pnl_rel_to_own_total_unrealized_pnl"),
|
||||
cfg.version + v1,
|
||||
&unrealized.net_unrealized_pnl,
|
||||
|
||||
@@ -9,10 +9,9 @@ use vecdb::{AnyStoredVec, AnyVec, Exit, GenericStoredVec};
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{
|
||||
HalfClosePriceTimesSats, HalveDollars, HalveSats, HalveSatsToBitcoin, LazyBinaryValueBlockLast,
|
||||
ValueBlockLast,
|
||||
HalfClosePriceTimesSats, HalveDollars, HalveSats, HalveSatsToBitcoin, LazyBinaryValueFromHeightLast,
|
||||
ValueFromHeightLast,
|
||||
},
|
||||
price,
|
||||
};
|
||||
|
||||
use super::ImportConfig;
|
||||
@@ -20,24 +19,22 @@ use super::ImportConfig;
|
||||
/// Supply metrics for a cohort.
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct SupplyMetrics {
|
||||
pub total: ValueBlockLast,
|
||||
pub halved: LazyBinaryValueBlockLast,
|
||||
pub total: ValueFromHeightLast,
|
||||
pub halved: LazyBinaryValueFromHeightLast,
|
||||
}
|
||||
|
||||
impl SupplyMetrics {
|
||||
/// Import supply metrics from database.
|
||||
pub fn forced_import(cfg: &ImportConfig) -> Result<Self> {
|
||||
let compute_dollars = cfg.compute_dollars();
|
||||
|
||||
let supply = ValueBlockLast::forced_import(
|
||||
let supply = ValueFromHeightLast::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("supply"),
|
||||
cfg.version,
|
||||
cfg.indexes,
|
||||
compute_dollars,
|
||||
cfg.price,
|
||||
)?;
|
||||
|
||||
let supply_half = LazyBinaryValueBlockLast::from_block_source::<
|
||||
let supply_half = LazyBinaryValueFromHeightLast::from_block_source::<
|
||||
HalveSats,
|
||||
HalveSatsToBitcoin,
|
||||
HalfClosePriceTimesSats,
|
||||
@@ -100,11 +97,9 @@ impl SupplyMetrics {
|
||||
pub fn compute_rest_part1(
|
||||
&mut self,
|
||||
indexes: &indexes::Vecs,
|
||||
price: Option<&price::Vecs>,
|
||||
starting_indexes: &ComputeIndexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.total
|
||||
.compute_rest(indexes, price, starting_indexes, exit)
|
||||
self.total.compute_rest(indexes, starting_indexes, exit)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,8 +8,8 @@ use crate::{
|
||||
ComputeIndexes,
|
||||
distribution::state::UnrealizedState,
|
||||
internal::{
|
||||
ComputedHeightDateLast, DollarsMinus, DollarsPlus, LazyBinaryBlockLast, LazyBlockLast,
|
||||
ValueHeightDateLast,
|
||||
ComputedFromHeightAndDateLast, DollarsMinus, DollarsPlus, LazyBinaryFromHeightLast, LazyFromHeightLast,
|
||||
ValueFromHeightAndDateLast,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -19,19 +19,19 @@ use super::ImportConfig;
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct UnrealizedMetrics {
|
||||
// === Supply in Profit/Loss ===
|
||||
pub supply_in_profit: ValueHeightDateLast,
|
||||
pub supply_in_loss: ValueHeightDateLast,
|
||||
pub supply_in_profit: ValueFromHeightAndDateLast,
|
||||
pub supply_in_loss: ValueFromHeightAndDateLast,
|
||||
|
||||
// === Unrealized Profit/Loss ===
|
||||
pub unrealized_profit: ComputedHeightDateLast<Dollars>,
|
||||
pub unrealized_loss: ComputedHeightDateLast<Dollars>,
|
||||
pub unrealized_profit: ComputedFromHeightAndDateLast<Dollars>,
|
||||
pub unrealized_loss: ComputedFromHeightAndDateLast<Dollars>,
|
||||
|
||||
// === Negated ===
|
||||
pub neg_unrealized_loss: LazyBlockLast<Dollars>,
|
||||
pub neg_unrealized_loss: LazyFromHeightLast<Dollars>,
|
||||
|
||||
// === Net and Total ===
|
||||
pub net_unrealized_pnl: LazyBinaryBlockLast<Dollars>,
|
||||
pub total_unrealized_pnl: LazyBinaryBlockLast<Dollars>,
|
||||
pub net_unrealized_pnl: LazyBinaryFromHeightLast<Dollars>,
|
||||
pub total_unrealized_pnl: LazyBinaryFromHeightLast<Dollars>,
|
||||
}
|
||||
|
||||
impl UnrealizedMetrics {
|
||||
@@ -40,7 +40,7 @@ impl UnrealizedMetrics {
|
||||
let compute_dollars = cfg.compute_dollars();
|
||||
|
||||
// === Supply in Profit/Loss ===
|
||||
let supply_in_profit = ValueHeightDateLast::forced_import(
|
||||
let supply_in_profit = ValueFromHeightAndDateLast::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("supply_in_profit"),
|
||||
cfg.version,
|
||||
@@ -48,7 +48,7 @@ impl UnrealizedMetrics {
|
||||
cfg.indexes,
|
||||
cfg.price,
|
||||
)?;
|
||||
let supply_in_loss = ValueHeightDateLast::forced_import(
|
||||
let supply_in_loss = ValueFromHeightAndDateLast::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("supply_in_loss"),
|
||||
cfg.version,
|
||||
@@ -58,13 +58,13 @@ impl UnrealizedMetrics {
|
||||
)?;
|
||||
|
||||
// === Unrealized Profit/Loss ===
|
||||
let unrealized_profit = ComputedHeightDateLast::forced_import(
|
||||
let unrealized_profit = ComputedFromHeightAndDateLast::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("unrealized_profit"),
|
||||
cfg.version,
|
||||
cfg.indexes,
|
||||
)?;
|
||||
let unrealized_loss = ComputedHeightDateLast::forced_import(
|
||||
let unrealized_loss = ComputedFromHeightAndDateLast::forced_import(
|
||||
cfg.db,
|
||||
&cfg.name("unrealized_loss"),
|
||||
cfg.version,
|
||||
@@ -72,20 +72,20 @@ impl UnrealizedMetrics {
|
||||
)?;
|
||||
|
||||
// === Negated ===
|
||||
let neg_unrealized_loss = LazyBlockLast::from_computed_height_date::<Negate>(
|
||||
let neg_unrealized_loss = LazyFromHeightLast::from_computed_height_date::<Negate>(
|
||||
&cfg.name("neg_unrealized_loss"),
|
||||
cfg.version,
|
||||
&unrealized_loss,
|
||||
);
|
||||
|
||||
// === Net and Total ===
|
||||
let net_unrealized_pnl = LazyBinaryBlockLast::from_computed_height_date_last::<DollarsMinus>(
|
||||
let net_unrealized_pnl = LazyBinaryFromHeightLast::from_computed_height_date_last::<DollarsMinus>(
|
||||
&cfg.name("net_unrealized_pnl"),
|
||||
cfg.version,
|
||||
&unrealized_profit,
|
||||
&unrealized_loss,
|
||||
);
|
||||
let total_unrealized_pnl = LazyBinaryBlockLast::from_computed_height_date_last::<DollarsPlus>(
|
||||
let total_unrealized_pnl = LazyBinaryFromHeightLast::from_computed_height_date_last::<DollarsPlus>(
|
||||
&cfg.name("total_unrealized_pnl"),
|
||||
cfg.version,
|
||||
&unrealized_profit,
|
||||
|
||||
@@ -43,9 +43,9 @@ pub struct Vecs {
|
||||
|
||||
pub addr_count: AddrCountVecs,
|
||||
pub empty_addr_count: AddrCountVecs,
|
||||
pub loadedaddressindex_to_loadedaddressindex:
|
||||
pub loadedaddressindex:
|
||||
LazyVecFrom1<LoadedAddressIndex, LoadedAddressIndex, LoadedAddressIndex, LoadedAddressData>,
|
||||
pub emptyaddressindex_to_emptyaddressindex:
|
||||
pub emptyaddressindex:
|
||||
LazyVecFrom1<EmptyAddressIndex, EmptyAddressIndex, EmptyAddressIndex, EmptyAddressData>,
|
||||
}
|
||||
|
||||
@@ -90,13 +90,13 @@ impl Vecs {
|
||||
)?;
|
||||
|
||||
// Identity mappings for traversable
|
||||
let loadedaddressindex_to_loadedaddressindex = LazyVecFrom1::init(
|
||||
let loadedaddressindex = LazyVecFrom1::init(
|
||||
"loadedaddressindex",
|
||||
version,
|
||||
loadedaddressindex_to_loadedaddressdata.boxed_clone(),
|
||||
|index, _| Some(index),
|
||||
);
|
||||
let emptyaddressindex_to_emptyaddressindex = LazyVecFrom1::init(
|
||||
let emptyaddressindex = LazyVecFrom1::init(
|
||||
"emptyaddressindex",
|
||||
version,
|
||||
emptyaddressindex_to_emptyaddressdata.boxed_clone(),
|
||||
@@ -125,8 +125,8 @@ impl Vecs {
|
||||
loaded: loadedaddressindex_to_loadedaddressdata,
|
||||
empty: emptyaddressindex_to_emptyaddressdata,
|
||||
},
|
||||
loadedaddressindex_to_loadedaddressindex,
|
||||
emptyaddressindex_to_emptyaddressindex,
|
||||
loadedaddressindex,
|
||||
emptyaddressindex,
|
||||
|
||||
db,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user