mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-04-24 06:39:58 -07:00
global: snapshot
This commit is contained in:
@@ -1087,34 +1087,34 @@ pub struct CapGrossInvestorLossMvrvNetPeakPriceProfitSellSoprPattern {
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct AverageCumulativeMaxMedianMinPct10Pct25Pct75Pct90RollingSumPattern {
|
||||
pub average: SeriesPattern18<StoredU64>,
|
||||
pub cumulative: SeriesPattern18<StoredU64>,
|
||||
pub max: SeriesPattern18<StoredU64>,
|
||||
pub median: SeriesPattern18<StoredU64>,
|
||||
pub min: SeriesPattern18<StoredU64>,
|
||||
pub pct10: SeriesPattern18<StoredU64>,
|
||||
pub pct25: SeriesPattern18<StoredU64>,
|
||||
pub pct75: SeriesPattern18<StoredU64>,
|
||||
pub pct90: SeriesPattern18<StoredU64>,
|
||||
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: SeriesPattern18<StoredU64>,
|
||||
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: SeriesPattern18::new(client.clone(), _m(&acc, "average")),
|
||||
cumulative: SeriesPattern18::new(client.clone(), _m(&acc, "cumulative")),
|
||||
max: SeriesPattern18::new(client.clone(), _m(&acc, "max")),
|
||||
median: SeriesPattern18::new(client.clone(), _m(&acc, "median")),
|
||||
min: SeriesPattern18::new(client.clone(), _m(&acc, "min")),
|
||||
pct10: SeriesPattern18::new(client.clone(), _m(&acc, "pct10")),
|
||||
pct25: SeriesPattern18::new(client.clone(), _m(&acc, "pct25")),
|
||||
pct75: SeriesPattern18::new(client.clone(), _m(&acc, "pct75")),
|
||||
pct90: SeriesPattern18::new(client.clone(), _m(&acc, "pct90")),
|
||||
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: SeriesPattern18::new(client.clone(), _m(&acc, "sum")),
|
||||
sum: SeriesPattern1::new(client.clone(), _m(&acc, "sum")),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1260,6 +1260,34 @@ impl AverageMaxMedianMinPct10Pct25Pct75Pct90SumPattern {
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct AverageMaxMedianMinPct10Pct25Pct75Pct90Pattern2 {
|
||||
pub average: SeriesPattern18<Weight>,
|
||||
pub max: SeriesPattern18<Weight>,
|
||||
pub median: SeriesPattern18<Weight>,
|
||||
pub min: SeriesPattern18<Weight>,
|
||||
pub pct10: SeriesPattern18<Weight>,
|
||||
pub pct25: SeriesPattern18<Weight>,
|
||||
pub pct75: SeriesPattern18<Weight>,
|
||||
pub pct90: SeriesPattern18<Weight>,
|
||||
}
|
||||
|
||||
impl AverageMaxMedianMinPct10Pct25Pct75Pct90Pattern2 {
|
||||
/// Create a new pattern node with accumulated series name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
average: SeriesPattern18::new(client.clone(), _m(&acc, "average")),
|
||||
max: SeriesPattern18::new(client.clone(), _m(&acc, "max")),
|
||||
median: SeriesPattern18::new(client.clone(), _m(&acc, "median")),
|
||||
min: SeriesPattern18::new(client.clone(), _m(&acc, "min")),
|
||||
pct10: SeriesPattern18::new(client.clone(), _m(&acc, "pct10")),
|
||||
pct25: SeriesPattern18::new(client.clone(), _m(&acc, "pct25")),
|
||||
pct75: SeriesPattern18::new(client.clone(), _m(&acc, "pct75")),
|
||||
pct90: SeriesPattern18::new(client.clone(), _m(&acc, "pct90")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct BaseCapitulationCumulativeNegativeSumToValuePattern {
|
||||
pub base: CentsUsdPattern2,
|
||||
@@ -1286,28 +1314,28 @@ pub struct BpsCentsPercentilesRatioSatsSmaStdUsdPattern {
|
||||
|
||||
/// Pattern struct for repeated tree structure.
|
||||
pub struct AverageMaxMedianMinPct10Pct25Pct75Pct90Pattern<T> {
|
||||
pub average: SeriesPattern18<T>,
|
||||
pub max: SeriesPattern18<T>,
|
||||
pub median: SeriesPattern18<T>,
|
||||
pub min: SeriesPattern18<T>,
|
||||
pub pct10: SeriesPattern18<T>,
|
||||
pub pct25: SeriesPattern18<T>,
|
||||
pub pct75: SeriesPattern18<T>,
|
||||
pub pct90: SeriesPattern18<T>,
|
||||
pub average: SeriesPattern1<T>,
|
||||
pub max: SeriesPattern1<T>,
|
||||
pub median: SeriesPattern1<T>,
|
||||
pub min: SeriesPattern1<T>,
|
||||
pub pct10: SeriesPattern1<T>,
|
||||
pub pct25: SeriesPattern1<T>,
|
||||
pub pct75: SeriesPattern1<T>,
|
||||
pub pct90: SeriesPattern1<T>,
|
||||
}
|
||||
|
||||
impl<T: DeserializeOwned> AverageMaxMedianMinPct10Pct25Pct75Pct90Pattern<T> {
|
||||
/// Create a new pattern node with accumulated series name.
|
||||
pub fn new(client: Arc<BrkClientBase>, acc: String) -> Self {
|
||||
Self {
|
||||
average: SeriesPattern18::new(client.clone(), _m(&acc, "average")),
|
||||
max: SeriesPattern18::new(client.clone(), _m(&acc, "max")),
|
||||
median: SeriesPattern18::new(client.clone(), _m(&acc, "median")),
|
||||
min: SeriesPattern18::new(client.clone(), _m(&acc, "min")),
|
||||
pct10: SeriesPattern18::new(client.clone(), _m(&acc, "pct10")),
|
||||
pct25: SeriesPattern18::new(client.clone(), _m(&acc, "pct25")),
|
||||
pct75: SeriesPattern18::new(client.clone(), _m(&acc, "pct75")),
|
||||
pct90: SeriesPattern18::new(client.clone(), _m(&acc, "pct90")),
|
||||
average: SeriesPattern1::new(client.clone(), _m(&acc, "average")),
|
||||
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")),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3086,14 +3114,14 @@ impl SeriesTree_Blocks_Weight {
|
||||
|
||||
/// Series tree node.
|
||||
pub struct SeriesTree_Blocks_Count {
|
||||
pub target: SeriesPattern1<StoredU64>,
|
||||
pub target: _1m1w1y24hPattern<StoredU64>,
|
||||
pub total: BaseCumulativeSumPattern2,
|
||||
}
|
||||
|
||||
impl SeriesTree_Blocks_Count {
|
||||
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
|
||||
Self {
|
||||
target: SeriesPattern1::new(client.clone(), "block_count_target".to_string()),
|
||||
target: _1m1w1y24hPattern::new(client.clone(), "block_count_target".to_string()),
|
||||
total: BaseCumulativeSumPattern2::new(client.clone(), "block_count".to_string()),
|
||||
}
|
||||
}
|
||||
@@ -3198,17 +3226,17 @@ impl SeriesTree_Blocks_Lookback {
|
||||
|
||||
/// Series tree node.
|
||||
pub struct SeriesTree_Blocks_Fullness {
|
||||
pub bps: _1m1w1y24hBasePattern<BasisPoints16>,
|
||||
pub ratio: SeriesPattern1<StoredF32>,
|
||||
pub percent: SeriesPattern1<StoredF32>,
|
||||
pub bps: SeriesPattern18<BasisPoints16>,
|
||||
pub ratio: SeriesPattern18<StoredF32>,
|
||||
pub percent: SeriesPattern18<StoredF32>,
|
||||
}
|
||||
|
||||
impl SeriesTree_Blocks_Fullness {
|
||||
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
|
||||
Self {
|
||||
bps: _1m1w1y24hBasePattern::new(client.clone(), "block_fullness_bps".to_string()),
|
||||
ratio: SeriesPattern1::new(client.clone(), "block_fullness_ratio".to_string()),
|
||||
percent: SeriesPattern1::new(client.clone(), "block_fullness".to_string()),
|
||||
bps: SeriesPattern18::new(client.clone(), "block_fullness_bps".to_string()),
|
||||
ratio: SeriesPattern18::new(client.clone(), "block_fullness_ratio".to_string()),
|
||||
percent: SeriesPattern18::new(client.clone(), "block_fullness".to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3302,14 +3330,31 @@ impl SeriesTree_Transactions_Count {
|
||||
/// Series tree node.
|
||||
pub struct SeriesTree_Transactions_Size {
|
||||
pub vsize: _6bBlockTxPattern<VSize>,
|
||||
pub weight: _6bBlockTxPattern<Weight>,
|
||||
pub weight: SeriesTree_Transactions_Size_Weight,
|
||||
}
|
||||
|
||||
impl SeriesTree_Transactions_Size {
|
||||
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
|
||||
Self {
|
||||
vsize: _6bBlockTxPattern::new(client.clone(), "tx_vsize".to_string()),
|
||||
weight: _6bBlockTxPattern::new(client.clone(), "tx_weight".to_string()),
|
||||
weight: SeriesTree_Transactions_Size_Weight::new(client.clone(), format!("{base_path}_weight")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Series tree node.
|
||||
pub struct SeriesTree_Transactions_Size_Weight {
|
||||
pub tx_index: SeriesPattern19<Weight>,
|
||||
pub block: AverageMaxMedianMinPct10Pct25Pct75Pct90Pattern2,
|
||||
pub _6b: AverageMaxMedianMinPct10Pct25Pct75Pct90Pattern2,
|
||||
}
|
||||
|
||||
impl SeriesTree_Transactions_Size_Weight {
|
||||
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
|
||||
Self {
|
||||
tx_index: SeriesPattern19::new(client.clone(), "tx_weight".to_string()),
|
||||
block: AverageMaxMedianMinPct10Pct25Pct75Pct90Pattern2::new(client.clone(), "tx_weight".to_string()),
|
||||
_6b: AverageMaxMedianMinPct10Pct25Pct75Pct90Pattern2::new(client.clone(), "tx_weight_6b".to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3353,7 +3398,6 @@ impl SeriesTree_Transactions_Versions {
|
||||
/// Series tree node.
|
||||
pub struct SeriesTree_Transactions_Volume {
|
||||
pub transfer_volume: BaseCumulativeSumPattern4,
|
||||
pub output_volume: BaseCumulativeSumPattern4,
|
||||
pub tx_per_sec: SeriesPattern1<StoredF32>,
|
||||
pub outputs_per_sec: SeriesPattern1<StoredF32>,
|
||||
pub inputs_per_sec: SeriesPattern1<StoredF32>,
|
||||
@@ -3363,7 +3407,6 @@ impl SeriesTree_Transactions_Volume {
|
||||
pub fn new(client: Arc<BrkClientBase>, base_path: String) -> Self {
|
||||
Self {
|
||||
transfer_volume: BaseCumulativeSumPattern4::new(client.clone(), "transfer_volume_bis".to_string()),
|
||||
output_volume: BaseCumulativeSumPattern4::new(client.clone(), "output_volume".to_string()),
|
||||
tx_per_sec: SeriesPattern1::new(client.clone(), "tx_per_sec".to_string()),
|
||||
outputs_per_sec: SeriesPattern1::new(client.clone(), "outputs_per_sec".to_string()),
|
||||
inputs_per_sec: SeriesPattern1::new(client.clone(), "inputs_per_sec".to_string()),
|
||||
|
||||
@@ -5,7 +5,10 @@ use vecdb::Database;
|
||||
use super::Vecs;
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{BlockCountTarget, CachedWindowStarts, PerBlockCumulativeWithSums, ConstantVecs},
|
||||
internal::{
|
||||
BlockCountTarget24h, BlockCountTarget1w, BlockCountTarget1m, BlockCountTarget1y,
|
||||
CachedWindowStarts, PerBlockCumulativeWithSums, ConstantVecs, Windows,
|
||||
},
|
||||
};
|
||||
|
||||
impl Vecs {
|
||||
@@ -16,11 +19,12 @@ impl Vecs {
|
||||
cached_starts: &CachedWindowStarts,
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
target: ConstantVecs::new::<BlockCountTarget>(
|
||||
"block_count_target",
|
||||
version,
|
||||
indexes,
|
||||
),
|
||||
target: Windows {
|
||||
_24h: ConstantVecs::new::<BlockCountTarget24h>("block_count_target_24h", version, indexes),
|
||||
_1w: ConstantVecs::new::<BlockCountTarget1w>("block_count_target_1w", version, indexes),
|
||||
_1m: ConstantVecs::new::<BlockCountTarget1m>("block_count_target_1m", version, indexes),
|
||||
_1y: ConstantVecs::new::<BlockCountTarget1y>("block_count_target_1y", version, indexes),
|
||||
},
|
||||
total: PerBlockCumulativeWithSums::forced_import(
|
||||
db,
|
||||
"block_count",
|
||||
|
||||
@@ -2,10 +2,10 @@ use brk_traversable::Traversable;
|
||||
use brk_types::{StoredU32, StoredU64};
|
||||
use vecdb::{Rw, StorageMode};
|
||||
|
||||
use crate::internal::{PerBlockCumulativeWithSums, ConstantVecs};
|
||||
use crate::internal::{PerBlockCumulativeWithSums, ConstantVecs, Windows};
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct Vecs<M: StorageMode = Rw> {
|
||||
pub target: ConstantVecs<StoredU64>,
|
||||
pub target: Windows<ConstantVecs<StoredU64>>,
|
||||
pub total: PerBlockCumulativeWithSums<StoredU32, StoredU64, M>,
|
||||
}
|
||||
|
||||
@@ -26,20 +26,12 @@ pub const DB_NAME: &str = "blocks";
|
||||
|
||||
pub(crate) const TARGET_BLOCKS_PER_DAY_F64: f64 = 144.0;
|
||||
pub(crate) const TARGET_BLOCKS_PER_DAY_F32: f32 = 144.0;
|
||||
pub(crate) const TARGET_BLOCKS_PER_MINUTE10: u64 = 1;
|
||||
pub(crate) const TARGET_BLOCKS_PER_MINUTE30: u64 = 3;
|
||||
pub(crate) const TARGET_BLOCKS_PER_HOUR1: u64 = 6;
|
||||
pub(crate) const TARGET_BLOCKS_PER_HOUR4: u64 = 24;
|
||||
pub(crate) const TARGET_BLOCKS_PER_HOUR12: u64 = 72;
|
||||
pub(crate) const TARGET_BLOCKS_PER_DAY: u64 = 144;
|
||||
pub(crate) const TARGET_BLOCKS_PER_DAY3: u64 = 3 * TARGET_BLOCKS_PER_DAY;
|
||||
pub(crate) const TARGET_BLOCKS_PER_WEEK: u64 = 7 * TARGET_BLOCKS_PER_DAY;
|
||||
pub(crate) const TARGET_BLOCKS_PER_MONTH: u64 = 30 * TARGET_BLOCKS_PER_DAY;
|
||||
pub(crate) const TARGET_BLOCKS_PER_QUARTER: u64 = 3 * TARGET_BLOCKS_PER_MONTH;
|
||||
pub(crate) const TARGET_BLOCKS_PER_SEMESTER: u64 = 2 * TARGET_BLOCKS_PER_QUARTER;
|
||||
pub(crate) const TARGET_BLOCKS_PER_YEAR: u64 = 2 * TARGET_BLOCKS_PER_SEMESTER;
|
||||
pub(crate) const TARGET_BLOCKS_PER_DECADE: u64 = 10 * TARGET_BLOCKS_PER_YEAR;
|
||||
pub(crate) const TARGET_BLOCKS_PER_HALVING: u64 = 210_000;
|
||||
pub(crate) const ONE_TERA_HASH: f64 = 1_000_000_000_000.0;
|
||||
|
||||
#[derive(Traversable)]
|
||||
|
||||
@@ -12,16 +12,12 @@ impl Vecs {
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
self.fullness
|
||||
.compute(starting_indexes.height, exit, |vec| {
|
||||
vec.compute_transform(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.blocks.weight,
|
||||
|(h, weight, ..)| (h, BasisPoints16::from(weight.fullness())),
|
||||
exit,
|
||||
)?;
|
||||
Ok(())
|
||||
})?;
|
||||
self.fullness.bps.compute_transform(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.blocks.weight,
|
||||
|(h, weight, ..)| (h, BasisPoints16::from(weight.fullness())),
|
||||
exit,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ use super::Vecs;
|
||||
use crate::{
|
||||
blocks::SizeVecs,
|
||||
indexes,
|
||||
internal::{CachedWindowStarts, LazyPerBlockRolling, PercentPerBlockRollingAverage, VBytesToWeight},
|
||||
internal::{CachedWindowStarts, LazyPerBlockRolling, PercentVec, VBytesToWeight},
|
||||
};
|
||||
|
||||
impl Vecs {
|
||||
@@ -25,13 +25,7 @@ impl Vecs {
|
||||
indexes,
|
||||
);
|
||||
|
||||
let fullness = PercentPerBlockRollingAverage::forced_import(
|
||||
db,
|
||||
"block_fullness",
|
||||
version,
|
||||
indexes,
|
||||
cached_starts,
|
||||
)?;
|
||||
let fullness = PercentVec::forced_import(db, "block_fullness", version)?;
|
||||
|
||||
Ok(Self { weight, fullness })
|
||||
}
|
||||
|
||||
@@ -2,10 +2,10 @@ use brk_traversable::Traversable;
|
||||
use brk_types::{BasisPoints16, StoredU64, Weight};
|
||||
use vecdb::{Rw, StorageMode};
|
||||
|
||||
use crate::internal::{LazyPerBlockRolling, PercentPerBlockRollingAverage};
|
||||
use crate::internal::{LazyPerBlockRolling, PercentVec};
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct Vecs<M: StorageMode = Rw> {
|
||||
pub weight: LazyPerBlockRolling<Weight, StoredU64>,
|
||||
pub fullness: PercentPerBlockRollingAverage<BasisPoints16, M>,
|
||||
pub fullness: PercentVec<BasisPoints16, M>,
|
||||
}
|
||||
|
||||
@@ -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_txin_index = &indexer.vecs.inputs.first_txin_index;
|
||||
let height_to_tx_count = &transactions.count.total.base.height;
|
||||
let height_to_output_count = &outputs.count.total.full.sum;
|
||||
let height_to_input_count = &inputs.count.full.sum;
|
||||
let height_to_output_count = &outputs.count.total.full.sum.height;
|
||||
let height_to_input_count = &inputs.count.full.sum.height;
|
||||
let tx_index_to_output_count = &indexes.tx_index.output_count;
|
||||
let tx_index_to_input_count = &indexes.tx_index.input_count;
|
||||
|
||||
|
||||
@@ -35,12 +35,14 @@ pub(super) fn compute(
|
||||
gini.bps
|
||||
.height
|
||||
.validate_computed_version_or_reset(source_version)?;
|
||||
gini.bps.height.truncate_if_needed_at(
|
||||
gini.bps
|
||||
.height
|
||||
.len()
|
||||
.min(starting_indexes.height.to_usize()),
|
||||
)?;
|
||||
|
||||
let min_len = gini
|
||||
.bps
|
||||
.height
|
||||
.len()
|
||||
.min(starting_indexes.height.to_usize());
|
||||
|
||||
gini.bps.height.truncate_if_needed_at(min_len)?;
|
||||
|
||||
let total_heights = supply_vecs
|
||||
.iter()
|
||||
|
||||
@@ -1,124 +0,0 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{
|
||||
CheckedSub, Database, EagerVec, Exit, ImportableVec, PcoVec, ReadableVec, Ro, Rw, StorageMode,
|
||||
StoredVec, VecIndex, VecValue, Version,
|
||||
};
|
||||
|
||||
use crate::internal::{
|
||||
ComputedVecValue, DistributionStats,
|
||||
algo::{compute_aggregations, compute_aggregations_nblock_window},
|
||||
};
|
||||
|
||||
#[derive(Traversable)]
|
||||
pub struct Distribution<I: VecIndex, T: ComputedVecValue + JsonSchema, M: StorageMode = Rw> {
|
||||
pub average: M::Stored<EagerVec<PcoVec<I, T>>>,
|
||||
pub min: M::Stored<EagerVec<PcoVec<I, T>>>,
|
||||
pub max: M::Stored<EagerVec<PcoVec<I, T>>>,
|
||||
pub pct10: M::Stored<EagerVec<PcoVec<I, T>>>,
|
||||
pub pct25: M::Stored<EagerVec<PcoVec<I, T>>>,
|
||||
pub median: M::Stored<EagerVec<PcoVec<I, T>>>,
|
||||
pub pct75: M::Stored<EagerVec<PcoVec<I, T>>>,
|
||||
pub pct90: M::Stored<EagerVec<PcoVec<I, T>>>,
|
||||
}
|
||||
|
||||
impl<I: VecIndex, T: ComputedVecValue + JsonSchema> Distribution<I, T> {
|
||||
pub(crate) fn forced_import(db: &Database, name: &str, version: Version) -> Result<Self> {
|
||||
let s = DistributionStats::<()>::SUFFIXES;
|
||||
Ok(Self {
|
||||
average: EagerVec::forced_import(db, &format!("{name}_{}", s[0]), version)?,
|
||||
min: EagerVec::forced_import(db, &format!("{name}_{}", s[1]), version)?,
|
||||
max: EagerVec::forced_import(db, &format!("{name}_{}", s[2]), version)?,
|
||||
pct10: EagerVec::forced_import(db, &format!("{name}_{}", s[3]), version)?,
|
||||
pct25: EagerVec::forced_import(db, &format!("{name}_{}", s[4]), version)?,
|
||||
median: EagerVec::forced_import(db, &format!("{name}_{}", s[5]), version)?,
|
||||
pct75: EagerVec::forced_import(db, &format!("{name}_{}", s[6]), version)?,
|
||||
pct90: EagerVec::forced_import(db, &format!("{name}_{}", s[7]), version)?,
|
||||
})
|
||||
}
|
||||
|
||||
/// Compute distribution stats, skipping first N items from all calculations.
|
||||
///
|
||||
/// Use `skip_count: 1` to exclude coinbase transactions from fee/feerate stats.
|
||||
pub(crate) fn compute_with_skip<A>(
|
||||
&mut self,
|
||||
max_from: I,
|
||||
source: &impl ReadableVec<A, T>,
|
||||
first_indexes: &impl ReadableVec<I, A>,
|
||||
count_indexes: &impl ReadableVec<I, brk_types::StoredU64>,
|
||||
exit: &Exit,
|
||||
skip_count: usize,
|
||||
) -> Result<()>
|
||||
where
|
||||
A: VecIndex + VecValue + brk_types::CheckedSub<A>,
|
||||
{
|
||||
compute_aggregations(
|
||||
max_from,
|
||||
source,
|
||||
first_indexes,
|
||||
count_indexes,
|
||||
exit,
|
||||
skip_count,
|
||||
None, // first
|
||||
None, // last
|
||||
Some(&mut self.min),
|
||||
Some(&mut self.max),
|
||||
Some(&mut self.average),
|
||||
None, // sum
|
||||
None, // cumulative
|
||||
Some(&mut self.median),
|
||||
Some(&mut self.pct10),
|
||||
Some(&mut self.pct25),
|
||||
Some(&mut self.pct75),
|
||||
Some(&mut self.pct90),
|
||||
)
|
||||
}
|
||||
|
||||
/// Compute distribution stats from a fixed n-block rolling window.
|
||||
///
|
||||
/// For each index `i`, aggregates all source items from blocks `max(0, i - n_blocks + 1)..=i`.
|
||||
pub(crate) fn compute_from_nblocks<A>(
|
||||
&mut self,
|
||||
max_from: I,
|
||||
source: &(impl ReadableVec<A, T> + Sized),
|
||||
first_indexes: &impl ReadableVec<I, A>,
|
||||
count_indexes: &impl ReadableVec<I, brk_types::StoredU64>,
|
||||
n_blocks: usize,
|
||||
exit: &Exit,
|
||||
) -> Result<()>
|
||||
where
|
||||
T: CheckedSub,
|
||||
A: VecIndex + VecValue + brk_types::CheckedSub<A>,
|
||||
{
|
||||
compute_aggregations_nblock_window(
|
||||
max_from,
|
||||
source,
|
||||
first_indexes,
|
||||
count_indexes,
|
||||
n_blocks,
|
||||
exit,
|
||||
&mut self.min,
|
||||
&mut self.max,
|
||||
&mut self.average,
|
||||
&mut self.median,
|
||||
&mut self.pct10,
|
||||
&mut self.pct25,
|
||||
&mut self.pct75,
|
||||
&mut self.pct90,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn read_only_clone(&self) -> Distribution<I, T, Ro> {
|
||||
Distribution {
|
||||
average: StoredVec::read_only_clone(&self.average),
|
||||
min: StoredVec::read_only_clone(&self.min),
|
||||
max: StoredVec::read_only_clone(&self.max),
|
||||
pct10: StoredVec::read_only_clone(&self.pct10),
|
||||
pct25: StoredVec::read_only_clone(&self.pct25),
|
||||
median: StoredVec::read_only_clone(&self.median),
|
||||
pct75: StoredVec::read_only_clone(&self.pct75),
|
||||
pct90: StoredVec::read_only_clone(&self.pct90),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{
|
||||
Database, EagerVec, Exit, ImportableVec, PcoVec, ReadableVec, Ro, Rw, StorageMode, StoredVec,
|
||||
VecIndex, VecValue, Version,
|
||||
};
|
||||
|
||||
use crate::internal::{ComputedVecValue, algo::compute_aggregations};
|
||||
|
||||
use super::Distribution;
|
||||
|
||||
/// Full stats aggregate: sum + cumulative + distribution
|
||||
#[derive(Traversable)]
|
||||
pub struct DistributionFull<I: VecIndex, T: ComputedVecValue + JsonSchema, M: StorageMode = Rw> {
|
||||
pub sum: M::Stored<EagerVec<PcoVec<I, T>>>,
|
||||
pub cumulative: M::Stored<EagerVec<PcoVec<I, T>>>,
|
||||
#[traversable(flatten)]
|
||||
pub distribution: Distribution<I, T, M>,
|
||||
}
|
||||
|
||||
impl<I: VecIndex, T: ComputedVecValue + JsonSchema> DistributionFull<I, T> {
|
||||
pub(crate) fn forced_import(db: &Database, name: &str, version: Version) -> Result<Self> {
|
||||
Ok(Self {
|
||||
distribution: Distribution::forced_import(db, name, version)?,
|
||||
sum: EagerVec::forced_import(db, &format!("{name}_sum"), version)?,
|
||||
cumulative: EagerVec::forced_import(db, &format!("{name}_cumulative"), version)?,
|
||||
})
|
||||
}
|
||||
|
||||
/// Compute all stats, skipping first N items from all calculations.
|
||||
///
|
||||
/// Use `skip_count: 1` to exclude coinbase transactions from fee/feerate stats.
|
||||
pub(crate) fn compute_with_skip<A>(
|
||||
&mut self,
|
||||
max_from: I,
|
||||
source: &impl ReadableVec<A, T>,
|
||||
first_indexes: &impl ReadableVec<I, A>,
|
||||
count_indexes: &impl ReadableVec<I, brk_types::StoredU64>,
|
||||
exit: &Exit,
|
||||
skip_count: usize,
|
||||
) -> Result<()>
|
||||
where
|
||||
A: VecIndex + VecValue + brk_types::CheckedSub<A>,
|
||||
{
|
||||
compute_aggregations(
|
||||
max_from,
|
||||
source,
|
||||
first_indexes,
|
||||
count_indexes,
|
||||
exit,
|
||||
skip_count,
|
||||
None, // first
|
||||
None, // last
|
||||
Some(&mut self.distribution.min),
|
||||
Some(&mut self.distribution.max),
|
||||
Some(&mut self.distribution.average),
|
||||
Some(&mut self.sum),
|
||||
Some(&mut self.cumulative),
|
||||
Some(&mut self.distribution.median),
|
||||
Some(&mut self.distribution.pct10),
|
||||
Some(&mut self.distribution.pct25),
|
||||
Some(&mut self.distribution.pct75),
|
||||
Some(&mut self.distribution.pct90),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn read_only_clone(&self) -> DistributionFull<I, T, Ro> {
|
||||
DistributionFull {
|
||||
distribution: self.distribution.read_only_clone(),
|
||||
sum: StoredVec::read_only_clone(&self.sum),
|
||||
cumulative: StoredVec::read_only_clone(&self.cumulative),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
mod distribution;
|
||||
mod distribution_full;
|
||||
mod lazy_distribution;
|
||||
|
||||
pub use distribution::*;
|
||||
pub use distribution_full::*;
|
||||
pub use lazy_distribution::*;
|
||||
@@ -4,10 +4,12 @@ mod per_resolution;
|
||||
mod window_24h;
|
||||
mod windows;
|
||||
mod windows_from_1w;
|
||||
mod percent;
|
||||
mod windows_to_1m;
|
||||
|
||||
pub use constant::*;
|
||||
pub use distribution_stats::*;
|
||||
pub use percent::*;
|
||||
pub use per_resolution::*;
|
||||
pub use window_24h::*;
|
||||
pub use windows::*;
|
||||
|
||||
8
crates/brk_computer/src/internal/containers/percent.rs
Normal file
8
crates/brk_computer/src/internal/containers/percent.rs
Normal file
@@ -0,0 +1,8 @@
|
||||
use brk_traversable::Traversable;
|
||||
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct Percent<A, B = A, C = B> {
|
||||
pub bps: A,
|
||||
pub ratio: B,
|
||||
pub percent: C,
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
mod aggregate;
|
||||
pub(crate) mod algo;
|
||||
mod amount;
|
||||
mod containers;
|
||||
@@ -9,7 +8,6 @@ mod indexes;
|
||||
mod traits;
|
||||
mod transform;
|
||||
|
||||
pub(crate) use aggregate::*;
|
||||
pub(crate) use amount::*;
|
||||
pub(crate) use containers::*;
|
||||
pub(crate) use per_block::*;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! PerBlockAggregated - DistributionFull (distribution + sum + cumulative) + RollingComplete.
|
||||
//! 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.
|
||||
@@ -11,7 +11,7 @@ use vecdb::{Database, Exit, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{CachedWindowStarts, DistributionFull, NumericValue, RollingComplete, WindowStarts},
|
||||
internal::{CachedWindowStarts, PerBlockDistributionFull, NumericValue, RollingComplete, WindowStarts},
|
||||
};
|
||||
|
||||
#[derive(Traversable)]
|
||||
@@ -20,7 +20,7 @@ where
|
||||
T: NumericValue + JsonSchema,
|
||||
{
|
||||
#[traversable(flatten)]
|
||||
pub full: DistributionFull<Height, T, M>,
|
||||
pub full: PerBlockDistributionFull<T, M>,
|
||||
pub rolling: RollingComplete<T, M>,
|
||||
}
|
||||
|
||||
@@ -35,26 +35,26 @@ where
|
||||
indexes: &indexes::Vecs,
|
||||
cached_starts: &CachedWindowStarts,
|
||||
) -> Result<Self> {
|
||||
let full = DistributionFull::forced_import(db, name, version)?;
|
||||
let full = PerBlockDistributionFull::forced_import(db, name, version, indexes)?;
|
||||
let rolling = RollingComplete::forced_import(
|
||||
db,
|
||||
name,
|
||||
version,
|
||||
indexes,
|
||||
&full.cumulative,
|
||||
&full.cumulative.height,
|
||||
cached_starts,
|
||||
)?;
|
||||
|
||||
Ok(Self { full, rolling })
|
||||
}
|
||||
|
||||
/// Compute DistributionFull stats via closure, then rolling distribution from the per-block sum.
|
||||
/// Compute PerBlockDistributionFull stats via closure, then rolling distribution from the per-block sum.
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
windows: &WindowStarts<'_>,
|
||||
exit: &Exit,
|
||||
compute_full: impl FnOnce(&mut DistributionFull<Height, T>) -> Result<()>,
|
||||
compute_full: impl FnOnce(&mut PerBlockDistributionFull<T>) -> Result<()>,
|
||||
) -> Result<()>
|
||||
where
|
||||
T: From<f64> + Default + Copy + Ord,
|
||||
@@ -62,7 +62,7 @@ where
|
||||
{
|
||||
compute_full(&mut self.full)?;
|
||||
self.rolling
|
||||
.compute(max_from, windows, &self.full.sum, exit)?;
|
||||
.compute(max_from, windows, &self.full.sum.height, exit)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::Height;
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{
|
||||
CheckedSub, Database, Exit, ReadableVec, Rw, StorageMode,
|
||||
VecIndex, VecValue, Version,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{
|
||||
ComputedVecValue, DistributionStats, NumericValue, PerBlock,
|
||||
algo::{compute_aggregations, compute_aggregations_nblock_window},
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
#[traversable(transparent)]
|
||||
pub struct PerBlockDistribution<T: ComputedVecValue + PartialOrd + JsonSchema, M: StorageMode = Rw>(
|
||||
pub DistributionStats<PerBlock<T, M>>,
|
||||
);
|
||||
|
||||
impl<T: NumericValue + JsonSchema> PerBlockDistribution<T> {
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
Ok(Self(DistributionStats::try_from_fn(|suffix| {
|
||||
PerBlock::forced_import(db, &format!("{name}_{suffix}"), 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 s = &mut self.0;
|
||||
compute_aggregations(
|
||||
max_from,
|
||||
source,
|
||||
first_indexes,
|
||||
count_indexes,
|
||||
exit,
|
||||
skip_count,
|
||||
None,
|
||||
None,
|
||||
Some(&mut s.min.height),
|
||||
Some(&mut s.max.height),
|
||||
Some(&mut s.average.height),
|
||||
None,
|
||||
None,
|
||||
Some(&mut s.median.height),
|
||||
Some(&mut s.pct10.height),
|
||||
Some(&mut s.pct25.height),
|
||||
Some(&mut s.pct75.height),
|
||||
Some(&mut s.pct90.height),
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn compute_from_nblocks<A>(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
source: &(impl ReadableVec<A, T> + Sized),
|
||||
first_indexes: &impl ReadableVec<Height, A>,
|
||||
count_indexes: &impl ReadableVec<Height, brk_types::StoredU64>,
|
||||
n_blocks: usize,
|
||||
exit: &Exit,
|
||||
) -> Result<()>
|
||||
where
|
||||
T: CheckedSub,
|
||||
A: VecIndex + VecValue + brk_types::CheckedSub<A>,
|
||||
{
|
||||
let s = &mut self.0;
|
||||
compute_aggregations_nblock_window(
|
||||
max_from,
|
||||
source,
|
||||
first_indexes,
|
||||
count_indexes,
|
||||
n_blocks,
|
||||
exit,
|
||||
&mut s.min.height,
|
||||
&mut s.max.height,
|
||||
&mut s.average.height,
|
||||
&mut s.median.height,
|
||||
&mut s.pct10.height,
|
||||
&mut s.pct25.height,
|
||||
&mut s.pct75.height,
|
||||
&mut s.pct90.height,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
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,10 +3,10 @@ use brk_types::{Height, Version};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{LazyVecFrom1, ReadableCloneableVec, UnaryTransform, VecIndex};
|
||||
|
||||
use crate::internal::{ComputedVecValue, Distribution, DistributionStats};
|
||||
use crate::internal::{ComputedVecValue, PerBlockDistribution, DistributionStats};
|
||||
|
||||
/// Lazy analog of `Distribution<I, T>`: 8 `LazyVecFrom1` fields,
|
||||
/// each derived by transforming the corresponding field of a source `Distribution<I, S1T>`.
|
||||
/// Lazy analog of `Distribution<T>`: 8 `LazyVecFrom1` fields,
|
||||
/// each derived by transforming the corresponding field of a source `PerBlockDistribution<S1T>`.
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct LazyDistribution<I, T, S1T>
|
||||
where
|
||||
@@ -27,54 +27,54 @@ where
|
||||
impl<T, S1T> LazyDistribution<Height, T, S1T>
|
||||
where
|
||||
T: ComputedVecValue + JsonSchema + 'static,
|
||||
S1T: ComputedVecValue + JsonSchema,
|
||||
S1T: ComputedVecValue + PartialOrd + JsonSchema,
|
||||
{
|
||||
pub(crate) fn from_distribution<F: UnaryTransform<S1T, T>>(
|
||||
name: &str,
|
||||
version: Version,
|
||||
source: &Distribution<Height, S1T>,
|
||||
source: &PerBlockDistribution<S1T>,
|
||||
) -> Self {
|
||||
let s = DistributionStats::<()>::SUFFIXES;
|
||||
Self {
|
||||
average: LazyVecFrom1::transformed::<F>(
|
||||
&format!("{name}_{}", s[0]),
|
||||
version,
|
||||
source.average.read_only_boxed_clone(),
|
||||
source.average.height.read_only_boxed_clone(),
|
||||
),
|
||||
min: LazyVecFrom1::transformed::<F>(
|
||||
&format!("{name}_{}", s[1]),
|
||||
version,
|
||||
source.min.read_only_boxed_clone(),
|
||||
source.min.height.read_only_boxed_clone(),
|
||||
),
|
||||
max: LazyVecFrom1::transformed::<F>(
|
||||
&format!("{name}_{}", s[2]),
|
||||
version,
|
||||
source.max.read_only_boxed_clone(),
|
||||
source.max.height.read_only_boxed_clone(),
|
||||
),
|
||||
pct10: LazyVecFrom1::transformed::<F>(
|
||||
&format!("{name}_{}", s[3]),
|
||||
version,
|
||||
source.pct10.read_only_boxed_clone(),
|
||||
source.pct10.height.read_only_boxed_clone(),
|
||||
),
|
||||
pct25: LazyVecFrom1::transformed::<F>(
|
||||
&format!("{name}_{}", s[4]),
|
||||
version,
|
||||
source.pct25.read_only_boxed_clone(),
|
||||
source.pct25.height.read_only_boxed_clone(),
|
||||
),
|
||||
median: LazyVecFrom1::transformed::<F>(
|
||||
&format!("{name}_{}", s[5]),
|
||||
version,
|
||||
source.median.read_only_boxed_clone(),
|
||||
source.median.height.read_only_boxed_clone(),
|
||||
),
|
||||
pct75: LazyVecFrom1::transformed::<F>(
|
||||
&format!("{name}_{}", s[6]),
|
||||
version,
|
||||
source.pct75.read_only_boxed_clone(),
|
||||
source.pct75.height.read_only_boxed_clone(),
|
||||
),
|
||||
pct90: LazyVecFrom1::transformed::<F>(
|
||||
&format!("{name}_{}", s[7]),
|
||||
version,
|
||||
source.pct90.read_only_boxed_clone(),
|
||||
source.pct90.height.read_only_boxed_clone(),
|
||||
),
|
||||
}
|
||||
}
|
||||
@@ -2,10 +2,13 @@ mod aggregated;
|
||||
mod base;
|
||||
mod cumulative;
|
||||
mod cumulative_sum;
|
||||
mod distribution;
|
||||
mod distribution_full;
|
||||
mod full;
|
||||
mod lazy_distribution;
|
||||
mod lazy_rolling;
|
||||
mod resolutions;
|
||||
mod rolling;
|
||||
mod lazy_rolling;
|
||||
mod full;
|
||||
mod rolling_average;
|
||||
mod with_deltas;
|
||||
|
||||
@@ -13,9 +16,12 @@ pub use aggregated::*;
|
||||
pub use base::*;
|
||||
pub use cumulative::*;
|
||||
pub use cumulative_sum::*;
|
||||
pub use distribution::*;
|
||||
pub use distribution_full::*;
|
||||
pub use full::*;
|
||||
pub use lazy_distribution::*;
|
||||
pub use lazy_rolling::*;
|
||||
pub use resolutions::*;
|
||||
pub use rolling::*;
|
||||
pub use lazy_rolling::*;
|
||||
pub use full::*;
|
||||
pub use rolling_average::*;
|
||||
pub use with_deltas::*;
|
||||
|
||||
@@ -2,11 +2,11 @@ use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{LazyVecFrom1, ReadableBoxedVec, ReadableCloneableVec, UnaryTransform};
|
||||
use vecdb::{LazyVecFrom1, ReadOnlyClone, ReadableBoxedVec, ReadableCloneableVec, UnaryTransform};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{PerBlock, ComputedVecValue, DerivedResolutions, NumericValue},
|
||||
internal::{ComputedVecValue, DerivedResolutions, NumericValue, PerBlock},
|
||||
};
|
||||
#[derive(Clone, Deref, DerefMut, Traversable)]
|
||||
#[traversable(merge)]
|
||||
@@ -38,7 +38,9 @@ where
|
||||
{
|
||||
Self {
|
||||
height: LazyVecFrom1::transformed::<F>(name, version, height_source),
|
||||
resolutions: Box::new(DerivedResolutions::from_computed::<F>(name, version, source)),
|
||||
resolutions: Box::new(DerivedResolutions::from_computed::<F>(
|
||||
name, version, source,
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,3 +88,14 @@ where
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, S1T> ReadOnlyClone for LazyPerBlock<T, S1T>
|
||||
where
|
||||
T: ComputedVecValue + PartialOrd + JsonSchema,
|
||||
S1T: ComputedVecValue,
|
||||
{
|
||||
type ReadOnly = Self;
|
||||
fn read_only_clone(&self) -> Self {
|
||||
self.clone()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, StoredF32, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{
|
||||
BinaryTransform, Database, Exit, ReadableCloneableVec, ReadableVec, Rw, StorageMode, VecValue,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{BpsType, algo::ComputeDrawdown},
|
||||
internal::{BpsType, Percent, algo::ComputeDrawdown},
|
||||
};
|
||||
|
||||
use crate::internal::{PerBlock, LazyPerBlock};
|
||||
use crate::internal::{LazyPerBlock, PerBlock};
|
||||
|
||||
/// Basis-point storage with both ratio and percentage float views.
|
||||
///
|
||||
@@ -18,12 +19,11 @@ use crate::internal::{PerBlock, LazyPerBlock};
|
||||
/// exposes two lazy StoredF32 views:
|
||||
/// - `ratio`: bps / 10000 (e.g., 4523 bps -> 0.4523)
|
||||
/// - `percent`: bps / 100 (e.g., 4523 bps -> 45.23%)
|
||||
#[derive(Traversable)]
|
||||
pub struct PercentPerBlock<B: BpsType, M: StorageMode = Rw> {
|
||||
pub bps: PerBlock<B, M>,
|
||||
pub ratio: LazyPerBlock<StoredF32, B>,
|
||||
pub percent: LazyPerBlock<StoredF32, B>,
|
||||
}
|
||||
#[derive(Deref, DerefMut, Traversable)]
|
||||
#[traversable(transparent)]
|
||||
pub struct PercentPerBlock<B: BpsType, M: StorageMode = Rw>(
|
||||
pub Percent<PerBlock<B, M>, LazyPerBlock<StoredF32, B>>,
|
||||
);
|
||||
|
||||
impl<B: BpsType> PercentPerBlock<B> {
|
||||
pub(crate) fn forced_import(
|
||||
@@ -44,11 +44,11 @@ impl<B: BpsType> PercentPerBlock<B> {
|
||||
|
||||
let percent = LazyPerBlock::from_computed::<B::ToPercent>(name, version, bps_clone, &bps);
|
||||
|
||||
Ok(Self {
|
||||
Ok(Self(Percent {
|
||||
bps,
|
||||
ratio,
|
||||
percent,
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
pub(crate) fn compute_binary<S1T, S2T, F>(
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{StoredF32, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{ReadableCloneableVec, UnaryTransform};
|
||||
|
||||
use crate::internal::{BpsType, LazyPerBlock, PercentPerBlock};
|
||||
use crate::internal::{BpsType, LazyPerBlock, Percent, PercentPerBlock};
|
||||
|
||||
/// Fully lazy variant of `PercentPerBlock` — no stored vecs.
|
||||
///
|
||||
/// BPS values are lazily derived from a source `PercentPerBlock` via a unary transform,
|
||||
/// and ratio/percent float views are chained from the lazy BPS.
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct LazyPercentPerBlock<B: BpsType> {
|
||||
pub bps: LazyPerBlock<B, B>,
|
||||
pub ratio: LazyPerBlock<StoredF32, B>,
|
||||
pub percent: LazyPerBlock<StoredF32, B>,
|
||||
}
|
||||
#[derive(Clone, Deref, DerefMut, Traversable)]
|
||||
#[traversable(transparent)]
|
||||
pub struct LazyPercentPerBlock<B: BpsType>(
|
||||
pub Percent<LazyPerBlock<B, B>, LazyPerBlock<StoredF32, B>>,
|
||||
);
|
||||
|
||||
impl<B: BpsType> LazyPercentPerBlock<B> {
|
||||
/// Create from a stored `PercentPerBlock` source via a BPS-to-BPS unary transform.
|
||||
@@ -37,10 +37,10 @@ impl<B: BpsType> LazyPercentPerBlock<B> {
|
||||
|
||||
let percent = LazyPerBlock::from_lazy::<B::ToPercent, B>(name, version, &bps);
|
||||
|
||||
Self {
|
||||
Self(Percent {
|
||||
bps,
|
||||
ratio,
|
||||
percent,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
mod base;
|
||||
mod lazy;
|
||||
mod lazy_windows;
|
||||
mod rolling_average;
|
||||
mod vec;
|
||||
mod windows;
|
||||
|
||||
pub use base::*;
|
||||
pub use lazy::*;
|
||||
pub use lazy_windows::*;
|
||||
pub use rolling_average::*;
|
||||
pub use vec::*;
|
||||
pub use windows::*;
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
use brk_error::Result;
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, StoredF32, Version};
|
||||
use vecdb::{Database, EagerVec, Exit, PcoVec, ReadableCloneableVec, Rw, StorageMode};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{BpsType, CachedWindowStarts},
|
||||
};
|
||||
|
||||
use crate::internal::{PerBlockRollingAverage, LazyPerBlock};
|
||||
|
||||
/// Like PercentPerBlock but with rolling average stats on the bps data.
|
||||
#[derive(Traversable)]
|
||||
pub struct PercentPerBlockRollingAverage<B: BpsType, M: StorageMode = Rw> {
|
||||
pub bps: PerBlockRollingAverage<B, M>,
|
||||
pub ratio: LazyPerBlock<StoredF32, B>,
|
||||
pub percent: LazyPerBlock<StoredF32, B>,
|
||||
}
|
||||
|
||||
impl<B: BpsType> PercentPerBlockRollingAverage<B> {
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
cached_starts: &CachedWindowStarts,
|
||||
) -> Result<Self> {
|
||||
let bps = PerBlockRollingAverage::forced_import(
|
||||
db,
|
||||
&format!("{name}_bps"),
|
||||
version,
|
||||
indexes,
|
||||
cached_starts,
|
||||
)?;
|
||||
|
||||
let ratio = LazyPerBlock::from_height_source::<B::ToRatio>(
|
||||
&format!("{name}_ratio"),
|
||||
version,
|
||||
bps.base.read_only_boxed_clone(),
|
||||
indexes,
|
||||
);
|
||||
|
||||
let percent = LazyPerBlock::from_height_source::<B::ToPercent>(
|
||||
name,
|
||||
version,
|
||||
bps.base.read_only_boxed_clone(),
|
||||
indexes,
|
||||
);
|
||||
|
||||
Ok(Self {
|
||||
bps,
|
||||
ratio,
|
||||
percent,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn compute(
|
||||
&mut self,
|
||||
max_from: Height,
|
||||
exit: &Exit,
|
||||
compute_height: impl FnOnce(&mut EagerVec<PcoVec<Height, B>>) -> Result<()>,
|
||||
) -> Result<()> {
|
||||
self.bps.compute(max_from, exit, compute_height)
|
||||
}
|
||||
}
|
||||
43
crates/brk_computer/src/internal/per_block/percent/vec.rs
Normal file
43
crates/brk_computer/src/internal/per_block/percent/vec.rs
Normal file
@@ -0,0 +1,43 @@
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, StoredF32, Version};
|
||||
use derive_more::{Deref, DerefMut};
|
||||
use vecdb::{
|
||||
Database, EagerVec, ImportableVec, LazyVecFrom1, PcoVec, ReadableCloneableVec, Rw, StorageMode,
|
||||
};
|
||||
|
||||
use crate::internal::{BpsType, Percent};
|
||||
|
||||
/// Lightweight percent container: BPS height vec + lazy ratio + lazy percent.
|
||||
/// No resolutions, no rolling stats.
|
||||
#[derive(Clone, Deref, DerefMut, Traversable)]
|
||||
#[traversable(transparent)]
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub struct PercentVec<B: BpsType, M: StorageMode = Rw>(
|
||||
pub Percent<M::Stored<EagerVec<PcoVec<Height, B>>>, LazyVecFrom1<Height, StoredF32, Height, B>>,
|
||||
);
|
||||
|
||||
impl<B: BpsType> PercentVec<B> {
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
) -> brk_error::Result<Self> {
|
||||
let bps: EagerVec<PcoVec<Height, B>> =
|
||||
EagerVec::forced_import(db, &format!("{name}_bps"), version)?;
|
||||
let bps_clone = bps.read_only_boxed_clone();
|
||||
|
||||
let ratio = LazyVecFrom1::transformed::<B::ToRatio>(
|
||||
&format!("{name}_ratio"),
|
||||
version,
|
||||
bps_clone.clone(),
|
||||
);
|
||||
|
||||
let percent = LazyVecFrom1::transformed::<B::ToPercent>(name, version, bps_clone);
|
||||
|
||||
Ok(Self(Percent {
|
||||
bps,
|
||||
ratio,
|
||||
percent,
|
||||
}))
|
||||
}
|
||||
}
|
||||
@@ -1,37 +1,36 @@
|
||||
//! TxDerivedDistribution - per-block + rolling window distribution stats from tx-level data.
|
||||
//!
|
||||
//! Computes true distribution stats (average, min, max, median, percentiles) by reading
|
||||
//! actual tx values for each scope: current block, last 6 blocks.
|
||||
|
||||
use brk_error::Result;
|
||||
use brk_indexer::Indexer;
|
||||
|
||||
use brk_traversable::Traversable;
|
||||
use brk_types::{Height, Indexes, TxIndex};
|
||||
use brk_types::{Indexes, TxIndex};
|
||||
use schemars::JsonSchema;
|
||||
use vecdb::{Database, Exit, ReadableVec, Rw, StorageMode, Version};
|
||||
|
||||
use crate::{
|
||||
indexes,
|
||||
internal::{ComputedVecValue, Distribution, NumericValue},
|
||||
internal::{ComputedVecValue, NumericValue, PerBlockDistribution},
|
||||
};
|
||||
|
||||
/// 6-block rolling window distribution with 8 distribution stat vecs.
|
||||
#[derive(Traversable)]
|
||||
pub struct BlockRollingDistribution<T, M: StorageMode = Rw>
|
||||
where
|
||||
T: ComputedVecValue + PartialOrd + JsonSchema,
|
||||
{
|
||||
pub _6b: Distribution<Height, T, M>,
|
||||
pub _6b: PerBlockDistribution<T, M>,
|
||||
}
|
||||
|
||||
impl<T> BlockRollingDistribution<T>
|
||||
where
|
||||
T: NumericValue + JsonSchema,
|
||||
{
|
||||
pub(crate) fn forced_import(db: &Database, name: &str, version: Version) -> Result<Self> {
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
Ok(Self {
|
||||
_6b: Distribution::forced_import(db, &format!("{name}_6b"), version)?,
|
||||
_6b: PerBlockDistribution::forced_import(db, &format!("{name}_6b"), version, indexes)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -41,20 +40,28 @@ pub struct TxDerivedDistribution<T, M: StorageMode = Rw>
|
||||
where
|
||||
T: ComputedVecValue + PartialOrd + JsonSchema,
|
||||
{
|
||||
pub block: Distribution<Height, T, M>,
|
||||
pub block: PerBlockDistribution<T, M>,
|
||||
#[traversable(flatten)]
|
||||
pub rolling: BlockRollingDistribution<T, M>,
|
||||
pub distribution: BlockRollingDistribution<T, M>,
|
||||
}
|
||||
|
||||
impl<T> TxDerivedDistribution<T>
|
||||
where
|
||||
T: NumericValue + JsonSchema,
|
||||
{
|
||||
pub(crate) fn forced_import(db: &Database, name: &str, version: Version) -> Result<Self> {
|
||||
let block = Distribution::forced_import(db, name, version)?;
|
||||
let rolling = BlockRollingDistribution::forced_import(db, name, version)?;
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
let block = PerBlockDistribution::forced_import(db, name, version, indexes)?;
|
||||
let distribution = BlockRollingDistribution::forced_import(db, name, version, indexes)?;
|
||||
|
||||
Ok(Self { block, rolling })
|
||||
Ok(Self {
|
||||
block,
|
||||
distribution,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn derive_from(
|
||||
@@ -72,10 +79,6 @@ where
|
||||
self.derive_from_with_skip(indexer, indexes, starting_indexes, tx_index_source, exit, 0)
|
||||
}
|
||||
|
||||
/// Derive from source, skipping first N transactions per block from per-block stats.
|
||||
///
|
||||
/// Use `skip_count: 1` to exclude coinbase transactions from fee/feerate stats.
|
||||
/// Rolling window distributions do NOT skip (negligible impact over many blocks).
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) fn derive_from_with_skip(
|
||||
&mut self,
|
||||
@@ -90,7 +93,6 @@ where
|
||||
T: Copy + Ord + From<f64> + Default,
|
||||
f64: From<T>,
|
||||
{
|
||||
// Per-block distribution (supports skip for coinbase exclusion)
|
||||
self.block.compute_with_skip(
|
||||
starting_indexes.height,
|
||||
tx_index_source,
|
||||
@@ -100,8 +102,7 @@ where
|
||||
skip_count,
|
||||
)?;
|
||||
|
||||
// 6-block rolling: true distribution from all txs in last 6 blocks
|
||||
self.rolling._6b.compute_from_nblocks(
|
||||
self.distribution._6b.compute_from_nblocks(
|
||||
starting_indexes.height,
|
||||
tx_index_source,
|
||||
&indexer.vecs.transactions.first_tx_index,
|
||||
|
||||
@@ -29,9 +29,9 @@ impl<T> PerTxDistribution<T>
|
||||
where
|
||||
T: NumericValue + JsonSchema,
|
||||
{
|
||||
pub(crate) fn forced_import(db: &Database, name: &str, version: Version) -> Result<Self> {
|
||||
pub(crate) fn forced_import(db: &Database, name: &str, version: Version, indexes: &indexes::Vecs) -> Result<Self> {
|
||||
let tx_index = EagerVec::forced_import(db, name, version)?;
|
||||
let distribution = TxDerivedDistribution::forced_import(db, name, version)?;
|
||||
let distribution = TxDerivedDistribution::forced_import(db, name, version, indexes)?;
|
||||
Ok(Self {
|
||||
tx_index,
|
||||
distribution,
|
||||
|
||||
@@ -14,8 +14,6 @@ where
|
||||
pub _6b: LazyDistribution<Height, T, S1T>,
|
||||
}
|
||||
|
||||
/// Lazy analog of `TxDerivedDistribution<T>`: per-block + 6-block rolling,
|
||||
/// each derived by transforming the corresponding source distribution.
|
||||
#[derive(Clone, Traversable)]
|
||||
pub struct LazyTxDerivedDistribution<T, S1T>
|
||||
where
|
||||
@@ -24,13 +22,13 @@ where
|
||||
{
|
||||
pub block: LazyDistribution<Height, T, S1T>,
|
||||
#[traversable(flatten)]
|
||||
pub rolling: LazyBlockRollingDistribution<T, S1T>,
|
||||
pub distribution: LazyBlockRollingDistribution<T, S1T>,
|
||||
}
|
||||
|
||||
impl<T, S1T> LazyTxDerivedDistribution<T, S1T>
|
||||
where
|
||||
T: ComputedVecValue + JsonSchema + 'static,
|
||||
S1T: ComputedVecValue + JsonSchema,
|
||||
S1T: ComputedVecValue + PartialOrd + JsonSchema,
|
||||
{
|
||||
pub(crate) fn from_tx_derived<F: UnaryTransform<S1T, T>>(
|
||||
name: &str,
|
||||
@@ -38,13 +36,16 @@ where
|
||||
source: &TxDerivedDistribution<S1T>,
|
||||
) -> Self {
|
||||
let block = LazyDistribution::from_distribution::<F>(name, version, &source.block);
|
||||
let rolling = LazyBlockRollingDistribution {
|
||||
let distribution = LazyBlockRollingDistribution {
|
||||
_6b: LazyDistribution::from_distribution::<F>(
|
||||
&format!("{name}_6b"),
|
||||
version,
|
||||
&source.rolling._6b,
|
||||
&source.distribution._6b,
|
||||
),
|
||||
};
|
||||
Self { block, rolling }
|
||||
Self {
|
||||
block,
|
||||
distribution,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,9 +32,10 @@ where
|
||||
db: &Database,
|
||||
name: &str,
|
||||
version: Version,
|
||||
indexes: &indexes::Vecs,
|
||||
tx_index: LazyVecFrom2<TxIndex, T, TxIndex, S1, TxIndex, S2>,
|
||||
) -> Result<Self> {
|
||||
let distribution = TxDerivedDistribution::forced_import(db, name, version)?;
|
||||
let distribution = TxDerivedDistribution::forced_import(db, name, version, indexes)?;
|
||||
Ok(Self {
|
||||
tx_index,
|
||||
distribution,
|
||||
|
||||
@@ -28,4 +28,4 @@ pub use ratio::{
|
||||
RatioCentsSignedDollarsBps32, RatioDiffCentsBps32, RatioDiffDollarsBps32, RatioDiffF32Bps32,
|
||||
RatioDollarsBp16, RatioDollarsBp32, RatioDollarsBps32, RatioSatsBp16, RatioU64Bp16,
|
||||
};
|
||||
pub use specialized::{BlockCountTarget, OhlcCentsToDollars, OhlcCentsToSats};
|
||||
pub use specialized::{BlockCountTarget24h, BlockCountTarget1w, BlockCountTarget1m, BlockCountTarget1y, OhlcCentsToDollars, OhlcCentsToSats};
|
||||
|
||||
@@ -1,132 +1,36 @@
|
||||
use brk_types::{
|
||||
Close, Day1, Day3, Epoch, Halving, Height, High, Hour1, Hour4, Hour12, Low,
|
||||
Minute10, Minute30, Month1, Month3, Month6, OHLCCents, OHLCDollars, OHLCSats, Open, StoredU64,
|
||||
Week1, Year1, Year10,
|
||||
Close, Day1, Day3, Epoch, Halving, Height, High, Hour1, Hour4, Hour12, Low, Minute10, Minute30,
|
||||
Month1, Month3, Month6, OHLCCents, OHLCDollars, OHLCSats, Open, StoredU64, Week1, Year1,
|
||||
Year10,
|
||||
};
|
||||
use vecdb::UnaryTransform;
|
||||
|
||||
use super::CentsUnsignedToSats;
|
||||
use crate::blocks::{
|
||||
TARGET_BLOCKS_PER_DAY, TARGET_BLOCKS_PER_DAY3, TARGET_BLOCKS_PER_DECADE,
|
||||
TARGET_BLOCKS_PER_HALVING, TARGET_BLOCKS_PER_HOUR1, TARGET_BLOCKS_PER_HOUR4,
|
||||
TARGET_BLOCKS_PER_HOUR12, TARGET_BLOCKS_PER_MINUTE10, TARGET_BLOCKS_PER_MINUTE30,
|
||||
TARGET_BLOCKS_PER_MONTH, TARGET_BLOCKS_PER_QUARTER, TARGET_BLOCKS_PER_SEMESTER,
|
||||
TARGET_BLOCKS_PER_WEEK, TARGET_BLOCKS_PER_YEAR,
|
||||
TARGET_BLOCKS_PER_DAY, TARGET_BLOCKS_PER_MONTH, TARGET_BLOCKS_PER_WEEK, TARGET_BLOCKS_PER_YEAR,
|
||||
};
|
||||
|
||||
pub struct BlockCountTarget;
|
||||
|
||||
impl UnaryTransform<Height, StoredU64> for BlockCountTarget {
|
||||
#[inline(always)]
|
||||
fn apply(_: Height) -> StoredU64 {
|
||||
StoredU64::from(TARGET_BLOCKS_PER_DAY)
|
||||
}
|
||||
macro_rules! const_block_target {
|
||||
($name:ident, $value:expr) => {
|
||||
pub struct $name;
|
||||
const_block_target!(@impl $name, $value, Height, Minute10, Minute30, Hour1, Hour4, Hour12, Day1, Day3, Week1, Month1, Month3, Month6, Year1, Year10, Halving, Epoch);
|
||||
};
|
||||
(@impl $name:ident, $value:expr, $($idx:ty),*) => {
|
||||
$(
|
||||
impl UnaryTransform<$idx, StoredU64> for $name {
|
||||
#[inline(always)]
|
||||
fn apply(_: $idx) -> StoredU64 {
|
||||
StoredU64::from($value)
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
impl UnaryTransform<Minute10, StoredU64> for BlockCountTarget {
|
||||
#[inline(always)]
|
||||
fn apply(_: Minute10) -> StoredU64 {
|
||||
StoredU64::from(TARGET_BLOCKS_PER_MINUTE10)
|
||||
}
|
||||
}
|
||||
|
||||
impl UnaryTransform<Minute30, StoredU64> for BlockCountTarget {
|
||||
#[inline(always)]
|
||||
fn apply(_: Minute30) -> StoredU64 {
|
||||
StoredU64::from(TARGET_BLOCKS_PER_MINUTE30)
|
||||
}
|
||||
}
|
||||
|
||||
impl UnaryTransform<Hour1, StoredU64> for BlockCountTarget {
|
||||
#[inline(always)]
|
||||
fn apply(_: Hour1) -> StoredU64 {
|
||||
StoredU64::from(TARGET_BLOCKS_PER_HOUR1)
|
||||
}
|
||||
}
|
||||
|
||||
impl UnaryTransform<Hour4, StoredU64> for BlockCountTarget {
|
||||
#[inline(always)]
|
||||
fn apply(_: Hour4) -> StoredU64 {
|
||||
StoredU64::from(TARGET_BLOCKS_PER_HOUR4)
|
||||
}
|
||||
}
|
||||
|
||||
impl UnaryTransform<Hour12, StoredU64> for BlockCountTarget {
|
||||
#[inline(always)]
|
||||
fn apply(_: Hour12) -> StoredU64 {
|
||||
StoredU64::from(TARGET_BLOCKS_PER_HOUR12)
|
||||
}
|
||||
}
|
||||
|
||||
impl UnaryTransform<Day1, StoredU64> for BlockCountTarget {
|
||||
#[inline(always)]
|
||||
fn apply(_: Day1) -> StoredU64 {
|
||||
StoredU64::from(TARGET_BLOCKS_PER_DAY)
|
||||
}
|
||||
}
|
||||
|
||||
impl UnaryTransform<Day3, StoredU64> for BlockCountTarget {
|
||||
#[inline(always)]
|
||||
fn apply(_: Day3) -> StoredU64 {
|
||||
StoredU64::from(TARGET_BLOCKS_PER_DAY3)
|
||||
}
|
||||
}
|
||||
|
||||
impl UnaryTransform<Week1, StoredU64> for BlockCountTarget {
|
||||
#[inline(always)]
|
||||
fn apply(_: Week1) -> StoredU64 {
|
||||
StoredU64::from(TARGET_BLOCKS_PER_WEEK)
|
||||
}
|
||||
}
|
||||
|
||||
impl UnaryTransform<Month1, StoredU64> for BlockCountTarget {
|
||||
#[inline(always)]
|
||||
fn apply(_: Month1) -> StoredU64 {
|
||||
StoredU64::from(TARGET_BLOCKS_PER_MONTH)
|
||||
}
|
||||
}
|
||||
|
||||
impl UnaryTransform<Month3, StoredU64> for BlockCountTarget {
|
||||
#[inline(always)]
|
||||
fn apply(_: Month3) -> StoredU64 {
|
||||
StoredU64::from(TARGET_BLOCKS_PER_QUARTER)
|
||||
}
|
||||
}
|
||||
|
||||
impl UnaryTransform<Month6, StoredU64> for BlockCountTarget {
|
||||
#[inline(always)]
|
||||
fn apply(_: Month6) -> StoredU64 {
|
||||
StoredU64::from(TARGET_BLOCKS_PER_SEMESTER)
|
||||
}
|
||||
}
|
||||
|
||||
impl UnaryTransform<Year1, StoredU64> for BlockCountTarget {
|
||||
#[inline(always)]
|
||||
fn apply(_: Year1) -> StoredU64 {
|
||||
StoredU64::from(TARGET_BLOCKS_PER_YEAR)
|
||||
}
|
||||
}
|
||||
|
||||
impl UnaryTransform<Year10, StoredU64> for BlockCountTarget {
|
||||
#[inline(always)]
|
||||
fn apply(_: Year10) -> StoredU64 {
|
||||
StoredU64::from(TARGET_BLOCKS_PER_DECADE)
|
||||
}
|
||||
}
|
||||
|
||||
impl UnaryTransform<Halving, StoredU64> for BlockCountTarget {
|
||||
#[inline(always)]
|
||||
fn apply(_: Halving) -> StoredU64 {
|
||||
StoredU64::from(TARGET_BLOCKS_PER_HALVING)
|
||||
}
|
||||
}
|
||||
|
||||
impl UnaryTransform<Epoch, StoredU64> for BlockCountTarget {
|
||||
#[inline(always)]
|
||||
fn apply(_: Epoch) -> StoredU64 {
|
||||
StoredU64::from(2016u64)
|
||||
}
|
||||
}
|
||||
const_block_target!(BlockCountTarget24h, TARGET_BLOCKS_PER_DAY);
|
||||
const_block_target!(BlockCountTarget1w, TARGET_BLOCKS_PER_WEEK);
|
||||
const_block_target!(BlockCountTarget1m, TARGET_BLOCKS_PER_MONTH);
|
||||
const_block_target!(BlockCountTarget1y, TARGET_BLOCKS_PER_YEAR);
|
||||
|
||||
pub struct OhlcCentsToDollars;
|
||||
|
||||
|
||||
@@ -33,8 +33,8 @@ impl Vecs {
|
||||
|
||||
self.unspent.height.compute_transform3(
|
||||
starting_indexes.height,
|
||||
&self.total.full.cumulative,
|
||||
&inputs_count.full.cumulative,
|
||||
&self.total.full.cumulative.height,
|
||||
&inputs_count.full.cumulative.height,
|
||||
&scripts_count.op_return.cumulative.height,
|
||||
|(h, output_count, input_count, op_return_count, ..)| {
|
||||
let block_count = u64::from(h + 1_usize);
|
||||
|
||||
@@ -39,14 +39,14 @@ impl Vecs {
|
||||
self.taproot.compute_binary::<_, _, RatioU64Bp16>(
|
||||
starting_indexes.height,
|
||||
&count.p2tr.base.height,
|
||||
&outputs_count.total.full.sum,
|
||||
&outputs_count.total.full.sum.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
self.segwit.compute_binary::<_, _, RatioU64Bp16>(
|
||||
starting_indexes.height,
|
||||
&count.segwit.base.height,
|
||||
&outputs_count.total.full.sum,
|
||||
&outputs_count.total.full.sum.height,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
|
||||
@@ -3,19 +3,19 @@ use brk_types::Version;
|
||||
use vecdb::{Database, EagerVec, ImportableVec};
|
||||
|
||||
use super::Vecs;
|
||||
use crate::internal::PerTxDistribution;
|
||||
use crate::{indexes, internal::PerTxDistribution};
|
||||
|
||||
/// Bump this when fee/feerate aggregation logic changes (e.g., skip coinbase).
|
||||
const VERSION: Version = Version::new(2);
|
||||
|
||||
impl Vecs {
|
||||
pub(crate) fn forced_import(db: &Database, version: Version) -> Result<Self> {
|
||||
pub(crate) fn forced_import(db: &Database, version: Version, indexes: &indexes::Vecs) -> Result<Self> {
|
||||
let v = version + VERSION;
|
||||
Ok(Self {
|
||||
input_value: EagerVec::forced_import(db, "input_value", version)?,
|
||||
output_value: EagerVec::forced_import(db, "output_value", version)?,
|
||||
fee: PerTxDistribution::forced_import(db, "fee", v)?,
|
||||
fee_rate: PerTxDistribution::forced_import(db, "fee_rate", v)?,
|
||||
fee: PerTxDistribution::forced_import(db, "fee", v, indexes)?,
|
||||
fee_rate: PerTxDistribution::forced_import(db, "fee_rate", v, indexes)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,8 +23,8 @@ impl Vecs {
|
||||
let version = parent_version;
|
||||
|
||||
let count = CountVecs::forced_import(&db, version, indexer, indexes, cached_starts)?;
|
||||
let size = SizeVecs::forced_import(&db, version, indexer)?;
|
||||
let fees = FeesVecs::forced_import(&db, version)?;
|
||||
let size = SizeVecs::forced_import(&db, version, indexer, indexes)?;
|
||||
let fees = FeesVecs::forced_import(&db, version, indexes)?;
|
||||
let versions = VersionsVecs::forced_import(&db, version, indexes, cached_starts)?;
|
||||
let volume = VolumeVecs::forced_import(&db, version, indexes, cached_starts)?;
|
||||
|
||||
|
||||
@@ -4,13 +4,14 @@ use brk_types::{TxIndex, VSize, Version, Weight};
|
||||
use vecdb::{Database, LazyVecFrom2, ReadableCloneableVec};
|
||||
|
||||
use super::Vecs;
|
||||
use crate::internal::{LazyPerTxDistribution, LazyPerTxDistributionTransformed, VSizeToWeight};
|
||||
use crate::{indexes, internal::{LazyPerTxDistribution, LazyPerTxDistributionTransformed, VSizeToWeight}};
|
||||
|
||||
impl Vecs {
|
||||
pub(crate) fn forced_import(
|
||||
db: &Database,
|
||||
version: Version,
|
||||
indexer: &Indexer,
|
||||
indexes: &indexes::Vecs,
|
||||
) -> Result<Self> {
|
||||
let tx_index_to_vsize = LazyVecFrom2::init(
|
||||
"tx_vsize",
|
||||
@@ -23,7 +24,7 @@ impl Vecs {
|
||||
);
|
||||
|
||||
let vsize =
|
||||
LazyPerTxDistribution::forced_import(db, "tx_vsize", version, tx_index_to_vsize)?;
|
||||
LazyPerTxDistribution::forced_import(db, "tx_vsize", version, indexes, tx_index_to_vsize)?;
|
||||
|
||||
let tx_index_to_weight = LazyVecFrom2::init(
|
||||
"tx_weight",
|
||||
|
||||
@@ -22,44 +22,21 @@ impl Vecs {
|
||||
starting_indexes: &Indexes,
|
||||
exit: &Exit,
|
||||
) -> Result<()> {
|
||||
// sent_sum and received_sum are independent — parallelize
|
||||
let (r1, r2) = rayon::join(
|
||||
|| {
|
||||
self.transfer_volume.compute(
|
||||
self.transfer_volume.compute(
|
||||
starting_indexes.height,
|
||||
prices,
|
||||
exit,
|
||||
|sats_vec| {
|
||||
Ok(sats_vec.compute_filtered_sum_from_indexes(
|
||||
starting_indexes.height,
|
||||
prices,
|
||||
&indexer.vecs.transactions.first_tx_index,
|
||||
&indexes.height.tx_index_count,
|
||||
&fees_vecs.input_value,
|
||||
|sats| !sats.is_max(),
|
||||
exit,
|
||||
|sats_vec| {
|
||||
Ok(sats_vec.compute_filtered_sum_from_indexes(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.transactions.first_tx_index,
|
||||
&indexes.height.tx_index_count,
|
||||
&fees_vecs.input_value,
|
||||
|sats| !sats.is_max(),
|
||||
exit,
|
||||
)?)
|
||||
},
|
||||
)
|
||||
)?)
|
||||
},
|
||||
|| {
|
||||
self.output_volume.compute(
|
||||
starting_indexes.height,
|
||||
prices,
|
||||
exit,
|
||||
|sats_vec| {
|
||||
Ok(sats_vec.compute_sum_from_indexes(
|
||||
starting_indexes.height,
|
||||
&indexer.vecs.transactions.first_tx_index,
|
||||
&indexes.height.tx_index_count,
|
||||
&fees_vecs.output_value,
|
||||
exit,
|
||||
)?)
|
||||
},
|
||||
)
|
||||
},
|
||||
);
|
||||
r1?;
|
||||
r2?;
|
||||
)?;
|
||||
|
||||
self.tx_per_sec
|
||||
.height
|
||||
@@ -73,7 +50,7 @@ impl Vecs {
|
||||
.height
|
||||
.compute_binary::<_, Timestamp, PerSec>(
|
||||
starting_indexes.height,
|
||||
&inputs_count.full.sum,
|
||||
&inputs_count.full.sum.height,
|
||||
&blocks.interval.base,
|
||||
exit,
|
||||
)?;
|
||||
@@ -81,7 +58,7 @@ impl Vecs {
|
||||
.height
|
||||
.compute_binary::<_, Timestamp, PerSec>(
|
||||
starting_indexes.height,
|
||||
&outputs_count.total.full.sum,
|
||||
&outputs_count.total.full.sum.height,
|
||||
&blocks.interval.base,
|
||||
exit,
|
||||
)?;
|
||||
|
||||
@@ -24,13 +24,6 @@ impl Vecs {
|
||||
indexes,
|
||||
cached_starts,
|
||||
)?,
|
||||
output_volume: AmountPerBlockCumulativeWithSums::forced_import(
|
||||
db,
|
||||
"output_volume",
|
||||
version,
|
||||
indexes,
|
||||
cached_starts,
|
||||
)?,
|
||||
tx_per_sec: PerBlock::forced_import(db, "tx_per_sec", version + v2, indexes)?,
|
||||
outputs_per_sec: PerBlock::forced_import(
|
||||
db,
|
||||
|
||||
@@ -7,7 +7,6 @@ use crate::internal::{AmountPerBlockCumulativeWithSums, PerBlock};
|
||||
#[derive(Traversable)]
|
||||
pub struct Vecs<M: StorageMode = Rw> {
|
||||
pub transfer_volume: AmountPerBlockCumulativeWithSums<M>,
|
||||
pub output_volume: AmountPerBlockCumulativeWithSums<M>,
|
||||
pub tx_per_sec: PerBlock<StoredF32, M>,
|
||||
pub outputs_per_sec: PerBlock<StoredF32, M>,
|
||||
pub inputs_per_sec: PerBlock<StoredF32, M>,
|
||||
|
||||
@@ -1797,17 +1797,17 @@ function create_10y1m1w1y2y3m3y4y5y6m6y8yPattern3(client, acc) {
|
||||
|
||||
/**
|
||||
* @typedef {Object} AverageCumulativeMaxMedianMinPct10Pct25Pct75Pct90RollingSumPattern
|
||||
* @property {SeriesPattern18<StoredU64>} average
|
||||
* @property {SeriesPattern18<StoredU64>} cumulative
|
||||
* @property {SeriesPattern18<StoredU64>} max
|
||||
* @property {SeriesPattern18<StoredU64>} median
|
||||
* @property {SeriesPattern18<StoredU64>} min
|
||||
* @property {SeriesPattern18<StoredU64>} pct10
|
||||
* @property {SeriesPattern18<StoredU64>} pct25
|
||||
* @property {SeriesPattern18<StoredU64>} pct75
|
||||
* @property {SeriesPattern18<StoredU64>} pct90
|
||||
* @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 {SeriesPattern18<StoredU64>} sum
|
||||
* @property {SeriesPattern1<StoredU64>} sum
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -1818,17 +1818,17 @@ function create_10y1m1w1y2y3m3y4y5y6m6y8yPattern3(client, acc) {
|
||||
*/
|
||||
function createAverageCumulativeMaxMedianMinPct10Pct25Pct75Pct90RollingSumPattern(client, acc) {
|
||||
return {
|
||||
average: createSeriesPattern18(client, _m(acc, 'average')),
|
||||
cumulative: createSeriesPattern18(client, _m(acc, 'cumulative')),
|
||||
max: createSeriesPattern18(client, _m(acc, 'max')),
|
||||
median: createSeriesPattern18(client, _m(acc, 'median')),
|
||||
min: createSeriesPattern18(client, _m(acc, 'min')),
|
||||
pct10: createSeriesPattern18(client, _m(acc, 'pct10')),
|
||||
pct25: createSeriesPattern18(client, _m(acc, 'pct25')),
|
||||
pct75: createSeriesPattern18(client, _m(acc, 'pct75')),
|
||||
pct90: createSeriesPattern18(client, _m(acc, 'pct90')),
|
||||
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: createSeriesPattern18(client, _m(acc, 'sum')),
|
||||
sum: createSeriesPattern1(client, _m(acc, 'sum')),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1987,6 +1987,37 @@ function createAverageMaxMedianMinPct10Pct25Pct75Pct90SumPattern(client, acc) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} AverageMaxMedianMinPct10Pct25Pct75Pct90Pattern2
|
||||
* @property {SeriesPattern18<Weight>} average
|
||||
* @property {SeriesPattern18<Weight>} max
|
||||
* @property {SeriesPattern18<Weight>} median
|
||||
* @property {SeriesPattern18<Weight>} min
|
||||
* @property {SeriesPattern18<Weight>} pct10
|
||||
* @property {SeriesPattern18<Weight>} pct25
|
||||
* @property {SeriesPattern18<Weight>} pct75
|
||||
* @property {SeriesPattern18<Weight>} pct90
|
||||
*/
|
||||
|
||||
/**
|
||||
* Create a AverageMaxMedianMinPct10Pct25Pct75Pct90Pattern2 pattern node
|
||||
* @param {BrkClientBase} client
|
||||
* @param {string} acc - Accumulated series name
|
||||
* @returns {AverageMaxMedianMinPct10Pct25Pct75Pct90Pattern2}
|
||||
*/
|
||||
function createAverageMaxMedianMinPct10Pct25Pct75Pct90Pattern2(client, acc) {
|
||||
return {
|
||||
average: createSeriesPattern18(client, _m(acc, 'average')),
|
||||
max: createSeriesPattern18(client, _m(acc, 'max')),
|
||||
median: createSeriesPattern18(client, _m(acc, 'median')),
|
||||
min: createSeriesPattern18(client, _m(acc, 'min')),
|
||||
pct10: createSeriesPattern18(client, _m(acc, 'pct10')),
|
||||
pct25: createSeriesPattern18(client, _m(acc, 'pct25')),
|
||||
pct75: createSeriesPattern18(client, _m(acc, 'pct75')),
|
||||
pct90: createSeriesPattern18(client, _m(acc, 'pct90')),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} BaseCapitulationCumulativeNegativeSumToValuePattern
|
||||
* @property {CentsUsdPattern2} base
|
||||
@@ -2014,14 +2045,14 @@ function createAverageMaxMedianMinPct10Pct25Pct75Pct90SumPattern(client, acc) {
|
||||
/**
|
||||
* @template T
|
||||
* @typedef {Object} AverageMaxMedianMinPct10Pct25Pct75Pct90Pattern
|
||||
* @property {SeriesPattern18<T>} average
|
||||
* @property {SeriesPattern18<T>} max
|
||||
* @property {SeriesPattern18<T>} median
|
||||
* @property {SeriesPattern18<T>} min
|
||||
* @property {SeriesPattern18<T>} pct10
|
||||
* @property {SeriesPattern18<T>} pct25
|
||||
* @property {SeriesPattern18<T>} pct75
|
||||
* @property {SeriesPattern18<T>} pct90
|
||||
* @property {SeriesPattern1<T>} average
|
||||
* @property {SeriesPattern1<T>} max
|
||||
* @property {SeriesPattern1<T>} median
|
||||
* @property {SeriesPattern1<T>} min
|
||||
* @property {SeriesPattern1<T>} pct10
|
||||
* @property {SeriesPattern1<T>} pct25
|
||||
* @property {SeriesPattern1<T>} pct75
|
||||
* @property {SeriesPattern1<T>} pct90
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -2033,14 +2064,14 @@ function createAverageMaxMedianMinPct10Pct25Pct75Pct90SumPattern(client, acc) {
|
||||
*/
|
||||
function createAverageMaxMedianMinPct10Pct25Pct75Pct90Pattern(client, acc) {
|
||||
return {
|
||||
average: createSeriesPattern18(client, _m(acc, 'average')),
|
||||
max: createSeriesPattern18(client, _m(acc, 'max')),
|
||||
median: createSeriesPattern18(client, _m(acc, 'median')),
|
||||
min: createSeriesPattern18(client, _m(acc, 'min')),
|
||||
pct10: createSeriesPattern18(client, _m(acc, 'pct10')),
|
||||
pct25: createSeriesPattern18(client, _m(acc, 'pct25')),
|
||||
pct75: createSeriesPattern18(client, _m(acc, 'pct75')),
|
||||
pct90: createSeriesPattern18(client, _m(acc, 'pct90')),
|
||||
average: createSeriesPattern1(client, _m(acc, 'average')),
|
||||
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')),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3959,7 +3990,7 @@ function createUnspentPattern(client, acc) {
|
||||
|
||||
/**
|
||||
* @typedef {Object} SeriesTree_Blocks_Count
|
||||
* @property {SeriesPattern1<StoredU64>} target
|
||||
* @property {_1m1w1y24hPattern<StoredU64>} target
|
||||
* @property {BaseCumulativeSumPattern2} total
|
||||
*/
|
||||
|
||||
@@ -4012,9 +4043,9 @@ function createUnspentPattern(client, acc) {
|
||||
|
||||
/**
|
||||
* @typedef {Object} SeriesTree_Blocks_Fullness
|
||||
* @property {_1m1w1y24hBasePattern<BasisPoints16>} bps
|
||||
* @property {SeriesPattern1<StoredF32>} ratio
|
||||
* @property {SeriesPattern1<StoredF32>} percent
|
||||
* @property {SeriesPattern18<BasisPoints16>} bps
|
||||
* @property {SeriesPattern18<StoredF32>} ratio
|
||||
* @property {SeriesPattern18<StoredF32>} percent
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -4057,7 +4088,14 @@ function createUnspentPattern(client, acc) {
|
||||
/**
|
||||
* @typedef {Object} SeriesTree_Transactions_Size
|
||||
* @property {_6bBlockTxPattern<VSize>} vsize
|
||||
* @property {_6bBlockTxPattern<Weight>} weight
|
||||
* @property {SeriesTree_Transactions_Size_Weight} weight
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} SeriesTree_Transactions_Size_Weight
|
||||
* @property {SeriesPattern19<Weight>} txIndex
|
||||
* @property {AverageMaxMedianMinPct10Pct25Pct75Pct90Pattern2} block
|
||||
* @property {AverageMaxMedianMinPct10Pct25Pct75Pct90Pattern2} _6b
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -4078,7 +4116,6 @@ function createUnspentPattern(client, acc) {
|
||||
/**
|
||||
* @typedef {Object} SeriesTree_Transactions_Volume
|
||||
* @property {BaseCumulativeSumPattern4} transferVolume
|
||||
* @property {BaseCumulativeSumPattern4} outputVolume
|
||||
* @property {SeriesPattern1<StoredF32>} txPerSec
|
||||
* @property {SeriesPattern1<StoredF32>} outputsPerSec
|
||||
* @property {SeriesPattern1<StoredF32>} inputsPerSec
|
||||
@@ -7492,7 +7529,7 @@ class BrkClient extends BrkClientBase {
|
||||
pct90: create_1m1w1y24hPattern(this, 'block_weight_pct90'),
|
||||
},
|
||||
count: {
|
||||
target: createSeriesPattern1(this, 'block_count_target'),
|
||||
target: create_1m1w1y24hPattern(this, 'block_count_target'),
|
||||
total: createBaseCumulativeSumPattern2(this, 'block_count'),
|
||||
},
|
||||
lookback: {
|
||||
@@ -7543,9 +7580,9 @@ class BrkClient extends BrkClientBase {
|
||||
interval: create_1m1w1y24hBasePattern(this, 'block_interval'),
|
||||
vbytes: createAverageBaseCumulativeMaxMedianMinPct10Pct25Pct75Pct90SumPattern2(this, 'block_vbytes'),
|
||||
fullness: {
|
||||
bps: create_1m1w1y24hBasePattern(this, 'block_fullness_bps'),
|
||||
ratio: createSeriesPattern1(this, 'block_fullness_ratio'),
|
||||
percent: createSeriesPattern1(this, 'block_fullness'),
|
||||
bps: createSeriesPattern18(this, 'block_fullness_bps'),
|
||||
ratio: createSeriesPattern18(this, 'block_fullness_ratio'),
|
||||
percent: createSeriesPattern18(this, 'block_fullness'),
|
||||
},
|
||||
halving: {
|
||||
epoch: createSeriesPattern1(this, 'halving_epoch'),
|
||||
@@ -7572,7 +7609,11 @@ class BrkClient extends BrkClientBase {
|
||||
},
|
||||
size: {
|
||||
vsize: create_6bBlockTxPattern(this, 'tx_vsize'),
|
||||
weight: create_6bBlockTxPattern(this, 'tx_weight'),
|
||||
weight: {
|
||||
txIndex: createSeriesPattern19(this, 'tx_weight'),
|
||||
block: createAverageMaxMedianMinPct10Pct25Pct75Pct90Pattern2(this, 'tx_weight'),
|
||||
_6b: createAverageMaxMedianMinPct10Pct25Pct75Pct90Pattern2(this, 'tx_weight_6b'),
|
||||
},
|
||||
},
|
||||
fees: {
|
||||
inputValue: createSeriesPattern19(this, 'input_value'),
|
||||
@@ -7587,7 +7628,6 @@ class BrkClient extends BrkClientBase {
|
||||
},
|
||||
volume: {
|
||||
transferVolume: createBaseCumulativeSumPattern4(this, 'transfer_volume_bis'),
|
||||
outputVolume: createBaseCumulativeSumPattern4(this, 'output_volume'),
|
||||
txPerSec: createSeriesPattern1(this, 'tx_per_sec'),
|
||||
outputsPerSec: createSeriesPattern1(this, 'outputs_per_sec'),
|
||||
inputsPerSec: createSeriesPattern1(this, 'inputs_per_sec'),
|
||||
|
||||
@@ -2226,17 +2226,17 @@ class AverageCumulativeMaxMedianMinPct10Pct25Pct75Pct90RollingSumPattern:
|
||||
|
||||
def __init__(self, client: BrkClientBase, acc: str):
|
||||
"""Create pattern node with accumulated series name."""
|
||||
self.average: SeriesPattern18[StoredU64] = SeriesPattern18(client, _m(acc, 'average'))
|
||||
self.cumulative: SeriesPattern18[StoredU64] = SeriesPattern18(client, _m(acc, 'cumulative'))
|
||||
self.max: SeriesPattern18[StoredU64] = SeriesPattern18(client, _m(acc, 'max'))
|
||||
self.median: SeriesPattern18[StoredU64] = SeriesPattern18(client, _m(acc, 'median'))
|
||||
self.min: SeriesPattern18[StoredU64] = SeriesPattern18(client, _m(acc, 'min'))
|
||||
self.pct10: SeriesPattern18[StoredU64] = SeriesPattern18(client, _m(acc, 'pct10'))
|
||||
self.pct25: SeriesPattern18[StoredU64] = SeriesPattern18(client, _m(acc, 'pct25'))
|
||||
self.pct75: SeriesPattern18[StoredU64] = SeriesPattern18(client, _m(acc, 'pct75'))
|
||||
self.pct90: SeriesPattern18[StoredU64] = SeriesPattern18(client, _m(acc, 'pct90'))
|
||||
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: SeriesPattern18[StoredU64] = SeriesPattern18(client, _m(acc, 'sum'))
|
||||
self.sum: SeriesPattern1[StoredU64] = SeriesPattern1(client, _m(acc, 'sum'))
|
||||
|
||||
class AverageBaseCumulativeMaxMedianMinPct10Pct25Pct75Pct90SumPattern2:
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
@@ -2305,6 +2305,20 @@ class AverageMaxMedianMinPct10Pct25Pct75Pct90SumPattern:
|
||||
self.pct90: _1m1w1y24hPattern[StoredU64] = _1m1w1y24hPattern(client, _m(acc, 'pct90'))
|
||||
self.sum: _1m1w1y24hPattern[StoredU64] = _1m1w1y24hPattern(client, _m(acc, 'sum'))
|
||||
|
||||
class AverageMaxMedianMinPct10Pct25Pct75Pct90Pattern2:
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
|
||||
def __init__(self, client: BrkClientBase, acc: str):
|
||||
"""Create pattern node with accumulated series name."""
|
||||
self.average: SeriesPattern18[Weight] = SeriesPattern18(client, _m(acc, 'average'))
|
||||
self.max: SeriesPattern18[Weight] = SeriesPattern18(client, _m(acc, 'max'))
|
||||
self.median: SeriesPattern18[Weight] = SeriesPattern18(client, _m(acc, 'median'))
|
||||
self.min: SeriesPattern18[Weight] = SeriesPattern18(client, _m(acc, 'min'))
|
||||
self.pct10: SeriesPattern18[Weight] = SeriesPattern18(client, _m(acc, 'pct10'))
|
||||
self.pct25: SeriesPattern18[Weight] = SeriesPattern18(client, _m(acc, 'pct25'))
|
||||
self.pct75: SeriesPattern18[Weight] = SeriesPattern18(client, _m(acc, 'pct75'))
|
||||
self.pct90: SeriesPattern18[Weight] = SeriesPattern18(client, _m(acc, 'pct90'))
|
||||
|
||||
class BaseCapitulationCumulativeNegativeSumToValuePattern:
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
pass
|
||||
@@ -2318,14 +2332,14 @@ class AverageMaxMedianMinPct10Pct25Pct75Pct90Pattern(Generic[T]):
|
||||
|
||||
def __init__(self, client: BrkClientBase, acc: str):
|
||||
"""Create pattern node with accumulated series name."""
|
||||
self.average: SeriesPattern18[T] = SeriesPattern18(client, _m(acc, 'average'))
|
||||
self.max: SeriesPattern18[T] = SeriesPattern18(client, _m(acc, 'max'))
|
||||
self.median: SeriesPattern18[T] = SeriesPattern18(client, _m(acc, 'median'))
|
||||
self.min: SeriesPattern18[T] = SeriesPattern18(client, _m(acc, 'min'))
|
||||
self.pct10: SeriesPattern18[T] = SeriesPattern18(client, _m(acc, 'pct10'))
|
||||
self.pct25: SeriesPattern18[T] = SeriesPattern18(client, _m(acc, 'pct25'))
|
||||
self.pct75: SeriesPattern18[T] = SeriesPattern18(client, _m(acc, 'pct75'))
|
||||
self.pct90: SeriesPattern18[T] = SeriesPattern18(client, _m(acc, 'pct90'))
|
||||
self.average: SeriesPattern1[T] = SeriesPattern1(client, _m(acc, 'average'))
|
||||
self.max: SeriesPattern1[T] = SeriesPattern1(client, _m(acc, 'max'))
|
||||
self.median: SeriesPattern1[T] = SeriesPattern1(client, _m(acc, 'median'))
|
||||
self.min: SeriesPattern1[T] = SeriesPattern1(client, _m(acc, 'min'))
|
||||
self.pct10: SeriesPattern1[T] = SeriesPattern1(client, _m(acc, 'pct10'))
|
||||
self.pct25: SeriesPattern1[T] = SeriesPattern1(client, _m(acc, 'pct25'))
|
||||
self.pct75: SeriesPattern1[T] = SeriesPattern1(client, _m(acc, 'pct75'))
|
||||
self.pct90: SeriesPattern1[T] = SeriesPattern1(client, _m(acc, 'pct90'))
|
||||
|
||||
class _10y2y3y4y5y6y8yPattern:
|
||||
"""Pattern struct for repeated tree structure."""
|
||||
@@ -3175,7 +3189,7 @@ class SeriesTree_Blocks_Count:
|
||||
"""Series tree node."""
|
||||
|
||||
def __init__(self, client: BrkClientBase, base_path: str = ''):
|
||||
self.target: SeriesPattern1[StoredU64] = SeriesPattern1(client, 'block_count_target')
|
||||
self.target: _1m1w1y24hPattern[StoredU64] = _1m1w1y24hPattern(client, 'block_count_target')
|
||||
self.total: BaseCumulativeSumPattern2 = BaseCumulativeSumPattern2(client, 'block_count')
|
||||
|
||||
class SeriesTree_Blocks_Lookback:
|
||||
@@ -3230,9 +3244,9 @@ class SeriesTree_Blocks_Fullness:
|
||||
"""Series tree node."""
|
||||
|
||||
def __init__(self, client: BrkClientBase, base_path: str = ''):
|
||||
self.bps: _1m1w1y24hBasePattern[BasisPoints16] = _1m1w1y24hBasePattern(client, 'block_fullness_bps')
|
||||
self.ratio: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'block_fullness_ratio')
|
||||
self.percent: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'block_fullness')
|
||||
self.bps: SeriesPattern18[BasisPoints16] = SeriesPattern18(client, 'block_fullness_bps')
|
||||
self.ratio: SeriesPattern18[StoredF32] = SeriesPattern18(client, 'block_fullness_ratio')
|
||||
self.percent: SeriesPattern18[StoredF32] = SeriesPattern18(client, 'block_fullness')
|
||||
|
||||
class SeriesTree_Blocks_Halving:
|
||||
"""Series tree node."""
|
||||
@@ -3280,12 +3294,20 @@ class SeriesTree_Transactions_Count:
|
||||
self.total: AverageBaseCumulativeMaxMedianMinPct10Pct25Pct75Pct90SumPattern2 = AverageBaseCumulativeMaxMedianMinPct10Pct25Pct75Pct90SumPattern2(client, 'tx_count')
|
||||
self.is_coinbase: SeriesPattern19[StoredBool] = SeriesPattern19(client, 'is_coinbase')
|
||||
|
||||
class SeriesTree_Transactions_Size_Weight:
|
||||
"""Series tree node."""
|
||||
|
||||
def __init__(self, client: BrkClientBase, base_path: str = ''):
|
||||
self.tx_index: SeriesPattern19[Weight] = SeriesPattern19(client, 'tx_weight')
|
||||
self.block: AverageMaxMedianMinPct10Pct25Pct75Pct90Pattern2 = AverageMaxMedianMinPct10Pct25Pct75Pct90Pattern2(client, 'tx_weight')
|
||||
self._6b: AverageMaxMedianMinPct10Pct25Pct75Pct90Pattern2 = AverageMaxMedianMinPct10Pct25Pct75Pct90Pattern2(client, 'tx_weight_6b')
|
||||
|
||||
class SeriesTree_Transactions_Size:
|
||||
"""Series tree node."""
|
||||
|
||||
def __init__(self, client: BrkClientBase, base_path: str = ''):
|
||||
self.vsize: _6bBlockTxPattern[VSize] = _6bBlockTxPattern(client, 'tx_vsize')
|
||||
self.weight: _6bBlockTxPattern[Weight] = _6bBlockTxPattern(client, 'tx_weight')
|
||||
self.weight: SeriesTree_Transactions_Size_Weight = SeriesTree_Transactions_Size_Weight(client)
|
||||
|
||||
class SeriesTree_Transactions_Fees:
|
||||
"""Series tree node."""
|
||||
@@ -3309,7 +3331,6 @@ class SeriesTree_Transactions_Volume:
|
||||
|
||||
def __init__(self, client: BrkClientBase, base_path: str = ''):
|
||||
self.transfer_volume: BaseCumulativeSumPattern4 = BaseCumulativeSumPattern4(client, 'transfer_volume_bis')
|
||||
self.output_volume: BaseCumulativeSumPattern4 = BaseCumulativeSumPattern4(client, 'output_volume')
|
||||
self.tx_per_sec: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'tx_per_sec')
|
||||
self.outputs_per_sec: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'outputs_per_sec')
|
||||
self.inputs_per_sec: SeriesPattern1[StoredF32] = SeriesPattern1(client, 'inputs_per_sec')
|
||||
|
||||
@@ -7,6 +7,7 @@ import { Unit } from "../utils/units.js";
|
||||
import { priceLine, priceLines } from "./constants.js";
|
||||
import {
|
||||
baseline,
|
||||
deltaTree,
|
||||
histogram,
|
||||
line,
|
||||
price,
|
||||
@@ -522,58 +523,12 @@ export function createMarketSection() {
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Change",
|
||||
tree: [
|
||||
{
|
||||
name: "Compare",
|
||||
title: "Market Cap Change",
|
||||
bottom: ROLLING_WINDOWS.map((w) =>
|
||||
baseline({
|
||||
series: supply.marketCap.delta.absolute[w.key].usd,
|
||||
name: w.name,
|
||||
color: w.color,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
),
|
||||
},
|
||||
...ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: `Market Cap Change ${w.name}`,
|
||||
bottom: [
|
||||
baseline({
|
||||
series: supply.marketCap.delta.absolute[w.key].usd,
|
||||
name: w.name,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
],
|
||||
})),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Growth Rate",
|
||||
tree: [
|
||||
{
|
||||
name: "Compare",
|
||||
title: "Market Cap Growth Rate",
|
||||
bottom: ROLLING_WINDOWS.flatMap((w) =>
|
||||
percentRatio({
|
||||
pattern: supply.marketCap.delta.rate[w.key],
|
||||
name: w.name,
|
||||
color: w.color,
|
||||
}),
|
||||
),
|
||||
},
|
||||
...ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: `Market Cap Growth Rate ${w.name}`,
|
||||
bottom: percentRatioBaseline({
|
||||
pattern: supply.marketCap.delta.rate[w.key],
|
||||
name: w.name,
|
||||
}),
|
||||
})),
|
||||
],
|
||||
},
|
||||
...deltaTree({
|
||||
delta: supply.marketCap.delta,
|
||||
title: "Market Cap",
|
||||
unit: Unit.usd,
|
||||
extract: (v) => v.usd,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -591,58 +546,12 @@ export function createMarketSection() {
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Change",
|
||||
tree: [
|
||||
{
|
||||
name: "Compare",
|
||||
title: "Realized Cap Change",
|
||||
bottom: ROLLING_WINDOWS.map((w) =>
|
||||
baseline({
|
||||
series: cohorts.utxo.all.realized.cap.delta.absolute[w.key].usd,
|
||||
name: w.name,
|
||||
color: w.color,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
),
|
||||
},
|
||||
...ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: `Realized Cap Change ${w.name}`,
|
||||
bottom: [
|
||||
baseline({
|
||||
series: cohorts.utxo.all.realized.cap.delta.absolute[w.key].usd,
|
||||
name: w.name,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
],
|
||||
})),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Growth Rate",
|
||||
tree: [
|
||||
{
|
||||
name: "Compare",
|
||||
title: "Realized Cap Growth Rate",
|
||||
bottom: ROLLING_WINDOWS.flatMap((w) =>
|
||||
percentRatio({
|
||||
pattern: cohorts.utxo.all.realized.cap.delta.rate[w.key],
|
||||
name: w.name,
|
||||
color: w.color,
|
||||
}),
|
||||
),
|
||||
},
|
||||
...ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: `Realized Cap Growth Rate ${w.name}`,
|
||||
bottom: percentRatioBaseline({
|
||||
pattern: cohorts.utxo.all.realized.cap.delta.rate[w.key],
|
||||
name: w.name,
|
||||
}),
|
||||
})),
|
||||
],
|
||||
},
|
||||
...deltaTree({
|
||||
delta: cohorts.utxo.all.realized.cap.delta,
|
||||
title: "Realized Cap",
|
||||
unit: Unit.usd,
|
||||
extract: (v) => v.usd,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -12,18 +12,18 @@ import {
|
||||
fromSupplyPattern,
|
||||
chartsFromFullPerBlock,
|
||||
chartsFromCount,
|
||||
chartsFromCountEntries,
|
||||
chartsFromSumPerBlock,
|
||||
rollingWindowsTree,
|
||||
distributionWindowsTree,
|
||||
|
||||
ROLLING_WINDOWS,
|
||||
chartsFromBlockAnd6b,
|
||||
fromStatsPattern,
|
||||
simpleDeltaTree,
|
||||
percentRatio,
|
||||
percentRatioDots,
|
||||
rollingPercentRatioTree,
|
||||
} from "./series.js";
|
||||
import { satsBtcUsd, satsBtcUsdFrom } from "./shared.js";
|
||||
import { satsBtcUsd, satsBtcUsdFrom, satsBtcUsdFullTree } from "./shared.js";
|
||||
|
||||
/**
|
||||
* Create Network section
|
||||
@@ -512,202 +512,97 @@ export function createNetworkSection() {
|
||||
unit: Unit.count,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "Volume",
|
||||
tree: satsBtcUsdFullTree({
|
||||
pattern: transactions.volume.transferVolume,
|
||||
name: "base",
|
||||
title: "Transaction Volume",
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "Fee Rate",
|
||||
title: "Fee Rate Distribution (6 Blocks)",
|
||||
bottom: fromStatsPattern({ pattern: transactions.fees.feeRate._6b, unit: Unit.feeRate }),
|
||||
tree: chartsFromBlockAnd6b({
|
||||
pattern: transactions.fees.feeRate,
|
||||
title: "Transaction Fee Rate",
|
||||
unit: Unit.feeRate,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "Fee",
|
||||
title: "Fee Distribution (6 Blocks)",
|
||||
bottom: fromStatsPattern({ pattern: transactions.fees.fee._6b, unit: Unit.sats }),
|
||||
tree: chartsFromBlockAnd6b({
|
||||
pattern: transactions.fees.fee,
|
||||
title: "Transaction Fee",
|
||||
unit: Unit.sats,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "Volume",
|
||||
tree: [
|
||||
{
|
||||
name: "Transferred",
|
||||
title: "Transaction Volume",
|
||||
bottom: [
|
||||
...satsBtcUsd({
|
||||
pattern: transactions.volume.transferVolume.base,
|
||||
name: "Sent",
|
||||
}),
|
||||
...satsBtcUsd({
|
||||
pattern: transactions.volume.outputVolume.base,
|
||||
name: "Received",
|
||||
color: colors.entity.output,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Sent Rolling",
|
||||
tree: [
|
||||
{
|
||||
name: "Compare",
|
||||
title: "Sent Volume Rolling",
|
||||
bottom: ROLLING_WINDOWS.flatMap((w) =>
|
||||
satsBtcUsd({
|
||||
pattern: transactions.volume.transferVolume.sum[w.key],
|
||||
name: w.name,
|
||||
color: w.color,
|
||||
}),
|
||||
),
|
||||
},
|
||||
...ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: `Sent Volume ${w.name}`,
|
||||
bottom: satsBtcUsd({
|
||||
pattern: transactions.volume.transferVolume.sum[w.key],
|
||||
name: w.name,
|
||||
color: w.color,
|
||||
}),
|
||||
})),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Sent Cumulative",
|
||||
title: "Sent Volume (Total)",
|
||||
bottom: satsBtcUsd({
|
||||
pattern: transactions.volume.transferVolume.cumulative,
|
||||
name: "all-time",
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "Received Rolling",
|
||||
tree: [
|
||||
{
|
||||
name: "Compare",
|
||||
title: "Received Volume Rolling",
|
||||
bottom: ROLLING_WINDOWS.flatMap((w) =>
|
||||
satsBtcUsd({
|
||||
pattern: transactions.volume.outputVolume.sum[w.key],
|
||||
name: w.name,
|
||||
color: w.color,
|
||||
}),
|
||||
),
|
||||
},
|
||||
...ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: `Received Volume ${w.name}`,
|
||||
bottom: satsBtcUsd({
|
||||
pattern: transactions.volume.outputVolume.sum[w.key],
|
||||
name: w.name,
|
||||
color: w.color,
|
||||
}),
|
||||
})),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Received Cumulative",
|
||||
title: "Received Volume (Total)",
|
||||
bottom: satsBtcUsd({
|
||||
pattern: transactions.volume.outputVolume.cumulative,
|
||||
name: "all-time",
|
||||
}),
|
||||
},
|
||||
],
|
||||
name: "Weight",
|
||||
tree: chartsFromBlockAnd6b({
|
||||
pattern: transactions.size.weight,
|
||||
title: "Transaction Weight",
|
||||
unit: Unit.wu,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "Size",
|
||||
tree: [
|
||||
{
|
||||
name: "Weight",
|
||||
tree: chartsFromBlockAnd6b({
|
||||
pattern: transactions.size.weight,
|
||||
title: "Transaction Weight",
|
||||
unit: Unit.wu,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "vSize",
|
||||
tree: chartsFromBlockAnd6b({
|
||||
pattern: transactions.size.vsize,
|
||||
title: "Transaction vSize",
|
||||
unit: Unit.vb,
|
||||
}),
|
||||
},
|
||||
],
|
||||
name: "vSize",
|
||||
tree: chartsFromBlockAnd6b({
|
||||
pattern: transactions.size.vsize,
|
||||
title: "Transaction vSize",
|
||||
unit: Unit.vb,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "Versions",
|
||||
tree: [
|
||||
{
|
||||
name: "Base",
|
||||
title: "Transaction Versions",
|
||||
bottom: entries(transactions.versions).map(
|
||||
([v, data], i, arr) =>
|
||||
line({
|
||||
series: data.base,
|
||||
name: v,
|
||||
color: colors.at(i, arr.length),
|
||||
unit: Unit.count,
|
||||
}),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "Rolling",
|
||||
tree: [
|
||||
{
|
||||
name: "Compare",
|
||||
title: "Transaction Versions Rolling",
|
||||
bottom: entries(transactions.versions).flatMap(
|
||||
([v, data], i, arr) =>
|
||||
ROLLING_WINDOWS.map((w) =>
|
||||
line({
|
||||
series: data.sum[w.key],
|
||||
name: `${v} ${w.name}`,
|
||||
color: colors.at(i, arr.length),
|
||||
unit: Unit.count,
|
||||
}),
|
||||
),
|
||||
),
|
||||
},
|
||||
...ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: `Transaction Versions (${w.name})`,
|
||||
bottom: entries(transactions.versions).map(
|
||||
([v, data], i, arr) =>
|
||||
line({
|
||||
series: data.sum[w.key],
|
||||
name: v,
|
||||
color: colors.at(i, arr.length),
|
||||
unit: Unit.count,
|
||||
}),
|
||||
),
|
||||
})),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: "Transaction Versions (Total)",
|
||||
bottom: entries(transactions.versions).map(
|
||||
([v, data], i, arr) =>
|
||||
line({
|
||||
series: data.cumulative,
|
||||
name: v,
|
||||
color: colors.at(i, arr.length),
|
||||
unit: Unit.count,
|
||||
}),
|
||||
),
|
||||
},
|
||||
],
|
||||
tree: chartsFromCountEntries({
|
||||
entries: entries(transactions.versions),
|
||||
title: "Transaction Versions",
|
||||
unit: Unit.count,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "Velocity",
|
||||
title: "Transaction Velocity",
|
||||
bottom: [
|
||||
line({
|
||||
series: supply.velocity.native,
|
||||
name: "BTC",
|
||||
unit: Unit.ratio,
|
||||
}),
|
||||
line({
|
||||
series: supply.velocity.fiat,
|
||||
name: "USD",
|
||||
color: colors.usd,
|
||||
unit: Unit.ratio,
|
||||
}),
|
||||
tree: [
|
||||
{
|
||||
name: "Compare",
|
||||
title: "Transaction Velocity",
|
||||
bottom: [
|
||||
line({
|
||||
series: supply.velocity.native,
|
||||
name: "BTC",
|
||||
unit: Unit.ratio,
|
||||
}),
|
||||
line({
|
||||
series: supply.velocity.fiat,
|
||||
name: "USD",
|
||||
color: colors.usd,
|
||||
unit: Unit.ratio,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Native",
|
||||
title: "Transaction Velocity (BTC)",
|
||||
bottom: [
|
||||
line({
|
||||
series: supply.velocity.native,
|
||||
name: "BTC",
|
||||
unit: Unit.ratio,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Fiat",
|
||||
title: "Transaction Velocity (USD)",
|
||||
bottom: [
|
||||
line({
|
||||
series: supply.velocity.fiat,
|
||||
name: "USD",
|
||||
color: colors.usd,
|
||||
unit: Unit.ratio,
|
||||
}),
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -721,37 +616,45 @@ export function createNetworkSection() {
|
||||
name: "Count",
|
||||
tree: [
|
||||
{
|
||||
name: "Base",
|
||||
title: "Block Count",
|
||||
bottom: [
|
||||
line({
|
||||
series: blocks.count.total.base,
|
||||
name: "base",
|
||||
unit: Unit.count,
|
||||
}),
|
||||
line({
|
||||
series: blocks.count.target,
|
||||
name: "Target",
|
||||
color: colors.gray,
|
||||
unit: Unit.count,
|
||||
options: { lineStyle: 4 },
|
||||
}),
|
||||
name: "Sums",
|
||||
tree: [
|
||||
{
|
||||
name: "Compare",
|
||||
title: "Block Count Rolling",
|
||||
bottom: ROLLING_WINDOWS.map((w) =>
|
||||
line({
|
||||
series: blocks.count.total.sum[w.key],
|
||||
name: w.name,
|
||||
color: w.color,
|
||||
unit: Unit.count,
|
||||
}),
|
||||
),
|
||||
},
|
||||
...ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: `Block Count ${w.name}`,
|
||||
bottom: [
|
||||
line({
|
||||
series: blocks.count.total.sum[w.key],
|
||||
name: "Actual",
|
||||
unit: Unit.count,
|
||||
}),
|
||||
line({
|
||||
series: blocks.count.target[w.key],
|
||||
name: "Target",
|
||||
color: colors.gray,
|
||||
unit: Unit.count,
|
||||
options: { lineStyle: 4 },
|
||||
}),
|
||||
],
|
||||
})),
|
||||
],
|
||||
},
|
||||
rollingWindowsTree({
|
||||
windows: blocks.count.total.sum,
|
||||
title: "Block Count",
|
||||
unit: Unit.count,
|
||||
}),
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: "Block Count (Total)",
|
||||
bottom: [
|
||||
line({
|
||||
series: blocks.count.total.cumulative,
|
||||
name: "all-time",
|
||||
unit: Unit.count,
|
||||
}),
|
||||
{ series: blocks.count.total.cumulative, title: "all-time", unit: Unit.count },
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -760,7 +663,7 @@ export function createNetworkSection() {
|
||||
name: "Interval",
|
||||
tree: [
|
||||
{
|
||||
name: "Base",
|
||||
name: "Per Block",
|
||||
title: "Block Interval",
|
||||
bottom: [
|
||||
dots({
|
||||
@@ -780,155 +683,34 @@ export function createNetworkSection() {
|
||||
rollingWindowsTree({
|
||||
windows: blocks.interval,
|
||||
title: "Block Interval",
|
||||
name: "Averages",
|
||||
unit: Unit.secs,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Size",
|
||||
tree: [
|
||||
{
|
||||
name: "Base",
|
||||
title: "Block Size",
|
||||
bottom: [
|
||||
line({
|
||||
series: blocks.size.base,
|
||||
name: "base",
|
||||
unit: Unit.bytes,
|
||||
}),
|
||||
],
|
||||
},
|
||||
rollingWindowsTree({
|
||||
windows: blocks.size.sum,
|
||||
title: "Block Size",
|
||||
unit: Unit.bytes,
|
||||
}),
|
||||
distributionWindowsTree({
|
||||
pattern: blocks.size,
|
||||
base: blocks.size.base,
|
||||
title: "Block Size",
|
||||
unit: Unit.bytes,
|
||||
}),
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: "Block Size (Total)",
|
||||
bottom: [
|
||||
line({
|
||||
series: blocks.size.cumulative,
|
||||
name: "all-time",
|
||||
unit: Unit.bytes,
|
||||
}),
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Weight",
|
||||
tree: [
|
||||
{
|
||||
name: "Base",
|
||||
title: "Block Weight",
|
||||
bottom: [
|
||||
line({
|
||||
series: blocks.weight.base,
|
||||
name: "base",
|
||||
unit: Unit.wu,
|
||||
}),
|
||||
],
|
||||
},
|
||||
rollingWindowsTree({
|
||||
windows: blocks.weight.sum,
|
||||
title: "Block Weight",
|
||||
unit: Unit.wu,
|
||||
}),
|
||||
distributionWindowsTree({
|
||||
pattern: blocks.weight,
|
||||
base: blocks.weight.base,
|
||||
title: "Block Weight",
|
||||
unit: Unit.wu,
|
||||
}),
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: "Block Weight (Total)",
|
||||
bottom: [
|
||||
line({
|
||||
series: blocks.weight.cumulative,
|
||||
name: "all-time",
|
||||
unit: Unit.wu,
|
||||
}),
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "vBytes",
|
||||
tree: [
|
||||
{
|
||||
name: "Base",
|
||||
title: "Block vBytes",
|
||||
bottom: [
|
||||
line({
|
||||
series: blocks.vbytes.base,
|
||||
name: "base",
|
||||
unit: Unit.vb,
|
||||
}),
|
||||
],
|
||||
},
|
||||
rollingWindowsTree({
|
||||
windows: blocks.vbytes.sum,
|
||||
title: "Block vBytes",
|
||||
unit: Unit.vb,
|
||||
}),
|
||||
distributionWindowsTree({
|
||||
pattern: blocks.vbytes,
|
||||
base: blocks.vbytes.base,
|
||||
title: "Block vBytes",
|
||||
unit: Unit.vb,
|
||||
}),
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: "Block vBytes (Total)",
|
||||
bottom: [
|
||||
line({
|
||||
series: blocks.vbytes.cumulative,
|
||||
name: "all-time",
|
||||
unit: Unit.vb,
|
||||
}),
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Fullness",
|
||||
title: "Block Fullness",
|
||||
bottom: percentRatioDots({
|
||||
pattern: blocks.fullness,
|
||||
name: "base",
|
||||
tree: chartsFromFullPerBlock({
|
||||
pattern: blocks.size,
|
||||
title: "Block Size",
|
||||
unit: Unit.bytes,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "Difficulty",
|
||||
tree: [
|
||||
{
|
||||
name: "Base",
|
||||
title: "Mining Difficulty",
|
||||
bottom: [
|
||||
line({
|
||||
series: blocks.difficulty.value,
|
||||
name: "Difficulty",
|
||||
unit: Unit.count,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Adjustment",
|
||||
title: "Difficulty Adjustment",
|
||||
bottom: percentRatioDots({
|
||||
pattern: blocks.difficulty.adjustment,
|
||||
name: "Adjustment",
|
||||
}),
|
||||
},
|
||||
],
|
||||
name: "Weight",
|
||||
tree: chartsFromFullPerBlock({
|
||||
pattern: blocks.weight,
|
||||
title: "Block Weight",
|
||||
unit: Unit.wu,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "vBytes",
|
||||
tree: chartsFromFullPerBlock({
|
||||
pattern: blocks.vbytes,
|
||||
title: "Block vBytes",
|
||||
unit: Unit.vb,
|
||||
}),
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -948,18 +730,11 @@ export function createNetworkSection() {
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "30d Change",
|
||||
title: "UTXO Count 30d Change",
|
||||
bottom: [
|
||||
baseline({
|
||||
series:
|
||||
cohorts.utxo.all.outputs.unspentCount.delta.absolute._1m,
|
||||
name: "30d Change",
|
||||
unit: Unit.count,
|
||||
}),
|
||||
],
|
||||
},
|
||||
...simpleDeltaTree({
|
||||
delta: cohorts.utxo.all.outputs.unspentCount.delta,
|
||||
title: "UTXO Count",
|
||||
unit: Unit.count,
|
||||
}),
|
||||
{
|
||||
name: "Flow",
|
||||
title: "UTXO Flow",
|
||||
|
||||
@@ -476,12 +476,13 @@ export function statsAtWindow(pattern, window) {
|
||||
* @param {{ _24h: AnySeriesPattern, _1w: AnySeriesPattern, _1m: AnySeriesPattern, _1y: AnySeriesPattern }} args.windows
|
||||
* @param {string} args.title
|
||||
* @param {Unit} args.unit
|
||||
* @param {string} [args.name]
|
||||
* @param {(args: {series: AnySeriesPattern, name: string, color: Color, unit: Unit}) => AnyFetchedSeriesBlueprint} [args.series]
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
export function rollingWindowsTree({ windows, title, unit, series = line }) {
|
||||
export function rollingWindowsTree({ windows, title, unit, name = "Sums", series = line }) {
|
||||
return {
|
||||
name: "Sums",
|
||||
name,
|
||||
tree: [
|
||||
{
|
||||
name: "Compare",
|
||||
@@ -523,17 +524,26 @@ export function rollingWindowsTree({ windows, title, unit, series = line }) {
|
||||
export function distributionWindowsTree({ pattern, base, title, unit }) {
|
||||
return {
|
||||
name: "Distributions",
|
||||
tree: ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: `${title} Distribution (${w.name})`,
|
||||
bottom: [
|
||||
...(base ? [line({ series: base, name: "base", unit })] : []),
|
||||
...fromStatsPattern({
|
||||
pattern: statsAtWindow(pattern, w.key),
|
||||
unit,
|
||||
}),
|
||||
],
|
||||
})),
|
||||
tree: [
|
||||
{
|
||||
name: "Compare",
|
||||
title: `${title} Average`,
|
||||
bottom: ROLLING_WINDOWS.map((w) =>
|
||||
line({ series: pattern.average[w.key], name: w.name, color: w.color, unit }),
|
||||
),
|
||||
},
|
||||
...ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: `${title} Distribution (${w.name})`,
|
||||
bottom: [
|
||||
...(base ? [line({ series: base, name: "base", unit })] : []),
|
||||
...fromStatsPattern({
|
||||
pattern: statsAtWindow(pattern, w.key),
|
||||
unit,
|
||||
}),
|
||||
],
|
||||
})),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -651,10 +661,10 @@ export function percentRatioDots({ pattern, name, color, defaultActive }) {
|
||||
* @param {boolean} [args.defaultActive]
|
||||
* @returns {AnyFetchedSeriesBlueprint[]}
|
||||
*/
|
||||
export function percentRatioBaseline({ pattern, name, color, defaultActive }) {
|
||||
export function percentRatioBaseline({ pattern, name, defaultActive }) {
|
||||
return [
|
||||
baseline({ series: pattern.percent, name, color, defaultActive, unit: Unit.percentage }),
|
||||
baseline({ series: pattern.ratio, name, color, defaultActive, unit: Unit.ratio }),
|
||||
baseline({ series: pattern.percent, name, defaultActive, unit: Unit.percentage }),
|
||||
baseline({ series: pattern.ratio, name, defaultActive, unit: Unit.ratio }),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -663,29 +673,86 @@ export function percentRatioBaseline({ pattern, name, color, defaultActive }) {
|
||||
* @param {Object} args
|
||||
* @param {{ _24h: { percent: AnySeriesPattern, ratio: AnySeriesPattern }, _1w: { percent: AnySeriesPattern, ratio: AnySeriesPattern }, _1m: { percent: AnySeriesPattern, ratio: AnySeriesPattern }, _1y: { percent: AnySeriesPattern, ratio: AnySeriesPattern } }} args.windows
|
||||
* @param {string} args.title
|
||||
* @param {(args: {pattern: { percent: AnySeriesPattern, ratio: AnySeriesPattern }, name: string, color: Color}) => AnyFetchedSeriesBlueprint[]} [args.series]
|
||||
* @param {string} [args.name]
|
||||
* @param {(args: {pattern: { percent: AnySeriesPattern, ratio: AnySeriesPattern }, name: string, color?: Color}) => AnyFetchedSeriesBlueprint[]} [args.series]
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
export function rollingPercentRatioTree({ windows, title, series = percentRatio }) {
|
||||
export function rollingPercentRatioTree({ windows, title, name = "Sums", series = percentRatio }) {
|
||||
return {
|
||||
name: "Sums",
|
||||
name,
|
||||
tree: [
|
||||
{
|
||||
name: "Compare",
|
||||
title: `${title} Rolling`,
|
||||
bottom: ROLLING_WINDOWS.flatMap((w) =>
|
||||
series({ pattern: windows[w.key], name: w.name, color: w.color }),
|
||||
percentRatio({ pattern: windows[w.key], name: w.name, color: w.color }),
|
||||
),
|
||||
},
|
||||
...ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: `${title} ${w.name}`,
|
||||
bottom: series({ pattern: windows[w.key], name: w.name, color: w.color }),
|
||||
bottom: series({ pattern: windows[w.key], name: w.name }),
|
||||
})),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Change + Growth Rate tree from a delta pattern (absolute + rate)
|
||||
* @template T
|
||||
* @param {Object} args
|
||||
* @param {{ absolute: { _24h: T, _1w: T, _1m: T, _1y: T }, rate: { _24h: { percent: AnySeriesPattern, ratio: AnySeriesPattern }, _1w: { percent: AnySeriesPattern, ratio: AnySeriesPattern }, _1m: { percent: AnySeriesPattern, ratio: AnySeriesPattern }, _1y: { percent: AnySeriesPattern, ratio: AnySeriesPattern } } }} args.delta
|
||||
* @param {string} args.title
|
||||
* @param {Unit} args.unit
|
||||
* @param {(v: T) => AnySeriesPattern} args.extract
|
||||
* @returns {PartialOptionsTree}
|
||||
*/
|
||||
export function deltaTree({ delta, title, unit, extract }) {
|
||||
return [
|
||||
{
|
||||
name: "Change",
|
||||
tree: [
|
||||
{
|
||||
name: "Compare",
|
||||
title: `${title} Change`,
|
||||
bottom: ROLLING_WINDOWS.map((w) =>
|
||||
baseline({
|
||||
series: extract(delta.absolute[w.key]),
|
||||
name: w.name,
|
||||
color: w.color,
|
||||
unit,
|
||||
}),
|
||||
),
|
||||
},
|
||||
...ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: `${title} Change ${w.name}`,
|
||||
bottom: [
|
||||
baseline({
|
||||
series: extract(delta.absolute[w.key]),
|
||||
name: w.name,
|
||||
unit,
|
||||
}),
|
||||
],
|
||||
})),
|
||||
],
|
||||
},
|
||||
rollingPercentRatioTree({ windows: delta.rate, title: `${title} Growth Rate`, name: "Growth Rate", series: percentRatioBaseline }),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* deltaTree where absolute windows are directly AnySeriesPattern (no extract needed)
|
||||
* @param {Object} args
|
||||
* @param {{ absolute: { _24h: AnySeriesPattern, _1w: AnySeriesPattern, _1m: AnySeriesPattern, _1y: AnySeriesPattern }, rate: { _24h: { percent: AnySeriesPattern, ratio: AnySeriesPattern }, _1w: { percent: AnySeriesPattern, ratio: AnySeriesPattern }, _1m: { percent: AnySeriesPattern, ratio: AnySeriesPattern }, _1y: { percent: AnySeriesPattern, ratio: AnySeriesPattern } } }} args.delta
|
||||
* @param {string} args.title
|
||||
* @param {Unit} args.unit
|
||||
* @returns {PartialOptionsTree}
|
||||
*/
|
||||
export function simpleDeltaTree({ delta, title, unit }) {
|
||||
return deltaTree({ delta, title, unit, extract: (v) => v });
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Chart-generating helpers (return PartialOptionsTree for folder structures)
|
||||
// ============================================================================
|
||||
@@ -896,20 +963,40 @@ export const chartsFromSumPerBlock = (args) =>
|
||||
export function chartsFromBlockAnd6b({ pattern, title, unit }) {
|
||||
return [
|
||||
{
|
||||
name: "Per Block",
|
||||
title: `${title} per Block`,
|
||||
name: "Block",
|
||||
title: `${title} (Block)`,
|
||||
bottom: fromStatsPattern({ pattern: pattern.block, unit }),
|
||||
},
|
||||
{
|
||||
name: "Per 6 Blocks",
|
||||
title: `${title} per 6 Blocks`,
|
||||
name: "Hourly",
|
||||
title: `${title} (Hourly)`,
|
||||
bottom: fromStatsPattern({ pattern: pattern._6b, unit }),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Split pattern with rolling sum windows + cumulative into charts
|
||||
* Sums + Cumulative charts (no Per Block)
|
||||
* @param {Object} args
|
||||
* @param {{ sum: { _24h: AnySeriesPattern, _1w: AnySeriesPattern, _1m: AnySeriesPattern, _1y: AnySeriesPattern }, cumulative: AnySeriesPattern }} args.pattern
|
||||
* @param {string} args.title
|
||||
* @param {Unit} args.unit
|
||||
* @param {Color} [args.color]
|
||||
* @returns {PartialOptionsTree}
|
||||
*/
|
||||
export function chartsFromSumsCumulative({ pattern, title, unit, color }) {
|
||||
return [
|
||||
rollingWindowsTree({ windows: pattern.sum, title, unit }),
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: `${title} (Total)`,
|
||||
bottom: [{ series: pattern.cumulative, title: "all-time", color, unit }],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Per Block + Sums + Cumulative charts
|
||||
* @param {Object} args
|
||||
* @param {CountPattern<any>} args.pattern
|
||||
* @param {string} args.title
|
||||
@@ -920,15 +1007,47 @@ export function chartsFromBlockAnd6b({ pattern, title, unit }) {
|
||||
export function chartsFromCount({ pattern, title, unit, color }) {
|
||||
return [
|
||||
{
|
||||
name: "Base",
|
||||
name: "Per Block",
|
||||
title,
|
||||
bottom: [{ series: pattern.base, title: "base", color, unit }],
|
||||
},
|
||||
rollingWindowsTree({ windows: pattern.sum, title, unit }),
|
||||
...chartsFromSumsCumulative({ pattern, title, unit, color }),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Split multiple named entries (each with base/sum/cumulative) into Per Block/Sums/Cumulative charts
|
||||
* @param {Object} args
|
||||
* @param {Array<[string, CountPattern<any>]>} args.entries
|
||||
* @param {string} args.title
|
||||
* @param {Unit} args.unit
|
||||
* @returns {PartialOptionsTree}
|
||||
*/
|
||||
export function chartsFromCountEntries({ entries, title, unit }) {
|
||||
return [
|
||||
{
|
||||
name: "Per Block",
|
||||
title,
|
||||
bottom: entries.map(([name, data], i, arr) =>
|
||||
line({ series: data.base, name, color: colors.at(i, arr.length), unit }),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "Sums",
|
||||
tree: ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: `${title} (${w.name})`,
|
||||
bottom: entries.map(([name, data], i, arr) =>
|
||||
line({ series: data.sum[w.key], name, color: colors.at(i, arr.length), unit }),
|
||||
),
|
||||
})),
|
||||
},
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: `${title} (Total)`,
|
||||
bottom: [{ series: pattern.cumulative, title: "all-time", color, unit }],
|
||||
bottom: entries.map(([name, data], i, arr) =>
|
||||
line({ series: data.cumulative, name, color: colors.at(i, arr.length), unit }),
|
||||
),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@@ -227,16 +227,16 @@ export function satsBtcUsdRolling({ pattern, name, color, defaultActive }) {
|
||||
export function satsBtcUsdFullTree({ pattern, name, title, color }) {
|
||||
return [
|
||||
{
|
||||
name: "Sum",
|
||||
name: "Per Block",
|
||||
title,
|
||||
bottom: satsBtcUsd({ pattern: pattern.base, name, color }),
|
||||
},
|
||||
{
|
||||
name: "Rolling",
|
||||
name: "Sums",
|
||||
tree: [
|
||||
{
|
||||
name: "Compare",
|
||||
title: `${title} Rolling Sum`,
|
||||
title: `${title} Rolling`,
|
||||
bottom: ROLLING_WINDOWS.flatMap((w) =>
|
||||
satsBtcUsd({
|
||||
pattern: pattern.sum[w.key],
|
||||
@@ -247,7 +247,7 @@ export function satsBtcUsdFullTree({ pattern, name, title, color }) {
|
||||
},
|
||||
...ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: `${title} ${w.name} Rolling Sum`,
|
||||
title: `${title} (${w.name})`,
|
||||
bottom: satsBtcUsd({
|
||||
pattern: pattern.sum[w.key],
|
||||
name: w.name,
|
||||
|
||||
Reference in New Issue
Block a user