mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-04-24 06:39:58 -07:00
global: snapshot part 17
This commit is contained in:
@@ -5,10 +5,10 @@ use brk_cohort::{
|
|||||||
};
|
};
|
||||||
use brk_error::Result;
|
use brk_error::Result;
|
||||||
use brk_traversable::Traversable;
|
use brk_traversable::Traversable;
|
||||||
use brk_types::{Height, Indexes, Version};
|
use brk_types::{Height, Indexes, StoredU64, Version};
|
||||||
use derive_more::{Deref, DerefMut};
|
use derive_more::{Deref, DerefMut};
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use vecdb::{AnyStoredVec, Database, Exit, Rw, StorageMode};
|
use vecdb::{AnyStoredVec, Database, Exit, ReadableVec, Rw, StorageMode};
|
||||||
|
|
||||||
use crate::{distribution::DynCohortVecs, indexes, internal::CachedWindowStarts, prices};
|
use crate::{distribution::DynCohortVecs, indexes, internal::CachedWindowStarts, prices};
|
||||||
|
|
||||||
@@ -106,11 +106,12 @@ impl AddrCohorts {
|
|||||||
&mut self,
|
&mut self,
|
||||||
prices: &prices::Vecs,
|
prices: &prices::Vecs,
|
||||||
starting_indexes: &Indexes,
|
starting_indexes: &Indexes,
|
||||||
|
all_utxo_count: &impl ReadableVec<Height, StoredU64>,
|
||||||
exit: &Exit,
|
exit: &Exit,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
self.0
|
self.0
|
||||||
.par_iter_mut()
|
.par_iter_mut()
|
||||||
.try_for_each(|v| v.compute_rest_part2(prices, starting_indexes, exit))
|
.try_for_each(|v| v.compute_rest_part2(prices, starting_indexes, all_utxo_count, exit))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a parallel iterator over all vecs for parallel writing.
|
/// Returns a parallel iterator over all vecs for parallel writing.
|
||||||
|
|||||||
@@ -232,9 +232,10 @@ impl CohortVecs for AddrCohortVecs {
|
|||||||
&mut self,
|
&mut self,
|
||||||
prices: &prices::Vecs,
|
prices: &prices::Vecs,
|
||||||
starting_indexes: &Indexes,
|
starting_indexes: &Indexes,
|
||||||
|
all_utxo_count: &impl ReadableVec<Height, StoredU64>,
|
||||||
exit: &Exit,
|
exit: &Exit,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
self.metrics
|
self.metrics
|
||||||
.compute_rest_part2(prices, starting_indexes, exit)
|
.compute_rest_part2(prices, starting_indexes, all_utxo_count, exit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use brk_error::Result;
|
use brk_error::Result;
|
||||||
use brk_types::{Cents, Height, Indexes, Version};
|
use brk_types::{Cents, Height, Indexes, StoredU64, Version};
|
||||||
use vecdb::Exit;
|
use vecdb::{Exit, ReadableVec};
|
||||||
|
|
||||||
use crate::prices;
|
use crate::prices;
|
||||||
|
|
||||||
@@ -62,6 +62,7 @@ pub trait CohortVecs: DynCohortVecs {
|
|||||||
&mut self,
|
&mut self,
|
||||||
prices: &prices::Vecs,
|
prices: &prices::Vecs,
|
||||||
starting_indexes: &Indexes,
|
starting_indexes: &Indexes,
|
||||||
|
all_utxo_count: &impl ReadableVec<Height, StoredU64>,
|
||||||
exit: &Exit,
|
exit: &Exit,
|
||||||
) -> Result<()>;
|
) -> Result<()>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -565,8 +565,9 @@ impl UTXOCohorts<Rw> {
|
|||||||
exit,
|
exit,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Clone all_supply_sats for non-all cohorts.
|
// Clone all_supply_sats and all_utxo_count for non-all cohorts.
|
||||||
let all_supply_sats = self.all.metrics.supply.total.sats.height.read_only_clone();
|
let all_supply_sats = self.all.metrics.supply.total.sats.height.read_only_clone();
|
||||||
|
let all_utxo_count = self.all.metrics.outputs.unspent_count.height.read_only_clone();
|
||||||
|
|
||||||
// Destructure to allow parallel mutable access to independent fields.
|
// Destructure to allow parallel mutable access to independent fields.
|
||||||
let Self {
|
let Self {
|
||||||
@@ -589,6 +590,7 @@ impl UTXOCohorts<Rw> {
|
|||||||
let vc = &under_1h_value_created;
|
let vc = &under_1h_value_created;
|
||||||
let vd = &under_1h_value_destroyed;
|
let vd = &under_1h_value_destroyed;
|
||||||
let ss = &all_supply_sats;
|
let ss = &all_supply_sats;
|
||||||
|
let au = &all_utxo_count;
|
||||||
|
|
||||||
let tasks: Vec<Box<dyn FnOnce() -> Result<()> + Send + '_>> = vec![
|
let tasks: Vec<Box<dyn FnOnce() -> Result<()> + Send + '_>> = vec![
|
||||||
Box::new(|| {
|
Box::new(|| {
|
||||||
@@ -600,6 +602,7 @@ impl UTXOCohorts<Rw> {
|
|||||||
vc,
|
vc,
|
||||||
vd,
|
vd,
|
||||||
ss,
|
ss,
|
||||||
|
au,
|
||||||
exit,
|
exit,
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
@@ -610,58 +613,59 @@ impl UTXOCohorts<Rw> {
|
|||||||
starting_indexes,
|
starting_indexes,
|
||||||
height_to_market_cap,
|
height_to_market_cap,
|
||||||
ss,
|
ss,
|
||||||
|
au,
|
||||||
exit,
|
exit,
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
Box::new(|| {
|
Box::new(|| {
|
||||||
age_range.par_iter_mut().try_for_each(|v| {
|
age_range.par_iter_mut().try_for_each(|v| {
|
||||||
v.metrics
|
v.metrics
|
||||||
.compute_rest_part2(prices, starting_indexes, ss, exit)
|
.compute_rest_part2(prices, starting_indexes, ss, au, exit)
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
Box::new(|| {
|
Box::new(|| {
|
||||||
under_age.par_iter_mut().try_for_each(|v| {
|
under_age.par_iter_mut().try_for_each(|v| {
|
||||||
v.metrics
|
v.metrics
|
||||||
.compute_rest_part2(prices, starting_indexes, ss, exit)
|
.compute_rest_part2(prices, starting_indexes, ss, au, exit)
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
Box::new(|| {
|
Box::new(|| {
|
||||||
over_age.par_iter_mut().try_for_each(|v| {
|
over_age.par_iter_mut().try_for_each(|v| {
|
||||||
v.metrics
|
v.metrics
|
||||||
.compute_rest_part2(prices, starting_indexes, ss, exit)
|
.compute_rest_part2(prices, starting_indexes, ss, au, exit)
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
Box::new(|| {
|
Box::new(|| {
|
||||||
over_amount
|
over_amount
|
||||||
.par_iter_mut()
|
.par_iter_mut()
|
||||||
.try_for_each(|v| v.metrics.compute_rest_part2(prices, starting_indexes, exit))
|
.try_for_each(|v| v.metrics.compute_rest_part2(prices, starting_indexes, au, exit))
|
||||||
}),
|
}),
|
||||||
Box::new(|| {
|
Box::new(|| {
|
||||||
epoch.par_iter_mut().try_for_each(|v| {
|
epoch.par_iter_mut().try_for_each(|v| {
|
||||||
v.metrics
|
v.metrics
|
||||||
.compute_rest_part2(prices, starting_indexes, ss, exit)
|
.compute_rest_part2(prices, starting_indexes, ss, au, exit)
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
Box::new(|| {
|
Box::new(|| {
|
||||||
class.par_iter_mut().try_for_each(|v| {
|
class.par_iter_mut().try_for_each(|v| {
|
||||||
v.metrics
|
v.metrics
|
||||||
.compute_rest_part2(prices, starting_indexes, ss, exit)
|
.compute_rest_part2(prices, starting_indexes, ss, au, exit)
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
Box::new(|| {
|
Box::new(|| {
|
||||||
amount_range
|
amount_range
|
||||||
.par_iter_mut()
|
.par_iter_mut()
|
||||||
.try_for_each(|v| v.metrics.compute_rest_part2(prices, starting_indexes, exit))
|
.try_for_each(|v| v.metrics.compute_rest_part2(prices, starting_indexes, au, exit))
|
||||||
}),
|
}),
|
||||||
Box::new(|| {
|
Box::new(|| {
|
||||||
under_amount
|
under_amount
|
||||||
.par_iter_mut()
|
.par_iter_mut()
|
||||||
.try_for_each(|v| v.metrics.compute_rest_part2(prices, starting_indexes, exit))
|
.try_for_each(|v| v.metrics.compute_rest_part2(prices, starting_indexes, au, exit))
|
||||||
}),
|
}),
|
||||||
Box::new(|| {
|
Box::new(|| {
|
||||||
type_
|
type_
|
||||||
.par_iter_mut()
|
.par_iter_mut()
|
||||||
.try_for_each(|v| v.metrics.compute_rest_part2(prices, starting_indexes, exit))
|
.try_for_each(|v| v.metrics.compute_rest_part2(prices, starting_indexes, au, exit))
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use brk_types::{
|
|||||||
Cents, Dollars, Height, Indexes, Version,
|
Cents, Dollars, Height, Indexes, Version,
|
||||||
};
|
};
|
||||||
use vecdb::AnyStoredVec;
|
use vecdb::AnyStoredVec;
|
||||||
use vecdb::{Exit, ReadableVec, Rw, StorageMode};
|
use vecdb::{Exit, ReadOnlyClone, ReadableVec, Rw, StorageMode};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
blocks,
|
blocks,
|
||||||
@@ -135,6 +135,13 @@ impl AllCohortMetrics {
|
|||||||
exit,
|
exit,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
let all_utxo_count = self.outputs.unspent_count.height.read_only_clone();
|
||||||
|
self.outputs.compute_part2(
|
||||||
|
starting_indexes.height,
|
||||||
|
&all_utxo_count,
|
||||||
|
exit,
|
||||||
|
)?;
|
||||||
|
|
||||||
self.cost_basis.compute_prices(
|
self.cost_basis.compute_prices(
|
||||||
starting_indexes,
|
starting_indexes,
|
||||||
&prices.spot.cents.height,
|
&prices.spot.cents.height,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use brk_cohort::Filter;
|
use brk_cohort::Filter;
|
||||||
use brk_error::Result;
|
use brk_error::Result;
|
||||||
use brk_traversable::Traversable;
|
use brk_traversable::Traversable;
|
||||||
use brk_types::{Height, Indexes, Sats};
|
use brk_types::{Height, Indexes, Sats, StoredU64};
|
||||||
use vecdb::{AnyStoredVec, Exit, ReadableVec, Rw, StorageMode};
|
use vecdb::{AnyStoredVec, Exit, ReadableVec, Rw, StorageMode};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -69,6 +69,7 @@ impl BasicCohortMetrics {
|
|||||||
prices: &prices::Vecs,
|
prices: &prices::Vecs,
|
||||||
starting_indexes: &Indexes,
|
starting_indexes: &Indexes,
|
||||||
all_supply_sats: &impl ReadableVec<Height, Sats>,
|
all_supply_sats: &impl ReadableVec<Height, Sats>,
|
||||||
|
all_utxo_count: &impl ReadableVec<Height, StoredU64>,
|
||||||
exit: &Exit,
|
exit: &Exit,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
self.realized.compute_rest_part2(
|
self.realized.compute_rest_part2(
|
||||||
@@ -93,6 +94,8 @@ impl BasicCohortMetrics {
|
|||||||
exit,
|
exit,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
self.outputs.compute_part2(starting_indexes.height, all_utxo_count, exit)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use brk_cohort::Filter;
|
use brk_cohort::Filter;
|
||||||
use brk_error::Result;
|
use brk_error::Result;
|
||||||
use brk_traversable::Traversable;
|
use brk_traversable::Traversable;
|
||||||
use brk_types::{Height, Indexes, Sats, Version};
|
use brk_types::{Height, Indexes, Sats, StoredU64, Version};
|
||||||
use vecdb::{AnyStoredVec, Exit, ReadableVec, Rw, StorageMode};
|
use vecdb::{AnyStoredVec, Exit, ReadableVec, Rw, StorageMode};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -108,6 +108,8 @@ impl CoreCohortMetrics {
|
|||||||
self.supply
|
self.supply
|
||||||
.compute(prices, starting_indexes.height, exit)?;
|
.compute(prices, starting_indexes.height, exit)?;
|
||||||
|
|
||||||
|
self.outputs.compute_rest(starting_indexes.height, exit)?;
|
||||||
|
|
||||||
self.activity
|
self.activity
|
||||||
.compute_rest_part1(prices, starting_indexes, exit)?;
|
.compute_rest_part1(prices, starting_indexes, exit)?;
|
||||||
|
|
||||||
@@ -124,6 +126,7 @@ impl CoreCohortMetrics {
|
|||||||
prices: &prices::Vecs,
|
prices: &prices::Vecs,
|
||||||
starting_indexes: &Indexes,
|
starting_indexes: &Indexes,
|
||||||
all_supply_sats: &impl ReadableVec<Height, Sats>,
|
all_supply_sats: &impl ReadableVec<Height, Sats>,
|
||||||
|
all_utxo_count: &impl ReadableVec<Height, StoredU64>,
|
||||||
exit: &Exit,
|
exit: &Exit,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
self.realized.compute_rest_part2(
|
self.realized.compute_rest_part2(
|
||||||
@@ -148,6 +151,8 @@ impl CoreCohortMetrics {
|
|||||||
exit,
|
exit,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
self.outputs.compute_part2(starting_indexes.height, all_utxo_count, exit)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use brk_cohort::Filter;
|
|||||||
use brk_error::Result;
|
use brk_error::Result;
|
||||||
use brk_traversable::Traversable;
|
use brk_traversable::Traversable;
|
||||||
use brk_types::{
|
use brk_types::{
|
||||||
Dollars, Height, Indexes, Sats, Version,
|
Dollars, Height, Indexes, Sats, StoredU64, Version,
|
||||||
};
|
};
|
||||||
use vecdb::AnyStoredVec;
|
use vecdb::AnyStoredVec;
|
||||||
use vecdb::{Exit, ReadableVec, Rw, StorageMode};
|
use vecdb::{Exit, ReadableVec, Rw, StorageMode};
|
||||||
@@ -94,6 +94,7 @@ impl ExtendedCohortMetrics {
|
|||||||
starting_indexes: &Indexes,
|
starting_indexes: &Indexes,
|
||||||
height_to_market_cap: &impl ReadableVec<Height, Dollars>,
|
height_to_market_cap: &impl ReadableVec<Height, Dollars>,
|
||||||
all_supply_sats: &impl ReadableVec<Height, Sats>,
|
all_supply_sats: &impl ReadableVec<Height, Sats>,
|
||||||
|
all_utxo_count: &impl ReadableVec<Height, StoredU64>,
|
||||||
exit: &Exit,
|
exit: &Exit,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
self.realized.compute_rest_part2(
|
self.realized.compute_rest_part2(
|
||||||
@@ -141,6 +142,8 @@ impl ExtendedCohortMetrics {
|
|||||||
exit,
|
exit,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
self.outputs.compute_part2(starting_indexes.height, all_utxo_count, exit)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use brk_error::Result;
|
use brk_error::Result;
|
||||||
use brk_traversable::Traversable;
|
use brk_traversable::Traversable;
|
||||||
use brk_types::{Cents, Dollars, Height, Indexes, Sats, Version};
|
use brk_types::{Cents, Dollars, Height, Indexes, Sats, StoredU64, Version};
|
||||||
use derive_more::{Deref, DerefMut};
|
use derive_more::{Deref, DerefMut};
|
||||||
use vecdb::{AnyStoredVec, Exit, ReadableVec, Rw, StorageMode};
|
use vecdb::{AnyStoredVec, Exit, ReadableVec, Rw, StorageMode};
|
||||||
|
|
||||||
@@ -67,6 +67,7 @@ impl ExtendedAdjustedCohortMetrics {
|
|||||||
under_1h_value_created: &impl ReadableVec<Height, Cents>,
|
under_1h_value_created: &impl ReadableVec<Height, Cents>,
|
||||||
under_1h_value_destroyed: &impl ReadableVec<Height, Cents>,
|
under_1h_value_destroyed: &impl ReadableVec<Height, Cents>,
|
||||||
all_supply_sats: &impl ReadableVec<Height, Sats>,
|
all_supply_sats: &impl ReadableVec<Height, Sats>,
|
||||||
|
all_utxo_count: &impl ReadableVec<Height, StoredU64>,
|
||||||
exit: &Exit,
|
exit: &Exit,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
self.inner.compute_rest_part2(
|
self.inner.compute_rest_part2(
|
||||||
@@ -75,6 +76,7 @@ impl ExtendedAdjustedCohortMetrics {
|
|||||||
starting_indexes,
|
starting_indexes,
|
||||||
height_to_market_cap,
|
height_to_market_cap,
|
||||||
all_supply_sats,
|
all_supply_sats,
|
||||||
|
all_utxo_count,
|
||||||
exit,
|
exit,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use brk_cohort::Filter;
|
use brk_cohort::Filter;
|
||||||
use brk_error::Result;
|
use brk_error::Result;
|
||||||
use brk_traversable::Traversable;
|
use brk_traversable::Traversable;
|
||||||
use brk_types::Indexes;
|
use brk_types::{Height, Indexes, StoredU64};
|
||||||
use vecdb::{AnyStoredVec, Exit, Rw, StorageMode};
|
use vecdb::{AnyStoredVec, Exit, ReadableVec, Rw, StorageMode};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
distribution::metrics::{
|
distribution::metrics::{
|
||||||
@@ -101,6 +101,7 @@ impl MinimalCohortMetrics {
|
|||||||
exit: &Exit,
|
exit: &Exit,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
self.supply.compute(prices, starting_indexes.height, exit)?;
|
self.supply.compute(prices, starting_indexes.height, exit)?;
|
||||||
|
self.outputs.compute_rest(starting_indexes.height, exit)?;
|
||||||
self.activity
|
self.activity
|
||||||
.compute_rest_part1(prices, starting_indexes, exit)?;
|
.compute_rest_part1(prices, starting_indexes, exit)?;
|
||||||
self.realized
|
self.realized
|
||||||
@@ -112,6 +113,7 @@ impl MinimalCohortMetrics {
|
|||||||
&mut self,
|
&mut self,
|
||||||
prices: &prices::Vecs,
|
prices: &prices::Vecs,
|
||||||
starting_indexes: &Indexes,
|
starting_indexes: &Indexes,
|
||||||
|
all_utxo_count: &impl ReadableVec<Height, StoredU64>,
|
||||||
exit: &Exit,
|
exit: &Exit,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
self.realized.compute_rest_part2(
|
self.realized.compute_rest_part2(
|
||||||
@@ -128,6 +130,8 @@ impl MinimalCohortMetrics {
|
|||||||
exit,
|
exit,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
self.outputs.compute_part2(starting_indexes.height, all_utxo_count, exit)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use brk_cohort::Filter;
|
use brk_cohort::Filter;
|
||||||
use brk_error::Result;
|
use brk_error::Result;
|
||||||
use brk_traversable::Traversable;
|
use brk_traversable::Traversable;
|
||||||
use brk_types::Indexes;
|
use brk_types::{Height, Indexes, StoredU64};
|
||||||
use vecdb::{AnyStoredVec, Exit, Rw, StorageMode};
|
use vecdb::{AnyStoredVec, Exit, ReadableVec, Rw, StorageMode};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
distribution::metrics::{
|
distribution::metrics::{
|
||||||
@@ -63,6 +63,7 @@ impl TypeCohortMetrics {
|
|||||||
exit: &Exit,
|
exit: &Exit,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
self.supply.compute(prices, starting_indexes.height, exit)?;
|
self.supply.compute(prices, starting_indexes.height, exit)?;
|
||||||
|
self.outputs.compute_rest(starting_indexes.height, exit)?;
|
||||||
self.activity
|
self.activity
|
||||||
.compute_rest_part1(prices, starting_indexes, exit)?;
|
.compute_rest_part1(prices, starting_indexes, exit)?;
|
||||||
self.realized
|
self.realized
|
||||||
@@ -74,6 +75,7 @@ impl TypeCohortMetrics {
|
|||||||
&mut self,
|
&mut self,
|
||||||
prices: &prices::Vecs,
|
prices: &prices::Vecs,
|
||||||
starting_indexes: &Indexes,
|
starting_indexes: &Indexes,
|
||||||
|
all_utxo_count: &impl ReadableVec<Height, StoredU64>,
|
||||||
exit: &Exit,
|
exit: &Exit,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
self.realized.compute_rest_part2(
|
self.realized.compute_rest_part2(
|
||||||
@@ -90,6 +92,8 @@ impl TypeCohortMetrics {
|
|||||||
exit,
|
exit,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
self.outputs.compute_part2(starting_indexes.height, all_utxo_count, exit)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -269,6 +269,8 @@ pub trait CohortMetricsBase:
|
|||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
self.supply_mut()
|
self.supply_mut()
|
||||||
.compute(prices, starting_indexes.height, exit)?;
|
.compute(prices, starting_indexes.height, exit)?;
|
||||||
|
self.outputs_mut()
|
||||||
|
.compute_rest(starting_indexes.height, exit)?;
|
||||||
self.activity_mut()
|
self.activity_mut()
|
||||||
.compute_rest_part1(prices, starting_indexes, exit)?;
|
.compute_rest_part1(prices, starting_indexes, exit)?;
|
||||||
|
|
||||||
|
|||||||
@@ -1,38 +1,44 @@
|
|||||||
use brk_error::Result;
|
use brk_error::Result;
|
||||||
use brk_traversable::Traversable;
|
use brk_traversable::Traversable;
|
||||||
use brk_types::{BasisPointsSigned32, Indexes, StoredI64, StoredU64, Version};
|
use brk_types::{BasisPointsSigned32, Height, Indexes, StoredF32, StoredI64, StoredU32, StoredU64, Version};
|
||||||
use vecdb::{AnyStoredVec, AnyVec, Exit, Rw, StorageMode, WritableVec};
|
use vecdb::{AnyStoredVec, AnyVec, Exit, ReadableVec, Rw, StorageMode, WritableVec};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
distribution::{
|
distribution::{
|
||||||
metrics::ImportConfig,
|
metrics::ImportConfig,
|
||||||
state::{CohortState, CostBasisOps, RealizedOps},
|
state::{CohortState, CostBasisOps, RealizedOps},
|
||||||
},
|
},
|
||||||
internal::PerBlockWithDeltas,
|
internal::{PerBlock, PerBlockCumulativeRolling, PerBlockWithDeltas, RatioU32U64F32},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Base output metrics: utxo_count + delta.
|
/// Base output metrics: utxo_count + delta.
|
||||||
#[derive(Traversable)]
|
#[derive(Traversable)]
|
||||||
pub struct OutputsBase<M: StorageMode = Rw> {
|
pub struct OutputsBase<M: StorageMode = Rw> {
|
||||||
pub unspent_count: PerBlockWithDeltas<StoredU64, StoredI64, BasisPointsSigned32, M>,
|
pub unspent_count: PerBlockWithDeltas<StoredU64, StoredI64, BasisPointsSigned32, M>,
|
||||||
|
pub spent_count: PerBlockCumulativeRolling<StoredU32, StoredU64, M>,
|
||||||
|
pub spending_rate: PerBlock<StoredF32, M>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OutputsBase {
|
impl OutputsBase {
|
||||||
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
|
pub(crate) fn forced_import(cfg: &ImportConfig) -> Result<Self> {
|
||||||
|
let v1 = Version::ONE;
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
unspent_count: PerBlockWithDeltas::forced_import(
|
unspent_count: PerBlockWithDeltas::forced_import(
|
||||||
cfg.db,
|
cfg.db,
|
||||||
&cfg.name("utxo_count"),
|
&cfg.name("utxo_count"),
|
||||||
cfg.version,
|
cfg.version,
|
||||||
Version::ONE,
|
v1,
|
||||||
cfg.indexes,
|
cfg.indexes,
|
||||||
cfg.cached_starts,
|
cfg.cached_starts,
|
||||||
)?,
|
)?,
|
||||||
|
spent_count: cfg.import("spent_utxo_count", v1)?,
|
||||||
|
spending_rate: cfg.import("spending_rate", v1)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn min_len(&self) -> usize {
|
pub(crate) fn min_len(&self) -> usize {
|
||||||
self.unspent_count.height.len()
|
self.unspent_count.height.len()
|
||||||
|
.min(self.spent_count.block.len())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@@ -40,10 +46,35 @@ impl OutputsBase {
|
|||||||
self.unspent_count
|
self.unspent_count
|
||||||
.height
|
.height
|
||||||
.push(StoredU64::from(state.supply.utxo_count));
|
.push(StoredU64::from(state.supply.utxo_count));
|
||||||
|
self.spent_count
|
||||||
|
.block
|
||||||
|
.push(StoredU32::from(state.spent_utxo_count));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn collect_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
|
pub(crate) fn collect_vecs_mut(&mut self) -> Vec<&mut dyn AnyStoredVec> {
|
||||||
vec![&mut self.unspent_count.height as &mut dyn AnyStoredVec]
|
vec![
|
||||||
|
&mut self.unspent_count.height as &mut dyn AnyStoredVec,
|
||||||
|
&mut self.spent_count.block,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn compute_rest(&mut self, max_from: Height, exit: &Exit) -> Result<()> {
|
||||||
|
self.spent_count.compute_rest(max_from, exit)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn compute_part2(
|
||||||
|
&mut self,
|
||||||
|
max_from: Height,
|
||||||
|
all_utxo_count: &impl ReadableVec<Height, StoredU64>,
|
||||||
|
exit: &Exit,
|
||||||
|
) -> Result<()> {
|
||||||
|
self.spending_rate
|
||||||
|
.compute_binary::<StoredU32, StoredU64, RatioU32U64F32>(
|
||||||
|
max_from,
|
||||||
|
&self.spent_count.block,
|
||||||
|
all_utxo_count,
|
||||||
|
exit,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn compute_from_stateful(
|
pub(crate) fn compute_from_stateful(
|
||||||
@@ -60,6 +91,7 @@ impl OutputsBase {
|
|||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
exit,
|
exit,
|
||||||
)?;
|
)?;
|
||||||
|
sum_others!(self, starting_indexes, others, exit; spent_count.block);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ impl<R: RealizedOps> AddrCohortState<R> {
|
|||||||
self.addr_count = 0;
|
self.addr_count = 0;
|
||||||
self.inner.supply = SupplyState::default();
|
self.inner.supply = SupplyState::default();
|
||||||
self.inner.sent = Sats::ZERO;
|
self.inner.sent = Sats::ZERO;
|
||||||
|
self.inner.spent_utxo_count = 0;
|
||||||
self.inner.satdays_destroyed = Sats::ZERO;
|
self.inner.satdays_destroyed = Sats::ZERO;
|
||||||
self.inner.realized = R::default();
|
self.inner.realized = R::default();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ pub struct CohortState<R: RealizedOps, C: CostBasisOps> {
|
|||||||
pub supply: SupplyState,
|
pub supply: SupplyState,
|
||||||
pub realized: R,
|
pub realized: R,
|
||||||
pub sent: Sats,
|
pub sent: Sats,
|
||||||
|
pub spent_utxo_count: u64,
|
||||||
pub satdays_destroyed: Sats,
|
pub satdays_destroyed: Sats,
|
||||||
cost_basis: C,
|
cost_basis: C,
|
||||||
}
|
}
|
||||||
@@ -63,6 +64,7 @@ impl<R: RealizedOps, C: CostBasisOps> CohortState<R, C> {
|
|||||||
supply: SupplyState::default(),
|
supply: SupplyState::default(),
|
||||||
realized: R::default(),
|
realized: R::default(),
|
||||||
sent: Sats::ZERO,
|
sent: Sats::ZERO,
|
||||||
|
spent_utxo_count: 0,
|
||||||
satdays_destroyed: Sats::ZERO,
|
satdays_destroyed: Sats::ZERO,
|
||||||
cost_basis: C::create(path, name),
|
cost_basis: C::create(path, name),
|
||||||
}
|
}
|
||||||
@@ -97,6 +99,7 @@ impl<R: RealizedOps, C: CostBasisOps> CohortState<R, C> {
|
|||||||
|
|
||||||
pub(crate) fn reset_single_iteration_values(&mut self) {
|
pub(crate) fn reset_single_iteration_values(&mut self) {
|
||||||
self.sent = Sats::ZERO;
|
self.sent = Sats::ZERO;
|
||||||
|
self.spent_utxo_count = 0;
|
||||||
if R::TRACK_ACTIVITY {
|
if R::TRACK_ACTIVITY {
|
||||||
self.satdays_destroyed = Sats::ZERO;
|
self.satdays_destroyed = Sats::ZERO;
|
||||||
}
|
}
|
||||||
@@ -197,6 +200,7 @@ impl<R: RealizedOps, C: CostBasisOps> CohortState<R, C> {
|
|||||||
) {
|
) {
|
||||||
self.supply -= supply;
|
self.supply -= supply;
|
||||||
self.sent += pre.sats;
|
self.sent += pre.sats;
|
||||||
|
self.spent_utxo_count += supply.utxo_count;
|
||||||
if R::TRACK_ACTIVITY {
|
if R::TRACK_ACTIVITY {
|
||||||
self.satdays_destroyed += pre.age.satdays_destroyed(pre.sats);
|
self.satdays_destroyed += pre.age.satdays_destroyed(pre.sats);
|
||||||
}
|
}
|
||||||
@@ -220,6 +224,7 @@ impl<R: RealizedOps, C: CostBasisOps> CohortState<R, C> {
|
|||||||
self.send_utxo_precomputed(supply, &pre);
|
self.send_utxo_precomputed(supply, &pre);
|
||||||
} else if supply.utxo_count > 0 {
|
} else if supply.utxo_count > 0 {
|
||||||
self.supply -= supply;
|
self.supply -= supply;
|
||||||
|
self.spent_utxo_count += supply.utxo_count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -239,6 +244,7 @@ impl<R: RealizedOps, C: CostBasisOps> CohortState<R, C> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
self.supply -= supply;
|
self.supply -= supply;
|
||||||
|
self.spent_utxo_count += supply.utxo_count;
|
||||||
|
|
||||||
if supply.value > Sats::ZERO {
|
if supply.value > Sats::ZERO {
|
||||||
self.sent += supply.value;
|
self.sent += supply.value;
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ impl<R: RealizedOps, C: CostBasisOps> UTXOCohortState<R, C> {
|
|||||||
pub(crate) fn reset(&mut self) {
|
pub(crate) fn reset(&mut self) {
|
||||||
self.0.supply = SupplyState::default();
|
self.0.supply = SupplyState::default();
|
||||||
self.0.sent = Sats::ZERO;
|
self.0.sent = Sats::ZERO;
|
||||||
|
self.0.spent_utxo_count = 0;
|
||||||
self.0.satdays_destroyed = Sats::ZERO;
|
self.0.satdays_destroyed = Sats::ZERO;
|
||||||
self.0.realized = R::default();
|
self.0.realized = R::default();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -467,8 +467,10 @@ impl Vecs {
|
|||||||
&height_to_market_cap,
|
&height_to_market_cap,
|
||||||
exit,
|
exit,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
let all_utxo_count = self.utxo_cohorts.all.metrics.outputs.unspent_count.height.read_only_clone();
|
||||||
self.addr_cohorts
|
self.addr_cohorts
|
||||||
.compute_rest_part2(prices, starting_indexes, exit)?;
|
.compute_rest_part2(prices, starting_indexes, &all_utxo_count, exit)?;
|
||||||
|
|
||||||
let _lock = exit.lock();
|
let _lock = exit.lock();
|
||||||
self.db.compact()?;
|
self.db.compact()?;
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ pub use derived::{
|
|||||||
pub use ratio::{
|
pub use ratio::{
|
||||||
RatioCentsBp32, RatioCentsSignedCentsBps32, RatioCentsSignedDollarsBps32, RatioDiffCentsBps32,
|
RatioCentsBp32, RatioCentsSignedCentsBps32, RatioCentsSignedDollarsBps32, RatioDiffCentsBps32,
|
||||||
RatioDiffDollarsBps32, RatioDiffF32Bps32, RatioDollarsBp16, RatioDollarsBp32,
|
RatioDiffDollarsBps32, RatioDiffF32Bps32, RatioDollarsBp16, RatioDollarsBp32,
|
||||||
RatioDollarsBps32, RatioSatsBp16, RatioU64Bp16,
|
RatioDollarsBps32, RatioSatsBp16, RatioU32U64F32, RatioU64Bp16,
|
||||||
};
|
};
|
||||||
pub use specialized::{
|
pub use specialized::{
|
||||||
BlockCountTarget1m, BlockCountTarget1w, BlockCountTarget1y, BlockCountTarget24h,
|
BlockCountTarget1m, BlockCountTarget1w, BlockCountTarget1y, BlockCountTarget24h,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use brk_types::{
|
use brk_types::{
|
||||||
BasisPoints16, BasisPoints32, BasisPointsSigned32, Cents, CentsSigned, Dollars, Sats, StoredF32,
|
BasisPoints16, BasisPoints32, BasisPointsSigned32, Cents, CentsSigned, Dollars, Sats, StoredF32,
|
||||||
StoredU64,
|
StoredU32, StoredU64,
|
||||||
};
|
};
|
||||||
use vecdb::BinaryTransform;
|
use vecdb::BinaryTransform;
|
||||||
|
|
||||||
@@ -112,6 +112,19 @@ impl BinaryTransform<Dollars, Dollars, BasisPoints32> for RatioDollarsBp32 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct RatioU32U64F32;
|
||||||
|
|
||||||
|
impl BinaryTransform<StoredU32, StoredU64, StoredF32> for RatioU32U64F32 {
|
||||||
|
#[inline(always)]
|
||||||
|
fn apply(numerator: StoredU32, denominator: StoredU64) -> StoredF32 {
|
||||||
|
if *denominator > 0 {
|
||||||
|
StoredF32::from(*numerator as f64 / *denominator as f64)
|
||||||
|
} else {
|
||||||
|
StoredF32::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct RatioDiffF32Bps32;
|
pub struct RatioDiffF32Bps32;
|
||||||
|
|
||||||
impl BinaryTransform<StoredF32, StoredF32, BasisPointsSigned32> for RatioDiffF32Bps32 {
|
impl BinaryTransform<StoredF32, StoredF32, BasisPointsSigned32> for RatioDiffF32Bps32 {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use core::panic;
|
|
||||||
use std::{
|
use std::{
|
||||||
cmp::Ordering,
|
cmp::Ordering,
|
||||||
f32,
|
f32,
|
||||||
@@ -33,9 +32,7 @@ impl From<f32> for StoredF32 {
|
|||||||
impl From<f64> for StoredF32 {
|
impl From<f64> for StoredF32 {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(value: f64) -> Self {
|
fn from(value: f64) -> Self {
|
||||||
if value > f32::MAX as f64 {
|
debug_assert!(value <= f32::MAX as f64);
|
||||||
panic!("f64 is too big")
|
|
||||||
}
|
|
||||||
Self(value as f32)
|
Self(value as f32)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,9 +40,7 @@ impl From<i16> for StoredI16 {
|
|||||||
impl From<usize> for StoredI16 {
|
impl From<usize> for StoredI16 {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(value: usize) -> Self {
|
fn from(value: usize) -> Self {
|
||||||
if value > i16::MAX as usize {
|
debug_assert!(value <= i16::MAX as usize);
|
||||||
panic!("usize too big (value = {value})")
|
|
||||||
}
|
|
||||||
Self(value as i16)
|
Self(value as i16)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -76,9 +74,7 @@ impl AddAssign for StoredI16 {
|
|||||||
impl From<f64> for StoredI16 {
|
impl From<f64> for StoredI16 {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(value: f64) -> Self {
|
fn from(value: f64) -> Self {
|
||||||
if value < 0.0 || value > i16::MAX as f64 {
|
debug_assert!(value >= 0.0 && value <= i16::MAX as f64);
|
||||||
panic!()
|
|
||||||
}
|
|
||||||
Self(value as i16)
|
Self(value as i16)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,9 +40,7 @@ impl From<i8> for StoredI8 {
|
|||||||
impl From<usize> for StoredI8 {
|
impl From<usize> for StoredI8 {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(value: usize) -> Self {
|
fn from(value: usize) -> Self {
|
||||||
if value > i8::MAX as usize {
|
debug_assert!(value <= i8::MAX as usize);
|
||||||
panic!("usize too big (value = {value})")
|
|
||||||
}
|
|
||||||
Self(value as i8)
|
Self(value as i8)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -76,9 +74,7 @@ impl AddAssign for StoredI8 {
|
|||||||
impl From<f64> for StoredI8 {
|
impl From<f64> for StoredI8 {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(value: f64) -> Self {
|
fn from(value: f64) -> Self {
|
||||||
if value < i8::MIN as f64 || value > i8::MAX as f64 {
|
debug_assert!(value >= i8::MIN as f64 && value <= i8::MAX as f64);
|
||||||
panic!()
|
|
||||||
}
|
|
||||||
Self(value as i8)
|
Self(value as i8)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,9 +47,7 @@ impl From<u16> for StoredU16 {
|
|||||||
impl From<usize> for StoredU16 {
|
impl From<usize> for StoredU16 {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(value: usize) -> Self {
|
fn from(value: usize) -> Self {
|
||||||
if value > u16::MAX as usize {
|
debug_assert!(value <= u16::MAX as usize);
|
||||||
panic!("usize too big (value = {value})")
|
|
||||||
}
|
|
||||||
Self(value as u16)
|
Self(value as u16)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,12 +63,18 @@ impl From<StoredU32> for f32 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<u64> for StoredU32 {
|
||||||
|
#[inline]
|
||||||
|
fn from(value: u64) -> Self {
|
||||||
|
debug_assert!(value <= u32::MAX as u64);
|
||||||
|
Self(value as u32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<usize> for StoredU32 {
|
impl From<usize> for StoredU32 {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(value: usize) -> Self {
|
fn from(value: usize) -> Self {
|
||||||
if value > u32::MAX as usize {
|
debug_assert!(value <= u32::MAX as usize);
|
||||||
panic!("usize too big (value = {value})")
|
|
||||||
}
|
|
||||||
Self(value as u32)
|
Self(value as u32)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -81,9 +87,7 @@ impl CheckedSub<StoredU32> for StoredU32 {
|
|||||||
|
|
||||||
impl CheckedSub<usize> for StoredU32 {
|
impl CheckedSub<usize> for StoredU32 {
|
||||||
fn checked_sub(self, rhs: usize) -> Option<Self> {
|
fn checked_sub(self, rhs: usize) -> Option<Self> {
|
||||||
if rhs > u32::MAX as usize {
|
debug_assert!(rhs <= u32::MAX as usize);
|
||||||
panic!()
|
|
||||||
}
|
|
||||||
self.0.checked_sub(rhs as u32).map(Self)
|
self.0.checked_sub(rhs as u32).map(Self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -125,9 +129,7 @@ impl Mul<usize> for StoredU32 {
|
|||||||
type Output = Self;
|
type Output = Self;
|
||||||
fn mul(self, rhs: usize) -> Self::Output {
|
fn mul(self, rhs: usize) -> Self::Output {
|
||||||
let res = self.0 as usize * rhs;
|
let res = self.0 as usize * rhs;
|
||||||
if res > u32::MAX as usize {
|
debug_assert!(res <= u32::MAX as usize);
|
||||||
panic!()
|
|
||||||
}
|
|
||||||
Self::from(res)
|
Self::from(res)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -285,16 +285,16 @@ export function createCointimeSection() {
|
|||||||
sum: pattern.sum,
|
sum: pattern.sum,
|
||||||
cumulative: pattern.cumulative,
|
cumulative: pattern.cumulative,
|
||||||
})),
|
})),
|
||||||
title: "Coinblocks",
|
metric: "Coinblocks",
|
||||||
unit: Unit.coinblocks,
|
unit: Unit.coinblocks,
|
||||||
}),
|
}),
|
||||||
...coinblocks.map(({ pattern, name, title, color }) => ({
|
...coinblocks.map(({ pattern, name, title: metric, color }) => ({
|
||||||
name,
|
name,
|
||||||
tree: sumsAndAveragesCumulative({
|
tree: sumsAndAveragesCumulative({
|
||||||
sum: pattern.sum,
|
sum: pattern.sum,
|
||||||
average: pattern.average,
|
average: pattern.average,
|
||||||
cumulative: pattern.cumulative,
|
cumulative: pattern.cumulative,
|
||||||
title,
|
metric,
|
||||||
unit: Unit.coinblocks,
|
unit: Unit.coinblocks,
|
||||||
color,
|
color,
|
||||||
}),
|
}),
|
||||||
@@ -322,16 +322,16 @@ export function createCointimeSection() {
|
|||||||
cumulative: vocdd.pattern.cumulative,
|
cumulative: vocdd.pattern.cumulative,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
title: "Cointime Value",
|
metric: "Cointime Value",
|
||||||
unit: Unit.usd,
|
unit: Unit.usd,
|
||||||
}),
|
}),
|
||||||
...cointimeValues.map(({ pattern, name, title, color }) => ({
|
...cointimeValues.map(({ pattern, name, title: metric, color }) => ({
|
||||||
name,
|
name,
|
||||||
tree: sumsAndAveragesCumulative({
|
tree: sumsAndAveragesCumulative({
|
||||||
sum: pattern.sum,
|
sum: pattern.sum,
|
||||||
average: pattern.average,
|
average: pattern.average,
|
||||||
cumulative: pattern.cumulative,
|
cumulative: pattern.cumulative,
|
||||||
title,
|
metric,
|
||||||
unit: Unit.usd,
|
unit: Unit.usd,
|
||||||
color,
|
color,
|
||||||
}),
|
}),
|
||||||
@@ -342,7 +342,7 @@ export function createCointimeSection() {
|
|||||||
sum: vocdd.pattern.sum,
|
sum: vocdd.pattern.sum,
|
||||||
average: vocdd.pattern.average,
|
average: vocdd.pattern.average,
|
||||||
cumulative: vocdd.pattern.cumulative,
|
cumulative: vocdd.pattern.cumulative,
|
||||||
title: vocdd.title,
|
metric: vocdd.title,
|
||||||
unit: Unit.usd,
|
unit: Unit.usd,
|
||||||
color: vocdd.color,
|
color: vocdd.color,
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -41,7 +41,8 @@ function volumeTree(tv, color, title) {
|
|||||||
return [
|
return [
|
||||||
...satsBtcUsdFullTree({
|
...satsBtcUsdFullTree({
|
||||||
pattern: tv,
|
pattern: tv,
|
||||||
title: title("Transfer Volume"),
|
title,
|
||||||
|
metric: "Transfer Volume",
|
||||||
color,
|
color,
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
@@ -83,7 +84,8 @@ function volumeTree(tv, color, title) {
|
|||||||
name: "In Profit",
|
name: "In Profit",
|
||||||
tree: satsBtcUsdFullTree({
|
tree: satsBtcUsdFullTree({
|
||||||
pattern: tv.inProfit,
|
pattern: tv.inProfit,
|
||||||
title: title("Transfer Volume In Profit"),
|
title,
|
||||||
|
metric: "Transfer Volume In Profit",
|
||||||
color: colors.profit,
|
color: colors.profit,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
@@ -91,7 +93,8 @@ function volumeTree(tv, color, title) {
|
|||||||
name: "In Loss",
|
name: "In Loss",
|
||||||
tree: satsBtcUsdFullTree({
|
tree: satsBtcUsdFullTree({
|
||||||
pattern: tv.inLoss,
|
pattern: tv.inLoss,
|
||||||
title: title("Transfer Volume In Loss"),
|
title,
|
||||||
|
metric: "Transfer Volume In Loss",
|
||||||
color: colors.loss,
|
color: colors.loss,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
@@ -122,7 +125,7 @@ function volumeFolderWithAdjusted(activity, adjustedTransferVolume, color, title
|
|||||||
name: "Volume",
|
name: "Volume",
|
||||||
tree: [
|
tree: [
|
||||||
...volumeTree(activity.transferVolume, color, title),
|
...volumeTree(activity.transferVolume, color, title),
|
||||||
{ name: "Adjusted", tree: chartsFromCount({ pattern: adjustedTransferVolume, title: title("Adjusted Transfer Volume"), unit: Unit.usd }) },
|
{ name: "Adjusted", tree: chartsFromCount({ pattern: adjustedTransferVolume, title, metric: "Adjusted Transfer Volume", unit: Unit.usd }) },
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -173,7 +176,7 @@ function singleRollingSoprTree(ratio, title, prefix = "") {
|
|||||||
* @returns {PartialOptionsTree}
|
* @returns {PartialOptionsTree}
|
||||||
*/
|
*/
|
||||||
function valueDestroyedTree(valueDestroyed, title) {
|
function valueDestroyedTree(valueDestroyed, title) {
|
||||||
return chartsFromCount({ pattern: valueDestroyed, title: title("Value Destroyed"), unit: Unit.usd });
|
return chartsFromCount({ pattern: valueDestroyed, title, metric: "Value Destroyed", unit: Unit.usd });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -196,7 +199,7 @@ function valueDestroyedFolderWithAdjusted(valueDestroyed, adjusted, title) {
|
|||||||
name: "Value Destroyed",
|
name: "Value Destroyed",
|
||||||
tree: [
|
tree: [
|
||||||
...valueDestroyedTree(valueDestroyed, title),
|
...valueDestroyedTree(valueDestroyed, title),
|
||||||
{ name: "Adjusted", tree: chartsFromCount({ pattern: adjusted, title: title("Adjusted Value Destroyed"), unit: Unit.usd }) },
|
{ name: "Adjusted", tree: chartsFromCount({ pattern: adjusted, title, metric: "Adjusted Value Destroyed", unit: Unit.usd }) },
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -258,7 +261,8 @@ function singleFullActivityTree(cohort, title, volumeItem, soprFolder, valueDest
|
|||||||
name: "Coindays Destroyed",
|
name: "Coindays Destroyed",
|
||||||
tree: chartsFromCount({
|
tree: chartsFromCount({
|
||||||
pattern: tree.activity.coindaysDestroyed,
|
pattern: tree.activity.coindaysDestroyed,
|
||||||
title: title("Coindays Destroyed"),
|
title,
|
||||||
|
metric: "Coindays Destroyed",
|
||||||
unit: Unit.coindays,
|
unit: Unit.coindays,
|
||||||
color,
|
color,
|
||||||
}),
|
}),
|
||||||
@@ -267,7 +271,8 @@ function singleFullActivityTree(cohort, title, volumeItem, soprFolder, valueDest
|
|||||||
name: "Dormancy",
|
name: "Dormancy",
|
||||||
tree: averagesArray({
|
tree: averagesArray({
|
||||||
windows: tree.activity.dormancy,
|
windows: tree.activity.dormancy,
|
||||||
title: title("Dormancy"),
|
title,
|
||||||
|
metric: "Dormancy",
|
||||||
unit: Unit.days,
|
unit: Unit.days,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
@@ -341,7 +346,8 @@ export function createActivitySectionWithActivity({ cohort, title }) {
|
|||||||
name: "Coindays Destroyed",
|
name: "Coindays Destroyed",
|
||||||
tree: chartsFromCount({
|
tree: chartsFromCount({
|
||||||
pattern: tree.activity.coindaysDestroyed,
|
pattern: tree.activity.coindaysDestroyed,
|
||||||
title: title("Coindays Destroyed"),
|
title,
|
||||||
|
metric: "Coindays Destroyed",
|
||||||
unit: Unit.coindays,
|
unit: Unit.coindays,
|
||||||
color,
|
color,
|
||||||
}),
|
}),
|
||||||
@@ -360,7 +366,8 @@ export function createActivitySectionMinimal({ cohort, title }) {
|
|||||||
name: "Activity",
|
name: "Activity",
|
||||||
tree: satsBtcUsdFullTree({
|
tree: satsBtcUsdFullTree({
|
||||||
pattern: cohort.tree.activity.transferVolume,
|
pattern: cohort.tree.activity.transferVolume,
|
||||||
title: title("Transfer Volume"),
|
title,
|
||||||
|
metric: "Transfer Volume",
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,12 +52,12 @@ function groupedUtxoCountFolder(list, all, title) {
|
|||||||
tree: [
|
tree: [
|
||||||
{
|
{
|
||||||
name: "Count",
|
name: "Count",
|
||||||
title: title("UTXOs"),
|
title: title("UTXO Count"),
|
||||||
bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||||
line({ series: tree.outputs.unspentCount.base, name, color, unit: Unit.count }),
|
line({ series: tree.outputs.unspentCount.base, name, color, unit: Unit.count }),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
...groupedDeltaItems(list, all, (c) => c.tree.outputs.unspentCount.delta, Unit.count, title, "UTXOs"),
|
...groupedDeltaItems(list, all, (c) => c.tree.outputs.unspentCount.delta, Unit.count, title, "UTXO Count"),
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -74,7 +74,8 @@ function singleDeltaItems(delta, unit, title, name) {
|
|||||||
{
|
{
|
||||||
...sumsTreeBaseline({
|
...sumsTreeBaseline({
|
||||||
windows: delta.absolute,
|
windows: delta.absolute,
|
||||||
title: title(`${name} Change`),
|
title,
|
||||||
|
metric: `${name} Change`,
|
||||||
unit,
|
unit,
|
||||||
}),
|
}),
|
||||||
name: "Change",
|
name: "Change",
|
||||||
@@ -82,7 +83,8 @@ function singleDeltaItems(delta, unit, title, name) {
|
|||||||
{
|
{
|
||||||
...rollingPercentRatioTree({
|
...rollingPercentRatioTree({
|
||||||
windows: delta.rate,
|
windows: delta.rate,
|
||||||
title: title(`${name} Growth Rate`),
|
title,
|
||||||
|
metric: `${name} Growth Rate`,
|
||||||
}),
|
}),
|
||||||
name: "Growth Rate",
|
name: "Growth Rate",
|
||||||
},
|
},
|
||||||
@@ -233,7 +235,7 @@ function countFolder(pattern, name, chartTitle, color, title) {
|
|||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
...singleDeltaItems(pattern.delta, Unit.count, title, "Change"),
|
...singleDeltaItems(pattern.delta, Unit.count, title, chartTitle),
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -257,7 +259,7 @@ export function createHoldingsSection({ cohort, title }) {
|
|||||||
title: title("Supply"),
|
title: title("Supply"),
|
||||||
bottom: simpleSupplySeries(supply),
|
bottom: simpleSupplySeries(supply),
|
||||||
},
|
},
|
||||||
...singleDeltaItems(supply.delta, Unit.sats, title, "Change"),
|
...singleDeltaItems(supply.delta, Unit.sats, title, "Supply"),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
countFolder(cohort.tree.outputs.unspentCount, "UTXOs", "UTXO Count", cohort.color, title),
|
countFolder(cohort.tree.outputs.unspentCount, "UTXOs", "UTXO Count", cohort.color, title),
|
||||||
@@ -281,7 +283,7 @@ export function createHoldingsSectionAll({ cohort, title }) {
|
|||||||
},
|
},
|
||||||
profitabilityChart(supply, title),
|
profitabilityChart(supply, title),
|
||||||
ownSupplyChart(supply, title),
|
ownSupplyChart(supply, title),
|
||||||
...singleDeltaItems(supply.delta, Unit.sats, title, "Change"),
|
...singleDeltaItems(supply.delta, Unit.sats, title, "Supply"),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
countFolder(cohort.tree.outputs.unspentCount, "UTXOs", "UTXO Count", cohort.color, title),
|
countFolder(cohort.tree.outputs.unspentCount, "UTXOs", "UTXO Count", cohort.color, title),
|
||||||
@@ -307,7 +309,7 @@ export function createHoldingsSectionWithRelative({ cohort, title }) {
|
|||||||
profitabilityChart(supply, title),
|
profitabilityChart(supply, title),
|
||||||
circulatingChart(supply, title),
|
circulatingChart(supply, title),
|
||||||
ownSupplyChart(supply, title),
|
ownSupplyChart(supply, title),
|
||||||
...singleDeltaItems(supply.delta, Unit.sats, title, "Change"),
|
...singleDeltaItems(supply.delta, Unit.sats, title, "Supply"),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
countFolder(cohort.tree.outputs.unspentCount, "UTXOs", "UTXO Count", cohort.color, title),
|
countFolder(cohort.tree.outputs.unspentCount, "UTXOs", "UTXO Count", cohort.color, title),
|
||||||
@@ -331,7 +333,7 @@ export function createHoldingsSectionWithOwnSupply({ cohort, title }) {
|
|||||||
},
|
},
|
||||||
profitabilityChart(supply, title),
|
profitabilityChart(supply, title),
|
||||||
circulatingChart(supply, title),
|
circulatingChart(supply, title),
|
||||||
...singleDeltaItems(supply.delta, Unit.sats, title, "Change"),
|
...singleDeltaItems(supply.delta, Unit.sats, title, "Supply"),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
countFolder(cohort.tree.outputs.unspentCount, "UTXOs", "UTXO Count", cohort.color, title),
|
countFolder(cohort.tree.outputs.unspentCount, "UTXOs", "UTXO Count", cohort.color, title),
|
||||||
@@ -354,7 +356,7 @@ export function createHoldingsSectionWithProfitLoss({ cohort, title }) {
|
|||||||
bottom: simpleSupplySeries(supply),
|
bottom: simpleSupplySeries(supply),
|
||||||
},
|
},
|
||||||
profitabilityChart(supply, title),
|
profitabilityChart(supply, title),
|
||||||
...singleDeltaItems(supply.delta, Unit.sats, title, "Change"),
|
...singleDeltaItems(supply.delta, Unit.sats, title, "Supply"),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
countFolder(cohort.tree.outputs.unspentCount, "UTXOs", "UTXO Count", cohort.color, title),
|
countFolder(cohort.tree.outputs.unspentCount, "UTXOs", "UTXO Count", cohort.color, title),
|
||||||
@@ -377,7 +379,7 @@ export function createHoldingsSectionAddress({ cohort, title }) {
|
|||||||
bottom: simpleSupplySeries(supply),
|
bottom: simpleSupplySeries(supply),
|
||||||
},
|
},
|
||||||
profitabilityChart(supply, title),
|
profitabilityChart(supply, title),
|
||||||
...singleDeltaItems(supply.delta, Unit.sats, title, "Change"),
|
...singleDeltaItems(supply.delta, Unit.sats, title, "Supply"),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
countFolder(cohort.tree.outputs.unspentCount, "UTXOs", "UTXO Count", cohort.color, title),
|
countFolder(cohort.tree.outputs.unspentCount, "UTXOs", "UTXO Count", cohort.color, title),
|
||||||
@@ -400,7 +402,7 @@ export function createHoldingsSectionAddressAmount({ cohort, title }) {
|
|||||||
title: title("Supply"),
|
title: title("Supply"),
|
||||||
bottom: simpleSupplySeries(supply),
|
bottom: simpleSupplySeries(supply),
|
||||||
},
|
},
|
||||||
...singleDeltaItems(supply.delta, Unit.sats, title, "Change"),
|
...singleDeltaItems(supply.delta, Unit.sats, title, "Supply"),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
countFolder(cohort.tree.outputs.unspentCount, "UTXOs", "UTXO Count", cohort.color, title),
|
countFolder(cohort.tree.outputs.unspentCount, "UTXOs", "UTXO Count", cohort.color, title),
|
||||||
@@ -461,12 +463,12 @@ export function createGroupedHoldingsSectionAddress({ list, all, title }) {
|
|||||||
tree: [
|
tree: [
|
||||||
{
|
{
|
||||||
name: "Count",
|
name: "Count",
|
||||||
title: title("Addresses"),
|
title: title("Address Count"),
|
||||||
bottom: mapCohortsWithAll(list, all, ({ name, color, addressCount }) =>
|
bottom: mapCohortsWithAll(list, all, ({ name, color, addressCount }) =>
|
||||||
line({ series: addressCount.base, name, color, unit: Unit.count }),
|
line({ series: addressCount.base, name, color, unit: Unit.count }),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
...groupedDeltaItems(list, all, (c) => c.addressCount.delta, Unit.count, title, "Addresses"),
|
...groupedDeltaItems(list, all, (c) => c.addressCount.delta, Unit.count, title, "Address Count"),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
@@ -492,12 +494,12 @@ export function createGroupedHoldingsSectionAddressAmount({ list, all, title })
|
|||||||
tree: [
|
tree: [
|
||||||
{
|
{
|
||||||
name: "Count",
|
name: "Count",
|
||||||
title: title("Addresses"),
|
title: title("Address Count"),
|
||||||
bottom: mapCohortsWithAll(list, all, ({ name, color, addressCount }) =>
|
bottom: mapCohortsWithAll(list, all, ({ name, color, addressCount }) =>
|
||||||
line({ series: addressCount.base, name, color, unit: Unit.count }),
|
line({ series: addressCount.base, name, color, unit: Unit.count }),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
...groupedDeltaItems(list, all, (c) => c.addressCount.delta, Unit.count, title, "Addresses"),
|
...groupedDeltaItems(list, all, (c) => c.addressCount.delta, Unit.count, title, "Address Count"),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -216,7 +216,8 @@ export function createCohortFolderAgeRangeWithMatured(cohort) {
|
|||||||
name: "Matured",
|
name: "Matured",
|
||||||
tree: satsBtcUsdFullTree({
|
tree: satsBtcUsdFullTree({
|
||||||
pattern: cohort.matured,
|
pattern: cohort.matured,
|
||||||
title: title("Matured Supply"),
|
title,
|
||||||
|
metric: "Matured Supply",
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
return folder;
|
return folder;
|
||||||
@@ -498,6 +499,7 @@ export function createGroupedAddressCohortFolder({
|
|||||||
* @returns {PartialOptionsGroup}
|
* @returns {PartialOptionsGroup}
|
||||||
*/
|
*/
|
||||||
function singleBucketFolder({ name, color, pattern }) {
|
function singleBucketFolder({ name, color, pattern }) {
|
||||||
|
const title = formatCohortTitle(name);
|
||||||
return {
|
return {
|
||||||
name,
|
name,
|
||||||
tree: [
|
tree: [
|
||||||
@@ -506,7 +508,7 @@ function singleBucketFolder({ name, color, pattern }) {
|
|||||||
tree: [
|
tree: [
|
||||||
{
|
{
|
||||||
name: "Value",
|
name: "Value",
|
||||||
title: `${name}: Supply`,
|
title: title("Supply"),
|
||||||
bottom: [
|
bottom: [
|
||||||
...satsBtcUsd({ pattern: pattern.supply.all, name: "Total" }),
|
...satsBtcUsd({ pattern: pattern.supply.all, name: "Total" }),
|
||||||
...satsBtcUsd({
|
...satsBtcUsd({
|
||||||
@@ -522,7 +524,8 @@ function singleBucketFolder({ name, color, pattern }) {
|
|||||||
{
|
{
|
||||||
...sumsTreeBaseline({
|
...sumsTreeBaseline({
|
||||||
windows: pattern.supply.all.delta.absolute,
|
windows: pattern.supply.all.delta.absolute,
|
||||||
title: `${name}: Supply Change`,
|
title,
|
||||||
|
metric: "Supply Change",
|
||||||
unit: Unit.sats,
|
unit: Unit.sats,
|
||||||
}),
|
}),
|
||||||
name: "Change",
|
name: "Change",
|
||||||
@@ -530,7 +533,8 @@ function singleBucketFolder({ name, color, pattern }) {
|
|||||||
{
|
{
|
||||||
...rollingPercentRatioTree({
|
...rollingPercentRatioTree({
|
||||||
windows: pattern.supply.all.delta.rate,
|
windows: pattern.supply.all.delta.rate,
|
||||||
title: `${name}: Supply Rate`,
|
title,
|
||||||
|
metric: "Supply Growth Rate",
|
||||||
}),
|
}),
|
||||||
name: "Growth Rate",
|
name: "Growth Rate",
|
||||||
},
|
},
|
||||||
@@ -540,7 +544,7 @@ function singleBucketFolder({ name, color, pattern }) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Realized Cap",
|
name: "Realized Cap",
|
||||||
title: `${name}: Realized Cap`,
|
title: title("Realized Cap"),
|
||||||
bottom: [
|
bottom: [
|
||||||
line({
|
line({
|
||||||
series: pattern.realizedCap.all,
|
series: pattern.realizedCap.all,
|
||||||
@@ -557,7 +561,7 @@ function singleBucketFolder({ name, color, pattern }) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "NUPL",
|
name: "NUPL",
|
||||||
title: `${name}: NUPL`,
|
title: title("NUPL"),
|
||||||
bottom: [
|
bottom: [
|
||||||
line({ series: pattern.nupl.ratio, name, color, unit: Unit.ratio }),
|
line({ series: pattern.nupl.ratio, name, color, unit: Unit.ratio }),
|
||||||
],
|
],
|
||||||
@@ -568,24 +572,25 @@ function singleBucketFolder({ name, color, pattern }) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {{ name: string, color: Color, pattern: RealizedSupplyPattern }[]} list
|
* @param {{ name: string, color: Color, pattern: RealizedSupplyPattern }[]} list
|
||||||
* @param {string} titlePrefix
|
* @param {string} groupTitle
|
||||||
* @returns {PartialOptionsTree}
|
* @returns {PartialOptionsTree}
|
||||||
*/
|
*/
|
||||||
function groupedBucketCharts(list, titlePrefix) {
|
function groupedBucketCharts(list, groupTitle) {
|
||||||
|
const title = formatCohortTitle(groupTitle);
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
name: "Supply",
|
name: "Supply",
|
||||||
tree: [
|
tree: [
|
||||||
{
|
{
|
||||||
name: "All",
|
name: "All",
|
||||||
title: `${titlePrefix}: Supply`,
|
title: title("Supply"),
|
||||||
bottom: list.flatMap(({ name, color, pattern }) =>
|
bottom: list.flatMap(({ name, color, pattern }) =>
|
||||||
satsBtcUsd({ pattern: pattern.supply.all, name, color }),
|
satsBtcUsd({ pattern: pattern.supply.all, name, color }),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "STH",
|
name: "STH",
|
||||||
title: `${titlePrefix}: STH Supply`,
|
title: title("STH Supply"),
|
||||||
bottom: list.flatMap(({ name, color, pattern }) =>
|
bottom: list.flatMap(({ name, color, pattern }) =>
|
||||||
satsBtcUsd({ pattern: pattern.supply.sth, name, color }),
|
satsBtcUsd({ pattern: pattern.supply.sth, name, color }),
|
||||||
),
|
),
|
||||||
@@ -598,7 +603,7 @@ function groupedBucketCharts(list, titlePrefix) {
|
|||||||
tree: [
|
tree: [
|
||||||
{
|
{
|
||||||
name: "Compare",
|
name: "Compare",
|
||||||
title: `${titlePrefix}: Supply Change`,
|
title: title("Supply Change"),
|
||||||
bottom: ROLLING_WINDOWS.flatMap((w) =>
|
bottom: ROLLING_WINDOWS.flatMap((w) =>
|
||||||
list.map(({ name, color, pattern }) =>
|
list.map(({ name, color, pattern }) =>
|
||||||
baseline({
|
baseline({
|
||||||
@@ -612,7 +617,7 @@ function groupedBucketCharts(list, titlePrefix) {
|
|||||||
},
|
},
|
||||||
...ROLLING_WINDOWS.map((w) => ({
|
...ROLLING_WINDOWS.map((w) => ({
|
||||||
name: w.name,
|
name: w.name,
|
||||||
title: `${titlePrefix}: Supply Change (${w.title})`,
|
title: title(`Supply Change (${w.title})`),
|
||||||
bottom: list.map(({ name, color, pattern }) =>
|
bottom: list.map(({ name, color, pattern }) =>
|
||||||
baseline({
|
baseline({
|
||||||
series: pattern.supply.all.delta.absolute[w.key],
|
series: pattern.supply.all.delta.absolute[w.key],
|
||||||
@@ -629,7 +634,7 @@ function groupedBucketCharts(list, titlePrefix) {
|
|||||||
tree: [
|
tree: [
|
||||||
{
|
{
|
||||||
name: "Compare",
|
name: "Compare",
|
||||||
title: `${titlePrefix}: Supply Rate`,
|
title: title("Supply Growth Rate"),
|
||||||
bottom: ROLLING_WINDOWS.flatMap((w) =>
|
bottom: ROLLING_WINDOWS.flatMap((w) =>
|
||||||
list.flatMap(({ name, color, pattern }) =>
|
list.flatMap(({ name, color, pattern }) =>
|
||||||
percentRatio({
|
percentRatio({
|
||||||
@@ -642,7 +647,7 @@ function groupedBucketCharts(list, titlePrefix) {
|
|||||||
},
|
},
|
||||||
...ROLLING_WINDOWS.map((w) => ({
|
...ROLLING_WINDOWS.map((w) => ({
|
||||||
name: w.name,
|
name: w.name,
|
||||||
title: `${titlePrefix}: Supply Rate (${w.title})`,
|
title: title(`Supply Growth Rate (${w.title})`),
|
||||||
bottom: list.flatMap(({ name, color, pattern }) =>
|
bottom: list.flatMap(({ name, color, pattern }) =>
|
||||||
percentRatio({
|
percentRatio({
|
||||||
pattern: pattern.supply.all.delta.rate[w.key],
|
pattern: pattern.supply.all.delta.rate[w.key],
|
||||||
@@ -662,7 +667,7 @@ function groupedBucketCharts(list, titlePrefix) {
|
|||||||
tree: [
|
tree: [
|
||||||
{
|
{
|
||||||
name: "All",
|
name: "All",
|
||||||
title: `${titlePrefix}: Realized Cap`,
|
title: title("Realized Cap"),
|
||||||
bottom: list.map(({ name, color, pattern }) =>
|
bottom: list.map(({ name, color, pattern }) =>
|
||||||
line({
|
line({
|
||||||
series: pattern.realizedCap.all,
|
series: pattern.realizedCap.all,
|
||||||
@@ -674,7 +679,7 @@ function groupedBucketCharts(list, titlePrefix) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "STH",
|
name: "STH",
|
||||||
title: `${titlePrefix}: STH Realized Cap`,
|
title: title("STH Realized Cap"),
|
||||||
bottom: list.map(({ name, color, pattern }) =>
|
bottom: list.map(({ name, color, pattern }) =>
|
||||||
line({
|
line({
|
||||||
series: pattern.realizedCap.sth,
|
series: pattern.realizedCap.sth,
|
||||||
@@ -688,7 +693,7 @@ function groupedBucketCharts(list, titlePrefix) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "NUPL",
|
name: "NUPL",
|
||||||
title: `${titlePrefix}: NUPL`,
|
title: title("NUPL"),
|
||||||
bottom: list.map(({ name, color, pattern }) =>
|
bottom: list.map(({ name, color, pattern }) =>
|
||||||
line({ series: pattern.nupl.ratio, name, color, unit: Unit.ratio }),
|
line({ series: pattern.nupl.ratio, name, color, unit: Unit.ratio }),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -389,7 +389,8 @@ function realizedNetFolder({ netPnl, title, extraChange = [] }) {
|
|||||||
{
|
{
|
||||||
...sumsTreeBaseline({
|
...sumsTreeBaseline({
|
||||||
windows: mapWindows(netPnl.delta.absolute, (c) => c.usd),
|
windows: mapWindows(netPnl.delta.absolute, (c) => c.usd),
|
||||||
title: title("Net Realized P&L Change"),
|
title,
|
||||||
|
metric: "Net Realized P&L Change",
|
||||||
unit: Unit.usd,
|
unit: Unit.usd,
|
||||||
}),
|
}),
|
||||||
name: "Change",
|
name: "Change",
|
||||||
@@ -399,7 +400,8 @@ function realizedNetFolder({ netPnl, title, extraChange = [] }) {
|
|||||||
tree: [
|
tree: [
|
||||||
...rollingPercentRatioTree({
|
...rollingPercentRatioTree({
|
||||||
windows: netPnl.delta.rate,
|
windows: netPnl.delta.rate,
|
||||||
title: title("Net Realized P&L Growth Rate"),
|
title,
|
||||||
|
metric: "Net Realized P&L Growth Rate",
|
||||||
}).tree,
|
}).tree,
|
||||||
...extraChange,
|
...extraChange,
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -19,8 +19,8 @@ import { ratioBottomSeries, mapCohortsWithAll, flatMapCohortsWithAll } from "../
|
|||||||
*/
|
*/
|
||||||
function singleDeltaItems(tree, title) {
|
function singleDeltaItems(tree, title) {
|
||||||
return [
|
return [
|
||||||
{ ...sumsTreeBaseline({ windows: mapWindows(tree.realized.cap.delta.absolute, (c) => c.usd), title: title("Realized Cap Change"), unit: Unit.usd }), name: "Change" },
|
{ ...sumsTreeBaseline({ windows: mapWindows(tree.realized.cap.delta.absolute, (c) => c.usd), title, metric: "Realized Cap Change", unit: Unit.usd }), name: "Change" },
|
||||||
{ ...rollingPercentRatioTree({ windows: tree.realized.cap.delta.rate, title: title("Realized Cap Growth Rate") }), name: "Growth Rate" },
|
{ ...rollingPercentRatioTree({ windows: tree.realized.cap.delta.rate, title, metric: "Realized Cap Growth Rate" }), name: "Growth Rate" },
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -46,8 +46,20 @@ import { periodIdToName } from "./utils.js";
|
|||||||
*/
|
*/
|
||||||
function indexRatio({ pattern, name, color, defaultActive }) {
|
function indexRatio({ pattern, name, color, defaultActive }) {
|
||||||
return [
|
return [
|
||||||
line({ series: pattern.percent, name, color, defaultActive, unit: Unit.index }),
|
line({
|
||||||
line({ series: pattern.ratio, name, color, defaultActive, unit: Unit.ratio }),
|
series: pattern.percent,
|
||||||
|
name,
|
||||||
|
color,
|
||||||
|
defaultActive,
|
||||||
|
unit: Unit.index,
|
||||||
|
}),
|
||||||
|
line({
|
||||||
|
series: pattern.ratio,
|
||||||
|
name,
|
||||||
|
color,
|
||||||
|
defaultActive,
|
||||||
|
unit: Unit.ratio,
|
||||||
|
}),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -530,7 +542,7 @@ export function createMarketSection() {
|
|||||||
},
|
},
|
||||||
...deltaTree({
|
...deltaTree({
|
||||||
delta: supply.marketCap.delta,
|
delta: supply.marketCap.delta,
|
||||||
title: "Market Cap",
|
metric: "Market Cap",
|
||||||
unit: Unit.usd,
|
unit: Unit.usd,
|
||||||
extract: (v) => v.usd,
|
extract: (v) => v.usd,
|
||||||
}),
|
}),
|
||||||
@@ -553,7 +565,7 @@ export function createMarketSection() {
|
|||||||
},
|
},
|
||||||
...deltaTree({
|
...deltaTree({
|
||||||
delta: cohorts.utxo.all.realized.cap.delta,
|
delta: cohorts.utxo.all.realized.cap.delta,
|
||||||
title: "Realized Cap",
|
metric: "Realized Cap",
|
||||||
unit: Unit.usd,
|
unit: Unit.usd,
|
||||||
extract: (v) => v.usd,
|
extract: (v) => v.usd,
|
||||||
}),
|
}),
|
||||||
@@ -650,7 +662,11 @@ export function createMarketSection() {
|
|||||||
title: "RSI Comparison",
|
title: "RSI Comparison",
|
||||||
bottom: [
|
bottom: [
|
||||||
...ROLLING_WINDOWS_TO_1M.flatMap((w) =>
|
...ROLLING_WINDOWS_TO_1M.flatMap((w) =>
|
||||||
indexRatio({ pattern: technical.rsi[w.key].rsi, name: w.name, color: w.color }),
|
indexRatio({
|
||||||
|
pattern: technical.rsi[w.key].rsi,
|
||||||
|
name: w.name,
|
||||||
|
color: w.color,
|
||||||
|
}),
|
||||||
),
|
),
|
||||||
priceLine({ unit: Unit.index, number: 70 }),
|
priceLine({ unit: Unit.index, number: 70 }),
|
||||||
priceLine({ unit: Unit.index, number: 30 }),
|
priceLine({ unit: Unit.index, number: 30 }),
|
||||||
@@ -662,9 +678,17 @@ export function createMarketSection() {
|
|||||||
name: w.name,
|
name: w.name,
|
||||||
title: `RSI (${w.title})`,
|
title: `RSI (${w.title})`,
|
||||||
bottom: [
|
bottom: [
|
||||||
...indexRatio({ pattern: rsi.rsi, name: "RSI", color: colors.indicator.main }),
|
...indexRatio({
|
||||||
|
pattern: rsi.rsi,
|
||||||
|
name: "RSI",
|
||||||
|
color: colors.indicator.main,
|
||||||
|
}),
|
||||||
priceLine({ unit: Unit.index, number: 70 }),
|
priceLine({ unit: Unit.index, number: 70 }),
|
||||||
priceLine({ unit: Unit.index, number: 50, defaultActive: false }),
|
priceLine({
|
||||||
|
unit: Unit.index,
|
||||||
|
number: 50,
|
||||||
|
defaultActive: false,
|
||||||
|
}),
|
||||||
priceLine({ unit: Unit.index, number: 30 }),
|
priceLine({ unit: Unit.index, number: 30 }),
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
@@ -672,17 +696,28 @@ export function createMarketSection() {
|
|||||||
{
|
{
|
||||||
name: "Stochastic",
|
name: "Stochastic",
|
||||||
tree: ROLLING_WINDOWS_TO_1M.map((w) => {
|
tree: ROLLING_WINDOWS_TO_1M.map((w) => {
|
||||||
const rsi = technical.rsi[w.key];
|
const rsi = technical.rsi[w.key];
|
||||||
return {
|
return {
|
||||||
name: w.name,
|
name: w.name,
|
||||||
title: `Stochastic RSI (${w.title})`,
|
title: `Stochastic RSI (${w.title})`,
|
||||||
bottom: [
|
bottom: [
|
||||||
...indexRatio({ pattern: rsi.stochRsiK, name: "K", color: colors.indicator.fast }),
|
...indexRatio({
|
||||||
...indexRatio({ pattern: rsi.stochRsiD, name: "D", color: colors.indicator.slow }),
|
pattern: rsi.stochRsiK,
|
||||||
...priceLines({ unit: Unit.index, numbers: [80, 20] }),
|
name: "K",
|
||||||
],
|
color: colors.indicator.fast,
|
||||||
};
|
}),
|
||||||
}),
|
...indexRatio({
|
||||||
|
pattern: rsi.stochRsiD,
|
||||||
|
name: "D",
|
||||||
|
color: colors.indicator.slow,
|
||||||
|
}),
|
||||||
|
...priceLines({
|
||||||
|
unit: Unit.index,
|
||||||
|
numbers: [80, 20],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -693,16 +728,35 @@ export function createMarketSection() {
|
|||||||
name: "Compare",
|
name: "Compare",
|
||||||
title: "MACD Comparison",
|
title: "MACD Comparison",
|
||||||
bottom: ROLLING_WINDOWS_TO_1M.map((w) =>
|
bottom: ROLLING_WINDOWS_TO_1M.map((w) =>
|
||||||
line({ series: technical.macd[w.key].line, name: w.name, color: w.color, unit: Unit.usd }),
|
line({
|
||||||
|
series: technical.macd[w.key].line,
|
||||||
|
name: w.name,
|
||||||
|
color: w.color,
|
||||||
|
unit: Unit.usd,
|
||||||
|
}),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
...ROLLING_WINDOWS_TO_1M.map((w) => ({
|
...ROLLING_WINDOWS_TO_1M.map((w) => ({
|
||||||
name: w.name,
|
name: w.name,
|
||||||
title: `MACD (${w.title})`,
|
title: `MACD (${w.title})`,
|
||||||
bottom: [
|
bottom: [
|
||||||
line({ series: technical.macd[w.key].line, name: "MACD", color: colors.indicator.fast, unit: Unit.usd }),
|
line({
|
||||||
line({ series: technical.macd[w.key].signal, name: "Signal", color: colors.indicator.slow, unit: Unit.usd }),
|
series: technical.macd[w.key].line,
|
||||||
histogram({ series: technical.macd[w.key].histogram, name: "Histogram", unit: Unit.usd }),
|
name: "MACD",
|
||||||
|
color: colors.indicator.fast,
|
||||||
|
unit: Unit.usd,
|
||||||
|
}),
|
||||||
|
line({
|
||||||
|
series: technical.macd[w.key].signal,
|
||||||
|
name: "Signal",
|
||||||
|
color: colors.indicator.slow,
|
||||||
|
unit: Unit.usd,
|
||||||
|
}),
|
||||||
|
histogram({
|
||||||
|
series: technical.macd[w.key].histogram,
|
||||||
|
name: "Histogram",
|
||||||
|
unit: Unit.usd,
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
})),
|
})),
|
||||||
],
|
],
|
||||||
@@ -721,13 +775,25 @@ export function createMarketSection() {
|
|||||||
name: "Compare",
|
name: "Compare",
|
||||||
title: "Volatility Index",
|
title: "Volatility Index",
|
||||||
bottom: ROLLING_WINDOWS.map((w) =>
|
bottom: ROLLING_WINDOWS.map((w) =>
|
||||||
line({ series: volatility[w.key], name: w.name, color: w.color, unit: Unit.percentage }),
|
line({
|
||||||
|
series: volatility[w.key],
|
||||||
|
name: w.name,
|
||||||
|
color: w.color,
|
||||||
|
unit: Unit.percentage,
|
||||||
|
}),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
...ROLLING_WINDOWS.map((w) => ({
|
...ROLLING_WINDOWS.map((w) => ({
|
||||||
name: w.name,
|
name: w.name,
|
||||||
title: `Volatility Index (${w.title})`,
|
title: `Volatility Index (${w.title})`,
|
||||||
bottom: [line({ series: volatility[w.key], name: w.name, color: w.color, unit: Unit.percentage })],
|
bottom: [
|
||||||
|
line({
|
||||||
|
series: volatility[w.key],
|
||||||
|
name: w.name,
|
||||||
|
color: w.color,
|
||||||
|
unit: Unit.percentage,
|
||||||
|
}),
|
||||||
|
],
|
||||||
})),
|
})),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import {
|
|||||||
satsBtcUsdFullTree,
|
satsBtcUsdFullTree,
|
||||||
revenueBtcSatsUsd,
|
revenueBtcSatsUsd,
|
||||||
revenueRollingBtcSatsUsd,
|
revenueRollingBtcSatsUsd,
|
||||||
|
formatCohortTitle,
|
||||||
} from "./shared.js";
|
} from "./shared.js";
|
||||||
import { brk } from "../client.js";
|
import { brk } from "../client.js";
|
||||||
|
|
||||||
@@ -76,13 +77,17 @@ export function createMiningSection() {
|
|||||||
includes(ANTPOOL_AND_FRIENDS_IDS, p.id),
|
includes(ANTPOOL_AND_FRIENDS_IDS, p.id),
|
||||||
);
|
);
|
||||||
|
|
||||||
/** @param {string} title @param {{ _24h: any, _1w: any, _1m: any, _1y: any, percent: any, ratio: any }} dominance */
|
/**
|
||||||
const dominanceTree = (title, dominance) => ({
|
* @param {(metric: string) => string} title
|
||||||
|
* @param {string} metric
|
||||||
|
* @param {{ _24h: any, _1w: any, _1m: any, _1y: any, percent: any, ratio: any }} dominance
|
||||||
|
*/
|
||||||
|
const dominanceTree = (title, metric, dominance) => ({
|
||||||
name: "Dominance",
|
name: "Dominance",
|
||||||
tree: [
|
tree: [
|
||||||
{
|
{
|
||||||
name: "Compare",
|
name: "Compare",
|
||||||
title,
|
title: title(metric),
|
||||||
bottom: [
|
bottom: [
|
||||||
...ROLLING_WINDOWS.flatMap((w) =>
|
...ROLLING_WINDOWS.flatMap((w) =>
|
||||||
percentRatio({ pattern: dominance[w.key], name: w.name, color: w.color, defaultActive: w.key !== "_24h" }),
|
percentRatio({ pattern: dominance[w.key], name: w.name, color: w.color, defaultActive: w.key !== "_24h" }),
|
||||||
@@ -92,12 +97,12 @@ export function createMiningSection() {
|
|||||||
},
|
},
|
||||||
...ROLLING_WINDOWS.map((w) => ({
|
...ROLLING_WINDOWS.map((w) => ({
|
||||||
name: w.name,
|
name: w.name,
|
||||||
title: `${title} ${w.title}`,
|
title: title(`${w.name} ${metric}`),
|
||||||
bottom: percentRatio({ pattern: dominance[w.key], name: w.name, color: w.color }),
|
bottom: percentRatio({ pattern: dominance[w.key], name: w.name, color: w.color }),
|
||||||
})),
|
})),
|
||||||
{
|
{
|
||||||
name: "All Time",
|
name: "All Time",
|
||||||
title: `${title} All Time`,
|
title: title(`${metric} All Time`),
|
||||||
bottom: percentRatio({ pattern: dominance, name: "All Time", color: colors.time.all }),
|
bottom: percentRatio({ pattern: dominance, name: "All Time", color: colors.time.all }),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -107,50 +112,59 @@ export function createMiningSection() {
|
|||||||
* @param {typeof majorPoolData} poolList
|
* @param {typeof majorPoolData} poolList
|
||||||
*/
|
*/
|
||||||
const createPoolTree = (poolList) =>
|
const createPoolTree = (poolList) =>
|
||||||
poolList.map(({ name, pool }) => ({
|
poolList.map(({ name, pool }) => {
|
||||||
name,
|
const title = formatCohortTitle(name);
|
||||||
tree: [
|
return {
|
||||||
dominanceTree(`Dominance: ${name}`, pool.dominance),
|
name,
|
||||||
{
|
tree: [
|
||||||
name: "Blocks Mined",
|
dominanceTree(title, "Dominance", pool.dominance),
|
||||||
tree: chartsFromCount({
|
{
|
||||||
pattern: pool.blocksMined,
|
name: "Blocks Mined",
|
||||||
title: `Blocks Mined: ${name}`,
|
tree: chartsFromCount({
|
||||||
unit: Unit.count,
|
pattern: pool.blocksMined,
|
||||||
}),
|
title,
|
||||||
},
|
metric: "Blocks Mined",
|
||||||
{
|
unit: Unit.count,
|
||||||
name: "Rewards",
|
}),
|
||||||
tree: satsBtcUsdFullTree({
|
},
|
||||||
pattern: pool.rewards,
|
{
|
||||||
title: `Rewards: ${name}`,
|
name: "Rewards",
|
||||||
}),
|
tree: satsBtcUsdFullTree({
|
||||||
},
|
pattern: pool.rewards,
|
||||||
],
|
title,
|
||||||
}));
|
metric: "Rewards",
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {typeof minorPoolData} poolList
|
* @param {typeof minorPoolData} poolList
|
||||||
*/
|
*/
|
||||||
const createMinorPoolTree = (poolList) =>
|
const createMinorPoolTree = (poolList) =>
|
||||||
poolList.map(({ name, pool }) => ({
|
poolList.map(({ name, pool }) => {
|
||||||
name,
|
const title = formatCohortTitle(name);
|
||||||
tree: [
|
return {
|
||||||
{
|
name,
|
||||||
name: "Dominance",
|
tree: [
|
||||||
title: `Dominance: ${name}`,
|
{
|
||||||
bottom: percentRatio({ pattern: pool.dominance, name: "All Time", color: colors.time.all }),
|
name: "Dominance",
|
||||||
},
|
title: title("Dominance"),
|
||||||
{
|
bottom: percentRatio({ pattern: pool.dominance, name: "All Time", color: colors.time.all }),
|
||||||
name: "Blocks Mined",
|
},
|
||||||
tree: chartsFromCount({
|
{
|
||||||
pattern: pool.blocksMined,
|
name: "Blocks Mined",
|
||||||
title: `Blocks Mined: ${name}`,
|
tree: chartsFromCount({
|
||||||
unit: Unit.count,
|
pattern: pool.blocksMined,
|
||||||
}),
|
title,
|
||||||
},
|
metric: "Blocks Mined",
|
||||||
],
|
unit: Unit.count,
|
||||||
}));
|
}),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} groupTitle
|
* @param {string} groupTitle
|
||||||
@@ -306,14 +320,14 @@ export function createMiningSection() {
|
|||||||
name: "Coinbase",
|
name: "Coinbase",
|
||||||
tree: satsBtcUsdFullTree({
|
tree: satsBtcUsdFullTree({
|
||||||
pattern: mining.rewards.coinbase,
|
pattern: mining.rewards.coinbase,
|
||||||
title: "Coinbase Rewards",
|
metric: "Coinbase Rewards",
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Subsidy",
|
name: "Subsidy",
|
||||||
tree: satsBtcUsdFullTree({
|
tree: satsBtcUsdFullTree({
|
||||||
pattern: mining.rewards.subsidy,
|
pattern: mining.rewards.subsidy,
|
||||||
title: "Block Subsidy",
|
metric: "Block Subsidy",
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -321,7 +335,7 @@ export function createMiningSection() {
|
|||||||
tree: [
|
tree: [
|
||||||
...satsBtcUsdFullTree({
|
...satsBtcUsdFullTree({
|
||||||
pattern: mining.rewards.fees,
|
pattern: mining.rewards.fees,
|
||||||
title: "Transaction Fee Revenue",
|
metric: "Transaction Fee Revenue",
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
name: "Distributions",
|
name: "Distributions",
|
||||||
|
|||||||
@@ -53,7 +53,12 @@ export function createNetworkSection() {
|
|||||||
// Non-addressable script types
|
// Non-addressable script types
|
||||||
const nonAddressableTypes = /** @type {const} */ ([
|
const nonAddressableTypes = /** @type {const} */ ([
|
||||||
{ key: "p2ms", name: "P2MS", color: st.p2ms, defaultActive: false },
|
{ key: "p2ms", name: "P2MS", color: st.p2ms, defaultActive: false },
|
||||||
{ key: "opReturn", name: "OP_RETURN", color: st.opReturn, defaultActive: true },
|
{
|
||||||
|
key: "opReturn",
|
||||||
|
name: "OP_RETURN",
|
||||||
|
color: st.opReturn,
|
||||||
|
defaultActive: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: "emptyOutput",
|
key: "emptyOutput",
|
||||||
name: "Empty",
|
name: "Empty",
|
||||||
@@ -71,7 +76,6 @@ export function createNetworkSection() {
|
|||||||
// All script types = addressable + non-addressable
|
// All script types = addressable + non-addressable
|
||||||
const scriptTypes = [...addressTypes, ...nonAddressableTypes];
|
const scriptTypes = [...addressTypes, ...nonAddressableTypes];
|
||||||
|
|
||||||
|
|
||||||
// Transacting types (transaction participation)
|
// Transacting types (transaction participation)
|
||||||
const activityTypes = /** @type {const} */ ([
|
const activityTypes = /** @type {const} */ ([
|
||||||
{ key: "sending", name: "Sending" },
|
{ key: "sending", name: "Sending" },
|
||||||
@@ -138,14 +142,16 @@ export function createNetworkSection() {
|
|||||||
},
|
},
|
||||||
...simpleDeltaTree({
|
...simpleDeltaTree({
|
||||||
delta: addrs.delta[key],
|
delta: addrs.delta[key],
|
||||||
title: `${titlePrefix}Address Count`,
|
title: (s) => `${titlePrefix}${s}`,
|
||||||
|
metric: "Address Count",
|
||||||
unit: Unit.count,
|
unit: Unit.count,
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
name: "New",
|
name: "New",
|
||||||
tree: chartsFromCount({
|
tree: chartsFromCount({
|
||||||
pattern: addrs.new[key],
|
pattern: addrs.new[key],
|
||||||
title: `${titlePrefix}New Addresses`,
|
title: (s) => `${titlePrefix}${s}`,
|
||||||
|
metric: "New Addresses",
|
||||||
unit: Unit.count,
|
unit: Unit.count,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
@@ -171,7 +177,8 @@ export function createNetworkSection() {
|
|||||||
name: t.name,
|
name: t.name,
|
||||||
tree: averagesArray({
|
tree: averagesArray({
|
||||||
windows: addrs.activity[key][t.key],
|
windows: addrs.activity[key][t.key],
|
||||||
title: `${titlePrefix}${t.name} Addresses`,
|
title: (s) => `${titlePrefix}${s}`,
|
||||||
|
metric: `${t.name} Addresses`,
|
||||||
unit: Unit.count,
|
unit: Unit.count,
|
||||||
}),
|
}),
|
||||||
})),
|
})),
|
||||||
@@ -187,7 +194,10 @@ export function createNetworkSection() {
|
|||||||
{ name: "Script Hash", types: [byKey.p2sh, byKey.p2ms] },
|
{ name: "Script Hash", types: [byKey.p2sh, byKey.p2ms] },
|
||||||
{ name: "SegWit", types: [byKey.p2wsh, byKey.p2wpkh] },
|
{ name: "SegWit", types: [byKey.p2wsh, byKey.p2wpkh] },
|
||||||
{ name: "Taproot", types: [byKey.p2a, byKey.p2tr] },
|
{ name: "Taproot", types: [byKey.p2a, byKey.p2tr] },
|
||||||
{ name: "Other", types: [byKey.opReturn, byKey.emptyOutput, byKey.unknownOutput] },
|
{
|
||||||
|
name: "Other",
|
||||||
|
types: [byKey.opReturn, byKey.emptyOutput, byKey.unknownOutput],
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -206,7 +216,9 @@ export function createNetworkSection() {
|
|||||||
title: `${groupName} Output Count ${w.title} Sum`,
|
title: `${groupName} Output Count ${w.title} Sum`,
|
||||||
bottom: types.map((t) =>
|
bottom: types.map((t) =>
|
||||||
line({
|
line({
|
||||||
series: /** @type {CountPattern<number>} */ (scripts.count[t.key]).sum[w.key],
|
series: /** @type {CountPattern<number>} */ (
|
||||||
|
scripts.count[t.key]
|
||||||
|
).sum[w.key],
|
||||||
name: t.name,
|
name: t.name,
|
||||||
color: t.color,
|
color: t.color,
|
||||||
unit: Unit.count,
|
unit: Unit.count,
|
||||||
@@ -231,7 +243,7 @@ export function createNetworkSection() {
|
|||||||
name: t.name,
|
name: t.name,
|
||||||
tree: chartsFromCount({
|
tree: chartsFromCount({
|
||||||
pattern: /** @type {CountPattern<number>} */ (scripts.count[t.key]),
|
pattern: /** @type {CountPattern<number>} */ (scripts.count[t.key]),
|
||||||
title: `${t.name} Output Count`,
|
metric: `${t.name} Output Count`,
|
||||||
unit: Unit.count,
|
unit: Unit.count,
|
||||||
}),
|
}),
|
||||||
})),
|
})),
|
||||||
@@ -298,7 +310,7 @@ export function createNetworkSection() {
|
|||||||
name: "Count",
|
name: "Count",
|
||||||
tree: chartsFromFullPerBlock({
|
tree: chartsFromFullPerBlock({
|
||||||
pattern: transactions.count.total,
|
pattern: transactions.count.total,
|
||||||
title: "Transaction Count",
|
metric: "Transaction Count",
|
||||||
unit: Unit.count,
|
unit: Unit.count,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
@@ -306,14 +318,14 @@ export function createNetworkSection() {
|
|||||||
name: "Volume",
|
name: "Volume",
|
||||||
tree: satsBtcUsdFullTree({
|
tree: satsBtcUsdFullTree({
|
||||||
pattern: transactions.volume.transferVolume,
|
pattern: transactions.volume.transferVolume,
|
||||||
title: "Transaction Volume",
|
metric: "Transaction Volume",
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Fee Rate",
|
name: "Fee Rate",
|
||||||
tree: chartsFromBlockAnd6b({
|
tree: chartsFromBlockAnd6b({
|
||||||
pattern: transactions.fees.feeRate,
|
pattern: transactions.fees.feeRate,
|
||||||
title: "Transaction Fee Rate",
|
metric: "Transaction Fee Rate",
|
||||||
unit: Unit.feeRate,
|
unit: Unit.feeRate,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
@@ -321,7 +333,7 @@ export function createNetworkSection() {
|
|||||||
name: "Fee",
|
name: "Fee",
|
||||||
tree: chartsFromBlockAnd6b({
|
tree: chartsFromBlockAnd6b({
|
||||||
pattern: transactions.fees.fee,
|
pattern: transactions.fees.fee,
|
||||||
title: "Transaction Fee",
|
metric: "Transaction Fee",
|
||||||
unit: Unit.sats,
|
unit: Unit.sats,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
@@ -329,7 +341,7 @@ export function createNetworkSection() {
|
|||||||
name: "Weight",
|
name: "Weight",
|
||||||
tree: chartsFromBlockAnd6b({
|
tree: chartsFromBlockAnd6b({
|
||||||
pattern: transactions.size.weight,
|
pattern: transactions.size.weight,
|
||||||
title: "Transaction Weight",
|
metric: "Transaction Weight",
|
||||||
unit: Unit.wu,
|
unit: Unit.wu,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
@@ -337,7 +349,7 @@ export function createNetworkSection() {
|
|||||||
name: "vSize",
|
name: "vSize",
|
||||||
tree: chartsFromBlockAnd6b({
|
tree: chartsFromBlockAnd6b({
|
||||||
pattern: transactions.size.vsize,
|
pattern: transactions.size.vsize,
|
||||||
title: "Transaction vSize",
|
metric: "Transaction vSize",
|
||||||
unit: Unit.vb,
|
unit: Unit.vb,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
@@ -345,7 +357,7 @@ export function createNetworkSection() {
|
|||||||
name: "Versions",
|
name: "Versions",
|
||||||
tree: chartsFromCountEntries({
|
tree: chartsFromCountEntries({
|
||||||
entries: entries(transactions.versions),
|
entries: entries(transactions.versions),
|
||||||
title: "Transaction Versions",
|
metric: "Transaction Versions",
|
||||||
unit: Unit.count,
|
unit: Unit.count,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
@@ -423,7 +435,7 @@ export function createNetworkSection() {
|
|||||||
name: "Interval",
|
name: "Interval",
|
||||||
tree: averagesArray({
|
tree: averagesArray({
|
||||||
windows: blocks.interval,
|
windows: blocks.interval,
|
||||||
title: "Block Interval",
|
metric: "Block Interval",
|
||||||
unit: Unit.secs,
|
unit: Unit.secs,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
@@ -431,7 +443,7 @@ export function createNetworkSection() {
|
|||||||
name: "Size",
|
name: "Size",
|
||||||
tree: chartsFromFullPerBlock({
|
tree: chartsFromFullPerBlock({
|
||||||
pattern: blocks.size,
|
pattern: blocks.size,
|
||||||
title: "Block Size",
|
metric: "Block Size",
|
||||||
unit: Unit.bytes,
|
unit: Unit.bytes,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
@@ -439,7 +451,7 @@ export function createNetworkSection() {
|
|||||||
name: "Weight",
|
name: "Weight",
|
||||||
tree: chartsFromFullPerBlock({
|
tree: chartsFromFullPerBlock({
|
||||||
pattern: blocks.weight,
|
pattern: blocks.weight,
|
||||||
title: "Block Weight",
|
metric: "Block Weight",
|
||||||
unit: Unit.wu,
|
unit: Unit.wu,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
@@ -447,7 +459,7 @@ export function createNetworkSection() {
|
|||||||
name: "vBytes",
|
name: "vBytes",
|
||||||
tree: chartsFromFullPerBlock({
|
tree: chartsFromFullPerBlock({
|
||||||
pattern: blocks.vbytes,
|
pattern: blocks.vbytes,
|
||||||
title: "Block vBytes",
|
metric: "Block vBytes",
|
||||||
unit: Unit.vb,
|
unit: Unit.vb,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
@@ -471,7 +483,7 @@ export function createNetworkSection() {
|
|||||||
},
|
},
|
||||||
...simpleDeltaTree({
|
...simpleDeltaTree({
|
||||||
delta: cohorts.utxo.all.outputs.unspentCount.delta,
|
delta: cohorts.utxo.all.outputs.unspentCount.delta,
|
||||||
title: "UTXO Count",
|
metric: "UTXO Count",
|
||||||
unit: Unit.count,
|
unit: Unit.count,
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
@@ -493,7 +505,7 @@ export function createNetworkSection() {
|
|||||||
cumulative: inputs.count.cumulative,
|
cumulative: inputs.count.cumulative,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
title: "UTXO Flow",
|
metric: "UTXO Flow",
|
||||||
unit: Unit.count,
|
unit: Unit.count,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
@@ -503,7 +515,7 @@ export function createNetworkSection() {
|
|||||||
name: "Inputs",
|
name: "Inputs",
|
||||||
tree: chartsFromAggregatedPerBlock({
|
tree: chartsFromAggregatedPerBlock({
|
||||||
pattern: inputs.count,
|
pattern: inputs.count,
|
||||||
title: "Input Count",
|
metric: "Input Count",
|
||||||
unit: Unit.count,
|
unit: Unit.count,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
@@ -511,7 +523,7 @@ export function createNetworkSection() {
|
|||||||
name: "Outputs",
|
name: "Outputs",
|
||||||
tree: chartsFromAggregatedPerBlock({
|
tree: chartsFromAggregatedPerBlock({
|
||||||
pattern: outputs.count.total,
|
pattern: outputs.count.total,
|
||||||
title: "Output Count",
|
metric: "Output Count",
|
||||||
unit: Unit.count,
|
unit: Unit.count,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
@@ -588,7 +600,9 @@ export function createNetworkSection() {
|
|||||||
title: `Output Count by Script Type ${w.title} Sum`,
|
title: `Output Count by Script Type ${w.title} Sum`,
|
||||||
bottom: scriptTypes.map((t) =>
|
bottom: scriptTypes.map((t) =>
|
||||||
line({
|
line({
|
||||||
series: /** @type {CountPattern<number>} */ (scripts.count[t.key]).sum[w.key],
|
series: /** @type {CountPattern<number>} */ (
|
||||||
|
scripts.count[t.key]
|
||||||
|
).sum[w.key],
|
||||||
name: t.name,
|
name: t.name,
|
||||||
color: t.color,
|
color: t.color,
|
||||||
unit: Unit.count,
|
unit: Unit.count,
|
||||||
|
|||||||
@@ -93,13 +93,48 @@ export function price({
|
|||||||
function percentileSeries({ pattern, unit, title = "" }) {
|
function percentileSeries({ pattern, unit, title = "" }) {
|
||||||
const { stat } = colors;
|
const { stat } = colors;
|
||||||
return [
|
return [
|
||||||
line({ series: pattern.max, name: `${title} max`.trim(), color: stat.max, unit }),
|
line({
|
||||||
line({ series: pattern.pct90, name: `${title} pct90`.trim(), color: stat.pct90, unit }),
|
series: pattern.max,
|
||||||
line({ series: pattern.pct75, name: `${title} pct75`.trim(), color: stat.pct75, unit }),
|
name: `${title} max`.trim(),
|
||||||
line({ series: pattern.median, name: `${title} median`.trim(), color: stat.median, unit }),
|
color: stat.max,
|
||||||
line({ series: pattern.pct25, name: `${title} pct25`.trim(), color: stat.pct25, unit }),
|
unit,
|
||||||
line({ series: pattern.pct10, name: `${title} pct10`.trim(), color: stat.pct10, unit }),
|
}),
|
||||||
line({ series: pattern.min, name: `${title} min`.trim(), color: stat.min, unit }),
|
line({
|
||||||
|
series: pattern.pct90,
|
||||||
|
name: `${title} pct90`.trim(),
|
||||||
|
color: stat.pct90,
|
||||||
|
unit,
|
||||||
|
}),
|
||||||
|
line({
|
||||||
|
series: pattern.pct75,
|
||||||
|
name: `${title} pct75`.trim(),
|
||||||
|
color: stat.pct75,
|
||||||
|
unit,
|
||||||
|
}),
|
||||||
|
line({
|
||||||
|
series: pattern.median,
|
||||||
|
name: `${title} median`.trim(),
|
||||||
|
color: stat.median,
|
||||||
|
unit,
|
||||||
|
}),
|
||||||
|
line({
|
||||||
|
series: pattern.pct25,
|
||||||
|
name: `${title} pct25`.trim(),
|
||||||
|
color: stat.pct25,
|
||||||
|
unit,
|
||||||
|
}),
|
||||||
|
line({
|
||||||
|
series: pattern.pct10,
|
||||||
|
name: `${title} pct10`.trim(),
|
||||||
|
color: stat.pct10,
|
||||||
|
unit,
|
||||||
|
}),
|
||||||
|
line({
|
||||||
|
series: pattern.min,
|
||||||
|
name: `${title} min`.trim(),
|
||||||
|
color: stat.min,
|
||||||
|
unit,
|
||||||
|
}),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -407,36 +442,6 @@ export function statsAtWindow(pattern, window) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Rolling folder tree with line series
|
|
||||||
* @param {Object} args
|
|
||||||
* @param {{ _24h: AnySeriesPattern, _1w: AnySeriesPattern, _1m: AnySeriesPattern, _1y: AnySeriesPattern }} args.windows
|
|
||||||
* @param {string} args.title
|
|
||||||
* @param {(w: typeof ROLLING_WINDOWS[number]) => string} args.windowTitle
|
|
||||||
* @param {Unit} args.unit
|
|
||||||
* @param {string} args.name
|
|
||||||
* @returns {PartialOptionsGroup}
|
|
||||||
*/
|
|
||||||
function rollingWindowsTreeLine({ windows, title, windowTitle, unit, name }) {
|
|
||||||
return {
|
|
||||||
name,
|
|
||||||
tree: [
|
|
||||||
{
|
|
||||||
name: "Compare",
|
|
||||||
title,
|
|
||||||
bottom: ROLLING_WINDOWS.map((w) =>
|
|
||||||
line({ series: windows[w.key], name: w.name, color: w.color, unit }),
|
|
||||||
),
|
|
||||||
},
|
|
||||||
...ROLLING_WINDOWS.map((w) => ({
|
|
||||||
name: w.name,
|
|
||||||
title: windowTitle(w),
|
|
||||||
bottom: [line({ series: windows[w.key], name: w.name, unit })],
|
|
||||||
})),
|
|
||||||
],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rolling folder tree with baseline series
|
* Rolling folder tree with baseline series
|
||||||
* @param {Object} args
|
* @param {Object} args
|
||||||
@@ -447,7 +452,13 @@ function rollingWindowsTreeLine({ windows, title, windowTitle, unit, name }) {
|
|||||||
* @param {string} args.name
|
* @param {string} args.name
|
||||||
* @returns {PartialOptionsGroup}
|
* @returns {PartialOptionsGroup}
|
||||||
*/
|
*/
|
||||||
function rollingWindowsTreeBaseline({ windows, title, windowTitle, unit, name }) {
|
function rollingWindowsTreeBaseline({
|
||||||
|
windows,
|
||||||
|
title,
|
||||||
|
windowTitle,
|
||||||
|
unit,
|
||||||
|
name,
|
||||||
|
}) {
|
||||||
return {
|
return {
|
||||||
name,
|
name,
|
||||||
tree: [
|
tree: [
|
||||||
@@ -455,7 +466,12 @@ function rollingWindowsTreeBaseline({ windows, title, windowTitle, unit, name })
|
|||||||
name: "Compare",
|
name: "Compare",
|
||||||
title,
|
title,
|
||||||
bottom: ROLLING_WINDOWS.map((w) =>
|
bottom: ROLLING_WINDOWS.map((w) =>
|
||||||
baseline({ series: windows[w.key], name: w.name, color: w.color, unit }),
|
baseline({
|
||||||
|
series: windows[w.key],
|
||||||
|
name: w.name,
|
||||||
|
color: w.color,
|
||||||
|
unit,
|
||||||
|
}),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
...ROLLING_WINDOWS.map((w) => ({
|
...ROLLING_WINDOWS.map((w) => ({
|
||||||
@@ -467,24 +483,6 @@ function rollingWindowsTreeBaseline({ windows, title, windowTitle, unit, name })
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Flat array of rolling sum charts (one per window)
|
|
||||||
* @param {Object} args
|
|
||||||
* @param {{ _24h: AnySeriesPattern, _1w: AnySeriesPattern, _1m: AnySeriesPattern, _1y: AnySeriesPattern }} args.windows
|
|
||||||
* @param {string} args.title
|
|
||||||
* @param {Unit} args.unit
|
|
||||||
* @returns {PartialChartOption[]}
|
|
||||||
*/
|
|
||||||
export function sumsArray({ windows, title, unit }) {
|
|
||||||
return ROLLING_WINDOWS.map((w) => ({
|
|
||||||
name: w.name,
|
|
||||||
title: `${title} ${w.title} Sum`,
|
|
||||||
bottom: [
|
|
||||||
line({ series: windows[w.key], name: w.name, color: w.color, unit }),
|
|
||||||
],
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generic helper: compare + per-window sum+avg + cumulative.
|
* Generic helper: compare + per-window sum+avg + cumulative.
|
||||||
* @template P
|
* @template P
|
||||||
@@ -492,7 +490,8 @@ export function sumsArray({ windows, title, unit }) {
|
|||||||
* @param {{ _24h: P, _1w: P, _1m: P, _1y: P }} args.sum
|
* @param {{ _24h: P, _1w: P, _1m: P, _1y: P }} args.sum
|
||||||
* @param {{ _24h: P, _1w: P, _1m: P, _1y: P }} args.average
|
* @param {{ _24h: P, _1w: P, _1m: P, _1y: P }} args.average
|
||||||
* @param {P} args.cumulative
|
* @param {P} args.cumulative
|
||||||
* @param {string} args.title
|
* @param {(metric: string) => string} [args.title]
|
||||||
|
* @param {string} args.metric
|
||||||
* @param {Color} [args.color]
|
* @param {Color} [args.color]
|
||||||
* @param {(args: { pattern: P, name: string, color?: Color, defaultActive?: boolean }) => AnyFetchedSeriesBlueprint[]} args.series
|
* @param {(args: { pattern: P, name: string, color?: Color, defaultActive?: boolean }) => AnyFetchedSeriesBlueprint[]} args.series
|
||||||
* @returns {PartialChartOption[]}
|
* @returns {PartialChartOption[]}
|
||||||
@@ -501,14 +500,15 @@ export function sumsAndAveragesCumulativeWith({
|
|||||||
sum,
|
sum,
|
||||||
average,
|
average,
|
||||||
cumulative,
|
cumulative,
|
||||||
title,
|
title = (s) => s,
|
||||||
|
metric,
|
||||||
color,
|
color,
|
||||||
series,
|
series,
|
||||||
}) {
|
}) {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
name: "Compare",
|
name: "Compare",
|
||||||
title: `${title} Averages`,
|
title: title(metric),
|
||||||
bottom: ROLLING_WINDOWS.flatMap((w) =>
|
bottom: ROLLING_WINDOWS.flatMap((w) =>
|
||||||
series({
|
series({
|
||||||
pattern: average[w.key],
|
pattern: average[w.key],
|
||||||
@@ -519,7 +519,7 @@ export function sumsAndAveragesCumulativeWith({
|
|||||||
},
|
},
|
||||||
...ROLLING_WINDOWS.map((w) => ({
|
...ROLLING_WINDOWS.map((w) => ({
|
||||||
name: w.name,
|
name: w.name,
|
||||||
title: `${title} ${w.title}`,
|
title: title(`${w.name} ${metric}`),
|
||||||
bottom: [
|
bottom: [
|
||||||
...series({ pattern: sum[w.key], name: "Sum", color: w.color }),
|
...series({ pattern: sum[w.key], name: "Sum", color: w.color }),
|
||||||
...series({
|
...series({
|
||||||
@@ -532,7 +532,7 @@ export function sumsAndAveragesCumulativeWith({
|
|||||||
})),
|
})),
|
||||||
{
|
{
|
||||||
name: "Cumulative",
|
name: "Cumulative",
|
||||||
title: `${title} (Total)`,
|
title: title(`Cumulative ${metric}`),
|
||||||
bottom: series({ pattern: cumulative, name: "all-time", color }),
|
bottom: series({ pattern: cumulative, name: "all-time", color }),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
@@ -543,14 +543,15 @@ export function sumsAndAveragesCumulativeWith({
|
|||||||
* @param {Object} args
|
* @param {Object} args
|
||||||
* @param {{ _24h: AnySeriesPattern, _1w: AnySeriesPattern, _1m: AnySeriesPattern, _1y: AnySeriesPattern }} args.sum
|
* @param {{ _24h: AnySeriesPattern, _1w: AnySeriesPattern, _1m: AnySeriesPattern, _1y: AnySeriesPattern }} args.sum
|
||||||
* @param {{ _24h: AnySeriesPattern, _1w: AnySeriesPattern, _1m: AnySeriesPattern, _1y: AnySeriesPattern }} args.average
|
* @param {{ _24h: AnySeriesPattern, _1w: AnySeriesPattern, _1m: AnySeriesPattern, _1y: AnySeriesPattern }} args.average
|
||||||
* @param {string} args.title
|
* @param {(metric: string) => string} [args.title]
|
||||||
|
* @param {string} args.metric
|
||||||
* @param {Unit} args.unit
|
* @param {Unit} args.unit
|
||||||
* @returns {PartialChartOption[]}
|
* @returns {PartialChartOption[]}
|
||||||
*/
|
*/
|
||||||
export function sumsAndAveragesArray({ sum, average, title, unit }) {
|
export function sumsAndAveragesArray({ sum, average, title = (s) => s, metric, unit }) {
|
||||||
return ROLLING_WINDOWS.map((w) => ({
|
return ROLLING_WINDOWS.map((w) => ({
|
||||||
name: w.name,
|
name: w.name,
|
||||||
title: `${title} ${w.title}`,
|
title: title(`${w.name} ${metric}`),
|
||||||
bottom: [
|
bottom: [
|
||||||
line({ series: sum[w.key], name: "Sum", color: w.color, unit }),
|
line({ series: sum[w.key], name: "Sum", color: w.color, unit }),
|
||||||
line({
|
line({
|
||||||
@@ -570,17 +571,27 @@ export function sumsAndAveragesArray({ sum, average, title, unit }) {
|
|||||||
* @param {{ _24h: AnySeriesPattern, _1w: AnySeriesPattern, _1m: AnySeriesPattern, _1y: AnySeriesPattern }} args.sum
|
* @param {{ _24h: AnySeriesPattern, _1w: AnySeriesPattern, _1m: AnySeriesPattern, _1y: AnySeriesPattern }} args.sum
|
||||||
* @param {{ _24h: AnySeriesPattern, _1w: AnySeriesPattern, _1m: AnySeriesPattern, _1y: AnySeriesPattern }} args.average
|
* @param {{ _24h: AnySeriesPattern, _1w: AnySeriesPattern, _1m: AnySeriesPattern, _1y: AnySeriesPattern }} args.average
|
||||||
* @param {AnySeriesPattern} args.cumulative
|
* @param {AnySeriesPattern} args.cumulative
|
||||||
* @param {string} args.title
|
* @param {(metric: string) => string} [args.title]
|
||||||
|
* @param {string} args.metric
|
||||||
* @param {Unit} args.unit
|
* @param {Unit} args.unit
|
||||||
* @param {Color} [args.color]
|
* @param {Color} [args.color]
|
||||||
* @returns {PartialChartOption[]}
|
* @returns {PartialChartOption[]}
|
||||||
*/
|
*/
|
||||||
export function sumsAndAveragesCumulative({ sum, average, cumulative, title, unit, color }) {
|
export function sumsAndAveragesCumulative({
|
||||||
|
sum,
|
||||||
|
average,
|
||||||
|
cumulative,
|
||||||
|
title,
|
||||||
|
metric,
|
||||||
|
unit,
|
||||||
|
color,
|
||||||
|
}) {
|
||||||
return sumsAndAveragesCumulativeWith({
|
return sumsAndAveragesCumulativeWith({
|
||||||
sum,
|
sum,
|
||||||
average,
|
average,
|
||||||
cumulative,
|
cumulative,
|
||||||
title,
|
title,
|
||||||
|
metric,
|
||||||
color,
|
color,
|
||||||
series: ({ pattern, name, color, defaultActive }) => [
|
series: ({ pattern, name, color, defaultActive }) => [
|
||||||
line({ series: pattern, name, color, unit, defaultActive }),
|
line({ series: pattern, name, color, unit, defaultActive }),
|
||||||
@@ -589,35 +600,18 @@ export function sumsAndAveragesCumulative({ sum, average, cumulative, title, uni
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rolling sums tree (Compare + individual windows in a folder)
|
|
||||||
* @param {Object} args
|
* @param {Object} args
|
||||||
* @param {{ _24h: AnySeriesPattern, _1w: AnySeriesPattern, _1m: AnySeriesPattern, _1y: AnySeriesPattern }} args.windows
|
* @param {{ _24h: AnySeriesPattern, _1w: AnySeriesPattern, _1m: AnySeriesPattern, _1y: AnySeriesPattern }} args.windows
|
||||||
* @param {string} args.title
|
* @param {(metric: string) => string} [args.title]
|
||||||
|
* @param {string} args.metric
|
||||||
* @param {Unit} args.unit
|
* @param {Unit} args.unit
|
||||||
* @returns {PartialOptionsGroup}
|
* @returns {PartialOptionsGroup}
|
||||||
*/
|
*/
|
||||||
export function sumsTree({ windows, title, unit }) {
|
export function sumsTreeBaseline({ windows, title = (s) => s, metric, unit }) {
|
||||||
return rollingWindowsTreeLine({
|
|
||||||
windows,
|
|
||||||
title,
|
|
||||||
windowTitle: (w) => `${title} ${w.title} Sum`,
|
|
||||||
unit,
|
|
||||||
name: "Sums",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {Object} args
|
|
||||||
* @param {{ _24h: AnySeriesPattern, _1w: AnySeriesPattern, _1m: AnySeriesPattern, _1y: AnySeriesPattern }} args.windows
|
|
||||||
* @param {string} args.title
|
|
||||||
* @param {Unit} args.unit
|
|
||||||
* @returns {PartialOptionsGroup}
|
|
||||||
*/
|
|
||||||
export function sumsTreeBaseline({ windows, title, unit }) {
|
|
||||||
return rollingWindowsTreeBaseline({
|
return rollingWindowsTreeBaseline({
|
||||||
windows,
|
windows,
|
||||||
title,
|
title: title(metric),
|
||||||
windowTitle: (w) => `${title} ${w.title} Sum`,
|
windowTitle: (w) => title(`${w.name} ${metric}`),
|
||||||
unit,
|
unit,
|
||||||
name: "Sums",
|
name: "Sums",
|
||||||
});
|
});
|
||||||
@@ -627,22 +621,23 @@ export function sumsTreeBaseline({ windows, title, unit }) {
|
|||||||
* Flat array of per-window average charts
|
* Flat array of per-window average charts
|
||||||
* @param {Object} args
|
* @param {Object} args
|
||||||
* @param {{ _24h: AnySeriesPattern, _1w: AnySeriesPattern, _1m: AnySeriesPattern, _1y: AnySeriesPattern }} args.windows
|
* @param {{ _24h: AnySeriesPattern, _1w: AnySeriesPattern, _1m: AnySeriesPattern, _1y: AnySeriesPattern }} args.windows
|
||||||
* @param {string} args.title
|
* @param {(metric: string) => string} [args.title]
|
||||||
|
* @param {string} args.metric
|
||||||
* @param {Unit} args.unit
|
* @param {Unit} args.unit
|
||||||
* @returns {PartialChartOption[]}
|
* @returns {PartialChartOption[]}
|
||||||
*/
|
*/
|
||||||
export function averagesArray({ windows, title, unit }) {
|
export function averagesArray({ windows, title = (s) => s, metric, unit }) {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
name: "Compare",
|
name: "Compare",
|
||||||
title: `${title} Averages`,
|
title: title(metric),
|
||||||
bottom: ROLLING_WINDOWS.map((w) =>
|
bottom: ROLLING_WINDOWS.map((w) =>
|
||||||
line({ series: windows[w.key], name: w.name, color: w.color, unit }),
|
line({ series: windows[w.key], name: w.name, color: w.color, unit }),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
...ROLLING_WINDOWS.map((w) => ({
|
...ROLLING_WINDOWS.map((w) => ({
|
||||||
name: w.name,
|
name: w.name,
|
||||||
title: `${title} ${w.title} Average`,
|
title: title(`${w.name} ${metric}`),
|
||||||
bottom: [
|
bottom: [
|
||||||
line({ series: windows[w.key], name: w.name, color: w.color, unit }),
|
line({ series: windows[w.key], name: w.name, color: w.color, unit }),
|
||||||
],
|
],
|
||||||
@@ -655,17 +650,18 @@ export function averagesArray({ windows, title, unit }) {
|
|||||||
* @param {Object} args
|
* @param {Object} args
|
||||||
* @param {Record<string, any>} args.pattern - Pattern with pct10/pct25/... and average/median/... as _1y24h30d7dPattern
|
* @param {Record<string, any>} args.pattern - Pattern with pct10/pct25/... and average/median/... as _1y24h30d7dPattern
|
||||||
* @param {AnySeriesPattern} [args.base] - Optional base series to show as dots on each chart
|
* @param {AnySeriesPattern} [args.base] - Optional base series to show as dots on each chart
|
||||||
* @param {string} args.title
|
* @param {(metric: string) => string} [args.title]
|
||||||
|
* @param {string} args.metric
|
||||||
* @param {Unit} args.unit
|
* @param {Unit} args.unit
|
||||||
* @returns {PartialOptionsGroup}
|
* @returns {PartialOptionsGroup}
|
||||||
*/
|
*/
|
||||||
export function distributionWindowsTree({ pattern, base, title, unit }) {
|
export function distributionWindowsTree({ pattern, base, title = (s) => s, metric, unit }) {
|
||||||
return {
|
return {
|
||||||
name: "Distribution",
|
name: "Distribution",
|
||||||
tree: [
|
tree: [
|
||||||
{
|
{
|
||||||
name: "Compare",
|
name: "Compare",
|
||||||
title: `${title} Median`,
|
title: title(`${metric} Distribution`),
|
||||||
bottom: ROLLING_WINDOWS.map((w) =>
|
bottom: ROLLING_WINDOWS.map((w) =>
|
||||||
line({
|
line({
|
||||||
series: pattern.median[w.key],
|
series: pattern.median[w.key],
|
||||||
@@ -677,7 +673,7 @@ export function distributionWindowsTree({ pattern, base, title, unit }) {
|
|||||||
},
|
},
|
||||||
...ROLLING_WINDOWS.map((w) => ({
|
...ROLLING_WINDOWS.map((w) => ({
|
||||||
name: w.name,
|
name: w.name,
|
||||||
title: `${title} Distribution (${w.title})`,
|
title: title(`${w.name} ${metric} Distribution`),
|
||||||
bottom: [
|
bottom: [
|
||||||
...(base ? [line({ series: base, name: "base", unit })] : []),
|
...(base ? [line({ series: base, name: "base", unit })] : []),
|
||||||
...percentileSeries({ pattern: statsAtWindow(pattern, w.key), unit }),
|
...percentileSeries({ pattern: statsAtWindow(pattern, w.key), unit }),
|
||||||
@@ -847,24 +843,34 @@ export function percentRatioBaseline({ pattern, name, color, defaultActive }) {
|
|||||||
* Rolling folder tree with percentRatio series (colored in compare, plain in individual)
|
* Rolling folder tree with percentRatio series (colored in compare, plain in individual)
|
||||||
* @param {Object} args
|
* @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 {{ _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 {(metric: string) => string} [args.title]
|
||||||
|
* @param {string} args.metric
|
||||||
* @param {string} [args.name]
|
* @param {string} [args.name]
|
||||||
* @returns {PartialOptionsGroup}
|
* @returns {PartialOptionsGroup}
|
||||||
*/
|
*/
|
||||||
export function rollingPercentRatioTree({ windows, title, name = "Sums" }) {
|
export function rollingPercentRatioTree({
|
||||||
|
windows,
|
||||||
|
title = (s) => s,
|
||||||
|
metric,
|
||||||
|
name = "Sums",
|
||||||
|
}) {
|
||||||
return {
|
return {
|
||||||
name,
|
name,
|
||||||
tree: [
|
tree: [
|
||||||
{
|
{
|
||||||
name: "Compare",
|
name: "Compare",
|
||||||
title: `${title} Rolling`,
|
title: title(metric),
|
||||||
bottom: ROLLING_WINDOWS.flatMap((w) =>
|
bottom: ROLLING_WINDOWS.flatMap((w) =>
|
||||||
percentRatio({ 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) => ({
|
...ROLLING_WINDOWS.map((w) => ({
|
||||||
name: w.name,
|
name: w.name,
|
||||||
title: `${title} (${w.title})`,
|
title: title(`${w.name} ${metric}`),
|
||||||
bottom: percentRatioBaseline({ pattern: windows[w.key], name: w.name }),
|
bottom: percentRatioBaseline({ pattern: windows[w.key], name: w.name }),
|
||||||
})),
|
})),
|
||||||
],
|
],
|
||||||
@@ -876,19 +882,20 @@ export function rollingPercentRatioTree({ windows, title, name = "Sums" }) {
|
|||||||
* @template T
|
* @template T
|
||||||
* @param {Object} args
|
* @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 {{ 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 {(metric: string) => string} [args.title]
|
||||||
|
* @param {string} args.metric
|
||||||
* @param {Unit} args.unit
|
* @param {Unit} args.unit
|
||||||
* @param {(v: T) => AnySeriesPattern} args.extract
|
* @param {(v: T) => AnySeriesPattern} args.extract
|
||||||
* @returns {PartialOptionsTree}
|
* @returns {PartialOptionsTree}
|
||||||
*/
|
*/
|
||||||
export function deltaTree({ delta, title, unit, extract }) {
|
export function deltaTree({ delta, title = (s) => s, metric, unit, extract }) {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
name: "Change",
|
name: "Change",
|
||||||
tree: [
|
tree: [
|
||||||
{
|
{
|
||||||
name: "Compare",
|
name: "Compare",
|
||||||
title: `${title} Change`,
|
title: title(`${metric} Change`),
|
||||||
bottom: ROLLING_WINDOWS.map((w) =>
|
bottom: ROLLING_WINDOWS.map((w) =>
|
||||||
baseline({
|
baseline({
|
||||||
series: extract(delta.absolute[w.key]),
|
series: extract(delta.absolute[w.key]),
|
||||||
@@ -900,7 +907,7 @@ export function deltaTree({ delta, title, unit, extract }) {
|
|||||||
},
|
},
|
||||||
...ROLLING_WINDOWS.map((w) => ({
|
...ROLLING_WINDOWS.map((w) => ({
|
||||||
name: w.name,
|
name: w.name,
|
||||||
title: `${title} Change (${w.title})`,
|
title: title(`${w.name} ${metric} Change`),
|
||||||
bottom: [
|
bottom: [
|
||||||
baseline({
|
baseline({
|
||||||
series: extract(delta.absolute[w.key]),
|
series: extract(delta.absolute[w.key]),
|
||||||
@@ -913,7 +920,8 @@ export function deltaTree({ delta, title, unit, extract }) {
|
|||||||
},
|
},
|
||||||
rollingPercentRatioTree({
|
rollingPercentRatioTree({
|
||||||
windows: delta.rate,
|
windows: delta.rate,
|
||||||
title: `${title} Growth Rate`,
|
title,
|
||||||
|
metric: `${metric} Growth Rate`,
|
||||||
name: "Growth Rate",
|
name: "Growth Rate",
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
@@ -923,12 +931,13 @@ export function deltaTree({ delta, title, unit, extract }) {
|
|||||||
* deltaTree where absolute windows are directly AnySeriesPattern (no extract needed)
|
* deltaTree where absolute windows are directly AnySeriesPattern (no extract needed)
|
||||||
* @param {Object} args
|
* @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 {{ 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 {(metric: string) => string} [args.title]
|
||||||
|
* @param {string} args.metric
|
||||||
* @param {Unit} args.unit
|
* @param {Unit} args.unit
|
||||||
* @returns {PartialOptionsTree}
|
* @returns {PartialOptionsTree}
|
||||||
*/
|
*/
|
||||||
export function simpleDeltaTree({ delta, title, unit }) {
|
export function simpleDeltaTree({ delta, title = (s) => s, metric, unit }) {
|
||||||
return deltaTree({ delta, title, unit, extract: (v) => v });
|
return deltaTree({ delta, title, metric, unit, extract: (v) => v });
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@@ -941,29 +950,32 @@ export function simpleDeltaTree({ delta, title, unit }) {
|
|||||||
* Pattern has: .height, .cumulative, .sum (windowed), .average/.pct10/... (windowed, flat)
|
* Pattern has: .height, .cumulative, .sum (windowed), .average/.pct10/... (windowed, flat)
|
||||||
* @param {Object} args
|
* @param {Object} args
|
||||||
* @param {FullPerBlockPattern} args.pattern
|
* @param {FullPerBlockPattern} args.pattern
|
||||||
* @param {string} args.title
|
* @param {(metric: string) => string} [args.title]
|
||||||
|
* @param {string} args.metric
|
||||||
* @param {Unit} args.unit
|
* @param {Unit} args.unit
|
||||||
* @param {string} [args.distributionSuffix]
|
* @param {string} [args.distributionSuffix]
|
||||||
* @returns {PartialOptionsTree}
|
* @returns {PartialOptionsTree}
|
||||||
*/
|
*/
|
||||||
export function chartsFromFull({
|
export function chartsFromFull({
|
||||||
pattern,
|
pattern,
|
||||||
title,
|
title = (s) => s,
|
||||||
|
metric,
|
||||||
unit,
|
unit,
|
||||||
distributionSuffix = "",
|
distributionSuffix = "",
|
||||||
}) {
|
}) {
|
||||||
const distTitle = distributionSuffix
|
const distMetric = distributionSuffix
|
||||||
? `${title} ${distributionSuffix}`
|
? `${metric} ${distributionSuffix}`
|
||||||
: title;
|
: metric;
|
||||||
return [
|
return [
|
||||||
...sumsAndAveragesCumulative({
|
...sumsAndAveragesCumulative({
|
||||||
sum: pattern.sum,
|
sum: pattern.sum,
|
||||||
average: pattern.average,
|
average: pattern.average,
|
||||||
cumulative: pattern.cumulative,
|
cumulative: pattern.cumulative,
|
||||||
title,
|
title,
|
||||||
|
metric,
|
||||||
unit,
|
unit,
|
||||||
}),
|
}),
|
||||||
distributionWindowsTree({ pattern, title: distTitle, unit }),
|
distributionWindowsTree({ pattern, title, metric: distMetric, unit }),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -971,7 +983,8 @@ export function chartsFromFull({
|
|||||||
* Split pattern into 4 charts with "per Block" in distribution title
|
* Split pattern into 4 charts with "per Block" in distribution title
|
||||||
* @param {Object} args
|
* @param {Object} args
|
||||||
* @param {FullPerBlockPattern} args.pattern
|
* @param {FullPerBlockPattern} args.pattern
|
||||||
* @param {string} args.title
|
* @param {(metric: string) => string} [args.title]
|
||||||
|
* @param {string} args.metric
|
||||||
* @param {Unit} args.unit
|
* @param {Unit} args.unit
|
||||||
* @returns {PartialOptionsTree}
|
* @returns {PartialOptionsTree}
|
||||||
*/
|
*/
|
||||||
@@ -982,31 +995,35 @@ export const chartsFromFullPerBlock = (args) =>
|
|||||||
* Split pattern with sum + distribution + cumulative into 3 charts (no base)
|
* Split pattern with sum + distribution + cumulative into 3 charts (no base)
|
||||||
* @param {Object} args
|
* @param {Object} args
|
||||||
* @param {AggregatedPattern} args.pattern
|
* @param {AggregatedPattern} args.pattern
|
||||||
* @param {string} args.title
|
* @param {(metric: string) => string} [args.title]
|
||||||
|
* @param {string} args.metric
|
||||||
* @param {Unit} args.unit
|
* @param {Unit} args.unit
|
||||||
* @param {string} [args.distributionSuffix]
|
* @param {string} [args.distributionSuffix]
|
||||||
* @returns {PartialOptionsTree}
|
* @returns {PartialOptionsTree}
|
||||||
*/
|
*/
|
||||||
export function chartsFromAggregated({
|
export function chartsFromAggregated({
|
||||||
pattern,
|
pattern,
|
||||||
title,
|
title = (s) => s,
|
||||||
|
metric,
|
||||||
unit,
|
unit,
|
||||||
distributionSuffix = "",
|
distributionSuffix = "",
|
||||||
}) {
|
}) {
|
||||||
const distTitle = distributionSuffix
|
const distMetric = distributionSuffix
|
||||||
? `${title} ${distributionSuffix}`
|
? `${metric} ${distributionSuffix}`
|
||||||
: title;
|
: metric;
|
||||||
return [
|
return [
|
||||||
...sumsAndAveragesCumulative({
|
...sumsAndAveragesCumulative({
|
||||||
sum: pattern.rolling.sum,
|
sum: pattern.rolling.sum,
|
||||||
average: pattern.rolling.average,
|
average: pattern.rolling.average,
|
||||||
cumulative: pattern.cumulative,
|
cumulative: pattern.cumulative,
|
||||||
title,
|
title,
|
||||||
|
metric,
|
||||||
unit,
|
unit,
|
||||||
}),
|
}),
|
||||||
distributionWindowsTree({
|
distributionWindowsTree({
|
||||||
pattern: pattern.rolling,
|
pattern: pattern.rolling,
|
||||||
title: distTitle,
|
title,
|
||||||
|
metric: distMetric,
|
||||||
unit,
|
unit,
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
@@ -1016,7 +1033,8 @@ export function chartsFromAggregated({
|
|||||||
* Split pattern into 3 charts with "per Block" in distribution title (no base)
|
* Split pattern into 3 charts with "per Block" in distribution title (no base)
|
||||||
* @param {Object} args
|
* @param {Object} args
|
||||||
* @param {AggregatedPattern} args.pattern
|
* @param {AggregatedPattern} args.pattern
|
||||||
* @param {string} args.title
|
* @param {(metric: string) => string} [args.title]
|
||||||
|
* @param {string} args.metric
|
||||||
* @param {Unit} args.unit
|
* @param {Unit} args.unit
|
||||||
* @returns {PartialOptionsTree}
|
* @returns {PartialOptionsTree}
|
||||||
*/
|
*/
|
||||||
@@ -1027,20 +1045,21 @@ export const chartsFromAggregatedPerBlock = (args) =>
|
|||||||
* Create Per Block + Per 6 Blocks stats charts from a _6bBlockTxPattern
|
* Create Per Block + Per 6 Blocks stats charts from a _6bBlockTxPattern
|
||||||
* @param {Object} args
|
* @param {Object} args
|
||||||
* @param {{ block: DistributionStats, _6b: DistributionStats }} args.pattern
|
* @param {{ block: DistributionStats, _6b: DistributionStats }} args.pattern
|
||||||
* @param {string} args.title
|
* @param {(metric: string) => string} [args.title]
|
||||||
|
* @param {string} args.metric
|
||||||
* @param {Unit} args.unit
|
* @param {Unit} args.unit
|
||||||
* @returns {PartialOptionsTree}
|
* @returns {PartialOptionsTree}
|
||||||
*/
|
*/
|
||||||
export function chartsFromBlockAnd6b({ pattern, title, unit }) {
|
export function chartsFromBlockAnd6b({ pattern, title = (s) => s, metric, unit }) {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
name: "Block",
|
name: "Block",
|
||||||
title: `${title} (Block)`,
|
title: title(`${metric} (Block)`),
|
||||||
bottom: percentileSeries({ pattern: pattern.block, unit }),
|
bottom: percentileSeries({ pattern: pattern.block, unit }),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Hourly",
|
name: "~Hourly",
|
||||||
title: `${title} (Hourly)`,
|
title: title(`${metric} (~Hourly)`),
|
||||||
bottom: percentileSeries({ pattern: pattern._6b, unit }),
|
bottom: percentileSeries({ pattern: pattern._6b, unit }),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
@@ -1050,17 +1069,19 @@ export function chartsFromBlockAnd6b({ pattern, title, unit }) {
|
|||||||
* Averages + Sums + Cumulative charts
|
* Averages + Sums + Cumulative charts
|
||||||
* @param {Object} args
|
* @param {Object} args
|
||||||
* @param {CountPattern<any>} args.pattern
|
* @param {CountPattern<any>} args.pattern
|
||||||
* @param {string} args.title
|
* @param {(metric: string) => string} [args.title]
|
||||||
|
* @param {string} args.metric
|
||||||
* @param {Unit} args.unit
|
* @param {Unit} args.unit
|
||||||
* @param {Color} [args.color]
|
* @param {Color} [args.color]
|
||||||
* @returns {PartialOptionsTree}
|
* @returns {PartialOptionsTree}
|
||||||
*/
|
*/
|
||||||
export function chartsFromCount({ pattern, title, unit, color }) {
|
export function chartsFromCount({ pattern, title = (s) => s, metric, unit, color }) {
|
||||||
return sumsAndAveragesCumulative({
|
return sumsAndAveragesCumulative({
|
||||||
sum: pattern.sum,
|
sum: pattern.sum,
|
||||||
average: pattern.average,
|
average: pattern.average,
|
||||||
cumulative: pattern.cumulative,
|
cumulative: pattern.cumulative,
|
||||||
title,
|
title,
|
||||||
|
metric,
|
||||||
unit,
|
unit,
|
||||||
color,
|
color,
|
||||||
});
|
});
|
||||||
@@ -1070,19 +1091,12 @@ export function chartsFromCount({ pattern, title, unit, color }) {
|
|||||||
* Windowed sums + cumulative for multiple named entries (e.g. transaction versions)
|
* Windowed sums + cumulative for multiple named entries (e.g. transaction versions)
|
||||||
* @param {Object} args
|
* @param {Object} args
|
||||||
* @param {Array<[string, CountPattern<any>]>} args.entries
|
* @param {Array<[string, CountPattern<any>]>} args.entries
|
||||||
* @param {string} args.title
|
* @param {(metric: string) => string} [args.title]
|
||||||
|
* @param {string} args.metric
|
||||||
* @param {Unit} args.unit
|
* @param {Unit} args.unit
|
||||||
* @returns {PartialOptionsTree}
|
* @returns {PartialOptionsTree}
|
||||||
*/
|
*/
|
||||||
/**
|
export function chartsFromCountEntries({ entries, title = (s) => s, metric, unit }) {
|
||||||
* Windowed sums + cumulative for multiple named entries (e.g. transaction versions)
|
|
||||||
* @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 }) {
|
|
||||||
const items = entries.map(([name, data], i, arr) => ({
|
const items = entries.map(([name, data], i, arr) => ({
|
||||||
name,
|
name,
|
||||||
color: colors.at(i, arr.length),
|
color: colors.at(i, arr.length),
|
||||||
@@ -1092,14 +1106,14 @@ export function chartsFromCountEntries({ entries, title, unit }) {
|
|||||||
return [
|
return [
|
||||||
...ROLLING_WINDOWS.map((w) => ({
|
...ROLLING_WINDOWS.map((w) => ({
|
||||||
name: w.name,
|
name: w.name,
|
||||||
title: `${title} ${w.title} Sum`,
|
title: title(`${w.name} ${metric}`),
|
||||||
bottom: items.map((e) =>
|
bottom: items.map((e) =>
|
||||||
line({ series: e.sum[w.key], name: e.name, color: e.color, unit }),
|
line({ series: e.sum[w.key], name: e.name, color: e.color, unit }),
|
||||||
),
|
),
|
||||||
})),
|
})),
|
||||||
{
|
{
|
||||||
name: "Cumulative",
|
name: "Cumulative",
|
||||||
title: `${title} (Total)`,
|
title: title(`Cumulative ${metric}`),
|
||||||
bottom: items.map((e) =>
|
bottom: items.map((e) =>
|
||||||
line({ series: e.cumulative, name: e.name, color: e.color, unit }),
|
line({ series: e.cumulative, name: e.name, color: e.color, unit }),
|
||||||
),
|
),
|
||||||
@@ -1111,26 +1125,26 @@ export function chartsFromCountEntries({ entries, title, unit }) {
|
|||||||
* Windowed averages + sums + cumulative for multiple named series (e.g. UTXO flow)
|
* Windowed averages + sums + cumulative for multiple named series (e.g. UTXO flow)
|
||||||
* @param {Object} args
|
* @param {Object} args
|
||||||
* @param {Array<{ name: string, color: Color, average: { _24h: AnySeriesPattern, _1w: AnySeriesPattern, _1m: AnySeriesPattern, _1y: AnySeriesPattern }, sum: { _24h: AnySeriesPattern, _1w: AnySeriesPattern, _1m: AnySeriesPattern, _1y: AnySeriesPattern }, cumulative: AnySeriesPattern }>} args.entries
|
* @param {Array<{ name: string, color: Color, average: { _24h: AnySeriesPattern, _1w: AnySeriesPattern, _1m: AnySeriesPattern, _1y: AnySeriesPattern }, sum: { _24h: AnySeriesPattern, _1w: AnySeriesPattern, _1m: AnySeriesPattern, _1y: AnySeriesPattern }, cumulative: AnySeriesPattern }>} args.entries
|
||||||
* @param {string} args.title
|
* @param {(metric: string) => string} [args.title]
|
||||||
|
* @param {string} args.metric
|
||||||
* @param {Unit} args.unit
|
* @param {Unit} args.unit
|
||||||
* @returns {PartialOptionsTree}
|
* @returns {PartialOptionsTree}
|
||||||
*/
|
*/
|
||||||
export function multiSeriesTree({ entries, title, unit }) {
|
export function multiSeriesTree({ entries, title = (s) => s, metric, unit }) {
|
||||||
return [
|
return [
|
||||||
...ROLLING_WINDOWS.map((w) => ({
|
...ROLLING_WINDOWS.map((w) => ({
|
||||||
name: w.name,
|
name: w.name,
|
||||||
title: `${title} ${w.title} Averages`,
|
title: title(`${w.name} ${metric}`),
|
||||||
bottom: entries.map((e) =>
|
bottom: entries.map((e) =>
|
||||||
line({ series: e.average[w.key], name: e.name, color: e.color, unit }),
|
line({ series: e.average[w.key], name: e.name, color: e.color, unit }),
|
||||||
),
|
),
|
||||||
})),
|
})),
|
||||||
{
|
{
|
||||||
name: "Cumulative",
|
name: "Cumulative",
|
||||||
title: `${title} (Total)`,
|
title: title(`Cumulative ${metric}`),
|
||||||
bottom: entries.map((e) =>
|
bottom: entries.map((e) =>
|
||||||
line({ series: e.cumulative, name: e.name, color: e.color, unit }),
|
line({ series: e.cumulative, name: e.name, color: e.color, unit }),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -219,16 +219,18 @@ export function satsBtcUsdRolling({ pattern, name, color, defaultActive }) {
|
|||||||
* Build a full Sum / Rolling / Cumulative tree from a FullValuePattern
|
* Build a full Sum / Rolling / Cumulative tree from a FullValuePattern
|
||||||
* @param {Object} args
|
* @param {Object} args
|
||||||
* @param {FullValuePattern} args.pattern
|
* @param {FullValuePattern} args.pattern
|
||||||
* @param {string} args.title
|
* @param {(metric: string) => string} [args.title]
|
||||||
|
* @param {string} args.metric
|
||||||
* @param {Color} [args.color]
|
* @param {Color} [args.color]
|
||||||
* @returns {PartialOptionsTree}
|
* @returns {PartialOptionsTree}
|
||||||
*/
|
*/
|
||||||
export function satsBtcUsdFullTree({ pattern, title, color }) {
|
export function satsBtcUsdFullTree({ pattern, title, metric, color }) {
|
||||||
return sumsAndAveragesCumulativeWith({
|
return sumsAndAveragesCumulativeWith({
|
||||||
sum: pattern.sum,
|
sum: pattern.sum,
|
||||||
average: pattern.average,
|
average: pattern.average,
|
||||||
cumulative: pattern.cumulative,
|
cumulative: pattern.cumulative,
|
||||||
title,
|
title,
|
||||||
|
metric,
|
||||||
color,
|
color,
|
||||||
series: ({ pattern, name, color, defaultActive }) =>
|
series: ({ pattern, name, color, defaultActive }) =>
|
||||||
satsBtcUsd({ pattern, name, color, defaultActive }),
|
satsBtcUsd({ pattern, name, color, defaultActive }),
|
||||||
|
|||||||
Reference in New Issue
Block a user