computer: snapshot

This commit is contained in:
nym21
2025-12-28 16:35:17 +01:00
parent f08ac7f916
commit f5790d5c8a
38 changed files with 4340 additions and 3996 deletions

View File

@@ -104,7 +104,10 @@ where
Some(("weekindex".to_string(), self.weekindex.to_tree_node())), Some(("weekindex".to_string(), self.weekindex.to_tree_node())),
Some(("monthindex".to_string(), self.monthindex.to_tree_node())), Some(("monthindex".to_string(), self.monthindex.to_tree_node())),
Some(("quarterindex".to_string(), self.quarterindex.to_tree_node())), Some(("quarterindex".to_string(), self.quarterindex.to_tree_node())),
Some(("semesterindex".to_string(), self.semesterindex.to_tree_node())), Some((
"semesterindex".to_string(),
self.semesterindex.to_tree_node(),
)),
Some(("yearindex".to_string(), self.yearindex.to_tree_node())), Some(("yearindex".to_string(), self.yearindex.to_tree_node())),
Some(("decadeindex".to_string(), self.decadeindex.to_tree_node())), Some(("decadeindex".to_string(), self.decadeindex.to_tree_node())),
] ]

View File

@@ -0,0 +1,163 @@
use brk_traversable::Traversable;
use brk_types::{
DateIndex, DecadeIndex, DifficultyEpoch, Height, MonthIndex, QuarterIndex, SemesterIndex,
Version, WeekIndex, YearIndex,
};
use schemars::JsonSchema;
use vecdb::{AnyExportableVec, BinaryTransform, IterableBoxedVec, LazyVecFrom2};
use super::{ComputedVecValue, ComputedVecsFromHeight, LazyTransform2Builder};
const VERSION: Version = Version::ZERO;
/// Lazy binary transform from two `ComputedVecsFromHeight` sources.
#[derive(Clone)]
pub struct LazyVecsFrom2FromHeight<T, S1T, S2T>
where
T: ComputedVecValue + PartialOrd + JsonSchema,
S1T: ComputedVecValue,
S2T: ComputedVecValue,
{
pub height: LazyVecFrom2<Height, T, Height, S1T, Height, S2T>,
pub height_extra: LazyTransform2Builder<Height, T, S1T, S2T>,
pub dateindex: LazyTransform2Builder<DateIndex, T, S1T, S2T>,
pub weekindex: LazyTransform2Builder<WeekIndex, T, S1T, S2T>,
pub difficultyepoch: LazyTransform2Builder<DifficultyEpoch, T, S1T, S2T>,
pub monthindex: LazyTransform2Builder<MonthIndex, T, S1T, S2T>,
pub quarterindex: LazyTransform2Builder<QuarterIndex, T, S1T, S2T>,
pub semesterindex: LazyTransform2Builder<SemesterIndex, T, S1T, S2T>,
pub yearindex: LazyTransform2Builder<YearIndex, T, S1T, S2T>,
pub decadeindex: LazyTransform2Builder<DecadeIndex, T, S1T, S2T>,
}
impl<T, S1T, S2T> LazyVecsFrom2FromHeight<T, S1T, S2T>
where
T: ComputedVecValue + JsonSchema + 'static,
S1T: ComputedVecValue + JsonSchema,
S2T: ComputedVecValue + JsonSchema,
{
/// Create from two `ComputedVecsFromHeight` sources with explicit height sources.
pub fn from_computed<F: BinaryTransform<S1T, S2T, T>>(
name: &str,
version: Version,
height_source1: IterableBoxedVec<Height, S1T>,
height_source2: IterableBoxedVec<Height, S2T>,
source1: &ComputedVecsFromHeight<S1T>,
source2: &ComputedVecsFromHeight<S2T>,
) -> Self {
let v = version + VERSION;
Self {
height: LazyVecFrom2::transformed::<F>(name, v, height_source1, height_source2),
height_extra: LazyTransform2Builder::from_eager::<F>(
name,
v,
&source1.height_extra,
&source2.height_extra,
),
dateindex: LazyTransform2Builder::from_eager::<F>(
name,
v,
&source1.dateindex,
&source2.dateindex,
),
weekindex: LazyTransform2Builder::from_lazy::<F, _, _, _, _>(
name,
v,
&source1.weekindex,
&source2.weekindex,
),
difficultyepoch: LazyTransform2Builder::from_eager::<F>(
name,
v,
&source1.difficultyepoch,
&source2.difficultyepoch,
),
monthindex: LazyTransform2Builder::from_lazy::<F, _, _, _, _>(
name,
v,
&source1.monthindex,
&source2.monthindex,
),
quarterindex: LazyTransform2Builder::from_lazy::<F, _, _, _, _>(
name,
v,
&source1.quarterindex,
&source2.quarterindex,
),
semesterindex: LazyTransform2Builder::from_lazy::<F, _, _, _, _>(
name,
v,
&source1.semesterindex,
&source2.semesterindex,
),
yearindex: LazyTransform2Builder::from_lazy::<F, _, _, _, _>(
name,
v,
&source1.yearindex,
&source2.yearindex,
),
decadeindex: LazyTransform2Builder::from_lazy::<F, _, _, _, _>(
name,
v,
&source1.decadeindex,
&source2.decadeindex,
),
}
}
}
impl<T, S1T, S2T> Traversable for LazyVecsFrom2FromHeight<T, S1T, S2T>
where
T: ComputedVecValue + JsonSchema,
S1T: ComputedVecValue,
S2T: ComputedVecValue,
{
fn to_tree_node(&self) -> brk_traversable::TreeNode {
let height_extra_node = self.height_extra.to_tree_node();
brk_traversable::TreeNode::Branch(
[
Some(("height".to_string(), self.height.to_tree_node())),
if height_extra_node.is_empty() {
None
} else {
Some(("height_extra".to_string(), height_extra_node))
},
Some(("dateindex".to_string(), self.dateindex.to_tree_node())),
Some(("weekindex".to_string(), self.weekindex.to_tree_node())),
Some((
"difficultyepoch".to_string(),
self.difficultyepoch.to_tree_node(),
)),
Some(("monthindex".to_string(), self.monthindex.to_tree_node())),
Some(("quarterindex".to_string(), self.quarterindex.to_tree_node())),
Some((
"semesterindex".to_string(),
self.semesterindex.to_tree_node(),
)),
Some(("yearindex".to_string(), self.yearindex.to_tree_node())),
Some(("decadeindex".to_string(), self.decadeindex.to_tree_node())),
]
.into_iter()
.flatten()
.collect(),
)
.merge_branches()
.unwrap()
}
fn iter_any_exportable(&self) -> impl Iterator<Item = &dyn AnyExportableVec> {
let mut iter: Box<dyn Iterator<Item = &dyn AnyExportableVec>> =
Box::new(self.height.iter_any_exportable());
iter = Box::new(iter.chain(self.height_extra.iter_any_exportable()));
iter = Box::new(iter.chain(self.dateindex.iter_any_exportable()));
iter = Box::new(iter.chain(self.weekindex.iter_any_exportable()));
iter = Box::new(iter.chain(self.difficultyepoch.iter_any_exportable()));
iter = Box::new(iter.chain(self.monthindex.iter_any_exportable()));
iter = Box::new(iter.chain(self.quarterindex.iter_any_exportable()));
iter = Box::new(iter.chain(self.semesterindex.iter_any_exportable()));
iter = Box::new(iter.chain(self.yearindex.iter_any_exportable()));
iter = Box::new(iter.chain(self.decadeindex.iter_any_exportable()));
iter
}
}

View File

@@ -9,6 +9,7 @@ mod computed_from_height;
mod computed_from_height_strict; mod computed_from_height_strict;
mod computed_from_txindex; mod computed_from_txindex;
mod lazy2_from_dateindex; mod lazy2_from_dateindex;
mod lazy2_from_height;
mod lazy_from_dateindex; mod lazy_from_dateindex;
mod lazy_from_height; mod lazy_from_height;
mod lazy_value_from_dateindex; mod lazy_value_from_dateindex;
@@ -40,6 +41,7 @@ pub use lazy_from_height::*;
pub use lazy_value_from_dateindex::*; pub use lazy_value_from_dateindex::*;
pub use lazy_value_height::*; pub use lazy_value_height::*;
pub use lazy2_from_dateindex::*; pub use lazy2_from_dateindex::*;
pub use lazy2_from_height::*;
// pub use lazy_from_height_strict::*; // pub use lazy_from_height_strict::*;
// pub use lazy_from_txindex::*; // pub use lazy_from_txindex::*;
pub use price_percentiles::*; pub use price_percentiles::*;

View File

@@ -1,4 +1,4 @@
use brk_types::{Bitcoin, Close, Dollars, Sats, StoredF32}; use brk_types::{Bitcoin, Close, Dollars, Sats, StoredF32, StoredF64};
use vecdb::{BinaryTransform, UnaryTransform}; use vecdb::{BinaryTransform, UnaryTransform};
/// (Dollars, Dollars) -> Dollars addition /// (Dollars, Dollars) -> Dollars addition
@@ -163,3 +163,52 @@ impl<const V: u16> UnaryTransform<Dollars, Dollars> for DollarsTimesTenths<V> {
d * (V as f64 / 10.0) d * (V as f64 / 10.0)
} }
} }
// === Percentage Transforms (a/b × 100) ===
/// (Bitcoin, Bitcoin) -> StoredF64 percentage (a/b × 100)
/// Used for supply ratio calculations like supply_in_profit / total_supply × 100
pub struct PercentageBtcF64;
impl BinaryTransform<Bitcoin, Bitcoin, StoredF64> for PercentageBtcF64 {
#[inline(always)]
fn apply(numerator: Bitcoin, denominator: Bitcoin) -> StoredF64 {
// Bitcoin / Bitcoin returns StoredF64, so dereference and multiply
StoredF64::from(*(numerator / denominator) * 100.0)
}
}
/// (Dollars, Dollars) -> StoredF32 percentage (a/b × 100)
/// Used for unrealized/realized ratio calculations
pub struct PercentageDollarsF32;
impl BinaryTransform<Dollars, Dollars, StoredF32> for PercentageDollarsF32 {
#[inline(always)]
fn apply(numerator: Dollars, denominator: Dollars) -> StoredF32 {
// Dollars / Dollars returns StoredF64, so dereference and multiply
StoredF32::from(*(numerator / denominator) * 100.0)
}
}
/// (Dollars, Dollars) -> StoredF32 negated percentage (-(a/b × 100))
/// Used for negated loss ratio calculations, avoiding lazy-from-lazy chains.
pub struct NegPercentageDollarsF32;
impl BinaryTransform<Dollars, Dollars, StoredF32> for NegPercentageDollarsF32 {
#[inline(always)]
fn apply(numerator: Dollars, denominator: Dollars) -> StoredF32 {
// Dollars / Dollars returns StoredF64, so dereference and multiply
StoredF32::from(-(*(numerator / denominator) * 100.0))
}
}
/// (Sats, Sats) -> StoredF64 percentage (a/b × 100)
/// Used for supply ratio calculations (equivalent to Bitcoin/Bitcoin since 1e8 cancels)
pub struct PercentageSatsF64;
impl BinaryTransform<Sats, Sats, StoredF64> for PercentageSatsF64 {
#[inline(always)]
fn apply(numerator: Sats, denominator: Sats) -> StoredF64 {
StoredF64::from((*numerator as f64 / *denominator as f64) * 100.0)
}
}

View File

@@ -9,7 +9,7 @@ use rayon::prelude::*;
use vecdb::{Exit, GenericStoredVec, IterableVec, TypedVecIterator, VecIndex}; use vecdb::{Exit, GenericStoredVec, IterableVec, TypedVecIterator, VecIndex};
use crate::{ use crate::{
chain, indexes, price, txins, chain, indexes, price,
stateful::{ stateful::{
address::AddressTypeToAddressCount, address::AddressTypeToAddressCount,
compute::write::{process_address_updates, write}, compute::write::{process_address_updates, write},
@@ -19,14 +19,15 @@ use crate::{
}, },
states::{BlockState, Transacted}, states::{BlockState, Transacted},
}, },
txins,
utils::OptionExt, utils::OptionExt,
}; };
use super::{ use super::{
super::{ super::{
RangeMap,
cohorts::{AddressCohorts, DynCohortVecs, UTXOCohorts}, cohorts::{AddressCohorts, DynCohortVecs, UTXOCohorts},
vecs::Vecs, vecs::Vecs,
RangeMap,
}, },
BIP30_DUPLICATE_HEIGHT_1, BIP30_DUPLICATE_HEIGHT_2, BIP30_ORIGINAL_HEIGHT_1, BIP30_DUPLICATE_HEIGHT_1, BIP30_DUPLICATE_HEIGHT_2, BIP30_ORIGINAL_HEIGHT_1,
BIP30_ORIGINAL_HEIGHT_2, ComputeContext, FLUSH_INTERVAL, TxInIterators, TxOutIterators, BIP30_ORIGINAL_HEIGHT_2, ComputeContext, FLUSH_INTERVAL, TxInIterators, TxOutIterators,
@@ -62,8 +63,6 @@ pub fn process_blocks(
ctx.price.is_some() ctx.price.is_some()
); );
info!("Setting up references...");
// References to vectors using correct field paths // References to vectors using correct field paths
// From indexer.vecs: // From indexer.vecs:
let height_to_first_txindex = &indexer.vecs.tx.height_to_first_txindex; let height_to_first_txindex = &indexer.vecs.tx.height_to_first_txindex;
@@ -97,8 +96,6 @@ pub fn process_blocks(
let height_to_price_vec = &ctx.height_to_price; let height_to_price_vec = &ctx.height_to_price;
let height_to_timestamp_vec = &ctx.height_to_timestamp; let height_to_timestamp_vec = &ctx.height_to_timestamp;
info!("Creating iterators...");
// Create iterators for sequential access // Create iterators for sequential access
let mut height_to_first_txindex_iter = height_to_first_txindex.into_iter(); let mut height_to_first_txindex_iter = height_to_first_txindex.into_iter();
let mut height_to_first_txoutindex_iter = height_to_first_txoutindex.into_iter(); let mut height_to_first_txoutindex_iter = height_to_first_txoutindex.into_iter();
@@ -116,12 +113,9 @@ pub fn process_blocks(
let mut height_to_price_iter = height_to_price.map(|v| v.into_iter()); let mut height_to_price_iter = height_to_price.map(|v| v.into_iter());
let mut dateindex_to_price_iter = dateindex_to_price.map(|v| v.into_iter()); let mut dateindex_to_price_iter = dateindex_to_price.map(|v| v.into_iter());
info!("Creating readers...");
let mut vr = VecsReaders::new(&vecs.any_address_indexes, &vecs.addresses_data); let mut vr = VecsReaders::new(&vecs.any_address_indexes, &vecs.addresses_data);
// Build txindex -> height lookup map for efficient prev_height computation // Build txindex -> height lookup map for efficient prev_height computation
info!("Building txindex_to_height map...");
let mut txindex_to_height: RangeMap<TxIndex, Height> = { let mut txindex_to_height: RangeMap<TxIndex, Height> = {
let mut map = RangeMap::with_capacity(last_height.to_usize() + 1); let mut map = RangeMap::with_capacity(last_height.to_usize() + 1);
for first_txindex in indexer.vecs.tx.height_to_first_txindex.into_iter() { for first_txindex in indexer.vecs.tx.height_to_first_txindex.into_iter() {
@@ -134,8 +128,6 @@ pub fn process_blocks(
let mut txout_iters = TxOutIterators::new(indexer); let mut txout_iters = TxOutIterators::new(indexer);
let mut txin_iters = TxInIterators::new(indexer, txins, &mut txindex_to_height); let mut txin_iters = TxInIterators::new(indexer, txins, &mut txindex_to_height);
info!("Creating address iterators...");
// Create iterators for first address indexes per type // Create iterators for first address indexes per type
let mut first_p2a_iter = indexer let mut first_p2a_iter = indexer
.vecs .vecs
@@ -178,8 +170,6 @@ pub fn process_blocks(
.height_to_first_p2wshaddressindex .height_to_first_p2wshaddressindex
.into_iter(); .into_iter();
info!("Recovering running totals...");
// Track running totals - recover from previous height if resuming // Track running totals - recover from previous height if resuming
let ( let (
mut unspendable_supply, mut unspendable_supply,
@@ -187,28 +177,23 @@ pub fn process_blocks(
mut addresstype_to_addr_count, mut addresstype_to_addr_count,
mut addresstype_to_empty_addr_count, mut addresstype_to_empty_addr_count,
) = if starting_height > Height::ZERO { ) = if starting_height > Height::ZERO {
info!("Reading unspendable_supply...");
let prev_height = starting_height.decremented().unwrap(); let prev_height = starting_height.decremented().unwrap();
let unspendable = vecs let unspendable = vecs
.height_to_unspendable_supply .height_to_unspendable_supply
.into_iter() .into_iter()
.get_unwrap(prev_height); .get_unwrap(prev_height);
info!("Reading opreturn_supply...");
let opreturn = vecs let opreturn = vecs
.height_to_opreturn_supply .height_to_opreturn_supply
.into_iter() .into_iter()
.get_unwrap(prev_height); .get_unwrap(prev_height);
info!("Reading addresstype_to_addr_count...");
let addr_count = AddressTypeToAddressCount::from(( let addr_count = AddressTypeToAddressCount::from((
&vecs.addresstype_to_height_to_addr_count, &vecs.addresstype_to_height_to_addr_count,
starting_height, starting_height,
)); ));
info!("Reading addresstype_to_empty_addr_count...");
let empty_addr_count = AddressTypeToAddressCount::from(( let empty_addr_count = AddressTypeToAddressCount::from((
&vecs.addresstype_to_height_to_empty_addr_count, &vecs.addresstype_to_height_to_empty_addr_count,
starting_height, starting_height,
)); ));
info!("Recovery complete.");
(unspendable, opreturn, addr_count, empty_addr_count) (unspendable, opreturn, addr_count, empty_addr_count)
} else { } else {
( (
@@ -221,8 +206,6 @@ pub fn process_blocks(
let mut cache = AddressCache::new(); let mut cache = AddressCache::new();
info!("Starting main block iteration...");
// Main block iteration // Main block iteration
for height in starting_height.to_usize()..=last_height.to_usize() { for height in starting_height.to_usize()..=last_height.to_usize() {
let height = Height::from(height); let height = Height::from(height);

View File

@@ -53,13 +53,20 @@ impl CohortMetrics {
pub fn forced_import(cfg: &ImportConfig) -> Result<Self> { pub fn forced_import(cfg: &ImportConfig) -> Result<Self> {
let compute_dollars = cfg.compute_dollars(); let compute_dollars = cfg.compute_dollars();
let supply = SupplyMetrics::forced_import(cfg)?;
let unrealized = compute_dollars let unrealized = compute_dollars
.then(|| UnrealizedMetrics::forced_import(cfg)) .then(|| UnrealizedMetrics::forced_import(cfg))
.transpose()?; .transpose()?;
let relative = unrealized
.as_ref()
.map(|u| RelativeMetrics::forced_import(cfg, u, &supply))
.transpose()?;
Ok(Self { Ok(Self {
filter: cfg.filter.clone(), filter: cfg.filter.clone(),
supply: SupplyMetrics::forced_import(cfg)?, supply,
activity: ActivityMetrics::forced_import(cfg)?, activity: ActivityMetrics::forced_import(cfg)?,
realized: compute_dollars realized: compute_dollars
.then(|| RealizedMetrics::forced_import(cfg)) .then(|| RealizedMetrics::forced_import(cfg))
@@ -67,10 +74,7 @@ impl CohortMetrics {
price_paid: compute_dollars price_paid: compute_dollars
.then(|| PricePaidMetrics::forced_import(cfg)) .then(|| PricePaidMetrics::forced_import(cfg))
.transpose()?, .transpose()?,
relative: unrealized relative,
.as_ref()
.map(|u| RelativeMetrics::forced_import(cfg, u))
.transpose()?,
unrealized, unrealized,
}) })
} }

View File

@@ -11,7 +11,8 @@ use crate::{
Indexes, Indexes,
grouped::{ grouped::{
ComputedRatioVecsFromDateIndex, ComputedVecsFromDateIndex, ComputedVecsFromHeight, ComputedRatioVecsFromDateIndex, ComputedVecsFromDateIndex, ComputedVecsFromHeight,
LazyVecsFromHeight, Source, VecBuilderOptions, LazyVecsFrom2FromHeight, LazyVecsFromHeight, PercentageDollarsF32, Source,
VecBuilderOptions,
}, },
indexes, price, indexes, price,
stateful::states::RealizedState, stateful::states::RealizedState,
@@ -40,10 +41,13 @@ pub struct RealizedMetrics {
pub indexes_to_net_realized_pnl: ComputedVecsFromHeight<Dollars>, pub indexes_to_net_realized_pnl: ComputedVecsFromHeight<Dollars>,
pub indexes_to_realized_value: ComputedVecsFromHeight<Dollars>, pub indexes_to_realized_value: ComputedVecsFromHeight<Dollars>,
// === Realized vs Realized Cap Ratios === // === Realized vs Realized Cap Ratios (lazy) ===
pub indexes_to_realized_profit_rel_to_realized_cap: ComputedVecsFromHeight<StoredF32>, pub indexes_to_realized_profit_rel_to_realized_cap:
pub indexes_to_realized_loss_rel_to_realized_cap: ComputedVecsFromHeight<StoredF32>, LazyVecsFrom2FromHeight<StoredF32, Dollars, Dollars>,
pub indexes_to_net_realized_pnl_rel_to_realized_cap: ComputedVecsFromHeight<StoredF32>, pub indexes_to_realized_loss_rel_to_realized_cap:
LazyVecsFrom2FromHeight<StoredF32, Dollars, Dollars>,
pub indexes_to_net_realized_pnl_rel_to_realized_cap:
LazyVecsFrom2FromHeight<StoredF32, Dollars, Dollars>,
// === Total Realized PnL === // === Total Realized PnL ===
pub indexes_to_total_realized_pnl: LazyVecsFromHeight<Dollars>, pub indexes_to_total_realized_pnl: LazyVecsFromHeight<Dollars>,
@@ -135,21 +139,75 @@ impl RealizedMetrics {
&indexes_to_realized_value, &indexes_to_realized_value,
); );
// Extract vecs needed for lazy ratio construction
let height_to_realized_cap: EagerVec<PcoVec<Height, Dollars>> =
EagerVec::forced_import(cfg.db, &cfg.name("realized_cap"), cfg.version + v0)?;
let indexes_to_realized_cap = ComputedVecsFromHeight::forced_import(
cfg.db,
&cfg.name("realized_cap"),
Source::None,
cfg.version + v0,
cfg.indexes,
last,
)?;
let height_to_realized_profit: EagerVec<PcoVec<Height, Dollars>> =
EagerVec::forced_import(cfg.db, &cfg.name("realized_profit"), cfg.version + v0)?;
let indexes_to_realized_profit = ComputedVecsFromHeight::forced_import(
cfg.db,
&cfg.name("realized_profit"),
Source::None,
cfg.version + v0,
cfg.indexes,
sum_cum,
)?;
let indexes_to_net_realized_pnl = ComputedVecsFromHeight::forced_import(
cfg.db,
&cfg.name("net_realized_pnl"),
Source::Compute,
cfg.version + v0,
cfg.indexes,
sum_cum,
)?;
// Construct lazy ratio vecs (before struct assignment to satisfy borrow checker)
let indexes_to_realized_profit_rel_to_realized_cap =
LazyVecsFrom2FromHeight::from_computed::<PercentageDollarsF32>(
&cfg.name("realized_profit_rel_to_realized_cap"),
cfg.version + v1,
height_to_realized_profit.boxed_clone(),
height_to_realized_cap.boxed_clone(),
&indexes_to_realized_profit,
&indexes_to_realized_cap,
);
let indexes_to_realized_loss_rel_to_realized_cap =
LazyVecsFrom2FromHeight::from_computed::<PercentageDollarsF32>(
&cfg.name("realized_loss_rel_to_realized_cap"),
cfg.version + v1,
height_to_realized_loss.boxed_clone(),
height_to_realized_cap.boxed_clone(),
&indexes_to_realized_loss,
&indexes_to_realized_cap,
);
let indexes_to_net_realized_pnl_rel_to_realized_cap =
LazyVecsFrom2FromHeight::from_computed::<PercentageDollarsF32>(
&cfg.name("net_realized_pnl_rel_to_realized_cap"),
cfg.version + v1,
indexes_to_net_realized_pnl.height.as_ref().unwrap().boxed_clone(),
height_to_realized_cap.boxed_clone(),
&indexes_to_net_realized_pnl,
&indexes_to_realized_cap,
);
Ok(Self { Ok(Self {
// === Realized Cap === // === Realized Cap ===
height_to_realized_cap: EagerVec::forced_import( height_to_realized_cap,
cfg.db, indexes_to_realized_cap,
&cfg.name("realized_cap"),
cfg.version + v0,
)?,
indexes_to_realized_cap: ComputedVecsFromHeight::forced_import(
cfg.db,
&cfg.name("realized_cap"),
Source::None,
cfg.version + v0,
cfg.indexes,
last,
)?,
indexes_to_realized_price: ComputedVecsFromHeight::forced_import( indexes_to_realized_price: ComputedVecsFromHeight::forced_import(
cfg.db, cfg.db,
&cfg.name("realized_price"), &cfg.name("realized_price"),
@@ -188,57 +246,18 @@ impl RealizedMetrics {
)?, )?,
// === Realized Profit/Loss === // === Realized Profit/Loss ===
height_to_realized_profit: EagerVec::forced_import( height_to_realized_profit,
cfg.db, indexes_to_realized_profit,
&cfg.name("realized_profit"),
cfg.version + v0,
)?,
indexes_to_realized_profit: ComputedVecsFromHeight::forced_import(
cfg.db,
&cfg.name("realized_profit"),
Source::None,
cfg.version + v0,
cfg.indexes,
sum_cum,
)?,
height_to_realized_loss, height_to_realized_loss,
indexes_to_realized_loss, indexes_to_realized_loss,
indexes_to_neg_realized_loss, indexes_to_neg_realized_loss,
indexes_to_net_realized_pnl: ComputedVecsFromHeight::forced_import( indexes_to_net_realized_pnl,
cfg.db,
&cfg.name("net_realized_pnl"),
Source::Compute,
cfg.version + v0,
cfg.indexes,
sum_cum,
)?,
indexes_to_realized_value, indexes_to_realized_value,
// === Realized vs Realized Cap Ratios === // === Realized vs Realized Cap Ratios (lazy) ===
indexes_to_realized_profit_rel_to_realized_cap: ComputedVecsFromHeight::forced_import( indexes_to_realized_profit_rel_to_realized_cap,
cfg.db, indexes_to_realized_loss_rel_to_realized_cap,
&cfg.name("realized_profit_rel_to_realized_cap"), indexes_to_net_realized_pnl_rel_to_realized_cap,
Source::Compute,
cfg.version + v0,
cfg.indexes,
sum,
)?,
indexes_to_realized_loss_rel_to_realized_cap: ComputedVecsFromHeight::forced_import(
cfg.db,
&cfg.name("realized_loss_rel_to_realized_cap"),
Source::Compute,
cfg.version + v0,
cfg.indexes,
sum,
)?,
indexes_to_net_realized_pnl_rel_to_realized_cap: ComputedVecsFromHeight::forced_import(
cfg.db,
&cfg.name("net_realized_pnl_rel_to_realized_cap"),
Source::Compute,
cfg.version + v1,
cfg.indexes,
sum,
)?,
// === Total Realized PnL === // === Total Realized PnL ===
indexes_to_total_realized_pnl, indexes_to_total_realized_pnl,
@@ -768,40 +787,6 @@ impl RealizedMetrics {
exit, exit,
)?; )?;
// Ratios relative to realized cap
self.indexes_to_realized_profit_rel_to_realized_cap
.compute_all(indexes, starting_indexes, exit, |vec| {
vec.compute_percentage(
starting_indexes.height,
&self.height_to_realized_profit,
&self.height_to_realized_cap,
exit,
)?;
Ok(())
})?;
self.indexes_to_realized_loss_rel_to_realized_cap
.compute_all(indexes, starting_indexes, exit, |vec| {
vec.compute_percentage(
starting_indexes.height,
&self.height_to_realized_loss,
&self.height_to_realized_cap,
exit,
)?;
Ok(())
})?;
self.indexes_to_net_realized_pnl_rel_to_realized_cap
.compute_all(indexes, starting_indexes, exit, |vec| {
vec.compute_percentage(
starting_indexes.height,
self.indexes_to_net_realized_pnl.height.u(),
&self.height_to_realized_cap,
exit,
)?;
Ok(())
})?;
// Net realized PnL cumulative 30d delta // Net realized PnL cumulative 30d delta
self.indexes_to_net_realized_pnl_cumulative_30d_delta self.indexes_to_net_realized_pnl_cumulative_30d_delta
.compute_all(starting_indexes, exit, |vec| { .compute_all(starting_indexes, exit, |vec| {

View File

@@ -1,6 +1,6 @@
use brk_error::Result; use brk_error::Result;
use brk_traversable::Traversable; use brk_traversable::Traversable;
use brk_types::{Bitcoin, DateIndex, Dollars, Height, StoredF32, StoredF64, Version}; use brk_types::{Bitcoin, DateIndex, Dollars, Height, Sats, StoredF32, StoredF64, Version};
use vecdb::{ use vecdb::{
EagerVec, Exit, ImportableVec, IterableCloneableVec, IterableVec, LazyVecFrom1, LazyVecFrom2, EagerVec, Exit, ImportableVec, IterableCloneableVec, IterableVec, LazyVecFrom1, LazyVecFrom2,
Negate, PcoVec, Negate, PcoVec,
@@ -10,7 +10,8 @@ use crate::{
Indexes, Indexes,
grouped::{ grouped::{
ComputedVecsFromDateIndex, ComputedVecsFromHeight, LazyVecsFrom2FromDateIndex, ComputedVecsFromDateIndex, ComputedVecsFromHeight, LazyVecsFrom2FromDateIndex,
LazyVecsFromDateIndex, NegRatio32, Ratio32, Source, VecBuilderOptions, LazyVecsFromDateIndex, NegPercentageDollarsF32, NegRatio32, PercentageDollarsF32,
PercentageSatsF64, Ratio32, Source, VecBuilderOptions,
}, },
indexes, indexes,
}; };
@@ -26,8 +27,10 @@ pub struct RelativeMetrics {
// === Supply in Profit/Loss Relative to Own Supply === // === Supply in Profit/Loss Relative to Own Supply ===
pub height_to_supply_in_profit_rel_to_own_supply: EagerVec<PcoVec<Height, StoredF64>>, pub height_to_supply_in_profit_rel_to_own_supply: EagerVec<PcoVec<Height, StoredF64>>,
pub height_to_supply_in_loss_rel_to_own_supply: EagerVec<PcoVec<Height, StoredF64>>, pub height_to_supply_in_loss_rel_to_own_supply: EagerVec<PcoVec<Height, StoredF64>>,
pub indexes_to_supply_in_profit_rel_to_own_supply: ComputedVecsFromDateIndex<StoredF64>, pub indexes_to_supply_in_profit_rel_to_own_supply:
pub indexes_to_supply_in_loss_rel_to_own_supply: ComputedVecsFromDateIndex<StoredF64>, LazyVecsFrom2FromDateIndex<StoredF64, Sats, Sats>,
pub indexes_to_supply_in_loss_rel_to_own_supply:
LazyVecsFrom2FromDateIndex<StoredF64, Sats, Sats>,
// === Supply in Profit/Loss Relative to Circulating Supply === // === Supply in Profit/Loss Relative to Circulating Supply ===
pub height_to_supply_in_profit_rel_to_circulating_supply: pub height_to_supply_in_profit_rel_to_circulating_supply:
@@ -60,15 +63,15 @@ pub struct RelativeMetrics {
pub height_to_net_unrealized_pnl_rel_to_own_market_cap: pub height_to_net_unrealized_pnl_rel_to_own_market_cap:
Option<EagerVec<PcoVec<Height, StoredF32>>>, Option<EagerVec<PcoVec<Height, StoredF32>>>,
pub indexes_to_unrealized_profit_rel_to_own_market_cap: pub indexes_to_unrealized_profit_rel_to_own_market_cap:
Option<ComputedVecsFromDateIndex<StoredF32>>, Option<LazyVecsFrom2FromDateIndex<StoredF32, Dollars, Dollars>>,
pub indexes_to_unrealized_loss_rel_to_own_market_cap: pub indexes_to_unrealized_loss_rel_to_own_market_cap:
Option<ComputedVecsFromDateIndex<StoredF32>>, Option<LazyVecsFrom2FromDateIndex<StoredF32, Dollars, Dollars>>,
pub indexes_to_neg_unrealized_loss_rel_to_own_market_cap: pub indexes_to_neg_unrealized_loss_rel_to_own_market_cap:
Option<LazyVecsFromDateIndex<StoredF32>>, Option<LazyVecsFrom2FromDateIndex<StoredF32, Dollars, Dollars>>,
pub indexes_to_net_unrealized_pnl_rel_to_own_market_cap: pub indexes_to_net_unrealized_pnl_rel_to_own_market_cap:
Option<ComputedVecsFromDateIndex<StoredF32>>, Option<LazyVecsFrom2FromDateIndex<StoredF32, Dollars, Dollars>>,
// === Unrealized vs Own Total Unrealized PnL (optional, lazy from unrealized sources) === // === Unrealized vs Own Total Unrealized PnL (optional) ===
pub height_to_unrealized_profit_rel_to_own_total_unrealized_pnl: pub height_to_unrealized_profit_rel_to_own_total_unrealized_pnl:
Option<LazyVecFrom2<Height, StoredF32, Height, Dollars, Height, Dollars>>, Option<LazyVecFrom2<Height, StoredF32, Height, Dollars, Height, Dollars>>,
pub height_to_unrealized_loss_rel_to_own_total_unrealized_pnl: pub height_to_unrealized_loss_rel_to_own_total_unrealized_pnl:
@@ -89,7 +92,11 @@ pub struct RelativeMetrics {
impl RelativeMetrics { impl RelativeMetrics {
/// Import relative metrics from database. /// Import relative metrics from database.
pub fn forced_import(cfg: &ImportConfig, unrealized: &UnrealizedMetrics) -> Result<Self> { pub fn forced_import(
cfg: &ImportConfig,
unrealized: &UnrealizedMetrics,
supply: &SupplyMetrics,
) -> Result<Self> {
let v0 = Version::ZERO; let v0 = Version::ZERO;
let v1 = Version::ONE; let v1 = Version::ONE;
let v2 = Version::new(2); let v2 = Version::new(2);
@@ -156,33 +163,6 @@ impl RelativeMetrics {
) )
}); });
let indexes_to_unrealized_loss_rel_to_own_market_cap: Option<
ComputedVecsFromDateIndex<StoredF32>,
> = (extended && compute_rel_to_all)
.then(|| {
ComputedVecsFromDateIndex::forced_import(
cfg.db,
&cfg.name("unrealized_loss_rel_to_own_market_cap"),
Source::Compute,
cfg.version + v2,
cfg.indexes,
last,
)
})
.transpose()?;
let indexes_to_neg_unrealized_loss_rel_to_own_market_cap =
indexes_to_unrealized_loss_rel_to_own_market_cap
.as_ref()
.map(|source| {
LazyVecsFromDateIndex::from_computed::<Negate>(
&cfg.name("neg_unrealized_loss_rel_to_own_market_cap"),
cfg.version + v2,
source.dateindex.as_ref().map(|v| v.boxed_clone()),
source,
)
});
// Optional: own total unrealized pnl vecs (lazy from unrealized sources) // Optional: own total unrealized pnl vecs (lazy from unrealized sources)
let height_to_unrealized_profit_rel_to_own_total_unrealized_pnl = extended.then(|| { let height_to_unrealized_profit_rel_to_own_total_unrealized_pnl = extended.then(|| {
LazyVecFrom2::transformed::<Ratio32>( LazyVecFrom2::transformed::<Ratio32>(
@@ -283,22 +263,20 @@ impl RelativeMetrics {
cfg.version + v1, cfg.version + v1,
)?, )?,
indexes_to_supply_in_profit_rel_to_own_supply: indexes_to_supply_in_profit_rel_to_own_supply:
ComputedVecsFromDateIndex::forced_import( LazyVecsFrom2FromDateIndex::from_computed::<PercentageSatsF64>(
cfg.db,
&cfg.name("supply_in_profit_rel_to_own_supply"), &cfg.name("supply_in_profit_rel_to_own_supply"),
Source::Compute,
cfg.version + v1, cfg.version + v1,
cfg.indexes, &unrealized.indexes_to_supply_in_profit.sats,
last, &supply.indexes_to_supply.sats,
)?, ),
indexes_to_supply_in_loss_rel_to_own_supply: ComputedVecsFromDateIndex::forced_import( indexes_to_supply_in_loss_rel_to_own_supply: LazyVecsFrom2FromDateIndex::from_computed::<
cfg.db, PercentageSatsF64,
>(
&cfg.name("supply_in_loss_rel_to_own_supply"), &cfg.name("supply_in_loss_rel_to_own_supply"),
Source::Compute,
cfg.version + v1, cfg.version + v1,
cfg.indexes, &unrealized.indexes_to_supply_in_loss.sats,
last, &supply.indexes_to_supply.sats,
)?, ),
// === Supply in Profit/Loss Relative to Circulating Supply === // === Supply in Profit/Loss Relative to Circulating Supply ===
height_to_supply_in_profit_rel_to_circulating_supply: compute_rel_to_all height_to_supply_in_profit_rel_to_circulating_supply: compute_rel_to_all
@@ -401,30 +379,68 @@ impl RelativeMetrics {
.transpose()?, .transpose()?,
indexes_to_unrealized_profit_rel_to_own_market_cap: (extended && compute_rel_to_all) indexes_to_unrealized_profit_rel_to_own_market_cap: (extended && compute_rel_to_all)
.then(|| { .then(|| {
ComputedVecsFromDateIndex::forced_import( supply
cfg.db, .indexes_to_supply
&cfg.name("unrealized_profit_rel_to_own_market_cap"), .dollars
Source::Compute, .as_ref()
cfg.version + v2, .map(|supply_dollars| {
cfg.indexes, LazyVecsFrom2FromDateIndex::from_computed::<PercentageDollarsF32>(
last, &cfg.name("unrealized_profit_rel_to_own_market_cap"),
) cfg.version + v2,
&unrealized.indexes_to_unrealized_profit,
supply_dollars,
)
})
}) })
.transpose()?, .flatten(),
indexes_to_unrealized_loss_rel_to_own_market_cap, indexes_to_unrealized_loss_rel_to_own_market_cap: (extended && compute_rel_to_all)
indexes_to_neg_unrealized_loss_rel_to_own_market_cap, .then(|| {
supply
.indexes_to_supply
.dollars
.as_ref()
.map(|supply_dollars| {
LazyVecsFrom2FromDateIndex::from_computed::<PercentageDollarsF32>(
&cfg.name("unrealized_loss_rel_to_own_market_cap"),
cfg.version + v2,
&unrealized.indexes_to_unrealized_loss,
supply_dollars,
)
})
})
.flatten(),
indexes_to_neg_unrealized_loss_rel_to_own_market_cap: (extended && compute_rel_to_all)
.then(|| {
supply
.indexes_to_supply
.dollars
.as_ref()
.map(|supply_dollars| {
LazyVecsFrom2FromDateIndex::from_computed::<NegPercentageDollarsF32>(
&cfg.name("neg_unrealized_loss_rel_to_own_market_cap"),
cfg.version + v2,
&unrealized.indexes_to_unrealized_loss,
supply_dollars,
)
})
})
.flatten(),
indexes_to_net_unrealized_pnl_rel_to_own_market_cap: (extended && compute_rel_to_all) indexes_to_net_unrealized_pnl_rel_to_own_market_cap: (extended && compute_rel_to_all)
.then(|| { .then(|| {
ComputedVecsFromDateIndex::forced_import( supply
cfg.db, .indexes_to_supply
&cfg.name("net_unrealized_pnl_rel_to_own_market_cap"), .dollars
Source::Compute, .as_ref()
cfg.version + v2, .map(|supply_dollars| {
cfg.indexes, LazyVecsFrom2FromDateIndex::from_computed::<PercentageDollarsF32>(
last, &cfg.name("net_unrealized_pnl_rel_to_own_market_cap"),
) cfg.version + v2,
&unrealized.indexes_to_net_unrealized_pnl,
supply_dollars,
)
})
}) })
.transpose()?, .flatten(),
// === Unrealized vs Own Total Unrealized PnL (optional) === // === Unrealized vs Own Total Unrealized PnL (optional) ===
height_to_unrealized_profit_rel_to_own_total_unrealized_pnl, height_to_unrealized_profit_rel_to_own_total_unrealized_pnl,
@@ -473,6 +489,7 @@ impl RelativeMetrics {
} }
// === Supply in Profit/Loss Relative to Own Supply === // === Supply in Profit/Loss Relative to Own Supply ===
// Note: indexes_to_* versions are now lazy (LazyVecsFrom2FromDateIndex)
if let Some(unrealized) = unrealized { if let Some(unrealized) = unrealized {
self.height_to_supply_in_profit_rel_to_own_supply self.height_to_supply_in_profit_rel_to_own_supply
.compute_percentage( .compute_percentage(
@@ -488,46 +505,6 @@ impl RelativeMetrics {
&supply.height_to_supply_value.bitcoin, &supply.height_to_supply_value.bitcoin,
exit, exit,
)?; )?;
self.indexes_to_supply_in_profit_rel_to_own_supply
.compute_all(starting_indexes, exit, |v| {
if let Some(dateindex_vec) = unrealized
.indexes_to_supply_in_profit
.bitcoin
.dateindex
.as_ref()
&& let Some(supply_dateindex) =
supply.indexes_to_supply.bitcoin.dateindex.as_ref()
{
v.compute_percentage(
starting_indexes.dateindex,
dateindex_vec,
supply_dateindex,
exit,
)?;
}
Ok(())
})?;
self.indexes_to_supply_in_loss_rel_to_own_supply
.compute_all(starting_indexes, exit, |v| {
if let Some(dateindex_vec) = unrealized
.indexes_to_supply_in_loss
.bitcoin
.dateindex
.as_ref()
&& let Some(supply_dateindex) =
supply.indexes_to_supply.bitcoin.dateindex.as_ref()
{
v.compute_percentage(
starting_indexes.dateindex,
dateindex_vec,
supply_dateindex,
exit,
)?;
}
Ok(())
})?;
} }
// === Supply in Profit/Loss Relative to Circulating Supply === // === Supply in Profit/Loss Relative to Circulating Supply ===
@@ -667,6 +644,7 @@ impl RelativeMetrics {
// === Unrealized vs Own Market Cap === // === Unrealized vs Own Market Cap ===
// own_market_cap = supply_value.dollars // own_market_cap = supply_value.dollars
// Note: indexes_to_* versions are now lazy (LazyVecsFrom2FromDateIndex)
if let Some(unrealized) = unrealized { if let Some(unrealized) = unrealized {
if let Some(v) = self if let Some(v) = self
.height_to_unrealized_profit_rel_to_own_market_cap .height_to_unrealized_profit_rel_to_own_market_cap
@@ -704,67 +682,6 @@ impl RelativeMetrics {
exit, exit,
)?; )?;
} }
// indexes versions
if let Some(v) = self
.indexes_to_unrealized_profit_rel_to_own_market_cap
.as_mut()
&& let Some(supply_dollars_dateindex) = supply
.indexes_to_supply
.dollars
.as_ref()
.and_then(|d| d.dateindex.as_ref())
{
v.compute_all(starting_indexes, exit, |vec| {
vec.compute_percentage(
starting_indexes.dateindex,
&unrealized.dateindex_to_unrealized_profit,
supply_dollars_dateindex,
exit,
)?;
Ok(())
})?;
}
if let Some(v) = self
.indexes_to_unrealized_loss_rel_to_own_market_cap
.as_mut()
&& let Some(supply_dollars_dateindex) = supply
.indexes_to_supply
.dollars
.as_ref()
.and_then(|d| d.dateindex.as_ref())
{
v.compute_all(starting_indexes, exit, |vec| {
vec.compute_percentage(
starting_indexes.dateindex,
&unrealized.dateindex_to_unrealized_loss,
supply_dollars_dateindex,
exit,
)?;
Ok(())
})?;
}
if let Some(v) = self
.indexes_to_net_unrealized_pnl_rel_to_own_market_cap
.as_mut()
&& let Some(supply_dollars_dateindex) = supply
.indexes_to_supply
.dollars
.as_ref()
.and_then(|d| d.dateindex.as_ref())
&& let Some(net_pnl_dateindex) =
unrealized.indexes_to_net_unrealized_pnl.dateindex.as_ref()
{
v.compute_all(starting_indexes, exit, |vec| {
vec.compute_percentage(
starting_indexes.dateindex,
net_pnl_dateindex,
supply_dollars_dateindex,
exit,
)?;
Ok(())
})?;
}
} }
Ok(()) Ok(())

View File

@@ -1,6 +1,6 @@
use brk_error::Result; use brk_error::Result;
use brk_traversable::Traversable; use brk_traversable::Traversable;
use brk_types::{Height, Sats, StoredU64, Version}; use brk_types::{Height, Sats, StoredU64, SupplyState, Version};
use rayon::prelude::*; use rayon::prelude::*;
use vecdb::{ use vecdb::{
AnyStoredVec, AnyVec, EagerVec, Exit, GenericStoredVec, ImportableVec, IterableCloneableVec, AnyStoredVec, AnyVec, EagerVec, Exit, GenericStoredVec, ImportableVec, IterableCloneableVec,
@@ -15,7 +15,6 @@ use crate::{
LazyValueVecsFromDateIndex, Source, VecBuilderOptions, LazyValueVecsFromDateIndex, Source, VecBuilderOptions,
}, },
indexes, price, indexes, price,
stateful::states::SupplyState,
}; };
use super::ImportConfig; use super::ImportConfig;
@@ -79,13 +78,16 @@ impl SupplyMetrics {
)?; )?;
// Create lazy supply_half from supply sources // Create lazy supply_half from supply sources
let height_to_supply_half_value = let height_to_supply_half_value = LazyHeightValueVecs::from_sources::<
LazyHeightValueVecs::from_sources::<HalveSats, HalveSatsToBitcoin, HalfClosePriceTimesSats>( HalveSats,
&cfg.name("supply_half"), HalveSatsToBitcoin,
height_to_supply.boxed_clone(), HalfClosePriceTimesSats,
price_source, >(
cfg.version + v0, &cfg.name("supply_half"),
); height_to_supply.boxed_clone(),
price_source,
cfg.version + v0,
);
let indexes_to_supply_half = let indexes_to_supply_half =
LazyValueVecsFromDateIndex::from_source::<HalveSats, HalveSatsToBitcoin, HalveDollars>( LazyValueVecsFromDateIndex::from_source::<HalveSats, HalveSatsToBitcoin, HalveDollars>(

View File

@@ -1,10 +1,10 @@
use std::path::Path; use std::path::Path;
use brk_error::Result; use brk_error::Result;
use brk_types::{Dollars, Height, LoadedAddressData, Sats}; use brk_types::{Dollars, Height, LoadedAddressData, Sats, SupplyState};
use vecdb::unlikely; use vecdb::unlikely;
use crate::stateful::states::{RealizedState, SupplyState}; use crate::stateful::states::RealizedState;
use super::CohortState; use super::CohortState;

View File

@@ -1,10 +1,8 @@
use std::ops::{Add, AddAssign, SubAssign}; use std::ops::{Add, AddAssign, SubAssign};
use brk_types::{Dollars, Timestamp}; use brk_types::{Dollars, SupplyState, Timestamp};
use serde::Serialize; use serde::Serialize;
use super::SupplyState;
#[derive(Debug, Clone, Serialize)] #[derive(Debug, Clone, Serialize)]
pub struct BlockState { pub struct BlockState {
#[serde(flatten)] #[serde(flatten)]

View File

@@ -1,11 +1,11 @@
use std::path::Path; use std::path::Path;
use brk_error::Result; use brk_error::Result;
use brk_types::{Dollars, Height, Sats}; use brk_types::{Dollars, Height, Sats, SupplyState};
use crate::grouped::PERCENTILES_LEN; use crate::grouped::PERCENTILES_LEN;
use super::{CachedUnrealizedState, PriceToAmount, RealizedState, SupplyState, UnrealizedState}; use super::{CachedUnrealizedState, PriceToAmount, RealizedState, UnrealizedState};
/// State tracked for each cohort during computation. /// State tracked for each cohort during computation.
#[derive(Clone)] #[derive(Clone)]

View File

@@ -3,7 +3,6 @@ mod block;
mod cohort; mod cohort;
mod price_to_amount; mod price_to_amount;
mod realized; mod realized;
mod supply;
mod transacted; mod transacted;
mod unrealized; mod unrealized;
mod utxo_cohort; mod utxo_cohort;
@@ -13,7 +12,6 @@ pub use block::*;
pub use cohort::*; pub use cohort::*;
pub use price_to_amount::*; pub use price_to_amount::*;
pub use realized::*; pub use realized::*;
pub use supply::*;
pub use transacted::*; pub use transacted::*;
pub use unrealized::*; pub use unrealized::*;
pub use utxo_cohort::*; pub use utxo_cohort::*;

View File

@@ -6,7 +6,7 @@ use std::{
}; };
use brk_error::{Error, Result}; use brk_error::{Error, Result};
use brk_types::{CentsCompact, Dollars, Height, Sats}; use brk_types::{CentsCompact, Dollars, Height, Sats, SupplyState};
use derive_deref::{Deref, DerefMut}; use derive_deref::{Deref, DerefMut};
use pco::standalone::{simple_decompress, simpler_compress}; use pco::standalone::{simple_decompress, simpler_compress};
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
@@ -18,8 +18,6 @@ use crate::{
utils::OptionExt, utils::OptionExt,
}; };
use super::SupplyState;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct PriceToAmount { pub struct PriceToAmount {
pathbuf: PathBuf, pathbuf: PathBuf,

View File

@@ -1,8 +1,6 @@
use std::cmp::Ordering; use std::cmp::Ordering;
use brk_types::{CheckedSub, Dollars}; use brk_types::{CheckedSub, Dollars, SupplyState};
use super::SupplyState;
#[derive(Debug, Default, Clone)] #[derive(Debug, Default, Clone)]
pub struct RealizedState { pub struct RealizedState {

View File

@@ -1,9 +1,7 @@
use std::ops::{Add, AddAssign}; use std::ops::{Add, AddAssign};
use brk_grouper::{ByAmountRange, GroupedByType}; use brk_grouper::{ByAmountRange, GroupedByType};
use brk_types::{OutputType, Sats}; use brk_types::{OutputType, Sats, SupplyState};
use super::SupplyState;
#[derive(Default, Debug)] #[derive(Default, Debug)]
pub struct Transacted { pub struct Transacted {

View File

@@ -1,10 +1,10 @@
use std::path::Path; use std::path::Path;
use brk_error::Result; use brk_error::Result;
use brk_types::Sats; use brk_types::{Sats, SupplyState};
use derive_deref::{Deref, DerefMut}; use derive_deref::{Deref, DerefMut};
use super::{CohortState, RealizedState, SupplyState}; use super::{CohortState, RealizedState};
#[derive(Clone, Deref, DerefMut)] #[derive(Clone, Deref, DerefMut)]
pub struct UTXOCohortState(CohortState); pub struct UTXOCohortState(CohortState);

View File

@@ -5,7 +5,7 @@ use brk_indexer::Indexer;
use brk_traversable::Traversable; use brk_traversable::Traversable;
use brk_types::{ use brk_types::{
Dollars, EmptyAddressData, EmptyAddressIndex, Height, LoadedAddressData, LoadedAddressIndex, Dollars, EmptyAddressData, EmptyAddressIndex, Height, LoadedAddressData, LoadedAddressIndex,
Sats, StoredU64, Version, Sats, StoredU64, SupplyState, Version,
}; };
use log::info; use log::info;
use vecdb::{ use vecdb::{
@@ -29,7 +29,7 @@ use crate::{
}; };
use super::{ use super::{
AddressCohorts, AddressesDataVecs, AnyAddressIndexesVecs, SupplyState, UTXOCohorts, AddressCohorts, AddressesDataVecs, AnyAddressIndexesVecs, UTXOCohorts,
address::{AddressTypeToHeightToAddressCount, AddressTypeToIndexesToAddressCount}, address::{AddressTypeToHeightToAddressCount, AddressTypeToIndexesToAddressCount},
compute::aggregates, compute::aggregates,
}; };

View File

@@ -1,4 +1,4 @@
use std::collections::BTreeMap; use std::{borrow::Cow, collections::BTreeMap};
use brk_computer::Computer; use brk_computer::Computer;
use brk_indexer::Indexer; use brk_indexer::Indexer;
@@ -73,7 +73,11 @@ impl<'a> Vecs<'a> {
.keys() .keys()
.map(|i| IndexInfo { .map(|i| IndexInfo {
index: *i, index: *i,
aliases: i.possible_values(), aliases: i
.possible_values()
.iter()
.map(|v| Cow::Borrowed(*v))
.collect(),
}) })
.collect(); .collect();
@@ -116,14 +120,20 @@ impl<'a> Vecs<'a> {
.entry(name) .entry(name)
.or_default() .or_default()
.insert(index, vec); .insert(index, vec);
assert!(prev.is_none(), "Duplicate metric: {name} for index {index:?}"); assert!(
prev.is_none(),
"Duplicate metric: {name} for index {index:?}"
);
let prev = self let prev = self
.index_to_metric_to_vec .index_to_metric_to_vec
.entry(index) .entry(index)
.or_default() .or_default()
.insert(name, vec); .insert(name, vec);
assert!(prev.is_none(), "Duplicate metric: {name} for index {index:?}"); assert!(
prev.is_none(),
"Duplicate metric: {name} for index {index:?}"
);
} }
pub fn metrics(&'static self, pagination: Pagination) -> PaginatedMetrics { pub fn metrics(&'static self, pagination: Pagination) -> PaginatedMetrics {
@@ -134,7 +144,10 @@ impl<'a> Vecs<'a> {
PaginatedMetrics { PaginatedMetrics {
current_page: pagination.page(), current_page: pagination.page(),
max_page: len.div_ceil(Pagination::PER_PAGE).saturating_sub(1), max_page: len.div_ceil(Pagination::PER_PAGE).saturating_sub(1),
metrics: &self.metrics[start..end], metrics: self.metrics[start..end]
.iter()
.map(|&s| Cow::Borrowed(s))
.collect(),
} }
} }

View File

@@ -1,4 +1,4 @@
use std::sync::Arc; use std::{borrow::Cow, sync::Arc};
use aide::{ use aide::{
axum::{ApiRouter, routing::get_with}, axum::{ApiRouter, routing::get_with},
@@ -71,8 +71,8 @@ impl ApiRoutes for ApiRouter<AppState> {
get_with( get_with(
async || -> Json<Health> { async || -> Json<Health> {
Json(Health { Json(Health {
status: "healthy", status: Cow::Borrowed("healthy"),
service: "brk", service: Cow::Borrowed("brk"),
timestamp: jiff::Timestamp::now().to_string(), timestamp: jiff::Timestamp::now().to_string(),
}) })
}, },

View File

@@ -1,11 +1,11 @@
use std::ops::Add; use std::ops::Add;
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::Serialize; use serde::{Deserialize, Serialize};
use vecdb::{Formattable, Pco}; use vecdb::{Formattable, Pco};
/// Position within a .blk file, encoding file index and byte offset /// Position within a .blk file, encoding file index and byte offset
#[derive(Debug, Clone, Copy, Serialize, Pco, JsonSchema)] #[derive(Debug, Clone, Copy, Serialize, Deserialize, Pco, JsonSchema)]
pub struct BlkPosition(u64); pub struct BlkPosition(u64);
impl BlkPosition { impl BlkPosition {

View File

@@ -1,10 +1,10 @@
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::Serialize; use serde::{Deserialize, Serialize};
use crate::{Height, Sats, Timestamp}; use crate::{Height, Sats, Timestamp};
/// A single block fees data point. /// A single block fees data point.
#[derive(Debug, Serialize, JsonSchema)] #[derive(Debug, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct BlockFeesEntry { pub struct BlockFeesEntry {
pub avg_height: Height, pub avg_height: Height,

View File

@@ -1,10 +1,10 @@
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::Serialize; use serde::{Deserialize, Serialize};
use crate::Height; use crate::Height;
/// Difficulty adjustment information. /// Difficulty adjustment information.
#[derive(Debug, Clone, Serialize, JsonSchema)] #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct DifficultyAdjustment { pub struct DifficultyAdjustment {
/// Progress through current difficulty epoch (0-100%) /// Progress through current difficulty epoch (0-100%)

View File

@@ -1,10 +1,12 @@
use std::borrow::Cow;
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
/// Server health status /// Server health status
#[derive(Debug, Serialize, Deserialize, JsonSchema)] #[derive(Debug, Serialize, Deserialize, JsonSchema)]
pub struct Health { pub struct Health {
pub status: &'static str, pub status: Cow<'static, str>,
pub service: &'static str, pub service: Cow<'static, str>,
pub timestamp: String, pub timestamp: String,
} }

View File

@@ -1,7 +1,7 @@
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::Serialize; use serde::{Deserialize, Serialize};
/// Hex-encoded string /// Hex-encoded string
#[derive(Debug, Clone, Serialize, JsonSchema)] #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(transparent)] #[serde(transparent)]
pub struct Hex(String); pub struct Hex(String);

View File

@@ -1,15 +1,17 @@
use std::borrow::Cow;
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::Serialize; use serde::{Deserialize, Serialize};
use super::Index; use super::Index;
/// Information about an available index and its query aliases /// Information about an available index and its query aliases
#[derive(Clone, Copy, Serialize, JsonSchema)] #[derive(Clone, Serialize, Deserialize, JsonSchema)]
pub struct IndexInfo { pub struct IndexInfo {
/// The canonical index name /// The canonical index name
pub index: Index, pub index: Index,
/// All Accepted query aliases /// All Accepted query aliases
#[schemars(example = vec!["d", "date", "dateindex"])] #[schemars(example = vec!["d", "date", "dateindex"])]
pub aliases: &'static [&'static str], pub aliases: Vec<Cow<'static, str>>,
} }

View File

@@ -125,6 +125,7 @@ mod stored_u16;
mod stored_u32; mod stored_u32;
mod stored_u64; mod stored_u64;
mod stored_u8; mod stored_u8;
mod supply_state;
mod timeperiod; mod timeperiod;
mod timeperiodparam; mod timeperiodparam;
mod timestamp; mod timestamp;
@@ -280,6 +281,7 @@ pub use stored_u8::*;
pub use stored_u16::*; pub use stored_u16::*;
pub use stored_u32::*; pub use stored_u32::*;
pub use stored_u64::*; pub use stored_u64::*;
pub use supply_state::*;
pub use timeperiod::*; pub use timeperiod::*;
pub use timeperiodparam::*; pub use timeperiodparam::*;
pub use timestamp::*; pub use timestamp::*;

View File

@@ -1,10 +1,10 @@
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::Serialize; use serde::{Deserialize, Serialize};
use crate::{FeeRate, Sats, VSize}; use crate::{FeeRate, Sats, VSize};
/// Block info in a mempool.space like format for fee estimation. /// Block info in a mempool.space like format for fee estimation.
#[derive(Debug, Clone, Serialize, JsonSchema)] #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct MempoolBlock { pub struct MempoolBlock {
/// Total block size in weight units /// Total block size in weight units

View File

@@ -1,8 +1,10 @@
use std::borrow::Cow;
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::Serialize; use serde::{Deserialize, Serialize};
/// A paginated list of available metric names (1000 per page) /// A paginated list of available metric names (1000 per page)
#[derive(Debug, Serialize, JsonSchema)] #[derive(Debug, Serialize, Deserialize, JsonSchema)]
pub struct PaginatedMetrics { pub struct PaginatedMetrics {
/// Current page number (0-indexed) /// Current page number (0-indexed)
#[schemars(example = 0)] #[schemars(example = 0)]
@@ -11,5 +13,5 @@ pub struct PaginatedMetrics {
#[schemars(example = 21)] #[schemars(example = 21)]
pub max_page: usize, pub max_page: usize,
/// List of metric names (max 1000 per page) /// List of metric names (max 1000 per page)
pub metrics: &'static [&'static str], pub metrics: Vec<Cow<'static, str>>,
} }

View File

@@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize};
use crate::{Pool, PoolSlug}; use crate::{Pool, PoolSlug};
/// Detailed pool information with statistics across time periods /// Detailed pool information with statistics across time periods
#[derive(Debug, Serialize, JsonSchema)] #[derive(Debug, Serialize, Deserialize, JsonSchema)]
pub struct PoolDetail { pub struct PoolDetail {
/// Pool information /// Pool information
pub pool: PoolDetailInfo, pub pool: PoolDetailInfo,
@@ -29,22 +29,22 @@ pub struct PoolDetail {
} }
/// Pool information for detail view /// Pool information for detail view
#[derive(Debug, Serialize, JsonSchema)] #[derive(Debug, Serialize, Deserialize, JsonSchema)]
pub struct PoolDetailInfo { pub struct PoolDetailInfo {
/// Unique pool identifier /// Unique pool identifier
pub id: u8, pub id: u8,
/// Pool name /// Pool name
pub name: &'static str, pub name: Cow<'static, str>,
/// Pool website URL /// Pool website URL
pub link: &'static str, pub link: Cow<'static, str>,
/// Known payout addresses /// Known payout addresses
pub addresses: Vec<&'static str>, pub addresses: Vec<Cow<'static, str>>,
/// Coinbase tag patterns (regexes) /// Coinbase tag patterns (regexes)
pub regexes: Vec<&'static str>, pub regexes: Vec<Cow<'static, str>>,
/// URL-friendly pool identifier /// URL-friendly pool identifier
pub slug: PoolSlug, pub slug: PoolSlug,
@@ -54,10 +54,10 @@ impl From<&'static Pool> for PoolDetailInfo {
fn from(pool: &'static Pool) -> Self { fn from(pool: &'static Pool) -> Self {
Self { Self {
id: pool.unique_id(), id: pool.unique_id(),
name: pool.name, name: Cow::Borrowed(pool.name),
link: pool.link, link: Cow::Borrowed(pool.link),
addresses: pool.addresses.to_vec(), addresses: pool.addresses.iter().map(|&s| Cow::Borrowed(s)).collect(),
regexes: pool.tags.to_vec(), regexes: pool.tags.iter().map(|&s| Cow::Borrowed(s)).collect(),
slug: pool.slug(), slug: pool.slug(),
} }
} }

View File

@@ -1,13 +1,15 @@
use std::borrow::Cow;
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::Serialize; use serde::{Deserialize, Serialize};
use crate::{Pool, PoolSlug}; use crate::{Pool, PoolSlug};
/// Basic pool information for listing all pools /// Basic pool information for listing all pools
#[derive(Debug, Serialize, JsonSchema)] #[derive(Debug, Serialize, Deserialize, JsonSchema)]
pub struct PoolInfo { pub struct PoolInfo {
/// Pool name /// Pool name
pub name: &'static str, pub name: Cow<'static, str>,
/// URL-friendly pool identifier /// URL-friendly pool identifier
pub slug: PoolSlug, pub slug: PoolSlug,
@@ -19,7 +21,7 @@ pub struct PoolInfo {
impl From<&'static Pool> for PoolInfo { impl From<&'static Pool> for PoolInfo {
fn from(pool: &'static Pool) -> Self { fn from(pool: &'static Pool) -> Self {
Self { Self {
name: pool.name, name: Cow::Borrowed(pool.name),
slug: pool.slug(), slug: pool.slug(),
unique_id: pool.unique_id(), unique_id: pool.unique_id(),
} }

View File

@@ -1,10 +1,10 @@
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::Serialize; use serde::{Deserialize, Serialize};
use crate::PoolStats; use crate::PoolStats;
/// Mining pools response for a time period /// Mining pools response for a time period
#[derive(Debug, Serialize, JsonSchema)] #[derive(Debug, Serialize, Deserialize, JsonSchema)]
pub struct PoolsSummary { pub struct PoolsSummary {
/// List of pools sorted by block count descending /// List of pools sorted by block count descending
pub pools: Vec<PoolStats>, pub pools: Vec<PoolStats>,

View File

@@ -1,20 +1,22 @@
use std::borrow::Cow;
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::Serialize; use serde::{Deserialize, Serialize};
use crate::{Pool, PoolSlug}; use crate::{Pool, PoolSlug};
/// Mining pool with block statistics for a time period /// Mining pool with block statistics for a time period
#[derive(Debug, Serialize, JsonSchema)] #[derive(Debug, Serialize, Deserialize, JsonSchema)]
pub struct PoolStats { pub struct PoolStats {
/// Unique pool identifier /// Unique pool identifier
#[serde(rename = "poolId")] #[serde(rename = "poolId")]
pub pool_id: u8, pub pool_id: u8,
/// Pool name /// Pool name
pub name: &'static str, pub name: Cow<'static, str>,
/// Pool website URL /// Pool website URL
pub link: &'static str, pub link: Cow<'static, str>,
/// Number of blocks mined in the time period /// Number of blocks mined in the time period
#[serde(rename = "blockCount")] #[serde(rename = "blockCount")]
@@ -39,8 +41,8 @@ impl PoolStats {
pub fn new(pool: &'static Pool, block_count: u32, rank: u32, share: f64) -> Self { pub fn new(pool: &'static Pool, block_count: u32, rank: u32, share: f64) -> Self {
Self { Self {
pool_id: pool.unique_id(), pool_id: pool.unique_id(),
name: pool.name, name: Cow::Borrowed(pool.name),
link: pool.link, link: Cow::Borrowed(pool.link),
block_count, block_count,
rank, rank,
empty_blocks: 0, // TODO: track empty blocks if needed empty_blocks: 0, // TODO: track empty blocks if needed

View File

@@ -1,10 +1,10 @@
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::Serialize; use serde::{Deserialize, Serialize};
use crate::FeeRate; use crate::FeeRate;
/// Recommended fee rates in sat/vB /// Recommended fee rates in sat/vB
#[derive(Debug, Default, Clone, Serialize, JsonSchema)] #[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct RecommendedFees { pub struct RecommendedFees {
/// Fee rate for fastest confirmation (next block) /// Fee rate for fastest confirmation (next block)

View File

@@ -1,10 +1,14 @@
use std::ops::{Add, AddAssign, SubAssign}; use std::{
fmt,
ops::{Add, AddAssign, SubAssign},
};
use brk_types::{CheckedSub, LoadedAddressData, Sats};
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use vecdb::{Bytes, Formattable}; use vecdb::{Bytes, Formattable};
use crate::{CheckedSub, LoadedAddressData, Sats};
/// Current supply state tracking UTXO count and total value /// Current supply state tracking UTXO count and total value
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema)] #[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema)]
pub struct SupplyState { pub struct SupplyState {
@@ -71,8 +75,8 @@ impl From<&LoadedAddressData> for SupplyState {
} }
} }
impl std::fmt::Display for SupplyState { impl fmt::Display for SupplyState {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "utxos: {}, value: {}", self.utxo_count, self.value) write!(f, "utxos: {}, value: {}", self.utxo_count, self.value)
} }
} }

View File

@@ -1,10 +1,10 @@
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::Serialize; use serde::{Deserialize, Serialize};
use crate::{TxStatus, Txid, Vin}; use crate::{TxStatus, Txid, Vin};
/// Status of an output indicating whether it has been spent /// Status of an output indicating whether it has been spent
#[derive(Debug, Clone, Serialize, JsonSchema)] #[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct TxOutspend { pub struct TxOutspend {
/// Whether the output has been spent /// Whether the output has been spent
pub spent: bool, pub spent: bool,

View File

@@ -1,9 +1,11 @@
use derive_deref::Deref; use derive_deref::Deref;
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::Serialize; use serde::{Deserialize, Serialize};
/// Input index in the spending transaction /// Input index in the spending transaction
#[derive(Debug, Deref, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, JsonSchema)] #[derive(
Debug, Deref, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, JsonSchema,
)]
#[schemars(example = 0)] #[schemars(example = 0)]
pub struct Vin(u16); pub struct Vin(u16);

File diff suppressed because it is too large Load Diff