website: snapshot

This commit is contained in:
nym21
2026-01-23 22:03:01 +01:00
parent f7bfe5ecaa
commit 9b706dfaee
49 changed files with 631818 additions and 1298 deletions

View File

@@ -1,6 +1,7 @@
/** Chain section builder - typed tree-based patterns */
import { Unit } from "../utils/units.js";
import { priceLine } from "./constants.js";
import { line, baseline, dots } from "./series.js";
import { satsBtcUsd } from "./shared.js";
@@ -13,9 +14,9 @@ export function createChainSection(ctx) {
const {
colors,
brk,
createPriceLine,
fromSizePattern,
fromFullnessPattern,
fromDollarsPattern,
fromFeeRatePattern,
fromCoinbasePattern,
fromValuePattern,
@@ -46,7 +47,7 @@ export function createChainSection(ctx) {
tree: [
{
name: "Dominance",
title: `Mining Dominance of ${poolName}`,
title: `${poolName} Dominance`,
bottom: [
line({
metric: pool._24hDominance,
@@ -85,7 +86,7 @@ export function createChainSection(ctx) {
},
{
name: "Blocks mined",
title: `Blocks mined by ${poolName}`,
title: `${poolName} Blocks`,
bottom: [
line({
metric: pool.blocksMined.sum,
@@ -123,7 +124,7 @@ export function createChainSection(ctx) {
},
{
name: "Rewards",
title: `Rewards collected by ${poolName}`,
title: `${poolName} Rewards`,
bottom: [
...fromValuePattern(
pool.coinbase,
@@ -142,7 +143,7 @@ export function createChainSection(ctx) {
},
{
name: "Days since block",
title: `Days since ${poolName} mined a block`,
title: `${poolName} Last Block`,
bottom: [
line({
metric: pool.daysSinceBlock,
@@ -166,11 +167,7 @@ export function createChainSection(ctx) {
name: "Count",
title: "Block Count",
bottom: [
...fromBlockCountWithUnit(
blocks.count.blockCount,
"Block",
Unit.count,
),
...fromBlockCountWithUnit(blocks.count.blockCount, Unit.count),
line({
metric: blocks.count.blockCountTarget,
name: "Target",
@@ -205,17 +202,17 @@ export function createChainSection(ctx) {
name: "Interval",
title: "Block Interval",
bottom: [
...fromIntervalPattern(blocks.interval, "Interval", Unit.secs),
createPriceLine({ unit: Unit.secs, name: "Target", number: 600 }),
...fromIntervalPattern(blocks.interval, Unit.secs),
priceLine({ ctx, unit: Unit.secs, name: "Target", number: 600 }),
],
},
{
name: "Size",
title: "Block Size",
bottom: [
...fromSizePattern(blocks.size, "Size", Unit.bytes),
...fromFullnessPattern(blocks.vbytes, "Vbytes", Unit.vb),
...fromFullnessPattern(blocks.weight, "Weight", Unit.wu),
...fromSizePattern(blocks.size, Unit.bytes),
...fromFullnessPattern(blocks.vbytes, Unit.vb),
...fromFullnessPattern(blocks.weight, Unit.wu),
],
},
],
@@ -228,17 +225,16 @@ export function createChainSection(ctx) {
{
name: "Count",
title: "Transaction Count",
bottom: fromFullnessPattern(
transactions.count.txCount,
"Count",
Unit.count,
),
bottom: fromDollarsPattern(transactions.count.txCount, Unit.count),
},
{
name: "Volume",
title: "Transaction Volume",
bottom: [
...satsBtcUsd( transactions.volume.sentSum, "Sent"),
...satsBtcUsd(transactions.volume.sentSum, "Sent"),
...satsBtcUsd(transactions.volume.receivedSum, "Received", colors.cyan, {
defaultActive: false,
}),
line({
metric: transactions.volume.annualizedVolume.sats,
name: "annualized",
@@ -266,12 +262,8 @@ export function createChainSection(ctx) {
name: "Size",
title: "Transaction Size",
bottom: [
...fromFeeRatePattern(
transactions.size.weight,
"weight",
Unit.wu,
),
...fromFeeRatePattern(transactions.size.vsize, "vsize", Unit.vb),
...fromFeeRatePattern(transactions.size.weight, Unit.wu),
...fromFeeRatePattern(transactions.size.vsize, Unit.vb),
],
},
{
@@ -280,22 +272,22 @@ export function createChainSection(ctx) {
bottom: [
...fromBlockCountWithUnit(
transactions.versions.v1,
"v1",
Unit.count,
"v1",
colors.orange,
colors.red,
),
...fromBlockCountWithUnit(
transactions.versions.v2,
"v2",
Unit.count,
"v2",
colors.cyan,
colors.blue,
),
...fromBlockCountWithUnit(
transactions.versions.v3,
"v3",
Unit.count,
"v3",
colors.lime,
colors.green,
),
@@ -322,7 +314,7 @@ export function createChainSection(ctx) {
name: "Speed",
title: "Transactions Per Second",
bottom: [
line({
dots({
metric: transactions.volume.txPerSec,
name: "Transactions",
unit: Unit.perSec,
@@ -338,14 +330,14 @@ export function createChainSection(ctx) {
tree: [
{
name: "Count",
title: "Transaction Input Count",
bottom: [...fromSizePattern(inputs.count, "Input", Unit.count)],
title: "Input Count",
bottom: [...fromSizePattern(inputs.count, Unit.count)],
},
{
name: "Speed",
title: "Inputs Per Second",
bottom: [
line({
dots({
metric: transactions.volume.inputsPerSec,
name: "Inputs",
unit: Unit.perSec,
@@ -361,20 +353,14 @@ export function createChainSection(ctx) {
tree: [
{
name: "Count",
title: "Transaction Output Count",
bottom: [
...fromSizePattern(
outputs.count.totalCount,
"Output",
Unit.count,
),
],
title: "Output Count",
bottom: [...fromSizePattern(outputs.count.totalCount, Unit.count)],
},
{
name: "Speed",
title: "Outputs Per Second",
bottom: [
line({
dots({
metric: transactions.volume.outputsPerSec,
name: "Outputs",
unit: Unit.perSec,
@@ -713,22 +699,22 @@ export function createChainSection(ctx) {
},
{
name: "Halving",
title: "Halving Info",
title: "Halving",
bottom: [
line({
metric: blocks.halving.blocksBeforeNextHalving,
name: "Blocks until halving",
name: "Blocks before next",
unit: Unit.blocks,
}),
line({
metric: blocks.halving.daysBeforeNextHalving,
name: "Days until halving",
name: "Days before next",
color: colors.orange,
unit: Unit.days,
}),
line({
metric: blocks.halving.epoch,
name: "Halving epoch",
name: "Epoch",
color: colors.purple,
unit: Unit.epoch,
defaultActive: false,
@@ -744,7 +730,7 @@ export function createChainSection(ctx) {
name: "Puell Multiple",
unit: Unit.ratio,
}),
createPriceLine({ unit: Unit.ratio, number: 1 }),
priceLine({ ctx, unit: Unit.ratio, number: 1 }),
],
},
],
@@ -771,11 +757,7 @@ export function createChainSection(ctx) {
{
name: "Outputs",
title: "OP_RETURN Outputs",
bottom: fromFullnessPattern(
scripts.count.opreturn,
"Count",
Unit.count,
),
bottom: fromFullnessPattern(scripts.count.opreturn, Unit.count),
},
{
name: "Supply",
@@ -810,7 +792,7 @@ export function createChainSection(ctx) {
// Unclaimed Rewards
{
name: "Unclaimed Rewards",
title: "Unclaimed Block Rewards",
title: "Unclaimed Rewards",
bottom: fromValuePattern(blocks.rewards.unclaimedRewards, "Unclaimed"),
},
],

View File

@@ -5,6 +5,7 @@
*/
import { Unit } from "../../utils/units.js";
import { priceLine } from "../constants.js";
import { line, baseline } from "../series.js";
import {
createSingleSupplySeries,
@@ -102,7 +103,7 @@ export function createAddressCohortFolder(ctx, args) {
)),
{
name: "capitalization",
title: `Realized Capitalization ${title}`,
title: `Realized Cap ${title}`,
bottom: createRealizedCapWithExtras(ctx, list, args, useGroupName),
},
...(!useGroupName
@@ -134,7 +135,7 @@ export function createAddressCohortFolder(ctx, args) {
* @returns {PartialOptionsTree}
*/
function createRealizedPriceOptions(args, title) {
const { tree, color } = args;
const { tree, color } = args;
return [
{
@@ -161,7 +162,6 @@ function createRealizedPriceOptions(args, title) {
* @returns {AnyFetchedSeriesBlueprint[]}
*/
function createRealizedCapWithExtras(ctx, list, args, useGroupName) {
const { createPriceLine } = ctx;
const isSingle = !("list" in args);
return list.flatMap(({ color, name, tree }) => [
@@ -179,7 +179,7 @@ function createRealizedCapWithExtras(ctx, list, args, useGroupName) {
unit: Unit.usd,
defaultActive: false,
}),
createPriceLine({ unit: Unit.usd, defaultActive: false }),
priceLine({ ctx, unit: Unit.usd, defaultActive: false }),
]
: []),
// RealizedPattern (address cohorts) doesn't have realizedCapRelToOwnMarketCap
@@ -200,7 +200,7 @@ function createRealizedPnlSection(ctx, args, title) {
return [
{
name: "pnl",
title: `Realized Profit And Loss ${title}`,
title: `Realized P&L ${title}`,
bottom: [
line({
metric: realized.realizedProfit.sum,
@@ -257,7 +257,7 @@ function createUnrealizedSection(ctx, list, useGroupName, title) {
tree: [
{
name: "nupl",
title: `Net Unrealized Profit/Loss ${title}`,
title: `Net Unrealized P&L ${title}`,
bottom: list.flatMap(({ color, name, tree }) => [
baseline({
metric: tree.unrealized.netUnrealizedPnl,

View File

@@ -1,6 +1,7 @@
/** Shared cohort chart section builders */
import { Unit } from "../../utils/units.js";
import { priceLine } from "../constants.js";
import { line } from "../series.js";
import { satsBtcUsd } from "../shared.js";
@@ -11,11 +12,11 @@ import { satsBtcUsd } from "../shared.js";
* @returns {AnyFetchedSeriesBlueprint[]}
*/
export function createSingleSupplySeries(ctx, cohort) {
const { colors, createPriceLine } = ctx;
const { colors } = ctx;
const { tree } = cohort;
return [
...satsBtcUsd( tree.supply.total, "Supply", colors.default),
...satsBtcUsd(tree.supply.total, "Supply", colors.default),
...("supplyRelToCirculatingSupply" in tree.relative
? [
line({
@@ -26,9 +27,9 @@ export function createSingleSupplySeries(ctx, cohort) {
}),
]
: []),
...satsBtcUsd( tree.unrealized.supplyInProfit, "In Profit", colors.green),
...satsBtcUsd( tree.unrealized.supplyInLoss, "In Loss", colors.red),
...satsBtcUsd( tree.supply.halved, "half", colors.gray).map((s) => ({
...satsBtcUsd(tree.unrealized.supplyInProfit, "In Profit", colors.green),
...satsBtcUsd(tree.unrealized.supplyInLoss, "In Loss", colors.red),
...satsBtcUsd(tree.supply.halved, "half", colors.gray).map((s) => ({
...s,
options: { lineStyle: 4 },
})),
@@ -60,13 +61,14 @@ export function createSingleSupplySeries(ctx, cohort) {
color: colors.red,
unit: Unit.pctOwn,
}),
createPriceLine({
priceLine({
ctx,
unit: Unit.pctOwn,
number: 100,
lineStyle: 0,
style: 0,
color: colors.default,
}),
createPriceLine({ unit: Unit.pctOwn, number: 50 }),
priceLine({ ctx, unit: Unit.pctOwn, number: 50 }),
];
}
@@ -81,7 +83,7 @@ export function createGroupedSupplyTotalSeries(ctx, list) {
const constant100 = brk.metrics.constants.constant100;
return list.flatMap(({ color, name, tree }) => [
...satsBtcUsd( tree.supply.total, name, color),
...satsBtcUsd(tree.supply.total, name, color),
line({
metric:
"supplyRelToCirculatingSupply" in tree.relative
@@ -100,9 +102,8 @@ export function createGroupedSupplyTotalSeries(ctx, list) {
* @returns {AnyFetchedSeriesBlueprint[]}
*/
export function createGroupedSupplyInProfitSeries(list) {
return list.flatMap(({ color, name, tree }) => [
...satsBtcUsd( tree.unrealized.supplyInProfit, name, color),
...satsBtcUsd(tree.unrealized.supplyInProfit, name, color),
...("supplyInProfitRelToCirculatingSupply" in tree.relative
? [
line({
@@ -122,9 +123,8 @@ export function createGroupedSupplyInProfitSeries(list) {
* @returns {AnyFetchedSeriesBlueprint[]}
*/
export function createGroupedSupplyInLossSeries(list) {
return list.flatMap(({ color, name, tree }) => [
...satsBtcUsd( tree.unrealized.supplyInLoss, name, color),
...satsBtcUsd(tree.unrealized.supplyInLoss, name, color),
...("supplyInLossRelToCirculatingSupply" in tree.relative
? [
line({
@@ -193,8 +193,6 @@ export function createRealizedPriceSeries(list) {
* @returns {AnyFetchedSeriesBlueprint[]}
*/
export function createRealizedPriceRatioSeries(ctx, list) {
const { createPriceLine } = ctx;
return [
...list.map(({ color, name, tree }) =>
line({
@@ -204,7 +202,7 @@ export function createRealizedPriceRatioSeries(ctx, list) {
unit: Unit.ratio,
}),
),
createPriceLine({ unit: Unit.ratio, number: 1 }),
priceLine({ ctx, unit: Unit.ratio, number: 1 }),
];
}

View File

@@ -35,6 +35,7 @@ import {
} from "./shared.js";
import { Unit } from "../../utils/units.js";
import { line, baseline } from "../series.js";
import { priceLine } from "../constants.js";
// ============================================================================
// Folder Builders (4 variants based on pattern capabilities)
@@ -260,7 +261,7 @@ function createSingleUtxoCountChart(cohort, title) {
return {
name: "utxo count",
title: `UTXO Count ${title}`,
bottom: createUtxoCountSeries( [cohort], false),
bottom: createUtxoCountSeries([cohort], false),
};
}
@@ -274,7 +275,7 @@ function createGroupedUtxoCountChart(list, title) {
return {
name: "utxo count",
title: `UTXO Count ${title}`,
bottom: createUtxoCountSeries( list, true),
bottom: createUtxoCountSeries(list, true),
};
}
@@ -292,7 +293,7 @@ function createSingleRealizedSectionWithAdjusted(ctx, cohort, title) {
createSingleRealizedPriceChart(cohort, title),
{
name: "capitalization",
title: `Realized Capitalization ${title}`,
title: `Realized Cap ${title}`,
bottom: createSingleRealizedCapSeries(ctx, cohort),
},
...createSingleRealizedPnlSection(ctx, cohort, title),
@@ -324,7 +325,7 @@ function createGroupedRealizedSectionWithAdjusted(ctx, list, title) {
},
{
name: "capitalization",
title: `Realized Capitalization ${title}`,
title: `Realized Cap ${title}`,
bottom: createGroupedRealizedCapSeries(list),
},
...createGroupedRealizedPnlSections(ctx, list, title),
@@ -347,7 +348,7 @@ function createSingleRealizedSectionBasic(ctx, cohort, title) {
createSingleRealizedPriceChart(cohort, title),
{
name: "capitalization",
title: `Realized Capitalization ${title}`,
title: `Realized Cap ${title}`,
bottom: createSingleRealizedCapSeries(ctx, cohort),
},
...createSingleRealizedPnlSection(ctx, cohort, title),
@@ -379,7 +380,7 @@ function createGroupedRealizedSectionBasic(ctx, list, title) {
},
{
name: "capitalization",
title: `Realized Capitalization ${title}`,
title: `Realized Cap ${title}`,
bottom: createGroupedRealizedCapSeries(list),
},
...createGroupedRealizedPnlSections(ctx, list, title),
@@ -395,7 +396,7 @@ function createGroupedRealizedSectionBasic(ctx, list, title) {
* @returns {PartialChartOption}
*/
function createSingleRealizedPriceChart(cohort, title) {
const { tree, color } = cohort;
const { tree, color } = cohort;
return {
name: "price",
@@ -418,7 +419,7 @@ function createSingleRealizedPriceChart(cohort, title) {
* @returns {AnyFetchedSeriesBlueprint[]}
*/
function createSingleRealizedCapSeries(ctx, cohort) {
const { colors, createPriceLine } = ctx;
const { colors } = ctx;
const { color, tree } = cohort;
return [
@@ -434,7 +435,7 @@ function createSingleRealizedCapSeries(ctx, cohort) {
unit: Unit.usd,
defaultActive: false,
}),
createPriceLine({ unit: Unit.usd, defaultActive: false }),
priceLine({ ctx, unit: Unit.usd, defaultActive: false }),
...("realizedCapRelToOwnMarketCap" in tree.realized
? [
baseline({
@@ -444,7 +445,8 @@ function createSingleRealizedCapSeries(ctx, cohort) {
options: { baseValue: { price: 100 } },
color: [colors.red, colors.green],
}),
createPriceLine({
priceLine({
ctx,
unit: Unit.pctOwnMcap,
defaultActive: true,
number: 100,
@@ -478,29 +480,24 @@ function createGroupedRealizedCapSeries(list) {
* @returns {PartialOptionsTree}
*/
function createSingleRealizedPnlSection(ctx, cohort, title) {
const {
colors,
createPriceLine,
fromBlockCountWithUnit,
fromBitcoinPatternWithUnit,
} = ctx;
const { colors, fromBlockCountWithUnit, fromBitcoinPatternWithUnit } = ctx;
const { tree } = cohort;
return [
{
name: "pnl",
title: `Realized Profit And Loss ${title}`,
title: `Realized P&L ${title}`,
bottom: [
...fromBlockCountWithUnit(
tree.realized.realizedProfit,
"Profit",
Unit.usd,
"Profit",
colors.green,
),
...fromBlockCountWithUnit(
tree.realized.realizedLoss,
"Loss",
Unit.usd,
"Loss",
colors.red,
),
...fromBitcoinPatternWithUnit(
@@ -538,18 +535,18 @@ function createSingleRealizedPnlSection(ctx, cohort, title) {
color: colors.red,
unit: Unit.pctRcap,
}),
createPriceLine({ unit: Unit.pctRcap }),
createPriceLine({ unit: Unit.usd, defaultActive: false }),
priceLine({ ctx, unit: Unit.pctRcap }),
priceLine({ ctx, unit: Unit.usd, defaultActive: false }),
],
},
{
name: "Net pnl",
title: `Net Realized Profit And Loss ${title}`,
title: `Net Realized P&L ${title}`,
bottom: [
...fromBlockCountWithUnit(
tree.realized.netRealizedPnl,
"Net",
Unit.usd,
"Net",
),
baseline({
metric: tree.realized.netRealizedPnlCumulative30dDelta,
@@ -574,9 +571,9 @@ function createSingleRealizedPnlSection(ctx, cohort, title) {
name: "Cumulative 30d change",
unit: Unit.pctMcap,
}),
createPriceLine({ unit: Unit.pctMcap }),
createPriceLine({ unit: Unit.pctRcap }),
createPriceLine({ unit: Unit.usd }),
priceLine({ ctx, unit: Unit.pctMcap }),
priceLine({ ctx, unit: Unit.pctRcap }),
priceLine({ ctx, unit: Unit.usd }),
],
},
];
@@ -590,8 +587,6 @@ function createSingleRealizedPnlSection(ctx, cohort, title) {
* @returns {PartialOptionsTree}
*/
function createGroupedRealizedPnlSections(ctx, list, title) {
const { createPriceLine } = ctx;
return [
{
name: "profit",
@@ -611,7 +606,7 @@ function createGroupedRealizedPnlSections(ctx, list, title) {
unit: Unit.pctRcap,
}),
]),
createPriceLine({ unit: Unit.usd }),
priceLine({ ctx, unit: Unit.usd }),
],
},
{
@@ -632,12 +627,12 @@ function createGroupedRealizedPnlSections(ctx, list, title) {
unit: Unit.pctRcap,
}),
]),
createPriceLine({ unit: Unit.usd }),
priceLine({ ctx, unit: Unit.usd }),
],
},
{
name: "Total pnl",
title: `Total Realized Profit And Loss ${title}`,
title: `Total Realized P&L ${title}`,
bottom: [
...list.flatMap(({ color, name, tree }) => [
line({
@@ -661,7 +656,7 @@ function createGroupedRealizedPnlSections(ctx, list, title) {
},
{
name: "Net pnl",
title: `Net Realized Profit And Loss ${title}`,
title: `Net Realized P&L ${title}`,
bottom: [
...list.flatMap(({ color, name, tree }) => [
baseline({
@@ -677,8 +672,8 @@ function createGroupedRealizedPnlSections(ctx, list, title) {
unit: Unit.pctRcap,
}),
]),
createPriceLine({ unit: Unit.usd }),
createPriceLine({ unit: Unit.pctRcap }),
priceLine({ ctx, unit: Unit.usd }),
priceLine({ ctx, unit: Unit.pctRcap }),
],
},
{
@@ -710,7 +705,7 @@ function createGroupedRealizedPnlSections(ctx, list, title) {
},
{
name: "Net pnl",
title: `Cumulative Net Realized Profit And Loss ${title}`,
title: `Cumulative Net Realized P&L ${title}`,
bottom: [
...list.flatMap(({ color, name, tree }) => [
baseline({
@@ -721,12 +716,12 @@ function createGroupedRealizedPnlSections(ctx, list, title) {
defaultActive: false,
}),
]),
createPriceLine({ unit: Unit.usd }),
priceLine({ ctx, unit: Unit.usd }),
],
},
{
name: "Net pnl 30d change",
title: `Cumulative Net Realized Profit And Loss 30 Day Change ${title}`,
title: `Net Realized P&L 30d Change ${title}`,
bottom: [
...list.flatMap(({ color, name, tree }) => [
baseline({
@@ -751,9 +746,9 @@ function createGroupedRealizedPnlSections(ctx, list, title) {
unit: Unit.pctMcap,
}),
]),
createPriceLine({ unit: Unit.usd }),
createPriceLine({ unit: Unit.pctMcap }),
createPriceLine({ unit: Unit.pctRcap }),
priceLine({ ctx, unit: Unit.usd }),
priceLine({ ctx, unit: Unit.pctMcap }),
priceLine({ ctx, unit: Unit.pctRcap }),
],
},
],
@@ -787,12 +782,12 @@ function createGroupedRealizedPnlSections(ctx, list, title) {
* @returns {PartialChartOption}
*/
function createSingleBaseSoprChart(ctx, cohort, title) {
const { colors, createPriceLine } = ctx;
const { colors } = ctx;
const { tree } = cohort;
return {
name: "Normal",
title: `Spent Output Profit Ratio ${title}`,
title: `SOPR ${title}`,
bottom: [
baseline({
metric: tree.realized.sopr,
@@ -816,7 +811,7 @@ function createSingleBaseSoprChart(ctx, cohort, title) {
defaultActive: false,
options: { baseValue: { price: 1 } },
}),
createPriceLine({ number: 1, unit: Unit.ratio }),
priceLine({ ctx, number: 1, unit: Unit.ratio }),
],
};
}
@@ -829,12 +824,12 @@ function createSingleBaseSoprChart(ctx, cohort, title) {
* @returns {PartialChartOption}
*/
function createSingleAdjustedSoprChart(ctx, cohort, title) {
const { colors, createPriceLine } = ctx;
const { colors } = ctx;
const { tree } = cohort;
return {
name: "Adjusted",
title: `Adjusted Spent Output Profit Ratio ${title}`,
title: `aSOPR ${title}`,
bottom: [
baseline({
metric: tree.realized.adjustedSopr,
@@ -859,7 +854,7 @@ function createSingleAdjustedSoprChart(ctx, cohort, title) {
defaultActive: false,
options: { baseValue: { price: 1 } },
}),
createPriceLine({ number: 1, unit: Unit.ratio }),
priceLine({ ctx, number: 1, unit: Unit.ratio }),
],
};
}
@@ -872,11 +867,9 @@ function createSingleAdjustedSoprChart(ctx, cohort, title) {
* @returns {PartialChartOption}
*/
function createGroupedBaseSoprChart(ctx, list, title) {
const { createPriceLine } = ctx;
return {
name: "Normal",
title: `Spent Output Profit Ratio ${title}`,
title: `SOPR ${title}`,
bottom: [
...list.flatMap(({ color, name, tree }) => [
baseline({
@@ -903,7 +896,7 @@ function createGroupedBaseSoprChart(ctx, list, title) {
options: { baseValue: { price: 1 } },
}),
]),
createPriceLine({ number: 1, unit: Unit.ratio }),
priceLine({ ctx, number: 1, unit: Unit.ratio }),
],
};
}
@@ -916,11 +909,9 @@ function createGroupedBaseSoprChart(ctx, list, title) {
* @returns {PartialChartOption}
*/
function createGroupedAdjustedSoprChart(ctx, list, title) {
const { createPriceLine } = ctx;
return {
name: "Adjusted",
title: `Adjusted Spent Output Profit Ratio ${title}`,
title: `aSOPR ${title}`,
bottom: [
...list.flatMap(({ color, name, tree }) => [
baseline({
@@ -947,7 +938,7 @@ function createGroupedAdjustedSoprChart(ctx, list, title) {
options: { baseValue: { price: 1 } },
}),
]),
createPriceLine({ number: 1, unit: Unit.ratio }),
priceLine({ ctx, number: 1, unit: Unit.ratio }),
],
};
}
@@ -1056,7 +1047,7 @@ function createUnrealizedPnlRelToMarketCapMetrics(ctx, rel) {
* @param {RelativeWithOwnMarketCap} rel
*/
function createUnrealizedPnlRelToOwnMarketCapMetrics(ctx, rel) {
const { colors, createPriceLine } = ctx;
const { colors } = ctx;
return [
line({
metric: rel.unrealizedProfitRelToOwnMarketCap,
@@ -1077,8 +1068,8 @@ function createUnrealizedPnlRelToOwnMarketCapMetrics(ctx, rel) {
color: colors.red,
unit: Unit.pctOwnMcap,
}),
createPriceLine({ unit: Unit.pctOwnMcap, number: 100 }),
createPriceLine({ unit: Unit.pctOwnMcap }),
priceLine({ ctx, unit: Unit.pctOwnMcap, number: 100 }),
priceLine({ ctx, unit: Unit.pctOwnMcap }),
];
}
@@ -1087,7 +1078,7 @@ function createUnrealizedPnlRelToOwnMarketCapMetrics(ctx, rel) {
* @param {RelativeWithOwnPnl} rel
*/
function createUnrealizedPnlRelToOwnPnlMetrics(ctx, rel) {
const { colors, createPriceLine } = ctx;
const { colors } = ctx;
return [
line({
metric: rel.unrealizedProfitRelToOwnTotalUnrealizedPnl,
@@ -1108,8 +1099,8 @@ function createUnrealizedPnlRelToOwnPnlMetrics(ctx, rel) {
color: colors.red,
unit: Unit.pctOwnPnl,
}),
createPriceLine({ unit: Unit.pctOwnPnl, number: 100 }),
createPriceLine({ unit: Unit.pctOwnPnl }),
priceLine({ ctx, unit: Unit.pctOwnPnl, number: 100 }),
priceLine({ ctx, unit: Unit.pctOwnPnl }),
];
}
@@ -1117,7 +1108,7 @@ function createUnrealizedPnlRelToOwnPnlMetrics(ctx, rel) {
* @param {RelativeWithMarketCap} rel
*/
function createNetUnrealizedPnlRelToMarketCapMetrics(rel) {
return [
return [
baseline({
metric: rel.netUnrealizedPnlRelToMarketCap,
name: "Net",
@@ -1131,14 +1122,13 @@ function createNetUnrealizedPnlRelToMarketCapMetrics(rel) {
* @param {RelativeWithOwnMarketCap} rel
*/
function createNetUnrealizedPnlRelToOwnMarketCapMetrics(ctx, rel) {
const { createPriceLine } = ctx;
return [
baseline({
metric: rel.netUnrealizedPnlRelToOwnMarketCap,
name: "Net",
unit: Unit.pctOwnMcap,
}),
createPriceLine({ unit: Unit.pctOwnMcap }),
priceLine({ ctx, unit: Unit.pctOwnMcap }),
];
}
@@ -1147,14 +1137,13 @@ function createNetUnrealizedPnlRelToOwnMarketCapMetrics(ctx, rel) {
* @param {RelativeWithOwnPnl} rel
*/
function createNetUnrealizedPnlRelToOwnPnlMetrics(ctx, rel) {
const { createPriceLine } = ctx;
return [
baseline({
metric: rel.netUnrealizedPnlRelToOwnTotalUnrealizedPnl,
name: "Net",
unit: Unit.pctOwnPnl,
}),
createPriceLine({ unit: Unit.pctOwnPnl }),
priceLine({ ctx, unit: Unit.pctOwnPnl }),
];
}
@@ -1199,7 +1188,7 @@ function createUnrealizedPnlBaseMetrics(ctx, tree) {
* @param {{ unrealized: { netUnrealizedPnl: AnyMetricPattern } }} tree
*/
function createNetUnrealizedPnlBaseMetric(tree) {
return baseline({
return baseline({
metric: tree.unrealized.netUnrealizedPnl,
name: "Net",
unit: Unit.ratio,
@@ -1219,27 +1208,26 @@ function createNetUnrealizedPnlBaseMetric(tree) {
* @returns {PartialOptionsGroup}
*/
function createSingleUnrealizedSectionAll(ctx, cohort, title) {
const { createPriceLine } = ctx;
const { tree } = cohort;
return {
name: "Unrealized",
tree: [
{
name: "pnl",
title: `Unrealized Profit And Loss ${title}`,
title: `Unrealized P&L ${title}`,
bottom: [
...createUnrealizedPnlBaseMetrics(ctx, tree),
...createUnrealizedPnlRelToOwnPnlMetrics(ctx, tree.relative),
createPriceLine({ unit: Unit.usd, defaultActive: false }),
priceLine({ ctx, unit: Unit.usd, defaultActive: false }),
],
},
{
name: "Net pnl",
title: `Net Unrealized Profit And Loss ${title}`,
title: `Net Unrealized P&L ${title}`,
bottom: [
createNetUnrealizedPnlBaseMetric(tree),
...createNetUnrealizedPnlRelToOwnPnlMetrics(ctx, tree.relative),
createPriceLine({ unit: Unit.usd }),
priceLine({ ctx, unit: Unit.usd }),
],
},
],
@@ -1254,33 +1242,32 @@ function createSingleUnrealizedSectionAll(ctx, cohort, title) {
* @returns {PartialOptionsGroup}
*/
function createSingleUnrealizedSectionFull(ctx, cohort, title) {
const { createPriceLine } = ctx;
const { tree } = cohort;
return {
name: "Unrealized",
tree: [
{
name: "pnl",
title: `Unrealized Profit And Loss ${title}`,
title: `Unrealized P&L ${title}`,
bottom: [
...createUnrealizedPnlBaseMetrics(ctx, tree),
...createUnrealizedPnlRelToMarketCapMetrics(ctx, tree.relative),
...createUnrealizedPnlRelToOwnMarketCapMetrics(ctx, tree.relative),
...createUnrealizedPnlRelToOwnPnlMetrics(ctx, tree.relative),
createPriceLine({ unit: Unit.usd, defaultActive: false }),
createPriceLine({ unit: Unit.pctMcap, defaultActive: false }),
priceLine({ ctx, unit: Unit.usd, defaultActive: false }),
priceLine({ ctx, unit: Unit.pctMcap, defaultActive: false }),
],
},
{
name: "Net pnl",
title: `Net Unrealized Profit And Loss ${title}`,
title: `Net Unrealized P&L ${title}`,
bottom: [
createNetUnrealizedPnlBaseMetric(tree),
...createNetUnrealizedPnlRelToMarketCapMetrics(tree.relative),
...createNetUnrealizedPnlRelToOwnMarketCapMetrics(ctx, tree.relative),
...createNetUnrealizedPnlRelToOwnPnlMetrics(ctx, tree.relative),
createPriceLine({ unit: Unit.usd }),
createPriceLine({ unit: Unit.pctMcap }),
priceLine({ ctx, unit: Unit.usd }),
priceLine({ ctx, unit: Unit.pctMcap }),
],
},
],
@@ -1295,29 +1282,28 @@ function createSingleUnrealizedSectionFull(ctx, cohort, title) {
* @returns {PartialOptionsGroup}
*/
function createSingleUnrealizedSectionWithMarketCap(ctx, cohort, title) {
const { createPriceLine } = ctx;
const { tree } = cohort;
return {
name: "Unrealized",
tree: [
{
name: "pnl",
title: `Unrealized Profit And Loss ${title}`,
title: `Unrealized P&L ${title}`,
bottom: [
...createUnrealizedPnlBaseMetrics(ctx, tree),
...createUnrealizedPnlRelToMarketCapMetrics(ctx, tree.relative),
createPriceLine({ unit: Unit.usd, defaultActive: false }),
createPriceLine({ unit: Unit.pctMcap, defaultActive: false }),
priceLine({ ctx, unit: Unit.usd, defaultActive: false }),
priceLine({ ctx, unit: Unit.pctMcap, defaultActive: false }),
],
},
{
name: "Net pnl",
title: `Net Unrealized Profit And Loss ${title}`,
title: `Net Unrealized P&L ${title}`,
bottom: [
createNetUnrealizedPnlBaseMetric(tree),
...createNetUnrealizedPnlRelToMarketCapMetrics(tree.relative),
createPriceLine({ unit: Unit.usd }),
createPriceLine({ unit: Unit.pctMcap }),
priceLine({ ctx, unit: Unit.usd }),
priceLine({ ctx, unit: Unit.pctMcap }),
],
},
],
@@ -1333,29 +1319,28 @@ function createSingleUnrealizedSectionWithMarketCap(ctx, cohort, title) {
* @returns {PartialOptionsGroup}
*/
function createSingleUnrealizedSectionWithOwnCaps(ctx, cohort, title) {
const { createPriceLine } = ctx;
const { tree } = cohort;
return {
name: "Unrealized",
tree: [
{
name: "pnl",
title: `Unrealized Profit And Loss ${title}`,
title: `Unrealized P&L ${title}`,
bottom: [
...createUnrealizedPnlBaseMetrics(ctx, tree),
...createUnrealizedPnlRelToOwnMarketCapMetrics(ctx, tree.relative),
...createUnrealizedPnlRelToOwnPnlMetrics(ctx, tree.relative),
createPriceLine({ unit: Unit.usd, defaultActive: false }),
priceLine({ ctx, unit: Unit.usd, defaultActive: false }),
],
},
{
name: "Net pnl",
title: `Net Unrealized Profit And Loss ${title}`,
title: `Net Unrealized P&L ${title}`,
bottom: [
createNetUnrealizedPnlBaseMetric(tree),
...createNetUnrealizedPnlRelToOwnMarketCapMetrics(ctx, tree.relative),
...createNetUnrealizedPnlRelToOwnPnlMetrics(ctx, tree.relative),
createPriceLine({ unit: Unit.usd }),
priceLine({ ctx, unit: Unit.usd }),
],
},
],
@@ -1371,25 +1356,24 @@ function createSingleUnrealizedSectionWithOwnCaps(ctx, cohort, title) {
* @returns {PartialOptionsGroup}
*/
function createSingleUnrealizedSectionBase(ctx, cohort, title) {
const { createPriceLine } = ctx;
const { tree } = cohort;
return {
name: "Unrealized",
tree: [
{
name: "pnl",
title: `Unrealized Profit And Loss ${title}`,
title: `Unrealized P&L ${title}`,
bottom: [
...createUnrealizedPnlBaseMetrics(ctx, tree),
createPriceLine({ unit: Unit.usd, defaultActive: false }),
priceLine({ ctx, unit: Unit.usd, defaultActive: false }),
],
},
{
name: "Net pnl",
title: `Net Unrealized Profit And Loss ${title}`,
title: `Net Unrealized P&L ${title}`,
bottom: [
createNetUnrealizedPnlBaseMetric(tree),
createPriceLine({ unit: Unit.usd }),
priceLine({ ctx, unit: Unit.usd }),
],
},
],
@@ -1402,7 +1386,7 @@ function createSingleUnrealizedSectionBase(ctx, cohort, title) {
* @param {string} title
*/
function createGroupedUnrealizedBaseCharts(list, title) {
return [
return [
{
name: "profit",
title: `Unrealized Profit ${title}`,
@@ -1429,7 +1413,7 @@ function createGroupedUnrealizedBaseCharts(list, title) {
},
{
name: "total pnl",
title: `Unrealized Total Profit And Loss ${title}`,
title: `Unrealized Total P&L ${title}`,
bottom: list.flatMap(({ color, name, tree }) => [
line({
metric: tree.unrealized.totalUnrealizedPnl,
@@ -1450,14 +1434,13 @@ function createGroupedUnrealizedBaseCharts(list, title) {
* @returns {PartialOptionsGroup}
*/
function createGroupedUnrealizedSectionFull(ctx, list, title) {
const { createPriceLine } = ctx;
return {
name: "Unrealized",
tree: [
...createGroupedUnrealizedBaseCharts(list, title),
{
name: "Net pnl",
title: `Net Unrealized Profit And Loss ${title}`,
title: `Net Unrealized P&L ${title}`,
bottom: [
...list.flatMap(({ color, name, tree }) => [
baseline({
@@ -1485,10 +1468,10 @@ function createGroupedUnrealizedSectionFull(ctx, list, title) {
unit: Unit.pctOwnPnl,
}),
]),
createPriceLine({ unit: Unit.usd }),
createPriceLine({ unit: Unit.pctMcap }),
createPriceLine({ unit: Unit.pctOwnMcap }),
createPriceLine({ unit: Unit.pctOwnPnl }),
priceLine({ ctx, unit: Unit.usd }),
priceLine({ ctx, unit: Unit.pctMcap }),
priceLine({ ctx, unit: Unit.pctOwnMcap }),
priceLine({ ctx, unit: Unit.pctOwnPnl }),
],
},
],
@@ -1503,14 +1486,13 @@ function createGroupedUnrealizedSectionFull(ctx, list, title) {
* @returns {PartialOptionsGroup}
*/
function createGroupedUnrealizedSectionWithMarketCap(ctx, list, title) {
const { createPriceLine } = ctx;
return {
name: "Unrealized",
tree: [
...createGroupedUnrealizedBaseCharts(list, title),
{
name: "Net pnl",
title: `Net Unrealized Profit And Loss ${title}`,
title: `Net Unrealized P&L ${title}`,
bottom: [
...list.flatMap(({ color, name, tree }) => [
baseline({
@@ -1526,8 +1508,8 @@ function createGroupedUnrealizedSectionWithMarketCap(ctx, list, title) {
unit: Unit.pctMcap,
}),
]),
createPriceLine({ unit: Unit.usd }),
createPriceLine({ unit: Unit.pctMcap }),
priceLine({ ctx, unit: Unit.usd }),
priceLine({ ctx, unit: Unit.pctMcap }),
],
},
],
@@ -1542,14 +1524,13 @@ function createGroupedUnrealizedSectionWithMarketCap(ctx, list, title) {
* @returns {PartialOptionsGroup}
*/
function createGroupedUnrealizedSectionWithOwnCaps(ctx, list, title) {
const { createPriceLine } = ctx;
return {
name: "Unrealized",
tree: [
...createGroupedUnrealizedBaseCharts(list, title),
{
name: "Net pnl",
title: `Net Unrealized Profit And Loss ${title}`,
title: `Net Unrealized P&L ${title}`,
bottom: [
...list.flatMap(({ color, name, tree }) => [
baseline({
@@ -1571,9 +1552,9 @@ function createGroupedUnrealizedSectionWithOwnCaps(ctx, list, title) {
unit: Unit.pctOwnPnl,
}),
]),
createPriceLine({ unit: Unit.usd }),
createPriceLine({ unit: Unit.pctOwnMcap }),
createPriceLine({ unit: Unit.pctOwnPnl }),
priceLine({ ctx, unit: Unit.usd }),
priceLine({ ctx, unit: Unit.pctOwnMcap }),
priceLine({ ctx, unit: Unit.pctOwnPnl }),
],
},
],
@@ -1588,14 +1569,13 @@ function createGroupedUnrealizedSectionWithOwnCaps(ctx, list, title) {
* @returns {PartialOptionsGroup}
*/
function createGroupedUnrealizedSectionBase(ctx, list, title) {
const { createPriceLine } = ctx;
return {
name: "Unrealized",
tree: [
...createGroupedUnrealizedBaseCharts(list, title),
{
name: "Net pnl",
title: `Net Unrealized Profit And Loss ${title}`,
title: `Net Unrealized P&L ${title}`,
bottom: [
...list.flatMap(({ color, name, tree }) => [
baseline({
@@ -1605,7 +1585,7 @@ function createGroupedUnrealizedSectionBase(ctx, list, title) {
unit: Unit.ratio,
}),
]),
createPriceLine({ unit: Unit.usd }),
priceLine({ ctx, unit: Unit.usd }),
],
},
],
@@ -1619,7 +1599,7 @@ function createGroupedUnrealizedSectionBase(ctx, list, title) {
* @returns {PartialOptionsGroup}
*/
function createSingleCostBasisSectionWithPercentiles(cohort, title) {
const { color, tree } = cohort;
const { color, tree } = cohort;
return {
name: "Cost Basis",
@@ -1652,7 +1632,7 @@ function createSingleCostBasisSectionWithPercentiles(cohort, title) {
{
name: "percentiles",
title: `Cost Basis Percentiles ${title}`,
top: createCostBasisPercentilesSeries( [cohort], false),
top: createCostBasisPercentilesSeries([cohort], false),
},
],
};
@@ -1705,7 +1685,7 @@ function createGroupedCostBasisSectionWithPercentiles(list, title) {
* @returns {PartialOptionsGroup}
*/
function createSingleCostBasisSection(cohort, title) {
const { color, tree } = cohort;
const { color, tree } = cohort;
return {
name: "Cost Basis",
@@ -2083,7 +2063,7 @@ function createGroupedActivitySectionWithAdjusted(list, title) {
tree: [
{
name: "Sum",
title: `Sum of Coins Destroyed ${title}`,
title: `Coins Destroyed ${title}`,
bottom: list.flatMap(({ color, name, tree }) => [
line({
metric: tree.activity.coinblocksDestroyed.sum,
@@ -2176,7 +2156,7 @@ function createGroupedActivitySectionBasic(list, title) {
tree: [
{
name: "Sum",
title: `Sum of Coins Destroyed ${title}`,
title: `Coins Destroyed ${title}`,
bottom: list.flatMap(({ color, name, tree }) => [
line({
metric: tree.activity.coinblocksDestroyed.sum,

View File

@@ -1,10 +1,8 @@
/** Cointime section builder - typed tree-based patterns */
import { Unit } from "../utils/units.js";
import { priceLine, priceLines } from "./constants.js";
import { line, baseline } from "./series.js";
import {
satsBtcUsd,
priceLines,
percentileUsdMap,
percentileMap,
sdPatterns,
@@ -26,7 +24,7 @@ function createCointimePriceWithRatioOptions(
ctx,
{ title, legend, price, ratio, color },
) {
const { colors, createPriceLine } = ctx;
const { colors } = ctx;
const pctUsdMap = percentileUsdMap(colors, ratio);
const pctMap = percentileMap(colors, ratio);
@@ -113,7 +111,7 @@ function createCointimePriceWithRatioOptions(
options: { lineStyle: 1 },
}),
),
createPriceLine({ unit: Unit.ratio, number: 1 }),
priceLine({ ctx, unit: Unit.ratio, number: 1 }),
],
},
{
@@ -122,34 +120,34 @@ function createCointimePriceWithRatioOptions(
// Compare all Z-Scores
{
name: "Compare",
title: `Compare ${title} Z-Scores`,
title: `${title} Z-Scores`,
top: [
line({ metric: price, name: legend, color, unit: Unit.usd }),
line({
metric: ratio.ratio1ySd._0sdUsd,
name: "1y 0sd",
color: colors.fuchsia,
name: "1y 0σ",
color: colors.orange,
defaultActive: false,
unit: Unit.usd,
}),
line({
metric: ratio.ratio2ySd._0sdUsd,
name: "2y 0sd",
color: colors.purple,
name: "2y 0σ",
color: colors.yellow,
defaultActive: false,
unit: Unit.usd,
}),
line({
metric: ratio.ratio4ySd._0sdUsd,
name: "4y 0sd",
color: colors.violet,
name: "4y 0σ",
color: colors.lime,
defaultActive: false,
unit: Unit.usd,
}),
line({
metric: ratio.ratioSd._0sdUsd,
name: "0sd",
color: colors.indigo,
name: "all 0σ",
color: colors.blue,
defaultActive: false,
unit: Unit.usd,
}),
@@ -157,8 +155,8 @@ function createCointimePriceWithRatioOptions(
bottom: [
line({
metric: ratio.ratioSd.zscore,
name: "All",
color: colors.default,
name: "all",
color: colors.blue,
unit: Unit.sd,
}),
line({
@@ -170,34 +168,48 @@ function createCointimePriceWithRatioOptions(
line({
metric: ratio.ratio2ySd.zscore,
name: "2y",
color: colors.avocado,
color: colors.yellow,
unit: Unit.sd,
}),
line({
metric: ratio.ratio1ySd.zscore,
name: "1y",
color: colors.yellow,
color: colors.orange,
unit: Unit.sd,
}),
...priceLines(ctx, Unit.sd, [0, 1, -1, 2, -2, 3, -3, 4, -4]),
...priceLines({
ctx,
unit: Unit.sd,
numbers: [0, 1, -1, 2, -2, 3, -3, 4, -4],
defaultActive: false,
}),
],
},
// Individual Z-Score charts
...sdPats.map(({ nameAddon, titleAddon, sd }) => ({
name: nameAddon,
title: `${title} ${titleAddon} Z-Score`,
top: sdBands(colors, sd).map(
({ name: bandName, prop, color: bandColor }) =>
line({
metric: prop,
name: bandName,
color: bandColor,
unit: Unit.usd,
}),
),
top: [
line({ metric: price, name: legend, color, unit: Unit.usd }),
...sdBands(colors, sd).map(
({ name: bandName, prop, color: bandColor }) =>
line({
metric: prop,
name: bandName,
color: bandColor,
unit: Unit.usd,
defaultActive: false,
}),
),
],
bottom: [
line({ metric: sd.zscore, name: "Z-Score", color, unit: Unit.sd }),
...priceLines(ctx, Unit.sd, [0, 1, -1, 2, -2, 3, -3]),
...priceLines({
ctx,
unit: Unit.sd,
numbers: [0, 1, -1, 2, -2, 3, -3],
defaultActive: false,
}),
],
})),
],
@@ -222,28 +234,28 @@ export function createCointimeSection(ctx) {
price: pricing.trueMarketMean,
ratio: pricing.trueMarketMeanRatio,
name: "True market mean",
title: "true market mean",
title: "True Market Mean",
color: colors.blue,
},
{
price: pricing.vaultedPrice,
ratio: pricing.vaultedPriceRatio,
name: "Vaulted",
title: "vaulted price",
title: "Vaulted Price",
color: colors.lime,
},
{
price: pricing.activePrice,
ratio: pricing.activePriceRatio,
name: "Active",
title: "active price",
title: "Active Price",
color: colors.rose,
},
{
price: pricing.cointimePrice,
ratio: pricing.cointimePriceRatio,
name: "cointime",
title: "cointime price",
title: "Cointime Price",
color: colors.yellow,
},
];
@@ -253,31 +265,31 @@ export function createCointimeSection(ctx) {
{
metric: cap.vaultedCap,
name: "vaulted",
title: "vaulted Capitalization",
title: "Vaulted Cap",
color: colors.lime,
},
{
metric: cap.activeCap,
name: "active",
title: "active Capitalization",
title: "Active Cap",
color: colors.rose,
},
{
metric: cap.cointimeCap,
name: "cointime",
title: "cointime Capitalization",
title: "Cointime Cap",
color: colors.yellow,
},
{
metric: cap.investorCap,
name: "investor",
title: "investor Capitalization",
title: "Investor Cap",
color: colors.fuchsia,
},
{
metric: cap.thermoCap,
name: "thermo",
title: "thermo Capitalization",
title: "Thermo Cap",
color: colors.emerald,
},
];
@@ -291,7 +303,7 @@ export function createCointimeSection(ctx) {
tree: [
{
name: "Compare",
title: "Compare Cointime Prices",
title: "Cointime Prices",
top: cointimePrices.map(({ price, name, color }) =>
line({ metric: price, name, color, unit: Unit.usd }),
),
@@ -315,7 +327,7 @@ export function createCointimeSection(ctx) {
tree: [
{
name: "Compare",
title: "Compare Cointime Capitalizations",
title: "Cointime Caps",
bottom: [
line({
metric: supply.marketCap,
@@ -361,17 +373,9 @@ export function createCointimeSection(ctx) {
name: "Supply",
title: "Cointime Supply",
bottom: [
...satsBtcUsd( all.supply.total, "All", colors.orange),
...satsBtcUsd(
cointimeSupply.vaultedSupply,
"Vaulted",
colors.lime,
),
...satsBtcUsd(
cointimeSupply.activeSupply,
"Active",
colors.rose,
),
...satsBtcUsd(all.supply.total, "All", colors.orange),
...satsBtcUsd(cointimeSupply.vaultedSupply, "Vaulted", colors.lime),
...satsBtcUsd(cointimeSupply.activeSupply, "Active", colors.rose),
],
},
@@ -457,7 +461,7 @@ export function createCointimeSection(ctx) {
// Inflation
{
name: "Inflation",
title: "Cointime-Adjusted Inflation Rate",
title: "Adjusted Inflation",
bottom: [
line({
metric: supply.inflation,
@@ -476,7 +480,7 @@ export function createCointimeSection(ctx) {
// Velocity
{
name: "Velocity",
title: "Cointime-Adjusted Transactions Velocity",
title: "Adjusted Velocity",
bottom: [
line({
metric: supply.velocity.btc,

View File

@@ -9,7 +9,7 @@ import { line } from "./series.js";
* @param {number} num
* @returns {AnyMetricPattern}
*/
export function getConstant(constants, num) {
function getConstant(constants, num) {
const key =
num >= 0
? `constant${String(num).replace(".", "")}`
@@ -23,96 +23,31 @@ export function getConstant(constants, num) {
/**
* Create a price line series (horizontal reference line)
* @param {Object} args
* @param {BrkClient["metrics"]["constants"]} args.constants
* @param {Colors} args.colors
* @param {number} [args.number]
* @param {string} [args.name]
* @param {boolean} [args.defaultActive]
* @param {number} [args.lineStyle]
* @param {Color} [args.color]
* @param {Unit} args.unit
* @returns {FetchedLineSeriesBlueprint}
* @param {{ ctx: PartialContext, number?: number, name?: string } & Omit<(Parameters<typeof line>)[0], 'name' | 'metric'>} args
*/
export function createPriceLine({
constants,
colors,
number = 0,
unit,
defaultActive,
color,
name,
lineStyle,
}) {
return {
metric: getConstant(constants, number),
title: name ?? `${number}`,
unit,
defaultActive,
color: color ?? colors.gray,
options: {
lineStyle: lineStyle ?? 4,
lastValueVisible: false,
crosshairMarkerVisible: false,
},
};
}
/**
* Create multiple price lines from an array of numbers
* @param {Object} args
* @param {BrkClient["metrics"]["constants"]} args.constants
* @param {Colors} args.colors
* @param {number[]} args.numbers
* @param {Unit} args.unit
* @returns {FetchedLineSeriesBlueprint[]}
*/
export function createPriceLines({ constants, colors, numbers, unit }) {
return numbers.map((number) => ({
metric: getConstant(constants, number),
title: `${number}`,
unit,
defaultActive: !number,
color: colors.gray,
options: {
lineStyle: 4,
lastValueVisible: false,
crosshairMarkerVisible: false,
},
}));
}
/**
* Create a constant line series
* @param {Object} args
* @param {Colors} args.colors
* @param {AnyMetricPattern} args.constant
* @param {string} args.name
* @param {Unit} args.unit
* @param {Color} [args.color]
* @param {number} [args.lineStyle]
* @param {boolean} [args.defaultActive]
* @returns {FetchedLineSeriesBlueprint}
*/
export function constantLine({
colors,
constant,
name,
unit,
color,
lineStyle,
defaultActive,
}) {
export function priceLine(args) {
return line({
metric: constant,
name,
unit,
defaultActive,
color: color ?? colors.gray,
...args,
metric: getConstant(args.ctx.brk.metrics.constants, args.number || 0),
name: args.name || `${args.number ?? 0}`,
color: args.color ?? args.ctx.colors.gray,
options: {
lineStyle: lineStyle ?? 4,
lineStyle: args.style ?? 4,
lastValueVisible: false,
crosshairMarkerVisible: false,
...args.options,
},
});
}
/**
* @param {{ numbers: number[] } & Omit<(Parameters<typeof priceLine>)[0], 'number'>} args
*/
export function priceLines(args) {
return args.numbers.map((number) =>
priceLine({
...args,
number,
}),
);
}

View File

@@ -4,6 +4,7 @@ import {
fromBlockSize,
fromSizePattern,
fromFullnessPattern,
fromDollarsPattern,
fromFeeRatePattern,
fromCoinbasePattern,
fromValuePattern,
@@ -12,11 +13,6 @@ import {
fromIntervalPattern,
fromSupplyPattern,
} from "./series.js";
import {
createPriceLine,
createPriceLines,
constantLine,
} from "./constants.js";
import { colors } from "../chart/colors.js";
/**
@@ -26,8 +22,6 @@ import { colors } from "../chart/colors.js";
* @returns {PartialContext}
*/
export function createContext({ brk }) {
const constants = brk.metrics.constants;
return {
colors,
brk,
@@ -38,12 +32,14 @@ export function createContext({ brk }) {
fromBitcoin(colors, pattern, title, color),
fromBlockSize: (pattern, title, color) =>
fromBlockSize(colors, pattern, title, color),
fromSizePattern: (pattern, title, unit) =>
fromSizePattern(colors, pattern, title, unit),
fromFullnessPattern: (pattern, title, unit) =>
fromFullnessPattern(colors, pattern, title, unit),
fromFeeRatePattern: (pattern, title, unit) =>
fromFeeRatePattern(colors, pattern, title, unit),
fromSizePattern: (pattern, unit, title) =>
fromSizePattern(colors, pattern, unit, title),
fromFullnessPattern: (pattern, unit, title) =>
fromFullnessPattern(colors, pattern, unit, title),
fromDollarsPattern: (pattern, unit, title) =>
fromDollarsPattern(colors, pattern, unit, title),
fromFeeRatePattern: (pattern, unit, title) =>
fromFeeRatePattern(colors, pattern, unit, title),
fromCoinbasePattern: (pattern, title) =>
fromCoinbasePattern(colors, pattern, title),
fromValuePattern: (pattern, title, sumColor, cumulativeColor) =>
@@ -63,23 +59,18 @@ export function createContext({ brk }) {
sumColor,
cumulativeColor,
),
fromBlockCountWithUnit: (pattern, title, unit, sumColor, cumulativeColor) =>
fromBlockCountWithUnit: (pattern, unit, title, sumColor, cumulativeColor) =>
fromBlockCountWithUnit(
colors,
pattern,
title,
unit,
title,
sumColor,
cumulativeColor,
),
fromIntervalPattern: (pattern, title, unit, color) =>
fromIntervalPattern(colors, pattern, title, unit, color),
fromIntervalPattern: (pattern, unit, title, color) =>
fromIntervalPattern(colors, pattern, unit, title, color),
fromSupplyPattern: (pattern, title, color) =>
fromSupplyPattern(colors, pattern, title, color),
createPriceLine: (args) => createPriceLine({ constants, colors, ...args }),
createPriceLines: (args) =>
createPriceLines({ constants, colors, ...args }),
constantLine: (args) => constantLine({ colors, ...args }),
};
}

View File

@@ -1,9 +1,9 @@
/** Moving averages section */
import { Unit } from "../../utils/units.js";
import { priceLine, priceLines } from "../constants.js";
import { line, baseline } from "../series.js";
import {
priceLines,
percentileUsdMap,
percentileMap,
sdPatterns,
@@ -56,7 +56,7 @@ export function createPriceWithRatioOptions(
ctx,
{ title, legend, ratio, color },
) {
const { colors, createPriceLine } = ctx;
const { colors } = ctx;
const priceMetric = ratio.price;
const pctUsdMap = percentileUsdMap(colors, ratio);
@@ -93,48 +93,47 @@ export function createPriceWithRatioOptions(
color,
unit: Unit.ratio,
}),
line({
metric: ratio.ratio1wSma,
name: "1w SMA",
color: colors.lime,
unit: Unit.ratio,
defaultActive: false,
}),
line({
metric: ratio.ratio1mSma,
name: "1m SMA",
color: colors.teal,
unit: Unit.ratio,
defaultActive: false,
}),
line({
metric: ratio.ratio1ySd.sma,
name: "1y SMA",
color: colors.sky,
unit: Unit.ratio,
defaultActive: false,
}),
line({
metric: ratio.ratio2ySd.sma,
name: "2y SMA",
color: colors.indigo,
unit: Unit.ratio,
defaultActive: false,
}),
line({
metric: ratio.ratio4ySd.sma,
name: "4y SMA",
color: colors.purple,
unit: Unit.ratio,
defaultActive: false,
}),
line({
metric: ratio.ratioSd.sma,
name: "All SMA",
color: colors.rose,
unit: Unit.ratio,
defaultActive: false,
}),
.../** @type {const} */ ([
{
metric: ratio.ratio1wSma,
name: "1w SMA",
color: colors.lime,
},
{
metric: ratio.ratio1mSma,
name: "1m SMA",
color: colors.teal,
},
{
metric: ratio.ratio1ySd.sma,
name: "1y SMA",
color: colors.sky,
},
{
metric: ratio.ratio2ySd.sma,
name: "2y SMA",
color: colors.indigo,
},
{
metric: ratio.ratio4ySd.sma,
name: "4y SMA",
color: colors.purple,
},
{
metric: ratio.ratioSd.sma,
name: "All SMA",
color: colors.rose,
},
]).map(({ metric, name, color }) =>
line({
metric,
name,
color,
unit: Unit.ratio,
defaultActive: false,
style: 1,
}),
),
...pctMap.map(({ name: pctName, prop, color: pctColor }) =>
line({
metric: prop,
@@ -142,31 +141,118 @@ export function createPriceWithRatioOptions(
color: pctColor,
defaultActive: false,
unit: Unit.ratio,
options: { lineStyle: 1 },
style: 1,
}),
),
createPriceLine({ unit: Unit.ratio, number: 1 }),
priceLine({ ctx, unit: Unit.ratio, number: 1 }),
],
},
{
name: "ZScores",
tree: sdPats.map(({ nameAddon, titleAddon, sd }) => ({
name: nameAddon,
title: `${title} ${titleAddon} Z-Score`,
top: sdBands(colors, sd).map(
({ name: bandName, prop, color: bandColor }) =>
tree: [
{
name: "Compare",
title: `${title} Z-Scores`,
top: [
line({ metric: priceMetric, name: legend, color, unit: Unit.usd }),
line({
metric: prop,
name: bandName,
color: bandColor,
metric: ratio.ratio1ySd._0sdUsd,
name: "1y 0σ",
color: colors.orange,
defaultActive: false,
unit: Unit.usd,
}),
),
bottom: [
line({ metric: sd.zscore, name: "Z-Score", color, unit: Unit.sd }),
...priceLines(ctx, Unit.sd, [0, 1, -1, 2, -2, 3, -3]),
],
})),
line({
metric: ratio.ratio2ySd._0sdUsd,
name: "2y 0σ",
color: colors.yellow,
defaultActive: false,
unit: Unit.usd,
}),
line({
metric: ratio.ratio4ySd._0sdUsd,
name: "4y 0σ",
color: colors.lime,
defaultActive: false,
unit: Unit.usd,
}),
line({
metric: ratio.ratioSd._0sdUsd,
name: "all 0σ",
color: colors.blue,
defaultActive: false,
unit: Unit.usd,
}),
],
bottom: [
line({
metric: ratio.ratioSd.zscore,
name: "all",
color: colors.blue,
unit: Unit.sd,
}),
line({
metric: ratio.ratio4ySd.zscore,
name: "4y",
color: colors.lime,
unit: Unit.sd,
}),
line({
metric: ratio.ratio2ySd.zscore,
name: "2y",
color: colors.yellow,
unit: Unit.sd,
}),
line({
metric: ratio.ratio1ySd.zscore,
name: "1y",
color: colors.orange,
unit: Unit.sd,
}),
...priceLines({
ctx,
unit: Unit.sd,
numbers: [0, 1, -1, 2, -2, 3, -3],
defaultActive: false,
}),
],
},
...sdPats.map(({ nameAddon, titleAddon, sd }) => ({
name: nameAddon,
title: `${title} ${titleAddon} Z-Score`,
top: [
line({ metric: priceMetric, name: legend, color, unit: Unit.usd }),
...sdBands(colors, sd).map(
({ name: bandName, prop, color: bandColor }) =>
line({
metric: prop,
name: bandName,
color: bandColor,
unit: Unit.usd,
defaultActive: false,
}),
),
],
bottom: [
baseline({
metric: sd.zscore,
name: "Z-Score",
color,
unit: Unit.sd,
}),
priceLine({
ctx,
unit: Unit.sd,
}),
...priceLines({
ctx,
unit: Unit.sd,
numbers: [1, -1, 2, -2, 3, -3],
defaultActive: false,
}),
],
})),
],
},
];
}
@@ -177,7 +263,6 @@ export function createPriceWithRatioOptions(
* @param {ReturnType<typeof buildAverages>} averages
*/
export function createAveragesSection(ctx, averages) {
return {
name: "Averages",
tree: [
@@ -188,7 +273,7 @@ export function createAveragesSection(ctx, averages) {
tree: [
{
name: "Compare",
title: `Market Price ${nameAddon} Moving Averages`,
title: `Price ${metricAddon.toUpperCase()}s`,
top: averages.map(({ id, color, sma, ema }) =>
line({
metric: (metricAddon === "sma" ? sma : ema).price,
@@ -202,7 +287,7 @@ export function createAveragesSection(ctx, averages) {
name,
tree: createPriceWithRatioOptions(ctx, {
ratio: metricAddon === "sma" ? sma : ema,
title: `${name} Market Price ${nameAddon} Moving Average`,
title: `${name} ${metricAddon.toUpperCase()}`,
legend: "average",
color,
}),

View File

@@ -2,7 +2,7 @@
import { localhost } from "../../utils/env.js";
import { Unit } from "../../utils/units.js";
import { line } from "../series.js";
import { candlestick, line } from "../series.js";
import { buildAverages, createAveragesSection } from "./averages.js";
import { createPerformanceSection } from "./performance.js";
import { createIndicatorsSection } from "./indicators/index.js";
@@ -43,6 +43,16 @@ export function createMarketSection(ctx) {
name: "Oracle",
title: "Oracle Price",
top: [
candlestick({
metric: price.oracle.closeOhlcDollars,
name: "close",
unit: Unit.usd,
}),
candlestick({
metric: price.oracle.midOhlcDollars,
name: "mid",
unit: Unit.usd,
}),
line({
metric: price.oracle.phaseDailyDollars.median,
name: "o. p50",
@@ -105,7 +115,7 @@ export function createMarketSection(ctx) {
// Capitalization
{
name: "Capitalization",
title: "Market Capitalization",
title: "Market Cap",
bottom: [
line({
metric: supply.marketCap,

View File

@@ -45,17 +45,19 @@ export function createBandsSection(ctx, { range, movingAverage }) {
},
].map(({ id, title, min, max }) => ({
name: id,
title: `Bitcoin Price ${title} MinMax Bands`,
title: `${title} MinMax`,
top: [
line({
metric: min,
name: "Min",
key: `price-min`,
color: colors.red,
unit: Unit.usd,
}),
line({
metric: max,
name: "Max",
key: `price-max`,
color: colors.green,
unit: Unit.usd,
}),

View File

@@ -1,6 +1,7 @@
/** Momentum indicators (RSI, StochRSI, Stochastic, MACD) */
import { Unit } from "../../../utils/units.js";
import { priceLine, priceLines } from "../../constants.js";
import { line, histogram } from "../../series.js";
/**
@@ -9,14 +10,14 @@ import { line, histogram } from "../../series.js";
* @param {Market["indicators"]} indicators
*/
export function createMomentumSection(ctx, indicators) {
const { colors, createPriceLine } = ctx;
const { colors } = ctx;
return {
name: "Momentum",
tree: [
{
name: "RSI",
title: "Relative Strength Index (14d)",
title: "RSI (14d)",
bottom: [
line({
metric: indicators.rsi14d,
@@ -38,13 +39,14 @@ export function createMomentumSection(ctx, indicators) {
defaultActive: false,
unit: Unit.index,
}),
createPriceLine({ unit: Unit.index, number: 70 }),
createPriceLine({
priceLine({ ctx, unit: Unit.index, number: 70 }),
priceLine({
ctx,
unit: Unit.index,
number: 50,
defaultActive: false,
}),
createPriceLine({ unit: Unit.index, number: 30 }),
priceLine({ ctx, unit: Unit.index, number: 30 }),
],
},
{
@@ -69,8 +71,7 @@ export function createMomentumSection(ctx, indicators) {
color: colors.orange,
unit: Unit.index,
}),
createPriceLine({ unit: Unit.index, number: 80 }),
createPriceLine({ unit: Unit.index, number: 20 }),
...priceLines({ ctx, unit: Unit.index, numbers: [80, 20] }),
],
},
// {
@@ -79,13 +80,12 @@ export function createMomentumSection(ctx, indicators) {
// bottom: [
// line({ metric: indicators.stochK, name: "K", color: colors.blue, unit: Unit.index }),
// line({ metric: indicators.stochD, name: "D", color: colors.orange, unit: Unit.index }),
// createPriceLine({ unit: Unit.index, number: 80 }),
// createPriceLine({ unit: Unit.index, number: 20 }),
// priceLines({ ctx, unit: Unit.index, numbers: [80, 20] }),
// ],
// },
{
name: "MACD",
title: "Moving Average Convergence Divergence",
title: "MACD",
bottom: [
line({
metric: indicators.macdLine,
@@ -104,7 +104,7 @@ export function createMomentumSection(ctx, indicators) {
name: "Histogram",
unit: Unit.usd,
}),
createPriceLine({ unit: Unit.usd }),
priceLine({ ctx, unit: Unit.usd }),
],
},
],

View File

@@ -1,7 +1,8 @@
/** On-chain indicators (Pi Cycle, Puell, NVT, Gini) */
import { Unit } from "../../../utils/units.js";
import { line } from "../../series.js";
import { priceLine } from "../../constants.js";
import { baseline, line } from "../../series.js";
/**
* Create On-chain section
@@ -11,14 +12,14 @@ import { line } from "../../series.js";
* @param {Market["movingAverage"]} args.movingAverage
*/
export function createOnchainSection(ctx, { indicators, movingAverage }) {
const { colors, createPriceLine } = ctx;
const { colors } = ctx;
return {
name: "On-chain",
tree: [
{
name: "Pi Cycle",
title: "Pi Cycle Top Indicator",
title: "Pi Cycle",
top: [
line({
metric: movingAverage.price111dSma.price,
@@ -34,13 +35,13 @@ export function createOnchainSection(ctx, { indicators, movingAverage }) {
}),
],
bottom: [
line({
baseline({
metric: indicators.piCycle,
name: "Pi Cycle",
color: colors.purple,
unit: Unit.ratio,
base: 1,
}),
createPriceLine({ unit: Unit.ratio, number: 1 }),
priceLine({ ctx, unit: Unit.ratio, number: 1 }),
],
},
{
@@ -57,7 +58,7 @@ export function createOnchainSection(ctx, { indicators, movingAverage }) {
},
{
name: "NVT",
title: "Network Value to Transactions Ratio",
title: "NVT Ratio",
bottom: [
line({
metric: indicators.nvt,

View File

@@ -1,6 +1,7 @@
/** Volatility indicators (Index, True Range, Choppiness, Sharpe, Sortino) */
import { Unit } from "../../../utils/units.js";
import { priceLine, priceLines } from "../../constants.js";
import { line } from "../../series.js";
/**
@@ -11,14 +12,14 @@ import { line } from "../../series.js";
* @param {Market["range"]} args.range
*/
export function createVolatilitySection(ctx, { volatility, range }) {
const { colors, createPriceLine } = ctx;
const { colors } = ctx;
return {
name: "Volatility",
tree: [
{
name: "Index",
title: "Bitcoin Price Volatility Index",
title: "Volatility Index",
bottom: [
line({
metric: volatility.price1wVolatility,
@@ -42,7 +43,7 @@ export function createVolatilitySection(ctx, { volatility, range }) {
},
{
name: "True Range",
title: "Bitcoin Price True Range",
title: "True Range",
bottom: [
line({
metric: range.priceTrueRange,
@@ -54,7 +55,7 @@ export function createVolatilitySection(ctx, { volatility, range }) {
},
{
name: "Choppiness",
title: "Bitcoin Price Choppiness Index",
title: "Choppiness Index",
bottom: [
line({
metric: range.price2wChoppinessIndex,
@@ -62,8 +63,7 @@ export function createVolatilitySection(ctx, { volatility, range }) {
color: colors.red,
unit: Unit.index,
}),
createPriceLine({ unit: Unit.index, number: 61.8 }),
createPriceLine({ unit: Unit.index, number: 38.2 }),
...priceLines({ ctx, unit: Unit.index, numbers: [61.8, 38.2] }),
],
},
{
@@ -88,7 +88,7 @@ export function createVolatilitySection(ctx, { volatility, range }) {
color: colors.lime,
unit: Unit.ratio,
}),
createPriceLine({ unit: Unit.ratio }),
priceLine({ ctx, unit: Unit.ratio }),
],
},
{
@@ -113,7 +113,7 @@ export function createVolatilitySection(ctx, { volatility, range }) {
color: colors.lime,
unit: Unit.ratio,
}),
createPriceLine({ unit: Unit.ratio }),
priceLine({ ctx, unit: Unit.ratio }),
],
},
],

View File

@@ -1,6 +1,7 @@
/** Investing section (DCA) */
import { Unit } from "../../utils/units.js";
import { priceLine } from "../constants.js";
import { line, baseline } from "../series.js";
import { satsBtcUsd } from "../shared.js";
import { periodIdToName } from "./utils.js";
@@ -12,6 +13,7 @@ import { periodIdToName } from "./utils.js";
*/
export function buildDcaClasses(colors, dca) {
return /** @type {const} */ ([
[2026, "rose", true],
[2025, "pink", true],
[2024, "fuchsia", true],
[2023, "purple", true],
@@ -42,7 +44,7 @@ export function buildDcaClasses(colors, dca) {
* @param {Market["returns"]} args.returns
*/
export function createInvestingSection(ctx, { dca, lookback, returns }) {
const { colors, createPriceLine } = ctx;
const { colors } = ctx;
const dcaClasses = buildDcaClasses(colors, dca);
return {
@@ -77,7 +79,7 @@ export function createInvestingSection(ctx, { dca, lookback, returns }) {
tree: [
{
name: "Cost basis",
title: `${name} DCA vs Lump Sum (Cost Basis)`,
title: `${name} Cost Basis`,
top: [
line({
metric: dcaCostBasis,
@@ -95,7 +97,7 @@ export function createInvestingSection(ctx, { dca, lookback, returns }) {
},
{
name: "Returns",
title: `${name} DCA vs Lump Sum (Returns)`,
title: `${name} Returns`,
bottom: [
baseline({
metric: dcaReturns,
@@ -105,18 +107,18 @@ export function createInvestingSection(ctx, { dca, lookback, returns }) {
baseline({
metric: priceReturns,
name: "Lump sum",
color: [colors.lime, colors.red],
color: [colors.cyan, colors.orange],
unit: Unit.percentage,
}),
createPriceLine({ unit: Unit.percentage }),
priceLine({ ctx, unit: Unit.percentage }),
],
},
{
name: "Stack",
title: `${name} DCA vs Lump Sum Stack ($100/day)`,
title: `${name} Stack`,
bottom: [
...satsBtcUsd( dcaStack, "DCA", colors.green),
...satsBtcUsd( lumpSumStack, "Lump sum", colors.orange),
...satsBtcUsd(dcaStack, "DCA", colors.green),
...satsBtcUsd(lumpSumStack, "Lump sum", colors.orange),
],
},
],
@@ -134,7 +136,7 @@ export function createInvestingSection(ctx, { dca, lookback, returns }) {
tree: [
{
name: "Cost basis",
title: "DCA Cost Basis by Year",
title: "DCA Cost Basis",
top: dcaClasses.map(
({ year, color, defaultActive, costBasis }) =>
line({
@@ -148,7 +150,7 @@ export function createInvestingSection(ctx, { dca, lookback, returns }) {
},
{
name: "Returns",
title: "DCA Returns by Year",
title: "DCA Returns",
bottom: dcaClasses.map(
({ year, color, defaultActive, returns }) =>
baseline({
@@ -162,10 +164,10 @@ export function createInvestingSection(ctx, { dca, lookback, returns }) {
},
{
name: "Stack",
title: "DCA Stack by Year ($100/day)",
title: "DCA Stack",
bottom: dcaClasses.flatMap(
({ year, color, defaultActive, stack }) =>
satsBtcUsd( stack, `${year}`, color, { defaultActive }),
satsBtcUsd(stack, `${year}`, color, { defaultActive }),
),
},
],
@@ -176,7 +178,7 @@ export function createInvestingSection(ctx, { dca, lookback, returns }) {
tree: [
{
name: "Cost basis",
title: `DCA Class ${year} Cost Basis`,
title: `${year} Cost Basis`,
top: [
line({
metric: costBasis,
@@ -188,7 +190,7 @@ export function createInvestingSection(ctx, { dca, lookback, returns }) {
},
{
name: "Returns",
title: `DCA Class ${year} Returns`,
title: `${year} Returns`,
bottom: [
baseline({
metric: returns,
@@ -200,8 +202,8 @@ export function createInvestingSection(ctx, { dca, lookback, returns }) {
},
{
name: "Stack",
title: `DCA Class ${year} Stack ($100/day)`,
bottom: satsBtcUsd( stack, "Stack", color),
title: `${year} Stack`,
bottom: satsBtcUsd(stack, "Stack", color),
},
],
})),

View File

@@ -1,6 +1,7 @@
/** Performance section */
import { Unit } from "../../utils/units.js";
import { priceLine } from "../constants.js";
import { baseline } from "../series.js";
import { periodIdToName } from "./utils.js";
@@ -10,7 +11,7 @@ import { periodIdToName } from "./utils.js";
* @param {Market["returns"]} returns
*/
export function createPerformanceSection(ctx, returns) {
const { colors, createPriceLine } = ctx;
const { colors } = ctx;
return {
name: "Performance",
@@ -34,7 +35,7 @@ export function createPerformanceSection(ctx, returns) {
const name = periodIdToName(id, true);
return {
name,
title: `${name} Performance`,
title: `${name} Returns`,
bottom: [
baseline({
metric: priceReturns,
@@ -46,12 +47,12 @@ export function createPerformanceSection(ctx, returns) {
baseline({
metric: cagr,
name: "CAGR",
color: [colors.lime, colors.pink],
color: [colors.cyan, colors.orange],
unit: Unit.percentage,
}),
]
: []),
createPriceLine({ unit: Unit.percentage }),
priceLine({ ctx, unit: Unit.percentage }),
],
};
}),

View File

@@ -8,19 +8,34 @@ import { Unit } from "../utils/units.js";
* @param {AnyMetricPattern} args.metric
* @param {string} args.name
* @param {Unit} args.unit
* @param {string} [args.key] - Optional key for persistence (derived from name if not provided)
* @param {LineStyle} [args.style]
* @param {Color} [args.color]
* @param {boolean} [args.defaultActive]
* @param {LineSeriesPartialOptions} [args.options]
* @returns {FetchedLineSeriesBlueprint}
*/
export function line({ metric, name, color, defaultActive, unit, options }) {
export function line({
metric,
name,
key,
style,
color,
defaultActive,
unit,
options,
}) {
return {
metric,
title: name,
key,
color,
unit,
defaultActive,
options,
options: {
lineStyle: style,
...options,
},
};
}
@@ -30,16 +45,26 @@ export function line({ metric, name, color, defaultActive, unit, options }) {
* @param {AnyMetricPattern} args.metric
* @param {string} args.name
* @param {Unit} args.unit
* @param {string} [args.key] - Optional key for persistence (derived from name if not provided)
* @param {Color} [args.color]
* @param {boolean} [args.defaultActive]
* @param {LineSeriesPartialOptions} [args.options]
* @returns {FetchedDotsSeriesBlueprint}
*/
export function dots({ metric, name, color, defaultActive, unit, options }) {
export function dots({
metric,
name,
key,
color,
defaultActive,
unit,
options,
}) {
return {
type: /** @type {const} */ ("Dots"),
metric,
title: name,
key,
color,
unit,
defaultActive,
@@ -53,6 +78,7 @@ export function dots({ metric, name, color, defaultActive, unit, options }) {
* @param {AnyMetricPattern} args.metric
* @param {string} args.name
* @param {Unit} args.unit
* @param {string} [args.key] - Optional key for persistence (derived from name if not provided)
* @param {[Color, Color]} [args.colors] - [upColor, downColor] for legend
* @param {boolean} [args.defaultActive]
* @param {CandlestickSeriesPartialOptions} [args.options]
@@ -61,6 +87,7 @@ export function dots({ metric, name, color, defaultActive, unit, options }) {
export function candlestick({
metric,
name,
key,
colors,
defaultActive,
unit,
@@ -70,6 +97,7 @@ export function candlestick({
type: /** @type {const} */ ("Candlestick"),
metric,
title: name,
key,
colors,
unit,
defaultActive,
@@ -83,9 +111,9 @@ export function candlestick({
* @param {AnyMetricPattern} args.metric
* @param {string} args.name
* @param {Unit} args.unit
* @param {string} [args.key] - Optional key for persistence (derived from name if not provided)
* @param {Color | [Color, Color]} [args.color]
* @param {boolean} [args.defaultActive]
* @param {boolean} [args.defaultActive]
* @param {number | undefined} [args.base]
* @param {BaselineSeriesPartialOptions} [args.options]
* @returns {FetchedBaselineSeriesBlueprint}
@@ -93,6 +121,7 @@ export function candlestick({
export function baseline({
metric,
name,
key,
color,
defaultActive,
unit,
@@ -104,6 +133,7 @@ export function baseline({
type: /** @type {const} */ ("Baseline"),
metric,
title: name,
key,
color: isTuple ? undefined : color,
colors: isTuple ? color : undefined,
unit,
@@ -123,6 +153,7 @@ export function baseline({
* @param {AnyMetricPattern} args.metric
* @param {string} args.name
* @param {Unit} args.unit
* @param {string} [args.key] - Optional key for persistence (derived from name if not provided)
* @param {Color | [Color, Color]} [args.color]
* @param {boolean} [args.defaultActive]
* @param {HistogramSeriesPartialOptions} [args.options]
@@ -131,6 +162,7 @@ export function baseline({
export function histogram({
metric,
name,
key,
color,
defaultActive,
unit,
@@ -140,6 +172,7 @@ export function histogram({
type: /** @type {const} */ ("Histogram"),
metric,
title: name,
key,
color,
unit,
defaultActive,
@@ -238,7 +271,7 @@ export function fromBlockSize(colors, pattern, title, color) {
{ metric: pattern.average, title: "Average", defaultActive: false },
{
metric: pattern.cumulative,
title: `${title} (cum.)`,
title: `Cumulative`,
color: colors.cyan,
defaultActive: false,
},
@@ -291,72 +324,72 @@ export function fromBlockSize(colors, pattern, title, color) {
* Create series from a SizePattern ({ average, sum, cumulative, min, max, percentiles })
* @param {Colors} colors
* @param {AnyStatsPattern} pattern
* @param {string} title
* @param {Unit} unit
* @param {string} [title]
* @returns {AnyFetchedSeriesBlueprint[]}
*/
export function fromSizePattern(colors, pattern, title, unit) {
export function fromSizePattern(colors, pattern, unit, title = "") {
return [
{ metric: pattern.average, title: `${title} avg`, unit },
{ metric: pattern.average, title: `${title} avg`.trim(), unit },
{
metric: pattern.sum,
title: `${title} sum`,
title: `${title} sum`.trim(),
color: colors.blue,
unit,
defaultActive: false,
},
{
metric: pattern.cumulative,
title: `${title} cumulative`,
title: `${title} cumulative`.trim(),
color: colors.indigo,
unit,
defaultActive: false,
},
{
metric: pattern.min,
title: `${title} min`,
title: `${title} min`.trim(),
color: colors.red,
unit,
defaultActive: false,
},
{
metric: pattern.max,
title: `${title} max`,
title: `${title} max`.trim(),
color: colors.green,
unit,
defaultActive: false,
},
{
metric: pattern.pct10,
title: `${title} pct10`,
title: `${title} pct10`.trim(),
color: colors.rose,
unit,
defaultActive: false,
},
{
metric: pattern.pct25,
title: `${title} pct25`,
title: `${title} pct25`.trim(),
color: colors.pink,
unit,
defaultActive: false,
},
{
metric: pattern.median,
title: `${title} median`,
title: `${title} median`.trim(),
color: colors.purple,
unit,
defaultActive: false,
},
{
metric: pattern.pct75,
title: `${title} pct75`,
title: `${title} pct75`.trim(),
color: colors.violet,
unit,
defaultActive: false,
},
{
metric: pattern.pct90,
title: `${title} pct90`,
title: `${title} pct90`.trim(),
color: colors.fuchsia,
unit,
defaultActive: false,
@@ -368,65 +401,148 @@ export function fromSizePattern(colors, pattern, title, unit) {
* Create series from a FullnessPattern ({ base, average, sum, cumulative, min, max, percentiles })
* @param {Colors} colors
* @param {FullnessPattern<any>} pattern
* @param {string} title
* @param {Unit} unit
* @param {string} [title]
* @returns {AnyFetchedSeriesBlueprint[]}
*/
export function fromFullnessPattern(colors, pattern, title, unit) {
export function fromFullnessPattern(colors, pattern, unit, title = "") {
return [
{ metric: pattern.base, title, unit },
{ metric: pattern.base, title: title || "base", unit },
{
metric: pattern.average,
title: `${title} avg`,
title: `${title} avg`.trim(),
color: colors.purple,
unit,
defaultActive: false,
},
{
metric: pattern.min,
title: `${title} min`,
title: `${title} min`.trim(),
color: colors.red,
unit,
defaultActive: false,
},
{
metric: pattern.max,
title: `${title} max`,
title: `${title} max`.trim(),
color: colors.green,
unit,
defaultActive: false,
},
{
metric: pattern.pct10,
title: `${title} pct10`,
title: `${title} pct10`.trim(),
color: colors.rose,
unit,
defaultActive: false,
},
{
metric: pattern.pct25,
title: `${title} pct25`,
title: `${title} pct25`.trim(),
color: colors.pink,
unit,
defaultActive: false,
},
{
metric: pattern.median,
title: `${title} median`,
title: `${title} median`.trim(),
color: colors.violet,
unit,
defaultActive: false,
},
{
metric: pattern.pct75,
title: `${title} pct75`,
title: `${title} pct75`.trim(),
color: colors.fuchsia,
unit,
defaultActive: false,
},
{
metric: pattern.pct90,
title: `${title} pct90`,
title: `${title} pct90`.trim(),
color: colors.amber,
unit,
defaultActive: false,
},
];
}
/**
* Create series from a DollarsPattern ({ base, sum, cumulative, average, min, max, percentiles })
* @param {Colors} colors
* @param {DollarsPattern<any>} pattern
* @param {Unit} unit
* @param {string} [title]
* @returns {AnyFetchedSeriesBlueprint[]}
*/
export function fromDollarsPattern(colors, pattern, unit, title = "") {
return [
{ metric: pattern.base, title: title || "base", unit },
{
metric: pattern.sum,
title: `${title} sum`.trim(),
color: colors.blue,
unit,
},
{
metric: pattern.cumulative,
title: `${title} cumulative`.trim(),
color: colors.cyan,
unit,
defaultActive: false,
},
{
metric: pattern.average,
title: `${title} avg`.trim(),
color: colors.purple,
unit,
defaultActive: false,
},
{
metric: pattern.min,
title: `${title} min`.trim(),
color: colors.red,
unit,
defaultActive: false,
},
{
metric: pattern.max,
title: `${title} max`.trim(),
color: colors.green,
unit,
defaultActive: false,
},
{
metric: pattern.pct10,
title: `${title} pct10`.trim(),
color: colors.rose,
unit,
defaultActive: false,
},
{
metric: pattern.pct25,
title: `${title} pct25`.trim(),
color: colors.pink,
unit,
defaultActive: false,
},
{
metric: pattern.median,
title: `${title} median`.trim(),
color: colors.violet,
unit,
defaultActive: false,
},
{
metric: pattern.pct75,
title: `${title} pct75`.trim(),
color: colors.fuchsia,
unit,
defaultActive: false,
},
{
metric: pattern.pct90,
title: `${title} pct90`.trim(),
color: colors.amber,
unit,
defaultActive: false,
@@ -438,58 +554,58 @@ export function fromFullnessPattern(colors, pattern, title, unit) {
* Create series from a FeeRatePattern ({ average, min, max, percentiles })
* @param {Colors} colors
* @param {FeeRatePattern<any>} pattern
* @param {string} title
* @param {Unit} unit
* @param {string} [title]
* @returns {AnyFetchedSeriesBlueprint[]}
*/
export function fromFeeRatePattern(colors, pattern, title, unit) {
export function fromFeeRatePattern(colors, pattern, unit, title = "") {
return [
{ metric: pattern.average, title: `${title} avg`, unit },
{ metric: pattern.average, title: `${title} avg`.trim(), unit },
{
metric: pattern.min,
title: `${title} min`,
title: `${title} min`.trim(),
color: colors.red,
unit,
defaultActive: false,
},
{
metric: pattern.max,
title: `${title} max`,
title: `${title} max`.trim(),
color: colors.green,
unit,
defaultActive: false,
},
{
metric: pattern.pct10,
title: `${title} pct10`,
title: `${title} pct10`.trim(),
color: colors.rose,
unit,
defaultActive: false,
},
{
metric: pattern.pct25,
title: `${title} pct25`,
title: `${title} pct25`.trim(),
color: colors.pink,
unit,
defaultActive: false,
},
{
metric: pattern.median,
title: `${title} median`,
title: `${title} median`.trim(),
color: colors.purple,
unit,
defaultActive: false,
},
{
metric: pattern.pct75,
title: `${title} pct75`,
title: `${title} pct75`.trim(),
color: colors.violet,
unit,
defaultActive: false,
},
{
metric: pattern.pct90,
title: `${title} pct90`,
title: `${title} pct90`.trim(),
color: colors.fuchsia,
unit,
defaultActive: false,
@@ -506,9 +622,9 @@ export function fromFeeRatePattern(colors, pattern, title, unit) {
*/
export function fromCoinbasePattern(colors, pattern, title) {
return [
...fromFullnessPattern(colors, pattern.sats, title, Unit.sats),
...fromFullnessPattern(colors, pattern.bitcoin, title, Unit.btc),
...fromFullnessPattern(colors, pattern.dollars, title, Unit.usd),
...fromFullnessPattern(colors, pattern.sats, Unit.sats, title),
...fromFullnessPattern(colors, pattern.bitcoin, Unit.btc, title),
...fromFullnessPattern(colors, pattern.dollars, Unit.usd, title),
];
}
@@ -610,8 +726,8 @@ export function fromBitcoinPatternWithUnit(
* Create sum/cumulative series from a BlockCountPattern with explicit unit and colors
* @param {Colors} colors
* @param {BlockCountPattern<any>} pattern
* @param {string} title
* @param {Unit} unit
* @param {string} [title]
* @param {Color} [sumColor]
* @param {Color} [cumulativeColor]
* @returns {AnyFetchedSeriesBlueprint[]}
@@ -619,21 +735,21 @@ export function fromBitcoinPatternWithUnit(
export function fromBlockCountWithUnit(
colors,
pattern,
title,
unit,
title,
sumColor,
cumulativeColor,
) {
return [
{
metric: pattern.sum,
title: `${title} sum`,
title: `${title} sum`.trim(),
color: sumColor,
unit,
},
{
metric: pattern.cumulative,
title: `${title} cumulative`,
title: `${title} cumulative`.trim(),
color: cumulativeColor ?? colors.blue,
unit,
defaultActive: false,
@@ -645,66 +761,66 @@ export function fromBlockCountWithUnit(
* Create series from an IntervalPattern (base + average/min/max/median/percentiles, no sum/cumulative)
* @param {Colors} colors
* @param {IntervalPattern} pattern
* @param {string} title
* @param {Unit} unit
* @param {string} [title]
* @param {Color} [color]
* @returns {AnyFetchedSeriesBlueprint[]}
*/
export function fromIntervalPattern(colors, pattern, title, unit, color) {
export function fromIntervalPattern(colors, pattern, unit, title = "", color) {
return [
{ metric: pattern.base, title, color, unit },
{ metric: pattern.base, title: title ?? "base", color, unit },
{
metric: pattern.average,
title: `${title} avg`,
color: colors.purple,
title: `${title} avg`.trim(),
color: colors.cyan,
unit,
defaultActive: false,
},
{
metric: pattern.min,
title: `${title} min`,
title: `${title} min`.trim(),
color: colors.red,
unit,
defaultActive: false,
},
{
metric: pattern.max,
title: `${title} max`,
title: `${title} max`.trim(),
color: colors.green,
unit,
defaultActive: false,
},
{
metric: pattern.median,
title: `${title} median`,
title: `${title} median`.trim(),
color: colors.violet,
unit,
defaultActive: false,
},
{
metric: pattern.pct10,
title: `${title} pct10`,
title: `${title} pct10`.trim(),
color: colors.rose,
unit,
defaultActive: false,
},
{
metric: pattern.pct25,
title: `${title} pct25`,
title: `${title} pct25`.trim(),
color: colors.pink,
unit,
defaultActive: false,
},
{
metric: pattern.pct75,
title: `${title} pct75`,
title: `${title} pct75`.trim(),
color: colors.fuchsia,
unit,
defaultActive: false,
},
{
metric: pattern.pct90,
title: `${title} pct90`,
title: `${title} pct90`.trim(),
color: colors.amber,
unit,
defaultActive: false,

View File

@@ -15,21 +15,23 @@ export function satsBtcUsd(pattern, name, color, options) {
const { defaultActive } = options || {};
return [
line({ metric: pattern.sats, name, color, unit: Unit.sats, defaultActive }),
line({ metric: pattern.bitcoin, name, color, unit: Unit.btc, defaultActive }),
line({ metric: pattern.dollars, name, color, unit: Unit.usd, defaultActive }),
line({
metric: pattern.bitcoin,
name,
color,
unit: Unit.btc,
defaultActive,
}),
line({
metric: pattern.dollars,
name,
color,
unit: Unit.usd,
defaultActive,
}),
];
}
/**
* Create multiple price lines with the same unit
* @param {PartialContext} ctx
* @param {Unit} unit
* @param {number[]} numbers
*/
export function priceLines(ctx, unit, numbers) {
return numbers.map((n) => ctx.createPriceLine({ unit, number: n }));
}
/**
* Build percentile USD mappings from a ratio pattern
* @param {Colors} colors
@@ -37,11 +39,11 @@ export function priceLines(ctx, unit, numbers) {
*/
export function percentileUsdMap(colors, ratio) {
return /** @type {const} */ ([
{ name: "pct99", prop: ratio.ratioPct99Usd, color: colors.rose },
{ name: "pct98", prop: ratio.ratioPct98Usd, color: colors.pink },
{ name: "pct95", prop: ratio.ratioPct95Usd, color: colors.fuchsia },
{ name: "pct5", prop: ratio.ratioPct5Usd, color: colors.cyan },
{ name: "pct98", prop: ratio.ratioPct98Usd, color: colors.pink },
{ name: "pct2", prop: ratio.ratioPct2Usd, color: colors.sky },
{ name: "pct99", prop: ratio.ratioPct99Usd, color: colors.rose },
{ name: "pct1", prop: ratio.ratioPct1Usd, color: colors.blue },
]);
}
@@ -53,11 +55,11 @@ export function percentileUsdMap(colors, ratio) {
*/
export function percentileMap(colors, ratio) {
return /** @type {const} */ ([
{ name: "pct99", prop: ratio.ratioPct99, color: colors.rose },
{ name: "pct98", prop: ratio.ratioPct98, color: colors.pink },
{ name: "pct95", prop: ratio.ratioPct95, color: colors.fuchsia },
{ name: "pct5", prop: ratio.ratioPct5, color: colors.cyan },
{ name: "pct98", prop: ratio.ratioPct98, color: colors.pink },
{ name: "pct2", prop: ratio.ratioPct2, color: colors.sky },
{ name: "pct99", prop: ratio.ratioPct99, color: colors.rose },
{ name: "pct1", prop: ratio.ratioPct1, color: colors.blue },
]);
}
@@ -84,16 +86,16 @@ export function sdBands(colors, sd) {
return /** @type {const} */ ([
{ name: "0σ", prop: sd._0sdUsd, color: colors.lime },
{ name: "+0.5σ", prop: sd.p05sdUsd, color: colors.yellow },
{ name: "+1σ", prop: sd.p1sdUsd, color: colors.amber },
{ name: "+1.5σ", prop: sd.p15sdUsd, color: colors.orange },
{ name: "+2σ", prop: sd.p2sdUsd, color: colors.red },
{ name: "+2.5σ", prop: sd.p25sdUsd, color: colors.rose },
{ name: "+3σ", prop: sd.p3sd, color: colors.pink },
{ name: "0.5σ", prop: sd.m05sdUsd, color: colors.teal },
{ name: "+1σ", prop: sd.p1sdUsd, color: colors.amber },
{ name: "1σ", prop: sd.m1sdUsd, color: colors.cyan },
{ name: "+1.5σ", prop: sd.p15sdUsd, color: colors.orange },
{ name: "1.5σ", prop: sd.m15sdUsd, color: colors.sky },
{ name: "+2σ", prop: sd.p2sdUsd, color: colors.red },
{ name: "2σ", prop: sd.m2sdUsd, color: colors.blue },
{ name: "+2.5σ", prop: sd.p25sdUsd, color: colors.rose },
{ name: "2.5σ", prop: sd.m25sdUsd, color: colors.indigo },
{ name: "+3σ", prop: sd.p3sd, color: colors.pink },
{ name: "3σ", prop: sd.m3sd, color: colors.violet },
]);
}

View File

@@ -1,6 +1,7 @@
/**
* @typedef {Object} BaseSeriesBlueprint
* @property {string} title
* @property {string} [key] - Optional key for persistence (derived from title if not provided)
* @property {boolean} [defaultActive]
*
* @typedef {Object} BaselineSeriesBlueprintSpecific
@@ -239,18 +240,16 @@
* @property {(pattern: BlockCountPattern<any>, title: string, color?: Color) => AnyFetchedSeriesBlueprint[]} fromBlockCount
* @property {(pattern: FullnessPattern<any>, title: string, color?: Color) => AnyFetchedSeriesBlueprint[]} fromBitcoin
* @property {(pattern: AnyStatsPattern, title: string, color?: Color) => AnyFetchedSeriesBlueprint[]} fromBlockSize
* @property {(pattern: AnyStatsPattern, title: string, unit: Unit) => AnyFetchedSeriesBlueprint[]} fromSizePattern
* @property {(pattern: FullnessPattern<any>, title: string, unit: Unit) => AnyFetchedSeriesBlueprint[]} fromFullnessPattern
* @property {(pattern: FeeRatePattern<any>, title: string, unit: Unit) => AnyFetchedSeriesBlueprint[]} fromFeeRatePattern
* @property {(pattern: AnyStatsPattern, unit: Unit, title?: string) => AnyFetchedSeriesBlueprint[]} fromSizePattern
* @property {(pattern: FullnessPattern<any>, unit: Unit, title?: string) => AnyFetchedSeriesBlueprint[]} fromFullnessPattern
* @property {(pattern: DollarsPattern<any>, unit: Unit, title?: string) => AnyFetchedSeriesBlueprint[]} fromDollarsPattern
* @property {(pattern: FeeRatePattern<any>, unit: Unit, title?: string) => AnyFetchedSeriesBlueprint[]} fromFeeRatePattern
* @property {(pattern: CoinbasePattern, title: string) => AnyFetchedSeriesBlueprint[]} fromCoinbasePattern
* @property {(pattern: ValuePattern, title: string, sumColor?: Color, cumulativeColor?: Color) => AnyFetchedSeriesBlueprint[]} fromValuePattern
* @property {(pattern: { sum: AnyMetricPattern, cumulative: AnyMetricPattern }, title: string, unit: Unit, sumColor?: Color, cumulativeColor?: Color) => AnyFetchedSeriesBlueprint[]} fromBitcoinPatternWithUnit
* @property {(pattern: BlockCountPattern<any>, title: string, unit: Unit, sumColor?: Color, cumulativeColor?: Color) => AnyFetchedSeriesBlueprint[]} fromBlockCountWithUnit
* @property {(pattern: IntervalPattern, title: string, unit: Unit, color?: Color) => AnyFetchedSeriesBlueprint[]} fromIntervalPattern
* @property {(pattern: BlockCountPattern<any>, unit: Unit, title?: string, sumColor?: Color, cumulativeColor?: Color) => AnyFetchedSeriesBlueprint[]} fromBlockCountWithUnit
* @property {(pattern: IntervalPattern, unit: Unit, title?: string, color?: Color) => AnyFetchedSeriesBlueprint[]} fromIntervalPattern
* @property {(pattern: SupplyPattern, title: string, color?: Color) => AnyFetchedSeriesBlueprint[]} fromSupplyPattern
* @property {(args: { number?: number, name?: string, defaultActive?: boolean, lineStyle?: LineStyle, color?: Color, unit: Unit }) => FetchedLineSeriesBlueprint} createPriceLine
* @property {(args: { numbers: number[], unit: Unit }) => FetchedLineSeriesBlueprint[]} createPriceLines
* @property {(args: { constant: AnyMetricPattern, name: string, unit: Unit, color?: Color, lineStyle?: number, defaultActive?: boolean }) => FetchedLineSeriesBlueprint} constantLine
*/
// Re-export for type consumers

View File

@@ -1,21 +1,22 @@
/** Track unused metrics (dev only) */
import { localhost } from "../utils/env.js";
/** @type {Set<AnyMetricPattern> | null} */
export const unused = localhost ? new Set() : null;
/** @type {Map<AnyMetricPattern, string[]> | null} */
export const unused = localhost ? new Map() : null;
/**
* Walk and collect AnyMetricPatterns
* @param {TreeNode | null | undefined} node
* @param {Set<AnyMetricPattern>} set
* @param {Map<AnyMetricPattern, string[]>} map
* @param {string[]} path
*/
function walk(node, set) {
function walk(node, map, path) {
if (node && "by" in node) {
set.add(/** @type {AnyMetricPattern} */ (node));
map.set(/** @type {AnyMetricPattern} */ (node), path);
} else if (node && typeof node === "object") {
for (const value of Object.values(node)) {
walk(/** @type {TreeNode | null | undefined} */ (value), set);
for (const [key, value] of Object.entries(node)) {
walk(/** @type {TreeNode | null | undefined} */ (value), map, [
...path,
key,
]);
}
}
}
@@ -25,7 +26,7 @@ function walk(node, set) {
* @param {TreeNode} tree
*/
export function collect(tree) {
if (unused) walk(tree, unused);
if (unused) walk(tree, unused, []);
}
/**
@@ -39,6 +40,22 @@ export function markUsed(metric) {
/** Log unused metrics to console */
export function logUnused() {
if (!unused?.size) return;
const paths = [...unused].map((m) => Object.values(m.by)[0].path);
console.warn("Unused metrics:", paths);
/** @type {Record<string, any>} */
const tree = {};
for (const path of unused.values()) {
let current = tree;
for (let i = 0; i < path.length; i++) {
const part = path[i];
if (i === path.length - 1) {
current[part] = null;
} else {
current[part] = current[part] || {};
current = current[part];
}
}
}
console.log("Unused metrics:", { count: unused.size, tree });
}