diff --git a/website/scripts/main.js b/website/scripts/main.js index 6955acd90..9ec2ee7fc 100644 --- a/website/scripts/main.js +++ b/website/scripts/main.js @@ -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}`); diff --git a/website/scripts/options/distribution/data.js b/website/scripts/options/distribution/data.js index 93a7ef68a..62c8b3fe6 100644 --- a/website/scripts/options/distribution/data.js +++ b/website/scripts/options/distribution/data.js @@ -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 { diff --git a/website/scripts/options/distribution/index.js b/website/scripts/options/distribution/index.js index 4662911b5..6d5627927 100644 --- a/website/scripts/options/distribution/index.js +++ b/website/scripts/options/distribution/index.js @@ -11,6 +11,8 @@ export { createCohortFolderFull, createCohortFolderWithAdjusted, createCohortFolderWithPercentiles, + createCohortFolderLongTerm, + createCohortFolderAgeRange, createCohortFolderBasicWithMarketCap, createCohortFolderBasicWithoutMarketCap, createCohortFolderAddress, diff --git a/website/scripts/options/distribution/utxo.js b/website/scripts/options/distribution/utxo.js index 9abf7197a..b7be421c2 100644 --- a/website/scripts/options/distribution/utxo.js +++ b/website/scripts/options/distribution/utxo.js @@ -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 diff --git a/website/scripts/options/partial.js b/website/scripts/options/partial.js index 67a65d3d8..8c6e66273 100644 --- a/website/scripts/options/partial.js +++ b/website/scripts/options/partial.js @@ -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), ], }, ], diff --git a/website/scripts/options/types.js b/website/scripts/options/types.js index 217387a2a..926d9aaef 100644 --- a/website/scripts/options/types.js +++ b/website/scripts/options/types.js @@ -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 diff --git a/website/scripts/types.js b/website/scripts/types.js index 520830402..69f6d2876 100644 --- a/website/scripts/types.js +++ b/website/scripts/types.js @@ -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