diff --git a/website/scripts/chart/capture.js b/website/scripts/chart/capture.js index 29a3ef2e4..fe3f1e297 100644 --- a/website/scripts/chart/capture.js +++ b/website/scripts/chart/capture.js @@ -1,6 +1,6 @@ import { ios, canShare } from "../utils/env.js"; import { style } from "../utils/elements.js"; -import { colors } from "./colors.js"; +import { colors } from "../utils/colors.js"; export const canCapture = !ios || canShare; @@ -90,7 +90,11 @@ export function capture({ screenshot, chartWidth, parent, legends }) { if (hasBottomLegend) { drawLegend( legends.bottom.element, - pad + titleOffset + topLegendOffset + screenshot.height + legendHeight / 2, + pad + + titleOffset + + topLegendOffset + + screenshot.height + + legendHeight / 2, ); } @@ -99,7 +103,11 @@ export function capture({ screenshot, chartWidth, parent, legends }) { ctx.font = `${fontSize}px ${style.fontFamily}`; ctx.textAlign = "right"; ctx.textBaseline = "bottom"; - ctx.fillText(window.location.host, canvas.width - pad, canvas.height - pad / 2); + ctx.fillText( + window.location.host, + canvas.width - pad, + canvas.height - pad / 2, + ); // Open in new tab canvas.toBlob((blob) => { diff --git a/website/scripts/chart/index.js b/website/scripts/chart/index.js index bfb9030ef..65201c835 100644 --- a/website/scripts/chart/index.js +++ b/website/scripts/chart/index.js @@ -8,7 +8,7 @@ import { } from "../modules/lightweight-charts/5.1.0/dist/lightweight-charts.standalone.production.mjs"; import { createLegend } from "./legend.js"; import { capture } from "./capture.js"; -import { colors } from "./colors.js"; +import { colors } from "../utils/colors.js"; import { createRadios, createSelect } from "../utils/dom.js"; import { createPersistedValue } from "../utils/persisted.js"; import { onChange as onThemeChange } from "../utils/theme.js"; diff --git a/website/scripts/client.js b/website/scripts/client.js new file mode 100644 index 000000000..435a910d4 --- /dev/null +++ b/website/scripts/client.js @@ -0,0 +1,8 @@ +import { BrkClient } from "./modules/brk-client/index.js"; + +// const brk = new BrkClient("https://next.bitview.space"); +const brk = new BrkClient("/"); + +console.log(`VERSION = ${brk.VERSION}`); + +export { brk }; diff --git a/website/scripts/main.js b/website/scripts/main.js index 9ec2ee7fc..69d389025 100644 --- a/website/scripts/main.js +++ b/website/scripts/main.js @@ -1,7 +1,6 @@ import { webSockets } from "./utils/ws.js"; import * as formatters from "./utils/format.js"; import { onFirstIntersection, getElementById, isHidden } from "./utils/dom.js"; -import { BrkClient } from "./modules/brk-client/index.js"; import { initOptions } from "./options/full.js"; import { init as initChart, @@ -106,17 +105,12 @@ function initFrameSelectors() { } initFrameSelectors(); -// const brk = new BrkClient("https://next.bitview.space"); -const brk = new BrkClient("/"); - -console.log(`VERSION = ${brk.VERSION}`); - webSockets.kraken1dCandle.onLatest((latest) => { console.log("close:", latest.close); window.document.title = `${latest.close.toLocaleString("en-us")} | ${window.location.host}`; }); -const options = initOptions(brk); +const options = initOptions(); window.addEventListener("popstate", (_event) => { const path = window.document.location.pathname.split("/").filter((v) => v); @@ -158,7 +152,7 @@ function initSelected() { element = chartElement; if (firstTimeLoadingChart) { - initChart(brk); + initChart(); } firstTimeLoadingChart = false; diff --git a/website/scripts/options/cointime.js b/website/scripts/options/cointime.js index 791fad05a..ba93fc3f0 100644 --- a/website/scripts/options/cointime.js +++ b/website/scripts/options/cointime.js @@ -1,14 +1,14 @@ +import { colors } from "../utils/colors.js"; +import { brk } from "../client.js"; import { Unit } from "../utils/units.js"; import { dots, line, price } from "./series.js"; import { satsBtcUsd, createPriceRatioCharts } from "./shared.js"; /** * Create Cointime section - * @param {PartialContext} ctx * @returns {PartialOptionsGroup} */ -export function createCointimeSection(ctx) { - const { colors, brk } = ctx; +export function createCointimeSection() { const { cointime, distribution, supply } = brk.metrics; const { pricing, @@ -141,8 +141,16 @@ export function createCointimeSection(ctx) { name: "Compare", title: "Cointime Prices", top: [ - price({ metric: all.realized.realizedPrice, name: "Realized", color: colors.orange }), - price({ metric: all.realized.investorPrice, name: "Investor", color: colors.fuchsia }), + price({ + metric: all.realized.realizedPrice, + name: "Realized", + color: colors.orange, + }), + price({ + metric: all.realized.investorPrice, + name: "Investor", + color: colors.fuchsia, + }), ...prices.map(({ pricePattern, name, color }) => price({ metric: pricePattern, name, color }), ), @@ -150,13 +158,20 @@ export function createCointimeSection(ctx) { }, ...prices.map(({ pricePattern, ratio, name, color }) => ({ name, - tree: createPriceRatioCharts(ctx, { + tree: createPriceRatioCharts({ context: `${name} Price`, legend: name, pricePattern, ratio, color, - priceReferences: [price({ metric: all.realized.realizedPrice, name: "Realized", color: colors.orange, defaultActive: false })], + priceReferences: [ + price({ + metric: all.realized.realizedPrice, + name: "Realized", + color: colors.orange, + defaultActive: false, + }), + ], }), })), ], diff --git a/website/scripts/options/colors/cohorts.js b/website/scripts/options/colors/cohorts.js deleted file mode 100644 index a15b6aad5..000000000 --- a/website/scripts/options/colors/cohorts.js +++ /dev/null @@ -1,175 +0,0 @@ -/** Cohort color mappings */ - -/** @type {Readonly>} */ -export const termColors = { - short: "yellow", - long: "fuchsia", -}; - -/** @type {Readonly>} */ -export const maxAgeColors = { - _1w: "red", - _1m: "orange", - _2m: "amber", - _3m: "yellow", - _4m: "lime", - _5m: "green", - _6m: "teal", - _1y: "sky", - _2y: "indigo", - _3y: "violet", - _4y: "purple", - _5y: "fuchsia", - _6y: "pink", - _7y: "red", - _8y: "orange", - _10y: "amber", - _12y: "yellow", - _15y: "lime", -}; - -/** @type {Readonly>} */ -export const minAgeColors = { - _1d: "red", - _1w: "orange", - _1m: "yellow", - _2m: "lime", - _3m: "green", - _4m: "teal", - _5m: "cyan", - _6m: "blue", - _1y: "indigo", - _2y: "violet", - _3y: "purple", - _4y: "fuchsia", - _5y: "pink", - _6y: "rose", - _7y: "red", - _8y: "orange", - _10y: "yellow", - _12y: "lime", -}; - -/** @type {Readonly>} */ -export const ageRangeColors = { - upTo1d: "pink", - _1dTo1w: "red", - _1wTo1m: "orange", - _1mTo2m: "yellow", - _2mTo3m: "yellow", - _3mTo4m: "lime", - _4mTo5m: "lime", - _5mTo6m: "lime", - _6mTo1y: "green", - _1yTo2y: "cyan", - _2yTo3y: "blue", - _3yTo4y: "indigo", - _4yTo5y: "violet", - _5yTo6y: "purple", - _6yTo7y: "purple", - _7yTo8y: "fuchsia", - _8yTo10y: "fuchsia", - _10yTo12y: "pink", - _12yTo15y: "red", - from15y: "orange", -}; - -/** @type {Readonly>} */ -export const epochColors = { - _0: "red", - _1: "yellow", - _2: "orange", - _3: "lime", - _4: "green", -}; - -/** @type {Readonly>} */ -export const geAmountColors = { - _1sat: "orange", - _10sats: "orange", - _100sats: "yellow", - _1kSats: "lime", - _10kSats: "green", - _100kSats: "cyan", - _1mSats: "blue", - _10mSats: "indigo", - _1btc: "purple", - _10btc: "violet", - _100btc: "fuchsia", - _1kBtc: "pink", - _10kBtc: "red", -}; - -/** @type {Readonly>} */ -export const ltAmountColors = { - _10sats: "orange", - _100sats: "yellow", - _1kSats: "lime", - _10kSats: "green", - _100kSats: "cyan", - _1mSats: "blue", - _10mSats: "indigo", - _1btc: "purple", - _10btc: "violet", - _100btc: "fuchsia", - _1kBtc: "pink", - _10kBtc: "red", - _100kBtc: "orange", -}; - -/** @type {Readonly>} */ -export const amountRangeColors = { - _0sats: "red", - _1satTo10sats: "orange", - _10satsTo100sats: "yellow", - _100satsTo1kSats: "lime", - _1kSatsTo10kSats: "green", - _10kSatsTo100kSats: "cyan", - _100kSatsTo1mSats: "blue", - _1mSatsTo10mSats: "indigo", - _10mSatsTo1btc: "purple", - _1btcTo10btc: "violet", - _10btcTo100btc: "fuchsia", - _100btcTo1kBtc: "pink", - _1kBtcTo10kBtc: "red", - _10kBtcTo100kBtc: "orange", - _100kBtcOrMore: "yellow", -}; - -/** @type {Readonly>} */ -export const spendableTypeColors = { - p2pk65: "red", - p2pk33: "orange", - p2pkh: "yellow", - p2ms: "lime", - p2sh: "green", - p2wpkh: "teal", - p2wsh: "blue", - p2tr: "indigo", - p2a: "purple", - opreturn: "pink", - unknown: "violet", - empty: "fuchsia", -}; - -/** @type {Readonly>} */ -export const yearColors = { - _2009: "red", - _2010: "orange", - _2011: "amber", - _2012: "yellow", - _2013: "lime", - _2014: "green", - _2015: "teal", - _2016: "cyan", - _2017: "sky", - _2018: "blue", - _2019: "indigo", - _2020: "violet", - _2021: "purple", - _2022: "fuchsia", - _2023: "pink", - _2024: "rose", - _2025: "red", - _2026: "orange", -}; diff --git a/website/scripts/options/colors/index.js b/website/scripts/options/colors/index.js deleted file mode 100644 index c7a2d2482..000000000 --- a/website/scripts/options/colors/index.js +++ /dev/null @@ -1,15 +0,0 @@ -// Re-export all color mappings -export { - termColors, - maxAgeColors, - minAgeColors, - ageRangeColors, - epochColors, - geAmountColors, - ltAmountColors, - amountRangeColors, - spendableTypeColors, - yearColors, -} from "./cohorts.js"; - -export { averageColors, dcaColors } from "./misc.js"; diff --git a/website/scripts/options/colors/misc.js b/website/scripts/options/colors/misc.js deleted file mode 100644 index c03e1968a..000000000 --- a/website/scripts/options/colors/misc.js +++ /dev/null @@ -1,42 +0,0 @@ -/** Miscellaneous color mappings for DCA and averages */ - -/** - * Moving average period colors - * Format: [periodId, days, colorName] - * @type {readonly [string, number, ColorName][]} - */ -export const averageColors = [ - ["1w", 7, "red"], - ["8d", 8, "orange"], - ["13d", 13, "amber"], - ["21d", 21, "yellow"], - ["1m", 30, "lime"], - ["34d", 34, "green"], - ["55d", 55, "emerald"], - ["89d", 89, "teal"], - ["144d", 144, "cyan"], - ["200d", 200, "sky"], - ["1y", 365, "blue"], - ["2y", 730, "indigo"], - ["200w", 1400, "violet"], - ["4y", 1460, "purple"], -]; - -/** - * DCA class colors by year - * Format: [year, colorName, defaultActive] - * @type {readonly [number, ColorName, boolean][]} - */ -export const dcaColors = [ - [2015, "pink", false], - [2016, "red", false], - [2017, "orange", true], - [2018, "yellow", true], - [2019, "green", true], - [2020, "teal", true], - [2021, "sky", true], - [2022, "blue", true], - [2023, "purple", true], - [2024, "fuchsia", true], - [2025, "pink", true], -]; diff --git a/website/scripts/options/constants.js b/website/scripts/options/constants.js index 64db477eb..cc76224ed 100644 --- a/website/scripts/options/constants.js +++ b/website/scripts/options/constants.js @@ -1,5 +1,7 @@ /** Constant helpers for creating price lines and reference lines */ +import { colors } from "../utils/colors.js"; +import { brk } from "../client.js"; import { line } from "./series.js"; /** @@ -23,14 +25,14 @@ export function getConstant(constants, num) { /** * Create a price line series (horizontal reference line) - * @param {{ ctx: PartialContext, number?: number, name?: string } & Omit<(Parameters)[0], 'name' | 'metric'>} args + * @param {{ number?: number, name?: string } & Omit<(Parameters)[0], 'name' | 'metric'>} args */ export function priceLine(args) { return line({ ...args, - metric: getConstant(args.ctx.brk.metrics.constants, args.number || 0), + metric: getConstant(brk.metrics.constants, args.number || 0), name: args.name || `${args.number ?? 0}`, - color: args.color ?? args.ctx.colors.gray, + color: args.color ?? colors.gray, options: { lineStyle: args.style ?? 4, lastValueVisible: false, diff --git a/website/scripts/options/context.js b/website/scripts/options/context.js deleted file mode 100644 index 18cba71ec..000000000 --- a/website/scripts/options/context.js +++ /dev/null @@ -1,45 +0,0 @@ -import { - fromBaseStatsPattern, - fromStatsPattern, - chartsFromFull, - chartsFromSum, - chartsFromValueFull, -} from "./series.js"; -import { colors } from "../chart/colors.js"; - -/** - * @template {(arg: any, ...args: any[]) => any} F - * @typedef {F extends (arg: any, ...args: infer P) => infer R ? (...args: P) => R : never} OmitFirstArg - */ - -/** @typedef {ReturnType} PartialContext */ - -/** - * @template {(colors: Colors, ...args: any[]) => any} T - * @param {T} fn - * @returns {OmitFirstArg} - */ -const bind = (fn) => - /** @type {any} */ ( - // @ts-ignore - (...args) => fn(colors, ...args) - ); - -/** - * Create a context object with all dependencies for building partial options - * @param {Object} args - * @param {BrkClient} args.brk - */ -export function createContext({ brk }) { - return { - colors, - brk, - // Series helpers (return series arrays for a single chart) - fromBaseStatsPattern: bind(fromBaseStatsPattern), - fromStatsPattern: bind(fromStatsPattern), - // Chart helpers (return chart trees for Sum/Distribution/Cumulative folders) - chartsFromFull: bind(chartsFromFull), - chartsFromSum: bind(chartsFromSum), - chartsFromValueFull: bind(chartsFromValueFull), - }; -} diff --git a/website/scripts/options/distribution/address.js b/website/scripts/options/distribution/address.js index cf72095bb..816e3aa09 100644 --- a/website/scripts/options/distribution/address.js +++ b/website/scripts/options/distribution/address.js @@ -4,6 +4,7 @@ * Address cohorts use _0satsPattern which has CostBasisPattern (no percentiles) */ +import { colors } from "../../utils/colors.js"; import { Unit } from "../../utils/units.js"; import { priceLine } from "../constants.js"; import { line, baseline, price } from "../series.js"; @@ -35,11 +36,10 @@ import { /** * Create a cohort folder for address cohorts * Includes address count section (addrCount exists on AddressCohortObject) - * @param {PartialContext} ctx * @param {AddressCohortObject | AddressCohortGroupObject} args * @returns {PartialOptionsGroup} */ -export function createAddressCohortFolder(ctx, args) { +export function createAddressCohortFolder(args) { const list = "list" in args ? args.list : [args]; const useGroupName = "list" in args; const isSingle = !("list" in args); @@ -55,10 +55,8 @@ export function createAddressCohortFolder(ctx, args) { name: "Supply", title: title("Supply"), bottom: createSingleSupplySeries( - ctx, /** @type {AddressCohortObject} */ (args), createSingleSupplyRelativeOptions( - ctx, /** @type {AddressCohortObject} */ (args), ), ), @@ -80,7 +78,7 @@ export function createAddressCohortFolder(ctx, args) { { name: "Address Count", title: title("Address Count"), - bottom: createAddressCountSeries(ctx, list, useGroupName), + bottom: createAddressCountSeries(list, useGroupName), }, // Realized section @@ -122,7 +120,7 @@ export function createAddressCohortFolder(ctx, args) { { name: "Capitalization", title: title("Realized Cap"), - bottom: createRealizedCapWithExtras(ctx, list, args, useGroupName), + bottom: createRealizedCapWithExtras(list, args, useGroupName), }, { name: "Value", @@ -137,9 +135,8 @@ export function createAddressCohortFolder(ctx, args) { ), }, ...(useGroupName - ? createGroupedRealizedPnlSection(ctx, list, title) + ? createGroupedRealizedPnlSection(list, title) : createRealizedPnlSection( - ctx, /** @type {AddressCohortObject} */ (args), title, )), @@ -147,7 +144,7 @@ export function createAddressCohortFolder(ctx, args) { }, // Unrealized section - ...createUnrealizedSection(ctx, list, useGroupName, title), + ...createUnrealizedSection(list, useGroupName, title), // Cost basis section (no percentiles for address cohorts) ...createCostBasisSection(list, useGroupName, title), @@ -199,13 +196,12 @@ function createRealizedPriceOptions(args, title) { /** * Create realized cap with extras - * @param {PartialContext} ctx * @param {readonly AddressCohortObject[]} list * @param {AddressCohortObject | AddressCohortGroupObject} args * @param {boolean} useGroupName * @returns {AnyFetchedSeriesBlueprint[]} */ -function createRealizedCapWithExtras(ctx, list, args, useGroupName) { +function createRealizedCapWithExtras(list, args, useGroupName) { const isSingle = !("list" in args); return list.flatMap(({ color, name, tree }) => [ @@ -231,13 +227,11 @@ function createRealizedCapWithExtras(ctx, list, args, useGroupName) { /** * Create realized PnL section for single cohort - * @param {PartialContext} ctx * @param {AddressCohortObject} args * @param {(metric: string) => string} title * @returns {PartialOptionsTree} */ -function createRealizedPnlSection(ctx, args, title) { - const { colors } = ctx; +function createRealizedPnlSection(args, title) { const { realized } = args.tree; return [ @@ -381,16 +375,13 @@ function createRealizedPnlSection(ctx, args, title) { unit: Unit.pctMcap, }), priceLine({ - ctx, unit: Unit.usd, number: 1, }), priceLine({ - ctx, unit: Unit.pctMcap, }), priceLine({ - ctx, unit: Unit.pctRcap, }), ], @@ -401,7 +392,6 @@ function createRealizedPnlSection(ctx, args, title) { bottom: [ ...createSingleSoprSeries(colors, args.tree), priceLine({ - ctx, unit: Unit.ratio, number: 1, }), @@ -575,12 +565,11 @@ function createRealizedPnlSection(ctx, args, title) { /** * Create grouped realized P&L section for address cohorts (for compare view) - * @param {PartialContext} ctx * @param {readonly AddressCohortObject[]} list * @param {(metric: string) => string} title * @returns {PartialOptionsTree} */ -function createGroupedRealizedPnlSection(ctx, list, title) { +function createGroupedRealizedPnlSection(list, title) { const pnlConfigs = /** @type {const} */ ([ { name: "Profit", @@ -814,15 +803,12 @@ function createGroupedRealizedPnlSection(ctx, list, title) { /** * Create unrealized section - * @param {PartialContext} ctx * @param {readonly AddressCohortObject[]} list * @param {boolean} useGroupName * @param {(metric: string) => string} title * @returns {PartialOptionsTree} */ -function createUnrealizedSection(ctx, list, useGroupName, title) { - const { colors } = ctx; - +function createUnrealizedSection(list, useGroupName, title) { return [ { name: "Unrealized", @@ -1005,7 +991,6 @@ function createUnrealizedSection(ctx, list, useGroupName, title) { unit: Unit.ratio, }), priceLine({ - ctx, unit: Unit.ratio, }), ]), @@ -1021,7 +1006,6 @@ function createUnrealizedSection(ctx, list, useGroupName, title) { unit: Unit.usd, }), priceLine({ - ctx, unit: Unit.usd, }), ]), diff --git a/website/scripts/options/distribution/data.js b/website/scripts/options/distribution/data.js index 1547f2d9a..d960346c3 100644 --- a/website/scripts/options/distribution/data.js +++ b/website/scripts/options/distribution/data.js @@ -1,18 +1,8 @@ /** Build cohort data arrays from brk.metrics */ -import { - termColors, - maxAgeColors, - minAgeColors, - ageRangeColors, - epochColors, - geAmountColors, - ltAmountColors, - amountRangeColors, - spendableTypeColors, - yearColors, -} from "../colors/index.js"; +import { colors } from "../../utils/colors.js"; import { entries } from "../../utils/array.js"; +import { brk } from "../../client.js"; /** @type {readonly AddressableType[]} */ const ADDRESSABLE_TYPES = [ @@ -32,10 +22,8 @@ const isAddressable = (key) => /** * Build all cohort data from brk tree - * @param {Colors} colors - * @param {BrkClient} brk */ -export function buildCohortData(colors, brk) { +export function buildCohortData() { const utxoCohorts = brk.metrics.distribution.utxoCohorts; const addressCohorts = brk.metrics.distribution.addressCohorts; const { addrCount } = brk.metrics.distribution; @@ -66,7 +54,7 @@ export function buildCohortData(colors, brk) { const termShort = { name: shortNames.short, title: shortNames.long, - color: colors[termColors.short], + color: colors.term.short, tree: utxoCohorts.term.short, }; @@ -74,7 +62,7 @@ export function buildCohortData(colors, brk) { const termLong = { name: longNames.short, title: longNames.long, - color: colors[termColors.long], + color: colors.term.long, tree: utxoCohorts.term.long, }; @@ -84,7 +72,7 @@ export function buildCohortData(colors, brk) { return { name: names.short, title: `UTXOs ${names.long}`, - color: colors[maxAgeColors[key]], + color: colors.age[key], tree, }; }); @@ -95,7 +83,7 @@ export function buildCohortData(colors, brk) { return { name: names.short, title: `UTXOs ${names.long}`, - color: colors[minAgeColors[key]], + color: colors.age[key], tree, }; }); @@ -106,7 +94,7 @@ export function buildCohortData(colors, brk) { return { name: names.short, title: `UTXOs ${names.long}`, - color: colors[ageRangeColors[key]], + color: colors.ageRange[key], tree, }; }); @@ -117,7 +105,7 @@ export function buildCohortData(colors, brk) { return { name: names.short, title: names.long, - color: colors[epochColors[key]], + color: colors.epoch[key], tree, }; }); @@ -128,7 +116,7 @@ export function buildCohortData(colors, brk) { return { name: names.short, title: `UTXOs ${names.long}`, - color: colors[geAmountColors[key]], + color: colors.amount[key], tree, }; }); @@ -140,7 +128,7 @@ export function buildCohortData(colors, brk) { return { name: names.short, title: `Addresses ${names.long}`, - color: colors[geAmountColors[key]], + color: colors.amount[key], tree, }; }, @@ -152,7 +140,7 @@ export function buildCohortData(colors, brk) { return { name: names.short, title: `UTXOs ${names.long}`, - color: colors[ltAmountColors[key]], + color: colors.amount[key], tree, }; }); @@ -164,7 +152,7 @@ export function buildCohortData(colors, brk) { return { name: names.short, title: `Addresses ${names.long}`, - color: colors[ltAmountColors[key]], + color: colors.amount[key], tree, }; }, @@ -177,7 +165,7 @@ export function buildCohortData(colors, brk) { return { name: names.short, title: `UTXOs ${names.long}`, - color: colors[amountRangeColors[key]], + color: colors.amountRange[key], tree, }; }, @@ -190,7 +178,7 @@ export function buildCohortData(colors, brk) { return { name: names.short, title: `Addresses ${names.long}`, - color: colors[amountRangeColors[key]], + color: colors.amountRange[key], tree, }; }, @@ -202,7 +190,7 @@ export function buildCohortData(colors, brk) { return { name: names.short, title: names.short, - color: colors[spendableTypeColors[key]], + color: colors.scriptType[key], tree: utxoCohorts.type[key], addrCount: addrCount[key], }; @@ -215,7 +203,7 @@ export function buildCohortData(colors, brk) { return { name: names.short, title: names.short, - color: colors[spendableTypeColors[key]], + color: colors.scriptType[key], tree, }; }); @@ -226,7 +214,7 @@ export function buildCohortData(colors, brk) { return { name: names.short, title: names.long, - color: colors[yearColors[key]], + color: colors.year[key], tree, }; }); diff --git a/website/scripts/options/distribution/shared.js b/website/scripts/options/distribution/shared.js index 0fc79425b..60905df58 100644 --- a/website/scripts/options/distribution/shared.js +++ b/website/scripts/options/distribution/shared.js @@ -1,9 +1,14 @@ /** Shared cohort chart section builders */ +import { colors } from "../../utils/colors.js"; import { Unit } from "../../utils/units.js"; import { priceLine } from "../constants.js"; import { baseline, dots, line, price } from "../series.js"; -import { satsBtcUsd, createPriceRatioCharts, formatCohortTitle } from "../shared.js"; +import { + satsBtcUsd, + createPriceRatioCharts, + formatCohortTitle, +} from "../shared.js"; // ============================================================================ // Generic Price Helpers @@ -12,15 +17,20 @@ import { satsBtcUsd, createPriceRatioCharts, formatCohortTitle } from "../shared /** * Create price folder (price + ratio + z-scores wrapped in folder) * For cohorts with full extended ratio metrics (ActivePriceRatioPattern) - * @param {PartialContext} ctx * @param {{ name: string, cohortTitle?: string, priceMetric: ActivePricePattern, ratioPattern: AnyRatioPattern, color: Color }} args * @returns {PartialOptionsGroup} */ -export function createPriceFolder(ctx, { name, cohortTitle, priceMetric, ratioPattern, color }) { +export function createPriceFolder({ + name, + cohortTitle, + priceMetric, + ratioPattern, + color, +}) { const context = cohortTitle ? `${cohortTitle} ${name}` : name; return { name, - tree: createPriceRatioCharts(ctx, { + tree: createPriceRatioCharts({ context, legend: name, pricePattern: priceMetric, @@ -37,7 +47,13 @@ export function createPriceFolder(ctx, { name, cohortTitle, priceMetric, ratioPa * @param {{ name: string, context: string, priceMetric: ActivePricePattern, ratioMetric: R, color: Color }} args * @returns {PartialOptionsTree} */ -export function createBasicPriceCharts({ name, context, priceMetric, ratioMetric, color }) { +export function createBasicPriceCharts({ + name, + context, + priceMetric, + ratioMetric, + color, +}) { return [ { name: "Price", @@ -67,11 +83,23 @@ export function createBasicPriceCharts({ name, context, priceMetric, ratioMetric * @param {{ name: string, cohortTitle?: string, priceMetric: ActivePricePattern, ratioMetric: R, color: Color }} args * @returns {PartialOptionsGroup} */ -export function createBasicPriceFolder({ name, cohortTitle, priceMetric, ratioMetric, color }) { +export function createBasicPriceFolder({ + name, + cohortTitle, + priceMetric, + ratioMetric, + color, +}) { const context = cohortTitle ? `${cohortTitle} ${name}` : name; return { name, - tree: createBasicPriceCharts({ name, context, priceMetric, ratioMetric, color }), + tree: createBasicPriceCharts({ + name, + context, + priceMetric, + ratioMetric, + color, + }), }; } @@ -81,7 +109,13 @@ export function createBasicPriceFolder({ name, cohortTitle, priceMetric, ratioMe * @param {{ name: string, title: (metric: string) => string, list: readonly T[], getPrice: (tree: T['tree']) => ActivePricePattern, getRatio: (tree: T['tree']) => AnyMetricPattern }} args * @returns {PartialOptionsTree} */ -export function createGroupedPriceCharts({ name, title, list, getPrice, getRatio }) { +export function createGroupedPriceCharts({ + name, + title, + list, + getPrice, + getRatio, +}) { return [ { name: "Price", @@ -94,7 +128,13 @@ export function createGroupedPriceCharts({ name, title, list, getPrice, getRatio name: "Ratio", title: title(`${name} Ratio`), bottom: list.map(({ color, name: cohortName, tree }) => - baseline({ metric: getRatio(tree), name: cohortName, color, unit: Unit.ratio, base: 1 }), + baseline({ + metric: getRatio(tree), + name: cohortName, + color, + unit: Unit.ratio, + base: 1, + }), ), }, ]; @@ -106,7 +146,13 @@ export function createGroupedPriceCharts({ name, title, list, getPrice, getRatio * @param {{ name: string, title: (metric: string) => string, list: readonly T[], getPrice: (tree: T['tree']) => ActivePricePattern, getRatio: (tree: T['tree']) => AnyMetricPattern }} args * @returns {PartialOptionsGroup} */ -export function createGroupedPriceFolder({ name, title, list, getPrice, getRatio }) { +export function createGroupedPriceFolder({ + name, + title, + list, + getPrice, + getRatio, +}) { return { name, tree: createGroupedPriceCharts({ name, title, list, getPrice, getRatio }), @@ -115,20 +161,38 @@ export function createGroupedPriceFolder({ name, title, list, getPrice, getRatio /** * Create base supply series (without relative metrics) - * @param {PartialContext} ctx * @param {CohortObject | CohortWithoutRelative} cohort * @returns {AnyFetchedSeriesBlueprint[]} */ -function createSingleSupplySeriesBase(ctx, cohort) { - const { colors } = ctx; +function createSingleSupplySeriesBase(cohort) { const { tree } = cohort; return [ - ...satsBtcUsd({ pattern: tree.supply.total, name: "Supply", color: colors.default }), - ...satsBtcUsd({ pattern: tree.supply._30dChange, name: "30d Change", color: colors.orange }), - ...satsBtcUsd({ pattern: tree.unrealized.supplyInProfit, name: "In Profit", color: colors.green }), - ...satsBtcUsd({ pattern: tree.unrealized.supplyInLoss, name: "In Loss", color: colors.red }), - ...satsBtcUsd({ pattern: tree.supply.halved, name: "half", color: colors.gray }).map((s) => ({ + ...satsBtcUsd({ + pattern: tree.supply.total, + name: "Supply", + color: colors.default, + }), + ...satsBtcUsd({ + pattern: tree.supply._30dChange, + name: "30d Change", + color: colors.orange, + }), + ...satsBtcUsd({ + pattern: tree.unrealized.supplyInProfit, + name: "In Profit", + color: colors.green, + }), + ...satsBtcUsd({ + pattern: tree.unrealized.supplyInLoss, + name: "In Loss", + color: colors.red, + }), + ...satsBtcUsd({ + pattern: tree.supply.halved, + name: "half", + color: colors.gray, + }).map((s) => ({ ...s, options: { lineStyle: 4 }, })), @@ -137,12 +201,10 @@ function createSingleSupplySeriesBase(ctx, cohort) { /** * Create supply relative to own supply metrics - * @param {PartialContext} ctx * @param {UtxoCohortObject | AddressCohortObject} cohort * @returns {AnyFetchedSeriesBlueprint[]} */ -function createSingleSupplyRelativeToOwnMetrics(ctx, cohort) { - const { colors } = ctx; +function createSingleSupplyRelativeToOwnMetrics(cohort) { const { tree } = cohort; return [ @@ -159,42 +221,42 @@ function createSingleSupplyRelativeToOwnMetrics(ctx, cohort) { unit: Unit.pctOwn, }), priceLine({ - ctx, unit: Unit.pctOwn, number: 100, style: 0, color: colors.default, }), - priceLine({ ctx, unit: Unit.pctOwn, number: 50 }), + priceLine({ unit: Unit.pctOwn, number: 50 }), ]; } /** * Create supply section for a single cohort (with relative metrics) - * @param {PartialContext} ctx * @param {UtxoCohortObject | AddressCohortObject} cohort * @param {Object} [options] * @param {AnyFetchedSeriesBlueprint[]} [options.supplyRelative] - Supply relative to circulating supply metrics * @param {AnyFetchedSeriesBlueprint[]} [options.pnlRelative] - Supply in profit/loss relative to circulating supply metrics * @returns {AnyFetchedSeriesBlueprint[]} */ -export function createSingleSupplySeries(ctx, cohort, { supplyRelative = [], pnlRelative = [] } = {}) { +export function createSingleSupplySeries( + cohort, + { supplyRelative = [], pnlRelative = [] } = {}, +) { return [ - ...createSingleSupplySeriesBase(ctx, cohort), + ...createSingleSupplySeriesBase(cohort), ...supplyRelative, ...pnlRelative, - ...createSingleSupplyRelativeToOwnMetrics(ctx, cohort), + ...createSingleSupplyRelativeToOwnMetrics(cohort), ]; } /** * Create supply series for cohorts WITHOUT relative metrics - * @param {PartialContext} ctx * @param {CohortWithoutRelative} cohort * @returns {AnyFetchedSeriesBlueprint[]} */ -export function createSingleSupplySeriesWithoutRelative(ctx, cohort) { - return createSingleSupplySeriesBase(ctx, cohort); +export function createSingleSupplySeriesWithoutRelative(cohort) { + return createSingleSupplySeriesBase(cohort); } /** @@ -207,7 +269,11 @@ export function createSingleSupplySeriesWithoutRelative(ctx, cohort) { */ export function createGroupedSupplyTotalSeries(list, { relativeMetrics } = {}) { return list.flatMap((cohort) => [ - ...satsBtcUsd({ pattern: cohort.tree.supply.total, name: cohort.name, color: cohort.color }), + ...satsBtcUsd({ + pattern: cohort.tree.supply.total, + name: cohort.name, + color: cohort.color, + }), ...(relativeMetrics ? relativeMetrics(cohort) : []), ]); } @@ -220,9 +286,16 @@ export function createGroupedSupplyTotalSeries(list, { relativeMetrics } = {}) { * @param {(cohort: T[number]) => AnyFetchedSeriesBlueprint[]} [options.relativeMetrics] - Generator for relative supply metrics * @returns {AnyFetchedSeriesBlueprint[]} */ -export function createGroupedSupplyInProfitSeries(list, { relativeMetrics } = {}) { +export function createGroupedSupplyInProfitSeries( + list, + { relativeMetrics } = {}, +) { return list.flatMap((cohort) => [ - ...satsBtcUsd({ pattern: cohort.tree.unrealized.supplyInProfit, name: cohort.name, color: cohort.color }), + ...satsBtcUsd({ + pattern: cohort.tree.unrealized.supplyInProfit, + name: cohort.name, + color: cohort.color, + }), ...(relativeMetrics ? relativeMetrics(cohort) : []), ]); } @@ -235,9 +308,16 @@ export function createGroupedSupplyInProfitSeries(list, { relativeMetrics } = {} * @param {(cohort: T[number]) => AnyFetchedSeriesBlueprint[]} [options.relativeMetrics] - Generator for relative supply metrics * @returns {AnyFetchedSeriesBlueprint[]} */ -export function createGroupedSupplyInLossSeries(list, { relativeMetrics } = {}) { +export function createGroupedSupplyInLossSeries( + list, + { relativeMetrics } = {}, +) { return list.flatMap((cohort) => [ - ...satsBtcUsd({ pattern: cohort.tree.unrealized.supplyInLoss, name: cohort.name, color: cohort.color }), + ...satsBtcUsd({ + pattern: cohort.tree.unrealized.supplyInLoss, + name: cohort.name, + color: cohort.color, + }), ...(relativeMetrics ? relativeMetrics(cohort) : []), ]); } @@ -253,14 +333,20 @@ export function createGroupedSupplyInLossSeries(list, { relativeMetrics } = {}) * @param {(cohort: T[number]) => AnyFetchedSeriesBlueprint[]} [options.lossRelativeMetrics] - Generator for supply in loss relative metrics * @returns {PartialOptionsGroup} */ -export function createGroupedSupplySection(list, title, { supplyRelativeMetrics, profitRelativeMetrics, lossRelativeMetrics } = {}) { +export function createGroupedSupplySection( + list, + title, + { supplyRelativeMetrics, profitRelativeMetrics, lossRelativeMetrics } = {}, +) { return { name: "Supply", tree: [ { name: "Total", title: title("Supply"), - bottom: createGroupedSupplyTotalSeries(list, { relativeMetrics: supplyRelativeMetrics }), + bottom: createGroupedSupplyTotalSeries(list, { + relativeMetrics: supplyRelativeMetrics, + }), }, { name: "30d Change", @@ -272,12 +358,16 @@ export function createGroupedSupplySection(list, title, { supplyRelativeMetrics, { name: "In Profit", title: title("Supply In Profit"), - bottom: createGroupedSupplyInProfitSeries(list, { relativeMetrics: profitRelativeMetrics }), + bottom: createGroupedSupplyInProfitSeries(list, { + relativeMetrics: profitRelativeMetrics, + }), }, { name: "In Loss", title: title("Supply In Loss"), - bottom: createGroupedSupplyInLossSeries(list, { relativeMetrics: lossRelativeMetrics }), + bottom: createGroupedSupplyInLossSeries(list, { + relativeMetrics: lossRelativeMetrics, + }), }, ], }; @@ -289,16 +379,15 @@ export function createGroupedSupplySection(list, title, { supplyRelativeMetrics, /** * Create supply relative to circulating supply series for single cohort - * @param {PartialContext} ctx * @param {CohortWithCirculatingSupplyRelative} cohort * @returns {AnyFetchedSeriesBlueprint[]} */ -export function createSupplyRelativeToCirculatingSeries(ctx, cohort) { +export function createSupplyRelativeToCirculatingSeries(cohort) { return [ line({ metric: cohort.tree.relative.supplyRelToCirculatingSupply, name: "Supply", - color: ctx.colors.default, + color: colors.default, unit: Unit.pctSupply, }), ]; @@ -306,22 +395,21 @@ export function createSupplyRelativeToCirculatingSeries(ctx, cohort) { /** * Create supply in profit/loss relative to circulating supply series for single cohort - * @param {PartialContext} ctx * @param {CohortWithCirculatingSupplyRelative} cohort * @returns {AnyFetchedSeriesBlueprint[]} */ -export function createSupplyPnlRelativeToCirculatingSeries(ctx, cohort) { +export function createSupplyPnlRelativeToCirculatingSeries(cohort) { return [ line({ metric: cohort.tree.relative.supplyInProfitRelToCirculatingSupply, name: "In Profit", - color: ctx.colors.green, + color: colors.green, unit: Unit.pctSupply, }), line({ metric: cohort.tree.relative.supplyInLossRelToCirculatingSupply, name: "In Loss", - color: ctx.colors.red, + color: colors.red, unit: Unit.pctSupply, }), ]; @@ -387,14 +475,13 @@ export const groupedSupplyRelativeGenerators = { /** * Create single cohort supply relative options for cohorts with circulating supply relative - * @param {PartialContext} ctx * @param {CohortWithCirculatingSupplyRelative} cohort * @returns {{ supplyRelative: AnyFetchedSeriesBlueprint[], pnlRelative: AnyFetchedSeriesBlueprint[] }} */ -export function createSingleSupplyRelativeOptions(ctx, cohort) { +export function createSingleSupplyRelativeOptions(cohort) { return { - supplyRelative: createSupplyRelativeToCirculatingSeries(ctx, cohort), - pnlRelative: createSupplyPnlRelativeToCirculatingSeries(ctx, cohort), + supplyRelative: createSupplyRelativeToCirculatingSeries(cohort), + pnlRelative: createSupplyPnlRelativeToCirculatingSeries(cohort), }; } @@ -417,14 +504,11 @@ export function createUtxoCountSeries(list, useGroupName) { /** * Create address count series (for address cohorts only) - * @param {PartialContext} ctx * @param {readonly AddressCohortObject[]} list * @param {boolean} useGroupName * @returns {AnyFetchedSeriesBlueprint[]} */ -export function createAddressCountSeries(ctx, list, useGroupName) { - const { colors } = ctx; - +export function createAddressCountSeries(list, useGroupName) { return list.flatMap(({ color, name, tree }) => [ line({ metric: tree.addrCount, @@ -491,29 +575,130 @@ export function createCostBasisPercentilesSeries(colors, list, useGroupName) { return list.flatMap(({ name, tree }) => { const cb = tree.costBasis; const p = cb.percentiles; - const n = (/** @type {number} */ pct) => (useGroupName ? `${name} p${pct}` : `p${pct}`); + const n = (/** @type {number} */ pct) => + useGroupName ? `${name} p${pct}` : `p${pct}`; return [ - price({ metric: cb.max, name: n(100), color: colors.purple, defaultActive: false }), - price({ metric: p.pct95, name: n(95), color: colors.fuchsia, defaultActive: false }), - price({ metric: p.pct90, name: n(90), color: colors.pink, defaultActive: false }), - price({ metric: p.pct85, name: n(85), color: colors.pink, defaultActive: false }), - price({ metric: p.pct80, name: n(80), color: colors.rose, defaultActive: false }), - price({ metric: p.pct75, name: n(75), color: colors.red, defaultActive: false }), - price({ metric: p.pct70, name: n(70), color: colors.orange, defaultActive: false }), - price({ metric: p.pct65, name: n(65), color: colors.amber, defaultActive: false }), - price({ metric: p.pct60, name: n(60), color: colors.yellow, defaultActive: false }), - price({ metric: p.pct55, name: n(55), color: colors.yellow, defaultActive: false }), + price({ + metric: cb.max, + name: n(100), + color: colors.purple, + defaultActive: false, + }), + price({ + metric: p.pct95, + name: n(95), + color: colors.fuchsia, + defaultActive: false, + }), + price({ + metric: p.pct90, + name: n(90), + color: colors.pink, + defaultActive: false, + }), + price({ + metric: p.pct85, + name: n(85), + color: colors.pink, + defaultActive: false, + }), + price({ + metric: p.pct80, + name: n(80), + color: colors.rose, + defaultActive: false, + }), + price({ + metric: p.pct75, + name: n(75), + color: colors.red, + defaultActive: false, + }), + price({ + metric: p.pct70, + name: n(70), + color: colors.orange, + defaultActive: false, + }), + price({ + metric: p.pct65, + name: n(65), + color: colors.amber, + defaultActive: false, + }), + price({ + metric: p.pct60, + name: n(60), + color: colors.yellow, + defaultActive: false, + }), + price({ + metric: p.pct55, + name: n(55), + color: colors.yellow, + defaultActive: false, + }), price({ metric: p.pct50, name: n(50), color: colors.avocado }), - price({ metric: p.pct45, name: n(45), color: colors.lime, defaultActive: false }), - price({ metric: p.pct40, name: n(40), color: colors.green, defaultActive: false }), - price({ metric: p.pct35, name: n(35), color: colors.emerald, defaultActive: false }), - price({ metric: p.pct30, name: n(30), color: colors.teal, defaultActive: false }), - price({ metric: p.pct25, name: n(25), color: colors.teal, defaultActive: false }), - price({ metric: p.pct20, name: n(20), color: colors.cyan, defaultActive: false }), - price({ metric: p.pct15, name: n(15), color: colors.sky, defaultActive: false }), - price({ metric: p.pct10, name: n(10), color: colors.blue, defaultActive: false }), - price({ metric: p.pct05, name: n(5), color: colors.indigo, defaultActive: false }), - price({ metric: cb.min, name: n(0), color: colors.violet, defaultActive: false }), + price({ + metric: p.pct45, + name: n(45), + color: colors.lime, + defaultActive: false, + }), + price({ + metric: p.pct40, + name: n(40), + color: colors.green, + defaultActive: false, + }), + price({ + metric: p.pct35, + name: n(35), + color: colors.emerald, + defaultActive: false, + }), + price({ + metric: p.pct30, + name: n(30), + color: colors.teal, + defaultActive: false, + }), + price({ + metric: p.pct25, + name: n(25), + color: colors.teal, + defaultActive: false, + }), + price({ + metric: p.pct20, + name: n(20), + color: colors.cyan, + defaultActive: false, + }), + price({ + metric: p.pct15, + name: n(15), + color: colors.sky, + defaultActive: false, + }), + price({ + metric: p.pct10, + name: n(10), + color: colors.blue, + defaultActive: false, + }), + price({ + metric: p.pct05, + name: n(5), + color: colors.indigo, + defaultActive: false, + }), + price({ + metric: cb.min, + name: n(0), + color: colors.violet, + defaultActive: false, + }), ]; }); } @@ -526,30 +711,125 @@ export function createCostBasisPercentilesSeries(colors, list, useGroupName) { * @param {boolean} useGroupName * @returns {FetchedPriceSeriesBlueprint[]} */ -export function createInvestedCapitalPercentilesSeries(colors, list, useGroupName) { +export function createInvestedCapitalPercentilesSeries( + colors, + list, + useGroupName, +) { return list.flatMap(({ name, tree }) => { const ic = tree.costBasis.investedCapital; - const n = (/** @type {number} */ pct) => (useGroupName ? `${name} p${pct}` : `p${pct}`); + const n = (/** @type {number} */ pct) => + useGroupName ? `${name} p${pct}` : `p${pct}`; return [ - price({ metric: ic.pct95, name: n(95), color: colors.fuchsia, defaultActive: false }), - price({ metric: ic.pct90, name: n(90), color: colors.pink, defaultActive: false }), - price({ metric: ic.pct85, name: n(85), color: colors.pink, defaultActive: false }), - price({ metric: ic.pct80, name: n(80), color: colors.rose, defaultActive: false }), - price({ metric: ic.pct75, name: n(75), color: colors.red, defaultActive: false }), - price({ metric: ic.pct70, name: n(70), color: colors.orange, defaultActive: false }), - price({ metric: ic.pct65, name: n(65), color: colors.amber, defaultActive: false }), - price({ metric: ic.pct60, name: n(60), color: colors.yellow, defaultActive: false }), - price({ metric: ic.pct55, name: n(55), color: colors.yellow, defaultActive: false }), + price({ + metric: ic.pct95, + name: n(95), + color: colors.fuchsia, + defaultActive: false, + }), + price({ + metric: ic.pct90, + name: n(90), + color: colors.pink, + defaultActive: false, + }), + price({ + metric: ic.pct85, + name: n(85), + color: colors.pink, + defaultActive: false, + }), + price({ + metric: ic.pct80, + name: n(80), + color: colors.rose, + defaultActive: false, + }), + price({ + metric: ic.pct75, + name: n(75), + color: colors.red, + defaultActive: false, + }), + price({ + metric: ic.pct70, + name: n(70), + color: colors.orange, + defaultActive: false, + }), + price({ + metric: ic.pct65, + name: n(65), + color: colors.amber, + defaultActive: false, + }), + price({ + metric: ic.pct60, + name: n(60), + color: colors.yellow, + defaultActive: false, + }), + price({ + metric: ic.pct55, + name: n(55), + color: colors.yellow, + defaultActive: false, + }), price({ metric: ic.pct50, name: n(50), color: colors.avocado }), - price({ metric: ic.pct45, name: n(45), color: colors.lime, defaultActive: false }), - price({ metric: ic.pct40, name: n(40), color: colors.green, defaultActive: false }), - price({ metric: ic.pct35, name: n(35), color: colors.emerald, defaultActive: false }), - price({ metric: ic.pct30, name: n(30), color: colors.teal, defaultActive: false }), - price({ metric: ic.pct25, name: n(25), color: colors.teal, defaultActive: false }), - price({ metric: ic.pct20, name: n(20), color: colors.cyan, defaultActive: false }), - price({ metric: ic.pct15, name: n(15), color: colors.sky, defaultActive: false }), - price({ metric: ic.pct10, name: n(10), color: colors.blue, defaultActive: false }), - price({ metric: ic.pct05, name: n(5), color: colors.indigo, defaultActive: false }), + price({ + metric: ic.pct45, + name: n(45), + color: colors.lime, + defaultActive: false, + }), + price({ + metric: ic.pct40, + name: n(40), + color: colors.green, + defaultActive: false, + }), + price({ + metric: ic.pct35, + name: n(35), + color: colors.emerald, + defaultActive: false, + }), + price({ + metric: ic.pct30, + name: n(30), + color: colors.teal, + defaultActive: false, + }), + price({ + metric: ic.pct25, + name: n(25), + color: colors.teal, + defaultActive: false, + }), + price({ + metric: ic.pct20, + name: n(20), + color: colors.cyan, + defaultActive: false, + }), + price({ + metric: ic.pct15, + name: n(15), + color: colors.sky, + defaultActive: false, + }), + price({ + metric: ic.pct10, + name: n(10), + color: colors.blue, + defaultActive: false, + }), + price({ + metric: ic.pct05, + name: n(5), + color: colors.indigo, + defaultActive: false, + }), ]; }); } @@ -946,14 +1226,13 @@ export function createInvestorPriceRatioSeries(list) { /** * Create investor price folder for extended cohorts (with full Z-scores) * For cohorts with ActivePriceRatioPattern (all, term.*, ageRange.* UTXO cohorts) - * @param {PartialContext} ctx * @param {{ tree: { realized: RealizedWithExtras }, color: Color }} cohort * @param {string} [cohortTitle] - Cohort title (e.g., "STH") * @returns {PartialOptionsGroup} */ -export function createInvestorPriceFolderFull(ctx, cohort, cohortTitle) { +export function createInvestorPriceFolderFull(cohort, cohortTitle) { const { tree, color } = cohort; - return createPriceFolder(ctx, { + return createPriceFolder({ name: "Investor Price", cohortTitle, priceMetric: tree.realized.investorPrice, diff --git a/website/scripts/options/distribution/utxo.js b/website/scripts/options/distribution/utxo.js index 607fe4612..2b48cc6da 100644 --- a/website/scripts/options/distribution/utxo.js +++ b/website/scripts/options/distribution/utxo.js @@ -60,6 +60,7 @@ import { import { Unit } from "../../utils/units.js"; import { line, baseline, price } from "../series.js"; import { priceLine } from "../constants.js"; +import { colors } from "../../utils/colors.js"; // ============================================================================ // Folder Builders (4 variants based on pattern capabilities) @@ -67,33 +68,31 @@ import { priceLine } from "../constants.js"; /** * All folder: for the special "All" cohort (adjustedSopr + percentiles + RelToMarketCap) - * @param {PartialContext} ctx * @param {CohortAll} args * @returns {PartialOptionsGroup} */ -export function createCohortFolderAll(ctx, args) { +export function createCohortFolderAll(args) { const title = formatCohortTitle(args.name); return { name: args.name || "all", tree: [ - createSingleSupplyChart(ctx, args, title), + createSingleSupplyChart(args, title), createSingleUtxoCountChart(args, title), - createSingleAddrCountChart(ctx, args, title), - createSingleRealizedSectionFull(ctx, args, title), - createSingleUnrealizedSectionAll(ctx, args, title), - createSingleCostBasisSectionWithPercentiles(ctx, args, title), - createSingleActivitySectionWithAdjusted(ctx, args, title), + createSingleAddrCountChart(args, title), + createSingleRealizedSectionFull(args, title), + createSingleUnrealizedSectionAll(args, title), + createSingleCostBasisSectionWithPercentiles(args, title), + createSingleActivitySectionWithAdjusted(args, title), ], }; } /** * Full folder: adjustedSopr + percentiles + RelToMarketCap (term.short only) - * @param {PartialContext} ctx * @param {CohortFull | CohortGroupFull} args * @returns {PartialOptionsGroup} */ -export function createCohortFolderFull(ctx, args) { +export function createCohortFolderFull(args) { if ("list" in args) { const { list } = args; const title = formatCohortTitle(args.title); @@ -106,11 +105,11 @@ export function createCohortFolderFull(ctx, args) { groupedSupplyRelativeGenerators, ), createGroupedUtxoCountChart(list, title), - createGroupedRealizedSectionWithAdjusted(ctx, list, title, { + createGroupedRealizedSectionWithAdjusted(list, title, { ratioMetrics: createGroupedRealizedPnlRatioMetrics, }), - createGroupedUnrealizedSectionFull(ctx, list, title), - createGroupedCostBasisSectionWithPercentiles(ctx, list, title), + createGroupedUnrealizedSectionFull(list, title), + createGroupedCostBasisSectionWithPercentiles(list, title), createGroupedActivitySectionWithAdjusted(list, title), ], }; @@ -120,27 +119,25 @@ export function createCohortFolderFull(ctx, args) { name: args.name || "all", tree: [ createSingleSupplyChart( - ctx, args, title, - createSingleSupplyRelativeOptions(ctx, args), + createSingleSupplyRelativeOptions(args), ), createSingleUtxoCountChart(args, title), - createSingleRealizedSectionFull(ctx, args, title), - createSingleUnrealizedSectionFull(ctx, args, title), - createSingleCostBasisSectionWithPercentiles(ctx, args, title), - createSingleActivitySectionWithAdjusted(ctx, args, title), + createSingleRealizedSectionFull(args, title), + createSingleUnrealizedSectionFull(args, title), + createSingleCostBasisSectionWithPercentiles(args, title), + createSingleActivitySectionWithAdjusted(args, title), ], }; } /** * Adjusted folder: adjustedSopr only, no percentiles (maxAge.*) - * @param {PartialContext} ctx * @param {CohortWithAdjusted | CohortGroupWithAdjusted} args * @returns {PartialOptionsGroup} */ -export function createCohortFolderWithAdjusted(ctx, args) { +export function createCohortFolderWithAdjusted(args) { if ("list" in args) { const { list } = args; const title = formatCohortTitle(args.title); @@ -153,8 +150,8 @@ export function createCohortFolderWithAdjusted(ctx, args) { groupedSupplyRelativeGenerators, ), createGroupedUtxoCountChart(list, title), - createGroupedRealizedSectionWithAdjusted(ctx, list, title), - createGroupedUnrealizedSectionWithMarketCap(ctx, list, title), + createGroupedRealizedSectionWithAdjusted(list, title), + createGroupedUnrealizedSectionWithMarketCap(list, title), createGroupedCostBasisSection({ list, title }), createGroupedActivitySectionWithAdjusted(list, title), ], @@ -165,27 +162,25 @@ export function createCohortFolderWithAdjusted(ctx, args) { name: args.name || "all", tree: [ createSingleSupplyChart( - ctx, args, title, - createSingleSupplyRelativeOptions(ctx, args), + createSingleSupplyRelativeOptions(args), ), createSingleUtxoCountChart(args, title), - createSingleRealizedSectionWithAdjusted(ctx, args, title), - createSingleUnrealizedSectionWithMarketCap(ctx, args, title), - createCostBasisSection(ctx, { cohort: args, title }), - createSingleActivitySectionWithAdjusted(ctx, args, title), + createSingleRealizedSectionWithAdjusted(args, title), + createSingleUnrealizedSectionWithMarketCap(args, title), + createCostBasisSection({ cohort: args, title }), + createSingleActivitySectionWithAdjusted(args, title), ], }; } /** * Folder for cohorts with nupl + percentiles (term.short, term.long) - * @param {PartialContext} ctx * @param {CohortWithNuplPercentiles | CohortGroupWithNuplPercentiles} args * @returns {PartialOptionsGroup} */ -export function createCohortFolderWithNupl(ctx, args) { +export function createCohortFolderWithNupl(args) { if ("list" in args) { const { list } = args; const title = formatCohortTitle(args.title); @@ -198,11 +193,11 @@ export function createCohortFolderWithNupl(ctx, args) { groupedSupplyRelativeGenerators, ), createGroupedUtxoCountChart(list, title), - createGroupedRealizedSectionBasic(ctx, list, title, { + createGroupedRealizedSectionBasic(list, title, { ratioMetrics: createGroupedRealizedPnlRatioMetrics, }), - createGroupedUnrealizedSectionWithNupl({ ctx, list, title }), - createGroupedCostBasisSectionWithPercentiles(ctx, list, title), + createGroupedUnrealizedSectionWithNupl({ list, title }), + createGroupedCostBasisSectionWithPercentiles(list, title), createGroupedActivitySection({ list, title }), ], }; @@ -212,27 +207,25 @@ export function createCohortFolderWithNupl(ctx, args) { name: args.name || "all", tree: [ createSingleSupplyChart( - ctx, args, title, - createSingleSupplyRelativeOptions(ctx, args), + createSingleSupplyRelativeOptions(args), ), createSingleUtxoCountChart(args, title), - createSingleRealizedSectionWithPercentiles(ctx, args, title), - createSingleUnrealizedSectionWithNupl({ ctx, cohort: args, title }), - createSingleCostBasisSectionWithPercentiles(ctx, args, title), - createActivitySection({ ctx, cohort: args, title }), + createSingleRealizedSectionWithPercentiles(args, title), + createSingleUnrealizedSectionWithNupl({ cohort: args, title }), + createSingleCostBasisSectionWithPercentiles(args, title), + createActivitySection({ cohort: args, title }), ], }; } /** * Age range folder: ageRange.* (no nupl via RelativePattern2) - * @param {PartialContext} ctx * @param {CohortAgeRange | CohortGroupAgeRange} args * @returns {PartialOptionsGroup} */ -export function createCohortFolderAgeRange(ctx, args) { +export function createCohortFolderAgeRange(args) { if ("list" in args) { const { list } = args; const title = formatCohortTitle(args.title); @@ -241,11 +234,11 @@ export function createCohortFolderAgeRange(ctx, args) { tree: [ createGroupedSupplySection(list, title), createGroupedUtxoCountChart(list, title), - createGroupedRealizedSectionBasic(ctx, list, title, { + createGroupedRealizedSectionBasic(list, title, { ratioMetrics: createGroupedRealizedPnlRatioMetrics, }), createGroupedUnrealizedSectionAgeRange(list, title), - createGroupedCostBasisSectionWithPercentiles(ctx, list, title), + createGroupedCostBasisSectionWithPercentiles(list, title), createGroupedActivitySection({ list, title }), ], }; @@ -254,23 +247,22 @@ export function createCohortFolderAgeRange(ctx, args) { return { name: args.name || "all", tree: [ - createSingleSupplyChart(ctx, args, title), + createSingleSupplyChart(args, title), createSingleUtxoCountChart(args, title), - createSingleRealizedSectionWithPercentiles(ctx, args, title), - createSingleUnrealizedSectionAgeRange(ctx, args, title), - createSingleCostBasisSectionWithPercentiles(ctx, args, title), - createActivitySection({ ctx, cohort: args, title }), + createSingleRealizedSectionWithPercentiles(args, title), + createSingleUnrealizedSectionAgeRange(args, title), + createSingleCostBasisSectionWithPercentiles(args, title), + createActivitySection({ cohort: args, title }), ], }; } /** * MinAge folder - has peakRegret in unrealized (minAge.*) - * @param {PartialContext} ctx * @param {CohortMinAge | CohortGroupMinAge} args * @returns {PartialOptionsGroup} */ -export function createCohortFolderMinAge(ctx, args) { +export function createCohortFolderMinAge(args) { if ("list" in args) { const { list } = args; const title = formatCohortTitle(args.title); @@ -283,8 +275,8 @@ export function createCohortFolderMinAge(ctx, args) { groupedSupplyRelativeGenerators, ), createGroupedUtxoCountChart(list, title), - createGroupedRealizedSectionBasic(ctx, list, title), - createGroupedUnrealizedSectionMinAge(ctx, list, title), + createGroupedRealizedSectionBasic(list, title), + createGroupedUnrealizedSectionMinAge(list, title), createGroupedCostBasisSection({ list, title }), createGroupedActivitySection({ list, title }), ], @@ -295,27 +287,25 @@ export function createCohortFolderMinAge(ctx, args) { name: args.name || "all", tree: [ createSingleSupplyChart( - ctx, args, title, - createSingleSupplyRelativeOptions(ctx, args), + createSingleSupplyRelativeOptions(args), ), createSingleUtxoCountChart(args, title), - createSingleRealizedSectionBasic(ctx, args, title), - createSingleUnrealizedSectionMinAge(ctx, args, title), - createCostBasisSection(ctx, { cohort: args, title }), - createActivitySection({ ctx, cohort: args, title }), + createSingleRealizedSectionBasic(args, title), + createSingleUnrealizedSectionMinAge(args, title), + createCostBasisSection({ cohort: args, title }), + createActivitySection({ cohort: args, title }), ], }; } /** * Basic folder WITH RelToMarketCap (geAmount.*, ltAmount.*) - * @param {PartialContext} ctx * @param {CohortBasicWithMarketCap | CohortGroupBasicWithMarketCap} args * @returns {PartialOptionsGroup} */ -export function createCohortFolderBasicWithMarketCap(ctx, args) { +export function createCohortFolderBasicWithMarketCap(args) { if ("list" in args) { const { list } = args; const title = formatCohortTitle(args.title); @@ -328,8 +318,8 @@ export function createCohortFolderBasicWithMarketCap(ctx, args) { groupedSupplyRelativeGenerators, ), createGroupedUtxoCountChart(list, title), - createGroupedRealizedSectionBasic(ctx, list, title), - createGroupedUnrealizedSectionWithMarketCapOnly(ctx, list, title), + createGroupedRealizedSectionBasic(list, title), + createGroupedUnrealizedSectionWithMarketCapOnly(list, title), createGroupedCostBasisSection({ list, title }), createGroupedActivitySection({ list, title }), ], @@ -340,27 +330,25 @@ export function createCohortFolderBasicWithMarketCap(ctx, args) { name: args.name || "all", tree: [ createSingleSupplyChart( - ctx, args, title, - createSingleSupplyRelativeOptions(ctx, args), + createSingleSupplyRelativeOptions(args), ), createSingleUtxoCountChart(args, title), - createSingleRealizedSectionBasic(ctx, args, title), - createSingleUnrealizedSectionWithMarketCapOnly(ctx, args, title), - createCostBasisSection(ctx, { cohort: args, title }), - createActivitySection({ ctx, cohort: args, title }), + createSingleRealizedSectionBasic(args, title), + createSingleUnrealizedSectionWithMarketCapOnly(args, title), + createCostBasisSection({ cohort: args, title }), + createActivitySection({ cohort: args, title }), ], }; } /** * Basic folder WITHOUT RelToMarketCap (epoch.*, amountRange.*, year.*) - * @param {PartialContext} ctx * @param {CohortBasicWithoutMarketCap | CohortGroupBasicWithoutMarketCap} args * @returns {PartialOptionsGroup} */ -export function createCohortFolderBasicWithoutMarketCap(ctx, args) { +export function createCohortFolderBasicWithoutMarketCap(args) { if ("list" in args) { const { list } = args; const title = formatCohortTitle(args.title); @@ -369,7 +357,7 @@ export function createCohortFolderBasicWithoutMarketCap(ctx, args) { tree: [ createGroupedSupplySection(list, title), createGroupedUtxoCountChart(list, title), - createGroupedRealizedSectionBasic(ctx, list, title), + createGroupedRealizedSectionBasic(list, title), createGroupedUnrealizedSectionBase(list, title), createGroupedCostBasisSection({ list, title }), createGroupedActivitySection({ list, title }), @@ -380,12 +368,12 @@ export function createCohortFolderBasicWithoutMarketCap(ctx, args) { return { name: args.name || "all", tree: [ - createSingleSupplyChart(ctx, args, title), + createSingleSupplyChart(args, title), createSingleUtxoCountChart(args, title), - createSingleRealizedSectionBasic(ctx, args, title), - createSingleUnrealizedSectionBase(ctx, args, title), - createCostBasisSection(ctx, { cohort: args, title }), - createActivitySection({ ctx, cohort: args, title }), + createSingleRealizedSectionBasic(args, title), + createSingleUnrealizedSectionBase(args, title), + createCostBasisSection({ cohort: args, title }), + createActivitySection({ cohort: args, title }), ], }; } @@ -393,11 +381,10 @@ export function createCohortFolderBasicWithoutMarketCap(ctx, args) { /** * Address folder: like basic but with address count (addressable type cohorts) * Uses base unrealized section (no RelToMarketCap since it extends CohortBasicWithoutMarketCap) - * @param {PartialContext} ctx * @param {CohortAddress | CohortGroupAddress} args * @returns {PartialOptionsGroup} */ -export function createCohortFolderAddress(ctx, args) { +export function createCohortFolderAddress(args) { if ("list" in args) { const { list } = args; const title = formatCohortTitle(args.title); @@ -407,7 +394,7 @@ export function createCohortFolderAddress(ctx, args) { createGroupedSupplySection(list, title), createGroupedUtxoCountChart(list, title), createGroupedAddrCountChart(list, title), - createGroupedRealizedSectionBasic(ctx, list, title), + createGroupedRealizedSectionBasic(list, title), createGroupedUnrealizedSectionBase(list, title), createGroupedCostBasisSection({ list, title }), createGroupedActivitySection({ list, title }), @@ -418,39 +405,37 @@ export function createCohortFolderAddress(ctx, args) { return { name: args.name || "all", tree: [ - createSingleSupplyChart(ctx, args, title), + createSingleSupplyChart(args, title), createSingleUtxoCountChart(args, title), - createSingleAddrCountChart(ctx, args, title), - createSingleRealizedSectionBasic(ctx, args, title), - createSingleUnrealizedSectionBase(ctx, args, title), - createCostBasisSection(ctx, { cohort: args, title }), - createActivitySection({ ctx, cohort: args, title }), + createSingleAddrCountChart(args, title), + createSingleRealizedSectionBasic(args, title), + createSingleUnrealizedSectionBase(args, title), + createCostBasisSection({ cohort: args, title }), + createActivitySection({ cohort: args, title }), ], }; } /** * Create supply chart for cohorts WITHOUT relative metrics - * @param {PartialContext} ctx * @param {CohortWithoutRelative} cohort * @param {(metric: string) => string} title * @returns {PartialChartOption} */ -function createSingleSupplyChartWithoutRelative(ctx, cohort, title) { +function createSingleSupplyChartWithoutRelative(cohort, title) { return { name: "Supply", title: title("Supply"), - bottom: createSingleSupplySeriesWithoutRelative(ctx, cohort), + bottom: createSingleSupplySeriesWithoutRelative(cohort), }; } /** * Folder for cohorts WITHOUT relative section (edge case types: empty, p2ms, unknown) - * @param {PartialContext} ctx * @param {CohortWithoutRelative | CohortGroupWithoutRelative} args * @returns {PartialOptionsGroup} */ -export function createCohortFolderWithoutRelative(ctx, args) { +export function createCohortFolderWithoutRelative(args) { if ("list" in args) { const { list } = args; const title = formatCohortTitle(args.title); @@ -459,7 +444,7 @@ export function createCohortFolderWithoutRelative(ctx, args) { tree: [ createGroupedSupplySection(list, title), createGroupedUtxoCountChart(list, title), - createGroupedRealizedSectionBasic(ctx, list, title), + createGroupedRealizedSectionBasic(list, title), createGroupedUnrealizedSectionWithoutRelative(list, title), createGroupedCostBasisSection({ list, title }), createGroupedActivitySection({ list, title }), @@ -470,19 +455,18 @@ export function createCohortFolderWithoutRelative(ctx, args) { return { name: args.name || "all", tree: [ - createSingleSupplyChartWithoutRelative(ctx, args, title), + createSingleSupplyChartWithoutRelative(args, title), createSingleUtxoCountChart(args, title), - createSingleRealizedSectionBasic(ctx, args, title), - createSingleUnrealizedSectionWithoutRelative(ctx, args, title), - createCostBasisSection(ctx, { cohort: args, title }), - createActivitySection({ ctx, cohort: args, title }), + createSingleRealizedSectionBasic(args, title), + createSingleUnrealizedSectionWithoutRelative(args, title), + createCostBasisSection({ cohort: args, title }), + createActivitySection({ cohort: args, title }), ], }; } /** * Create supply chart for single cohort - * @param {PartialContext} ctx * @param {UtxoCohortObject} cohort * @param {(metric: string) => string} title * @param {Object} [options] @@ -490,11 +474,11 @@ export function createCohortFolderWithoutRelative(ctx, args) { * @param {AnyFetchedSeriesBlueprint[]} [options.pnlRelative] - Supply in profit/loss relative to circulating supply * @returns {PartialChartOption} */ -function createSingleSupplyChart(ctx, cohort, title, options = {}) { +function createSingleSupplyChart(cohort, title, options = {}) { return { name: "Supply", title: title("Supply"), - bottom: createSingleSupplySeries(ctx, cohort, options), + bottom: createSingleSupplySeries(cohort, options), }; } @@ -528,12 +512,11 @@ function createGroupedUtxoCountChart(list, title) { /** * Create address count chart for single cohort with addrCount - * @param {PartialContext} ctx * @param {CohortAll | CohortAddress} cohort * @param {(metric: string) => string} title * @returns {PartialChartOption} */ -function createSingleAddrCountChart(ctx, cohort, title) { +function createSingleAddrCountChart(cohort, title) { return { name: "Address Count", title: title("Address Count"), @@ -541,7 +524,7 @@ function createSingleAddrCountChart(ctx, cohort, title) { line({ metric: cohort.addrCount, name: "Count", - color: ctx.colors.orange, + color: colors.orange, unit: Unit.count, }), ], @@ -566,27 +549,25 @@ function createGroupedAddrCountChart(list, title) { /** * Create realized section for CohortAll/CohortFull (adjustedSopr + full ratio) - * @param {PartialContext} ctx * @param {CohortAll | CohortFull} cohort * @param {(metric: string) => string} title * @returns {PartialOptionsGroup} */ -function createSingleRealizedSectionFull(ctx, cohort, title) { - const { colors } = ctx; +function createSingleRealizedSectionFull(cohort, title) { const { tree, color } = cohort; return { name: "Realized", tree: [ - ...createSingleRealizedPriceChartsWithRatio(ctx, cohort, title), - createInvestorPriceFolderFull(ctx, cohort, cohort.name), + ...createSingleRealizedPriceChartsWithRatio(cohort, title), + createInvestorPriceFolderFull(cohort, cohort.name), { name: "Capitalization", title: title("Realized Cap"), - bottom: createSingleRealizedCapSeries(ctx, cohort, { - extra: createRealizedCapRatioSeries(ctx, tree), + bottom: createSingleRealizedCapSeries(cohort, { + extra: createRealizedCapRatioSeries(tree), }), }, - ...createSingleRealizedPnlSection(ctx, cohort, title, { + ...createSingleRealizedPnlSection(cohort, title, { extra: createRealizedPnlRatioSeries(colors, tree), }), { @@ -594,37 +575,36 @@ function createSingleRealizedSectionFull(ctx, cohort, title) { title: title("Realized Peak Regret"), bottom: createSingleRealizedAthRegretSeries(tree, color), }, - createSingleSoprSectionWithAdjusted(ctx, cohort, title), + createSingleSoprSectionWithAdjusted(cohort, title), ], }; } /** * Create realized section for CohortWithAdjusted (adjustedSopr but partial ratio) - * @param {PartialContext} ctx * @param {CohortWithAdjusted} cohort * @param {(metric: string) => string} title * @returns {PartialOptionsGroup} */ -function createSingleRealizedSectionWithAdjusted(ctx, cohort, title) { +function createSingleRealizedSectionWithAdjusted(cohort, title) { const { tree, color } = cohort; return { name: "Realized", tree: [ - ...createSingleRealizedPriceChartsBasic(ctx, cohort, title), + ...createSingleRealizedPriceChartsBasic(cohort, title), createInvestorPriceFolderBasic(cohort, cohort.name), { name: "Capitalization", title: title("Realized Cap"), - bottom: createSingleRealizedCapSeries(ctx, cohort), + bottom: createSingleRealizedCapSeries(cohort), }, - ...createSingleRealizedPnlSection(ctx, cohort, title), + ...createSingleRealizedPnlSection(cohort, title), { name: "Peak Regret", title: title("Realized Peak Regret"), bottom: createSingleRealizedAthRegretSeries(tree, color), }, - createSingleSoprSectionWithAdjusted(ctx, cohort, title), + createSingleSoprSectionWithAdjusted(cohort, title), ], }; } @@ -632,7 +612,6 @@ function createSingleRealizedSectionWithAdjusted(ctx, cohort, title) { /** * Create realized section with adjusted SOPR for grouped cohorts * @template {readonly (CohortFull | CohortWithAdjusted)[]} T - * @param {PartialContext} ctx * @param {T} list * @param {(metric: string) => string} title * @param {Object} [options] @@ -640,7 +619,6 @@ function createSingleRealizedSectionWithAdjusted(ctx, cohort, title) { * @returns {PartialOptionsGroup} */ function createGroupedRealizedSectionWithAdjusted( - ctx, list, title, { ratioMetrics } = {}, @@ -664,7 +642,7 @@ function createGroupedRealizedSectionWithAdjusted( title: title("Realized Cap"), bottom: createGroupedRealizedCapSeries(list), }, - ...createGroupedRealizedPnlSections(ctx, list, title, { ratioMetrics }), + ...createGroupedRealizedPnlSections(list, title, { ratioMetrics }), { name: "Peak Regret", title: title("Realized Peak Regret"), @@ -677,27 +655,25 @@ function createGroupedRealizedSectionWithAdjusted( /** * Create realized section for CohortWithPercentiles (no adjustedSopr but full ratio) - * @param {PartialContext} ctx * @param {CohortWithPercentiles} cohort * @param {(metric: string) => string} title * @returns {PartialOptionsGroup} */ -function createSingleRealizedSectionWithPercentiles(ctx, cohort, title) { - const { colors } = ctx; +function createSingleRealizedSectionWithPercentiles(cohort, title) { const { tree, color } = cohort; return { name: "Realized", tree: [ - ...createSingleRealizedPriceChartsWithRatio(ctx, cohort, title), - createInvestorPriceFolderFull(ctx, cohort, cohort.name), + ...createSingleRealizedPriceChartsWithRatio(cohort, title), + createInvestorPriceFolderFull(cohort, cohort.name), { name: "Capitalization", title: title("Realized Cap"), - bottom: createSingleRealizedCapSeries(ctx, cohort, { - extra: createRealizedCapRatioSeries(ctx, tree), + bottom: createSingleRealizedCapSeries(cohort, { + extra: createRealizedCapRatioSeries(tree), }), }, - ...createSingleRealizedPnlSection(ctx, cohort, title, { + ...createSingleRealizedPnlSection(cohort, title, { extra: createRealizedPnlRatioSeries(colors, tree), }), { @@ -705,37 +681,36 @@ function createSingleRealizedSectionWithPercentiles(ctx, cohort, title) { title: title("Realized Peak Regret"), bottom: createSingleRealizedAthRegretSeries(tree, color), }, - createSingleSoprSectionBasic(ctx, cohort, title), + createSingleSoprSectionBasic(cohort, title), ], }; } /** * Create realized section for CohortBasic (no adjustedSopr, partial ratio) - * @param {PartialContext} ctx * @param {CohortBasic | CohortAddress | CohortWithoutRelative} cohort * @param {(metric: string) => string} title * @returns {PartialOptionsGroup} */ -function createSingleRealizedSectionBasic(ctx, cohort, title) { +function createSingleRealizedSectionBasic(cohort, title) { const { tree, color } = cohort; return { name: "Realized", tree: [ - ...createSingleRealizedPriceChartsBasic(ctx, cohort, title), + ...createSingleRealizedPriceChartsBasic(cohort, title), createInvestorPriceFolderBasic(cohort, cohort.name), { name: "Capitalization", title: title("Realized Cap"), - bottom: createSingleRealizedCapSeries(ctx, cohort), + bottom: createSingleRealizedCapSeries(cohort), }, - ...createSingleRealizedPnlSection(ctx, cohort, title), + ...createSingleRealizedPnlSection(cohort, title), { name: "Peak Regret", title: title("Realized Peak Regret"), bottom: createSingleRealizedAthRegretSeries(tree, color), }, - createSingleSoprSectionBasic(ctx, cohort, title), + createSingleSoprSectionBasic(cohort, title), ], }; } @@ -743,19 +718,13 @@ function createSingleRealizedSectionBasic(ctx, cohort, title) { /** * Create realized section without adjusted SOPR for grouped cohorts * @template {readonly (CohortWithPercentiles | CohortBasic | CohortAddress | CohortWithoutRelative)[]} T - * @param {PartialContext} ctx * @param {T} list * @param {(metric: string) => string} title * @param {Object} [options] * @param {(cohort: T[number]) => AnyFetchedSeriesBlueprint[]} [options.ratioMetrics] - Generator for ratio metrics per cohort * @returns {PartialOptionsGroup} */ -function createGroupedRealizedSectionBasic( - ctx, - list, - title, - { ratioMetrics } = {}, -) { +function createGroupedRealizedSectionBasic(list, title, { ratioMetrics } = {}) { return { name: "Realized", tree: [ @@ -775,7 +744,7 @@ function createGroupedRealizedSectionBasic( title: title("Realized Cap"), bottom: createGroupedRealizedCapSeries(list), }, - ...createGroupedRealizedPnlSections(ctx, list, title, { ratioMetrics }), + ...createGroupedRealizedPnlSections(list, title, { ratioMetrics }), { name: "Peak Regret", title: title("Realized Peak Regret"), @@ -810,17 +779,16 @@ function createSingleRealizedPriceChart(cohort, title) { /** * Create realized price and ratio charts for cohorts with full ActivePriceRatioPattern * (CohortAll, CohortFull, CohortWithPercentiles have RealizedPattern2/3 which has ActivePriceRatioPattern) - * @param {PartialContext} ctx * @param {CohortAll | CohortFull | CohortWithPercentiles} cohort * @param {(metric: string) => string} title * @returns {PartialOptionsTree} */ -function createSingleRealizedPriceChartsWithRatio(ctx, cohort, title) { +function createSingleRealizedPriceChartsWithRatio(cohort, title) { const { tree, color } = cohort; const ratio = /** @type {ActivePriceRatioPattern} */ ( tree.realized.realizedPriceExtra ); - return createPriceRatioCharts(ctx, { + return createPriceRatioCharts({ context: cohort.name, legend: "Realized", pricePattern: tree.realized.realizedPrice, @@ -835,12 +803,11 @@ function createSingleRealizedPriceChartsWithRatio(ctx, cohort, title) { /** * Create realized price and basic ratio charts for cohorts with RealizedPriceExtraPattern * (CohortWithAdjusted, CohortBasic have RealizedPattern/4 which has RealizedPriceExtraPattern) - * @param {PartialContext} ctx * @param {CohortWithAdjusted | CohortBasic | CohortAddress | CohortWithoutRelative} cohort * @param {(metric: string) => string} title * @returns {PartialChartOption[]} */ -function createSingleRealizedPriceChartsBasic(ctx, cohort, title) { +function createSingleRealizedPriceChartsBasic(cohort, title) { const { tree, color } = cohort; return [ createSingleRealizedPriceChart(cohort, title), @@ -861,13 +828,12 @@ function createSingleRealizedPriceChartsBasic(ctx, cohort, title) { /** * Create realized cap series for single cohort - * @param {PartialContext} ctx * @param {UtxoCohortObject | CohortWithoutRelative} cohort * @param {Object} [options] * @param {AnyFetchedSeriesBlueprint[]} [options.extra] - Additional series (e.g., ratio for cohorts with RealizedWithCapRatio) * @returns {AnyFetchedSeriesBlueprint[]} */ -function createSingleRealizedCapSeries(ctx, cohort, { extra = [] } = {}) { +function createSingleRealizedCapSeries(cohort, { extra = [] } = {}) { const { color, tree } = cohort; return [ @@ -896,11 +862,10 @@ function createSingleRealizedCapSeries(ctx, cohort, { extra = [] } = {}) { /** * Create realized cap ratio series (for cohorts with RealizedPattern2 or RealizedPattern3) - * @param {PartialContext} ctx * @param {{ realized: RealizedWithExtras }} tree * @returns {AnyFetchedSeriesBlueprint[]} */ -function createRealizedCapRatioSeries(ctx, tree) { +function createRealizedCapRatioSeries(tree) { return [ baseline({ metric: tree.realized.realizedCapRelToOwnMarketCap, @@ -909,7 +874,6 @@ function createRealizedCapRatioSeries(ctx, tree) { options: { baseValue: { price: 100 } }, }), priceLine({ - ctx, unit: Unit.pctOwnMcap, defaultActive: true, number: 100, @@ -968,20 +932,13 @@ function createGroupedRealizedPnlRatioMetrics(cohort) { /** * Create realized PnL section for single cohort - * @param {PartialContext} ctx * @param {UtxoCohortObject | CohortWithoutRelative} cohort * @param {(metric: string) => string} title * @param {Object} [options] * @param {AnyFetchedSeriesBlueprint[]} [options.extra] - Extra series (e.g., pnl ratio for cohorts with RealizedWithPnlRatio) * @returns {PartialOptionsTree} */ -function createSingleRealizedPnlSection( - ctx, - cohort, - title, - { extra = [] } = {}, -) { - const { colors } = ctx; +function createSingleRealizedPnlSection(cohort, title, { extra = [] } = {}) { const { tree } = cohort; return [ @@ -1272,19 +1229,13 @@ function createSingleRealizedPnlSection( /** * Create realized PnL sections for grouped cohorts * @template {readonly (UtxoCohortObject | CohortWithoutRelative)[]} T - * @param {PartialContext} ctx * @param {T} list * @param {(metric: string) => string} title * @param {Object} [options] * @param {(cohort: T[number]) => AnyFetchedSeriesBlueprint[]} [options.ratioMetrics] - Generator for ratio metrics per cohort * @returns {PartialOptionsTree} */ -function createGroupedRealizedPnlSections( - ctx, - list, - title, - { ratioMetrics } = {}, -) { +function createGroupedRealizedPnlSections(list, title, { ratioMetrics } = {}) { const pnlConfigs = /** @type {const} */ ([ { name: "Profit", @@ -1566,28 +1517,25 @@ function createGroupedRealizedPnlSections( /** * Create single base SOPR chart (all UTXO cohorts have base SOPR) - * @param {PartialContext} ctx * @param {CohortAll | CohortFull | CohortWithAdjusted | CohortLongTerm | CohortAgeRange | CohortBasicWithMarketCap | CohortBasicWithoutMarketCap | CohortAddress | CohortWithoutRelative} cohort * @param {(metric: string) => string} title * @returns {PartialChartOption} */ -function createSingleBaseSoprChart(ctx, cohort, title) { +function createSingleBaseSoprChart(cohort, title) { return { name: "Normal", title: title("SOPR"), - bottom: createSingleSoprSeries(ctx.colors, cohort.tree), + bottom: createSingleSoprSeries(colors, cohort.tree), }; } /** * Create single adjusted SOPR chart (cohorts with RealizedPattern3/4) - * @param {PartialContext} ctx * @param {CohortAll | CohortFull | CohortWithAdjusted} cohort * @param {(metric: string) => string} title * @returns {PartialChartOption} */ -function createSingleAdjustedSoprChart(ctx, cohort, title) { - const { colors } = ctx; +function createSingleAdjustedSoprChart(cohort, title) { const { tree } = cohort; return { @@ -1707,17 +1655,16 @@ function createGroupedAdjustedSoprChart(list, title) { /** * Create SOPR section with adjusted SOPR (for cohorts with RealizedPattern3/4) - * @param {PartialContext} ctx * @param {CohortAll | CohortFull | CohortWithAdjusted} cohort * @param {(metric: string) => string} title * @returns {PartialOptionsGroup} */ -function createSingleSoprSectionWithAdjusted(ctx, cohort, title) { +function createSingleSoprSectionWithAdjusted(cohort, title) { return { name: "SOPR", tree: [ - createSingleBaseSoprChart(ctx, cohort, title), - createSingleAdjustedSoprChart(ctx, cohort, title), + createSingleBaseSoprChart(cohort, title), + createSingleAdjustedSoprChart(cohort, title), ], }; } @@ -1740,15 +1687,14 @@ function createGroupedSoprSectionWithAdjusted(list, title) { /** * Create SOPR section without adjusted SOPR (for cohorts with RealizedPattern/2) - * @param {PartialContext} ctx * @param {CohortWithPercentiles | CohortBasic | CohortAddress | CohortWithoutRelative} cohort * @param {(metric: string) => string} title * @returns {PartialOptionsGroup} */ -function createSingleSoprSectionBasic(ctx, cohort, title) { +function createSingleSoprSectionBasic(cohort, title) { return { name: "SOPR", - tree: [createSingleBaseSoprChart(ctx, cohort, title)], + tree: [createSingleBaseSoprChart(cohort, title)], }; } @@ -1770,11 +1716,9 @@ function createGroupedSoprSectionBasic(list, title) { // ============================================================================ /** - * @param {PartialContext} ctx * @param {RelativeWithMarketCap} rel */ -function createUnrealizedPnlRelToMarketCapMetrics(ctx, rel) { - const { colors } = ctx; +function createUnrealizedPnlRelToMarketCapMetrics(rel) { return [ line({ metric: rel.unrealizedProfitRelToMarketCap, @@ -1800,11 +1744,9 @@ function createUnrealizedPnlRelToMarketCapMetrics(ctx, rel) { } /** - * @param {PartialContext} ctx * @param {RelativeWithOwnMarketCap} rel */ -function createUnrealizedPnlRelToOwnMarketCapMetrics(ctx, rel) { - const { colors } = ctx; +function createUnrealizedPnlRelToOwnMarketCapMetrics(rel) { return [ line({ metric: rel.unrealizedProfitRelToOwnMarketCap, @@ -1826,17 +1768,15 @@ function createUnrealizedPnlRelToOwnMarketCapMetrics(ctx, rel) { unit: Unit.pctOwnMcap, defaultActive: false, }), - priceLine({ ctx, unit: Unit.pctOwnMcap, number: 100 }), - priceLine({ ctx, unit: Unit.pctOwnMcap }), + priceLine({ unit: Unit.pctOwnMcap, number: 100 }), + priceLine({ unit: Unit.pctOwnMcap }), ]; } /** - * @param {PartialContext} ctx * @param {RelativeWithOwnPnl} rel */ -function createUnrealizedPnlRelToOwnPnlMetrics(ctx, rel) { - const { colors } = ctx; +function createUnrealizedPnlRelToOwnPnlMetrics(rel) { return [ line({ metric: rel.unrealizedProfitRelToOwnTotalUnrealizedPnl, @@ -1858,8 +1798,8 @@ function createUnrealizedPnlRelToOwnPnlMetrics(ctx, rel) { unit: Unit.pctOwnPnl, defaultActive: false, }), - priceLine({ ctx, unit: Unit.pctOwnPnl, number: 100 }), - priceLine({ ctx, unit: Unit.pctOwnPnl }), + priceLine({ unit: Unit.pctOwnPnl, number: 100 }), + priceLine({ unit: Unit.pctOwnPnl }), ]; } @@ -1877,25 +1817,23 @@ function createNetUnrealizedPnlRelToMarketCapMetrics(rel) { } /** - * @param {PartialContext} ctx * @param {RelativeWithOwnMarketCap} rel */ -function createNetUnrealizedPnlRelToOwnMarketCapMetrics(ctx, rel) { +function createNetUnrealizedPnlRelToOwnMarketCapMetrics(rel) { return [ baseline({ metric: rel.netUnrealizedPnlRelToOwnMarketCap, name: "Net", unit: Unit.pctOwnMcap, }), - priceLine({ ctx, unit: Unit.pctOwnMcap }), + priceLine({ unit: Unit.pctOwnMcap }), ]; } /** - * @param {PartialContext} ctx * @param {RelativeWithOwnPnl} rel */ -function createNetUnrealizedPnlRelToOwnPnlMetrics(ctx, rel) { +function createNetUnrealizedPnlRelToOwnPnlMetrics(rel) { return [ baseline({ metric: rel.netUnrealizedPnlRelToOwnTotalUnrealizedPnl, @@ -1907,12 +1845,10 @@ function createNetUnrealizedPnlRelToOwnPnlMetrics(ctx, rel) { /** * Create invested capital relative metrics (% of realized cap) - * @param {PartialContext} ctx * @param {RelativeWithInvestedCapitalPct} rel * @returns {AnyFetchedSeriesBlueprint[]} */ -function createInvestedCapitalRelMetrics(ctx, rel) { - const { colors } = ctx; +function createInvestedCapitalRelMetrics(rel) { return [ baseline({ metric: rel.investedCapitalInProfitPct, @@ -1931,11 +1867,9 @@ function createInvestedCapitalRelMetrics(ctx, rel) { /** * Base unrealized metrics (always present) - * @param {PartialContext} ctx * @param {{ unrealized: UnrealizedPattern }} tree */ -function createUnrealizedPnlBaseMetrics(ctx, tree) { - const { colors } = ctx; +function createUnrealizedPnlBaseMetrics(tree) { return [ line({ metric: tree.unrealized.totalUnrealizedPnl, @@ -1984,12 +1918,11 @@ function createNetUnrealizedPnlBaseMetric(tree) { /** * Create NUPL chart for single cohort - * @param {PartialContext} ctx * @param {RelativeWithNupl} rel * @param {(metric: string) => string} title * @returns {PartialChartOption} */ -function createNuplChart(ctx, rel, title) { +function createNuplChart(rel, title) { return { name: "NUPL", title: title("NUPL"), @@ -2005,12 +1938,11 @@ function createNuplChart(ctx, rel, title) { /** * Create peak regret chart (basic - just absolute value) - * @param {PartialContext} ctx * @param {{ unrealized: UnrealizedFullPattern }} tree * @param {(metric: string) => string} title * @returns {PartialChartOption} */ -function createPeakRegretChart(ctx, tree, title) { +function createPeakRegretChart(tree, title) { return { name: "Peak Regret", title: title("Unrealized Peak Regret"), @@ -2018,7 +1950,7 @@ function createPeakRegretChart(ctx, tree, title) { line({ metric: tree.unrealized.peakRegret, name: "Peak Regret", - color: ctx.colors.orange, + color: colors.orange, unit: Unit.usd, }), ], @@ -2027,12 +1959,11 @@ function createPeakRegretChart(ctx, tree, title) { /** * Create peak regret chart with RelToMarketCap metric - * @param {PartialContext} ctx * @param {{ unrealized: UnrealizedFullPattern, relative: RelativeWithMarketCap }} tree * @param {(metric: string) => string} title * @returns {PartialChartOption} */ -function createPeakRegretChartWithMarketCap(ctx, tree, title) { +function createPeakRegretChartWithMarketCap(tree, title) { return { name: "Peak Regret", title: title("Unrealized Peak Regret"), @@ -2040,13 +1971,13 @@ function createPeakRegretChartWithMarketCap(ctx, tree, title) { line({ metric: tree.unrealized.peakRegret, name: "Peak Regret", - color: ctx.colors.orange, + color: colors.orange, unit: Unit.usd, }), baseline({ metric: tree.relative.unrealizedPeakRegretRelToMarketCap, name: "Peak Regret", - color: ctx.colors.orange, + color: colors.orange, unit: Unit.pctMcap, }), ], @@ -2055,13 +1986,11 @@ function createPeakRegretChartWithMarketCap(ctx, tree, title) { /** * Create invested capital absolute chart - * @param {PartialContext} ctx * @param {{ unrealized: UnrealizedPattern }} tree * @param {(metric: string) => string} title * @returns {PartialChartOption} */ -function createSingleInvestedCapitalAbsoluteChart(ctx, tree, title) { - const { colors } = ctx; +function createSingleInvestedCapitalAbsoluteChart(tree, title) { return { name: "Absolute", title: title("Invested Capital In Profit & Loss"), @@ -2084,58 +2013,54 @@ function createSingleInvestedCapitalAbsoluteChart(ctx, tree, title) { /** * Create invested capital relative chart - * @param {PartialContext} ctx * @param {RelativeWithInvestedCapitalPct} rel * @param {(metric: string) => string} title * @returns {PartialChartOption} */ -function createSingleInvestedCapitalRelativeChart(ctx, rel, title) { +function createSingleInvestedCapitalRelativeChart(rel, title) { return { name: "Relative", title: title("Invested Capital In Profit & Loss %"), - bottom: [...createInvestedCapitalRelMetrics(ctx, rel)], + bottom: [...createInvestedCapitalRelMetrics(rel)], }; } /** * Create invested capital folder for cohorts WITHOUT relative metrics - * @param {PartialContext} ctx * @param {{ unrealized: UnrealizedPattern }} tree * @param {(metric: string) => string} title * @returns {PartialOptionsGroup} */ -function createSingleInvestedCapitalFolder(ctx, tree, title) { +function createSingleInvestedCapitalFolder(tree, title) { return { name: "Invested Capital", - tree: [createSingleInvestedCapitalAbsoluteChart(ctx, tree, title)], + tree: [createSingleInvestedCapitalAbsoluteChart(tree, title)], }; } /** * Create invested capital folder for cohorts WITH relative metrics - * @param {PartialContext} ctx * @param {{ unrealized: UnrealizedPattern, relative: RelativeWithInvestedCapitalPct }} tree * @param {(metric: string) => string} title * @returns {PartialOptionsGroup} */ -function createSingleInvestedCapitalFolderFull(ctx, tree, title) { +function createSingleInvestedCapitalFolderFull(tree, title) { return { name: "Invested Capital", tree: [ - createSingleInvestedCapitalAbsoluteChart(ctx, tree, title), - createSingleInvestedCapitalRelativeChart(ctx, tree.relative, title), + createSingleInvestedCapitalAbsoluteChart(tree, title), + createSingleInvestedCapitalRelativeChart(tree.relative, title), ], }; } /** * Create NUPL chart for grouped cohorts - * @param {PartialContext} ctx * @param {readonly { name: string, color: Color, tree: { relative: RelativeWithNupl } }[]} list * @param {(metric: string) => string} title * @returns {PartialChartOption} */ -function createGroupedNuplChart(ctx, list, title) { +function createGroupedNuplChart(list, title) { return { name: "NUPL", title: title("NUPL"), @@ -2207,7 +2132,6 @@ function createGroupedPeakRegretChart(list, title) { /** * Generic single unrealized section builder - callers pass typed metrics * @param {Object} args - * @param {PartialContext} args.ctx * @param {{ unrealized: UnrealizedPattern }} args.tree * @param {(metric: string) => string} args.title * @param {AnyFetchedSeriesBlueprint[]} [args.pnl] - Extra pnl metrics @@ -2217,7 +2141,6 @@ function createGroupedPeakRegretChart(list, title) { * @returns {PartialOptionsGroup} */ function createUnrealizedSection({ - ctx, tree, title, pnl = [], @@ -2225,14 +2148,13 @@ function createUnrealizedSection({ investedCapitalFolder, charts = [], }) { - const { colors } = ctx; return { name: "Unrealized", tree: [ { name: "P&L", title: title("Unrealized P&L"), - bottom: [...createUnrealizedPnlBaseMetrics(ctx, tree), ...pnl], + bottom: [...createUnrealizedPnlBaseMetrics(tree), ...pnl], }, { name: "Net P&L", @@ -2444,174 +2366,140 @@ function createGroupedUnrealizedSectionWithoutRelative(list, title) { /** * Unrealized section for All cohort (only RelToOwnPnl) - * @param {PartialContext} ctx * @param {CohortAll} cohort * @param {(metric: string) => string} title */ -function createSingleUnrealizedSectionAll(ctx, cohort, title) { +function createSingleUnrealizedSectionAll(cohort, title) { const { tree } = cohort; return createUnrealizedSection({ - ctx, tree, title, pnl: [ - ...createUnrealizedPnlRelToMarketCapMetrics(ctx, tree.relative), - ...createUnrealizedPnlRelToOwnPnlMetrics(ctx, tree.relative), + ...createUnrealizedPnlRelToMarketCapMetrics(tree.relative), + ...createUnrealizedPnlRelToOwnPnlMetrics(tree.relative), ], netPnl: [ ...createNetUnrealizedPnlRelToMarketCapMetrics(tree.relative), - ...createNetUnrealizedPnlRelToOwnPnlMetrics(ctx, tree.relative), + ...createNetUnrealizedPnlRelToOwnPnlMetrics(tree.relative), ], - investedCapitalFolder: createSingleInvestedCapitalFolderFull( - ctx, - tree, - title, - ), + investedCapitalFolder: createSingleInvestedCapitalFolderFull(tree, title), charts: [ - createNuplChart(ctx, tree.relative, title), - createPeakRegretChartWithMarketCap(ctx, tree, title), + createNuplChart(tree.relative, title), + createPeakRegretChartWithMarketCap(tree, title), ], }); } /** * Unrealized section for Full cohort (all capabilities: MarketCap + OwnMarketCap + OwnPnl) - * @param {PartialContext} ctx * @param {CohortFull} cohort * @param {(metric: string) => string} title */ -function createSingleUnrealizedSectionFull(ctx, cohort, title) { +function createSingleUnrealizedSectionFull(cohort, title) { const { tree } = cohort; return createUnrealizedSection({ - ctx, tree, title, pnl: [ - ...createUnrealizedPnlRelToMarketCapMetrics(ctx, tree.relative), - ...createUnrealizedPnlRelToOwnMarketCapMetrics(ctx, tree.relative), - ...createUnrealizedPnlRelToOwnPnlMetrics(ctx, tree.relative), + ...createUnrealizedPnlRelToMarketCapMetrics(tree.relative), + ...createUnrealizedPnlRelToOwnMarketCapMetrics(tree.relative), + ...createUnrealizedPnlRelToOwnPnlMetrics(tree.relative), ], netPnl: [ ...createNetUnrealizedPnlRelToMarketCapMetrics(tree.relative), - ...createNetUnrealizedPnlRelToOwnMarketCapMetrics(ctx, tree.relative), - ...createNetUnrealizedPnlRelToOwnPnlMetrics(ctx, tree.relative), + ...createNetUnrealizedPnlRelToOwnMarketCapMetrics(tree.relative), + ...createNetUnrealizedPnlRelToOwnPnlMetrics(tree.relative), ], - investedCapitalFolder: createSingleInvestedCapitalFolderFull( - ctx, - tree, - title, - ), + investedCapitalFolder: createSingleInvestedCapitalFolderFull(tree, title), charts: [ - createNuplChart(ctx, tree.relative, title), - createPeakRegretChartWithMarketCap(ctx, tree, title), + createNuplChart(tree.relative, title), + createPeakRegretChartWithMarketCap(tree, title), ], }); } /** * Unrealized section for WithAdjusted cohort (MarketCap + nupl) - * @param {PartialContext} ctx * @param {CohortWithAdjusted} cohort * @param {(metric: string) => string} title */ -function createSingleUnrealizedSectionWithMarketCap(ctx, cohort, title) { +function createSingleUnrealizedSectionWithMarketCap(cohort, title) { const { tree } = cohort; return createUnrealizedSection({ - ctx, tree, title, - pnl: [...createUnrealizedPnlRelToMarketCapMetrics(ctx, tree.relative)], + pnl: [...createUnrealizedPnlRelToMarketCapMetrics(tree.relative)], netPnl: [...createNetUnrealizedPnlRelToMarketCapMetrics(tree.relative)], - investedCapitalFolder: createSingleInvestedCapitalFolderFull( - ctx, - tree, - title, - ), + investedCapitalFolder: createSingleInvestedCapitalFolderFull(tree, title), charts: [ - createNuplChart(ctx, tree.relative, title), - createPeakRegretChartWithMarketCap(ctx, tree, title), + createNuplChart(tree.relative, title), + createPeakRegretChartWithMarketCap(tree, title), ], }); } /** * Unrealized section WITH RelToMarketCap metrics (for CohortBasicWithMarketCap) - * @param {PartialContext} ctx * @param {CohortBasicWithMarketCap} cohort * @param {(metric: string) => string} title */ -function createSingleUnrealizedSectionWithMarketCapOnly(ctx, cohort, title) { +function createSingleUnrealizedSectionWithMarketCapOnly(cohort, title) { const { tree } = cohort; return createUnrealizedSection({ - ctx, tree, title, - pnl: [...createUnrealizedPnlRelToMarketCapMetrics(ctx, tree.relative)], + pnl: [...createUnrealizedPnlRelToMarketCapMetrics(tree.relative)], netPnl: [...createNetUnrealizedPnlRelToMarketCapMetrics(tree.relative)], - investedCapitalFolder: createSingleInvestedCapitalFolderFull( - ctx, - tree, - title, - ), - charts: [createNuplChart(ctx, tree.relative, title)], + investedCapitalFolder: createSingleInvestedCapitalFolderFull(tree, title), + charts: [createNuplChart(tree.relative, title)], }); } /** * Unrealized section for minAge cohorts (has peakRegret) - * @param {PartialContext} ctx * @param {CohortMinAge} cohort * @param {(metric: string) => string} title */ -function createSingleUnrealizedSectionMinAge(ctx, cohort, title) { +function createSingleUnrealizedSectionMinAge(cohort, title) { const { tree } = cohort; return createUnrealizedSection({ - ctx, tree, title, - pnl: [...createUnrealizedPnlRelToMarketCapMetrics(ctx, tree.relative)], + pnl: [...createUnrealizedPnlRelToMarketCapMetrics(tree.relative)], netPnl: [...createNetUnrealizedPnlRelToMarketCapMetrics(tree.relative)], - investedCapitalFolder: createSingleInvestedCapitalFolderFull( - ctx, - tree, - title, - ), + investedCapitalFolder: createSingleInvestedCapitalFolderFull(tree, title), charts: [ - createNuplChart(ctx, tree.relative, title), - createPeakRegretChartWithMarketCap(ctx, tree, title), + createNuplChart(tree.relative, title), + createPeakRegretChartWithMarketCap(tree, title), ], }); } /** * Unrealized section with only base metrics (no RelToMarketCap) - * @param {PartialContext} ctx * @param {CohortBasicWithoutMarketCap} cohort * @param {(metric: string) => string} title */ -function createSingleUnrealizedSectionBase(ctx, cohort, title) { +function createSingleUnrealizedSectionBase(cohort, title) { const { tree } = cohort; return createUnrealizedSection({ - ctx, tree, title, - investedCapitalFolder: createSingleInvestedCapitalFolder(ctx, tree, title), + investedCapitalFolder: createSingleInvestedCapitalFolder(tree, title), }); } /** * Unrealized section for cohorts WITHOUT relative (edge case types: empty, p2ms, unknown) - * @param {PartialContext} ctx * @param {CohortWithoutRelative} cohort * @param {(metric: string) => string} title */ -function createSingleUnrealizedSectionWithoutRelative(ctx, cohort, title) { +function createSingleUnrealizedSectionWithoutRelative(cohort, title) { const { tree } = cohort; return createUnrealizedSection({ - ctx, tree, title, - investedCapitalFolder: createSingleInvestedCapitalFolder(ctx, tree, title), + investedCapitalFolder: createSingleInvestedCapitalFolder(tree, title), }); } @@ -2663,11 +2551,10 @@ function createGroupedUnrealizedBaseCharts(list, title) { /** * Grouped unrealized section for Full cohorts (all relative capabilities) - * @param {PartialContext} ctx * @param {readonly CohortFull[]} list * @param {(metric: string) => string} title */ -function createGroupedUnrealizedSectionFull(ctx, list, title) { +function createGroupedUnrealizedSectionFull(list, title) { return createGroupedUnrealizedSection({ list, title, @@ -2692,7 +2579,7 @@ function createGroupedUnrealizedSectionFull(ctx, list, title) { }), ], charts: [ - createGroupedNuplChart(ctx, list, title), + createGroupedNuplChart(list, title), createGroupedPeakRegretChart(list, title), ], }); @@ -2700,11 +2587,10 @@ function createGroupedUnrealizedSectionFull(ctx, list, title) { /** * Grouped unrealized section for WithAdjusted cohorts (MarketCap + nupl) - * @param {PartialContext} ctx * @param {readonly CohortWithAdjusted[]} list * @param {(metric: string) => string} title */ -function createGroupedUnrealizedSectionWithMarketCap(ctx, list, title) { +function createGroupedUnrealizedSectionWithMarketCap(list, title) { return createGroupedUnrealizedSection({ list, title, @@ -2717,7 +2603,7 @@ function createGroupedUnrealizedSectionWithMarketCap(ctx, list, title) { }), ], charts: [ - createGroupedNuplChart(ctx, list, title), + createGroupedNuplChart(list, title), createGroupedPeakRegretChart(list, title), ], }); @@ -2725,11 +2611,10 @@ function createGroupedUnrealizedSectionWithMarketCap(ctx, list, title) { /** * Grouped unrealized section WITH RelToMarketCap (for CohortBasicWithMarketCap) - * @param {PartialContext} ctx * @param {readonly CohortBasicWithMarketCap[]} list * @param {(metric: string) => string} title */ -function createGroupedUnrealizedSectionWithMarketCapOnly(ctx, list, title) { +function createGroupedUnrealizedSectionWithMarketCapOnly(list, title) { return createGroupedUnrealizedSection({ list, title, @@ -2741,17 +2626,16 @@ function createGroupedUnrealizedSectionWithMarketCapOnly(ctx, list, title) { unit: Unit.pctMcap, }), ], - charts: [createGroupedNuplChart(ctx, list, title)], + charts: [createGroupedNuplChart(list, title)], }); } /** * Grouped unrealized section for minAge cohorts (has peakRegret) - * @param {PartialContext} ctx * @param {readonly CohortMinAge[]} list * @param {(metric: string) => string} title */ -function createGroupedUnrealizedSectionMinAge(ctx, list, title) { +function createGroupedUnrealizedSectionMinAge(list, title) { return createGroupedUnrealizedSection({ list, title, @@ -2764,7 +2648,7 @@ function createGroupedUnrealizedSectionMinAge(ctx, list, title) { }), ], charts: [ - createGroupedNuplChart(ctx, list, title), + createGroupedNuplChart(list, title), createGroupedPeakRegretChart(list, title), ], }); @@ -2782,34 +2666,28 @@ function createGroupedUnrealizedSectionBase(list, title) { /** * Unrealized section for cohorts with nupl (OwnMarketCap + OwnPnl + nupl) * @param {Object} args - * @param {PartialContext} args.ctx * @param {CohortWithNuplPercentiles} args.cohort * @param {(metric: string) => string} args.title */ -function createSingleUnrealizedSectionWithNupl({ ctx, cohort, title }) { +function createSingleUnrealizedSectionWithNupl({ cohort, title }) { const { tree } = cohort; return createUnrealizedSection({ - ctx, tree, title, pnl: [ - ...createUnrealizedPnlRelToMarketCapMetrics(ctx, tree.relative), - ...createUnrealizedPnlRelToOwnMarketCapMetrics(ctx, tree.relative), - ...createUnrealizedPnlRelToOwnPnlMetrics(ctx, tree.relative), + ...createUnrealizedPnlRelToMarketCapMetrics(tree.relative), + ...createUnrealizedPnlRelToOwnMarketCapMetrics(tree.relative), + ...createUnrealizedPnlRelToOwnPnlMetrics(tree.relative), ], netPnl: [ ...createNetUnrealizedPnlRelToMarketCapMetrics(tree.relative), - ...createNetUnrealizedPnlRelToOwnMarketCapMetrics(ctx, tree.relative), - ...createNetUnrealizedPnlRelToOwnPnlMetrics(ctx, tree.relative), + ...createNetUnrealizedPnlRelToOwnMarketCapMetrics(tree.relative), + ...createNetUnrealizedPnlRelToOwnPnlMetrics(tree.relative), ], - investedCapitalFolder: createSingleInvestedCapitalFolderFull( - ctx, - tree, - title, - ), + investedCapitalFolder: createSingleInvestedCapitalFolderFull(tree, title), charts: [ - createNuplChart(ctx, tree.relative, title), - createPeakRegretChartWithMarketCap(ctx, tree, title), + createNuplChart(tree.relative, title), + createPeakRegretChartWithMarketCap(tree, title), ], }); } @@ -2817,11 +2695,10 @@ function createSingleUnrealizedSectionWithNupl({ ctx, cohort, title }) { /** * Grouped unrealized section for cohorts with nupl (OwnMarketCap + OwnPnl + nupl) * @param {Object} args - * @param {PartialContext} args.ctx * @param {readonly CohortWithNuplPercentiles[]} args.list * @param {(metric: string) => string} args.title */ -function createGroupedUnrealizedSectionWithNupl({ ctx, list, title }) { +function createGroupedUnrealizedSectionWithNupl({ list, title }) { return createGroupedUnrealizedSection({ list, title, @@ -2846,7 +2723,7 @@ function createGroupedUnrealizedSectionWithNupl({ ctx, list, title }) { }), ], charts: [ - createGroupedNuplChart(ctx, list, title), + createGroupedNuplChart(list, title), createGroupedPeakRegretChart(list, title), ], }); @@ -2854,30 +2731,24 @@ function createGroupedUnrealizedSectionWithNupl({ ctx, list, title }) { /** * Unrealized section for AgeRange cohort (no nupl via RelativePattern2) - * @param {PartialContext} ctx * @param {CohortAgeRange} cohort * @param {(metric: string) => string} title */ -function createSingleUnrealizedSectionAgeRange(ctx, cohort, title) { +function createSingleUnrealizedSectionAgeRange(cohort, title) { const { tree } = cohort; return createUnrealizedSection({ - ctx, tree, title, pnl: [ - ...createUnrealizedPnlRelToOwnMarketCapMetrics(ctx, tree.relative), - ...createUnrealizedPnlRelToOwnPnlMetrics(ctx, tree.relative), + ...createUnrealizedPnlRelToOwnMarketCapMetrics(tree.relative), + ...createUnrealizedPnlRelToOwnPnlMetrics(tree.relative), ], netPnl: [ - ...createNetUnrealizedPnlRelToOwnMarketCapMetrics(ctx, tree.relative), - ...createNetUnrealizedPnlRelToOwnPnlMetrics(ctx, tree.relative), + ...createNetUnrealizedPnlRelToOwnMarketCapMetrics(tree.relative), + ...createNetUnrealizedPnlRelToOwnPnlMetrics(tree.relative), ], - investedCapitalFolder: createSingleInvestedCapitalFolderFull( - ctx, - tree, - title, - ), - charts: [createPeakRegretChart(ctx, tree, title)], + investedCapitalFolder: createSingleInvestedCapitalFolderFull(tree, title), + charts: [createPeakRegretChart(tree, title)], }); } @@ -2914,15 +2785,13 @@ function createGroupedUnrealizedSectionAgeRange(list, title) { /** * Generic single cost basis section builder - callers pass optional percentiles - * @param {PartialContext} ctx * @param {Object} args * @param {UtxoCohortObject | CohortWithoutRelative} args.cohort * @param {(metric: string) => string} args.title * @param {PartialChartOption[]} [args.charts] - Extra charts (e.g., percentiles) * @returns {PartialOptionsGroup} */ -function createCostBasisSection(ctx, { cohort, title, charts = [] }) { - const { colors } = ctx; +function createCostBasisSection({ cohort, title, charts = [] }) { const { color, tree } = cohort; return { name: "Cost Basis", @@ -3026,14 +2895,12 @@ function createGroupedCostBasisSection({ list, title, charts = [] }) { /** * Create cost basis section for single cohort WITH percentiles - * @param {PartialContext} ctx * @param {CohortAll | CohortFull | CohortWithPercentiles} cohort * @param {(metric: string) => string} title * @returns {PartialOptionsGroup} */ -function createSingleCostBasisSectionWithPercentiles(ctx, cohort, title) { - const { colors } = ctx; - return createCostBasisSection(ctx, { +function createSingleCostBasisSectionWithPercentiles(cohort, title) { + return createCostBasisSection({ cohort, title, charts: [ @@ -3058,13 +2925,11 @@ function createSingleCostBasisSectionWithPercentiles(ctx, cohort, title) { /** * Create cost basis section for grouped cohorts WITH percentiles - * @param {PartialContext} ctx * @param {readonly (CohortFull | CohortWithPercentiles)[]} list * @param {(metric: string) => string} title * @returns {PartialOptionsGroup} */ -function createGroupedCostBasisSectionWithPercentiles(ctx, list, title) { - const { colors } = ctx; +function createGroupedCostBasisSectionWithPercentiles(list, title) { return createGroupedCostBasisSection({ list, title, @@ -3095,14 +2960,12 @@ function createGroupedCostBasisSectionWithPercentiles(ctx, list, title) { /** * Generic single activity section builder - callers pass optional extra value metrics * @param {Object} args - * @param {PartialContext} args.ctx * @param {UtxoCohortObject | CohortWithoutRelative} args.cohort * @param {(metric: string) => string} args.title * @param {AnyFetchedSeriesBlueprint[]} [args.valueMetrics] - Extra value metrics (e.g., adjusted) * @returns {PartialOptionsGroup} */ -function createActivitySection({ ctx, cohort, title, valueMetrics = [] }) { - const { colors } = ctx; +function createActivitySection({ cohort, title, valueMetrics = [] }) { const { tree, color } = cohort; return { @@ -3423,16 +3286,13 @@ function createGroupedActivitySection({ list, title, valueTree }) { /** * Create activity section with adjusted values (for cohorts with RealizedPattern3/4) - * @param {PartialContext} ctx * @param {CohortAll | CohortFull | CohortWithAdjusted} cohort * @param {(metric: string) => string} title * @returns {PartialOptionsGroup} */ -function createSingleActivitySectionWithAdjusted(ctx, cohort, title) { - const { colors } = ctx; +function createSingleActivitySectionWithAdjusted(cohort, title) { const { tree } = cohort; return createActivitySection({ - ctx, cohort, title, valueMetrics: [ diff --git a/website/scripts/options/full.js b/website/scripts/options/full.js index f69192844..1c6ff1877 100644 --- a/website/scripts/options/full.js +++ b/website/scripts/options/full.js @@ -3,17 +3,20 @@ import { createButtonElement, createAnchorElement } from "../utils/dom.js"; import { pushHistory, resetParams } from "../utils/url.js"; import { readStored, writeToStorage } from "../utils/storage.js"; import { stringToId } from "../utils/format.js"; -import { collect, markUsed, logUnused, extractTreeStructure } from "./unused.js"; +import { + collect, + markUsed, + logUnused, + extractTreeStructure, +} from "./unused.js"; import { localhost } from "../utils/env.js"; import { setQr } from "../panes/share.js"; import { getConstant } from "./constants.js"; -import { colors } from "../chart/colors.js"; +import { colors } from "../utils/colors.js"; import { Unit } from "../utils/units.js"; +import { brk } from "../client.js"; -/** - * @param {BrkClient} brk - */ -export function initOptions(brk) { +export function initOptions() { collect(brk.metrics); const LS_SELECTED_KEY = `selected_path`; @@ -26,9 +29,7 @@ export function initOptions(brk) { JSON.parse(readStored(LS_SELECTED_KEY) || "[]") || [] ).filter((v) => v); - const partialOptions = createPartialOptions({ - brk, - }); + const partialOptions = createPartialOptions(); // Log tree structure for analysis (localhost only) if (localhost) { @@ -115,9 +116,10 @@ export function initOptions(brk) { // Check for price pattern blueprint (has dollars/sats sub-metrics) // Use unknown cast for safe property access check - const maybePriceMetric = /** @type {{ dollars?: AnyMetricPattern, sats?: AnyMetricPattern }} */ ( - /** @type {unknown} */ (blueprint.metric) - ); + const maybePriceMetric = + /** @type {{ dollars?: AnyMetricPattern, sats?: AnyMetricPattern }} */ ( + /** @type {unknown} */ (blueprint.metric) + ); if (maybePriceMetric.dollars?.by && maybePriceMetric.sats?.by) { const { dollars, sats } = maybePriceMetric; markUsed(dollars); @@ -131,7 +133,9 @@ export function initOptions(brk) { } // After continue, we know this is a regular metric blueprint - const regularBlueprint = /** @type {AnyFetchedSeriesBlueprint} */ (blueprint); + const regularBlueprint = /** @type {AnyFetchedSeriesBlueprint} */ ( + blueprint + ); const metric = regularBlueprint.metric; const unit = regularBlueprint.unit; if (!unit) continue; @@ -171,7 +175,11 @@ export function initOptions(brk) { title: `${baseValue}`, color: colors.gray, unit, - options: { lineStyle: 4, lastValueVisible: false, crosshairMarkerVisible: false }, + options: { + lineStyle: 4, + lastValueVisible: false, + crosshairMarkerVisible: false, + }, }); } } @@ -247,7 +255,11 @@ export function initOptions(brk) { * @param {string} parentPathStr * @returns {{ nodes: ProcessedNode[], count: number }} */ - function processPartialTree(partialTree, parentPath = [], parentPathStr = "") { + function processPartialTree( + partialTree, + parentPath = [], + parentPathStr = "", + ) { /** @type {ProcessedNode[]} */ const nodes = []; let totalCount = 0; @@ -258,7 +270,11 @@ export function initOptions(brk) { const serName = stringToId(anyPartial.name); const pathStr = parentPathStr ? `${parentPathStr}/${serName}` : serName; const path = parentPath.concat(serName); - const { nodes: children, count } = processPartialTree(anyPartial.tree, path, pathStr); + const { nodes: children, count } = processPartialTree( + anyPartial.tree, + path, + pathStr, + ); // Skip groups with no children if (count === 0) continue; diff --git a/website/scripts/options/investing.js b/website/scripts/options/investing.js index 38c564c75..602594f02 100644 --- a/website/scripts/options/investing.js +++ b/website/scripts/options/investing.js @@ -1,5 +1,7 @@ /** Investing section - Investment strategy tools and analysis */ +import { colors } from "../utils/colors.js"; +import { brk } from "../client.js"; import { Unit } from "../utils/units.js"; import { line, baseline, price, dotted } from "./series.js"; import { satsBtcUsd } from "./shared.js"; @@ -39,6 +41,9 @@ const YEARS_2020S = /** @type {const} */ ([ ]); const YEARS_2010S = /** @type {const} */ ([2019, 2018, 2017, 2016, 2015]); +/** @typedef {typeof YEARS_2020S[number] | typeof YEARS_2010S[number]} DcaYear */ +/** @typedef {`_${DcaYear}`} DcaYearKey */ + /** @param {AllPeriodKey} key */ const periodName = (key) => periodIdToName(key.slice(1), true); @@ -65,14 +70,14 @@ const periodName = (key) => periodIdToName(key.slice(1), true); * Build DCA class entry from year * @param {Colors} colors * @param {MarketDca} dca - * @param {number} year + * @param {DcaYear} year * @returns {BaseEntryItem} */ function buildYearEntry(colors, dca, year) { - const key = /** @type {keyof Colors["dcaYears"]} */ (`_${year}`); + const key = /** @type {DcaYearKey} */ (`_${year}`); return { name: `${year}`, - color: colors.dcaYears[key], + color: colors.year[key], costBasis: dca.classAveragePrice[key], returns: dca.classReturns[key], minReturn: dca.classMinReturn[key], @@ -85,21 +90,19 @@ function buildYearEntry(colors, dca, year) { /** * Create Investing section - * @param {PartialContext} ctx * @returns {PartialOptionsGroup} */ -export function createInvestingSection(ctx) { - const { brk } = ctx; +export function createInvestingSection() { const { market } = brk.metrics; const { dca, lookback, returns } = market; return { name: "Investing", tree: [ - createDcaVsLumpSumSection(ctx, { dca, lookback, returns }), - createDcaByPeriodSection(ctx, { dca, returns }), - createLumpSumByPeriodSection(ctx, { dca, lookback, returns }), - createDcaByStartYearSection(ctx, { dca }), + createDcaVsLumpSumSection({ dca, lookback, returns }), + createDcaByPeriodSection({ dca, returns }), + createLumpSumByPeriodSection({ dca, lookback, returns }), + createDcaByStartYearSection({ dca }), ], }; } @@ -290,15 +293,12 @@ function createLongSingleEntry(colors, item) { /** * Create DCA vs Lump Sum section - * @param {PartialContext} ctx * @param {Object} args * @param {Market["dca"]} args.dca * @param {Market["lookback"]} args.lookback * @param {Market["returns"]} args.returns */ -export function createDcaVsLumpSumSection(ctx, { dca, lookback, returns }) { - const { colors } = ctx; - +export function createDcaVsLumpSumSection({ dca, lookback, returns }) { /** @param {AllPeriodKey} key */ const topPane = (key) => [ price({ @@ -531,21 +531,19 @@ export function createDcaVsLumpSumSection(ctx, { dca, lookback, returns }) { /** * Create period-based section (DCA or Lump Sum) - * @param {PartialContext} ctx * @param {Object} args * @param {Market["dca"]} args.dca * @param {Market["lookback"]} [args.lookback] * @param {Market["returns"]} args.returns */ -function createPeriodSection(ctx, { dca, lookback, returns }) { - const { colors } = ctx; +function createPeriodSection({ dca, lookback, returns }) { const isLumpSum = !!lookback; const suffix = isLumpSum ? "Lump Sum" : "DCA"; /** @param {AllPeriodKey} key @returns {BaseEntryItem} */ const buildBaseEntry = (key) => ({ name: periodName(key), - color: colors.dcaPeriods[key], + color: colors.dca[key], costBasis: isLumpSum ? lookback[key] : dca.periodAveragePrice[key], returns: isLumpSum ? dca.periodLumpSumReturns[key] : dca.periodReturns[key], minReturn: isLumpSum @@ -617,36 +615,31 @@ function createPeriodSection(ctx, { dca, lookback, returns }) { /** * Create DCA by Period section - * @param {PartialContext} ctx * @param {Object} args * @param {Market["dca"]} args.dca * @param {Market["returns"]} args.returns */ -export function createDcaByPeriodSection(ctx, { dca, returns }) { - return createPeriodSection(ctx, { dca, returns }); +export function createDcaByPeriodSection({ dca, returns }) { + return createPeriodSection({ dca, returns }); } /** * Create Lump Sum by Period section - * @param {PartialContext} ctx * @param {Object} args * @param {Market["dca"]} args.dca * @param {Market["lookback"]} args.lookback * @param {Market["returns"]} args.returns */ -export function createLumpSumByPeriodSection(ctx, { dca, lookback, returns }) { - return createPeriodSection(ctx, { dca, lookback, returns }); +export function createLumpSumByPeriodSection({ dca, lookback, returns }) { + return createPeriodSection({ dca, lookback, returns }); } /** * Create DCA by Start Year section - * @param {PartialContext} ctx * @param {Object} args * @param {Market["dca"]} args.dca */ -export function createDcaByStartYearSection(ctx, { dca }) { - const { colors } = ctx; - +export function createDcaByStartYearSection({ dca }) { /** @param {string} name @param {string} title @param {BaseEntryItem[]} entries */ const createDecadeGroup = (name, title, entries) => ({ name, diff --git a/website/scripts/options/market.js b/website/scripts/options/market.js index 05de32869..eac13d79c 100644 --- a/website/scripts/options/market.js +++ b/website/scripts/options/market.js @@ -1,5 +1,7 @@ /** Market section */ +import { colors } from "../utils/colors.js"; +import { brk } from "../client.js"; import { includes } from "../utils/array.js"; import { Unit } from "../utils/units.js"; import { priceLine, priceLines } from "./constants.js"; @@ -27,21 +29,27 @@ import { periodIdToName } from "./utils.js"; * @property {ActivePriceRatioPattern} ratio */ -const commonMaIds = /** @type {const} */ (["1w", "1m", "200d", "1y", "200w", "4y"]); +const commonMaIds = /** @type {const} */ ([ + "1w", + "1m", + "200d", + "1y", + "200w", + "4y", +]); /** - * @param {PartialContext} ctx * @param {string} label * @param {MaPeriod[]} averages */ -function createMaSubSection(ctx, label, averages) { +function createMaSubSection(label, averages) { const common = averages.filter((a) => includes(commonMaIds, a.id)); const more = averages.filter((a) => !includes(commonMaIds, a.id)); /** @param {MaPeriod} a */ const toFolder = (a) => ({ name: periodIdToName(a.id, true), - tree: createPriceRatioCharts(ctx, { + tree: createPriceRatioCharts({ context: `${periodIdToName(a.id, true)} ${label}`, legend: "average", pricePattern: a.ratio.price, @@ -56,7 +64,9 @@ function createMaSubSection(ctx, label, averages) { { name: "Compare", title: `Price ${label}s`, - top: averages.map((a) => price({ metric: a.ratio.price, name: a.id, color: a.color })), + top: averages.map((a) => + price({ metric: a.ratio.price, name: a.id, color: a.color }), + ), }, ...common.map(toFolder), { name: "More...", tree: more.map(toFolder) }, @@ -94,12 +104,21 @@ function returnsSubSection(name, periods) { { name: "Compare", title: `${name} Returns`, - bottom: periods.map((p) => baseline({ metric: p.returns, name: p.id, color: p.color, unit: Unit.percentage })), + bottom: periods.map((p) => + baseline({ + metric: p.returns, + name: p.id, + color: p.color, + unit: Unit.percentage, + }), + ), }, ...periods.map((p) => ({ name: periodIdToName(p.id, true), title: `${periodIdToName(p.id, true)} Returns`, - bottom: [baseline({ metric: p.returns, name: "Total", unit: Unit.percentage })], + bottom: [ + baseline({ metric: p.returns, name: "Total", unit: Unit.percentage }), + ], })), ], }; @@ -116,7 +135,14 @@ function returnsSubSectionWithCagr(name, periods) { { name: "Compare", title: `${name} Returns`, - bottom: periods.map((p) => baseline({ metric: p.returns, name: p.id, color: p.color, unit: Unit.percentage })), + bottom: periods.map((p) => + baseline({ + metric: p.returns, + name: p.id, + color: p.color, + unit: Unit.percentage, + }), + ), }, ...periods.map((p) => ({ name: periodIdToName(p.id, true), @@ -141,7 +167,9 @@ function historicalSubSection(name, periods) { { name: "Compare", title: `${name} Historical`, - top: periods.map((p) => price({ metric: p.lookback, name: p.id, color: p.color })), + top: periods.map((p) => + price({ metric: p.lookback, name: p.id, color: p.color }), + ), }, ...periods.map((p) => ({ name: periodIdToName(p.id, true), @@ -154,33 +182,119 @@ function historicalSubSection(name, periods) { /** * Create Market section - * @param {PartialContext} ctx * @returns {PartialOptionsGroup} */ -export function createMarketSection(ctx) { - const { colors, brk } = ctx; +export function createMarketSection() { const { market, supply, price: priceMetrics } = brk.metrics; - const { movingAverage: ma, ath, returns, volatility, range, indicators, lookback } = market; + const { + movingAverage: ma, + ath, + returns, + volatility, + range, + indicators, + lookback, + } = market; /** @type {Period[]} */ const shortPeriods = [ - { id: "1d", color: colors.returns._1d, returns: returns.priceReturns._1d, lookback: lookback._1d }, - { id: "1w", color: colors.returns._1w, returns: returns.priceReturns._1w, lookback: lookback._1w }, - { id: "1m", color: colors.returns._1m, returns: returns.priceReturns._1m, lookback: lookback._1m }, - { id: "3m", color: colors.returns._3m, returns: returns.priceReturns._3m, lookback: lookback._3m, defaultActive: false }, - { id: "6m", color: colors.returns._6m, returns: returns.priceReturns._6m, lookback: lookback._6m, defaultActive: false }, - { id: "1y", color: colors.returns._1y, returns: returns.priceReturns._1y, lookback: lookback._1y }, + { + id: "1d", + color: colors.returns._1d, + returns: returns.priceReturns._1d, + lookback: lookback._1d, + }, + { + id: "1w", + color: colors.returns._1w, + returns: returns.priceReturns._1w, + lookback: lookback._1w, + }, + { + id: "1m", + color: colors.returns._1m, + returns: returns.priceReturns._1m, + lookback: lookback._1m, + }, + { + id: "3m", + color: colors.returns._3m, + returns: returns.priceReturns._3m, + lookback: lookback._3m, + defaultActive: false, + }, + { + id: "6m", + color: colors.returns._6m, + returns: returns.priceReturns._6m, + lookback: lookback._6m, + defaultActive: false, + }, + { + id: "1y", + color: colors.returns._1y, + returns: returns.priceReturns._1y, + lookback: lookback._1y, + }, ]; /** @type {PeriodWithCagr[]} */ const longPeriods = [ - { id: "2y", color: colors.returns._2y, returns: returns.priceReturns._2y, cagr: returns.cagr._2y, lookback: lookback._2y, defaultActive: false }, - { id: "3y", color: colors.returns._3y, returns: returns.priceReturns._3y, cagr: returns.cagr._3y, lookback: lookback._3y, defaultActive: false }, - { id: "4y", color: colors.returns._4y, returns: returns.priceReturns._4y, cagr: returns.cagr._4y, lookback: lookback._4y }, - { id: "5y", color: colors.returns._5y, returns: returns.priceReturns._5y, cagr: returns.cagr._5y, lookback: lookback._5y, defaultActive: false }, - { id: "6y", color: colors.returns._6y, returns: returns.priceReturns._6y, cagr: returns.cagr._6y, lookback: lookback._6y, defaultActive: false }, - { id: "8y", color: colors.returns._8y, returns: returns.priceReturns._8y, cagr: returns.cagr._8y, lookback: lookback._8y, defaultActive: false }, - { id: "10y", color: colors.returns._10y, returns: returns.priceReturns._10y, cagr: returns.cagr._10y, lookback: lookback._10y, defaultActive: false }, + { + id: "2y", + color: colors.returns._2y, + returns: returns.priceReturns._2y, + cagr: returns.cagr._2y, + lookback: lookback._2y, + defaultActive: false, + }, + { + id: "3y", + color: colors.returns._3y, + returns: returns.priceReturns._3y, + cagr: returns.cagr._3y, + lookback: lookback._3y, + defaultActive: false, + }, + { + id: "4y", + color: colors.returns._4y, + returns: returns.priceReturns._4y, + cagr: returns.cagr._4y, + lookback: lookback._4y, + }, + { + id: "5y", + color: colors.returns._5y, + returns: returns.priceReturns._5y, + cagr: returns.cagr._5y, + lookback: lookback._5y, + defaultActive: false, + }, + { + id: "6y", + color: colors.returns._6y, + returns: returns.priceReturns._6y, + cagr: returns.cagr._6y, + lookback: lookback._6y, + defaultActive: false, + }, + { + id: "8y", + color: colors.returns._8y, + returns: returns.priceReturns._8y, + cagr: returns.cagr._8y, + lookback: lookback._8y, + defaultActive: false, + }, + { + id: "10y", + color: colors.returns._10y, + returns: returns.priceReturns._10y, + cagr: returns.cagr._10y, + lookback: lookback._10y, + defaultActive: false, + }, ]; /** @type {MaPeriod[]} */ @@ -225,12 +339,48 @@ export function createMarketSection(ctx) { // SMA vs EMA comparison periods (common periods only) const smaVsEma = [ - { id: "1w", name: "1 Week", color: colors.ma._1w, sma: ma.price1wSma, ema: ma.price1wEma }, - { id: "1m", name: "1 Month", color: colors.ma._1m, sma: ma.price1mSma, ema: ma.price1mEma }, - { id: "200d", name: "200 Day", color: colors.ma._200d, sma: ma.price200dSma, ema: ma.price200dEma }, - { id: "1y", name: "1 Year", color: colors.ma._1y, sma: ma.price1ySma, ema: ma.price1yEma }, - { id: "200w", name: "200 Week", color: colors.ma._200w, sma: ma.price200wSma, ema: ma.price200wEma }, - { id: "4y", name: "4 Year", color: colors.ma._4y, sma: ma.price4ySma, ema: ma.price4yEma }, + { + id: "1w", + name: "1 Week", + color: colors.ma._1w, + sma: ma.price1wSma, + ema: ma.price1wEma, + }, + { + id: "1m", + name: "1 Month", + color: colors.ma._1m, + sma: ma.price1mSma, + ema: ma.price1mEma, + }, + { + id: "200d", + name: "200 Day", + color: colors.ma._200d, + sma: ma.price200dSma, + ema: ma.price200dEma, + }, + { + id: "1y", + name: "1 Year", + color: colors.ma._1y, + sma: ma.price1ySma, + ema: ma.price1yEma, + }, + { + id: "200w", + name: "200 Week", + color: colors.ma._200w, + sma: ma.price200wSma, + ema: ma.price200wEma, + }, + { + id: "4y", + name: "4 Year", + color: colors.ma._4y, + sma: ma.price4ySma, + ema: ma.price4yEma, + }, ]; return { @@ -241,13 +391,25 @@ export function createMarketSection(ctx) { { name: "Sats/$", title: "Sats per Dollar", - bottom: [line({ metric: priceMetrics.sats.split.close, name: "Sats/$", unit: Unit.sats })], + bottom: [ + line({ + metric: priceMetrics.sats.split.close, + name: "Sats/$", + unit: Unit.sats, + }), + ], }, { name: "Capitalization", title: "Market Capitalization", - bottom: [line({ metric: supply.marketCap, name: "Capitalization", unit: Unit.usd })], + bottom: [ + line({ + metric: supply.marketCap, + name: "Capitalization", + unit: Unit.usd, + }), + ], }, { @@ -257,17 +419,42 @@ export function createMarketSection(ctx) { name: "Drawdown", title: "ATH Drawdown", top: [price({ metric: ath.priceAth, name: "ATH" })], - bottom: [line({ metric: ath.priceDrawdown, name: "Drawdown", color: colors.red, unit: Unit.percentage })], + bottom: [ + line({ + metric: ath.priceDrawdown, + name: "Drawdown", + color: colors.red, + unit: Unit.percentage, + }), + ], }, { name: "Time Since", title: "Time Since ATH", top: [price({ metric: ath.priceAth, name: "ATH" })], bottom: [ - line({ metric: ath.daysSincePriceAth, name: "Since", unit: Unit.days }), - line({ metric: ath.yearsSincePriceAth, name: "Since", unit: Unit.years }), - line({ metric: ath.maxDaysBetweenPriceAths, name: "Max", color: colors.red, unit: Unit.days }), - line({ metric: ath.maxYearsBetweenPriceAths, name: "Max", color: colors.red, unit: Unit.years }), + line({ + metric: ath.daysSincePriceAth, + name: "Since", + unit: Unit.days, + }), + line({ + metric: ath.yearsSincePriceAth, + name: "Since", + unit: Unit.years, + }), + line({ + metric: ath.maxDaysBetweenPriceAths, + name: "Max", + color: colors.red, + unit: Unit.days, + }), + line({ + metric: ath.maxYearsBetweenPriceAths, + name: "Max", + color: colors.red, + unit: Unit.years, + }), ], }, ], @@ -297,25 +484,47 @@ export function createMarketSection(ctx) { { name: "Volatility", tree: [ - volatilityChart(colors, "Index", "Volatility Index", Unit.percentage, { - _1w: volatility.price1wVolatility, - _1m: volatility.price1mVolatility, - _1y: volatility.price1yVolatility, - }), + volatilityChart( + colors, + "Index", + "Volatility Index", + Unit.percentage, + { + _1w: volatility.price1wVolatility, + _1m: volatility.price1mVolatility, + _1y: volatility.price1yVolatility, + }, + ), { name: "True Range", title: "True Range", bottom: [ - line({ metric: range.priceTrueRange, name: "Daily", color: colors.yellow, unit: Unit.usd }), - line({ metric: range.priceTrueRange2wSum, name: "2w Sum", color: colors.orange, unit: Unit.usd, defaultActive: false }), + line({ + metric: range.priceTrueRange, + name: "Daily", + color: colors.yellow, + unit: Unit.usd, + }), + line({ + metric: range.priceTrueRange2wSum, + name: "2w Sum", + color: colors.orange, + unit: Unit.usd, + defaultActive: false, + }), ], }, { name: "Choppiness", title: "Choppiness Index", bottom: [ - line({ metric: range.price2wChoppinessIndex, name: "2w", color: colors.red, unit: Unit.index }), - ...priceLines({ ctx, unit: Unit.index, numbers: [61.8, 38.2] }), + line({ + metric: range.price2wChoppinessIndex, + name: "2w", + color: colors.red, + unit: Unit.index, + }), + ...priceLines({ unit: Unit.index, numbers: [61.8, 38.2] }), ], }, volatilityChart(colors, "Sharpe Ratio", "Sharpe Ratio", Unit.ratio, { @@ -323,11 +532,17 @@ export function createMarketSection(ctx) { _1m: volatility.sharpe1m, _1y: volatility.sharpe1y, }), - volatilityChart(colors, "Sortino Ratio", "Sortino Ratio", Unit.ratio, { - _1w: volatility.sortino1w, - _1m: volatility.sortino1m, - _1y: volatility.sortino1y, - }), + volatilityChart( + colors, + "Sortino Ratio", + "Sortino Ratio", + Unit.ratio, + { + _1w: volatility.sortino1w, + _1m: volatility.sortino1m, + _1y: volatility.sortino1y, + }, + ), ], }, @@ -341,8 +556,17 @@ export function createMarketSection(ctx) { name: "All Periods", title: "SMA vs EMA Comparison", top: smaVsEma.flatMap((p) => [ - price({ metric: p.sma.price, name: `${p.id} SMA`, color: p.color }), - price({ metric: p.ema.price, name: `${p.id} EMA`, color: p.color, style: 1 }), + price({ + metric: p.sma.price, + name: `${p.id} SMA`, + color: p.color, + }), + price({ + metric: p.ema.price, + name: `${p.id} EMA`, + color: p.color, + style: 1, + }), ]), }, ...smaVsEma.map((p) => ({ @@ -350,13 +574,18 @@ export function createMarketSection(ctx) { title: `${p.name} SMA vs EMA`, top: [ price({ metric: p.sma.price, name: "SMA", color: p.color }), - price({ metric: p.ema.price, name: "EMA", color: p.color, style: 1 }), + price({ + metric: p.ema.price, + name: "EMA", + color: p.color, + style: 1, + }), ], })), ], }, - createMaSubSection(ctx, "SMA", sma), - createMaSubSection(ctx, "EMA", ema), + createMaSubSection("SMA", sma), + createMaSubSection("EMA", ema), ], }, @@ -366,16 +595,46 @@ export function createMarketSection(ctx) { { name: "MinMax", tree: [ - { id: "1w", name: "1 Week", min: range.price1wMin, max: range.price1wMax }, - { id: "2w", name: "2 Week", min: range.price2wMin, max: range.price2wMax }, - { id: "1m", name: "1 Month", min: range.price1mMin, max: range.price1mMax }, - { id: "1y", name: "1 Year", min: range.price1yMin, max: range.price1yMax }, + { + id: "1w", + name: "1 Week", + min: range.price1wMin, + max: range.price1wMax, + }, + { + id: "2w", + name: "2 Week", + min: range.price2wMin, + max: range.price2wMax, + }, + { + id: "1m", + name: "1 Month", + min: range.price1mMin, + max: range.price1mMax, + }, + { + id: "1y", + name: "1 Year", + min: range.price1yMin, + max: range.price1yMax, + }, ].map((p) => ({ name: p.id, title: `${p.name} MinMax`, top: [ - price({ metric: p.min, name: "Min", key: "price-min", color: colors.red }), - price({ metric: p.max, name: "Max", key: "price-max", color: colors.green }), + price({ + metric: p.min, + name: "Min", + key: "price-min", + color: colors.red, + }), + price({ + metric: p.max, + name: "Max", + key: "price-max", + color: colors.green, + }), ], })), }, @@ -383,9 +642,21 @@ export function createMarketSection(ctx) { name: "Mayer Multiple", title: "Mayer Multiple", top: [ - price({ metric: ma.price200dSma.price, name: "200d SMA", color: colors.yellow }), - price({ metric: ma.price200dSmaX24, name: "200d SMA x2.4", color: colors.green }), - price({ metric: ma.price200dSmaX08, name: "200d SMA x0.8", color: colors.red }), + price({ + metric: ma.price200dSma.price, + name: "200d SMA", + color: colors.yellow, + }), + price({ + metric: ma.price200dSmaX24, + name: "200d SMA x2.4", + color: colors.green, + }), + price({ + metric: ma.price200dSmaX08, + name: "200d SMA x0.8", + color: colors.red, + }), ], }, ], @@ -398,30 +669,71 @@ export function createMarketSection(ctx) { name: "RSI", title: "RSI (14d)", bottom: [ - line({ metric: indicators.rsi14d, name: "RSI", color: colors.indigo, unit: Unit.index }), - line({ metric: indicators.rsi14dMin, name: "Min", color: colors.red, defaultActive: false, unit: Unit.index }), - line({ metric: indicators.rsi14dMax, name: "Max", color: colors.green, defaultActive: false, unit: Unit.index }), - priceLine({ ctx, unit: Unit.index, number: 70 }), - priceLine({ ctx, unit: Unit.index, number: 50, defaultActive: false }), - priceLine({ ctx, unit: Unit.index, number: 30 }), + line({ + metric: indicators.rsi14d, + name: "RSI", + color: colors.indigo, + unit: Unit.index, + }), + line({ + metric: indicators.rsi14dMin, + name: "Min", + color: colors.red, + defaultActive: false, + unit: Unit.index, + }), + line({ + metric: indicators.rsi14dMax, + name: "Max", + color: colors.green, + defaultActive: false, + unit: Unit.index, + }), + priceLine({ unit: Unit.index, number: 70 }), + priceLine({ unit: Unit.index, number: 50, defaultActive: false }), + priceLine({ unit: Unit.index, number: 30 }), ], }, { name: "StochRSI", title: "Stochastic RSI", bottom: [ - line({ metric: indicators.stochRsiK, name: "K", color: colors.blue, unit: Unit.index }), - line({ metric: indicators.stochRsiD, name: "D", color: colors.orange, unit: Unit.index }), - ...priceLines({ ctx, unit: Unit.index, numbers: [80, 20] }), + line({ + metric: indicators.stochRsiK, + name: "K", + color: colors.blue, + unit: Unit.index, + }), + line({ + metric: indicators.stochRsiD, + name: "D", + color: colors.orange, + unit: Unit.index, + }), + ...priceLines({ unit: Unit.index, numbers: [80, 20] }), ], }, { name: "MACD", title: "MACD", bottom: [ - line({ metric: indicators.macdLine, name: "MACD", color: colors.blue, unit: Unit.usd }), - line({ metric: indicators.macdSignal, name: "Signal", color: colors.orange, unit: Unit.usd }), - histogram({ metric: indicators.macdHistogram, name: "Histogram", unit: Unit.usd }), + line({ + metric: indicators.macdLine, + name: "MACD", + color: colors.blue, + unit: Unit.usd, + }), + line({ + metric: indicators.macdSignal, + name: "Signal", + color: colors.orange, + unit: Unit.usd, + }), + histogram({ + metric: indicators.macdHistogram, + name: "Histogram", + unit: Unit.usd, + }), ], }, ], @@ -454,25 +766,61 @@ export function createMarketSection(ctx) { name: "Pi Cycle", title: "Pi Cycle", top: [ - price({ metric: ma.price111dSma.price, name: "111d SMA", color: colors.green }), - price({ metric: ma.price350dSmaX2, name: "350d SMA x2", color: colors.red }), + price({ + metric: ma.price111dSma.price, + name: "111d SMA", + color: colors.green, + }), + price({ + metric: ma.price350dSmaX2, + name: "350d SMA x2", + color: colors.red, + }), + ], + bottom: [ + baseline({ + metric: indicators.piCycle, + name: "Pi Cycle", + unit: Unit.ratio, + base: 1, + }), ], - bottom: [baseline({ metric: indicators.piCycle, name: "Pi Cycle", unit: Unit.ratio, base: 1 })], }, { name: "Puell Multiple", title: "Puell Multiple", - bottom: [line({ metric: indicators.puellMultiple, name: "Puell", color: colors.green, unit: Unit.ratio })], + bottom: [ + line({ + metric: indicators.puellMultiple, + name: "Puell", + color: colors.green, + unit: Unit.ratio, + }), + ], }, { name: "NVT", title: "NVT Ratio", - bottom: [line({ metric: indicators.nvt, name: "NVT", color: colors.orange, unit: Unit.ratio })], + bottom: [ + line({ + metric: indicators.nvt, + name: "NVT", + color: colors.orange, + unit: Unit.ratio, + }), + ], }, { name: "Gini", title: "Gini Coefficient", - bottom: [line({ metric: indicators.gini, name: "Gini", color: colors.red, unit: Unit.ratio })], + bottom: [ + line({ + metric: indicators.gini, + name: "Gini", + color: colors.red, + unit: Unit.ratio, + }), + ], }, ], }, diff --git a/website/scripts/options/mining.js b/website/scripts/options/mining.js index 8c43b3a98..def27cb5b 100644 --- a/website/scripts/options/mining.js +++ b/website/scripts/options/mining.js @@ -2,7 +2,7 @@ import { Unit } from "../utils/units.js"; import { entries, includes } from "../utils/array.js"; -import { colorAt } from "../chart/colors.js"; +import { colorAt, colors } from "../utils/colors.js"; import { line, baseline, @@ -16,6 +16,7 @@ import { satsBtcUsdFromFull, revenueBtcSatsUsd, } from "./shared.js"; +import { brk } from "../client.js"; /** Major pools to show in Compare section (by current hashrate dominance) */ const MAJOR_POOL_IDS = /** @type {const} */ ([ @@ -49,11 +50,9 @@ const ANTPOOL_AND_FRIENDS_IDS = /** @type {const} */ ([ /** * Create Mining section - * @param {PartialContext} ctx * @returns {PartialOptionsGroup} */ -export function createMiningSection(ctx) { - const { colors, brk } = ctx; +export function createMiningSection() { const { blocks, transactions, pools } = brk.metrics; // Pre-compute pool entries with resolved names @@ -174,7 +173,7 @@ export function createMiningSection(ctx) { { name: "Sum", title: `Rewards: ${name}`, - bottom: revenueBtcSatsUsd(colors, { + bottom: revenueBtcSatsUsd({ coinbase: pool.coinbase, subsidy: pool.subsidy, fee: pool.fee, @@ -184,7 +183,7 @@ export function createMiningSection(ctx) { { name: "Cumulative", title: `Rewards: ${name} (Total)`, - bottom: revenueBtcSatsUsd(colors, { + bottom: revenueBtcSatsUsd({ coinbase: pool.coinbase, subsidy: pool.subsidy, fee: pool.fee, @@ -330,7 +329,7 @@ export function createMiningSection(ctx) { { name: "Sum", title: "Revenue Comparison", - bottom: revenueBtcSatsUsd(colors, { + bottom: revenueBtcSatsUsd({ coinbase: blocks.rewards.coinbase, subsidy: blocks.rewards.subsidy, fee: transactions.fees.fee, @@ -340,7 +339,7 @@ export function createMiningSection(ctx) { { name: "Cumulative", title: "Revenue Comparison (Total)", - bottom: revenueBtcSatsUsd(colors, { + bottom: revenueBtcSatsUsd({ coinbase: blocks.rewards.coinbase, subsidy: blocks.rewards.subsidy, fee: transactions.fees.fee, diff --git a/website/scripts/options/network.js b/website/scripts/options/network.js index 7d529cea7..b4670be46 100644 --- a/website/scripts/options/network.js +++ b/website/scripts/options/network.js @@ -1,26 +1,26 @@ /** Network section - On-chain activity and health */ +import { colors } from "../utils/colors.js"; +import { brk } from "../client.js"; import { Unit } from "../utils/units.js"; -import { includes } from "../utils/array.js"; import { priceLine } from "./constants.js"; -import { line, dots, fromSupplyPattern } from "./series.js"; +import { + line, + dots, + fromSupplyPattern, + fromBaseStatsPattern, + chartsFromFull, + chartsFromValueFull, + fromStatsPattern, + chartsFromSum, +} from "./series.js"; import { satsBtcUsd, satsBtcUsdFrom } from "./shared.js"; /** * Create Network section - * @param {PartialContext} ctx * @returns {PartialOptionsGroup} */ -export function createNetworkSection(ctx) { - const { - colors, - brk, - fromBaseStatsPattern, - fromStatsPattern, - chartsFromFull, - chartsFromSum, - chartsFromValueFull, - } = ctx; +export function createNetworkSection() { const { blocks, transactions, @@ -31,162 +31,62 @@ export function createNetworkSection(ctx) { distribution, } = brk.metrics; - // Address types for mapping (newest to oldest) + const st = colors.scriptType; + + // Addressable types - newest to oldest (for addresses/counts that only support addressable types) const addressTypes = /** @type {const} */ ([ - { - key: "p2a", - name: "P2A", - color: colors[spendableTypeColors.p2a], - defaultActive: false, - }, - { - key: "p2tr", - name: "P2TR", - color: colors[spendableTypeColors.p2tr], - defaultActive: true, - }, - { - key: "p2wsh", - name: "P2WSH", - color: colors[spendableTypeColors.p2wsh], - defaultActive: true, - }, - { - key: "p2wpkh", - name: "P2WPKH", - color: colors[spendableTypeColors.p2wpkh], - defaultActive: true, - }, - { - key: "p2sh", - name: "P2SH", - color: colors[spendableTypeColors.p2sh], - defaultActive: true, - }, - { - key: "p2pkh", - name: "P2PKH", - color: colors[spendableTypeColors.p2pkh], - defaultActive: true, - }, - { - key: "p2pk33", - name: "P2PK33", - color: colors[spendableTypeColors.p2pk33], - defaultActive: false, - }, - { - key: "p2pk65", - name: "P2PK65", - color: colors[spendableTypeColors.p2pk65], - defaultActive: false, - }, + { key: "p2a", name: "P2A", color: st.p2a, defaultActive: false }, + { key: "p2tr", name: "P2TR", color: st.p2tr, defaultActive: true }, + { key: "p2wsh", name: "P2WSH", color: st.p2wsh, defaultActive: true }, + { key: "p2wpkh", name: "P2WPKH", color: st.p2wpkh, defaultActive: true }, + { key: "p2sh", name: "P2SH", color: st.p2sh, defaultActive: true }, + { key: "p2pkh", name: "P2PKH", color: st.p2pkh, defaultActive: true }, + { key: "p2pk33", name: "P2PK33", color: st.p2pk33, defaultActive: false }, + { key: "p2pk65", name: "P2PK65", color: st.p2pk65, defaultActive: false }, ]); - // Address type groups (newest to oldest) - const legacyAddresses = /** @type {const} */ ([ - { key: "p2sh", name: "P2SH", color: colors[spendableTypeColors.p2sh] }, - { key: "p2pkh", name: "P2PKH", color: colors[spendableTypeColors.p2pkh] }, - { - key: "p2pk33", - name: "P2PK33", - color: colors[spendableTypeColors.p2pk33], - }, - { - key: "p2pk65", - name: "P2PK65", - color: colors[spendableTypeColors.p2pk65], - }, - ]); - const segwitAddresses = /** @type {const} */ ([ - { key: "p2wsh", name: "P2WSH", color: colors[spendableTypeColors.p2wsh] }, - { - key: "p2wpkh", - name: "P2WPKH", - color: colors[spendableTypeColors.p2wpkh], - }, - ]); - const taprootAddresses = /** @type {const} */ ([ - { key: "p2a", name: "P2A", color: colors[spendableTypeColors.p2a] }, - { key: "p2tr", name: "P2TR", color: colors[spendableTypeColors.p2tr] }, - ]); - - // Script types for output count comparisons (newest to oldest, then non-addressable) - const scriptTypes = /** @type {const} */ ([ - { - key: "p2a", - name: "P2A", - color: colors[spendableTypeColors.p2a], - defaultActive: false, - }, - { - key: "p2tr", - name: "P2TR", - color: colors[spendableTypeColors.p2tr], - defaultActive: true, - }, - { - key: "p2wsh", - name: "P2WSH", - color: colors[spendableTypeColors.p2wsh], - defaultActive: true, - }, - { - key: "p2wpkh", - name: "P2WPKH", - color: colors[spendableTypeColors.p2wpkh], - defaultActive: true, - }, - { - key: "p2sh", - name: "P2SH", - color: colors[spendableTypeColors.p2sh], - defaultActive: true, - }, - { - key: "p2pkh", - name: "P2PKH", - color: colors[spendableTypeColors.p2pkh], - defaultActive: true, - }, - { - key: "p2pk33", - name: "P2PK33", - color: colors[spendableTypeColors.p2pk33], - defaultActive: false, - }, - { - key: "p2pk65", - name: "P2PK65", - color: colors[spendableTypeColors.p2pk65], - defaultActive: false, - }, - { - key: "p2ms", - name: "P2MS", - color: colors[spendableTypeColors.p2ms], - defaultActive: false, - }, + // Non-addressable script types + const nonAddressableTypes = /** @type {const} */ ([ + { key: "p2ms", name: "P2MS", color: st.p2ms, defaultActive: false }, { key: "opreturn", name: "OP_RETURN", - color: colors[spendableTypeColors.opreturn], + color: st.opreturn, defaultActive: false, }, { key: "emptyoutput", name: "Empty", - color: colors[spendableTypeColors.empty], + color: st.empty, defaultActive: false, }, { key: "unknownoutput", name: "Unknown", - color: colors[spendableTypeColors.unknown], + color: st.unknown, defaultActive: false, }, ]); + // All script types = addressable + non-addressable + const scriptTypes = [...addressTypes, ...nonAddressableTypes]; + + // Address type groups (by era) + const taprootAddresses = /** @type {const} */ ([ + { key: "p2a", name: "P2A", color: st.p2a }, + { key: "p2tr", name: "P2TR", color: st.p2tr }, + ]); + const segwitAddresses = /** @type {const} */ ([ + { key: "p2wsh", name: "P2WSH", color: st.p2wsh }, + { key: "p2wpkh", name: "P2WPKH", color: st.p2wpkh }, + ]); + const legacyAddresses = /** @type {const} */ ([ + { key: "p2sh", name: "P2SH", color: st.p2sh }, + { key: "p2pkh", name: "P2PKH", color: st.p2pkh }, + { key: "p2pk33", name: "P2PK33", color: st.p2pk33 }, + { key: "p2pk65", name: "P2PK65", color: st.p2pk65 }, + ]); + // Transacting types (transaction participation) const transactingTypes = /** @type {const} */ ([ { @@ -442,49 +342,25 @@ export function createNetworkSection(ctx) { ], }); - // Script type groups for Output Counts (newest to oldest) + // Script type groups for Output Counts const legacyScripts = /** @type {const} */ ([ - { key: "p2pkh", name: "P2PKH", color: colors[spendableTypeColors.p2pkh] }, - { - key: "p2pk33", - name: "P2PK33", - color: colors[spendableTypeColors.p2pk33], - }, - { - key: "p2pk65", - name: "P2PK65", - color: colors[spendableTypeColors.p2pk65], - }, + { key: "p2pkh", name: "P2PKH", color: st.p2pkh }, + { key: "p2pk33", name: "P2PK33", color: st.p2pk33 }, + { key: "p2pk65", name: "P2PK65", color: st.p2pk65 }, ]); const scriptHashScripts = /** @type {const} */ ([ - { key: "p2sh", name: "P2SH", color: colors[spendableTypeColors.p2sh] }, - { key: "p2ms", name: "P2MS", color: colors[spendableTypeColors.p2ms] }, + { key: "p2sh", name: "P2SH", color: st.p2sh }, + { key: "p2ms", name: "P2MS", color: st.p2ms }, ]); const segwitScripts = /** @type {const} */ ([ { key: "segwit", name: "All SegWit", color: colors.cyan }, - { key: "p2wsh", name: "P2WSH", color: colors[spendableTypeColors.p2wsh] }, - { - key: "p2wpkh", - name: "P2WPKH", - color: colors[spendableTypeColors.p2wpkh], - }, + { key: "p2wsh", name: "P2WSH", color: st.p2wsh }, + { key: "p2wpkh", name: "P2WPKH", color: st.p2wpkh }, ]); const otherScripts = /** @type {const} */ ([ - { - key: "opreturn", - name: "OP_RETURN", - color: colors[spendableTypeColors.opreturn], - }, - { - key: "emptyoutput", - name: "Empty", - color: colors[spendableTypeColors.empty], - }, - { - key: "unknownoutput", - name: "Unknown", - color: colors[spendableTypeColors.unknown], - }, + { key: "opreturn", name: "OP_RETURN", color: st.opreturn }, + { key: "emptyoutput", name: "Empty", color: st.empty }, + { key: "unknownoutput", name: "Unknown", color: st.unknown }, ]); /** @@ -793,7 +669,7 @@ export function createNetworkSection(ctx) { pattern: blocks.interval, unit: Unit.secs, }), - priceLine({ ctx, unit: Unit.secs, name: "Target", number: 600 }), + priceLine({ unit: Unit.secs, name: "Target", number: 600 }), ], }, { diff --git a/website/scripts/options/partial.js b/website/scripts/options/partial.js index cb45ed5e2..f90d4742e 100644 --- a/website/scripts/options/partial.js +++ b/website/scripts/options/partial.js @@ -1,6 +1,5 @@ /** Partial options - Main entry point */ -import { createContext } from "./context.js"; import { buildCohortData, createCohortFolderAll, @@ -20,22 +19,15 @@ import { createNetworkSection } from "./network.js"; import { createMiningSection } from "./mining.js"; import { createCointimeSection } from "./cointime.js"; import { createInvestingSection } from "./investing.js"; -import { colors } from "../chart/colors.js"; // Re-export types for external consumers export * from "./types.js"; -export * from "./context.js"; /** * Create partial options tree - * @param {Object} args - * @param {BrkClient} args.brk * @returns {PartialOptionsTree} */ -export function createPartialOptions({ brk }) { - // Create context with all helpers - const ctx = createContext({ brk }); - +export function createPartialOptions() { // Build cohort data const { cohortAll, @@ -54,29 +46,7 @@ export function createPartialOptions({ brk }) { typeAddressable, typeOther, year, - } = buildCohortData(colors, brk); - - // Helpers to map cohorts by capability type - /** @param {CohortWithAdjusted} cohort */ - const mapWithAdjusted = (cohort) => - createCohortFolderWithAdjusted(ctx, cohort); - /** @param {CohortAgeRange} cohort */ - const mapAgeRange = (cohort) => createCohortFolderAgeRange(ctx, cohort); - /** @param {CohortBasicWithMarketCap} cohort */ - const mapBasicWithMarketCap = (cohort) => - createCohortFolderBasicWithMarketCap(ctx, cohort); - /** @param {CohortMinAge} cohort */ - const mapMinAge = (cohort) => createCohortFolderMinAge(ctx, cohort); - /** @param {CohortBasicWithoutMarketCap} cohort */ - const mapBasicWithoutMarketCap = (cohort) => - createCohortFolderBasicWithoutMarketCap(ctx, cohort); - /** @param {CohortWithoutRelative} cohort */ - const mapWithoutRelative = (cohort) => - createCohortFolderWithoutRelative(ctx, cohort); - /** @param {CohortAddress} cohort */ - const mapAddress = (cohort) => createCohortFolderAddress(ctx, cohort); - /** @param {AddressCohortObject} cohort */ - const mapAddressCohorts = (cohort) => createAddressCohortFolder(ctx, cohort); + } = buildCohortData(); return [ // Debug explorer (disabled) @@ -95,29 +65,29 @@ export function createPartialOptions({ brk }) { name: "Charts", tree: [ // Market section - createMarketSection(ctx), + createMarketSection(), // Network section (on-chain activity) - createNetworkSection(ctx), + createNetworkSection(), // Mining section (security & economics) - createMiningSection(ctx), + createMiningSection(), // Cohorts section { name: "Distribution", tree: [ // Overview - All UTXOs (adjustedSopr + percentiles but no RelToMarketCap) - createCohortFolderAll(ctx, { ...cohortAll, name: "Overview" }), + createCohortFolderAll({ ...cohortAll, name: "Overview" }), // STH - Short term holder cohort (Full capability) - createCohortFolderFull(ctx, termShort), + createCohortFolderFull(termShort), // LTH - Long term holder cohort (nupl) - createCohortFolderWithNupl(ctx, termLong), + createCohortFolderWithNupl(termLong), // STH vs LTH - Direct comparison - createCohortFolderWithNupl(ctx, { + createCohortFolderWithNupl({ name: "STH vs LTH", title: "STH vs LTH", list: [termShort, termLong], @@ -131,36 +101,36 @@ export function createPartialOptions({ brk }) { { name: "Younger Than", tree: [ - createCohortFolderWithAdjusted(ctx, { + createCohortFolderWithAdjusted({ name: "Compare", title: "Max Age", list: upToDate, }), - ...upToDate.map(mapWithAdjusted), + ...upToDate.map(createCohortFolderWithAdjusted), ], }, // Older Than (≥ X old) { name: "Older Than", tree: [ - createCohortFolderMinAge(ctx, { + createCohortFolderMinAge({ name: "Compare", title: "Min Age", list: fromDate, }), - ...fromDate.map(mapMinAge), + ...fromDate.map(createCohortFolderMinAge), ], }, // Range { name: "Range", tree: [ - createCohortFolderAgeRange(ctx, { + createCohortFolderAgeRange({ name: "Compare", title: "Age Ranges", list: dateRange, }), - ...dateRange.map(mapAgeRange), + ...dateRange.map(createCohortFolderAgeRange), ], }, ], @@ -174,36 +144,42 @@ export function createPartialOptions({ brk }) { { name: "Less Than", tree: [ - createCohortFolderBasicWithMarketCap(ctx, { + createCohortFolderBasicWithMarketCap({ name: "Compare", title: "Max Size", list: utxosUnderAmount, }), - ...utxosUnderAmount.map(mapBasicWithMarketCap), + ...utxosUnderAmount.map( + createCohortFolderBasicWithMarketCap, + ), ], }, // More Than (≥ X sats) { name: "More Than", tree: [ - createCohortFolderBasicWithMarketCap(ctx, { + createCohortFolderBasicWithMarketCap({ name: "Compare", title: "Min Size", list: utxosAboveAmount, }), - ...utxosAboveAmount.map(mapBasicWithMarketCap), + ...utxosAboveAmount.map( + createCohortFolderBasicWithMarketCap, + ), ], }, // Range { name: "Range", tree: [ - createCohortFolderBasicWithoutMarketCap(ctx, { + createCohortFolderBasicWithoutMarketCap({ name: "Compare", title: "Size Ranges", list: utxosAmountRanges, }), - ...utxosAmountRanges.map(mapBasicWithoutMarketCap), + ...utxosAmountRanges.map( + createCohortFolderBasicWithoutMarketCap, + ), ], }, ], @@ -217,36 +193,36 @@ export function createPartialOptions({ brk }) { { name: "Less Than", tree: [ - createAddressCohortFolder(ctx, { + createAddressCohortFolder({ name: "Compare", title: "Max Balance", list: addressesUnderAmount, }), - ...addressesUnderAmount.map(mapAddressCohorts), + ...addressesUnderAmount.map(createAddressCohortFolder), ], }, // More Than (≥ X sats) { name: "More Than", tree: [ - createAddressCohortFolder(ctx, { + createAddressCohortFolder({ name: "Compare", title: "Min Balance", list: addressesAboveAmount, }), - ...addressesAboveAmount.map(mapAddressCohorts), + ...addressesAboveAmount.map(createAddressCohortFolder), ], }, // Range { name: "Range", tree: [ - createAddressCohortFolder(ctx, { + createAddressCohortFolder({ name: "Compare", title: "Balance Ranges", list: addressesAmountRanges, }), - ...addressesAmountRanges.map(mapAddressCohorts), + ...addressesAmountRanges.map(createAddressCohortFolder), ], }, ], @@ -256,13 +232,13 @@ export function createPartialOptions({ brk }) { { name: "Script Types", tree: [ - createCohortFolderAddress(ctx, { + createCohortFolderAddress({ name: "Compare", title: "Script Types", list: typeAddressable, }), - ...typeAddressable.map(mapAddress), - ...typeOther.map(mapWithoutRelative), + ...typeAddressable.map(createCohortFolderAddress), + ...typeOther.map(createCohortFolderWithoutRelative), ], }, @@ -270,12 +246,12 @@ export function createPartialOptions({ brk }) { { name: "Epochs", tree: [ - createCohortFolderBasicWithoutMarketCap(ctx, { + createCohortFolderBasicWithoutMarketCap({ name: "Compare", title: "Epochs", list: epoch, }), - ...epoch.map(mapBasicWithoutMarketCap), + ...epoch.map(createCohortFolderBasicWithoutMarketCap), ], }, @@ -283,12 +259,12 @@ export function createPartialOptions({ brk }) { { name: "Years", tree: [ - createCohortFolderBasicWithoutMarketCap(ctx, { + createCohortFolderBasicWithoutMarketCap({ name: "Compare", title: "Years", list: year, }), - ...year.map(mapBasicWithoutMarketCap), + ...year.map(createCohortFolderBasicWithoutMarketCap), ], }, ], @@ -297,11 +273,11 @@ export function createPartialOptions({ brk }) { // Frameworks section { name: "Frameworks", - tree: [createCointimeSection(ctx)], + tree: [createCointimeSection()], }, // Investing section - createInvestingSection(ctx), + createInvestingSection(), ], }, diff --git a/website/scripts/options/series.js b/website/scripts/options/series.js index c7ecc7eb9..17624454d 100644 --- a/website/scripts/options/series.js +++ b/website/scripts/options/series.js @@ -1,6 +1,6 @@ /** Series helpers for creating chart series blueprints */ -import { colors } from "../chart/colors.js"; +import { colors } from "../utils/colors.js"; import { Unit } from "../utils/units.js"; // ============================================================================ @@ -47,13 +47,12 @@ export function price({ /** * Create percentile series (max/min/median/pct75/pct25/pct90/pct10) from any stats pattern - * @param {Colors} colors * @param {StatsPattern | BaseStatsPattern | FullStatsPattern | AnyStatsPattern} pattern * @param {Unit} unit * @param {string} title * @returns {AnyFetchedSeriesBlueprint[]} */ -function percentileSeries(colors, pattern, unit, title) { +function percentileSeries(pattern, unit, title) { const { stat } = colors; return [ dots({ @@ -212,7 +211,7 @@ export function candlestick({ metric, name, key, - colors, + defaultActive, unit, options, @@ -222,7 +221,7 @@ export function candlestick({ metric, title: name, key, - colors, + unit, defaultActive, options, @@ -306,7 +305,6 @@ export function histogram({ /** * Create series from a BaseStatsPattern (base + avg + percentiles, NO sum) - * @param {Colors} colors * @param {Object} args * @param {BaseStatsPattern} args.pattern * @param {Unit} args.unit @@ -315,10 +313,13 @@ export function histogram({ * @param {boolean} [args.avgActive] * @returns {AnyFetchedSeriesBlueprint[]} */ -export function fromBaseStatsPattern( - colors, - { pattern, unit, title = "", baseColor, avgActive = true }, -) { +export function fromBaseStatsPattern({ + pattern, + unit, + title = "", + baseColor, + avgActive = true, +}) { const { stat } = colors; return [ dots({ @@ -334,20 +335,19 @@ export function fromBaseStatsPattern( unit, defaultActive: avgActive, }), - ...percentileSeries(colors, pattern, unit, title), + ...percentileSeries(pattern, unit, title), ]; } /** * Create series from any pattern with avg + percentiles (works with StatsPattern, SumStatsPattern, etc.) - * @param {Colors} colors * @param {Object} args * @param {StatsPattern | BaseStatsPattern | FullStatsPattern | AnyStatsPattern} args.pattern * @param {Unit} args.unit * @param {string} [args.title] * @returns {AnyFetchedSeriesBlueprint[]} */ -export function fromStatsPattern(colors, { pattern, unit, title = "" }) { +export function fromStatsPattern({ pattern, unit, title = "" }) { return [ { type: "Dots", @@ -355,7 +355,7 @@ export function fromStatsPattern(colors, { pattern, unit, title = "" }) { title: `${title} avg`.trim(), unit, }, - ...percentileSeries(colors, pattern, unit, title), + ...percentileSeries(pattern, unit, title), ]; } @@ -365,9 +365,9 @@ export function fromStatsPattern(colors, { pattern, unit, title = "" }) { * @returns {AnyFetchedSeriesBlueprint[]} */ export const distributionBtcSatsUsd = (source) => [ - ...fromStatsPattern(colors, { pattern: source.bitcoin, unit: Unit.btc }), - ...fromStatsPattern(colors, { pattern: source.sats, unit: Unit.sats }), - ...fromStatsPattern(colors, { pattern: source.dollars, unit: Unit.usd }), + ...fromStatsPattern({ pattern: source.bitcoin, unit: Unit.btc }), + ...fromStatsPattern({ pattern: source.sats, unit: Unit.sats }), + ...fromStatsPattern({ pattern: source.dollars, unit: Unit.usd }), ]; /** @@ -408,12 +408,11 @@ export function fromSupplyPattern({ pattern, title, color }) { /** * Create distribution series (avg + percentiles) - * @param {Colors} colors * @param {StatsPattern | BaseStatsPattern | FullStatsPattern | AnyStatsPattern} pattern * @param {Unit} unit * @returns {AnyFetchedSeriesBlueprint[]} */ -function distributionSeries(colors, pattern, unit) { +function distributionSeries(pattern, unit) { const { stat } = colors; return [ dots({ metric: pattern.average, name: "avg", color: stat.avg, unit }), @@ -506,14 +505,13 @@ function btcSatsUsdSeries({ metrics, name, color, defaultActive }) { /** * Split pattern with base + sum + distribution + cumulative into 3 charts - * @param {Colors} colors * @param {Object} args * @param {FullStatsPattern} args.pattern * @param {string} args.title * @param {Unit} args.unit * @returns {PartialOptionsTree} */ -export function chartsFromFull(colors, { pattern, title, unit }) { +export function chartsFromFull({ pattern, title, unit }) { return [ { name: "Sum", @@ -526,7 +524,7 @@ export function chartsFromFull(colors, { pattern, title, unit }) { { name: "Distribution", title: `${title} Distribution`, - bottom: distributionSeries(colors, pattern, unit), + bottom: distributionSeries(pattern, unit), }, { name: "Cumulative", @@ -538,14 +536,13 @@ export function chartsFromFull(colors, { pattern, title, unit }) { /** * Split pattern with sum + distribution + cumulative into 3 charts (no base) - * @param {Colors} colors * @param {Object} args * @param {AnyStatsPattern} args.pattern * @param {string} args.title * @param {Unit} args.unit * @returns {PartialOptionsTree} */ -export function chartsFromSum(colors, { pattern, title, unit }) { +export function chartsFromSum({ pattern, title, unit }) { const { stat } = colors; return [ { @@ -556,7 +553,7 @@ export function chartsFromSum(colors, { pattern, title, unit }) { { name: "Distribution", title: `${title} Distribution`, - bottom: distributionSeries(colors, pattern, unit), + bottom: distributionSeries(pattern, unit), }, { name: "Cumulative", @@ -631,13 +628,12 @@ export function chartsFromValue({ pattern, title, color }) { /** * Split btc/sats/usd pattern with full stats into 3 charts - * @param {Colors} colors * @param {Object} args * @param {CoinbasePattern} args.pattern * @param {string} args.title * @returns {PartialOptionsTree} */ -export function chartsFromValueFull(colors, { pattern, title }) { +export function chartsFromValueFull({ pattern, title }) { return [ { name: "Sum", @@ -665,9 +661,9 @@ export function chartsFromValueFull(colors, { pattern, title }) { name: "Distribution", title: `${title} Distribution`, bottom: [ - ...distributionSeries(colors, pattern.bitcoin, Unit.btc), - ...distributionSeries(colors, pattern.sats, Unit.sats), - ...distributionSeries(colors, pattern.dollars, Unit.usd), + ...distributionSeries(pattern.bitcoin, Unit.btc), + ...distributionSeries(pattern.sats, Unit.sats), + ...distributionSeries(pattern.dollars, Unit.usd), ], }, { diff --git a/website/scripts/options/shared.js b/website/scripts/options/shared.js index 9140896fa..dd89943c0 100644 --- a/website/scripts/options/shared.js +++ b/website/scripts/options/shared.js @@ -3,14 +3,15 @@ import { Unit } from "../utils/units.js"; import { line, baseline, price } from "./series.js"; import { priceLine, priceLines } from "./constants.js"; +import { colors } from "../utils/colors.js"; /** * Create a title formatter for chart titles * @param {string} [cohortTitle] * @returns {(metric: string) => string} */ -export const formatCohortTitle = (cohortTitle) => - (metric) => cohortTitle ? `${metric}: ${cohortTitle}` : metric; +export const formatCohortTitle = (cohortTitle) => (metric) => + cohortTitle ? `${metric}: ${cohortTitle}` : metric; /** * Create sats/btc/usd line series from a pattern with .sats/.bitcoin/.dollars @@ -23,9 +24,21 @@ export const formatCohortTitle = (cohortTitle) => */ export function satsBtcUsd({ pattern, name, color, defaultActive }) { return [ - line({ metric: pattern.bitcoin, name, color, unit: Unit.btc, defaultActive }), + line({ + metric: pattern.bitcoin, + name, + color, + unit: Unit.btc, + defaultActive, + }), line({ metric: pattern.sats, name, color, unit: Unit.sats, defaultActive }), - line({ metric: pattern.dollars, name, color, unit: Unit.usd, defaultActive }), + line({ + metric: pattern.dollars, + name, + color, + unit: Unit.usd, + defaultActive, + }), ]; } @@ -41,7 +54,11 @@ export function satsBtcUsd({ pattern, name, color, defaultActive }) { */ export function satsBtcUsdFrom({ source, key, name, color, defaultActive }) { return satsBtcUsd({ - pattern: { bitcoin: source.bitcoin[key], sats: source.sats[key], dollars: source.dollars[key] }, + pattern: { + bitcoin: source.bitcoin[key], + sats: source.sats[key], + dollars: source.dollars[key], + }, name, color, defaultActive, @@ -58,9 +75,19 @@ export function satsBtcUsdFrom({ source, key, name, color, defaultActive }) { * @param {boolean} [args.defaultActive] * @returns {FetchedLineSeriesBlueprint[]} */ -export function satsBtcUsdFromFull({ source, key, name, color, defaultActive }) { +export function satsBtcUsdFromFull({ + source, + key, + name, + color, + defaultActive, +}) { return satsBtcUsd({ - pattern: { bitcoin: source.bitcoin[key], sats: source.sats[key], dollars: source.dollars[key] }, + pattern: { + bitcoin: source.bitcoin[key], + sats: source.sats[key], + dollars: source.dollars[key], + }, name, color, defaultActive, @@ -69,7 +96,6 @@ export function satsBtcUsdFromFull({ source, key, name, color, defaultActive }) /** * Create coinbase/subsidy/fee series from separate sources - * @param {Colors} colors * @param {Object} args * @param {AnyValuePatternType} args.coinbase * @param {AnyValuePatternType} args.subsidy @@ -77,20 +103,29 @@ export function satsBtcUsdFromFull({ source, key, name, color, defaultActive }) * @param {'sum' | 'cumulative'} args.key * @returns {FetchedLineSeriesBlueprint[]} */ -export function revenueBtcSatsUsd(colors, { coinbase, subsidy, fee, key }) { +export function revenueBtcSatsUsd({ coinbase, subsidy, fee, key }) { return [ - ...satsBtcUsdFrom({ source: coinbase, key, name: "Coinbase", color: colors.orange }), - ...satsBtcUsdFrom({ source: subsidy, key, name: "Subsidy", color: colors.lime }), + ...satsBtcUsdFrom({ + source: coinbase, + key, + name: "Coinbase", + color: colors.orange, + }), + ...satsBtcUsdFrom({ + source: subsidy, + key, + name: "Subsidy", + color: colors.lime, + }), ...satsBtcUsdFrom({ source: fee, key, name: "Fees", color: colors.cyan }), ]; } /** * Build percentile USD mappings from a ratio pattern - * @param {Colors} colors * @param {AnyRatioPattern} ratio */ -export function percentileUsdMap(colors, ratio) { +export function percentileUsdMap(ratio) { return /** @type {const} */ ([ { name: "pct95", prop: ratio.ratioPct95Usd, color: colors.fuchsia }, { name: "pct5", prop: ratio.ratioPct5Usd, color: colors.cyan }, @@ -103,10 +138,9 @@ export function percentileUsdMap(colors, ratio) { /** * Build percentile ratio mappings from a ratio pattern - * @param {Colors} colors * @param {AnyRatioPattern} ratio */ -export function percentileMap(colors, ratio) { +export function percentileMap(ratio) { return /** @type {const} */ ([ { name: "pct95", prop: ratio.ratioPct95, color: colors.fuchsia }, { name: "pct5", prop: ratio.ratioPct5, color: colors.cyan }, @@ -132,10 +166,9 @@ export function sdPatterns(ratio) { /** * Build SD band mappings from an SD pattern - * @param {Colors} colors * @param {Ratio1ySdPattern} sd */ -export function sdBandsUsd(colors, sd) { +export function sdBandsUsd(sd) { return /** @type {const} */ ([ { name: "0σ", prop: sd._0sdUsd, color: colors.lime }, { name: "+0.5σ", prop: sd.p05sdUsd, color: colors.yellow }, @@ -155,10 +188,9 @@ export function sdBandsUsd(colors, sd) { /** * Build SD band mappings (ratio) from an SD pattern - * @param {Colors} colors * @param {Ratio1ySdPattern} sd */ -export function sdBandsRatio(colors, sd) { +export function sdBandsRatio(sd) { return /** @type {const} */ ([ { name: "0σ", prop: sd.sma, color: colors.lime }, { name: "+0.5σ", prop: sd.p05sd, color: colors.yellow }, @@ -178,10 +210,9 @@ export function sdBandsRatio(colors, sd) { /** * Build ratio SMA series from a ratio pattern - * @param {Colors} colors * @param {AnyRatioPattern} ratio */ -export function ratioSmas(colors, ratio) { +export function ratioSmas(ratio) { return /** @type {const} */ ([ { name: "1w SMA", metric: ratio.ratio1wSma, color: colors.lime }, { name: "1m SMA", metric: ratio.ratio1mSma, color: colors.teal }, @@ -194,7 +225,6 @@ export function ratioSmas(colors, ratio) { /** * Create ratio chart from ActivePriceRatioPattern - * @param {PartialContext} ctx * @param {Object} args * @param {(metric: string) => string} args.title * @param {AnyPricePattern} args.pricePattern - The price pattern to show in top pane @@ -203,15 +233,13 @@ export function ratioSmas(colors, ratio) { * @param {string} [args.name] - Optional name override (default: "ratio") * @returns {PartialChartOption} */ -export function createRatioChart(ctx, { title, pricePattern, ratio, color, name }) { - const { colors } = ctx; - +export function createRatioChart({ title, pricePattern, ratio, color, name }) { return { name: name ?? "ratio", title: title(name ?? "Ratio"), top: [ price({ metric: pricePattern, name: "Price", color }), - ...percentileUsdMap(colors, ratio).map(({ name, prop, color }) => + ...percentileUsdMap(ratio).map(({ name, prop, color }) => price({ metric: prop, name, @@ -228,10 +256,10 @@ export function createRatioChart(ctx, { title, pricePattern, ratio, color, name unit: Unit.ratio, base: 1, }), - ...ratioSmas(colors, ratio).map(({ name, metric, color }) => + ...ratioSmas(ratio).map(({ name, metric, color }) => line({ metric, name, color, unit: Unit.ratio, defaultActive: false }), ), - ...percentileMap(colors, ratio).map(({ name, prop, color }) => + ...percentileMap(ratio).map(({ name, prop, color }) => line({ metric: prop, name, @@ -247,7 +275,6 @@ export function createRatioChart(ctx, { title, pricePattern, ratio, color, name /** * Create ZScores folder from ActivePriceRatioPattern - * @param {PartialContext} ctx * @param {Object} args * @param {(suffix: string) => string} args.formatTitle - Function that takes metric suffix and returns full title * @param {string} args.legend @@ -256,11 +283,13 @@ export function createRatioChart(ctx, { title, pricePattern, ratio, color, name * @param {Color} args.color * @returns {PartialOptionsGroup} */ -export function createZScoresFolder( - ctx, - { formatTitle, legend, pricePattern, ratio, color }, -) { - const { colors } = ctx; +export function createZScoresFolder({ + formatTitle, + legend, + pricePattern, + ratio, + color, +}) { const sdPats = sdPatterns(ratio); return { @@ -322,7 +351,6 @@ export function createZScoresFolder( unit: Unit.sd, }), ...priceLines({ - ctx, unit: Unit.sd, numbers: [0, 1, -1, 2, -2, 3, -3], defaultActive: false, @@ -334,14 +362,13 @@ export function createZScoresFolder( title: formatTitle(`${titleAddon ? `${titleAddon} ` : ""}Z-Score`), top: [ price({ metric: pricePattern, name: legend, color }), - ...sdBandsUsd(colors, sd).map( - ({ name: bandName, prop, color: bandColor }) => - price({ - metric: prop, - name: bandName, - color: bandColor, - defaultActive: false, - }), + ...sdBandsUsd(sd).map(({ name: bandName, prop, color: bandColor }) => + price({ + metric: prop, + name: bandName, + color: bandColor, + defaultActive: false, + }), ), ], bottom: [ @@ -362,7 +389,7 @@ export function createZScoresFolder( color: colors.gray, unit: Unit.percentage, }), - ...sdBandsRatio(colors, sd).map( + ...sdBandsRatio(sd).map( ({ name: bandName, prop, color: bandColor }) => line({ metric: prop, @@ -373,11 +400,9 @@ export function createZScoresFolder( }), ), priceLine({ - ctx, unit: Unit.sd, }), ...priceLines({ - ctx, unit: Unit.sd, numbers: [1, -1, 2, -2, 3, -3], defaultActive: false, @@ -391,7 +416,6 @@ export function createZScoresFolder( /** * Create price + ratio + z-scores charts - flat array * Unified helper for averages, distribution, and other price-based metrics - * @param {PartialContext} ctx * @param {Object} args * @param {string} args.context - Context string for ratio/z-scores titles (e.g., "1 Week SMA", "STH") * @param {string} args.legend - Legend name for the price series @@ -404,23 +428,38 @@ export function createZScoresFolder( * @param {FetchedPriceSeriesBlueprint[]} [args.priceReferences] - Optional additional price series to show in Price chart * @returns {PartialOptionsTree} */ -export function createPriceRatioCharts(ctx, { context, legend, pricePattern, ratio, color, ratioName, priceTitle, zScoresSuffix, priceReferences }) { +export function createPriceRatioCharts({ + context, + legend, + pricePattern, + ratio, + color, + ratioName, + priceTitle, + zScoresSuffix, + priceReferences, +}) { const titleFn = formatCohortTitle(context); - const zScoresTitleFn = zScoresSuffix ? formatCohortTitle(`${context} ${zScoresSuffix}`) : titleFn; + const zScoresTitleFn = zScoresSuffix + ? formatCohortTitle(`${context} ${zScoresSuffix}`) + : titleFn; return [ { name: "Price", title: priceTitle ?? context, - top: [price({ metric: pricePattern, name: legend, color }), ...(priceReferences ?? [])], + top: [ + price({ metric: pricePattern, name: legend, color }), + ...(priceReferences ?? []), + ], }, - createRatioChart(ctx, { + createRatioChart({ title: titleFn, pricePattern, ratio, color, name: ratioName, }), - createZScoresFolder(ctx, { + createZScoresFolder({ formatTitle: zScoresTitleFn, legend, pricePattern, diff --git a/website/scripts/panes/chart.js b/website/scripts/panes/chart.js index ae271f13d..dfea11114 100644 --- a/website/scripts/panes/chart.js +++ b/website/scripts/panes/chart.js @@ -3,8 +3,9 @@ import { chartElement } from "../utils/elements.js"; import { serdeChartableIndex } from "../utils/serde.js"; import { Unit } from "../utils/units.js"; import { createChart } from "../chart/index.js"; -import { colors } from "../chart/colors.js"; +import { colors } from "../utils/colors.js"; import { webSockets } from "../utils/ws.js"; +import { brk } from "../client.js"; const ONE_BTC_IN_SATS = 100_000_000; @@ -19,10 +20,7 @@ export function setOption(opt) { _setOption(opt); } -/** - * @param {BrkClient} brk - */ -export function init(brk) { +export function init() { chartElement.append(createShadow("left")); chartElement.append(createShadow("right")); diff --git a/website/scripts/types.js b/website/scripts/types.js index c277c3f13..ffe6c9024 100644 --- a/website/scripts/types.js +++ b/website/scripts/types.js @@ -10,11 +10,11 @@ * * @import { SingleValueData, CandlestickData, Series, AnySeries, ISeries, HistogramData, LineData, BaselineData, LineSeriesPartialOptions, BaselineSeriesPartialOptions, HistogramSeriesPartialOptions, CandlestickSeriesPartialOptions, Chart, Legend } from "./chart/index.js" * - * @import { Color, ColorName, Colors } from "./chart/colors.js" + * @import { Color, ColorName, Colors } from "./utils/colors.js" * * @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, PatternWithoutRelative, CohortAll, CohortFull, CohortWithAdjusted, CohortWithPercentiles, CohortBasic, CohortBasicWithMarketCap, CohortBasicWithoutMarketCap, CohortWithoutRelative, CohortAddress, CohortLongTerm, CohortAgeRange, CohortMinAge, CohortGroupFull, CohortGroupWithAdjusted, CohortGroupWithPercentiles, CohortGroupLongTerm, CohortGroupAgeRange, CohortGroupBasic, CohortGroupBasicWithMarketCap, CohortGroupBasicWithoutMarketCap, CohortGroupWithoutRelative, CohortGroupMinAge, CohortGroupAddress, UtxoCohortGroupObject, AddressCohortGroupObject, FetchedDotsSeriesBlueprint, FetchedCandlestickSeriesBlueprint, FetchedPriceSeriesBlueprint, AnyPricePattern, AnyValuePattern } 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, PatternAll, PatternFull, PatternWithAdjusted, PatternWithPercentiles, PatternBasic, PatternBasicWithMarketCap, PatternBasicWithoutMarketCap, PatternWithoutRelative, CohortAll, CohortFull, CohortWithAdjusted, CohortWithPercentiles, CohortBasic, CohortBasicWithMarketCap, CohortBasicWithoutMarketCap, CohortWithoutRelative, CohortAddress, CohortLongTerm, CohortAgeRange, CohortMinAge, CohortGroupFull, CohortGroupWithAdjusted, CohortGroupWithPercentiles, CohortGroupLongTerm, CohortGroupAgeRange, CohortGroupBasic, CohortGroupBasicWithMarketCap, CohortGroupBasicWithoutMarketCap, CohortGroupWithoutRelative, CohortGroupMinAge, CohortGroupAddress, UtxoCohortGroupObject, AddressCohortGroupObject, FetchedDotsSeriesBlueprint, FetchedCandlestickSeriesBlueprint, FetchedPriceSeriesBlueprint, AnyPricePattern, AnyValuePattern } from "./options/partial.js" * * * @import { UnitObject as Unit } from "./utils/units.js" diff --git a/website/scripts/chart/colors.js b/website/scripts/utils/colors.js similarity index 65% rename from website/scripts/chart/colors.js rename to website/scripts/utils/colors.js index fca4ea708..31a3f3ee3 100644 --- a/website/scripts/chart/colors.js +++ b/website/scripts/utils/colors.js @@ -1,5 +1,5 @@ -import { oklchToRgba } from "./oklch.js"; -import { dark } from "../utils/theme.js"; +import { oklchToRgba } from "../chart/oklch.js"; +import { dark } from "./theme.js"; /** @type {Map} */ const rgbaCache = new Map(); @@ -117,7 +117,6 @@ const baseColors = { export const colors = { ...baseColors, - /** Semantic stat colors for pattern helpers */ stat: { sum: blue, cumulative: indigo, @@ -131,7 +130,6 @@ export const colors = { min: red, }, - /** Common time period colors */ time: { _24h: pink, _1w: red, @@ -140,45 +138,121 @@ export const colors = { all: teal, }, - /** DCA period colors by term */ - dcaPeriods: { - // Short term - _1w: red, - _1m: orange, - _3m: yellow, - _6m: lime, - // Medium term - _1y: green, - _2y: teal, - _3y: cyan, - // Long term - _4y: sky, - _5y: blue, - _6y: indigo, - _8y: violet, - _10y: purple, + term: { + short: yellow, + long: fuchsia, }, - /** DCA year colors by halving epoch */ - dcaYears: { - // Epoch 5 (2024+) - _2026: rose, - _2025: fuchsia, - _2024: purple, - // Epoch 4 (2020-2023) - _2023: violet, - _2022: blue, - _2021: sky, - _2020: teal, - // Epoch 3 (2016-2019) - _2019: green, - _2018: yellow, - _2017: orange, - _2016: red, - _2015: pink, + age: { + _1d: red, + _1w: orange, + _1m: yellow, + _2m: lime, + _3m: green, + _4m: teal, + _5m: cyan, + _6m: blue, + _1y: indigo, + _2y: violet, + _3y: purple, + _4y: fuchsia, + _5y: pink, + _6y: rose, + _7y: red, + _8y: orange, + _10y: yellow, + _12y: lime, + _15y: green, + }, + + ageRange: { + upTo1h: rose, + _1hTo1d: pink, + _1dTo1w: red, + _1wTo1m: orange, + _1mTo2m: yellow, + _2mTo3m: yellow, + _3mTo4m: lime, + _4mTo5m: lime, + _5mTo6m: lime, + _6mTo1y: green, + _1yTo2y: cyan, + _2yTo3y: blue, + _3yTo4y: indigo, + _4yTo5y: violet, + _5yTo6y: purple, + _6yTo7y: purple, + _7yTo8y: fuchsia, + _8yTo10y: fuchsia, + _10yTo12y: pink, + _12yTo15y: red, + from15y: orange, + }, + + amount: { + _1sat: orange, + _10sats: orange, + _100sats: yellow, + _1kSats: lime, + _10kSats: green, + _100kSats: cyan, + _1mSats: blue, + _10mSats: indigo, + _1btc: purple, + _10btc: violet, + _100btc: fuchsia, + _1kBtc: pink, + _10kBtc: red, + _100kBtc: orange, + }, + + amountRange: { + _0sats: red, + _1satTo10sats: orange, + _10satsTo100sats: yellow, + _100satsTo1kSats: lime, + _1kSatsTo10kSats: green, + _10kSatsTo100kSats: cyan, + _100kSatsTo1mSats: blue, + _1mSatsTo10mSats: indigo, + _10mSatsTo1btc: purple, + _1btcTo10btc: violet, + _10btcTo100btc: fuchsia, + _100btcTo1kBtc: pink, + _1kBtcTo10kBtc: red, + _10kBtcTo100kBtc: orange, + _100kBtcOrMore: yellow, + }, + + epoch: { + _0: red, + _1: yellow, + _2: orange, + _3: lime, + _4: green, + }, + + year: { + _2009: red, + _2010: orange, + _2011: amber, + _2012: yellow, + _2013: lime, + _2014: green, + _2015: teal, + _2016: cyan, + _2017: sky, + _2018: blue, + _2019: indigo, + _2020: violet, + _2021: purple, + _2022: fuchsia, + _2023: pink, + _2024: rose, + _2025: red, + _2026: orange, }, - /** Returns/lookback colors by period */ returns: { _1d: red, _1w: orange, @@ -195,29 +269,42 @@ export const colors = { _10y: fuchsia, }, - /** Moving average colors by period */ ma: { _1w: red, _8d: orange, _12d: amber, - _13d: amber, - _21d: yellow, + _13d: yellow, + _21d: avocado, _26d: lime, - _1m: lime, - _34d: green, - _55d: emerald, - _89d: teal, - _111d: cyan, - _144d: sky, - _200d: blue, - _350d: indigo, - _1y: violet, - _2y: purple, - _200w: fuchsia, - _4y: pink, + _1m: green, + _34d: emerald, + _55d: teal, + _89d: cyan, + _111d: sky, + _144d: blue, + _200d: indigo, + _350d: violet, + _1y: purple, + _2y: fuchsia, + _200w: pink, + _4y: rose, + }, + + dca: { + _1w: red, + _1m: orange, + _3m: yellow, + _6m: lime, + _1y: green, + _2y: teal, + _3y: cyan, + _4y: sky, + _5y: blue, + _6y: indigo, + _8y: violet, + _10y: purple, }, - /** Script type colors (oldest to newest) */ scriptType: { p2pk65: red, p2pk33: orange,