global: snapshot

This commit is contained in:
nym21
2026-03-15 11:25:21 +01:00
parent 9e36a4188a
commit 9626c7de32
54 changed files with 884 additions and 750 deletions
@@ -33,6 +33,9 @@ export function buildCohortData() {
AMOUNT_RANGE_NAMES,
SPENDABLE_TYPE_NAMES,
CLASS_NAMES,
PROFITABILITY_RANGE_NAMES,
PROFIT_NAMES,
LOSS_NAMES,
} = brk;
const cohortAll = {
@@ -191,6 +194,28 @@ export function buildCohortData() {
tree: utxoCohorts.class[key],
}));
const { range, profit, loss } = utxoCohorts.profitability;
const profitabilityRange = entries(PROFITABILITY_RANGE_NAMES).map(
([key, names], i, arr) => ({
name: names.short,
color: colors.at(i, arr.length),
pattern: range[key],
}),
);
const profitabilityProfit = entries(PROFIT_NAMES).map(([key, names], i, arr) => ({
name: names.short,
color: colors.at(i, arr.length),
pattern: profit[key],
}));
const profitabilityLoss = entries(LOSS_NAMES).map(([key, names], i, arr) => ({
name: names.short,
color: colors.at(i, arr.length),
pattern: loss[key],
}));
return {
cohortAll,
termShort,
@@ -208,5 +233,8 @@ export function buildCohortData() {
typeAddressable,
typeOther,
class: class_,
profitabilityRange,
profitabilityProfit,
profitabilityLoss,
};
}
+218 -4
View File
@@ -10,7 +10,9 @@
* - activity.js: SOPR, Volume, Lifespan
*/
import { formatCohortTitle, satsBtcUsd } from "../shared.js";
import { formatCohortTitle, satsBtcUsd, satsBtcUsdFullTree, simplePriceRatioTree, groupedSimplePriceRatioTree } from "../shared.js";
import { ROLLING_WINDOWS, line, baseline, percentRatio, rollingWindowsTree, rollingPercentRatioTree } from "../series.js";
import { Unit } from "../../utils/units.js";
// Section builders
import {
@@ -205,8 +207,11 @@ export function createCohortFolderAgeRangeWithMatured(cohort) {
const title = formatCohortTitle(cohort.name);
folder.tree.push({
name: "Matured",
title: title("Matured Supply"),
bottom: satsBtcUsd({ pattern: cohort.matured, name: cohort.name }),
tree: satsBtcUsdFullTree({
pattern: cohort.matured,
name: cohort.name,
title: title("Matured Supply"),
}),
});
return folder;
}
@@ -452,7 +457,7 @@ export function createGroupedCohortFolderAgeRangeWithMatured({
name: "Matured",
title: title("Matured Supply"),
bottom: list.flatMap((cohort) =>
satsBtcUsd({ pattern: cohort.matured, name: cohort.name, color: cohort.color }),
satsBtcUsd({ pattern: cohort.matured.base, name: cohort.name, color: cohort.color }),
),
});
return folder;
@@ -580,3 +585,212 @@ export function createGroupedAddressCohortFolder({
],
};
}
// ============================================================================
// UTXO Profitability Folder Builders
// ============================================================================
/**
* @param {{ name: string, color: Color, pattern: RealizedSupplyPattern }} bucket
* @returns {PartialOptionsGroup}
*/
function singleBucketFolder({ name, color, pattern }) {
return {
name,
tree: [
{
name: "Supply",
tree: [
{
name: "All",
title: `${name}: Supply`,
bottom: satsBtcUsd({ pattern: pattern.supply.all, name, color }),
},
{
name: "STH",
title: `${name}: STH Supply`,
bottom: satsBtcUsd({ pattern: pattern.supply.sth, name, color }),
},
{
name: "Change",
tree: [
{ ...rollingWindowsTree({ windows: pattern.supply.all.delta.absolute, title: `${name}: Supply Change`, unit: Unit.sats, series: baseline }), name: "Absolute" },
{ ...rollingPercentRatioTree({ windows: pattern.supply.all.delta.rate, title: `${name}: Supply Rate` }), name: "Rate" },
],
},
],
},
{
name: "Realized Cap",
tree: [
{
name: "All",
title: `${name}: Realized Cap`,
bottom: [line({ metric: pattern.realizedCap.all, name, color, unit: Unit.usd })],
},
{
name: "STH",
title: `${name}: STH Realized Cap`,
bottom: [line({ metric: pattern.realizedCap.sth, name, color, unit: Unit.usd })],
},
],
},
{
name: "Realized Price",
tree: simplePriceRatioTree({
pattern: pattern.realizedPrice,
title: `${name}: Realized Price`,
legend: name,
color,
}),
},
{
name: "NUPL",
title: `${name}: NUPL`,
bottom: [line({ metric: pattern.nupl.ratio, name, color, unit: Unit.ratio })],
},
],
};
}
/**
* @param {{ name: string, color: Color, pattern: RealizedSupplyPattern }[]} list
* @param {string} titlePrefix
* @returns {PartialOptionsTree}
*/
function groupedBucketCharts(list, titlePrefix) {
return [
{
name: "Supply",
tree: [
{
name: "All",
title: `${titlePrefix}: Supply`,
bottom: list.flatMap(({ name, color, pattern }) =>
satsBtcUsd({ pattern: pattern.supply.all, name, color }),
),
},
{
name: "STH",
title: `${titlePrefix}: STH Supply`,
bottom: list.flatMap(({ name, color, pattern }) =>
satsBtcUsd({ pattern: pattern.supply.sth, name, color }),
),
},
{
name: "Change",
tree: [
{
name: "Absolute",
tree: [
{
name: "Compare",
title: `${titlePrefix}: Supply Change`,
bottom: ROLLING_WINDOWS.flatMap((w) =>
list.map(({ name, color, pattern }) =>
baseline({ metric: pattern.supply.all.delta.absolute[w.key], name: `${name} ${w.name}`, color, unit: Unit.sats }),
),
),
},
...ROLLING_WINDOWS.map((w) => ({
name: w.name,
title: `${titlePrefix}: Supply Change ${w.name}`,
bottom: list.map(({ name, color, pattern }) =>
baseline({ metric: pattern.supply.all.delta.absolute[w.key], name, color, unit: Unit.sats }),
),
})),
],
},
{
name: "Rate",
tree: [
{
name: "Compare",
title: `${titlePrefix}: Supply Rate`,
bottom: ROLLING_WINDOWS.flatMap((w) =>
list.flatMap(({ name, color, pattern }) =>
percentRatio({ pattern: pattern.supply.all.delta.rate[w.key], name: `${name} ${w.name}`, color }),
),
),
},
...ROLLING_WINDOWS.map((w) => ({
name: w.name,
title: `${titlePrefix}: Supply Rate ${w.name}`,
bottom: list.flatMap(({ name, color, pattern }) =>
percentRatio({ pattern: pattern.supply.all.delta.rate[w.key], name, color }),
),
})),
],
},
],
},
],
},
{
name: "Realized Cap",
tree: [
{
name: "All",
title: `${titlePrefix}: Realized Cap`,
bottom: list.map(({ name, color, pattern }) =>
line({ metric: pattern.realizedCap.all, name, color, unit: Unit.usd }),
),
},
{
name: "STH",
title: `${titlePrefix}: STH Realized Cap`,
bottom: list.map(({ name, color, pattern }) =>
line({ metric: pattern.realizedCap.sth, name, color, unit: Unit.usd }),
),
},
],
},
{
name: "Realized Price",
tree: groupedSimplePriceRatioTree({
list: list.map(({ name, color, pattern }) => ({ name, color, pattern: pattern.realizedPrice })),
title: `${titlePrefix}: Realized Price`,
}),
},
{
name: "NUPL",
title: `${titlePrefix}: NUPL`,
bottom: list.map(({ name, color, pattern }) =>
line({ metric: pattern.nupl.ratio, name, color, unit: Unit.ratio }),
),
},
];
}
/**
* @param {{ range: { name: string, color: Color, pattern: RealizedSupplyPattern }[], profit: { name: string, color: Color, pattern: RealizedSupplyPattern }[], loss: { name: string, color: Color, pattern: RealizedSupplyPattern }[] }} args
* @returns {PartialOptionsGroup}
*/
export function createUtxoProfitabilitySection({ range, profit, loss }) {
return {
name: "UTXO Profitability",
tree: [
{
name: "Range",
tree: [
{ name: "Compare", tree: groupedBucketCharts(range, "Profitability Range") },
...range.map(singleBucketFolder),
],
},
{
name: "In Profit",
tree: [
{ name: "Compare", tree: groupedBucketCharts(profit, "In Profit") },
...profit.map(singleBucketFolder),
],
},
{
name: "In Loss",
tree: [
{ name: "Compare", tree: groupedBucketCharts(loss, "In Loss") },
...loss.map(singleBucketFolder),
],
},
],
};
}
+7 -21
View File
@@ -14,7 +14,7 @@
*/
import { colors } from "../../utils/colors.js";
import { createPriceRatioCharts, mapCohortsWithAll } from "../shared.js";
import { createPriceRatioCharts, mapCohortsWithAll, priceRatioPercentilesTree } from "../shared.js";
import { baseline, price } from "../series.js";
import { Unit } from "../../utils/units.js";
@@ -53,26 +53,12 @@ export function createPricesSectionFull({ cohort, title }) {
},
{
name: "Investor",
tree: [
{
name: "Price",
title: title("Investor Price"),
top: [price({ metric: tree.realized.investor.price, name: "Investor", color })],
},
{
name: "Ratio",
title: title("Investor Price Ratio"),
top: [price({ metric: tree.realized.investor.price, name: "Investor", color })],
bottom: [
baseline({
metric: tree.realized.investor.price.ratio,
name: "Ratio",
unit: Unit.ratio,
base: 1,
}),
],
},
],
tree: priceRatioPercentilesTree({
pattern: tree.realized.investor.price,
title: title("Investor Price"),
legend: "Investor",
color,
}),
},
],
};
@@ -1,89 +0,0 @@
/** UTXO Profitability section — range bands, cumulative profit/loss thresholds */
import { colors } from "../../utils/colors.js";
import { entries } from "../../utils/array.js";
import { Unit } from "../../utils/units.js";
import { line, price } from "../series.js";
import { brk } from "../../client.js";
import { satsBtcUsd } from "../shared.js";
/**
* @param {{ name: string, color: Color, pattern: RealizedSupplyPattern }[]} list
* @param {string} titlePrefix
* @returns {PartialOptionsTree}
*/
function bucketCharts(list, titlePrefix) {
return [
{
name: "Supply",
title: `${titlePrefix}: Supply`,
bottom: list.flatMap(({ name, color, pattern }) =>
satsBtcUsd({ pattern: pattern.supply, name, color }),
),
},
{
name: "Realized Cap",
title: `${titlePrefix}: Realized Cap`,
bottom: list.map(({ name, color, pattern }) =>
line({ metric: pattern.realizedCap, name, color, unit: Unit.usd }),
),
},
{
name: "Realized Price",
title: `${titlePrefix}: Realized Price`,
top: list.map(({ name, color, pattern }) =>
price({ metric: pattern.realizedPrice, name, color }),
),
},
];
}
/**
* @returns {PartialOptionsGroup}
*/
export function createUtxoProfitabilitySection() {
const { range, profit, loss } = brk.metrics.cohorts.utxo.profitability;
const {
PROFITABILITY_RANGE_NAMES,
PROFIT_NAMES,
LOSS_NAMES,
} = brk;
const rangeList = entries(PROFITABILITY_RANGE_NAMES).map(
([key, names], i, arr) => ({
name: names.short,
color: colors.at(i, arr.length),
pattern: range[key],
}),
);
const profitList = entries(PROFIT_NAMES).map(([key, names], i, arr) => ({
name: names.short,
color: colors.at(i, arr.length),
pattern: profit[key],
}));
const lossList = entries(LOSS_NAMES).map(([key, names], i, arr) => ({
name: names.short,
color: colors.at(i, arr.length),
pattern: loss[key],
}));
return {
name: "UTXO Profitability",
tree: [
{
name: "Range",
tree: bucketCharts(rangeList, "Profitability Range"),
},
{
name: "In Profit",
tree: bucketCharts(profitList, "In Profit"),
},
{
name: "In Loss",
tree: bucketCharts(lossList, "In Loss"),
},
],
};
}