global: refactor

This commit is contained in:
nym21
2026-04-22 22:23:39 +02:00
parent c5b16e7048
commit 84e924b77e
63 changed files with 726 additions and 257 deletions

View File

@@ -144,7 +144,7 @@
*/
/**
* Sell side risk rolling windows pattern
* @typedef {Brk._1m1w1y24hPattern7} SellSideRiskPattern
* @typedef {Brk._1m1w1y24hPattern8} SellSideRiskPattern
*/
/**
* Stats pattern: min, max, median, percentiles
@@ -245,6 +245,8 @@
* Delta patterns with absolute + rate rolling windows
* @typedef {Brk.AbsoluteRatePattern} DeltaPattern
* @typedef {Brk.AbsoluteRatePattern2} FiatDeltaPattern
* @typedef {Brk.AbsoluteRatePattern3} AmountDeltaPattern
* @typedef {Brk.BtcSatsPattern} AmountPattern
*
* Capitalized price percentiles (pct1/2/5/95/98/99)
* @typedef {Brk.Pct0Pct1Pct2Pct5Pct95Pct98Pct99Pattern} CapitalizedPercentilesPattern

View File

@@ -15,12 +15,14 @@ import {
line,
baseline,
sumsTreeBaseline,
amountSumsTreeBaseline,
rollingPercentRatioTree,
percentRatio,
percentRatioBaseline,
chartsFromCount,
} from "../series.js";
import {
amountBaseline,
satsBtcUsd,
flatMapCohorts,
mapCohortsWithAll,
@@ -160,6 +162,79 @@ function groupedDeltaItems(list, all, getDelta, unit, title, name) {
];
}
/**
* Amount-valued single-cohort delta: Change exposes sats + lazy btc per window.
* @param {AmountDeltaPattern} delta
* @param {(name: string) => string} title
* @param {string} name
* @returns {PartialOptionsTree}
*/
function singleAmountDeltaItems(delta, title, name) {
return [
{
...amountSumsTreeBaseline({
windows: delta.absolute,
title,
metric: `${name} Change`,
legend: "Change",
}),
name: "Change",
},
{
...rollingPercentRatioTree({
windows: delta.rate,
title,
metric: `${name} Growth Rate`,
}),
name: "Growth Rate",
},
];
}
/**
* Amount-valued grouped-cohort delta: Change exposes sats + lazy btc per window.
* @template {{ name: string, color: Color }} T
* @template {{ name: string, color: Color }} A
* @param {readonly T[]} list
* @param {A} all
* @param {(c: T | A) => AmountDeltaPattern} getDelta
* @param {(name: string) => string} title
* @param {string} name
* @returns {PartialOptionsTree}
*/
function groupedAmountDeltaItems(list, all, getDelta, title, name) {
return [
{
name: "Change",
tree: ROLLING_WINDOWS.map((w) => ({
name: w.name,
title: title(`${w.title} ${name} Change`),
bottom: flatMapCohortsWithAll(list, all, (c) =>
amountBaseline({
pattern: getDelta(c).absolute[w.key],
name: c.name,
color: c.color,
}),
),
})),
},
{
name: "Growth Rate",
tree: ROLLING_WINDOWS.map((w) => ({
name: w.name,
title: title(`${w.title} ${name} Growth Rate`),
bottom: flatMapCohortsWithAll(list, all, (c) =>
percentRatioBaseline({
pattern: getDelta(c).rate[w.key],
name: c.name,
color: c.color,
}),
),
})),
},
];
}
// ============================================================================
// Single Cohort Composable Builders
// ============================================================================
@@ -292,7 +367,7 @@ export function createHoldingsSection({ cohort, title }) {
bottom: simpleSupplySeries(supply),
},
dominanceChart(supply, cohort.color, title),
...singleDeltaItems(supply.delta, Unit.sats, title, "Supply"),
...singleAmountDeltaItems(supply.delta, title, "Supply"),
],
},
outputsFolder(cohort.tree.outputs, cohort.color, title),
@@ -321,7 +396,7 @@ export function createHoldingsSectionAll({ cohort, title }) {
profitabilityCompositionChart(supply, title),
],
},
...singleDeltaItems(supply.delta, Unit.sats, title, "Supply"),
...singleAmountDeltaItems(supply.delta, title, "Supply"),
],
},
outputsFolder(cohort.tree.outputs, cohort.color, title),
@@ -352,7 +427,7 @@ export function createHoldingsSectionWithRelative({ cohort, title }) {
profitabilityCompositionChart(supply, title),
],
},
...singleDeltaItems(supply.delta, Unit.sats, title, "Supply"),
...singleAmountDeltaItems(supply.delta, title, "Supply"),
],
},
outputsFolder(cohort.tree.outputs, cohort.color, title),
@@ -379,7 +454,7 @@ export function createHoldingsSectionWithOwnSupply({ cohort, title }) {
name: "Profitability",
tree: [profitabilityAmountChart(supply, title)],
},
...singleDeltaItems(supply.delta, Unit.sats, title, "Supply"),
...singleAmountDeltaItems(supply.delta, title, "Supply"),
],
},
outputsFolder(cohort.tree.outputs, cohort.color, title),
@@ -406,7 +481,7 @@ export function createHoldingsSectionWithProfitLoss({ cohort, title }) {
name: "Profitability",
tree: [profitabilityAmountChart(supply, title)],
},
...singleDeltaItems(supply.delta, Unit.sats, title, "Supply"),
...singleAmountDeltaItems(supply.delta, title, "Supply"),
],
},
outputsFolder(cohort.tree.outputs, cohort.color, title),
@@ -433,7 +508,7 @@ export function createHoldingsSectionAddress({ cohort, title }) {
name: "Profitability",
tree: [profitabilityAmountChart(supply, title)],
},
...singleDeltaItems(supply.delta, Unit.sats, title, "Supply"),
...singleAmountDeltaItems(supply.delta, title, "Supply"),
],
},
outputsFolder(cohort.tree.outputs, cohort.color, title),
@@ -457,7 +532,7 @@ export function createHoldingsSectionAddressAmount({ cohort, title }) {
bottom: simpleSupplySeries(supply),
},
dominanceChart(supply, cohort.color, title),
...singleDeltaItems(supply.delta, Unit.sats, title, "Supply"),
...singleAmountDeltaItems(supply.delta, title, "Supply"),
],
},
outputsFolder(cohort.tree.outputs, cohort.color, title),
@@ -529,7 +604,7 @@ export function createGroupedHoldingsSectionAddress({ list, all, title }) {
name: "Profitability",
tree: groupedSupplyProfitLoss(list, all, title),
},
...groupedDeltaItems(list, all, (c) => c.tree.supply.delta, Unit.sats, title, "Supply"),
...groupedAmountDeltaItems(list, all, (c) => c.tree.supply.delta, title, "Supply"),
],
},
groupedOutputsFolder(list, all, title),
@@ -580,7 +655,7 @@ export function createGroupedHoldingsSectionAddressAmount({ list, all, title })
tree: [
groupedSupplyTotal(list, all, title),
groupedDominanceChart(list, title),
...groupedDeltaItems(list, all, (c) => c.tree.supply.delta, Unit.sats, title, "Supply"),
...groupedAmountDeltaItems(list, all, (c) => c.tree.supply.delta, title, "Supply"),
],
},
groupedOutputsFolder(list, all, title),
@@ -608,7 +683,7 @@ export function createGroupedHoldingsSection({ list, all, title }) {
tree: [
groupedSupplyTotal(list, all, title),
groupedDominanceChart(list, title),
...groupedDeltaItems(list, all, (c) => c.tree.supply.delta, Unit.sats, title, "Supply"),
...groupedAmountDeltaItems(list, all, (c) => c.tree.supply.delta, title, "Supply"),
],
},
groupedOutputsFolder(list, all, title),
@@ -627,7 +702,7 @@ export function createGroupedHoldingsSectionWithProfitLoss({ list, all, title })
name: "Profitability",
tree: groupedSupplyProfitLoss(list, all, title),
},
...groupedDeltaItems(list, all, (c) => c.tree.supply.delta, Unit.sats, title, "Supply"),
...groupedAmountDeltaItems(list, all, (c) => c.tree.supply.delta, title, "Supply"),
],
},
groupedOutputsFolder(list, all, title),
@@ -646,7 +721,7 @@ export function createGroupedHoldingsSectionWithOwnSupply({ list, all, title })
name: "Profitability",
tree: groupedSupplyProfitLoss(list, all, title),
},
...groupedDeltaItems(list, all, (c) => c.tree.supply.delta, Unit.sats, title, "Supply"),
...groupedAmountDeltaItems(list, all, (c) => c.tree.supply.delta, title, "Supply"),
],
},
groupedOutputsFolder(list, all, title),
@@ -679,7 +754,7 @@ export function createGroupedHoldingsSectionWithRelative({ list, all, title }) {
},
],
},
...groupedDeltaItems(list, all, (c) => c.tree.supply.delta, Unit.sats, title, "Supply"),
...groupedAmountDeltaItems(list, all, (c) => c.tree.supply.delta, title, "Supply"),
],
},
groupedOutputsFolder(list, all, title),

View File

@@ -12,6 +12,7 @@
import {
formatCohortTitle,
amountBaseline,
satsBtcUsd,
satsBtcUsdFullTree,
avgHoldingsSubtree,
@@ -21,9 +22,8 @@ import {
import {
ROLLING_WINDOWS,
line,
baseline,
percentRatio,
sumsTreeBaseline,
amountSumsTreeBaseline,
rollingPercentRatioTree,
} from "../series.js";
import { Unit } from "../../utils/units.js";
@@ -528,11 +528,10 @@ function singleBucketFolder({ name, color, pattern }, parentName) {
],
},
{
...sumsTreeBaseline({
...amountSumsTreeBaseline({
windows: pattern.supply.all.delta.absolute,
title,
metric: "Supply Change",
unit: Unit.sats,
legend: "Change",
}),
name: "Change",
@@ -622,12 +621,11 @@ function groupedBucketCharts(list, groupTitle) {
tree: ROLLING_WINDOWS.map((w) => ({
name: w.name,
title: title(`${w.title} Supply Change`),
bottom: list.map(({ name, color, pattern }) =>
baseline({
series: pattern.supply.all.delta.absolute[w.key],
bottom: list.flatMap(({ name, color, pattern }) =>
amountBaseline({
pattern: pattern.supply.all.delta.absolute[w.key],
name,
color,
unit: Unit.sats,
}),
),
})),

View File

@@ -629,6 +629,39 @@ export function sumsTreeBaseline({ windows, title = (s) => s, metric, unit, lege
});
}
/**
* Rolling tree with baseline series for an amount pattern (sats + lazy btc per window).
* @param {Object} args
* @param {{ _24h: AmountPattern, _1w: AmountPattern, _1m: AmountPattern, _1y: AmountPattern }} args.windows
* @param {(metric: string) => string} [args.title]
* @param {string} args.metric
* @param {string} [args.legend]
* @returns {PartialOptionsGroup}
*/
export function amountSumsTreeBaseline({ windows, title = (s) => s, metric, legend = "Sum" }) {
return {
name: "Sums",
tree: [
{
name: "Compare",
title: title(metric),
bottom: ROLLING_WINDOWS.flatMap((w) => [
baseline({ series: windows[w.key].btc, name: w.name, color: w.color, unit: Unit.btc }),
baseline({ series: windows[w.key].sats, name: w.name, color: w.color, unit: Unit.sats }),
]),
},
...ROLLING_WINDOWS.map((w) => ({
name: w.name,
title: title(`${w.title} ${metric}`),
bottom: [
baseline({ series: windows[w.key].btc, name: legend, unit: Unit.btc }),
baseline({ series: windows[w.key].sats, name: legend, unit: Unit.sats }),
],
})),
],
};
}
/**
* Flat array of per-window average charts
* @param {Object} args

View File

@@ -86,6 +86,65 @@ export function flatMapCohortsWithAll(list, all, fn) {
export const formatCohortTitle = (cohortTitle) => (name) =>
cohortTitle ? `${name}: ${cohortTitle}` : name;
/**
* Create line series from an amount pattern (sats stored + lazy btc).
* @param {Object} args
* @param {AmountPattern} args.pattern
* @param {string} args.name
* @param {Color} [args.color]
* @param {boolean} [args.defaultActive]
* @param {number} [args.style]
* @returns {FetchedLineSeriesBlueprint[]}
*/
export function amount({ pattern, name, color, defaultActive, style }) {
return [
line({
series: pattern.btc,
name,
color,
unit: Unit.btc,
defaultActive,
style,
}),
line({
series: pattern.sats,
name,
color,
unit: Unit.sats,
defaultActive,
style,
}),
];
}
/**
* Create baseline series from an amount pattern (sats stored + lazy btc).
* @param {Object} args
* @param {AmountPattern} args.pattern
* @param {string} args.name
* @param {Color} [args.color]
* @param {boolean} [args.defaultActive]
* @returns {FetchedBaselineSeriesBlueprint[]}
*/
export function amountBaseline({ pattern, name, color, defaultActive }) {
return [
baseline({
series: pattern.btc,
name,
color,
unit: Unit.btc,
defaultActive,
}),
baseline({
series: pattern.sats,
name,
color,
unit: Unit.sats,
defaultActive,
}),
];
}
/**
* Create sats/btc/usd line series from a pattern with .sats/.btc/.usd
* @param {Object} args