mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-04-24 06:39:58 -07:00
website: snapshot
This commit is contained in:
@@ -67,32 +67,65 @@ function getLightDarkValue(property) {
|
||||
return dark ? _dark : light;
|
||||
}
|
||||
|
||||
const red = createColor(() => getColor("red"));
|
||||
const orange = createColor(() => getColor("orange"));
|
||||
const amber = createColor(() => getColor("amber"));
|
||||
const yellow = createColor(() => getColor("yellow"));
|
||||
const avocado = createColor(() => getColor("avocado"));
|
||||
const lime = createColor(() => getColor("lime"));
|
||||
const green = createColor(() => getColor("green"));
|
||||
const emerald = createColor(() => getColor("emerald"));
|
||||
const teal = createColor(() => getColor("teal"));
|
||||
const cyan = createColor(() => getColor("cyan"));
|
||||
const sky = createColor(() => getColor("sky"));
|
||||
const blue = createColor(() => getColor("blue"));
|
||||
const indigo = createColor(() => getColor("indigo"));
|
||||
const violet = createColor(() => getColor("violet"));
|
||||
const purple = createColor(() => getColor("purple"));
|
||||
const fuchsia = createColor(() => getColor("fuchsia"));
|
||||
const pink = createColor(() => getColor("pink"));
|
||||
const rose = createColor(() => getColor("rose"));
|
||||
|
||||
export const colors = {
|
||||
default: createColor(() => getLightDarkValue("--color")),
|
||||
gray: createColor(() => getColor("gray")),
|
||||
border: createColor(() => getLightDarkValue("--border-color")),
|
||||
|
||||
red: createColor(() => getColor("red")),
|
||||
orange: createColor(() => getColor("orange")),
|
||||
amber: createColor(() => getColor("amber")),
|
||||
yellow: createColor(() => getColor("yellow")),
|
||||
avocado: createColor(() => getColor("avocado")),
|
||||
lime: createColor(() => getColor("lime")),
|
||||
green: createColor(() => getColor("green")),
|
||||
emerald: createColor(() => getColor("emerald")),
|
||||
teal: createColor(() => getColor("teal")),
|
||||
cyan: createColor(() => getColor("cyan")),
|
||||
sky: createColor(() => getColor("sky")),
|
||||
blue: createColor(() => getColor("blue")),
|
||||
indigo: createColor(() => getColor("indigo")),
|
||||
violet: createColor(() => getColor("violet")),
|
||||
purple: createColor(() => getColor("purple")),
|
||||
fuchsia: createColor(() => getColor("fuchsia")),
|
||||
pink: createColor(() => getColor("pink")),
|
||||
rose: createColor(() => getColor("rose")),
|
||||
red,
|
||||
orange,
|
||||
amber,
|
||||
yellow,
|
||||
avocado,
|
||||
lime,
|
||||
green,
|
||||
emerald,
|
||||
teal,
|
||||
cyan,
|
||||
sky,
|
||||
blue,
|
||||
indigo,
|
||||
violet,
|
||||
purple,
|
||||
fuchsia,
|
||||
pink,
|
||||
rose,
|
||||
|
||||
/** Semantic stat colors for pattern helpers */
|
||||
stat: {
|
||||
sum: blue,
|
||||
cumulative: indigo,
|
||||
avg: orange,
|
||||
max: green,
|
||||
pct90: cyan,
|
||||
pct75: blue,
|
||||
median: yellow,
|
||||
pct25: violet,
|
||||
pct10: fuchsia,
|
||||
min: red,
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* @typedef {typeof colors} Colors
|
||||
* @typedef {keyof Colors} ColorName
|
||||
* @typedef {Exclude<keyof Colors, "stat">} ColorName
|
||||
*/
|
||||
|
||||
@@ -219,6 +219,10 @@ export function createChart({ parent, id: chartId, brk, fitContent }) {
|
||||
? initialRange.to - initialRange.from
|
||||
: Infinity;
|
||||
|
||||
/** @param {number} count */
|
||||
const getDotsRadius = (count) =>
|
||||
count > 1000 ? 1 : count > 200 ? 1.5 : count > 100 ? 2 : 3;
|
||||
|
||||
/** @type {Set<ZoomChangeCallback>} */
|
||||
const onZoomChange = new Set();
|
||||
|
||||
@@ -1023,11 +1027,12 @@ export function createChart({ parent, id: chartId, brk, fitContent }) {
|
||||
|
||||
let active = defaultActive !== false;
|
||||
let highlighted = true;
|
||||
const showLastValue = options?.lastValueVisible !== false;
|
||||
|
||||
function update() {
|
||||
iseries.applyOptions({
|
||||
visible: active,
|
||||
lastValueVisible: highlighted,
|
||||
lastValueVisible: showLastValue && highlighted,
|
||||
color: color.highlight(highlighted),
|
||||
});
|
||||
}
|
||||
@@ -1117,21 +1122,21 @@ export function createChart({ parent, id: chartId, brk, fitContent }) {
|
||||
|
||||
let active = defaultActive !== false;
|
||||
let highlighted = true;
|
||||
let radius =
|
||||
visibleBarsCount > 1000 ? 1 : visibleBarsCount > 200 ? 1.5 : 2;
|
||||
let radius = getDotsRadius(visibleBarsCount);
|
||||
|
||||
function update() {
|
||||
iseries.applyOptions({
|
||||
visible: active,
|
||||
lastValueVisible: highlighted,
|
||||
color: color.highlight(highlighted),
|
||||
pointMarkersRadius: radius,
|
||||
});
|
||||
}
|
||||
update();
|
||||
|
||||
/** @type {ZoomChangeCallback} */
|
||||
function handleZoom(count) {
|
||||
const newRadius = count > 1000 ? 1 : count > 200 ? 1.5 : 2;
|
||||
const newRadius = getDotsRadius(count);
|
||||
if (newRadius === radius) return;
|
||||
radius = newRadius;
|
||||
iseries.applyOptions({ pointMarkersRadius: radius });
|
||||
@@ -1507,8 +1512,13 @@ export function createChart({ parent, id: chartId, brk, fitContent }) {
|
||||
}
|
||||
|
||||
const defaultUnit = units[0];
|
||||
const sortedUnitIds = units
|
||||
.map((u) => u.id)
|
||||
.sort()
|
||||
.join(",");
|
||||
const persistedUnit = createPersistedValue({
|
||||
defaultValue: /** @type {string} */ (defaultUnit.id),
|
||||
storageKey: `unit-${sortedUnitIds}`,
|
||||
urlKey: paneIndex === 0 ? "u0" : "u1",
|
||||
serialize: (v) => v,
|
||||
deserialize: (s) => s,
|
||||
|
||||
@@ -33,6 +33,7 @@ export function createChainSection(ctx) {
|
||||
market,
|
||||
scripts,
|
||||
supply,
|
||||
distribution,
|
||||
} = brk.metrics;
|
||||
|
||||
// Build pools tree dynamically
|
||||
@@ -49,10 +50,10 @@ export function createChainSection(ctx) {
|
||||
name: "Dominance",
|
||||
title: `${poolName} Dominance`,
|
||||
bottom: [
|
||||
line({
|
||||
dots({
|
||||
metric: pool._24hDominance,
|
||||
name: "24h",
|
||||
color: colors.orange,
|
||||
color: colors.pink,
|
||||
unit: Unit.percentage,
|
||||
defaultActive: false,
|
||||
}),
|
||||
@@ -88,7 +89,7 @@ export function createChainSection(ctx) {
|
||||
name: "Blocks mined",
|
||||
title: `${poolName} Blocks`,
|
||||
bottom: [
|
||||
line({
|
||||
dots({
|
||||
metric: pool.blocksMined.sum,
|
||||
name: "Sum",
|
||||
unit: Unit.count,
|
||||
@@ -98,6 +99,14 @@ export function createChainSection(ctx) {
|
||||
name: "Cumulative",
|
||||
color: colors.blue,
|
||||
unit: Unit.count,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: pool._24hBlocksMined,
|
||||
name: "24h Sum",
|
||||
color: colors.pink,
|
||||
unit: Unit.count,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: pool._1wBlocksMined,
|
||||
@@ -142,12 +151,17 @@ export function createChainSection(ctx) {
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Days since block",
|
||||
title: `${poolName} Last Block`,
|
||||
name: "Since last block",
|
||||
title: `${poolName} Since Last Block`,
|
||||
bottom: [
|
||||
line({
|
||||
metric: pool.blocksSinceBlock,
|
||||
name: "Blocks",
|
||||
unit: Unit.count,
|
||||
}),
|
||||
line({
|
||||
metric: pool.daysSinceBlock,
|
||||
name: "Since block",
|
||||
name: "Days",
|
||||
unit: Unit.days,
|
||||
}),
|
||||
],
|
||||
@@ -227,12 +241,35 @@ export function createChainSection(ctx) {
|
||||
title: "Transaction Count",
|
||||
bottom: fromDollarsPattern(transactions.count.txCount, Unit.count),
|
||||
},
|
||||
{
|
||||
name: "Speed",
|
||||
title: "Transactions Per Second",
|
||||
bottom: [
|
||||
dots({
|
||||
metric: transactions.volume.txPerSec,
|
||||
name: "Transactions",
|
||||
unit: Unit.perSec,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Volume",
|
||||
title: "Transaction Volume",
|
||||
bottom: [
|
||||
...satsBtcUsd(transactions.volume.sentSum, "Sent"),
|
||||
...satsBtcUsd(transactions.volume.receivedSum, "Received", colors.cyan, {
|
||||
...satsBtcUsd(
|
||||
transactions.volume.receivedSum,
|
||||
"Received",
|
||||
colors.cyan,
|
||||
{
|
||||
defaultActive: false,
|
||||
},
|
||||
),
|
||||
line({
|
||||
metric: transactions.volume.annualizedVolume.bitcoin,
|
||||
name: "annualized",
|
||||
color: colors.red,
|
||||
unit: Unit.btc,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
@@ -242,13 +279,6 @@ export function createChainSection(ctx) {
|
||||
unit: Unit.sats,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: transactions.volume.annualizedVolume.bitcoin,
|
||||
name: "annualized",
|
||||
color: colors.red,
|
||||
unit: Unit.btc,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: transactions.volume.annualizedVolume.dollars,
|
||||
name: "annualized",
|
||||
@@ -266,6 +296,11 @@ export function createChainSection(ctx) {
|
||||
...fromFeeRatePattern(transactions.size.vsize, Unit.vb),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Fee Rate",
|
||||
title: "Fee Rate",
|
||||
bottom: fromFeeRatePattern(transactions.fees.feeRate, Unit.feeRate),
|
||||
},
|
||||
{
|
||||
name: "Versions",
|
||||
title: "Transaction Versions",
|
||||
@@ -310,17 +345,6 @@ export function createChainSection(ctx) {
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Speed",
|
||||
title: "Transactions Per Second",
|
||||
bottom: [
|
||||
dots({
|
||||
metric: transactions.volume.txPerSec,
|
||||
name: "Transactions",
|
||||
unit: Unit.perSec,
|
||||
}),
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -356,6 +380,11 @@ export function createChainSection(ctx) {
|
||||
title: "Output Count",
|
||||
bottom: [...fromSizePattern(outputs.count.totalCount, Unit.count)],
|
||||
},
|
||||
{
|
||||
name: "OP_RETURN",
|
||||
title: "OP_RETURN Outputs",
|
||||
bottom: fromFullnessPattern(scripts.count.opreturn, Unit.count),
|
||||
},
|
||||
{
|
||||
name: "Speed",
|
||||
title: "Outputs Per Second",
|
||||
@@ -387,70 +416,99 @@ export function createChainSection(ctx) {
|
||||
],
|
||||
},
|
||||
|
||||
// Coinbase
|
||||
// Supply
|
||||
{
|
||||
name: "Coinbase",
|
||||
title: "Coinbase Rewards",
|
||||
bottom: fromCoinbasePattern(blocks.rewards.coinbase, "Coinbase"),
|
||||
},
|
||||
|
||||
// Subsidy
|
||||
{
|
||||
name: "Subsidy",
|
||||
title: "Block Subsidy",
|
||||
bottom: [
|
||||
...fromCoinbasePattern(blocks.rewards.subsidy, "Subsidy"),
|
||||
line({
|
||||
metric: blocks.rewards.subsidyDominance,
|
||||
name: "Dominance",
|
||||
color: colors.purple,
|
||||
unit: Unit.percentage,
|
||||
defaultActive: false,
|
||||
}),
|
||||
name: "Supply",
|
||||
tree: [
|
||||
{
|
||||
name: "Circulating",
|
||||
title: "Circulating Supply",
|
||||
bottom: fromSupplyPattern(supply.circulating, "Supply"),
|
||||
},
|
||||
{
|
||||
name: "Inflation",
|
||||
title: "Inflation Rate",
|
||||
bottom: [
|
||||
dots({
|
||||
metric: supply.inflation,
|
||||
name: "Rate",
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Unspendable",
|
||||
title: "Unspendable Supply",
|
||||
bottom: fromValuePattern(supply.burned.unspendable),
|
||||
},
|
||||
{
|
||||
name: "OP_RETURN",
|
||||
title: "OP_RETURN Supply",
|
||||
bottom: fromValuePattern(supply.burned.opreturn),
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
// Fee
|
||||
// Rewards
|
||||
{
|
||||
name: "Fee",
|
||||
name: "Rewards",
|
||||
tree: [
|
||||
{
|
||||
name: "Total",
|
||||
name: "Coinbase",
|
||||
title: "Coinbase Rewards",
|
||||
bottom: fromCoinbasePattern(blocks.rewards.coinbase),
|
||||
},
|
||||
{
|
||||
name: "Subsidy",
|
||||
title: "Block Subsidy",
|
||||
bottom: [
|
||||
...fromCoinbasePattern(blocks.rewards.subsidy),
|
||||
line({
|
||||
metric: blocks.rewards.subsidyDominance,
|
||||
name: "Dominance",
|
||||
color: colors.purple,
|
||||
unit: Unit.percentage,
|
||||
defaultActive: false,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Fee",
|
||||
title: "Transaction Fees",
|
||||
bottom: [
|
||||
line({
|
||||
metric: transactions.fees.fee.sats.sum,
|
||||
name: "Sum",
|
||||
unit: Unit.sats,
|
||||
}),
|
||||
line({
|
||||
metric: transactions.fees.fee.sats.cumulative,
|
||||
name: "Cumulative",
|
||||
color: colors.blue,
|
||||
unit: Unit.sats,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: transactions.fees.fee.bitcoin.sum,
|
||||
name: "Sum",
|
||||
name: "sum",
|
||||
unit: Unit.btc,
|
||||
}),
|
||||
line({
|
||||
metric: transactions.fees.fee.bitcoin.cumulative,
|
||||
name: "Cumulative",
|
||||
color: colors.blue,
|
||||
name: "cumulative",
|
||||
color: colors.stat.cumulative,
|
||||
unit: Unit.btc,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: transactions.fees.fee.sats.sum,
|
||||
name: "sum",
|
||||
unit: Unit.sats,
|
||||
}),
|
||||
line({
|
||||
metric: transactions.fees.fee.sats.cumulative,
|
||||
name: "cumulative",
|
||||
color: colors.stat.cumulative,
|
||||
unit: Unit.sats,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: transactions.fees.fee.dollars.sum,
|
||||
name: "Sum",
|
||||
name: "sum",
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
line({
|
||||
metric: transactions.fees.fee.dollars.cumulative,
|
||||
name: "Cumulative",
|
||||
color: colors.blue,
|
||||
name: "cumulative",
|
||||
color: colors.stat.cumulative,
|
||||
unit: Unit.usd,
|
||||
defaultActive: false,
|
||||
}),
|
||||
@@ -464,64 +522,98 @@ export function createChainSection(ctx) {
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Rate",
|
||||
title: "Fee Rate",
|
||||
bottom: [
|
||||
line({
|
||||
metric: transactions.fees.feeRate.median,
|
||||
name: "Median",
|
||||
color: colors.purple,
|
||||
unit: Unit.feeRate,
|
||||
}),
|
||||
line({
|
||||
metric: transactions.fees.feeRate.average,
|
||||
name: "Average",
|
||||
color: colors.blue,
|
||||
unit: Unit.feeRate,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: transactions.fees.feeRate.min,
|
||||
name: "Min",
|
||||
color: colors.red,
|
||||
unit: Unit.feeRate,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: transactions.fees.feeRate.max,
|
||||
name: "Max",
|
||||
color: colors.green,
|
||||
unit: Unit.feeRate,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: transactions.fees.feeRate.pct10,
|
||||
name: "pct10",
|
||||
color: colors.rose,
|
||||
unit: Unit.feeRate,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: transactions.fees.feeRate.pct25,
|
||||
name: "pct25",
|
||||
color: colors.pink,
|
||||
unit: Unit.feeRate,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: transactions.fees.feeRate.pct75,
|
||||
name: "pct75",
|
||||
color: colors.violet,
|
||||
unit: Unit.feeRate,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: transactions.fees.feeRate.pct90,
|
||||
name: "pct90",
|
||||
color: colors.fuchsia,
|
||||
unit: Unit.feeRate,
|
||||
defaultActive: false,
|
||||
}),
|
||||
name: "Unclaimed",
|
||||
title: "Unclaimed Rewards",
|
||||
bottom: fromValuePattern(
|
||||
blocks.rewards.unclaimedRewards,
|
||||
"Unclaimed",
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
// Addresses
|
||||
{
|
||||
name: "Addresses",
|
||||
tree: [
|
||||
{
|
||||
name: "Count",
|
||||
tree: [
|
||||
{
|
||||
name: "All",
|
||||
title: "Total Address Count",
|
||||
bottom: [
|
||||
line({
|
||||
metric: distribution.addrCount.all,
|
||||
name: "Loaded",
|
||||
unit: Unit.count,
|
||||
}),
|
||||
line({
|
||||
metric: distribution.emptyAddrCount.all,
|
||||
name: "Empty",
|
||||
color: colors.gray,
|
||||
unit: Unit.count,
|
||||
defaultActive: false,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "By Type",
|
||||
title: "Address Count by Type",
|
||||
bottom: [
|
||||
line({
|
||||
metric: distribution.addrCount.p2pkh,
|
||||
name: "P2PKH",
|
||||
color: colors.orange,
|
||||
unit: Unit.count,
|
||||
}),
|
||||
line({
|
||||
metric: distribution.addrCount.p2sh,
|
||||
name: "P2SH",
|
||||
color: colors.yellow,
|
||||
unit: Unit.count,
|
||||
}),
|
||||
line({
|
||||
metric: distribution.addrCount.p2wpkh,
|
||||
name: "P2WPKH",
|
||||
color: colors.green,
|
||||
unit: Unit.count,
|
||||
}),
|
||||
line({
|
||||
metric: distribution.addrCount.p2wsh,
|
||||
name: "P2WSH",
|
||||
color: colors.teal,
|
||||
unit: Unit.count,
|
||||
}),
|
||||
line({
|
||||
metric: distribution.addrCount.p2tr,
|
||||
name: "P2TR",
|
||||
color: colors.purple,
|
||||
unit: Unit.count,
|
||||
}),
|
||||
line({
|
||||
metric: distribution.addrCount.p2pk65,
|
||||
name: "P2PK65",
|
||||
color: colors.pink,
|
||||
unit: Unit.count,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: distribution.addrCount.p2pk33,
|
||||
name: "P2PK33",
|
||||
color: colors.red,
|
||||
unit: Unit.count,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: distribution.addrCount.p2a,
|
||||
name: "P2A",
|
||||
color: colors.blue,
|
||||
unit: Unit.count,
|
||||
defaultActive: false,
|
||||
}),
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -568,6 +660,13 @@ export function createChainSection(ctx) {
|
||||
unit: Unit.hashRate,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: blocks.difficulty.asHash,
|
||||
name: "Difficulty",
|
||||
color: colors.default,
|
||||
unit: Unit.hashRate,
|
||||
options: { lineStyle: 1 },
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -579,34 +678,17 @@ export function createChainSection(ctx) {
|
||||
name: "Difficulty",
|
||||
unit: Unit.difficulty,
|
||||
}),
|
||||
line({
|
||||
metric: blocks.difficulty.adjustment,
|
||||
name: "Adjustment",
|
||||
color: colors.orange,
|
||||
unit: Unit.percentage,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: blocks.difficulty.asHash,
|
||||
name: "As hash",
|
||||
color: colors.default,
|
||||
unit: Unit.hashRate,
|
||||
defaultActive: false,
|
||||
options: { lineStyle: 1 },
|
||||
}),
|
||||
line({
|
||||
metric: blocks.difficulty.blocksBeforeNextAdjustment,
|
||||
name: "Blocks until adj.",
|
||||
name: "before next",
|
||||
color: colors.indigo,
|
||||
unit: Unit.blocks,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: blocks.difficulty.daysBeforeNextAdjustment,
|
||||
name: "Days until adj.",
|
||||
name: "before next",
|
||||
color: colors.purple,
|
||||
unit: Unit.days,
|
||||
defaultActive: false,
|
||||
}),
|
||||
],
|
||||
},
|
||||
@@ -619,6 +701,7 @@ export function createChainSection(ctx) {
|
||||
name: "Difficulty Change",
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
priceLine({ ctx, number: 0, unit: Unit.percentage }),
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -701,23 +784,22 @@ export function createChainSection(ctx) {
|
||||
name: "Halving",
|
||||
title: "Halving",
|
||||
bottom: [
|
||||
line({
|
||||
metric: blocks.halving.blocksBeforeNextHalving,
|
||||
name: "Blocks before next",
|
||||
unit: Unit.blocks,
|
||||
}),
|
||||
line({
|
||||
metric: blocks.halving.daysBeforeNextHalving,
|
||||
name: "Days before next",
|
||||
color: colors.orange,
|
||||
unit: Unit.days,
|
||||
}),
|
||||
line({
|
||||
metric: blocks.halving.epoch,
|
||||
name: "Epoch",
|
||||
color: colors.purple,
|
||||
unit: Unit.epoch,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: blocks.halving.blocksBeforeNextHalving,
|
||||
name: "before next",
|
||||
unit: Unit.blocks,
|
||||
}),
|
||||
line({
|
||||
metric: blocks.halving.daysBeforeNextHalving,
|
||||
name: "before next",
|
||||
color: colors.blue,
|
||||
unit: Unit.days,
|
||||
}),
|
||||
],
|
||||
},
|
||||
@@ -725,10 +807,11 @@ export function createChainSection(ctx) {
|
||||
name: "Puell Multiple",
|
||||
title: "Puell Multiple",
|
||||
bottom: [
|
||||
line({
|
||||
baseline({
|
||||
metric: market.indicators.puellMultiple,
|
||||
name: "Puell Multiple",
|
||||
unit: Unit.ratio,
|
||||
base: 1,
|
||||
}),
|
||||
priceLine({ ctx, unit: Unit.ratio, number: 1 }),
|
||||
],
|
||||
@@ -741,60 +824,6 @@ export function createChainSection(ctx) {
|
||||
name: "Pools",
|
||||
tree: poolsTree,
|
||||
},
|
||||
|
||||
// Unspendable
|
||||
{
|
||||
name: "Unspendable",
|
||||
tree: [
|
||||
{
|
||||
name: "Supply",
|
||||
title: "Unspendable Supply",
|
||||
bottom: fromValuePattern(supply.burned.unspendable, "Supply"),
|
||||
},
|
||||
{
|
||||
name: "OP_RETURN",
|
||||
tree: [
|
||||
{
|
||||
name: "Outputs",
|
||||
title: "OP_RETURN Outputs",
|
||||
bottom: fromFullnessPattern(scripts.count.opreturn, Unit.count),
|
||||
},
|
||||
{
|
||||
name: "Supply",
|
||||
title: "OP_RETURN Supply",
|
||||
bottom: fromValuePattern(supply.burned.opreturn, "Supply"),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
// Supply
|
||||
{
|
||||
name: "Supply",
|
||||
title: "Circulating Supply",
|
||||
bottom: fromSupplyPattern(supply.circulating, "Supply"),
|
||||
},
|
||||
|
||||
// Inflation
|
||||
{
|
||||
name: "Inflation",
|
||||
title: "Inflation Rate",
|
||||
bottom: [
|
||||
line({
|
||||
metric: supply.inflation,
|
||||
name: "Rate",
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
],
|
||||
},
|
||||
|
||||
// Unclaimed Rewards
|
||||
{
|
||||
name: "Unclaimed Rewards",
|
||||
title: "Unclaimed Rewards",
|
||||
bottom: fromValuePattern(blocks.rewards.unclaimedRewards, "Unclaimed"),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,7 +6,8 @@ import {
|
||||
percentileUsdMap,
|
||||
percentileMap,
|
||||
sdPatterns,
|
||||
sdBands,
|
||||
sdBandsUsd,
|
||||
sdBandsRatio,
|
||||
} from "./shared.js";
|
||||
|
||||
/**
|
||||
@@ -56,8 +57,8 @@ function createCointimePriceWithRatioOptions(
|
||||
baseline({
|
||||
metric: ratio.ratio,
|
||||
name: "Ratio",
|
||||
color,
|
||||
unit: Unit.ratio,
|
||||
base: 1,
|
||||
}),
|
||||
line({
|
||||
metric: ratio.ratio1wSma,
|
||||
@@ -191,7 +192,7 @@ function createCointimePriceWithRatioOptions(
|
||||
title: `${title} ${titleAddon} Z-Score`,
|
||||
top: [
|
||||
line({ metric: price, name: legend, color, unit: Unit.usd }),
|
||||
...sdBands(colors, sd).map(
|
||||
...sdBandsUsd(colors, sd).map(
|
||||
({ name: bandName, prop, color: bandColor }) =>
|
||||
line({
|
||||
metric: prop,
|
||||
@@ -203,7 +204,33 @@ function createCointimePriceWithRatioOptions(
|
||||
),
|
||||
],
|
||||
bottom: [
|
||||
line({ metric: sd.zscore, name: "Z-Score", color, unit: Unit.sd }),
|
||||
baseline({
|
||||
metric: sd.zscore,
|
||||
name: "Z-Score",
|
||||
unit: Unit.sd,
|
||||
}),
|
||||
baseline({
|
||||
metric: ratio.ratio,
|
||||
name: "Ratio",
|
||||
unit: Unit.ratio,
|
||||
base: 1,
|
||||
}),
|
||||
line({
|
||||
metric: sd.sd,
|
||||
name: "Volatility",
|
||||
color: colors.gray,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
...sdBandsRatio(colors, sd).map(
|
||||
({ name: bandName, prop, color: bandColor }) =>
|
||||
line({
|
||||
metric: prop,
|
||||
name: bandName,
|
||||
color: bandColor,
|
||||
unit: Unit.ratio,
|
||||
defaultActive: false,
|
||||
}),
|
||||
),
|
||||
...priceLines({
|
||||
ctx,
|
||||
unit: Unit.sd,
|
||||
|
||||
@@ -15,35 +15,51 @@ import {
|
||||
} from "./series.js";
|
||||
import { colors } from "../chart/colors.js";
|
||||
|
||||
/**
|
||||
* @template {(arg: any, ...args: any[]) => any} F
|
||||
* @typedef {F extends (arg: any, ...args: infer P) => infer R ? (...args: P) => R : never} OmitFirstArg
|
||||
*/
|
||||
|
||||
/** @typedef {ReturnType<typeof createContext>} PartialContext */
|
||||
|
||||
/**
|
||||
* Create a context object with all dependencies for building partial options
|
||||
* @param {Object} args
|
||||
* @param {BrkClient} args.brk
|
||||
* @returns {PartialContext}
|
||||
*/
|
||||
export function createContext({ brk }) {
|
||||
return {
|
||||
colors,
|
||||
brk,
|
||||
|
||||
/** @type {OmitFirstArg<typeof fromBlockCount>} */
|
||||
fromBlockCount: (pattern, title, color) =>
|
||||
fromBlockCount(colors, pattern, title, color),
|
||||
/** @type {OmitFirstArg<typeof fromBitcoin>} */
|
||||
fromBitcoin: (pattern, title, color) =>
|
||||
fromBitcoin(colors, pattern, title, color),
|
||||
/** @type {OmitFirstArg<typeof fromBlockSize>} */
|
||||
fromBlockSize: (pattern, title, color) =>
|
||||
fromBlockSize(colors, pattern, title, color),
|
||||
/** @type {OmitFirstArg<typeof fromSizePattern>} */
|
||||
fromSizePattern: (pattern, unit, title) =>
|
||||
fromSizePattern(colors, pattern, unit, title),
|
||||
/** @type {OmitFirstArg<typeof fromFullnessPattern>} */
|
||||
fromFullnessPattern: (pattern, unit, title) =>
|
||||
fromFullnessPattern(colors, pattern, unit, title),
|
||||
/** @type {OmitFirstArg<typeof fromDollarsPattern>} */
|
||||
fromDollarsPattern: (pattern, unit, title) =>
|
||||
fromDollarsPattern(colors, pattern, unit, title),
|
||||
/** @type {OmitFirstArg<typeof fromFeeRatePattern>} */
|
||||
fromFeeRatePattern: (pattern, unit, title) =>
|
||||
fromFeeRatePattern(colors, pattern, unit, title),
|
||||
/** @type {OmitFirstArg<typeof fromCoinbasePattern>} */
|
||||
fromCoinbasePattern: (pattern, title) =>
|
||||
fromCoinbasePattern(colors, pattern, title),
|
||||
/** @type {OmitFirstArg<typeof fromValuePattern>} */
|
||||
fromValuePattern: (pattern, title, sumColor, cumulativeColor) =>
|
||||
fromValuePattern(colors, pattern, title, sumColor, cumulativeColor),
|
||||
/** @type {OmitFirstArg<typeof fromBitcoinPatternWithUnit>} */
|
||||
fromBitcoinPatternWithUnit: (
|
||||
pattern,
|
||||
title,
|
||||
@@ -59,6 +75,7 @@ export function createContext({ brk }) {
|
||||
sumColor,
|
||||
cumulativeColor,
|
||||
),
|
||||
/** @type {OmitFirstArg<typeof fromBlockCountWithUnit>} */
|
||||
fromBlockCountWithUnit: (pattern, unit, title, sumColor, cumulativeColor) =>
|
||||
fromBlockCountWithUnit(
|
||||
colors,
|
||||
@@ -68,9 +85,11 @@ export function createContext({ brk }) {
|
||||
sumColor,
|
||||
cumulativeColor,
|
||||
),
|
||||
/** @type {OmitFirstArg<typeof fromIntervalPattern>} */
|
||||
fromIntervalPattern: (pattern, unit, title, color) =>
|
||||
fromIntervalPattern(colors, pattern, unit, title, color),
|
||||
/** @type {fromSupplyPattern} */
|
||||
fromSupplyPattern: (pattern, title, color) =>
|
||||
fromSupplyPattern(colors, pattern, title, color),
|
||||
fromSupplyPattern(pattern, title, color),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ export function createAddressCohortFolder(ctx, args) {
|
||||
{
|
||||
name: "total",
|
||||
title: `Supply ${title}`,
|
||||
bottom: createGroupedSupplyTotalSeries(ctx, list),
|
||||
bottom: createGroupedSupplyTotalSeries(list),
|
||||
},
|
||||
{
|
||||
name: "in profit",
|
||||
@@ -23,6 +23,12 @@ const entries = (obj) =>
|
||||
Object.entries(obj)
|
||||
);
|
||||
|
||||
/** @type {readonly AddressableType[]} */
|
||||
const ADDRESSABLE_TYPES = ["p2pk65", "p2pk33", "p2pkh", "p2sh", "p2wpkh", "p2wsh", "p2tr", "p2a"];
|
||||
|
||||
/** @type {(key: SpendableType) => key is AddressableType} */
|
||||
const isAddressable = (key) => ADDRESSABLE_TYPES.includes(/** @type {any} */ (key));
|
||||
|
||||
/**
|
||||
* Build all cohort data from brk tree
|
||||
* @param {Colors} colors
|
||||
@@ -31,6 +37,7 @@ const entries = (obj) =>
|
||||
export function buildCohortData(colors, brk) {
|
||||
const utxoCohorts = brk.metrics.distribution.utxoCohorts;
|
||||
const addressCohorts = brk.metrics.distribution.addressCohorts;
|
||||
const { addrCount } = brk.metrics.distribution;
|
||||
const {
|
||||
TERM_NAMES,
|
||||
EPOCH_NAMES,
|
||||
@@ -51,6 +58,7 @@ export function buildCohortData(colors, brk) {
|
||||
title: "",
|
||||
color: colors.orange,
|
||||
tree: utxoCohorts.all,
|
||||
addrCount: addrCount.all,
|
||||
};
|
||||
|
||||
// Term cohorts - split because short is CohortFull, long is CohortWithPercentiles
|
||||
@@ -200,18 +208,32 @@ export function buildCohortData(colors, brk) {
|
||||
},
|
||||
);
|
||||
|
||||
// Spendable type cohorts - CohortBasic (neither adjustedSopr nor percentiles)
|
||||
/** @type {readonly CohortBasic[]} */
|
||||
const type = entries(utxoCohorts.type).map(([key, tree]) => {
|
||||
// Spendable type cohorts - split by addressability
|
||||
/** @type {readonly CohortAddress[]} */
|
||||
const typeAddressable = ADDRESSABLE_TYPES.map((key) => {
|
||||
const names = SPENDABLE_TYPE_NAMES[key];
|
||||
return {
|
||||
name: names.short,
|
||||
title: names.long,
|
||||
color: colors[spendableTypeColors[key]],
|
||||
tree,
|
||||
tree: utxoCohorts.type[key],
|
||||
addrCount: addrCount[key],
|
||||
};
|
||||
});
|
||||
|
||||
/** @type {readonly CohortBasic[]} */
|
||||
const typeOther = entries(utxoCohorts.type)
|
||||
.filter(([key]) => !isAddressable(key))
|
||||
.map(([key, tree]) => {
|
||||
const names = SPENDABLE_TYPE_NAMES[key];
|
||||
return {
|
||||
name: names.short,
|
||||
title: names.long,
|
||||
color: colors[spendableTypeColors[key]],
|
||||
tree,
|
||||
};
|
||||
});
|
||||
|
||||
// Year cohorts - CohortBasic (neither adjustedSopr nor percentiles)
|
||||
/** @type {readonly CohortBasic[]} */
|
||||
const year = entries(utxoCohorts.year).map(([key, tree]) => {
|
||||
@@ -238,7 +260,8 @@ export function buildCohortData(colors, brk) {
|
||||
addressesUnderAmount,
|
||||
utxosAmountRanges,
|
||||
addressesAmountRanges,
|
||||
type,
|
||||
typeAddressable,
|
||||
typeOther,
|
||||
year,
|
||||
};
|
||||
}
|
||||
@@ -12,6 +12,7 @@ export {
|
||||
createCohortFolderWithAdjusted,
|
||||
createCohortFolderWithPercentiles,
|
||||
createCohortFolderBasic,
|
||||
createCohortFolderAddress,
|
||||
} from "./utxo.js";
|
||||
export { createAddressCohortFolder } from "./address.js";
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { Unit } from "../../utils/units.js";
|
||||
import { priceLine } from "../constants.js";
|
||||
import { line } from "../series.js";
|
||||
import { baseline, line } from "../series.js";
|
||||
import { satsBtcUsd } from "../shared.js";
|
||||
|
||||
/**
|
||||
@@ -74,25 +74,22 @@ export function createSingleSupplySeries(ctx, cohort) {
|
||||
|
||||
/**
|
||||
* Create supply total series for grouped cohorts
|
||||
* @param {PartialContext} ctx
|
||||
* @param {readonly CohortObject[]} list
|
||||
* @returns {AnyFetchedSeriesBlueprint[]}
|
||||
*/
|
||||
export function createGroupedSupplyTotalSeries(ctx, list) {
|
||||
const { brk } = ctx;
|
||||
const constant100 = brk.metrics.constants.constant100;
|
||||
|
||||
export function createGroupedSupplyTotalSeries(list) {
|
||||
return list.flatMap(({ color, name, tree }) => [
|
||||
...satsBtcUsd(tree.supply.total, name, color),
|
||||
line({
|
||||
metric:
|
||||
"supplyRelToCirculatingSupply" in tree.relative
|
||||
? tree.relative.supplyRelToCirculatingSupply
|
||||
: constant100,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.pctSupply,
|
||||
}),
|
||||
...("supplyRelToCirculatingSupply" in tree.relative
|
||||
? [
|
||||
line({
|
||||
metric: tree.relative.supplyRelToCirculatingSupply,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.pctSupply,
|
||||
}),
|
||||
]
|
||||
: []),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -194,12 +191,12 @@ export function createRealizedPriceSeries(list) {
|
||||
*/
|
||||
export function createRealizedPriceRatioSeries(ctx, list) {
|
||||
return [
|
||||
...list.map(({ color, name, tree }) =>
|
||||
line({
|
||||
...list.map(({ name, tree }) =>
|
||||
baseline({
|
||||
metric: tree.realized.realizedPriceExtra.ratio,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.ratio,
|
||||
base: 1,
|
||||
}),
|
||||
),
|
||||
priceLine({ ctx, unit: Unit.ratio, number: 1 }),
|
||||
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,8 @@ import {
|
||||
percentileUsdMap,
|
||||
percentileMap,
|
||||
sdPatterns,
|
||||
sdBands,
|
||||
sdBandsUsd,
|
||||
sdBandsRatio,
|
||||
} from "../shared.js";
|
||||
import { periodIdToName } from "./utils.js";
|
||||
|
||||
@@ -90,7 +91,6 @@ export function createPriceWithRatioOptions(
|
||||
metric: ratio.ratio,
|
||||
name: "Ratio",
|
||||
base: 1,
|
||||
color,
|
||||
unit: Unit.ratio,
|
||||
}),
|
||||
.../** @type {const} */ ([
|
||||
@@ -222,7 +222,7 @@ export function createPriceWithRatioOptions(
|
||||
title: `${title} ${titleAddon} Z-Score`,
|
||||
top: [
|
||||
line({ metric: priceMetric, name: legend, color, unit: Unit.usd }),
|
||||
...sdBands(colors, sd).map(
|
||||
...sdBandsUsd(colors, sd).map(
|
||||
({ name: bandName, prop, color: bandColor }) =>
|
||||
line({
|
||||
metric: prop,
|
||||
@@ -237,9 +237,31 @@ export function createPriceWithRatioOptions(
|
||||
baseline({
|
||||
metric: sd.zscore,
|
||||
name: "Z-Score",
|
||||
color,
|
||||
unit: Unit.sd,
|
||||
}),
|
||||
baseline({
|
||||
metric: ratio.ratio,
|
||||
name: "Ratio",
|
||||
unit: Unit.ratio,
|
||||
base: 1,
|
||||
}),
|
||||
line({
|
||||
metric: sd.sd,
|
||||
name: "Volatility",
|
||||
color: colors.gray,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
...sdBandsRatio(colors, sd).map(
|
||||
({ name: bandName, prop, color: bandColor }) =>
|
||||
line({
|
||||
metric: prop,
|
||||
name: bandName,
|
||||
color: bandColor,
|
||||
unit: Unit.ratio,
|
||||
defaultActive: false,
|
||||
}),
|
||||
),
|
||||
priceLine({ ctx, unit: Unit.ratio, number: 1 }),
|
||||
priceLine({
|
||||
ctx,
|
||||
unit: Unit.sd,
|
||||
|
||||
@@ -8,8 +8,9 @@ import {
|
||||
createCohortFolderWithAdjusted,
|
||||
createCohortFolderWithPercentiles,
|
||||
createCohortFolderBasic,
|
||||
createCohortFolderAddress,
|
||||
createAddressCohortFolder,
|
||||
} from "./cohorts/index.js";
|
||||
} from "./distribution/index.js";
|
||||
import { createMarketSection } from "./market/index.js";
|
||||
import { createChainSection } from "./chain.js";
|
||||
import { createCointimeSection } from "./cointime.js";
|
||||
@@ -17,6 +18,7 @@ import { colors } from "../chart/colors.js";
|
||||
|
||||
// Re-export types for external consumers
|
||||
export * from "./types.js";
|
||||
export * from "./context.js";
|
||||
|
||||
/**
|
||||
* Create partial options tree
|
||||
@@ -43,17 +45,22 @@ export function createPartialOptions({ brk }) {
|
||||
addressesUnderAmount,
|
||||
utxosAmountRanges,
|
||||
addressesAmountRanges,
|
||||
type,
|
||||
typeAddressable,
|
||||
typeOther,
|
||||
year,
|
||||
} = buildCohortData(colors, brk);
|
||||
|
||||
// Helpers to map cohorts by capability type
|
||||
/** @param {CohortWithAdjusted} cohort */
|
||||
const mapWithAdjusted = (cohort) => createCohortFolderWithAdjusted(ctx, cohort);
|
||||
const mapWithAdjusted = (cohort) =>
|
||||
createCohortFolderWithAdjusted(ctx, cohort);
|
||||
/** @param {CohortWithPercentiles} cohort */
|
||||
const mapWithPercentiles = (cohort) => createCohortFolderWithPercentiles(ctx, cohort);
|
||||
const mapWithPercentiles = (cohort) =>
|
||||
createCohortFolderWithPercentiles(ctx, cohort);
|
||||
/** @param {CohortBasic} cohort */
|
||||
const mapBasic = (cohort) => createCohortFolderBasic(ctx, cohort);
|
||||
/** @param {CohortAddress} cohort */
|
||||
const mapAddress = (cohort) => createCohortFolderAddress(ctx, cohort);
|
||||
/** @param {AddressCohortObject} cohort */
|
||||
const mapAddressCohorts = (cohort) => createAddressCohortFolder(ctx, cohort);
|
||||
|
||||
@@ -81,7 +88,7 @@ export function createPartialOptions({ brk }) {
|
||||
|
||||
// Cohorts section
|
||||
{
|
||||
name: "Cohorts",
|
||||
name: "Distribution",
|
||||
tree: [
|
||||
// All UTXOs - CohortAll (adjustedSopr + percentiles but no RelToMarketCap)
|
||||
createCohortFolderAll(ctx, cohortAll),
|
||||
@@ -90,22 +97,29 @@ export function createPartialOptions({ brk }) {
|
||||
{
|
||||
name: "Terms",
|
||||
tree: [
|
||||
// Compare folder uses WithPercentiles (common capabilities)
|
||||
createCohortFolderWithPercentiles(ctx, {
|
||||
name: "Compare",
|
||||
title: "Term",
|
||||
list: [termShort, termLong],
|
||||
}),
|
||||
// Individual cohorts with their specific capabilities
|
||||
createCohortFolderFull(ctx, termShort),
|
||||
createCohortFolderWithPercentiles(ctx, termLong),
|
||||
],
|
||||
},
|
||||
|
||||
// Types - CohortBasic
|
||||
// Types - addressable types have addrCount, others don't
|
||||
{
|
||||
name: "Types",
|
||||
tree: [
|
||||
createCohortFolderBasic(ctx, {
|
||||
createCohortFolderAddress(ctx, {
|
||||
name: "Compare",
|
||||
title: "Type",
|
||||
list: type,
|
||||
list: typeAddressable,
|
||||
}),
|
||||
...type.map(mapBasic),
|
||||
...typeAddressable.map(mapAddress),
|
||||
...typeOther.map(mapBasic),
|
||||
],
|
||||
},
|
||||
|
||||
|
||||
@@ -193,8 +193,8 @@ export function fromBlockCount(colors, pattern, title, color) {
|
||||
{ metric: pattern.sum, title, color: color ?? colors.default },
|
||||
{
|
||||
metric: pattern.cumulative,
|
||||
title: `${title} (cum.)`,
|
||||
color: colors.cyan,
|
||||
title: `${title} cumulative`,
|
||||
color: colors.stat.cumulative,
|
||||
defaultActive: false,
|
||||
},
|
||||
];
|
||||
@@ -209,49 +209,55 @@ export function fromBlockCount(colors, pattern, title, color) {
|
||||
* @returns {AnyFetchedSeriesBlueprint[]}
|
||||
*/
|
||||
export function fromBitcoin(colors, pattern, title, color) {
|
||||
const { stat } = colors;
|
||||
return [
|
||||
{ metric: pattern.base, title, color: color ?? colors.default },
|
||||
{ metric: pattern.average, title: "Average", defaultActive: false },
|
||||
{
|
||||
metric: pattern.average,
|
||||
title: `${title} avg`,
|
||||
color: stat.avg,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
metric: pattern.max,
|
||||
title: "Max",
|
||||
color: colors.pink,
|
||||
title: `${title} max`,
|
||||
color: stat.max,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
metric: pattern.min,
|
||||
title: "Min",
|
||||
color: colors.green,
|
||||
title: `${title} min`,
|
||||
color: stat.min,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
metric: pattern.median,
|
||||
title: "Median",
|
||||
color: colors.amber,
|
||||
title: `${title} median`,
|
||||
color: stat.median,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
metric: pattern.pct75,
|
||||
title: "pct75",
|
||||
color: colors.red,
|
||||
title: `${title} pct75`,
|
||||
color: stat.pct75,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
metric: pattern.pct25,
|
||||
title: "pct25",
|
||||
color: colors.yellow,
|
||||
title: `${title} pct25`,
|
||||
color: stat.pct25,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
metric: pattern.pct90,
|
||||
title: "pct90",
|
||||
color: colors.rose,
|
||||
title: `${title} pct90`,
|
||||
color: stat.pct90,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
metric: pattern.pct10,
|
||||
title: "pct10",
|
||||
color: colors.lime,
|
||||
title: `${title} pct10`,
|
||||
color: stat.pct10,
|
||||
defaultActive: false,
|
||||
},
|
||||
];
|
||||
@@ -266,55 +272,61 @@ export function fromBitcoin(colors, pattern, title, color) {
|
||||
* @returns {AnyFetchedSeriesBlueprint[]}
|
||||
*/
|
||||
export function fromBlockSize(colors, pattern, title, color) {
|
||||
const { stat } = colors;
|
||||
return [
|
||||
{ metric: pattern.sum, title, color: color ?? colors.default },
|
||||
{ metric: pattern.average, title: "Average", defaultActive: false },
|
||||
{
|
||||
metric: pattern.average,
|
||||
title: `${title} avg`,
|
||||
color: stat.avg,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
metric: pattern.cumulative,
|
||||
title: `Cumulative`,
|
||||
color: colors.cyan,
|
||||
title: `${title} cumulative`,
|
||||
color: stat.cumulative,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
metric: pattern.max,
|
||||
title: "Max",
|
||||
color: colors.pink,
|
||||
title: `${title} max`,
|
||||
color: stat.max,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
metric: pattern.min,
|
||||
title: "Min",
|
||||
color: colors.green,
|
||||
title: `${title} min`,
|
||||
color: stat.min,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
metric: pattern.median,
|
||||
title: "Median",
|
||||
color: colors.amber,
|
||||
title: `${title} median`,
|
||||
color: stat.median,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
metric: pattern.pct75,
|
||||
title: "pct75",
|
||||
color: colors.red,
|
||||
title: `${title} pct75`,
|
||||
color: stat.pct75,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
metric: pattern.pct25,
|
||||
title: "pct25",
|
||||
color: colors.yellow,
|
||||
title: `${title} pct25`,
|
||||
color: stat.pct25,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
metric: pattern.pct90,
|
||||
title: "pct90",
|
||||
color: colors.rose,
|
||||
title: `${title} pct90`,
|
||||
color: stat.pct90,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
metric: pattern.pct10,
|
||||
title: "pct10",
|
||||
color: colors.lime,
|
||||
title: `${title} pct10`,
|
||||
color: stat.pct10,
|
||||
defaultActive: false,
|
||||
},
|
||||
];
|
||||
@@ -329,68 +341,69 @@ export function fromBlockSize(colors, pattern, title, color) {
|
||||
* @returns {AnyFetchedSeriesBlueprint[]}
|
||||
*/
|
||||
export function fromSizePattern(colors, pattern, unit, title = "") {
|
||||
const { stat } = colors;
|
||||
return [
|
||||
{ metric: pattern.average, title: `${title} avg`.trim(), unit },
|
||||
{
|
||||
metric: pattern.sum,
|
||||
title: `${title} sum`.trim(),
|
||||
color: colors.blue,
|
||||
color: stat.sum,
|
||||
unit,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
metric: pattern.cumulative,
|
||||
title: `${title} cumulative`.trim(),
|
||||
color: colors.indigo,
|
||||
unit,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
metric: pattern.min,
|
||||
title: `${title} min`.trim(),
|
||||
color: colors.red,
|
||||
color: stat.cumulative,
|
||||
unit,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
metric: pattern.max,
|
||||
title: `${title} max`.trim(),
|
||||
color: colors.green,
|
||||
color: stat.max,
|
||||
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,
|
||||
metric: pattern.min,
|
||||
title: `${title} min`.trim(),
|
||||
color: stat.min,
|
||||
unit,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
metric: pattern.median,
|
||||
title: `${title} median`.trim(),
|
||||
color: colors.purple,
|
||||
color: stat.median,
|
||||
unit,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
metric: pattern.pct75,
|
||||
title: `${title} pct75`.trim(),
|
||||
color: colors.violet,
|
||||
color: stat.pct75,
|
||||
unit,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
metric: pattern.pct25,
|
||||
title: `${title} pct25`.trim(),
|
||||
color: stat.pct25,
|
||||
unit,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
metric: pattern.pct90,
|
||||
title: `${title} pct90`.trim(),
|
||||
color: colors.fuchsia,
|
||||
color: stat.pct90,
|
||||
unit,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
metric: pattern.pct10,
|
||||
title: `${title} pct10`.trim(),
|
||||
color: stat.pct10,
|
||||
unit,
|
||||
defaultActive: false,
|
||||
},
|
||||
@@ -406,61 +419,61 @@ export function fromSizePattern(colors, pattern, unit, title = "") {
|
||||
* @returns {AnyFetchedSeriesBlueprint[]}
|
||||
*/
|
||||
export function fromFullnessPattern(colors, pattern, unit, title = "") {
|
||||
const { stat } = colors;
|
||||
return [
|
||||
{ metric: pattern.base, title: title || "base", unit },
|
||||
{
|
||||
metric: pattern.average,
|
||||
title: `${title} avg`.trim(),
|
||||
color: colors.purple,
|
||||
color: stat.avg,
|
||||
unit,
|
||||
},
|
||||
{
|
||||
metric: pattern.max,
|
||||
title: `${title} max`.trim(),
|
||||
color: stat.max,
|
||||
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,
|
||||
color: stat.min,
|
||||
unit,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
metric: pattern.median,
|
||||
title: `${title} median`.trim(),
|
||||
color: colors.violet,
|
||||
color: stat.median,
|
||||
unit,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
metric: pattern.pct75,
|
||||
title: `${title} pct75`.trim(),
|
||||
color: colors.fuchsia,
|
||||
color: stat.pct75,
|
||||
unit,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
metric: pattern.pct25,
|
||||
title: `${title} pct25`.trim(),
|
||||
color: stat.pct25,
|
||||
unit,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
metric: pattern.pct90,
|
||||
title: `${title} pct90`.trim(),
|
||||
color: colors.amber,
|
||||
color: stat.pct90,
|
||||
unit,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
metric: pattern.pct10,
|
||||
title: `${title} pct10`.trim(),
|
||||
color: stat.pct10,
|
||||
unit,
|
||||
defaultActive: false,
|
||||
},
|
||||
@@ -476,74 +489,75 @@ export function fromFullnessPattern(colors, pattern, unit, title = "") {
|
||||
* @returns {AnyFetchedSeriesBlueprint[]}
|
||||
*/
|
||||
export function fromDollarsPattern(colors, pattern, unit, title = "") {
|
||||
const { stat } = colors;
|
||||
return [
|
||||
{ metric: pattern.base, title: title || "base", unit },
|
||||
{
|
||||
metric: pattern.sum,
|
||||
title: `${title} sum`.trim(),
|
||||
color: colors.blue,
|
||||
color: stat.sum,
|
||||
unit,
|
||||
},
|
||||
{
|
||||
metric: pattern.cumulative,
|
||||
title: `${title} cumulative`.trim(),
|
||||
color: colors.cyan,
|
||||
color: stat.cumulative,
|
||||
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,
|
||||
color: stat.avg,
|
||||
unit,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
metric: pattern.max,
|
||||
title: `${title} max`.trim(),
|
||||
color: colors.green,
|
||||
color: stat.max,
|
||||
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,
|
||||
metric: pattern.min,
|
||||
title: `${title} min`.trim(),
|
||||
color: stat.min,
|
||||
unit,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
metric: pattern.median,
|
||||
title: `${title} median`.trim(),
|
||||
color: colors.violet,
|
||||
color: stat.median,
|
||||
unit,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
metric: pattern.pct75,
|
||||
title: `${title} pct75`.trim(),
|
||||
color: colors.fuchsia,
|
||||
color: stat.pct75,
|
||||
unit,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
metric: pattern.pct25,
|
||||
title: `${title} pct25`.trim(),
|
||||
color: stat.pct25,
|
||||
unit,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
metric: pattern.pct90,
|
||||
title: `${title} pct90`.trim(),
|
||||
color: colors.amber,
|
||||
color: stat.pct90,
|
||||
unit,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
metric: pattern.pct10,
|
||||
title: `${title} pct10`.trim(),
|
||||
color: stat.pct10,
|
||||
unit,
|
||||
defaultActive: false,
|
||||
},
|
||||
@@ -559,54 +573,67 @@ export function fromDollarsPattern(colors, pattern, unit, title = "") {
|
||||
* @returns {AnyFetchedSeriesBlueprint[]}
|
||||
*/
|
||||
export function fromFeeRatePattern(colors, pattern, unit, title = "") {
|
||||
const { stat } = colors;
|
||||
return [
|
||||
{ metric: pattern.average, title: `${title} avg`.trim(), unit },
|
||||
{
|
||||
metric: pattern.min,
|
||||
title: `${title} min`.trim(),
|
||||
color: colors.red,
|
||||
type: "Dots",
|
||||
metric: pattern.average,
|
||||
title: `${title} avg`.trim(),
|
||||
unit,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
type: "Dots",
|
||||
metric: pattern.max,
|
||||
title: `${title} max`.trim(),
|
||||
color: colors.green,
|
||||
color: stat.max,
|
||||
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,
|
||||
type: "Dots",
|
||||
metric: pattern.min,
|
||||
title: `${title} min`.trim(),
|
||||
color: stat.min,
|
||||
unit,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
type: "Dots",
|
||||
metric: pattern.median,
|
||||
title: `${title} median`.trim(),
|
||||
color: colors.purple,
|
||||
color: stat.median,
|
||||
unit,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
type: "Dots",
|
||||
metric: pattern.pct75,
|
||||
title: `${title} pct75`.trim(),
|
||||
color: colors.violet,
|
||||
color: stat.pct75,
|
||||
unit,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
type: "Dots",
|
||||
metric: pattern.pct25,
|
||||
title: `${title} pct25`.trim(),
|
||||
color: stat.pct25,
|
||||
unit,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
type: "Dots",
|
||||
metric: pattern.pct90,
|
||||
title: `${title} pct90`.trim(),
|
||||
color: colors.fuchsia,
|
||||
color: stat.pct90,
|
||||
unit,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
type: "Dots",
|
||||
metric: pattern.pct10,
|
||||
title: `${title} pct10`.trim(),
|
||||
color: stat.pct10,
|
||||
unit,
|
||||
defaultActive: false,
|
||||
},
|
||||
@@ -617,13 +644,13 @@ export function fromFeeRatePattern(colors, pattern, unit, title = "") {
|
||||
* Create series from a CoinbasePattern ({ sats, bitcoin, dollars } each as FullnessPattern)
|
||||
* @param {Colors} colors
|
||||
* @param {CoinbasePattern} pattern
|
||||
* @param {string} title
|
||||
* @param {string} [title]
|
||||
* @returns {AnyFetchedSeriesBlueprint[]}
|
||||
*/
|
||||
export function fromCoinbasePattern(colors, pattern, title) {
|
||||
return [
|
||||
...fromFullnessPattern(colors, pattern.sats, Unit.sats, title),
|
||||
...fromFullnessPattern(colors, pattern.bitcoin, Unit.btc, title),
|
||||
...fromFullnessPattern(colors, pattern.sats, Unit.sats, title),
|
||||
...fromFullnessPattern(colors, pattern.dollars, Unit.usd, title),
|
||||
];
|
||||
}
|
||||
@@ -632,7 +659,7 @@ export function fromCoinbasePattern(colors, pattern, title) {
|
||||
* Create series from a ValuePattern ({ sats, bitcoin, dollars } each as BlockCountPattern with sum + cumulative)
|
||||
* @param {Colors} colors
|
||||
* @param {ValuePattern} pattern
|
||||
* @param {string} title
|
||||
* @param {string} [title]
|
||||
* @param {Color} [sumColor]
|
||||
* @param {Color} [cumulativeColor]
|
||||
* @returns {AnyFetchedSeriesBlueprint[]}
|
||||
@@ -640,47 +667,47 @@ export function fromCoinbasePattern(colors, pattern, title) {
|
||||
export function fromValuePattern(
|
||||
colors,
|
||||
pattern,
|
||||
title,
|
||||
title = "",
|
||||
sumColor,
|
||||
cumulativeColor,
|
||||
) {
|
||||
return [
|
||||
{
|
||||
metric: pattern.sats.sum,
|
||||
title,
|
||||
color: sumColor,
|
||||
unit: Unit.sats,
|
||||
},
|
||||
{
|
||||
metric: pattern.sats.cumulative,
|
||||
title: `${title} cumulative`,
|
||||
color: cumulativeColor ?? colors.blue,
|
||||
unit: Unit.sats,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
metric: pattern.bitcoin.sum,
|
||||
title,
|
||||
title: title || "sum",
|
||||
color: sumColor,
|
||||
unit: Unit.btc,
|
||||
},
|
||||
{
|
||||
metric: pattern.bitcoin.cumulative,
|
||||
title: `${title} cumulative`,
|
||||
color: cumulativeColor ?? colors.blue,
|
||||
title: `${title} cumulative`.trim(),
|
||||
color: cumulativeColor ?? colors.stat.cumulative,
|
||||
unit: Unit.btc,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
metric: pattern.sats.sum,
|
||||
title: title || "sum",
|
||||
color: sumColor,
|
||||
unit: Unit.sats,
|
||||
},
|
||||
{
|
||||
metric: pattern.sats.cumulative,
|
||||
title: `${title} cumulative`.trim(),
|
||||
color: cumulativeColor ?? colors.stat.cumulative,
|
||||
unit: Unit.sats,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
metric: pattern.dollars.sum,
|
||||
title,
|
||||
title: title || "sum",
|
||||
color: sumColor,
|
||||
unit: Unit.usd,
|
||||
},
|
||||
{
|
||||
metric: pattern.dollars.cumulative,
|
||||
title: `${title} cumulative`,
|
||||
color: cumulativeColor ?? colors.blue,
|
||||
title: `${title} cumulative`.trim(),
|
||||
color: cumulativeColor ?? colors.stat.cumulative,
|
||||
unit: Unit.usd,
|
||||
defaultActive: false,
|
||||
},
|
||||
@@ -715,7 +742,7 @@ export function fromBitcoinPatternWithUnit(
|
||||
{
|
||||
metric: pattern.cumulative,
|
||||
title: `${title} cumulative`,
|
||||
color: cumulativeColor ?? colors.blue,
|
||||
color: cumulativeColor ?? colors.stat.cumulative,
|
||||
unit,
|
||||
defaultActive: false,
|
||||
},
|
||||
@@ -750,7 +777,7 @@ export function fromBlockCountWithUnit(
|
||||
{
|
||||
metric: pattern.cumulative,
|
||||
title: `${title} cumulative`.trim(),
|
||||
color: cumulativeColor ?? colors.blue,
|
||||
color: cumulativeColor ?? colors.stat.cumulative,
|
||||
unit,
|
||||
defaultActive: false,
|
||||
},
|
||||
@@ -767,61 +794,62 @@ export function fromBlockCountWithUnit(
|
||||
* @returns {AnyFetchedSeriesBlueprint[]}
|
||||
*/
|
||||
export function fromIntervalPattern(colors, pattern, unit, title = "", color) {
|
||||
const { stat } = colors;
|
||||
return [
|
||||
{ metric: pattern.base, title: title ?? "base", color, unit },
|
||||
{
|
||||
metric: pattern.average,
|
||||
title: `${title} avg`.trim(),
|
||||
color: colors.cyan,
|
||||
unit,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
metric: pattern.min,
|
||||
title: `${title} min`.trim(),
|
||||
color: colors.red,
|
||||
color: stat.avg,
|
||||
unit,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
metric: pattern.max,
|
||||
title: `${title} max`.trim(),
|
||||
color: colors.green,
|
||||
color: stat.max,
|
||||
unit,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
metric: pattern.min,
|
||||
title: `${title} min`.trim(),
|
||||
color: stat.min,
|
||||
unit,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
metric: pattern.median,
|
||||
title: `${title} median`.trim(),
|
||||
color: colors.violet,
|
||||
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,
|
||||
color: stat.median,
|
||||
unit,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
metric: pattern.pct75,
|
||||
title: `${title} pct75`.trim(),
|
||||
color: colors.fuchsia,
|
||||
color: stat.pct75,
|
||||
unit,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
metric: pattern.pct25,
|
||||
title: `${title} pct25`.trim(),
|
||||
color: stat.pct25,
|
||||
unit,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
metric: pattern.pct90,
|
||||
title: `${title} pct90`.trim(),
|
||||
color: colors.amber,
|
||||
color: stat.pct90,
|
||||
unit,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
metric: pattern.pct10,
|
||||
title: `${title} pct10`.trim(),
|
||||
color: stat.pct10,
|
||||
unit,
|
||||
defaultActive: false,
|
||||
},
|
||||
@@ -830,30 +858,29 @@ export function fromIntervalPattern(colors, pattern, unit, title = "", color) {
|
||||
|
||||
/**
|
||||
* Create series from a SupplyPattern (sats/bitcoin/dollars, no sum/cumulative)
|
||||
* @param {Colors} colors
|
||||
* @param {SupplyPattern} pattern
|
||||
* @param {string} title
|
||||
* @param {Color} [color]
|
||||
* @returns {AnyFetchedSeriesBlueprint[]}
|
||||
*/
|
||||
export function fromSupplyPattern(colors, pattern, title, color) {
|
||||
export function fromSupplyPattern(pattern, title, color) {
|
||||
return [
|
||||
{
|
||||
metric: pattern.sats,
|
||||
title,
|
||||
color: color ?? colors.default,
|
||||
unit: Unit.sats,
|
||||
},
|
||||
{
|
||||
metric: pattern.bitcoin,
|
||||
title,
|
||||
color: color ?? colors.default,
|
||||
color,
|
||||
unit: Unit.btc,
|
||||
},
|
||||
{
|
||||
metric: pattern.sats,
|
||||
title,
|
||||
color,
|
||||
unit: Unit.sats,
|
||||
},
|
||||
{
|
||||
metric: pattern.dollars,
|
||||
title,
|
||||
color: color ?? colors.default,
|
||||
color,
|
||||
unit: Unit.usd,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/** Shared helpers for options */
|
||||
|
||||
import { Unit } from "../utils/units.js";
|
||||
import { line } from "./series.js";
|
||||
import { line, baseline } from "./series.js";
|
||||
import { priceLine, priceLines } from "./constants.js";
|
||||
|
||||
/**
|
||||
* Create sats/btc/usd line series from a pattern with .sats/.bitcoin/.dollars
|
||||
@@ -14,7 +15,6 @@ import { line } from "./series.js";
|
||||
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,
|
||||
@@ -22,6 +22,7 @@ export function satsBtcUsd(pattern, name, color, options) {
|
||||
unit: Unit.btc,
|
||||
defaultActive,
|
||||
}),
|
||||
line({ metric: pattern.sats, name, color, unit: Unit.sats, defaultActive }),
|
||||
line({
|
||||
metric: pattern.dollars,
|
||||
name,
|
||||
@@ -82,7 +83,7 @@ export function sdPatterns(ratio) {
|
||||
* @param {Colors} colors
|
||||
* @param {Ratio1ySdPattern} sd
|
||||
*/
|
||||
export function sdBands(colors, sd) {
|
||||
export function sdBandsUsd(colors, sd) {
|
||||
return /** @type {const} */ ([
|
||||
{ name: "0σ", prop: sd._0sdUsd, color: colors.lime },
|
||||
{ name: "+0.5σ", prop: sd.p05sdUsd, color: colors.yellow },
|
||||
@@ -95,7 +96,249 @@ export function sdBands(colors, sd) {
|
||||
{ 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.p3sdUsd, color: colors.pink },
|
||||
{ name: "−3σ", prop: sd.m3sdUsd, color: colors.violet },
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build SD band mappings (ratio) from an SD pattern
|
||||
* @param {Colors} colors
|
||||
* @param {Ratio1ySdPattern} sd
|
||||
*/
|
||||
export function sdBandsRatio(colors, sd) {
|
||||
return /** @type {const} */ ([
|
||||
{ name: "0σ", prop: sd.sma, color: colors.lime },
|
||||
{ name: "+0.5σ", prop: sd.p05sd, color: colors.yellow },
|
||||
{ name: "−0.5σ", prop: sd.m05sd, color: colors.teal },
|
||||
{ name: "+1σ", prop: sd.p1sd, color: colors.amber },
|
||||
{ name: "−1σ", prop: sd.m1sd, color: colors.cyan },
|
||||
{ name: "+1.5σ", prop: sd.p15sd, color: colors.orange },
|
||||
{ name: "−1.5σ", prop: sd.m15sd, color: colors.sky },
|
||||
{ name: "+2σ", prop: sd.p2sd, color: colors.red },
|
||||
{ name: "−2σ", prop: sd.m2sd, color: colors.blue },
|
||||
{ name: "+2.5σ", prop: sd.p25sd, color: colors.rose },
|
||||
{ name: "−2.5σ", prop: sd.m25sd, color: colors.indigo },
|
||||
{ name: "+3σ", prop: sd.p3sd, color: colors.pink },
|
||||
{ name: "−3σ", prop: sd.m3sd, color: colors.violet },
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build ratio SMA series from a ratio pattern
|
||||
* @param {Colors} colors
|
||||
* @param {ActivePriceRatioPattern} ratio
|
||||
*/
|
||||
export function ratioSmas(colors, ratio) {
|
||||
return /** @type {const} */ ([
|
||||
{ name: "1w SMA", metric: ratio.ratio1wSma, color: colors.lime },
|
||||
{ name: "1m SMA", metric: ratio.ratio1mSma, color: colors.teal },
|
||||
{ name: "1y SMA", metric: ratio.ratio1ySd.sma, color: colors.sky },
|
||||
{ name: "2y SMA", metric: ratio.ratio2ySd.sma, color: colors.indigo },
|
||||
{ name: "4y SMA", metric: ratio.ratio4ySd.sma, color: colors.purple },
|
||||
{ name: "All SMA", metric: ratio.ratioSd.sma, color: colors.rose },
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create ratio chart from ActivePriceRatioPattern
|
||||
* @param {PartialContext} ctx
|
||||
* @param {Object} args
|
||||
* @param {string} args.title
|
||||
* @param {AnyMetricPattern} args.price - The price metric to show in top pane
|
||||
* @param {ActivePriceRatioPattern} args.ratio - The ratio pattern
|
||||
* @param {Color} args.color
|
||||
* @returns {PartialChartOption}
|
||||
*/
|
||||
export function createRatioChart(ctx, { title, price, ratio, color }) {
|
||||
const { colors } = ctx;
|
||||
|
||||
return {
|
||||
name: "ratio",
|
||||
title: `${title} Ratio`,
|
||||
top: [
|
||||
line({ metric: price, name: "price", color, unit: Unit.usd }),
|
||||
...percentileUsdMap(colors, ratio).map(({ name, prop, color }) =>
|
||||
line({
|
||||
metric: prop,
|
||||
name,
|
||||
color,
|
||||
defaultActive: false,
|
||||
unit: Unit.usd,
|
||||
options: { lineStyle: 1 },
|
||||
}),
|
||||
),
|
||||
],
|
||||
bottom: [
|
||||
baseline({
|
||||
metric: ratio.ratio,
|
||||
name: "Ratio",
|
||||
unit: Unit.ratio,
|
||||
base: 1,
|
||||
}),
|
||||
...ratioSmas(colors, ratio).map(({ name, metric, color }) =>
|
||||
line({ metric, name, color, unit: Unit.ratio, defaultActive: false }),
|
||||
),
|
||||
...percentileMap(colors, ratio).map(({ name, prop, color }) =>
|
||||
line({
|
||||
metric: prop,
|
||||
name,
|
||||
color,
|
||||
defaultActive: false,
|
||||
unit: Unit.ratio,
|
||||
options: { lineStyle: 1 },
|
||||
}),
|
||||
),
|
||||
priceLine({ ctx, unit: Unit.ratio, number: 1 }),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create ZScores folder from ActivePriceRatioPattern
|
||||
* @param {PartialContext} ctx
|
||||
* @param {Object} args
|
||||
* @param {string} args.title
|
||||
* @param {string} args.legend
|
||||
* @param {AnyMetricPattern} args.price - The price metric to show in top pane
|
||||
* @param {ActivePriceRatioPattern} args.ratio - The ratio pattern
|
||||
* @param {Color} args.color
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
export function createZScoresFolder(
|
||||
ctx,
|
||||
{ title, legend, price, ratio, color },
|
||||
) {
|
||||
const { colors } = ctx;
|
||||
const sdPats = sdPatterns(ratio);
|
||||
|
||||
return {
|
||||
name: "ZScores",
|
||||
tree: [
|
||||
{
|
||||
name: "Compare",
|
||||
title: `${title} Z-Scores`,
|
||||
top: [
|
||||
line({ metric: price, name: legend, color, unit: Unit.usd }),
|
||||
line({
|
||||
metric: ratio.ratio1ySd._0sdUsd,
|
||||
name: "1y 0σ",
|
||||
color: colors.orange,
|
||||
defaultActive: false,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
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: price, name: legend, color, unit: Unit.usd }),
|
||||
...sdBandsUsd(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",
|
||||
unit: Unit.sd,
|
||||
}),
|
||||
baseline({
|
||||
metric: ratio.ratio,
|
||||
name: "Ratio",
|
||||
unit: Unit.ratio,
|
||||
base: 1,
|
||||
}),
|
||||
line({
|
||||
metric: sd.sd,
|
||||
name: "Volatility",
|
||||
color: colors.gray,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
...sdBandsRatio(colors, sd).map(
|
||||
({ name: bandName, prop, color: bandColor }) =>
|
||||
line({
|
||||
metric: prop,
|
||||
name: bandName,
|
||||
color: bandColor,
|
||||
unit: Unit.ratio,
|
||||
defaultActive: false,
|
||||
}),
|
||||
),
|
||||
priceLine({ ctx, unit: Unit.ratio, number: 1 }),
|
||||
priceLine({
|
||||
ctx,
|
||||
unit: Unit.sd,
|
||||
}),
|
||||
...priceLines({
|
||||
ctx,
|
||||
unit: Unit.sd,
|
||||
numbers: [1, -1, 2, -2, 3, -3],
|
||||
defaultActive: false,
|
||||
}),
|
||||
],
|
||||
})),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -160,6 +160,7 @@
|
||||
* @property {string} title
|
||||
* @property {Color} color
|
||||
* @property {PatternAll} tree
|
||||
* @property {Brk.MetricPattern1<Brk.StoredU64>} addrCount
|
||||
*
|
||||
* Full cohort: adjustedSopr + percentiles + RelToMarketCap (term.short)
|
||||
* @typedef {Object} CohortFull
|
||||
@@ -190,6 +191,13 @@
|
||||
* @property {PatternBasic} tree
|
||||
*
|
||||
* ============================================================================
|
||||
* Extended Cohort Types (with address count)
|
||||
* ============================================================================
|
||||
*
|
||||
* Basic cohort with address count (for "type" cohorts)
|
||||
* @typedef {CohortBasic & { addrCount: Brk.MetricPattern1<Brk.StoredU64> }} CohortAddress
|
||||
*
|
||||
* ============================================================================
|
||||
* Cohort Group Types (by capability)
|
||||
* ============================================================================
|
||||
*
|
||||
@@ -233,23 +241,6 @@
|
||||
* @property {readonly AddressCohortObject[]} list
|
||||
*
|
||||
* @typedef {UtxoCohortGroupObject | AddressCohortGroupObject} CohortGroupObject
|
||||
*
|
||||
* @typedef {Object} PartialContext
|
||||
* @property {Colors} colors
|
||||
* @property {BrkClient} brk
|
||||
* @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, 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>, 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
|
||||
*/
|
||||
|
||||
// Re-export for type consumers
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
*
|
||||
* @import { WebSockets } from "./utils/ws.js"
|
||||
*
|
||||
* @import { Option, PartialChartOption, ChartOption, AnyPartialOption, ProcessedOptionAddons, OptionsTree, SimulationOption, AnySeriesBlueprint, SeriesType, AnyFetchedSeriesBlueprint, TableOption, ExplorerOption, UrlOption, PartialOptionsGroup, OptionsGroup, PartialOptionsTree, UtxoCohortObject, AddressCohortObject, CohortObject, CohortGroupObject, FetchedLineSeriesBlueprint, FetchedBaselineSeriesBlueprint, FetchedHistogramSeriesBlueprint, PartialContext, PatternAll, PatternFull, PatternWithAdjusted, PatternWithPercentiles, PatternBasic, CohortAll, CohortFull, CohortWithAdjusted, CohortWithPercentiles, CohortBasic, CohortGroupFull, CohortGroupWithAdjusted, CohortGroupWithPercentiles, CohortGroupBasic, UtxoCohortGroupObject, AddressCohortGroupObject, FetchedDotsSeriesBlueprint, FetchedCandlestickSeriesBlueprint } from "./options/partial.js"
|
||||
* @import { Option, PartialChartOption, ChartOption, AnyPartialOption, ProcessedOptionAddons, OptionsTree, SimulationOption, AnySeriesBlueprint, SeriesType, AnyFetchedSeriesBlueprint, TableOption, ExplorerOption, UrlOption, PartialOptionsGroup, OptionsGroup, PartialOptionsTree, UtxoCohortObject, AddressCohortObject, CohortObject, CohortGroupObject, FetchedLineSeriesBlueprint, FetchedBaselineSeriesBlueprint, FetchedHistogramSeriesBlueprint, PartialContext, PatternAll, PatternFull, PatternWithAdjusted, PatternWithPercentiles, PatternBasic, CohortAll, CohortFull, CohortWithAdjusted, CohortWithPercentiles, CohortBasic, CohortAddress, CohortGroupFull, CohortGroupWithAdjusted, CohortGroupWithPercentiles, CohortGroupBasic, UtxoCohortGroupObject, AddressCohortGroupObject, FetchedDotsSeriesBlueprint, FetchedCandlestickSeriesBlueprint } from "./options/partial.js"
|
||||
*
|
||||
*
|
||||
* @import { UnitObject as Unit } from "./utils/units.js"
|
||||
@@ -41,7 +41,7 @@
|
||||
* @typedef {Brk._10yTo12yPattern} AgeRangePattern
|
||||
* @typedef {Brk._0satsPattern2} UtxoAmountPattern
|
||||
* @typedef {Brk._0satsPattern} AddressAmountPattern
|
||||
* @typedef {Brk._100btcPattern} BasicUtxoPattern
|
||||
* @typedef {Brk._0Pattern} BasicUtxoPattern
|
||||
* @typedef {Brk._0satsPattern2} EpochPattern
|
||||
* @typedef {Brk.Ratio1ySdPattern} Ratio1ySdPattern
|
||||
* @typedef {Brk.Dollars} Dollars
|
||||
@@ -53,6 +53,8 @@
|
||||
* @typedef {Brk.AnyMetricEndpointBuilder} AnyMetricEndpoint
|
||||
* @typedef {Brk.AnyMetricData} AnyMetricData
|
||||
* @typedef {Brk.AddrCountPattern} AddrCountPattern
|
||||
* @typedef {keyof Brk.MetricsTree_Distribution_UtxoCohorts_Type} SpendableType
|
||||
* @typedef {keyof Brk.MetricsTree_Distribution_AnyAddressIndexes} AddressableType
|
||||
* @typedef {FullnessPattern<any>} IntervalPattern
|
||||
* @typedef {Brk.MetricsTree_Supply_Circulating} SupplyPattern
|
||||
* @typedef {Brk.RelativePattern} GlobalRelativePattern
|
||||
|
||||
@@ -31,7 +31,7 @@ export const numberToPercentage = new Intl.NumberFormat("en-US", {
|
||||
export function numberToShortUSFormat(value, digits) {
|
||||
const absoluteValue = Math.abs(value);
|
||||
|
||||
if (isNaN(value)) {
|
||||
if (isNaN(value) || !isFinite(value)) {
|
||||
return "";
|
||||
} else if (absoluteValue < 10) {
|
||||
return numberToUSNumber(value, Math.min(3, digits || 10));
|
||||
@@ -41,13 +41,13 @@ export function numberToShortUSFormat(value, digits) {
|
||||
return numberToUSNumber(value, Math.min(1, digits || 10));
|
||||
} else if (absoluteValue < 1_000_000) {
|
||||
return numberToUSNumber(value, 0);
|
||||
} else if (absoluteValue >= 1_000_000_000_000_000_000_000) {
|
||||
} else if (absoluteValue >= 1e27) {
|
||||
return "Inf.";
|
||||
}
|
||||
|
||||
const log = Math.floor(Math.log10(absoluteValue) - 6);
|
||||
|
||||
const suffices = ["M", "B", "T", "P", "E", "Z"];
|
||||
const suffices = ["M", "B", "T", "P", "E", "Z", "Y"];
|
||||
const letterIndex = Math.floor(log / 3);
|
||||
const letter = suffices[letterIndex];
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ export const Unit = /** @type {const} */ ({
|
||||
sd: { id: "sd", name: "Std Dev" },
|
||||
|
||||
// Relative percentages
|
||||
pctSupply: { id: "pct-supply", name: "% of Supply" },
|
||||
pctSupply: { id: "pct-supply", name: "% of circulating Supply" },
|
||||
pctOwn: { id: "pct-own", name: "% of Own Supply" },
|
||||
pctMcap: { id: "pct-mcap", name: "% of Market Cap" },
|
||||
pctRcap: { id: "pct-rcap", name: "% of Realized Cap" },
|
||||
|
||||
Reference in New Issue
Block a user