mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-06-21 20:12:15 -07:00
global: snap
This commit is contained in:
@@ -128,6 +128,9 @@
|
||||
*
|
||||
* Address count pattern (base + delta with absolute + rate)
|
||||
* @typedef {Brk.BaseDeltaPattern} AddrCountPattern
|
||||
* @typedef {Brk.AddrUtxoPattern} AvgAmountPattern
|
||||
* @typedef {Brk.SeriesTree_Addrs_Exposed} ExposedTree
|
||||
* @typedef {Brk.SeriesTree_Addrs_Reused} ReusedTree
|
||||
*/
|
||||
|
||||
/**
|
||||
|
||||
@@ -273,6 +273,15 @@ function createBlockCube(block) {
|
||||
function createCube() {
|
||||
const cubeElement = document.createElement("div");
|
||||
cubeElement.classList.add("cube");
|
||||
const bottomElement = document.createElement("div");
|
||||
bottomElement.classList.add("face", "bottom");
|
||||
cubeElement.append(bottomElement);
|
||||
const rearRightElement = document.createElement("div");
|
||||
rearRightElement.classList.add("face", "rear-right");
|
||||
cubeElement.append(rearRightElement);
|
||||
const rearLeftElement = document.createElement("div");
|
||||
rearLeftElement.classList.add("face", "rear-left");
|
||||
cubeElement.append(rearLeftElement);
|
||||
const innerTopElement = document.createElement("div");
|
||||
innerTopElement.classList.add("face", "inner-top");
|
||||
cubeElement.append(innerTopElement);
|
||||
|
||||
@@ -47,6 +47,7 @@ export function buildCohortData() {
|
||||
base: addrs.funded.all,
|
||||
delta: addrs.delta.all,
|
||||
},
|
||||
avgAmount: addrs.avgAmount.all,
|
||||
};
|
||||
|
||||
const shortNames = TERM_NAMES.short;
|
||||
@@ -174,6 +175,9 @@ export function buildCohortData() {
|
||||
base: addrs.funded[key],
|
||||
delta: addrs.delta[key],
|
||||
},
|
||||
avgAmount: addrs.avgAmount[key],
|
||||
exposed: addrs.exposed,
|
||||
reused: addrs.reused,
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ import {
|
||||
groupedWindowsCumulativeWithAll,
|
||||
} from "../shared.js";
|
||||
import { colors } from "../../utils/colors.js";
|
||||
import { priceLines } from "../constants.js";
|
||||
import { priceLine } from "../constants.js";
|
||||
|
||||
/**
|
||||
* Simple supply series (total + half only, no profit/loss)
|
||||
@@ -165,41 +165,44 @@ function groupedDeltaItems(list, all, getDelta, unit, title, name) {
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* Profitability chart (in profit + in loss supply)
|
||||
* Amount chart: total + halved + in profit + in loss in sats/btc/usd.
|
||||
* @param {{ total: AnyValuePattern, half: AnyValuePattern, inProfit: AnyValuePattern, inLoss: AnyValuePattern }} supply
|
||||
* @param {(name: string) => string} title
|
||||
* @returns {PartialChartOption}
|
||||
*/
|
||||
function profitabilityChart(supply, title) {
|
||||
function profitabilityAmountChart(supply, title) {
|
||||
return {
|
||||
name: "Profitability",
|
||||
name: "Amount",
|
||||
title: title("Supply Profitability"),
|
||||
bottom: [
|
||||
...satsBtcUsd({
|
||||
pattern: supply.total,
|
||||
name: "Total",
|
||||
color: colors.default,
|
||||
}),
|
||||
...satsBtcUsd({
|
||||
pattern: supply.inProfit,
|
||||
name: "In Profit",
|
||||
color: colors.profit,
|
||||
}),
|
||||
...satsBtcUsd({
|
||||
pattern: supply.inLoss,
|
||||
name: "In Loss",
|
||||
color: colors.loss,
|
||||
}),
|
||||
...satsBtcUsd({
|
||||
pattern: supply.half,
|
||||
name: "Halved",
|
||||
color: colors.gray,
|
||||
style: 4,
|
||||
}),
|
||||
...satsBtcUsd({ pattern: supply.total, name: "Total", color: colors.default }),
|
||||
...satsBtcUsd({ pattern: supply.inProfit, name: "In Profit", color: colors.profit }),
|
||||
...satsBtcUsd({ pattern: supply.inLoss, name: "In Loss", color: colors.loss }),
|
||||
...satsBtcUsd({ pattern: supply.half, name: "Halved", color: colors.gray, style: 4 }),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Share chart: in profit / in loss as % of own supply.
|
||||
* @param {{ inProfit: { toOwn: { percent: AnySeriesPattern, ratio: AnySeriesPattern } }, inLoss: { toOwn: { percent: AnySeriesPattern, ratio: AnySeriesPattern } } }} supply
|
||||
* @param {(name: string) => string} title
|
||||
* @returns {PartialChartOption}
|
||||
*/
|
||||
function profitabilityShareChart(supply, title) {
|
||||
return {
|
||||
name: "Share",
|
||||
title: title("Supply Profitability"),
|
||||
bottom: [
|
||||
...percentRatio({ pattern: supply.inProfit.toOwn, name: "In Profit", color: colors.profit }),
|
||||
...percentRatio({ pattern: supply.inLoss.toOwn, name: "In Loss", color: colors.loss }),
|
||||
priceLine({ number: 100, color: colors.default, style: 0, unit: Unit.percentage }),
|
||||
priceLine({ number: 50, unit: Unit.percentage }),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param {{ toCirculating: PercentRatioPattern, inProfit: { toCirculating: PercentRatioPattern }, inLoss: { toCirculating: PercentRatioPattern } }} supply
|
||||
* @param {(name: string) => string} title
|
||||
@@ -207,8 +210,8 @@ function profitabilityChart(supply, title) {
|
||||
*/
|
||||
function circulatingChart(supply, title) {
|
||||
return {
|
||||
name: "% of Circulating",
|
||||
title: title("Supply (% of Circulating)"),
|
||||
name: "Dominance",
|
||||
title: title("Supply Dominance"),
|
||||
bottom: [
|
||||
...percentRatio({ pattern: supply.toCirculating, name: "Total", color: colors.default }),
|
||||
...percentRatio({ pattern: supply.inProfit.toCirculating, name: "In Profit", color: colors.profit }),
|
||||
@@ -217,23 +220,6 @@ function circulatingChart(supply, title) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{ inProfit: { toOwn: { percent: AnySeriesPattern, ratio: AnySeriesPattern } }, inLoss: { toOwn: { percent: AnySeriesPattern, ratio: AnySeriesPattern } } }} supply
|
||||
* @param {(name: string) => string} title
|
||||
* @returns {PartialChartOption}
|
||||
*/
|
||||
function ownSupplyChart(supply, title) {
|
||||
return {
|
||||
name: "% of Own Supply",
|
||||
title: title("Supply (% of Own)"),
|
||||
bottom: [
|
||||
...percentRatio({ pattern: supply.inProfit.toOwn, name: "In Profit", color: colors.profit }),
|
||||
...percentRatio({ pattern: supply.inLoss.toOwn, name: "In Loss", color: colors.loss }),
|
||||
...priceLines({ numbers: [100, 50, 0], unit: Unit.percentage }),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {OutputsPattern} outputs
|
||||
* @param {Color} color
|
||||
@@ -330,8 +316,13 @@ export function createHoldingsSectionAll({ cohort, title }) {
|
||||
title: title("Supply"),
|
||||
bottom: simpleSupplySeries(supply),
|
||||
},
|
||||
profitabilityChart(supply, title),
|
||||
ownSupplyChart(supply, title),
|
||||
{
|
||||
name: "Profitability",
|
||||
tree: [
|
||||
profitabilityAmountChart(supply, title),
|
||||
profitabilityShareChart(supply, title),
|
||||
],
|
||||
},
|
||||
...singleDeltaItems(supply.delta, Unit.sats, title, "Supply"),
|
||||
],
|
||||
},
|
||||
@@ -355,9 +346,14 @@ export function createHoldingsSectionWithRelative({ cohort, title }) {
|
||||
title: title("Supply"),
|
||||
bottom: simpleSupplySeries(supply),
|
||||
},
|
||||
profitabilityChart(supply, title),
|
||||
circulatingChart(supply, title),
|
||||
ownSupplyChart(supply, title),
|
||||
{
|
||||
name: "Profitability",
|
||||
tree: [
|
||||
profitabilityAmountChart(supply, title),
|
||||
profitabilityShareChart(supply, title),
|
||||
circulatingChart(supply, title),
|
||||
],
|
||||
},
|
||||
...singleDeltaItems(supply.delta, Unit.sats, title, "Supply"),
|
||||
],
|
||||
},
|
||||
@@ -380,8 +376,13 @@ export function createHoldingsSectionWithOwnSupply({ cohort, title }) {
|
||||
title: title("Supply"),
|
||||
bottom: simpleSupplySeries(supply),
|
||||
},
|
||||
profitabilityChart(supply, title),
|
||||
circulatingChart(supply, title),
|
||||
{
|
||||
name: "Profitability",
|
||||
tree: [
|
||||
profitabilityAmountChart(supply, title),
|
||||
circulatingChart(supply, title),
|
||||
],
|
||||
},
|
||||
...singleDeltaItems(supply.delta, Unit.sats, title, "Supply"),
|
||||
],
|
||||
},
|
||||
@@ -404,7 +405,10 @@ export function createHoldingsSectionWithProfitLoss({ cohort, title }) {
|
||||
title: title("Supply"),
|
||||
bottom: simpleSupplySeries(supply),
|
||||
},
|
||||
profitabilityChart(supply, title),
|
||||
{
|
||||
name: "Profitability",
|
||||
tree: [profitabilityAmountChart(supply, title)],
|
||||
},
|
||||
...singleDeltaItems(supply.delta, Unit.sats, title, "Supply"),
|
||||
],
|
||||
},
|
||||
@@ -427,7 +431,10 @@ export function createHoldingsSectionAddress({ cohort, title }) {
|
||||
title: title("Supply"),
|
||||
bottom: simpleSupplySeries(supply),
|
||||
},
|
||||
profitabilityChart(supply, title),
|
||||
{
|
||||
name: "Profitability",
|
||||
tree: [profitabilityAmountChart(supply, title)],
|
||||
},
|
||||
...singleDeltaItems(supply.delta, Unit.sats, title, "Supply"),
|
||||
],
|
||||
},
|
||||
@@ -502,7 +509,10 @@ export function createGroupedHoldingsSectionAddress({ list, all, title }) {
|
||||
name: "Supply",
|
||||
tree: [
|
||||
groupedSupplyTotal(list, all, title),
|
||||
...groupedSupplyProfitLoss(list, all, title),
|
||||
{
|
||||
name: "Profitability",
|
||||
tree: groupedSupplyProfitLoss(list, all, title),
|
||||
},
|
||||
...groupedDeltaItems(list, all, (c) => c.tree.supply.delta, Unit.sats, title, "Supply"),
|
||||
],
|
||||
},
|
||||
@@ -520,6 +530,25 @@ export function createGroupedHoldingsSectionAddress({ list, all, title }) {
|
||||
...groupedDeltaItems(list, all, (c) => c.addressCount.delta, Unit.count, title, "Address Count"),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Average Holdings",
|
||||
tree: [
|
||||
{
|
||||
name: "Per UTXO",
|
||||
title: title("Average Holdings per UTXO"),
|
||||
bottom: flatMapCohortsWithAll(list, all, ({ name, color, avgAmount }) =>
|
||||
satsBtcUsd({ pattern: avgAmount.utxo, name, color }),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "Per Address",
|
||||
title: title("Average Holdings per Funded Address"),
|
||||
bottom: flatMapCohortsWithAll(list, all, ({ name, color, avgAmount }) =>
|
||||
satsBtcUsd({ pattern: avgAmount.addr, name, color }),
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,9 @@ import {
|
||||
formatCohortTitle,
|
||||
satsBtcUsd,
|
||||
satsBtcUsdFullTree,
|
||||
avgHoldingsSubtree,
|
||||
exposedSubtree,
|
||||
reusedSubtree,
|
||||
} from "../shared.js";
|
||||
import {
|
||||
ROLLING_WINDOWS,
|
||||
@@ -103,6 +106,7 @@ export function createCohortFolderAll(cohort) {
|
||||
createCostBasisSectionWithPercentiles({ cohort, title }),
|
||||
createProfitabilitySectionAll({ cohort, title }),
|
||||
createActivitySectionWithAdjusted({ cohort, title }),
|
||||
avgHoldingsSubtree(cohort.avgAmount, title),
|
||||
],
|
||||
};
|
||||
}
|
||||
@@ -259,6 +263,9 @@ export function createCohortFolderAddress(cohort) {
|
||||
createPricesSectionBasic({ cohort, title }),
|
||||
createProfitabilitySectionWithProfitLoss({ cohort, title }),
|
||||
createActivitySectionMinimal({ cohort, title }),
|
||||
avgHoldingsSubtree(cohort.avgAmount, title),
|
||||
reusedSubtree(cohort.reused, cohort.key, title),
|
||||
exposedSubtree(cohort.exposed, cohort.key, title),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { Unit } from "../../utils/units.js";
|
||||
import { colors } from "../../utils/colors.js";
|
||||
import { ROLLING_WINDOWS, line, baseline, mapWindows, sumsTreeBaseline, rollingPercentRatioTree, percentRatio, percentRatioBaseline } from "../series.js";
|
||||
import { ratioBottomSeries, mapCohortsWithAll, flatMapCohortsWithAll } from "../shared.js";
|
||||
import { priceLine } from "../constants.js";
|
||||
|
||||
// ============================================================================
|
||||
// Shared building blocks
|
||||
@@ -80,11 +81,26 @@ export function createValuationSectionFull({ cohort, title }) {
|
||||
{ name: "Total", title: title("Realized Cap"), bottom: [line({ series: tree.realized.cap.usd, name: "Realized Cap", color, unit: Unit.usd })] },
|
||||
{
|
||||
name: "Profitability",
|
||||
title: title("Invested Capital"),
|
||||
bottom: [
|
||||
line({ series: tree.realized.cap.usd, name: "Total", color: colors.default, unit: Unit.usd }),
|
||||
line({ series: tree.unrealized.investedCapital.inProfit.usd, name: "In Profit", color: colors.profit, unit: Unit.usd }),
|
||||
line({ series: tree.unrealized.investedCapital.inLoss.usd, name: "In Loss", color: colors.loss, unit: Unit.usd }),
|
||||
tree: [
|
||||
{
|
||||
name: "Amount",
|
||||
title: title("Invested Capital"),
|
||||
bottom: [
|
||||
line({ series: tree.realized.cap.usd, name: "Total", color: colors.default, unit: Unit.usd }),
|
||||
line({ series: tree.unrealized.investedCapital.inProfit.usd, name: "In Profit", color: colors.profit, unit: Unit.usd }),
|
||||
line({ series: tree.unrealized.investedCapital.inLoss.usd, name: "In Loss", color: colors.loss, unit: Unit.usd }),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Share",
|
||||
title: title("Invested Capital Profitability"),
|
||||
bottom: [
|
||||
...percentRatio({ pattern: tree.investedCapital.inProfit.toOwn, name: "In Profit", color: colors.profit }),
|
||||
...percentRatio({ pattern: tree.investedCapital.inLoss.toOwn, name: "In Loss", color: colors.loss }),
|
||||
priceLine({ number: 100, color: colors.default, style: 0, unit: Unit.percentage }),
|
||||
priceLine({ number: 50, unit: Unit.percentage }),
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{ name: "MVRV", title: title("MVRV"), bottom: ratioBottomSeries(tree.realized.price) },
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
ROLLING_WINDOWS,
|
||||
chartsFromBlockAnd6b,
|
||||
multiSeriesTree,
|
||||
percentRatio,
|
||||
percentRatioDots,
|
||||
} from "./series.js";
|
||||
import {
|
||||
@@ -29,6 +30,9 @@ import {
|
||||
satsBtcUsdFullTree,
|
||||
formatCohortTitle,
|
||||
groupedWindowsCumulative,
|
||||
avgHoldingsSubtree,
|
||||
exposedSubtree,
|
||||
reusedSubtree,
|
||||
} from "./shared.js";
|
||||
|
||||
/**
|
||||
@@ -210,34 +214,6 @@ export function createNetworkSection() {
|
||||
],
|
||||
});
|
||||
|
||||
const reusedOutputsSubtreeForType =
|
||||
/**
|
||||
* @param {AddressableType} key
|
||||
* @param {(name: string) => string} title
|
||||
*/
|
||||
(key, title) => ({
|
||||
name: "Outputs",
|
||||
tree: [
|
||||
{
|
||||
name: "Count",
|
||||
tree: chartsFromCount({
|
||||
pattern: addrs.reused.events.outputToReusedAddrCount[key],
|
||||
title,
|
||||
metric: "Transaction Outputs to Reused Addresses",
|
||||
unit: Unit.count,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "Share",
|
||||
tree: chartsFromPercentCumulative({
|
||||
pattern: addrs.reused.events.outputToReusedAddrShare[key],
|
||||
title,
|
||||
metric: "Share of Transaction Outputs to Reused Addresses",
|
||||
}),
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const reusedActiveSubtreeForAll =
|
||||
/** @param {(name: string) => string} title */
|
||||
(title) => ({
|
||||
@@ -276,19 +252,6 @@ export function createNetworkSection() {
|
||||
],
|
||||
});
|
||||
|
||||
const reusedSubtreeForType =
|
||||
/**
|
||||
* @param {AddressableType} key
|
||||
* @param {(name: string) => string} title
|
||||
*/
|
||||
(key, title) => ({
|
||||
name: "Reused",
|
||||
tree: [
|
||||
...reusedSetEntries(key, title),
|
||||
reusedOutputsSubtreeForType(key, title),
|
||||
reusedInputsSubtree(key, title),
|
||||
],
|
||||
});
|
||||
|
||||
const countSubtree =
|
||||
/**
|
||||
@@ -324,64 +287,6 @@ export function createNetworkSection() {
|
||||
],
|
||||
});
|
||||
|
||||
const exposedSubtree =
|
||||
/**
|
||||
* @param {AddressableType | "all"} key
|
||||
* @param {(name: string) => string} title
|
||||
*/
|
||||
(key, title) => ({
|
||||
name: "Exposed",
|
||||
tree: [
|
||||
{
|
||||
name: "Compare",
|
||||
title: title("Exposed Address Count"),
|
||||
bottom: [
|
||||
line({
|
||||
series: addrs.exposed.count.funded[key],
|
||||
name: "Funded",
|
||||
unit: Unit.count,
|
||||
}),
|
||||
line({
|
||||
series: addrs.exposed.count.total[key],
|
||||
name: "Total",
|
||||
color: colors.gray,
|
||||
unit: Unit.count,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Funded",
|
||||
title: title("Funded Exposed Address Count"),
|
||||
bottom: [
|
||||
line({
|
||||
series: addrs.exposed.count.funded[key],
|
||||
name: "Funded Exposed",
|
||||
unit: Unit.count,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Total",
|
||||
title: title("Total Exposed Address Count"),
|
||||
bottom: [
|
||||
line({
|
||||
series: addrs.exposed.count.total[key],
|
||||
name: "Total Exposed",
|
||||
color: colors.gray,
|
||||
unit: Unit.count,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Supply",
|
||||
title: title("Supply in Exposed Addresses"),
|
||||
bottom: satsBtcUsd({
|
||||
pattern: addrs.exposed.supply[key],
|
||||
name: "Supply",
|
||||
}),
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const activityPerTypeEntries =
|
||||
/**
|
||||
@@ -478,7 +383,8 @@ export function createNetworkSection() {
|
||||
}),
|
||||
activitySubtreeForAll(title),
|
||||
reusedSubtreeForAll(title),
|
||||
exposedSubtree("all", title),
|
||||
exposedSubtree(addrs.exposed, "all", title),
|
||||
avgHoldingsSubtree(addrs.avgAmount.all, title),
|
||||
];
|
||||
};
|
||||
|
||||
@@ -507,8 +413,9 @@ export function createNetworkSection() {
|
||||
unit: Unit.count,
|
||||
}),
|
||||
activitySubtreeForType(addrType, title),
|
||||
reusedSubtreeForType(addrType, title),
|
||||
exposedSubtree(addrType, title),
|
||||
reusedSubtree(addrs.reused, addrType, title),
|
||||
exposedSubtree(addrs.exposed, addrType, title),
|
||||
avgHoldingsSubtree(addrs.avgAmount[addrType], title),
|
||||
];
|
||||
};
|
||||
|
||||
@@ -761,6 +668,49 @@ export function createNetworkSection() {
|
||||
}),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "Share",
|
||||
title: "Share of Supply in Exposed Addresses by Type",
|
||||
bottom: addressTypes.flatMap((t) =>
|
||||
percentRatio({
|
||||
pattern: addrs.exposed.supply.share[t.key],
|
||||
name: t.name,
|
||||
color: t.color,
|
||||
defaultActive: t.defaultActive,
|
||||
}),
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
// Average Holdings
|
||||
{
|
||||
name: "Average Holdings",
|
||||
tree: [
|
||||
{
|
||||
name: "Per UTXO",
|
||||
title: "Average Holdings per UTXO by Type",
|
||||
bottom: addressTypes.flatMap((t) =>
|
||||
satsBtcUsd({
|
||||
pattern: addrs.avgAmount[t.key].utxo,
|
||||
name: t.name,
|
||||
color: t.color,
|
||||
defaultActive: t.defaultActive,
|
||||
}),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "Per Address",
|
||||
title: "Average Holdings per Funded Address by Type",
|
||||
bottom: addressTypes.flatMap((t) =>
|
||||
satsBtcUsd({
|
||||
pattern: addrs.avgAmount[t.key].addr,
|
||||
name: t.name,
|
||||
color: t.color,
|
||||
defaultActive: t.defaultActive,
|
||||
}),
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
@@ -6,6 +6,9 @@ import {
|
||||
line,
|
||||
baseline,
|
||||
price,
|
||||
percentRatio,
|
||||
chartsFromCount,
|
||||
chartsFromPercentCumulative,
|
||||
sumsAndAveragesCumulativeWith,
|
||||
} from "./series.js";
|
||||
import { priceLine, priceLines } from "./constants.js";
|
||||
@@ -243,6 +246,198 @@ export function satsBtcUsdFullTree({ pattern, title, metric, color }) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* "Exposed" subtree (quantum-risk / revealed-pubkey addresses).
|
||||
* Shape: Compare (funded + total) / Funded / Total / Supply / Share.
|
||||
* Shared between Network and Distribution (per-type cohort view).
|
||||
* @param {ExposedTree} exposed
|
||||
* @param {AddressableType | "all"} key
|
||||
* @param {(name: string) => string} title
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
export function exposedSubtree(exposed, key, title) {
|
||||
return {
|
||||
name: "Exposed",
|
||||
tree: [
|
||||
{
|
||||
name: "Compare",
|
||||
title: title("Exposed Address Count"),
|
||||
bottom: [
|
||||
line({ series: exposed.count.funded[key], name: "Funded", unit: Unit.count }),
|
||||
line({
|
||||
series: exposed.count.total[key],
|
||||
name: "Total",
|
||||
color: colors.gray,
|
||||
unit: Unit.count,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Funded",
|
||||
title: title("Funded Exposed Address Count"),
|
||||
bottom: [
|
||||
line({ series: exposed.count.funded[key], name: "Funded Exposed", unit: Unit.count }),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Total",
|
||||
title: title("Total Exposed Address Count"),
|
||||
bottom: [
|
||||
line({
|
||||
series: exposed.count.total[key],
|
||||
name: "Total Exposed",
|
||||
color: colors.gray,
|
||||
unit: Unit.count,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Supply",
|
||||
title: title("Supply in Exposed Addresses"),
|
||||
bottom: satsBtcUsd({ pattern: exposed.supply[key], name: "Supply" }),
|
||||
},
|
||||
{
|
||||
name: "Share",
|
||||
title: title("Share of Supply in Exposed Addresses"),
|
||||
bottom: percentRatio({ pattern: exposed.supply.share[key], name: "Supply" }),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* "Reused" subtree (per-type / per-cohort — no "Active" window, since that
|
||||
* data is only tracked globally). Shape:
|
||||
* Compare (funded + total) / Funded / Total / Outputs / Inputs.
|
||||
* @param {ReusedTree} reused
|
||||
* @param {AddressableType | "all"} key
|
||||
* @param {(name: string) => string} title
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
export function reusedSubtree(reused, key, title) {
|
||||
return {
|
||||
name: "Reused",
|
||||
tree: [
|
||||
{
|
||||
name: "Compare",
|
||||
title: title("Reused Address Count"),
|
||||
bottom: [
|
||||
line({ series: reused.count.funded[key], name: "Funded", unit: Unit.count }),
|
||||
line({
|
||||
series: reused.count.total[key],
|
||||
name: "Total",
|
||||
color: colors.gray,
|
||||
unit: Unit.count,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Funded",
|
||||
title: title("Funded Reused Addresses"),
|
||||
bottom: [
|
||||
line({ series: reused.count.funded[key], name: "Funded Reused", unit: Unit.count }),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Total",
|
||||
title: title("Total Reused Addresses"),
|
||||
bottom: [
|
||||
line({
|
||||
series: reused.count.total[key],
|
||||
name: "Total Reused",
|
||||
color: colors.gray,
|
||||
unit: Unit.count,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Outputs",
|
||||
tree: [
|
||||
{
|
||||
name: "Count",
|
||||
tree: chartsFromCount({
|
||||
pattern: reused.events.outputToReusedAddrCount[key],
|
||||
title,
|
||||
metric: "Transaction Outputs to Reused Addresses",
|
||||
unit: Unit.count,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "Share",
|
||||
tree: chartsFromPercentCumulative({
|
||||
pattern: reused.events.outputToReusedAddrShare[key],
|
||||
title,
|
||||
metric: "Share of Transaction Outputs to Reused Addresses",
|
||||
}),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Inputs",
|
||||
tree: [
|
||||
{
|
||||
name: "Count",
|
||||
tree: chartsFromCount({
|
||||
pattern: reused.events.inputFromReusedAddrCount[key],
|
||||
title,
|
||||
metric: "Transaction Inputs from Reused Addresses",
|
||||
unit: Unit.count,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "Share",
|
||||
tree: chartsFromPercentCumulative({
|
||||
pattern: reused.events.inputFromReusedAddrShare[key],
|
||||
title,
|
||||
metric: "Share of Transaction Inputs from Reused Addresses",
|
||||
}),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* "Average Holdings" subtree: Compare (both) + Per UTXO + Per Funded Address.
|
||||
* Shared between Network and Distribution.
|
||||
* @param {AvgAmountPattern} pattern
|
||||
* @param {(name: string) => string} title
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
export function avgHoldingsSubtree(pattern, title) {
|
||||
return {
|
||||
name: "Average Holdings",
|
||||
tree: [
|
||||
{
|
||||
name: "Compare",
|
||||
title: title("Average Holdings"),
|
||||
bottom: [
|
||||
...satsBtcUsd({ pattern: pattern.utxo, name: "Per UTXO" }),
|
||||
...satsBtcUsd({
|
||||
pattern: pattern.addr,
|
||||
name: "Per Funded Address",
|
||||
color: colors.gray,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Per UTXO",
|
||||
title: title("Average Holdings per UTXO"),
|
||||
bottom: satsBtcUsd({ pattern: pattern.utxo, name: "Per UTXO" }),
|
||||
},
|
||||
{
|
||||
name: "Per Address",
|
||||
title: title("Average Holdings per Funded Address"),
|
||||
bottom: satsBtcUsd({
|
||||
pattern: pattern.addr,
|
||||
name: "Per Funded Address",
|
||||
}),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Price + Ratio charts from a simple price pattern (BpsCentsRatioSatsUsdPattern)
|
||||
* @param {Object} args
|
||||
|
||||
@@ -183,6 +183,7 @@
|
||||
* @property {Color} color
|
||||
* @property {PatternAll} tree
|
||||
* @property {AddrCountPattern} addressCount
|
||||
* @property {AvgAmountPattern} avgAmount
|
||||
*
|
||||
* Full cohort: adjustedSopr + percentiles + RelToMarketCap (term.short)
|
||||
* @typedef {Object} CohortFull
|
||||
@@ -251,7 +252,7 @@
|
||||
* ============================================================================
|
||||
*
|
||||
* Addressable cohort with address count (for "type" cohorts - uses OutputsRealizedSupplyUnrealizedPattern2)
|
||||
* @typedef {{ name: string, title: string, color: Color, tree: EmptyPattern, addressCount: AddrCountPattern }} CohortAddr
|
||||
* @typedef {{ name: string, key: AddressableType, title: string, color: Color, tree: EmptyPattern, addressCount: AddrCountPattern, avgAmount: AvgAmountPattern, exposed: ExposedTree, reused: ReusedTree }} CohortAddr
|
||||
*
|
||||
* ============================================================================
|
||||
* Cohort Group Types (by capability)
|
||||
|
||||
Reference in New Issue
Block a user