mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-04-28 16:49:58 -07:00
global: snapshot part 4
This commit is contained in:
@@ -5,9 +5,9 @@ import {
|
||||
dots,
|
||||
line,
|
||||
price,
|
||||
sumsArray,
|
||||
multiSeriesTree,
|
||||
percentRatioDots,
|
||||
sumsAndAveragesCumulative,
|
||||
} from "./series.js";
|
||||
import { satsBtcUsd, priceRatioPercentilesTree } from "./shared.js";
|
||||
|
||||
@@ -277,50 +277,27 @@ export function createCointimeSection() {
|
||||
{
|
||||
name: "Coinblocks",
|
||||
tree: [
|
||||
{
|
||||
name: "Compare",
|
||||
tree: multiSeriesTree({
|
||||
entries: coinblocks.map(({ pattern, name, color }) => ({
|
||||
name,
|
||||
color,
|
||||
base: pattern.base,
|
||||
average: pattern.average,
|
||||
rolling: pattern.sum,
|
||||
cumulative: pattern.cumulative,
|
||||
})),
|
||||
title: "Coinblocks",
|
||||
unit: Unit.coinblocks,
|
||||
}),
|
||||
},
|
||||
...multiSeriesTree({
|
||||
entries: coinblocks.map(({ pattern, name, color }) => ({
|
||||
name,
|
||||
color,
|
||||
average: pattern.average,
|
||||
sum: pattern.sum,
|
||||
cumulative: pattern.cumulative,
|
||||
})),
|
||||
title: "Coinblocks",
|
||||
unit: Unit.coinblocks,
|
||||
}),
|
||||
...coinblocks.map(({ pattern, name, title, color }) => ({
|
||||
name,
|
||||
tree: [
|
||||
{
|
||||
name: "Per Block",
|
||||
title,
|
||||
bottom: [
|
||||
line({
|
||||
series: pattern.base,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.coinblocks,
|
||||
}),
|
||||
],
|
||||
},
|
||||
...sumsArray({ windows: pattern.sum, title, unit: Unit.coinblocks }),
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: `${title} (Total)`,
|
||||
bottom: [
|
||||
line({
|
||||
series: pattern.cumulative,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.coinblocks,
|
||||
}),
|
||||
],
|
||||
},
|
||||
],
|
||||
tree: sumsAndAveragesCumulative({
|
||||
sum: pattern.sum,
|
||||
average: pattern.average,
|
||||
cumulative: pattern.cumulative,
|
||||
title,
|
||||
unit: Unit.coinblocks,
|
||||
color,
|
||||
}),
|
||||
})),
|
||||
],
|
||||
},
|
||||
@@ -328,95 +305,47 @@ export function createCointimeSection() {
|
||||
{
|
||||
name: "Value",
|
||||
tree: [
|
||||
{
|
||||
name: "Compare",
|
||||
tree: multiSeriesTree({
|
||||
entries: [
|
||||
...cointimeValues.map(({ pattern, name, color }) => ({
|
||||
name,
|
||||
color,
|
||||
base: pattern.base,
|
||||
average: pattern.average,
|
||||
rolling: pattern.sum,
|
||||
cumulative: pattern.cumulative,
|
||||
})),
|
||||
{
|
||||
name: vocdd.name,
|
||||
color: vocdd.color,
|
||||
base: vocdd.pattern.base,
|
||||
average: vocdd.pattern.average,
|
||||
rolling: vocdd.pattern.sum,
|
||||
cumulative: vocdd.pattern.cumulative,
|
||||
},
|
||||
],
|
||||
title: "Cointime Value",
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
},
|
||||
...cointimeValues.map(({ pattern, name, title, color }) => ({
|
||||
name,
|
||||
tree: [
|
||||
...multiSeriesTree({
|
||||
entries: [
|
||||
...cointimeValues.map(({ pattern, name, color }) => ({
|
||||
name,
|
||||
color,
|
||||
average: pattern.average,
|
||||
sum: pattern.sum,
|
||||
cumulative: pattern.cumulative,
|
||||
})),
|
||||
{
|
||||
name: "Per Block",
|
||||
title,
|
||||
bottom: [
|
||||
line({ series: pattern.base, name, color, unit: Unit.usd }),
|
||||
],
|
||||
},
|
||||
...sumsArray({ windows: pattern.sum, title, unit: Unit.usd }),
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: `${title} (Total)`,
|
||||
bottom: [
|
||||
line({
|
||||
series: pattern.cumulative,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
],
|
||||
name: vocdd.name,
|
||||
color: vocdd.color,
|
||||
average: vocdd.pattern.average,
|
||||
sum: vocdd.pattern.sum,
|
||||
cumulative: vocdd.pattern.cumulative,
|
||||
},
|
||||
],
|
||||
title: "Cointime Value",
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
...cointimeValues.map(({ pattern, name, title, color }) => ({
|
||||
name,
|
||||
tree: sumsAndAveragesCumulative({
|
||||
sum: pattern.sum,
|
||||
average: pattern.average,
|
||||
cumulative: pattern.cumulative,
|
||||
title,
|
||||
unit: Unit.usd,
|
||||
color,
|
||||
}),
|
||||
})),
|
||||
{
|
||||
name: vocdd.name,
|
||||
tree: [
|
||||
{
|
||||
name: "Per Block",
|
||||
title: vocdd.title,
|
||||
bottom: [
|
||||
line({
|
||||
series: vocdd.pattern.base,
|
||||
name: vocdd.name,
|
||||
color: vocdd.color,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
line({
|
||||
series: reserveRisk.vocddMedian1y,
|
||||
name: "365d Median",
|
||||
color: colors.time._1y,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
],
|
||||
},
|
||||
...sumsArray({
|
||||
windows: vocdd.pattern.sum,
|
||||
title: vocdd.title,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: `${vocdd.title} (Total)`,
|
||||
bottom: [
|
||||
line({
|
||||
series: vocdd.pattern.cumulative,
|
||||
name: vocdd.name,
|
||||
color: vocdd.color,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
],
|
||||
},
|
||||
],
|
||||
tree: sumsAndAveragesCumulative({
|
||||
sum: vocdd.pattern.sum,
|
||||
average: vocdd.pattern.average,
|
||||
cumulative: vocdd.pattern.cumulative,
|
||||
title: vocdd.title,
|
||||
unit: Unit.usd,
|
||||
color: vocdd.color,
|
||||
}),
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -9,7 +9,15 @@
|
||||
*/
|
||||
|
||||
import { Unit } from "../../utils/units.js";
|
||||
import { line, baseline, dotsBaseline, percentRatio, chartsFromCount, averagesTree, ROLLING_WINDOWS } from "../series.js";
|
||||
import {
|
||||
line,
|
||||
baseline,
|
||||
dotsBaseline,
|
||||
percentRatio,
|
||||
chartsFromCount,
|
||||
averagesArray,
|
||||
ROLLING_WINDOWS,
|
||||
} from "../series.js";
|
||||
import {
|
||||
satsBtcUsdFullTree,
|
||||
mapCohortsWithAll,
|
||||
@@ -33,7 +41,6 @@ function volumeAndCoinsTree(activity, color, title) {
|
||||
name: "Volume",
|
||||
tree: satsBtcUsdFullTree({
|
||||
pattern: activity.transferVolume,
|
||||
name: "Volume",
|
||||
title: title("Sent Volume"),
|
||||
color,
|
||||
}),
|
||||
@@ -62,7 +69,6 @@ function sentProfitLossTree(sent, title) {
|
||||
name: "Sent In Profit",
|
||||
tree: satsBtcUsdFullTree({
|
||||
pattern: sent.inProfit,
|
||||
name: "In Profit",
|
||||
title: title("Sent Volume In Profit"),
|
||||
color: colors.profit,
|
||||
}),
|
||||
@@ -71,7 +77,6 @@ function sentProfitLossTree(sent, title) {
|
||||
name: "Sent In Loss",
|
||||
tree: satsBtcUsdFullTree({
|
||||
pattern: sent.inLoss,
|
||||
name: "In Loss",
|
||||
title: title("Sent Volume In Loss"),
|
||||
color: colors.loss,
|
||||
}),
|
||||
@@ -90,7 +95,14 @@ function fullVolumeTree(activity, color, title) {
|
||||
return [
|
||||
...volumeAndCoinsTree(activity, color, title),
|
||||
...sentProfitLossTree(activity.transferVolume, title),
|
||||
averagesTree({ windows: activity.dormancy, title: title("Dormancy"), unit: Unit.days, name: "Dormancy" }),
|
||||
{
|
||||
name: "Dormancy",
|
||||
tree: averagesArray({
|
||||
windows: activity.dormancy,
|
||||
title: title("Dormancy"),
|
||||
unit: Unit.days,
|
||||
}),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@@ -110,13 +122,26 @@ function singleRollingSoprTree(ratio, title, prefix = "") {
|
||||
name: "Compare",
|
||||
title: title(`${prefix}SOPR`),
|
||||
bottom: ROLLING_WINDOWS.map((w) =>
|
||||
baseline({ series: ratio[w.key], name: w.name, color: w.color, unit: Unit.ratio, base: 1 }),
|
||||
baseline({
|
||||
series: ratio[w.key],
|
||||
name: w.name,
|
||||
color: w.color,
|
||||
unit: Unit.ratio,
|
||||
base: 1,
|
||||
}),
|
||||
),
|
||||
},
|
||||
...ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: title(`${prefix}SOPR (${w.title})`),
|
||||
bottom: [baseline({ series: ratio[w.key], name: "SOPR", unit: Unit.ratio, base: 1 })],
|
||||
bottom: [
|
||||
baseline({
|
||||
series: ratio[w.key],
|
||||
name: "SOPR",
|
||||
unit: Unit.ratio,
|
||||
base: 1,
|
||||
}),
|
||||
],
|
||||
})),
|
||||
];
|
||||
}
|
||||
@@ -136,7 +161,11 @@ function singleSellSideRiskTree(sellSideRisk, title) {
|
||||
name: "Compare",
|
||||
title: title("Sell Side Risk"),
|
||||
bottom: ROLLING_WINDOWS.flatMap((w) =>
|
||||
percentRatio({ pattern: sellSideRisk[w.key], name: w.name, color: w.color }),
|
||||
percentRatio({
|
||||
pattern: sellSideRisk[w.key],
|
||||
name: w.name,
|
||||
color: w.color,
|
||||
}),
|
||||
),
|
||||
},
|
||||
...ROLLING_WINDOWS.map((w) => ({
|
||||
@@ -171,11 +200,18 @@ export function createActivitySectionWithAdjusted({ cohort, title }) {
|
||||
...singleRollingSoprTree(sopr.ratio, title),
|
||||
{
|
||||
name: "Adjusted",
|
||||
tree: singleRollingSoprTree(sopr.adjusted.ratio, title, "Adjusted "),
|
||||
tree: singleRollingSoprTree(
|
||||
sopr.adjusted.ratio,
|
||||
title,
|
||||
"Adjusted ",
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
{ name: "Sell Side Risk", tree: singleSellSideRiskTree(r.sellSideRiskRatio, title) },
|
||||
{
|
||||
name: "Sell Side Risk",
|
||||
tree: singleSellSideRiskTree(r.sellSideRiskRatio, title),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
@@ -198,7 +234,10 @@ export function createActivitySection({ cohort, title }) {
|
||||
name: "SOPR",
|
||||
tree: singleRollingSoprTree(sopr.ratio, title),
|
||||
},
|
||||
{ name: "Sell Side Risk", tree: singleSellSideRiskTree(r.sellSideRiskRatio, title) },
|
||||
{
|
||||
name: "Sell Side Risk",
|
||||
tree: singleSellSideRiskTree(r.sellSideRiskRatio, title),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
@@ -220,13 +259,19 @@ export function createActivitySectionWithActivity({ cohort, title }) {
|
||||
{
|
||||
name: "SOPR",
|
||||
title: title("SOPR (24h)"),
|
||||
bottom: [dotsBaseline({ series: sopr.ratio._24h, name: "SOPR", unit: Unit.ratio, base: 1 })],
|
||||
bottom: [
|
||||
dotsBaseline({
|
||||
series: sopr.ratio._24h,
|
||||
name: "SOPR",
|
||||
unit: Unit.ratio,
|
||||
base: 1,
|
||||
}),
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Minimal activity section: volume only
|
||||
* @param {{ cohort: CohortBasicWithMarketCap | CohortBasicWithoutMarketCap | CohortWithoutRelative | CohortAddr | AddrCohortObject, title: (name: string) => string }} args
|
||||
@@ -237,7 +282,6 @@ export function createActivitySectionMinimal({ cohort, title }) {
|
||||
name: "Activity",
|
||||
tree: satsBtcUsdFullTree({
|
||||
pattern: cohort.tree.activity.transferVolume,
|
||||
name: "Volume",
|
||||
title: title("Volume"),
|
||||
}),
|
||||
};
|
||||
@@ -255,7 +299,12 @@ export function createGroupedActivitySectionMinimal({ list, all, title }) {
|
||||
name: w.name,
|
||||
title: title(`Volume (${w.title})`),
|
||||
bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
line({ series: tree.activity.transferVolume.sum[w.key].sats, name, color, unit: Unit.sats }),
|
||||
line({
|
||||
series: tree.activity.transferVolume.sum[w.key].sats,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.sats,
|
||||
}),
|
||||
),
|
||||
})),
|
||||
};
|
||||
@@ -280,7 +329,13 @@ function groupedSoprCharts(list, all, getRatio, title, prefix = "") {
|
||||
name: w.name,
|
||||
title: title(`${prefix}SOPR (${w.title})`),
|
||||
bottom: mapCohortsWithAll(list, all, (c) =>
|
||||
baseline({ series: getRatio(c)[w.key], name: c.name, color: c.color, unit: Unit.ratio, base: 1 }),
|
||||
baseline({
|
||||
series: getRatio(c)[w.key],
|
||||
name: c.name,
|
||||
color: c.color,
|
||||
unit: Unit.ratio,
|
||||
base: 1,
|
||||
}),
|
||||
),
|
||||
}));
|
||||
}
|
||||
@@ -300,7 +355,6 @@ function groupedSoprCharts(list, all, getRatio, title, prefix = "") {
|
||||
* @returns {PartialOptionsTree}
|
||||
*/
|
||||
|
||||
|
||||
// ============================================================================
|
||||
// Grouped Activity Sections
|
||||
// ============================================================================
|
||||
@@ -317,16 +371,32 @@ export function createGroupedActivitySectionWithAdjusted({ list, all, title }) {
|
||||
name: "Volume",
|
||||
title: title("Sent Volume"),
|
||||
bottom: flatMapCohortsWithAll(list, all, ({ name, color, tree }) => [
|
||||
line({ series: tree.activity.transferVolume.sum._24h.sats, name, color, unit: Unit.sats }),
|
||||
line({
|
||||
series: tree.activity.transferVolume.sum._24h.sats,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.sats,
|
||||
}),
|
||||
]),
|
||||
},
|
||||
{
|
||||
name: "SOPR",
|
||||
tree: [
|
||||
...groupedSoprCharts(list, all, (c) => c.tree.realized.sopr.ratio, title),
|
||||
...groupedSoprCharts(
|
||||
list,
|
||||
all,
|
||||
(c) => c.tree.realized.sopr.ratio,
|
||||
title,
|
||||
),
|
||||
{
|
||||
name: "Adjusted",
|
||||
tree: groupedSoprCharts(list, all, (c) => c.tree.realized.sopr.adjusted.ratio, title, "Adjusted "),
|
||||
tree: groupedSoprCharts(
|
||||
list,
|
||||
all,
|
||||
(c) => c.tree.realized.sopr.adjusted.ratio,
|
||||
title,
|
||||
"Adjusted ",
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -336,7 +406,12 @@ export function createGroupedActivitySectionWithAdjusted({ list, all, title }) {
|
||||
name: w.name,
|
||||
title: title(`Sell Side Risk (${w.title})`),
|
||||
bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
line({ series: tree.realized.sellSideRiskRatio[w.key].ratio, name, color, unit: Unit.ratio }),
|
||||
line({
|
||||
series: tree.realized.sellSideRiskRatio[w.key].ratio,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.ratio,
|
||||
}),
|
||||
),
|
||||
})),
|
||||
},
|
||||
@@ -344,7 +419,12 @@ export function createGroupedActivitySectionWithAdjusted({ list, all, title }) {
|
||||
name: "Coindays Destroyed",
|
||||
title: title("Coindays Destroyed"),
|
||||
bottom: flatMapCohortsWithAll(list, all, ({ name, color, tree }) => [
|
||||
line({ series: tree.activity.coindaysDestroyed.sum._24h, name, color, unit: Unit.coindays }),
|
||||
line({
|
||||
series: tree.activity.coindaysDestroyed.sum._24h,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.coindays,
|
||||
}),
|
||||
]),
|
||||
},
|
||||
],
|
||||
@@ -364,13 +444,23 @@ export function createGroupedActivitySection({ list, all, title }) {
|
||||
name: "Volume",
|
||||
title: title("Sent Volume"),
|
||||
bottom: flatMapCohortsWithAll(list, all, ({ name, color, tree }) => [
|
||||
line({ series: tree.activity.transferVolume.sum._24h.sats, name, color, unit: Unit.sats }),
|
||||
line({
|
||||
series: tree.activity.transferVolume.sum._24h.sats,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.sats,
|
||||
}),
|
||||
]),
|
||||
},
|
||||
{
|
||||
name: "SOPR",
|
||||
tree: [
|
||||
...groupedSoprCharts(list, all, (c) => c.tree.realized.sopr.ratio, title),
|
||||
...groupedSoprCharts(
|
||||
list,
|
||||
all,
|
||||
(c) => c.tree.realized.sopr.ratio,
|
||||
title,
|
||||
),
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -379,7 +469,12 @@ export function createGroupedActivitySection({ list, all, title }) {
|
||||
name: w.name,
|
||||
title: title(`Sell Side Risk (${w.title})`),
|
||||
bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
line({ series: tree.realized.sellSideRiskRatio[w.key].ratio, name, color, unit: Unit.ratio }),
|
||||
line({
|
||||
series: tree.realized.sellSideRiskRatio[w.key].ratio,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.ratio,
|
||||
}),
|
||||
),
|
||||
})),
|
||||
},
|
||||
@@ -387,7 +482,12 @@ export function createGroupedActivitySection({ list, all, title }) {
|
||||
name: "Coindays Destroyed",
|
||||
title: title("Coindays Destroyed"),
|
||||
bottom: flatMapCohortsWithAll(list, all, ({ name, color, tree }) => [
|
||||
line({ series: tree.activity.coindaysDestroyed.sum._24h, name, color, unit: Unit.coindays }),
|
||||
line({
|
||||
series: tree.activity.coindaysDestroyed.sum._24h,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.coindays,
|
||||
}),
|
||||
]),
|
||||
},
|
||||
],
|
||||
@@ -407,24 +507,39 @@ export function createGroupedActivitySectionWithActivity({ list, all, title }) {
|
||||
name: "Volume",
|
||||
title: title("Sent Volume"),
|
||||
bottom: flatMapCohortsWithAll(list, all, ({ name, color, tree }) => [
|
||||
line({ series: tree.activity.transferVolume.sum._24h.sats, name, color, unit: Unit.sats }),
|
||||
line({
|
||||
series: tree.activity.transferVolume.sum._24h.sats,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.sats,
|
||||
}),
|
||||
]),
|
||||
},
|
||||
{
|
||||
name: "SOPR",
|
||||
title: title("SOPR (24h)"),
|
||||
bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
baseline({ series: tree.realized.sopr.ratio._24h, name, color, unit: Unit.ratio, base: 1 }),
|
||||
baseline({
|
||||
series: tree.realized.sopr.ratio._24h,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.ratio,
|
||||
base: 1,
|
||||
}),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "Coindays Destroyed",
|
||||
title: title("Coindays Destroyed"),
|
||||
bottom: flatMapCohortsWithAll(list, all, ({ name, color, tree }) => [
|
||||
line({ series: tree.activity.coindaysDestroyed.sum._24h, name, color, unit: Unit.coindays }),
|
||||
line({
|
||||
series: tree.activity.coindaysDestroyed.sum._24h,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.coindays,
|
||||
}),
|
||||
]),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -93,7 +93,7 @@ export function createCostBasisSectionWithPercentiles({ cohort, title }) {
|
||||
tree: singleWeightFolder({
|
||||
avgPrice: tree.realized.price, avgName: "Average",
|
||||
inProfit: cb.inProfit.perCoin, inLoss: cb.inLoss.perCoin,
|
||||
percentiles: cb.percentiles, color, weightLabel: "BTC-weighted", title,
|
||||
percentiles: cb.perCoin, color, weightLabel: "BTC-weighted", title,
|
||||
min: cb.min, max: cb.max,
|
||||
}),
|
||||
},
|
||||
@@ -102,7 +102,7 @@ export function createCostBasisSectionWithPercentiles({ cohort, title }) {
|
||||
tree: singleWeightFolder({
|
||||
avgPrice: tree.realized.investor.price, avgName: "Average",
|
||||
inProfit: cb.inProfit.perDollar, inLoss: cb.inLoss.perDollar,
|
||||
percentiles: cb.investedCapital, color, weightLabel: "USD-weighted", title,
|
||||
percentiles: cb.perDollar, color, weightLabel: "USD-weighted", title,
|
||||
}),
|
||||
},
|
||||
{
|
||||
@@ -215,7 +215,7 @@ export function createGroupedCostBasisSectionWithPercentiles({ list, all, title
|
||||
getAvgPrice: (c) => c.tree.realized.price,
|
||||
getInProfit: (c) => c.tree.costBasis.inProfit.perCoin,
|
||||
getInLoss: (c) => c.tree.costBasis.inLoss.perCoin,
|
||||
getPercentiles: (c) => c.tree.costBasis.percentiles,
|
||||
getPercentiles: (c) => c.tree.costBasis.perCoin,
|
||||
avgTitle: "Average", weightLabel: "BTC-weighted",
|
||||
}),
|
||||
},
|
||||
@@ -226,7 +226,7 @@ export function createGroupedCostBasisSectionWithPercentiles({ list, all, title
|
||||
getAvgPrice: (c) => c.tree.realized.investor.price,
|
||||
getInProfit: (c) => c.tree.costBasis.inProfit.perDollar,
|
||||
getInLoss: (c) => c.tree.costBasis.inLoss.perDollar,
|
||||
getPercentiles: (c) => c.tree.costBasis.investedCapital,
|
||||
getPercentiles: (c) => c.tree.costBasis.perDollar,
|
||||
avgTitle: "Average", weightLabel: "USD-weighted",
|
||||
}),
|
||||
},
|
||||
|
||||
@@ -10,8 +10,19 @@
|
||||
* - activity.js: SOPR, Volume, Lifespan
|
||||
*/
|
||||
|
||||
import { formatCohortTitle, satsBtcUsd, satsBtcUsdFullTree } from "../shared.js";
|
||||
import { ROLLING_WINDOWS, line, baseline, percentRatio, sumsTree, rollingPercentRatioTree } from "../series.js";
|
||||
import {
|
||||
formatCohortTitle,
|
||||
satsBtcUsd,
|
||||
satsBtcUsdFullTree,
|
||||
} from "../shared.js";
|
||||
import {
|
||||
ROLLING_WINDOWS,
|
||||
line,
|
||||
baseline,
|
||||
percentRatio,
|
||||
sumsTree,
|
||||
rollingPercentRatioTree,
|
||||
} from "../series.js";
|
||||
import { Unit } from "../../utils/units.js";
|
||||
import { colors } from "../../utils/colors.js";
|
||||
|
||||
@@ -210,7 +221,6 @@ export function createCohortFolderAgeRangeWithMatured(cohort) {
|
||||
name: "Matured",
|
||||
tree: satsBtcUsdFullTree({
|
||||
pattern: cohort.matured,
|
||||
name: cohort.name,
|
||||
title: title("Matured Supply"),
|
||||
}),
|
||||
});
|
||||
@@ -452,13 +462,22 @@ export function createGroupedCohortFolderAgeRangeWithMatured({
|
||||
list,
|
||||
all,
|
||||
}) {
|
||||
const folder = createGroupedCohortFolderAgeRange({ name, title: groupTitle, list, all });
|
||||
const folder = createGroupedCohortFolderAgeRange({
|
||||
name,
|
||||
title: groupTitle,
|
||||
list,
|
||||
all,
|
||||
});
|
||||
const title = formatCohortTitle(groupTitle);
|
||||
folder.tree.push({
|
||||
name: "Matured",
|
||||
title: title("Matured Supply"),
|
||||
bottom: list.flatMap((cohort) =>
|
||||
satsBtcUsd({ pattern: cohort.matured.base, name: cohort.name, color: cohort.color }),
|
||||
satsBtcUsd({
|
||||
pattern: cohort.matured.base,
|
||||
name: cohort.name,
|
||||
color: cohort.color,
|
||||
}),
|
||||
),
|
||||
});
|
||||
return folder;
|
||||
@@ -607,14 +626,32 @@ function singleBucketFolder({ name, color, pattern }) {
|
||||
title: `${name}: Supply`,
|
||||
bottom: [
|
||||
...satsBtcUsd({ pattern: pattern.supply.all, name: "Total" }),
|
||||
...satsBtcUsd({ pattern: pattern.supply.sth, name: "STH", color: colors.term.short }),
|
||||
...satsBtcUsd({
|
||||
pattern: pattern.supply.sth,
|
||||
name: "STH",
|
||||
color: colors.term.short,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Change",
|
||||
tree: [
|
||||
{ ...sumsTree({ windows: pattern.supply.all.delta.absolute, title: `${name}: Supply Change`, unit: Unit.sats, series: baseline }), name: "Absolute" },
|
||||
{ ...rollingPercentRatioTree({ windows: pattern.supply.all.delta.rate, title: `${name}: Supply Rate` }), name: "Rate" },
|
||||
{
|
||||
...sumsTree({
|
||||
windows: pattern.supply.all.delta.absolute,
|
||||
title: `${name}: Supply Change`,
|
||||
unit: Unit.sats,
|
||||
series: baseline,
|
||||
}),
|
||||
name: "Absolute",
|
||||
},
|
||||
{
|
||||
...rollingPercentRatioTree({
|
||||
windows: pattern.supply.all.delta.rate,
|
||||
title: `${name}: Supply Rate`,
|
||||
}),
|
||||
name: "Rate",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -623,14 +660,25 @@ function singleBucketFolder({ name, color, pattern }) {
|
||||
name: "Realized Cap",
|
||||
title: `${name}: Realized Cap`,
|
||||
bottom: [
|
||||
line({ series: pattern.realizedCap.all, name: "Total", unit: Unit.usd }),
|
||||
line({ series: pattern.realizedCap.sth, name: "STH", color: colors.term.short, unit: Unit.usd }),
|
||||
line({
|
||||
series: pattern.realizedCap.all,
|
||||
name: "Total",
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
line({
|
||||
series: pattern.realizedCap.sth,
|
||||
name: "STH",
|
||||
color: colors.term.short,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "NUPL",
|
||||
title: `${name}: NUPL`,
|
||||
bottom: [line({ series: pattern.nupl.ratio, name, color, unit: Unit.ratio })],
|
||||
bottom: [
|
||||
line({ series: pattern.nupl.ratio, name, color, unit: Unit.ratio }),
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -671,7 +719,12 @@ function groupedBucketCharts(list, titlePrefix) {
|
||||
title: `${titlePrefix}: Supply Change`,
|
||||
bottom: ROLLING_WINDOWS.flatMap((w) =>
|
||||
list.map(({ name, color, pattern }) =>
|
||||
baseline({ series: pattern.supply.all.delta.absolute[w.key], name: `${name} ${w.name}`, color, unit: Unit.sats }),
|
||||
baseline({
|
||||
series: pattern.supply.all.delta.absolute[w.key],
|
||||
name: `${name} ${w.name}`,
|
||||
color,
|
||||
unit: Unit.sats,
|
||||
}),
|
||||
),
|
||||
),
|
||||
},
|
||||
@@ -679,7 +732,12 @@ function groupedBucketCharts(list, titlePrefix) {
|
||||
name: w.name,
|
||||
title: `${titlePrefix}: Supply Change (${w.title})`,
|
||||
bottom: list.map(({ name, color, pattern }) =>
|
||||
baseline({ series: pattern.supply.all.delta.absolute[w.key], name, color, unit: Unit.sats }),
|
||||
baseline({
|
||||
series: pattern.supply.all.delta.absolute[w.key],
|
||||
name,
|
||||
color,
|
||||
unit: Unit.sats,
|
||||
}),
|
||||
),
|
||||
})),
|
||||
],
|
||||
@@ -692,7 +750,11 @@ function groupedBucketCharts(list, titlePrefix) {
|
||||
title: `${titlePrefix}: Supply Rate`,
|
||||
bottom: ROLLING_WINDOWS.flatMap((w) =>
|
||||
list.flatMap(({ name, color, pattern }) =>
|
||||
percentRatio({ pattern: pattern.supply.all.delta.rate[w.key], name: `${name} ${w.name}`, color }),
|
||||
percentRatio({
|
||||
pattern: pattern.supply.all.delta.rate[w.key],
|
||||
name: `${name} ${w.name}`,
|
||||
color,
|
||||
}),
|
||||
),
|
||||
),
|
||||
},
|
||||
@@ -700,7 +762,11 @@ function groupedBucketCharts(list, titlePrefix) {
|
||||
name: w.name,
|
||||
title: `${titlePrefix}: Supply Rate (${w.title})`,
|
||||
bottom: list.flatMap(({ name, color, pattern }) =>
|
||||
percentRatio({ pattern: pattern.supply.all.delta.rate[w.key], name, color }),
|
||||
percentRatio({
|
||||
pattern: pattern.supply.all.delta.rate[w.key],
|
||||
name,
|
||||
color,
|
||||
}),
|
||||
),
|
||||
})),
|
||||
],
|
||||
@@ -716,14 +782,24 @@ function groupedBucketCharts(list, titlePrefix) {
|
||||
name: "All",
|
||||
title: `${titlePrefix}: Realized Cap`,
|
||||
bottom: list.map(({ name, color, pattern }) =>
|
||||
line({ series: pattern.realizedCap.all, name, color, unit: Unit.usd }),
|
||||
line({
|
||||
series: pattern.realizedCap.all,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "STH",
|
||||
title: `${titlePrefix}: STH Realized Cap`,
|
||||
bottom: list.map(({ name, color, pattern }) =>
|
||||
line({ series: pattern.realizedCap.sth, name, color, unit: Unit.usd }),
|
||||
line({
|
||||
series: pattern.realizedCap.sth,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
),
|
||||
},
|
||||
],
|
||||
@@ -749,7 +825,10 @@ export function createUtxoProfitabilitySection({ range, profit, loss }) {
|
||||
{
|
||||
name: "Range",
|
||||
tree: [
|
||||
{ name: "Compare", tree: groupedBucketCharts(range, "Profitability Range") },
|
||||
{
|
||||
name: "Compare",
|
||||
tree: groupedBucketCharts(range, "Profitability Range"),
|
||||
},
|
||||
...range.map(singleBucketFolder),
|
||||
],
|
||||
},
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
*/
|
||||
|
||||
import { Unit } from "../../utils/units.js";
|
||||
import { ROLLING_WINDOWS, line, dotted, baseline, dots, dotsBaseline, percentRatio, percentRatioBaseline } from "../series.js";
|
||||
import { ROLLING_WINDOWS, line, dotted, baseline, percentRatio, percentRatioBaseline } from "../series.js";
|
||||
import { colors } from "../../utils/colors.js";
|
||||
import { priceLine } from "../constants.js";
|
||||
import {
|
||||
@@ -236,7 +236,7 @@ function nuplSeries(nupl) {
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Flat metric folder: Compare + windows + Cumulative + Per Block + optional % of Realized Cap
|
||||
* Flat metric folder: Compare + windows + Cumulative + optional % of Realized Cap
|
||||
* @param {Object} args
|
||||
* @param {{ sum: Record<string, { usd: AnySeriesPattern }>, cumulative: { usd: AnySeriesPattern }, base: { usd: AnySeriesPattern } }} args.pattern
|
||||
* @param {string} args.metricTitle
|
||||
@@ -264,11 +264,6 @@ function realizedMetricFolder({ pattern, metricTitle, color, title, toRcap }) {
|
||||
title: title(`Realized ${metricTitle} (Total)`),
|
||||
bottom: [line({ series: pattern.cumulative.usd, name: metricTitle, color, unit: Unit.usd })],
|
||||
},
|
||||
{
|
||||
name: "Per Block",
|
||||
title: title(`Realized ${metricTitle} per Block`),
|
||||
bottom: [dots({ series: pattern.base.usd, name: metricTitle, color, unit: Unit.usd })],
|
||||
},
|
||||
...(toRcap ? [{
|
||||
name: "% of Realized Cap",
|
||||
title: title(`Realized ${metricTitle} (% of Realized Cap)`),
|
||||
@@ -278,7 +273,7 @@ function realizedMetricFolder({ pattern, metricTitle, color, title, toRcap }) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Net P&L folder: Compare + windows + Cumulative + Per Block + optional % of Rcap + Change/
|
||||
* Net P&L folder: Compare + windows + Cumulative + optional % of Rcap + Change/
|
||||
* @param {Object} args
|
||||
* @param {NetPnlFullPattern | NetPnlBasicPattern} args.netPnl
|
||||
* @param {(name: string) => string} args.title
|
||||
@@ -307,11 +302,6 @@ function realizedNetFolder({ netPnl, title, toRcap, extraChange = [] }) {
|
||||
title: title("Net Realized P&L (Total)"),
|
||||
bottom: [baseline({ series: netPnl.cumulative.usd, name: "Net", unit: Unit.usd })],
|
||||
},
|
||||
{
|
||||
name: "Per Block",
|
||||
title: title("Net Realized P&L per Block"),
|
||||
bottom: [dotsBaseline({ series: netPnl.base.usd, name: "Net", unit: Unit.usd })],
|
||||
},
|
||||
...(toRcap ? [{
|
||||
name: "% of Realized Cap",
|
||||
title: title("Net Realized P&L (% of Realized Cap)"),
|
||||
@@ -464,11 +454,6 @@ function realizedSubfolderFull(r, title) {
|
||||
title: title("Peak Regret (Total)"),
|
||||
bottom: [line({ series: r.peakRegret.cumulative.usd, name: "Peak Regret", unit: Unit.usd })],
|
||||
},
|
||||
{
|
||||
name: "Per Block",
|
||||
title: title("Peak Regret per Block"),
|
||||
bottom: [dots({ series: r.peakRegret.base.usd, name: "Peak Regret", unit: Unit.usd })],
|
||||
},
|
||||
{
|
||||
name: "% of Realized Cap",
|
||||
title: title("Peak Regret (% of Realized Cap)"),
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
satsBtcUsdFrom,
|
||||
satsBtcUsdFullTree,
|
||||
revenueBtcSatsUsd,
|
||||
revenueRollingBtcSatsUsd,
|
||||
} from "./shared.js";
|
||||
import { brk } from "../client.js";
|
||||
|
||||
@@ -122,7 +123,6 @@ export function createMiningSection() {
|
||||
name: "Rewards",
|
||||
tree: satsBtcUsdFullTree({
|
||||
pattern: pool.rewards,
|
||||
name: "Rewards",
|
||||
title: `Rewards: ${name}`,
|
||||
}),
|
||||
},
|
||||
@@ -283,77 +283,44 @@ export function createMiningSection() {
|
||||
{
|
||||
name: "Revenue",
|
||||
tree: [
|
||||
...ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: `Revenue ${w.title} Averages`,
|
||||
bottom: revenueRollingBtcSatsUsd({
|
||||
coinbase: mining.rewards.coinbase.average[w.key],
|
||||
subsidy: mining.rewards.subsidy.average[w.key],
|
||||
fee: mining.rewards.fees.average[w.key],
|
||||
}),
|
||||
})),
|
||||
{
|
||||
name: "Compare",
|
||||
tree: [
|
||||
{
|
||||
name: "Per Block",
|
||||
title: "Revenue Comparison",
|
||||
bottom: revenueBtcSatsUsd({
|
||||
coinbase: mining.rewards.coinbase,
|
||||
subsidy: mining.rewards.subsidy,
|
||||
fee: mining.rewards.fees,
|
||||
key: "base",
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: "Revenue Comparison (Total)",
|
||||
bottom: revenueBtcSatsUsd({
|
||||
coinbase: mining.rewards.coinbase,
|
||||
subsidy: mining.rewards.subsidy,
|
||||
fee: mining.rewards.fees,
|
||||
key: "cumulative",
|
||||
}),
|
||||
},
|
||||
],
|
||||
name: "Cumulative",
|
||||
title: "Revenue Comparison (Total)",
|
||||
bottom: revenueBtcSatsUsd({
|
||||
coinbase: mining.rewards.coinbase,
|
||||
subsidy: mining.rewards.subsidy,
|
||||
fee: mining.rewards.fees,
|
||||
key: "cumulative",
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "Coinbase",
|
||||
tree: satsBtcUsdFullTree({
|
||||
pattern: mining.rewards.coinbase,
|
||||
name: "Coinbase",
|
||||
title: "Coinbase Rewards",
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "Subsidy",
|
||||
tree: [
|
||||
{
|
||||
name: "Per Block",
|
||||
title: "Block Subsidy",
|
||||
bottom: [
|
||||
...satsBtcUsdFrom({
|
||||
source: mining.rewards.subsidy,
|
||||
key: "base",
|
||||
name: "base",
|
||||
}),
|
||||
line({
|
||||
series: mining.rewards.subsidy.sma1y.usd,
|
||||
name: "1y SMA",
|
||||
color: colors.time._1y,
|
||||
unit: Unit.usd,
|
||||
defaultActive: false,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: "Block Subsidy (Total)",
|
||||
bottom: satsBtcUsdFrom({
|
||||
source: mining.rewards.subsidy,
|
||||
key: "cumulative",
|
||||
name: "all-time",
|
||||
}),
|
||||
},
|
||||
],
|
||||
tree: satsBtcUsdFullTree({
|
||||
pattern: mining.rewards.subsidy,
|
||||
title: "Block Subsidy",
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "Fees",
|
||||
tree: [
|
||||
...satsBtcUsdFullTree({
|
||||
pattern: mining.rewards.fees,
|
||||
name: "Fees",
|
||||
title: "Transaction Fee Revenue",
|
||||
}),
|
||||
{
|
||||
|
||||
@@ -4,16 +4,14 @@ import { colors } from "../utils/colors.js";
|
||||
import { brk } from "../client.js";
|
||||
import { Unit } from "../utils/units.js";
|
||||
import { entries } from "../utils/array.js";
|
||||
import { priceLine } from "./constants.js";
|
||||
import {
|
||||
line,
|
||||
dots,
|
||||
fromSupplyPattern,
|
||||
chartsFromFullPerBlock,
|
||||
chartsFromCount,
|
||||
chartsFromCountEntries,
|
||||
chartsFromAggregatedPerBlock,
|
||||
averagesTree,
|
||||
averagesArray,
|
||||
simpleDeltaTree,
|
||||
ROLLING_WINDOWS,
|
||||
chartsFromBlockAnd6b,
|
||||
@@ -169,14 +167,14 @@ export function createNetworkSection() {
|
||||
),
|
||||
})),
|
||||
},
|
||||
...activityTypes.map((t) =>
|
||||
averagesTree({
|
||||
...activityTypes.map((t) => ({
|
||||
name: t.name,
|
||||
tree: averagesArray({
|
||||
windows: addrs.activity[key][t.key],
|
||||
title: `${titlePrefix}${t.name} Addresses`,
|
||||
unit: Unit.count,
|
||||
name: t.name,
|
||||
}),
|
||||
),
|
||||
})),
|
||||
],
|
||||
},
|
||||
];
|
||||
@@ -308,7 +306,6 @@ export function createNetworkSection() {
|
||||
name: "Volume",
|
||||
tree: satsBtcUsdFullTree({
|
||||
pattern: transactions.volume.transferVolume,
|
||||
name: "base",
|
||||
title: "Transaction Volume",
|
||||
}),
|
||||
},
|
||||
@@ -429,31 +426,11 @@ export function createNetworkSection() {
|
||||
},
|
||||
{
|
||||
name: "Interval",
|
||||
tree: [
|
||||
{
|
||||
name: "Per Block",
|
||||
title: "Block Interval",
|
||||
bottom: [
|
||||
dots({
|
||||
series: blocks.interval.base,
|
||||
name: "base",
|
||||
unit: Unit.secs,
|
||||
}),
|
||||
line({
|
||||
series: blocks.interval._24h,
|
||||
name: "24h avg",
|
||||
color: colors.stat.avg,
|
||||
unit: Unit.secs,
|
||||
}),
|
||||
priceLine({ unit: Unit.secs, name: "Target", number: 600 }),
|
||||
],
|
||||
},
|
||||
averagesTree({
|
||||
windows: blocks.interval,
|
||||
title: "Block Interval",
|
||||
unit: Unit.secs,
|
||||
}),
|
||||
],
|
||||
tree: averagesArray({
|
||||
windows: blocks.interval,
|
||||
title: "Block Interval",
|
||||
unit: Unit.secs,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "Size",
|
||||
@@ -509,17 +486,15 @@ export function createNetworkSection() {
|
||||
{
|
||||
name: "Created",
|
||||
color: colors.entity.output,
|
||||
base: outputs.count.total.sum,
|
||||
average: outputs.count.total.rolling.average,
|
||||
rolling: outputs.count.total.rolling.sum,
|
||||
sum: outputs.count.total.rolling.sum,
|
||||
cumulative: outputs.count.total.cumulative,
|
||||
},
|
||||
{
|
||||
name: "Spent",
|
||||
color: colors.entity.input,
|
||||
base: inputs.count.sum,
|
||||
average: inputs.count.rolling.average,
|
||||
rolling: inputs.count.rolling.sum,
|
||||
sum: inputs.count.rolling.sum,
|
||||
cumulative: inputs.count.cumulative,
|
||||
},
|
||||
],
|
||||
|
||||
@@ -517,6 +517,61 @@ export function sumsArray({ windows, title, unit }) {
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Flat array of per-window charts with both sum (active) and average (off by default)
|
||||
* @param {Object} args
|
||||
* @param {{ _24h: AnySeriesPattern, _1w: AnySeriesPattern, _1m: AnySeriesPattern, _1y: AnySeriesPattern }} args.sum
|
||||
* @param {{ _24h: AnySeriesPattern, _1w: AnySeriesPattern, _1m: AnySeriesPattern, _1y: AnySeriesPattern }} args.average
|
||||
* @param {string} args.title
|
||||
* @param {Unit} args.unit
|
||||
* @returns {PartialChartOption[]}
|
||||
*/
|
||||
export function sumsAndAveragesArray({ sum, average, title, unit }) {
|
||||
return ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: `${title} ${w.title}`,
|
||||
bottom: [
|
||||
line({ series: sum[w.key], name: "Sum", color: w.color, unit }),
|
||||
line({
|
||||
series: average[w.key],
|
||||
name: "Avg",
|
||||
color: w.color,
|
||||
unit,
|
||||
defaultActive: false,
|
||||
}),
|
||||
],
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Windowed sum+avg charts + cumulative
|
||||
* @param {Object} args
|
||||
* @param {{ _24h: AnySeriesPattern, _1w: AnySeriesPattern, _1m: AnySeriesPattern, _1y: AnySeriesPattern }} args.sum
|
||||
* @param {{ _24h: AnySeriesPattern, _1w: AnySeriesPattern, _1m: AnySeriesPattern, _1y: AnySeriesPattern }} args.average
|
||||
* @param {AnySeriesPattern} args.cumulative
|
||||
* @param {string} args.title
|
||||
* @param {Unit} args.unit
|
||||
* @param {Color} [args.color]
|
||||
* @returns {PartialChartOption[]}
|
||||
*/
|
||||
export function sumsAndAveragesCumulative({ sum, average, cumulative, title, unit, color }) {
|
||||
return [
|
||||
{
|
||||
name: "Compare",
|
||||
title: `${title} Averages`,
|
||||
bottom: ROLLING_WINDOWS.map((w) =>
|
||||
line({ series: average[w.key], name: w.name, color: w.color, unit }),
|
||||
),
|
||||
},
|
||||
...sumsAndAveragesArray({ sum, average, title, unit }),
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: `${title} (Total)`,
|
||||
bottom: [{ series: cumulative, title: "all-time", color, unit }],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Rolling sums tree (Compare + individual windows in a folder)
|
||||
* @param {Object} args
|
||||
@@ -538,22 +593,21 @@ export function sumsTree({ windows, title, unit, series }) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Rolling averages tree
|
||||
* Flat array of per-window average charts
|
||||
* @param {Object} args
|
||||
* @param {{ _24h: AnySeriesPattern, _1w: AnySeriesPattern, _1m: AnySeriesPattern, _1y: AnySeriesPattern }} args.windows
|
||||
* @param {string} args.title
|
||||
* @param {Unit} args.unit
|
||||
* @param {string} [args.name]
|
||||
* @returns {PartialOptionsGroup}
|
||||
* @returns {PartialChartOption[]}
|
||||
*/
|
||||
export function averagesTree({ windows, title, unit, name = "Averages" }) {
|
||||
return rollingWindowsTree({
|
||||
windows,
|
||||
title,
|
||||
windowTitle: (w) => `${title} ${w.title} Average`,
|
||||
unit,
|
||||
name,
|
||||
});
|
||||
export function averagesArray({ windows, title, unit }) {
|
||||
return ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: `${title} ${w.title} Average`,
|
||||
bottom: [
|
||||
line({ series: windows[w.key], name: w.name, color: w.color, unit }),
|
||||
],
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -854,7 +908,7 @@ export function simpleDeltaTree({ delta, title, unit }) {
|
||||
// These split patterns into separate Sum/Distribution/Cumulative charts
|
||||
|
||||
/**
|
||||
* Split flat per-block pattern into charts (Sum/Rolling/Distribution/Cumulative)
|
||||
* Split flat per-block pattern into charts (Averages/Sums/Distribution/Cumulative)
|
||||
* Pattern has: .height, .cumulative, .sum (windowed), .average/.pct10/... (windowed, flat)
|
||||
* @param {Object} args
|
||||
* @param {FullPerBlockPattern} args.pattern
|
||||
@@ -873,14 +927,14 @@ export function chartsFromFull({
|
||||
? `${title} ${distributionSuffix}`
|
||||
: title;
|
||||
return [
|
||||
averagesTree({ windows: pattern.average, title, unit }),
|
||||
sumsTree({ windows: pattern.sum, title, unit }),
|
||||
...sumsAndAveragesCumulative({
|
||||
sum: pattern.sum,
|
||||
average: pattern.average,
|
||||
cumulative: pattern.cumulative,
|
||||
title,
|
||||
unit,
|
||||
}),
|
||||
distributionWindowsTree({ pattern, title: distTitle, unit }),
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: `${title} (Total)`,
|
||||
bottom: [{ series: pattern.cumulative, title: "all-time", unit }],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@@ -914,18 +968,18 @@ export function chartsFromAggregated({
|
||||
? `${title} ${distributionSuffix}`
|
||||
: title;
|
||||
return [
|
||||
averagesTree({ windows: pattern.rolling.average, title, unit }),
|
||||
sumsTree({ windows: pattern.rolling.sum, title, unit }),
|
||||
...sumsAndAveragesCumulative({
|
||||
sum: pattern.rolling.sum,
|
||||
average: pattern.rolling.average,
|
||||
cumulative: pattern.cumulative,
|
||||
title,
|
||||
unit,
|
||||
}),
|
||||
distributionWindowsTree({
|
||||
pattern: pattern.rolling,
|
||||
title: distTitle,
|
||||
unit,
|
||||
}),
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: `${title} (Total)`,
|
||||
bottom: [{ series: pattern.cumulative, title: "all-time", unit }],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@@ -964,27 +1018,7 @@ export function chartsFromBlockAnd6b({ pattern, title, unit }) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sums + Cumulative charts (no Per Block)
|
||||
* @param {Object} args
|
||||
* @param {{ sum: { _24h: AnySeriesPattern, _1w: AnySeriesPattern, _1m: AnySeriesPattern, _1y: AnySeriesPattern }, cumulative: AnySeriesPattern }} args.pattern
|
||||
* @param {string} args.title
|
||||
* @param {Unit} args.unit
|
||||
* @param {Color} [args.color]
|
||||
* @returns {PartialOptionsTree}
|
||||
*/
|
||||
export function chartsFromSumsCumulative({ pattern, title, unit, color }) {
|
||||
return [
|
||||
...sumsArray({ windows: pattern.sum, title, unit }),
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: `${title} (Total)`,
|
||||
bottom: [{ series: pattern.cumulative, title: "all-time", color, unit }],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Per Block + Sums + Cumulative charts
|
||||
* Averages + Sums + Cumulative charts
|
||||
* @param {Object} args
|
||||
* @param {CountPattern<any>} args.pattern
|
||||
* @param {string} args.title
|
||||
@@ -993,14 +1027,26 @@ export function chartsFromSumsCumulative({ pattern, title, unit, color }) {
|
||||
* @returns {PartialOptionsTree}
|
||||
*/
|
||||
export function chartsFromCount({ pattern, title, unit, color }) {
|
||||
return [
|
||||
averagesTree({ windows: pattern.average, title, unit }),
|
||||
...chartsFromSumsCumulative({ pattern, title, unit, color }),
|
||||
];
|
||||
return sumsAndAveragesCumulative({
|
||||
sum: pattern.sum,
|
||||
average: pattern.average,
|
||||
cumulative: pattern.cumulative,
|
||||
title,
|
||||
unit,
|
||||
color,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Split multiple named entries (each with base/sum/cumulative) into Per Block/Sums/Cumulative charts
|
||||
* Windowed sums + cumulative for multiple named entries (e.g. transaction versions)
|
||||
* @param {Object} args
|
||||
* @param {Array<[string, CountPattern<any>]>} args.entries
|
||||
* @param {string} args.title
|
||||
* @param {Unit} args.unit
|
||||
* @returns {PartialOptionsTree}
|
||||
*/
|
||||
/**
|
||||
* Windowed sums + cumulative for multiple named entries (e.g. transaction versions)
|
||||
* @param {Object} args
|
||||
* @param {Array<[string, CountPattern<any>]>} args.entries
|
||||
* @param {string} args.title
|
||||
@@ -1008,49 +1054,45 @@ export function chartsFromCount({ pattern, title, unit, color }) {
|
||||
* @returns {PartialOptionsTree}
|
||||
*/
|
||||
export function chartsFromCountEntries({ entries, title, unit }) {
|
||||
return multiSeriesTree({
|
||||
entries: entries.map(([name, data], i, arr) => ({
|
||||
name,
|
||||
color: colors.at(i, arr.length),
|
||||
base: data.base,
|
||||
average: data.average,
|
||||
rolling: data.sum,
|
||||
cumulative: data.cumulative,
|
||||
const items = entries.map(([name, data], i, arr) => ({
|
||||
name,
|
||||
color: colors.at(i, arr.length),
|
||||
sum: data.sum,
|
||||
cumulative: data.cumulative,
|
||||
}));
|
||||
return [
|
||||
...ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: `${title} ${w.title} Sum`,
|
||||
bottom: items.map((e) =>
|
||||
line({ series: e.sum[w.key], name: e.name, color: e.color, unit }),
|
||||
),
|
||||
})),
|
||||
title,
|
||||
unit,
|
||||
});
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: `${title} (Total)`,
|
||||
bottom: items.map((e) =>
|
||||
line({ series: e.cumulative, name: e.name, color: e.color, unit }),
|
||||
),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Per Block + Sums + Cumulative tree for multiple named series shown side-by-side
|
||||
* Windowed averages + sums + cumulative for multiple named series (e.g. UTXO flow)
|
||||
* @param {Object} args
|
||||
* @param {Array<{ name: string, color: Color, base: AnySeriesPattern, average: { _24h: AnySeriesPattern, _1w: AnySeriesPattern, _1m: AnySeriesPattern, _1y: AnySeriesPattern }, rolling: { _24h: AnySeriesPattern, _1w: AnySeriesPattern, _1m: AnySeriesPattern, _1y: AnySeriesPattern }, cumulative: AnySeriesPattern }>} args.entries
|
||||
* @param {Array<{ name: string, color: Color, average: { _24h: AnySeriesPattern, _1w: AnySeriesPattern, _1m: AnySeriesPattern, _1y: AnySeriesPattern }, sum: { _24h: AnySeriesPattern, _1w: AnySeriesPattern, _1m: AnySeriesPattern, _1y: AnySeriesPattern }, cumulative: AnySeriesPattern }>} args.entries
|
||||
* @param {string} args.title
|
||||
* @param {Unit} args.unit
|
||||
* @returns {PartialOptionsTree}
|
||||
*/
|
||||
export function multiSeriesTree({ entries, title, unit }) {
|
||||
return [
|
||||
{
|
||||
name: "Compare",
|
||||
title: `${title} Average`,
|
||||
bottom: ROLLING_WINDOWS.flatMap((w) =>
|
||||
entries.map((e) =>
|
||||
line({
|
||||
series: e.average[w.key],
|
||||
name: `${e.name} ${w.name}`,
|
||||
color: e.color,
|
||||
unit,
|
||||
}),
|
||||
),
|
||||
),
|
||||
},
|
||||
...ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: `${title} ${w.title} Sum`,
|
||||
title: `${title} ${w.title} Averages`,
|
||||
bottom: entries.map((e) =>
|
||||
line({ series: e.rolling[w.key], name: e.name, color: e.color, unit }),
|
||||
line({ series: e.average[w.key], name: e.name, color: e.color, unit }),
|
||||
),
|
||||
})),
|
||||
{
|
||||
|
||||
@@ -219,7 +219,6 @@ export function satsBtcUsdRolling({ pattern, name, color, defaultActive }) {
|
||||
* Build a full Sum / Rolling / Cumulative tree from a FullValuePattern
|
||||
* @param {Object} args
|
||||
* @param {FullValuePattern} args.pattern
|
||||
* @param {string} args.name
|
||||
* @param {string} args.title
|
||||
* @param {Color} [args.color]
|
||||
* @returns {PartialOptionsTree}
|
||||
|
||||
Reference in New Issue
Block a user