mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-04-24 06:39:58 -07:00
computer: snapshot
This commit is contained in:
@@ -13,25 +13,23 @@ impl Vecs {
|
||||
starting_indexes: &ComputeIndexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
let mut height_to_difficultyepoch_iter =
|
||||
indexes.height.difficultyepoch.into_iter();
|
||||
self.difficultyepoch
|
||||
.compute_all(starting_indexes, exit, |vec| {
|
||||
let mut height_count_iter = indexes.dateindex.height_count.into_iter();
|
||||
vec.compute_transform(
|
||||
starting_indexes.dateindex,
|
||||
&indexes.dateindex.first_height,
|
||||
|(di, height, ..)| {
|
||||
(
|
||||
di,
|
||||
height_to_difficultyepoch_iter
|
||||
.get_unwrap(height + (*height_count_iter.get_unwrap(di) - 1)),
|
||||
)
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
let mut height_to_difficultyepoch_iter = indexes.height.difficultyepoch.into_iter();
|
||||
self.epoch.compute_all(starting_indexes, exit, |vec| {
|
||||
let mut height_count_iter = indexes.dateindex.height_count.into_iter();
|
||||
vec.compute_transform(
|
||||
starting_indexes.dateindex,
|
||||
&indexes.dateindex.first_height,
|
||||
|(di, height, ..)| {
|
||||
(
|
||||
di,
|
||||
height_to_difficultyepoch_iter
|
||||
.get_unwrap(height + (*height_count_iter.get_unwrap(di) - 1)),
|
||||
)
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
self.blocks_before_next_difficulty_adjustment.compute_all(
|
||||
indexes,
|
||||
|
||||
@@ -13,12 +13,7 @@ impl Vecs {
|
||||
let v2 = Version::TWO;
|
||||
|
||||
Ok(Self {
|
||||
difficultyepoch: ComputedDateLast::forced_import(
|
||||
db,
|
||||
"difficultyepoch",
|
||||
version,
|
||||
indexes,
|
||||
)?,
|
||||
epoch: ComputedDateLast::forced_import(db, "difficultyepoch", version, indexes)?,
|
||||
blocks_before_next_difficulty_adjustment: ComputedBlockLast::forced_import(
|
||||
db,
|
||||
"blocks_before_next_difficulty_adjustment",
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::internal::{ComputedBlockLast, ComputedDateLast};
|
||||
/// Difficulty epoch metrics and countdown
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct Vecs {
|
||||
pub difficultyepoch: ComputedDateLast<DifficultyEpoch>,
|
||||
pub epoch: ComputedDateLast<DifficultyEpoch>,
|
||||
pub blocks_before_next_difficulty_adjustment: ComputedBlockLast<StoredU32>,
|
||||
pub days_before_next_difficulty_adjustment: ComputedBlockLast<StoredF32>,
|
||||
}
|
||||
|
||||
@@ -14,23 +14,22 @@ impl Vecs {
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
let mut height_to_halvingepoch_iter = indexes.height.halvingepoch.into_iter();
|
||||
self.halvingepoch
|
||||
.compute_all(starting_indexes, exit, |vec| {
|
||||
let mut height_count_iter = indexes.dateindex.height_count.into_iter();
|
||||
vec.compute_transform(
|
||||
starting_indexes.dateindex,
|
||||
&indexes.dateindex.first_height,
|
||||
|(di, height, ..)| {
|
||||
(
|
||||
di,
|
||||
height_to_halvingepoch_iter
|
||||
.get_unwrap(height + (*height_count_iter.get_unwrap(di) - 1)),
|
||||
)
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
self.epoch.compute_all(starting_indexes, exit, |vec| {
|
||||
let mut height_count_iter = indexes.dateindex.height_count.into_iter();
|
||||
vec.compute_transform(
|
||||
starting_indexes.dateindex,
|
||||
&indexes.dateindex.first_height,
|
||||
|(di, height, ..)| {
|
||||
(
|
||||
di,
|
||||
height_to_halvingepoch_iter
|
||||
.get_unwrap(height + (*height_count_iter.get_unwrap(di) - 1)),
|
||||
)
|
||||
},
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
self.blocks_before_next_halving
|
||||
.compute_all(indexes, starting_indexes, exit, |v| {
|
||||
|
||||
@@ -13,7 +13,7 @@ impl Vecs {
|
||||
let v2 = Version::TWO;
|
||||
|
||||
Ok(Self {
|
||||
halvingepoch: ComputedDateLast::forced_import(db, "halvingepoch", version, indexes)?,
|
||||
epoch: ComputedDateLast::forced_import(db, "halvingepoch", version, indexes)?,
|
||||
blocks_before_next_halving: ComputedBlockLast::forced_import(
|
||||
db,
|
||||
"blocks_before_next_halving",
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::internal::{ComputedBlockLast, ComputedDateLast};
|
||||
/// Halving epoch metrics and countdown
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct Vecs {
|
||||
pub halvingepoch: ComputedDateLast<HalvingEpoch>,
|
||||
pub epoch: ComputedDateLast<HalvingEpoch>,
|
||||
pub blocks_before_next_halving: ComputedBlockLast<StoredU32>,
|
||||
pub days_before_next_halving: ComputedBlockLast<StoredF32>,
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ impl Vecs {
|
||||
.all
|
||||
.metrics
|
||||
.supply
|
||||
.supply
|
||||
.total
|
||||
.sats
|
||||
.height;
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ impl Vecs {
|
||||
.all
|
||||
.metrics
|
||||
.supply
|
||||
.supply
|
||||
.total
|
||||
.bitcoin
|
||||
.height;
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ impl Vecs {
|
||||
.all
|
||||
.metrics
|
||||
.supply
|
||||
.supply
|
||||
.total
|
||||
.bitcoin
|
||||
.height;
|
||||
let realized_price = &distribution
|
||||
|
||||
@@ -20,7 +20,7 @@ impl Vecs {
|
||||
.all
|
||||
.metrics
|
||||
.supply
|
||||
.supply
|
||||
.total
|
||||
.sats
|
||||
.height;
|
||||
|
||||
|
||||
@@ -225,6 +225,7 @@ impl AddressTypeToAddrCountVecs {
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct AddrCountVecs {
|
||||
pub all: ComputedBlockLast<StoredU64>,
|
||||
#[traversable(flatten)]
|
||||
pub by_addresstype: AddressTypeToAddrCountVecs,
|
||||
}
|
||||
|
||||
|
||||
@@ -8,10 +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::ComputedBlockLast,
|
||||
price,
|
||||
};
|
||||
|
||||
@@ -155,7 +152,7 @@ impl DynCohortVecs for AddressCohortVecs {
|
||||
state.inner.supply.value = self
|
||||
.metrics
|
||||
.supply
|
||||
.supply
|
||||
.total
|
||||
.sats
|
||||
.height
|
||||
.read_once(prev_height)?;
|
||||
|
||||
@@ -7,7 +7,7 @@ use brk_types::{DateIndex, Dollars, Height, Version};
|
||||
use rayon::prelude::*;
|
||||
use vecdb::{AnyStoredVec, Database, Exit, IterableVec};
|
||||
|
||||
use crate::{ComputeIndexes, indexes, price, distribution::state::UTXOCohortState};
|
||||
use crate::{ComputeIndexes, distribution::state::UTXOCohortState, indexes, price};
|
||||
|
||||
use crate::distribution::metrics::{CohortMetrics, ImportConfig, RealizedMetrics, SupplyMetrics};
|
||||
|
||||
@@ -149,7 +149,7 @@ impl DynCohortVecs for UTXOCohortVecs {
|
||||
state.supply.value = self
|
||||
.metrics
|
||||
.supply
|
||||
.supply
|
||||
.total
|
||||
.sats
|
||||
.height
|
||||
.read_once(prev_height)?;
|
||||
|
||||
@@ -92,7 +92,11 @@ impl CohortMetrics {
|
||||
|
||||
/// Get minimum length across height-indexed vectors written in block loop.
|
||||
pub fn min_stateful_height_len(&self) -> usize {
|
||||
let mut min = self.supply.min_len().min(self.outputs.min_len()).min(self.activity.min_len());
|
||||
let mut min = self
|
||||
.supply
|
||||
.min_len()
|
||||
.min(self.outputs.min_len())
|
||||
.min(self.activity.min_len());
|
||||
|
||||
if let Some(realized) = &self.realized {
|
||||
min = min.min(realized.min_stateful_height_len());
|
||||
@@ -124,7 +128,8 @@ impl CohortMetrics {
|
||||
/// Push state values to height-indexed vectors.
|
||||
pub fn truncate_push(&mut self, height: Height, state: &CohortState) -> Result<()> {
|
||||
self.supply.truncate_push(height, state.supply.value)?;
|
||||
self.outputs.truncate_push(height, state.supply.utxo_count)?;
|
||||
self.outputs
|
||||
.truncate_push(height, state.supply.utxo_count)?;
|
||||
self.activity.truncate_push(
|
||||
height,
|
||||
state.sent,
|
||||
@@ -309,8 +314,7 @@ impl CohortMetrics {
|
||||
) -> Result<()> {
|
||||
self.supply
|
||||
.compute_rest_part1(indexes, price, starting_indexes, exit)?;
|
||||
self.outputs
|
||||
.compute_rest(indexes, starting_indexes, exit)?;
|
||||
self.outputs.compute_rest(indexes, starting_indexes, exit)?;
|
||||
self.activity
|
||||
.compute_rest_part1(indexes, starting_indexes, exit)?;
|
||||
|
||||
@@ -345,7 +349,7 @@ impl CohortMetrics {
|
||||
indexes,
|
||||
price,
|
||||
starting_indexes,
|
||||
&self.supply.supply.bitcoin.height,
|
||||
&self.supply.total.bitcoin.height,
|
||||
height_to_market_cap,
|
||||
dateindex_to_market_cap,
|
||||
exit,
|
||||
|
||||
@@ -77,14 +77,14 @@ impl RelativeMetrics {
|
||||
let compute_rel_to_all = cfg.compute_rel_to_all();
|
||||
|
||||
// Global sources from "all" cohort
|
||||
let global_supply_sats_height = all_supply.map(|s| &s.supply.sats.height);
|
||||
let global_supply_sats_difficultyepoch = all_supply.map(|s| &s.supply.sats.difficultyepoch);
|
||||
let global_supply_sats_dates = all_supply.map(|s| &s.supply.sats.rest.dates);
|
||||
let global_supply_sats_dateindex = all_supply.map(|s| &s.supply.sats.rest.dateindex);
|
||||
let global_market_cap = all_supply.and_then(|s| s.supply.dollars.as_ref());
|
||||
let global_supply_sats_height = all_supply.map(|s| &s.total.sats.height);
|
||||
let global_supply_sats_difficultyepoch = all_supply.map(|s| &s.total.sats.difficultyepoch);
|
||||
let global_supply_sats_dates = all_supply.map(|s| &s.total.sats.rest.dates);
|
||||
let global_supply_sats_dateindex = all_supply.map(|s| &s.total.sats.rest.dateindex);
|
||||
let global_market_cap = all_supply.and_then(|s| s.total.dollars.as_ref());
|
||||
|
||||
// Own market cap source
|
||||
let own_market_cap = supply.supply.dollars.as_ref();
|
||||
let own_market_cap = supply.total.dollars.as_ref();
|
||||
|
||||
Ok(Self {
|
||||
// === Supply Relative to Circulating Supply (lazy from global supply) ===
|
||||
@@ -94,8 +94,8 @@ impl RelativeMetrics {
|
||||
LazyBinaryDateLast::from_both_derived_last::<PercentageSatsF64>(
|
||||
&cfg.name("supply_rel_to_circulating_supply"),
|
||||
cfg.version + v1,
|
||||
supply.supply.sats.rest.dateindex.boxed_clone(),
|
||||
&supply.supply.sats.rest.dates,
|
||||
supply.total.sats.rest.dateindex.boxed_clone(),
|
||||
&supply.total.sats.rest.dates,
|
||||
global_supply_sats_dateindex.unwrap().boxed_clone(),
|
||||
global_supply_sats_dates.unwrap(),
|
||||
)
|
||||
@@ -107,34 +107,34 @@ impl RelativeMetrics {
|
||||
&cfg.name("supply_in_profit_rel_to_own_supply"),
|
||||
cfg.version + v1,
|
||||
unrealized.supply_in_profit.height.boxed_clone(),
|
||||
supply.supply.sats.height.boxed_clone(),
|
||||
unrealized.supply_in_profit.difficultyepoch.boxed_clone(),
|
||||
supply.supply.sats.difficultyepoch.boxed_clone(),
|
||||
supply.total.sats.height.boxed_clone(),
|
||||
unrealized.supply_in_profit.difficultyepoch.sats.boxed_clone(),
|
||||
supply.total.sats.difficultyepoch.boxed_clone(),
|
||||
unrealized
|
||||
.supply_in_profit
|
||||
.indexes
|
||||
.sats_dateindex
|
||||
.boxed_clone(),
|
||||
&unrealized.supply_in_profit.indexes.sats,
|
||||
supply.supply.sats.rest.dateindex.boxed_clone(),
|
||||
&supply.supply.sats.rest.dates,
|
||||
supply.total.sats.rest.dateindex.boxed_clone(),
|
||||
&supply.total.sats.rest.dates,
|
||||
),
|
||||
supply_in_loss_rel_to_own_supply:
|
||||
LazyBinaryBlockLast::from_height_difficultyepoch_dates::<PercentageSatsF64>(
|
||||
&cfg.name("supply_in_loss_rel_to_own_supply"),
|
||||
cfg.version + v1,
|
||||
unrealized.supply_in_loss.height.boxed_clone(),
|
||||
supply.supply.sats.height.boxed_clone(),
|
||||
unrealized.supply_in_loss.difficultyepoch.boxed_clone(),
|
||||
supply.supply.sats.difficultyepoch.boxed_clone(),
|
||||
supply.total.sats.height.boxed_clone(),
|
||||
unrealized.supply_in_loss.difficultyepoch.sats.boxed_clone(),
|
||||
supply.total.sats.difficultyepoch.boxed_clone(),
|
||||
unrealized
|
||||
.supply_in_loss
|
||||
.indexes
|
||||
.sats_dateindex
|
||||
.boxed_clone(),
|
||||
&unrealized.supply_in_loss.indexes.sats,
|
||||
supply.supply.sats.rest.dateindex.boxed_clone(),
|
||||
&supply.supply.sats.rest.dates,
|
||||
supply.total.sats.rest.dateindex.boxed_clone(),
|
||||
&supply.total.sats.rest.dates,
|
||||
),
|
||||
|
||||
// === Supply in Profit/Loss Relative to Circulating Supply (lazy from global supply) ===
|
||||
@@ -146,7 +146,7 @@ impl RelativeMetrics {
|
||||
cfg.version + v1,
|
||||
unrealized.supply_in_profit.height.boxed_clone(),
|
||||
global_supply_sats_height.unwrap().boxed_clone(),
|
||||
unrealized.supply_in_profit.difficultyepoch.boxed_clone(),
|
||||
unrealized.supply_in_profit.difficultyepoch.sats.boxed_clone(),
|
||||
global_supply_sats_difficultyepoch.unwrap().boxed_clone(),
|
||||
unrealized
|
||||
.supply_in_profit
|
||||
@@ -166,7 +166,7 @@ impl RelativeMetrics {
|
||||
cfg.version + v1,
|
||||
unrealized.supply_in_loss.height.boxed_clone(),
|
||||
global_supply_sats_height.unwrap().boxed_clone(),
|
||||
unrealized.supply_in_loss.difficultyepoch.boxed_clone(),
|
||||
unrealized.supply_in_loss.difficultyepoch.sats.boxed_clone(),
|
||||
global_supply_sats_difficultyepoch.unwrap().boxed_clone(),
|
||||
unrealized
|
||||
.supply_in_loss
|
||||
|
||||
@@ -4,13 +4,13 @@ use brk_types::{Height, Sats, Version};
|
||||
|
||||
use crate::ComputeIndexes;
|
||||
use rayon::prelude::*;
|
||||
use vecdb::{AnyStoredVec, AnyVec, Exit, GenericStoredVec, IterableCloneableVec};
|
||||
use vecdb::{AnyStoredVec, AnyVec, Exit, GenericStoredVec};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{
|
||||
HalfClosePriceTimesSats, HalveDollars, HalveSats, HalveSatsToBitcoin, LazyBlockValue,
|
||||
LazyValueDateLast, ValueBlockLast,
|
||||
HalfClosePriceTimesSats, HalveDollars, HalveSats, HalveSatsToBitcoin, LazyBinaryLastBlockValue,
|
||||
ValueBlockLast,
|
||||
},
|
||||
price,
|
||||
};
|
||||
@@ -20,9 +20,8 @@ use super::ImportConfig;
|
||||
/// Supply metrics for a cohort.
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct SupplyMetrics {
|
||||
pub supply: ValueBlockLast,
|
||||
pub supply_half_value: LazyBlockValue,
|
||||
pub supply_half: LazyValueDateLast,
|
||||
pub total: ValueBlockLast,
|
||||
pub halved: LazyBinaryLastBlockValue,
|
||||
}
|
||||
|
||||
impl SupplyMetrics {
|
||||
@@ -38,52 +37,39 @@ impl SupplyMetrics {
|
||||
compute_dollars,
|
||||
)?;
|
||||
|
||||
let price_source = cfg
|
||||
.price
|
||||
.map(|p| p.usd.split.close.height.boxed_clone());
|
||||
|
||||
// Create lazy supply_half from supply sources
|
||||
let supply_half_value =
|
||||
LazyBlockValue::from_sources::<HalveSats, HalveSatsToBitcoin, HalfClosePriceTimesSats>(
|
||||
&cfg.name("supply_half"),
|
||||
supply.sats.height.boxed_clone(),
|
||||
price_source,
|
||||
cfg.version,
|
||||
);
|
||||
|
||||
let supply_half = LazyValueDateLast::from_block_source::<
|
||||
let supply_half = LazyBinaryLastBlockValue::from_block_source::<
|
||||
HalveSats,
|
||||
HalveSatsToBitcoin,
|
||||
HalfClosePriceTimesSats,
|
||||
HalveDollars,
|
||||
>(&cfg.name("supply_half"), &supply, cfg.version);
|
||||
>(&cfg.name("supply_half"), &supply, cfg.price, cfg.version);
|
||||
|
||||
Ok(Self {
|
||||
supply,
|
||||
supply_half_value,
|
||||
supply_half,
|
||||
total: supply,
|
||||
halved: supply_half,
|
||||
})
|
||||
}
|
||||
|
||||
/// Get minimum length across height-indexed vectors.
|
||||
pub fn min_len(&self) -> usize {
|
||||
self.supply.sats.height.len()
|
||||
self.total.sats.height.len()
|
||||
}
|
||||
|
||||
/// Push supply state values to height-indexed vectors.
|
||||
pub fn truncate_push(&mut self, height: Height, supply: Sats) -> Result<()> {
|
||||
self.supply.sats.height.truncate_push(height, supply)?;
|
||||
self.total.sats.height.truncate_push(height, supply)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Write height-indexed vectors to disk.
|
||||
pub fn write(&mut self) -> Result<()> {
|
||||
self.supply.sats.height.write()?;
|
||||
self.total.sats.height.write()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns a parallel iterator over all vecs for parallel writing.
|
||||
pub fn par_iter_mut(&mut self) -> impl ParallelIterator<Item = &mut dyn AnyStoredVec> {
|
||||
vec![&mut self.supply.sats.height as &mut dyn AnyStoredVec].into_par_iter()
|
||||
vec![&mut self.total.sats.height as &mut dyn AnyStoredVec].into_par_iter()
|
||||
}
|
||||
|
||||
/// Validate computed versions against base version.
|
||||
@@ -99,11 +85,11 @@ impl SupplyMetrics {
|
||||
others: &[&Self],
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.supply.sats.height.compute_sum_of_others(
|
||||
self.total.sats.height.compute_sum_of_others(
|
||||
starting_indexes.height,
|
||||
&others
|
||||
.iter()
|
||||
.map(|v| &v.supply.sats.height)
|
||||
.map(|v| &v.total.sats.height)
|
||||
.collect::<Vec<_>>(),
|
||||
exit,
|
||||
)?;
|
||||
@@ -118,7 +104,7 @@ impl SupplyMetrics {
|
||||
starting_indexes: &ComputeIndexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.supply
|
||||
self.total
|
||||
.compute_rest(indexes, price, starting_indexes, exit)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -297,13 +297,13 @@ impl Vecs {
|
||||
let supply_metrics = &self.utxo_cohorts.all.metrics.supply;
|
||||
|
||||
let height_to_market_cap = supply_metrics
|
||||
.supply
|
||||
.total
|
||||
.dollars
|
||||
.as_ref()
|
||||
.map(|d| d.height.clone());
|
||||
|
||||
let dateindex_to_market_cap = supply_metrics
|
||||
.supply
|
||||
.total
|
||||
.dollars
|
||||
.as_ref()
|
||||
.map(|d| d.dateindex.0.clone());
|
||||
@@ -373,16 +373,10 @@ fn adjust_for_dateindex_gap(
|
||||
}
|
||||
|
||||
// Get the dateindex at the height we want to resume at
|
||||
let required_dateindex: usize = indexes
|
||||
.height
|
||||
.dateindex
|
||||
.read_once(height_based_min)?
|
||||
.into();
|
||||
let required_dateindex: usize = indexes.height.dateindex.read_once(height_based_min)?.into();
|
||||
|
||||
// If dateindex vecs are behind, restart from first height of the missing day
|
||||
if dateindex_min < required_dateindex
|
||||
&& dateindex_min < indexes.dateindex.first_height.len()
|
||||
{
|
||||
if dateindex_min < required_dateindex && dateindex_min < indexes.dateindex.first_height.len() {
|
||||
Ok(indexes
|
||||
.dateindex
|
||||
.first_height
|
||||
|
||||
@@ -29,7 +29,7 @@ where
|
||||
S1T: ComputedVecValue,
|
||||
S2T: ComputedVecValue,
|
||||
{
|
||||
#[traversable(wrap = "sum")]
|
||||
#[traversable(rename = "sum")]
|
||||
pub height: LazyVecFrom2<Height, T, Height, S1T, Height, S2T>,
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
|
||||
@@ -21,7 +21,7 @@ pub struct ComputedBlockSumCum<T>
|
||||
where
|
||||
T: ComputedVecValue + PartialOrd + JsonSchema,
|
||||
{
|
||||
#[traversable(wrap = "sum")]
|
||||
#[traversable(rename = "sum")]
|
||||
pub height: EagerVec<PcoVec<Height, T>>,
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
|
||||
@@ -20,7 +20,7 @@ where
|
||||
S1T: ComputedVecValue,
|
||||
S2T: ComputedVecValue,
|
||||
{
|
||||
#[traversable(wrap = "sum")]
|
||||
#[traversable(rename = "sum")]
|
||||
pub height: LazyVecFrom2<Height, T, Height, S1T, Height, S2T>,
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
|
||||
@@ -21,7 +21,7 @@ where
|
||||
S1T: ComputedVecValue,
|
||||
S2T: ComputedVecValue,
|
||||
{
|
||||
#[traversable(wrap = "sum")]
|
||||
#[traversable(rename = "sum")]
|
||||
pub height: LazyVecFrom2<Height, T, Height, S1T, Height, S2T>,
|
||||
#[traversable(rename = "cumulative")]
|
||||
pub height_cumulative: LazyVecFrom2<Height, T, Height, S1T, Height, S2T>,
|
||||
|
||||
@@ -18,7 +18,7 @@ where
|
||||
T: ComputedVecValue + PartialOrd + JsonSchema,
|
||||
S1T: ComputedVecValue,
|
||||
{
|
||||
#[traversable(wrap = "sum")]
|
||||
#[traversable(rename = "sum")]
|
||||
pub height: LazyVecFrom1<Height, T, Height, S1T>,
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
|
||||
@@ -11,7 +11,7 @@ use vecdb::{
|
||||
use crate::internal::{ComputedVecValue, LastVec, LazyLast};
|
||||
|
||||
#[derive(Clone, Deref, DerefMut, Traversable)]
|
||||
#[traversable(wrap = "last")]
|
||||
#[traversable(transparent)]
|
||||
pub struct LazyTransformLast<I, T, S1T = T>(pub LazyVecFrom1<I, T, I, S1T>)
|
||||
where
|
||||
I: VecIndex,
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
//! Lazy binary value wrapper combining height (with price) + difficultyepoch + date last transforms.
|
||||
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Bitcoin, Close, Dollars, Sats, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{BinaryTransform, IterableCloneableVec, UnaryTransform};
|
||||
|
||||
use super::{LazyBlockValue, LazyTransformedValueDifficultyEpoch};
|
||||
use crate::internal::LazyValueDateLast;
|
||||
use crate::{internal::ValueBlockLast, price};
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
/// Lazy binary value wrapper with height (using price binary transform) + difficultyepoch + date last transforms.
|
||||
///
|
||||
/// Use this when the height-level dollars need a binary transform (e.g., price × sats)
|
||||
/// rather than a unary transform from existing dollars.
|
||||
///
|
||||
/// No merge at this level - denominations (sats, bitcoin, dollars) stay as separate branches.
|
||||
/// Each inner field has merge which combines indexes within each denomination.
|
||||
#[derive(Clone, Deref, DerefMut, Traversable)]
|
||||
pub struct LazyBinaryLastBlockValue {
|
||||
#[traversable(flatten)]
|
||||
pub height: LazyBlockValue,
|
||||
#[traversable(flatten)]
|
||||
pub difficultyepoch: LazyTransformedValueDifficultyEpoch,
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
#[traversable(flatten)]
|
||||
pub dates: LazyValueDateLast,
|
||||
}
|
||||
|
||||
impl LazyBinaryLastBlockValue {
|
||||
pub fn from_block_source<SatsTransform, BitcoinTransform, HeightDollarsTransform, DateDollarsTransform>(
|
||||
name: &str,
|
||||
source: &ValueBlockLast,
|
||||
price: Option<&price::Vecs>,
|
||||
version: Version,
|
||||
) -> Self
|
||||
where
|
||||
SatsTransform: UnaryTransform<Sats, Sats>,
|
||||
BitcoinTransform: UnaryTransform<Sats, Bitcoin>,
|
||||
HeightDollarsTransform: BinaryTransform<Close<Dollars>, Sats, Dollars>,
|
||||
DateDollarsTransform: UnaryTransform<Dollars, Dollars>,
|
||||
{
|
||||
let v = version + VERSION;
|
||||
|
||||
let price_source = price.map(|p| p.usd.split.close.height.boxed_clone());
|
||||
|
||||
let height = LazyBlockValue::from_sources::<SatsTransform, BitcoinTransform, HeightDollarsTransform>(
|
||||
name,
|
||||
source.sats.height.boxed_clone(),
|
||||
price_source,
|
||||
v,
|
||||
);
|
||||
|
||||
let difficultyepoch = LazyTransformedValueDifficultyEpoch::from_block_source::<
|
||||
SatsTransform,
|
||||
BitcoinTransform,
|
||||
HeightDollarsTransform,
|
||||
>(name, source, price, v);
|
||||
|
||||
let dates = LazyValueDateLast::from_block_source::<SatsTransform, BitcoinTransform, DateDollarsTransform>(
|
||||
name, source, v,
|
||||
);
|
||||
|
||||
Self { height, difficultyepoch, dates }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
//! Fully lazy value types for DifficultyEpoch indexing.
|
||||
//!
|
||||
//! Two variants exist for different source patterns:
|
||||
//! - `LazyValueDifficultyEpochFromHeight`: For sources without dollars (computes from price × sats)
|
||||
//! - `LazyTransformedValueDifficultyEpoch`: For transformed views (e.g., halved supply)
|
||||
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Bitcoin, Close, DifficultyEpoch, Dollars, Height, Sats, Version};
|
||||
use vecdb::{BinaryTransform, IterableBoxedVec, IterableCloneableVec, LazyVecFrom1, LazyVecFrom2, UnaryTransform};
|
||||
|
||||
use crate::internal::{ClosePriceTimesSats, LazyLast, SatsToBitcoin};
|
||||
use crate::price;
|
||||
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
/// Lazy value type at difficultyepoch level - computed from height sats + price.
|
||||
///
|
||||
/// Use this when the source only has height-indexed sats (e.g., ValueBlockDateLast).
|
||||
/// Dollars are computed via price × sats binary transform.
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct LazyValueDifficultyEpochFromHeight {
|
||||
pub sats: LazyLast<DifficultyEpoch, Sats, Height, DifficultyEpoch>,
|
||||
pub bitcoin: LazyVecFrom1<DifficultyEpoch, Bitcoin, DifficultyEpoch, Sats>,
|
||||
pub dollars: Option<LazyVecFrom2<DifficultyEpoch, Dollars, DifficultyEpoch, Close<Dollars>, DifficultyEpoch, Sats>>,
|
||||
}
|
||||
|
||||
impl LazyValueDifficultyEpochFromHeight {
|
||||
/// Create from height sats source and difficultyepoch identity.
|
||||
/// Bitcoin is derived from sats. Dollars are computed from price × sats.
|
||||
pub fn from_height_source(
|
||||
name: &str,
|
||||
height_sats: IterableBoxedVec<Height, Sats>,
|
||||
difficultyepoch_identity: IterableBoxedVec<DifficultyEpoch, DifficultyEpoch>,
|
||||
price: Option<&price::Vecs>,
|
||||
version: Version,
|
||||
) -> Self {
|
||||
let v = version + VERSION;
|
||||
|
||||
let sats = LazyLast::from_source(name, v, height_sats, difficultyepoch_identity);
|
||||
|
||||
let bitcoin = LazyVecFrom1::transformed::<SatsToBitcoin>(
|
||||
&format!("{name}_btc"),
|
||||
v,
|
||||
sats.boxed_clone(),
|
||||
);
|
||||
|
||||
let dollars = price.map(|p| {
|
||||
LazyVecFrom2::transformed::<ClosePriceTimesSats>(
|
||||
&format!("{name}_usd"),
|
||||
v,
|
||||
p.usd.split.close.difficultyepoch.boxed_clone(),
|
||||
sats.boxed_clone(),
|
||||
)
|
||||
});
|
||||
|
||||
Self { sats, bitcoin, dollars }
|
||||
}
|
||||
}
|
||||
|
||||
/// Lazy value type at difficultyepoch level - transformed from existing difficultyepoch sources.
|
||||
///
|
||||
/// Use this when creating transformed views (e.g., halved supply) from sources that
|
||||
/// already have difficultyepoch aggregations. Applies transforms to the existing aggregations.
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct LazyTransformedValueDifficultyEpoch {
|
||||
pub sats: LazyVecFrom1<DifficultyEpoch, Sats, DifficultyEpoch, Sats>,
|
||||
pub bitcoin: LazyVecFrom1<DifficultyEpoch, Bitcoin, DifficultyEpoch, Sats>,
|
||||
pub dollars: Option<LazyVecFrom2<DifficultyEpoch, Dollars, DifficultyEpoch, Close<Dollars>, DifficultyEpoch, Sats>>,
|
||||
}
|
||||
|
||||
impl LazyTransformedValueDifficultyEpoch {
|
||||
/// Create transformed difficultyepoch values from a ValueBlockLast source.
|
||||
/// SatsTransform is applied to the source's difficultyepoch sats.
|
||||
/// BitcoinTransform converts source sats to bitcoin (should combine sats transform + conversion).
|
||||
/// Dollars are computed from price × transformed sats.
|
||||
pub fn from_block_source<SatsTransform, BitcoinTransform, DollarsTransform>(
|
||||
name: &str,
|
||||
source: &super::ValueBlockLast,
|
||||
price: Option<&price::Vecs>,
|
||||
version: Version,
|
||||
) -> Self
|
||||
where
|
||||
SatsTransform: UnaryTransform<Sats, Sats>,
|
||||
BitcoinTransform: UnaryTransform<Sats, Bitcoin>,
|
||||
DollarsTransform: BinaryTransform<Close<Dollars>, Sats, Dollars>,
|
||||
{
|
||||
let v = version + VERSION;
|
||||
|
||||
let sats = LazyVecFrom1::transformed::<SatsTransform>(
|
||||
name,
|
||||
v,
|
||||
source.sats.rest.difficultyepoch.boxed_clone(),
|
||||
);
|
||||
|
||||
let bitcoin = LazyVecFrom1::transformed::<BitcoinTransform>(
|
||||
&format!("{name}_btc"),
|
||||
v,
|
||||
source.sats.rest.difficultyepoch.boxed_clone(),
|
||||
);
|
||||
|
||||
let dollars = price.map(|p| {
|
||||
LazyVecFrom2::transformed::<DollarsTransform>(
|
||||
&format!("{name}_usd"),
|
||||
v,
|
||||
p.usd.split.close.difficultyepoch.boxed_clone(),
|
||||
source.sats.rest.difficultyepoch.boxed_clone(),
|
||||
)
|
||||
});
|
||||
|
||||
Self { sats, bitcoin, dollars }
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,10 @@ mod full;
|
||||
mod height;
|
||||
mod last;
|
||||
mod lazy;
|
||||
mod lazy_binary_last;
|
||||
mod lazy_computed_sum_cum;
|
||||
mod lazy_derived;
|
||||
mod lazy_difficultyepoch;
|
||||
mod lazy_height;
|
||||
mod lazy_last;
|
||||
mod lazy_sum_cum;
|
||||
@@ -16,8 +18,10 @@ pub use full::*;
|
||||
pub use height::*;
|
||||
pub use last::*;
|
||||
pub use lazy::*;
|
||||
pub use lazy_binary_last::*;
|
||||
pub use lazy_computed_sum_cum::*;
|
||||
pub use lazy_derived::*;
|
||||
pub use lazy_difficultyepoch::*;
|
||||
pub use lazy_height::*;
|
||||
pub use lazy_last::*;
|
||||
pub use lazy_sum_cum::*;
|
||||
|
||||
@@ -5,26 +5,26 @@
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{DifficultyEpoch, Height, Sats, Version};
|
||||
use brk_types::{Height, Sats, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{Database, EagerVec, Exit, ImportableVec, IterableCloneableVec, PcoVec};
|
||||
|
||||
use crate::{ComputeIndexes, indexes, price};
|
||||
|
||||
use super::super::block::LazyDerivedBlockValue;
|
||||
use super::super::block::{LazyDerivedBlockValue, LazyValueDifficultyEpochFromHeight};
|
||||
use super::ValueDateLast;
|
||||
use crate::internal::LazyLast;
|
||||
|
||||
/// Value type where both height and dateindex are stored independently.
|
||||
/// Dateindex values cannot be derived from height (e.g., unrealized P&L).
|
||||
#[derive(Clone, Deref, DerefMut, Traversable)]
|
||||
#[traversable(merge)]
|
||||
pub struct ValueBlockDateLast {
|
||||
#[traversable(wrap = "sats")]
|
||||
#[traversable(rename = "sats")]
|
||||
pub height: EagerVec<PcoVec<Height, Sats>>,
|
||||
#[traversable(flatten)]
|
||||
pub height_value: LazyDerivedBlockValue,
|
||||
pub difficultyepoch: LazyLast<DifficultyEpoch, Sats, Height, DifficultyEpoch>,
|
||||
#[traversable(flatten)]
|
||||
pub difficultyepoch: LazyValueDifficultyEpochFromHeight,
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
#[traversable(flatten)]
|
||||
@@ -51,11 +51,12 @@ impl ValueBlockDateLast {
|
||||
let height_value =
|
||||
LazyDerivedBlockValue::from_source(name, height.boxed_clone(), v, price_source);
|
||||
|
||||
let difficultyepoch = LazyLast::from_source(
|
||||
let difficultyepoch = LazyValueDifficultyEpochFromHeight::from_height_source(
|
||||
name,
|
||||
v,
|
||||
height.boxed_clone(),
|
||||
indexes.difficultyepoch.identity.boxed_clone(),
|
||||
price,
|
||||
v,
|
||||
);
|
||||
|
||||
let indexes = ValueDateLast::forced_import(db, name, v, compute_dollars, indexes)?;
|
||||
|
||||
@@ -14,9 +14,8 @@ use super::ValueDerivedTxFull;
|
||||
const VERSION: Version = Version::ZERO;
|
||||
|
||||
#[derive(Clone, Deref, DerefMut, Traversable)]
|
||||
#[traversable(merge)]
|
||||
pub struct ValueTxFull {
|
||||
#[traversable(wrap = "sats")]
|
||||
#[traversable(rename = "txindex")]
|
||||
pub base: EagerVec<PcoVec<TxIndex, Sats>>,
|
||||
#[deref]
|
||||
#[deref_mut]
|
||||
|
||||
@@ -153,7 +153,7 @@ impl Vecs {
|
||||
|
||||
let supply_vecs: Vec<_> = amount_range
|
||||
.iter()
|
||||
.map(|c| &c.metrics.supply.supply.sats.dateindex.0)
|
||||
.map(|c| &c.metrics.supply.total.sats.dateindex.0)
|
||||
.collect();
|
||||
let count_vecs: Vec<_> = amount_range
|
||||
.iter()
|
||||
|
||||
@@ -29,7 +29,7 @@ impl Vecs {
|
||||
.all
|
||||
.metrics
|
||||
.supply
|
||||
.supply
|
||||
.total
|
||||
.dollars
|
||||
.as_ref()
|
||||
.zip(transactions.volume.sent_sum.dollars.as_ref())
|
||||
|
||||
@@ -13,6 +13,8 @@ impl Vecs {
|
||||
Self(LazyLastBlockValue::from_block_source::<
|
||||
SatsIdentity,
|
||||
DollarsIdentity,
|
||||
>("circulating_supply", &supply_metrics.supply, version))
|
||||
>(
|
||||
"circulating_supply", &supply_metrics.total, version)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ impl Vecs {
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
// inflation = daily_subsidy / circulating_supply * 365 * 100
|
||||
let circulating_supply = &distribution.utxo_cohorts.all.metrics.supply.supply.sats;
|
||||
let circulating_supply = &distribution.utxo_cohorts.all.metrics.supply.total.sats;
|
||||
|
||||
self.compute_all(starting_indexes, exit, |v| {
|
||||
v.compute_transform2(
|
||||
|
||||
@@ -2,13 +2,16 @@ use brk_types::Version;
|
||||
use vecdb::IterableCloneableVec;
|
||||
|
||||
use super::Vecs;
|
||||
use crate::{distribution, internal::{DollarsIdentity, LazyBlockLast}};
|
||||
use crate::{
|
||||
distribution,
|
||||
internal::{DollarsIdentity, LazyBlockLast},
|
||||
};
|
||||
|
||||
impl Vecs {
|
||||
pub fn import(version: Version, distribution: &distribution::Vecs) -> Option<Self> {
|
||||
let supply_metrics = &distribution.utxo_cohorts.all.metrics.supply;
|
||||
|
||||
supply_metrics.supply.dollars.as_ref().map(|d| {
|
||||
supply_metrics.total.dollars.as_ref().map(|d| {
|
||||
Self(LazyBlockLast::from_computed::<DollarsIdentity>(
|
||||
"market_cap",
|
||||
version,
|
||||
|
||||
@@ -13,7 +13,7 @@ impl Vecs {
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
// velocity = annualized_volume / circulating_supply
|
||||
let circulating_supply = &distribution.utxo_cohorts.all.metrics.supply.supply;
|
||||
let circulating_supply = &distribution.utxo_cohorts.all.metrics.supply.total;
|
||||
|
||||
// BTC velocity
|
||||
self.btc.compute_all(starting_indexes, exit, |v| {
|
||||
|
||||
Reference in New Issue
Block a user