global: snapshot part 17

This commit is contained in:
nym21
2026-03-21 19:41:41 +01:00
parent 2991562234
commit 8859de5393
35 changed files with 567 additions and 360 deletions

View File

@@ -285,16 +285,16 @@ export function createCointimeSection() {
sum: pattern.sum,
cumulative: pattern.cumulative,
})),
title: "Coinblocks",
metric: "Coinblocks",
unit: Unit.coinblocks,
}),
...coinblocks.map(({ pattern, name, title, color }) => ({
...coinblocks.map(({ pattern, name, title: metric, color }) => ({
name,
tree: sumsAndAveragesCumulative({
sum: pattern.sum,
average: pattern.average,
cumulative: pattern.cumulative,
title,
metric,
unit: Unit.coinblocks,
color,
}),
@@ -322,16 +322,16 @@ export function createCointimeSection() {
cumulative: vocdd.pattern.cumulative,
},
],
title: "Cointime Value",
metric: "Cointime Value",
unit: Unit.usd,
}),
...cointimeValues.map(({ pattern, name, title, color }) => ({
...cointimeValues.map(({ pattern, name, title: metric, color }) => ({
name,
tree: sumsAndAveragesCumulative({
sum: pattern.sum,
average: pattern.average,
cumulative: pattern.cumulative,
title,
metric,
unit: Unit.usd,
color,
}),
@@ -342,7 +342,7 @@ export function createCointimeSection() {
sum: vocdd.pattern.sum,
average: vocdd.pattern.average,
cumulative: vocdd.pattern.cumulative,
title: vocdd.title,
metric: vocdd.title,
unit: Unit.usd,
color: vocdd.color,
}),

View File

@@ -41,7 +41,8 @@ function volumeTree(tv, color, title) {
return [
...satsBtcUsdFullTree({
pattern: tv,
title: title("Transfer Volume"),
title,
metric: "Transfer Volume",
color,
}),
{
@@ -83,7 +84,8 @@ function volumeTree(tv, color, title) {
name: "In Profit",
tree: satsBtcUsdFullTree({
pattern: tv.inProfit,
title: title("Transfer Volume In Profit"),
title,
metric: "Transfer Volume In Profit",
color: colors.profit,
}),
},
@@ -91,7 +93,8 @@ function volumeTree(tv, color, title) {
name: "In Loss",
tree: satsBtcUsdFullTree({
pattern: tv.inLoss,
title: title("Transfer Volume In Loss"),
title,
metric: "Transfer Volume In Loss",
color: colors.loss,
}),
},
@@ -122,7 +125,7 @@ function volumeFolderWithAdjusted(activity, adjustedTransferVolume, color, title
name: "Volume",
tree: [
...volumeTree(activity.transferVolume, color, title),
{ name: "Adjusted", tree: chartsFromCount({ pattern: adjustedTransferVolume, title: title("Adjusted Transfer Volume"), unit: Unit.usd }) },
{ name: "Adjusted", tree: chartsFromCount({ pattern: adjustedTransferVolume, title, metric: "Adjusted Transfer Volume", unit: Unit.usd }) },
],
};
}
@@ -173,7 +176,7 @@ function singleRollingSoprTree(ratio, title, prefix = "") {
* @returns {PartialOptionsTree}
*/
function valueDestroyedTree(valueDestroyed, title) {
return chartsFromCount({ pattern: valueDestroyed, title: title("Value Destroyed"), unit: Unit.usd });
return chartsFromCount({ pattern: valueDestroyed, title, metric: "Value Destroyed", unit: Unit.usd });
}
/**
@@ -196,7 +199,7 @@ function valueDestroyedFolderWithAdjusted(valueDestroyed, adjusted, title) {
name: "Value Destroyed",
tree: [
...valueDestroyedTree(valueDestroyed, title),
{ name: "Adjusted", tree: chartsFromCount({ pattern: adjusted, title: title("Adjusted Value Destroyed"), unit: Unit.usd }) },
{ name: "Adjusted", tree: chartsFromCount({ pattern: adjusted, title, metric: "Adjusted Value Destroyed", unit: Unit.usd }) },
],
};
}
@@ -258,7 +261,8 @@ function singleFullActivityTree(cohort, title, volumeItem, soprFolder, valueDest
name: "Coindays Destroyed",
tree: chartsFromCount({
pattern: tree.activity.coindaysDestroyed,
title: title("Coindays Destroyed"),
title,
metric: "Coindays Destroyed",
unit: Unit.coindays,
color,
}),
@@ -267,7 +271,8 @@ function singleFullActivityTree(cohort, title, volumeItem, soprFolder, valueDest
name: "Dormancy",
tree: averagesArray({
windows: tree.activity.dormancy,
title: title("Dormancy"),
title,
metric: "Dormancy",
unit: Unit.days,
}),
},
@@ -341,7 +346,8 @@ export function createActivitySectionWithActivity({ cohort, title }) {
name: "Coindays Destroyed",
tree: chartsFromCount({
pattern: tree.activity.coindaysDestroyed,
title: title("Coindays Destroyed"),
title,
metric: "Coindays Destroyed",
unit: Unit.coindays,
color,
}),
@@ -360,7 +366,8 @@ export function createActivitySectionMinimal({ cohort, title }) {
name: "Activity",
tree: satsBtcUsdFullTree({
pattern: cohort.tree.activity.transferVolume,
title: title("Transfer Volume"),
title,
metric: "Transfer Volume",
}),
};
}

View File

@@ -52,12 +52,12 @@ function groupedUtxoCountFolder(list, all, title) {
tree: [
{
name: "Count",
title: title("UTXOs"),
title: title("UTXO Count"),
bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
line({ series: tree.outputs.unspentCount.base, name, color, unit: Unit.count }),
),
},
...groupedDeltaItems(list, all, (c) => c.tree.outputs.unspentCount.delta, Unit.count, title, "UTXOs"),
...groupedDeltaItems(list, all, (c) => c.tree.outputs.unspentCount.delta, Unit.count, title, "UTXO Count"),
],
};
}
@@ -74,7 +74,8 @@ function singleDeltaItems(delta, unit, title, name) {
{
...sumsTreeBaseline({
windows: delta.absolute,
title: title(`${name} Change`),
title,
metric: `${name} Change`,
unit,
}),
name: "Change",
@@ -82,7 +83,8 @@ function singleDeltaItems(delta, unit, title, name) {
{
...rollingPercentRatioTree({
windows: delta.rate,
title: title(`${name} Growth Rate`),
title,
metric: `${name} Growth Rate`,
}),
name: "Growth Rate",
},
@@ -233,7 +235,7 @@ function countFolder(pattern, name, chartTitle, color, title) {
}),
],
},
...singleDeltaItems(pattern.delta, Unit.count, title, "Change"),
...singleDeltaItems(pattern.delta, Unit.count, title, chartTitle),
],
};
}
@@ -257,7 +259,7 @@ export function createHoldingsSection({ cohort, title }) {
title: title("Supply"),
bottom: simpleSupplySeries(supply),
},
...singleDeltaItems(supply.delta, Unit.sats, title, "Change"),
...singleDeltaItems(supply.delta, Unit.sats, title, "Supply"),
],
},
countFolder(cohort.tree.outputs.unspentCount, "UTXOs", "UTXO Count", cohort.color, title),
@@ -281,7 +283,7 @@ export function createHoldingsSectionAll({ cohort, title }) {
},
profitabilityChart(supply, title),
ownSupplyChart(supply, title),
...singleDeltaItems(supply.delta, Unit.sats, title, "Change"),
...singleDeltaItems(supply.delta, Unit.sats, title, "Supply"),
],
},
countFolder(cohort.tree.outputs.unspentCount, "UTXOs", "UTXO Count", cohort.color, title),
@@ -307,7 +309,7 @@ export function createHoldingsSectionWithRelative({ cohort, title }) {
profitabilityChart(supply, title),
circulatingChart(supply, title),
ownSupplyChart(supply, title),
...singleDeltaItems(supply.delta, Unit.sats, title, "Change"),
...singleDeltaItems(supply.delta, Unit.sats, title, "Supply"),
],
},
countFolder(cohort.tree.outputs.unspentCount, "UTXOs", "UTXO Count", cohort.color, title),
@@ -331,7 +333,7 @@ export function createHoldingsSectionWithOwnSupply({ cohort, title }) {
},
profitabilityChart(supply, title),
circulatingChart(supply, title),
...singleDeltaItems(supply.delta, Unit.sats, title, "Change"),
...singleDeltaItems(supply.delta, Unit.sats, title, "Supply"),
],
},
countFolder(cohort.tree.outputs.unspentCount, "UTXOs", "UTXO Count", cohort.color, title),
@@ -354,7 +356,7 @@ export function createHoldingsSectionWithProfitLoss({ cohort, title }) {
bottom: simpleSupplySeries(supply),
},
profitabilityChart(supply, title),
...singleDeltaItems(supply.delta, Unit.sats, title, "Change"),
...singleDeltaItems(supply.delta, Unit.sats, title, "Supply"),
],
},
countFolder(cohort.tree.outputs.unspentCount, "UTXOs", "UTXO Count", cohort.color, title),
@@ -377,7 +379,7 @@ export function createHoldingsSectionAddress({ cohort, title }) {
bottom: simpleSupplySeries(supply),
},
profitabilityChart(supply, title),
...singleDeltaItems(supply.delta, Unit.sats, title, "Change"),
...singleDeltaItems(supply.delta, Unit.sats, title, "Supply"),
],
},
countFolder(cohort.tree.outputs.unspentCount, "UTXOs", "UTXO Count", cohort.color, title),
@@ -400,7 +402,7 @@ export function createHoldingsSectionAddressAmount({ cohort, title }) {
title: title("Supply"),
bottom: simpleSupplySeries(supply),
},
...singleDeltaItems(supply.delta, Unit.sats, title, "Change"),
...singleDeltaItems(supply.delta, Unit.sats, title, "Supply"),
],
},
countFolder(cohort.tree.outputs.unspentCount, "UTXOs", "UTXO Count", cohort.color, title),
@@ -461,12 +463,12 @@ export function createGroupedHoldingsSectionAddress({ list, all, title }) {
tree: [
{
name: "Count",
title: title("Addresses"),
title: title("Address Count"),
bottom: mapCohortsWithAll(list, all, ({ name, color, addressCount }) =>
line({ series: addressCount.base, name, color, unit: Unit.count }),
),
},
...groupedDeltaItems(list, all, (c) => c.addressCount.delta, Unit.count, title, "Addresses"),
...groupedDeltaItems(list, all, (c) => c.addressCount.delta, Unit.count, title, "Address Count"),
],
},
];
@@ -492,12 +494,12 @@ export function createGroupedHoldingsSectionAddressAmount({ list, all, title })
tree: [
{
name: "Count",
title: title("Addresses"),
title: title("Address Count"),
bottom: mapCohortsWithAll(list, all, ({ name, color, addressCount }) =>
line({ series: addressCount.base, name, color, unit: Unit.count }),
),
},
...groupedDeltaItems(list, all, (c) => c.addressCount.delta, Unit.count, title, "Addresses"),
...groupedDeltaItems(list, all, (c) => c.addressCount.delta, Unit.count, title, "Address Count"),
],
},
];

View File

@@ -216,7 +216,8 @@ export function createCohortFolderAgeRangeWithMatured(cohort) {
name: "Matured",
tree: satsBtcUsdFullTree({
pattern: cohort.matured,
title: title("Matured Supply"),
title,
metric: "Matured Supply",
}),
});
return folder;
@@ -498,6 +499,7 @@ export function createGroupedAddressCohortFolder({
* @returns {PartialOptionsGroup}
*/
function singleBucketFolder({ name, color, pattern }) {
const title = formatCohortTitle(name);
return {
name,
tree: [
@@ -506,7 +508,7 @@ function singleBucketFolder({ name, color, pattern }) {
tree: [
{
name: "Value",
title: `${name}: Supply`,
title: title("Supply"),
bottom: [
...satsBtcUsd({ pattern: pattern.supply.all, name: "Total" }),
...satsBtcUsd({
@@ -522,7 +524,8 @@ function singleBucketFolder({ name, color, pattern }) {
{
...sumsTreeBaseline({
windows: pattern.supply.all.delta.absolute,
title: `${name}: Supply Change`,
title,
metric: "Supply Change",
unit: Unit.sats,
}),
name: "Change",
@@ -530,7 +533,8 @@ function singleBucketFolder({ name, color, pattern }) {
{
...rollingPercentRatioTree({
windows: pattern.supply.all.delta.rate,
title: `${name}: Supply Rate`,
title,
metric: "Supply Growth Rate",
}),
name: "Growth Rate",
},
@@ -540,7 +544,7 @@ function singleBucketFolder({ name, color, pattern }) {
},
{
name: "Realized Cap",
title: `${name}: Realized Cap`,
title: title("Realized Cap"),
bottom: [
line({
series: pattern.realizedCap.all,
@@ -557,7 +561,7 @@ function singleBucketFolder({ name, color, pattern }) {
},
{
name: "NUPL",
title: `${name}: NUPL`,
title: title("NUPL"),
bottom: [
line({ series: pattern.nupl.ratio, name, color, unit: Unit.ratio }),
],
@@ -568,24 +572,25 @@ function singleBucketFolder({ name, color, pattern }) {
/**
* @param {{ name: string, color: Color, pattern: RealizedSupplyPattern }[]} list
* @param {string} titlePrefix
* @param {string} groupTitle
* @returns {PartialOptionsTree}
*/
function groupedBucketCharts(list, titlePrefix) {
function groupedBucketCharts(list, groupTitle) {
const title = formatCohortTitle(groupTitle);
return [
{
name: "Supply",
tree: [
{
name: "All",
title: `${titlePrefix}: Supply`,
title: title("Supply"),
bottom: list.flatMap(({ name, color, pattern }) =>
satsBtcUsd({ pattern: pattern.supply.all, name, color }),
),
},
{
name: "STH",
title: `${titlePrefix}: STH Supply`,
title: title("STH Supply"),
bottom: list.flatMap(({ name, color, pattern }) =>
satsBtcUsd({ pattern: pattern.supply.sth, name, color }),
),
@@ -598,7 +603,7 @@ function groupedBucketCharts(list, titlePrefix) {
tree: [
{
name: "Compare",
title: `${titlePrefix}: Supply Change`,
title: title("Supply Change"),
bottom: ROLLING_WINDOWS.flatMap((w) =>
list.map(({ name, color, pattern }) =>
baseline({
@@ -612,7 +617,7 @@ function groupedBucketCharts(list, titlePrefix) {
},
...ROLLING_WINDOWS.map((w) => ({
name: w.name,
title: `${titlePrefix}: Supply Change (${w.title})`,
title: title(`Supply Change (${w.title})`),
bottom: list.map(({ name, color, pattern }) =>
baseline({
series: pattern.supply.all.delta.absolute[w.key],
@@ -629,7 +634,7 @@ function groupedBucketCharts(list, titlePrefix) {
tree: [
{
name: "Compare",
title: `${titlePrefix}: Supply Rate`,
title: title("Supply Growth Rate"),
bottom: ROLLING_WINDOWS.flatMap((w) =>
list.flatMap(({ name, color, pattern }) =>
percentRatio({
@@ -642,7 +647,7 @@ function groupedBucketCharts(list, titlePrefix) {
},
...ROLLING_WINDOWS.map((w) => ({
name: w.name,
title: `${titlePrefix}: Supply Rate (${w.title})`,
title: title(`Supply Growth Rate (${w.title})`),
bottom: list.flatMap(({ name, color, pattern }) =>
percentRatio({
pattern: pattern.supply.all.delta.rate[w.key],
@@ -662,7 +667,7 @@ function groupedBucketCharts(list, titlePrefix) {
tree: [
{
name: "All",
title: `${titlePrefix}: Realized Cap`,
title: title("Realized Cap"),
bottom: list.map(({ name, color, pattern }) =>
line({
series: pattern.realizedCap.all,
@@ -674,7 +679,7 @@ function groupedBucketCharts(list, titlePrefix) {
},
{
name: "STH",
title: `${titlePrefix}: STH Realized Cap`,
title: title("STH Realized Cap"),
bottom: list.map(({ name, color, pattern }) =>
line({
series: pattern.realizedCap.sth,
@@ -688,7 +693,7 @@ function groupedBucketCharts(list, titlePrefix) {
},
{
name: "NUPL",
title: `${titlePrefix}: NUPL`,
title: title("NUPL"),
bottom: list.map(({ name, color, pattern }) =>
line({ series: pattern.nupl.ratio, name, color, unit: Unit.ratio }),
),

View File

@@ -389,7 +389,8 @@ function realizedNetFolder({ netPnl, title, extraChange = [] }) {
{
...sumsTreeBaseline({
windows: mapWindows(netPnl.delta.absolute, (c) => c.usd),
title: title("Net Realized P&L Change"),
title,
metric: "Net Realized P&L Change",
unit: Unit.usd,
}),
name: "Change",
@@ -399,7 +400,8 @@ function realizedNetFolder({ netPnl, title, extraChange = [] }) {
tree: [
...rollingPercentRatioTree({
windows: netPnl.delta.rate,
title: title("Net Realized P&L Growth Rate"),
title,
metric: "Net Realized P&L Growth Rate",
}).tree,
...extraChange,
],

View File

@@ -19,8 +19,8 @@ import { ratioBottomSeries, mapCohortsWithAll, flatMapCohortsWithAll } from "../
*/
function singleDeltaItems(tree, title) {
return [
{ ...sumsTreeBaseline({ windows: mapWindows(tree.realized.cap.delta.absolute, (c) => c.usd), title: title("Realized Cap Change"), unit: Unit.usd }), name: "Change" },
{ ...rollingPercentRatioTree({ windows: tree.realized.cap.delta.rate, title: title("Realized Cap Growth Rate") }), name: "Growth Rate" },
{ ...sumsTreeBaseline({ windows: mapWindows(tree.realized.cap.delta.absolute, (c) => c.usd), title, metric: "Realized Cap Change", unit: Unit.usd }), name: "Change" },
{ ...rollingPercentRatioTree({ windows: tree.realized.cap.delta.rate, title, metric: "Realized Cap Growth Rate" }), name: "Growth Rate" },
];
}

View File

@@ -46,8 +46,20 @@ import { periodIdToName } from "./utils.js";
*/
function indexRatio({ pattern, name, color, defaultActive }) {
return [
line({ series: pattern.percent, name, color, defaultActive, unit: Unit.index }),
line({ series: pattern.ratio, name, color, defaultActive, unit: Unit.ratio }),
line({
series: pattern.percent,
name,
color,
defaultActive,
unit: Unit.index,
}),
line({
series: pattern.ratio,
name,
color,
defaultActive,
unit: Unit.ratio,
}),
];
}
@@ -530,7 +542,7 @@ export function createMarketSection() {
},
...deltaTree({
delta: supply.marketCap.delta,
title: "Market Cap",
metric: "Market Cap",
unit: Unit.usd,
extract: (v) => v.usd,
}),
@@ -553,7 +565,7 @@ export function createMarketSection() {
},
...deltaTree({
delta: cohorts.utxo.all.realized.cap.delta,
title: "Realized Cap",
metric: "Realized Cap",
unit: Unit.usd,
extract: (v) => v.usd,
}),
@@ -650,7 +662,11 @@ export function createMarketSection() {
title: "RSI Comparison",
bottom: [
...ROLLING_WINDOWS_TO_1M.flatMap((w) =>
indexRatio({ pattern: technical.rsi[w.key].rsi, name: w.name, color: w.color }),
indexRatio({
pattern: technical.rsi[w.key].rsi,
name: w.name,
color: w.color,
}),
),
priceLine({ unit: Unit.index, number: 70 }),
priceLine({ unit: Unit.index, number: 30 }),
@@ -662,9 +678,17 @@ export function createMarketSection() {
name: w.name,
title: `RSI (${w.title})`,
bottom: [
...indexRatio({ pattern: rsi.rsi, name: "RSI", color: colors.indicator.main }),
...indexRatio({
pattern: rsi.rsi,
name: "RSI",
color: colors.indicator.main,
}),
priceLine({ unit: Unit.index, number: 70 }),
priceLine({ unit: Unit.index, number: 50, defaultActive: false }),
priceLine({
unit: Unit.index,
number: 50,
defaultActive: false,
}),
priceLine({ unit: Unit.index, number: 30 }),
],
};
@@ -672,17 +696,28 @@ export function createMarketSection() {
{
name: "Stochastic",
tree: ROLLING_WINDOWS_TO_1M.map((w) => {
const rsi = technical.rsi[w.key];
return {
name: w.name,
title: `Stochastic RSI (${w.title})`,
bottom: [
...indexRatio({ pattern: rsi.stochRsiK, name: "K", color: colors.indicator.fast }),
...indexRatio({ pattern: rsi.stochRsiD, name: "D", color: colors.indicator.slow }),
...priceLines({ unit: Unit.index, numbers: [80, 20] }),
],
};
}),
const rsi = technical.rsi[w.key];
return {
name: w.name,
title: `Stochastic RSI (${w.title})`,
bottom: [
...indexRatio({
pattern: rsi.stochRsiK,
name: "K",
color: colors.indicator.fast,
}),
...indexRatio({
pattern: rsi.stochRsiD,
name: "D",
color: colors.indicator.slow,
}),
...priceLines({
unit: Unit.index,
numbers: [80, 20],
}),
],
};
}),
},
],
},
@@ -693,16 +728,35 @@ export function createMarketSection() {
name: "Compare",
title: "MACD Comparison",
bottom: ROLLING_WINDOWS_TO_1M.map((w) =>
line({ series: technical.macd[w.key].line, name: w.name, color: w.color, unit: Unit.usd }),
line({
series: technical.macd[w.key].line,
name: w.name,
color: w.color,
unit: Unit.usd,
}),
),
},
...ROLLING_WINDOWS_TO_1M.map((w) => ({
name: w.name,
title: `MACD (${w.title})`,
bottom: [
line({ series: technical.macd[w.key].line, name: "MACD", color: colors.indicator.fast, unit: Unit.usd }),
line({ series: technical.macd[w.key].signal, name: "Signal", color: colors.indicator.slow, unit: Unit.usd }),
histogram({ series: technical.macd[w.key].histogram, name: "Histogram", unit: Unit.usd }),
line({
series: technical.macd[w.key].line,
name: "MACD",
color: colors.indicator.fast,
unit: Unit.usd,
}),
line({
series: technical.macd[w.key].signal,
name: "Signal",
color: colors.indicator.slow,
unit: Unit.usd,
}),
histogram({
series: technical.macd[w.key].histogram,
name: "Histogram",
unit: Unit.usd,
}),
],
})),
],
@@ -721,13 +775,25 @@ export function createMarketSection() {
name: "Compare",
title: "Volatility Index",
bottom: ROLLING_WINDOWS.map((w) =>
line({ series: volatility[w.key], name: w.name, color: w.color, unit: Unit.percentage }),
line({
series: volatility[w.key],
name: w.name,
color: w.color,
unit: Unit.percentage,
}),
),
},
...ROLLING_WINDOWS.map((w) => ({
name: w.name,
title: `Volatility Index (${w.title})`,
bottom: [line({ series: volatility[w.key], name: w.name, color: w.color, unit: Unit.percentage })],
bottom: [
line({
series: volatility[w.key],
name: w.name,
color: w.color,
unit: Unit.percentage,
}),
],
})),
],
},

View File

@@ -19,6 +19,7 @@ import {
satsBtcUsdFullTree,
revenueBtcSatsUsd,
revenueRollingBtcSatsUsd,
formatCohortTitle,
} from "./shared.js";
import { brk } from "../client.js";
@@ -76,13 +77,17 @@ export function createMiningSection() {
includes(ANTPOOL_AND_FRIENDS_IDS, p.id),
);
/** @param {string} title @param {{ _24h: any, _1w: any, _1m: any, _1y: any, percent: any, ratio: any }} dominance */
const dominanceTree = (title, dominance) => ({
/**
* @param {(metric: string) => string} title
* @param {string} metric
* @param {{ _24h: any, _1w: any, _1m: any, _1y: any, percent: any, ratio: any }} dominance
*/
const dominanceTree = (title, metric, dominance) => ({
name: "Dominance",
tree: [
{
name: "Compare",
title,
title: title(metric),
bottom: [
...ROLLING_WINDOWS.flatMap((w) =>
percentRatio({ pattern: dominance[w.key], name: w.name, color: w.color, defaultActive: w.key !== "_24h" }),
@@ -92,12 +97,12 @@ export function createMiningSection() {
},
...ROLLING_WINDOWS.map((w) => ({
name: w.name,
title: `${title} ${w.title}`,
title: title(`${w.name} ${metric}`),
bottom: percentRatio({ pattern: dominance[w.key], name: w.name, color: w.color }),
})),
{
name: "All Time",
title: `${title} All Time`,
title: title(`${metric} All Time`),
bottom: percentRatio({ pattern: dominance, name: "All Time", color: colors.time.all }),
},
],
@@ -107,50 +112,59 @@ export function createMiningSection() {
* @param {typeof majorPoolData} poolList
*/
const createPoolTree = (poolList) =>
poolList.map(({ name, pool }) => ({
name,
tree: [
dominanceTree(`Dominance: ${name}`, pool.dominance),
{
name: "Blocks Mined",
tree: chartsFromCount({
pattern: pool.blocksMined,
title: `Blocks Mined: ${name}`,
unit: Unit.count,
}),
},
{
name: "Rewards",
tree: satsBtcUsdFullTree({
pattern: pool.rewards,
title: `Rewards: ${name}`,
}),
},
],
}));
poolList.map(({ name, pool }) => {
const title = formatCohortTitle(name);
return {
name,
tree: [
dominanceTree(title, "Dominance", pool.dominance),
{
name: "Blocks Mined",
tree: chartsFromCount({
pattern: pool.blocksMined,
title,
metric: "Blocks Mined",
unit: Unit.count,
}),
},
{
name: "Rewards",
tree: satsBtcUsdFullTree({
pattern: pool.rewards,
title,
metric: "Rewards",
}),
},
],
};
});
/**
* @param {typeof minorPoolData} poolList
*/
const createMinorPoolTree = (poolList) =>
poolList.map(({ name, pool }) => ({
name,
tree: [
{
name: "Dominance",
title: `Dominance: ${name}`,
bottom: percentRatio({ pattern: pool.dominance, name: "All Time", color: colors.time.all }),
},
{
name: "Blocks Mined",
tree: chartsFromCount({
pattern: pool.blocksMined,
title: `Blocks Mined: ${name}`,
unit: Unit.count,
}),
},
],
}));
poolList.map(({ name, pool }) => {
const title = formatCohortTitle(name);
return {
name,
tree: [
{
name: "Dominance",
title: title("Dominance"),
bottom: percentRatio({ pattern: pool.dominance, name: "All Time", color: colors.time.all }),
},
{
name: "Blocks Mined",
tree: chartsFromCount({
pattern: pool.blocksMined,
title,
metric: "Blocks Mined",
unit: Unit.count,
}),
},
],
};
});
/**
* @param {string} groupTitle
@@ -306,14 +320,14 @@ export function createMiningSection() {
name: "Coinbase",
tree: satsBtcUsdFullTree({
pattern: mining.rewards.coinbase,
title: "Coinbase Rewards",
metric: "Coinbase Rewards",
}),
},
{
name: "Subsidy",
tree: satsBtcUsdFullTree({
pattern: mining.rewards.subsidy,
title: "Block Subsidy",
metric: "Block Subsidy",
}),
},
{
@@ -321,7 +335,7 @@ export function createMiningSection() {
tree: [
...satsBtcUsdFullTree({
pattern: mining.rewards.fees,
title: "Transaction Fee Revenue",
metric: "Transaction Fee Revenue",
}),
{
name: "Distributions",

View File

@@ -53,7 +53,12 @@ export function createNetworkSection() {
// Non-addressable script types
const nonAddressableTypes = /** @type {const} */ ([
{ key: "p2ms", name: "P2MS", color: st.p2ms, defaultActive: false },
{ key: "opReturn", name: "OP_RETURN", color: st.opReturn, defaultActive: true },
{
key: "opReturn",
name: "OP_RETURN",
color: st.opReturn,
defaultActive: true,
},
{
key: "emptyOutput",
name: "Empty",
@@ -71,7 +76,6 @@ export function createNetworkSection() {
// All script types = addressable + non-addressable
const scriptTypes = [...addressTypes, ...nonAddressableTypes];
// Transacting types (transaction participation)
const activityTypes = /** @type {const} */ ([
{ key: "sending", name: "Sending" },
@@ -138,14 +142,16 @@ export function createNetworkSection() {
},
...simpleDeltaTree({
delta: addrs.delta[key],
title: `${titlePrefix}Address Count`,
title: (s) => `${titlePrefix}${s}`,
metric: "Address Count",
unit: Unit.count,
}),
{
name: "New",
tree: chartsFromCount({
pattern: addrs.new[key],
title: `${titlePrefix}New Addresses`,
title: (s) => `${titlePrefix}${s}`,
metric: "New Addresses",
unit: Unit.count,
}),
},
@@ -171,7 +177,8 @@ export function createNetworkSection() {
name: t.name,
tree: averagesArray({
windows: addrs.activity[key][t.key],
title: `${titlePrefix}${t.name} Addresses`,
title: (s) => `${titlePrefix}${s}`,
metric: `${t.name} Addresses`,
unit: Unit.count,
}),
})),
@@ -187,7 +194,10 @@ export function createNetworkSection() {
{ name: "Script Hash", types: [byKey.p2sh, byKey.p2ms] },
{ name: "SegWit", types: [byKey.p2wsh, byKey.p2wpkh] },
{ name: "Taproot", types: [byKey.p2a, byKey.p2tr] },
{ name: "Other", types: [byKey.opReturn, byKey.emptyOutput, byKey.unknownOutput] },
{
name: "Other",
types: [byKey.opReturn, byKey.emptyOutput, byKey.unknownOutput],
},
];
/**
@@ -206,7 +216,9 @@ export function createNetworkSection() {
title: `${groupName} Output Count ${w.title} Sum`,
bottom: types.map((t) =>
line({
series: /** @type {CountPattern<number>} */ (scripts.count[t.key]).sum[w.key],
series: /** @type {CountPattern<number>} */ (
scripts.count[t.key]
).sum[w.key],
name: t.name,
color: t.color,
unit: Unit.count,
@@ -231,7 +243,7 @@ export function createNetworkSection() {
name: t.name,
tree: chartsFromCount({
pattern: /** @type {CountPattern<number>} */ (scripts.count[t.key]),
title: `${t.name} Output Count`,
metric: `${t.name} Output Count`,
unit: Unit.count,
}),
})),
@@ -298,7 +310,7 @@ export function createNetworkSection() {
name: "Count",
tree: chartsFromFullPerBlock({
pattern: transactions.count.total,
title: "Transaction Count",
metric: "Transaction Count",
unit: Unit.count,
}),
},
@@ -306,14 +318,14 @@ export function createNetworkSection() {
name: "Volume",
tree: satsBtcUsdFullTree({
pattern: transactions.volume.transferVolume,
title: "Transaction Volume",
metric: "Transaction Volume",
}),
},
{
name: "Fee Rate",
tree: chartsFromBlockAnd6b({
pattern: transactions.fees.feeRate,
title: "Transaction Fee Rate",
metric: "Transaction Fee Rate",
unit: Unit.feeRate,
}),
},
@@ -321,7 +333,7 @@ export function createNetworkSection() {
name: "Fee",
tree: chartsFromBlockAnd6b({
pattern: transactions.fees.fee,
title: "Transaction Fee",
metric: "Transaction Fee",
unit: Unit.sats,
}),
},
@@ -329,7 +341,7 @@ export function createNetworkSection() {
name: "Weight",
tree: chartsFromBlockAnd6b({
pattern: transactions.size.weight,
title: "Transaction Weight",
metric: "Transaction Weight",
unit: Unit.wu,
}),
},
@@ -337,7 +349,7 @@ export function createNetworkSection() {
name: "vSize",
tree: chartsFromBlockAnd6b({
pattern: transactions.size.vsize,
title: "Transaction vSize",
metric: "Transaction vSize",
unit: Unit.vb,
}),
},
@@ -345,7 +357,7 @@ export function createNetworkSection() {
name: "Versions",
tree: chartsFromCountEntries({
entries: entries(transactions.versions),
title: "Transaction Versions",
metric: "Transaction Versions",
unit: Unit.count,
}),
},
@@ -423,7 +435,7 @@ export function createNetworkSection() {
name: "Interval",
tree: averagesArray({
windows: blocks.interval,
title: "Block Interval",
metric: "Block Interval",
unit: Unit.secs,
}),
},
@@ -431,7 +443,7 @@ export function createNetworkSection() {
name: "Size",
tree: chartsFromFullPerBlock({
pattern: blocks.size,
title: "Block Size",
metric: "Block Size",
unit: Unit.bytes,
}),
},
@@ -439,7 +451,7 @@ export function createNetworkSection() {
name: "Weight",
tree: chartsFromFullPerBlock({
pattern: blocks.weight,
title: "Block Weight",
metric: "Block Weight",
unit: Unit.wu,
}),
},
@@ -447,7 +459,7 @@ export function createNetworkSection() {
name: "vBytes",
tree: chartsFromFullPerBlock({
pattern: blocks.vbytes,
title: "Block vBytes",
metric: "Block vBytes",
unit: Unit.vb,
}),
},
@@ -471,7 +483,7 @@ export function createNetworkSection() {
},
...simpleDeltaTree({
delta: cohorts.utxo.all.outputs.unspentCount.delta,
title: "UTXO Count",
metric: "UTXO Count",
unit: Unit.count,
}),
{
@@ -493,7 +505,7 @@ export function createNetworkSection() {
cumulative: inputs.count.cumulative,
},
],
title: "UTXO Flow",
metric: "UTXO Flow",
unit: Unit.count,
}),
},
@@ -503,7 +515,7 @@ export function createNetworkSection() {
name: "Inputs",
tree: chartsFromAggregatedPerBlock({
pattern: inputs.count,
title: "Input Count",
metric: "Input Count",
unit: Unit.count,
}),
},
@@ -511,7 +523,7 @@ export function createNetworkSection() {
name: "Outputs",
tree: chartsFromAggregatedPerBlock({
pattern: outputs.count.total,
title: "Output Count",
metric: "Output Count",
unit: Unit.count,
}),
},
@@ -588,7 +600,9 @@ export function createNetworkSection() {
title: `Output Count by Script Type ${w.title} Sum`,
bottom: scriptTypes.map((t) =>
line({
series: /** @type {CountPattern<number>} */ (scripts.count[t.key]).sum[w.key],
series: /** @type {CountPattern<number>} */ (
scripts.count[t.key]
).sum[w.key],
name: t.name,
color: t.color,
unit: Unit.count,

View File

@@ -93,13 +93,48 @@ export function price({
function percentileSeries({ pattern, unit, title = "" }) {
const { stat } = colors;
return [
line({ series: pattern.max, name: `${title} max`.trim(), color: stat.max, unit }),
line({ series: pattern.pct90, name: `${title} pct90`.trim(), color: stat.pct90, unit }),
line({ series: pattern.pct75, name: `${title} pct75`.trim(), color: stat.pct75, unit }),
line({ series: pattern.median, name: `${title} median`.trim(), color: stat.median, unit }),
line({ series: pattern.pct25, name: `${title} pct25`.trim(), color: stat.pct25, unit }),
line({ series: pattern.pct10, name: `${title} pct10`.trim(), color: stat.pct10, unit }),
line({ series: pattern.min, name: `${title} min`.trim(), color: stat.min, unit }),
line({
series: pattern.max,
name: `${title} max`.trim(),
color: stat.max,
unit,
}),
line({
series: pattern.pct90,
name: `${title} pct90`.trim(),
color: stat.pct90,
unit,
}),
line({
series: pattern.pct75,
name: `${title} pct75`.trim(),
color: stat.pct75,
unit,
}),
line({
series: pattern.median,
name: `${title} median`.trim(),
color: stat.median,
unit,
}),
line({
series: pattern.pct25,
name: `${title} pct25`.trim(),
color: stat.pct25,
unit,
}),
line({
series: pattern.pct10,
name: `${title} pct10`.trim(),
color: stat.pct10,
unit,
}),
line({
series: pattern.min,
name: `${title} min`.trim(),
color: stat.min,
unit,
}),
];
}
@@ -407,36 +442,6 @@ export function statsAtWindow(pattern, window) {
};
}
/**
* Rolling folder tree with line series
* @param {Object} args
* @param {{ _24h: AnySeriesPattern, _1w: AnySeriesPattern, _1m: AnySeriesPattern, _1y: AnySeriesPattern }} args.windows
* @param {string} args.title
* @param {(w: typeof ROLLING_WINDOWS[number]) => string} args.windowTitle
* @param {Unit} args.unit
* @param {string} args.name
* @returns {PartialOptionsGroup}
*/
function rollingWindowsTreeLine({ windows, title, windowTitle, unit, name }) {
return {
name,
tree: [
{
name: "Compare",
title,
bottom: ROLLING_WINDOWS.map((w) =>
line({ series: windows[w.key], name: w.name, color: w.color, unit }),
),
},
...ROLLING_WINDOWS.map((w) => ({
name: w.name,
title: windowTitle(w),
bottom: [line({ series: windows[w.key], name: w.name, unit })],
})),
],
};
}
/**
* Rolling folder tree with baseline series
* @param {Object} args
@@ -447,7 +452,13 @@ function rollingWindowsTreeLine({ windows, title, windowTitle, unit, name }) {
* @param {string} args.name
* @returns {PartialOptionsGroup}
*/
function rollingWindowsTreeBaseline({ windows, title, windowTitle, unit, name }) {
function rollingWindowsTreeBaseline({
windows,
title,
windowTitle,
unit,
name,
}) {
return {
name,
tree: [
@@ -455,7 +466,12 @@ function rollingWindowsTreeBaseline({ windows, title, windowTitle, unit, name })
name: "Compare",
title,
bottom: ROLLING_WINDOWS.map((w) =>
baseline({ series: windows[w.key], name: w.name, color: w.color, unit }),
baseline({
series: windows[w.key],
name: w.name,
color: w.color,
unit,
}),
),
},
...ROLLING_WINDOWS.map((w) => ({
@@ -467,24 +483,6 @@ function rollingWindowsTreeBaseline({ windows, title, windowTitle, unit, name })
};
}
/**
* Flat array of rolling sum charts (one per window)
* @param {Object} args
* @param {{ _24h: AnySeriesPattern, _1w: AnySeriesPattern, _1m: AnySeriesPattern, _1y: AnySeriesPattern }} args.windows
* @param {string} args.title
* @param {Unit} args.unit
* @returns {PartialChartOption[]}
*/
export function sumsArray({ windows, title, unit }) {
return ROLLING_WINDOWS.map((w) => ({
name: w.name,
title: `${title} ${w.title} Sum`,
bottom: [
line({ series: windows[w.key], name: w.name, color: w.color, unit }),
],
}));
}
/**
* Generic helper: compare + per-window sum+avg + cumulative.
* @template P
@@ -492,7 +490,8 @@ export function sumsArray({ windows, title, unit }) {
* @param {{ _24h: P, _1w: P, _1m: P, _1y: P }} args.sum
* @param {{ _24h: P, _1w: P, _1m: P, _1y: P }} args.average
* @param {P} args.cumulative
* @param {string} args.title
* @param {(metric: string) => string} [args.title]
* @param {string} args.metric
* @param {Color} [args.color]
* @param {(args: { pattern: P, name: string, color?: Color, defaultActive?: boolean }) => AnyFetchedSeriesBlueprint[]} args.series
* @returns {PartialChartOption[]}
@@ -501,14 +500,15 @@ export function sumsAndAveragesCumulativeWith({
sum,
average,
cumulative,
title,
title = (s) => s,
metric,
color,
series,
}) {
return [
{
name: "Compare",
title: `${title} Averages`,
title: title(metric),
bottom: ROLLING_WINDOWS.flatMap((w) =>
series({
pattern: average[w.key],
@@ -519,7 +519,7 @@ export function sumsAndAveragesCumulativeWith({
},
...ROLLING_WINDOWS.map((w) => ({
name: w.name,
title: `${title} ${w.title}`,
title: title(`${w.name} ${metric}`),
bottom: [
...series({ pattern: sum[w.key], name: "Sum", color: w.color }),
...series({
@@ -532,7 +532,7 @@ export function sumsAndAveragesCumulativeWith({
})),
{
name: "Cumulative",
title: `${title} (Total)`,
title: title(`Cumulative ${metric}`),
bottom: series({ pattern: cumulative, name: "all-time", color }),
},
];
@@ -543,14 +543,15 @@ export function sumsAndAveragesCumulativeWith({
* @param {Object} args
* @param {{ _24h: AnySeriesPattern, _1w: AnySeriesPattern, _1m: AnySeriesPattern, _1y: AnySeriesPattern }} args.sum
* @param {{ _24h: AnySeriesPattern, _1w: AnySeriesPattern, _1m: AnySeriesPattern, _1y: AnySeriesPattern }} args.average
* @param {string} args.title
* @param {(metric: string) => string} [args.title]
* @param {string} args.metric
* @param {Unit} args.unit
* @returns {PartialChartOption[]}
*/
export function sumsAndAveragesArray({ sum, average, title, unit }) {
export function sumsAndAveragesArray({ sum, average, title = (s) => s, metric, unit }) {
return ROLLING_WINDOWS.map((w) => ({
name: w.name,
title: `${title} ${w.title}`,
title: title(`${w.name} ${metric}`),
bottom: [
line({ series: sum[w.key], name: "Sum", color: w.color, unit }),
line({
@@ -570,17 +571,27 @@ export function sumsAndAveragesArray({ sum, average, title, unit }) {
* @param {{ _24h: AnySeriesPattern, _1w: AnySeriesPattern, _1m: AnySeriesPattern, _1y: AnySeriesPattern }} args.sum
* @param {{ _24h: AnySeriesPattern, _1w: AnySeriesPattern, _1m: AnySeriesPattern, _1y: AnySeriesPattern }} args.average
* @param {AnySeriesPattern} args.cumulative
* @param {string} args.title
* @param {(metric: string) => string} [args.title]
* @param {string} args.metric
* @param {Unit} args.unit
* @param {Color} [args.color]
* @returns {PartialChartOption[]}
*/
export function sumsAndAveragesCumulative({ sum, average, cumulative, title, unit, color }) {
export function sumsAndAveragesCumulative({
sum,
average,
cumulative,
title,
metric,
unit,
color,
}) {
return sumsAndAveragesCumulativeWith({
sum,
average,
cumulative,
title,
metric,
color,
series: ({ pattern, name, color, defaultActive }) => [
line({ series: pattern, name, color, unit, defaultActive }),
@@ -589,35 +600,18 @@ export function sumsAndAveragesCumulative({ sum, average, cumulative, title, uni
}
/**
* Rolling sums tree (Compare + individual windows in a folder)
* @param {Object} args
* @param {{ _24h: AnySeriesPattern, _1w: AnySeriesPattern, _1m: AnySeriesPattern, _1y: AnySeriesPattern }} args.windows
* @param {string} args.title
* @param {(metric: string) => string} [args.title]
* @param {string} args.metric
* @param {Unit} args.unit
* @returns {PartialOptionsGroup}
*/
export function sumsTree({ windows, title, unit }) {
return rollingWindowsTreeLine({
windows,
title,
windowTitle: (w) => `${title} ${w.title} Sum`,
unit,
name: "Sums",
});
}
/**
* @param {Object} args
* @param {{ _24h: AnySeriesPattern, _1w: AnySeriesPattern, _1m: AnySeriesPattern, _1y: AnySeriesPattern }} args.windows
* @param {string} args.title
* @param {Unit} args.unit
* @returns {PartialOptionsGroup}
*/
export function sumsTreeBaseline({ windows, title, unit }) {
export function sumsTreeBaseline({ windows, title = (s) => s, metric, unit }) {
return rollingWindowsTreeBaseline({
windows,
title,
windowTitle: (w) => `${title} ${w.title} Sum`,
title: title(metric),
windowTitle: (w) => title(`${w.name} ${metric}`),
unit,
name: "Sums",
});
@@ -627,22 +621,23 @@ export function sumsTreeBaseline({ windows, title, unit }) {
* Flat array of per-window average charts
* @param {Object} args
* @param {{ _24h: AnySeriesPattern, _1w: AnySeriesPattern, _1m: AnySeriesPattern, _1y: AnySeriesPattern }} args.windows
* @param {string} args.title
* @param {(metric: string) => string} [args.title]
* @param {string} args.metric
* @param {Unit} args.unit
* @returns {PartialChartOption[]}
*/
export function averagesArray({ windows, title, unit }) {
export function averagesArray({ windows, title = (s) => s, metric, unit }) {
return [
{
name: "Compare",
title: `${title} Averages`,
title: title(metric),
bottom: ROLLING_WINDOWS.map((w) =>
line({ series: windows[w.key], name: w.name, color: w.color, unit }),
),
},
...ROLLING_WINDOWS.map((w) => ({
name: w.name,
title: `${title} ${w.title} Average`,
title: title(`${w.name} ${metric}`),
bottom: [
line({ series: windows[w.key], name: w.name, color: w.color, unit }),
],
@@ -655,17 +650,18 @@ export function averagesArray({ windows, title, unit }) {
* @param {Object} args
* @param {Record<string, any>} args.pattern - Pattern with pct10/pct25/... and average/median/... as _1y24h30d7dPattern
* @param {AnySeriesPattern} [args.base] - Optional base series to show as dots on each chart
* @param {string} args.title
* @param {(metric: string) => string} [args.title]
* @param {string} args.metric
* @param {Unit} args.unit
* @returns {PartialOptionsGroup}
*/
export function distributionWindowsTree({ pattern, base, title, unit }) {
export function distributionWindowsTree({ pattern, base, title = (s) => s, metric, unit }) {
return {
name: "Distribution",
tree: [
{
name: "Compare",
title: `${title} Median`,
title: title(`${metric} Distribution`),
bottom: ROLLING_WINDOWS.map((w) =>
line({
series: pattern.median[w.key],
@@ -677,7 +673,7 @@ export function distributionWindowsTree({ pattern, base, title, unit }) {
},
...ROLLING_WINDOWS.map((w) => ({
name: w.name,
title: `${title} Distribution (${w.title})`,
title: title(`${w.name} ${metric} Distribution`),
bottom: [
...(base ? [line({ series: base, name: "base", unit })] : []),
...percentileSeries({ pattern: statsAtWindow(pattern, w.key), unit }),
@@ -847,24 +843,34 @@ export function percentRatioBaseline({ pattern, name, color, defaultActive }) {
* Rolling folder tree with percentRatio series (colored in compare, plain in individual)
* @param {Object} args
* @param {{ _24h: { percent: AnySeriesPattern, ratio: AnySeriesPattern }, _1w: { percent: AnySeriesPattern, ratio: AnySeriesPattern }, _1m: { percent: AnySeriesPattern, ratio: AnySeriesPattern }, _1y: { percent: AnySeriesPattern, ratio: AnySeriesPattern } }} args.windows
* @param {string} args.title
* @param {(metric: string) => string} [args.title]
* @param {string} args.metric
* @param {string} [args.name]
* @returns {PartialOptionsGroup}
*/
export function rollingPercentRatioTree({ windows, title, name = "Sums" }) {
export function rollingPercentRatioTree({
windows,
title = (s) => s,
metric,
name = "Sums",
}) {
return {
name,
tree: [
{
name: "Compare",
title: `${title} Rolling`,
title: title(metric),
bottom: ROLLING_WINDOWS.flatMap((w) =>
percentRatio({ pattern: windows[w.key], name: w.name, color: w.color }),
percentRatio({
pattern: windows[w.key],
name: w.name,
color: w.color,
}),
),
},
...ROLLING_WINDOWS.map((w) => ({
name: w.name,
title: `${title} (${w.title})`,
title: title(`${w.name} ${metric}`),
bottom: percentRatioBaseline({ pattern: windows[w.key], name: w.name }),
})),
],
@@ -876,19 +882,20 @@ export function rollingPercentRatioTree({ windows, title, name = "Sums" }) {
* @template T
* @param {Object} args
* @param {{ absolute: { _24h: T, _1w: T, _1m: T, _1y: T }, rate: { _24h: { percent: AnySeriesPattern, ratio: AnySeriesPattern }, _1w: { percent: AnySeriesPattern, ratio: AnySeriesPattern }, _1m: { percent: AnySeriesPattern, ratio: AnySeriesPattern }, _1y: { percent: AnySeriesPattern, ratio: AnySeriesPattern } } }} args.delta
* @param {string} args.title
* @param {(metric: string) => string} [args.title]
* @param {string} args.metric
* @param {Unit} args.unit
* @param {(v: T) => AnySeriesPattern} args.extract
* @returns {PartialOptionsTree}
*/
export function deltaTree({ delta, title, unit, extract }) {
export function deltaTree({ delta, title = (s) => s, metric, unit, extract }) {
return [
{
name: "Change",
tree: [
{
name: "Compare",
title: `${title} Change`,
title: title(`${metric} Change`),
bottom: ROLLING_WINDOWS.map((w) =>
baseline({
series: extract(delta.absolute[w.key]),
@@ -900,7 +907,7 @@ export function deltaTree({ delta, title, unit, extract }) {
},
...ROLLING_WINDOWS.map((w) => ({
name: w.name,
title: `${title} Change (${w.title})`,
title: title(`${w.name} ${metric} Change`),
bottom: [
baseline({
series: extract(delta.absolute[w.key]),
@@ -913,7 +920,8 @@ export function deltaTree({ delta, title, unit, extract }) {
},
rollingPercentRatioTree({
windows: delta.rate,
title: `${title} Growth Rate`,
title,
metric: `${metric} Growth Rate`,
name: "Growth Rate",
}),
];
@@ -923,12 +931,13 @@ export function deltaTree({ delta, title, unit, extract }) {
* deltaTree where absolute windows are directly AnySeriesPattern (no extract needed)
* @param {Object} args
* @param {{ absolute: { _24h: AnySeriesPattern, _1w: AnySeriesPattern, _1m: AnySeriesPattern, _1y: AnySeriesPattern }, rate: { _24h: { percent: AnySeriesPattern, ratio: AnySeriesPattern }, _1w: { percent: AnySeriesPattern, ratio: AnySeriesPattern }, _1m: { percent: AnySeriesPattern, ratio: AnySeriesPattern }, _1y: { percent: AnySeriesPattern, ratio: AnySeriesPattern } } }} args.delta
* @param {string} args.title
* @param {(metric: string) => string} [args.title]
* @param {string} args.metric
* @param {Unit} args.unit
* @returns {PartialOptionsTree}
*/
export function simpleDeltaTree({ delta, title, unit }) {
return deltaTree({ delta, title, unit, extract: (v) => v });
export function simpleDeltaTree({ delta, title = (s) => s, metric, unit }) {
return deltaTree({ delta, title, metric, unit, extract: (v) => v });
}
// ============================================================================
@@ -941,29 +950,32 @@ export function simpleDeltaTree({ delta, title, unit }) {
* Pattern has: .height, .cumulative, .sum (windowed), .average/.pct10/... (windowed, flat)
* @param {Object} args
* @param {FullPerBlockPattern} args.pattern
* @param {string} args.title
* @param {(metric: string) => string} [args.title]
* @param {string} args.metric
* @param {Unit} args.unit
* @param {string} [args.distributionSuffix]
* @returns {PartialOptionsTree}
*/
export function chartsFromFull({
pattern,
title,
title = (s) => s,
metric,
unit,
distributionSuffix = "",
}) {
const distTitle = distributionSuffix
? `${title} ${distributionSuffix}`
: title;
const distMetric = distributionSuffix
? `${metric} ${distributionSuffix}`
: metric;
return [
...sumsAndAveragesCumulative({
sum: pattern.sum,
average: pattern.average,
cumulative: pattern.cumulative,
title,
metric,
unit,
}),
distributionWindowsTree({ pattern, title: distTitle, unit }),
distributionWindowsTree({ pattern, title, metric: distMetric, unit }),
];
}
@@ -971,7 +983,8 @@ export function chartsFromFull({
* Split pattern into 4 charts with "per Block" in distribution title
* @param {Object} args
* @param {FullPerBlockPattern} args.pattern
* @param {string} args.title
* @param {(metric: string) => string} [args.title]
* @param {string} args.metric
* @param {Unit} args.unit
* @returns {PartialOptionsTree}
*/
@@ -982,31 +995,35 @@ export const chartsFromFullPerBlock = (args) =>
* Split pattern with sum + distribution + cumulative into 3 charts (no base)
* @param {Object} args
* @param {AggregatedPattern} args.pattern
* @param {string} args.title
* @param {(metric: string) => string} [args.title]
* @param {string} args.metric
* @param {Unit} args.unit
* @param {string} [args.distributionSuffix]
* @returns {PartialOptionsTree}
*/
export function chartsFromAggregated({
pattern,
title,
title = (s) => s,
metric,
unit,
distributionSuffix = "",
}) {
const distTitle = distributionSuffix
? `${title} ${distributionSuffix}`
: title;
const distMetric = distributionSuffix
? `${metric} ${distributionSuffix}`
: metric;
return [
...sumsAndAveragesCumulative({
sum: pattern.rolling.sum,
average: pattern.rolling.average,
cumulative: pattern.cumulative,
title,
metric,
unit,
}),
distributionWindowsTree({
pattern: pattern.rolling,
title: distTitle,
title,
metric: distMetric,
unit,
}),
];
@@ -1016,7 +1033,8 @@ export function chartsFromAggregated({
* Split pattern into 3 charts with "per Block" in distribution title (no base)
* @param {Object} args
* @param {AggregatedPattern} args.pattern
* @param {string} args.title
* @param {(metric: string) => string} [args.title]
* @param {string} args.metric
* @param {Unit} args.unit
* @returns {PartialOptionsTree}
*/
@@ -1027,20 +1045,21 @@ export const chartsFromAggregatedPerBlock = (args) =>
* Create Per Block + Per 6 Blocks stats charts from a _6bBlockTxPattern
* @param {Object} args
* @param {{ block: DistributionStats, _6b: DistributionStats }} args.pattern
* @param {string} args.title
* @param {(metric: string) => string} [args.title]
* @param {string} args.metric
* @param {Unit} args.unit
* @returns {PartialOptionsTree}
*/
export function chartsFromBlockAnd6b({ pattern, title, unit }) {
export function chartsFromBlockAnd6b({ pattern, title = (s) => s, metric, unit }) {
return [
{
name: "Block",
title: `${title} (Block)`,
title: title(`${metric} (Block)`),
bottom: percentileSeries({ pattern: pattern.block, unit }),
},
{
name: "Hourly",
title: `${title} (Hourly)`,
name: "~Hourly",
title: title(`${metric} (~Hourly)`),
bottom: percentileSeries({ pattern: pattern._6b, unit }),
},
];
@@ -1050,17 +1069,19 @@ export function chartsFromBlockAnd6b({ pattern, title, unit }) {
* Averages + Sums + Cumulative charts
* @param {Object} args
* @param {CountPattern<any>} args.pattern
* @param {string} args.title
* @param {(metric: string) => string} [args.title]
* @param {string} args.metric
* @param {Unit} args.unit
* @param {Color} [args.color]
* @returns {PartialOptionsTree}
*/
export function chartsFromCount({ pattern, title, unit, color }) {
export function chartsFromCount({ pattern, title = (s) => s, metric, unit, color }) {
return sumsAndAveragesCumulative({
sum: pattern.sum,
average: pattern.average,
cumulative: pattern.cumulative,
title,
metric,
unit,
color,
});
@@ -1070,19 +1091,12 @@ export function chartsFromCount({ pattern, title, unit, color }) {
* Windowed sums + cumulative for multiple named entries (e.g. transaction versions)
* @param {Object} args
* @param {Array<[string, CountPattern<any>]>} args.entries
* @param {string} args.title
* @param {(metric: string) => string} [args.title]
* @param {string} args.metric
* @param {Unit} args.unit
* @returns {PartialOptionsTree}
*/
/**
* Windowed sums + cumulative for multiple named entries (e.g. transaction versions)
* @param {Object} args
* @param {Array<[string, CountPattern<any>]>} args.entries
* @param {string} args.title
* @param {Unit} args.unit
* @returns {PartialOptionsTree}
*/
export function chartsFromCountEntries({ entries, title, unit }) {
export function chartsFromCountEntries({ entries, title = (s) => s, metric, unit }) {
const items = entries.map(([name, data], i, arr) => ({
name,
color: colors.at(i, arr.length),
@@ -1092,14 +1106,14 @@ export function chartsFromCountEntries({ entries, title, unit }) {
return [
...ROLLING_WINDOWS.map((w) => ({
name: w.name,
title: `${title} ${w.title} Sum`,
title: title(`${w.name} ${metric}`),
bottom: items.map((e) =>
line({ series: e.sum[w.key], name: e.name, color: e.color, unit }),
),
})),
{
name: "Cumulative",
title: `${title} (Total)`,
title: title(`Cumulative ${metric}`),
bottom: items.map((e) =>
line({ series: e.cumulative, name: e.name, color: e.color, unit }),
),
@@ -1111,26 +1125,26 @@ export function chartsFromCountEntries({ entries, title, unit }) {
* Windowed averages + sums + cumulative for multiple named series (e.g. UTXO flow)
* @param {Object} args
* @param {Array<{ name: string, color: Color, average: { _24h: AnySeriesPattern, _1w: AnySeriesPattern, _1m: AnySeriesPattern, _1y: AnySeriesPattern }, sum: { _24h: AnySeriesPattern, _1w: AnySeriesPattern, _1m: AnySeriesPattern, _1y: AnySeriesPattern }, cumulative: AnySeriesPattern }>} args.entries
* @param {string} args.title
* @param {(metric: string) => string} [args.title]
* @param {string} args.metric
* @param {Unit} args.unit
* @returns {PartialOptionsTree}
*/
export function multiSeriesTree({ entries, title, unit }) {
export function multiSeriesTree({ entries, title = (s) => s, metric, unit }) {
return [
...ROLLING_WINDOWS.map((w) => ({
name: w.name,
title: `${title} ${w.title} Averages`,
title: title(`${w.name} ${metric}`),
bottom: entries.map((e) =>
line({ series: e.average[w.key], name: e.name, color: e.color, unit }),
),
})),
{
name: "Cumulative",
title: `${title} (Total)`,
title: title(`Cumulative ${metric}`),
bottom: entries.map((e) =>
line({ series: e.cumulative, name: e.name, color: e.color, unit }),
),
},
];
}

View File

@@ -219,16 +219,18 @@ export function satsBtcUsdRolling({ pattern, name, color, defaultActive }) {
* Build a full Sum / Rolling / Cumulative tree from a FullValuePattern
* @param {Object} args
* @param {FullValuePattern} args.pattern
* @param {string} args.title
* @param {(metric: string) => string} [args.title]
* @param {string} args.metric
* @param {Color} [args.color]
* @returns {PartialOptionsTree}
*/
export function satsBtcUsdFullTree({ pattern, title, color }) {
export function satsBtcUsdFullTree({ pattern, title, metric, color }) {
return sumsAndAveragesCumulativeWith({
sum: pattern.sum,
average: pattern.average,
cumulative: pattern.cumulative,
title,
metric,
color,
series: ({ pattern, name, color, defaultActive }) =>
satsBtcUsd({ pattern, name, color, defaultActive }),