Files
brk/website/scripts/options/cohorts/data.js
2026-01-14 20:09:51 +01:00

245 lines
6.3 KiB
JavaScript

/** Build cohort data arrays from brk.metrics */
import {
termColors,
maxAgeColors,
minAgeColors,
ageRangeColors,
epochColors,
geAmountColors,
ltAmountColors,
amountRangeColors,
spendableTypeColors,
yearColors,
} from "../colors/index.js";
/**
* @template {Record<string, any>} T
* @param {T} obj
* @returns {[keyof T & string, T[keyof T & string]][]}
*/
const entries = (obj) =>
/** @type {[keyof T & string, T[keyof T & string]][]} */ (
Object.entries(obj)
);
/**
* Build all cohort data from brk tree
* @param {Colors} colors
* @param {BrkClient} brk
*/
export function buildCohortData(colors, brk) {
const utxoCohorts = brk.metrics.distribution.utxoCohorts;
const addressCohorts = brk.metrics.distribution.addressCohorts;
const {
TERM_NAMES,
EPOCH_NAMES,
MAX_AGE_NAMES,
MIN_AGE_NAMES,
AGE_RANGE_NAMES,
GE_AMOUNT_NAMES,
LT_AMOUNT_NAMES,
AMOUNT_RANGE_NAMES,
SPENDABLE_TYPE_NAMES,
YEAR_NAMES,
} = brk;
// Base cohort representing "all" - CohortAll (adjustedSopr + percentiles but no RelToMarketCap)
/** @type {CohortAll} */
const cohortAll = {
name: "",
title: "",
color: colors.orange,
tree: utxoCohorts.all,
};
// Term cohorts - split because short is CohortFull, long is CohortWithPercentiles
const shortNames = TERM_NAMES.short;
/** @type {CohortFull} */
const termShort = {
name: shortNames.short,
title: shortNames.long,
color: colors[termColors.short],
tree: utxoCohorts.term.short,
};
const longNames = TERM_NAMES.long;
/** @type {CohortWithPercentiles} */
const termLong = {
name: longNames.short,
title: longNames.long,
color: colors[termColors.long],
tree: utxoCohorts.term.long,
};
// Max age cohorts (up to X time) - CohortWithAdjusted (adjustedSopr only)
/** @type {readonly CohortWithAdjusted[]} */
const upToDate = entries(utxoCohorts.maxAge).map(([key, tree]) => {
const names = MAX_AGE_NAMES[key];
return {
name: names.short,
title: names.long,
color: colors[maxAgeColors[key]],
tree,
};
});
// Min age cohorts (from X time) - CohortBasic (neither adjustedSopr nor percentiles)
/** @type {readonly CohortBasic[]} */
const fromDate = entries(utxoCohorts.minAge).map(([key, tree]) => {
const names = MIN_AGE_NAMES[key];
return {
name: names.short,
title: names.long,
color: colors[minAgeColors[key]],
tree,
};
});
// Age range cohorts - CohortWithPercentiles (percentiles only)
/** @type {readonly CohortWithPercentiles[]} */
const dateRange = entries(utxoCohorts.ageRange).map(([key, tree]) => {
const names = AGE_RANGE_NAMES[key];
return {
name: names.short,
title: names.long,
color: colors[ageRangeColors[key]],
tree,
};
});
// Epoch cohorts - CohortBasic (neither adjustedSopr nor percentiles)
/** @type {readonly CohortBasic[]} */
const epoch = entries(utxoCohorts.epoch).map(([key, tree]) => {
const names = EPOCH_NAMES[key];
return {
name: names.short,
title: names.long,
color: colors[epochColors[key]],
tree,
};
});
// UTXOs above amount - CohortBasic (neither adjustedSopr nor percentiles)
/** @type {readonly CohortBasic[]} */
const utxosAboveAmount = entries(utxoCohorts.geAmount).map(([key, tree]) => {
const names = GE_AMOUNT_NAMES[key];
return {
name: names.short,
title: names.long,
color: colors[geAmountColors[key]],
tree,
};
});
// Addresses above amount
/** @type {readonly AddressCohortObject[]} */
const addressesAboveAmount = entries(addressCohorts.geAmount).map(
([key, tree]) => {
const names = GE_AMOUNT_NAMES[key];
return {
name: names.short,
title: names.long,
color: colors[geAmountColors[key]],
tree,
};
},
);
// UTXOs under amount - CohortBasic (neither adjustedSopr nor percentiles)
/** @type {readonly CohortBasic[]} */
const utxosUnderAmount = entries(utxoCohorts.ltAmount).map(([key, tree]) => {
const names = LT_AMOUNT_NAMES[key];
return {
name: names.short,
title: names.long,
color: colors[ltAmountColors[key]],
tree,
};
});
// Addresses under amount
/** @type {readonly AddressCohortObject[]} */
const addressesUnderAmount = entries(addressCohorts.ltAmount).map(
([key, tree]) => {
const names = LT_AMOUNT_NAMES[key];
return {
name: names.short,
title: names.long,
color: colors[ltAmountColors[key]],
tree,
};
},
);
// UTXOs amount ranges - CohortBasic (neither adjustedSopr nor percentiles)
/** @type {readonly CohortBasic[]} */
const utxosAmountRanges = entries(utxoCohorts.amountRange).map(
([key, tree]) => {
const names = AMOUNT_RANGE_NAMES[key];
return {
name: names.short,
title: names.long,
color: colors[amountRangeColors[key]],
tree,
};
},
);
// Addresses amount ranges
/** @type {readonly AddressCohortObject[]} */
const addressesAmountRanges = entries(addressCohorts.amountRange).map(
([key, tree]) => {
const names = AMOUNT_RANGE_NAMES[key];
return {
name: names.short,
title: names.long,
color: colors[amountRangeColors[key]],
tree,
};
},
);
// Spendable type cohorts - CohortBasic (neither adjustedSopr nor percentiles)
/** @type {readonly CohortBasic[]} */
const type = entries(utxoCohorts.type).map(([key, tree]) => {
const names = SPENDABLE_TYPE_NAMES[key];
return {
name: names.short,
title: names.long,
color: colors[spendableTypeColors[key]],
tree,
};
});
// Year cohorts - CohortBasic (neither adjustedSopr nor percentiles)
/** @type {readonly CohortBasic[]} */
const year = entries(utxoCohorts.year).map(([key, tree]) => {
const names = YEAR_NAMES[key];
return {
name: names.short,
title: names.long,
color: colors[yearColors[key]],
tree,
};
});
return {
cohortAll,
termShort,
termLong,
upToDate,
fromDate,
dateRange,
epoch,
utxosAboveAmount,
addressesAboveAmount,
utxosUnderAmount,
addressesUnderAmount,
utxosAmountRanges,
addressesAmountRanges,
type,
year,
};
}