global: snapshot

This commit is contained in:
nym21
2026-03-18 12:51:31 +01:00
parent b397b811f9
commit 455dc683eb
14 changed files with 198 additions and 329 deletions
+22 -38
View File
@@ -1085,40 +1085,6 @@ pub struct CapGrossInvestorLossMvrvNetPeakPriceProfitSellSoprPattern {
pub sopr: AdjustedRatioValuePattern, pub sopr: AdjustedRatioValuePattern,
} }
/// Pattern struct for repeated tree structure.
pub struct AverageCumulativeMaxMedianMinPct10Pct25Pct75Pct90RollingSumPattern {
pub average: SeriesPattern1<StoredU64>,
pub cumulative: SeriesPattern1<StoredU64>,
pub max: SeriesPattern1<StoredU64>,
pub median: SeriesPattern1<StoredU64>,
pub min: SeriesPattern1<StoredU64>,
pub pct10: SeriesPattern1<StoredU64>,
pub pct25: SeriesPattern1<StoredU64>,
pub pct75: SeriesPattern1<StoredU64>,
pub pct90: SeriesPattern1<StoredU64>,
pub rolling: AverageMaxMedianMinPct10Pct25Pct75Pct90SumPattern,
pub sum: SeriesPattern1<StoredU64>,
}
impl AverageCumulativeMaxMedianMinPct10Pct25Pct75Pct90RollingSumPattern {
/// Create a new pattern node with accumulated series name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
average: SeriesPattern1::new(client.clone(), _m(&acc, "average")),
cumulative: SeriesPattern1::new(client.clone(), _m(&acc, "cumulative")),
max: SeriesPattern1::new(client.clone(), _m(&acc, "max")),
median: SeriesPattern1::new(client.clone(), _m(&acc, "median")),
min: SeriesPattern1::new(client.clone(), _m(&acc, "min")),
pct10: SeriesPattern1::new(client.clone(), _m(&acc, "pct10")),
pct25: SeriesPattern1::new(client.clone(), _m(&acc, "pct25")),
pct75: SeriesPattern1::new(client.clone(), _m(&acc, "pct75")),
pct90: SeriesPattern1::new(client.clone(), _m(&acc, "pct90")),
rolling: AverageMaxMedianMinPct10Pct25Pct75Pct90SumPattern::new(client.clone(), acc.clone()),
sum: SeriesPattern1::new(client.clone(), _m(&acc, "sum")),
}
}
}
/// Pattern struct for repeated tree structure. /// Pattern struct for repeated tree structure.
pub struct AverageBaseCumulativeMaxMedianMinPct10Pct25Pct75Pct90SumPattern2 { pub struct AverageBaseCumulativeMaxMedianMinPct10Pct25Pct75Pct90SumPattern2 {
pub average: _1m1w1y24hPattern<StoredU64>, pub average: _1m1w1y24hPattern<StoredU64>,
@@ -2498,6 +2464,24 @@ impl CentsSatsUsdPattern {
} }
} }
/// Pattern struct for repeated tree structure.
pub struct CumulativeRollingSumPattern {
pub cumulative: SeriesPattern1<StoredU64>,
pub rolling: AverageMaxMedianMinPct10Pct25Pct75Pct90SumPattern,
pub sum: SeriesPattern1<StoredU64>,
}
impl CumulativeRollingSumPattern {
/// Create a new pattern node with accumulated series name.
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
Self {
cumulative: SeriesPattern1::new(client.clone(), _m(&acc, "cumulative")),
rolling: AverageMaxMedianMinPct10Pct25Pct75Pct90SumPattern::new(client.clone(), acc.clone()),
sum: SeriesPattern1::new(client.clone(), _m(&acc, "sum")),
}
}
}
/// Pattern struct for repeated tree structure. /// Pattern struct for repeated tree structure.
pub struct DeltaHalfTotalPattern { pub struct DeltaHalfTotalPattern {
pub delta: AbsoluteRatePattern, pub delta: AbsoluteRatePattern,
@@ -3418,7 +3402,7 @@ impl SeriesTree_Transactions_Volume {
pub struct SeriesTree_Inputs { pub struct SeriesTree_Inputs {
pub raw: SeriesTree_Inputs_Raw, pub raw: SeriesTree_Inputs_Raw,
pub spent: SeriesTree_Inputs_Spent, pub spent: SeriesTree_Inputs_Spent,
pub count: AverageCumulativeMaxMedianMinPct10Pct25Pct75Pct90RollingSumPattern, pub count: CumulativeRollingSumPattern,
} }
impl SeriesTree_Inputs { impl SeriesTree_Inputs {
@@ -3426,7 +3410,7 @@ impl SeriesTree_Inputs {
Self { Self {
raw: SeriesTree_Inputs_Raw::new(client.clone(), format!("{base_path}_raw")), raw: SeriesTree_Inputs_Raw::new(client.clone(), format!("{base_path}_raw")),
spent: SeriesTree_Inputs_Spent::new(client.clone(), format!("{base_path}_spent")), spent: SeriesTree_Inputs_Spent::new(client.clone(), format!("{base_path}_spent")),
count: AverageCumulativeMaxMedianMinPct10Pct25Pct75Pct90RollingSumPattern::new(client.clone(), "input_count".to_string()), count: CumulativeRollingSumPattern::new(client.clone(), "input_count".to_string()),
} }
} }
} }
@@ -3520,14 +3504,14 @@ impl SeriesTree_Outputs_Spent {
/// Series tree node. /// Series tree node.
pub struct SeriesTree_Outputs_Count { pub struct SeriesTree_Outputs_Count {
pub total: AverageCumulativeMaxMedianMinPct10Pct25Pct75Pct90RollingSumPattern, pub total: CumulativeRollingSumPattern,
pub unspent: SeriesPattern1<StoredU64>, pub unspent: SeriesPattern1<StoredU64>,
} }
impl SeriesTree_Outputs_Count { impl SeriesTree_Outputs_Count {
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self { pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
Self { Self {
total: AverageCumulativeMaxMedianMinPct10Pct25Pct75Pct90RollingSumPattern::new(client.clone(), "output_count".to_string()), total: CumulativeRollingSumPattern::new(client.clone(), "output_count".to_string()),
unspent: SeriesPattern1::new(client.clone(), "utxo_count_bis".to_string()), unspent: SeriesPattern1::new(client.clone(), "utxo_count_bis".to_string()),
} }
} }
@@ -67,8 +67,8 @@ pub(crate) fn process_blocks(
let height_to_first_txout_index = &indexer.vecs.outputs.first_txout_index; let height_to_first_txout_index = &indexer.vecs.outputs.first_txout_index;
let height_to_first_txin_index = &indexer.vecs.inputs.first_txin_index; let height_to_first_txin_index = &indexer.vecs.inputs.first_txin_index;
let height_to_tx_count = &transactions.count.total.base.height; let height_to_tx_count = &transactions.count.total.base.height;
let height_to_output_count = &outputs.count.total.full.sum.height; let height_to_output_count = &outputs.count.total.sum.height;
let height_to_input_count = &inputs.count.full.sum.height; let height_to_input_count = &inputs.count.sum.height;
let tx_index_to_output_count = &indexes.tx_index.output_count; let tx_index_to_output_count = &indexes.tx_index.output_count;
let tx_index_to_input_count = &indexes.tx_index.input_count; let tx_index_to_input_count = &indexes.tx_index.input_count;
@@ -16,17 +16,15 @@ impl Vecs {
exit: &Exit, exit: &Exit,
) -> Result<()> { ) -> Result<()> {
let window_starts = blocks.lookback.window_starts(); let window_starts = blocks.lookback.window_starts();
self.0 self.0.compute(
.compute(starting_indexes.height, &window_starts, exit, |full| { starting_indexes.height,
full.compute_with_skip( &indexes.tx_index.input_count,
starting_indexes.height, &indexer.vecs.transactions.first_tx_index,
&indexes.tx_index.input_count, &indexes.height.tx_index_count,
&indexer.vecs.transactions.first_tx_index, &window_starts,
&indexes.height.tx_index_count, exit,
exit, 0,
0, )?;
)
})?;
Ok(()) Ok(())
} }
@@ -1,17 +1,15 @@
//! PerBlockAggregated - PerBlockDistributionFull (distribution + sum + cumulative) + RollingComplete.
//!
//! For metrics aggregated per-block from finer-grained sources (e.g., per-tx data),
//! where we want full per-block stats plus rolling window stats.
use brk_error::Result; use brk_error::Result;
use brk_traversable::Traversable; use brk_traversable::Traversable;
use brk_types::{Height, Version}; use brk_types::Height;
use schemars::JsonSchema; use schemars::JsonSchema;
use vecdb::{Database, Exit, Rw, StorageMode}; use vecdb::{Database, Exit, ReadableVec, Rw, StorageMode, VecIndex, VecValue, Version};
use crate::{ use crate::{
indexes, indexes,
internal::{CachedWindowStarts, PerBlockDistributionFull, NumericValue, RollingComplete, WindowStarts}, internal::{
CachedWindowStarts, NumericValue, PerBlock, RollingComplete, WindowStarts,
algo::compute_aggregations,
},
}; };
#[derive(Traversable)] #[derive(Traversable)]
@@ -19,8 +17,8 @@ pub struct PerBlockAggregated<T, M: StorageMode = Rw>
where where
T: NumericValue + JsonSchema, T: NumericValue + JsonSchema,
{ {
#[traversable(flatten)] pub sum: PerBlock<T, M>,
pub full: PerBlockDistributionFull<T, M>, pub cumulative: PerBlock<T, M>,
pub rolling: RollingComplete<T, M>, pub rolling: RollingComplete<T, M>,
} }
@@ -35,34 +33,63 @@ where
indexes: &indexes::Vecs, indexes: &indexes::Vecs,
cached_starts: &CachedWindowStarts, cached_starts: &CachedWindowStarts,
) -> Result<Self> { ) -> Result<Self> {
let full = PerBlockDistributionFull::forced_import(db, name, version, indexes)?; let sum = PerBlock::forced_import(db, &format!("{name}_sum"), version, indexes)?;
let cumulative =
PerBlock::forced_import(db, &format!("{name}_cumulative"), version, indexes)?;
let rolling = RollingComplete::forced_import( let rolling = RollingComplete::forced_import(
db, db,
name, name,
version, version,
indexes, indexes,
&full.cumulative.height, &cumulative.height,
cached_starts, cached_starts,
)?; )?;
Ok(Self { full, rolling }) Ok(Self {
sum,
cumulative,
rolling,
})
} }
/// Compute PerBlockDistributionFull stats via closure, then rolling distribution from the per-block sum. #[allow(clippy::too_many_arguments)]
pub(crate) fn compute( pub(crate) fn compute<A>(
&mut self, &mut self,
max_from: Height, max_from: Height,
source: &impl ReadableVec<A, T>,
first_indexes: &impl ReadableVec<Height, A>,
count_indexes: &impl ReadableVec<Height, brk_types::StoredU64>,
windows: &WindowStarts<'_>, windows: &WindowStarts<'_>,
exit: &Exit, exit: &Exit,
compute_full: impl FnOnce(&mut PerBlockDistributionFull<T>) -> Result<()>, skip_count: usize,
) -> Result<()> ) -> Result<()>
where where
T: From<f64> + Default + Copy + Ord, T: From<f64> + Default + Copy + Ord,
f64: From<T>, f64: From<T>,
A: VecIndex + VecValue + brk_types::CheckedSub<A>,
{ {
compute_full(&mut self.full)?; compute_aggregations(
max_from,
source,
first_indexes,
count_indexes,
exit,
skip_count,
None,
None,
None,
None,
None,
Some(&mut self.sum.height),
Some(&mut self.cumulative.height),
None,
None,
None,
None,
None,
)?;
self.rolling self.rolling
.compute(max_from, windows, &self.full.sum.height, exit)?; .compute(max_from, windows, &self.sum.height, exit)?;
Ok(()) Ok(())
} }
} }
@@ -1,73 +0,0 @@
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::Height;
use schemars::JsonSchema;
use vecdb::{
Database, Exit, ReadableVec, Rw, StorageMode,
VecIndex, VecValue, Version,
};
use crate::{
indexes,
internal::{ComputedVecValue, NumericValue, PerBlock, algo::compute_aggregations},
};
use super::PerBlockDistribution;
#[derive(Traversable)]
pub struct PerBlockDistributionFull<T: ComputedVecValue + PartialOrd + JsonSchema, M: StorageMode = Rw> {
pub sum: PerBlock<T, M>,
pub cumulative: PerBlock<T, M>,
#[traversable(flatten)]
pub distribution: PerBlockDistribution<T, M>,
}
impl<T: NumericValue + JsonSchema> PerBlockDistributionFull<T> {
pub(crate) fn forced_import(
db: &Database,
name: &str,
version: Version,
indexes: &indexes::Vecs,
) -> Result<Self> {
Ok(Self {
distribution: PerBlockDistribution::forced_import(db, name, version, indexes)?,
sum: PerBlock::forced_import(db, &format!("{name}_sum"), version, indexes)?,
cumulative: PerBlock::forced_import(db, &format!("{name}_cumulative"), version, indexes)?,
})
}
pub(crate) fn compute_with_skip<A>(
&mut self,
max_from: Height,
source: &impl ReadableVec<A, T>,
first_indexes: &impl ReadableVec<Height, A>,
count_indexes: &impl ReadableVec<Height, brk_types::StoredU64>,
exit: &Exit,
skip_count: usize,
) -> Result<()>
where
A: VecIndex + VecValue + brk_types::CheckedSub<A>,
{
let d = &mut self.distribution.0;
compute_aggregations(
max_from,
source,
first_indexes,
count_indexes,
exit,
skip_count,
None,
None,
Some(&mut d.min.height),
Some(&mut d.max.height),
Some(&mut d.average.height),
Some(&mut self.sum.height),
Some(&mut self.cumulative.height),
Some(&mut d.median.height),
Some(&mut d.pct10.height),
Some(&mut d.pct25.height),
Some(&mut d.pct75.height),
Some(&mut d.pct90.height),
)
}
}
@@ -3,7 +3,6 @@ mod base;
mod cumulative; mod cumulative;
mod cumulative_sum; mod cumulative_sum;
mod distribution; mod distribution;
mod distribution_full;
mod full; mod full;
mod lazy_distribution; mod lazy_distribution;
mod lazy_rolling; mod lazy_rolling;
@@ -17,7 +16,6 @@ pub use base::*;
pub use cumulative::*; pub use cumulative::*;
pub use cumulative_sum::*; pub use cumulative_sum::*;
pub use distribution::*; pub use distribution::*;
pub use distribution_full::*;
pub use full::*; pub use full::*;
pub use lazy_distribution::*; pub use lazy_distribution::*;
pub use lazy_rolling::*; pub use lazy_rolling::*;
@@ -1,5 +1,6 @@
use brk_traversable::Traversable; use brk_traversable::Traversable;
use brk_types::{Dollars, Height, StoredF32, Version}; use brk_types::{Dollars, Height, StoredF32, Version};
use derive_more::{Deref, DerefMut};
use schemars::JsonSchema; use schemars::JsonSchema;
use vecdb::{DeltaChange, DeltaRate, LazyDeltaVec, LazyVecFrom1, ReadableCloneableVec, VecValue}; use vecdb::{DeltaChange, DeltaRate, LazyDeltaVec, LazyVecFrom1, ReadableCloneableVec, VecValue};
@@ -7,7 +8,7 @@ use crate::{
indexes, indexes,
internal::{ internal::{
BpsType, CachedWindowStarts, CentsType, DerivedResolutions, LazyPerBlock, NumericValue, BpsType, CachedWindowStarts, CentsType, DerivedResolutions, LazyPerBlock, NumericValue,
Resolutions, Windows, Percent, Resolutions, Windows,
}, },
}; };
@@ -31,16 +32,14 @@ where
/// Single-slot lazy delta percent: BPS delta + lazy ratio + lazy percent views. /// Single-slot lazy delta percent: BPS delta + lazy ratio + lazy percent views.
/// ///
/// Mirrors `PercentPerBlock<B>` but with lazy delta for the BPS source. /// Mirrors `PercentPerBlock<B>` but with lazy delta for the BPS source.
#[derive(Clone, Traversable)] #[derive(Clone, Deref, DerefMut, Traversable)]
pub struct LazyDeltaPercentFromHeight<S, B> #[traversable(transparent)]
pub struct LazyDeltaPercentFromHeight<S, B>(
pub Percent<LazyDeltaFromHeight<S, B, DeltaRate>, LazyPerBlock<StoredF32, B>>,
)
where where
S: VecValue, S: VecValue,
B: BpsType, B: BpsType;
{
pub bps: LazyDeltaFromHeight<S, B, DeltaRate>,
pub ratio: LazyPerBlock<StoredF32, B>,
pub percent: LazyPerBlock<StoredF32, B>,
}
/// Lazy rolling deltas for all 4 window durations (24h, 1w, 1m, 1y). /// Lazy rolling deltas for all 4 window durations (24h, 1w, 1m, 1y).
/// ///
@@ -151,11 +150,11 @@ where
)), )),
}; };
let rate = LazyDeltaPercentFromHeight { let rate = LazyDeltaPercentFromHeight(Percent {
bps, bps,
ratio, ratio,
percent, percent,
}; });
(absolute, rate) (absolute, rate)
}; };
@@ -304,11 +303,11 @@ where
)), )),
}; };
let rate = LazyDeltaPercentFromHeight { let rate = LazyDeltaPercentFromHeight(Percent {
bps, bps,
ratio, ratio,
percent, percent,
}; });
(absolute, rate) (absolute, rate)
}; };
@@ -19,22 +19,20 @@ impl Vecs {
exit: &Exit, exit: &Exit,
) -> Result<()> { ) -> Result<()> {
let window_starts = blocks.lookback.window_starts(); let window_starts = blocks.lookback.window_starts();
self.total self.total.compute(
.compute(starting_indexes.height, &window_starts, exit, |full| { starting_indexes.height,
full.compute_with_skip( &indexes.tx_index.output_count,
starting_indexes.height, &indexer.vecs.transactions.first_tx_index,
&indexes.tx_index.output_count, &indexes.height.tx_index_count,
&indexer.vecs.transactions.first_tx_index, &window_starts,
&indexes.height.tx_index_count, exit,
exit, 0,
0, )?;
)
})?;
self.unspent.height.compute_transform3( self.unspent.height.compute_transform3(
starting_indexes.height, starting_indexes.height,
&self.total.full.cumulative.height, &self.total.cumulative.height,
&inputs_count.full.cumulative.height, &inputs_count.cumulative.height,
&scripts_count.op_return.cumulative.height, &scripts_count.op_return.cumulative.height,
|(h, output_count, input_count, op_return_count, ..)| { |(h, output_count, input_count, op_return_count, ..)| {
let block_count = u64::from(h + 1_usize); let block_count = u64::from(h + 1_usize);
+2 -2
View File
@@ -39,14 +39,14 @@ impl Vecs {
self.taproot.compute_binary::<_, _, RatioU64Bp16>( self.taproot.compute_binary::<_, _, RatioU64Bp16>(
starting_indexes.height, starting_indexes.height,
&count.p2tr.base.height, &count.p2tr.base.height,
&outputs_count.total.full.sum.height, &outputs_count.total.sum.height,
exit, exit,
)?; )?;
self.segwit.compute_binary::<_, _, RatioU64Bp16>( self.segwit.compute_binary::<_, _, RatioU64Bp16>(
starting_indexes.height, starting_indexes.height,
&count.segwit.base.height, &count.segwit.base.height,
&outputs_count.total.full.sum.height, &outputs_count.total.sum.height,
exit, exit,
)?; )?;
@@ -50,7 +50,7 @@ impl Vecs {
.height .height
.compute_binary::<_, Timestamp, PerSec>( .compute_binary::<_, Timestamp, PerSec>(
starting_indexes.height, starting_indexes.height,
&inputs_count.full.sum.height, &inputs_count.sum.height,
&blocks.interval.base, &blocks.interval.base,
exit, exit,
)?; )?;
@@ -58,7 +58,7 @@ impl Vecs {
.height .height
.compute_binary::<_, Timestamp, PerSec>( .compute_binary::<_, Timestamp, PerSec>(
starting_indexes.height, starting_indexes.height,
&outputs_count.total.full.sum.height, &outputs_count.total.sum.height,
&blocks.interval.base, &blocks.interval.base,
exit, exit,
)?; )?;
+25 -41
View File
@@ -1795,43 +1795,6 @@ function create_10y1m1w1y2y3m3y4y5y6m6y8yPattern3(client, acc) {
* @property {AdjustedRatioValuePattern} sopr * @property {AdjustedRatioValuePattern} sopr
*/ */
/**
* @typedef {Object} AverageCumulativeMaxMedianMinPct10Pct25Pct75Pct90RollingSumPattern
* @property {SeriesPattern1<StoredU64>} average
* @property {SeriesPattern1<StoredU64>} cumulative
* @property {SeriesPattern1<StoredU64>} max
* @property {SeriesPattern1<StoredU64>} median
* @property {SeriesPattern1<StoredU64>} min
* @property {SeriesPattern1<StoredU64>} pct10
* @property {SeriesPattern1<StoredU64>} pct25
* @property {SeriesPattern1<StoredU64>} pct75
* @property {SeriesPattern1<StoredU64>} pct90
* @property {AverageMaxMedianMinPct10Pct25Pct75Pct90SumPattern} rolling
* @property {SeriesPattern1<StoredU64>} sum
*/
/**
* Create a AverageCumulativeMaxMedianMinPct10Pct25Pct75Pct90RollingSumPattern pattern node
* @param {BrkClientBase} client
* @param {string} acc - Accumulated series name
* @returns {AverageCumulativeMaxMedianMinPct10Pct25Pct75Pct90RollingSumPattern}
*/
function createAverageCumulativeMaxMedianMinPct10Pct25Pct75Pct90RollingSumPattern(client, acc) {
return {
average: createSeriesPattern1(client, _m(acc, 'average')),
cumulative: createSeriesPattern1(client, _m(acc, 'cumulative')),
max: createSeriesPattern1(client, _m(acc, 'max')),
median: createSeriesPattern1(client, _m(acc, 'median')),
min: createSeriesPattern1(client, _m(acc, 'min')),
pct10: createSeriesPattern1(client, _m(acc, 'pct10')),
pct25: createSeriesPattern1(client, _m(acc, 'pct25')),
pct75: createSeriesPattern1(client, _m(acc, 'pct75')),
pct90: createSeriesPattern1(client, _m(acc, 'pct90')),
rolling: createAverageMaxMedianMinPct10Pct25Pct75Pct90SumPattern(client, acc),
sum: createSeriesPattern1(client, _m(acc, 'sum')),
};
}
/** /**
* @typedef {Object} AverageBaseCumulativeMaxMedianMinPct10Pct25Pct75Pct90SumPattern2 * @typedef {Object} AverageBaseCumulativeMaxMedianMinPct10Pct25Pct75Pct90SumPattern2
* @property {_1m1w1y24hPattern<StoredU64>} average * @property {_1m1w1y24hPattern<StoredU64>} average
@@ -3395,6 +3358,27 @@ function createCentsSatsUsdPattern(client, acc) {
}; };
} }
/**
* @typedef {Object} CumulativeRollingSumPattern
* @property {SeriesPattern1<StoredU64>} cumulative
* @property {AverageMaxMedianMinPct10Pct25Pct75Pct90SumPattern} rolling
* @property {SeriesPattern1<StoredU64>} sum
*/
/**
* Create a CumulativeRollingSumPattern pattern node
* @param {BrkClientBase} client
* @param {string} acc - Accumulated series name
* @returns {CumulativeRollingSumPattern}
*/
function createCumulativeRollingSumPattern(client, acc) {
return {
cumulative: createSeriesPattern1(client, _m(acc, 'cumulative')),
rolling: createAverageMaxMedianMinPct10Pct25Pct75Pct90SumPattern(client, acc),
sum: createSeriesPattern1(client, _m(acc, 'sum')),
};
}
/** /**
* @typedef {Object} DeltaHalfTotalPattern * @typedef {Object} DeltaHalfTotalPattern
* @property {AbsoluteRatePattern} delta * @property {AbsoluteRatePattern} delta
@@ -4125,7 +4109,7 @@ function createUnspentPattern(client, acc) {
* @typedef {Object} SeriesTree_Inputs * @typedef {Object} SeriesTree_Inputs
* @property {SeriesTree_Inputs_Raw} raw * @property {SeriesTree_Inputs_Raw} raw
* @property {SeriesTree_Inputs_Spent} spent * @property {SeriesTree_Inputs_Spent} spent
* @property {AverageCumulativeMaxMedianMinPct10Pct25Pct75Pct90RollingSumPattern} count * @property {CumulativeRollingSumPattern} count
*/ */
/** /**
@@ -4166,7 +4150,7 @@ function createUnspentPattern(client, acc) {
/** /**
* @typedef {Object} SeriesTree_Outputs_Count * @typedef {Object} SeriesTree_Outputs_Count
* @property {AverageCumulativeMaxMedianMinPct10Pct25Pct75Pct90RollingSumPattern} total * @property {CumulativeRollingSumPattern} total
* @property {SeriesPattern1<StoredU64>} unspent * @property {SeriesPattern1<StoredU64>} unspent
*/ */
@@ -7645,7 +7629,7 @@ class BrkClient extends BrkClientBase {
txoutIndex: createSeriesPattern20(this, 'txout_index'), txoutIndex: createSeriesPattern20(this, 'txout_index'),
value: createSeriesPattern20(this, 'value'), value: createSeriesPattern20(this, 'value'),
}, },
count: createAverageCumulativeMaxMedianMinPct10Pct25Pct75Pct90RollingSumPattern(this, 'input_count'), count: createCumulativeRollingSumPattern(this, 'input_count'),
}, },
outputs: { outputs: {
raw: { raw: {
@@ -7659,7 +7643,7 @@ class BrkClient extends BrkClientBase {
txinIndex: createSeriesPattern21(this, 'txin_index'), txinIndex: createSeriesPattern21(this, 'txin_index'),
}, },
count: { count: {
total: createAverageCumulativeMaxMedianMinPct10Pct25Pct75Pct90RollingSumPattern(this, 'output_count'), total: createCumulativeRollingSumPattern(this, 'output_count'),
unspent: createSeriesPattern1(this, 'utxo_count_bis'), unspent: createSeriesPattern1(this, 'utxo_count_bis'),
}, },
}, },
+11 -19
View File
@@ -2221,23 +2221,6 @@ class CapGrossInvestorLossMvrvNetPeakPriceProfitSellSoprPattern:
"""Pattern struct for repeated tree structure.""" """Pattern struct for repeated tree structure."""
pass pass
class AverageCumulativeMaxMedianMinPct10Pct25Pct75Pct90RollingSumPattern:
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated series name."""
self.average: SeriesPattern1[StoredU64] = SeriesPattern1(client, _m(acc, 'average'))
self.cumulative: SeriesPattern1[StoredU64] = SeriesPattern1(client, _m(acc, 'cumulative'))
self.max: SeriesPattern1[StoredU64] = SeriesPattern1(client, _m(acc, 'max'))
self.median: SeriesPattern1[StoredU64] = SeriesPattern1(client, _m(acc, 'median'))
self.min: SeriesPattern1[StoredU64] = SeriesPattern1(client, _m(acc, 'min'))
self.pct10: SeriesPattern1[StoredU64] = SeriesPattern1(client, _m(acc, 'pct10'))
self.pct25: SeriesPattern1[StoredU64] = SeriesPattern1(client, _m(acc, 'pct25'))
self.pct75: SeriesPattern1[StoredU64] = SeriesPattern1(client, _m(acc, 'pct75'))
self.pct90: SeriesPattern1[StoredU64] = SeriesPattern1(client, _m(acc, 'pct90'))
self.rolling: AverageMaxMedianMinPct10Pct25Pct75Pct90SumPattern = AverageMaxMedianMinPct10Pct25Pct75Pct90SumPattern(client, acc)
self.sum: SeriesPattern1[StoredU64] = SeriesPattern1(client, _m(acc, 'sum'))
class AverageBaseCumulativeMaxMedianMinPct10Pct25Pct75Pct90SumPattern2: class AverageBaseCumulativeMaxMedianMinPct10Pct25Pct75Pct90SumPattern2:
"""Pattern struct for repeated tree structure.""" """Pattern struct for repeated tree structure."""
@@ -2916,6 +2899,15 @@ class CentsSatsUsdPattern:
self.sats: SeriesPattern1[SatsFract] = SeriesPattern1(client, _m(acc, 'sats')) self.sats: SeriesPattern1[SatsFract] = SeriesPattern1(client, _m(acc, 'sats'))
self.usd: SeriesPattern1[Dollars] = SeriesPattern1(client, acc) self.usd: SeriesPattern1[Dollars] = SeriesPattern1(client, acc)
class CumulativeRollingSumPattern:
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated series name."""
self.cumulative: SeriesPattern1[StoredU64] = SeriesPattern1(client, _m(acc, 'cumulative'))
self.rolling: AverageMaxMedianMinPct10Pct25Pct75Pct90SumPattern = AverageMaxMedianMinPct10Pct25Pct75Pct90SumPattern(client, acc)
self.sum: SeriesPattern1[StoredU64] = SeriesPattern1(client, _m(acc, 'sum'))
class DeltaHalfTotalPattern: class DeltaHalfTotalPattern:
"""Pattern struct for repeated tree structure.""" """Pattern struct for repeated tree structure."""
@@ -3369,7 +3361,7 @@ class SeriesTree_Inputs:
def __init__(self, client: BrkClientBase, base_path: str = ''): def __init__(self, client: BrkClientBase, base_path: str = ''):
self.raw: SeriesTree_Inputs_Raw = SeriesTree_Inputs_Raw(client) self.raw: SeriesTree_Inputs_Raw = SeriesTree_Inputs_Raw(client)
self.spent: SeriesTree_Inputs_Spent = SeriesTree_Inputs_Spent(client) self.spent: SeriesTree_Inputs_Spent = SeriesTree_Inputs_Spent(client)
self.count: AverageCumulativeMaxMedianMinPct10Pct25Pct75Pct90RollingSumPattern = AverageCumulativeMaxMedianMinPct10Pct25Pct75Pct90RollingSumPattern(client, 'input_count') self.count: CumulativeRollingSumPattern = CumulativeRollingSumPattern(client, 'input_count')
class SeriesTree_Outputs_Raw: class SeriesTree_Outputs_Raw:
"""Series tree node.""" """Series tree node."""
@@ -3391,7 +3383,7 @@ class SeriesTree_Outputs_Count:
"""Series tree node.""" """Series tree node."""
def __init__(self, client: BrkClientBase, base_path: str = ''): def __init__(self, client: BrkClientBase, base_path: str = ''):
self.total: AverageCumulativeMaxMedianMinPct10Pct25Pct75Pct90RollingSumPattern = AverageCumulativeMaxMedianMinPct10Pct25Pct75Pct90RollingSumPattern(client, 'output_count') self.total: CumulativeRollingSumPattern = CumulativeRollingSumPattern(client, 'output_count')
self.unspent: SeriesPattern1[StoredU64] = SeriesPattern1(client, 'utxo_count_bis') self.unspent: SeriesPattern1[StoredU64] = SeriesPattern1(client, 'utxo_count_bis')
class SeriesTree_Outputs: class SeriesTree_Outputs:
+24 -18
View File
@@ -13,11 +13,12 @@ import {
chartsFromFullPerBlock, chartsFromFullPerBlock,
chartsFromCount, chartsFromCount,
chartsFromCountEntries, chartsFromCountEntries,
chartsFromSumPerBlock, chartsFromAggregatedPerBlock,
rollingWindowsTree, rollingWindowsTree,
ROLLING_WINDOWS, ROLLING_WINDOWS,
chartsFromBlockAnd6b, chartsFromBlockAnd6b,
multiSeriesTree,
simpleDeltaTree, simpleDeltaTree,
percentRatio, percentRatio,
percentRatioDots, percentRatioDots,
@@ -737,27 +738,32 @@ export function createNetworkSection() {
}), }),
{ {
name: "Flow", name: "Flow",
title: "UTXO Flow", tree: multiSeriesTree({
bottom: [ entries: [
line({ {
series: outputs.count.total.sum, name: "Created",
name: "Created", color: colors.entity.output,
color: colors.entity.output, base: outputs.count.total.sum,
unit: Unit.count, rolling: outputs.count.total.rolling.sum,
}), cumulative: outputs.count.total.cumulative,
line({ },
series: inputs.count.sum, {
name: "Spent", name: "Spent",
color: colors.entity.input, color: colors.entity.input,
unit: Unit.count, base: inputs.count.sum,
}), rolling: inputs.count.rolling.sum,
], cumulative: inputs.count.cumulative,
},
],
title: "UTXO Flow",
unit: Unit.count,
}),
}, },
], ],
}, },
{ {
name: "Inputs", name: "Inputs",
tree: chartsFromSumPerBlock({ tree: chartsFromAggregatedPerBlock({
pattern: inputs.count, pattern: inputs.count,
title: "Input Count", title: "Input Count",
unit: Unit.count, unit: Unit.count,
@@ -765,7 +771,7 @@ export function createNetworkSection() {
}, },
{ {
name: "Outputs", name: "Outputs",
tree: chartsFromSumPerBlock({ tree: chartsFromAggregatedPerBlock({
pattern: outputs.count.total, pattern: outputs.count.total,
title: "Output Count", title: "Output Count",
unit: Unit.count, unit: Unit.count,
+33 -77
View File
@@ -758,67 +758,6 @@ export function simpleDeltaTree({ delta, title, unit }) {
// ============================================================================ // ============================================================================
// These split patterns into separate Sum/Distribution/Cumulative charts // These split patterns into separate Sum/Distribution/Cumulative charts
/**
* Create distribution series (avg + percentiles)
* @param {DistributionStats} pattern
* @param {Unit} unit
* @returns {AnyFetchedSeriesBlueprint[]}
*/
function distributionSeries(pattern, unit) {
const { stat } = colors;
return [
dots({ series: pattern.average, name: "avg", color: stat.avg, unit }),
dots({
series: pattern.median,
name: "median",
color: stat.median,
unit,
defaultActive: false,
}),
dots({
series: pattern.max,
name: "max",
color: stat.max,
unit,
defaultActive: false,
}),
dots({
series: pattern.min,
name: "min",
color: stat.min,
unit,
defaultActive: false,
}),
dots({
series: pattern.pct75,
name: "pct75",
color: stat.pct75,
unit,
defaultActive: false,
}),
dots({
series: pattern.pct25,
name: "pct25",
color: stat.pct25,
unit,
defaultActive: false,
}),
dots({
series: pattern.pct90,
name: "pct90",
color: stat.pct90,
unit,
defaultActive: false,
}),
dots({
series: pattern.pct10,
name: "pct10",
color: stat.pct10,
unit,
defaultActive: false,
}),
];
}
/** /**
* Create btc/sats/usd series from patterns * Create btc/sats/usd series from patterns
@@ -910,7 +849,7 @@ export const chartsFromFullPerBlock = (args) =>
* @param {string} [args.distributionSuffix] * @param {string} [args.distributionSuffix]
* @returns {PartialOptionsTree} * @returns {PartialOptionsTree}
*/ */
export function chartsFromSum({ export function chartsFromAggregated({
pattern, pattern,
title, title,
unit, unit,
@@ -922,17 +861,12 @@ export function chartsFromSum({
: title; : title;
return [ return [
{ {
name: "Sum", name: "Per Block",
title, title,
bottom: [{ series: pattern.sum, title: "sum", color: stat.sum, unit }], bottom: [{ series: pattern.sum, title: "base", color: stat.sum, unit }],
}, },
rollingWindowsTree({ windows: pattern.rolling.sum, title, unit }), rollingWindowsTree({ windows: pattern.rolling.sum, title, unit }),
distributionWindowsTree({ pattern: pattern.rolling, title: distTitle, unit }), distributionWindowsTree({ pattern: pattern.rolling, title: distTitle, unit }),
{
name: "Distribution",
title: `${distTitle} Distribution`,
bottom: distributionSeries(pattern, unit),
},
{ {
name: "Cumulative", name: "Cumulative",
title: `${title} (Total)`, title: `${title} (Total)`,
@@ -949,8 +883,8 @@ export function chartsFromSum({
* @param {Unit} args.unit * @param {Unit} args.unit
* @returns {PartialOptionsTree} * @returns {PartialOptionsTree}
*/ */
export const chartsFromSumPerBlock = (args) => export const chartsFromAggregatedPerBlock = (args) =>
chartsFromSum({ ...args, distributionSuffix: "per Block" }); chartsFromAggregated({ ...args, distributionSuffix: "per Block" });
/** /**
* Create Per Block + Per 6 Blocks stats charts from a _6bBlockTxPattern * Create Per Block + Per 6 Blocks stats charts from a _6bBlockTxPattern
@@ -1024,12 +958,34 @@ export function chartsFromCount({ pattern, title, unit, color }) {
* @returns {PartialOptionsTree} * @returns {PartialOptionsTree}
*/ */
export function chartsFromCountEntries({ entries, title, unit }) { export function chartsFromCountEntries({ entries, title, unit }) {
return multiSeriesTree({
entries: entries.map(([name, data], i, arr) => ({
name,
color: colors.at(i, arr.length),
base: data.base,
rolling: data.sum,
cumulative: data.cumulative,
})),
title,
unit,
});
}
/**
* Per Block + Sums + Cumulative tree for multiple named series shown side-by-side
* @param {Object} args
* @param {Array<{ name: string, color: Color, base: AnySeriesPattern, rolling: { _24h: AnySeriesPattern, _1w: AnySeriesPattern, _1m: AnySeriesPattern, _1y: AnySeriesPattern }, cumulative: AnySeriesPattern }>} args.entries
* @param {string} args.title
* @param {Unit} args.unit
* @returns {PartialOptionsTree}
*/
export function multiSeriesTree({ entries, title, unit }) {
return [ return [
{ {
name: "Per Block", name: "Per Block",
title, title,
bottom: entries.map(([name, data], i, arr) => bottom: entries.map((e) =>
line({ series: data.base, name, color: colors.at(i, arr.length), unit }), line({ series: e.base, name: e.name, color: e.color, unit }),
), ),
}, },
{ {
@@ -1037,16 +993,16 @@ export function chartsFromCountEntries({ entries, title, unit }) {
tree: ROLLING_WINDOWS.map((w) => ({ tree: ROLLING_WINDOWS.map((w) => ({
name: w.name, name: w.name,
title: `${title} (${w.name})`, title: `${title} (${w.name})`,
bottom: entries.map(([name, data], i, arr) => bottom: entries.map((e) =>
line({ series: data.sum[w.key], name, color: colors.at(i, arr.length), unit }), line({ series: e.rolling[w.key], name: e.name, color: e.color, unit }),
), ),
})), })),
}, },
{ {
name: "Cumulative", name: "Cumulative",
title: `${title} (Total)`, title: `${title} (Total)`,
bottom: entries.map(([name, data], i, arr) => bottom: entries.map((e) =>
line({ series: data.cumulative, name, color: colors.at(i, arr.length), unit }), line({ series: e.cumulative, name: e.name, color: e.color, unit }),
), ),
}, },
]; ];