mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-04-24 06:39:58 -07:00
global: snapshot
This commit is contained in:
@@ -15,7 +15,7 @@ pub struct Vecs<M: StorageMode = Rw> {
|
||||
impl Vecs {
|
||||
pub(crate) fn forced_import(db: &Database, version: Version) -> Result<Self> {
|
||||
Ok(Self {
|
||||
identity: EagerVec::forced_import(db, "day1", version)?,
|
||||
identity: EagerVec::forced_import(db, "day1_index", version)?,
|
||||
date: EagerVec::forced_import(db, "date", version + Version::ONE)?,
|
||||
first_height: EagerVec::forced_import(db, "first_height", version)?,
|
||||
height_count: EagerVec::forced_import(db, "height_count", version)?,
|
||||
|
||||
@@ -13,7 +13,7 @@ pub struct Vecs<M: StorageMode = Rw> {
|
||||
impl Vecs {
|
||||
pub(crate) fn forced_import(db: &Database, version: Version) -> Result<Self> {
|
||||
Ok(Self {
|
||||
identity: EagerVec::forced_import(db, "day3", version)?,
|
||||
identity: EagerVec::forced_import(db, "day3_index", version)?,
|
||||
first_height: EagerVec::forced_import(db, "day3_first_height", version)?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ pub struct Vecs<M: StorageMode = Rw> {
|
||||
impl Vecs {
|
||||
pub(crate) fn forced_import(db: &Database, version: Version) -> Result<Self> {
|
||||
Ok(Self {
|
||||
identity: EagerVec::forced_import(db, "hour1", version)?,
|
||||
identity: EagerVec::forced_import(db, "hour1_index", version)?,
|
||||
first_height: EagerVec::forced_import(db, "hour1_first_height", version)?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ pub struct Vecs<M: StorageMode = Rw> {
|
||||
impl Vecs {
|
||||
pub(crate) fn forced_import(db: &Database, version: Version) -> Result<Self> {
|
||||
Ok(Self {
|
||||
identity: EagerVec::forced_import(db, "hour12", version)?,
|
||||
identity: EagerVec::forced_import(db, "hour12_index", version)?,
|
||||
first_height: EagerVec::forced_import(db, "hour12_first_height", version)?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ pub struct Vecs<M: StorageMode = Rw> {
|
||||
impl Vecs {
|
||||
pub(crate) fn forced_import(db: &Database, version: Version) -> Result<Self> {
|
||||
Ok(Self {
|
||||
identity: EagerVec::forced_import(db, "hour4", version)?,
|
||||
identity: EagerVec::forced_import(db, "hour4_index", version)?,
|
||||
first_height: EagerVec::forced_import(db, "hour4_first_height", version)?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ pub struct Vecs<M: StorageMode = Rw> {
|
||||
impl Vecs {
|
||||
pub(crate) fn forced_import(db: &Database, version: Version) -> Result<Self> {
|
||||
Ok(Self {
|
||||
identity: EagerVec::forced_import(db, "minute10", version)?,
|
||||
identity: EagerVec::forced_import(db, "minute10_index", version)?,
|
||||
first_height: EagerVec::forced_import(db, "minute10_first_height", version)?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ pub struct Vecs<M: StorageMode = Rw> {
|
||||
impl Vecs {
|
||||
pub(crate) fn forced_import(db: &Database, version: Version) -> Result<Self> {
|
||||
Ok(Self {
|
||||
identity: EagerVec::forced_import(db, "minute30", version)?,
|
||||
identity: EagerVec::forced_import(db, "minute30_index", version)?,
|
||||
first_height: EagerVec::forced_import(db, "minute30_first_height", version)?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ pub struct Vecs<M: StorageMode = Rw> {
|
||||
impl Vecs {
|
||||
pub(crate) fn forced_import(db: &Database, version: Version) -> Result<Self> {
|
||||
Ok(Self {
|
||||
identity: EagerVec::forced_import(db, "month1", version)?,
|
||||
identity: EagerVec::forced_import(db, "month1_index", version)?,
|
||||
date: EagerVec::forced_import(db, "date", version)?,
|
||||
first_height: EagerVec::forced_import(db, "month1_first_height", version)?,
|
||||
})
|
||||
|
||||
@@ -14,7 +14,7 @@ pub struct Vecs<M: StorageMode = Rw> {
|
||||
impl Vecs {
|
||||
pub(crate) fn forced_import(db: &Database, version: Version) -> Result<Self> {
|
||||
Ok(Self {
|
||||
identity: EagerVec::forced_import(db, "month3", version)?,
|
||||
identity: EagerVec::forced_import(db, "month3_index", version)?,
|
||||
date: EagerVec::forced_import(db, "date", version)?,
|
||||
first_height: EagerVec::forced_import(db, "month3_first_height", version)?,
|
||||
})
|
||||
|
||||
@@ -14,7 +14,7 @@ pub struct Vecs<M: StorageMode = Rw> {
|
||||
impl Vecs {
|
||||
pub(crate) fn forced_import(db: &Database, version: Version) -> Result<Self> {
|
||||
Ok(Self {
|
||||
identity: EagerVec::forced_import(db, "month6", version)?,
|
||||
identity: EagerVec::forced_import(db, "month6_index", version)?,
|
||||
date: EagerVec::forced_import(db, "date", version)?,
|
||||
first_height: EagerVec::forced_import(db, "month6_first_height", version)?,
|
||||
})
|
||||
|
||||
@@ -14,7 +14,7 @@ pub struct Vecs<M: StorageMode = Rw> {
|
||||
impl Vecs {
|
||||
pub(crate) fn forced_import(db: &Database, version: Version) -> Result<Self> {
|
||||
Ok(Self {
|
||||
identity: EagerVec::forced_import(db, "week1", version)?,
|
||||
identity: EagerVec::forced_import(db, "week1_index", version)?,
|
||||
date: EagerVec::forced_import(db, "date", version)?,
|
||||
first_height: EagerVec::forced_import(db, "week1_first_height", version)?,
|
||||
})
|
||||
|
||||
@@ -14,7 +14,7 @@ pub struct Vecs<M: StorageMode = Rw> {
|
||||
impl Vecs {
|
||||
pub(crate) fn forced_import(db: &Database, version: Version) -> Result<Self> {
|
||||
Ok(Self {
|
||||
identity: EagerVec::forced_import(db, "year1", version)?,
|
||||
identity: EagerVec::forced_import(db, "year1_index", version)?,
|
||||
date: EagerVec::forced_import(db, "date", version)?,
|
||||
first_height: EagerVec::forced_import(db, "year1_first_height", version)?,
|
||||
})
|
||||
|
||||
@@ -14,7 +14,7 @@ pub struct Vecs<M: StorageMode = Rw> {
|
||||
impl Vecs {
|
||||
pub(crate) fn forced_import(db: &Database, version: Version) -> Result<Self> {
|
||||
Ok(Self {
|
||||
identity: EagerVec::forced_import(db, "year10", version)?,
|
||||
identity: EagerVec::forced_import(db, "year10_index", version)?,
|
||||
date: EagerVec::forced_import(db, "date", version)?,
|
||||
first_height: EagerVec::forced_import(db, "year10_first_height", version)?,
|
||||
})
|
||||
|
||||
@@ -5,8 +5,6 @@ use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use vecdb::PrintableIndex;
|
||||
|
||||
use crate::PairOutputIndex;
|
||||
|
||||
use super::{
|
||||
Date, Day1, Day3, EmptyAddressIndex, EmptyOutputIndex, Epoch, FundedAddressIndex, Halving,
|
||||
Height, Hour1, Hour4, Hour12, Minute10, Minute30, Month1, Month3, Month6, OpReturnIndex,
|
||||
@@ -39,28 +37,44 @@ pub enum Index {
|
||||
Halving,
|
||||
Epoch,
|
||||
Height,
|
||||
#[serde(rename = "tx_index")]
|
||||
TxIndex,
|
||||
#[serde(rename = "txin_index")]
|
||||
TxInIndex,
|
||||
#[serde(rename = "txout_index")]
|
||||
TxOutIndex,
|
||||
#[serde(rename = "empty_output_index")]
|
||||
EmptyOutputIndex,
|
||||
#[serde(rename = "op_return_index")]
|
||||
OpReturnIndex,
|
||||
#[serde(rename = "p2a_address_index")]
|
||||
P2AAddressIndex,
|
||||
#[serde(rename = "p2ms_output_index")]
|
||||
P2MSOutputIndex,
|
||||
#[serde(rename = "p2pk33_address_index")]
|
||||
P2PK33AddressIndex,
|
||||
#[serde(rename = "p2pk65_address_index")]
|
||||
P2PK65AddressIndex,
|
||||
#[serde(rename = "p2pkh_address_index")]
|
||||
P2PKHAddressIndex,
|
||||
#[serde(rename = "p2sh_address_index")]
|
||||
P2SHAddressIndex,
|
||||
#[serde(rename = "p2tr_address_index")]
|
||||
P2TRAddressIndex,
|
||||
#[serde(rename = "p2wpkh_address_index")]
|
||||
P2WPKHAddressIndex,
|
||||
#[serde(rename = "p2wsh_address_index")]
|
||||
P2WSHAddressIndex,
|
||||
#[serde(rename = "unknown_output_index")]
|
||||
UnknownOutputIndex,
|
||||
#[serde(rename = "funded_address_index")]
|
||||
FundedAddressIndex,
|
||||
#[serde(rename = "empty_address_index")]
|
||||
EmptyAddressIndex,
|
||||
PairOutputIndex,
|
||||
}
|
||||
|
||||
impl Index {
|
||||
pub const fn all() -> [Self; 34] {
|
||||
pub const fn all() -> [Self; 33] {
|
||||
[
|
||||
Self::Minute10,
|
||||
Self::Minute30,
|
||||
@@ -95,7 +109,6 @@ impl Index {
|
||||
Self::UnknownOutputIndex,
|
||||
Self::FundedAddressIndex,
|
||||
Self::EmptyAddressIndex,
|
||||
Self::PairOutputIndex,
|
||||
]
|
||||
}
|
||||
|
||||
@@ -134,7 +147,6 @@ impl Index {
|
||||
Self::UnknownOutputIndex => UnknownOutputIndex::to_possible_strings(),
|
||||
Self::FundedAddressIndex => FundedAddressIndex::to_possible_strings(),
|
||||
Self::EmptyAddressIndex => EmptyAddressIndex::to_possible_strings(),
|
||||
Self::PairOutputIndex => PairOutputIndex::to_possible_strings(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,7 +192,6 @@ impl Index {
|
||||
Self::UnknownOutputIndex => <UnknownOutputIndex as PrintableIndex>::to_string(),
|
||||
Self::FundedAddressIndex => <FundedAddressIndex as PrintableIndex>::to_string(),
|
||||
Self::EmptyAddressIndex => <EmptyAddressIndex as PrintableIndex>::to_string(),
|
||||
Self::PairOutputIndex => <PairOutputIndex as PrintableIndex>::to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -131,7 +131,6 @@ mod p2wsh_address_index;
|
||||
mod p2wsh_bytes;
|
||||
mod pagination;
|
||||
mod pagination_index;
|
||||
mod pair_output_index;
|
||||
mod percentile;
|
||||
mod pool;
|
||||
mod pool_detail;
|
||||
@@ -327,7 +326,6 @@ pub use p2wsh_address_index::*;
|
||||
pub use p2wsh_bytes::*;
|
||||
pub use pagination::*;
|
||||
pub use pagination_index::*;
|
||||
pub use pair_output_index::*;
|
||||
pub use percentile::*;
|
||||
pub use pool::*;
|
||||
pub use pool_detail::*;
|
||||
|
||||
@@ -426,7 +426,7 @@
|
||||
* Aggregation dimension for querying metrics. Includes time-based (date, week, month, year),
|
||||
* block-based (height, tx_index), and address/output type indexes.
|
||||
*
|
||||
* @typedef {("minute10"|"minute30"|"hour1"|"hour4"|"hour12"|"day1"|"day3"|"week1"|"month1"|"month3"|"month6"|"year1"|"year10"|"halving"|"epoch"|"height"|"txindex"|"txinindex"|"txoutindex"|"emptyoutputindex"|"opreturnindex"|"p2aaddressindex"|"p2msoutputindex"|"p2pk33addressindex"|"p2pk65addressindex"|"p2pkhaddressindex"|"p2shaddressindex"|"p2traddressindex"|"p2wpkhaddressindex"|"p2wshaddressindex"|"unknownoutputindex"|"fundedaddressindex"|"emptyaddressindex"|"pairoutputindex")} Index
|
||||
* @typedef {("minute10"|"minute30"|"hour1"|"hour4"|"hour12"|"day1"|"day3"|"week1"|"month1"|"month3"|"month6"|"year1"|"year10"|"halving"|"epoch"|"height"|"tx_index"|"txin_index"|"txout_index"|"empty_output_index"|"op_return_index"|"p2a_address_index"|"p2ms_output_index"|"p2pk33_address_index"|"p2pk65_address_index"|"p2pkh_address_index"|"p2sh_address_index"|"p2tr_address_index"|"p2wpkh_address_index"|"p2wsh_address_index"|"unknown_output_index"|"funded_address_index"|"empty_address_index")} Index
|
||||
*/
|
||||
/**
|
||||
* Information about an available index and its query aliases
|
||||
@@ -6001,8 +6001,7 @@ class BrkClient extends BrkClientBase {
|
||||
"p2wsh_address_index",
|
||||
"unknown_output_index",
|
||||
"funded_address_index",
|
||||
"empty_address_index",
|
||||
"pair_output_index"
|
||||
"empty_address_index"
|
||||
]);
|
||||
|
||||
POOL_ID_TO_POOL_NAME = /** @type {const} */ ({
|
||||
|
||||
@@ -199,7 +199,7 @@ Year1 = int
|
||||
Year10 = int
|
||||
# Aggregation dimension for querying metrics. Includes time-based (date, week, month, year),
|
||||
# block-based (height, tx_index), and address/output type indexes.
|
||||
Index = Literal["minute10", "minute30", "hour1", "hour4", "hour12", "day1", "day3", "week1", "month1", "month3", "month6", "year1", "year10", "halving", "epoch", "height", "txindex", "txinindex", "txoutindex", "emptyoutputindex", "opreturnindex", "p2aaddressindex", "p2msoutputindex", "p2pk33addressindex", "p2pk65addressindex", "p2pkhaddressindex", "p2shaddressindex", "p2traddressindex", "p2wpkhaddressindex", "p2wshaddressindex", "unknownoutputindex", "fundedaddressindex", "emptyaddressindex", "pairoutputindex"]
|
||||
Index = Literal["minute10", "minute30", "hour1", "hour4", "hour12", "day1", "day3", "week1", "month1", "month3", "month6", "year1", "year10", "halving", "epoch", "height", "tx_index", "txin_index", "txout_index", "empty_output_index", "op_return_index", "p2a_address_index", "p2ms_output_index", "p2pk33_address_index", "p2pk65_address_index", "p2pkh_address_index", "p2sh_address_index", "p2tr_address_index", "p2wpkh_address_index", "p2wsh_address_index", "unknown_output_index", "funded_address_index", "empty_address_index"]
|
||||
# Hierarchical tree node for organizing metrics into categories
|
||||
TreeNode = Union[dict[str, "TreeNode"], "MetricLeafWithSchema"]
|
||||
class AddressChainStats(TypedDict):
|
||||
@@ -5322,8 +5322,7 @@ class BrkClient(BrkClientBase):
|
||||
"p2wsh_address_index",
|
||||
"unknown_output_index",
|
||||
"funded_address_index",
|
||||
"empty_address_index",
|
||||
"pair_output_index"
|
||||
"empty_address_index"
|
||||
]
|
||||
|
||||
POOL_ID_TO_POOL_NAME = {
|
||||
|
||||
@@ -71,6 +71,8 @@ import { Unit } from "../utils/units.js";
|
||||
|
||||
const lineWidth = /** @type {any} */ (1.5);
|
||||
|
||||
const MAX_SIZE = 100_000;
|
||||
|
||||
/**
|
||||
* @param {Object} args
|
||||
* @param {HTMLElement} args.parent
|
||||
@@ -138,7 +140,7 @@ export function createChart({ parent, brk, fitContent }) {
|
||||
if (cached) {
|
||||
this.data = cached;
|
||||
}
|
||||
endpoint.slice(-10000).fetch((/** @type {any} */ result) => {
|
||||
endpoint.slice(-MAX_SIZE).fetch((/** @type {any} */ result) => {
|
||||
if (currentGen !== generation) return;
|
||||
cache.set(endpoint.path, result);
|
||||
this.data = result;
|
||||
@@ -741,7 +743,7 @@ export function createChart({ parent, brk, fitContent }) {
|
||||
valuesVersion = cachedValues.version;
|
||||
tryProcess();
|
||||
}
|
||||
await valuesEndpoint.slice(-10000).fetch((result) => {
|
||||
await valuesEndpoint.slice(-MAX_SIZE).fetch((result) => {
|
||||
cache.set(valuesEndpoint.path, result);
|
||||
valuesData = result.data;
|
||||
valuesStamp = result.stamp;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { colors } from "../utils/colors.js";
|
||||
import { brk } from "../client.js";
|
||||
import { Unit } from "../utils/units.js";
|
||||
import { dots, line, price, rollingWindowsTree } from "./series.js";
|
||||
import { satsBtcUsd, createPriceRatioCharts } from "./shared.js";
|
||||
import { dots, line, baseline, price, rollingWindowsTree } from "./series.js";
|
||||
import { satsBtcUsd } from "./shared.js";
|
||||
|
||||
/**
|
||||
* Create Cointime section
|
||||
@@ -24,7 +24,7 @@ export function createCointimeSection() {
|
||||
|
||||
// Reference lines for cap comparisons
|
||||
const capReferenceLines = /** @type {const} */ ([
|
||||
{ metric: supply.marketCap, name: "Market", color: colors.default },
|
||||
{ metric: supply.marketCap.usd, name: "Market", color: colors.default },
|
||||
{
|
||||
metric: all.realized.cap.usd,
|
||||
name: "Realized",
|
||||
@@ -153,24 +153,53 @@ export function createCointimeSection() {
|
||||
),
|
||||
],
|
||||
},
|
||||
...prices.map(({ pattern, name, color }) => ({
|
||||
name,
|
||||
tree: createPriceRatioCharts({
|
||||
context: `${name} Price`,
|
||||
legend: name,
|
||||
pricePattern: pattern,
|
||||
ratio: pattern,
|
||||
color,
|
||||
priceReferences: [
|
||||
price({
|
||||
metric: all.realized.price,
|
||||
name: "Realized",
|
||||
color: colors.realized,
|
||||
defaultActive: false,
|
||||
}),
|
||||
...prices.map(({ pattern, name, color }) => {
|
||||
const p = pattern.percentiles;
|
||||
const pctUsd = /** @type {const} */ ([
|
||||
{ name: "pct95", prop: p.pct95.price, color: colors.ratioPct._95 },
|
||||
{ name: "pct5", prop: p.pct5.price, color: colors.ratioPct._5 },
|
||||
{ name: "pct99", prop: p.pct99.price, color: colors.ratioPct._99 },
|
||||
{ name: "pct1", prop: p.pct1.price, color: colors.ratioPct._1 },
|
||||
]);
|
||||
const pctRatio = /** @type {const} */ ([
|
||||
{ name: "pct95", prop: p.pct95.ratio, color: colors.ratioPct._95 },
|
||||
{ name: "pct5", prop: p.pct5.ratio, color: colors.ratioPct._5 },
|
||||
{ name: "pct99", prop: p.pct99.ratio, color: colors.ratioPct._99 },
|
||||
{ name: "pct1", prop: p.pct1.ratio, color: colors.ratioPct._1 },
|
||||
]);
|
||||
return {
|
||||
name,
|
||||
tree: [
|
||||
{
|
||||
name: "Price",
|
||||
title: `${name} Price`,
|
||||
top: [
|
||||
price({ metric: pattern, name, color }),
|
||||
price({
|
||||
metric: all.realized.price,
|
||||
name: "Realized",
|
||||
color: colors.realized,
|
||||
defaultActive: false,
|
||||
}),
|
||||
...pctUsd.map(({ name: pName, prop, color: pColor }) =>
|
||||
price({ metric: prop, name: pName, color: pColor, defaultActive: false, options: { lineStyle: 1 } }),
|
||||
),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Ratio",
|
||||
title: `${name} Price Ratio`,
|
||||
top: [price({ metric: pattern, name, color })],
|
||||
bottom: [
|
||||
baseline({ metric: pattern.ratio, name: "Ratio", unit: Unit.ratio, base: 1 }),
|
||||
...pctRatio.map(({ name: pName, prop, color: pColor }) =>
|
||||
line({ metric: prop, name: pName, color: pColor, defaultActive: false, unit: Unit.ratio, options: { lineStyle: 1 } }),
|
||||
),
|
||||
],
|
||||
},
|
||||
],
|
||||
}),
|
||||
})),
|
||||
};
|
||||
}),
|
||||
],
|
||||
},
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import { line } from "./series.js";
|
||||
|
||||
/**
|
||||
* Get constant pattern by number dynamically from tree
|
||||
* Examples: 0 → constant0, 38.2 → constant382, -1 → constantMinus1
|
||||
* Examples: 0 → _0, 38.2 → _382, -1 → minus1
|
||||
* @param {BrkClient["metrics"]["constants"]} constants
|
||||
* @param {number} num
|
||||
* @returns {AnyMetricPattern}
|
||||
@@ -14,8 +14,8 @@ import { line } from "./series.js";
|
||||
export function getConstant(constants, num) {
|
||||
const key =
|
||||
num >= 0
|
||||
? `constant${String(num).replace(".", "")}`
|
||||
: `constantMinus${Math.abs(num)}`;
|
||||
? `_${String(num).replace(".", "")}`
|
||||
: `minus${Math.abs(num)}`;
|
||||
const constant = /** @type {AnyMetricPattern | undefined} */ (
|
||||
/** @type {Record<string, AnyMetricPattern>} */ (constants)[key]
|
||||
);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,9 +5,9 @@
|
||||
* - Summary: Key stats (avg + median active, quartiles/extremes available)
|
||||
* - By Coin: BTC-weighted percentiles (IQR active: p25, p50, p75)
|
||||
* - By Capital: USD-weighted percentiles (IQR active: p25, p50, p75)
|
||||
* - Price Position: Spot percentile (both perspectives active)
|
||||
* - Supply Density: Cost basis supply density percentage
|
||||
*
|
||||
* For cohorts WITHOUT percentiles: Summary only
|
||||
* Only for cohorts WITH costBasis (All, STH, LTH)
|
||||
*/
|
||||
|
||||
import { colors } from "../../utils/colors.js";
|
||||
@@ -38,37 +38,14 @@ function createCorePercentileSeries(p, n = (x) => x) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {UtxoCohortObject | CohortWithoutRelative} cohort
|
||||
* @param {CohortAll | CohortFull | CohortLongTerm} cohort
|
||||
* @returns {FetchedPriceSeriesBlueprint[]}
|
||||
*/
|
||||
function createSingleSummarySeriesBasic(cohort) {
|
||||
const { color, tree } = cohort;
|
||||
return [
|
||||
price({ metric: tree.realized.realizedPrice, name: "Average", color }),
|
||||
price({
|
||||
metric: tree.costBasis.max,
|
||||
name: "Max",
|
||||
color: colors.stat.max,
|
||||
defaultActive: false,
|
||||
}),
|
||||
price({
|
||||
metric: tree.costBasis.min,
|
||||
name: "Min",
|
||||
color: colors.stat.min,
|
||||
defaultActive: false,
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {CohortAll | CohortFull | CohortWithPercentiles} cohort
|
||||
* @returns {FetchedPriceSeriesBlueprint[]}
|
||||
*/
|
||||
function createSingleSummarySeriesWithPercentiles(cohort) {
|
||||
function createSingleSummarySeries(cohort) {
|
||||
const { color, tree } = cohort;
|
||||
const p = tree.costBasis.percentiles;
|
||||
return [
|
||||
price({ metric: tree.realized.realizedPrice, name: "Average", color }),
|
||||
price({ metric: tree.realized.price, name: "Average", color }),
|
||||
price({
|
||||
metric: tree.costBasis.max,
|
||||
name: "Max (p100)",
|
||||
@@ -98,25 +75,25 @@ function createSingleSummarySeriesWithPercentiles(cohort) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {readonly CohortObject[]} list
|
||||
* @param {readonly (CohortAll | CohortFull | CohortLongTerm)[]} list
|
||||
* @param {CohortAll} all
|
||||
* @returns {FetchedPriceSeriesBlueprint[]}
|
||||
*/
|
||||
function createGroupedSummarySeries(list, all) {
|
||||
return mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
price({ metric: tree.realized.realizedPrice, name, color }),
|
||||
price({ metric: tree.realized.price, name, color }),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {CohortAll | CohortFull | CohortWithPercentiles} cohort
|
||||
* @param {CohortAll | CohortFull | CohortLongTerm} cohort
|
||||
* @returns {FetchedPriceSeriesBlueprint[]}
|
||||
*/
|
||||
function createSingleByCoinSeries(cohort) {
|
||||
const { color, tree } = cohort;
|
||||
const cb = tree.costBasis;
|
||||
return [
|
||||
price({ metric: tree.realized.realizedPrice, name: "Average", color }),
|
||||
price({ metric: tree.realized.price, name: "Average", color }),
|
||||
price({
|
||||
metric: cb.max,
|
||||
name: "p100",
|
||||
@@ -134,59 +111,36 @@ function createSingleByCoinSeries(cohort) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {CohortAll | CohortFull | CohortWithPercentiles} cohort
|
||||
* @param {CohortAll | CohortFull | CohortLongTerm} cohort
|
||||
* @returns {FetchedPriceSeriesBlueprint[]}
|
||||
*/
|
||||
function createSingleByCapitalSeries(cohort) {
|
||||
const { color, tree } = cohort;
|
||||
return [
|
||||
price({ metric: tree.realized.investorPrice, name: "Average", color }),
|
||||
price({ metric: tree.realized.investor.price, name: "Average", color }),
|
||||
...createCorePercentileSeries(tree.costBasis.investedCapital),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {CohortAll | CohortFull | CohortWithPercentiles} cohort
|
||||
* @param {CohortAll | CohortFull | CohortLongTerm} cohort
|
||||
* @returns {AnyFetchedSeriesBlueprint[]}
|
||||
*/
|
||||
function createSinglePricePositionSeries(cohort) {
|
||||
function createSingleSupplyDensitySeries(cohort) {
|
||||
const { tree } = cohort;
|
||||
return [
|
||||
line({
|
||||
metric: tree.costBasis.spotCostBasisPercentile,
|
||||
name: "By Coin",
|
||||
metric: tree.costBasis.supplyDensity.percent,
|
||||
name: "Supply Density",
|
||||
color: colors.bitcoin,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
line({
|
||||
metric: tree.costBasis.spotInvestedCapitalPercentile,
|
||||
name: "By Capital",
|
||||
color: colors.usd,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
...priceLines({ numbers: [100, 50, 0], unit: Unit.percentage }),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{ cohort: UtxoCohortObject | CohortWithoutRelative, title: (metric: string) => string }} args
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
export function createCostBasisSection({ cohort, title }) {
|
||||
return {
|
||||
name: "Cost Basis",
|
||||
tree: [
|
||||
{
|
||||
name: "Summary",
|
||||
title: title("Cost Basis Summary"),
|
||||
top: createSingleSummarySeriesBasic(cohort),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{ cohort: CohortAll | CohortFull | CohortWithPercentiles, title: (metric: string) => string }} args
|
||||
* @param {{ cohort: CohortAll | CohortFull | CohortLongTerm, title: (metric: string) => string }} args
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
export function createCostBasisSectionWithPercentiles({ cohort, title }) {
|
||||
@@ -196,7 +150,7 @@ export function createCostBasisSectionWithPercentiles({ cohort, title }) {
|
||||
{
|
||||
name: "Summary",
|
||||
title: title("Cost Basis Summary"),
|
||||
top: createSingleSummarySeriesWithPercentiles(cohort),
|
||||
top: createSingleSummarySeries(cohort),
|
||||
},
|
||||
{
|
||||
name: "By Coin",
|
||||
@@ -209,33 +163,16 @@ export function createCostBasisSectionWithPercentiles({ cohort, title }) {
|
||||
top: createSingleByCapitalSeries(cohort),
|
||||
},
|
||||
{
|
||||
name: "Price Position",
|
||||
title: title("Current Price Position"),
|
||||
bottom: createSinglePricePositionSeries(cohort),
|
||||
name: "Supply Density",
|
||||
title: title("Cost Basis Supply Density"),
|
||||
bottom: createSingleSupplyDensitySeries(cohort),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{ list: readonly (UtxoCohortObject | CohortWithoutRelative)[], all: CohortAll, title: (metric: string) => string }} args
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
export function createGroupedCostBasisSection({ list, all, title }) {
|
||||
return {
|
||||
name: "Cost Basis",
|
||||
tree: [
|
||||
{
|
||||
name: "Summary",
|
||||
title: title("Cost Basis Summary"),
|
||||
top: createGroupedSummarySeries(list, all),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{ list: readonly (CohortAll | CohortFull | CohortWithPercentiles)[], all: CohortAll, title: (metric: string) => string }} args
|
||||
* @param {{ list: readonly (CohortAll | CohortFull | CohortLongTerm)[], all: CohortAll, title: (metric: string) => string }} args
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
export function createGroupedCostBasisSectionWithPercentiles({
|
||||
@@ -258,7 +195,7 @@ export function createGroupedCostBasisSectionWithPercentiles({
|
||||
name: "Average",
|
||||
title: title("Realized Price Comparison"),
|
||||
top: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
price({ metric: tree.realized.realizedPrice, name, color }),
|
||||
price({ metric: tree.realized.price, name, color }),
|
||||
),
|
||||
},
|
||||
{
|
||||
@@ -291,7 +228,7 @@ export function createGroupedCostBasisSectionWithPercentiles({
|
||||
name: "Average",
|
||||
title: title("Investor Price Comparison"),
|
||||
top: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
price({ metric: tree.realized.investorPrice, name, color }),
|
||||
price({ metric: tree.realized.investor.price, name, color }),
|
||||
),
|
||||
},
|
||||
{
|
||||
@@ -330,39 +267,16 @@ export function createGroupedCostBasisSectionWithPercentiles({
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Price Position",
|
||||
tree: [
|
||||
{
|
||||
name: "By Coin",
|
||||
title: title("Price Position (BTC-weighted)"),
|
||||
bottom: [
|
||||
...mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
line({
|
||||
metric: tree.costBasis.spotCostBasisPercentile,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
),
|
||||
...priceLines({ numbers: [100, 50, 0], unit: Unit.percentage }),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "By Capital",
|
||||
title: title("Price Position (USD-weighted)"),
|
||||
bottom: [
|
||||
...mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
line({
|
||||
metric: tree.costBasis.spotInvestedCapitalPercentile,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
),
|
||||
...priceLines({ numbers: [100, 50, 0], unit: Unit.percentage }),
|
||||
],
|
||||
},
|
||||
],
|
||||
name: "Supply Density",
|
||||
title: title("Cost Basis Supply Density"),
|
||||
bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
line({
|
||||
metric: tree.costBasis.supplyDensity.percent,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@@ -16,7 +16,7 @@ const ADDRESSABLE_TYPES = [
|
||||
|
||||
/** @type {(key: SpendableType) => key is AddressableType} */
|
||||
const isAddressable = (key) =>
|
||||
ADDRESSABLE_TYPES.includes(/** @type {any} */ (key));
|
||||
/** @type {readonly string[]} */ (ADDRESSABLE_TYPES).includes(key);
|
||||
|
||||
export function buildCohortData() {
|
||||
const utxoCohorts = brk.metrics.cohorts.utxo;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -38,9 +38,7 @@ import {
|
||||
createGroupedPricesSection,
|
||||
} from "./prices.js";
|
||||
import {
|
||||
createCostBasisSection,
|
||||
createCostBasisSectionWithPercentiles,
|
||||
createGroupedCostBasisSection,
|
||||
createGroupedCostBasisSectionWithPercentiles,
|
||||
} from "./cost-basis.js";
|
||||
import {
|
||||
@@ -60,8 +58,12 @@ import {
|
||||
import {
|
||||
createActivitySection,
|
||||
createActivitySectionWithAdjusted,
|
||||
createActivitySectionWithActivity,
|
||||
createActivitySectionMinimal,
|
||||
createGroupedActivitySection,
|
||||
createGroupedActivitySectionWithAdjusted,
|
||||
createGroupedActivitySectionWithActivity,
|
||||
createGroupedActivitySectionMinimal,
|
||||
} from "./activity.js";
|
||||
|
||||
// Re-export data builder
|
||||
@@ -121,12 +123,11 @@ export function createCohortFolderWithAdjusted(cohort) {
|
||||
return {
|
||||
name: cohort.name || "all",
|
||||
tree: [
|
||||
createHoldingsSectionWithRelative({ cohort, title }),
|
||||
createHoldingsSectionWithOwnSupply({ cohort, title }),
|
||||
createValuationSection({ cohort, title }),
|
||||
createPricesSectionBasic({ cohort, title }),
|
||||
createCostBasisSection({ cohort, title }),
|
||||
createProfitabilitySectionWithNupl({ cohort, title }),
|
||||
createActivitySectionWithAdjusted({ cohort, title }),
|
||||
createActivitySectionWithActivity({ cohort, title }),
|
||||
],
|
||||
};
|
||||
}
|
||||
@@ -182,11 +183,10 @@ export function createCohortFolderAgeRange(cohort) {
|
||||
name: cohort.name || "all",
|
||||
tree: [
|
||||
createHoldingsSectionWithOwnSupply({ cohort, title }),
|
||||
createValuationSectionFull({ cohort, title }),
|
||||
createPricesSectionFull({ cohort, title }),
|
||||
createCostBasisSectionWithPercentiles({ cohort, title }),
|
||||
createValuationSection({ cohort, title }),
|
||||
createPricesSectionBasic({ cohort, title }),
|
||||
createProfitabilitySectionWithInvestedCapitalPct({ cohort, title }),
|
||||
createActivitySection({ cohort, title }),
|
||||
createActivitySectionWithActivity({ cohort, title }),
|
||||
],
|
||||
};
|
||||
}
|
||||
@@ -201,12 +201,11 @@ export function createCohortFolderBasicWithMarketCap(cohort) {
|
||||
return {
|
||||
name: cohort.name || "all",
|
||||
tree: [
|
||||
createHoldingsSectionWithRelative({ cohort, title }),
|
||||
createHoldingsSection({ cohort, title }),
|
||||
createValuationSection({ cohort, title }),
|
||||
createPricesSectionBasic({ cohort, title }),
|
||||
createCostBasisSection({ cohort, title }),
|
||||
createProfitabilitySectionWithNupl({ cohort, title }),
|
||||
createActivitySection({ cohort, title }),
|
||||
createActivitySectionMinimal({ cohort, title }),
|
||||
],
|
||||
};
|
||||
}
|
||||
@@ -221,12 +220,11 @@ export function createCohortFolderBasicWithoutMarketCap(cohort) {
|
||||
return {
|
||||
name: cohort.name || "all",
|
||||
tree: [
|
||||
createHoldingsSectionWithOwnSupply({ cohort, title }),
|
||||
createHoldingsSection({ cohort, title }),
|
||||
createValuationSection({ cohort, title }),
|
||||
createPricesSectionBasic({ cohort, title }),
|
||||
createCostBasisSection({ cohort, title }),
|
||||
createProfitabilitySectionBasicWithInvestedCapitalPct({ cohort, title }),
|
||||
createActivitySection({ cohort, title }),
|
||||
createActivitySectionMinimal({ cohort, title }),
|
||||
],
|
||||
};
|
||||
}
|
||||
@@ -244,9 +242,8 @@ export function createCohortFolderAddress(cohort) {
|
||||
createHoldingsSectionAddress({ cohort, title }),
|
||||
createValuationSection({ cohort, title }),
|
||||
createPricesSectionBasic({ cohort, title }),
|
||||
createCostBasisSection({ cohort, title }),
|
||||
createProfitabilitySectionBasicWithInvestedCapitalPct({ cohort, title }),
|
||||
createActivitySection({ cohort, title }),
|
||||
createActivitySectionMinimal({ cohort, title }),
|
||||
],
|
||||
};
|
||||
}
|
||||
@@ -264,9 +261,8 @@ export function createCohortFolderWithoutRelative(cohort) {
|
||||
createHoldingsSection({ cohort, title }),
|
||||
createValuationSection({ cohort, title }),
|
||||
createPricesSectionBasic({ cohort, title }),
|
||||
createCostBasisSection({ cohort, title }),
|
||||
createProfitabilitySection({ cohort, title }),
|
||||
createActivitySection({ cohort, title }),
|
||||
createActivitySectionMinimal({ cohort, title }),
|
||||
],
|
||||
};
|
||||
}
|
||||
@@ -284,9 +280,8 @@ export function createAddressCohortFolder(cohort) {
|
||||
createHoldingsSectionAddressAmount({ cohort, title }),
|
||||
createValuationSection({ cohort, title }),
|
||||
createPricesSectionBasic({ cohort, title }),
|
||||
createCostBasisSection({ cohort, title }),
|
||||
createProfitabilitySectionWithNupl({ cohort, title }),
|
||||
createActivitySection({ cohort, title }),
|
||||
createActivitySectionMinimal({ cohort, title }),
|
||||
],
|
||||
};
|
||||
}
|
||||
@@ -333,12 +328,11 @@ export function createGroupedCohortFolderWithAdjusted({
|
||||
return {
|
||||
name: name || "all",
|
||||
tree: [
|
||||
createGroupedHoldingsSectionWithRelative({ list, all, title }),
|
||||
createGroupedHoldingsSectionWithOwnSupply({ list, all, title }),
|
||||
createGroupedValuationSection({ list, all, title }),
|
||||
createGroupedPricesSection({ list, all, title }),
|
||||
createGroupedCostBasisSection({ list, all, title }),
|
||||
createGroupedProfitabilitySectionWithNupl({ list, all, title }),
|
||||
createGroupedActivitySectionWithAdjusted({ list, all, title }),
|
||||
createGroupedProfitabilitySectionWithInvestedCapitalPct({ list, all, title }),
|
||||
createGroupedActivitySectionWithActivity({ list, all, title }),
|
||||
],
|
||||
};
|
||||
}
|
||||
@@ -406,15 +400,14 @@ export function createGroupedCohortFolderAgeRange({
|
||||
name: name || "all",
|
||||
tree: [
|
||||
createGroupedHoldingsSectionWithOwnSupply({ list, all, title }),
|
||||
createGroupedValuationSectionWithOwnMarketCap({ list, all, title }),
|
||||
createGroupedValuationSection({ list, all, title }),
|
||||
createGroupedPricesSection({ list, all, title }),
|
||||
createGroupedCostBasisSectionWithPercentiles({ list, all, title }),
|
||||
createGroupedProfitabilitySectionWithInvestedCapitalPct({
|
||||
list,
|
||||
all,
|
||||
title,
|
||||
}),
|
||||
createGroupedActivitySection({ list, all, title }),
|
||||
createGroupedActivitySectionWithActivity({ list, all, title }),
|
||||
],
|
||||
};
|
||||
}
|
||||
@@ -433,12 +426,11 @@ export function createGroupedCohortFolderBasicWithMarketCap({
|
||||
return {
|
||||
name: name || "all",
|
||||
tree: [
|
||||
createGroupedHoldingsSectionWithRelative({ list, all, title }),
|
||||
createGroupedHoldingsSection({ list, all, title }),
|
||||
createGroupedValuationSection({ list, all, title }),
|
||||
createGroupedPricesSection({ list, all, title }),
|
||||
createGroupedCostBasisSection({ list, all, title }),
|
||||
createGroupedProfitabilitySectionWithNupl({ list, all, title }),
|
||||
createGroupedActivitySection({ list, all, title }),
|
||||
createGroupedProfitabilitySection({ list, all, title }),
|
||||
createGroupedActivitySectionMinimal({ list, all, title }),
|
||||
],
|
||||
};
|
||||
}
|
||||
@@ -457,16 +449,15 @@ export function createGroupedCohortFolderBasicWithoutMarketCap({
|
||||
return {
|
||||
name: name || "all",
|
||||
tree: [
|
||||
createGroupedHoldingsSectionWithOwnSupply({ list, all, title }),
|
||||
createGroupedHoldingsSection({ list, all, title }),
|
||||
createGroupedValuationSection({ list, all, title }),
|
||||
createGroupedPricesSection({ list, all, title }),
|
||||
createGroupedCostBasisSection({ list, all, title }),
|
||||
createGroupedProfitabilitySectionBasicWithInvestedCapitalPct({
|
||||
list,
|
||||
all,
|
||||
title,
|
||||
}),
|
||||
createGroupedActivitySection({ list, all, title }),
|
||||
createGroupedActivitySectionMinimal({ list, all, title }),
|
||||
],
|
||||
};
|
||||
}
|
||||
@@ -488,13 +479,12 @@ export function createGroupedCohortFolderAddress({
|
||||
createGroupedHoldingsSectionAddress({ list, all, title }),
|
||||
createGroupedValuationSection({ list, all, title }),
|
||||
createGroupedPricesSection({ list, all, title }),
|
||||
createGroupedCostBasisSection({ list, all, title }),
|
||||
createGroupedProfitabilitySectionBasicWithInvestedCapitalPct({
|
||||
list,
|
||||
all,
|
||||
title,
|
||||
}),
|
||||
createGroupedActivitySection({ list, all, title }),
|
||||
createGroupedActivitySectionMinimal({ list, all, title }),
|
||||
],
|
||||
};
|
||||
}
|
||||
@@ -516,9 +506,8 @@ export function createGroupedCohortFolderWithoutRelative({
|
||||
createGroupedHoldingsSection({ list, all, title }),
|
||||
createGroupedValuationSection({ list, all, title }),
|
||||
createGroupedPricesSection({ list, all, title }),
|
||||
createGroupedCostBasisSection({ list, all, title }),
|
||||
createGroupedProfitabilitySection({ list, all, title }),
|
||||
createGroupedActivitySection({ list, all, title }),
|
||||
createGroupedActivitySectionMinimal({ list, all, title }),
|
||||
],
|
||||
};
|
||||
}
|
||||
@@ -540,9 +529,8 @@ export function createGroupedAddressCohortFolder({
|
||||
createGroupedHoldingsSectionAddressAmount({ list, all, title }),
|
||||
createGroupedValuationSection({ list, all, title }),
|
||||
createGroupedPricesSection({ list, all, title }),
|
||||
createGroupedCostBasisSection({ list, all, title }),
|
||||
createGroupedProfitabilitySectionWithNupl({ list, all, title }),
|
||||
createGroupedActivitySection({ list, all, title }),
|
||||
createGroupedProfitabilitySection({ list, all, title }),
|
||||
createGroupedActivitySectionMinimal({ list, all, title }),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -19,47 +19,9 @@ import { baseline, price } from "../series.js";
|
||||
import { Unit } from "../../utils/units.js";
|
||||
|
||||
/**
|
||||
* @param {{ realized: { realizedPrice: ActivePricePattern, investorPrice: ActivePricePattern, lowerPriceBand: ActivePricePattern, upperPriceBand: ActivePricePattern } }} tree
|
||||
* @param {(metric: string) => string} title
|
||||
* @returns {PartialChartOption}
|
||||
*/
|
||||
function createCompareChart(tree, title) {
|
||||
return {
|
||||
name: "Compare",
|
||||
title: title("Prices"),
|
||||
top: [
|
||||
price({
|
||||
metric: tree.realized.realizedPrice,
|
||||
name: "Realized",
|
||||
color: colors.realized,
|
||||
}),
|
||||
price({
|
||||
metric: tree.realized.investorPrice,
|
||||
name: "Investor",
|
||||
color: colors.investor,
|
||||
}),
|
||||
price({
|
||||
metric: tree.realized.upperPriceBand,
|
||||
name: "I²/R",
|
||||
color: colors.stat.max,
|
||||
style: 2,
|
||||
defaultActive: false,
|
||||
}),
|
||||
price({
|
||||
metric: tree.realized.lowerPriceBand,
|
||||
name: "R²/I",
|
||||
color: colors.stat.min,
|
||||
style: 2,
|
||||
defaultActive: false,
|
||||
}),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create prices section for cohorts with full ActivePriceRatioPattern
|
||||
* (CohortAll, CohortFull, CohortWithPercentiles)
|
||||
* @param {{ cohort: CohortAll | CohortFull | CohortWithPercentiles, title: (metric: string) => string }} args
|
||||
* Create prices section for cohorts with full ratio patterns
|
||||
* (CohortAll, CohortFull, CohortLongTerm)
|
||||
* @param {{ cohort: CohortAll | CohortFull | CohortLongTerm, title: (metric: string) => string }} args
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
export function createPricesSectionFull({ cohort, title }) {
|
||||
@@ -67,14 +29,23 @@ export function createPricesSectionFull({ cohort, title }) {
|
||||
return {
|
||||
name: "Prices",
|
||||
tree: [
|
||||
createCompareChart(tree, title),
|
||||
{
|
||||
name: "Compare",
|
||||
title: title("Prices"),
|
||||
top: [
|
||||
price({ metric: tree.realized.price, name: "Realized", color: colors.realized }),
|
||||
price({ metric: tree.realized.investor.price, name: "Investor", color: colors.investor }),
|
||||
price({ metric: tree.realized.investor.upperPriceBand, name: "I²/R", color: colors.stat.max, style: 2, defaultActive: false }),
|
||||
price({ metric: tree.realized.investor.lowerPriceBand, name: "R²/I", color: colors.stat.min, style: 2, defaultActive: false }),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Realized",
|
||||
tree: createPriceRatioCharts({
|
||||
context: cohort.name,
|
||||
legend: "Realized",
|
||||
pricePattern: tree.realized.realizedPrice,
|
||||
ratio: { ...tree.realized.realizedPriceExtra, ...tree.realized.realizedPriceRatioExt },
|
||||
pricePattern: tree.realized.price,
|
||||
ratio: tree.realized.price,
|
||||
color,
|
||||
priceTitle: title("Realized Price"),
|
||||
titlePrefix: "Realized Price",
|
||||
@@ -82,52 +53,19 @@ export function createPricesSectionFull({ cohort, title }) {
|
||||
},
|
||||
{
|
||||
name: "Investor",
|
||||
tree: createPriceRatioCharts({
|
||||
context: cohort.name,
|
||||
legend: "Investor",
|
||||
pricePattern: tree.realized.investorPrice,
|
||||
ratio: { ...tree.realized.investorPriceExtra, ...tree.realized.investorPriceRatioExt },
|
||||
color,
|
||||
priceTitle: title("Investor Price"),
|
||||
titlePrefix: "Investor Price",
|
||||
}),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create prices section for cohorts with basic ratio patterns only
|
||||
* (CohortWithAdjusted, CohortBasic, CohortAddress, CohortWithoutRelative)
|
||||
* @param {{ cohort: CohortWithAdjusted | CohortBasic | CohortAddress | CohortWithoutRelative, title: (metric: string) => string }} args
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
export function createPricesSectionBasic({ cohort, title }) {
|
||||
const { tree, color } = cohort;
|
||||
return {
|
||||
name: "Prices",
|
||||
tree: [
|
||||
createCompareChart(tree, title),
|
||||
{
|
||||
name: "Realized",
|
||||
tree: [
|
||||
{
|
||||
name: "Price",
|
||||
title: title("Realized Price"),
|
||||
top: [
|
||||
price({
|
||||
metric: tree.realized.realizedPrice,
|
||||
name: "Realized",
|
||||
color,
|
||||
}),
|
||||
],
|
||||
title: title("Investor Price"),
|
||||
top: [price({ metric: tree.realized.investor.price, name: "Investor", color })],
|
||||
},
|
||||
{
|
||||
name: "Ratio",
|
||||
title: title("Realized Price Ratio"),
|
||||
title: title("Investor Price Ratio"),
|
||||
top: [price({ metric: tree.realized.investor.price, name: "Investor", color })],
|
||||
bottom: [
|
||||
baseline({
|
||||
metric: tree.realized.realizedPriceExtra.ratio,
|
||||
metric: tree.realized.investor.price.ratio,
|
||||
name: "Ratio",
|
||||
unit: Unit.ratio,
|
||||
base: 1,
|
||||
@@ -136,27 +74,36 @@ export function createPricesSectionBasic({ cohort, title }) {
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create prices section for cohorts with basic ratio patterns only
|
||||
* (CohortWithAdjusted, CohortBasic, CohortAddress, CohortWithoutRelative)
|
||||
* @param {{ cohort: CohortWithAdjusted | CohortBasic | CohortAddress | CohortWithoutRelative | CohortAgeRange, title: (metric: string) => string }} args
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
export function createPricesSectionBasic({ cohort, title }) {
|
||||
const { tree, color } = cohort;
|
||||
return {
|
||||
name: "Prices",
|
||||
tree: [
|
||||
{
|
||||
name: "Investor",
|
||||
name: "Realized",
|
||||
tree: [
|
||||
{
|
||||
name: "Price",
|
||||
title: title("Investor Price"),
|
||||
top: [
|
||||
price({
|
||||
metric: tree.realized.investorPrice,
|
||||
name: "Investor",
|
||||
color,
|
||||
}),
|
||||
],
|
||||
title: title("Realized Price"),
|
||||
top: [price({ metric: tree.realized.price, name: "Realized", color })],
|
||||
},
|
||||
{
|
||||
name: "Ratio",
|
||||
title: title("Investor Price Ratio"),
|
||||
title: title("Realized Price Ratio"),
|
||||
bottom: [
|
||||
baseline({
|
||||
metric: tree.realized.investorPriceExtra.ratio,
|
||||
name: "Ratio",
|
||||
metric: tree.realized.mvrv,
|
||||
name: "MVRV",
|
||||
unit: Unit.ratio,
|
||||
base: 1,
|
||||
}),
|
||||
@@ -184,40 +131,15 @@ export function createGroupedPricesSection({ list, all, title }) {
|
||||
name: "Price",
|
||||
title: title("Realized Price"),
|
||||
top: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
price({ metric: tree.realized.realizedPrice, name, color }),
|
||||
price({ metric: tree.realized.price, name, color }),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "Ratio",
|
||||
title: title("Realized Price Ratio"),
|
||||
title: title("MVRV"),
|
||||
bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
baseline({
|
||||
metric: tree.realized.realizedPriceExtra.ratio,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.ratio,
|
||||
base: 1,
|
||||
}),
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Investor",
|
||||
tree: [
|
||||
{
|
||||
name: "Price",
|
||||
title: title("Investor Price"),
|
||||
top: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
price({ metric: tree.realized.investorPrice, name, color }),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "Ratio",
|
||||
title: title("Investor Price Ratio"),
|
||||
bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
baseline({
|
||||
metric: tree.realized.investorPriceExtra.ratio,
|
||||
metric: tree.realized.mvrv,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.ratio,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -22,7 +22,7 @@ function createSingleRealizedCapSeries(cohort) {
|
||||
const { color, tree } = cohort;
|
||||
return [
|
||||
line({
|
||||
metric: tree.realized.realizedCap,
|
||||
metric: tree.realized.cap.usd,
|
||||
name: "Realized Cap",
|
||||
color,
|
||||
unit: Unit.usd,
|
||||
@@ -37,7 +37,7 @@ function createSingleRealizedCapSeries(cohort) {
|
||||
function createSingle30dChangeSeries(cohort) {
|
||||
return [
|
||||
baseline({
|
||||
metric: cohort.tree.realized.realizedCap30dDelta,
|
||||
metric: cohort.tree.realized.cap.delta.change._1m.usd,
|
||||
name: "30d Change",
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
@@ -47,7 +47,7 @@ function createSingle30dChangeSeries(cohort) {
|
||||
/**
|
||||
* Create valuation section for cohorts with full ratio patterns
|
||||
* (CohortAll, CohortFull, CohortWithPercentiles)
|
||||
* @param {{ cohort: CohortAll | CohortFull | CohortWithPercentiles, title: (metric: string) => string }} args
|
||||
* @param {{ cohort: CohortAll | CohortFull | CohortLongTerm, title: (metric: string) => string }} args
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
export function createValuationSectionFull({ cohort, title }) {
|
||||
@@ -61,7 +61,7 @@ export function createValuationSectionFull({ cohort, title }) {
|
||||
bottom: [
|
||||
...createSingleRealizedCapSeries(cohort),
|
||||
baseline({
|
||||
metric: tree.realized.realizedCapRelToOwnMarketCap,
|
||||
metric: tree.realized.cap.relToOwnMcap.percent,
|
||||
name: "Rel. to Own M.Cap",
|
||||
color,
|
||||
unit: Unit.pctOwnMcap,
|
||||
@@ -75,8 +75,8 @@ export function createValuationSectionFull({ cohort, title }) {
|
||||
},
|
||||
createRatioChart({
|
||||
title,
|
||||
pricePattern: tree.realized.realizedPrice,
|
||||
ratio: { ...tree.realized.realizedPriceExtra, ...tree.realized.realizedPriceRatioExt },
|
||||
pricePattern: tree.realized.price,
|
||||
ratio: tree.realized.price,
|
||||
color,
|
||||
name: "MVRV",
|
||||
}),
|
||||
@@ -110,7 +110,7 @@ export function createValuationSection({ cohort, title }) {
|
||||
title: title("MVRV"),
|
||||
bottom: [
|
||||
baseline({
|
||||
metric: tree.realized.realizedPriceExtra.ratio,
|
||||
metric: tree.realized.mvrv,
|
||||
name: "MVRV",
|
||||
unit: Unit.ratio,
|
||||
base: 1,
|
||||
@@ -134,7 +134,7 @@ export function createGroupedValuationSection({ list, all, title }) {
|
||||
title: title("Realized Cap"),
|
||||
bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
line({
|
||||
metric: tree.realized.realizedCap,
|
||||
metric: tree.realized.cap.usd,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.usd,
|
||||
@@ -146,7 +146,7 @@ export function createGroupedValuationSection({ list, all, title }) {
|
||||
title: title("Realized Cap 30d Change"),
|
||||
bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
baseline({
|
||||
metric: tree.realized.realizedCap30dDelta,
|
||||
metric: tree.realized.cap.delta.change._1m.usd,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.usd,
|
||||
@@ -158,7 +158,7 @@ export function createGroupedValuationSection({ list, all, title }) {
|
||||
title: title("MVRV"),
|
||||
bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
baseline({
|
||||
metric: tree.realized.realizedPriceExtra.ratio,
|
||||
metric: tree.realized.mvrv,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.ratio,
|
||||
@@ -171,7 +171,7 @@ export function createGroupedValuationSection({ list, all, title }) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{ list: readonly (CohortAll | CohortFull | CohortWithPercentiles)[], all: CohortAll, title: (metric: string) => string }} args
|
||||
* @param {{ list: readonly (CohortAll | CohortFull | CohortLongTerm)[], all: CohortAll, title: (metric: string) => string }} args
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
export function createGroupedValuationSectionWithOwnMarketCap({
|
||||
@@ -188,7 +188,7 @@ export function createGroupedValuationSectionWithOwnMarketCap({
|
||||
bottom: [
|
||||
...mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
line({
|
||||
metric: tree.realized.realizedCap,
|
||||
metric: tree.realized.cap.usd,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.usd,
|
||||
@@ -196,7 +196,7 @@ export function createGroupedValuationSectionWithOwnMarketCap({
|
||||
),
|
||||
...mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
baseline({
|
||||
metric: tree.realized.realizedCapRelToOwnMarketCap,
|
||||
metric: tree.realized.cap.relToOwnMcap.percent,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.pctOwnMcap,
|
||||
@@ -209,7 +209,7 @@ export function createGroupedValuationSectionWithOwnMarketCap({
|
||||
title: title("Realized Cap 30d Change"),
|
||||
bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
baseline({
|
||||
metric: tree.realized.realizedCap30dDelta,
|
||||
metric: tree.realized.cap.delta.change._1m.usd,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.usd,
|
||||
@@ -221,7 +221,7 @@ export function createGroupedValuationSectionWithOwnMarketCap({
|
||||
title: title("MVRV"),
|
||||
bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
baseline({
|
||||
metric: tree.realized.realizedPriceExtra.ratio,
|
||||
metric: tree.realized.mvrv,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.ratio,
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { colors } from "../utils/colors.js";
|
||||
import { brk } from "../client.js";
|
||||
import { Unit } from "../utils/units.js";
|
||||
import { line, baseline, price, dotted } from "./series.js";
|
||||
import { baseline, price } from "./series.js";
|
||||
import { satsBtcUsd } from "./shared.js";
|
||||
import { periodIdToName } from "./utils.js";
|
||||
|
||||
@@ -42,7 +42,7 @@ const YEARS_2020S = /** @type {const} */ ([
|
||||
const YEARS_2010S = /** @type {const} */ ([2019, 2018, 2017, 2016, 2015]);
|
||||
|
||||
/** @typedef {typeof YEARS_2020S[number] | typeof YEARS_2010S[number]} DcaYear */
|
||||
/** @typedef {`_${DcaYear}`} DcaYearKey */
|
||||
/** @typedef {`from${DcaYear}`} DcaYearKey */
|
||||
|
||||
/** @param {AllPeriodKey} key */
|
||||
const periodName = (key) => periodIdToName(key.slice(1), true);
|
||||
@@ -54,10 +54,6 @@ const periodName = (key) => periodIdToName(key.slice(1), true);
|
||||
* @property {Color} color - Item color
|
||||
* @property {AnyPricePattern} costBasis - Cost basis metric
|
||||
* @property {AnyMetricPattern} returns - Returns metric
|
||||
* @property {AnyMetricPattern} minReturn - Min return metric
|
||||
* @property {AnyMetricPattern} maxReturn - Max return metric
|
||||
* @property {AnyMetricPattern} daysInProfit - Days in profit metric
|
||||
* @property {AnyMetricPattern} daysInLoss - Days in loss metric
|
||||
* @property {AnyValuePattern} stack - Stack pattern
|
||||
*/
|
||||
|
||||
@@ -76,17 +72,13 @@ const ALL_YEARS = /** @type {const} */ ([...YEARS_2020S, ...YEARS_2010S]);
|
||||
* @returns {BaseEntryItem}
|
||||
*/
|
||||
function buildYearEntry(dca, year, i) {
|
||||
const key = /** @type {DcaYearKey} */ (`_${year}`);
|
||||
const key = /** @type {DcaYearKey} */ (`from${year}`);
|
||||
return {
|
||||
name: `${year}`,
|
||||
color: colors.at(i, ALL_YEARS.length),
|
||||
costBasis: dca.classAveragePrice[key],
|
||||
returns: dca.classReturns[key],
|
||||
minReturn: dca.classMinReturn[key],
|
||||
maxReturn: dca.classMaxReturn[key],
|
||||
daysInProfit: dca.classDaysInProfit[key],
|
||||
daysInLoss: dca.classDaysInLoss[key],
|
||||
stack: dca.classStack[key],
|
||||
costBasis: dca.class.costBasis[key],
|
||||
returns: dca.class.return[key].ratio,
|
||||
stack: dca.class.stack[key],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -109,42 +101,10 @@ export function createInvestingSection() {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create profitability folder for compare charts
|
||||
* @param {string} context
|
||||
* @param {Pick<BaseEntryItem, 'name' | 'color' | 'costBasis' | 'daysInProfit' | 'daysInLoss'>[]} items
|
||||
*/
|
||||
function createProfitabilityFolder(context, items) {
|
||||
const top = items.map(({ name, color, costBasis }) =>
|
||||
price({ metric: costBasis, name, color }),
|
||||
);
|
||||
return {
|
||||
name: "Profitability",
|
||||
tree: [
|
||||
{
|
||||
name: "Days in Profit",
|
||||
title: `Days in Profit: ${context}`,
|
||||
top,
|
||||
bottom: items.map(({ name, color, daysInProfit }) =>
|
||||
line({ metric: daysInProfit, name, color, unit: Unit.days }),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "Days in Loss",
|
||||
title: `Days in Loss: ${context}`,
|
||||
top,
|
||||
bottom: items.map(({ name, color, daysInLoss }) =>
|
||||
line({ metric: daysInLoss, name, color, unit: Unit.days }),
|
||||
),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create compare folder from items
|
||||
* @param {string} context
|
||||
* @param {Pick<BaseEntryItem, 'name' | 'color' | 'costBasis' | 'returns' | 'daysInProfit' | 'daysInLoss' | 'stack'>[]} items
|
||||
* @param {Pick<BaseEntryItem, 'name' | 'color' | 'costBasis' | 'returns' | 'stack'>[]} items
|
||||
*/
|
||||
function createCompareFolder(context, items) {
|
||||
const topPane = items.map(({ name, color, costBasis }) =>
|
||||
@@ -171,7 +131,6 @@ function createCompareFolder(context, items) {
|
||||
}),
|
||||
),
|
||||
},
|
||||
createProfitabilityFolder(context, items),
|
||||
{
|
||||
name: "Accumulated",
|
||||
title: `Accumulated Value: ${context}`,
|
||||
@@ -190,15 +149,7 @@ function createCompareFolder(context, items) {
|
||||
* @param {object[]} returnsBottom - Bottom pane items for returns chart
|
||||
*/
|
||||
function createSingleEntryTree(item, returnsBottom) {
|
||||
const {
|
||||
name,
|
||||
titlePrefix = name,
|
||||
color,
|
||||
costBasis,
|
||||
daysInProfit,
|
||||
daysInLoss,
|
||||
stack,
|
||||
} = item;
|
||||
const { name, titlePrefix = name, color, costBasis, stack } = item;
|
||||
const top = [price({ metric: costBasis, name: "Cost Basis", color })];
|
||||
return {
|
||||
name,
|
||||
@@ -210,25 +161,6 @@ function createSingleEntryTree(item, returnsBottom) {
|
||||
top,
|
||||
bottom: returnsBottom,
|
||||
},
|
||||
{
|
||||
name: "Profitability",
|
||||
title: `Profitability: ${titlePrefix}`,
|
||||
top,
|
||||
bottom: [
|
||||
line({
|
||||
metric: daysInProfit,
|
||||
name: "Days in Profit",
|
||||
color: colors.profit,
|
||||
unit: Unit.days,
|
||||
}),
|
||||
line({
|
||||
metric: daysInLoss,
|
||||
name: "Days in Loss",
|
||||
color: colors.loss,
|
||||
unit: Unit.days,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Accumulated",
|
||||
title: `Accumulated Value: ${titlePrefix}`,
|
||||
@@ -244,23 +176,8 @@ function createSingleEntryTree(item, returnsBottom) {
|
||||
* @param {BaseEntryItem & { titlePrefix?: string }} item
|
||||
*/
|
||||
function createShortSingleEntry(item) {
|
||||
const { returns, minReturn, maxReturn } = item;
|
||||
return createSingleEntryTree(item, [
|
||||
baseline({ metric: returns, name: "Current", unit: Unit.percentage }),
|
||||
dotted({
|
||||
metric: maxReturn,
|
||||
name: "Max",
|
||||
color: colors.profit,
|
||||
unit: Unit.percentage,
|
||||
defaultActive: false,
|
||||
}),
|
||||
dotted({
|
||||
metric: minReturn,
|
||||
name: "Min",
|
||||
color: colors.loss,
|
||||
unit: Unit.percentage,
|
||||
defaultActive: false,
|
||||
}),
|
||||
baseline({ metric: item.returns, name: "Current", unit: Unit.percentage }),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -269,24 +186,9 @@ function createShortSingleEntry(item) {
|
||||
* @param {LongEntryItem & { titlePrefix?: string }} item
|
||||
*/
|
||||
function createLongSingleEntry(item) {
|
||||
const { returns, minReturn, maxReturn, cagr } = item;
|
||||
return createSingleEntryTree(item, [
|
||||
baseline({ metric: returns, name: "Current", unit: Unit.percentage }),
|
||||
baseline({ metric: cagr, name: "CAGR", unit: Unit.cagr }),
|
||||
dotted({
|
||||
metric: maxReturn,
|
||||
name: "Max",
|
||||
color: colors.profit,
|
||||
unit: Unit.percentage,
|
||||
defaultActive: false,
|
||||
}),
|
||||
dotted({
|
||||
metric: minReturn,
|
||||
name: "Min",
|
||||
color: colors.loss,
|
||||
unit: Unit.percentage,
|
||||
defaultActive: false,
|
||||
}),
|
||||
baseline({ metric: item.returns, name: "Current", unit: Unit.percentage }),
|
||||
baseline({ metric: item.cagr, name: "CAGR", unit: Unit.cagr }),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -301,7 +203,7 @@ export function createDcaVsLumpSumSection({ dca, lookback, returns }) {
|
||||
/** @param {AllPeriodKey} key */
|
||||
const topPane = (key) => [
|
||||
price({
|
||||
metric: dca.periodAveragePrice[key],
|
||||
metric: dca.period.costBasis[key],
|
||||
name: "DCA",
|
||||
color: colors.profit,
|
||||
}),
|
||||
@@ -315,151 +217,54 @@ export function createDcaVsLumpSumSection({ dca, lookback, returns }) {
|
||||
top: topPane(key),
|
||||
});
|
||||
|
||||
/** @param {string} name @param {AllPeriodKey} key */
|
||||
const returnsMinMax = (name, key) => [
|
||||
{
|
||||
name: "Max",
|
||||
title: `Max Return: ${name} DCA vs Lump Sum`,
|
||||
top: topPane(key),
|
||||
bottom: [
|
||||
baseline({
|
||||
metric: dca.periodMaxReturn[key],
|
||||
name: "DCA",
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
baseline({
|
||||
metric: dca.periodLumpSumMaxReturn[key],
|
||||
name: "Lump Sum",
|
||||
color: colors.bi.p2,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Min",
|
||||
title: `Min Return: ${name} DCA vs Lump Sum`,
|
||||
top: topPane(key),
|
||||
bottom: [
|
||||
baseline({
|
||||
metric: dca.periodMinReturn[key],
|
||||
name: "DCA",
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
baseline({
|
||||
metric: dca.periodLumpSumMinReturn[key],
|
||||
name: "Lump Sum",
|
||||
color: colors.bi.p2,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
/** @param {string} name @param {ShortPeriodKey} key */
|
||||
const shortReturnsFolder = (name, key) => ({
|
||||
const shortReturnsChart = (name, key) => ({
|
||||
name: "Returns",
|
||||
tree: [
|
||||
{
|
||||
name: "Current",
|
||||
title: `Returns: ${name} DCA vs Lump Sum`,
|
||||
top: topPane(key),
|
||||
bottom: [
|
||||
baseline({
|
||||
metric: dca.periodReturns[key],
|
||||
name: "DCA",
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
baseline({
|
||||
metric: dca.periodLumpSumReturns[key],
|
||||
name: "Lump Sum",
|
||||
color: colors.bi.p2,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
],
|
||||
},
|
||||
...returnsMinMax(name, key),
|
||||
title: `Returns: ${name} DCA vs Lump Sum`,
|
||||
top: topPane(key),
|
||||
bottom: [
|
||||
baseline({
|
||||
metric: dca.period.return[key].ratio,
|
||||
name: "DCA",
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
baseline({
|
||||
metric: dca.period.lumpSumReturn[key].ratio,
|
||||
name: "Lump Sum",
|
||||
color: colors.bi.p2,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
/** @param {string} name @param {LongPeriodKey} key */
|
||||
const longReturnsFolder = (name, key) => ({
|
||||
const longReturnsChart = (name, key) => ({
|
||||
name: "Returns",
|
||||
tree: [
|
||||
{
|
||||
name: "Current",
|
||||
title: `Returns: ${name} DCA vs Lump Sum`,
|
||||
top: topPane(key),
|
||||
bottom: [
|
||||
baseline({
|
||||
metric: dca.periodReturns[key],
|
||||
name: "DCA",
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
baseline({
|
||||
metric: dca.periodLumpSumReturns[key],
|
||||
name: "Lump Sum",
|
||||
color: colors.bi.p2,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
baseline({
|
||||
metric: dca.periodCagr[key],
|
||||
name: "DCA",
|
||||
unit: Unit.cagr,
|
||||
}),
|
||||
baseline({
|
||||
metric: returns.cagr[key],
|
||||
name: "Lump Sum",
|
||||
color: colors.bi.p2,
|
||||
unit: Unit.cagr,
|
||||
}),
|
||||
],
|
||||
},
|
||||
...returnsMinMax(name, key),
|
||||
],
|
||||
});
|
||||
|
||||
/** @param {string} name @param {AllPeriodKey} key */
|
||||
const profitabilityFolder = (name, key) => ({
|
||||
name: "Profitability",
|
||||
tree: [
|
||||
{
|
||||
name: "Days in Profit",
|
||||
title: `Days in Profit: ${name} DCA vs Lump Sum`,
|
||||
top: topPane(key),
|
||||
bottom: [
|
||||
line({
|
||||
metric: dca.periodDaysInProfit[key],
|
||||
name: "DCA",
|
||||
color: colors.profit,
|
||||
unit: Unit.days,
|
||||
}),
|
||||
line({
|
||||
metric: dca.periodLumpSumDaysInProfit[key],
|
||||
name: "Lump Sum",
|
||||
color: colors.bitcoin,
|
||||
unit: Unit.days,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Days in Loss",
|
||||
title: `Days in Loss: ${name} DCA vs Lump Sum`,
|
||||
top: topPane(key),
|
||||
bottom: [
|
||||
line({
|
||||
metric: dca.periodDaysInLoss[key],
|
||||
name: "DCA",
|
||||
color: colors.profit,
|
||||
unit: Unit.days,
|
||||
}),
|
||||
line({
|
||||
metric: dca.periodLumpSumDaysInLoss[key],
|
||||
name: "Lump Sum",
|
||||
color: colors.bitcoin,
|
||||
unit: Unit.days,
|
||||
}),
|
||||
],
|
||||
},
|
||||
title: `Returns: ${name} DCA vs Lump Sum`,
|
||||
top: topPane(key),
|
||||
bottom: [
|
||||
baseline({
|
||||
metric: dca.period.return[key].ratio,
|
||||
name: "DCA",
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
baseline({
|
||||
metric: dca.period.lumpSumReturn[key].ratio,
|
||||
name: "Lump Sum",
|
||||
color: colors.bi.p2,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
baseline({
|
||||
metric: dca.period.cagr[key].ratio,
|
||||
name: "DCA CAGR",
|
||||
unit: Unit.cagr,
|
||||
}),
|
||||
baseline({
|
||||
metric: returns.cagr[key].ratio,
|
||||
name: "Lump Sum CAGR",
|
||||
color: colors.bi.p2,
|
||||
unit: Unit.cagr,
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
@@ -470,12 +275,12 @@ export function createDcaVsLumpSumSection({ dca, lookback, returns }) {
|
||||
top: topPane(key),
|
||||
bottom: [
|
||||
...satsBtcUsd({
|
||||
pattern: dca.periodStack[key],
|
||||
pattern: dca.period.stack[key],
|
||||
name: "DCA",
|
||||
color: colors.profit,
|
||||
}),
|
||||
...satsBtcUsd({
|
||||
pattern: dca.periodLumpSumStack[key],
|
||||
pattern: dca.period.lumpSumStack[key],
|
||||
name: "Lump Sum",
|
||||
color: colors.bitcoin,
|
||||
}),
|
||||
@@ -489,8 +294,7 @@ export function createDcaVsLumpSumSection({ dca, lookback, returns }) {
|
||||
name,
|
||||
tree: [
|
||||
costBasisChart(name, key),
|
||||
shortReturnsFolder(name, key),
|
||||
profitabilityFolder(name, key),
|
||||
shortReturnsChart(name, key),
|
||||
stackChart(name, key),
|
||||
],
|
||||
};
|
||||
@@ -503,8 +307,7 @@ export function createDcaVsLumpSumSection({ dca, lookback, returns }) {
|
||||
name,
|
||||
tree: [
|
||||
costBasisChart(name, key),
|
||||
longReturnsFolder(name, key),
|
||||
profitabilityFolder(name, key),
|
||||
longReturnsChart(name, key),
|
||||
stackChart(name, key),
|
||||
],
|
||||
};
|
||||
@@ -545,28 +348,20 @@ function createPeriodSection({ dca, lookback, returns }) {
|
||||
const buildBaseEntry = (key, i) => ({
|
||||
name: periodName(key),
|
||||
color: colors.at(i, allPeriods.length),
|
||||
costBasis: isLumpSum ? lookback[key] : dca.periodAveragePrice[key],
|
||||
returns: isLumpSum ? dca.periodLumpSumReturns[key] : dca.periodReturns[key],
|
||||
minReturn: isLumpSum
|
||||
? dca.periodLumpSumMinReturn[key]
|
||||
: dca.periodMinReturn[key],
|
||||
maxReturn: isLumpSum
|
||||
? dca.periodLumpSumMaxReturn[key]
|
||||
: dca.periodMaxReturn[key],
|
||||
daysInProfit: isLumpSum
|
||||
? dca.periodLumpSumDaysInProfit[key]
|
||||
: dca.periodDaysInProfit[key],
|
||||
daysInLoss: isLumpSum
|
||||
? dca.periodLumpSumDaysInLoss[key]
|
||||
: dca.periodDaysInLoss[key],
|
||||
stack: isLumpSum ? dca.periodLumpSumStack[key] : dca.periodStack[key],
|
||||
costBasis: isLumpSum ? lookback[key] : dca.period.costBasis[key],
|
||||
returns: isLumpSum
|
||||
? dca.period.lumpSumReturn[key].ratio
|
||||
: dca.period.return[key].ratio,
|
||||
stack: isLumpSum
|
||||
? dca.period.lumpSumStack[key]
|
||||
: dca.period.stack[key],
|
||||
});
|
||||
|
||||
/** @param {LongPeriodKey} key @param {number} i @returns {LongEntryItem} */
|
||||
const buildLongEntry = (key, i) =>
|
||||
withCagr(
|
||||
buildBaseEntry(key, i),
|
||||
isLumpSum ? returns.cagr[key] : dca.periodCagr[key],
|
||||
isLumpSum ? returns.cagr[key].ratio : dca.period.cagr[key].ratio,
|
||||
);
|
||||
|
||||
/** @param {BaseEntryItem} entry */
|
||||
|
||||
@@ -6,7 +6,6 @@ import { includes } from "../utils/array.js";
|
||||
import { Unit } from "../utils/units.js";
|
||||
import { priceLine, priceLines } from "./constants.js";
|
||||
import { baseline, histogram, line, price } from "./series.js";
|
||||
import { createPriceRatioCharts } from "./shared.js";
|
||||
import { periodIdToName } from "./utils.js";
|
||||
|
||||
/**
|
||||
@@ -26,7 +25,7 @@ import { periodIdToName } from "./utils.js";
|
||||
* @typedef {Object} MaPeriod
|
||||
* @property {string} id
|
||||
* @property {Color} color
|
||||
* @property {ActivePriceRatioPattern} ratio
|
||||
* @property {Brk.BpsCentsRatioSatsUsdPattern} ratio
|
||||
*/
|
||||
|
||||
const commonMaIds = /** @type {const} */ ([
|
||||
@@ -49,13 +48,26 @@ function createMaSubSection(label, averages) {
|
||||
/** @param {MaPeriod} a */
|
||||
const toFolder = (a) => ({
|
||||
name: periodIdToName(a.id, true),
|
||||
tree: createPriceRatioCharts({
|
||||
context: `${periodIdToName(a.id, true)} ${label}`,
|
||||
legend: "average",
|
||||
pricePattern: a.ratio.price,
|
||||
ratio: a.ratio,
|
||||
color: a.color,
|
||||
}),
|
||||
tree: [
|
||||
{
|
||||
name: "Price",
|
||||
title: `${periodIdToName(a.id, true)} ${label}`,
|
||||
top: [price({ metric: a.ratio, name: "average", color: a.color })],
|
||||
},
|
||||
{
|
||||
name: "Ratio",
|
||||
title: `${periodIdToName(a.id, true)} ${label} Ratio`,
|
||||
top: [price({ metric: a.ratio, name: "average", color: a.color })],
|
||||
bottom: [
|
||||
baseline({
|
||||
metric: a.ratio.ratio,
|
||||
name: "Ratio",
|
||||
color: a.color,
|
||||
unit: Unit.ratio,
|
||||
}),
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
return {
|
||||
@@ -65,7 +77,7 @@ function createMaSubSection(label, averages) {
|
||||
name: "Compare",
|
||||
title: `Price ${label}s`,
|
||||
top: averages.map((a) =>
|
||||
price({ metric: a.ratio.price, name: a.id, color: a.color }),
|
||||
price({ metric: a.ratio, name: a.id, color: a.color }),
|
||||
),
|
||||
},
|
||||
...common.map(toFolder),
|
||||
@@ -184,83 +196,83 @@ function historicalSubSection(name, periods) {
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
export function createMarketSection() {
|
||||
const { market, supply, distribution, prices } = brk.metrics;
|
||||
const { market, supply, cohorts, prices, indicators } = brk.metrics;
|
||||
const {
|
||||
movingAverage: ma,
|
||||
ath,
|
||||
returns,
|
||||
volatility,
|
||||
range,
|
||||
indicators,
|
||||
technical,
|
||||
lookback,
|
||||
dca,
|
||||
} = market;
|
||||
|
||||
const shortPeriodsBase = [
|
||||
{ id: "24h", returns: returns.priceReturns._24h, lookback: lookback._24h },
|
||||
{ id: "1w", returns: returns.priceReturns._1w, lookback: lookback._1w },
|
||||
{ id: "1m", returns: returns.priceReturns._1m, lookback: lookback._1m },
|
||||
{ id: "24h", returns: returns.periods._24h.ratio, lookback: lookback._24h },
|
||||
{ id: "1w", returns: returns.periods._1w.ratio, lookback: lookback._1w },
|
||||
{ id: "1m", returns: returns.periods._1m.ratio, lookback: lookback._1m },
|
||||
{
|
||||
id: "3m",
|
||||
returns: returns.priceReturns._3m,
|
||||
returns: returns.periods._3m.ratio,
|
||||
lookback: lookback._3m,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
id: "6m",
|
||||
returns: returns.priceReturns._6m,
|
||||
returns: returns.periods._6m.ratio,
|
||||
lookback: lookback._6m,
|
||||
defaultActive: false,
|
||||
},
|
||||
{ id: "1y", returns: returns.priceReturns._1y, lookback: lookback._1y },
|
||||
{ id: "1y", returns: returns.periods._1y.ratio, lookback: lookback._1y },
|
||||
];
|
||||
|
||||
const longPeriodsBase = [
|
||||
{
|
||||
id: "2y",
|
||||
returns: returns.priceReturns._2y,
|
||||
cagr: returns.cagr._2y,
|
||||
returns: returns.periods._2y.ratio,
|
||||
cagr: returns.cagr._2y.ratio,
|
||||
lookback: lookback._2y,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
id: "3y",
|
||||
returns: returns.priceReturns._3y,
|
||||
cagr: returns.cagr._3y,
|
||||
returns: returns.periods._3y.ratio,
|
||||
cagr: returns.cagr._3y.ratio,
|
||||
lookback: lookback._3y,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
id: "4y",
|
||||
returns: returns.priceReturns._4y,
|
||||
cagr: returns.cagr._4y,
|
||||
returns: returns.periods._4y.ratio,
|
||||
cagr: returns.cagr._4y.ratio,
|
||||
lookback: lookback._4y,
|
||||
},
|
||||
{
|
||||
id: "5y",
|
||||
returns: returns.priceReturns._5y,
|
||||
cagr: returns.cagr._5y,
|
||||
returns: returns.periods._5y.ratio,
|
||||
cagr: returns.cagr._5y.ratio,
|
||||
lookback: lookback._5y,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
id: "6y",
|
||||
returns: returns.priceReturns._6y,
|
||||
cagr: returns.cagr._6y,
|
||||
returns: returns.periods._6y.ratio,
|
||||
cagr: returns.cagr._6y.ratio,
|
||||
lookback: lookback._6y,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
id: "8y",
|
||||
returns: returns.priceReturns._8y,
|
||||
cagr: returns.cagr._8y,
|
||||
returns: returns.periods._8y.ratio,
|
||||
cagr: returns.cagr._8y.ratio,
|
||||
lookback: lookback._8y,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
id: "10y",
|
||||
returns: returns.priceReturns._10y,
|
||||
cagr: returns.cagr._10y,
|
||||
returns: returns.periods._10y.ratio,
|
||||
cagr: returns.cagr._10y.ratio,
|
||||
lookback: lookback._10y,
|
||||
defaultActive: false,
|
||||
},
|
||||
@@ -282,42 +294,42 @@ export function createMarketSection() {
|
||||
|
||||
/** @type {MaPeriod[]} */
|
||||
const sma = [
|
||||
{ id: "1w", ratio: ma.price1wSma },
|
||||
{ id: "8d", ratio: ma.price8dSma },
|
||||
{ id: "13d", ratio: ma.price13dSma },
|
||||
{ id: "21d", ratio: ma.price21dSma },
|
||||
{ id: "1m", ratio: ma.price1mSma },
|
||||
{ id: "34d", ratio: ma.price34dSma },
|
||||
{ id: "55d", ratio: ma.price55dSma },
|
||||
{ id: "89d", ratio: ma.price89dSma },
|
||||
{ id: "111d", ratio: ma.price111dSma },
|
||||
{ id: "144d", ratio: ma.price144dSma },
|
||||
{ id: "200d", ratio: ma.price200dSma },
|
||||
{ id: "350d", ratio: ma.price350dSma },
|
||||
{ id: "1y", ratio: ma.price1ySma },
|
||||
{ id: "2y", ratio: ma.price2ySma },
|
||||
{ id: "200w", ratio: ma.price200wSma },
|
||||
{ id: "4y", ratio: ma.price4ySma },
|
||||
{ id: "1w", ratio: ma.sma._1w },
|
||||
{ id: "8d", ratio: ma.sma._8d },
|
||||
{ id: "13d", ratio: ma.sma._13d },
|
||||
{ id: "21d", ratio: ma.sma._21d },
|
||||
{ id: "1m", ratio: ma.sma._1m },
|
||||
{ id: "34d", ratio: ma.sma._34d },
|
||||
{ id: "55d", ratio: ma.sma._55d },
|
||||
{ id: "89d", ratio: ma.sma._89d },
|
||||
{ id: "111d", ratio: ma.sma._111d },
|
||||
{ id: "144d", ratio: ma.sma._144d },
|
||||
{ id: "200d", ratio: ma.sma._200d },
|
||||
{ id: "350d", ratio: ma.sma._350d },
|
||||
{ id: "1y", ratio: ma.sma._1y },
|
||||
{ id: "2y", ratio: ma.sma._2y },
|
||||
{ id: "200w", ratio: ma.sma._200w },
|
||||
{ id: "4y", ratio: ma.sma._4y },
|
||||
].map((p, i, arr) => ({ ...p, color: colors.at(i, arr.length) }));
|
||||
|
||||
/** @type {MaPeriod[]} */
|
||||
const ema = [
|
||||
{ id: "1w", ratio: ma.price1wEma },
|
||||
{ id: "8d", ratio: ma.price8dEma },
|
||||
{ id: "12d", ratio: ma.price12dEma },
|
||||
{ id: "13d", ratio: ma.price13dEma },
|
||||
{ id: "21d", ratio: ma.price21dEma },
|
||||
{ id: "26d", ratio: ma.price26dEma },
|
||||
{ id: "1m", ratio: ma.price1mEma },
|
||||
{ id: "34d", ratio: ma.price34dEma },
|
||||
{ id: "55d", ratio: ma.price55dEma },
|
||||
{ id: "89d", ratio: ma.price89dEma },
|
||||
{ id: "144d", ratio: ma.price144dEma },
|
||||
{ id: "200d", ratio: ma.price200dEma },
|
||||
{ id: "1y", ratio: ma.price1yEma },
|
||||
{ id: "2y", ratio: ma.price2yEma },
|
||||
{ id: "200w", ratio: ma.price200wEma },
|
||||
{ id: "4y", ratio: ma.price4yEma },
|
||||
{ id: "1w", ratio: ma.ema._1w },
|
||||
{ id: "8d", ratio: ma.ema._8d },
|
||||
{ id: "12d", ratio: ma.ema._12d },
|
||||
{ id: "13d", ratio: ma.ema._13d },
|
||||
{ id: "21d", ratio: ma.ema._21d },
|
||||
{ id: "26d", ratio: ma.ema._26d },
|
||||
{ id: "1m", ratio: ma.ema._1m },
|
||||
{ id: "34d", ratio: ma.ema._34d },
|
||||
{ id: "55d", ratio: ma.ema._55d },
|
||||
{ id: "89d", ratio: ma.ema._89d },
|
||||
{ id: "144d", ratio: ma.ema._144d },
|
||||
{ id: "200d", ratio: ma.ema._200d },
|
||||
{ id: "1y", ratio: ma.ema._1y },
|
||||
{ id: "2y", ratio: ma.ema._2y },
|
||||
{ id: "200w", ratio: ma.ema._200w },
|
||||
{ id: "4y", ratio: ma.ema._4y },
|
||||
].map((p, i, arr) => ({ ...p, color: colors.at(i, arr.length) }));
|
||||
|
||||
// SMA vs EMA comparison periods (common periods only)
|
||||
@@ -325,38 +337,38 @@ export function createMarketSection() {
|
||||
{
|
||||
id: "1w",
|
||||
name: "1 Week",
|
||||
sma: ma.price1wSma,
|
||||
ema: ma.price1wEma,
|
||||
sma: ma.sma._1w,
|
||||
ema: ma.ema._1w,
|
||||
},
|
||||
{
|
||||
id: "1m",
|
||||
name: "1 Month",
|
||||
sma: ma.price1mSma,
|
||||
ema: ma.price1mEma,
|
||||
sma: ma.sma._1m,
|
||||
ema: ma.ema._1m,
|
||||
},
|
||||
{
|
||||
id: "200d",
|
||||
name: "200 Day",
|
||||
sma: ma.price200dSma,
|
||||
ema: ma.price200dEma,
|
||||
sma: ma.sma._200d,
|
||||
ema: ma.ema._200d,
|
||||
},
|
||||
{
|
||||
id: "1y",
|
||||
name: "1 Year",
|
||||
sma: ma.price1ySma,
|
||||
ema: ma.price1yEma,
|
||||
sma: ma.sma._1y,
|
||||
ema: ma.ema._1y,
|
||||
},
|
||||
{
|
||||
id: "200w",
|
||||
name: "200 Week",
|
||||
sma: ma.price200wSma,
|
||||
ema: ma.price200wEma,
|
||||
sma: ma.sma._200w,
|
||||
ema: ma.ema._200w,
|
||||
},
|
||||
{
|
||||
id: "4y",
|
||||
name: "4 Year",
|
||||
sma: ma.price4ySma,
|
||||
ema: ma.price4yEma,
|
||||
sma: ma.sma._4y,
|
||||
ema: ma.ema._4y,
|
||||
},
|
||||
].map((p, i, arr) => ({ ...p, color: colors.at(i, arr.length) }));
|
||||
|
||||
@@ -370,7 +382,7 @@ export function createMarketSection() {
|
||||
title: "Sats per Dollar",
|
||||
bottom: [
|
||||
line({
|
||||
metric: prices.price.sats,
|
||||
metric: prices.spot.sats,
|
||||
name: "Sats/$",
|
||||
unit: Unit.sats,
|
||||
}),
|
||||
@@ -385,7 +397,7 @@ export function createMarketSection() {
|
||||
title: "Market Capitalization",
|
||||
bottom: [
|
||||
line({
|
||||
metric: supply.marketCap,
|
||||
metric: supply.marketCap.usd,
|
||||
name: "Market Cap",
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
@@ -396,7 +408,7 @@ export function createMarketSection() {
|
||||
title: "Realized Capitalization",
|
||||
bottom: [
|
||||
line({
|
||||
metric: distribution.utxoCohorts.all.realized.realizedCap,
|
||||
metric: cohorts.utxo.all.realized.cap.usd,
|
||||
name: "Realized Cap",
|
||||
color: colors.realized,
|
||||
unit: Unit.usd,
|
||||
@@ -408,20 +420,14 @@ export function createMarketSection() {
|
||||
title: "Capitalization Growth Rate",
|
||||
bottom: [
|
||||
line({
|
||||
metric: supply.marketCapGrowthRate,
|
||||
name: "Market Cap",
|
||||
metric: supply.marketCap.delta.rate._24h.percent,
|
||||
name: "Market Cap (24h)",
|
||||
color: colors.bitcoin,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
line({
|
||||
metric: supply.realizedCapGrowthRate,
|
||||
name: "Realized Cap",
|
||||
color: colors.usd,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
baseline({
|
||||
metric: supply.capGrowthRateDiff,
|
||||
name: "Difference",
|
||||
metric: supply.marketMinusRealizedCapGrowthRate._24h,
|
||||
name: "Market - Realized",
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
],
|
||||
@@ -435,10 +441,10 @@ export function createMarketSection() {
|
||||
{
|
||||
name: "Drawdown",
|
||||
title: "ATH Drawdown",
|
||||
top: [price({ metric: ath.priceAth, name: "ATH" })],
|
||||
top: [price({ metric: ath.high, name: "ATH" })],
|
||||
bottom: [
|
||||
line({
|
||||
metric: ath.priceDrawdown,
|
||||
metric: ath.drawdown.percent,
|
||||
name: "Drawdown",
|
||||
color: colors.loss,
|
||||
unit: Unit.percentage,
|
||||
@@ -448,26 +454,26 @@ export function createMarketSection() {
|
||||
{
|
||||
name: "Time Since",
|
||||
title: "Time Since ATH",
|
||||
top: [price({ metric: ath.priceAth, name: "ATH" })],
|
||||
top: [price({ metric: ath.high, name: "ATH" })],
|
||||
bottom: [
|
||||
line({
|
||||
metric: ath.daysSincePriceAth,
|
||||
metric: ath.daysSince,
|
||||
name: "Since",
|
||||
unit: Unit.days,
|
||||
}),
|
||||
line({
|
||||
metric: ath.yearsSincePriceAth,
|
||||
metric: ath.yearsSince,
|
||||
name: "Since",
|
||||
unit: Unit.years,
|
||||
}),
|
||||
line({
|
||||
metric: ath.maxDaysBetweenPriceAths,
|
||||
metric: ath.maxDaysBetween,
|
||||
name: "Max",
|
||||
color: colors.loss,
|
||||
unit: Unit.days,
|
||||
}),
|
||||
line({
|
||||
metric: ath.maxYearsBetweenPriceAths,
|
||||
metric: ath.maxYearsBetween,
|
||||
name: "Max",
|
||||
color: colors.loss,
|
||||
unit: Unit.years,
|
||||
@@ -502,22 +508,22 @@ export function createMarketSection() {
|
||||
name: "Volatility",
|
||||
tree: [
|
||||
volatilityChart("Index", "Volatility Index", Unit.percentage, {
|
||||
_1w: volatility.price1wVolatility,
|
||||
_1m: volatility.price1mVolatility,
|
||||
_1y: volatility.price1yVolatility,
|
||||
_1w: volatility._1w,
|
||||
_1m: volatility._1m,
|
||||
_1y: volatility._1y,
|
||||
}),
|
||||
{
|
||||
name: "True Range",
|
||||
title: "True Range",
|
||||
bottom: [
|
||||
line({
|
||||
metric: range.priceTrueRange,
|
||||
metric: range.trueRange,
|
||||
name: "Daily",
|
||||
color: colors.time._24h,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
line({
|
||||
metric: range.priceTrueRange2wSum,
|
||||
metric: range.trueRangeSum2w,
|
||||
name: "2w Sum",
|
||||
color: colors.time._1w,
|
||||
unit: Unit.usd,
|
||||
@@ -530,7 +536,7 @@ export function createMarketSection() {
|
||||
title: "Choppiness Index",
|
||||
bottom: [
|
||||
line({
|
||||
metric: range.price2wChoppinessIndex,
|
||||
metric: range.choppinessIndex2w.percent,
|
||||
name: "2w",
|
||||
color: colors.indicator.main,
|
||||
unit: Unit.index,
|
||||
@@ -552,12 +558,12 @@ export function createMarketSection() {
|
||||
title: "SMA vs EMA Comparison",
|
||||
top: smaVsEma.flatMap((p) => [
|
||||
price({
|
||||
metric: p.sma.price,
|
||||
metric: p.sma,
|
||||
name: `${p.id} SMA`,
|
||||
color: p.color,
|
||||
}),
|
||||
price({
|
||||
metric: p.ema.price,
|
||||
metric: p.ema,
|
||||
name: `${p.id} EMA`,
|
||||
color: p.color,
|
||||
style: 1,
|
||||
@@ -568,9 +574,9 @@ export function createMarketSection() {
|
||||
name: p.name,
|
||||
title: `${p.name} SMA vs EMA`,
|
||||
top: [
|
||||
price({ metric: p.sma.price, name: "SMA", color: p.color }),
|
||||
price({ metric: p.sma, name: "SMA", color: p.color }),
|
||||
price({
|
||||
metric: p.ema.price,
|
||||
metric: p.ema,
|
||||
name: "EMA",
|
||||
color: p.color,
|
||||
style: 1,
|
||||
@@ -593,26 +599,26 @@ export function createMarketSection() {
|
||||
{
|
||||
id: "1w",
|
||||
name: "1 Week",
|
||||
min: range.price1wMin,
|
||||
max: range.price1wMax,
|
||||
min: range.min._1w,
|
||||
max: range.max._1w,
|
||||
},
|
||||
{
|
||||
id: "2w",
|
||||
name: "2 Week",
|
||||
min: range.price2wMin,
|
||||
max: range.price2wMax,
|
||||
min: range.min._2w,
|
||||
max: range.max._2w,
|
||||
},
|
||||
{
|
||||
id: "1m",
|
||||
name: "1 Month",
|
||||
min: range.price1mMin,
|
||||
max: range.price1mMax,
|
||||
min: range.min._1m,
|
||||
max: range.max._1m,
|
||||
},
|
||||
{
|
||||
id: "1y",
|
||||
name: "1 Year",
|
||||
min: range.price1yMin,
|
||||
max: range.price1yMax,
|
||||
min: range.min._1y,
|
||||
max: range.max._1y,
|
||||
},
|
||||
].map((p) => ({
|
||||
name: p.id,
|
||||
@@ -638,17 +644,17 @@ export function createMarketSection() {
|
||||
title: "Mayer Multiple",
|
||||
top: [
|
||||
price({
|
||||
metric: ma.price200dSma.price,
|
||||
metric: ma.sma._200d,
|
||||
name: "200d SMA",
|
||||
color: colors.indicator.main,
|
||||
}),
|
||||
price({
|
||||
metric: ma.price200dSmaX24,
|
||||
metric: ma.sma._200d.x24,
|
||||
name: "200d SMA x2.4",
|
||||
color: colors.indicator.upper,
|
||||
}),
|
||||
price({
|
||||
metric: ma.price200dSmaX08,
|
||||
metric: ma.sma._200d.x08,
|
||||
name: "200d SMA x0.8",
|
||||
color: colors.indicator.lower,
|
||||
}),
|
||||
@@ -668,25 +674,25 @@ export function createMarketSection() {
|
||||
title: "RSI Comparison",
|
||||
bottom: [
|
||||
line({
|
||||
metric: indicators.rsi._1d.rsi,
|
||||
metric: technical.rsi._24h.rsi.percent,
|
||||
name: "1d",
|
||||
color: colors.time._24h,
|
||||
unit: Unit.index,
|
||||
}),
|
||||
line({
|
||||
metric: indicators.rsi._1w.rsi,
|
||||
metric: technical.rsi._1w.rsi.percent,
|
||||
name: "1w",
|
||||
color: colors.time._1w,
|
||||
unit: Unit.index,
|
||||
}),
|
||||
line({
|
||||
metric: indicators.rsi._1m.rsi,
|
||||
metric: technical.rsi._1m.rsi.percent,
|
||||
name: "1m",
|
||||
color: colors.time._1m,
|
||||
unit: Unit.index,
|
||||
}),
|
||||
line({
|
||||
metric: indicators.rsi._1y.rsi,
|
||||
metric: technical.rsi._1y.rsi.percent,
|
||||
name: "1y",
|
||||
color: colors.time._1y,
|
||||
unit: Unit.index,
|
||||
@@ -700,20 +706,20 @@ export function createMarketSection() {
|
||||
title: "RSI (1d)",
|
||||
bottom: [
|
||||
line({
|
||||
metric: indicators.rsi._1d.rsi,
|
||||
metric: technical.rsi._24h.rsi.percent,
|
||||
name: "RSI",
|
||||
color: colors.indicator.main,
|
||||
unit: Unit.index,
|
||||
}),
|
||||
line({
|
||||
metric: indicators.rsi._1d.rsiMax,
|
||||
metric: technical.rsi._24h.rsiMax.percent,
|
||||
name: "Max",
|
||||
color: colors.stat.max,
|
||||
defaultActive: false,
|
||||
unit: Unit.index,
|
||||
}),
|
||||
line({
|
||||
metric: indicators.rsi._1d.rsiMin,
|
||||
metric: technical.rsi._24h.rsiMin.percent,
|
||||
name: "Min",
|
||||
color: colors.stat.min,
|
||||
defaultActive: false,
|
||||
@@ -733,20 +739,20 @@ export function createMarketSection() {
|
||||
title: "RSI (1w)",
|
||||
bottom: [
|
||||
line({
|
||||
metric: indicators.rsi._1w.rsi,
|
||||
metric: technical.rsi._1w.rsi.percent,
|
||||
name: "RSI",
|
||||
color: colors.indicator.main,
|
||||
unit: Unit.index,
|
||||
}),
|
||||
line({
|
||||
metric: indicators.rsi._1w.rsiMax,
|
||||
metric: technical.rsi._1w.rsiMax.percent,
|
||||
name: "Max",
|
||||
color: colors.stat.max,
|
||||
defaultActive: false,
|
||||
unit: Unit.index,
|
||||
}),
|
||||
line({
|
||||
metric: indicators.rsi._1w.rsiMin,
|
||||
metric: technical.rsi._1w.rsiMin.percent,
|
||||
name: "Min",
|
||||
color: colors.stat.min,
|
||||
defaultActive: false,
|
||||
@@ -766,20 +772,20 @@ export function createMarketSection() {
|
||||
title: "RSI (1m)",
|
||||
bottom: [
|
||||
line({
|
||||
metric: indicators.rsi._1m.rsi,
|
||||
metric: technical.rsi._1m.rsi.percent,
|
||||
name: "RSI",
|
||||
color: colors.indicator.main,
|
||||
unit: Unit.index,
|
||||
}),
|
||||
line({
|
||||
metric: indicators.rsi._1m.rsiMax,
|
||||
metric: technical.rsi._1m.rsiMax.percent,
|
||||
name: "Max",
|
||||
color: colors.stat.max,
|
||||
defaultActive: false,
|
||||
unit: Unit.index,
|
||||
}),
|
||||
line({
|
||||
metric: indicators.rsi._1m.rsiMin,
|
||||
metric: technical.rsi._1m.rsiMin.percent,
|
||||
name: "Min",
|
||||
color: colors.stat.min,
|
||||
defaultActive: false,
|
||||
@@ -799,20 +805,20 @@ export function createMarketSection() {
|
||||
title: "RSI (1y)",
|
||||
bottom: [
|
||||
line({
|
||||
metric: indicators.rsi._1y.rsi,
|
||||
metric: technical.rsi._1y.rsi.percent,
|
||||
name: "RSI",
|
||||
color: colors.indicator.main,
|
||||
unit: Unit.index,
|
||||
}),
|
||||
line({
|
||||
metric: indicators.rsi._1y.rsiMax,
|
||||
metric: technical.rsi._1y.rsiMax.percent,
|
||||
name: "Max",
|
||||
color: colors.stat.max,
|
||||
defaultActive: false,
|
||||
unit: Unit.index,
|
||||
}),
|
||||
line({
|
||||
metric: indicators.rsi._1y.rsiMin,
|
||||
metric: technical.rsi._1y.rsiMin.percent,
|
||||
name: "Min",
|
||||
color: colors.stat.min,
|
||||
defaultActive: false,
|
||||
@@ -837,25 +843,25 @@ export function createMarketSection() {
|
||||
title: "Stochastic RSI Comparison",
|
||||
bottom: [
|
||||
line({
|
||||
metric: indicators.rsi._1d.stochRsiK,
|
||||
metric: technical.rsi._24h.stochRsiK.percent,
|
||||
name: "1d K",
|
||||
color: colors.time._24h,
|
||||
unit: Unit.index,
|
||||
}),
|
||||
line({
|
||||
metric: indicators.rsi._1w.stochRsiK,
|
||||
metric: technical.rsi._1w.stochRsiK.percent,
|
||||
name: "1w K",
|
||||
color: colors.time._1w,
|
||||
unit: Unit.index,
|
||||
}),
|
||||
line({
|
||||
metric: indicators.rsi._1m.stochRsiK,
|
||||
metric: technical.rsi._1m.stochRsiK.percent,
|
||||
name: "1m K",
|
||||
color: colors.time._1m,
|
||||
unit: Unit.index,
|
||||
}),
|
||||
line({
|
||||
metric: indicators.rsi._1y.stochRsiK,
|
||||
metric: technical.rsi._1y.stochRsiK.percent,
|
||||
name: "1y K",
|
||||
color: colors.time._1y,
|
||||
unit: Unit.index,
|
||||
@@ -868,13 +874,13 @@ export function createMarketSection() {
|
||||
title: "Stochastic RSI (1d)",
|
||||
bottom: [
|
||||
line({
|
||||
metric: indicators.rsi._1d.stochRsiK,
|
||||
metric: technical.rsi._24h.stochRsiK.percent,
|
||||
name: "K",
|
||||
color: colors.indicator.fast,
|
||||
unit: Unit.index,
|
||||
}),
|
||||
line({
|
||||
metric: indicators.rsi._1d.stochRsiD,
|
||||
metric: technical.rsi._24h.stochRsiD.percent,
|
||||
name: "D",
|
||||
color: colors.indicator.slow,
|
||||
unit: Unit.index,
|
||||
@@ -887,13 +893,13 @@ export function createMarketSection() {
|
||||
title: "Stochastic RSI (1w)",
|
||||
bottom: [
|
||||
line({
|
||||
metric: indicators.rsi._1w.stochRsiK,
|
||||
metric: technical.rsi._1w.stochRsiK.percent,
|
||||
name: "K",
|
||||
color: colors.indicator.fast,
|
||||
unit: Unit.index,
|
||||
}),
|
||||
line({
|
||||
metric: indicators.rsi._1w.stochRsiD,
|
||||
metric: technical.rsi._1w.stochRsiD.percent,
|
||||
name: "D",
|
||||
color: colors.indicator.slow,
|
||||
unit: Unit.index,
|
||||
@@ -906,13 +912,13 @@ export function createMarketSection() {
|
||||
title: "Stochastic RSI (1m)",
|
||||
bottom: [
|
||||
line({
|
||||
metric: indicators.rsi._1m.stochRsiK,
|
||||
metric: technical.rsi._1m.stochRsiK.percent,
|
||||
name: "K",
|
||||
color: colors.indicator.fast,
|
||||
unit: Unit.index,
|
||||
}),
|
||||
line({
|
||||
metric: indicators.rsi._1m.stochRsiD,
|
||||
metric: technical.rsi._1m.stochRsiD.percent,
|
||||
name: "D",
|
||||
color: colors.indicator.slow,
|
||||
unit: Unit.index,
|
||||
@@ -925,13 +931,13 @@ export function createMarketSection() {
|
||||
title: "Stochastic RSI (1y)",
|
||||
bottom: [
|
||||
line({
|
||||
metric: indicators.rsi._1y.stochRsiK,
|
||||
metric: technical.rsi._1y.stochRsiK.percent,
|
||||
name: "K",
|
||||
color: colors.indicator.fast,
|
||||
unit: Unit.index,
|
||||
}),
|
||||
line({
|
||||
metric: indicators.rsi._1y.stochRsiD,
|
||||
metric: technical.rsi._1y.stochRsiD.percent,
|
||||
name: "D",
|
||||
color: colors.indicator.slow,
|
||||
unit: Unit.index,
|
||||
@@ -946,13 +952,13 @@ export function createMarketSection() {
|
||||
title: "Stochastic Oscillator",
|
||||
bottom: [
|
||||
line({
|
||||
metric: indicators.stochK,
|
||||
metric: technical.stochK.percent,
|
||||
name: "K",
|
||||
color: colors.indicator.fast,
|
||||
unit: Unit.index,
|
||||
}),
|
||||
line({
|
||||
metric: indicators.stochD,
|
||||
metric: technical.stochD.percent,
|
||||
name: "D",
|
||||
color: colors.indicator.slow,
|
||||
unit: Unit.index,
|
||||
@@ -968,25 +974,25 @@ export function createMarketSection() {
|
||||
title: "MACD Comparison",
|
||||
bottom: [
|
||||
line({
|
||||
metric: indicators.macd._1d.line,
|
||||
metric: technical.macd._24h.line,
|
||||
name: "1d",
|
||||
color: colors.time._24h,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
line({
|
||||
metric: indicators.macd._1w.line,
|
||||
metric: technical.macd._1w.line,
|
||||
name: "1w",
|
||||
color: colors.time._1w,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
line({
|
||||
metric: indicators.macd._1m.line,
|
||||
metric: technical.macd._1m.line,
|
||||
name: "1m",
|
||||
color: colors.time._1m,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
line({
|
||||
metric: indicators.macd._1y.line,
|
||||
metric: technical.macd._1y.line,
|
||||
name: "1y",
|
||||
color: colors.time._1y,
|
||||
unit: Unit.usd,
|
||||
@@ -998,19 +1004,19 @@ export function createMarketSection() {
|
||||
title: "MACD (1d)",
|
||||
bottom: [
|
||||
line({
|
||||
metric: indicators.macd._1d.line,
|
||||
metric: technical.macd._24h.line,
|
||||
name: "MACD",
|
||||
color: colors.indicator.fast,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
line({
|
||||
metric: indicators.macd._1d.signal,
|
||||
metric: technical.macd._24h.signal,
|
||||
name: "Signal",
|
||||
color: colors.indicator.slow,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
histogram({
|
||||
metric: indicators.macd._1d.histogram,
|
||||
metric: technical.macd._24h.histogram,
|
||||
name: "Histogram",
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
@@ -1021,19 +1027,19 @@ export function createMarketSection() {
|
||||
title: "MACD (1w)",
|
||||
bottom: [
|
||||
line({
|
||||
metric: indicators.macd._1w.line,
|
||||
metric: technical.macd._1w.line,
|
||||
name: "MACD",
|
||||
color: colors.indicator.fast,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
line({
|
||||
metric: indicators.macd._1w.signal,
|
||||
metric: technical.macd._1w.signal,
|
||||
name: "Signal",
|
||||
color: colors.indicator.slow,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
histogram({
|
||||
metric: indicators.macd._1w.histogram,
|
||||
metric: technical.macd._1w.histogram,
|
||||
name: "Histogram",
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
@@ -1044,19 +1050,19 @@ export function createMarketSection() {
|
||||
title: "MACD (1m)",
|
||||
bottom: [
|
||||
line({
|
||||
metric: indicators.macd._1m.line,
|
||||
metric: technical.macd._1m.line,
|
||||
name: "MACD",
|
||||
color: colors.indicator.fast,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
line({
|
||||
metric: indicators.macd._1m.signal,
|
||||
metric: technical.macd._1m.signal,
|
||||
name: "Signal",
|
||||
color: colors.indicator.slow,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
histogram({
|
||||
metric: indicators.macd._1m.histogram,
|
||||
metric: technical.macd._1m.histogram,
|
||||
name: "Histogram",
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
@@ -1067,19 +1073,19 @@ export function createMarketSection() {
|
||||
title: "MACD (1y)",
|
||||
bottom: [
|
||||
line({
|
||||
metric: indicators.macd._1y.line,
|
||||
metric: technical.macd._1y.line,
|
||||
name: "MACD",
|
||||
color: colors.indicator.fast,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
line({
|
||||
metric: indicators.macd._1y.signal,
|
||||
metric: technical.macd._1y.signal,
|
||||
name: "Signal",
|
||||
color: colors.indicator.slow,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
histogram({
|
||||
metric: indicators.macd._1y.histogram,
|
||||
metric: technical.macd._1y.histogram,
|
||||
name: "Histogram",
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
@@ -1115,7 +1121,7 @@ export function createMarketSection() {
|
||||
title: "Dollar Cost Average Sats/Day",
|
||||
bottom: [
|
||||
line({
|
||||
metric: dca.dcaSatsPerDay,
|
||||
metric: dca.satsPerDay,
|
||||
name: "Sats/Day",
|
||||
unit: Unit.sats,
|
||||
}),
|
||||
@@ -1130,19 +1136,19 @@ export function createMarketSection() {
|
||||
title: "Pi Cycle",
|
||||
top: [
|
||||
price({
|
||||
metric: ma.price111dSma.price,
|
||||
metric: ma.sma._111d,
|
||||
name: "111d SMA",
|
||||
color: colors.indicator.upper,
|
||||
}),
|
||||
price({
|
||||
metric: ma.price350dSmaX2,
|
||||
metric: ma.sma._350d.x2,
|
||||
name: "350d SMA x2",
|
||||
color: colors.indicator.lower,
|
||||
}),
|
||||
],
|
||||
bottom: [
|
||||
baseline({
|
||||
metric: indicators.piCycle,
|
||||
metric: technical.piCycle.ratio,
|
||||
name: "Pi Cycle",
|
||||
unit: Unit.ratio,
|
||||
base: 1,
|
||||
@@ -1154,7 +1160,7 @@ export function createMarketSection() {
|
||||
title: "Puell Multiple",
|
||||
bottom: [
|
||||
line({
|
||||
metric: indicators.puellMultiple,
|
||||
metric: indicators.puellMultiple.ratio,
|
||||
name: "Puell",
|
||||
color: colors.usd,
|
||||
unit: Unit.ratio,
|
||||
@@ -1166,7 +1172,7 @@ export function createMarketSection() {
|
||||
title: "NVT Ratio",
|
||||
bottom: [
|
||||
line({
|
||||
metric: indicators.nvt,
|
||||
metric: indicators.nvt.ratio,
|
||||
name: "NVT",
|
||||
color: colors.bitcoin,
|
||||
unit: Unit.ratio,
|
||||
@@ -1178,13 +1184,104 @@ export function createMarketSection() {
|
||||
title: "Gini Coefficient",
|
||||
bottom: [
|
||||
line({
|
||||
metric: indicators.gini,
|
||||
metric: indicators.gini.percent,
|
||||
name: "Gini",
|
||||
color: colors.loss,
|
||||
unit: Unit.ratio,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "RHODL Ratio",
|
||||
title: "RHODL Ratio",
|
||||
bottom: [
|
||||
line({
|
||||
metric: indicators.rhodlRatio.ratio,
|
||||
name: "RHODL",
|
||||
color: colors.bitcoin,
|
||||
unit: Unit.ratio,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Thermocap Multiple",
|
||||
title: "Thermocap Multiple",
|
||||
bottom: [
|
||||
line({
|
||||
metric: indicators.thermocapMultiple.ratio,
|
||||
name: "Thermocap",
|
||||
color: colors.bitcoin,
|
||||
unit: Unit.ratio,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Stock-to-Flow",
|
||||
title: "Stock-to-Flow",
|
||||
bottom: [
|
||||
line({
|
||||
metric: indicators.stockToFlow,
|
||||
name: "S2F",
|
||||
color: colors.bitcoin,
|
||||
unit: Unit.ratio,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Dormancy",
|
||||
title: "Dormancy",
|
||||
bottom: [
|
||||
line({
|
||||
metric: indicators.dormancy.supplyAdjusted,
|
||||
name: "Supply Adjusted",
|
||||
color: colors.bitcoin,
|
||||
unit: Unit.ratio,
|
||||
}),
|
||||
line({
|
||||
metric: indicators.dormancy.flow,
|
||||
name: "Flow",
|
||||
color: colors.usd,
|
||||
unit: Unit.ratio,
|
||||
defaultActive: false,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Seller Exhaustion",
|
||||
title: "Seller Exhaustion Constant",
|
||||
bottom: [
|
||||
line({
|
||||
metric: indicators.sellerExhaustionConstant,
|
||||
name: "SEC",
|
||||
color: colors.bitcoin,
|
||||
unit: Unit.ratio,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "CDD Supply Adjusted",
|
||||
title: "Coindays Destroyed (Supply Adjusted)",
|
||||
bottom: [
|
||||
line({
|
||||
metric: indicators.coindaysDestroyedSupplyAdjusted,
|
||||
name: "CDD SA",
|
||||
color: colors.bitcoin,
|
||||
unit: Unit.ratio,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "CYD Supply Adjusted",
|
||||
title: "Coinyears Destroyed (Supply Adjusted)",
|
||||
bottom: [
|
||||
line({
|
||||
metric: indicators.coinyearsDestroyedSupplyAdjusted,
|
||||
name: "CYD SA",
|
||||
color: colors.bitcoin,
|
||||
unit: Unit.ratio,
|
||||
}),
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
@@ -56,20 +56,27 @@ export function createMiningSection() {
|
||||
const { blocks, pools, mining } = brk.metrics;
|
||||
|
||||
// Pre-compute pool entries with resolved names
|
||||
const poolData = entries(pools.vecs).map(([id, pool]) => ({
|
||||
const majorPoolData = entries(pools.major).map(([id, pool]) => ({
|
||||
id,
|
||||
name: brk.POOL_ID_TO_POOL_NAME[id],
|
||||
pool,
|
||||
}));
|
||||
const minorPoolData = entries(pools.minor).map(([id, pool]) => ({
|
||||
id,
|
||||
name: brk.POOL_ID_TO_POOL_NAME[id],
|
||||
pool,
|
||||
}));
|
||||
|
||||
// Filtered pool groups for comparisons
|
||||
const majorPools = poolData.filter((p) => includes(MAJOR_POOL_IDS, p.id));
|
||||
const antpoolFriends = poolData.filter((p) =>
|
||||
// Filtered pool groups for comparisons (major pools only have windowed dominance)
|
||||
const featuredPools = majorPoolData.filter((p) =>
|
||||
includes(MAJOR_POOL_IDS, p.id),
|
||||
);
|
||||
const antpoolFriends = majorPoolData.filter((p) =>
|
||||
includes(ANTPOOL_AND_FRIENDS_IDS, p.id),
|
||||
);
|
||||
|
||||
// Build individual pool trees
|
||||
const poolsTree = poolData.map(({ name, pool }) => ({
|
||||
const majorPoolsTree = majorPoolData.map(({ name, pool }) => ({
|
||||
name,
|
||||
tree: [
|
||||
{
|
||||
@@ -77,34 +84,34 @@ export function createMiningSection() {
|
||||
title: `Dominance: ${name}`,
|
||||
bottom: [
|
||||
dots({
|
||||
metric: pool.dominance24h,
|
||||
metric: pool.dominance._24h.percent,
|
||||
name: "24h",
|
||||
color: colors.time._24h,
|
||||
unit: Unit.percentage,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: pool.dominance1w,
|
||||
metric: pool.dominance._1w.percent,
|
||||
name: "1w",
|
||||
color: colors.time._1w,
|
||||
unit: Unit.percentage,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: pool.dominance1m,
|
||||
metric: pool.dominance._1m.percent,
|
||||
name: "1m",
|
||||
color: colors.time._1m,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
line({
|
||||
metric: pool.dominance1y,
|
||||
metric: pool.dominance._1y.percent,
|
||||
name: "1y",
|
||||
color: colors.time._1y,
|
||||
unit: Unit.percentage,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: pool.dominance,
|
||||
metric: pool.dominance.percent,
|
||||
name: "All Time",
|
||||
color: colors.time.all,
|
||||
unit: Unit.percentage,
|
||||
@@ -120,7 +127,7 @@ export function createMiningSection() {
|
||||
title: `Blocks Mined: ${name}`,
|
||||
bottom: [
|
||||
line({
|
||||
metric: pool.blocksMined.height,
|
||||
metric: pool.blocksMined.base,
|
||||
name: "base",
|
||||
unit: Unit.count,
|
||||
}),
|
||||
@@ -146,41 +153,69 @@ export function createMiningSection() {
|
||||
{
|
||||
name: "Sum",
|
||||
title: `Rewards: ${name}`,
|
||||
bottom: revenueBtcSatsUsd({
|
||||
coinbase: pool.coinbase,
|
||||
subsidy: pool.subsidy,
|
||||
fee: pool.fee,
|
||||
bottom: satsBtcUsdFrom({
|
||||
source: pool.rewards,
|
||||
key: "base",
|
||||
name: "sum",
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: `Rewards: ${name} (Total)`,
|
||||
bottom: revenueBtcSatsUsd({
|
||||
coinbase: pool.coinbase,
|
||||
subsidy: pool.subsidy,
|
||||
fee: pool.fee,
|
||||
bottom: satsBtcUsdFrom({
|
||||
source: pool.rewards,
|
||||
key: "cumulative",
|
||||
name: "all-time",
|
||||
}),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}));
|
||||
|
||||
const minorPoolsTree = minorPoolData.map(({ name, pool }) => ({
|
||||
name,
|
||||
tree: [
|
||||
{
|
||||
name: "Since Last Block",
|
||||
title: `Since Last Block: ${name}`,
|
||||
name: "Dominance",
|
||||
title: `Dominance: ${name}`,
|
||||
bottom: [
|
||||
line({
|
||||
metric: pool.blocksSinceBlock,
|
||||
name: "Elapsed",
|
||||
unit: Unit.blocks,
|
||||
}),
|
||||
line({
|
||||
metric: pool.daysSinceBlock,
|
||||
name: "Elapsed",
|
||||
unit: Unit.days,
|
||||
metric: pool.dominance.percent,
|
||||
name: "All Time",
|
||||
color: colors.time.all,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Blocks Mined",
|
||||
tree: [
|
||||
{
|
||||
name: "Base",
|
||||
title: `Blocks Mined: ${name}`,
|
||||
bottom: [
|
||||
line({
|
||||
metric: pool.blocksMined.base,
|
||||
name: "base",
|
||||
unit: Unit.count,
|
||||
}),
|
||||
],
|
||||
},
|
||||
rollingWindowsTree({ windows: pool.blocksMined.sum, title: `Blocks Mined: ${name}`, unit: Unit.count }),
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: `Blocks Mined: ${name} (Total)`,
|
||||
bottom: [
|
||||
line({
|
||||
metric: pool.blocksMined.cumulative,
|
||||
name: "all-time",
|
||||
unit: Unit.count,
|
||||
}),
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}));
|
||||
|
||||
@@ -196,33 +231,33 @@ export function createMiningSection() {
|
||||
title: "Network Hashrate",
|
||||
bottom: [
|
||||
dots({
|
||||
metric: mining.hashrate.hashRate,
|
||||
metric: mining.hashrate.rate.base,
|
||||
name: "Hashrate",
|
||||
unit: Unit.hashRate,
|
||||
}),
|
||||
line({
|
||||
metric: mining.hashrate.hashRate1wSma,
|
||||
metric: mining.hashrate.rate.sma._1w,
|
||||
name: "1w SMA",
|
||||
color: colors.time._1w,
|
||||
unit: Unit.hashRate,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: mining.hashrate.hashRate1mSma,
|
||||
metric: mining.hashrate.rate.sma._1m,
|
||||
name: "1m SMA",
|
||||
color: colors.time._1m,
|
||||
unit: Unit.hashRate,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: mining.hashrate.hashRate2mSma,
|
||||
metric: mining.hashrate.rate.sma._2m,
|
||||
name: "2m SMA",
|
||||
color: colors.indicator.main,
|
||||
unit: Unit.hashRate,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: mining.hashrate.hashRate1ySma,
|
||||
metric: mining.hashrate.rate.sma._1y,
|
||||
name: "1y SMA",
|
||||
color: colors.time._1y,
|
||||
unit: Unit.hashRate,
|
||||
@@ -235,7 +270,7 @@ export function createMiningSection() {
|
||||
unit: Unit.hashRate,
|
||||
}),
|
||||
line({
|
||||
metric: mining.hashrate.hashRateAth,
|
||||
metric: mining.hashrate.rate.ath,
|
||||
name: "ATH",
|
||||
color: colors.loss,
|
||||
unit: Unit.hashRate,
|
||||
@@ -248,13 +283,13 @@ export function createMiningSection() {
|
||||
title: "Network Hashrate ATH",
|
||||
bottom: [
|
||||
line({
|
||||
metric: mining.hashrate.hashRateAth,
|
||||
metric: mining.hashrate.rate.ath,
|
||||
name: "ATH",
|
||||
color: colors.loss,
|
||||
unit: Unit.hashRate,
|
||||
}),
|
||||
dots({
|
||||
metric: mining.hashrate.hashRate,
|
||||
metric: mining.hashrate.rate.base,
|
||||
name: "Hashrate",
|
||||
color: colors.bitcoin,
|
||||
unit: Unit.hashRate,
|
||||
@@ -266,7 +301,7 @@ export function createMiningSection() {
|
||||
title: "Network Hashrate Drawdown",
|
||||
bottom: [
|
||||
line({
|
||||
metric: mining.hashrate.hashRateDrawdown,
|
||||
metric: mining.hashrate.rate.drawdown.percent,
|
||||
name: "Drawdown",
|
||||
unit: Unit.percentage,
|
||||
color: colors.loss,
|
||||
@@ -307,7 +342,7 @@ export function createMiningSection() {
|
||||
title: "Difficulty Adjustment",
|
||||
bottom: [
|
||||
baseline({
|
||||
metric: blocks.difficulty.adjustment,
|
||||
metric: blocks.difficulty.adjustment.percent,
|
||||
name: "Change",
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
@@ -318,12 +353,12 @@ export function createMiningSection() {
|
||||
title: "Next Difficulty Adjustment",
|
||||
bottom: [
|
||||
line({
|
||||
metric: blocks.difficulty.blocksBeforeNextAdjustment,
|
||||
metric: blocks.difficulty.blocksBeforeNext,
|
||||
name: "Remaining",
|
||||
unit: Unit.blocks,
|
||||
}),
|
||||
line({
|
||||
metric: blocks.difficulty.daysBeforeNextAdjustment,
|
||||
metric: blocks.difficulty.daysBeforeNext,
|
||||
name: "Remaining",
|
||||
unit: Unit.days,
|
||||
}),
|
||||
@@ -381,22 +416,22 @@ export function createMiningSection() {
|
||||
title: "Coinbase Rolling Sum",
|
||||
bottom: [
|
||||
...satsBtcUsd({
|
||||
pattern: mining.rewards.coinbase._24h.sum,
|
||||
pattern: mining.rewards.coinbase.sum._24h,
|
||||
name: "24h",
|
||||
color: colors.time._24h,
|
||||
}),
|
||||
...satsBtcUsd({
|
||||
pattern: mining.rewards.coinbase._7d.sum,
|
||||
pattern: mining.rewards.coinbase.sum._1w,
|
||||
name: "7d",
|
||||
color: colors.time._1w,
|
||||
}),
|
||||
...satsBtcUsd({
|
||||
pattern: mining.rewards.coinbase._30d.sum,
|
||||
pattern: mining.rewards.coinbase.sum._1m,
|
||||
name: "30d",
|
||||
color: colors.time._1m,
|
||||
}),
|
||||
...satsBtcUsd({
|
||||
pattern: mining.rewards.coinbase._1y.sum,
|
||||
pattern: mining.rewards.coinbase.sum._1y,
|
||||
name: "1y",
|
||||
color: colors.time._1y,
|
||||
}),
|
||||
@@ -406,7 +441,7 @@ export function createMiningSection() {
|
||||
name: "24h",
|
||||
title: "Coinbase 24h Rolling Sum",
|
||||
bottom: satsBtcUsd({
|
||||
pattern: mining.rewards.coinbase._24h.sum,
|
||||
pattern: mining.rewards.coinbase.sum._24h,
|
||||
name: "24h",
|
||||
color: colors.time._24h,
|
||||
}),
|
||||
@@ -415,7 +450,7 @@ export function createMiningSection() {
|
||||
name: "7d",
|
||||
title: "Coinbase 7d Rolling Sum",
|
||||
bottom: satsBtcUsd({
|
||||
pattern: mining.rewards.coinbase._7d.sum,
|
||||
pattern: mining.rewards.coinbase.sum._1w,
|
||||
name: "7d",
|
||||
color: colors.time._1w,
|
||||
}),
|
||||
@@ -424,7 +459,7 @@ export function createMiningSection() {
|
||||
name: "30d",
|
||||
title: "Coinbase 30d Rolling Sum",
|
||||
bottom: satsBtcUsd({
|
||||
pattern: mining.rewards.coinbase._30d.sum,
|
||||
pattern: mining.rewards.coinbase.sum._1m,
|
||||
name: "30d",
|
||||
color: colors.time._1m,
|
||||
}),
|
||||
@@ -433,18 +468,13 @@ export function createMiningSection() {
|
||||
name: "1y",
|
||||
title: "Coinbase 1y Rolling Sum",
|
||||
bottom: satsBtcUsd({
|
||||
pattern: mining.rewards.coinbase._1y.sum,
|
||||
pattern: mining.rewards.coinbase.sum._1y,
|
||||
name: "1y",
|
||||
color: colors.time._1y,
|
||||
}),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Distribution",
|
||||
title: "Coinbase Rewards per Block Distribution",
|
||||
bottom: distributionBtcSatsUsd(mining.rewards.coinbase._24h),
|
||||
},
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: "Coinbase Rewards (Total)",
|
||||
@@ -469,7 +499,7 @@ export function createMiningSection() {
|
||||
name: "sum",
|
||||
}),
|
||||
line({
|
||||
metric: mining.rewards.subsidyUsd1ySma,
|
||||
metric: mining.rewards.subsidy.sma1y.usd,
|
||||
name: "1y SMA",
|
||||
color: colors.time._1y,
|
||||
unit: Unit.usd,
|
||||
@@ -477,78 +507,6 @@ export function createMiningSection() {
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Rolling",
|
||||
tree: [
|
||||
{
|
||||
name: "Compare",
|
||||
title: "Subsidy Rolling Sum",
|
||||
bottom: [
|
||||
...satsBtcUsd({
|
||||
pattern: mining.rewards.subsidy._24h.sum,
|
||||
name: "24h",
|
||||
color: colors.time._24h,
|
||||
}),
|
||||
...satsBtcUsd({
|
||||
pattern: mining.rewards.subsidy._7d.sum,
|
||||
name: "7d",
|
||||
color: colors.time._1w,
|
||||
}),
|
||||
...satsBtcUsd({
|
||||
pattern: mining.rewards.subsidy._30d.sum,
|
||||
name: "30d",
|
||||
color: colors.time._1m,
|
||||
}),
|
||||
...satsBtcUsd({
|
||||
pattern: mining.rewards.subsidy._1y.sum,
|
||||
name: "1y",
|
||||
color: colors.time._1y,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "24h",
|
||||
title: "Subsidy 24h Rolling Sum",
|
||||
bottom: satsBtcUsd({
|
||||
pattern: mining.rewards.subsidy._24h.sum,
|
||||
name: "24h",
|
||||
color: colors.time._24h,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "7d",
|
||||
title: "Subsidy 7d Rolling Sum",
|
||||
bottom: satsBtcUsd({
|
||||
pattern: mining.rewards.subsidy._7d.sum,
|
||||
name: "7d",
|
||||
color: colors.time._1w,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "30d",
|
||||
title: "Subsidy 30d Rolling Sum",
|
||||
bottom: satsBtcUsd({
|
||||
pattern: mining.rewards.subsidy._30d.sum,
|
||||
name: "30d",
|
||||
color: colors.time._1m,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "1y",
|
||||
title: "Subsidy 1y Rolling Sum",
|
||||
bottom: satsBtcUsd({
|
||||
pattern: mining.rewards.subsidy._1y.sum,
|
||||
name: "1y",
|
||||
color: colors.time._1y,
|
||||
}),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Distribution",
|
||||
title: "Block Subsidy Distribution",
|
||||
bottom: distributionBtcSatsUsd(mining.rewards.subsidy._24h),
|
||||
},
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: "Block Subsidy (Total)",
|
||||
@@ -580,22 +538,22 @@ export function createMiningSection() {
|
||||
title: "Fee Rolling Sum",
|
||||
bottom: [
|
||||
...satsBtcUsd({
|
||||
pattern: mining.rewards.fees._24h.sum,
|
||||
pattern: mining.rewards.fees.sum._24h,
|
||||
name: "24h",
|
||||
color: colors.time._24h,
|
||||
}),
|
||||
...satsBtcUsd({
|
||||
pattern: mining.rewards.fees._7d.sum,
|
||||
pattern: mining.rewards.fees.sum._1w,
|
||||
name: "7d",
|
||||
color: colors.time._1w,
|
||||
}),
|
||||
...satsBtcUsd({
|
||||
pattern: mining.rewards.fees._30d.sum,
|
||||
pattern: mining.rewards.fees.sum._1m,
|
||||
name: "30d",
|
||||
color: colors.time._1m,
|
||||
}),
|
||||
...satsBtcUsd({
|
||||
pattern: mining.rewards.fees._1y.sum,
|
||||
pattern: mining.rewards.fees.sum._1y,
|
||||
name: "1y",
|
||||
color: colors.time._1y,
|
||||
}),
|
||||
@@ -605,7 +563,7 @@ export function createMiningSection() {
|
||||
name: "24h",
|
||||
title: "Fee 24h Rolling Sum",
|
||||
bottom: satsBtcUsd({
|
||||
pattern: mining.rewards.fees._24h.sum,
|
||||
pattern: mining.rewards.fees.sum._24h,
|
||||
name: "24h",
|
||||
color: colors.time._24h,
|
||||
}),
|
||||
@@ -614,7 +572,7 @@ export function createMiningSection() {
|
||||
name: "7d",
|
||||
title: "Fee 7d Rolling Sum",
|
||||
bottom: satsBtcUsd({
|
||||
pattern: mining.rewards.fees._7d.sum,
|
||||
pattern: mining.rewards.fees.sum._1w,
|
||||
name: "7d",
|
||||
color: colors.time._1w,
|
||||
}),
|
||||
@@ -623,7 +581,7 @@ export function createMiningSection() {
|
||||
name: "30d",
|
||||
title: "Fee 30d Rolling Sum",
|
||||
bottom: satsBtcUsd({
|
||||
pattern: mining.rewards.fees._30d.sum,
|
||||
pattern: mining.rewards.fees.sum._1m,
|
||||
name: "30d",
|
||||
color: colors.time._1m,
|
||||
}),
|
||||
@@ -632,7 +590,7 @@ export function createMiningSection() {
|
||||
name: "1y",
|
||||
title: "Fee 1y Rolling Sum",
|
||||
bottom: satsBtcUsd({
|
||||
pattern: mining.rewards.fees._1y.sum,
|
||||
pattern: mining.rewards.fees.sum._1y,
|
||||
name: "1y",
|
||||
color: colors.time._1y,
|
||||
}),
|
||||
@@ -666,31 +624,31 @@ export function createMiningSection() {
|
||||
title: "Subsidy Dominance",
|
||||
bottom: [
|
||||
line({
|
||||
metric: mining.rewards.subsidyDominance,
|
||||
metric: mining.rewards.subsidy.dominance.percent,
|
||||
name: "All-time",
|
||||
color: colors.time.all,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
line({
|
||||
metric: mining.rewards.subsidyDominance24h,
|
||||
metric: mining.rewards.subsidy.dominance._24h.percent,
|
||||
name: "24h",
|
||||
color: colors.time._24h,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
line({
|
||||
metric: mining.rewards.subsidyDominance7d,
|
||||
metric: mining.rewards.subsidy.dominance._1w.percent,
|
||||
name: "7d",
|
||||
color: colors.time._1w,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
line({
|
||||
metric: mining.rewards.subsidyDominance30d,
|
||||
metric: mining.rewards.subsidy.dominance._1m.percent,
|
||||
name: "30d",
|
||||
color: colors.time._1m,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
line({
|
||||
metric: mining.rewards.subsidyDominance1y,
|
||||
metric: mining.rewards.subsidy.dominance._1y.percent,
|
||||
name: "1y",
|
||||
color: colors.time._1y,
|
||||
unit: Unit.percentage,
|
||||
@@ -702,31 +660,31 @@ export function createMiningSection() {
|
||||
title: "Fee Dominance",
|
||||
bottom: [
|
||||
line({
|
||||
metric: mining.rewards.feeDominance,
|
||||
metric: mining.rewards.fees.dominance.percent,
|
||||
name: "All-time",
|
||||
color: colors.time.all,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
line({
|
||||
metric: mining.rewards.feeDominance24h,
|
||||
metric: mining.rewards.fees.dominance._24h.percent,
|
||||
name: "24h",
|
||||
color: colors.time._24h,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
line({
|
||||
metric: mining.rewards.feeDominance7d,
|
||||
metric: mining.rewards.fees.dominance._1w.percent,
|
||||
name: "7d",
|
||||
color: colors.time._1w,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
line({
|
||||
metric: mining.rewards.feeDominance30d,
|
||||
metric: mining.rewards.fees.dominance._1m.percent,
|
||||
name: "30d",
|
||||
color: colors.time._1m,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
line({
|
||||
metric: mining.rewards.feeDominance1y,
|
||||
metric: mining.rewards.fees.dominance._1y.percent,
|
||||
name: "1y",
|
||||
color: colors.time._1y,
|
||||
unit: Unit.percentage,
|
||||
@@ -740,13 +698,13 @@ export function createMiningSection() {
|
||||
title: "Revenue Dominance (All-time)",
|
||||
bottom: [
|
||||
line({
|
||||
metric: mining.rewards.subsidyDominance,
|
||||
metric: mining.rewards.subsidy.dominance.percent,
|
||||
name: "Subsidy",
|
||||
color: colors.mining.subsidy,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
line({
|
||||
metric: mining.rewards.feeDominance,
|
||||
metric: mining.rewards.fees.dominance.percent,
|
||||
name: "Fees",
|
||||
color: colors.mining.fee,
|
||||
unit: Unit.percentage,
|
||||
@@ -758,13 +716,13 @@ export function createMiningSection() {
|
||||
title: "Revenue Dominance (24h)",
|
||||
bottom: [
|
||||
line({
|
||||
metric: mining.rewards.subsidyDominance24h,
|
||||
metric: mining.rewards.subsidy.dominance._24h.percent,
|
||||
name: "Subsidy",
|
||||
color: colors.mining.subsidy,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
line({
|
||||
metric: mining.rewards.feeDominance24h,
|
||||
metric: mining.rewards.fees.dominance._24h.percent,
|
||||
name: "Fees",
|
||||
color: colors.mining.fee,
|
||||
unit: Unit.percentage,
|
||||
@@ -776,13 +734,13 @@ export function createMiningSection() {
|
||||
title: "Revenue Dominance (7d)",
|
||||
bottom: [
|
||||
line({
|
||||
metric: mining.rewards.subsidyDominance7d,
|
||||
metric: mining.rewards.subsidy.dominance._1w.percent,
|
||||
name: "Subsidy",
|
||||
color: colors.mining.subsidy,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
line({
|
||||
metric: mining.rewards.feeDominance7d,
|
||||
metric: mining.rewards.fees.dominance._1w.percent,
|
||||
name: "Fees",
|
||||
color: colors.mining.fee,
|
||||
unit: Unit.percentage,
|
||||
@@ -794,13 +752,13 @@ export function createMiningSection() {
|
||||
title: "Revenue Dominance (30d)",
|
||||
bottom: [
|
||||
line({
|
||||
metric: mining.rewards.subsidyDominance30d,
|
||||
metric: mining.rewards.subsidy.dominance._1m.percent,
|
||||
name: "Subsidy",
|
||||
color: colors.mining.subsidy,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
line({
|
||||
metric: mining.rewards.feeDominance30d,
|
||||
metric: mining.rewards.fees.dominance._1m.percent,
|
||||
name: "Fees",
|
||||
color: colors.mining.fee,
|
||||
unit: Unit.percentage,
|
||||
@@ -812,13 +770,13 @@ export function createMiningSection() {
|
||||
title: "Revenue Dominance (1y)",
|
||||
bottom: [
|
||||
line({
|
||||
metric: mining.rewards.subsidyDominance1y,
|
||||
metric: mining.rewards.subsidy.dominance._1y.percent,
|
||||
name: "Subsidy",
|
||||
color: colors.mining.subsidy,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
line({
|
||||
metric: mining.rewards.feeDominance1y,
|
||||
metric: mining.rewards.fees.dominance._1y.percent,
|
||||
name: "Fees",
|
||||
color: colors.mining.fee,
|
||||
unit: Unit.percentage,
|
||||
@@ -834,7 +792,7 @@ export function createMiningSection() {
|
||||
name: "Sum",
|
||||
title: "Unclaimed Rewards",
|
||||
bottom: satsBtcUsdFrom({
|
||||
source: mining.rewards.unclaimedRewards,
|
||||
source: mining.rewards.unclaimed,
|
||||
key: "base",
|
||||
name: "sum",
|
||||
}),
|
||||
@@ -843,7 +801,7 @@ export function createMiningSection() {
|
||||
name: "Cumulative",
|
||||
title: "Unclaimed Rewards (Total)",
|
||||
bottom: satsBtcUsdFrom({
|
||||
source: mining.rewards.unclaimedRewards,
|
||||
source: mining.rewards.unclaimed,
|
||||
key: "cumulative",
|
||||
name: "all-time",
|
||||
}),
|
||||
@@ -862,25 +820,25 @@ export function createMiningSection() {
|
||||
title: "Hash Price",
|
||||
bottom: [
|
||||
line({
|
||||
metric: mining.hashrate.hashPriceThs,
|
||||
metric: mining.hashrate.price.ths,
|
||||
name: "TH/s",
|
||||
color: colors.usd,
|
||||
unit: Unit.usdPerThsPerDay,
|
||||
}),
|
||||
line({
|
||||
metric: mining.hashrate.hashPricePhs,
|
||||
metric: mining.hashrate.price.phs,
|
||||
name: "PH/s",
|
||||
color: colors.usd,
|
||||
unit: Unit.usdPerPhsPerDay,
|
||||
}),
|
||||
dotted({
|
||||
metric: mining.hashrate.hashPriceThsMin,
|
||||
metric: mining.hashrate.price.thsMin,
|
||||
name: "TH/s Min",
|
||||
color: colors.stat.min,
|
||||
unit: Unit.usdPerThsPerDay,
|
||||
}),
|
||||
dotted({
|
||||
metric: mining.hashrate.hashPricePhsMin,
|
||||
metric: mining.hashrate.price.phsMin,
|
||||
name: "PH/s Min",
|
||||
color: colors.stat.min,
|
||||
unit: Unit.usdPerPhsPerDay,
|
||||
@@ -892,25 +850,25 @@ export function createMiningSection() {
|
||||
title: "Hash Value",
|
||||
bottom: [
|
||||
line({
|
||||
metric: mining.hashrate.hashValueThs,
|
||||
metric: mining.hashrate.value.ths,
|
||||
name: "TH/s",
|
||||
color: colors.bitcoin,
|
||||
unit: Unit.satsPerThsPerDay,
|
||||
}),
|
||||
line({
|
||||
metric: mining.hashrate.hashValuePhs,
|
||||
metric: mining.hashrate.value.phs,
|
||||
name: "PH/s",
|
||||
color: colors.bitcoin,
|
||||
unit: Unit.satsPerPhsPerDay,
|
||||
}),
|
||||
dotted({
|
||||
metric: mining.hashrate.hashValueThsMin,
|
||||
metric: mining.hashrate.value.thsMin,
|
||||
name: "TH/s Min",
|
||||
color: colors.stat.min,
|
||||
unit: Unit.satsPerThsPerDay,
|
||||
}),
|
||||
dotted({
|
||||
metric: mining.hashrate.hashValuePhsMin,
|
||||
metric: mining.hashrate.value.phsMin,
|
||||
name: "PH/s Min",
|
||||
color: colors.stat.min,
|
||||
unit: Unit.satsPerPhsPerDay,
|
||||
@@ -922,13 +880,13 @@ export function createMiningSection() {
|
||||
title: "Recovery",
|
||||
bottom: [
|
||||
line({
|
||||
metric: mining.hashrate.hashPriceRebound,
|
||||
metric: mining.hashrate.price.rebound.percent,
|
||||
name: "Hash Price",
|
||||
color: colors.usd,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
line({
|
||||
metric: mining.hashrate.hashValueRebound,
|
||||
metric: mining.hashrate.value.rebound.percent,
|
||||
name: "Hash Value",
|
||||
color: colors.bitcoin,
|
||||
unit: Unit.percentage,
|
||||
@@ -947,12 +905,12 @@ export function createMiningSection() {
|
||||
title: "Next Halving",
|
||||
bottom: [
|
||||
line({
|
||||
metric: blocks.halving.blocksBeforeNextHalving,
|
||||
metric: blocks.halving.blocksBeforeNext,
|
||||
name: "Remaining",
|
||||
unit: Unit.blocks,
|
||||
}),
|
||||
line({
|
||||
metric: blocks.halving.daysBeforeNextHalving,
|
||||
metric: blocks.halving.daysBeforeNext,
|
||||
name: "Remaining",
|
||||
unit: Unit.days,
|
||||
}),
|
||||
@@ -983,11 +941,11 @@ export function createMiningSection() {
|
||||
{
|
||||
name: "Dominance",
|
||||
title: "Dominance: Major Pools (1m)",
|
||||
bottom: majorPools.map((p, i) =>
|
||||
bottom: featuredPools.map((p, i) =>
|
||||
line({
|
||||
metric: p.pool.dominance1m,
|
||||
metric: p.pool.dominance._1m.percent,
|
||||
name: p.name,
|
||||
color: colors.at(i, majorPools.length),
|
||||
color: colors.at(i, featuredPools.length),
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
),
|
||||
@@ -995,11 +953,11 @@ export function createMiningSection() {
|
||||
{
|
||||
name: "Blocks Mined",
|
||||
title: "Blocks Mined: Major Pools (1m)",
|
||||
bottom: majorPools.map((p, i) =>
|
||||
bottom: featuredPools.map((p, i) =>
|
||||
line({
|
||||
metric: p.pool.blocksMined1mSum,
|
||||
metric: p.pool.blocksMined.sum._1m,
|
||||
name: p.name,
|
||||
color: colors.at(i, majorPools.length),
|
||||
color: colors.at(i, featuredPools.length),
|
||||
unit: Unit.count,
|
||||
}),
|
||||
),
|
||||
@@ -1007,12 +965,12 @@ export function createMiningSection() {
|
||||
{
|
||||
name: "Total Rewards",
|
||||
title: "Total Rewards: Major Pools",
|
||||
bottom: majorPools.flatMap((p, i) =>
|
||||
bottom: featuredPools.flatMap((p, i) =>
|
||||
satsBtcUsdFrom({
|
||||
source: p.pool.coinbase,
|
||||
source: p.pool.rewards,
|
||||
key: "base",
|
||||
name: p.name,
|
||||
color: colors.at(i, majorPools.length),
|
||||
color: colors.at(i, featuredPools.length),
|
||||
}),
|
||||
),
|
||||
},
|
||||
@@ -1027,7 +985,7 @@ export function createMiningSection() {
|
||||
title: "Dominance: AntPool & Friends (1m)",
|
||||
bottom: antpoolFriends.map((p, i) =>
|
||||
line({
|
||||
metric: p.pool.dominance1m,
|
||||
metric: p.pool.dominance._1m.percent,
|
||||
name: p.name,
|
||||
color: colors.at(i, antpoolFriends.length),
|
||||
unit: Unit.percentage,
|
||||
@@ -1039,7 +997,7 @@ export function createMiningSection() {
|
||||
title: "Blocks Mined: AntPool & Friends (1m)",
|
||||
bottom: antpoolFriends.map((p, i) =>
|
||||
line({
|
||||
metric: p.pool.blocksMined1mSum,
|
||||
metric: p.pool.blocksMined.sum._1m,
|
||||
name: p.name,
|
||||
color: colors.at(i, antpoolFriends.length),
|
||||
unit: Unit.count,
|
||||
@@ -1051,7 +1009,7 @@ export function createMiningSection() {
|
||||
title: "Total Rewards: AntPool & Friends",
|
||||
bottom: antpoolFriends.flatMap((p, i) =>
|
||||
satsBtcUsdFrom({
|
||||
source: p.pool.coinbase,
|
||||
source: p.pool.rewards,
|
||||
key: "base",
|
||||
name: p.name,
|
||||
color: colors.at(i, antpoolFriends.length),
|
||||
@@ -1063,7 +1021,7 @@ export function createMiningSection() {
|
||||
// All pools
|
||||
{
|
||||
name: "All Pools",
|
||||
tree: poolsTree,
|
||||
tree: [...majorPoolsTree, ...minorPoolsTree],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -10,14 +10,14 @@ import {
|
||||
dots,
|
||||
baseline,
|
||||
fromSupplyPattern,
|
||||
fromBaseStatsPattern,
|
||||
chartsFromFullPerBlock,
|
||||
chartsFromCount,
|
||||
chartsFromValueFull,
|
||||
fromStatsPattern,
|
||||
chartsFromSumPerBlock,
|
||||
statsAtWindow,
|
||||
rollingWindowsTree,
|
||||
distributionWindowsTree,
|
||||
mapWindows,
|
||||
ROLLING_WINDOWS,
|
||||
} from "./series.js";
|
||||
import { satsBtcUsd, satsBtcUsdFrom } from "./shared.js";
|
||||
|
||||
@@ -33,7 +33,8 @@ export function createNetworkSection() {
|
||||
outputs,
|
||||
scripts,
|
||||
supply,
|
||||
distribution,
|
||||
addresses,
|
||||
cohorts,
|
||||
} = brk.metrics;
|
||||
|
||||
const st = colors.scriptType;
|
||||
@@ -60,13 +61,13 @@ export function createNetworkSection() {
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
key: "emptyoutput",
|
||||
key: "emptyOutput",
|
||||
name: "Empty",
|
||||
color: st.empty,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
key: "unknownoutput",
|
||||
key: "unknownOutput",
|
||||
name: "Unknown",
|
||||
color: st.unknown,
|
||||
defaultActive: false,
|
||||
@@ -114,42 +115,25 @@ export function createNetworkSection() {
|
||||
},
|
||||
]);
|
||||
|
||||
// Balance change types
|
||||
const balanceTypes = /** @type {const} */ ([
|
||||
{
|
||||
key: "balanceIncreased",
|
||||
name: "Accumulating",
|
||||
title: "Accumulating Addresses per Block",
|
||||
compareTitle: "Accumulating Addresses per Block by Type",
|
||||
},
|
||||
{
|
||||
key: "balanceDecreased",
|
||||
name: "Distributing",
|
||||
title: "Distributing Addresses per Block",
|
||||
compareTitle: "Distributing Addresses per Block by Type",
|
||||
},
|
||||
]);
|
||||
|
||||
// Count types for comparison charts
|
||||
// addrCount and emptyAddrCount have .count, totalAddrCount doesn't
|
||||
const countTypes = /** @type {const} */ ([
|
||||
{
|
||||
name: "Funded",
|
||||
title: "Address Count by Type",
|
||||
/** @param {AddressableType} t */
|
||||
getMetric: (t) => distribution.addrCount[t].count,
|
||||
getMetric: (t) => addresses.funded[t],
|
||||
},
|
||||
{
|
||||
name: "Empty",
|
||||
title: "Empty Address Count by Type",
|
||||
/** @param {AddressableType} t */
|
||||
getMetric: (t) => distribution.emptyAddrCount[t].count,
|
||||
getMetric: (t) => addresses.empty[t],
|
||||
},
|
||||
{
|
||||
name: "Total",
|
||||
title: "Total Address Count by Type",
|
||||
/** @param {AddressableType} t */
|
||||
getMetric: (t) => distribution.totalAddrCount[t],
|
||||
getMetric: (t) => addresses.total[t],
|
||||
},
|
||||
]);
|
||||
|
||||
@@ -164,19 +148,19 @@ export function createNetworkSection() {
|
||||
title: `${titlePrefix}Address Count`,
|
||||
bottom: [
|
||||
line({
|
||||
metric: distribution.addrCount[key].count,
|
||||
metric: addresses.funded[key],
|
||||
name: "Funded",
|
||||
unit: Unit.count,
|
||||
}),
|
||||
line({
|
||||
metric: distribution.emptyAddrCount[key].count,
|
||||
metric: addresses.empty[key],
|
||||
name: "Empty",
|
||||
color: colors.gray,
|
||||
unit: Unit.count,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: distribution.totalAddrCount[key],
|
||||
metric: addresses.total[key],
|
||||
name: "Total",
|
||||
color: colors.default,
|
||||
unit: Unit.count,
|
||||
@@ -187,35 +171,23 @@ export function createNetworkSection() {
|
||||
{
|
||||
name: "Trends",
|
||||
tree: [
|
||||
{
|
||||
name: "30d Change",
|
||||
title: `${titlePrefix}Address Count 30d Change`,
|
||||
bottom: [
|
||||
baseline({
|
||||
metric: distribution.addrCount[key]._30dChange,
|
||||
name: "Funded",
|
||||
unit: Unit.count,
|
||||
}),
|
||||
baseline({
|
||||
metric: distribution.emptyAddrCount[key]._30dChange,
|
||||
name: "Empty",
|
||||
color: colors.gray,
|
||||
unit: Unit.count,
|
||||
defaultActive: false,
|
||||
}),
|
||||
],
|
||||
},
|
||||
rollingWindowsTree({
|
||||
windows: addresses.delta[key].change,
|
||||
title: `${titlePrefix}Address Count Change`,
|
||||
unit: Unit.count,
|
||||
series: baseline,
|
||||
}),
|
||||
{
|
||||
name: "New",
|
||||
tree: (() => {
|
||||
const p = distribution.newAddrCount[key];
|
||||
const p = addresses.new[key];
|
||||
const t = `${titlePrefix}New Address Count`;
|
||||
return [
|
||||
{
|
||||
name: "Sum",
|
||||
title: t,
|
||||
bottom: [
|
||||
line({ metric: p.height, name: "base", unit: Unit.count }),
|
||||
line({ metric: p.base, name: "base", unit: Unit.count }),
|
||||
],
|
||||
},
|
||||
rollingWindowsTree({
|
||||
@@ -239,46 +211,66 @@ export function createNetworkSection() {
|
||||
},
|
||||
{
|
||||
name: "Reactivated",
|
||||
title: `${titlePrefix}Reactivated Addresses per Block`,
|
||||
bottom: fromBaseStatsPattern({
|
||||
pattern: distribution.addressActivity[key].reactivated,
|
||||
window: "_24h",
|
||||
unit: Unit.count,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "Growth Rate",
|
||||
title: `${titlePrefix}Address Growth Rate per Block`,
|
||||
bottom: fromBaseStatsPattern({
|
||||
pattern: distribution.growthRate[key],
|
||||
window: "_24h",
|
||||
unit: Unit.ratio,
|
||||
}),
|
||||
tree: [
|
||||
{
|
||||
name: "Base",
|
||||
title: `${titlePrefix}Reactivated Addresses per Block`,
|
||||
bottom: [
|
||||
dots({
|
||||
metric: addresses.activity[key].reactivated.height,
|
||||
name: "base",
|
||||
unit: Unit.count,
|
||||
}),
|
||||
line({
|
||||
metric: addresses.activity[key].reactivated._24h,
|
||||
name: "24h avg",
|
||||
color: colors.stat.avg,
|
||||
unit: Unit.count,
|
||||
}),
|
||||
],
|
||||
},
|
||||
rollingWindowsTree({
|
||||
windows: addresses.activity[key].reactivated,
|
||||
title: `${titlePrefix}Reactivated Addresses`,
|
||||
unit: Unit.count,
|
||||
}),
|
||||
],
|
||||
},
|
||||
rollingWindowsTree({
|
||||
windows: mapWindows(addresses.delta[key].rate, (r) => r.ratio),
|
||||
title: `${titlePrefix}Address Growth Rate`,
|
||||
unit: Unit.ratio,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Transacting",
|
||||
tree: transactingTypes.map((t) => ({
|
||||
name: t.name,
|
||||
title: `${titlePrefix}${t.title}`,
|
||||
bottom: fromBaseStatsPattern({
|
||||
pattern: distribution.addressActivity[key][t.key],
|
||||
window: "_24h",
|
||||
unit: Unit.count,
|
||||
}),
|
||||
})),
|
||||
},
|
||||
{
|
||||
name: "Balance",
|
||||
tree: balanceTypes.map((b) => ({
|
||||
name: b.name,
|
||||
title: `${titlePrefix}${b.title}`,
|
||||
bottom: fromBaseStatsPattern({
|
||||
pattern: distribution.addressActivity[key][b.key],
|
||||
window: "_24h",
|
||||
unit: Unit.count,
|
||||
}),
|
||||
tree: [
|
||||
{
|
||||
name: "Base",
|
||||
title: `${titlePrefix}${t.title}`,
|
||||
bottom: [
|
||||
dots({
|
||||
metric: addresses.activity[key][t.key].height,
|
||||
name: "base",
|
||||
unit: Unit.count,
|
||||
}),
|
||||
line({
|
||||
metric: addresses.activity[key][t.key]._24h,
|
||||
name: "24h avg",
|
||||
color: colors.stat.avg,
|
||||
unit: Unit.count,
|
||||
}),
|
||||
],
|
||||
},
|
||||
rollingWindowsTree({
|
||||
windows: addresses.activity[key][t.key],
|
||||
title: `${titlePrefix}${t.title.replace(" per Block", "")}`,
|
||||
unit: Unit.count,
|
||||
}),
|
||||
],
|
||||
})),
|
||||
},
|
||||
];
|
||||
@@ -312,13 +304,13 @@ export function createNetworkSection() {
|
||||
title: `${groupName} New Address Count`,
|
||||
bottom: types.flatMap((t) => [
|
||||
line({
|
||||
metric: distribution.newAddrCount[t.key].height,
|
||||
metric: addresses.new[t.key].base,
|
||||
name: t.name,
|
||||
color: t.color,
|
||||
unit: Unit.count,
|
||||
}),
|
||||
line({
|
||||
metric: distribution.newAddrCount[t.key].sum._24h,
|
||||
metric: addresses.new[t.key].sum._24h,
|
||||
name: t.name,
|
||||
color: t.color,
|
||||
unit: Unit.count,
|
||||
@@ -327,81 +319,78 @@ export function createNetworkSection() {
|
||||
},
|
||||
{
|
||||
name: "Reactivated",
|
||||
title: `${groupName} Reactivated Addresses per Block`,
|
||||
bottom: types.flatMap((t) => [
|
||||
line({
|
||||
metric: distribution.addressActivity[t.key].reactivated.height,
|
||||
name: t.name,
|
||||
color: t.color,
|
||||
unit: Unit.count,
|
||||
}),
|
||||
line({
|
||||
metric:
|
||||
distribution.addressActivity[t.key].reactivated.average._24h,
|
||||
name: t.name,
|
||||
color: t.color,
|
||||
unit: Unit.count,
|
||||
}),
|
||||
]),
|
||||
tree: [
|
||||
{
|
||||
name: "Base",
|
||||
title: `${groupName} Reactivated Addresses per Block`,
|
||||
bottom: types.map((t) =>
|
||||
line({
|
||||
metric: addresses.activity[t.key].reactivated.height,
|
||||
name: t.name,
|
||||
color: t.color,
|
||||
unit: Unit.count,
|
||||
}),
|
||||
),
|
||||
},
|
||||
...ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: `${groupName} Reactivated Addresses (${w.name})`,
|
||||
bottom: types.map((t) =>
|
||||
line({
|
||||
metric: addresses.activity[t.key].reactivated[w.key],
|
||||
name: t.name,
|
||||
color: t.color,
|
||||
unit: Unit.count,
|
||||
}),
|
||||
),
|
||||
})),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Growth Rate",
|
||||
title: `${groupName} Address Growth Rate per Block`,
|
||||
bottom: types.flatMap((t) => [
|
||||
dots({
|
||||
metric: distribution.growthRate[t.key].height,
|
||||
name: t.name,
|
||||
color: t.color,
|
||||
unit: Unit.ratio,
|
||||
}),
|
||||
dots({
|
||||
metric: distribution.growthRate[t.key].average._24h,
|
||||
name: t.name,
|
||||
color: t.color,
|
||||
unit: Unit.ratio,
|
||||
}),
|
||||
]),
|
||||
tree: ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: `${groupName} Address Growth Rate (${w.name})`,
|
||||
bottom: types.map((t) =>
|
||||
line({
|
||||
metric: addresses.delta[t.key].rate[w.key].ratio,
|
||||
name: t.name,
|
||||
color: t.color,
|
||||
unit: Unit.ratio,
|
||||
}),
|
||||
),
|
||||
})),
|
||||
},
|
||||
{
|
||||
name: "Transacting",
|
||||
tree: transactingTypes.map((tr) => ({
|
||||
name: tr.name,
|
||||
title: `${groupName} ${tr.compareTitle}`,
|
||||
bottom: types.flatMap((t) => [
|
||||
line({
|
||||
metric: distribution.addressActivity[t.key][tr.key].height,
|
||||
name: t.name,
|
||||
color: t.color,
|
||||
unit: Unit.count,
|
||||
}),
|
||||
line({
|
||||
metric: distribution.addressActivity[t.key][tr.key].average._24h,
|
||||
name: t.name,
|
||||
color: t.color,
|
||||
unit: Unit.count,
|
||||
}),
|
||||
]),
|
||||
})),
|
||||
},
|
||||
{
|
||||
name: "Balance",
|
||||
tree: balanceTypes.map((b) => ({
|
||||
name: b.name,
|
||||
title: `${groupName} ${b.compareTitle}`,
|
||||
bottom: types.flatMap((t) => [
|
||||
line({
|
||||
metric: distribution.addressActivity[t.key][b.key].height,
|
||||
name: t.name,
|
||||
color: t.color,
|
||||
unit: Unit.count,
|
||||
}),
|
||||
line({
|
||||
metric: distribution.addressActivity[t.key][b.key].average._24h,
|
||||
name: t.name,
|
||||
color: t.color,
|
||||
unit: Unit.count,
|
||||
}),
|
||||
]),
|
||||
tree: [
|
||||
{
|
||||
name: "Base",
|
||||
title: `${groupName} ${tr.compareTitle}`,
|
||||
bottom: types.map((t) =>
|
||||
line({
|
||||
metric: addresses.activity[t.key][tr.key].height,
|
||||
name: t.name,
|
||||
color: t.color,
|
||||
unit: Unit.count,
|
||||
}),
|
||||
),
|
||||
},
|
||||
...ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: `${groupName} ${tr.compareTitle} (${w.name})`,
|
||||
bottom: types.map((t) =>
|
||||
line({
|
||||
metric: addresses.activity[t.key][tr.key][w.key],
|
||||
name: t.name,
|
||||
color: t.color,
|
||||
unit: Unit.count,
|
||||
}),
|
||||
),
|
||||
})),
|
||||
],
|
||||
})),
|
||||
},
|
||||
],
|
||||
@@ -478,7 +467,7 @@ export function createNetworkSection() {
|
||||
title: "Inflation Rate",
|
||||
bottom: [
|
||||
dots({
|
||||
metric: supply.inflation,
|
||||
metric: supply.inflationRate.percent,
|
||||
name: "Rate",
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
@@ -509,10 +498,18 @@ export function createNetworkSection() {
|
||||
},
|
||||
{
|
||||
name: "OP_RETURN",
|
||||
tree: chartsFromValueFull({
|
||||
pattern: scripts.value.opreturn,
|
||||
title: "OP_RETURN Burned",
|
||||
}),
|
||||
tree: [
|
||||
{
|
||||
name: "Sum",
|
||||
title: "OP_RETURN Burned",
|
||||
bottom: satsBtcUsd({ pattern: scripts.value.opreturn.base, name: "sum" }),
|
||||
},
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: "OP_RETURN Burned (Total)",
|
||||
bottom: satsBtcUsd({ pattern: scripts.value.opreturn.cumulative, name: "all-time" }),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -524,7 +521,7 @@ export function createNetworkSection() {
|
||||
{
|
||||
name: "Count",
|
||||
tree: chartsFromFullPerBlock({
|
||||
pattern: transactions.count.txCount,
|
||||
pattern: transactions.count.total,
|
||||
title: "Transaction Count",
|
||||
unit: Unit.count,
|
||||
}),
|
||||
@@ -545,11 +542,11 @@ export function createNetworkSection() {
|
||||
title: "Transaction Volume",
|
||||
bottom: [
|
||||
...satsBtcUsd({
|
||||
pattern: transactions.volume.sentSum,
|
||||
pattern: transactions.volume.sentSum.base,
|
||||
name: "Sent",
|
||||
}),
|
||||
...satsBtcUsd({
|
||||
pattern: transactions.volume.receivedSum,
|
||||
pattern: transactions.volume.receivedSum.base,
|
||||
name: "Received",
|
||||
color: colors.entity.output,
|
||||
}),
|
||||
@@ -559,7 +556,7 @@ export function createNetworkSection() {
|
||||
name: "Annualized",
|
||||
title: "Annualized Transaction Volume",
|
||||
bottom: satsBtcUsd({
|
||||
pattern: transactions.volume.annualizedVolume,
|
||||
pattern: transactions.volume.sentSum.sum._1y,
|
||||
name: "Annualized",
|
||||
}),
|
||||
},
|
||||
@@ -588,7 +585,7 @@ export function createNetworkSection() {
|
||||
bottom: entries(transactions.versions).map(
|
||||
([v, data], i, arr) =>
|
||||
line({
|
||||
metric: data.height,
|
||||
metric: data.base,
|
||||
name: v,
|
||||
color: colors.at(i, arr.length),
|
||||
unit: Unit.count,
|
||||
@@ -642,12 +639,12 @@ export function createNetworkSection() {
|
||||
title: "Block Count",
|
||||
bottom: [
|
||||
line({
|
||||
metric: blocks.count.blockCount.height,
|
||||
metric: blocks.count.total.base,
|
||||
name: "base",
|
||||
unit: Unit.count,
|
||||
}),
|
||||
line({
|
||||
metric: blocks.count.blockCountTarget,
|
||||
metric: blocks.count.target,
|
||||
name: "Target",
|
||||
color: colors.gray,
|
||||
unit: Unit.count,
|
||||
@@ -656,7 +653,7 @@ export function createNetworkSection() {
|
||||
],
|
||||
},
|
||||
rollingWindowsTree({
|
||||
windows: blocks.count.blockCountSum,
|
||||
windows: blocks.count.total.sum,
|
||||
title: "Block Count",
|
||||
unit: Unit.count,
|
||||
}),
|
||||
@@ -665,7 +662,7 @@ export function createNetworkSection() {
|
||||
title: "Block Count (Total)",
|
||||
bottom: [
|
||||
line({
|
||||
metric: blocks.count.blockCount.cumulative,
|
||||
metric: blocks.count.total.cumulative,
|
||||
name: "all-time",
|
||||
unit: Unit.count,
|
||||
}),
|
||||
@@ -675,14 +672,30 @@ export function createNetworkSection() {
|
||||
},
|
||||
{
|
||||
name: "Interval",
|
||||
title: "Block Interval",
|
||||
bottom: [
|
||||
...fromBaseStatsPattern({
|
||||
pattern: blocks.interval,
|
||||
window: "_24h",
|
||||
tree: [
|
||||
{
|
||||
name: "Base",
|
||||
title: "Block Interval",
|
||||
bottom: [
|
||||
dots({
|
||||
metric: blocks.interval.height,
|
||||
name: "base",
|
||||
unit: Unit.secs,
|
||||
}),
|
||||
line({
|
||||
metric: blocks.interval._24h,
|
||||
name: "24h avg",
|
||||
color: colors.stat.avg,
|
||||
unit: Unit.secs,
|
||||
}),
|
||||
priceLine({ unit: Unit.secs, name: "Target", number: 600 }),
|
||||
],
|
||||
},
|
||||
rollingWindowsTree({
|
||||
windows: blocks.interval,
|
||||
title: "Block Interval",
|
||||
unit: Unit.secs,
|
||||
}),
|
||||
priceLine({ unit: Unit.secs, name: "Target", number: 600 }),
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -693,7 +706,7 @@ export function createNetworkSection() {
|
||||
title: "Block Size",
|
||||
bottom: [
|
||||
line({
|
||||
metric: blocks.totalSize,
|
||||
metric: blocks.size.total,
|
||||
name: "base",
|
||||
unit: Unit.bytes,
|
||||
}),
|
||||
@@ -704,21 +717,12 @@ export function createNetworkSection() {
|
||||
title: "Block Size",
|
||||
unit: Unit.bytes,
|
||||
}),
|
||||
{
|
||||
name: "Distribution",
|
||||
title: "Block Size Distribution",
|
||||
bottom: [
|
||||
line({
|
||||
metric: blocks.totalSize,
|
||||
name: "base",
|
||||
unit: Unit.bytes,
|
||||
}),
|
||||
...fromStatsPattern({
|
||||
pattern: statsAtWindow(blocks.size, "_24h"),
|
||||
unit: Unit.bytes,
|
||||
}),
|
||||
],
|
||||
},
|
||||
distributionWindowsTree({
|
||||
pattern: blocks.size,
|
||||
base: blocks.size.total,
|
||||
title: "Block Size",
|
||||
unit: Unit.bytes,
|
||||
}),
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: "Block Size (Total)",
|
||||
@@ -740,7 +744,7 @@ export function createNetworkSection() {
|
||||
title: "Block Weight",
|
||||
bottom: [
|
||||
line({
|
||||
metric: blocks.weight.base,
|
||||
metric: blocks.weight.raw,
|
||||
name: "base",
|
||||
unit: Unit.wu,
|
||||
}),
|
||||
@@ -751,21 +755,12 @@ export function createNetworkSection() {
|
||||
title: "Block Weight",
|
||||
unit: Unit.wu,
|
||||
}),
|
||||
{
|
||||
name: "Distribution",
|
||||
title: "Block Weight Distribution",
|
||||
bottom: [
|
||||
line({
|
||||
metric: blocks.weight.base,
|
||||
name: "base",
|
||||
unit: Unit.wu,
|
||||
}),
|
||||
...fromStatsPattern({
|
||||
pattern: statsAtWindow(blocks.weight, "_24h"),
|
||||
unit: Unit.wu,
|
||||
}),
|
||||
],
|
||||
},
|
||||
distributionWindowsTree({
|
||||
pattern: blocks.weight,
|
||||
base: blocks.weight.raw,
|
||||
title: "Block Weight",
|
||||
unit: Unit.wu,
|
||||
}),
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: "Block Weight (Total)",
|
||||
@@ -787,7 +782,7 @@ export function createNetworkSection() {
|
||||
title: "Block vBytes",
|
||||
bottom: [
|
||||
line({
|
||||
metric: blocks.vbytes.height,
|
||||
metric: blocks.vbytes.base,
|
||||
name: "base",
|
||||
unit: Unit.vb,
|
||||
}),
|
||||
@@ -798,21 +793,12 @@ export function createNetworkSection() {
|
||||
title: "Block vBytes",
|
||||
unit: Unit.vb,
|
||||
}),
|
||||
{
|
||||
name: "Distribution",
|
||||
title: "Block vBytes Distribution",
|
||||
bottom: [
|
||||
line({
|
||||
metric: blocks.vbytes.height,
|
||||
name: "base",
|
||||
unit: Unit.vb,
|
||||
}),
|
||||
...fromStatsPattern({
|
||||
pattern: statsAtWindow(blocks.vbytes, "_24h"),
|
||||
unit: Unit.vb,
|
||||
}),
|
||||
],
|
||||
},
|
||||
distributionWindowsTree({
|
||||
pattern: blocks.vbytes,
|
||||
base: blocks.vbytes.base,
|
||||
title: "Block vBytes",
|
||||
unit: Unit.vb,
|
||||
}),
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: "Block vBytes (Total)",
|
||||
@@ -829,11 +815,13 @@ export function createNetworkSection() {
|
||||
{
|
||||
name: "Fullness",
|
||||
title: "Block Fullness",
|
||||
bottom: fromBaseStatsPattern({
|
||||
pattern: blocks.fullness,
|
||||
window: "_24h",
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
bottom: [
|
||||
dots({
|
||||
metric: blocks.fullness.percent,
|
||||
name: "base",
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -847,7 +835,7 @@ export function createNetworkSection() {
|
||||
title: "UTXO Count",
|
||||
bottom: [
|
||||
line({
|
||||
metric: outputs.count.utxoCount,
|
||||
metric: outputs.count.unspent,
|
||||
name: "Count",
|
||||
unit: Unit.count,
|
||||
}),
|
||||
@@ -858,7 +846,7 @@ export function createNetworkSection() {
|
||||
title: "UTXO Count 30d Change",
|
||||
bottom: [
|
||||
baseline({
|
||||
metric: distribution.utxoCohorts.all.outputs.utxoCount30dChange,
|
||||
metric: cohorts.utxo.all.outputs.unspentCount.delta.change._1m,
|
||||
name: "30d Change",
|
||||
unit: Unit.count,
|
||||
}),
|
||||
@@ -869,7 +857,7 @@ export function createNetworkSection() {
|
||||
title: "UTXO Flow",
|
||||
bottom: [
|
||||
line({
|
||||
metric: outputs.count.totalCount.sum,
|
||||
metric: outputs.count.total.sum,
|
||||
name: "Created",
|
||||
color: colors.entity.output,
|
||||
unit: Unit.count,
|
||||
@@ -895,7 +883,7 @@ export function createNetworkSection() {
|
||||
{
|
||||
name: "Outputs",
|
||||
tree: chartsFromSumPerBlock({
|
||||
pattern: outputs.count.totalCount,
|
||||
pattern: outputs.count.total,
|
||||
title: "Output Count",
|
||||
unit: Unit.count,
|
||||
}),
|
||||
@@ -957,14 +945,14 @@ export function createNetworkSection() {
|
||||
title: "New Address Count by Type",
|
||||
bottom: addressTypes.flatMap((t) => [
|
||||
line({
|
||||
metric: distribution.newAddrCount[t.key].height,
|
||||
metric: addresses.new[t.key].base,
|
||||
name: t.name,
|
||||
color: t.color,
|
||||
unit: Unit.count,
|
||||
defaultActive: t.defaultActive,
|
||||
}),
|
||||
line({
|
||||
metric: distribution.newAddrCount[t.key].sum._24h,
|
||||
metric: addresses.new[t.key].sum._24h,
|
||||
name: t.name,
|
||||
color: t.color,
|
||||
unit: Unit.count,
|
||||
@@ -974,95 +962,83 @@ export function createNetworkSection() {
|
||||
},
|
||||
{
|
||||
name: "Reactivated",
|
||||
title: "Reactivated Addresses per Block by Type",
|
||||
bottom: addressTypes.flatMap((t) => [
|
||||
line({
|
||||
metric:
|
||||
distribution.addressActivity[t.key].reactivated.height,
|
||||
name: t.name,
|
||||
color: t.color,
|
||||
unit: Unit.count,
|
||||
defaultActive: t.defaultActive,
|
||||
}),
|
||||
line({
|
||||
metric:
|
||||
distribution.addressActivity[t.key].reactivated.average
|
||||
._24h,
|
||||
name: t.name,
|
||||
color: t.color,
|
||||
unit: Unit.count,
|
||||
defaultActive: t.defaultActive,
|
||||
}),
|
||||
]),
|
||||
tree: [
|
||||
{
|
||||
name: "Base",
|
||||
title: "Reactivated Addresses per Block by Type",
|
||||
bottom: addressTypes.map((t) =>
|
||||
line({
|
||||
metric: addresses.activity[t.key].reactivated.height,
|
||||
name: t.name,
|
||||
color: t.color,
|
||||
unit: Unit.count,
|
||||
defaultActive: t.defaultActive,
|
||||
}),
|
||||
),
|
||||
},
|
||||
...ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: `Reactivated Addresses by Type (${w.name})`,
|
||||
bottom: addressTypes.map((t) =>
|
||||
line({
|
||||
metric: addresses.activity[t.key].reactivated[w.key],
|
||||
name: t.name,
|
||||
color: t.color,
|
||||
unit: Unit.count,
|
||||
defaultActive: t.defaultActive,
|
||||
}),
|
||||
),
|
||||
})),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Growth Rate",
|
||||
title: "Address Growth Rate per Block by Type",
|
||||
bottom: addressTypes.flatMap((t) => [
|
||||
dots({
|
||||
metric: distribution.growthRate[t.key].height,
|
||||
name: t.name,
|
||||
color: t.color,
|
||||
unit: Unit.ratio,
|
||||
defaultActive: t.defaultActive,
|
||||
}),
|
||||
dots({
|
||||
metric: distribution.growthRate[t.key].average._24h,
|
||||
name: t.name,
|
||||
color: t.color,
|
||||
unit: Unit.ratio,
|
||||
defaultActive: t.defaultActive,
|
||||
}),
|
||||
]),
|
||||
tree: ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: `Address Growth Rate by Type (${w.name})`,
|
||||
bottom: addressTypes.map((t) =>
|
||||
line({
|
||||
metric: addresses.delta[t.key].rate[w.key].ratio,
|
||||
name: t.name,
|
||||
color: t.color,
|
||||
unit: Unit.ratio,
|
||||
defaultActive: t.defaultActive,
|
||||
}),
|
||||
),
|
||||
})),
|
||||
},
|
||||
{
|
||||
name: "Transacting",
|
||||
tree: transactingTypes.map((tr) => ({
|
||||
name: tr.name,
|
||||
title: tr.compareTitle,
|
||||
bottom: addressTypes.flatMap((t) => [
|
||||
line({
|
||||
metric:
|
||||
distribution.addressActivity[t.key][tr.key].height,
|
||||
name: t.name,
|
||||
color: t.color,
|
||||
unit: Unit.count,
|
||||
defaultActive: t.defaultActive,
|
||||
}),
|
||||
line({
|
||||
metric:
|
||||
distribution.addressActivity[t.key][tr.key].average
|
||||
._24h,
|
||||
name: t.name,
|
||||
color: t.color,
|
||||
unit: Unit.count,
|
||||
defaultActive: t.defaultActive,
|
||||
}),
|
||||
]),
|
||||
})),
|
||||
},
|
||||
{
|
||||
name: "Balance",
|
||||
tree: balanceTypes.map((b) => ({
|
||||
name: b.name,
|
||||
title: b.compareTitle,
|
||||
bottom: addressTypes.flatMap((t) => [
|
||||
line({
|
||||
metric: distribution.addressActivity[t.key][b.key].height,
|
||||
name: t.name,
|
||||
color: t.color,
|
||||
unit: Unit.count,
|
||||
defaultActive: t.defaultActive,
|
||||
}),
|
||||
line({
|
||||
metric:
|
||||
distribution.addressActivity[t.key][b.key].average._24h,
|
||||
name: t.name,
|
||||
color: t.color,
|
||||
unit: Unit.count,
|
||||
defaultActive: t.defaultActive,
|
||||
}),
|
||||
]),
|
||||
tree: [
|
||||
{
|
||||
name: "Base",
|
||||
title: tr.compareTitle,
|
||||
bottom: addressTypes.map((t) =>
|
||||
line({
|
||||
metric: addresses.activity[t.key][tr.key].height,
|
||||
name: t.name,
|
||||
color: t.color,
|
||||
unit: Unit.count,
|
||||
defaultActive: t.defaultActive,
|
||||
}),
|
||||
),
|
||||
},
|
||||
...ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: `${tr.compareTitle} (${w.name})`,
|
||||
bottom: addressTypes.map((t) =>
|
||||
line({
|
||||
metric: addresses.activity[t.key][tr.key][w.key],
|
||||
name: t.name,
|
||||
color: t.color,
|
||||
unit: Unit.count,
|
||||
defaultActive: t.defaultActive,
|
||||
}),
|
||||
),
|
||||
})),
|
||||
],
|
||||
})),
|
||||
},
|
||||
],
|
||||
@@ -1237,13 +1213,13 @@ export function createNetworkSection() {
|
||||
title: "Script Adoption",
|
||||
bottom: [
|
||||
line({
|
||||
metric: scripts.adoption.segwit,
|
||||
metric: scripts.adoption.segwit.percent,
|
||||
name: "SegWit",
|
||||
color: colors.segwit,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
line({
|
||||
metric: scripts.adoption.taproot,
|
||||
metric: scripts.adoption.taproot.percent,
|
||||
name: "Taproot",
|
||||
color: taprootAddresses[1].color,
|
||||
unit: Unit.percentage,
|
||||
@@ -1255,7 +1231,7 @@ export function createNetworkSection() {
|
||||
title: "SegWit Adoption",
|
||||
bottom: [
|
||||
line({
|
||||
metric: scripts.adoption.segwit,
|
||||
metric: scripts.adoption.segwit.percent,
|
||||
name: "Adoption",
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
@@ -1266,7 +1242,7 @@ export function createNetworkSection() {
|
||||
title: "Taproot Adoption",
|
||||
bottom: [
|
||||
line({
|
||||
metric: scripts.adoption.taproot,
|
||||
metric: scripts.adoption.taproot.percent,
|
||||
name: "Adoption",
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
|
||||
@@ -3,6 +3,36 @@
|
||||
import { colors } from "../utils/colors.js";
|
||||
import { Unit } from "../utils/units.js";
|
||||
|
||||
// ============================================================================
|
||||
// Rolling window constants
|
||||
// ============================================================================
|
||||
|
||||
/** @typedef {'_24h' | '_1w' | '_1m' | '_1y'} RollingWindowKey */
|
||||
|
||||
/** @type {ReadonlyArray<{key: RollingWindowKey, name: string, color: Color}>} */
|
||||
export const ROLLING_WINDOWS = [
|
||||
{ key: "_24h", name: "24h", color: colors.time._24h },
|
||||
{ key: "_1w", name: "1w", color: colors.time._1w },
|
||||
{ key: "_1m", name: "1m", color: colors.time._1m },
|
||||
{ key: "_1y", name: "1y", color: colors.time._1y },
|
||||
];
|
||||
|
||||
/**
|
||||
* Extract a metric from each rolling window via a mapping function
|
||||
* @template T
|
||||
* @param {{ _24h: T, _1w: T, _1m: T, _1y: T }} windows
|
||||
* @param {(v: T) => AnyMetricPattern} extract
|
||||
* @returns {{ _24h: AnyMetricPattern, _1w: AnyMetricPattern, _1m: AnyMetricPattern, _1y: AnyMetricPattern }}
|
||||
*/
|
||||
export function mapWindows(windows, extract) {
|
||||
return {
|
||||
_24h: extract(windows._24h),
|
||||
_1w: extract(windows._1w),
|
||||
_1m: extract(windows._1m),
|
||||
_1y: extract(windows._1y),
|
||||
};
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Price helper for top pane (auto-expands to USD + sats)
|
||||
// ============================================================================
|
||||
@@ -439,46 +469,67 @@ export function statsAtWindow(pattern, window) {
|
||||
* @param {{ _24h: AnyMetricPattern, _1w: AnyMetricPattern, _1m: AnyMetricPattern, _1y: AnyMetricPattern }} args.windows
|
||||
* @param {string} args.title
|
||||
* @param {Unit} args.unit
|
||||
* @param {(args: {metric: AnyMetricPattern, name: string, color: Color, unit: Unit}) => AnyFetchedSeriesBlueprint} [args.series]
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
export function rollingWindowsTree({ windows, title, unit }) {
|
||||
export function rollingWindowsTree({ windows, title, unit, series = line }) {
|
||||
return {
|
||||
name: "Rolling",
|
||||
tree: [
|
||||
{
|
||||
name: "Compare",
|
||||
title: `${title} Rolling`,
|
||||
bottom: ROLLING_WINDOWS.map((w) =>
|
||||
series({
|
||||
metric: windows[w.key],
|
||||
name: w.name,
|
||||
color: w.color,
|
||||
unit,
|
||||
}),
|
||||
),
|
||||
},
|
||||
...ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: `${title} ${w.name}`,
|
||||
bottom: [
|
||||
line({ metric: windows._24h, name: "24h", color: colors.time._24h, unit }),
|
||||
line({ metric: windows._1w, name: "7d", color: colors.time._1w, unit }),
|
||||
line({ metric: windows._1m, name: "30d", color: colors.time._1m, unit }),
|
||||
line({ metric: windows._1y, name: "1y", color: colors.time._1y, unit }),
|
||||
series({
|
||||
metric: windows[w.key],
|
||||
name: w.name,
|
||||
color: w.color,
|
||||
unit,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "24h",
|
||||
title: `${title} 24h`,
|
||||
bottom: [line({ metric: windows._24h, name: "24h", color: colors.time._24h, unit })],
|
||||
},
|
||||
{
|
||||
name: "7d",
|
||||
title: `${title} 7d`,
|
||||
bottom: [line({ metric: windows._1w, name: "7d", color: colors.time._1w, unit })],
|
||||
},
|
||||
{
|
||||
name: "30d",
|
||||
title: `${title} 30d`,
|
||||
bottom: [line({ metric: windows._1m, name: "30d", color: colors.time._1m, unit })],
|
||||
},
|
||||
{
|
||||
name: "1y",
|
||||
title: `${title} 1y`,
|
||||
bottom: [line({ metric: windows._1y, name: "1y", color: colors.time._1y, unit })],
|
||||
},
|
||||
})),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Distribution folder tree with stats at each rolling window (24h/7d/30d/1y)
|
||||
* @param {Object} args
|
||||
* @param {Record<string, any>} args.pattern - Pattern with pct10/pct25/... and average/median/... as _1y24h30d7dPattern
|
||||
* @param {AnyMetricPattern} [args.base] - Optional base metric to show as dots on each chart
|
||||
* @param {string} args.title
|
||||
* @param {Unit} args.unit
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
export function distributionWindowsTree({ pattern, base, title, unit }) {
|
||||
return {
|
||||
name: "Distribution",
|
||||
tree: ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: `${title} Distribution (${w.name})`,
|
||||
bottom: [
|
||||
...(base ? [line({ metric: base, name: "base", unit })] : []),
|
||||
...fromStatsPattern({
|
||||
pattern: statsAtWindow(pattern, w.key),
|
||||
unit,
|
||||
}),
|
||||
],
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Map a rolling window slot's stats to a specific unit, producing a stats-compatible pattern
|
||||
* @param {RollingWindowSlot} slot - Rolling window slot (e.g., pattern.rolling._24h)
|
||||
@@ -503,9 +554,18 @@ function rollingSlotForUnit(slot, unitKey) {
|
||||
* @returns {AnyFetchedSeriesBlueprint[]}
|
||||
*/
|
||||
export const distributionBtcSatsUsd = (slot) => [
|
||||
...fromStatsPattern({ pattern: rollingSlotForUnit(slot, "btc"), unit: Unit.btc }),
|
||||
...fromStatsPattern({ pattern: rollingSlotForUnit(slot, "sats"), unit: Unit.sats }),
|
||||
...fromStatsPattern({ pattern: rollingSlotForUnit(slot, "usd"), unit: Unit.usd }),
|
||||
...fromStatsPattern({
|
||||
pattern: rollingSlotForUnit(slot, "btc"),
|
||||
unit: Unit.btc,
|
||||
}),
|
||||
...fromStatsPattern({
|
||||
pattern: rollingSlotForUnit(slot, "sats"),
|
||||
unit: Unit.sats,
|
||||
}),
|
||||
...fromStatsPattern({
|
||||
pattern: rollingSlotForUnit(slot, "usd"),
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -658,20 +718,16 @@ export function chartsFromFull({
|
||||
distributionSuffix = "",
|
||||
}) {
|
||||
const distTitle = distributionSuffix
|
||||
? `${title} ${distributionSuffix} Distribution`
|
||||
: `${title} Distribution`;
|
||||
? `${title} ${distributionSuffix}`
|
||||
: title;
|
||||
return [
|
||||
{
|
||||
name: "Sum",
|
||||
title,
|
||||
bottom: [{ metric: pattern.height, title: "base", unit }],
|
||||
bottom: [{ metric: pattern.base, title: "base", unit }],
|
||||
},
|
||||
rollingWindowsTree({ windows: pattern.sum, title, unit }),
|
||||
{
|
||||
name: "Distribution",
|
||||
title: distTitle,
|
||||
bottom: distributionSeries(statsAtWindow(pattern, "_24h"), unit),
|
||||
},
|
||||
distributionWindowsTree({ pattern, title: distTitle, unit }),
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: `${title} (Total)`,
|
||||
@@ -708,17 +764,19 @@ export function chartsFromSum({
|
||||
}) {
|
||||
const { stat } = colors;
|
||||
const distTitle = distributionSuffix
|
||||
? `${title} ${distributionSuffix} Distribution`
|
||||
: `${title} Distribution`;
|
||||
? `${title} ${distributionSuffix}`
|
||||
: title;
|
||||
return [
|
||||
{
|
||||
name: "Sum",
|
||||
title,
|
||||
bottom: [{ metric: pattern.sum, title: "sum", color: stat.sum, unit }],
|
||||
},
|
||||
rollingWindowsTree({ windows: pattern.rolling.sum, title, unit }),
|
||||
distributionWindowsTree({ pattern: pattern.rolling, title: distTitle, unit }),
|
||||
{
|
||||
name: "Distribution",
|
||||
title: distTitle,
|
||||
title: `${distTitle} Distribution`,
|
||||
bottom: distributionSeries(pattern, unit),
|
||||
},
|
||||
{
|
||||
@@ -775,21 +833,19 @@ export function chartsFromValueFull({ pattern, title }) {
|
||||
bottom: [
|
||||
...btcSatsUsdSeries({ metrics: pattern.base, name: "sum" }),
|
||||
...btcSatsUsdSeries({
|
||||
metrics: pattern._24h.sum,
|
||||
metrics: pattern.sum._24h,
|
||||
name: "24h sum",
|
||||
defaultActive: false,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Distribution",
|
||||
title: `${title} Distribution`,
|
||||
bottom: distributionBtcSatsUsd(pattern._24h),
|
||||
},
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: `${title} (Total)`,
|
||||
bottom: btcSatsUsdSeries({ metrics: pattern.cumulative, name: "all-time" }),
|
||||
bottom: btcSatsUsdSeries({
|
||||
metrics: pattern.cumulative,
|
||||
name: "all-time",
|
||||
}),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@@ -148,7 +148,7 @@ export function satsBtcUsdBaseline({ pattern, name, color, defaultActive }) {
|
||||
/**
|
||||
* Create sats/btc/usd series from any value pattern using base or cumulative key
|
||||
* @param {Object} args
|
||||
* @param {AnyValuePatternType} args.source
|
||||
* @param {{ base: AnyValuePattern, cumulative: AnyValuePattern }} args.source
|
||||
* @param {'base' | 'cumulative'} args.key
|
||||
* @param {string} args.name
|
||||
* @param {Color} [args.color]
|
||||
@@ -167,7 +167,7 @@ export function satsBtcUsdFrom({ source, key, name, color, defaultActive }) {
|
||||
/**
|
||||
* Create sats/btc/usd series from a full value pattern using base or cumulative key
|
||||
* @param {Object} args
|
||||
* @param {FullValuePattern} args.source
|
||||
* @param {{ base: AnyValuePattern, cumulative: AnyValuePattern }} args.source
|
||||
* @param {'base' | 'cumulative'} args.key
|
||||
* @param {string} args.name
|
||||
* @param {Color} [args.color]
|
||||
@@ -192,9 +192,9 @@ export function satsBtcUsdFromFull({
|
||||
/**
|
||||
* Create coinbase/subsidy/fee series from separate sources
|
||||
* @param {Object} args
|
||||
* @param {AnyValuePatternType} args.coinbase
|
||||
* @param {AnyValuePatternType} args.subsidy
|
||||
* @param {AnyValuePatternType} args.fee
|
||||
* @param {{ base: AnyValuePattern, cumulative: AnyValuePattern }} args.coinbase
|
||||
* @param {{ base: AnyValuePattern, cumulative: AnyValuePattern }} args.subsidy
|
||||
* @param {{ base: AnyValuePattern, cumulative: AnyValuePattern }} args.fee
|
||||
* @param {'base' | 'cumulative'} args.key
|
||||
* @returns {FetchedLineSeriesBlueprint[]}
|
||||
*/
|
||||
@@ -267,13 +267,14 @@ export function revenueRollingBtcSatsUsd({ coinbase, subsidy, fee }) {
|
||||
* @param {AnyRatioPattern} ratio
|
||||
*/
|
||||
export function percentileUsdMap(ratio) {
|
||||
const p = ratio.percentiles;
|
||||
return /** @type {const} */ ([
|
||||
{ name: "pct95", prop: ratio.ratioPct95Usd, color: colors.ratioPct._95 },
|
||||
{ name: "pct5", prop: ratio.ratioPct5Usd, color: colors.ratioPct._5 },
|
||||
{ name: "pct98", prop: ratio.ratioPct98Usd, color: colors.ratioPct._98 },
|
||||
{ name: "pct2", prop: ratio.ratioPct2Usd, color: colors.ratioPct._2 },
|
||||
{ name: "pct99", prop: ratio.ratioPct99Usd, color: colors.ratioPct._99 },
|
||||
{ name: "pct1", prop: ratio.ratioPct1Usd, color: colors.ratioPct._1 },
|
||||
{ name: "pct95", prop: p.pct95.price, color: colors.ratioPct._95 },
|
||||
{ name: "pct5", prop: p.pct5.price, color: colors.ratioPct._5 },
|
||||
{ name: "pct98", prop: p.pct98.price, color: colors.ratioPct._98 },
|
||||
{ name: "pct2", prop: p.pct2.price, color: colors.ratioPct._2 },
|
||||
{ name: "pct99", prop: p.pct99.price, color: colors.ratioPct._99 },
|
||||
{ name: "pct1", prop: p.pct1.price, color: colors.ratioPct._1 },
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -282,13 +283,14 @@ export function percentileUsdMap(ratio) {
|
||||
* @param {AnyRatioPattern} ratio
|
||||
*/
|
||||
export function percentileMap(ratio) {
|
||||
const p = ratio.percentiles;
|
||||
return /** @type {const} */ ([
|
||||
{ name: "pct95", prop: ratio.ratioPct95, color: colors.ratioPct._95 },
|
||||
{ name: "pct5", prop: ratio.ratioPct5, color: colors.ratioPct._5 },
|
||||
{ name: "pct98", prop: ratio.ratioPct98, color: colors.ratioPct._98 },
|
||||
{ name: "pct2", prop: ratio.ratioPct2, color: colors.ratioPct._2 },
|
||||
{ name: "pct99", prop: ratio.ratioPct99, color: colors.ratioPct._99 },
|
||||
{ name: "pct1", prop: ratio.ratioPct1, color: colors.ratioPct._1 },
|
||||
{ name: "pct95", prop: p.pct95.ratio, color: colors.ratioPct._95 },
|
||||
{ name: "pct5", prop: p.pct5.ratio, color: colors.ratioPct._5 },
|
||||
{ name: "pct98", prop: p.pct98.ratio, color: colors.ratioPct._98 },
|
||||
{ name: "pct2", prop: p.pct2.ratio, color: colors.ratioPct._2 },
|
||||
{ name: "pct99", prop: p.pct99.ratio, color: colors.ratioPct._99 },
|
||||
{ name: "pct1", prop: p.pct1.ratio, color: colors.ratioPct._1 },
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -298,10 +300,10 @@ export function percentileMap(ratio) {
|
||||
*/
|
||||
export function sdPatterns(ratio) {
|
||||
return /** @type {const} */ ([
|
||||
{ nameAddon: "All Time", titleAddon: "", sd: ratio.ratioSd },
|
||||
{ nameAddon: "4y", titleAddon: "4y", sd: ratio.ratio4ySd },
|
||||
{ nameAddon: "2y", titleAddon: "2y", sd: ratio.ratio2ySd },
|
||||
{ nameAddon: "1y", titleAddon: "1y", sd: ratio.ratio1ySd },
|
||||
{ nameAddon: "All Time", titleAddon: "", sd: ratio.stdDev.all, smaRatio: ratio.sma.all.ratio },
|
||||
{ nameAddon: "4y", titleAddon: "4y", sd: ratio.stdDev._4y, smaRatio: ratio.sma._4y.ratio },
|
||||
{ nameAddon: "2y", titleAddon: "2y", sd: ratio.stdDev._2y, smaRatio: ratio.sma._2y.ratio },
|
||||
{ nameAddon: "1y", titleAddon: "1y", sd: ratio.stdDev._1y, smaRatio: ratio.sma._1y.ratio },
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -311,41 +313,42 @@ export function sdPatterns(ratio) {
|
||||
*/
|
||||
export function sdBandsUsd(sd) {
|
||||
return /** @type {const} */ ([
|
||||
{ name: "0σ", prop: sd._0sdUsd, color: colors.sd._0 },
|
||||
{ name: "+0.5σ", prop: sd.p05sdUsd, color: colors.sd.p05 },
|
||||
{ name: "−0.5σ", prop: sd.m05sdUsd, color: colors.sd.m05 },
|
||||
{ name: "+1σ", prop: sd.p1sdUsd, color: colors.sd.p1 },
|
||||
{ name: "−1σ", prop: sd.m1sdUsd, color: colors.sd.m1 },
|
||||
{ name: "+1.5σ", prop: sd.p15sdUsd, color: colors.sd.p15 },
|
||||
{ name: "−1.5σ", prop: sd.m15sdUsd, color: colors.sd.m15 },
|
||||
{ name: "+2σ", prop: sd.p2sdUsd, color: colors.sd.p2 },
|
||||
{ name: "−2σ", prop: sd.m2sdUsd, color: colors.sd.m2 },
|
||||
{ name: "+2.5σ", prop: sd.p25sdUsd, color: colors.sd.p25 },
|
||||
{ name: "−2.5σ", prop: sd.m25sdUsd, color: colors.sd.m25 },
|
||||
{ name: "+3σ", prop: sd.p3sdUsd, color: colors.sd.p3 },
|
||||
{ name: "−3σ", prop: sd.m3sdUsd, color: colors.sd.m3 },
|
||||
{ name: "0σ", prop: sd._0sd, color: colors.sd._0 },
|
||||
{ name: "+0.5σ", prop: sd.p05sd.price, color: colors.sd.p05 },
|
||||
{ name: "−0.5σ", prop: sd.m05sd.price, color: colors.sd.m05 },
|
||||
{ name: "+1σ", prop: sd.p1sd.price, color: colors.sd.p1 },
|
||||
{ name: "−1σ", prop: sd.m1sd.price, color: colors.sd.m1 },
|
||||
{ name: "+1.5σ", prop: sd.p15sd.price, color: colors.sd.p15 },
|
||||
{ name: "−1.5σ", prop: sd.m15sd.price, color: colors.sd.m15 },
|
||||
{ name: "+2σ", prop: sd.p2sd.price, color: colors.sd.p2 },
|
||||
{ name: "−2σ", prop: sd.m2sd.price, color: colors.sd.m2 },
|
||||
{ name: "+2.5σ", prop: sd.p25sd.price, color: colors.sd.p25 },
|
||||
{ name: "−2.5σ", prop: sd.m25sd.price, color: colors.sd.m25 },
|
||||
{ name: "+3σ", prop: sd.p3sd.price, color: colors.sd.p3 },
|
||||
{ name: "−3σ", prop: sd.m3sd.price, color: colors.sd.m3 },
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build SD band mappings (ratio) from an SD pattern
|
||||
* @param {Ratio1ySdPattern} sd
|
||||
* @param {AnyMetricPattern} smaRatio
|
||||
*/
|
||||
export function sdBandsRatio(sd) {
|
||||
export function sdBandsRatio(sd, smaRatio) {
|
||||
return /** @type {const} */ ([
|
||||
{ name: "0σ", prop: sd.sma, color: colors.sd._0 },
|
||||
{ name: "+0.5σ", prop: sd.p05sd, color: colors.sd.p05 },
|
||||
{ name: "−0.5σ", prop: sd.m05sd, color: colors.sd.m05 },
|
||||
{ name: "+1σ", prop: sd.p1sd, color: colors.sd.p1 },
|
||||
{ name: "−1σ", prop: sd.m1sd, color: colors.sd.m1 },
|
||||
{ name: "+1.5σ", prop: sd.p15sd, color: colors.sd.p15 },
|
||||
{ name: "−1.5σ", prop: sd.m15sd, color: colors.sd.m15 },
|
||||
{ name: "+2σ", prop: sd.p2sd, color: colors.sd.p2 },
|
||||
{ name: "−2σ", prop: sd.m2sd, color: colors.sd.m2 },
|
||||
{ name: "+2.5σ", prop: sd.p25sd, color: colors.sd.p25 },
|
||||
{ name: "−2.5σ", prop: sd.m25sd, color: colors.sd.m25 },
|
||||
{ name: "+3σ", prop: sd.p3sd, color: colors.sd.p3 },
|
||||
{ name: "−3σ", prop: sd.m3sd, color: colors.sd.m3 },
|
||||
{ name: "0σ", prop: smaRatio, color: colors.sd._0 },
|
||||
{ name: "+0.5σ", prop: sd.p05sd.value, color: colors.sd.p05 },
|
||||
{ name: "−0.5σ", prop: sd.m05sd.value, color: colors.sd.m05 },
|
||||
{ name: "+1σ", prop: sd.p1sd.value, color: colors.sd.p1 },
|
||||
{ name: "−1σ", prop: sd.m1sd.value, color: colors.sd.m1 },
|
||||
{ name: "+1.5σ", prop: sd.p15sd.value, color: colors.sd.p15 },
|
||||
{ name: "−1.5σ", prop: sd.m15sd.value, color: colors.sd.m15 },
|
||||
{ name: "+2σ", prop: sd.p2sd.value, color: colors.sd.p2 },
|
||||
{ name: "−2σ", prop: sd.m2sd.value, color: colors.sd.m2 },
|
||||
{ name: "+2.5σ", prop: sd.p25sd.value, color: colors.sd.p25 },
|
||||
{ name: "−2.5σ", prop: sd.m25sd.value, color: colors.sd.m25 },
|
||||
{ name: "+3σ", prop: sd.p3sd.value, color: colors.sd.p3 },
|
||||
{ name: "−3σ", prop: sd.m3sd.value, color: colors.sd.m3 },
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -355,12 +358,12 @@ export function sdBandsRatio(sd) {
|
||||
*/
|
||||
export function ratioSmas(ratio) {
|
||||
return [
|
||||
{ name: "1w SMA", metric: ratio.ratio1wSma },
|
||||
{ name: "1m SMA", metric: ratio.ratio1mSma },
|
||||
{ name: "1y SMA", metric: ratio.ratio1ySd.sma },
|
||||
{ name: "2y SMA", metric: ratio.ratio2ySd.sma },
|
||||
{ name: "4y SMA", metric: ratio.ratio4ySd.sma },
|
||||
{ name: "All SMA", metric: ratio.ratioSd.sma, color: colors.time.all },
|
||||
{ name: "1w SMA", metric: ratio.sma._1w.ratio },
|
||||
{ name: "1m SMA", metric: ratio.sma._1m.ratio },
|
||||
{ name: "1y SMA", metric: ratio.sma._1y.ratio },
|
||||
{ name: "2y SMA", metric: ratio.sma._2y.ratio },
|
||||
{ name: "4y SMA", metric: ratio.sma._4y.ratio },
|
||||
{ name: "All SMA", metric: ratio.sma.all.ratio, color: colors.time.all },
|
||||
].map((s, i, arr) => ({ color: colors.at(i, arr.length), ...s }));
|
||||
}
|
||||
|
||||
@@ -434,10 +437,10 @@ export function createZScoresFolder({
|
||||
const sdPats = sdPatterns(ratio);
|
||||
|
||||
const zscorePeriods = [
|
||||
{ name: "1y", sd: ratio.ratio1ySd },
|
||||
{ name: "2y", sd: ratio.ratio2ySd },
|
||||
{ name: "4y", sd: ratio.ratio4ySd },
|
||||
{ name: "all", sd: ratio.ratioSd, color: colors.time.all },
|
||||
{ name: "1y", sd: ratio.stdDev._1y },
|
||||
{ name: "2y", sd: ratio.stdDev._2y },
|
||||
{ name: "4y", sd: ratio.stdDev._4y },
|
||||
{ name: "all", sd: ratio.stdDev.all, color: colors.time.all },
|
||||
].map((s, i, arr) => ({ color: colors.at(i, arr.length), ...s }));
|
||||
|
||||
return {
|
||||
@@ -450,7 +453,7 @@ export function createZScoresFolder({
|
||||
price({ metric: pricePattern, name: legend, color }),
|
||||
...zscorePeriods.map((p) =>
|
||||
price({
|
||||
metric: p.sd._0sdUsd,
|
||||
metric: p.sd._0sd,
|
||||
name: `${p.name} 0σ`,
|
||||
color: p.color,
|
||||
defaultActive: false,
|
||||
@@ -473,7 +476,7 @@ export function createZScoresFolder({
|
||||
}),
|
||||
],
|
||||
},
|
||||
...sdPats.map(({ nameAddon, titleAddon, sd }) => {
|
||||
...sdPats.map(({ nameAddon, titleAddon, sd, smaRatio }) => {
|
||||
const prefix = titleAddon ? `${titleAddon} ` : "";
|
||||
const topPrice = price({ metric: pricePattern, name: legend, color });
|
||||
return {
|
||||
@@ -521,7 +524,7 @@ export function createZScoresFolder({
|
||||
unit: Unit.ratio,
|
||||
base: 1,
|
||||
}),
|
||||
...sdBandsRatio(sd).map(
|
||||
...sdBandsRatio(sd, smaRatio).map(
|
||||
({ name: bandName, prop, color: bandColor }) =>
|
||||
line({
|
||||
metric: prop,
|
||||
|
||||
@@ -173,9 +173,11 @@
|
||||
* Patterns with RelToMarketCap in relative (geAmount.*, ltAmount.*):
|
||||
* @typedef {UtxoAmountPattern | AddressAmountPattern} PatternBasicWithMarketCap
|
||||
*
|
||||
* Patterns without RelToMarketCap in relative (RelativePattern4):
|
||||
* - EpochPattern (epoch.*, amountRange.*, year.*, type.*)
|
||||
* @typedef {EpochPattern} PatternBasicWithoutMarketCap
|
||||
* Patterns without RelToMarketCap in relative:
|
||||
* - EpochPattern (epoch.*, year.*)
|
||||
* - UtxoAmountPattern (amountRange.*)
|
||||
* - OutputsRealizedSupplyUnrealizedPattern2 (addressable type.*)
|
||||
* @typedef {EpochPattern | UtxoAmountPattern | Brk.OutputsRealizedSupplyUnrealizedPattern2} PatternBasicWithoutMarketCap
|
||||
*
|
||||
* Patterns without relative section entirely (edge case output types):
|
||||
* - EmptyPattern (type.empty, type.p2ms, type.unknown)
|
||||
@@ -259,8 +261,8 @@
|
||||
* Extended Cohort Types (with address count)
|
||||
* ============================================================================
|
||||
*
|
||||
* Addressable cohort with address count (for "type" cohorts - no RelToMarketCap)
|
||||
* @typedef {CohortBasicWithoutMarketCap & { addressCount: Brk.DeltaInnerPattern }} CohortAddress
|
||||
* Addressable cohort with address count (for "type" cohorts - uses OutputsRealizedSupplyUnrealizedPattern2)
|
||||
* @typedef {{ name: string, title: string, color: Color, tree: Brk.OutputsRealizedSupplyUnrealizedPattern2, addressCount: Brk.DeltaInnerPattern }} CohortAddress
|
||||
*
|
||||
* ============================================================================
|
||||
* Cohort Group Types (by capability)
|
||||
|
||||
@@ -30,6 +30,8 @@ function walkMetrics(node, map, path) {
|
||||
key.endsWith("Cents") ||
|
||||
key.endsWith("State") ||
|
||||
key.endsWith("Start") ||
|
||||
kn === "cents" ||
|
||||
kn === "bps" ||
|
||||
kn === "mvrv" ||
|
||||
kn === "constants" ||
|
||||
kn === "blockhash" ||
|
||||
@@ -144,8 +146,10 @@ export function extractTreeStructure(options) {
|
||||
/** @type {Record<string, string[]>} */
|
||||
const grouped = {};
|
||||
for (const s of series) {
|
||||
const metric = /** @type {any} */ (s.metric);
|
||||
if (isTop && metric?.usd && metric?.sats) {
|
||||
const metric = /** @type {AnyMetricPattern | AnyPricePattern} */ (
|
||||
s.metric
|
||||
);
|
||||
if (isTop && "usd" in metric && "sats" in metric) {
|
||||
const title = s.title || s.key || "unnamed";
|
||||
(grouped["USD"] ??= []).push(title);
|
||||
(grouped["sats"] ??= []).push(title);
|
||||
|
||||
@@ -30,63 +30,63 @@
|
||||
* @typedef {SeriesMarker<Time>} TimeSeriesMarker
|
||||
*
|
||||
* Brk tree types (stable across regenerations)
|
||||
* @typedef {Brk.MetricsTree_Distribution_UtxoCohorts} UtxoCohortTree
|
||||
* @typedef {Brk.MetricsTree_Distribution_AddressCohorts} AddressCohortTree
|
||||
* @typedef {Brk.MetricsTree_Distribution_UtxoCohorts_All} AllUtxoPattern
|
||||
* @typedef {Brk.MetricsTree_Distribution_UtxoCohorts_Sth} ShortTermPattern
|
||||
* @typedef {Brk.ActivityCostOutputsRealizedRelativeSupplyUnrealizedPattern} LongTermPattern
|
||||
* @typedef {Brk.MetricsTree_Distribution_UtxoCohorts_All_Relative} AllRelativePattern
|
||||
* @typedef {keyof Brk.BtcSatsUsdPattern} BtcSatsUsdKey
|
||||
* @typedef {Brk.BtcSatsUsdPattern} SupplyPattern
|
||||
* @typedef {Brk.AverageCumulativeMaxMedianMinPct10Pct25Pct75Pct90SumPattern} BlockSizePattern
|
||||
* @typedef {Brk.MetricsTree_Cohorts_Utxo} UtxoCohortTree
|
||||
* @typedef {Brk.MetricsTree_Cohorts_Address} AddressCohortTree
|
||||
* @typedef {Brk.MetricsTree_Cohorts_Utxo_All} AllUtxoPattern
|
||||
* @typedef {Brk.MetricsTree_Cohorts_Utxo_Sth} ShortTermPattern
|
||||
* @typedef {Brk.MetricsTree_Cohorts_Utxo_Lth} LongTermPattern
|
||||
* @typedef {Brk.MetricsTree_Cohorts_Utxo_All_Unrealized} AllRelativePattern
|
||||
* @typedef {keyof Brk.BtcCentsSatsUsdPattern} BtcSatsUsdKey
|
||||
* @typedef {Brk.BtcCentsSatsUsdPattern} SupplyPattern
|
||||
* @typedef {Brk.AverageBaseCumulativeMaxMedianMinPct10Pct25Pct75Pct90SumPattern} BlockSizePattern
|
||||
* @typedef {keyof Brk.MetricsTree_Cohorts_Utxo_Type} SpendableType
|
||||
* @typedef {keyof Brk.MetricsTree_Addresses_Raw} AddressableType
|
||||
*
|
||||
* Brk pattern types (using new pattern names)
|
||||
* @typedef {Brk.ActivityCostOutputsRealizedRelativeSupplyUnrealizedPattern4} MaxAgePattern
|
||||
* @typedef {Brk.ActivityCostOutputsRealizedRelativeSupplyUnrealizedPattern} AgeRangePattern
|
||||
* @typedef {Brk.ActivityCostOutputsRealizedRelativeSupplyUnrealizedPattern3} UtxoAmountPattern
|
||||
* @typedef {Brk.ActivityAddrCostOutputsRealizedRelativeSupplyUnrealizedPattern} AddressAmountPattern
|
||||
* @typedef {Brk.ActivityCostOutputsRealizedRelativeSupplyUnrealizedPattern4} BasicUtxoPattern
|
||||
* @typedef {Brk.ActivityCostOutputsRealizedRelativeSupplyUnrealizedPattern3} EpochPattern
|
||||
* @typedef {Brk.ActivityCostOutputsRealizedRelativeSupplyUnrealizedPattern3} EmptyPattern
|
||||
* @typedef {Brk._0sdM0M1M1sdM2M2sdM3sdP0P1P1sdP2P2sdP3sdSdSmaZscorePattern} Ratio1ySdPattern
|
||||
* @typedef {Brk.ActivityOutputsRealizedSupplyUnrealizedPattern} MaxAgePattern
|
||||
* @typedef {Brk.ActivityOutputsRealizedSupplyUnrealizedPattern} AgeRangePattern
|
||||
* @typedef {Brk.OutputsRealizedSupplyUnrealizedPattern} UtxoAmountPattern
|
||||
* @typedef {Brk.AddressOutputsRealizedSupplyUnrealizedPattern} AddressAmountPattern
|
||||
* @typedef {Brk.ActivityOutputsRealizedSupplyUnrealizedPattern} BasicUtxoPattern
|
||||
* @typedef {Brk.ActivityOutputsRealizedSupplyUnrealizedPattern} EpochPattern
|
||||
* @typedef {Brk.OutputsRealizedSupplyUnrealizedPattern} EmptyPattern
|
||||
* @typedef {Brk._0sdM0M1M1sdM2M2sdM3sdP0P1P1sdP2P2sdP3sdSdZscorePattern} Ratio1ySdPattern
|
||||
* @typedef {Brk.Dollars} Dollars
|
||||
* CoinbasePattern: base + cumulative + rolling windows (flattened)
|
||||
* @typedef {Brk._1y24h30d7dBaseCumulativePattern} CoinbasePattern
|
||||
* @typedef {Brk.BaseCumulativeSumPattern4} CoinbasePattern
|
||||
* ActivePriceRatioPattern: ratio pattern with price (extended)
|
||||
* @typedef {Brk.PriceRatioPattern} ActivePriceRatioPattern
|
||||
* AnyRatioPattern: full ratio patterns (with or without price) - has ratio, percentiles, z-scores
|
||||
* @typedef {Brk.RatioPattern | Brk.PriceRatioPattern} AnyRatioPattern
|
||||
* @typedef {Brk.BpsPriceRatioPattern} ActivePriceRatioPattern
|
||||
* AnyRatioPattern: full ratio pattern with percentiles, SMAs, and std dev bands
|
||||
* @typedef {Brk.BpsCentsPercentilesRatioSatsSmaStdUsdPattern} AnyRatioPattern
|
||||
* ValuePattern: patterns with base + cumulative (no rolling)
|
||||
* @typedef {Brk.BaseCumulativeSumPattern<number> | Brk.BaseCumulativePattern} ValuePattern
|
||||
* @typedef {Brk.BaseCumulativeSumPattern<number> | Brk.BaseCumulativeRelPattern} ValuePattern
|
||||
* FullValuePattern: base + cumulative + rolling windows (flattened)
|
||||
* @typedef {Brk._1y24h30d7dBaseCumulativePattern} FullValuePattern
|
||||
* RollingWindowSlot: a single rolling window with stats (average, pct10, pct25, median, pct75, pct90, max, min, sum) per unit
|
||||
* @typedef {Brk.AverageMaxMedianMinPct10Pct25Pct75Pct90SumPattern2} RollingWindowSlot
|
||||
* @typedef {Brk.BaseCumulativeSumPattern4} FullValuePattern
|
||||
* RollingWindowSlot: a single rolling window with stats (average, pct10, pct25, median, pct75, pct90, max, min) per unit
|
||||
* @typedef {Brk.AverageMaxMedianMinPct10Pct25Pct75Pct90Pattern} RollingWindowSlot
|
||||
* AnyValuePatternType: union of all value pattern types
|
||||
* @typedef {Brk._1y24h30d7dBaseCumulativePattern | Brk.BaseCumulativeSumPattern<number> | Brk.BaseCumulativePattern} AnyValuePatternType
|
||||
* @typedef {Brk.BaseCumulativeSumPattern4 | Brk.BaseCumulativeSumPattern<number> | Brk.BaseCumulativeRelPattern} AnyValuePatternType
|
||||
* @typedef {Brk.AnyMetricPattern} AnyMetricPattern
|
||||
* @typedef {Brk.SatsUsdPattern} ActivePricePattern
|
||||
* @typedef {Brk.CentsSatsUsdPattern} ActivePricePattern
|
||||
* @typedef {Brk.AnyMetricEndpointBuilder} AnyMetricEndpoint
|
||||
* @typedef {Brk.AnyMetricData} AnyMetricData
|
||||
* @typedef {Brk.AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern} AddrCountPattern
|
||||
* @typedef {Brk.AllP2aP2pk33P2pk65P2pkhP2shP2trP2wpkhP2wshPattern3} AddrCountPattern
|
||||
* Relative patterns by capability:
|
||||
* - BasicRelativePattern: minimal relative (investedCapitalIn*Pct, supplyIn*RelToOwnSupply only)
|
||||
* - GlobalRelativePattern: has RelToMarketCap metrics (netUnrealizedPnlRelToMarketCap, etc)
|
||||
* - OwnRelativePattern: has RelToOwnMarketCap metrics (netUnrealizedPnlRelToOwnMarketCap, etc)
|
||||
* - FullRelativePattern: has BOTH RelToMarketCap AND RelToOwnMarketCap
|
||||
* @typedef {Brk.InvestedNegNetNuplSupplyUnrealizedPattern} BasicRelativePattern
|
||||
* @typedef {Brk.InvestedNegNetNuplSupplyUnrealizedPattern} GlobalRelativePattern
|
||||
* @typedef {Brk.InvestedNegNetNuplSupplyUnrealizedPattern2} OwnRelativePattern
|
||||
* @typedef {Brk.InvestedNegNetNuplSupplyUnrealizedPattern2} FullRelativePattern
|
||||
* @typedef {Brk.GreedInvestedInvestorNegNetPainSupplyTotalUnrealizedPattern} UnrealizedPattern
|
||||
* @typedef {Brk.LossNetNuplProfitPattern} BasicRelativePattern
|
||||
* @typedef {Brk.LossNetNuplProfitPattern} GlobalRelativePattern
|
||||
* @typedef {Brk.GrossInvestedLossNetNuplProfitSentimentPattern2} OwnRelativePattern
|
||||
* @typedef {Brk.GrossInvestedLossNetNuplProfitSentimentPattern2} FullRelativePattern
|
||||
* @typedef {Brk.GrossInvestedLossNetNuplProfitSentimentPattern2} UnrealizedPattern
|
||||
*
|
||||
* Realized patterns
|
||||
* @typedef {Brk.CapCapitulationInvestorLossLowerMvrvNegNetPeakProfitRealizedSellSentSoprTotalUpperValuePattern} RealizedPattern
|
||||
* @typedef {Brk.CapCapitulationInvestorLossLowerMvrvNegNetPeakProfitRealizedSellSentSoprTotalUpperValuePattern2} RealizedPattern2
|
||||
* @typedef {Brk.AdjustedCapCapitulationInvestorLossLowerMvrvNegNetPeakProfitRealizedSellSentSoprTotalUpperValuePattern} RealizedPattern3
|
||||
* @typedef {Brk.AdjustedCapCapitulationInvestorLossLowerMvrvNegNetPeakProfitRealizedSellSentSoprTotalUpperValuePattern2} RealizedPattern4
|
||||
* @typedef {Brk.CapGrossInvestorLossMvrvNetPeakPriceProfitSellSoprPattern} RealizedPattern
|
||||
* @typedef {Brk.CapGrossInvestorLossMvrvNetPeakPriceProfitSellSoprPattern} RealizedPattern2
|
||||
* @typedef {Brk.CapGrossInvestorLossMvrvNetPeakPriceProfitSellSoprPattern} RealizedPattern3
|
||||
* @typedef {Brk.CapGrossInvestorLossMvrvNetPeakPriceProfitSellSoprPattern} RealizedPattern4
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -98,9 +98,8 @@
|
||||
* @typedef {Brk.AverageMaxMedianMinPct10Pct25Pct75Pct90Pattern} StatsPattern
|
||||
*/
|
||||
/**
|
||||
* Base stats pattern: height, average, min, max, percentiles (windowed, NO sum/cumulative)
|
||||
* @template T
|
||||
* @typedef {Brk.AverageHeightMaxMedianMinPct10Pct25Pct75Pct90Pattern<T>} BaseStatsPattern
|
||||
* Base stats pattern: average, min, max, percentiles
|
||||
* @typedef {Brk.AverageMaxMedianMinPct10Pct25Pct75Pct90Pattern} BaseStatsPattern
|
||||
*/
|
||||
/**
|
||||
* Full stats pattern: cumulative, sum, average, min, max, percentiles + rolling
|
||||
@@ -117,11 +116,11 @@
|
||||
/**
|
||||
* Count pattern: height, cumulative, and rolling sum windows
|
||||
* @template T
|
||||
* @typedef {Brk.CumulativeHeightSumPattern<T>} CountPattern
|
||||
* @typedef {Brk.BaseCumulativeSumPattern<T>} CountPattern
|
||||
*/
|
||||
/**
|
||||
* Full per-block pattern: height, cumulative, sum, and distribution stats (all flat)
|
||||
* @typedef {Brk.AverageCumulativeHeightMaxMedianMinPct10Pct25Pct75Pct90SumPattern} FullPerBlockPattern
|
||||
* @typedef {Brk.AverageBaseCumulativeMaxMedianMinPct10Pct25Pct75Pct90SumPattern} FullPerBlockPattern
|
||||
*/
|
||||
/**
|
||||
* Any stats pattern union - patterns with sum/cumulative + percentiles
|
||||
|
||||
Reference in New Issue
Block a user