diff --git a/website/scripts/options/distribution/activity.js b/website/scripts/options/distribution/activity.js index 540c9202e..7f6a6c01f 100644 --- a/website/scripts/options/distribution/activity.js +++ b/website/scripts/options/distribution/activity.js @@ -19,6 +19,7 @@ import { ROLLING_WINDOWS, } from "../series.js"; import { + satsBtcUsd, satsBtcUsdFullTree, mapCohortsWithAll, flatMapCohortsWithAll, @@ -298,13 +299,8 @@ export function createGroupedActivitySectionMinimal({ list, all, title }) { tree: ROLLING_WINDOWS.map((w) => ({ name: w.name, title: title(`Volume (${w.title})`), - bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => - line({ - series: tree.activity.transferVolume.sum[w.key].sats, - name, - color, - unit: Unit.sats, - }), + bottom: flatMapCohortsWithAll(list, all, ({ name, color, tree }) => + satsBtcUsd({ pattern: tree.activity.transferVolume.sum[w.key], name, color }), ), })), }; @@ -369,34 +365,51 @@ export function createGroupedActivitySectionWithAdjusted({ list, all, title }) { tree: [ { name: "Volume", - title: title("Sent Volume"), - bottom: flatMapCohortsWithAll(list, all, ({ name, color, tree }) => [ - line({ - series: tree.activity.transferVolume.sum._24h.sats, - name, - color, - unit: Unit.sats, - }), - ]), + tree: ROLLING_WINDOWS.map((w) => ({ + name: w.name, + title: title(`Sent Volume (${w.title})`), + bottom: flatMapCohortsWithAll(list, all, ({ name, color, tree }) => + satsBtcUsd({ pattern: tree.activity.transferVolume.sum[w.key], name, color }), + ), + })), + }, + { + name: "Sent In Profit", + tree: ROLLING_WINDOWS.map((w) => ({ + name: w.name, + title: title(`Sent In Profit (${w.title})`), + bottom: flatMapCohortsWithAll(list, all, ({ name, color, tree }) => + satsBtcUsd({ pattern: tree.activity.transferVolume.inProfit.sum[w.key], name, color }), + ), + })), + }, + { + name: "Sent In Loss", + tree: ROLLING_WINDOWS.map((w) => ({ + name: w.name, + title: title(`Sent In Loss (${w.title})`), + bottom: flatMapCohortsWithAll(list, all, ({ name, color, tree }) => + satsBtcUsd({ pattern: tree.activity.transferVolume.inLoss.sum[w.key], name, color }), + ), + })), + }, + { + name: "Dormancy", + tree: ROLLING_WINDOWS.map((w) => ({ + name: w.name, + title: title(`Dormancy (${w.title})`), + bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => + line({ series: tree.activity.dormancy[w.key], name, color, unit: Unit.days }), + ), + })), }, { name: "SOPR", tree: [ - ...groupedSoprCharts( - list, - all, - (c) => c.tree.realized.sopr.ratio, - title, - ), + ...groupedSoprCharts(list, all, (c) => c.tree.realized.sopr.ratio, title), { name: "Adjusted", - tree: groupedSoprCharts( - list, - all, - (c) => c.tree.realized.sopr.adjusted.ratio, - title, - "Adjusted ", - ), + tree: groupedSoprCharts(list, all, (c) => c.tree.realized.sopr.adjusted.ratio, title, "Adjusted "), }, ], }, @@ -417,15 +430,13 @@ export function createGroupedActivitySectionWithAdjusted({ list, all, title }) { }, { name: "Coindays Destroyed", - title: title("Coindays Destroyed"), - bottom: flatMapCohortsWithAll(list, all, ({ name, color, tree }) => [ - line({ - series: tree.activity.coindaysDestroyed.sum._24h, - name, - color, - unit: Unit.coindays, - }), - ]), + tree: ROLLING_WINDOWS.map((w) => ({ + name: w.name, + title: title(`Coindays Destroyed (${w.title})`), + bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => + line({ series: tree.activity.coindaysDestroyed.sum[w.key], name, color, unit: Unit.coindays }), + ), + })), }, ], }; @@ -442,26 +453,47 @@ export function createGroupedActivitySection({ list, all, title }) { tree: [ { name: "Volume", - title: title("Sent Volume"), - bottom: flatMapCohortsWithAll(list, all, ({ name, color, tree }) => [ - line({ - series: tree.activity.transferVolume.sum._24h.sats, - name, - color, - unit: Unit.sats, - }), - ]), + tree: ROLLING_WINDOWS.map((w) => ({ + name: w.name, + title: title(`Sent Volume (${w.title})`), + bottom: flatMapCohortsWithAll(list, all, ({ name, color, tree }) => + satsBtcUsd({ pattern: tree.activity.transferVolume.sum[w.key], name, color }), + ), + })), + }, + { + name: "Sent In Profit", + tree: ROLLING_WINDOWS.map((w) => ({ + name: w.name, + title: title(`Sent In Profit (${w.title})`), + bottom: flatMapCohortsWithAll(list, all, ({ name, color, tree }) => + satsBtcUsd({ pattern: tree.activity.transferVolume.inProfit.sum[w.key], name, color }), + ), + })), + }, + { + name: "Sent In Loss", + tree: ROLLING_WINDOWS.map((w) => ({ + name: w.name, + title: title(`Sent In Loss (${w.title})`), + bottom: flatMapCohortsWithAll(list, all, ({ name, color, tree }) => + satsBtcUsd({ pattern: tree.activity.transferVolume.inLoss.sum[w.key], name, color }), + ), + })), + }, + { + name: "Dormancy", + tree: ROLLING_WINDOWS.map((w) => ({ + name: w.name, + title: title(`Dormancy (${w.title})`), + bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => + line({ series: tree.activity.dormancy[w.key], name, color, unit: Unit.days }), + ), + })), }, { name: "SOPR", - tree: [ - ...groupedSoprCharts( - list, - all, - (c) => c.tree.realized.sopr.ratio, - title, - ), - ], + tree: groupedSoprCharts(list, all, (c) => c.tree.realized.sopr.ratio, title), }, { name: "Sell Side Risk", @@ -480,15 +512,13 @@ export function createGroupedActivitySection({ list, all, title }) { }, { name: "Coindays Destroyed", - title: title("Coindays Destroyed"), - bottom: flatMapCohortsWithAll(list, all, ({ name, color, tree }) => [ - line({ - series: tree.activity.coindaysDestroyed.sum._24h, - name, - color, - unit: Unit.coindays, - }), - ]), + tree: ROLLING_WINDOWS.map((w) => ({ + name: w.name, + title: title(`Coindays Destroyed (${w.title})`), + bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => + line({ series: tree.activity.coindaysDestroyed.sum[w.key], name, color, unit: Unit.coindays }), + ), + })), }, ], }; @@ -505,15 +535,13 @@ export function createGroupedActivitySectionWithActivity({ list, all, title }) { tree: [ { name: "Volume", - title: title("Sent Volume"), - bottom: flatMapCohortsWithAll(list, all, ({ name, color, tree }) => [ - line({ - series: tree.activity.transferVolume.sum._24h.sats, - name, - color, - unit: Unit.sats, - }), - ]), + tree: ROLLING_WINDOWS.map((w) => ({ + name: w.name, + title: title(`Sent Volume (${w.title})`), + bottom: flatMapCohortsWithAll(list, all, ({ name, color, tree }) => + satsBtcUsd({ pattern: tree.activity.transferVolume.sum[w.key], name, color }), + ), + })), }, { name: "SOPR", @@ -530,15 +558,13 @@ export function createGroupedActivitySectionWithActivity({ list, all, title }) { }, { name: "Coindays Destroyed", - title: title("Coindays Destroyed"), - bottom: flatMapCohortsWithAll(list, all, ({ name, color, tree }) => [ - line({ - series: tree.activity.coindaysDestroyed.sum._24h, - name, - color, - unit: Unit.coindays, - }), - ]), + tree: ROLLING_WINDOWS.map((w) => ({ + name: w.name, + title: title(`Coindays Destroyed (${w.title})`), + bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => + line({ series: tree.activity.coindaysDestroyed.sum[w.key], name, color, unit: Unit.coindays }), + ), + })), }, ], }; diff --git a/website/scripts/options/distribution/cost-basis.js b/website/scripts/options/distribution/cost-basis.js index 5327f396e..5736ff8ad 100644 --- a/website/scripts/options/distribution/cost-basis.js +++ b/website/scripts/options/distribution/cost-basis.js @@ -137,6 +137,13 @@ function groupedWeightFolder({ list, all, getAvgPrice, getInProfit, getInLoss, g { name: "Average", tree: [ + { + name: "All", + title: title(`${avgTitle} Comparison`), + top: mapCohortsWithAll(list, all, (c) => + price({ series: getAvgPrice(c), name: c.name, color: c.color }), + ), + }, { name: "In Profit", title: title(`Cost Basis In Profit (${weightLabel})`), diff --git a/website/scripts/options/distribution/index.js b/website/scripts/options/distribution/index.js index 840979cc3..43735faca 100644 --- a/website/scripts/options/distribution/index.js +++ b/website/scripts/options/distribution/index.js @@ -52,6 +52,7 @@ import { createPricesSectionFull, createPricesSectionBasic, createGroupedPricesSection, + createGroupedPricesSectionFull, } from "./prices.js"; import { createCostBasisSectionWithPercentiles, @@ -342,7 +343,7 @@ export function createGroupedCohortFolderFull({ tree: [ ...createGroupedHoldingsSectionWithRelative({ list, all, title }), createGroupedValuationSectionWithOwnMarketCap({ list, all, title }), - createGroupedPricesSection({ list, all, title }), + createGroupedPricesSectionFull({ list, all, title }), createGroupedCostBasisSectionWithPercentiles({ list, all, title }), createGroupedProfitabilitySectionWithNupl({ list, all, title }), createGroupedActivitySectionWithAdjusted({ list, all, title }), @@ -392,8 +393,8 @@ export function createGroupedCohortFolderWithNupl({ name: name || "all", tree: [ ...createGroupedHoldingsSectionWithRelative({ list, all, title }), - createGroupedValuationSection({ list, all, title }), - createGroupedPricesSection({ list, all, title }), + createGroupedValuationSectionWithOwnMarketCap({ list, all, title }), + createGroupedPricesSectionFull({ list, all, title }), createGroupedCostBasisSectionWithPercentiles({ list, all, title }), createGroupedProfitabilitySectionWithNupl({ list, all, title }), createGroupedActivitySection({ list, all, title }), @@ -417,7 +418,7 @@ export function createGroupedCohortFolderLongTerm({ tree: [ ...createGroupedHoldingsSectionWithRelative({ list, all, title }), createGroupedValuationSectionWithOwnMarketCap({ list, all, title }), - createGroupedPricesSection({ list, all, title }), + createGroupedPricesSectionFull({ list, all, title }), createGroupedCostBasisSectionWithPercentiles({ list, all, title }), createGroupedProfitabilitySectionLongTerm({ list, all, title }), createGroupedActivitySection({ list, all, title }), diff --git a/website/scripts/options/distribution/prices.js b/website/scripts/options/distribution/prices.js index c3d36bcf4..120c9224f 100644 --- a/website/scripts/options/distribution/prices.js +++ b/website/scripts/options/distribution/prices.js @@ -116,31 +116,65 @@ export function createPricesSectionBasic({ cohort, title }) { * @param {{ list: readonly CohortObject[], all: CohortAll, title: (name: string) => string }} args * @returns {PartialOptionsGroup} */ +/** + * @param {readonly CohortObject[]} list + * @param {CohortAll} all + * @param {(name: string) => string} title + * @returns {PartialOptionsTree} + */ +function groupedRealizedPriceItems(list, all, title) { + return [ + { + name: "Realized", + tree: [ + { + name: "Price", + title: title("Realized Price"), + top: mapCohortsWithAll(list, all, ({ name, color, tree }) => + price({ series: tree.realized.price, name, color }), + ), + }, + { + name: "Ratio", + title: title("MVRV"), + bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => + baseline({ series: tree.realized.mvrv, name, color, unit: Unit.ratio, base: 1 }), + ), + }, + ], + }, + ]; +} + +/** @param {{ list: readonly CohortObject[], all: CohortAll, title: (name: string) => string }} args */ export function createGroupedPricesSection({ list, all, title }) { + return { + name: "Prices", + tree: groupedRealizedPriceItems(list, all, title), + }; +} + +/** @param {{ list: readonly (CohortAll | CohortFull | CohortLongTerm)[], all: CohortAll, title: (name: string) => string }} args */ +export function createGroupedPricesSectionFull({ list, all, title }) { return { name: "Prices", tree: [ + ...groupedRealizedPriceItems(list, all, title), { - name: "Realized", + name: "Investor", tree: [ { name: "Price", - title: title("Realized Price"), + title: title("Investor Price"), top: mapCohortsWithAll(list, all, ({ name, color, tree }) => - price({ series: tree.realized.price, name, color }), + price({ series: tree.realized.investor.price, name, color }), ), }, { name: "Ratio", - title: title("MVRV"), + title: title("Investor Price Ratio"), bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => - baseline({ - series: tree.realized.mvrv, - name, - color, - unit: Unit.ratio, - base: 1, - }), + baseline({ series: tree.realized.investor.price.ratio, name, color, unit: Unit.ratio, base: 1 }), ), }, ], diff --git a/website/scripts/options/distribution/profitability.js b/website/scripts/options/distribution/profitability.js index e53b2588f..ebfb436e7 100644 --- a/website/scripts/options/distribution/profitability.js +++ b/website/scripts/options/distribution/profitability.js @@ -718,25 +718,6 @@ function groupedRealizedPnlSum(list, all, title) { * @param {(name: string) => string} title * @returns {PartialOptionsTree} */ -function groupedRealizedPnlSumFull(list, all, title) { - return [ - ...groupedRealizedPnlSum(list, all, title), - { - name: "Total", - title: title("Total Realized P&L"), - bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => - line({ series: tree.realized.grossPnl.cumulative.usd, name, color, unit: Unit.usd }), - ), - }, - { - name: "P/L Ratio", - title: title("Realized Profit/Loss Ratio"), - bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => - baseline({ series: tree.realized.profitToLossRatio._1y, name, color, unit: Unit.ratio, base: 1 }), - ), - }, - ]; -} /** * Grouped rolling realized charts (basic — profit/loss sums only) @@ -768,28 +749,6 @@ function groupedRollingRealizedCharts(list, all, title) { ]; } -/** - * Grouped rolling realized with P/L ratio (full cohorts) - * @param {readonly (CohortAll | CohortFull | CohortLongTerm)[]} list - * @param {CohortAll} all - * @param {(name: string) => string} title - * @returns {PartialOptionsTree} - */ -function groupedRollingRealizedChartsFull(list, all, title) { - return [ - ...groupedRollingRealizedCharts(list, all, title), - { - name: "P/L Ratio", - tree: ROLLING_WINDOWS.map((w) => ({ - name: w.name, - title: title(`Realized P/L Ratio (${w.title})`), - bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => - baseline({ series: tree.realized.profitToLossRatio[w.key], name, color, unit: Unit.ratio, base: 1 }), - ), - })), - }, - ]; -} /** * Grouped realized subfolder (basic) @@ -834,14 +793,12 @@ function groupedRealizedSubfolder(list, all, title) { * @param {readonly (CohortAll | CohortFull | CohortLongTerm)[]} list * @param {CohortAll} all * @param {(name: string) => string} title - * @returns {PartialOptionsGroup} + * @returns {PartialOptionsTree} */ -function groupedRealizedNetPnlDeltaTree(list, all, title) { - return { - name: "Change", - tree: [ +function groupedRealizedNetPnlDeltaItems(list, all, title) { + return [ { - name: "Absolute", + name: "Change", tree: [ { name: "Compare", @@ -882,8 +839,7 @@ function groupedRealizedNetPnlDeltaTree(list, all, title) { })), ], }, - ], - }; + ]; } /** @@ -897,41 +853,108 @@ function groupedRealizedSubfolderFull(list, all, title) { return { name: "Realized", tree: [ - { name: "P&L", tree: groupedRealizedPnlSumFull(list, all, title) }, { name: "Net", - tree: ROLLING_WINDOWS.map((w) => ({ - name: w.name, - title: title(`Net Realized P&L (${w.title})`), - bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => - baseline({ series: tree.realized.netPnl.sum[w.key].usd, name, color, unit: Unit.usd }), - ), - })), - }, - groupedRealizedNetPnlDeltaTree(list, all, title), - { name: "Rolling", tree: groupedRollingRealizedChartsFull(list, all, title) }, - { - name: "Cumulative", tree: [ + ...ROLLING_WINDOWS.map((w) => ({ + name: w.name, + title: title(`Net Realized P&L (${w.title})`), + bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => + baseline({ series: tree.realized.netPnl.sum[w.key].usd, name, color, unit: Unit.usd }), + ), + })), { - name: "Profit", + name: "Cumulative", + title: title("Cumulative Net Realized P&L"), + bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => + baseline({ series: tree.realized.netPnl.cumulative.usd, name, color, unit: Unit.usd }), + ), + }, + ...groupedRealizedNetPnlDeltaItems(list, all, title), + ], + }, + { + name: "Profit", + tree: [ + ...ROLLING_WINDOWS.map((w) => ({ + name: w.name, + title: title(`Realized Profit (${w.title})`), + bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => + line({ series: tree.realized.profit.sum[w.key].usd, name, color, unit: Unit.usd }), + ), + })), + { + name: "Cumulative", title: title("Cumulative Realized Profit"), bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => line({ series: tree.realized.profit.cumulative.usd, name, color, unit: Unit.usd }), ), }, + ], + }, + { + name: "Loss", + tree: [ + ...ROLLING_WINDOWS.map((w) => ({ + name: w.name, + title: title(`Realized Loss (${w.title})`), + bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => + line({ series: tree.realized.loss.sum[w.key].usd, name, color, unit: Unit.usd }), + ), + })), { - name: "Loss", + name: "Cumulative", title: title("Cumulative Realized Loss"), bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => line({ series: tree.realized.loss.cumulative.usd, name, color, unit: Unit.usd }), ), }, - { - name: "Net", - title: title("Cumulative Net Realized P&L"), + ], + }, + { + name: "Gross", + tree: [ + ...ROLLING_WINDOWS.map((w) => ({ + name: w.name, + title: title(`Gross Realized P&L (${w.title})`), bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => - baseline({ series: tree.realized.netPnl.cumulative.usd, name, color, unit: Unit.usd }), + line({ series: tree.realized.grossPnl.sum[w.key].usd, name, color, unit: Unit.usd }), + ), + })), + { + name: "Cumulative", + title: title("Cumulative Gross Realized P&L"), + bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => + line({ series: tree.realized.grossPnl.cumulative.usd, name, color, unit: Unit.usd }), + ), + }, + ], + }, + { + name: "P/L Ratio", + tree: ROLLING_WINDOWS.map((w) => ({ + name: w.name, + title: title(`Realized P/L Ratio (${w.title})`), + bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => + baseline({ series: tree.realized.profitToLossRatio[w.key], name, color, unit: Unit.ratio, base: 1 }), + ), + })), + }, + { + name: "Peak Regret", + tree: [ + ...ROLLING_WINDOWS.map((w) => ({ + name: w.name, + title: title(`Peak Regret (${w.title})`), + bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => + line({ series: tree.realized.peakRegret.sum[w.key].usd, name, color, unit: Unit.usd }), + ), + })), + { + name: "Cumulative", + title: title("Cumulative Peak Regret"), + bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => + line({ series: tree.realized.peakRegret.cumulative.usd, name, color, unit: Unit.usd }), ), }, ], @@ -1234,7 +1257,8 @@ export function createGroupedProfitabilitySectionWithNupl({ list, all, title }) name: "Profitability", tree: [ { name: "Unrealized", tree: groupedUnrealizedWithMarketCap(list, all, title) }, - groupedRealizedSubfolder(list, all, title), + groupedRealizedSubfolderFull(list, all, title), + groupedSentiment(list, all, title), ], }; } diff --git a/website/scripts/options/distribution/valuation.js b/website/scripts/options/distribution/valuation.js index f79ec50e5..63b0fea50 100644 --- a/website/scripts/options/distribution/valuation.js +++ b/website/scripts/options/distribution/valuation.js @@ -95,7 +95,7 @@ export function createValuationSectionFull({ cohort, title }) { line({ series: tree.unrealized.investedCapital.inLoss.usd, name: "In Loss", color: colors.loss, unit: Unit.usd }), ], }, - createRatioChart({ title, pricePattern: tree.realized.price, ratio: tree.realized.price, color, name: "MVRV" }), + { name: "MVRV", title: title("MVRV"), bottom: [baseline({ series: tree.realized.mvrv, name: "MVRV", unit: Unit.ratio, base: 1 })] }, { name: "% of Own Market Cap", title: title("Realized Cap (% of Own Market Cap)"), bottom: percentRatioBaseline({ pattern: tree.realized.cap.toOwnMcap, name: "Rel. to Own Market Cap", color }) }, ...singleDeltaItems(tree, title), ], @@ -158,6 +158,20 @@ export function createGroupedValuationSectionWithOwnMarketCap({ list, all, title line({ series: tree.realized.cap.usd, name, color, unit: Unit.usd }), ), }, + { + name: "In Profit", + title: title("Invested Capital In Profit"), + bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => + line({ series: tree.unrealized.investedCapital.inProfit.usd, name, color, unit: Unit.usd }), + ), + }, + { + name: "In Loss", + title: title("Invested Capital In Loss"), + bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) => + line({ series: tree.unrealized.investedCapital.inLoss.usd, name, color, unit: Unit.usd }), + ), + }, { name: "% of Own Market Cap", title: title("Realized Cap (% of Own Market Cap)"),