website: snapshot

This commit is contained in:
nym21
2026-01-25 13:16:00 +01:00
parent c6f63fd4a2
commit 36b56a400c
7 changed files with 343 additions and 135 deletions
+2 -2
View File
@@ -106,8 +106,8 @@ function initFrameSelectors() {
}
initFrameSelectors();
const brk = new BrkClient("https://next.bitview.space");
// const brk = new BrkClient("/");
// const brk = new BrkClient("https://next.bitview.space");
const brk = new BrkClient("/");
console.log(`VERSION = ${brk.VERSION}`);
+1 -3
View File
@@ -72,7 +72,6 @@ export function buildCohortData(colors, brk) {
};
const longNames = TERM_NAMES.long;
/** @type {CohortWithPercentiles} */
const termLong = {
name: longNames.short,
title: longNames.long,
@@ -104,8 +103,7 @@ export function buildCohortData(colors, brk) {
};
});
// Age range cohorts - CohortWithPercentiles (percentiles only)
/** @type {readonly CohortWithPercentiles[]} */
// Age range cohorts - CohortAgeRange (no nupl)
const dateRange = entries(utxoCohorts.ageRange).map(([key, tree]) => {
const names = AGE_RANGE_NAMES[key];
return {
@@ -11,6 +11,8 @@ export {
createCohortFolderFull,
createCohortFolderWithAdjusted,
createCohortFolderWithPercentiles,
createCohortFolderLongTerm,
createCohortFolderAgeRange,
createCohortFolderBasicWithMarketCap,
createCohortFolderBasicWithoutMarketCap,
createCohortFolderAddress,
+302 -121
View File
@@ -137,6 +137,79 @@ export function createCohortFolderWithAdjusted(ctx, args) {
}
/**
* Long term folder: term.long (has nupl via RelativePattern5)
* @param {PartialContext} ctx
* @param {CohortLongTerm | CohortGroupLongTerm} args
* @returns {PartialOptionsGroup}
*/
export function createCohortFolderLongTerm(ctx, args) {
if ("list" in args) {
const { list } = args;
const title = args.title ? `by ${args.title}` : "";
return {
name: args.name || "all",
tree: [
createGroupedSupplySection(list, title),
createGroupedUtxoCountChart(list, title),
createGroupedRealizedSectionBasic(ctx, list, title),
createGroupedUnrealizedSectionLongTerm(ctx, list, title),
createGroupedCostBasisSectionWithPercentiles(ctx, list, title),
createGroupedActivitySectionBasic(list, title),
],
};
}
const title = args.title ? `of ${args.title}` : "";
return {
name: args.name || "all",
tree: [
createSingleSupplyChart(ctx, args, title),
createSingleUtxoCountChart(args, title),
createSingleRealizedSectionWithPercentiles(ctx, args, title),
createSingleUnrealizedSectionLongTerm(ctx, args, title),
createSingleCostBasisSectionWithPercentiles(ctx, args, title),
createSingleActivitySectionBasic(ctx, args, title),
],
};
}
/**
* Age range folder: ageRange.* (no nupl via RelativePattern2)
* @param {PartialContext} ctx
* @param {CohortAgeRange | CohortGroupAgeRange} args
* @returns {PartialOptionsGroup}
*/
export function createCohortFolderAgeRange(ctx, args) {
if ("list" in args) {
const { list } = args;
const title = args.title ? `by ${args.title}` : "";
return {
name: args.name || "all",
tree: [
createGroupedSupplySection(list, title),
createGroupedUtxoCountChart(list, title),
createGroupedRealizedSectionBasic(ctx, list, title),
createGroupedUnrealizedSectionAgeRange(ctx, list, title),
createGroupedCostBasisSectionWithPercentiles(ctx, list, title),
createGroupedActivitySectionBasic(list, title),
],
};
}
const title = args.title ? `of ${args.title}` : "";
return {
name: args.name || "all",
tree: [
createSingleSupplyChart(ctx, args, title),
createSingleUtxoCountChart(args, title),
createSingleRealizedSectionWithPercentiles(ctx, args, title),
createSingleUnrealizedSectionAgeRange(ctx, args, title),
createSingleCostBasisSectionWithPercentiles(ctx, args, title),
createSingleActivitySectionBasic(ctx, args, title),
],
};
}
/**
* @deprecated Use createCohortFolderLongTerm or createCohortFolderAgeRange instead
* Percentiles folder: percentiles only, no adjustedSopr (term.long, ageRange.*)
* @param {PartialContext} ctx
* @param {CohortWithPercentiles | CohortGroupWithPercentiles} args
@@ -1401,7 +1474,7 @@ function createNetUnrealizedPnlRelToOwnPnlMetrics(ctx, rel) {
/**
* Base unrealized metrics (always present)
* @param {PartialContext} ctx
* @param {{ unrealized: { totalUnrealizedPnl: AnyMetricPattern, unrealizedProfit: AnyMetricPattern, unrealizedLoss: AnyMetricPattern, negUnrealizedLoss: AnyMetricPattern } }} tree
* @param {{ unrealized: UnrealizedPattern }} tree
*/
function createUnrealizedPnlBaseMetrics(ctx, tree) {
const { colors } = ctx;
@@ -1436,7 +1509,7 @@ function createUnrealizedPnlBaseMetrics(ctx, tree) {
/**
* Base net unrealized metric (always present)
* @param {{ unrealized: { netUnrealizedPnl: AnyMetricPattern } }} tree
* @param {{ unrealized: UnrealizedPattern }} tree
*/
function createNetUnrealizedPnlBaseMetric(tree) {
return baseline({
@@ -1446,6 +1519,57 @@ function createNetUnrealizedPnlBaseMetric(tree) {
});
}
// ============================================================================
// Unrealized Chart Builders (composable charts)
// ============================================================================
/**
* Create NUPL chart for single cohort
* @param {PartialContext} ctx
* @param {RelativeWithNupl} rel
* @param {string} title
* @returns {PartialChartOption}
*/
function createNuplChart(ctx, rel, title) {
return {
name: "nupl",
title: `NUPL ${title}`,
bottom: [
baseline({
metric: rel.nupl,
name: "NUPL",
unit: Unit.ratio,
}),
priceLine({ ctx, unit: Unit.ratio }),
],
};
}
/**
* Create NUPL chart for grouped cohorts
* @param {PartialContext} ctx
* @param {readonly { name: string, color: Color, tree: { relative: RelativeWithNupl } }[]} list
* @param {string} title
* @returns {PartialChartOption}
*/
function createGroupedNuplChart(ctx, list, title) {
return {
name: "nupl",
title: `NUPL ${title}`,
bottom: [
...list.map(({ color, name, tree }) =>
baseline({
metric: tree.relative.nupl,
name,
color,
unit: Unit.ratio,
}),
),
priceLine({ ctx, unit: Unit.ratio }),
],
};
}
// ============================================================================
// Unrealized Section Variants (by cohort capability)
// ============================================================================
@@ -1520,18 +1644,7 @@ function createSingleUnrealizedSectionFull(ctx, cohort, title) {
priceLine({ ctx, unit: Unit.pctMcap }),
],
},
{
name: "nupl",
title: `NUPL ${title}`,
bottom: [
baseline({
metric: tree.relative.nupl,
name: "NUPL",
unit: Unit.ratio,
}),
priceLine({ ctx, unit: Unit.ratio }),
],
},
createNuplChart(ctx, tree.relative, title),
],
};
}
@@ -1569,20 +1682,7 @@ function createSingleUnrealizedSectionWithMarketCap(ctx, cohort, title) {
],
},
...("nupl" in tree.relative
? [
{
name: "nupl",
title: `NUPL ${title}`,
bottom: [
baseline({
metric: tree.relative.nupl,
name: "NUPL",
unit: Unit.ratio,
}),
priceLine({ ctx, unit: Unit.ratio }),
],
},
]
? [createNuplChart(ctx, tree.relative, title)]
: []),
],
};
@@ -1622,20 +1722,7 @@ function createSingleUnrealizedSectionWithOwnCaps(ctx, cohort, title) {
],
},
...("nupl" in tree.relative
? [
{
name: "nupl",
title: `NUPL ${title}`,
bottom: [
baseline({
metric: tree.relative.nupl,
name: "NUPL",
unit: Unit.ratio,
}),
priceLine({ ctx, unit: Unit.ratio }),
],
},
]
? [createNuplChart(ctx, tree.relative, title)]
: []),
],
};
@@ -1676,20 +1763,7 @@ function createSingleUnrealizedSectionWithMarketCapOnly(ctx, cohort, title) {
],
},
...("nupl" in tree.relative
? [
{
name: "nupl",
title: `NUPL ${title}`,
bottom: [
baseline({
metric: tree.relative.nupl,
name: "NUPL",
unit: Unit.ratio,
}),
priceLine({ ctx, unit: Unit.ratio }),
],
},
]
? [createNuplChart(ctx, tree.relative, title)]
: []),
],
};
@@ -1823,21 +1897,7 @@ function createGroupedUnrealizedSectionFull(ctx, list, title) {
priceLine({ ctx, unit: Unit.pctOwnPnl }),
],
},
{
name: "nupl",
title: `NUPL ${title}`,
bottom: [
...list.flatMap(({ color, name, tree }) => [
baseline({
metric: tree.relative.nupl,
name,
color,
unit: Unit.ratio,
}),
]),
priceLine({ ctx, unit: Unit.ratio }),
],
},
createGroupedNuplChart(ctx, list, title),
],
};
}
@@ -1876,21 +1936,7 @@ function createGroupedUnrealizedSectionWithMarketCap(ctx, list, title) {
priceLine({ ctx, unit: Unit.pctMcap }),
],
},
{
name: "nupl",
title: `NUPL ${title}`,
bottom: [
...list.flatMap(({ color, name, tree }) => [
baseline({
metric: tree.relative.nupl,
name,
color,
unit: Unit.ratio,
}),
]),
priceLine({ ctx, unit: Unit.ratio }),
],
},
createGroupedNuplChart(ctx, list, title),
],
};
}
@@ -1903,7 +1949,10 @@ function createGroupedUnrealizedSectionWithMarketCap(ctx, list, title) {
* @returns {PartialOptionsGroup}
*/
function createGroupedUnrealizedSectionWithOwnCaps(ctx, list, title) {
const cohortsWithNupl = list.filter(({ tree }) => "nupl" in tree.relative);
const cohortsWithNupl =
/** @type {CohortLongTerm[]} */ (
list.filter(({ tree }) => "nupl" in tree.relative)
);
return {
name: "Unrealized",
tree: [
@@ -1938,25 +1987,7 @@ function createGroupedUnrealizedSectionWithOwnCaps(ctx, list, title) {
],
},
...(cohortsWithNupl.length > 0
? [
{
name: "nupl",
title: `NUPL ${title}`,
bottom: [
...cohortsWithNupl.flatMap(({ color, name, tree }) => [
baseline({
metric: /** @type {{ nupl: AnyMetricPattern }} */ (
tree.relative
).nupl,
name,
color,
unit: Unit.ratio,
}),
]),
priceLine({ ctx, unit: Unit.ratio }),
],
},
]
? [createGroupedNuplChart(ctx, cohortsWithNupl, title)]
: []),
],
};
@@ -1997,21 +2028,7 @@ function createGroupedUnrealizedSectionWithMarketCapOnly(ctx, list, title) {
priceLine({ ctx, unit: Unit.pctMcap }),
],
},
{
name: "nupl",
title: `NUPL ${title}`,
bottom: [
...list.flatMap(({ color, name, tree }) => [
baseline({
metric: tree.relative.nupl,
name,
color,
unit: Unit.ratio,
}),
]),
priceLine({ ctx, unit: Unit.ratio }),
],
},
createGroupedNuplChart(ctx, list, title),
],
};
}
@@ -2047,6 +2064,170 @@ function createGroupedUnrealizedSectionBase(ctx, list, title) {
};
}
/**
* Unrealized section for LongTerm cohort (has nupl via RelativePattern5)
* @param {PartialContext} ctx
* @param {CohortLongTerm} cohort
* @param {string} title
* @returns {PartialOptionsGroup}
*/
function createSingleUnrealizedSectionLongTerm(ctx, cohort, title) {
const { tree } = cohort;
return {
name: "Unrealized",
tree: [
{
name: "pnl",
title: `Unrealized P&L ${title}`,
bottom: [
...createUnrealizedPnlBaseMetrics(ctx, tree),
...createUnrealizedPnlRelToOwnMarketCapMetrics(ctx, tree.relative),
...createUnrealizedPnlRelToOwnPnlMetrics(ctx, tree.relative),
priceLine({ ctx, unit: Unit.usd, defaultActive: false }),
],
},
{
name: "Net pnl",
title: `Net Unrealized P&L ${title}`,
bottom: [
createNetUnrealizedPnlBaseMetric(tree),
...createNetUnrealizedPnlRelToOwnMarketCapMetrics(ctx, tree.relative),
...createNetUnrealizedPnlRelToOwnPnlMetrics(ctx, tree.relative),
priceLine({ ctx, unit: Unit.usd }),
],
},
createNuplChart(ctx, tree.relative, title),
],
};
}
/**
* Grouped unrealized section for LongTerm cohorts (has nupl via RelativePattern5)
* @param {PartialContext} ctx
* @param {readonly CohortLongTerm[]} list
* @param {string} title
* @returns {PartialOptionsGroup}
*/
function createGroupedUnrealizedSectionLongTerm(ctx, list, title) {
return {
name: "Unrealized",
tree: [
...createGroupedUnrealizedBaseCharts(list, title),
{
name: "Net pnl",
title: `Net Unrealized P&L ${title}`,
bottom: [
...list.flatMap(({ color, name, tree }) => [
baseline({
metric: tree.unrealized.netUnrealizedPnl,
name,
color,
unit: Unit.usd,
}),
baseline({
metric: tree.relative.netUnrealizedPnlRelToOwnMarketCap,
name,
color,
unit: Unit.pctOwnMcap,
}),
baseline({
metric: tree.relative.netUnrealizedPnlRelToOwnTotalUnrealizedPnl,
name,
color,
unit: Unit.pctOwnPnl,
}),
]),
priceLine({ ctx, unit: Unit.usd }),
priceLine({ ctx, unit: Unit.pctOwnMcap }),
priceLine({ ctx, unit: Unit.pctOwnPnl }),
],
},
createGroupedNuplChart(ctx, list, title),
],
};
}
/**
* Unrealized section for AgeRange cohort (no nupl via RelativePattern2)
* @param {PartialContext} ctx
* @param {CohortAgeRange} cohort
* @param {string} title
* @returns {PartialOptionsGroup}
*/
function createSingleUnrealizedSectionAgeRange(ctx, cohort, title) {
const { tree } = cohort;
return {
name: "Unrealized",
tree: [
{
name: "pnl",
title: `Unrealized P&L ${title}`,
bottom: [
...createUnrealizedPnlBaseMetrics(ctx, tree),
...createUnrealizedPnlRelToOwnMarketCapMetrics(ctx, tree.relative),
...createUnrealizedPnlRelToOwnPnlMetrics(ctx, tree.relative),
priceLine({ ctx, unit: Unit.usd, defaultActive: false }),
],
},
{
name: "Net pnl",
title: `Net Unrealized P&L ${title}`,
bottom: [
createNetUnrealizedPnlBaseMetric(tree),
...createNetUnrealizedPnlRelToOwnMarketCapMetrics(ctx, tree.relative),
...createNetUnrealizedPnlRelToOwnPnlMetrics(ctx, tree.relative),
priceLine({ ctx, unit: Unit.usd }),
],
},
],
};
}
/**
* Grouped unrealized section for AgeRange cohorts (no nupl via RelativePattern2)
* @param {PartialContext} ctx
* @param {readonly CohortAgeRange[]} list
* @param {string} title
* @returns {PartialOptionsGroup}
*/
function createGroupedUnrealizedSectionAgeRange(ctx, list, title) {
return {
name: "Unrealized",
tree: [
...createGroupedUnrealizedBaseCharts(list, title),
{
name: "Net pnl",
title: `Net Unrealized P&L ${title}`,
bottom: [
...list.flatMap(({ color, name, tree }) => [
baseline({
metric: tree.unrealized.netUnrealizedPnl,
name,
color,
unit: Unit.usd,
}),
baseline({
metric: tree.relative.netUnrealizedPnlRelToOwnMarketCap,
name,
color,
unit: Unit.pctOwnMcap,
}),
baseline({
metric: tree.relative.netUnrealizedPnlRelToOwnTotalUnrealizedPnl,
name,
color,
unit: Unit.pctOwnPnl,
}),
]),
priceLine({ ctx, unit: Unit.usd }),
priceLine({ ctx, unit: Unit.pctOwnMcap }),
priceLine({ ctx, unit: Unit.pctOwnPnl }),
],
},
],
};
}
/**
* Create cost basis section for single cohort WITH percentiles
* @param {PartialContext} ctx
+8 -7
View File
@@ -7,6 +7,8 @@ import {
createCohortFolderFull,
createCohortFolderWithAdjusted,
createCohortFolderWithPercentiles,
createCohortFolderLongTerm,
createCohortFolderAgeRange,
createCohortFolderBasicWithMarketCap,
createCohortFolderBasicWithoutMarketCap,
createCohortFolderAddress,
@@ -55,9 +57,8 @@ export function createPartialOptions({ brk }) {
/** @param {CohortWithAdjusted} cohort */
const mapWithAdjusted = (cohort) =>
createCohortFolderWithAdjusted(ctx, cohort);
/** @param {CohortWithPercentiles} cohort */
const mapWithPercentiles = (cohort) =>
createCohortFolderWithPercentiles(ctx, cohort);
/** @param {CohortAgeRange} cohort */
const mapAgeRange = (cohort) => createCohortFolderAgeRange(ctx, cohort);
/** @param {CohortBasicWithMarketCap} cohort */
const mapBasicWithMarketCap = (cohort) =>
createCohortFolderBasicWithMarketCap(ctx, cohort);
@@ -98,7 +99,7 @@ export function createPartialOptions({ brk }) {
// All UTXOs - CohortAll (adjustedSopr + percentiles but no RelToMarketCap)
createCohortFolderAll(ctx, cohortAll),
// Terms (STH/LTH) - Short is Full, Long is WithPercentiles
// Terms (STH/LTH) - Short is Full, Long is LongTerm
{
name: "Terms",
tree: [
@@ -110,7 +111,7 @@ export function createPartialOptions({ brk }) {
}),
// Individual cohorts with their specific capabilities
createCohortFolderFull(ctx, termShort),
createCohortFolderWithPercentiles(ctx, termLong),
createCohortFolderLongTerm(ctx, termLong),
],
},
@@ -160,12 +161,12 @@ export function createPartialOptions({ brk }) {
{
name: "Range",
tree: [
createCohortFolderWithPercentiles(ctx, {
createCohortFolderAgeRange(ctx, {
name: "Compare",
title: "Age Range",
list: dateRange,
}),
...dateRange.map(mapWithPercentiles),
...dateRange.map(mapAgeRange),
],
},
],
+24
View File
@@ -189,6 +189,20 @@
* @property {Color} color
* @property {PatternWithPercentiles} tree
*
* Long term cohort (term.long) - has nupl via RelativePattern5
* @typedef {Object} CohortLongTerm
* @property {string} name
* @property {string} title
* @property {Color} color
* @property {LongTermPattern} tree
*
* Age range cohort (ageRange.*) - no nupl via RelativePattern2
* @typedef {Object} CohortAgeRange
* @property {string} name
* @property {string} title
* @property {Color} color
* @property {AgeRangePattern} tree
*
* Basic cohort WITH RelToMarketCap (minAge.*, geAmount.*, ltAmount.*)
* @typedef {Object} CohortBasicWithMarketCap
* @property {string} name
@@ -232,6 +246,16 @@
* @property {string} title
* @property {readonly CohortWithPercentiles[]} list
*
* @typedef {Object} CohortGroupLongTerm
* @property {string} name
* @property {string} title
* @property {readonly CohortLongTerm[]} list
*
* @typedef {Object} CohortGroupAgeRange
* @property {string} name
* @property {string} title
* @property {readonly CohortAgeRange[]} list
*
* @typedef {Object} CohortGroupBasicWithMarketCap
* @property {string} name
* @property {string} title
+4 -2
View File
@@ -14,7 +14,7 @@
*
* @import { WebSockets } from "./utils/ws.js"
*
* @import { Option, PartialChartOption, ChartOption, AnyPartialOption, ProcessedOptionAddons, OptionsTree, SimulationOption, AnySeriesBlueprint, SeriesType, AnyFetchedSeriesBlueprint, TableOption, ExplorerOption, UrlOption, PartialOptionsGroup, OptionsGroup, PartialOptionsTree, UtxoCohortObject, AddressCohortObject, CohortObject, CohortGroupObject, FetchedLineSeriesBlueprint, FetchedBaselineSeriesBlueprint, FetchedHistogramSeriesBlueprint, PartialContext, PatternAll, PatternFull, PatternWithAdjusted, PatternWithPercentiles, PatternBasic, PatternBasicWithMarketCap, PatternBasicWithoutMarketCap, CohortAll, CohortFull, CohortWithAdjusted, CohortWithPercentiles, CohortBasic, CohortBasicWithMarketCap, CohortBasicWithoutMarketCap, CohortAddress, CohortGroupFull, CohortGroupWithAdjusted, CohortGroupWithPercentiles, CohortGroupBasic, CohortGroupBasicWithMarketCap, CohortGroupBasicWithoutMarketCap, UtxoCohortGroupObject, AddressCohortGroupObject, FetchedDotsSeriesBlueprint, FetchedCandlestickSeriesBlueprint } from "./options/partial.js"
* @import { Option, PartialChartOption, ChartOption, AnyPartialOption, ProcessedOptionAddons, OptionsTree, SimulationOption, AnySeriesBlueprint, SeriesType, AnyFetchedSeriesBlueprint, TableOption, ExplorerOption, UrlOption, PartialOptionsGroup, OptionsGroup, PartialOptionsTree, UtxoCohortObject, AddressCohortObject, CohortObject, CohortGroupObject, FetchedLineSeriesBlueprint, FetchedBaselineSeriesBlueprint, FetchedHistogramSeriesBlueprint, PartialContext, PatternAll, PatternFull, PatternWithAdjusted, PatternWithPercentiles, PatternBasic, PatternBasicWithMarketCap, PatternBasicWithoutMarketCap, CohortAll, CohortFull, CohortWithAdjusted, CohortWithPercentiles, CohortBasic, CohortBasicWithMarketCap, CohortBasicWithoutMarketCap, CohortAddress, CohortLongTerm, CohortAgeRange, CohortGroupFull, CohortGroupWithAdjusted, CohortGroupWithPercentiles, CohortGroupLongTerm, CohortGroupAgeRange, CohortGroupBasic, CohortGroupBasicWithMarketCap, CohortGroupBasicWithoutMarketCap, UtxoCohortGroupObject, AddressCohortGroupObject, FetchedDotsSeriesBlueprint, FetchedCandlestickSeriesBlueprint } from "./options/partial.js"
*
*
* @import { UnitObject as Unit } from "./utils/units.js"
@@ -61,6 +61,7 @@
* @typedef {Brk.RelativePattern2} OwnRelativePattern
* @typedef {Brk.RelativePattern5} FullRelativePattern
* @typedef {Brk.MetricsTree_Distribution_UtxoCohorts_All_Relative} AllRelativePattern
* @typedef {Brk.UnrealizedPattern} UnrealizedPattern
*/
/**
@@ -120,11 +121,12 @@
* @typedef {GlobalRelativePattern | FullRelativePattern} RelativeWithMarketCap
* @typedef {OwnRelativePattern | FullRelativePattern} RelativeWithOwnMarketCap
* @typedef {OwnRelativePattern | FullRelativePattern | AllRelativePattern} RelativeWithOwnPnl
* @typedef {GlobalRelativePattern | FullRelativePattern} RelativeWithNupl
*
* Capability-based pattern groupings (patterns that have specific properties)
* @typedef {AllUtxoPattern | AgeRangePattern | UtxoAmountPattern} PatternWithRealizedPrice
* @typedef {AllUtxoPattern} PatternWithFullRealized
* @typedef {AllUtxoPattern | AgeRangePattern | UtxoAmountPattern} PatternWithNupl
* @typedef {ShortTermPattern | LongTermPattern | MaxAgePattern | BasicUtxoPattern} PatternWithNupl
* @typedef {AllUtxoPattern | AgeRangePattern | UtxoAmountPattern} PatternWithCostBasis
* @typedef {AllUtxoPattern | AgeRangePattern | UtxoAmountPattern} PatternWithActivity
* @typedef {AllUtxoPattern | AgeRangePattern} PatternWithCostBasisPercentiles