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
+12 -51
View File
@@ -2,7 +2,7 @@ import { colors } from "../utils/colors.js";
import { brk } from "../client.js";
import { Unit } from "../utils/units.js";
import { dots, line, baseline, price, rollingWindowsTree, percentRatioDots } from "./series.js";
import { satsBtcUsd } from "./shared.js";
import { satsBtcUsd, priceRatioPercentilesTree } from "./shared.js";
/**
* Create Cointime section
@@ -173,57 +173,18 @@ export function createCointimeSection() {
),
],
},
...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: "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 },
]);
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: "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 },
]);
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 } }),
),
],
},
...prices.map(({ pattern, name, color }) => ({
name,
tree: priceRatioPercentilesTree({
pattern,
title: `${name} Price`,
legend: name,
color,
priceReferences: [
price({ metric: all.realized.price, name: "Realized", color: colors.realized, defaultActive: false }),
],
};
}),
}),
})),
],
},
@@ -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"),
},
],
};
}
+7 -20
View File
@@ -14,6 +14,7 @@ import {
percentRatioBaseline,
ROLLING_WINDOWS,
} from "./series.js";
import { simplePriceRatioTree } from "./shared.js";
import { periodIdToName } from "./utils.js";
/**
@@ -68,26 +69,12 @@ function createMaSubSection(label, averages) {
/** @param {MaPeriod} a */
const toFolder = (a) => ({
name: periodIdToName(a.id, true),
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,
}),
],
},
],
tree: simplePriceRatioTree({
pattern: a.ratio,
title: `${periodIdToName(a.id, true)} ${label}`,
legend: "average",
color: a.color,
}),
});
return {
+19 -12
View File
@@ -19,8 +19,8 @@ import {
createGroupedCohortFolderBasicWithoutMarketCap,
createGroupedCohortFolderAddress,
createGroupedAddressCohortFolder,
createUtxoProfitabilitySection,
} from "./distribution/index.js";
import { createUtxoProfitabilitySection } from "./distribution/utxo-profitability.js";
import { createMarketSection } from "./market.js";
import { createNetworkSection } from "./network.js";
import { createMiningSection } from "./mining.js";
@@ -53,6 +53,9 @@ export function createPartialOptions() {
typeAddressable,
typeOther,
class: class_,
profitabilityRange,
profitabilityProfit,
profitabilityLoss,
} = buildCohortData();
return [
@@ -92,7 +95,7 @@ export function createPartialOptions() {
// Ages cohorts
{
name: "UTXO Ages",
name: "UTXO Age",
tree: [
// Younger Than (< X old)
{
@@ -138,7 +141,7 @@ export function createPartialOptions() {
// Sizes cohorts (UTXO size)
{
name: "UTXO Sizes",
name: "UTXO Size",
tree: [
// Less Than (< X sats)
{
@@ -184,7 +187,7 @@ export function createPartialOptions() {
// Balances cohorts (Address balance)
{
name: "Address Balances",
name: "Address Balance",
tree: [
// Less Than (< X sats)
{
@@ -230,11 +233,11 @@ export function createPartialOptions() {
// Script Types - addressable types have addrCount, others don't
{
name: "Script Types",
name: "Script Type",
tree: [
createGroupedCohortFolderAddress({
name: "Compare",
title: "Script Types",
title: "Script Type",
list: typeAddressable,
all: cohortAll,
}),
@@ -245,11 +248,11 @@ export function createPartialOptions() {
// Epochs
{
name: "Epochs",
name: "Epoch",
tree: [
createGroupedCohortFolderWithAdjusted({
name: "Compare",
title: "Epochs",
title: "Epoch",
list: epoch,
all: cohortAll,
}),
@@ -257,13 +260,13 @@ export function createPartialOptions() {
],
},
// Years
// Classes
{
name: "Years",
name: "Class",
tree: [
createGroupedCohortFolderWithAdjusted({
name: "Compare",
title: "Years",
title: "Class",
list: class_,
all: cohortAll,
}),
@@ -272,7 +275,11 @@ export function createPartialOptions() {
},
// UTXO Profitability bands
createUtxoProfitabilitySection(),
createUtxoProfitabilitySection({
range: profitabilityRange,
profit: profitabilityProfit,
loss: profitabilityLoss,
}),
],
},
+154 -1
View File
@@ -1,7 +1,7 @@
/** Shared helpers for options */
import { Unit } from "../utils/units.js";
import { line, baseline, price } from "./series.js";
import { line, baseline, price, ROLLING_WINDOWS } from "./series.js";
import { priceLine, priceLines } from "./constants.js";
import { colors } from "../utils/colors.js";
@@ -234,6 +234,159 @@ export function satsBtcUsdRolling({ pattern, name, color, defaultActive }) {
return satsBtcUsd({ pattern, name, color, defaultActive });
}
/**
* Build a full Sum / Rolling / Cumulative tree from a FullValuePattern
* @param {Object} args
* @param {FullValuePattern} args.pattern
* @param {string} args.name
* @param {string} args.title
* @param {Color} [args.color]
* @returns {PartialOptionsTree}
*/
export function satsBtcUsdFullTree({ pattern, name, title, color }) {
return [
{
name: "Sum",
title,
bottom: satsBtcUsd({ pattern: pattern.base, name, color }),
},
{
name: "Rolling",
tree: [
{
name: "Compare",
title: `${title} Rolling Sum`,
bottom: ROLLING_WINDOWS.flatMap((w) =>
satsBtcUsd({ pattern: pattern.sum[w.key], name: w.name, color: w.color }),
),
},
...ROLLING_WINDOWS.map((w) => ({
name: w.name,
title: `${title} ${w.name} Rolling Sum`,
bottom: satsBtcUsd({ pattern: pattern.sum[w.key], name: w.name, color: w.color }),
})),
],
},
{
name: "Cumulative",
title: `${title} (Total)`,
bottom: satsBtcUsd({ pattern: pattern.cumulative, name: "all-time", color }),
},
];
}
/**
* Create Price + Ratio charts from a simple price pattern (BpsCentsRatioSatsUsdPattern)
* @param {Object} args
* @param {AnyPricePattern & { ratio: AnyMetricPattern }} args.pattern
* @param {string} args.title
* @param {string} args.legend
* @param {Color} [args.color]
* @returns {PartialOptionsTree}
*/
export function simplePriceRatioTree({ pattern, title, legend, color }) {
return [
{
name: "Price",
title,
top: [price({ metric: pattern, name: legend, color })],
},
{
name: "Ratio",
title: `${title} Ratio`,
top: [price({ metric: pattern, name: legend, color })],
bottom: [
baseline({ metric: pattern.ratio, name: "Ratio", unit: Unit.ratio, base: 1 }),
],
},
];
}
/**
* Create Price + Ratio charts with percentile bands (no SMAs/z-scores)
* @param {Object} args
* @param {PriceRatioPercentilesPattern} args.pattern
* @param {string} args.title
* @param {string} args.legend
* @param {Color} [args.color]
* @param {FetchedPriceSeriesBlueprint[]} [args.priceReferences]
* @returns {PartialOptionsTree}
*/
export function priceRatioPercentilesTree({ pattern, title, legend, color, priceReferences }) {
const p = pattern.percentiles;
const pctUsd = [
{ 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 },
];
const pctRatio = [
{ 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 },
];
return [
{
name: "Price",
title,
top: [
price({ metric: pattern, name: legend, color }),
...(priceReferences ?? []),
...pctUsd.map(({ name, prop, color }) =>
price({ metric: prop, name, color, defaultActive: false, options: { lineStyle: 1 } }),
),
],
},
{
name: "Ratio",
title: `${title} Ratio`,
top: [
price({ metric: pattern, name: legend, color }),
...pctUsd.map(({ name, prop, color }) =>
price({ metric: prop, name, color, defaultActive: false, options: { lineStyle: 1 } }),
),
],
bottom: [
baseline({ metric: pattern.ratio, name: "Ratio", unit: Unit.ratio, base: 1 }),
...pctRatio.map(({ name, prop, color }) =>
line({ metric: prop, name, color, defaultActive: false, unit: Unit.ratio, options: { lineStyle: 1 } }),
),
],
},
];
}
/**
* Create grouped Price + Ratio charts overlaying multiple series
* @param {Object} args
* @param {{ name: string, color?: Color, pattern: AnyPricePattern & { ratio: AnyMetricPattern } }[]} args.list
* @param {string} args.title
* @returns {PartialOptionsTree}
*/
export function groupedSimplePriceRatioTree({ list, title }) {
return [
{
name: "Price",
title,
top: list.map(({ name, color, pattern }) =>
price({ metric: pattern, name, color }),
),
},
{
name: "Ratio",
title: `${title} Ratio`,
bottom: list.map(({ name, color, pattern }) =>
baseline({ metric: pattern.ratio, name, color, unit: Unit.ratio, base: 1 }),
),
},
];
}
/**
* Create coinbase/subsidy/fee rolling sum series from separate sources
* @param {Object} args
+1 -1
View File
@@ -234,7 +234,7 @@
* @property {AgeRangePattern} tree
*
* Age range cohort with matured supply
* @typedef {CohortAgeRange & { matured: AnyValuePattern }} CohortAgeRangeWithMatured
* @typedef {CohortAgeRange & { matured: FullValuePattern }} CohortAgeRangeWithMatured
*
* Basic cohort WITH RelToMarketCap (geAmount.*, ltAmount.*)
* @typedef {Object} CohortBasicWithMarketCap
+8
View File
@@ -26,6 +26,14 @@ function walkMetrics(node, map, path) {
for (const [key, value] of Object.entries(node)) {
const kn = key.toLowerCase();
if (
key === "lookback" ||
key === "cumulativeMarketCap" ||
key === "sd24h" ||
key === "spot" ||
key === "ohlc" ||
key === "state" ||
key === "emaSlow" ||
key === "emaFast" ||
key.endsWith("Raw") ||
key.endsWith("Cents") ||
key.endsWith("State") ||