global: sats version of all prices

This commit is contained in:
nym21
2026-01-26 15:04:45 +01:00
parent f066fcda32
commit 3d01822d27
53 changed files with 2843 additions and 1688 deletions
File diff suppressed because it is too large Load Diff
@@ -5,7 +5,7 @@ use vecdb::Database;
use super::Vecs; use super::Vecs;
use crate::{ use crate::{
indexes, indexes,
internal::{ComputedFromHeightLast, ComputedFromDateRatio}, internal::{ComputedFromDateRatio, PriceFromHeight},
price, price,
}; };
@@ -16,41 +16,61 @@ impl Vecs {
indexes: &indexes::Vecs, indexes: &indexes::Vecs,
price: Option<&price::Vecs>, price: Option<&price::Vecs>,
) -> Result<Self> { ) -> Result<Self> {
macro_rules! computed_h { let vaulted_price = PriceFromHeight::forced_import(db, "vaulted_price", version, indexes)?;
($name:expr) => { let vaulted_price_ratio = ComputedFromDateRatio::forced_import(
ComputedFromHeightLast::forced_import(db, $name, version, indexes)? db,
}; "vaulted_price",
} Some(&vaulted_price),
version,
indexes,
true,
price,
)?;
// Extract price vecs before struct literal so they can be used as sources for ratios let active_price = PriceFromHeight::forced_import(db, "active_price", version, indexes)?;
let vaulted_price = computed_h!("vaulted_price"); let active_price_ratio = ComputedFromDateRatio::forced_import(
let active_price = computed_h!("active_price"); db,
let true_market_mean = computed_h!("true_market_mean"); "active_price",
let cointime_price = computed_h!("cointime_price"); Some(&active_price),
version,
indexes,
true,
price,
)?;
macro_rules! ratio_di { let true_market_mean =
($name:expr, $source:expr) => { PriceFromHeight::forced_import(db, "true_market_mean", version, indexes)?;
ComputedFromDateRatio::forced_import( let true_market_mean_ratio = ComputedFromDateRatio::forced_import(
db, db,
$name, "true_market_mean",
Some($source), Some(&true_market_mean),
version, version,
indexes, indexes,
true, true,
price, price,
)? )?;
};
} let cointime_price =
PriceFromHeight::forced_import(db, "cointime_price", version, indexes)?;
let cointime_price_ratio = ComputedFromDateRatio::forced_import(
db,
"cointime_price",
Some(&cointime_price),
version,
indexes,
true,
price,
)?;
Ok(Self { Ok(Self {
vaulted_price_ratio: ratio_di!("vaulted_price", &vaulted_price),
vaulted_price, vaulted_price,
active_price_ratio: ratio_di!("active_price", &active_price), vaulted_price_ratio,
active_price, active_price,
true_market_mean_ratio: ratio_di!("true_market_mean", &true_market_mean), active_price_ratio,
true_market_mean, true_market_mean,
cointime_price_ratio: ratio_di!("cointime_price", &cointime_price), true_market_mean_ratio,
cointime_price, cointime_price,
cointime_price_ratio,
}) })
} }
} }
@@ -1,16 +1,15 @@
use brk_traversable::Traversable; use brk_traversable::Traversable;
use brk_types::Dollars;
use crate::internal::{ComputedFromHeightLast, ComputedFromDateRatio}; use crate::internal::{ComputedFromDateRatio, PriceFromHeight};
#[derive(Clone, Traversable)] #[derive(Clone, Traversable)]
pub struct Vecs { pub struct Vecs {
pub vaulted_price: ComputedFromHeightLast<Dollars>, pub vaulted_price: PriceFromHeight,
pub vaulted_price_ratio: ComputedFromDateRatio, pub vaulted_price_ratio: ComputedFromDateRatio,
pub active_price: ComputedFromHeightLast<Dollars>, pub active_price: PriceFromHeight,
pub active_price_ratio: ComputedFromDateRatio, pub active_price_ratio: ComputedFromDateRatio,
pub true_market_mean: ComputedFromHeightLast<Dollars>, pub true_market_mean: PriceFromHeight,
pub true_market_mean_ratio: ComputedFromDateRatio, pub true_market_mean_ratio: ComputedFromDateRatio,
pub cointime_price: ComputedFromHeightLast<Dollars>, pub cointime_price: PriceFromHeight,
pub cointime_price_ratio: ComputedFromDateRatio, pub cointime_price_ratio: ComputedFromDateRatio,
} }
@@ -8,7 +8,7 @@ use crate::{
ComputeIndexes, ComputeIndexes,
distribution::state::CohortState, distribution::state::CohortState,
indexes, indexes,
internal::{ComputedFromHeightLast, CostBasisPercentiles}, internal::{CostBasisPercentiles, PriceFromHeight},
}; };
use super::ImportConfig; use super::ImportConfig;
@@ -17,10 +17,10 @@ use super::ImportConfig;
#[derive(Clone, Traversable)] #[derive(Clone, Traversable)]
pub struct CostBasisMetrics { pub struct CostBasisMetrics {
/// Minimum cost basis for any UTXO at this height /// Minimum cost basis for any UTXO at this height
pub min: ComputedFromHeightLast<Dollars>, pub min: PriceFromHeight,
/// Maximum cost basis for any UTXO at this height /// Maximum cost basis for any UTXO at this height
pub max: ComputedFromHeightLast<Dollars>, pub max: PriceFromHeight,
/// Cost basis distribution percentiles (median, quartiles, etc.) /// Cost basis distribution percentiles (median, quartiles, etc.)
pub percentiles: Option<CostBasisPercentiles>, pub percentiles: Option<CostBasisPercentiles>,
@@ -32,13 +32,13 @@ impl CostBasisMetrics {
let extended = cfg.extended(); let extended = cfg.extended();
Ok(Self { Ok(Self {
min: ComputedFromHeightLast::forced_import( min: PriceFromHeight::forced_import(
cfg.db, cfg.db,
&cfg.name("min_cost_basis"), &cfg.name("min_cost_basis"),
cfg.version, cfg.version,
cfg.indexes, cfg.indexes,
)?, )?,
max: ComputedFromHeightLast::forced_import( max: PriceFromHeight::forced_import(
cfg.db, cfg.db,
&cfg.name("max_cost_basis"), &cfg.name("max_cost_basis"),
cfg.version, cfg.version,
@@ -14,7 +14,8 @@ use crate::{
internal::{ internal::{
ComputedFromHeightLast, ComputedFromHeightSum, ComputedFromHeightSumCum, ComputedFromDateLast, ComputedFromHeightLast, ComputedFromHeightSum, ComputedFromHeightSumCum, ComputedFromDateLast,
ComputedFromDateRatio, DollarsMinus, LazyBinaryFromHeightSum, LazyBinaryFromHeightSumCum, ComputedFromDateRatio, DollarsMinus, LazyBinaryFromHeightSum, LazyBinaryFromHeightSumCum,
LazyFromHeightSum, LazyFromHeightSumCum, LazyFromDateLast, PercentageDollarsF32, StoredF32Identity, LazyFromHeightSum, LazyFromHeightSumCum, LazyFromDateLast, PercentageDollarsF32,
PriceFromHeight, StoredF32Identity,
}, },
price, price,
}; };
@@ -26,7 +27,7 @@ use super::ImportConfig;
pub struct RealizedMetrics { pub struct RealizedMetrics {
// === Realized Cap === // === Realized Cap ===
pub realized_cap: ComputedFromHeightLast<Dollars>, pub realized_cap: ComputedFromHeightLast<Dollars>,
pub realized_price: ComputedFromHeightLast<Dollars>, pub realized_price: PriceFromHeight,
pub realized_price_extra: ComputedFromDateRatio, pub realized_price_extra: ComputedFromDateRatio,
pub realized_cap_rel_to_own_market_cap: Option<ComputedFromHeightLast<StoredF32>>, pub realized_cap_rel_to_own_market_cap: Option<ComputedFromHeightLast<StoredF32>>,
pub realized_cap_30d_delta: ComputedFromDateLast<Dollars>, pub realized_cap_30d_delta: ComputedFromDateLast<Dollars>,
@@ -169,7 +170,7 @@ impl RealizedMetrics {
&realized_cap, &realized_cap,
); );
let realized_price = ComputedFromHeightLast::forced_import( let realized_price = PriceFromHeight::forced_import(
cfg.db, cfg.db,
&cfg.name("realized_price"), &cfg.name("realized_price"),
cfg.version + v1, cfg.version + v1,
@@ -0,0 +1,135 @@
//! Lazy binary price wrapper with both USD and sats representations.
//!
//! For binary operations (e.g., price × ratio) that produce price values.
//! Both dollars and sats are computed lazily from the same sources.
use brk_traversable::Traversable;
use brk_types::{Dollars, SatsFract, Version};
use derive_more::{Deref, DerefMut};
use schemars::JsonSchema;
use vecdb::{BinaryTransform, IterableCloneableVec, LazyVecFrom1};
use super::{ComputedFromDateLast, LazyBinaryFromDateLast};
use crate::internal::{ComputedFromHeightLast, ComputedVecValue, DollarsToSatsFract, LazyTransformLast, NumericValue};
/// Lazy binary price with both USD and sats representations.
///
/// Wraps a binary operation that produces Dollars and lazily converts to sats.
/// Derefs to the dollars metric.
#[derive(Clone, Deref, DerefMut, Traversable)]
#[traversable(merge)]
pub struct LazyBinaryPrice<S1T, S2T>
where
S1T: ComputedVecValue,
S2T: ComputedVecValue,
{
#[deref]
#[deref_mut]
#[traversable(flatten)]
pub dollars: LazyBinaryFromDateLast<Dollars, S1T, S2T>,
pub sats: LazyUnaryFromBinaryLast<SatsFract, Dollars, S1T, S2T>,
}
/// Lazy unary transform chain on a LazyBinaryFromDateLast output.
#[derive(Clone, Traversable)]
#[traversable(merge)]
pub struct LazyUnaryFromBinaryLast<T, ST, S1T, S2T>
where
T: ComputedVecValue + PartialOrd + JsonSchema,
ST: ComputedVecValue,
S1T: ComputedVecValue,
S2T: ComputedVecValue,
{
pub dateindex: LazyVecFrom1<brk_types::DateIndex, T, brk_types::DateIndex, ST>,
pub weekindex: LazyTransformLast<brk_types::WeekIndex, T, ST>,
pub monthindex: LazyTransformLast<brk_types::MonthIndex, T, ST>,
pub quarterindex: LazyTransformLast<brk_types::QuarterIndex, T, ST>,
pub semesterindex: LazyTransformLast<brk_types::SemesterIndex, T, ST>,
pub yearindex: LazyTransformLast<brk_types::YearIndex, T, ST>,
pub decadeindex: LazyTransformLast<brk_types::DecadeIndex, T, ST>,
_phantom: std::marker::PhantomData<(S1T, S2T)>,
}
impl<S1T, S2T> LazyBinaryPrice<S1T, S2T>
where
S1T: ComputedVecValue + JsonSchema + 'static,
S2T: ComputedVecValue + JsonSchema + 'static,
{
/// Create from height-based price and dateindex-based ratio sources.
pub fn from_height_and_dateindex_last<F: BinaryTransform<S1T, S2T, Dollars>>(
name: &str,
version: Version,
source1: &ComputedFromHeightLast<S1T>,
source2: &ComputedFromDateLast<S2T>,
) -> Self
where
S1T: NumericValue,
{
let dollars = LazyBinaryFromDateLast::from_height_and_dateindex_last::<F>(
name, version, source1, source2,
);
Self::from_dollars(name, version, dollars)
}
/// Create from two computed dateindex sources.
pub fn from_computed_both_last<F: BinaryTransform<S1T, S2T, Dollars>>(
name: &str,
version: Version,
source1: &ComputedFromDateLast<S1T>,
source2: &ComputedFromDateLast<S2T>,
) -> Self {
let dollars = LazyBinaryFromDateLast::from_computed_both_last::<F>(
name, version, source1, source2,
);
Self::from_dollars(name, version, dollars)
}
/// Create sats version from dollars.
fn from_dollars(
name: &str,
version: Version,
dollars: LazyBinaryFromDateLast<Dollars, S1T, S2T>,
) -> Self {
let sats_name = format!("{name}_sats");
let sats = LazyUnaryFromBinaryLast {
dateindex: LazyVecFrom1::transformed::<DollarsToSatsFract>(
&sats_name,
version,
dollars.dateindex.boxed_clone(),
),
weekindex: LazyTransformLast::from_boxed::<DollarsToSatsFract>(
&sats_name,
version,
dollars.weekindex.boxed_clone(),
),
monthindex: LazyTransformLast::from_boxed::<DollarsToSatsFract>(
&sats_name,
version,
dollars.monthindex.boxed_clone(),
),
quarterindex: LazyTransformLast::from_boxed::<DollarsToSatsFract>(
&sats_name,
version,
dollars.quarterindex.boxed_clone(),
),
semesterindex: LazyTransformLast::from_boxed::<DollarsToSatsFract>(
&sats_name,
version,
dollars.semesterindex.boxed_clone(),
),
yearindex: LazyTransformLast::from_boxed::<DollarsToSatsFract>(
&sats_name,
version,
dollars.yearindex.boxed_clone(),
),
decadeindex: LazyTransformLast::from_boxed::<DollarsToSatsFract>(
&sats_name,
version,
dollars.decadeindex.boxed_clone(),
),
_phantom: std::marker::PhantomData,
};
Self { dollars, sats }
}
}
@@ -0,0 +1,60 @@
//! Lazy price wrapper with both USD and sats representations.
//!
//! Both dollars and sats are computed from the same source.
use brk_traversable::Traversable;
use brk_types::{Dollars, SatsFract, Version};
use derive_more::{Deref, DerefMut};
use vecdb::UnaryTransform;
use super::{ComputedFromDateLast, LazyFromDateLast};
use crate::internal::{ComputedVecValue, DollarsToSatsFract};
/// Lazy price with both USD and sats representations.
///
/// Both are computed from the same source via separate transforms.
/// Derefs to the dollars metric.
#[derive(Clone, Deref, DerefMut, Traversable)]
#[traversable(merge)]
pub struct LazyPrice<ST>
where
ST: ComputedVecValue,
{
#[deref]
#[deref_mut]
#[traversable(flatten)]
pub dollars: LazyFromDateLast<Dollars, ST>,
pub sats: LazyFromDateLast<SatsFract, ST>,
}
/// Composed transform: ST -> Dollars -> SatsFract
pub struct ComposedDollarsToSatsFract<F>(std::marker::PhantomData<F>);
impl<F, ST> UnaryTransform<ST, SatsFract> for ComposedDollarsToSatsFract<F>
where
F: UnaryTransform<ST, Dollars>,
{
#[inline(always)]
fn apply(source: ST) -> SatsFract {
DollarsToSatsFract::apply(F::apply(source))
}
}
impl<ST> LazyPrice<ST>
where
ST: ComputedVecValue + schemars::JsonSchema + 'static,
{
pub fn from_source<F: UnaryTransform<ST, Dollars>>(
name: &str,
version: Version,
source: &ComputedFromDateLast<ST>,
) -> Self {
let dollars = LazyFromDateLast::from_source::<F>(name, version, source);
let sats = LazyFromDateLast::from_source::<ComposedDollarsToSatsFract<F>>(
&format!("{name}_sats"),
version,
source,
);
Self { dollars, sats }
}
}
@@ -5,16 +5,20 @@ mod binary_sum_cum;
mod first; mod first;
mod last; mod last;
mod lazy; mod lazy;
mod lazy_binary_price;
mod lazy_distribution; mod lazy_distribution;
mod lazy_full; mod lazy_full;
mod lazy_last; mod lazy_last;
mod lazy_price;
mod lazy_sum; mod lazy_sum;
mod lazy_sum_cum; mod lazy_sum_cum;
mod max; mod max;
mod min; mod min;
mod percentiles; mod percentiles;
mod price;
mod ratio; mod ratio;
mod stddev; mod stddev;
mod unary_last;
mod value_derived_last; mod value_derived_last;
mod value_last; mod value_last;
mod value_lazy_last; mod value_lazy_last;
@@ -26,16 +30,20 @@ pub use binary_sum_cum::*;
pub use first::*; pub use first::*;
pub use last::*; pub use last::*;
pub use lazy::*; pub use lazy::*;
pub use lazy_binary_price::*;
pub use lazy_distribution::*; pub use lazy_distribution::*;
pub use lazy_full::*; pub use lazy_full::*;
pub use lazy_last::*; pub use lazy_last::*;
pub use lazy_price::*;
pub use lazy_sum::*; pub use lazy_sum::*;
pub use lazy_sum_cum::*; pub use lazy_sum_cum::*;
pub use max::*; pub use max::*;
pub use min::*; pub use min::*;
pub use percentiles::*; pub use percentiles::*;
pub use price::*;
pub use ratio::*; pub use ratio::*;
pub use stddev::*; pub use stddev::*;
pub use unary_last::*;
pub use value_derived_last::*; pub use value_derived_last::*;
pub use value_last::*; pub use value_last::*;
pub use value_lazy_last::*; pub use value_lazy_last::*;
@@ -8,7 +8,7 @@ use vecdb::{
use crate::{ComputeIndexes, indexes}; use crate::{ComputeIndexes, indexes};
use super::ComputedFromDateLast; use super::Price;
pub const PERCENTILES: [u8; 19] = [ pub const PERCENTILES: [u8; 19] = [
5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95,
@@ -17,7 +17,7 @@ pub const PERCENTILES_LEN: usize = PERCENTILES.len();
#[derive(Clone)] #[derive(Clone)]
pub struct CostBasisPercentiles { pub struct CostBasisPercentiles {
pub vecs: [Option<ComputedFromDateLast<Dollars>>; PERCENTILES_LEN], pub vecs: [Option<Price>; PERCENTILES_LEN],
} }
const VERSION: Version = Version::ZERO; const VERSION: Version = Version::ZERO;
@@ -37,8 +37,7 @@ impl CostBasisPercentiles {
} else { } else {
format!("{name}_cost_basis_pct{p:02}") format!("{name}_cost_basis_pct{p:02}")
}; };
ComputedFromDateLast::forced_import(db, &metric_name, version + VERSION, indexes) Price::forced_import(db, &metric_name, version + VERSION, indexes).unwrap()
.unwrap()
}) })
}); });
@@ -81,7 +80,7 @@ impl CostBasisPercentiles {
Ok(()) Ok(())
} }
pub fn get(&self, percentile: u8) -> Option<&ComputedFromDateLast<Dollars>> { pub fn get(&self, percentile: u8) -> Option<&Price> {
PERCENTILES PERCENTILES
.iter() .iter()
.position(|&p| p == percentile) .position(|&p| p == percentile)
@@ -0,0 +1,48 @@
//! Price wrapper that provides both USD and sats representations.
//!
//! The struct derefs to dollars, making it transparent for existing code.
//! Access `.sats` for the sats representation.
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Dollars, SatsFract, Version};
use derive_more::{Deref, DerefMut};
use vecdb::Database;
use super::{ComputedFromDateLast, LazyUnaryFromDateLast};
use crate::{indexes, internal::DollarsToSatsFract};
/// Price metric with both USD and sats representations.
///
/// Derefs to the dollars metric, so existing code works unchanged.
/// Access `.sats` for the sats exchange rate version.
#[derive(Clone, Deref, DerefMut, Traversable)]
#[traversable(merge)]
pub struct Price {
#[deref]
#[deref_mut]
#[traversable(flatten)]
pub dollars: ComputedFromDateLast<Dollars>,
pub sats: LazyUnaryFromDateLast<SatsFract, Dollars>,
}
impl Price {
pub fn forced_import(
db: &Database,
name: &str,
version: Version,
indexes: &indexes::Vecs,
) -> Result<Self> {
let dollars = ComputedFromDateLast::forced_import(db, name, version, indexes)?;
Ok(Self::from_computed(name, version, dollars))
}
pub fn from_computed(name: &str, version: Version, dollars: ComputedFromDateLast<Dollars>) -> Self {
let sats = LazyUnaryFromDateLast::from_computed_last::<DollarsToSatsFract>(
&format!("{name}_sats"),
version,
&dollars,
);
Self { dollars, sats }
}
}
@@ -9,19 +9,19 @@ use vecdb::{
use crate::{ use crate::{
ComputeIndexes, indexes, ComputeIndexes, indexes,
internal::{ internal::{
ComputedFromDateStdDev, LazyBinaryFromDateLast, PriceTimesRatio, ComputedFromDateStdDev, LazyBinaryPrice, PriceTimesRatio,
StandardDeviationVecsOptions, StandardDeviationVecsOptions,
}, },
price, price,
utils::get_percentile, utils::get_percentile,
}; };
use super::ComputedFromDateLast; use super::{ComputedFromDateLast, Price};
use crate::internal::ComputedFromHeightLast; use crate::internal::ComputedFromHeightLast;
#[derive(Clone, Traversable)] #[derive(Clone, Traversable)]
pub struct ComputedFromDateRatio { pub struct ComputedFromDateRatio {
pub price: Option<ComputedFromDateLast<Dollars>>, pub price: Option<Price>,
pub ratio: ComputedFromDateLast<StoredF32>, pub ratio: ComputedFromDateLast<StoredF32>,
pub ratio_1w_sma: Option<ComputedFromDateLast<StoredF32>>, pub ratio_1w_sma: Option<ComputedFromDateLast<StoredF32>>,
@@ -32,12 +32,12 @@ pub struct ComputedFromDateRatio {
pub ratio_pct5: Option<ComputedFromDateLast<StoredF32>>, pub ratio_pct5: Option<ComputedFromDateLast<StoredF32>>,
pub ratio_pct2: Option<ComputedFromDateLast<StoredF32>>, pub ratio_pct2: Option<ComputedFromDateLast<StoredF32>>,
pub ratio_pct1: Option<ComputedFromDateLast<StoredF32>>, pub ratio_pct1: Option<ComputedFromDateLast<StoredF32>>,
pub ratio_pct99_usd: Option<LazyBinaryFromDateLast<Dollars, Dollars, StoredF32>>, pub ratio_pct99_usd: Option<LazyBinaryPrice<Dollars, StoredF32>>,
pub ratio_pct98_usd: Option<LazyBinaryFromDateLast<Dollars, Dollars, StoredF32>>, pub ratio_pct98_usd: Option<LazyBinaryPrice<Dollars, StoredF32>>,
pub ratio_pct95_usd: Option<LazyBinaryFromDateLast<Dollars, Dollars, StoredF32>>, pub ratio_pct95_usd: Option<LazyBinaryPrice<Dollars, StoredF32>>,
pub ratio_pct5_usd: Option<LazyBinaryFromDateLast<Dollars, Dollars, StoredF32>>, pub ratio_pct5_usd: Option<LazyBinaryPrice<Dollars, StoredF32>>,
pub ratio_pct2_usd: Option<LazyBinaryFromDateLast<Dollars, Dollars, StoredF32>>, pub ratio_pct2_usd: Option<LazyBinaryPrice<Dollars, StoredF32>>,
pub ratio_pct1_usd: Option<LazyBinaryFromDateLast<Dollars, Dollars, StoredF32>>, pub ratio_pct1_usd: Option<LazyBinaryPrice<Dollars, StoredF32>>,
pub ratio_sd: Option<ComputedFromDateStdDev>, pub ratio_sd: Option<ComputedFromDateStdDev>,
pub ratio_4y_sd: Option<ComputedFromDateStdDev>, pub ratio_4y_sd: Option<ComputedFromDateStdDev>,
@@ -70,7 +70,7 @@ impl ComputedFromDateRatio {
// Only compute internally when metric_price is None // Only compute internally when metric_price is None
let price = metric_price let price = metric_price
.is_none() .is_none()
.then(|| ComputedFromDateLast::forced_import(db, name, v, indexes).unwrap()); .then(|| Price::forced_import(db, name, v, indexes).unwrap());
macro_rules! import_sd { macro_rules! import_sd {
($suffix:expr, $days:expr) => { ($suffix:expr, $days:expr) => {
@@ -98,7 +98,7 @@ impl ComputedFromDateRatio {
($ratio:expr, $suffix:expr) => { ($ratio:expr, $suffix:expr) => {
if let Some(mp) = metric_price { if let Some(mp) = metric_price {
$ratio.as_ref().map(|r| { $ratio.as_ref().map(|r| {
LazyBinaryFromDateLast::from_height_and_dateindex_last::<PriceTimesRatio>( LazyBinaryPrice::from_height_and_dateindex_last::<PriceTimesRatio>(
&format!("{name}_{}", $suffix), &format!("{name}_{}", $suffix),
v, v,
mp, mp,
@@ -107,7 +107,7 @@ impl ComputedFromDateRatio {
}) })
} else { } else {
price.as_ref().zip($ratio.as_ref()).map(|(p, r)| { price.as_ref().zip($ratio.as_ref()).map(|(p, r)| {
LazyBinaryFromDateLast::from_computed_both_last::<PriceTimesRatio>( LazyBinaryPrice::from_computed_both_last::<PriceTimesRatio>(
&format!("{name}_{}", $suffix), &format!("{name}_{}", $suffix),
v, v,
p, p,
@@ -10,7 +10,7 @@ use vecdb::{
use crate::{ComputeIndexes, indexes, price}; use crate::{ComputeIndexes, indexes, price};
use crate::internal::{ClosePriceTimesRatio, ComputedFromDateLast, LazyBinaryFromDateLast}; use crate::internal::{ClosePriceTimesRatio, ComputedFromDateLast, LazyBinaryPrice};
#[derive(Clone, Traversable)] #[derive(Clone, Traversable)]
pub struct ComputedFromDateStdDev { pub struct ComputedFromDateStdDev {
@@ -35,19 +35,19 @@ pub struct ComputedFromDateStdDev {
pub m2_5sd: Option<ComputedFromDateLast<StoredF32>>, pub m2_5sd: Option<ComputedFromDateLast<StoredF32>>,
pub m3sd: Option<ComputedFromDateLast<StoredF32>>, pub m3sd: Option<ComputedFromDateLast<StoredF32>>,
pub _0sd_usd: Option<LazyBinaryFromDateLast<Dollars, Close<Dollars>, StoredF32>>, pub _0sd_usd: Option<LazyBinaryPrice<Close<Dollars>, StoredF32>>,
pub p0_5sd_usd: Option<LazyBinaryFromDateLast<Dollars, Close<Dollars>, StoredF32>>, pub p0_5sd_usd: Option<LazyBinaryPrice<Close<Dollars>, StoredF32>>,
pub p1sd_usd: Option<LazyBinaryFromDateLast<Dollars, Close<Dollars>, StoredF32>>, pub p1sd_usd: Option<LazyBinaryPrice<Close<Dollars>, StoredF32>>,
pub p1_5sd_usd: Option<LazyBinaryFromDateLast<Dollars, Close<Dollars>, StoredF32>>, pub p1_5sd_usd: Option<LazyBinaryPrice<Close<Dollars>, StoredF32>>,
pub p2sd_usd: Option<LazyBinaryFromDateLast<Dollars, Close<Dollars>, StoredF32>>, pub p2sd_usd: Option<LazyBinaryPrice<Close<Dollars>, StoredF32>>,
pub p2_5sd_usd: Option<LazyBinaryFromDateLast<Dollars, Close<Dollars>, StoredF32>>, pub p2_5sd_usd: Option<LazyBinaryPrice<Close<Dollars>, StoredF32>>,
pub p3sd_usd: Option<LazyBinaryFromDateLast<Dollars, Close<Dollars>, StoredF32>>, pub p3sd_usd: Option<LazyBinaryPrice<Close<Dollars>, StoredF32>>,
pub m0_5sd_usd: Option<LazyBinaryFromDateLast<Dollars, Close<Dollars>, StoredF32>>, pub m0_5sd_usd: Option<LazyBinaryPrice<Close<Dollars>, StoredF32>>,
pub m1sd_usd: Option<LazyBinaryFromDateLast<Dollars, Close<Dollars>, StoredF32>>, pub m1sd_usd: Option<LazyBinaryPrice<Close<Dollars>, StoredF32>>,
pub m1_5sd_usd: Option<LazyBinaryFromDateLast<Dollars, Close<Dollars>, StoredF32>>, pub m1_5sd_usd: Option<LazyBinaryPrice<Close<Dollars>, StoredF32>>,
pub m2sd_usd: Option<LazyBinaryFromDateLast<Dollars, Close<Dollars>, StoredF32>>, pub m2sd_usd: Option<LazyBinaryPrice<Close<Dollars>, StoredF32>>,
pub m2_5sd_usd: Option<LazyBinaryFromDateLast<Dollars, Close<Dollars>, StoredF32>>, pub m2_5sd_usd: Option<LazyBinaryPrice<Close<Dollars>, StoredF32>>,
pub m3sd_usd: Option<LazyBinaryFromDateLast<Dollars, Close<Dollars>, StoredF32>>, pub m3sd_usd: Option<LazyBinaryPrice<Close<Dollars>, StoredF32>>,
} }
#[derive(Debug, Default)] #[derive(Debug, Default)]
@@ -140,7 +140,7 @@ impl ComputedFromDateStdDev {
.zip($band.as_ref()) .zip($band.as_ref())
.filter(|_| options.price_bands()) .filter(|_| options.price_bands())
.map(|(p, b)| { .map(|(p, b)| {
LazyBinaryFromDateLast::from_computed_both_last::<ClosePriceTimesRatio>( LazyBinaryPrice::from_computed_both_last::<ClosePriceTimesRatio>(
&format!("{name}_{}", $suffix), &format!("{name}_{}", $suffix),
version, version,
p, p,
@@ -0,0 +1,58 @@
//! Unary transform composite from DateIndex - Last aggregation only.
use brk_traversable::Traversable;
use brk_types::{
DateIndex, DecadeIndex, MonthIndex, QuarterIndex, SemesterIndex, Version, WeekIndex, YearIndex,
};
use schemars::JsonSchema;
use vecdb::{IterableCloneableVec, LazyVecFrom1, UnaryTransform};
use crate::internal::{ComputedFromDateLast, ComputedVecValue, LazyTransformLast};
const VERSION: Version = Version::ZERO;
#[derive(Clone, Traversable)]
#[traversable(merge)]
pub struct LazyUnaryFromDateLast<T, ST>
where
T: ComputedVecValue + PartialOrd + JsonSchema,
ST: ComputedVecValue,
{
pub dateindex: LazyVecFrom1<DateIndex, T, DateIndex, ST>,
pub weekindex: LazyTransformLast<WeekIndex, T, ST>,
pub monthindex: LazyTransformLast<MonthIndex, T, ST>,
pub quarterindex: LazyTransformLast<QuarterIndex, T, ST>,
pub semesterindex: LazyTransformLast<SemesterIndex, T, ST>,
pub yearindex: LazyTransformLast<YearIndex, T, ST>,
pub decadeindex: LazyTransformLast<DecadeIndex, T, ST>,
}
impl<T, ST> LazyUnaryFromDateLast<T, ST>
where
T: ComputedVecValue + JsonSchema + 'static,
ST: ComputedVecValue + JsonSchema,
{
pub fn from_computed_last<F: UnaryTransform<ST, T>>(
name: &str,
version: Version,
source: &ComputedFromDateLast<ST>,
) -> Self {
let v = version + VERSION;
macro_rules! period {
($p:ident) => {
LazyTransformLast::from_lazy_last::<F, _, _>(name, v, &source.$p)
};
}
Self {
dateindex: LazyVecFrom1::transformed::<F>(name, v, source.dateindex.boxed_clone()),
weekindex: period!(weekindex),
monthindex: period!(monthindex),
quarterindex: period!(quarterindex),
semesterindex: period!(semesterindex),
yearindex: period!(yearindex),
decadeindex: period!(decadeindex),
}
}
}
@@ -15,6 +15,8 @@ mod lazy_computed_full;
mod lazy_computed_sum_cum; mod lazy_computed_sum_cum;
mod lazy_last; mod lazy_last;
mod lazy_sum; mod lazy_sum;
mod price;
mod unary_last;
mod lazy_sum_cum; mod lazy_sum_cum;
mod sum; mod sum;
mod sum_cum; mod sum_cum;
@@ -46,6 +48,8 @@ pub use lazy_computed_full::*;
pub use lazy_computed_sum_cum::*; pub use lazy_computed_sum_cum::*;
pub use lazy_last::*; pub use lazy_last::*;
pub use lazy_sum::*; pub use lazy_sum::*;
pub use price::*;
pub use unary_last::*;
pub use lazy_sum_cum::*; pub use lazy_sum_cum::*;
pub use sum::*; pub use sum::*;
pub use sum_cum::*; pub use sum_cum::*;
@@ -0,0 +1,49 @@
//! Price wrapper for height-based metrics with both USD and sats representations.
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Dollars, SatsFract, Version};
use derive_more::{Deref, DerefMut};
use vecdb::Database;
use super::{ComputedFromHeightLast, LazyUnaryFromHeightLast};
use crate::{indexes, internal::DollarsToSatsFract};
/// Price metric (height-based) with both USD and sats representations.
///
/// Derefs to the dollars metric, so existing code works unchanged.
/// Access `.sats` for the sats exchange rate version.
#[derive(Clone, Deref, DerefMut, Traversable)]
#[traversable(merge)]
pub struct PriceFromHeight {
#[deref]
#[deref_mut]
#[traversable(flatten)]
pub dollars: ComputedFromHeightLast<Dollars>,
pub sats: LazyUnaryFromHeightLast<SatsFract, Dollars>,
}
impl PriceFromHeight {
pub fn forced_import(
db: &Database,
name: &str,
version: Version,
indexes: &indexes::Vecs,
) -> Result<Self> {
let dollars = ComputedFromHeightLast::forced_import(db, name, version, indexes)?;
Ok(Self::from_computed(name, version, dollars))
}
pub fn from_computed(
name: &str,
version: Version,
dollars: ComputedFromHeightLast<Dollars>,
) -> Self {
let sats = LazyUnaryFromHeightLast::from_computed_last::<DollarsToSatsFract>(
&format!("{name}_sats"),
version,
&dollars,
);
Self { dollars, sats }
}
}
@@ -0,0 +1,65 @@
//! Unary transform composite from Height - Last aggregation only.
use brk_traversable::Traversable;
use brk_types::{
DateIndex, DecadeIndex, DifficultyEpoch, Height, MonthIndex, QuarterIndex, SemesterIndex,
Version, WeekIndex, YearIndex,
};
use schemars::JsonSchema;
use vecdb::{IterableCloneableVec, LazyVecFrom1, UnaryTransform};
use crate::internal::{
ComputedFromHeightLast, ComputedVecValue, LazyTransformLast, NumericValue,
};
const VERSION: Version = Version::ZERO;
#[derive(Clone, Traversable)]
#[traversable(merge)]
pub struct LazyUnaryFromHeightLast<T, ST>
where
T: ComputedVecValue + PartialOrd + JsonSchema,
ST: ComputedVecValue,
{
pub height: LazyVecFrom1<Height, T, Height, ST>,
pub dateindex: LazyTransformLast<DateIndex, T, ST>,
pub weekindex: LazyTransformLast<WeekIndex, T, ST>,
pub monthindex: LazyTransformLast<MonthIndex, T, ST>,
pub quarterindex: LazyTransformLast<QuarterIndex, T, ST>,
pub semesterindex: LazyTransformLast<SemesterIndex, T, ST>,
pub yearindex: LazyTransformLast<YearIndex, T, ST>,
pub decadeindex: LazyTransformLast<DecadeIndex, T, ST>,
pub difficultyepoch: LazyTransformLast<DifficultyEpoch, T, ST>,
}
impl<T, ST> LazyUnaryFromHeightLast<T, ST>
where
T: ComputedVecValue + JsonSchema + 'static,
ST: NumericValue + JsonSchema,
{
pub fn from_computed_last<F: UnaryTransform<ST, T>>(
name: &str,
version: Version,
source: &ComputedFromHeightLast<ST>,
) -> Self {
let v = version + VERSION;
macro_rules! period {
($p:ident) => {
LazyTransformLast::from_lazy_last::<F, _, _>(name, v, &source.$p)
};
}
Self {
height: LazyVecFrom1::transformed::<F>(name, v, source.height.boxed_clone()),
dateindex: LazyTransformLast::from_last_vec::<F>(name, v, &source.rest.dateindex),
weekindex: period!(weekindex),
monthindex: period!(monthindex),
quarterindex: period!(quarterindex),
semesterindex: period!(semesterindex),
yearindex: period!(yearindex),
decadeindex: period!(decadeindex),
difficultyepoch: period!(difficultyepoch),
}
}
}
@@ -6,6 +6,8 @@ mod last;
mod lazy_ohlc; mod lazy_ohlc;
mod max; mod max;
mod min; mod min;
mod price;
mod unary_last;
mod value_last; mod value_last;
pub use binary_last::*; pub use binary_last::*;
@@ -16,4 +18,6 @@ pub use last::*;
pub use lazy_ohlc::*; pub use lazy_ohlc::*;
pub use max::*; pub use max::*;
pub use min::*; pub use min::*;
pub use price::*;
pub use unary_last::*;
pub use value_last::*; pub use value_last::*;
@@ -0,0 +1,49 @@
//! Price wrapper for height+date-based metrics with both USD and sats representations.
use brk_error::Result;
use brk_traversable::Traversable;
use brk_types::{Dollars, SatsFract, Version};
use derive_more::{Deref, DerefMut};
use vecdb::Database;
use super::{ComputedFromHeightAndDateLast, LazyUnaryFromHeightAndDateLast};
use crate::{indexes, internal::DollarsToSatsFract};
/// Price metric (height+date-based) with both USD and sats representations.
///
/// Derefs to the dollars metric, so existing code works unchanged.
/// Access `.sats` for the sats exchange rate version.
#[derive(Clone, Deref, DerefMut, Traversable)]
#[traversable(merge)]
pub struct PriceFromHeightAndDate {
#[deref]
#[deref_mut]
#[traversable(flatten)]
pub dollars: ComputedFromHeightAndDateLast<Dollars>,
pub sats: LazyUnaryFromHeightAndDateLast<SatsFract, Dollars>,
}
impl PriceFromHeightAndDate {
pub fn forced_import(
db: &Database,
name: &str,
version: Version,
indexes: &indexes::Vecs,
) -> Result<Self> {
let dollars = ComputedFromHeightAndDateLast::forced_import(db, name, version, indexes)?;
Ok(Self::from_computed(name, version, dollars))
}
pub fn from_computed(
name: &str,
version: Version,
dollars: ComputedFromHeightAndDateLast<Dollars>,
) -> Self {
let sats = LazyUnaryFromHeightAndDateLast::from_computed_last::<DollarsToSatsFract>(
&format!("{name}_sats"),
version,
&dollars,
);
Self { dollars, sats }
}
}
@@ -0,0 +1,71 @@
//! Unary transform composite from Height+Date - Last aggregation only.
use brk_traversable::Traversable;
use brk_types::{
DateIndex, DecadeIndex, DifficultyEpoch, Height, MonthIndex, QuarterIndex, SemesterIndex,
Version, WeekIndex, YearIndex,
};
use schemars::JsonSchema;
use vecdb::{IterableCloneableVec, LazyVecFrom1, UnaryTransform};
use crate::internal::{ComputedFromHeightAndDateLast, ComputedVecValue, LazyTransformLast};
const VERSION: Version = Version::ZERO;
#[derive(Clone, Traversable)]
#[traversable(merge)]
pub struct LazyUnaryFromHeightAndDateLast<T, ST>
where
T: ComputedVecValue + PartialOrd + JsonSchema,
ST: ComputedVecValue,
{
pub height: LazyVecFrom1<Height, T, Height, ST>,
pub dateindex: LazyTransformLast<DateIndex, T, ST>,
pub weekindex: LazyTransformLast<WeekIndex, T, ST>,
pub monthindex: LazyTransformLast<MonthIndex, T, ST>,
pub quarterindex: LazyTransformLast<QuarterIndex, T, ST>,
pub semesterindex: LazyTransformLast<SemesterIndex, T, ST>,
pub yearindex: LazyTransformLast<YearIndex, T, ST>,
pub decadeindex: LazyTransformLast<DecadeIndex, T, ST>,
pub difficultyepoch: LazyTransformLast<DifficultyEpoch, T, ST>,
}
impl<T, ST> LazyUnaryFromHeightAndDateLast<T, ST>
where
T: ComputedVecValue + JsonSchema + 'static,
ST: ComputedVecValue + JsonSchema,
{
pub fn from_computed_last<F: UnaryTransform<ST, T>>(
name: &str,
version: Version,
source: &ComputedFromHeightAndDateLast<ST>,
) -> Self {
let v = version + VERSION;
macro_rules! period {
($p:ident) => {
LazyTransformLast::from_lazy_last::<F, _, _>(name, v, &source.rest.$p)
};
}
Self {
height: LazyVecFrom1::transformed::<F>(name, v, source.height.boxed_clone()),
dateindex: LazyTransformLast(LazyVecFrom1::transformed::<F>(
name,
v,
source.rest.dateindex.boxed_clone(),
)),
weekindex: period!(weekindex),
monthindex: period!(monthindex),
quarterindex: period!(quarterindex),
semesterindex: period!(semesterindex),
yearindex: period!(yearindex),
decadeindex: period!(decadeindex),
difficultyepoch: LazyTransformLast::from_lazy_last::<F, _, _>(
name,
v,
&source.difficultyepoch,
),
}
}
}
@@ -1,4 +1,4 @@
use brk_types::{Close, Dollars, SatsFract}; use brk_types::{Dollars, SatsFract};
use vecdb::UnaryTransform; use vecdb::UnaryTransform;
/// Dollars -> SatsFract (exchange rate: sats per dollar at this price level) /// Dollars -> SatsFract (exchange rate: sats per dollar at this price level)
@@ -8,21 +8,6 @@ pub struct DollarsToSatsFract;
impl UnaryTransform<Dollars, SatsFract> for DollarsToSatsFract { impl UnaryTransform<Dollars, SatsFract> for DollarsToSatsFract {
#[inline(always)] #[inline(always)]
fn apply(usd: Dollars) -> SatsFract { fn apply(usd: Dollars) -> SatsFract {
let usd_f64 = f64::from(usd); SatsFract::ONE_BTC / usd
if usd_f64 == 0.0 {
SatsFract::NAN
} else {
SatsFract::from(SatsFract::SATS_PER_BTC / usd_f64)
}
}
}
/// Close<Dollars> -> SatsFract
pub struct CloseDollarsToSatsFract;
impl UnaryTransform<Close<Dollars>, SatsFract> for CloseDollarsToSatsFract {
#[inline(always)]
fn apply(usd: Close<Dollars>) -> SatsFract {
DollarsToSatsFract::apply(*usd)
} }
} }
@@ -4,6 +4,7 @@ mod close_price_times_sats;
mod difference_f32; mod difference_f32;
mod dollar_halve; mod dollar_halve;
mod dollar_identity; mod dollar_identity;
mod dollars_to_sats_fract;
mod dollar_minus; mod dollar_minus;
mod dollar_plus; mod dollar_plus;
mod dollar_times_tenths; mod dollar_times_tenths;
@@ -44,6 +45,7 @@ pub use close_price_times_sats::*;
pub use difference_f32::*; pub use difference_f32::*;
pub use dollar_halve::*; pub use dollar_halve::*;
pub use dollar_identity::*; pub use dollar_identity::*;
pub use dollars_to_sats_fract::*;
pub use dollar_minus::*; pub use dollar_minus::*;
pub use dollar_plus::*; pub use dollar_plus::*;
pub use dollar_times_tenths::*; pub use dollar_times_tenths::*;
+3 -3
View File
@@ -6,8 +6,8 @@ use super::Vecs;
use crate::{ use crate::{
indexes, indexes,
internal::{ internal::{
ComputedFromDateLast, ComputedFromHeightAndDateLast, LazyBinaryFromHeightAndDateLast, LazyFromDateLast, ComputedFromDateLast, LazyBinaryFromHeightAndDateLast, LazyFromDateLast,
PercentageDiffCloseDollars, StoredU16ToYears, PercentageDiffCloseDollars, PriceFromHeightAndDate, StoredU16ToYears,
}, },
price, price,
}; };
@@ -19,7 +19,7 @@ impl Vecs {
indexes: &indexes::Vecs, indexes: &indexes::Vecs,
price: &price::Vecs, price: &price::Vecs,
) -> Result<Self> { ) -> Result<Self> {
let price_ath = ComputedFromHeightAndDateLast::forced_import(db, "price_ath", version, indexes)?; let price_ath = PriceFromHeightAndDate::forced_import(db, "price_ath", version, indexes)?;
let max_days_between_price_aths = let max_days_between_price_aths =
ComputedFromDateLast::forced_import(db, "max_days_between_price_aths", version, indexes)?; ComputedFromDateLast::forced_import(db, "max_days_between_price_aths", version, indexes)?;
+2 -2
View File
@@ -2,13 +2,13 @@ use brk_traversable::Traversable;
use brk_types::{Close, Dollars, StoredF32, StoredU16}; use brk_types::{Close, Dollars, StoredF32, StoredU16};
use crate::internal::{ use crate::internal::{
ComputedFromDateLast, ComputedFromHeightAndDateLast, LazyBinaryFromHeightAndDateLast, LazyFromDateLast, ComputedFromDateLast, LazyBinaryFromHeightAndDateLast, LazyFromDateLast, PriceFromHeightAndDate,
}; };
/// All-time high related metrics /// All-time high related metrics
#[derive(Clone, Traversable)] #[derive(Clone, Traversable)]
pub struct Vecs { pub struct Vecs {
pub price_ath: ComputedFromHeightAndDateLast<Dollars>, pub price_ath: PriceFromHeightAndDate,
pub price_drawdown: LazyBinaryFromHeightAndDateLast<StoredF32, Close<Dollars>, Dollars>, pub price_drawdown: LazyBinaryFromHeightAndDateLast<StoredF32, Close<Dollars>, Dollars>,
pub days_since_price_ath: ComputedFromDateLast<StoredU16>, pub days_since_price_ath: ComputedFromDateLast<StoredU16>,
pub years_since_price_ath: LazyFromDateLast<StoredF32, StoredU16>, pub years_since_price_ath: LazyFromDateLast<StoredF32, StoredU16>,
+3 -8
View File
@@ -6,7 +6,7 @@ use super::{ByDcaCagr, ByDcaClass, ByDcaPeriod, DCA_CLASS_NAMES, DCA_PERIOD_NAME
use crate::{ use crate::{
indexes, indexes,
internal::{ internal::{
ComputedFromDateLast, LazyBinaryFromDateLast, PercentageDiffCloseDollars, ComputedFromDateLast, LazyBinaryFromDateLast, PercentageDiffCloseDollars, Price,
ValueFromDateLast, ValueFromDateLast,
}, },
market::lookback, market::lookback,
@@ -28,12 +28,7 @@ impl Vecs {
// DCA by period - average price // DCA by period - average price
let period_average_price = ByDcaPeriod::try_new(|name, _days| { let period_average_price = ByDcaPeriod::try_new(|name, _days| {
ComputedFromDateLast::forced_import( Price::forced_import(db, &format!("{name}_dca_average_price"), version, indexes)
db,
&format!("{name}_dca_average_price"),
version,
indexes,
)
})?; })?;
let period_returns = let period_returns =
@@ -160,7 +155,7 @@ impl Vecs {
// DCA by year class - average price // DCA by year class - average price
let class_average_price = ByDcaClass::try_new(|name, _year, _dateindex| { let class_average_price = ByDcaClass::try_new(|name, _year, _dateindex| {
ComputedFromDateLast::forced_import(db, &format!("{name}_average_price"), version, indexes) Price::forced_import(db, &format!("{name}_average_price"), version, indexes)
})?; })?;
let class_returns = let class_returns =
+3 -3
View File
@@ -2,14 +2,14 @@ use brk_traversable::Traversable;
use brk_types::{Close, Dollars, StoredF32, StoredU32}; use brk_types::{Close, Dollars, StoredF32, StoredU32};
use super::{ByDcaCagr, ByDcaClass, ByDcaPeriod}; use super::{ByDcaCagr, ByDcaClass, ByDcaPeriod};
use crate::internal::{ComputedFromDateLast, LazyBinaryFromDateLast, ValueFromDateLast}; use crate::internal::{ComputedFromDateLast, LazyBinaryFromDateLast, Price, ValueFromDateLast};
/// Dollar-cost averaging metrics by time period and year class /// Dollar-cost averaging metrics by time period and year class
#[derive(Clone, Traversable)] #[derive(Clone, Traversable)]
pub struct Vecs { pub struct Vecs {
// DCA by period - KISS types // DCA by period - KISS types
pub period_stack: ByDcaPeriod<ValueFromDateLast>, pub period_stack: ByDcaPeriod<ValueFromDateLast>,
pub period_average_price: ByDcaPeriod<ComputedFromDateLast<Dollars>>, pub period_average_price: ByDcaPeriod<Price>,
pub period_returns: ByDcaPeriod<LazyBinaryFromDateLast<StoredF32, Close<Dollars>, Dollars>>, pub period_returns: ByDcaPeriod<LazyBinaryFromDateLast<StoredF32, Close<Dollars>, Dollars>>,
pub period_cagr: ByDcaCagr<ComputedFromDateLast<StoredF32>>, pub period_cagr: ByDcaCagr<ComputedFromDateLast<StoredF32>>,
@@ -31,7 +31,7 @@ pub struct Vecs {
// DCA by year class - KISS types // DCA by year class - KISS types
pub class_stack: ByDcaClass<ValueFromDateLast>, pub class_stack: ByDcaClass<ValueFromDateLast>,
pub class_average_price: ByDcaClass<ComputedFromDateLast<Dollars>>, pub class_average_price: ByDcaClass<Price>,
pub class_returns: ByDcaClass<LazyBinaryFromDateLast<StoredF32, Close<Dollars>, Dollars>>, pub class_returns: ByDcaClass<LazyBinaryFromDateLast<StoredF32, Close<Dollars>, Dollars>>,
// DCA by year class - profitability // DCA by year class - profitability
@@ -3,12 +3,12 @@ use brk_types::Version;
use vecdb::Database; use vecdb::Database;
use super::{ByLookbackPeriod, Vecs}; use super::{ByLookbackPeriod, Vecs};
use crate::{indexes, internal::ComputedFromDateLast}; use crate::{indexes, internal::Price};
impl Vecs { impl Vecs {
pub fn forced_import(db: &Database, version: Version, indexes: &indexes::Vecs) -> Result<Self> { pub fn forced_import(db: &Database, version: Version, indexes: &indexes::Vecs) -> Result<Self> {
let price_ago = ByLookbackPeriod::try_new(|name, _days| { let price_ago = ByLookbackPeriod::try_new(|name, _days| {
ComputedFromDateLast::forced_import(db, &format!("price_{name}_ago"), version, indexes) Price::forced_import(db, &format!("price_{name}_ago"), version, indexes)
})?; })?;
Ok(Self { price_ago }) Ok(Self { price_ago })
@@ -1,12 +1,11 @@
use brk_traversable::Traversable; use brk_traversable::Traversable;
use brk_types::Dollars;
use super::ByLookbackPeriod; use super::ByLookbackPeriod;
use crate::internal::ComputedFromDateLast; use crate::internal::Price;
/// Price lookback metrics /// Price lookback metrics
#[derive(Clone, Traversable)] #[derive(Clone, Traversable)]
pub struct Vecs { pub struct Vecs {
#[traversable(flatten)] #[traversable(flatten)]
pub price_ago: ByLookbackPeriod<ComputedFromDateLast<Dollars>>, pub price_ago: ByLookbackPeriod<Price>,
} }
@@ -5,7 +5,7 @@ use vecdb::Database;
use super::Vecs; use super::Vecs;
use crate::{ use crate::{
indexes, indexes,
internal::{ComputedFromDateRatio, DollarsTimesTenths, LazyFromDateLast}, internal::{ComputedFromDateRatio, DollarsTimesTenths, LazyPrice},
price, price,
}; };
@@ -307,19 +307,19 @@ impl Vecs {
)?; )?;
let price_200d_sma_source = price_200d_sma.price.as_ref().unwrap(); let price_200d_sma_source = price_200d_sma.price.as_ref().unwrap();
let price_200d_sma_x2_4 = LazyFromDateLast::from_source::<DollarsTimesTenths<24>>( let price_200d_sma_x2_4 = LazyPrice::from_source::<DollarsTimesTenths<24>>(
"price_200d_sma_x2_4", "price_200d_sma_x2_4",
version, version,
price_200d_sma_source, price_200d_sma_source,
); );
let price_200d_sma_x0_8 = LazyFromDateLast::from_source::<DollarsTimesTenths<8>>( let price_200d_sma_x0_8 = LazyPrice::from_source::<DollarsTimesTenths<8>>(
"price_200d_sma_x0_8", "price_200d_sma_x0_8",
version, version,
price_200d_sma_source, price_200d_sma_source,
); );
let price_350d_sma_source = price_350d_sma.price.as_ref().unwrap(); let price_350d_sma_source = price_350d_sma.price.as_ref().unwrap();
let price_350d_sma_x2 = LazyFromDateLast::from_source::<DollarsTimesTenths<20>>( let price_350d_sma_x2 = LazyPrice::from_source::<DollarsTimesTenths<20>>(
"price_350d_sma_x2", "price_350d_sma_x2",
version, version,
price_350d_sma_source, price_350d_sma_source,
@@ -1,7 +1,7 @@
use brk_traversable::Traversable; use brk_traversable::Traversable;
use brk_types::Dollars; use brk_types::Dollars;
use crate::internal::{ComputedFromDateRatio, LazyFromDateLast}; use crate::internal::{ComputedFromDateRatio, LazyPrice};
/// Simple and exponential moving average metrics /// Simple and exponential moving average metrics
#[derive(Clone, Traversable)] #[derive(Clone, Traversable)]
@@ -40,7 +40,7 @@ pub struct Vecs {
pub price_200w_ema: ComputedFromDateRatio, pub price_200w_ema: ComputedFromDateRatio,
pub price_4y_ema: ComputedFromDateRatio, pub price_4y_ema: ComputedFromDateRatio,
pub price_200d_sma_x2_4: LazyFromDateLast<Dollars>, pub price_200d_sma_x2_4: LazyPrice<Dollars>,
pub price_200d_sma_x0_8: LazyFromDateLast<Dollars>, pub price_200d_sma_x0_8: LazyPrice<Dollars>,
pub price_350d_sma_x2: LazyFromDateLast<Dollars>, pub price_350d_sma_x2: LazyPrice<Dollars>,
} }
+10 -54
View File
@@ -3,67 +3,23 @@ use brk_types::Version;
use vecdb::{Database, EagerVec, ImportableVec}; use vecdb::{Database, EagerVec, ImportableVec};
use super::Vecs; use super::Vecs;
use crate::{indexes, internal::ComputedFromDateLast}; use crate::{indexes, internal::{ComputedFromDateLast, Price}};
impl Vecs { impl Vecs {
pub fn forced_import(db: &Database, version: Version, indexes: &indexes::Vecs) -> Result<Self> { pub fn forced_import(db: &Database, version: Version, indexes: &indexes::Vecs) -> Result<Self> {
let v1 = Version::ONE; let v1 = Version::ONE;
Ok(Self { Ok(Self {
price_1w_min: ComputedFromDateLast::forced_import( price_1w_min: Price::forced_import(db, "price_1w_min", version + v1, indexes)?,
db, price_1w_max: Price::forced_import(db, "price_1w_max", version + v1, indexes)?,
"price_1w_min", price_2w_min: Price::forced_import(db, "price_2w_min", version + v1, indexes)?,
version + v1, price_2w_max: Price::forced_import(db, "price_2w_max", version + v1, indexes)?,
indexes, price_1m_min: Price::forced_import(db, "price_1m_min", version + v1, indexes)?,
)?, price_1m_max: Price::forced_import(db, "price_1m_max", version + v1, indexes)?,
price_1w_max: ComputedFromDateLast::forced_import( price_1y_min: Price::forced_import(db, "price_1y_min", version + v1, indexes)?,
db, price_1y_max: Price::forced_import(db, "price_1y_max", version + v1, indexes)?,
"price_1w_max",
version + v1,
indexes,
)?,
price_2w_min: ComputedFromDateLast::forced_import(
db,
"price_2w_min",
version + v1,
indexes,
)?,
price_2w_max: ComputedFromDateLast::forced_import(
db,
"price_2w_max",
version + v1,
indexes,
)?,
price_1m_min: ComputedFromDateLast::forced_import(
db,
"price_1m_min",
version + v1,
indexes,
)?,
price_1m_max: ComputedFromDateLast::forced_import(
db,
"price_1m_max",
version + v1,
indexes,
)?,
price_1y_min: ComputedFromDateLast::forced_import(
db,
"price_1y_min",
version + v1,
indexes,
)?,
price_1y_max: ComputedFromDateLast::forced_import(
db,
"price_1y_max",
version + v1,
indexes,
)?,
price_true_range: EagerVec::forced_import(db, "price_true_range", version)?, price_true_range: EagerVec::forced_import(db, "price_true_range", version)?,
price_true_range_2w_sum: EagerVec::forced_import( price_true_range_2w_sum: EagerVec::forced_import(db, "price_true_range_2w_sum", version)?,
db,
"price_true_range_2w_sum",
version,
)?,
price_2w_choppiness_index: ComputedFromDateLast::forced_import( price_2w_choppiness_index: ComputedFromDateLast::forced_import(
db, db,
"price_2w_choppiness_index", "price_2w_choppiness_index",
+10 -10
View File
@@ -1,20 +1,20 @@
use brk_traversable::Traversable; use brk_traversable::Traversable;
use brk_types::{DateIndex, Dollars, StoredF32}; use brk_types::{DateIndex, StoredF32};
use vecdb::{EagerVec, PcoVec}; use vecdb::{EagerVec, PcoVec};
use crate::internal::ComputedFromDateLast; use crate::internal::{ComputedFromDateLast, Price};
/// Price range and choppiness metrics /// Price range and choppiness metrics
#[derive(Clone, Traversable)] #[derive(Clone, Traversable)]
pub struct Vecs { pub struct Vecs {
pub price_1w_min: ComputedFromDateLast<Dollars>, pub price_1w_min: Price,
pub price_1w_max: ComputedFromDateLast<Dollars>, pub price_1w_max: Price,
pub price_2w_min: ComputedFromDateLast<Dollars>, pub price_2w_min: Price,
pub price_2w_max: ComputedFromDateLast<Dollars>, pub price_2w_max: Price,
pub price_1m_min: ComputedFromDateLast<Dollars>, pub price_1m_min: Price,
pub price_1m_max: ComputedFromDateLast<Dollars>, pub price_1m_max: Price,
pub price_1y_min: ComputedFromDateLast<Dollars>, pub price_1y_min: Price,
pub price_1y_max: ComputedFromDateLast<Dollars>, pub price_1y_max: Price,
pub price_true_range: EagerVec<PcoVec<DateIndex, StoredF32>>, pub price_true_range: EagerVec<PcoVec<DateIndex, StoredF32>>,
pub price_true_range_2w_sum: EagerVec<PcoVec<DateIndex, StoredF32>>, pub price_true_range_2w_sum: EagerVec<PcoVec<DateIndex, StoredF32>>,
pub price_2w_choppiness_index: ComputedFromDateLast<StoredF32>, pub price_2w_choppiness_index: ComputedFromDateLast<StoredF32>,
+20
View File
@@ -26,6 +26,7 @@ pub struct SatsFract(f64);
impl SatsFract { impl SatsFract {
pub const ZERO: Self = Self(0.0); pub const ZERO: Self = Self(0.0);
pub const NAN: Self = Self(f64::NAN); pub const NAN: Self = Self(f64::NAN);
pub const ONE_BTC: Self = Self(100_000_000.0);
pub const SATS_PER_BTC: f64 = 100_000_000.0; pub const SATS_PER_BTC: f64 = 100_000_000.0;
pub const fn new(sats: f64) -> Self { pub const fn new(sats: f64) -> Self {
@@ -191,3 +192,22 @@ impl Formattable for SatsFract {
false false
} }
} }
impl Div<Dollars> for SatsFract {
type Output = Self;
fn div(self, rhs: Dollars) -> Self::Output {
let rhs = f64::from(rhs);
if rhs == 0.0 {
Self::NAN
} else {
Self(self.0 / rhs)
}
}
}
impl Div<Close<Dollars>> for SatsFract {
type Output = Self;
fn div(self, rhs: Close<Dollars>) -> Self::Output {
self / *rhs
}
}
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -34,5 +34,5 @@
"url": "git+https://github.com/bitcoinresearchkit/brk.git" "url": "git+https://github.com/bitcoinresearchkit/brk.git"
}, },
"type": "module", "type": "module",
"version": "0.1.0-beta.0" "version": "0.1.0-beta.1"
} }
+231 -190
View File
@@ -107,6 +107,15 @@ PoolSlug = Literal["unknown", "blockfills", "ultimuspool", "terrapool", "luxor",
QuarterIndex = int QuarterIndex = int
# Transaction locktime # Transaction locktime
RawLockTime = int RawLockTime = int
# Fractional satoshis (f64) - for representing USD prices in sats
#
# Formula: `sats_fract = usd_value * 100_000_000 / btc_price`
#
# When BTC is $100,000:
# - $1 = 1,000 sats
# - $0.001 = 1 sat
# - $0.0001 = 0.1 sats (fractional)
SatsFract = float
SemesterIndex = int SemesterIndex = int
# Fixed-size boolean value optimized for on-disk storage (stored as u8) # Fixed-size boolean value optimized for on-disk storage (stored as u8)
StoredBool = int StoredBool = int
@@ -1746,7 +1755,7 @@ class RealizedPattern3:
self.realized_cap_rel_to_own_market_cap: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'realized_cap_rel_to_own_market_cap')) self.realized_cap_rel_to_own_market_cap: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'realized_cap_rel_to_own_market_cap'))
self.realized_loss: BlockCountPattern[Dollars] = BlockCountPattern(client, _m(acc, 'realized_loss')) self.realized_loss: BlockCountPattern[Dollars] = BlockCountPattern(client, _m(acc, 'realized_loss'))
self.realized_loss_rel_to_realized_cap: BlockCountPattern[StoredF32] = BlockCountPattern(client, _m(acc, 'realized_loss_rel_to_realized_cap')) self.realized_loss_rel_to_realized_cap: BlockCountPattern[StoredF32] = BlockCountPattern(client, _m(acc, 'realized_loss_rel_to_realized_cap'))
self.realized_price: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'realized_price')) self.realized_price: ActivePricePattern = ActivePricePattern(client, _m(acc, 'realized_price'))
self.realized_price_extra: ActivePriceRatioPattern = ActivePriceRatioPattern(client, _m(acc, 'realized_price_ratio')) self.realized_price_extra: ActivePriceRatioPattern = ActivePriceRatioPattern(client, _m(acc, 'realized_price_ratio'))
self.realized_profit: BlockCountPattern[Dollars] = BlockCountPattern(client, _m(acc, 'realized_profit')) self.realized_profit: BlockCountPattern[Dollars] = BlockCountPattern(client, _m(acc, 'realized_profit'))
self.realized_profit_rel_to_realized_cap: BlockCountPattern[StoredF32] = BlockCountPattern(client, _m(acc, 'realized_profit_rel_to_realized_cap')) self.realized_profit_rel_to_realized_cap: BlockCountPattern[StoredF32] = BlockCountPattern(client, _m(acc, 'realized_profit_rel_to_realized_cap'))
@@ -1783,7 +1792,7 @@ class RealizedPattern4:
self.realized_cap_30d_delta: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'realized_cap_30d_delta')) self.realized_cap_30d_delta: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'realized_cap_30d_delta'))
self.realized_loss: BlockCountPattern[Dollars] = BlockCountPattern(client, _m(acc, 'realized_loss')) self.realized_loss: BlockCountPattern[Dollars] = BlockCountPattern(client, _m(acc, 'realized_loss'))
self.realized_loss_rel_to_realized_cap: BlockCountPattern[StoredF32] = BlockCountPattern(client, _m(acc, 'realized_loss_rel_to_realized_cap')) self.realized_loss_rel_to_realized_cap: BlockCountPattern[StoredF32] = BlockCountPattern(client, _m(acc, 'realized_loss_rel_to_realized_cap'))
self.realized_price: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'realized_price')) self.realized_price: ActivePricePattern = ActivePricePattern(client, _m(acc, 'realized_price'))
self.realized_price_extra: RealizedPriceExtraPattern = RealizedPriceExtraPattern(client, _m(acc, 'realized_price_ratio')) self.realized_price_extra: RealizedPriceExtraPattern = RealizedPriceExtraPattern(client, _m(acc, 'realized_price_ratio'))
self.realized_profit: BlockCountPattern[Dollars] = BlockCountPattern(client, _m(acc, 'realized_profit')) self.realized_profit: BlockCountPattern[Dollars] = BlockCountPattern(client, _m(acc, 'realized_profit'))
self.realized_profit_rel_to_realized_cap: BlockCountPattern[StoredF32] = BlockCountPattern(client, _m(acc, 'realized_profit_rel_to_realized_cap')) self.realized_profit_rel_to_realized_cap: BlockCountPattern[StoredF32] = BlockCountPattern(client, _m(acc, 'realized_profit_rel_to_realized_cap'))
@@ -1803,31 +1812,31 @@ class Ratio1ySdPattern:
def __init__(self, client: BrkClientBase, acc: str): def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated metric name.""" """Create pattern node with accumulated metric name."""
self._0sd_usd: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, '0sd_usd')) self._0sd_usd: _0sdUsdPattern = _0sdUsdPattern(client, _m(acc, '0sd_usd'))
self.m0_5sd: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'm0_5sd')) self.m0_5sd: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'm0_5sd'))
self.m0_5sd_usd: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'm0_5sd_usd')) self.m0_5sd_usd: _0sdUsdPattern = _0sdUsdPattern(client, _m(acc, 'm0_5sd_usd'))
self.m1_5sd: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'm1_5sd')) self.m1_5sd: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'm1_5sd'))
self.m1_5sd_usd: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'm1_5sd_usd')) self.m1_5sd_usd: _0sdUsdPattern = _0sdUsdPattern(client, _m(acc, 'm1_5sd_usd'))
self.m1sd: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'm1sd')) self.m1sd: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'm1sd'))
self.m1sd_usd: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'm1sd_usd')) self.m1sd_usd: _0sdUsdPattern = _0sdUsdPattern(client, _m(acc, 'm1sd_usd'))
self.m2_5sd: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'm2_5sd')) self.m2_5sd: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'm2_5sd'))
self.m2_5sd_usd: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'm2_5sd_usd')) self.m2_5sd_usd: _0sdUsdPattern = _0sdUsdPattern(client, _m(acc, 'm2_5sd_usd'))
self.m2sd: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'm2sd')) self.m2sd: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'm2sd'))
self.m2sd_usd: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'm2sd_usd')) self.m2sd_usd: _0sdUsdPattern = _0sdUsdPattern(client, _m(acc, 'm2sd_usd'))
self.m3sd: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'm3sd')) self.m3sd: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'm3sd'))
self.m3sd_usd: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'm3sd_usd')) self.m3sd_usd: _0sdUsdPattern = _0sdUsdPattern(client, _m(acc, 'm3sd_usd'))
self.p0_5sd: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'p0_5sd')) self.p0_5sd: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'p0_5sd'))
self.p0_5sd_usd: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'p0_5sd_usd')) self.p0_5sd_usd: _0sdUsdPattern = _0sdUsdPattern(client, _m(acc, 'p0_5sd_usd'))
self.p1_5sd: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'p1_5sd')) self.p1_5sd: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'p1_5sd'))
self.p1_5sd_usd: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'p1_5sd_usd')) self.p1_5sd_usd: _0sdUsdPattern = _0sdUsdPattern(client, _m(acc, 'p1_5sd_usd'))
self.p1sd: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'p1sd')) self.p1sd: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'p1sd'))
self.p1sd_usd: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'p1sd_usd')) self.p1sd_usd: _0sdUsdPattern = _0sdUsdPattern(client, _m(acc, 'p1sd_usd'))
self.p2_5sd: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'p2_5sd')) self.p2_5sd: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'p2_5sd'))
self.p2_5sd_usd: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'p2_5sd_usd')) self.p2_5sd_usd: _0sdUsdPattern = _0sdUsdPattern(client, _m(acc, 'p2_5sd_usd'))
self.p2sd: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'p2sd')) self.p2sd: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'p2sd'))
self.p2sd_usd: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'p2sd_usd')) self.p2sd_usd: _0sdUsdPattern = _0sdUsdPattern(client, _m(acc, 'p2sd_usd'))
self.p3sd: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'p3sd')) self.p3sd: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'p3sd'))
self.p3sd_usd: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'p3sd_usd')) self.p3sd_usd: _0sdUsdPattern = _0sdUsdPattern(client, _m(acc, 'p3sd_usd'))
self.sd: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'sd')) self.sd: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'sd'))
self.sma: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'sma')) self.sma: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'sma'))
self.zscore: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'zscore')) self.zscore: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'zscore'))
@@ -1849,7 +1858,7 @@ class RealizedPattern2:
self.realized_cap_rel_to_own_market_cap: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'realized_cap_rel_to_own_market_cap')) self.realized_cap_rel_to_own_market_cap: MetricPattern1[StoredF32] = MetricPattern1(client, _m(acc, 'realized_cap_rel_to_own_market_cap'))
self.realized_loss: BlockCountPattern[Dollars] = BlockCountPattern(client, _m(acc, 'realized_loss')) self.realized_loss: BlockCountPattern[Dollars] = BlockCountPattern(client, _m(acc, 'realized_loss'))
self.realized_loss_rel_to_realized_cap: BlockCountPattern[StoredF32] = BlockCountPattern(client, _m(acc, 'realized_loss_rel_to_realized_cap')) self.realized_loss_rel_to_realized_cap: BlockCountPattern[StoredF32] = BlockCountPattern(client, _m(acc, 'realized_loss_rel_to_realized_cap'))
self.realized_price: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'realized_price')) self.realized_price: ActivePricePattern = ActivePricePattern(client, _m(acc, 'realized_price'))
self.realized_price_extra: ActivePriceRatioPattern = ActivePriceRatioPattern(client, _m(acc, 'realized_price_ratio')) self.realized_price_extra: ActivePriceRatioPattern = ActivePriceRatioPattern(client, _m(acc, 'realized_price_ratio'))
self.realized_profit: BlockCountPattern[Dollars] = BlockCountPattern(client, _m(acc, 'realized_profit')) self.realized_profit: BlockCountPattern[Dollars] = BlockCountPattern(client, _m(acc, 'realized_profit'))
self.realized_profit_rel_to_realized_cap: BlockCountPattern[StoredF32] = BlockCountPattern(client, _m(acc, 'realized_profit_rel_to_realized_cap')) self.realized_profit_rel_to_realized_cap: BlockCountPattern[StoredF32] = BlockCountPattern(client, _m(acc, 'realized_profit_rel_to_realized_cap'))
@@ -1881,7 +1890,7 @@ class RealizedPattern:
self.realized_cap_30d_delta: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'realized_cap_30d_delta')) self.realized_cap_30d_delta: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'realized_cap_30d_delta'))
self.realized_loss: BlockCountPattern[Dollars] = BlockCountPattern(client, _m(acc, 'realized_loss')) self.realized_loss: BlockCountPattern[Dollars] = BlockCountPattern(client, _m(acc, 'realized_loss'))
self.realized_loss_rel_to_realized_cap: BlockCountPattern[StoredF32] = BlockCountPattern(client, _m(acc, 'realized_loss_rel_to_realized_cap')) self.realized_loss_rel_to_realized_cap: BlockCountPattern[StoredF32] = BlockCountPattern(client, _m(acc, 'realized_loss_rel_to_realized_cap'))
self.realized_price: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'realized_price')) self.realized_price: ActivePricePattern = ActivePricePattern(client, _m(acc, 'realized_price'))
self.realized_price_extra: RealizedPriceExtraPattern = RealizedPriceExtraPattern(client, _m(acc, 'realized_price_ratio')) self.realized_price_extra: RealizedPriceExtraPattern = RealizedPriceExtraPattern(client, _m(acc, 'realized_price_ratio'))
self.realized_profit: BlockCountPattern[Dollars] = BlockCountPattern(client, _m(acc, 'realized_profit')) self.realized_profit: BlockCountPattern[Dollars] = BlockCountPattern(client, _m(acc, 'realized_profit'))
self.realized_profit_rel_to_realized_cap: BlockCountPattern[StoredF32] = BlockCountPattern(client, _m(acc, 'realized_profit_rel_to_realized_cap')) self.realized_profit_rel_to_realized_cap: BlockCountPattern[StoredF32] = BlockCountPattern(client, _m(acc, 'realized_profit_rel_to_realized_cap'))
@@ -1901,7 +1910,7 @@ class Price111dSmaPattern:
def __init__(self, client: BrkClientBase, acc: str): def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated metric name.""" """Create pattern node with accumulated metric name."""
self.price: MetricPattern4[Dollars] = MetricPattern4(client, acc) self.price: _0sdUsdPattern = _0sdUsdPattern(client, acc)
self.ratio: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'ratio')) self.ratio: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'ratio'))
self.ratio_1m_sma: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'ratio_1m_sma')) self.ratio_1m_sma: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'ratio_1m_sma'))
self.ratio_1w_sma: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'ratio_1w_sma')) self.ratio_1w_sma: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'ratio_1w_sma'))
@@ -1909,17 +1918,17 @@ class Price111dSmaPattern:
self.ratio_2y_sd: Ratio1ySdPattern = Ratio1ySdPattern(client, _m(acc, 'ratio_2y')) self.ratio_2y_sd: Ratio1ySdPattern = Ratio1ySdPattern(client, _m(acc, 'ratio_2y'))
self.ratio_4y_sd: Ratio1ySdPattern = Ratio1ySdPattern(client, _m(acc, 'ratio_4y')) self.ratio_4y_sd: Ratio1ySdPattern = Ratio1ySdPattern(client, _m(acc, 'ratio_4y'))
self.ratio_pct1: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'ratio_pct1')) self.ratio_pct1: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'ratio_pct1'))
self.ratio_pct1_usd: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'ratio_pct1_usd')) self.ratio_pct1_usd: _0sdUsdPattern = _0sdUsdPattern(client, _m(acc, 'ratio_pct1_usd'))
self.ratio_pct2: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'ratio_pct2')) self.ratio_pct2: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'ratio_pct2'))
self.ratio_pct2_usd: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'ratio_pct2_usd')) self.ratio_pct2_usd: _0sdUsdPattern = _0sdUsdPattern(client, _m(acc, 'ratio_pct2_usd'))
self.ratio_pct5: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'ratio_pct5')) self.ratio_pct5: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'ratio_pct5'))
self.ratio_pct5_usd: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'ratio_pct5_usd')) self.ratio_pct5_usd: _0sdUsdPattern = _0sdUsdPattern(client, _m(acc, 'ratio_pct5_usd'))
self.ratio_pct95: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'ratio_pct95')) self.ratio_pct95: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'ratio_pct95'))
self.ratio_pct95_usd: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'ratio_pct95_usd')) self.ratio_pct95_usd: _0sdUsdPattern = _0sdUsdPattern(client, _m(acc, 'ratio_pct95_usd'))
self.ratio_pct98: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'ratio_pct98')) self.ratio_pct98: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'ratio_pct98'))
self.ratio_pct98_usd: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'ratio_pct98_usd')) self.ratio_pct98_usd: _0sdUsdPattern = _0sdUsdPattern(client, _m(acc, 'ratio_pct98_usd'))
self.ratio_pct99: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'ratio_pct99')) self.ratio_pct99: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'ratio_pct99'))
self.ratio_pct99_usd: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'ratio_pct99_usd')) self.ratio_pct99_usd: _0sdUsdPattern = _0sdUsdPattern(client, _m(acc, 'ratio_pct99_usd'))
self.ratio_sd: Ratio1ySdPattern = Ratio1ySdPattern(client, _m(acc, 'ratio')) self.ratio_sd: Ratio1ySdPattern = Ratio1ySdPattern(client, _m(acc, 'ratio'))
class ActivePriceRatioPattern: class ActivePriceRatioPattern:
@@ -1934,17 +1943,17 @@ class ActivePriceRatioPattern:
self.ratio_2y_sd: Ratio1ySdPattern = Ratio1ySdPattern(client, _m(acc, '2y')) self.ratio_2y_sd: Ratio1ySdPattern = Ratio1ySdPattern(client, _m(acc, '2y'))
self.ratio_4y_sd: Ratio1ySdPattern = Ratio1ySdPattern(client, _m(acc, '4y')) self.ratio_4y_sd: Ratio1ySdPattern = Ratio1ySdPattern(client, _m(acc, '4y'))
self.ratio_pct1: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'pct1')) self.ratio_pct1: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'pct1'))
self.ratio_pct1_usd: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct1_usd')) self.ratio_pct1_usd: _0sdUsdPattern = _0sdUsdPattern(client, _m(acc, 'pct1_usd'))
self.ratio_pct2: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'pct2')) self.ratio_pct2: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'pct2'))
self.ratio_pct2_usd: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct2_usd')) self.ratio_pct2_usd: _0sdUsdPattern = _0sdUsdPattern(client, _m(acc, 'pct2_usd'))
self.ratio_pct5: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'pct5')) self.ratio_pct5: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'pct5'))
self.ratio_pct5_usd: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct5_usd')) self.ratio_pct5_usd: _0sdUsdPattern = _0sdUsdPattern(client, _m(acc, 'pct5_usd'))
self.ratio_pct95: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'pct95')) self.ratio_pct95: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'pct95'))
self.ratio_pct95_usd: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct95_usd')) self.ratio_pct95_usd: _0sdUsdPattern = _0sdUsdPattern(client, _m(acc, 'pct95_usd'))
self.ratio_pct98: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'pct98')) self.ratio_pct98: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'pct98'))
self.ratio_pct98_usd: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct98_usd')) self.ratio_pct98_usd: _0sdUsdPattern = _0sdUsdPattern(client, _m(acc, 'pct98_usd'))
self.ratio_pct99: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'pct99')) self.ratio_pct99: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'pct99'))
self.ratio_pct99_usd: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct99_usd')) self.ratio_pct99_usd: _0sdUsdPattern = _0sdUsdPattern(client, _m(acc, 'pct99_usd'))
self.ratio_sd: Ratio1ySdPattern = Ratio1ySdPattern(client, acc) self.ratio_sd: Ratio1ySdPattern = Ratio1ySdPattern(client, acc)
class PercentilesPattern: class PercentilesPattern:
@@ -1952,25 +1961,25 @@ class PercentilesPattern:
def __init__(self, client: BrkClientBase, acc: str): def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated metric name.""" """Create pattern node with accumulated metric name."""
self.pct05: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct05')) self.pct05: _0sdUsdPattern = _0sdUsdPattern(client, _m(acc, 'pct05'))
self.pct10: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct10')) self.pct10: _0sdUsdPattern = _0sdUsdPattern(client, _m(acc, 'pct10'))
self.pct15: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct15')) self.pct15: _0sdUsdPattern = _0sdUsdPattern(client, _m(acc, 'pct15'))
self.pct20: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct20')) self.pct20: _0sdUsdPattern = _0sdUsdPattern(client, _m(acc, 'pct20'))
self.pct25: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct25')) self.pct25: _0sdUsdPattern = _0sdUsdPattern(client, _m(acc, 'pct25'))
self.pct30: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct30')) self.pct30: _0sdUsdPattern = _0sdUsdPattern(client, _m(acc, 'pct30'))
self.pct35: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct35')) self.pct35: _0sdUsdPattern = _0sdUsdPattern(client, _m(acc, 'pct35'))
self.pct40: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct40')) self.pct40: _0sdUsdPattern = _0sdUsdPattern(client, _m(acc, 'pct40'))
self.pct45: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct45')) self.pct45: _0sdUsdPattern = _0sdUsdPattern(client, _m(acc, 'pct45'))
self.pct50: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct50')) self.pct50: _0sdUsdPattern = _0sdUsdPattern(client, _m(acc, 'pct50'))
self.pct55: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct55')) self.pct55: _0sdUsdPattern = _0sdUsdPattern(client, _m(acc, 'pct55'))
self.pct60: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct60')) self.pct60: _0sdUsdPattern = _0sdUsdPattern(client, _m(acc, 'pct60'))
self.pct65: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct65')) self.pct65: _0sdUsdPattern = _0sdUsdPattern(client, _m(acc, 'pct65'))
self.pct70: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct70')) self.pct70: _0sdUsdPattern = _0sdUsdPattern(client, _m(acc, 'pct70'))
self.pct75: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct75')) self.pct75: _0sdUsdPattern = _0sdUsdPattern(client, _m(acc, 'pct75'))
self.pct80: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct80')) self.pct80: _0sdUsdPattern = _0sdUsdPattern(client, _m(acc, 'pct80'))
self.pct85: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct85')) self.pct85: _0sdUsdPattern = _0sdUsdPattern(client, _m(acc, 'pct85'))
self.pct90: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct90')) self.pct90: _0sdUsdPattern = _0sdUsdPattern(client, _m(acc, 'pct90'))
self.pct95: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'pct95')) self.pct95: _0sdUsdPattern = _0sdUsdPattern(client, _m(acc, 'pct95'))
class RelativePattern5: class RelativePattern5:
"""Pattern struct for repeated tree structure.""" """Pattern struct for repeated tree structure."""
@@ -2017,25 +2026,6 @@ class AaopoolPattern:
self.fee: UnclaimedRewardsPattern = UnclaimedRewardsPattern(client, _m(acc, 'fee')) self.fee: UnclaimedRewardsPattern = UnclaimedRewardsPattern(client, _m(acc, 'fee'))
self.subsidy: UnclaimedRewardsPattern = UnclaimedRewardsPattern(client, _m(acc, 'subsidy')) self.subsidy: UnclaimedRewardsPattern = UnclaimedRewardsPattern(client, _m(acc, 'subsidy'))
class LookbackPattern(Generic[T]):
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated metric name."""
self._10y: MetricPattern4[T] = MetricPattern4(client, _m(acc, '10y_ago'))
self._1d: MetricPattern4[T] = MetricPattern4(client, _m(acc, '1d_ago'))
self._1m: MetricPattern4[T] = MetricPattern4(client, _m(acc, '1m_ago'))
self._1w: MetricPattern4[T] = MetricPattern4(client, _m(acc, '1w_ago'))
self._1y: MetricPattern4[T] = MetricPattern4(client, _m(acc, '1y_ago'))
self._2y: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2y_ago'))
self._3m: MetricPattern4[T] = MetricPattern4(client, _m(acc, '3m_ago'))
self._3y: MetricPattern4[T] = MetricPattern4(client, _m(acc, '3y_ago'))
self._4y: MetricPattern4[T] = MetricPattern4(client, _m(acc, '4y_ago'))
self._5y: MetricPattern4[T] = MetricPattern4(client, _m(acc, '5y_ago'))
self._6m: MetricPattern4[T] = MetricPattern4(client, _m(acc, '6m_ago'))
self._6y: MetricPattern4[T] = MetricPattern4(client, _m(acc, '6y_ago'))
self._8y: MetricPattern4[T] = MetricPattern4(client, _m(acc, '8y_ago'))
class PeriodLumpSumStackPattern: class PeriodLumpSumStackPattern:
"""Pattern struct for repeated tree structure.""" """Pattern struct for repeated tree structure."""
@@ -2054,25 +2044,25 @@ class PeriodLumpSumStackPattern:
self._6y: _2015Pattern = _2015Pattern(client, _p('6y', acc)) self._6y: _2015Pattern = _2015Pattern(client, _p('6y', acc))
self._8y: _2015Pattern = _2015Pattern(client, _p('8y', acc)) self._8y: _2015Pattern = _2015Pattern(client, _p('8y', acc))
class ClassAveragePricePattern(Generic[T]): class ClassDaysInLossPattern(Generic[T]):
"""Pattern struct for repeated tree structure.""" """Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str): def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated metric name.""" """Create pattern node with accumulated metric name."""
self._2015: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2015_average_price')) self._2015: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2015_max_return'))
self._2016: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2016_average_price')) self._2016: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2016_max_return'))
self._2017: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2017_average_price')) self._2017: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2017_max_return'))
self._2018: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2018_average_price')) self._2018: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2018_max_return'))
self._2019: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2019_average_price')) self._2019: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2019_max_return'))
self._2020: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2020_average_price')) self._2020: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2020_max_return'))
self._2021: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2021_average_price')) self._2021: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2021_max_return'))
self._2022: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2022_average_price')) self._2022: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2022_max_return'))
self._2023: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2023_average_price')) self._2023: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2023_max_return'))
self._2024: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2024_average_price')) self._2024: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2024_max_return'))
self._2025: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2025_average_price')) self._2025: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2025_max_return'))
self._2026: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2026_average_price')) self._2026: MetricPattern4[T] = MetricPattern4(client, _m(acc, '2026_max_return'))
class PeriodAveragePricePattern(Generic[T]): class PeriodDaysInLossPattern(Generic[T]):
"""Pattern struct for repeated tree structure.""" """Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str): def __init__(self, client: BrkClientBase, acc: str):
@@ -2245,7 +2235,20 @@ class PhaseDailyCentsPattern(Generic[T]):
self.pct75: MetricPattern6[T] = MetricPattern6(client, _m(acc, 'pct75')) self.pct75: MetricPattern6[T] = MetricPattern6(client, _m(acc, 'pct75'))
self.pct90: MetricPattern6[T] = MetricPattern6(client, _m(acc, 'pct90')) self.pct90: MetricPattern6[T] = MetricPattern6(client, _m(acc, 'pct90'))
class _0satsPattern2: class UnrealizedPattern:
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated metric name."""
self.neg_unrealized_loss: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'neg_unrealized_loss'))
self.net_unrealized_pnl: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'net_unrealized_pnl'))
self.supply_in_loss: ActiveSupplyPattern = ActiveSupplyPattern(client, _m(acc, 'supply_in_loss'))
self.supply_in_profit: ActiveSupplyPattern = ActiveSupplyPattern(client, _m(acc, 'supply_in_profit'))
self.total_unrealized_pnl: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'total_unrealized_pnl'))
self.unrealized_loss: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'unrealized_loss'))
self.unrealized_profit: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'unrealized_profit'))
class _10yPattern:
"""Pattern struct for repeated tree structure.""" """Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str): def __init__(self, client: BrkClientBase, acc: str):
@@ -2253,8 +2256,8 @@ class _0satsPattern2:
self.activity: ActivityPattern2 = ActivityPattern2(client, acc) self.activity: ActivityPattern2 = ActivityPattern2(client, acc)
self.cost_basis: CostBasisPattern = CostBasisPattern(client, acc) self.cost_basis: CostBasisPattern = CostBasisPattern(client, acc)
self.outputs: OutputsPattern = OutputsPattern(client, _m(acc, 'utxo_count')) self.outputs: OutputsPattern = OutputsPattern(client, _m(acc, 'utxo_count'))
self.realized: RealizedPattern = RealizedPattern(client, acc) self.realized: RealizedPattern4 = RealizedPattern4(client, acc)
self.relative: RelativePattern4 = RelativePattern4(client, _m(acc, 'supply_in')) self.relative: RelativePattern = RelativePattern(client, acc)
self.supply: SupplyPattern2 = SupplyPattern2(client, _m(acc, 'supply')) self.supply: SupplyPattern2 = SupplyPattern2(client, _m(acc, 'supply'))
self.unrealized: UnrealizedPattern = UnrealizedPattern(client, acc) self.unrealized: UnrealizedPattern = UnrealizedPattern(client, acc)
@@ -2271,6 +2274,19 @@ class _100btcPattern:
self.supply: SupplyPattern2 = SupplyPattern2(client, _m(acc, 'supply')) self.supply: SupplyPattern2 = SupplyPattern2(client, _m(acc, 'supply'))
self.unrealized: UnrealizedPattern = UnrealizedPattern(client, acc) self.unrealized: UnrealizedPattern = UnrealizedPattern(client, acc)
class _0satsPattern2:
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated metric name."""
self.activity: ActivityPattern2 = ActivityPattern2(client, acc)
self.cost_basis: CostBasisPattern = CostBasisPattern(client, acc)
self.outputs: OutputsPattern = OutputsPattern(client, _m(acc, 'utxo_count'))
self.realized: RealizedPattern = RealizedPattern(client, acc)
self.relative: RelativePattern4 = RelativePattern4(client, _m(acc, 'supply_in'))
self.supply: SupplyPattern2 = SupplyPattern2(client, _m(acc, 'supply'))
self.unrealized: UnrealizedPattern = UnrealizedPattern(client, acc)
class PeriodCagrPattern: class PeriodCagrPattern:
"""Pattern struct for repeated tree structure.""" """Pattern struct for repeated tree structure."""
@@ -2297,32 +2313,6 @@ class _10yTo12yPattern:
self.supply: SupplyPattern2 = SupplyPattern2(client, _m(acc, 'supply')) self.supply: SupplyPattern2 = SupplyPattern2(client, _m(acc, 'supply'))
self.unrealized: UnrealizedPattern = UnrealizedPattern(client, acc) self.unrealized: UnrealizedPattern = UnrealizedPattern(client, acc)
class _10yPattern:
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated metric name."""
self.activity: ActivityPattern2 = ActivityPattern2(client, acc)
self.cost_basis: CostBasisPattern = CostBasisPattern(client, acc)
self.outputs: OutputsPattern = OutputsPattern(client, _m(acc, 'utxo_count'))
self.realized: RealizedPattern4 = RealizedPattern4(client, acc)
self.relative: RelativePattern = RelativePattern(client, acc)
self.supply: SupplyPattern2 = SupplyPattern2(client, _m(acc, 'supply'))
self.unrealized: UnrealizedPattern = UnrealizedPattern(client, acc)
class UnrealizedPattern:
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated metric name."""
self.neg_unrealized_loss: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'neg_unrealized_loss'))
self.net_unrealized_pnl: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'net_unrealized_pnl'))
self.supply_in_loss: ActiveSupplyPattern = ActiveSupplyPattern(client, _m(acc, 'supply_in_loss'))
self.supply_in_profit: ActiveSupplyPattern = ActiveSupplyPattern(client, _m(acc, 'supply_in_profit'))
self.total_unrealized_pnl: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'total_unrealized_pnl'))
self.unrealized_loss: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'unrealized_loss'))
self.unrealized_profit: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'unrealized_profit'))
class ActivityPattern2: class ActivityPattern2:
"""Pattern struct for repeated tree structure.""" """Pattern struct for repeated tree structure."""
@@ -2349,8 +2339,8 @@ class CostBasisPattern2:
def __init__(self, client: BrkClientBase, acc: str): def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated metric name.""" """Create pattern node with accumulated metric name."""
self.max: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'max_cost_basis')) self.max: ActivePricePattern = ActivePricePattern(client, _m(acc, 'max_cost_basis'))
self.min: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'min_cost_basis')) self.min: ActivePricePattern = ActivePricePattern(client, _m(acc, 'min_cost_basis'))
self.percentiles: PercentilesPattern = PercentilesPattern(client, _m(acc, 'cost_basis')) self.percentiles: PercentilesPattern = PercentilesPattern(client, _m(acc, 'cost_basis'))
class ActiveSupplyPattern: class ActiveSupplyPattern:
@@ -2362,6 +2352,15 @@ class ActiveSupplyPattern:
self.dollars: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'usd')) self.dollars: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'usd'))
self.sats: MetricPattern1[Sats] = MetricPattern1(client, acc) self.sats: MetricPattern1[Sats] = MetricPattern1(client, acc)
class CoinbasePattern:
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated metric name."""
self.bitcoin: BitcoinPattern = BitcoinPattern(client, _m(acc, 'btc'))
self.dollars: DollarsPattern[Dollars] = DollarsPattern(client, _m(acc, 'usd'))
self.sats: DollarsPattern[Sats] = DollarsPattern(client, acc)
class SegwitAdoptionPattern: class SegwitAdoptionPattern:
"""Pattern struct for repeated tree structure.""" """Pattern struct for repeated tree structure."""
@@ -2389,15 +2388,6 @@ class UnclaimedRewardsPattern:
self.dollars: BlockCountPattern[Dollars] = BlockCountPattern(client, _m(acc, 'usd')) self.dollars: BlockCountPattern[Dollars] = BlockCountPattern(client, _m(acc, 'usd'))
self.sats: BlockCountPattern[Sats] = BlockCountPattern(client, acc) self.sats: BlockCountPattern[Sats] = BlockCountPattern(client, acc)
class CoinbasePattern:
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated metric name."""
self.bitcoin: BitcoinPattern = BitcoinPattern(client, _m(acc, 'btc'))
self.dollars: DollarsPattern[Dollars] = DollarsPattern(client, _m(acc, 'usd'))
self.sats: DollarsPattern[Sats] = DollarsPattern(client, acc)
class _2015Pattern: class _2015Pattern:
"""Pattern struct for repeated tree structure.""" """Pattern struct for repeated tree structure."""
@@ -2407,13 +2397,13 @@ class _2015Pattern:
self.dollars: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'usd')) self.dollars: MetricPattern4[Dollars] = MetricPattern4(client, _m(acc, 'usd'))
self.sats: MetricPattern4[Sats] = MetricPattern4(client, acc) self.sats: MetricPattern4[Sats] = MetricPattern4(client, acc)
class RelativePattern4: class _1dReturns1mSdPattern:
"""Pattern struct for repeated tree structure.""" """Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str): def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated metric name.""" """Create pattern node with accumulated metric name."""
self.supply_in_loss_rel_to_own_supply: MetricPattern1[StoredF64] = MetricPattern1(client, _m(acc, 'loss_rel_to_own_supply')) self.sd: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'sd'))
self.supply_in_profit_rel_to_own_supply: MetricPattern1[StoredF64] = MetricPattern1(client, _m(acc, 'profit_rel_to_own_supply')) self.sma: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'sma'))
class SupplyPattern2: class SupplyPattern2:
"""Pattern struct for repeated tree structure.""" """Pattern struct for repeated tree structure."""
@@ -2423,29 +2413,37 @@ class SupplyPattern2:
self.halved: ActiveSupplyPattern = ActiveSupplyPattern(client, _m(acc, 'halved')) self.halved: ActiveSupplyPattern = ActiveSupplyPattern(client, _m(acc, 'halved'))
self.total: ActiveSupplyPattern = ActiveSupplyPattern(client, acc) self.total: ActiveSupplyPattern = ActiveSupplyPattern(client, acc)
class RelativePattern4:
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated metric name."""
self.supply_in_loss_rel_to_own_supply: MetricPattern1[StoredF64] = MetricPattern1(client, _m(acc, 'loss_rel_to_own_supply'))
self.supply_in_profit_rel_to_own_supply: MetricPattern1[StoredF64] = MetricPattern1(client, _m(acc, 'profit_rel_to_own_supply'))
class CostBasisPattern: class CostBasisPattern:
"""Pattern struct for repeated tree structure.""" """Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str): def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated metric name.""" """Create pattern node with accumulated metric name."""
self.max: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'max_cost_basis')) self.max: ActivePricePattern = ActivePricePattern(client, _m(acc, 'max_cost_basis'))
self.min: MetricPattern1[Dollars] = MetricPattern1(client, _m(acc, 'min_cost_basis')) self.min: ActivePricePattern = ActivePricePattern(client, _m(acc, 'min_cost_basis'))
class _1dReturns1mSdPattern: class _0sdUsdPattern:
"""Pattern struct for repeated tree structure.""" """Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str): def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated metric name.""" """Create pattern node with accumulated metric name."""
self.sd: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'sd')) self.dollars: MetricPattern4[Dollars] = MetricPattern4(client, acc)
self.sma: MetricPattern4[StoredF32] = MetricPattern4(client, _m(acc, 'sma')) self.sats: MetricPattern4[SatsFract] = MetricPattern4(client, _m(acc, 'sats'))
class BitcoinPattern2(Generic[T]): class ActivePricePattern:
"""Pattern struct for repeated tree structure.""" """Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str): def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated metric name.""" """Create pattern node with accumulated metric name."""
self.cumulative: MetricPattern2[T] = MetricPattern2(client, _m(acc, 'cumulative')) self.dollars: MetricPattern1[Dollars] = MetricPattern1(client, acc)
self.sum: MetricPattern1[T] = MetricPattern1(client, acc) self.sats: MetricPattern1[SatsFract] = MetricPattern1(client, _m(acc, 'sats'))
class BlockCountPattern(Generic[T]): class BlockCountPattern(Generic[T]):
"""Pattern struct for repeated tree structure.""" """Pattern struct for repeated tree structure."""
@@ -2455,6 +2453,14 @@ class BlockCountPattern(Generic[T]):
self.cumulative: MetricPattern1[T] = MetricPattern1(client, _m(acc, 'cumulative')) self.cumulative: MetricPattern1[T] = MetricPattern1(client, _m(acc, 'cumulative'))
self.sum: MetricPattern1[T] = MetricPattern1(client, acc) self.sum: MetricPattern1[T] = MetricPattern1(client, acc)
class BitcoinPattern2(Generic[T]):
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated metric name."""
self.cumulative: MetricPattern2[T] = MetricPattern2(client, _m(acc, 'cumulative'))
self.sum: MetricPattern1[T] = MetricPattern1(client, acc)
class SatsPattern(Generic[T]): class SatsPattern(Generic[T]):
"""Pattern struct for repeated tree structure.""" """Pattern struct for repeated tree structure."""
@@ -2463,13 +2469,6 @@ class SatsPattern(Generic[T]):
self.ohlc: MetricPattern1[T] = MetricPattern1(client, _m(acc, 'ohlc')) self.ohlc: MetricPattern1[T] = MetricPattern1(client, _m(acc, 'ohlc'))
self.split: SplitPattern2[T] = SplitPattern2(client, acc) self.split: SplitPattern2[T] = SplitPattern2(client, acc)
class RealizedPriceExtraPattern:
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated metric name."""
self.ratio: MetricPattern4[StoredF32] = MetricPattern4(client, acc)
class OutputsPattern: class OutputsPattern:
"""Pattern struct for repeated tree structure.""" """Pattern struct for repeated tree structure."""
@@ -2477,6 +2476,13 @@ class OutputsPattern:
"""Create pattern node with accumulated metric name.""" """Create pattern node with accumulated metric name."""
self.utxo_count: MetricPattern1[StoredU64] = MetricPattern1(client, acc) self.utxo_count: MetricPattern1[StoredU64] = MetricPattern1(client, acc)
class RealizedPriceExtraPattern:
"""Pattern struct for repeated tree structure."""
def __init__(self, client: BrkClientBase, acc: str):
"""Create pattern node with accumulated metric name."""
self.ratio: MetricPattern4[StoredF32] = MetricPattern4(client, acc)
# Metrics tree classes # Metrics tree classes
class MetricsTree_Addresses: class MetricsTree_Addresses:
@@ -2647,13 +2653,13 @@ class MetricsTree_Cointime_Pricing:
"""Metrics tree node.""" """Metrics tree node."""
def __init__(self, client: BrkClientBase, base_path: str = ''): def __init__(self, client: BrkClientBase, base_path: str = ''):
self.active_price: MetricPattern1[Dollars] = MetricPattern1(client, 'active_price') self.active_price: ActivePricePattern = ActivePricePattern(client, 'active_price')
self.active_price_ratio: ActivePriceRatioPattern = ActivePriceRatioPattern(client, 'active_price_ratio') self.active_price_ratio: ActivePriceRatioPattern = ActivePriceRatioPattern(client, 'active_price_ratio')
self.cointime_price: MetricPattern1[Dollars] = MetricPattern1(client, 'cointime_price') self.cointime_price: ActivePricePattern = ActivePricePattern(client, 'cointime_price')
self.cointime_price_ratio: ActivePriceRatioPattern = ActivePriceRatioPattern(client, 'cointime_price_ratio') self.cointime_price_ratio: ActivePriceRatioPattern = ActivePriceRatioPattern(client, 'cointime_price_ratio')
self.true_market_mean: MetricPattern1[Dollars] = MetricPattern1(client, 'true_market_mean') self.true_market_mean: ActivePricePattern = ActivePricePattern(client, 'true_market_mean')
self.true_market_mean_ratio: ActivePriceRatioPattern = ActivePriceRatioPattern(client, 'true_market_mean_ratio') self.true_market_mean_ratio: ActivePriceRatioPattern = ActivePriceRatioPattern(client, 'true_market_mean_ratio')
self.vaulted_price: MetricPattern1[Dollars] = MetricPattern1(client, 'vaulted_price') self.vaulted_price: ActivePricePattern = ActivePricePattern(client, 'vaulted_price')
self.vaulted_price_ratio: ActivePriceRatioPattern = ActivePriceRatioPattern(client, 'vaulted_price_ratio') self.vaulted_price_ratio: ActivePriceRatioPattern = ActivePriceRatioPattern(client, 'vaulted_price_ratio')
class MetricsTree_Cointime_ReserveRisk: class MetricsTree_Cointime_ReserveRisk:
@@ -2829,8 +2835,8 @@ class MetricsTree_Distribution_UtxoCohorts_All_CostBasis:
"""Metrics tree node.""" """Metrics tree node."""
def __init__(self, client: BrkClientBase, base_path: str = ''): def __init__(self, client: BrkClientBase, base_path: str = ''):
self.max: MetricPattern1[Dollars] = MetricPattern1(client, 'max_cost_basis') self.max: ActivePricePattern = ActivePricePattern(client, 'max_cost_basis')
self.min: MetricPattern1[Dollars] = MetricPattern1(client, 'min_cost_basis') self.min: ActivePricePattern = ActivePricePattern(client, 'min_cost_basis')
self.percentiles: PercentilesPattern = PercentilesPattern(client, 'cost_basis') self.percentiles: PercentilesPattern = PercentilesPattern(client, 'cost_basis')
class MetricsTree_Distribution_UtxoCohorts_All_Relative: class MetricsTree_Distribution_UtxoCohorts_All_Relative:
@@ -3316,10 +3322,27 @@ class MetricsTree_Market_Ath:
self.days_since_price_ath: MetricPattern4[StoredU16] = MetricPattern4(client, 'days_since_price_ath') self.days_since_price_ath: MetricPattern4[StoredU16] = MetricPattern4(client, 'days_since_price_ath')
self.max_days_between_price_aths: MetricPattern4[StoredU16] = MetricPattern4(client, 'max_days_between_price_aths') self.max_days_between_price_aths: MetricPattern4[StoredU16] = MetricPattern4(client, 'max_days_between_price_aths')
self.max_years_between_price_aths: MetricPattern4[StoredF32] = MetricPattern4(client, 'max_years_between_price_aths') self.max_years_between_price_aths: MetricPattern4[StoredF32] = MetricPattern4(client, 'max_years_between_price_aths')
self.price_ath: MetricPattern1[Dollars] = MetricPattern1(client, 'price_ath') self.price_ath: ActivePricePattern = ActivePricePattern(client, 'price_ath')
self.price_drawdown: MetricPattern3[StoredF32] = MetricPattern3(client, 'price_drawdown') self.price_drawdown: MetricPattern3[StoredF32] = MetricPattern3(client, 'price_drawdown')
self.years_since_price_ath: MetricPattern4[StoredF32] = MetricPattern4(client, 'years_since_price_ath') self.years_since_price_ath: MetricPattern4[StoredF32] = MetricPattern4(client, 'years_since_price_ath')
class MetricsTree_Market_Dca_ClassAveragePrice:
"""Metrics tree node."""
def __init__(self, client: BrkClientBase, base_path: str = ''):
self._2015: _0sdUsdPattern = _0sdUsdPattern(client, 'dca_class_2015_average_price')
self._2016: _0sdUsdPattern = _0sdUsdPattern(client, 'dca_class_2016_average_price')
self._2017: _0sdUsdPattern = _0sdUsdPattern(client, 'dca_class_2017_average_price')
self._2018: _0sdUsdPattern = _0sdUsdPattern(client, 'dca_class_2018_average_price')
self._2019: _0sdUsdPattern = _0sdUsdPattern(client, 'dca_class_2019_average_price')
self._2020: _0sdUsdPattern = _0sdUsdPattern(client, 'dca_class_2020_average_price')
self._2021: _0sdUsdPattern = _0sdUsdPattern(client, 'dca_class_2021_average_price')
self._2022: _0sdUsdPattern = _0sdUsdPattern(client, 'dca_class_2022_average_price')
self._2023: _0sdUsdPattern = _0sdUsdPattern(client, 'dca_class_2023_average_price')
self._2024: _0sdUsdPattern = _0sdUsdPattern(client, 'dca_class_2024_average_price')
self._2025: _0sdUsdPattern = _0sdUsdPattern(client, 'dca_class_2025_average_price')
self._2026: _0sdUsdPattern = _0sdUsdPattern(client, 'dca_class_2026_average_price')
class MetricsTree_Market_Dca_ClassDaysInLoss: class MetricsTree_Market_Dca_ClassDaysInLoss:
"""Metrics tree node.""" """Metrics tree node."""
@@ -3371,23 +3394,6 @@ class MetricsTree_Market_Dca_ClassMaxDrawdown:
self._2025: MetricPattern4[StoredF32] = MetricPattern4(client, 'dca_class_2025_max_drawdown') self._2025: MetricPattern4[StoredF32] = MetricPattern4(client, 'dca_class_2025_max_drawdown')
self._2026: MetricPattern4[StoredF32] = MetricPattern4(client, 'dca_class_2026_max_drawdown') self._2026: MetricPattern4[StoredF32] = MetricPattern4(client, 'dca_class_2026_max_drawdown')
class MetricsTree_Market_Dca_ClassMaxReturn:
"""Metrics tree node."""
def __init__(self, client: BrkClientBase, base_path: str = ''):
self._2015: MetricPattern4[StoredF32] = MetricPattern4(client, 'dca_class_2015_max_return')
self._2016: MetricPattern4[StoredF32] = MetricPattern4(client, 'dca_class_2016_max_return')
self._2017: MetricPattern4[StoredF32] = MetricPattern4(client, 'dca_class_2017_max_return')
self._2018: MetricPattern4[StoredF32] = MetricPattern4(client, 'dca_class_2018_max_return')
self._2019: MetricPattern4[StoredF32] = MetricPattern4(client, 'dca_class_2019_max_return')
self._2020: MetricPattern4[StoredF32] = MetricPattern4(client, 'dca_class_2020_max_return')
self._2021: MetricPattern4[StoredF32] = MetricPattern4(client, 'dca_class_2021_max_return')
self._2022: MetricPattern4[StoredF32] = MetricPattern4(client, 'dca_class_2022_max_return')
self._2023: MetricPattern4[StoredF32] = MetricPattern4(client, 'dca_class_2023_max_return')
self._2024: MetricPattern4[StoredF32] = MetricPattern4(client, 'dca_class_2024_max_return')
self._2025: MetricPattern4[StoredF32] = MetricPattern4(client, 'dca_class_2025_max_return')
self._2026: MetricPattern4[StoredF32] = MetricPattern4(client, 'dca_class_2026_max_return')
class MetricsTree_Market_Dca_ClassReturns: class MetricsTree_Market_Dca_ClassReturns:
"""Metrics tree node.""" """Metrics tree node."""
@@ -3422,30 +3428,47 @@ class MetricsTree_Market_Dca_ClassStack:
self._2025: _2015Pattern = _2015Pattern(client, 'dca_class_2025_stack') self._2025: _2015Pattern = _2015Pattern(client, 'dca_class_2025_stack')
self._2026: _2015Pattern = _2015Pattern(client, 'dca_class_2026_stack') self._2026: _2015Pattern = _2015Pattern(client, 'dca_class_2026_stack')
class MetricsTree_Market_Dca_PeriodAveragePrice:
"""Metrics tree node."""
def __init__(self, client: BrkClientBase, base_path: str = ''):
self._10y: _0sdUsdPattern = _0sdUsdPattern(client, '10y_dca_average_price')
self._1m: _0sdUsdPattern = _0sdUsdPattern(client, '1m_dca_average_price')
self._1w: _0sdUsdPattern = _0sdUsdPattern(client, '1w_dca_average_price')
self._1y: _0sdUsdPattern = _0sdUsdPattern(client, '1y_dca_average_price')
self._2y: _0sdUsdPattern = _0sdUsdPattern(client, '2y_dca_average_price')
self._3m: _0sdUsdPattern = _0sdUsdPattern(client, '3m_dca_average_price')
self._3y: _0sdUsdPattern = _0sdUsdPattern(client, '3y_dca_average_price')
self._4y: _0sdUsdPattern = _0sdUsdPattern(client, '4y_dca_average_price')
self._5y: _0sdUsdPattern = _0sdUsdPattern(client, '5y_dca_average_price')
self._6m: _0sdUsdPattern = _0sdUsdPattern(client, '6m_dca_average_price')
self._6y: _0sdUsdPattern = _0sdUsdPattern(client, '6y_dca_average_price')
self._8y: _0sdUsdPattern = _0sdUsdPattern(client, '8y_dca_average_price')
class MetricsTree_Market_Dca: class MetricsTree_Market_Dca:
"""Metrics tree node.""" """Metrics tree node."""
def __init__(self, client: BrkClientBase, base_path: str = ''): def __init__(self, client: BrkClientBase, base_path: str = ''):
self.class_average_price: ClassAveragePricePattern[Dollars] = ClassAveragePricePattern(client, 'dca_class') self.class_average_price: MetricsTree_Market_Dca_ClassAveragePrice = MetricsTree_Market_Dca_ClassAveragePrice(client)
self.class_days_in_loss: MetricsTree_Market_Dca_ClassDaysInLoss = MetricsTree_Market_Dca_ClassDaysInLoss(client) self.class_days_in_loss: MetricsTree_Market_Dca_ClassDaysInLoss = MetricsTree_Market_Dca_ClassDaysInLoss(client)
self.class_days_in_profit: MetricsTree_Market_Dca_ClassDaysInProfit = MetricsTree_Market_Dca_ClassDaysInProfit(client) self.class_days_in_profit: MetricsTree_Market_Dca_ClassDaysInProfit = MetricsTree_Market_Dca_ClassDaysInProfit(client)
self.class_max_drawdown: MetricsTree_Market_Dca_ClassMaxDrawdown = MetricsTree_Market_Dca_ClassMaxDrawdown(client) self.class_max_drawdown: MetricsTree_Market_Dca_ClassMaxDrawdown = MetricsTree_Market_Dca_ClassMaxDrawdown(client)
self.class_max_return: MetricsTree_Market_Dca_ClassMaxReturn = MetricsTree_Market_Dca_ClassMaxReturn(client) self.class_max_return: ClassDaysInLossPattern[StoredF32] = ClassDaysInLossPattern(client, 'dca_class')
self.class_returns: MetricsTree_Market_Dca_ClassReturns = MetricsTree_Market_Dca_ClassReturns(client) self.class_returns: MetricsTree_Market_Dca_ClassReturns = MetricsTree_Market_Dca_ClassReturns(client)
self.class_stack: MetricsTree_Market_Dca_ClassStack = MetricsTree_Market_Dca_ClassStack(client) self.class_stack: MetricsTree_Market_Dca_ClassStack = MetricsTree_Market_Dca_ClassStack(client)
self.period_average_price: PeriodAveragePricePattern[Dollars] = PeriodAveragePricePattern(client, 'dca_average_price') self.period_average_price: MetricsTree_Market_Dca_PeriodAveragePrice = MetricsTree_Market_Dca_PeriodAveragePrice(client)
self.period_cagr: PeriodCagrPattern = PeriodCagrPattern(client, 'dca_cagr') self.period_cagr: PeriodCagrPattern = PeriodCagrPattern(client, 'dca_cagr')
self.period_days_in_loss: PeriodAveragePricePattern[StoredU32] = PeriodAveragePricePattern(client, 'dca_days_in_loss') self.period_days_in_loss: PeriodDaysInLossPattern[StoredU32] = PeriodDaysInLossPattern(client, 'dca_days_in_loss')
self.period_days_in_profit: PeriodAveragePricePattern[StoredU32] = PeriodAveragePricePattern(client, 'dca_days_in_profit') self.period_days_in_profit: PeriodDaysInLossPattern[StoredU32] = PeriodDaysInLossPattern(client, 'dca_days_in_profit')
self.period_lump_sum_days_in_loss: PeriodAveragePricePattern[StoredU32] = PeriodAveragePricePattern(client, 'lump_sum_days_in_loss') self.period_lump_sum_days_in_loss: PeriodDaysInLossPattern[StoredU32] = PeriodDaysInLossPattern(client, 'lump_sum_days_in_loss')
self.period_lump_sum_days_in_profit: PeriodAveragePricePattern[StoredU32] = PeriodAveragePricePattern(client, 'lump_sum_days_in_profit') self.period_lump_sum_days_in_profit: PeriodDaysInLossPattern[StoredU32] = PeriodDaysInLossPattern(client, 'lump_sum_days_in_profit')
self.period_lump_sum_max_drawdown: PeriodAveragePricePattern[StoredF32] = PeriodAveragePricePattern(client, 'lump_sum_max_drawdown') self.period_lump_sum_max_drawdown: PeriodDaysInLossPattern[StoredF32] = PeriodDaysInLossPattern(client, 'lump_sum_max_drawdown')
self.period_lump_sum_max_return: PeriodAveragePricePattern[StoredF32] = PeriodAveragePricePattern(client, 'lump_sum_max_return') self.period_lump_sum_max_return: PeriodDaysInLossPattern[StoredF32] = PeriodDaysInLossPattern(client, 'lump_sum_max_return')
self.period_lump_sum_returns: PeriodAveragePricePattern[StoredF32] = PeriodAveragePricePattern(client, 'lump_sum_returns') self.period_lump_sum_returns: PeriodDaysInLossPattern[StoredF32] = PeriodDaysInLossPattern(client, 'lump_sum_returns')
self.period_lump_sum_stack: PeriodLumpSumStackPattern = PeriodLumpSumStackPattern(client, 'lump_sum_stack') self.period_lump_sum_stack: PeriodLumpSumStackPattern = PeriodLumpSumStackPattern(client, 'lump_sum_stack')
self.period_max_drawdown: PeriodAveragePricePattern[StoredF32] = PeriodAveragePricePattern(client, 'dca_max_drawdown') self.period_max_drawdown: PeriodDaysInLossPattern[StoredF32] = PeriodDaysInLossPattern(client, 'dca_max_drawdown')
self.period_max_return: PeriodAveragePricePattern[StoredF32] = PeriodAveragePricePattern(client, 'dca_max_return') self.period_max_return: PeriodDaysInLossPattern[StoredF32] = PeriodDaysInLossPattern(client, 'dca_max_return')
self.period_returns: PeriodAveragePricePattern[StoredF32] = PeriodAveragePricePattern(client, 'dca_returns') self.period_returns: PeriodDaysInLossPattern[StoredF32] = PeriodDaysInLossPattern(client, 'dca_returns')
self.period_stack: PeriodLumpSumStackPattern = PeriodLumpSumStackPattern(client, 'dca_stack') self.period_stack: PeriodLumpSumStackPattern = PeriodLumpSumStackPattern(client, 'dca_stack')
class MetricsTree_Market_Indicators: class MetricsTree_Market_Indicators:
@@ -3472,6 +3495,24 @@ class MetricsTree_Market_Indicators:
self.stoch_rsi_d: MetricPattern6[StoredF32] = MetricPattern6(client, 'stoch_rsi_d') self.stoch_rsi_d: MetricPattern6[StoredF32] = MetricPattern6(client, 'stoch_rsi_d')
self.stoch_rsi_k: MetricPattern6[StoredF32] = MetricPattern6(client, 'stoch_rsi_k') self.stoch_rsi_k: MetricPattern6[StoredF32] = MetricPattern6(client, 'stoch_rsi_k')
class MetricsTree_Market_Lookback:
"""Metrics tree node."""
def __init__(self, client: BrkClientBase, base_path: str = ''):
self._10y: _0sdUsdPattern = _0sdUsdPattern(client, 'price_10y_ago')
self._1d: _0sdUsdPattern = _0sdUsdPattern(client, 'price_1d_ago')
self._1m: _0sdUsdPattern = _0sdUsdPattern(client, 'price_1m_ago')
self._1w: _0sdUsdPattern = _0sdUsdPattern(client, 'price_1w_ago')
self._1y: _0sdUsdPattern = _0sdUsdPattern(client, 'price_1y_ago')
self._2y: _0sdUsdPattern = _0sdUsdPattern(client, 'price_2y_ago')
self._3m: _0sdUsdPattern = _0sdUsdPattern(client, 'price_3m_ago')
self._3y: _0sdUsdPattern = _0sdUsdPattern(client, 'price_3y_ago')
self._4y: _0sdUsdPattern = _0sdUsdPattern(client, 'price_4y_ago')
self._5y: _0sdUsdPattern = _0sdUsdPattern(client, 'price_5y_ago')
self._6m: _0sdUsdPattern = _0sdUsdPattern(client, 'price_6m_ago')
self._6y: _0sdUsdPattern = _0sdUsdPattern(client, 'price_6y_ago')
self._8y: _0sdUsdPattern = _0sdUsdPattern(client, 'price_8y_ago')
class MetricsTree_Market_MovingAverage: class MetricsTree_Market_MovingAverage:
"""Metrics tree node.""" """Metrics tree node."""
@@ -3490,8 +3531,8 @@ class MetricsTree_Market_MovingAverage:
self.price_1y_sma: Price111dSmaPattern = Price111dSmaPattern(client, 'price_1y_sma') self.price_1y_sma: Price111dSmaPattern = Price111dSmaPattern(client, 'price_1y_sma')
self.price_200d_ema: Price111dSmaPattern = Price111dSmaPattern(client, 'price_200d_ema') self.price_200d_ema: Price111dSmaPattern = Price111dSmaPattern(client, 'price_200d_ema')
self.price_200d_sma: Price111dSmaPattern = Price111dSmaPattern(client, 'price_200d_sma') self.price_200d_sma: Price111dSmaPattern = Price111dSmaPattern(client, 'price_200d_sma')
self.price_200d_sma_x0_8: MetricPattern4[Dollars] = MetricPattern4(client, 'price_200d_sma_x0_8') self.price_200d_sma_x0_8: _0sdUsdPattern = _0sdUsdPattern(client, 'price_200d_sma_x0_8')
self.price_200d_sma_x2_4: MetricPattern4[Dollars] = MetricPattern4(client, 'price_200d_sma_x2_4') self.price_200d_sma_x2_4: _0sdUsdPattern = _0sdUsdPattern(client, 'price_200d_sma_x2_4')
self.price_200w_ema: Price111dSmaPattern = Price111dSmaPattern(client, 'price_200w_ema') self.price_200w_ema: Price111dSmaPattern = Price111dSmaPattern(client, 'price_200w_ema')
self.price_200w_sma: Price111dSmaPattern = Price111dSmaPattern(client, 'price_200w_sma') self.price_200w_sma: Price111dSmaPattern = Price111dSmaPattern(client, 'price_200w_sma')
self.price_21d_ema: Price111dSmaPattern = Price111dSmaPattern(client, 'price_21d_ema') self.price_21d_ema: Price111dSmaPattern = Price111dSmaPattern(client, 'price_21d_ema')
@@ -3502,7 +3543,7 @@ class MetricsTree_Market_MovingAverage:
self.price_34d_ema: Price111dSmaPattern = Price111dSmaPattern(client, 'price_34d_ema') self.price_34d_ema: Price111dSmaPattern = Price111dSmaPattern(client, 'price_34d_ema')
self.price_34d_sma: Price111dSmaPattern = Price111dSmaPattern(client, 'price_34d_sma') self.price_34d_sma: Price111dSmaPattern = Price111dSmaPattern(client, 'price_34d_sma')
self.price_350d_sma: Price111dSmaPattern = Price111dSmaPattern(client, 'price_350d_sma') self.price_350d_sma: Price111dSmaPattern = Price111dSmaPattern(client, 'price_350d_sma')
self.price_350d_sma_x2: MetricPattern4[Dollars] = MetricPattern4(client, 'price_350d_sma_x2') self.price_350d_sma_x2: _0sdUsdPattern = _0sdUsdPattern(client, 'price_350d_sma_x2')
self.price_4y_ema: Price111dSmaPattern = Price111dSmaPattern(client, 'price_4y_ema') self.price_4y_ema: Price111dSmaPattern = Price111dSmaPattern(client, 'price_4y_ema')
self.price_4y_sma: Price111dSmaPattern = Price111dSmaPattern(client, 'price_4y_sma') self.price_4y_sma: Price111dSmaPattern = Price111dSmaPattern(client, 'price_4y_sma')
self.price_55d_ema: Price111dSmaPattern = Price111dSmaPattern(client, 'price_55d_ema') self.price_55d_ema: Price111dSmaPattern = Price111dSmaPattern(client, 'price_55d_ema')
@@ -3516,15 +3557,15 @@ class MetricsTree_Market_Range:
"""Metrics tree node.""" """Metrics tree node."""
def __init__(self, client: BrkClientBase, base_path: str = ''): def __init__(self, client: BrkClientBase, base_path: str = ''):
self.price_1m_max: MetricPattern4[Dollars] = MetricPattern4(client, 'price_1m_max') self.price_1m_max: _0sdUsdPattern = _0sdUsdPattern(client, 'price_1m_max')
self.price_1m_min: MetricPattern4[Dollars] = MetricPattern4(client, 'price_1m_min') self.price_1m_min: _0sdUsdPattern = _0sdUsdPattern(client, 'price_1m_min')
self.price_1w_max: MetricPattern4[Dollars] = MetricPattern4(client, 'price_1w_max') self.price_1w_max: _0sdUsdPattern = _0sdUsdPattern(client, 'price_1w_max')
self.price_1w_min: MetricPattern4[Dollars] = MetricPattern4(client, 'price_1w_min') self.price_1w_min: _0sdUsdPattern = _0sdUsdPattern(client, 'price_1w_min')
self.price_1y_max: MetricPattern4[Dollars] = MetricPattern4(client, 'price_1y_max') self.price_1y_max: _0sdUsdPattern = _0sdUsdPattern(client, 'price_1y_max')
self.price_1y_min: MetricPattern4[Dollars] = MetricPattern4(client, 'price_1y_min') self.price_1y_min: _0sdUsdPattern = _0sdUsdPattern(client, 'price_1y_min')
self.price_2w_choppiness_index: MetricPattern4[StoredF32] = MetricPattern4(client, 'price_2w_choppiness_index') self.price_2w_choppiness_index: MetricPattern4[StoredF32] = MetricPattern4(client, 'price_2w_choppiness_index')
self.price_2w_max: MetricPattern4[Dollars] = MetricPattern4(client, 'price_2w_max') self.price_2w_max: _0sdUsdPattern = _0sdUsdPattern(client, 'price_2w_max')
self.price_2w_min: MetricPattern4[Dollars] = MetricPattern4(client, 'price_2w_min') self.price_2w_min: _0sdUsdPattern = _0sdUsdPattern(client, 'price_2w_min')
self.price_true_range: MetricPattern6[StoredF32] = MetricPattern6(client, 'price_true_range') self.price_true_range: MetricPattern6[StoredF32] = MetricPattern6(client, 'price_true_range')
self.price_true_range_2w_sum: MetricPattern6[StoredF32] = MetricPattern6(client, 'price_true_range_2w_sum') self.price_true_range_2w_sum: MetricPattern6[StoredF32] = MetricPattern6(client, 'price_true_range_2w_sum')
@@ -3581,7 +3622,7 @@ class MetricsTree_Market:
self.ath: MetricsTree_Market_Ath = MetricsTree_Market_Ath(client) self.ath: MetricsTree_Market_Ath = MetricsTree_Market_Ath(client)
self.dca: MetricsTree_Market_Dca = MetricsTree_Market_Dca(client) self.dca: MetricsTree_Market_Dca = MetricsTree_Market_Dca(client)
self.indicators: MetricsTree_Market_Indicators = MetricsTree_Market_Indicators(client) self.indicators: MetricsTree_Market_Indicators = MetricsTree_Market_Indicators(client)
self.lookback: LookbackPattern[Dollars] = LookbackPattern(client, 'price') self.lookback: MetricsTree_Market_Lookback = MetricsTree_Market_Lookback(client)
self.moving_average: MetricsTree_Market_MovingAverage = MetricsTree_Market_MovingAverage(client) self.moving_average: MetricsTree_Market_MovingAverage = MetricsTree_Market_MovingAverage(client)
self.range: MetricsTree_Market_Range = MetricsTree_Market_Range(client) self.range: MetricsTree_Market_Range = MetricsTree_Market_Range(client)
self.returns: MetricsTree_Market_Returns = MetricsTree_Market_Returns(client) self.returns: MetricsTree_Market_Returns = MetricsTree_Market_Returns(client)
@@ -4023,7 +4064,7 @@ class MetricsTree:
class BrkClient(BrkClientBase): class BrkClient(BrkClientBase):
"""Main BRK client with metrics tree and API methods.""" """Main BRK client with metrics tree and API methods."""
VERSION = "v0.1.0-beta.0" VERSION = "v0.1.0-beta.1"
INDEXES = [ INDEXES = [
"dateindex", "dateindex",
+1 -1
View File
@@ -1,6 +1,6 @@
[project] [project]
name = "brk-client" name = "brk-client"
version = "0.1.0-beta.0" version = "0.1.0-beta.1"
description = "Python client for the Bitcoin Research Kit" description = "Python client for the Bitcoin Research Kit"
readme = "README.md" readme = "README.md"
requires-python = ">=3.9" requires-python = ">=3.9"
+262 -146
View File
@@ -5,6 +5,36 @@ import { priceLine } from "./constants.js";
import { line, baseline, dots } from "./series.js"; import { line, baseline, dots } from "./series.js";
import { satsBtcUsd } from "./shared.js"; import { satsBtcUsd } from "./shared.js";
/** Major pools to show in Compare section (by current hashrate dominance) */
const MAJOR_POOL_IDS = [
"foundryusa", // ~32% - largest pool
"antpool", // ~18% - Bitmain-owned
"viabtc", // ~14% - independent
"f2pool", // ~10% - one of the oldest pools
"marapool", // MARA Holdings
"braiinspool", // formerly Slush Pool
"spiderpool", // growing Asian pool
"ocean", // decentralization-focused
];
/**
* AntPool & friends - pools sharing AntPool's block templates
* Based on b10c's research: https://b10c.me/blog/015-bitcoin-mining-centralization/
* Collectively ~35-40% of network hashrate
*/
const ANTPOOL_AND_FRIENDS_IDS = [
"antpool", // Bitmain-owned, template source
"poolin", // shares AntPool templates
"btccom", // CloverPool (formerly BTC.com)
"braiinspool", // shares AntPool templates
"ultimuspool", // shares AntPool templates
"binancepool", // shares AntPool templates
"secpool", // shares AntPool templates
"sigmapoolcom", // SigmaPool
"rawpool", // shares AntPool templates
"luxor", // shares AntPool templates
];
/** /**
* Create Chain section * Create Chain section
* @param {PartialContext} ctx * @param {PartialContext} ctx
@@ -77,7 +107,7 @@ export function createChainSection(ctx) {
}), }),
line({ line({
metric: pool.dominance, metric: pool.dominance,
name: "all time", name: "All Time",
color: colors.teal, color: colors.teal,
unit: Unit.percentage, unit: Unit.percentage,
defaultActive: false, defaultActive: false,
@@ -233,7 +263,7 @@ export function createChainSection(ctx) {
...fromSizePattern(blocks.size, Unit.bytes), ...fromSizePattern(blocks.size, Unit.bytes),
line({ line({
metric: blocks.totalSize, metric: blocks.totalSize,
name: "total", name: "Total",
color: colors.purple, color: colors.purple,
unit: Unit.bytes, unit: Unit.bytes,
defaultActive: false, defaultActive: false,
@@ -242,14 +272,14 @@ export function createChainSection(ctx) {
...fromFullnessPattern(blocks.weight, Unit.wu), ...fromFullnessPattern(blocks.weight, Unit.wu),
line({ line({
metric: blocks.weight.sum, metric: blocks.weight.sum,
name: "sum", name: "Sum",
color: colors.stat.sum, color: colors.stat.sum,
unit: Unit.wu, unit: Unit.wu,
defaultActive: false, defaultActive: false,
}), }),
line({ line({
metric: blocks.weight.cumulative, metric: blocks.weight.cumulative,
name: "cumulative", name: "Cumulative",
color: colors.stat.cumulative, color: colors.stat.cumulative,
unit: Unit.wu, unit: Unit.wu,
defaultActive: false, defaultActive: false,
@@ -297,27 +327,7 @@ export function createChainSection(ctx) {
defaultActive: false, defaultActive: false,
}, },
), ),
line({ ...satsBtcUsd(transactions.volume.annualizedVolume, "Annualized", colors.red, { defaultActive: false }),
metric: transactions.volume.annualizedVolume.bitcoin,
name: "annualized",
color: colors.red,
unit: Unit.btc,
defaultActive: false,
}),
line({
metric: transactions.volume.annualizedVolume.sats,
name: "annualized",
color: colors.red,
unit: Unit.sats,
defaultActive: false,
}),
line({
metric: transactions.volume.annualizedVolume.dollars,
name: "annualized",
color: colors.lime,
unit: Unit.usd,
defaultActive: false,
}),
], ],
}, },
{ {
@@ -366,12 +376,12 @@ export function createChainSection(ctx) {
bottom: [ bottom: [
line({ line({
metric: supply.velocity.btc, metric: supply.velocity.btc,
name: "bitcoin", name: "Bitcoin",
unit: Unit.ratio, unit: Unit.ratio,
}), }),
line({ line({
metric: supply.velocity.usd, metric: supply.velocity.usd,
name: "dollars", name: "Dollars",
color: colors.emerald, color: colors.emerald,
unit: Unit.ratio, unit: Unit.ratio,
}), }),
@@ -489,18 +499,18 @@ export function createChainSection(ctx) {
name: "SegWit", name: "SegWit",
title: "SegWit Adoption", title: "SegWit Adoption",
bottom: [ bottom: [
line({ metric: scripts.count.segwitAdoption.base, name: "base", unit: Unit.percentage }), line({ metric: scripts.count.segwitAdoption.base, name: "Base", unit: Unit.percentage }),
line({ metric: scripts.count.segwitAdoption.sum, name: "sum", color: colors.stat.sum, unit: Unit.percentage }), line({ metric: scripts.count.segwitAdoption.sum, name: "Sum", color: colors.stat.sum, unit: Unit.percentage }),
line({ metric: scripts.count.segwitAdoption.cumulative, name: "cumulative", color: colors.stat.cumulative, unit: Unit.percentage, defaultActive: false }), line({ metric: scripts.count.segwitAdoption.cumulative, name: "Cumulative", color: colors.stat.cumulative, unit: Unit.percentage, defaultActive: false }),
], ],
}, },
{ {
name: "Taproot", name: "Taproot",
title: "Taproot Adoption", title: "Taproot Adoption",
bottom: [ bottom: [
line({ metric: scripts.count.taprootAdoption.base, name: "base", unit: Unit.percentage }), line({ metric: scripts.count.taprootAdoption.base, name: "Base", unit: Unit.percentage }),
line({ metric: scripts.count.taprootAdoption.sum, name: "sum", color: colors.stat.sum, unit: Unit.percentage }), line({ metric: scripts.count.taprootAdoption.sum, name: "Sum", color: colors.stat.sum, unit: Unit.percentage }),
line({ metric: scripts.count.taprootAdoption.cumulative, name: "cumulative", color: colors.stat.cumulative, unit: Unit.percentage, defaultActive: false }), line({ metric: scripts.count.taprootAdoption.cumulative, name: "Cumulative", color: colors.stat.cumulative, unit: Unit.percentage, defaultActive: false }),
], ],
}, },
], ],
@@ -750,6 +760,7 @@ export function createChainSection(ctx) {
{ {
name: "Mining", name: "Mining",
tree: [ tree: [
// Hashrate
{ {
name: "Hashrate", name: "Hashrate",
title: "Network Hashrate", title: "Network Hashrate",
@@ -796,123 +807,145 @@ export function createChainSection(ctx) {
}), }),
], ],
}, },
// Difficulty group
{ {
name: "Difficulty", name: "Difficulty",
title: "Network Difficulty", tree: [
bottom: [ {
line({ name: "Level",
metric: blocks.difficulty.raw, title: "Network Difficulty",
name: "Difficulty", bottom: [
unit: Unit.difficulty, line({
}), metric: blocks.difficulty.raw,
line({ name: "Difficulty",
metric: blocks.difficulty.epoch, unit: Unit.difficulty,
name: "Epoch", }),
color: colors.teal, line({
unit: Unit.epoch, metric: blocks.difficulty.epoch,
}), name: "Epoch",
line({ color: colors.teal,
metric: blocks.difficulty.blocksBeforeNextAdjustment, unit: Unit.epoch,
name: "before next", }),
color: colors.indigo, ],
unit: Unit.blocks, },
}), {
line({ name: "Adjustment",
metric: blocks.difficulty.daysBeforeNextAdjustment, title: "Difficulty Adjustment",
name: "before next", bottom: [
color: colors.purple, baseline({
unit: Unit.days, metric: blocks.difficulty.adjustment,
}), name: "Difficulty Change",
unit: Unit.percentage,
}),
priceLine({ ctx, number: 0, unit: Unit.percentage }),
],
},
{
name: "Countdown",
title: "Next Adjustment",
bottom: [
line({
metric: blocks.difficulty.blocksBeforeNextAdjustment,
name: "Before Next",
color: colors.indigo,
unit: Unit.blocks,
}),
line({
metric: blocks.difficulty.daysBeforeNextAdjustment,
name: "Before Next",
color: colors.purple,
unit: Unit.days,
}),
],
},
], ],
}, },
// Economics group
{ {
name: "Adjustment", name: "Economics",
title: "Difficulty Adjustment", tree: [
bottom: [ {
baseline({ name: "Hash Price",
metric: blocks.difficulty.adjustment, title: "Hash Price",
name: "Difficulty Change", bottom: [
unit: Unit.percentage, line({
}), metric: blocks.mining.hashPriceThs,
priceLine({ ctx, number: 0, unit: Unit.percentage }), name: "TH/s",
], color: colors.emerald,
}, unit: Unit.usdPerThsPerDay,
{ }),
name: "Hash Price", line({
title: "Hash Price", metric: blocks.mining.hashPricePhs,
bottom: [ name: "PH/s",
line({ color: colors.emerald,
metric: blocks.mining.hashPriceThs, unit: Unit.usdPerPhsPerDay,
name: "TH/s", }),
color: colors.emerald, line({
unit: Unit.usdPerThsPerDay, metric: blocks.mining.hashPriceRebound,
}), name: "Rebound",
line({ color: colors.yellow,
metric: blocks.mining.hashPricePhs, unit: Unit.percentage,
name: "PH/s", }),
color: colors.emerald, line({
unit: Unit.usdPerPhsPerDay, metric: blocks.mining.hashPriceThsMin,
}), name: "TH/s Min",
line({ color: colors.red,
metric: blocks.mining.hashPriceRebound, unit: Unit.usdPerThsPerDay,
name: "Rebound", options: { lineStyle: 1 },
color: colors.yellow, }),
unit: Unit.percentage, line({
}), metric: blocks.mining.hashPricePhsMin,
line({ name: "PH/s Min",
metric: blocks.mining.hashPriceThsMin, color: colors.red,
name: "TH/s Min", unit: Unit.usdPerPhsPerDay,
color: colors.red, options: { lineStyle: 1 },
unit: Unit.usdPerThsPerDay, }),
options: { lineStyle: 1 }, ],
}), },
line({ {
metric: blocks.mining.hashPricePhsMin, name: "Hash Value",
name: "PH/s Min", title: "Hash Value",
color: colors.red, bottom: [
unit: Unit.usdPerPhsPerDay, line({
options: { lineStyle: 1 }, metric: blocks.mining.hashValueThs,
}), name: "TH/s",
], color: colors.orange,
}, unit: Unit.satsPerThsPerDay,
{ }),
name: "Hash Value", line({
title: "Hash Value", metric: blocks.mining.hashValuePhs,
bottom: [ name: "PH/s",
line({ color: colors.orange,
metric: blocks.mining.hashValueThs, unit: Unit.satsPerPhsPerDay,
name: "TH/s", }),
color: colors.orange, line({
unit: Unit.satsPerThsPerDay, metric: blocks.mining.hashValueRebound,
}), name: "Rebound",
line({ color: colors.yellow,
metric: blocks.mining.hashValuePhs, unit: Unit.percentage,
name: "PH/s", }),
color: colors.orange, line({
unit: Unit.satsPerPhsPerDay, metric: blocks.mining.hashValueThsMin,
}), name: "TH/s Min",
line({ color: colors.red,
metric: blocks.mining.hashValueRebound, unit: Unit.satsPerThsPerDay,
name: "Rebound", options: { lineStyle: 1 },
color: colors.yellow, }),
unit: Unit.percentage, line({
}), metric: blocks.mining.hashValuePhsMin,
line({ name: "PH/s Min",
metric: blocks.mining.hashValueThsMin, color: colors.red,
name: "TH/s Min", unit: Unit.satsPerPhsPerDay,
color: colors.red, options: { lineStyle: 1 },
unit: Unit.satsPerThsPerDay, }),
options: { lineStyle: 1 }, ],
}), },
line({
metric: blocks.mining.hashValuePhsMin,
name: "PH/s Min",
color: colors.red,
unit: Unit.satsPerPhsPerDay,
options: { lineStyle: 1 },
}),
], ],
}, },
// Halving (at top level for quick access)
{ {
name: "Halving", name: "Halving",
title: "Halving", title: "Halving",
@@ -925,12 +958,12 @@ export function createChainSection(ctx) {
}), }),
line({ line({
metric: blocks.halving.blocksBeforeNextHalving, metric: blocks.halving.blocksBeforeNextHalving,
name: "before next", name: "Before Next",
unit: Unit.blocks, unit: Unit.blocks,
}), }),
line({ line({
metric: blocks.halving.daysBeforeNextHalving, metric: blocks.halving.daysBeforeNextHalving,
name: "before next", name: "Before Next",
color: colors.blue, color: colors.blue,
unit: Unit.days, unit: Unit.days,
}), }),
@@ -942,7 +975,90 @@ export function createChainSection(ctx) {
// Pools // Pools
{ {
name: "Pools", name: "Pools",
tree: poolsTree, tree: [
// Compare section (major pools only)
{
name: "Compare",
tree: [
{
name: "Dominance",
title: "Pool Dominance (Major Pools)",
bottom: poolEntries
.filter(([key]) => MAJOR_POOL_IDS.includes(key.toLowerCase()))
.map(([key, pool]) => {
const poolName =
brk.POOL_ID_TO_POOL_NAME[
/** @type {keyof typeof brk.POOL_ID_TO_POOL_NAME} */ (key.toLowerCase())
] || key;
return line({
metric: pool._1mDominance,
name: poolName,
unit: Unit.percentage,
});
}),
},
{
name: "Blocks Mined",
title: "Blocks Mined - 1m (Major Pools)",
bottom: poolEntries
.filter(([key]) => MAJOR_POOL_IDS.includes(key.toLowerCase()))
.map(([key, pool]) => {
const poolName =
brk.POOL_ID_TO_POOL_NAME[
/** @type {keyof typeof brk.POOL_ID_TO_POOL_NAME} */ (key.toLowerCase())
] || key;
return line({
metric: pool._1mBlocksMined,
name: poolName,
unit: Unit.count,
});
}),
},
],
},
// AntPool & friends - pools sharing block templates
{
name: "AntPool & Friends",
tree: [
{
name: "Dominance",
title: "AntPool & Friends Dominance",
bottom: poolEntries
.filter(([key]) => ANTPOOL_AND_FRIENDS_IDS.includes(key.toLowerCase()))
.map(([key, pool]) => {
const poolName =
brk.POOL_ID_TO_POOL_NAME[
/** @type {keyof typeof brk.POOL_ID_TO_POOL_NAME} */ (key.toLowerCase())
] || key;
return line({
metric: pool._1mDominance,
name: poolName,
unit: Unit.percentage,
});
}),
},
{
name: "Blocks Mined",
title: "AntPool & Friends Blocks Mined (1m)",
bottom: poolEntries
.filter(([key]) => ANTPOOL_AND_FRIENDS_IDS.includes(key.toLowerCase()))
.map(([key, pool]) => {
const poolName =
brk.POOL_ID_TO_POOL_NAME[
/** @type {keyof typeof brk.POOL_ID_TO_POOL_NAME} */ (key.toLowerCase())
] || key;
return line({
metric: pool._1mBlocksMined,
name: poolName,
unit: Unit.count,
});
}),
},
],
},
// Individual pools
...poolsTree,
],
}, },
], ],
}; };
+15 -15
View File
@@ -1,5 +1,5 @@
import { Unit } from "../utils/units.js"; import { Unit } from "../utils/units.js";
import { line } from "./series.js"; import { line, price } from "./series.js";
import { import {
satsBtcUsd, satsBtcUsd,
createRatioChart, createRatioChart,
@@ -13,23 +13,23 @@ import {
* @param {Object} args * @param {Object} args
* @param {string} args.title * @param {string} args.title
* @param {string} args.legend * @param {string} args.legend
* @param {AnyMetricPattern} args.price * @param {AnyPricePattern} args.pricePattern
* @param {ActivePriceRatioPattern} args.ratio * @param {ActivePriceRatioPattern} args.ratio
* @param {Color} args.color * @param {Color} args.color
* @returns {PartialOptionsTree} * @returns {PartialOptionsTree}
*/ */
function createCointimePriceWithRatioOptions( function createCointimePriceWithRatioOptions(
ctx, ctx,
{ title, legend, price, ratio, color }, { title, legend, pricePattern, ratio, color },
) { ) {
return [ return [
{ {
name: "Price", name: "Price",
title, title,
top: [line({ metric: price, name: legend, color, unit: Unit.usd })], top: [price({ metric: pricePattern, name: legend, color })],
}, },
createRatioChart(ctx, { title: formatCohortTitle(title), price, ratio, color }), createRatioChart(ctx, { title: formatCohortTitle(title), pricePattern, ratio, color }),
createZScoresFolder(ctx, { title, legend, price, ratio, color }), createZScoresFolder(ctx, { title, legend, pricePattern, ratio, color }),
]; ];
} }
@@ -55,28 +55,28 @@ export function createCointimeSection(ctx) {
// Cointime prices data // Cointime prices data
const cointimePrices = [ const cointimePrices = [
{ {
price: pricing.trueMarketMean, pricePattern: pricing.trueMarketMean,
ratio: pricing.trueMarketMeanRatio, ratio: pricing.trueMarketMeanRatio,
name: "True market mean", name: "True Market Mean",
title: "True Market Mean", title: "True Market Mean",
color: colors.blue, color: colors.blue,
}, },
{ {
price: pricing.vaultedPrice, pricePattern: pricing.vaultedPrice,
ratio: pricing.vaultedPriceRatio, ratio: pricing.vaultedPriceRatio,
name: "Vaulted", name: "Vaulted",
title: "Vaulted Price", title: "Vaulted Price",
color: colors.lime, color: colors.lime,
}, },
{ {
price: pricing.activePrice, pricePattern: pricing.activePrice,
ratio: pricing.activePriceRatio, ratio: pricing.activePriceRatio,
name: "Active", name: "Active",
title: "Active Price", title: "Active Price",
color: colors.rose, color: colors.rose,
}, },
{ {
price: pricing.cointimePrice, pricePattern: pricing.cointimePrice,
ratio: pricing.cointimePriceRatio, ratio: pricing.cointimePriceRatio,
name: "Cointime", name: "Cointime",
title: "Cointime Price", title: "Cointime Price",
@@ -128,14 +128,14 @@ export function createCointimeSection(ctx) {
{ {
name: "Compare", name: "Compare",
title: "Cointime Prices", title: "Cointime Prices",
top: cointimePrices.map(({ price, name, color }) => top: cointimePrices.map(({ pricePattern, name, color }) =>
line({ metric: price, name, color, unit: Unit.usd }), price({ metric: pricePattern, name, color }),
), ),
}, },
...cointimePrices.map(({ price, ratio, name, color, title }) => ({ ...cointimePrices.map(({ pricePattern, ratio, name, color, title }) => ({
name, name,
tree: createCointimePriceWithRatioOptions(ctx, { tree: createCointimePriceWithRatioOptions(ctx, {
price, pricePattern,
ratio, ratio,
legend: name, legend: name,
color, color,
+29 -77
View File
@@ -6,7 +6,7 @@
import { Unit } from "../../utils/units.js"; import { Unit } from "../../utils/units.js";
import { priceLine } from "../constants.js"; import { priceLine } from "../constants.js";
import { line, baseline } from "../series.js"; import { line, baseline, price } from "../series.js";
import { formatCohortTitle } from "../shared.js"; import { formatCohortTitle } from "../shared.js";
import { import {
createSingleSupplySeries, createSingleSupplySeries,
@@ -24,6 +24,9 @@ import {
createGroupedSentDollarsSeries, createGroupedSentDollarsSeries,
groupedSupplyRelativeGenerators, groupedSupplyRelativeGenerators,
createSingleSupplyRelativeOptions, createSingleSupplyRelativeOptions,
createSingleSellSideRiskSeries,
createSingleValueCreatedDestroyedSeries,
createSingleSoprSeries,
} from "./shared.js"; } from "./shared.js";
/** /**
@@ -51,10 +54,17 @@ export function createAddressCohortFolder(ctx, args) {
bottom: createSingleSupplySeries( bottom: createSingleSupplySeries(
ctx, ctx,
/** @type {AddressCohortObject} */ (args), /** @type {AddressCohortObject} */ (args),
createSingleSupplyRelativeOptions(ctx, /** @type {AddressCohortObject} */ (args)), createSingleSupplyRelativeOptions(
ctx,
/** @type {AddressCohortObject} */ (args),
),
), ),
} }
: createGroupedSupplySection(list, title, groupedSupplyRelativeGenerators), : createGroupedSupplySection(
list,
title,
groupedSupplyRelativeGenerators,
),
// UTXO count // UTXO count
{ {
@@ -144,11 +154,10 @@ function createRealizedPriceOptions(args, title) {
name: "Price", name: "Price",
title: title("Realized Price"), title: title("Realized Price"),
top: [ top: [
line({ price({
metric: tree.realized.realizedPrice, metric: tree.realized.realizedPrice,
name: "Realized", name: "Realized",
color, color,
unit: Unit.usd,
}), }),
], ],
}, },
@@ -177,7 +186,7 @@ function createRealizedCapWithExtras(ctx, list, args, useGroupName) {
? [ ? [
baseline({ baseline({
metric: tree.realized.realizedCap30dDelta, metric: tree.realized.realizedCap30dDelta,
name: "1m Change", name: "30d Change",
unit: Unit.usd, unit: Unit.usd,
defaultActive: false, defaultActive: false,
}), }),
@@ -295,7 +304,7 @@ function createRealizedPnlSection(ctx, args, title) {
}), }),
baseline({ baseline({
metric: realized.netRealizedPnlCumulative30dDelta, metric: realized.netRealizedPnlCumulative30dDelta,
name: "Cumulative 1m Change", name: "Cumulative 30d Change",
unit: Unit.usd, unit: Unit.usd,
defaultActive: false, defaultActive: false,
}), }),
@@ -312,13 +321,13 @@ function createRealizedPnlSection(ctx, args, title) {
}), }),
baseline({ baseline({
metric: realized.netRealizedPnlCumulative30dDeltaRelToRealizedCap, metric: realized.netRealizedPnlCumulative30dDeltaRelToRealizedCap,
name: "Cumulative 1m Change", name: "Cumulative 30d Change",
unit: Unit.pctRcap, unit: Unit.pctRcap,
defaultActive: false, defaultActive: false,
}), }),
baseline({ baseline({
metric: realized.netRealizedPnlCumulative30dDeltaRelToMarketCap, metric: realized.netRealizedPnlCumulative30dDeltaRelToMarketCap,
name: "Cumulative 1m Change", name: "Cumulative 30d Change",
unit: Unit.pctMcap, unit: Unit.pctMcap,
}), }),
priceLine({ priceLine({
@@ -340,28 +349,7 @@ function createRealizedPnlSection(ctx, args, title) {
name: "SOPR", name: "SOPR",
title: title("SOPR"), title: title("SOPR"),
bottom: [ bottom: [
baseline({ ...createSingleSoprSeries(colors, args.tree),
metric: realized.sopr,
name: "SOPR",
unit: Unit.ratio,
base: 1,
}),
baseline({
metric: realized.sopr7dEma,
name: "7d EMA",
color: [colors.lime, colors.rose],
unit: Unit.ratio,
defaultActive: false,
base: 1,
}),
baseline({
metric: realized.sopr30dEma,
name: "30d EMA",
color: [colors.avocado, colors.pink],
unit: Unit.ratio,
defaultActive: false,
base: 1,
}),
priceLine({ priceLine({
ctx, ctx,
unit: Unit.ratio, unit: Unit.ratio,
@@ -372,46 +360,12 @@ function createRealizedPnlSection(ctx, args, title) {
{ {
name: "Sell Side Risk", name: "Sell Side Risk",
title: title("Sell Side Risk Ratio"), title: title("Sell Side Risk Ratio"),
bottom: [ bottom: createSingleSellSideRiskSeries(colors, args.tree),
line({
metric: realized.sellSideRiskRatio,
name: "Raw",
color: colors.orange,
unit: Unit.ratio,
}),
line({
metric: realized.sellSideRiskRatio7dEma,
name: "7d EMA",
color: colors.red,
unit: Unit.ratio,
defaultActive: false,
}),
line({
metric: realized.sellSideRiskRatio30dEma,
name: "30d EMA",
color: colors.rose,
unit: Unit.ratio,
defaultActive: false,
}),
],
}, },
{ {
name: "Value", name: "Value",
title: title("Value Created & Destroyed"), title: title("Value Created & Destroyed"),
bottom: [ bottom: createSingleValueCreatedDestroyedSeries(colors, args.tree),
line({
metric: realized.valueCreated,
name: "Created",
color: colors.emerald,
unit: Unit.usd,
}),
line({
metric: realized.valueDestroyed,
name: "Destroyed",
color: colors.red,
unit: Unit.usd,
}),
],
}, },
]; ];
} }
@@ -582,23 +536,21 @@ function createCostBasisSection(list, useGroupName, title) {
name: "Min", name: "Min",
title: title("Min Cost Basis"), title: title("Min Cost Basis"),
top: list.map(({ color, name, tree }) => top: list.map(({ color, name, tree }) =>
line({ price({
metric: tree.costBasis.min, metric: tree.costBasis.min,
name: useGroupName ? name : "Min", name: useGroupName ? name : "Min",
color, color,
unit: Unit.usd,
}), }),
), ),
}, },
{ {
name: "max", name: "Max",
title: title("Max Cost Basis"), title: title("Max Cost Basis"),
top: list.map(({ color, name, tree }) => top: list.map(({ color, name, tree }) =>
line({ price({
metric: tree.costBasis.max, metric: tree.costBasis.max,
name: useGroupName ? name : "Max", name: useGroupName ? name : "Max",
color, color,
unit: Unit.usd,
}), }),
), ),
}, },
@@ -645,12 +597,12 @@ function createActivitySection(args, title) {
name: "Activity", name: "Activity",
tree: [ tree: [
{ {
name: "coinblocks destroyed", name: "Coinblocks Destroyed",
title: title("Coinblocks Destroyed"), title: title("Coinblocks Destroyed"),
bottom: createGroupedCoinblocksDestroyedSeries(list), bottom: createGroupedCoinblocksDestroyedSeries(list),
}, },
{ {
name: "coindays destroyed", name: "Coindays Destroyed",
title: title("Coindays Destroyed"), title: title("Coindays Destroyed"),
bottom: createGroupedCoindaysDestroyedSeries(list), bottom: createGroupedCoindaysDestroyedSeries(list),
}, },
@@ -658,17 +610,17 @@ function createActivitySection(args, title) {
name: "Sent", name: "Sent",
tree: [ tree: [
{ {
name: "sats", name: "Sats",
title: title("Sent (Sats)"), title: title("Sent (Sats)"),
bottom: createGroupedSentSatsSeries(list), bottom: createGroupedSentSatsSeries(list),
}, },
{ {
name: "bitcoin", name: "Bitcoin",
title: title("Sent (BTC)"), title: title("Sent (BTC)"),
bottom: createGroupedSentBitcoinSeries(list), bottom: createGroupedSentBitcoinSeries(list),
}, },
{ {
name: "dollars", name: "Dollars",
title: title("Sent ($)"), title: title("Sent ($)"),
bottom: createGroupedSentDollarsSeries(list), bottom: createGroupedSentDollarsSeries(list),
}, },
+138 -25
View File
@@ -2,7 +2,7 @@
import { Unit } from "../../utils/units.js"; import { Unit } from "../../utils/units.js";
import { priceLine } from "../constants.js"; import { priceLine } from "../constants.js";
import { baseline, line } from "../series.js"; import { baseline, dots, line, price } from "../series.js";
import { satsBtcUsd } from "../shared.js"; import { satsBtcUsd } from "../shared.js";
/** /**
@@ -285,11 +285,11 @@ export function createAddressCountSeries(ctx, list, useGroupName) {
/** /**
* Create realized price series for grouped cohorts * Create realized price series for grouped cohorts
* @param {readonly CohortObject[]} list * @param {readonly CohortObject[]} list
* @returns {AnyFetchedSeriesBlueprint[]} * @returns {FetchedPriceSeriesBlueprint[]}
*/ */
export function createRealizedPriceSeries(list) { export function createRealizedPriceSeries(list) {
return list.map(({ color, name, tree }) => return list.map(({ color, name, tree }) =>
line({ metric: tree.realized.realizedPrice, name, color, unit: Unit.usd }), price({ metric: tree.realized.realizedPrice, name, color }),
); );
} }
@@ -332,7 +332,7 @@ export function createRealizedCapSeries(list, useGroupName) {
* @param {Colors} colors * @param {Colors} colors
* @param {readonly CohortWithCostBasisPercentiles[]} list * @param {readonly CohortWithCostBasisPercentiles[]} list
* @param {boolean} useGroupName * @param {boolean} useGroupName
* @returns {AnyFetchedSeriesBlueprint[]} * @returns {FetchedPriceSeriesBlueprint[]}
*/ */
export function createCostBasisPercentilesSeries(colors, list, useGroupName) { export function createCostBasisPercentilesSeries(colors, list, useGroupName) {
return list.flatMap(({ name, tree }) => { return list.flatMap(({ name, tree }) => {
@@ -340,27 +340,27 @@ export function createCostBasisPercentilesSeries(colors, list, useGroupName) {
const p = cb.percentiles; const p = cb.percentiles;
const n = (/** @type {number} */ pct) => (useGroupName ? `${name} p${pct}` : `p${pct}`); const n = (/** @type {number} */ pct) => (useGroupName ? `${name} p${pct}` : `p${pct}`);
return [ return [
line({ metric: cb.max, name: n(100), color: colors.purple, unit: Unit.usd, defaultActive: false }), price({ metric: cb.max, name: n(100), color: colors.purple, defaultActive: false }),
line({ metric: p.pct95, name: n(95), color: colors.fuchsia, unit: Unit.usd, defaultActive: false }), price({ metric: p.pct95, name: n(95), color: colors.fuchsia, defaultActive: false }),
line({ metric: p.pct90, name: n(90), color: colors.pink, unit: Unit.usd, defaultActive: false }), price({ metric: p.pct90, name: n(90), color: colors.pink, defaultActive: false }),
line({ metric: p.pct85, name: n(85), color: colors.pink, unit: Unit.usd, defaultActive: false }), price({ metric: p.pct85, name: n(85), color: colors.pink, defaultActive: false }),
line({ metric: p.pct80, name: n(80), color: colors.rose, unit: Unit.usd, defaultActive: false }), price({ metric: p.pct80, name: n(80), color: colors.rose, defaultActive: false }),
line({ metric: p.pct75, name: n(75), color: colors.red, unit: Unit.usd, defaultActive: false }), price({ metric: p.pct75, name: n(75), color: colors.red, defaultActive: false }),
line({ metric: p.pct70, name: n(70), color: colors.orange, unit: Unit.usd, defaultActive: false }), price({ metric: p.pct70, name: n(70), color: colors.orange, defaultActive: false }),
line({ metric: p.pct65, name: n(65), color: colors.amber, unit: Unit.usd, defaultActive: false }), price({ metric: p.pct65, name: n(65), color: colors.amber, defaultActive: false }),
line({ metric: p.pct60, name: n(60), color: colors.yellow, unit: Unit.usd, defaultActive: false }), price({ metric: p.pct60, name: n(60), color: colors.yellow, defaultActive: false }),
line({ metric: p.pct55, name: n(55), color: colors.yellow, unit: Unit.usd, defaultActive: false }), price({ metric: p.pct55, name: n(55), color: colors.yellow, defaultActive: false }),
line({ metric: p.pct50, name: n(50), color: colors.avocado, unit: Unit.usd }), price({ metric: p.pct50, name: n(50), color: colors.avocado }),
line({ metric: p.pct45, name: n(45), color: colors.lime, unit: Unit.usd, defaultActive: false }), price({ metric: p.pct45, name: n(45), color: colors.lime, defaultActive: false }),
line({ metric: p.pct40, name: n(40), color: colors.green, unit: Unit.usd, defaultActive: false }), price({ metric: p.pct40, name: n(40), color: colors.green, defaultActive: false }),
line({ metric: p.pct35, name: n(35), color: colors.emerald, unit: Unit.usd, defaultActive: false }), price({ metric: p.pct35, name: n(35), color: colors.emerald, defaultActive: false }),
line({ metric: p.pct30, name: n(30), color: colors.teal, unit: Unit.usd, defaultActive: false }), price({ metric: p.pct30, name: n(30), color: colors.teal, defaultActive: false }),
line({ metric: p.pct25, name: n(25), color: colors.teal, unit: Unit.usd, defaultActive: false }), price({ metric: p.pct25, name: n(25), color: colors.teal, defaultActive: false }),
line({ metric: p.pct20, name: n(20), color: colors.cyan, unit: Unit.usd, defaultActive: false }), price({ metric: p.pct20, name: n(20), color: colors.cyan, defaultActive: false }),
line({ metric: p.pct15, name: n(15), color: colors.sky, unit: Unit.usd, defaultActive: false }), price({ metric: p.pct15, name: n(15), color: colors.sky, defaultActive: false }),
line({ metric: p.pct10, name: n(10), color: colors.blue, unit: Unit.usd, defaultActive: false }), price({ metric: p.pct10, name: n(10), color: colors.blue, defaultActive: false }),
line({ metric: p.pct05, name: n(5), color: colors.indigo, unit: Unit.usd, defaultActive: false }), price({ metric: p.pct05, name: n(5), color: colors.indigo, defaultActive: false }),
line({ metric: cb.min, name: n(0), color: colors.violet, unit: Unit.usd, defaultActive: false }), price({ metric: cb.min, name: n(0), color: colors.violet, defaultActive: false }),
]; ];
}); });
} }
@@ -536,3 +536,116 @@ export function createGroupedSentDollarsSeries(list) {
}), }),
]); ]);
} }
// ============================================================================
// Sell Side Risk Ratio Helpers
// ============================================================================
/**
* Create sell side risk ratio series for single cohort
* @param {Colors} colors
* @param {{ realized: AnyRealizedPattern }} tree
* @returns {AnyFetchedSeriesBlueprint[]}
*/
export function createSingleSellSideRiskSeries(colors, tree) {
return [
dots({
metric: tree.realized.sellSideRiskRatio,
name: "Raw",
color: colors.orange,
unit: Unit.ratio,
}),
line({
metric: tree.realized.sellSideRiskRatio7dEma,
name: "7d EMA",
color: colors.red,
unit: Unit.ratio,
}),
line({
metric: tree.realized.sellSideRiskRatio30dEma,
name: "30d EMA",
color: colors.pink,
unit: Unit.ratio,
}),
];
}
/**
* Create sell side risk ratio series for grouped cohorts
* @param {readonly CohortObject[]} list
* @returns {AnyFetchedSeriesBlueprint[]}
*/
export function createGroupedSellSideRiskSeries(list) {
return list.flatMap(({ color, name, tree }) => [
line({
metric: tree.realized.sellSideRiskRatio,
name,
color,
unit: Unit.ratio,
}),
]);
}
// ============================================================================
// Value Created & Destroyed Helpers
// ============================================================================
/**
* Create value created & destroyed series for single cohort
* @param {Colors} colors
* @param {{ realized: AnyRealizedPattern }} tree
* @returns {AnyFetchedSeriesBlueprint[]}
*/
export function createSingleValueCreatedDestroyedSeries(colors, tree) {
return [
line({
metric: tree.realized.valueCreated,
name: "Created",
color: colors.emerald,
unit: Unit.usd,
}),
line({
metric: tree.realized.valueDestroyed,
name: "Destroyed",
color: colors.red,
unit: Unit.usd,
}),
];
}
// ============================================================================
// SOPR Helpers
// ============================================================================
/**
* Create base SOPR series for single cohort (all cohorts have base SOPR)
* @param {Colors} colors
* @param {{ realized: AnyRealizedPattern }} tree
* @returns {AnyFetchedSeriesBlueprint[]}
*/
export function createSingleSoprSeries(colors, tree) {
return [
baseline({
metric: tree.realized.sopr,
name: "SOPR",
unit: Unit.ratio,
base: 1,
}),
baseline({
metric: tree.realized.sopr7dEma,
name: "7d EMA",
color: [colors.lime, colors.rose],
unit: Unit.ratio,
defaultActive: false,
base: 1,
}),
baseline({
metric: tree.realized.sopr30dEma,
name: "30d EMA",
color: [colors.avocado, colors.pink],
unit: Unit.ratio,
defaultActive: false,
base: 1,
}),
];
}
+263 -115
View File
@@ -32,6 +32,11 @@ import {
createCostBasisPercentilesSeries, createCostBasisPercentilesSeries,
groupedSupplyRelativeGenerators, groupedSupplyRelativeGenerators,
createSingleSupplyRelativeOptions, createSingleSupplyRelativeOptions,
createSingleSellSideRiskSeries,
createGroupedSellSideRiskSeries,
createSingleValueCreatedDestroyedSeries,
createSingleSoprSeries,
createSingleCoinsDestroyedSeries,
} from "./shared.js"; } from "./shared.js";
import { import {
createRatioChart, createRatioChart,
@@ -39,7 +44,7 @@ import {
formatCohortTitle, formatCohortTitle,
} from "../shared.js"; } from "../shared.js";
import { Unit } from "../../utils/units.js"; import { Unit } from "../../utils/units.js";
import { line, baseline } from "../series.js"; import { line, baseline, price } from "../series.js";
import { priceLine } from "../constants.js"; import { priceLine } from "../constants.js";
// ============================================================================ // ============================================================================
@@ -81,7 +86,11 @@ export function createCohortFolderFull(ctx, args) {
return { return {
name: args.name || "all", name: args.name || "all",
tree: [ tree: [
createGroupedSupplySection(list, title, groupedSupplyRelativeGenerators), createGroupedSupplySection(
list,
title,
groupedSupplyRelativeGenerators,
),
createGroupedUtxoCountChart(list, title), createGroupedUtxoCountChart(list, title),
createGroupedRealizedSectionWithAdjusted(ctx, list, title, { createGroupedRealizedSectionWithAdjusted(ctx, list, title, {
ratioMetrics: createGroupedRealizedPnlRatioMetrics, ratioMetrics: createGroupedRealizedPnlRatioMetrics,
@@ -96,7 +105,12 @@ export function createCohortFolderFull(ctx, args) {
return { return {
name: args.name || "all", name: args.name || "all",
tree: [ tree: [
createSingleSupplyChart(ctx, args, title, createSingleSupplyRelativeOptions(ctx, args)), createSingleSupplyChart(
ctx,
args,
title,
createSingleSupplyRelativeOptions(ctx, args),
),
createSingleUtxoCountChart(args, title), createSingleUtxoCountChart(args, title),
createSingleRealizedSectionFull(ctx, args, title), createSingleRealizedSectionFull(ctx, args, title),
createSingleUnrealizedSectionFull(ctx, args, title), createSingleUnrealizedSectionFull(ctx, args, title),
@@ -119,7 +133,11 @@ export function createCohortFolderWithAdjusted(ctx, args) {
return { return {
name: args.name || "all", name: args.name || "all",
tree: [ tree: [
createGroupedSupplySection(list, title, groupedSupplyRelativeGenerators), createGroupedSupplySection(
list,
title,
groupedSupplyRelativeGenerators,
),
createGroupedUtxoCountChart(list, title), createGroupedUtxoCountChart(list, title),
createGroupedRealizedSectionWithAdjusted(ctx, list, title), createGroupedRealizedSectionWithAdjusted(ctx, list, title),
createGroupedUnrealizedSectionWithMarketCap(ctx, list, title), createGroupedUnrealizedSectionWithMarketCap(ctx, list, title),
@@ -132,7 +150,12 @@ export function createCohortFolderWithAdjusted(ctx, args) {
return { return {
name: args.name || "all", name: args.name || "all",
tree: [ tree: [
createSingleSupplyChart(ctx, args, title, createSingleSupplyRelativeOptions(ctx, args)), createSingleSupplyChart(
ctx,
args,
title,
createSingleSupplyRelativeOptions(ctx, args),
),
createSingleUtxoCountChart(args, title), createSingleUtxoCountChart(args, title),
createSingleRealizedSectionWithAdjusted(ctx, args, title), createSingleRealizedSectionWithAdjusted(ctx, args, title),
createSingleUnrealizedSectionWithMarketCap(ctx, args, title), createSingleUnrealizedSectionWithMarketCap(ctx, args, title),
@@ -155,7 +178,11 @@ export function createCohortFolderWithNupl(ctx, args) {
return { return {
name: args.name || "all", name: args.name || "all",
tree: [ tree: [
createGroupedSupplySection(list, title, groupedSupplyRelativeGenerators), createGroupedSupplySection(
list,
title,
groupedSupplyRelativeGenerators,
),
createGroupedUtxoCountChart(list, title), createGroupedUtxoCountChart(list, title),
createGroupedRealizedSectionBasic(ctx, list, title, { createGroupedRealizedSectionBasic(ctx, list, title, {
ratioMetrics: createGroupedRealizedPnlRatioMetrics, ratioMetrics: createGroupedRealizedPnlRatioMetrics,
@@ -170,7 +197,12 @@ export function createCohortFolderWithNupl(ctx, args) {
return { return {
name: args.name || "all", name: args.name || "all",
tree: [ tree: [
createSingleSupplyChart(ctx, args, title, createSingleSupplyRelativeOptions(ctx, args)), createSingleSupplyChart(
ctx,
args,
title,
createSingleSupplyRelativeOptions(ctx, args),
),
createSingleUtxoCountChart(args, title), createSingleUtxoCountChart(args, title),
createSingleRealizedSectionWithPercentiles(ctx, args, title), createSingleRealizedSectionWithPercentiles(ctx, args, title),
createSingleUnrealizedSectionWithNupl({ ctx, cohort: args, title }), createSingleUnrealizedSectionWithNupl({ ctx, cohort: args, title }),
@@ -180,7 +212,6 @@ export function createCohortFolderWithNupl(ctx, args) {
}; };
} }
/** /**
* Age range folder: ageRange.* (no nupl via RelativePattern2) * Age range folder: ageRange.* (no nupl via RelativePattern2)
* @param {PartialContext} ctx * @param {PartialContext} ctx
@@ -232,7 +263,11 @@ export function createCohortFolderBasicWithMarketCap(ctx, args) {
return { return {
name: args.name || "all", name: args.name || "all",
tree: [ tree: [
createGroupedSupplySection(list, title, groupedSupplyRelativeGenerators), createGroupedSupplySection(
list,
title,
groupedSupplyRelativeGenerators,
),
createGroupedUtxoCountChart(list, title), createGroupedUtxoCountChart(list, title),
createGroupedRealizedSectionBasic(ctx, list, title), createGroupedRealizedSectionBasic(ctx, list, title),
createGroupedUnrealizedSectionWithMarketCapOnly(ctx, list, title), createGroupedUnrealizedSectionWithMarketCapOnly(ctx, list, title),
@@ -245,7 +280,12 @@ export function createCohortFolderBasicWithMarketCap(ctx, args) {
return { return {
name: args.name || "all", name: args.name || "all",
tree: [ tree: [
createSingleSupplyChart(ctx, args, title, createSingleSupplyRelativeOptions(ctx, args)), createSingleSupplyChart(
ctx,
args,
title,
createSingleSupplyRelativeOptions(ctx, args),
),
createSingleUtxoCountChart(args, title), createSingleUtxoCountChart(args, title),
createSingleRealizedSectionBasic(ctx, args, title), createSingleRealizedSectionBasic(ctx, args, title),
createSingleUnrealizedSectionWithMarketCapOnly(ctx, args, title), createSingleUnrealizedSectionWithMarketCapOnly(ctx, args, title),
@@ -474,7 +514,12 @@ function createSingleRealizedSectionWithAdjusted(ctx, cohort, title) {
* @param {(cohort: T[number]) => AnyFetchedSeriesBlueprint[]} [options.ratioMetrics] - Generator for ratio metrics per cohort * @param {(cohort: T[number]) => AnyFetchedSeriesBlueprint[]} [options.ratioMetrics] - Generator for ratio metrics per cohort
* @returns {PartialOptionsGroup} * @returns {PartialOptionsGroup}
*/ */
function createGroupedRealizedSectionWithAdjusted(ctx, list, title, { ratioMetrics } = {}) { function createGroupedRealizedSectionWithAdjusted(
ctx,
list,
title,
{ ratioMetrics } = {},
) {
return { return {
name: "Realized", name: "Realized",
tree: [ tree: [
@@ -559,7 +604,12 @@ function createSingleRealizedSectionBasic(ctx, cohort, title) {
* @param {(cohort: T[number]) => AnyFetchedSeriesBlueprint[]} [options.ratioMetrics] - Generator for ratio metrics per cohort * @param {(cohort: T[number]) => AnyFetchedSeriesBlueprint[]} [options.ratioMetrics] - Generator for ratio metrics per cohort
* @returns {PartialOptionsGroup} * @returns {PartialOptionsGroup}
*/ */
function createGroupedRealizedSectionBasic(ctx, list, title, { ratioMetrics } = {}) { function createGroupedRealizedSectionBasic(
ctx,
list,
title,
{ ratioMetrics } = {},
) {
return { return {
name: "Realized", name: "Realized",
tree: [ tree: [
@@ -596,11 +646,10 @@ function createSingleRealizedPriceChart(cohort, title) {
name: "Price", name: "Price",
title: title("Realized Price"), title: title("Realized Price"),
top: [ top: [
line({ price({
metric: tree.realized.realizedPrice, metric: tree.realized.realizedPrice,
name: "realized", name: "Realized",
color, color,
unit: Unit.usd,
}), }),
], ],
}; };
@@ -623,7 +672,7 @@ function createSingleRealizedPriceChartsWithRatio(ctx, cohort, title) {
createSingleRealizedPriceChart(cohort, title), createSingleRealizedPriceChart(cohort, title),
createRatioChart(ctx, { createRatioChart(ctx, {
title, title,
price: tree.realized.realizedPrice, pricePattern: tree.realized.realizedPrice,
ratio, ratio,
color, color,
name: "MVRV", name: "MVRV",
@@ -631,7 +680,7 @@ function createSingleRealizedPriceChartsWithRatio(ctx, cohort, title) {
createZScoresFolder(ctx, { createZScoresFolder(ctx, {
title: title("Realized Price"), title: title("Realized Price"),
legend: "price", legend: "price",
price: tree.realized.realizedPrice, pricePattern: tree.realized.realizedPrice,
ratio, ratio,
color, color,
}), }),
@@ -693,7 +742,7 @@ function createSingleRealizedCapSeries(ctx, cohort, { extra = [] } = {}) {
}), }),
baseline({ baseline({
metric: tree.realized.realizedCap30dDelta, metric: tree.realized.realizedCap30dDelta,
name: "1m Change", name: "30d Change",
unit: Unit.usd, unit: Unit.usd,
defaultActive: false, defaultActive: false,
}), }),
@@ -712,7 +761,7 @@ function createRealizedCapRatioSeries(ctx, tree) {
return [ return [
baseline({ baseline({
metric: tree.realized.realizedCapRelToOwnMarketCap, metric: tree.realized.realizedCapRelToOwnMarketCap,
name: "ratio", name: "Ratio",
unit: Unit.pctOwnMcap, unit: Unit.pctOwnMcap,
options: { baseValue: { price: 100 } }, options: { baseValue: { price: 100 } },
}), }),
@@ -751,7 +800,7 @@ function createRealizedPnlRatioSeries(colors, tree) {
return [ return [
line({ line({
metric: tree.realized.realizedProfitToLossRatio, metric: tree.realized.realizedProfitToLossRatio,
name: "Profit / Loss", name: "P/L Ratio",
color: colors.yellow, color: colors.yellow,
unit: Unit.ratio, unit: Unit.ratio,
}), }),
@@ -783,7 +832,12 @@ function createGroupedRealizedPnlRatioMetrics(cohort) {
* @param {AnyFetchedSeriesBlueprint[]} [options.extra] - Extra series (e.g., pnl ratio for cohorts with RealizedWithPnlRatio) * @param {AnyFetchedSeriesBlueprint[]} [options.extra] - Extra series (e.g., pnl ratio for cohorts with RealizedWithPnlRatio)
* @returns {PartialOptionsTree} * @returns {PartialOptionsTree}
*/ */
function createSingleRealizedPnlSection(ctx, cohort, title, { extra = [] } = {}) { function createSingleRealizedPnlSection(
ctx,
cohort,
title,
{ extra = [] } = {},
) {
const { colors, fromBlockCountWithUnit, fromBitcoinPatternWithUnit } = ctx; const { colors, fromBlockCountWithUnit, fromBitcoinPatternWithUnit } = ctx;
const { tree } = cohort; const { tree } = cohort;
@@ -859,7 +913,7 @@ function createSingleRealizedPnlSection(ctx, cohort, title, { extra = [] } = {})
), ),
baseline({ baseline({
metric: tree.realized.netRealizedPnlCumulative30dDelta, metric: tree.realized.netRealizedPnlCumulative30dDelta,
name: "Cumulative 1m Change", name: "Cumulative 30d Change",
unit: Unit.usd, unit: Unit.usd,
defaultActive: false, defaultActive: false,
}), }),
@@ -877,13 +931,13 @@ function createSingleRealizedPnlSection(ctx, cohort, title, { extra = [] } = {})
baseline({ baseline({
metric: metric:
tree.realized.netRealizedPnlCumulative30dDeltaRelToRealizedCap, tree.realized.netRealizedPnlCumulative30dDeltaRelToRealizedCap,
name: "Cumulative 1m Change", name: "Cumulative 30d Change",
unit: Unit.pctRcap, unit: Unit.pctRcap,
defaultActive: false, defaultActive: false,
}), }),
baseline({ baseline({
metric: tree.realized.netRealizedPnlCumulative30dDeltaRelToMarketCap, metric: tree.realized.netRealizedPnlCumulative30dDeltaRelToMarketCap,
name: "Cumulative 1m Change", name: "Cumulative 30d Change",
unit: Unit.pctMcap, unit: Unit.pctMcap,
}), }),
priceLine({ ctx, unit: Unit.pctMcap }), priceLine({ ctx, unit: Unit.pctMcap }),
@@ -904,7 +958,12 @@ function createSingleRealizedPnlSection(ctx, cohort, title, { extra = [] } = {})
* @param {(cohort: T[number]) => AnyFetchedSeriesBlueprint[]} [options.ratioMetrics] - Generator for ratio metrics per cohort * @param {(cohort: T[number]) => AnyFetchedSeriesBlueprint[]} [options.ratioMetrics] - Generator for ratio metrics per cohort
* @returns {PartialOptionsTree} * @returns {PartialOptionsTree}
*/ */
function createGroupedRealizedPnlSections(ctx, list, title, { ratioMetrics } = {}) { function createGroupedRealizedPnlSections(
ctx,
list,
title,
{ ratioMetrics } = {},
) {
return [ return [
{ {
name: "Profit", name: "Profit",
@@ -1028,8 +1087,8 @@ function createGroupedRealizedPnlSections(ctx, list, title, { ratioMetrics } = {
], ],
}, },
{ {
name: "Net P&L 1m Change", name: "Net P&L 30d Change",
title: title("Net Realized P&L 1m Change"), title: title("Net Realized P&L 30d Change"),
bottom: [ bottom: [
...list.flatMap(({ color, name, tree }) => [ ...list.flatMap(({ color, name, tree }) => [
baseline({ baseline({
@@ -1076,36 +1135,10 @@ function createGroupedRealizedPnlSections(ctx, list, title, { ratioMetrics } = {
* @returns {PartialChartOption} * @returns {PartialChartOption}
*/ */
function createSingleBaseSoprChart(ctx, cohort, title) { function createSingleBaseSoprChart(ctx, cohort, title) {
const { colors } = ctx;
const { tree } = cohort;
return { return {
name: "Normal", name: "Normal",
title: title("SOPR"), title: title("SOPR"),
bottom: [ bottom: createSingleSoprSeries(ctx.colors, cohort.tree),
baseline({
metric: tree.realized.sopr,
name: "SOPR",
unit: Unit.ratio,
base: 1,
}),
baseline({
metric: tree.realized.sopr7dEma,
name: "7d EMA",
color: [colors.lime, colors.rose],
unit: Unit.ratio,
defaultActive: false,
base: 1,
}),
baseline({
metric: tree.realized.sopr30dEma,
name: "30d EMA",
color: [colors.avocado, colors.pink],
unit: Unit.ratio,
defaultActive: false,
base: 1,
}),
],
}; };
} }
@@ -1547,7 +1580,14 @@ function createGroupedNuplChart(ctx, list, title) {
* @param {PartialChartOption[]} [args.charts] - Extra charts (e.g., nupl) * @param {PartialChartOption[]} [args.charts] - Extra charts (e.g., nupl)
* @returns {PartialOptionsGroup} * @returns {PartialOptionsGroup}
*/ */
function createUnrealizedSection({ ctx, tree, title, pnl = [], netPnl = [], charts = [] }) { function createUnrealizedSection({
ctx,
tree,
title,
pnl = [],
netPnl = [],
charts = [],
}) {
return { return {
name: "Unrealized", name: "Unrealized",
tree: [ tree: [
@@ -1584,7 +1624,12 @@ function createUnrealizedSection({ ctx, tree, title, pnl = [], netPnl = [], char
* @param {PartialChartOption[]} [args.charts] - Extra charts * @param {PartialChartOption[]} [args.charts] - Extra charts
* @returns {PartialOptionsGroup} * @returns {PartialOptionsGroup}
*/ */
function createGroupedUnrealizedSection({ list, title, netPnlMetrics, charts = [] }) { function createGroupedUnrealizedSection({
list,
title,
netPnlMetrics,
charts = [],
}) {
return { return {
name: "Unrealized", name: "Unrealized",
tree: [ tree: [
@@ -1682,7 +1727,6 @@ function createSingleUnrealizedSectionWithMarketCap(ctx, cohort, title) {
}); });
} }
/** /**
* Unrealized section WITH RelToMarketCap metrics (for CohortBasicWithMarketCap) * Unrealized section WITH RelToMarketCap metrics (for CohortBasicWithMarketCap)
* @param {PartialContext} ctx * @param {PartialContext} ctx
@@ -1774,9 +1818,24 @@ function createGroupedUnrealizedSectionFull(ctx, list, title) {
list, list,
title, title,
netPnlMetrics: ({ color, name, tree }) => [ netPnlMetrics: ({ color, name, tree }) => [
baseline({ metric: tree.relative.netUnrealizedPnlRelToMarketCap, name, color, unit: Unit.pctMcap }), baseline({
baseline({ metric: tree.relative.netUnrealizedPnlRelToOwnMarketCap, name, color, unit: Unit.pctOwnMcap }), metric: tree.relative.netUnrealizedPnlRelToMarketCap,
baseline({ metric: tree.relative.netUnrealizedPnlRelToOwnTotalUnrealizedPnl, name, color, unit: Unit.pctOwnPnl }), name,
color,
unit: Unit.pctMcap,
}),
baseline({
metric: tree.relative.netUnrealizedPnlRelToOwnMarketCap,
name,
color,
unit: Unit.pctOwnMcap,
}),
baseline({
metric: tree.relative.netUnrealizedPnlRelToOwnTotalUnrealizedPnl,
name,
color,
unit: Unit.pctOwnPnl,
}),
], ],
charts: [createGroupedNuplChart(ctx, list, title)], charts: [createGroupedNuplChart(ctx, list, title)],
}); });
@@ -1793,13 +1852,17 @@ function createGroupedUnrealizedSectionWithMarketCap(ctx, list, title) {
list, list,
title, title,
netPnlMetrics: ({ color, name, tree }) => [ netPnlMetrics: ({ color, name, tree }) => [
baseline({ metric: tree.relative.netUnrealizedPnlRelToMarketCap, name, color, unit: Unit.pctMcap }), baseline({
metric: tree.relative.netUnrealizedPnlRelToMarketCap,
name,
color,
unit: Unit.pctMcap,
}),
], ],
charts: [createGroupedNuplChart(ctx, list, title)], charts: [createGroupedNuplChart(ctx, list, title)],
}); });
} }
/** /**
* Grouped unrealized section WITH RelToMarketCap (for CohortBasicWithMarketCap) * Grouped unrealized section WITH RelToMarketCap (for CohortBasicWithMarketCap)
* @param {PartialContext} ctx * @param {PartialContext} ctx
@@ -1811,7 +1874,12 @@ function createGroupedUnrealizedSectionWithMarketCapOnly(ctx, list, title) {
list, list,
title, title,
netPnlMetrics: ({ color, name, tree }) => [ netPnlMetrics: ({ color, name, tree }) => [
baseline({ metric: tree.relative.netUnrealizedPnlRelToMarketCap, name, color, unit: Unit.pctMcap }), baseline({
metric: tree.relative.netUnrealizedPnlRelToMarketCap,
name,
color,
unit: Unit.pctMcap,
}),
], ],
charts: [createGroupedNuplChart(ctx, list, title)], charts: [createGroupedNuplChart(ctx, list, title)],
}); });
@@ -1865,9 +1933,24 @@ function createGroupedUnrealizedSectionWithNupl({ ctx, list, title }) {
list, list,
title, title,
netPnlMetrics: ({ color, name, tree }) => [ netPnlMetrics: ({ color, name, tree }) => [
baseline({ metric: tree.relative.netUnrealizedPnlRelToMarketCap, name, color, unit: Unit.pctMcap }), baseline({
baseline({ metric: tree.relative.netUnrealizedPnlRelToOwnMarketCap, name, color, unit: Unit.pctOwnMcap }), metric: tree.relative.netUnrealizedPnlRelToMarketCap,
baseline({ metric: tree.relative.netUnrealizedPnlRelToOwnTotalUnrealizedPnl, name, color, unit: Unit.pctOwnPnl }), name,
color,
unit: Unit.pctMcap,
}),
baseline({
metric: tree.relative.netUnrealizedPnlRelToOwnMarketCap,
name,
color,
unit: Unit.pctOwnMcap,
}),
baseline({
metric: tree.relative.netUnrealizedPnlRelToOwnTotalUnrealizedPnl,
name,
color,
unit: Unit.pctOwnPnl,
}),
], ],
charts: [createGroupedNuplChart(ctx, list, title)], charts: [createGroupedNuplChart(ctx, list, title)],
}); });
@@ -1906,8 +1989,18 @@ function createGroupedUnrealizedSectionAgeRange(list, title) {
list, list,
title, title,
netPnlMetrics: ({ color, name, tree }) => [ netPnlMetrics: ({ color, name, tree }) => [
baseline({ metric: tree.relative.netUnrealizedPnlRelToOwnMarketCap, name, color, unit: Unit.pctOwnMcap }), baseline({
baseline({ metric: tree.relative.netUnrealizedPnlRelToOwnTotalUnrealizedPnl, name, color, unit: Unit.pctOwnPnl }), metric: tree.relative.netUnrealizedPnlRelToOwnMarketCap,
name,
color,
unit: Unit.pctOwnMcap,
}),
baseline({
metric: tree.relative.netUnrealizedPnlRelToOwnTotalUnrealizedPnl,
name,
color,
unit: Unit.pctOwnPnl,
}),
], ],
}); });
} }
@@ -1935,24 +2028,21 @@ function createCostBasisSection(ctx, { cohort, title, charts = [] }) {
name: "Average", name: "Average",
title: title("Cost Basis"), title: title("Cost Basis"),
top: [ top: [
line({ price({
metric: tree.realized.realizedPrice, metric: tree.realized.realizedPrice,
name: "Average", name: "Average",
color, color,
unit: Unit.usd,
}), }),
line({ price({
metric: tree.costBasis.max, metric: tree.costBasis.max,
name: "Max", name: "Max",
color: colors.green, color: colors.green,
unit: Unit.usd,
defaultActive: false, defaultActive: false,
}), }),
line({ price({
metric: tree.costBasis.min, metric: tree.costBasis.min,
name: "Min", name: "Min",
color: colors.red, color: colors.red,
unit: Unit.usd,
defaultActive: false, defaultActive: false,
}), }),
], ],
@@ -1961,11 +2051,10 @@ function createCostBasisSection(ctx, { cohort, title, charts = [] }) {
name: "Max", name: "Max",
title: title("Max Cost Basis"), title: title("Max Cost Basis"),
top: [ top: [
line({ price({
metric: tree.costBasis.max, metric: tree.costBasis.max,
name: "Max", name: "Max",
color: colors.green, color: colors.green,
unit: Unit.usd,
}), }),
], ],
}, },
@@ -1973,11 +2062,10 @@ function createCostBasisSection(ctx, { cohort, title, charts = [] }) {
name: "Min", name: "Min",
title: title("Min Cost Basis"), title: title("Min Cost Basis"),
top: [ top: [
line({ price({
metric: tree.costBasis.min, metric: tree.costBasis.min,
name: "Min", name: "Min",
color: colors.red, color: colors.red,
unit: Unit.usd,
}), }),
], ],
}, },
@@ -2003,11 +2091,10 @@ function createGroupedCostBasisSection({ list, title, charts = [] }) {
name: "Average", name: "Average",
title: title("Average Cost Basis"), title: title("Average Cost Basis"),
top: list.map(({ color, name, tree }) => top: list.map(({ color, name, tree }) =>
line({ price({
metric: tree.realized.realizedPrice, metric: tree.realized.realizedPrice,
name, name,
color, color,
unit: Unit.usd,
}), }),
), ),
}, },
@@ -2015,14 +2102,14 @@ function createGroupedCostBasisSection({ list, title, charts = [] }) {
name: "Max", name: "Max",
title: title("Max Cost Basis"), title: title("Max Cost Basis"),
top: list.map(({ color, name, tree }) => top: list.map(({ color, name, tree }) =>
line({ metric: tree.costBasis.max, name, color, unit: Unit.usd }), price({ metric: tree.costBasis.max, name, color }),
), ),
}, },
{ {
name: "Min", name: "Min",
title: title("Min Cost Basis"), title: title("Min Cost Basis"),
top: list.map(({ color, name, tree }) => top: list.map(({ color, name, tree }) =>
line({ metric: tree.costBasis.min, name, color, unit: Unit.usd }), price({ metric: tree.costBasis.min, name, color }),
), ),
}, },
...charts, ...charts,
@@ -2078,7 +2165,6 @@ function createGroupedCostBasisSectionWithPercentiles(ctx, list, title) {
}); });
} }
// ============================================================================ // ============================================================================
// Activity Section Builders (generic, type-safe composition) // Activity Section Builders (generic, type-safe composition)
// ============================================================================ // ============================================================================
@@ -2103,38 +2189,43 @@ function createActivitySection({ ctx, cohort, title, valueMetrics = [] }) {
name: "Sent", name: "Sent",
title: title("Sent"), title: title("Sent"),
bottom: [ bottom: [
...fromBlockCountWithUnit(tree.activity.sent.sats, Unit.sats, undefined, color), ...fromBlockCountWithUnit(
...fromBitcoinPatternWithUnit(tree.activity.sent.bitcoin, Unit.btc, undefined, color), tree.activity.sent.sats,
...fromBlockCountWithUnit(tree.activity.sent.dollars, Unit.usd, undefined, color), Unit.sats,
undefined,
color,
),
...fromBitcoinPatternWithUnit(
tree.activity.sent.bitcoin,
Unit.btc,
undefined,
color,
),
...fromBlockCountWithUnit(
tree.activity.sent.dollars,
Unit.usd,
undefined,
color,
),
], ],
}, },
{ {
name: "Sell Side Risk", name: "Sell Side Risk",
title: title("Sell Side Risk Ratio"), title: title("Sell Side Risk Ratio"),
bottom: [ bottom: createSingleSellSideRiskSeries(colors, tree),
line({ metric: tree.realized.sellSideRiskRatio, name: "Raw", color: colors.orange, unit: Unit.ratio }),
line({ metric: tree.realized.sellSideRiskRatio7dEma, name: "7d EMA", color: colors.red, unit: Unit.ratio, defaultActive: false }),
line({ metric: tree.realized.sellSideRiskRatio30dEma, name: "30d EMA", color: colors.rose, unit: Unit.ratio, defaultActive: false }),
],
}, },
{ {
name: "Value", name: "Value",
title: title("Value Created & Destroyed"), title: title("Value Created & Destroyed"),
bottom: [ bottom: [
line({ metric: tree.realized.valueCreated, name: "Created", color: colors.emerald, unit: Unit.usd }), ...createSingleValueCreatedDestroyedSeries(colors, tree),
line({ metric: tree.realized.valueDestroyed, name: "Destroyed", color: colors.red, unit: Unit.usd }),
...valueMetrics, ...valueMetrics,
], ],
}, },
{ {
name: "Coins Destroyed", name: "Coins Destroyed",
title: title("Coins Destroyed"), title: title("Coins Destroyed"),
bottom: [ bottom: createSingleCoinsDestroyedSeries(cohort),
line({ metric: tree.activity.coinblocksDestroyed.sum, name: "Coinblocks", color, unit: Unit.coinblocks }),
line({ metric: tree.activity.coinblocksDestroyed.cumulative, name: "Cumulative", color, unit: Unit.coinblocks, defaultActive: false }),
line({ metric: tree.activity.coindaysDestroyed.sum, name: "Coindays", color, unit: Unit.coindays }),
line({ metric: tree.activity.coindaysDestroyed.cumulative, name: "Cumulative", color, unit: Unit.coindays, defaultActive: false }),
],
}, },
], ],
}; };
@@ -2156,9 +2247,7 @@ function createGroupedActivitySection({ list, title, valueTree }) {
{ {
name: "Sell Side Risk", name: "Sell Side Risk",
title: title("Sell Side Risk Ratio"), title: title("Sell Side Risk Ratio"),
bottom: list.flatMap(({ color, name, tree }) => [ bottom: createGroupedSellSideRiskSeries(list),
line({ metric: tree.realized.sellSideRiskRatio, name, color, unit: Unit.ratio }),
]),
}, },
{ {
name: "Value", name: "Value",
@@ -2167,14 +2256,24 @@ function createGroupedActivitySection({ list, title, valueTree }) {
name: "Created", name: "Created",
title: title("Value Created"), title: title("Value Created"),
bottom: list.flatMap(({ color, name, tree }) => [ bottom: list.flatMap(({ color, name, tree }) => [
line({ metric: tree.realized.valueCreated, name, color, unit: Unit.usd }), line({
metric: tree.realized.valueCreated,
name,
color,
unit: Unit.usd,
}),
]), ]),
}, },
{ {
name: "Destroyed", name: "Destroyed",
title: title("Value Destroyed"), title: title("Value Destroyed"),
bottom: list.flatMap(({ color, name, tree }) => [ bottom: list.flatMap(({ color, name, tree }) => [
line({ metric: tree.realized.valueDestroyed, name, color, unit: Unit.usd }), line({
metric: tree.realized.valueDestroyed,
name,
color,
unit: Unit.usd,
}),
]), ]),
}, },
], ],
@@ -2186,16 +2285,36 @@ function createGroupedActivitySection({ list, title, valueTree }) {
name: "Sum", name: "Sum",
title: title("Coins Destroyed"), title: title("Coins Destroyed"),
bottom: list.flatMap(({ color, name, tree }) => [ bottom: list.flatMap(({ color, name, tree }) => [
line({ metric: tree.activity.coinblocksDestroyed.sum, name, color, unit: Unit.coinblocks }), line({
line({ metric: tree.activity.coindaysDestroyed.sum, name, color, unit: Unit.coindays }), metric: tree.activity.coinblocksDestroyed.sum,
name,
color,
unit: Unit.coinblocks,
}),
line({
metric: tree.activity.coindaysDestroyed.sum,
name,
color,
unit: Unit.coindays,
}),
]), ]),
}, },
{ {
name: "Cumulative", name: "Cumulative",
title: title("Cumulative Coins Destroyed"), title: title("Cumulative Coins Destroyed"),
bottom: list.flatMap(({ color, name, tree }) => [ bottom: list.flatMap(({ color, name, tree }) => [
line({ metric: tree.activity.coinblocksDestroyed.cumulative, name, color, unit: Unit.coinblocks }), line({
line({ metric: tree.activity.coindaysDestroyed.cumulative, name, color, unit: Unit.coindays }), metric: tree.activity.coinblocksDestroyed.cumulative,
name,
color,
unit: Unit.coinblocks,
}),
line({
metric: tree.activity.coindaysDestroyed.cumulative,
name,
color,
unit: Unit.coindays,
}),
]), ]),
}, },
], ],
@@ -2223,8 +2342,18 @@ function createSingleActivitySectionWithAdjusted(ctx, cohort, title) {
cohort, cohort,
title, title,
valueMetrics: [ valueMetrics: [
line({ metric: tree.realized.adjustedValueCreated, name: "Adjusted Created", color: colors.lime, unit: Unit.usd }), line({
line({ metric: tree.realized.adjustedValueDestroyed, name: "Adjusted Destroyed", color: colors.pink, unit: Unit.usd }), metric: tree.realized.adjustedValueCreated,
name: "Adjusted Created",
color: colors.lime,
unit: Unit.usd,
}),
line({
metric: tree.realized.adjustedValueDestroyed,
name: "Adjusted Destroyed",
color: colors.pink,
unit: Unit.usd,
}),
], ],
}); });
} }
@@ -2247,14 +2376,24 @@ function createGroupedActivitySectionWithAdjusted(list, title) {
name: "Normal", name: "Normal",
title: title("Value Created"), title: title("Value Created"),
bottom: list.flatMap(({ color, name, tree }) => [ bottom: list.flatMap(({ color, name, tree }) => [
line({ metric: tree.realized.valueCreated, name, color, unit: Unit.usd }), line({
metric: tree.realized.valueCreated,
name,
color,
unit: Unit.usd,
}),
]), ]),
}, },
{ {
name: "Adjusted", name: "Adjusted",
title: title("Adjusted Value Created"), title: title("Adjusted Value Created"),
bottom: list.flatMap(({ color, name, tree }) => [ bottom: list.flatMap(({ color, name, tree }) => [
line({ metric: tree.realized.adjustedValueCreated, name, color, unit: Unit.usd }), line({
metric: tree.realized.adjustedValueCreated,
name,
color,
unit: Unit.usd,
}),
]), ]),
}, },
], ],
@@ -2266,14 +2405,24 @@ function createGroupedActivitySectionWithAdjusted(list, title) {
name: "Normal", name: "Normal",
title: title("Value Destroyed"), title: title("Value Destroyed"),
bottom: list.flatMap(({ color, name, tree }) => [ bottom: list.flatMap(({ color, name, tree }) => [
line({ metric: tree.realized.valueDestroyed, name, color, unit: Unit.usd }), line({
metric: tree.realized.valueDestroyed,
name,
color,
unit: Unit.usd,
}),
]), ]),
}, },
{ {
name: "Adjusted", name: "Adjusted",
title: title("Adjusted Value Destroyed"), title: title("Adjusted Value Destroyed"),
bottom: list.flatMap(({ color, name, tree }) => [ bottom: list.flatMap(({ color, name, tree }) => [
line({ metric: tree.realized.adjustedValueDestroyed, name, color, unit: Unit.usd }), line({
metric: tree.realized.adjustedValueDestroyed,
name,
color,
unit: Unit.usd,
}),
]), ]),
}, },
], ],
@@ -2281,4 +2430,3 @@ function createGroupedActivitySectionWithAdjusted(list, title) {
], ],
}); });
} }
+49 -11
View File
@@ -7,6 +7,7 @@ import { collect, markUsed, logUnused } from "./unused.js";
import { setQr } from "../panes/share.js"; import { setQr } from "../panes/share.js";
import { getConstant } from "./constants.js"; import { getConstant } from "./constants.js";
import { colors } from "../chart/colors.js"; import { colors } from "../chart/colors.js";
import { Unit } from "../utils/units.js";
/** /**
* @param {BrkClient} brk * @param {BrkClient} brk
@@ -83,7 +84,23 @@ export function initOptions(brk) {
} }
/** /**
* @param {AnyFetchedSeriesBlueprint[]} [arr] * Check if a metric is an ActivePricePattern (has dollars and sats sub-metrics)
* @param {any} metric
* @returns {metric is ActivePricePattern}
*/
function isActivePricePattern(metric) {
return (
metric &&
typeof metric === "object" &&
"dollars" in metric &&
"sats" in metric &&
metric.dollars?.by &&
metric.sats?.by
);
}
/**
* @param {(AnyFetchedSeriesBlueprint | FetchedPriceSeriesBlueprint)[]} [arr]
*/ */
function arrayToMap(arr = []) { function arrayToMap(arr = []) {
/** @type {Map<Unit, AnyFetchedSeriesBlueprint[]>} */ /** @type {Map<Unit, AnyFetchedSeriesBlueprint[]>} */
@@ -97,29 +114,50 @@ export function initOptions(brk) {
`Blueprint missing metric: ${JSON.stringify(blueprint)}`, `Blueprint missing metric: ${JSON.stringify(blueprint)}`,
); );
} }
if (!blueprint.unit) {
throw new Error(`Blueprint missing unit: ${blueprint.title}`); // Auto-expand ActivePricePattern into USD and sats versions
if (isActivePricePattern(blueprint.metric)) {
const pricePattern = /** @type {AnyPricePattern} */ (blueprint.metric);
// USD version
markUsed(pricePattern.dollars);
if (!map.has(Unit.usd)) map.set(Unit.usd, []);
map.get(Unit.usd)?.push({ ...blueprint, metric: pricePattern.dollars, unit: Unit.usd });
// Sats version
markUsed(pricePattern.sats);
if (!map.has(Unit.sats)) map.set(Unit.sats, []);
map.get(Unit.sats)?.push({ ...blueprint, metric: pricePattern.sats, unit: Unit.sats });
continue;
} }
markUsed(blueprint.metric);
const unit = blueprint.unit; // At this point, blueprint is definitely an AnyFetchedSeriesBlueprint (not a price pattern)
const regularBlueprint = /** @type {AnyFetchedSeriesBlueprint} */ (blueprint);
if (!regularBlueprint.unit) {
throw new Error(`Blueprint missing unit: ${regularBlueprint.title}`);
}
markUsed(regularBlueprint.metric);
const unit = regularBlueprint.unit;
if (!map.has(unit)) { if (!map.has(unit)) {
map.set(unit, []); map.set(unit, []);
} }
map.get(unit)?.push(blueprint); map.get(unit)?.push(regularBlueprint);
// Track baseline base values for auto price lines // Track baseline base values for auto price lines
if (blueprint.type === "Baseline") { if (regularBlueprint.type === "Baseline") {
const baseValue = blueprint.options?.baseValue?.price ?? 0; const baseValue = regularBlueprint.options?.baseValue?.price ?? 0;
if (!priceLines.has(unit)) priceLines.set(unit, new Set()); if (!priceLines.has(unit)) priceLines.set(unit, new Set());
priceLines.get(unit)?.add(baseValue); priceLines.get(unit)?.add(baseValue);
} }
// Remove from set if manual price line already exists // Remove from set if manual price line already exists
// Note: line() doesn't set type, so undefined means Line // Note: line() doesn't set type, so undefined means Line
if (blueprint.type === "Line" || blueprint.type === undefined) { if (regularBlueprint.type === "Line" || regularBlueprint.type === undefined) {
const path = Object.values(blueprint.metric.by)[0]?.path ?? ""; const path = Object.values(regularBlueprint.metric.by)[0]?.path ?? "";
if (path.includes("constant_")) { if (path.includes("constant_")) {
priceLines.get(unit)?.delete(parseFloat(blueprint.title)); priceLines.get(unit)?.delete(parseFloat(regularBlueprint.title));
} }
} }
} }
+47 -8
View File
@@ -1,7 +1,6 @@
/** Moving averages section */ /** Moving averages section */
import { Unit } from "../../utils/units.js"; import { price } from "../series.js";
import { line } from "../series.js";
import { createRatioChart, createZScoresFolder, formatCohortTitle } from "../shared.js"; import { createRatioChart, createZScoresFolder, formatCohortTitle } from "../shared.js";
import { periodIdToName } from "./utils.js"; import { periodIdToName } from "./utils.js";
@@ -81,19 +80,19 @@ export function createPriceWithRatioOptions(
ctx, ctx,
{ title, legend, ratio, color }, { title, legend, ratio, color },
) { ) {
const priceMetric = ratio.price; const pricePattern = ratio.price;
return [ return [
{ {
name: "Price", name: "Price",
title, title,
top: [line({ metric: priceMetric, name: legend, color, unit: Unit.usd })], top: [price({ metric: pricePattern, name: legend, color })],
}, },
createRatioChart(ctx, { title: formatCohortTitle(title), price: priceMetric, ratio, color }), createRatioChart(ctx, { title: formatCohortTitle(title), pricePattern, ratio, color }),
createZScoresFolder(ctx, { createZScoresFolder(ctx, {
title, title,
legend, legend,
price: priceMetric, pricePattern,
ratio, ratio,
color, color,
}), }),
@@ -103,6 +102,46 @@ export function createPriceWithRatioOptions(
/** Common period IDs to show at top level */ /** Common period IDs to show at top level */
const COMMON_PERIODS = ["1w", "1m", "200d", "1y", "200w", "4y"]; const COMMON_PERIODS = ["1w", "1m", "200d", "1y", "200w", "4y"];
/** Periods to compare SMA vs EMA */
const COMPARISON_PERIODS = ["1w", "1m", "200d", "1y", "200w", "4y"];
/**
* Create SMA vs EMA comparison section
* @param {ReturnType<typeof buildSmaAverages>} smaAverages
* @param {ReturnType<typeof buildEmaAverages>} emaAverages
*/
function createCompareSection(smaAverages, emaAverages) {
// Find matching SMA/EMA pairs
const pairs = COMPARISON_PERIODS.map(id => {
const sma = smaAverages.find(a => a.id === id);
const ema = emaAverages.find(a => a.id === id);
if (!sma || !ema) return null;
return { id, sma, ema };
}).filter(/** @type {(p: any) => p is { id: string, sma: ReturnType<typeof buildSmaAverages>[number], ema: ReturnType<typeof buildEmaAverages>[number] }} */ (p) => p !== null);
return {
name: "Compare",
tree: [
{
name: "All Periods",
title: "SMA vs EMA Comparison",
top: pairs.flatMap(({ sma, ema }) => [
price({ metric: sma.ratio.price, name: `${sma.id} SMA`, color: sma.color }),
price({ metric: ema.ratio.price, name: `${ema.id} EMA`, color: ema.color, options: { lineStyle: 1 } }),
]),
},
...pairs.map(({ id, sma, ema }) => ({
name: periodIdToName(id, true),
title: `${periodIdToName(id, true)} SMA vs EMA`,
top: [
price({ metric: sma.ratio.price, name: "SMA", color: sma.color }),
price({ metric: ema.ratio.price, name: "EMA", color: ema.color, options: { lineStyle: 1 } }),
],
})),
],
};
}
/** /**
* @param {PartialContext} ctx * @param {PartialContext} ctx
* @param {MarketMovingAverage} movingAverage * @param {MarketMovingAverage} movingAverage
@@ -127,11 +166,10 @@ export function createAveragesSection(ctx, movingAverage) {
name: "Compare", name: "Compare",
title: `Price ${label}s`, title: `Price ${label}s`,
top: averages.map(({ id, color, ratio }) => top: averages.map(({ id, color, ratio }) =>
line({ price({
metric: ratio.price, metric: ratio.price,
name: id, name: id,
color, color,
unit: Unit.usd,
}), }),
), ),
}, },
@@ -165,6 +203,7 @@ export function createAveragesSection(ctx, movingAverage) {
return { return {
name: "Moving Averages", name: "Moving Averages",
tree: [ tree: [
createCompareSection(smaAverages, emaAverages),
createSubSection("SMA", smaAverages), createSubSection("SMA", smaAverages),
createSubSection("EMA", emaAverages), createSubSection("EMA", emaAverages),
], ],
+6 -14
View File
@@ -1,7 +1,4 @@
/** Bands indicators (MinMax, Mayer Multiple) */ import { price } from "../series.js";
import { Unit } from "../../utils/units.js";
import { line } from "../series.js";
/** /**
* Create Bands section * Create Bands section
@@ -47,19 +44,17 @@ export function createBandsSection(ctx, { range, movingAverage }) {
name: id, name: id,
title: `${title} MinMax`, title: `${title} MinMax`,
top: [ top: [
line({ price({
metric: min, metric: min,
name: "Min", name: "Min",
key: `price-min`, key: `price-min`,
color: colors.red, color: colors.red,
unit: Unit.usd,
}), }),
line({ price({
metric: max, metric: max,
name: "Max", name: "Max",
key: `price-max`, key: `price-max`,
color: colors.green, color: colors.green,
unit: Unit.usd,
}), }),
], ],
})), })),
@@ -68,23 +63,20 @@ export function createBandsSection(ctx, { range, movingAverage }) {
name: "Mayer Multiple", name: "Mayer Multiple",
title: "Mayer Multiple", title: "Mayer Multiple",
top: [ top: [
line({ price({
metric: movingAverage.price200dSma.price, metric: movingAverage.price200dSma.price,
name: "200d SMA", name: "200d SMA",
color: colors.yellow, color: colors.yellow,
unit: Unit.usd,
}), }),
line({ price({
metric: movingAverage.price200dSmaX24, metric: movingAverage.price200dSmaX24,
name: "200d SMA x2.4", name: "200d SMA x2.4",
color: colors.green, color: colors.green,
unit: Unit.usd,
}), }),
line({ price({
metric: movingAverage.price200dSmaX08, metric: movingAverage.price200dSmaX08,
name: "200d SMA x0.8", name: "200d SMA x0.8",
color: colors.red, color: colors.red,
unit: Unit.usd,
}), }),
], ],
}, },
+21 -20
View File
@@ -2,7 +2,7 @@
import { localhost } from "../../utils/env.js"; import { localhost } from "../../utils/env.js";
import { Unit } from "../../utils/units.js"; import { Unit } from "../../utils/units.js";
import { candlestick, line } from "../series.js"; import { candlestick, line, price } from "../series.js";
import { createAveragesSection } from "./averages.js"; import { createAveragesSection } from "./averages.js";
import { createReturnsSection } from "./performance.js"; import { createReturnsSection } from "./performance.js";
import { createMomentumSection } from "./momentum.js"; import { createMomentumSection } from "./momentum.js";
@@ -18,7 +18,7 @@ import { createDcaVsLumpSumSection, createDcaByYearSection } from "./investing.j
*/ */
export function createMarketSection(ctx) { export function createMarketSection(ctx) {
const { colors, brk } = ctx; const { colors, brk } = ctx;
const { market, supply, price } = brk.metrics; const { market, supply, price: priceMetrics } = brk.metrics;
const { const {
movingAverage, movingAverage,
ath, ath,
@@ -39,79 +39,80 @@ export function createMarketSection(ctx) {
name: "Price", name: "Price",
title: "Bitcoin Price", title: "Bitcoin Price",
}, },
// Oracle section is localhost-only debug - uses non-price-pattern metrics
...(localhost ...(localhost
? [ ? /** @type {PartialOptionsTree} */ ([
{ {
name: "Oracle", name: "Oracle",
title: "Oracle Price", title: "Oracle Price",
top: [ top: /** @type {any} */ ([
candlestick({ candlestick({
metric: price.oracle.closeOhlcDollars, metric: priceMetrics.oracle.closeOhlcDollars,
name: "close", name: "Close",
unit: Unit.usd, unit: Unit.usd,
}), }),
candlestick({ candlestick({
metric: price.oracle.midOhlcDollars, metric: priceMetrics.oracle.midOhlcDollars,
name: "mid", name: "Mid",
unit: Unit.usd, unit: Unit.usd,
}), }),
line({ line({
metric: price.oracle.phaseDailyDollars.median, metric: priceMetrics.oracle.phaseDailyDollars.median,
name: "o. p50", name: "o. p50",
unit: Unit.usd, unit: Unit.usd,
color: colors.yellow, color: colors.yellow,
}), }),
line({ line({
metric: price.oracle.phaseV2DailyDollars.median, metric: priceMetrics.oracle.phaseV2DailyDollars.median,
name: "o2. p50", name: "o2. p50",
unit: Unit.usd, unit: Unit.usd,
color: colors.orange, color: colors.orange,
}), }),
line({ line({
metric: price.oracle.phaseV2PeakDailyDollars.median, metric: priceMetrics.oracle.phaseV2PeakDailyDollars.median,
name: "o2.2 p50", name: "o2.2 p50",
unit: Unit.usd, unit: Unit.usd,
color: colors.orange, color: colors.orange,
}), }),
line({ line({
metric: price.oracle.phaseV3DailyDollars.median, metric: priceMetrics.oracle.phaseV3DailyDollars.median,
name: "o3. p50", name: "o3. p50",
unit: Unit.usd, unit: Unit.usd,
color: colors.red, color: colors.red,
}), }),
line({ line({
metric: price.oracle.phaseV3PeakDailyDollars.median, metric: priceMetrics.oracle.phaseV3PeakDailyDollars.median,
name: "o3.2 p50", name: "o3.2 p50",
unit: Unit.usd, unit: Unit.usd,
color: colors.red, color: colors.red,
}), }),
line({ line({
metric: price.oracle.phaseDailyDollars.max, metric: priceMetrics.oracle.phaseDailyDollars.max,
name: "o. max", name: "o. max",
unit: Unit.usd, unit: Unit.usd,
color: colors.lime, color: colors.lime,
}), }),
line({ line({
metric: price.oracle.phaseV2DailyDollars.max, metric: priceMetrics.oracle.phaseV2DailyDollars.max,
name: "o.2 max", name: "o.2 max",
unit: Unit.usd, unit: Unit.usd,
color: colors.emerald, color: colors.emerald,
}), }),
line({ line({
metric: price.oracle.phaseDailyDollars.min, metric: priceMetrics.oracle.phaseDailyDollars.min,
name: "o. min", name: "o. min",
unit: Unit.usd, unit: Unit.usd,
color: colors.rose, color: colors.rose,
}), }),
line({ line({
metric: price.oracle.phaseV2DailyDollars.min, metric: priceMetrics.oracle.phaseV2DailyDollars.min,
name: "o.2 min", name: "o.2 min",
unit: Unit.usd, unit: Unit.usd,
color: colors.purple, color: colors.purple,
}), }),
], ]),
}, },
] ])
: []), : []),
// Capitalization // Capitalization
@@ -131,7 +132,7 @@ export function createMarketSection(ctx) {
{ {
name: "All Time High", name: "All Time High",
title: "All Time High", title: "All Time High",
top: [line({ metric: ath.priceAth, name: "ATH", unit: Unit.usd })], top: [price({ metric: ath.priceAth, name: "ATH" })],
bottom: [ bottom: [
line({ line({
metric: ath.priceDrawdown, metric: ath.priceDrawdown,
+13 -17
View File
@@ -2,7 +2,7 @@
import { Unit } from "../../utils/units.js"; import { Unit } from "../../utils/units.js";
import { priceLine } from "../constants.js"; import { priceLine } from "../constants.js";
import { line, baseline } from "../series.js"; import { line, baseline, price } from "../series.js";
import { satsBtcUsd } from "../shared.js"; import { satsBtcUsd } from "../shared.js";
import { periodIdToName } from "./utils.js"; import { periodIdToName } from "./utils.js";
@@ -58,17 +58,15 @@ export function createDcaVsLumpSumSection(ctx, { dca, lookback, returns }) {
name: "Cost Basis", name: "Cost Basis",
title: `${name} Cost Basis`, title: `${name} Cost Basis`,
top: [ top: [
line({ price({
metric: dca.periodAveragePrice[key], metric: dca.periodAveragePrice[key],
name: "DCA", name: "DCA",
color: colors.green, color: colors.green,
unit: Unit.usd,
}), }),
line({ price({
metric: lookback[key], metric: lookback[key],
name: "Lump sum", name: "Lump sum",
color: colors.orange, color: colors.orange,
unit: Unit.usd,
}), }),
], ],
}); });
@@ -78,8 +76,8 @@ export function createDcaVsLumpSumSection(ctx, { dca, lookback, returns }) {
name: "Days in Profit", name: "Days in Profit",
title: `${name} Days in Profit`, title: `${name} Days in Profit`,
top: [ top: [
line({ metric: dca.periodAveragePrice[key], name: "DCA", color: colors.green, unit: Unit.usd }), price({ metric: dca.periodAveragePrice[key], name: "DCA", color: colors.green }),
line({ metric: lookback[key], name: "Lump sum", color: colors.orange, unit: Unit.usd }), price({ metric: lookback[key], name: "Lump sum", color: colors.orange }),
], ],
bottom: [ bottom: [
line({ metric: dca.periodDaysInProfit[key], name: "DCA", color: colors.green, unit: Unit.days }), line({ metric: dca.periodDaysInProfit[key], name: "DCA", color: colors.green, unit: Unit.days }),
@@ -92,8 +90,8 @@ export function createDcaVsLumpSumSection(ctx, { dca, lookback, returns }) {
name: "Days in Loss", name: "Days in Loss",
title: `${name} Days in Loss`, title: `${name} Days in Loss`,
top: [ top: [
line({ metric: dca.periodAveragePrice[key], name: "DCA", color: colors.green, unit: Unit.usd }), price({ metric: dca.periodAveragePrice[key], name: "DCA", color: colors.green }),
line({ metric: lookback[key], name: "Lump sum", color: colors.orange, unit: Unit.usd }), price({ metric: lookback[key], name: "Lump sum", color: colors.orange }),
], ],
bottom: [ bottom: [
line({ metric: dca.periodDaysInLoss[key], name: "DCA", color: colors.red, unit: Unit.days }), line({ metric: dca.periodDaysInLoss[key], name: "DCA", color: colors.red, unit: Unit.days }),
@@ -106,8 +104,8 @@ export function createDcaVsLumpSumSection(ctx, { dca, lookback, returns }) {
name: "Max Drawdown", name: "Max Drawdown",
title: `${name} Max Drawdown`, title: `${name} Max Drawdown`,
top: [ top: [
line({ metric: dca.periodAveragePrice[key], name: "DCA", color: colors.green, unit: Unit.usd }), price({ metric: dca.periodAveragePrice[key], name: "DCA", color: colors.green }),
line({ metric: lookback[key], name: "Lump sum", color: colors.orange, unit: Unit.usd }), price({ metric: lookback[key], name: "Lump sum", color: colors.orange }),
], ],
bottom: [ bottom: [
line({ metric: dca.periodMaxDrawdown[key], name: "DCA", color: colors.green, unit: Unit.percentage }), line({ metric: dca.periodMaxDrawdown[key], name: "DCA", color: colors.green, unit: Unit.percentage }),
@@ -120,8 +118,8 @@ export function createDcaVsLumpSumSection(ctx, { dca, lookback, returns }) {
name: "Max Return", name: "Max Return",
title: `${name} Max Return`, title: `${name} Max Return`,
top: [ top: [
line({ metric: dca.periodAveragePrice[key], name: "DCA", color: colors.green, unit: Unit.usd }), price({ metric: dca.periodAveragePrice[key], name: "DCA", color: colors.green }),
line({ metric: lookback[key], name: "Lump sum", color: colors.orange, unit: Unit.usd }), price({ metric: lookback[key], name: "Lump sum", color: colors.orange }),
], ],
bottom: [ bottom: [
line({ metric: dca.periodMaxReturn[key], name: "DCA", color: colors.green, unit: Unit.percentage }), line({ metric: dca.periodMaxReturn[key], name: "DCA", color: colors.green, unit: Unit.percentage }),
@@ -279,12 +277,11 @@ export function createDcaByYearSection(ctx, { dca }) {
name: "Cost basis", name: "Cost basis",
title: "DCA Cost Basis", title: "DCA Cost Basis",
top: dcaClasses.map(({ year, color, defaultActive, costBasis }) => top: dcaClasses.map(({ year, color, defaultActive, costBasis }) =>
line({ price({
metric: costBasis, metric: costBasis,
name: `${year}`, name: `${year}`,
color, color,
defaultActive, defaultActive,
unit: Unit.usd,
}), }),
), ),
}, },
@@ -353,11 +350,10 @@ export function createDcaByYearSection(ctx, { dca }) {
name: "Cost Basis", name: "Cost Basis",
title: `${year} Cost Basis`, title: `${year} Cost Basis`,
top: [ top: [
line({ price({
metric: costBasis, metric: costBasis,
name: "Cost Basis", name: "Cost Basis",
color, color,
unit: Unit.usd,
}), }),
], ],
}, },
+3 -5
View File
@@ -1,7 +1,7 @@
/** On-chain indicators (Pi Cycle, Puell, NVT, Gini) */ /** On-chain indicators (Pi Cycle, Puell, NVT, Gini) */
import { Unit } from "../../utils/units.js"; import { Unit } from "../../utils/units.js";
import { baseline, line } from "../series.js"; import { baseline, line, price } from "../series.js";
/** /**
* Create Valuation section * Create Valuation section
@@ -20,17 +20,15 @@ export function createValuationSection(ctx, { indicators, movingAverage }) {
name: "Pi Cycle", name: "Pi Cycle",
title: "Pi Cycle", title: "Pi Cycle",
top: [ top: [
line({ price({
metric: movingAverage.price111dSma.price, metric: movingAverage.price111dSma.price,
name: "111d SMA", name: "111d SMA",
color: colors.green, color: colors.green,
unit: Unit.usd,
}), }),
line({ price({
metric: movingAverage.price350dSmaX2, metric: movingAverage.price350dSmaX2,
name: "350d SMA x2", name: "350d SMA x2",
color: colors.red, color: colors.red,
unit: Unit.usd,
}), }),
], ],
bottom: [ bottom: [
+2 -2
View File
@@ -282,9 +282,9 @@ export function createPartialOptions({ brk }) {
], ],
}, },
// Research section // Frameworks section
{ {
name: "Research", name: "Frameworks",
tree: [ tree: [
createCointimeSection(ctx), createCointimeSection(ctx),
], ],
+38
View File
@@ -2,6 +2,44 @@
import { Unit } from "../utils/units.js"; import { Unit } from "../utils/units.js";
// ============================================================================
// Price helper for top pane (auto-expands to USD + sats)
// ============================================================================
/**
* Create a price series for the top pane (auto-expands to USD + sats versions)
* @param {Object} args
* @param {AnyPricePattern} args.metric - Price pattern with dollars and sats
* @param {string} args.name
* @param {string} [args.key]
* @param {LineStyle} [args.style]
* @param {Color} [args.color]
* @param {boolean} [args.defaultActive]
* @param {LineSeriesPartialOptions} [args.options]
* @returns {FetchedPriceSeriesBlueprint}
*/
export function price({
metric,
name,
key,
style,
color,
defaultActive,
options,
}) {
return {
metric,
title: name,
key,
color,
defaultActive,
options: {
lineStyle: style,
...options,
},
};
}
// ============================================================================ // ============================================================================
// Shared percentile helper // Shared percentile helper
// ============================================================================ // ============================================================================
+15 -21
View File
@@ -1,7 +1,7 @@
/** Shared helpers for options */ /** Shared helpers for options */
import { Unit } from "../utils/units.js"; import { Unit } from "../utils/units.js";
import { line, baseline } from "./series.js"; import { line, baseline, price } from "./series.js";
import { priceLine, priceLines } from "./constants.js"; import { priceLine, priceLines } from "./constants.js";
/** /**
@@ -153,27 +153,26 @@ export function ratioSmas(colors, ratio) {
* @param {PartialContext} ctx * @param {PartialContext} ctx
* @param {Object} args * @param {Object} args
* @param {(metric: string) => string} args.title * @param {(metric: string) => string} args.title
* @param {AnyMetricPattern} args.price - The price metric to show in top pane * @param {AnyPricePattern} args.pricePattern - The price pattern to show in top pane
* @param {ActivePriceRatioPattern} args.ratio - The ratio pattern * @param {ActivePriceRatioPattern} args.ratio - The ratio pattern
* @param {Color} args.color * @param {Color} args.color
* @param {string} [args.name] - Optional name override (default: "ratio") * @param {string} [args.name] - Optional name override (default: "ratio")
* @returns {PartialChartOption} * @returns {PartialChartOption}
*/ */
export function createRatioChart(ctx, { title, price, ratio, color, name }) { export function createRatioChart(ctx, { title, pricePattern, ratio, color, name }) {
const { colors } = ctx; const { colors } = ctx;
return { return {
name: name ?? "ratio", name: name ?? "ratio",
title: title(name ?? "Ratio"), title: title(name ?? "Ratio"),
top: [ top: [
line({ metric: price, name: "Price", color, unit: Unit.usd }), price({ metric: pricePattern, name: "Price", color }),
...percentileUsdMap(colors, ratio).map(({ name, prop, color }) => ...percentileUsdMap(colors, ratio).map(({ name, prop, color }) =>
line({ price({
metric: prop, metric: prop,
name, name,
color, color,
defaultActive: false, defaultActive: false,
unit: Unit.usd,
options: { lineStyle: 1 }, options: { lineStyle: 1 },
}), }),
), ),
@@ -208,14 +207,14 @@ export function createRatioChart(ctx, { title, price, ratio, color, name }) {
* @param {Object} args * @param {Object} args
* @param {string} args.title * @param {string} args.title
* @param {string} args.legend * @param {string} args.legend
* @param {AnyMetricPattern} args.price - The price metric to show in top pane * @param {AnyPricePattern} args.pricePattern - The price pattern to show in top pane
* @param {ActivePriceRatioPattern} args.ratio - The ratio pattern * @param {ActivePriceRatioPattern} args.ratio - The ratio pattern
* @param {Color} args.color * @param {Color} args.color
* @returns {PartialOptionsGroup} * @returns {PartialOptionsGroup}
*/ */
export function createZScoresFolder( export function createZScoresFolder(
ctx, ctx,
{ title, legend, price, ratio, color }, { title, legend, pricePattern, ratio, color },
) { ) {
const { colors } = ctx; const { colors } = ctx;
const sdPats = sdPatterns(ratio); const sdPats = sdPatterns(ratio);
@@ -227,40 +226,36 @@ export function createZScoresFolder(
name: "Compare", name: "Compare",
title: `${title} Z-Scores`, title: `${title} Z-Scores`,
top: [ top: [
line({ metric: price, name: legend, color, unit: Unit.usd }), price({ metric: pricePattern, name: legend, color }),
line({ price({
metric: ratio.ratio1ySd._0sdUsd, metric: ratio.ratio1ySd._0sdUsd,
name: "1y 0σ", name: "1y 0σ",
color: colors.orange, color: colors.orange,
defaultActive: false, defaultActive: false,
unit: Unit.usd,
}), }),
line({ price({
metric: ratio.ratio2ySd._0sdUsd, metric: ratio.ratio2ySd._0sdUsd,
name: "2y 0σ", name: "2y 0σ",
color: colors.yellow, color: colors.yellow,
defaultActive: false, defaultActive: false,
unit: Unit.usd,
}), }),
line({ price({
metric: ratio.ratio4ySd._0sdUsd, metric: ratio.ratio4ySd._0sdUsd,
name: "4y 0σ", name: "4y 0σ",
color: colors.lime, color: colors.lime,
defaultActive: false, defaultActive: false,
unit: Unit.usd,
}), }),
line({ price({
metric: ratio.ratioSd._0sdUsd, metric: ratio.ratioSd._0sdUsd,
name: "all 0σ", name: "all 0σ",
color: colors.blue, color: colors.blue,
defaultActive: false, defaultActive: false,
unit: Unit.usd,
}), }),
], ],
bottom: [ bottom: [
line({ line({
metric: ratio.ratioSd.zscore, metric: ratio.ratioSd.zscore,
name: "all", name: "All",
color: colors.blue, color: colors.blue,
unit: Unit.sd, unit: Unit.sd,
}), }),
@@ -294,14 +289,13 @@ export function createZScoresFolder(
name: nameAddon, name: nameAddon,
title: `${title} ${titleAddon} Z-Score`, title: `${title} ${titleAddon} Z-Score`,
top: [ top: [
line({ metric: price, name: legend, color, unit: Unit.usd }), price({ metric: pricePattern, name: legend, color }),
...sdBandsUsd(colors, sd).map( ...sdBandsUsd(colors, sd).map(
({ name: bandName, prop, color: bandColor }) => ({ name: bandName, prop, color: bandColor }) =>
line({ price({
metric: prop, metric: prop,
name: bandName, name: bandName,
color: bandColor, color: bandColor,
unit: Unit.usd,
defaultActive: false, defaultActive: false,
}), }),
), ),
+8 -1
View File
@@ -48,6 +48,13 @@
* @typedef {DotsSeriesBlueprint & FetchedAnySeriesOptions} FetchedDotsSeriesBlueprint * @typedef {DotsSeriesBlueprint & FetchedAnySeriesOptions} FetchedDotsSeriesBlueprint
* @typedef {AnySeriesBlueprint & FetchedAnySeriesOptions} AnyFetchedSeriesBlueprint * @typedef {AnySeriesBlueprint & FetchedAnySeriesOptions} AnyFetchedSeriesBlueprint
* *
* Any pattern with dollars and sats sub-metrics (auto-expands to USD + sats)
* @typedef {{ dollars: AnyMetricPattern, sats: AnyMetricPattern }} AnyPricePattern
*
* Top pane price series - requires a price pattern with dollars/sats, auto-expands to USD + sats
* @typedef {{ metric: AnyPricePattern }} FetchedPriceSeriesOptions
* @typedef {LineSeriesBlueprint & FetchedPriceSeriesOptions} FetchedPriceSeriesBlueprint
*
* @typedef {Object} PartialOption * @typedef {Object} PartialOption
* @property {string} name * @property {string} name
* *
@@ -66,7 +73,7 @@
* @typedef {Object} PartialChartOptionSpecific * @typedef {Object} PartialChartOptionSpecific
* @property {"chart"} [kind] * @property {"chart"} [kind]
* @property {string} title * @property {string} title
* @property {AnyFetchedSeriesBlueprint[]} [top] * @property {FetchedPriceSeriesBlueprint[]} [top]
* @property {AnyFetchedSeriesBlueprint[]} [bottom] * @property {AnyFetchedSeriesBlueprint[]} [bottom]
* *
* @typedef {PartialOption & PartialChartOptionSpecific} PartialChartOption * @typedef {PartialOption & PartialChartOptionSpecific} PartialChartOption
+5 -1
View File
@@ -14,7 +14,7 @@
* *
* @import { WebSockets } from "./utils/ws.js" * @import { WebSockets } from "./utils/ws.js"
* *
* @import { Option, PartialChartOption, ChartOption, AnyPartialOption, ProcessedOptionAddons, OptionsTree, SimulationOption, AnySeriesBlueprint, SeriesType, AnyFetchedSeriesBlueprint, TableOption, ExplorerOption, UrlOption, PartialOptionsGroup, OptionsGroup, PartialOptionsTree, UtxoCohortObject, AddressCohortObject, CohortObject, CohortGroupObject, FetchedLineSeriesBlueprint, FetchedBaselineSeriesBlueprint, FetchedHistogramSeriesBlueprint, PartialContext, PatternAll, PatternFull, PatternWithAdjusted, PatternWithPercentiles, PatternBasic, PatternBasicWithMarketCap, PatternBasicWithoutMarketCap, CohortAll, CohortFull, CohortWithAdjusted, CohortWithPercentiles, CohortBasic, CohortBasicWithMarketCap, CohortBasicWithoutMarketCap, CohortAddress, CohortLongTerm, CohortAgeRange, CohortGroupFull, CohortGroupWithAdjusted, CohortGroupWithPercentiles, CohortGroupLongTerm, CohortGroupAgeRange, CohortGroupBasic, CohortGroupBasicWithMarketCap, CohortGroupBasicWithoutMarketCap, CohortGroupAddress, UtxoCohortGroupObject, AddressCohortGroupObject, FetchedDotsSeriesBlueprint, FetchedCandlestickSeriesBlueprint } from "./options/partial.js" * @import { Option, PartialChartOption, ChartOption, AnyPartialOption, ProcessedOptionAddons, OptionsTree, SimulationOption, AnySeriesBlueprint, SeriesType, AnyFetchedSeriesBlueprint, TableOption, ExplorerOption, UrlOption, PartialOptionsGroup, OptionsGroup, PartialOptionsTree, UtxoCohortObject, AddressCohortObject, CohortObject, CohortGroupObject, FetchedLineSeriesBlueprint, FetchedBaselineSeriesBlueprint, FetchedHistogramSeriesBlueprint, PartialContext, PatternAll, PatternFull, PatternWithAdjusted, PatternWithPercentiles, PatternBasic, PatternBasicWithMarketCap, PatternBasicWithoutMarketCap, CohortAll, CohortFull, CohortWithAdjusted, CohortWithPercentiles, CohortBasic, CohortBasicWithMarketCap, CohortBasicWithoutMarketCap, CohortAddress, CohortLongTerm, CohortAgeRange, CohortGroupFull, CohortGroupWithAdjusted, CohortGroupWithPercentiles, CohortGroupLongTerm, CohortGroupAgeRange, CohortGroupBasic, CohortGroupBasicWithMarketCap, CohortGroupBasicWithoutMarketCap, CohortGroupAddress, UtxoCohortGroupObject, AddressCohortGroupObject, FetchedDotsSeriesBlueprint, FetchedCandlestickSeriesBlueprint, FetchedPriceSeriesBlueprint, AnyPricePattern } from "./options/partial.js"
* *
* *
* @import { UnitObject as Unit } from "./utils/units.js" * @import { UnitObject as Unit } from "./utils/units.js"
@@ -50,6 +50,7 @@
* @typedef {Brk.ActivePriceRatioPattern} ActivePriceRatioPattern * @typedef {Brk.ActivePriceRatioPattern} ActivePriceRatioPattern
* @typedef {Brk.UnclaimedRewardsPattern} ValuePattern * @typedef {Brk.UnclaimedRewardsPattern} ValuePattern
* @typedef {Brk.AnyMetricPattern} AnyMetricPattern * @typedef {Brk.AnyMetricPattern} AnyMetricPattern
* @typedef {Brk.ActivePricePattern} ActivePricePattern
* @typedef {Brk.AnyMetricEndpointBuilder} AnyMetricEndpoint * @typedef {Brk.AnyMetricEndpointBuilder} AnyMetricEndpoint
* @typedef {Brk.AnyMetricData} AnyMetricData * @typedef {Brk.AnyMetricData} AnyMetricData
* @typedef {Brk.AddrCountPattern} AddrCountPattern * @typedef {Brk.AddrCountPattern} AddrCountPattern
@@ -132,6 +133,9 @@
* Realized pattern capability types (RealizedPattern2 and RealizedPattern3 have extra metrics) * Realized pattern capability types (RealizedPattern2 and RealizedPattern3 have extra metrics)
* @typedef {Brk.RealizedPattern2 | Brk.RealizedPattern3} RealizedWithExtras * @typedef {Brk.RealizedPattern2 | Brk.RealizedPattern3} RealizedWithExtras
* *
* Any realized pattern (all have sellSideRiskRatio, valueCreated, valueDestroyed, etc.)
* @typedef {Brk.RealizedPattern | Brk.RealizedPattern2 | Brk.RealizedPattern3 | Brk.RealizedPattern4} AnyRealizedPattern
*
* Capability-based pattern groupings (patterns that have specific properties) * Capability-based pattern groupings (patterns that have specific properties)
* @typedef {AllUtxoPattern | AgeRangePattern | UtxoAmountPattern} PatternWithRealizedPrice * @typedef {AllUtxoPattern | AgeRangePattern | UtxoAmountPattern} PatternWithRealizedPrice
* @typedef {AllUtxoPattern} PatternWithFullRealized * @typedef {AllUtxoPattern} PatternWithFullRealized
+22 -4
View File
@@ -123,7 +123,11 @@ search {
.shadow-top { .shadow-top {
position: absolute; position: absolute;
background-image: linear-gradient(to top, transparent, var(--background-color)); background-image: linear-gradient(
to top,
transparent,
var(--background-color)
);
height: var(--main-padding); height: var(--main-padding);
top: 0; top: 0;
left: 0; left: 0;
@@ -134,7 +138,12 @@ search {
.shadow-bottom { .shadow-bottom {
position: absolute; position: absolute;
background-image: linear-gradient(to bottom, transparent, var(--background-color) 90%, var(--background-color)); background-image: linear-gradient(
to bottom,
transparent,
var(--background-color) 90%,
var(--background-color)
);
height: 21rem; height: 21rem;
bottom: 0; bottom: 0;
left: 0; left: 0;
@@ -145,7 +154,11 @@ search {
.shadow-left { .shadow-left {
position: absolute; position: absolute;
background-image: linear-gradient(to left, transparent, var(--background-color)); background-image: linear-gradient(
to left,
transparent,
var(--background-color)
);
width: var(--main-padding); width: var(--main-padding);
left: 0; left: 0;
top: 0; top: 0;
@@ -156,7 +169,11 @@ search {
.shadow-right { .shadow-right {
position: absolute; position: absolute;
background-image: linear-gradient(to right, transparent, var(--background-color)); background-image: linear-gradient(
to right,
transparent,
var(--background-color)
);
width: var(--main-padding); width: var(--main-padding);
right: 0; right: 0;
top: 0; top: 0;
@@ -290,6 +307,7 @@ fieldset {
padding-right: 0.375rem; padding-right: 0.375rem;
margin-left: -0.375rem; margin-left: -0.375rem;
margin-right: -0.375rem; margin-right: -0.375rem;
margin-top: 0.1rem;
} }
} }
} }