mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-04-28 08:39:59 -07:00
global: snapshot
This commit is contained in:
@@ -4,17 +4,18 @@ import { Unit } from "../utils/units.js";
|
||||
import { priceLine } from "./constants.js";
|
||||
import { line, baseline, dots } from "./series.js";
|
||||
import { satsBtcUsd } from "./shared.js";
|
||||
import { spendableTypeColors } from "./colors/index.js";
|
||||
|
||||
/** Major pools to show in Compare section (by current hashrate dominance) */
|
||||
const MAJOR_POOL_IDS = [
|
||||
"foundryusa", // ~32% - largest pool
|
||||
"antpool", // ~18% - Bitmain-owned
|
||||
"viabtc", // ~14% - independent
|
||||
"f2pool", // ~10% - one of the oldest pools
|
||||
"marapool", // MARA Holdings
|
||||
"braiinspool", // formerly Slush Pool
|
||||
"spiderpool", // growing Asian pool
|
||||
"ocean", // decentralization-focused
|
||||
"foundryusa", // ~32% - largest pool
|
||||
"antpool", // ~18% - Bitmain-owned
|
||||
"viabtc", // ~14% - independent
|
||||
"f2pool", // ~10% - one of the oldest pools
|
||||
"marapool", // MARA Holdings
|
||||
"braiinspool", // formerly Slush Pool
|
||||
"spiderpool", // growing Asian pool
|
||||
"ocean", // decentralization-focused
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -23,16 +24,16 @@ const MAJOR_POOL_IDS = [
|
||||
* Collectively ~35-40% of network hashrate
|
||||
*/
|
||||
const ANTPOOL_AND_FRIENDS_IDS = [
|
||||
"antpool", // Bitmain-owned, template source
|
||||
"poolin", // shares AntPool templates
|
||||
"btccom", // CloverPool (formerly BTC.com)
|
||||
"braiinspool", // shares AntPool templates
|
||||
"ultimuspool", // shares AntPool templates
|
||||
"binancepool", // shares AntPool templates
|
||||
"secpool", // shares AntPool templates
|
||||
"antpool", // Bitmain-owned, template source
|
||||
"poolin", // shares AntPool templates
|
||||
"btccom", // CloverPool (formerly BTC.com)
|
||||
"braiinspool", // shares AntPool templates
|
||||
"ultimuspool", // shares AntPool templates
|
||||
"binancepool", // shares AntPool templates
|
||||
"secpool", // shares AntPool templates
|
||||
"sigmapoolcom", // SigmaPool
|
||||
"rawpool", // shares AntPool templates
|
||||
"luxor", // shares AntPool templates
|
||||
"rawpool", // shares AntPool templates
|
||||
"luxor", // shares AntPool templates
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -65,6 +66,92 @@ export function createChainSection(ctx) {
|
||||
distribution,
|
||||
} = brk.metrics;
|
||||
|
||||
// Address types for mapping (using spendableTypeColors for consistency)
|
||||
/** @type {ReadonlyArray<{key: AddressableType, name: string, color: Color, defaultActive?: boolean}>} */
|
||||
const addressTypes = [
|
||||
{ key: "p2pkh", name: "P2PKH", color: colors[spendableTypeColors.p2pkh] },
|
||||
{ key: "p2sh", name: "P2SH", color: colors[spendableTypeColors.p2sh] },
|
||||
{ key: "p2wpkh", name: "P2WPKH", color: colors[spendableTypeColors.p2wpkh] },
|
||||
{ key: "p2wsh", name: "P2WSH", color: colors[spendableTypeColors.p2wsh] },
|
||||
{ key: "p2tr", name: "P2TR", color: colors[spendableTypeColors.p2tr] },
|
||||
{ key: "p2pk65", name: "P2PK65", color: colors[spendableTypeColors.p2pk65], defaultActive: false },
|
||||
{ key: "p2pk33", name: "P2PK33", color: colors[spendableTypeColors.p2pk33], defaultActive: false },
|
||||
{ key: "p2a", name: "P2A", color: colors[spendableTypeColors.p2a], defaultActive: false },
|
||||
];
|
||||
|
||||
// Activity types for mapping
|
||||
/** @type {ReadonlyArray<{key: "sending" | "receiving" | "both" | "reactivated" | "balanceIncreased" | "balanceDecreased", name: string, title: string, compareTitle: string}>} */
|
||||
const activityTypes = [
|
||||
{ key: "sending", name: "Sending", title: "Sending Address Count", compareTitle: "Sending Address Count by Type" },
|
||||
{ key: "receiving", name: "Receiving", title: "Receiving Address Count", compareTitle: "Receiving Address Count by Type" },
|
||||
{ key: "both", name: "Both", title: "Addresses Sending & Receiving (Same Block)", compareTitle: "Addresses Sending & Receiving by Type" },
|
||||
{ key: "reactivated", name: "Reactivated", title: "Reactivated Address Count (Was Empty)", compareTitle: "Reactivated Address Count by Type" },
|
||||
{ key: "balanceIncreased", name: "Balance Increased", title: "Addresses with Increased Balance", compareTitle: "Addresses with Increased Balance by Type" },
|
||||
{ key: "balanceDecreased", name: "Balance Decreased", title: "Addresses with Decreased Balance", compareTitle: "Addresses with Decreased Balance by Type" },
|
||||
];
|
||||
|
||||
// Count types for comparison charts
|
||||
/** @type {ReadonlyArray<{key: "addrCount" | "emptyAddrCount" | "totalAddrCount", name: string, title: string}>} */
|
||||
const countTypes = [
|
||||
{ key: "addrCount", name: "Loaded", title: "Address Count by Type" },
|
||||
{ key: "emptyAddrCount", name: "Empty", title: "Empty Address Count by Type" },
|
||||
{ key: "totalAddrCount", name: "Total", title: "Total Address Count by Type" },
|
||||
];
|
||||
|
||||
/**
|
||||
* Create address metrics tree for a given type key
|
||||
* @param {AddressableType | "all"} key
|
||||
* @param {string} titlePrefix
|
||||
*/
|
||||
const createAddressMetricsTree = (key, titlePrefix) => [
|
||||
{
|
||||
name: "Count",
|
||||
title: `${titlePrefix}Address Count`,
|
||||
bottom: [
|
||||
line({
|
||||
metric: distribution.addrCount[key],
|
||||
name: "Loaded",
|
||||
unit: Unit.count,
|
||||
}),
|
||||
line({
|
||||
metric: distribution.totalAddrCount[key],
|
||||
name: "Total",
|
||||
color: colors.default,
|
||||
unit: Unit.count,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: distribution.emptyAddrCount[key],
|
||||
name: "Empty",
|
||||
color: colors.gray,
|
||||
unit: Unit.count,
|
||||
defaultActive: false,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "New",
|
||||
title: `${titlePrefix}New Address Count`,
|
||||
bottom: fromDollarsPattern(distribution.newAddrCount[key], Unit.count),
|
||||
},
|
||||
{
|
||||
name: "Growth Rate",
|
||||
title: `${titlePrefix}Address Growth Rate`,
|
||||
bottom: fromFullnessPattern(distribution.growthRate[key], Unit.ratio),
|
||||
},
|
||||
{
|
||||
name: "Activity",
|
||||
tree: activityTypes.map((a) => ({
|
||||
name: a.name,
|
||||
title: `${titlePrefix}${a.name} Address Count`,
|
||||
bottom: fromFullnessPattern(
|
||||
distribution.addressActivity[key][a.key],
|
||||
Unit.count,
|
||||
),
|
||||
})),
|
||||
},
|
||||
];
|
||||
|
||||
// Build pools tree dynamically
|
||||
const poolEntries = Object.entries(pools.vecs);
|
||||
const poolsTree = poolEntries.map(([key, pool]) => {
|
||||
@@ -327,7 +414,12 @@ export function createChainSection(ctx) {
|
||||
defaultActive: false,
|
||||
},
|
||||
),
|
||||
...satsBtcUsd(transactions.volume.annualizedVolume, "Annualized", colors.red, { defaultActive: false }),
|
||||
...satsBtcUsd(
|
||||
transactions.volume.annualizedVolume,
|
||||
"Annualized",
|
||||
colors.red,
|
||||
{ defaultActive: false },
|
||||
),
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -451,43 +543,116 @@ export function createChainSection(ctx) {
|
||||
{
|
||||
name: "Legacy",
|
||||
tree: [
|
||||
{ name: "P2PKH", title: "P2PKH Output Count", bottom: fromDollarsPattern(scripts.count.p2pkh, Unit.count) },
|
||||
{ name: "P2PK33", title: "P2PK33 Output Count", bottom: fromDollarsPattern(scripts.count.p2pk33, Unit.count) },
|
||||
{ name: "P2PK65", title: "P2PK65 Output Count", bottom: fromDollarsPattern(scripts.count.p2pk65, Unit.count) },
|
||||
{
|
||||
name: "P2PKH",
|
||||
title: "P2PKH Output Count",
|
||||
bottom: fromDollarsPattern(scripts.count.p2pkh, Unit.count),
|
||||
},
|
||||
{
|
||||
name: "P2PK33",
|
||||
title: "P2PK33 Output Count",
|
||||
bottom: fromDollarsPattern(
|
||||
scripts.count.p2pk33,
|
||||
Unit.count,
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "P2PK65",
|
||||
title: "P2PK65 Output Count",
|
||||
bottom: fromDollarsPattern(
|
||||
scripts.count.p2pk65,
|
||||
Unit.count,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
// Script Hash
|
||||
{
|
||||
name: "Script Hash",
|
||||
tree: [
|
||||
{ name: "P2SH", title: "P2SH Output Count", bottom: fromDollarsPattern(scripts.count.p2sh, Unit.count) },
|
||||
{ name: "P2MS", title: "P2MS Output Count", bottom: fromDollarsPattern(scripts.count.p2ms, Unit.count) },
|
||||
{
|
||||
name: "P2SH",
|
||||
title: "P2SH Output Count",
|
||||
bottom: fromDollarsPattern(scripts.count.p2sh, Unit.count),
|
||||
},
|
||||
{
|
||||
name: "P2MS",
|
||||
title: "P2MS Output Count",
|
||||
bottom: fromDollarsPattern(scripts.count.p2ms, Unit.count),
|
||||
},
|
||||
],
|
||||
},
|
||||
// SegWit scripts
|
||||
{
|
||||
name: "SegWit",
|
||||
tree: [
|
||||
{ name: "All SegWit", title: "SegWit Output Count", bottom: fromDollarsPattern(scripts.count.segwit, Unit.count) },
|
||||
{ name: "P2WPKH", title: "P2WPKH Output Count", bottom: fromDollarsPattern(scripts.count.p2wpkh, Unit.count) },
|
||||
{ name: "P2WSH", title: "P2WSH Output Count", bottom: fromDollarsPattern(scripts.count.p2wsh, Unit.count) },
|
||||
{
|
||||
name: "All SegWit",
|
||||
title: "SegWit Output Count",
|
||||
bottom: fromDollarsPattern(
|
||||
scripts.count.segwit,
|
||||
Unit.count,
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "P2WPKH",
|
||||
title: "P2WPKH Output Count",
|
||||
bottom: fromDollarsPattern(
|
||||
scripts.count.p2wpkh,
|
||||
Unit.count,
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "P2WSH",
|
||||
title: "P2WSH Output Count",
|
||||
bottom: fromDollarsPattern(scripts.count.p2wsh, Unit.count),
|
||||
},
|
||||
],
|
||||
},
|
||||
// Taproot scripts
|
||||
{
|
||||
name: "Taproot",
|
||||
tree: [
|
||||
{ name: "P2TR", title: "P2TR Output Count", bottom: fromDollarsPattern(scripts.count.p2tr, Unit.count) },
|
||||
{ name: "P2A", title: "P2A Output Count", bottom: fromDollarsPattern(scripts.count.p2a, Unit.count) },
|
||||
{
|
||||
name: "P2TR",
|
||||
title: "P2TR Output Count",
|
||||
bottom: fromDollarsPattern(scripts.count.p2tr, Unit.count),
|
||||
},
|
||||
{
|
||||
name: "P2A",
|
||||
title: "P2A Output Count",
|
||||
bottom: fromDollarsPattern(scripts.count.p2a, Unit.count),
|
||||
},
|
||||
],
|
||||
},
|
||||
// Other scripts
|
||||
{
|
||||
name: "Other",
|
||||
tree: [
|
||||
{ name: "OP_RETURN", title: "OP_RETURN Output Count", bottom: fromDollarsPattern(scripts.count.opreturn, Unit.count) },
|
||||
{ name: "Empty", title: "Empty Output Count", bottom: fromDollarsPattern(scripts.count.emptyoutput, Unit.count) },
|
||||
{ name: "Unknown", title: "Unknown Output Count", bottom: fromDollarsPattern(scripts.count.unknownoutput, Unit.count) },
|
||||
{
|
||||
name: "OP_RETURN",
|
||||
title: "OP_RETURN Output Count",
|
||||
bottom: fromDollarsPattern(
|
||||
scripts.count.opreturn,
|
||||
Unit.count,
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "Empty",
|
||||
title: "Empty Output Count",
|
||||
bottom: fromDollarsPattern(
|
||||
scripts.count.emptyoutput,
|
||||
Unit.count,
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "Unknown",
|
||||
title: "Unknown Output Count",
|
||||
bottom: fromDollarsPattern(
|
||||
scripts.count.unknownoutput,
|
||||
Unit.count,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -499,23 +664,57 @@ export function createChainSection(ctx) {
|
||||
name: "SegWit",
|
||||
title: "SegWit Adoption",
|
||||
bottom: [
|
||||
line({ metric: scripts.count.segwitAdoption.base, name: "Base", unit: Unit.percentage }),
|
||||
line({ metric: scripts.count.segwitAdoption.sum, name: "Sum", color: colors.stat.sum, unit: Unit.percentage }),
|
||||
line({ metric: scripts.count.segwitAdoption.cumulative, name: "Cumulative", color: colors.stat.cumulative, unit: Unit.percentage, defaultActive: false }),
|
||||
line({
|
||||
metric: scripts.count.segwitAdoption.base,
|
||||
name: "Base",
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
line({
|
||||
metric: scripts.count.segwitAdoption.sum,
|
||||
name: "Sum",
|
||||
color: colors.stat.sum,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
line({
|
||||
metric: scripts.count.segwitAdoption.cumulative,
|
||||
name: "Cumulative",
|
||||
color: colors.stat.cumulative,
|
||||
unit: Unit.percentage,
|
||||
defaultActive: false,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Taproot",
|
||||
title: "Taproot Adoption",
|
||||
bottom: [
|
||||
line({ metric: scripts.count.taprootAdoption.base, name: "Base", unit: Unit.percentage }),
|
||||
line({ metric: scripts.count.taprootAdoption.sum, name: "Sum", color: colors.stat.sum, unit: Unit.percentage }),
|
||||
line({ metric: scripts.count.taprootAdoption.cumulative, name: "Cumulative", color: colors.stat.cumulative, unit: Unit.percentage, defaultActive: false }),
|
||||
line({
|
||||
metric: scripts.count.taprootAdoption.base,
|
||||
name: "Base",
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
line({
|
||||
metric: scripts.count.taprootAdoption.sum,
|
||||
name: "Sum",
|
||||
color: colors.stat.sum,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
line({
|
||||
metric: scripts.count.taprootAdoption.cumulative,
|
||||
name: "Cumulative",
|
||||
color: colors.stat.cumulative,
|
||||
unit: Unit.percentage,
|
||||
defaultActive: false,
|
||||
}),
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{ name: "OP_RETURN Value", title: "OP_RETURN Value", bottom: fromCoinbasePattern(scripts.value.opreturn) },
|
||||
{
|
||||
name: "OP_RETURN Value",
|
||||
title: "OP_RETURN Value",
|
||||
bottom: fromCoinbasePattern(scripts.value.opreturn),
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -561,7 +760,12 @@ export function createChainSection(ctx) {
|
||||
title: "Coinbase Rewards",
|
||||
bottom: [
|
||||
...fromCoinbasePattern(blocks.rewards.coinbase),
|
||||
...satsBtcUsd(blocks.rewards._24hCoinbaseSum, "24h sum", colors.pink, { defaultActive: false }),
|
||||
...satsBtcUsd(
|
||||
blocks.rewards._24hCoinbaseSum,
|
||||
"24h sum",
|
||||
colors.pink,
|
||||
{ defaultActive: false },
|
||||
),
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -616,143 +820,100 @@ export function createChainSection(ctx) {
|
||||
{
|
||||
name: "Addresses",
|
||||
tree: [
|
||||
// Overview - global metrics for all addresses
|
||||
{ name: "Overview", tree: createAddressMetricsTree("all", "") },
|
||||
|
||||
// Compare - cross-type comparisons (base + average, system selects appropriate one)
|
||||
{
|
||||
name: "Count",
|
||||
name: "Compare",
|
||||
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: "Count",
|
||||
tree: countTypes.map((c) => ({
|
||||
name: c.name,
|
||||
title: c.title,
|
||||
bottom: addressTypes.map((t) =>
|
||||
line({
|
||||
metric: distribution[c.key][t.key],
|
||||
name: t.name,
|
||||
color: t.color,
|
||||
unit: Unit.count,
|
||||
defaultActive: t.defaultActive,
|
||||
}),
|
||||
),
|
||||
})),
|
||||
},
|
||||
{
|
||||
name: "Empty by Type",
|
||||
title: "Empty Address Count by Type",
|
||||
bottom: [
|
||||
name: "New",
|
||||
title: "New Address Count by Type",
|
||||
bottom: addressTypes.flatMap((t) => [
|
||||
line({
|
||||
metric: distribution.emptyAddrCount.p2pkh,
|
||||
name: "P2PKH",
|
||||
color: colors.orange,
|
||||
metric: distribution.newAddrCount[t.key].base,
|
||||
name: t.name,
|
||||
color: t.color,
|
||||
unit: Unit.count,
|
||||
defaultActive: t.defaultActive,
|
||||
}),
|
||||
line({
|
||||
metric: distribution.emptyAddrCount.p2sh,
|
||||
name: "P2SH",
|
||||
color: colors.yellow,
|
||||
metric: distribution.newAddrCount[t.key].average,
|
||||
name: t.name,
|
||||
color: t.color,
|
||||
unit: Unit.count,
|
||||
defaultActive: t.defaultActive,
|
||||
}),
|
||||
line({
|
||||
metric: distribution.emptyAddrCount.p2wpkh,
|
||||
name: "P2WPKH",
|
||||
color: colors.green,
|
||||
unit: Unit.count,
|
||||
}),
|
||||
line({
|
||||
metric: distribution.emptyAddrCount.p2wsh,
|
||||
name: "P2WSH",
|
||||
color: colors.teal,
|
||||
unit: Unit.count,
|
||||
}),
|
||||
line({
|
||||
metric: distribution.emptyAddrCount.p2tr,
|
||||
name: "P2TR",
|
||||
color: colors.purple,
|
||||
unit: Unit.count,
|
||||
}),
|
||||
line({
|
||||
metric: distribution.emptyAddrCount.p2pk65,
|
||||
name: "P2PK65",
|
||||
color: colors.pink,
|
||||
unit: Unit.count,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: distribution.emptyAddrCount.p2pk33,
|
||||
name: "P2PK33",
|
||||
color: colors.red,
|
||||
unit: Unit.count,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: distribution.emptyAddrCount.p2a,
|
||||
name: "P2A",
|
||||
color: colors.blue,
|
||||
unit: Unit.count,
|
||||
defaultActive: false,
|
||||
}),
|
||||
],
|
||||
]),
|
||||
},
|
||||
{
|
||||
name: "By Type",
|
||||
title: "Address Count by Type",
|
||||
bottom: [
|
||||
name: "Growth Rate",
|
||||
title: "Address Growth Rate by Type",
|
||||
bottom: addressTypes.flatMap((t) => [
|
||||
line({
|
||||
metric: distribution.addrCount.p2pkh,
|
||||
name: "P2PKH",
|
||||
color: colors.orange,
|
||||
unit: Unit.count,
|
||||
metric: distribution.growthRate[t.key].base,
|
||||
name: t.name,
|
||||
color: t.color,
|
||||
unit: Unit.ratio,
|
||||
defaultActive: t.defaultActive,
|
||||
}),
|
||||
line({
|
||||
metric: distribution.addrCount.p2sh,
|
||||
name: "P2SH",
|
||||
color: colors.yellow,
|
||||
unit: Unit.count,
|
||||
metric: distribution.growthRate[t.key].average,
|
||||
name: t.name,
|
||||
color: t.color,
|
||||
unit: Unit.ratio,
|
||||
defaultActive: t.defaultActive,
|
||||
}),
|
||||
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,
|
||||
}),
|
||||
],
|
||||
]),
|
||||
},
|
||||
{
|
||||
name: "Activity",
|
||||
tree: activityTypes.map((a) => ({
|
||||
name: a.name,
|
||||
title: a.compareTitle,
|
||||
bottom: addressTypes.flatMap((t) => [
|
||||
line({
|
||||
metric: distribution.addressActivity[t.key][a.key].base,
|
||||
name: t.name,
|
||||
color: t.color,
|
||||
unit: Unit.count,
|
||||
defaultActive: t.defaultActive,
|
||||
}),
|
||||
line({
|
||||
metric: distribution.addressActivity[t.key][a.key].average,
|
||||
name: t.name,
|
||||
color: t.color,
|
||||
unit: Unit.count,
|
||||
defaultActive: t.defaultActive,
|
||||
}),
|
||||
]),
|
||||
})),
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
// Individual address types - each with same structure as Overview
|
||||
...addressTypes.map((t) => ({
|
||||
name: t.name,
|
||||
tree: createAddressMetricsTree(t.key, `${t.name} `),
|
||||
})),
|
||||
],
|
||||
},
|
||||
|
||||
@@ -988,7 +1149,9 @@ export function createChainSection(ctx) {
|
||||
.map(([key, pool]) => {
|
||||
const poolName =
|
||||
brk.POOL_ID_TO_POOL_NAME[
|
||||
/** @type {keyof typeof brk.POOL_ID_TO_POOL_NAME} */ (key.toLowerCase())
|
||||
/** @type {keyof typeof brk.POOL_ID_TO_POOL_NAME} */ (
|
||||
key.toLowerCase()
|
||||
)
|
||||
] || key;
|
||||
return line({
|
||||
metric: pool._1mDominance,
|
||||
@@ -1005,7 +1168,9 @@ export function createChainSection(ctx) {
|
||||
.map(([key, pool]) => {
|
||||
const poolName =
|
||||
brk.POOL_ID_TO_POOL_NAME[
|
||||
/** @type {keyof typeof brk.POOL_ID_TO_POOL_NAME} */ (key.toLowerCase())
|
||||
/** @type {keyof typeof brk.POOL_ID_TO_POOL_NAME} */ (
|
||||
key.toLowerCase()
|
||||
)
|
||||
] || key;
|
||||
return line({
|
||||
metric: pool._1mBlocksMined,
|
||||
@@ -1024,11 +1189,15 @@ export function createChainSection(ctx) {
|
||||
name: "Dominance",
|
||||
title: "AntPool & Friends Dominance",
|
||||
bottom: poolEntries
|
||||
.filter(([key]) => ANTPOOL_AND_FRIENDS_IDS.includes(key.toLowerCase()))
|
||||
.filter(([key]) =>
|
||||
ANTPOOL_AND_FRIENDS_IDS.includes(key.toLowerCase()),
|
||||
)
|
||||
.map(([key, pool]) => {
|
||||
const poolName =
|
||||
brk.POOL_ID_TO_POOL_NAME[
|
||||
/** @type {keyof typeof brk.POOL_ID_TO_POOL_NAME} */ (key.toLowerCase())
|
||||
/** @type {keyof typeof brk.POOL_ID_TO_POOL_NAME} */ (
|
||||
key.toLowerCase()
|
||||
)
|
||||
] || key;
|
||||
return line({
|
||||
metric: pool._1mDominance,
|
||||
@@ -1041,11 +1210,15 @@ export function createChainSection(ctx) {
|
||||
name: "Blocks Mined",
|
||||
title: "AntPool & Friends Blocks Mined (1m)",
|
||||
bottom: poolEntries
|
||||
.filter(([key]) => ANTPOOL_AND_FRIENDS_IDS.includes(key.toLowerCase()))
|
||||
.filter(([key]) =>
|
||||
ANTPOOL_AND_FRIENDS_IDS.includes(key.toLowerCase()),
|
||||
)
|
||||
.map(([key, pool]) => {
|
||||
const poolName =
|
||||
brk.POOL_ID_TO_POOL_NAME[
|
||||
/** @type {keyof typeof brk.POOL_ID_TO_POOL_NAME} */ (key.toLowerCase())
|
||||
/** @type {keyof typeof brk.POOL_ID_TO_POOL_NAME} */ (
|
||||
key.toLowerCase()
|
||||
)
|
||||
] || key;
|
||||
return line({
|
||||
metric: pool._1mBlocksMined,
|
||||
|
||||
@@ -244,6 +244,7 @@ function createRealizedPnlSection(ctx, args, title) {
|
||||
name: "Negative Loss",
|
||||
color: colors.red,
|
||||
unit: Unit.usd,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: realized.negRealizedLoss.cumulative,
|
||||
|
||||
@@ -863,6 +863,8 @@ function createSingleRealizedPnlSection(
|
||||
Unit.usd,
|
||||
"Negative Loss",
|
||||
colors.red,
|
||||
undefined,
|
||||
false,
|
||||
),
|
||||
...extra,
|
||||
line({
|
||||
@@ -1357,6 +1359,7 @@ function createUnrealizedPnlRelToMarketCapMetrics(ctx, rel) {
|
||||
name: "Negative Loss",
|
||||
color: colors.red,
|
||||
unit: Unit.pctMcap,
|
||||
defaultActive: false,
|
||||
}),
|
||||
];
|
||||
}
|
||||
@@ -1386,6 +1389,7 @@ function createUnrealizedPnlRelToOwnMarketCapMetrics(ctx, rel) {
|
||||
name: "Negative Loss",
|
||||
color: colors.red,
|
||||
unit: Unit.pctOwnMcap,
|
||||
defaultActive: false,
|
||||
}),
|
||||
priceLine({ ctx, unit: Unit.pctOwnMcap, number: 100 }),
|
||||
priceLine({ ctx, unit: Unit.pctOwnMcap }),
|
||||
@@ -1417,6 +1421,7 @@ function createUnrealizedPnlRelToOwnPnlMetrics(ctx, rel) {
|
||||
name: "Negative Loss",
|
||||
color: colors.red,
|
||||
unit: Unit.pctOwnPnl,
|
||||
defaultActive: false,
|
||||
}),
|
||||
priceLine({ ctx, unit: Unit.pctOwnPnl, number: 100 }),
|
||||
priceLine({ ctx, unit: Unit.pctOwnPnl }),
|
||||
@@ -1498,6 +1503,7 @@ function createUnrealizedPnlBaseMetrics(ctx, tree) {
|
||||
name: "Negative Loss",
|
||||
color: colors.red,
|
||||
unit: Unit.usd,
|
||||
defaultActive: false,
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -9,7 +9,10 @@ import { createMomentumSection } from "./momentum.js";
|
||||
import { createVolatilitySection } from "./volatility.js";
|
||||
import { createBandsSection } from "./bands.js";
|
||||
import { createValuationSection } from "./onchain.js";
|
||||
import { createDcaVsLumpSumSection, createDcaByYearSection } from "./investing.js";
|
||||
import {
|
||||
createDcaVsLumpSumSection,
|
||||
createDcaByYearSection,
|
||||
} from "./investing.js";
|
||||
|
||||
/**
|
||||
* Create Market section
|
||||
@@ -30,7 +33,6 @@ export function createMarketSection(ctx) {
|
||||
indicators,
|
||||
} = market;
|
||||
|
||||
|
||||
return {
|
||||
name: "Market",
|
||||
tree: [
|
||||
@@ -40,80 +42,80 @@ export function createMarketSection(ctx) {
|
||||
title: "Bitcoin Price",
|
||||
},
|
||||
// Oracle section is localhost-only debug - uses non-price-pattern metrics
|
||||
...(localhost
|
||||
? /** @type {PartialOptionsTree} */ ([
|
||||
{
|
||||
name: "Oracle",
|
||||
title: "Oracle Price",
|
||||
top: /** @type {any} */ ([
|
||||
candlestick({
|
||||
metric: priceMetrics.oracle.closeOhlcDollars,
|
||||
name: "Close",
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
candlestick({
|
||||
metric: priceMetrics.oracle.midOhlcDollars,
|
||||
name: "Mid",
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
line({
|
||||
metric: priceMetrics.oracle.phaseDailyDollars.median,
|
||||
name: "o. p50",
|
||||
unit: Unit.usd,
|
||||
color: colors.yellow,
|
||||
}),
|
||||
line({
|
||||
metric: priceMetrics.oracle.phaseV2DailyDollars.median,
|
||||
name: "o2. p50",
|
||||
unit: Unit.usd,
|
||||
color: colors.orange,
|
||||
}),
|
||||
line({
|
||||
metric: priceMetrics.oracle.phaseV2PeakDailyDollars.median,
|
||||
name: "o2.2 p50",
|
||||
unit: Unit.usd,
|
||||
color: colors.orange,
|
||||
}),
|
||||
line({
|
||||
metric: priceMetrics.oracle.phaseV3DailyDollars.median,
|
||||
name: "o3. p50",
|
||||
unit: Unit.usd,
|
||||
color: colors.red,
|
||||
}),
|
||||
line({
|
||||
metric: priceMetrics.oracle.phaseV3PeakDailyDollars.median,
|
||||
name: "o3.2 p50",
|
||||
unit: Unit.usd,
|
||||
color: colors.red,
|
||||
}),
|
||||
line({
|
||||
metric: priceMetrics.oracle.phaseDailyDollars.max,
|
||||
name: "o. max",
|
||||
unit: Unit.usd,
|
||||
color: colors.lime,
|
||||
}),
|
||||
line({
|
||||
metric: priceMetrics.oracle.phaseV2DailyDollars.max,
|
||||
name: "o.2 max",
|
||||
unit: Unit.usd,
|
||||
color: colors.emerald,
|
||||
}),
|
||||
line({
|
||||
metric: priceMetrics.oracle.phaseDailyDollars.min,
|
||||
name: "o. min",
|
||||
unit: Unit.usd,
|
||||
color: colors.rose,
|
||||
}),
|
||||
line({
|
||||
metric: priceMetrics.oracle.phaseV2DailyDollars.min,
|
||||
name: "o.2 min",
|
||||
unit: Unit.usd,
|
||||
color: colors.purple,
|
||||
}),
|
||||
]),
|
||||
},
|
||||
])
|
||||
: []),
|
||||
// ...(localhost
|
||||
// ? /** @type {PartialOptionsTree} */ ([
|
||||
// {
|
||||
// name: "Oracle",
|
||||
// title: "Oracle Price",
|
||||
// top: /** @type {any} */ ([
|
||||
// candlestick({
|
||||
// metric: priceMetrics.oracle.closeOhlcDollars,
|
||||
// name: "Close",
|
||||
// unit: Unit.usd,
|
||||
// }),
|
||||
// candlestick({
|
||||
// metric: priceMetrics.oracle.midOhlcDollars,
|
||||
// name: "Mid",
|
||||
// unit: Unit.usd,
|
||||
// }),
|
||||
// line({
|
||||
// metric: priceMetrics.oracle.phaseDailyDollars.median,
|
||||
// name: "o. p50",
|
||||
// unit: Unit.usd,
|
||||
// color: colors.yellow,
|
||||
// }),
|
||||
// line({
|
||||
// metric: priceMetrics.oracle.phaseV2DailyDollars.median,
|
||||
// name: "o2. p50",
|
||||
// unit: Unit.usd,
|
||||
// color: colors.orange,
|
||||
// }),
|
||||
// line({
|
||||
// metric: priceMetrics.oracle.phaseV2PeakDailyDollars.median,
|
||||
// name: "o2.2 p50",
|
||||
// unit: Unit.usd,
|
||||
// color: colors.orange,
|
||||
// }),
|
||||
// line({
|
||||
// metric: priceMetrics.oracle.phaseV3DailyDollars.median,
|
||||
// name: "o3. p50",
|
||||
// unit: Unit.usd,
|
||||
// color: colors.red,
|
||||
// }),
|
||||
// line({
|
||||
// metric: priceMetrics.oracle.phaseV3PeakDailyDollars.median,
|
||||
// name: "o3.2 p50",
|
||||
// unit: Unit.usd,
|
||||
// color: colors.red,
|
||||
// }),
|
||||
// line({
|
||||
// metric: priceMetrics.oracle.phaseDailyDollars.max,
|
||||
// name: "o. max",
|
||||
// unit: Unit.usd,
|
||||
// color: colors.lime,
|
||||
// }),
|
||||
// line({
|
||||
// metric: priceMetrics.oracle.phaseV2DailyDollars.max,
|
||||
// name: "o.2 max",
|
||||
// unit: Unit.usd,
|
||||
// color: colors.emerald,
|
||||
// }),
|
||||
// line({
|
||||
// metric: priceMetrics.oracle.phaseDailyDollars.min,
|
||||
// name: "o. min",
|
||||
// unit: Unit.usd,
|
||||
// color: colors.rose,
|
||||
// }),
|
||||
// line({
|
||||
// metric: priceMetrics.oracle.phaseV2DailyDollars.min,
|
||||
// name: "o.2 min",
|
||||
// unit: Unit.usd,
|
||||
// color: colors.purple,
|
||||
// }),
|
||||
// ]),
|
||||
// },
|
||||
// ])
|
||||
// : []),
|
||||
|
||||
// Capitalization
|
||||
{
|
||||
|
||||
@@ -107,7 +107,7 @@ export function createPartialOptions({ brk }) {
|
||||
// STH vs LTH - Direct comparison
|
||||
createCohortFolderWithNupl(ctx, {
|
||||
name: "STH vs LTH",
|
||||
title: "Term",
|
||||
title: "Holders",
|
||||
list: [termShort, termLong],
|
||||
}),
|
||||
|
||||
@@ -285,9 +285,7 @@ export function createPartialOptions({ brk }) {
|
||||
// Frameworks section
|
||||
{
|
||||
name: "Frameworks",
|
||||
tree: [
|
||||
createCointimeSection(ctx),
|
||||
],
|
||||
tree: [createCointimeSection(ctx)],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -58,13 +58,55 @@ function percentileSeries(colors, pattern, unit, title, { type } = {}) {
|
||||
const { stat } = colors;
|
||||
const base = { unit, defaultActive: false };
|
||||
return [
|
||||
{ type, metric: pattern.max, title: `${title} max`.trim(), color: stat.max, ...base },
|
||||
{ type, metric: pattern.min, title: `${title} min`.trim(), color: stat.min, ...base },
|
||||
{ type, metric: pattern.median, title: `${title} median`.trim(), color: stat.median, ...base },
|
||||
{ type, metric: pattern.pct75, title: `${title} pct75`.trim(), color: stat.pct75, ...base },
|
||||
{ type, metric: pattern.pct25, title: `${title} pct25`.trim(), color: stat.pct25, ...base },
|
||||
{ type, metric: pattern.pct90, title: `${title} pct90`.trim(), color: stat.pct90, ...base },
|
||||
{ type, metric: pattern.pct10, title: `${title} pct10`.trim(), color: stat.pct10, ...base },
|
||||
{
|
||||
type,
|
||||
metric: pattern.max,
|
||||
title: `${title} max`.trim(),
|
||||
color: stat.max,
|
||||
...base,
|
||||
},
|
||||
{
|
||||
type,
|
||||
metric: pattern.min,
|
||||
title: `${title} min`.trim(),
|
||||
color: stat.min,
|
||||
...base,
|
||||
},
|
||||
{
|
||||
type,
|
||||
metric: pattern.median,
|
||||
title: `${title} median`.trim(),
|
||||
color: stat.median,
|
||||
...base,
|
||||
},
|
||||
{
|
||||
type,
|
||||
metric: pattern.pct75,
|
||||
title: `${title} pct75`.trim(),
|
||||
color: stat.pct75,
|
||||
...base,
|
||||
},
|
||||
{
|
||||
type,
|
||||
metric: pattern.pct25,
|
||||
title: `${title} pct25`.trim(),
|
||||
color: stat.pct25,
|
||||
...base,
|
||||
},
|
||||
{
|
||||
type,
|
||||
metric: pattern.pct90,
|
||||
title: `${title} pct90`.trim(),
|
||||
color: stat.pct90,
|
||||
...base,
|
||||
},
|
||||
{
|
||||
type,
|
||||
metric: pattern.pct10,
|
||||
title: `${title} pct10`.trim(),
|
||||
color: stat.pct10,
|
||||
...base,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@@ -258,8 +300,20 @@ 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: stat.sum, unit, defaultActive: false },
|
||||
{ metric: pattern.cumulative, title: `${title} cumulative`.trim(), color: stat.cumulative, unit, defaultActive: false },
|
||||
{
|
||||
metric: pattern.sum,
|
||||
title: `${title} sum`.trim(),
|
||||
color: stat.sum,
|
||||
unit,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
metric: pattern.cumulative,
|
||||
title: `${title} cumulative`.trim(),
|
||||
color: stat.cumulative,
|
||||
unit,
|
||||
defaultActive: false,
|
||||
},
|
||||
...percentileSeries(colors, pattern, unit, title),
|
||||
];
|
||||
}
|
||||
@@ -276,7 +330,12 @@ 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: stat.avg, unit },
|
||||
{
|
||||
metric: pattern.average,
|
||||
title: `${title} avg`.trim(),
|
||||
color: stat.avg,
|
||||
unit,
|
||||
},
|
||||
...percentileSeries(colors, pattern, unit, title),
|
||||
];
|
||||
}
|
||||
@@ -293,9 +352,26 @@ 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: stat.sum, unit },
|
||||
{ metric: pattern.cumulative, title: `${title} cumulative`.trim(), color: stat.cumulative, unit, defaultActive: false },
|
||||
{ metric: pattern.average, title: `${title} avg`.trim(), color: stat.avg, unit, defaultActive: false },
|
||||
{
|
||||
metric: pattern.sum,
|
||||
title: `${title} sum`.trim(),
|
||||
color: stat.sum,
|
||||
unit,
|
||||
},
|
||||
{
|
||||
metric: pattern.cumulative,
|
||||
title: `${title} cumulative`.trim(),
|
||||
color: stat.cumulative,
|
||||
unit,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
metric: pattern.average,
|
||||
title: `${title} avg`.trim(),
|
||||
color: stat.avg,
|
||||
unit,
|
||||
defaultActive: false,
|
||||
},
|
||||
...percentileSeries(colors, pattern, unit, title),
|
||||
];
|
||||
}
|
||||
@@ -310,7 +386,12 @@ export function fromDollarsPattern(colors, pattern, unit, title = "") {
|
||||
*/
|
||||
export function fromFeeRatePattern(colors, pattern, unit, title = "") {
|
||||
return [
|
||||
{ type: "Dots", metric: pattern.average, title: `${title} avg`.trim(), unit },
|
||||
{
|
||||
type: "Dots",
|
||||
metric: pattern.average,
|
||||
title: `${title} avg`.trim(),
|
||||
unit,
|
||||
},
|
||||
...percentileSeries(colors, pattern, unit, title, { type: "Dots" }),
|
||||
];
|
||||
}
|
||||
@@ -323,12 +404,28 @@ export function fromFeeRatePattern(colors, pattern, unit, title = "") {
|
||||
* @param {string} [title]
|
||||
* @returns {AnyFetchedSeriesBlueprint[]}
|
||||
*/
|
||||
export function fromFullnessPatternWithSumCumulative(colors, pattern, unit, title = "") {
|
||||
export function fromFullnessPatternWithSumCumulative(
|
||||
colors,
|
||||
pattern,
|
||||
unit,
|
||||
title = "",
|
||||
) {
|
||||
const { stat } = colors;
|
||||
return [
|
||||
...fromFullnessPattern(colors, pattern, unit, title),
|
||||
{ metric: pattern.sum, title: `${title} sum`.trim(), color: stat.sum, unit },
|
||||
{ metric: pattern.cumulative, title: `${title} cumulative`.trim(), color: stat.cumulative, unit, defaultActive: false },
|
||||
{
|
||||
metric: pattern.sum,
|
||||
title: `${title} sum`.trim(),
|
||||
color: stat.sum,
|
||||
unit,
|
||||
},
|
||||
{
|
||||
metric: pattern.cumulative,
|
||||
title: `${title} cumulative`.trim(),
|
||||
color: stat.cumulative,
|
||||
unit,
|
||||
defaultActive: false,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@@ -341,9 +438,24 @@ export function fromFullnessPatternWithSumCumulative(colors, pattern, unit, titl
|
||||
*/
|
||||
export function fromCoinbasePattern(colors, pattern, title = "") {
|
||||
return [
|
||||
...fromFullnessPatternWithSumCumulative(colors, pattern.bitcoin, Unit.btc, title),
|
||||
...fromFullnessPatternWithSumCumulative(colors, pattern.sats, Unit.sats, title),
|
||||
...fromFullnessPatternWithSumCumulative(colors, pattern.dollars, Unit.usd, title),
|
||||
...fromFullnessPatternWithSumCumulative(
|
||||
colors,
|
||||
pattern.bitcoin,
|
||||
Unit.btc,
|
||||
title,
|
||||
),
|
||||
...fromFullnessPatternWithSumCumulative(
|
||||
colors,
|
||||
pattern.sats,
|
||||
Unit.sats,
|
||||
title,
|
||||
),
|
||||
...fromFullnessPatternWithSumCumulative(
|
||||
colors,
|
||||
pattern.dollars,
|
||||
Unit.usd,
|
||||
title,
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -414,6 +526,7 @@ export function fromValuePattern(
|
||||
* @param {string} [title]
|
||||
* @param {Color} [sumColor]
|
||||
* @param {Color} [cumulativeColor]
|
||||
* @param {boolean} [defaultActive]
|
||||
* @returns {AnyFetchedSeriesBlueprint[]}
|
||||
*/
|
||||
export function fromBitcoinPatternWithUnit(
|
||||
@@ -423,6 +536,7 @@ export function fromBitcoinPatternWithUnit(
|
||||
title = "",
|
||||
sumColor,
|
||||
cumulativeColor,
|
||||
defaultActive,
|
||||
) {
|
||||
return [
|
||||
{
|
||||
@@ -430,6 +544,7 @@ export function fromBitcoinPatternWithUnit(
|
||||
title: `${title} sum`.trim(),
|
||||
color: sumColor,
|
||||
unit,
|
||||
defaultActive,
|
||||
},
|
||||
{
|
||||
metric: pattern.cumulative,
|
||||
@@ -489,7 +604,13 @@ 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: stat.avg, unit, defaultActive: false },
|
||||
{
|
||||
metric: pattern.average,
|
||||
title: `${title} avg`.trim(),
|
||||
color: stat.avg,
|
||||
unit,
|
||||
defaultActive: false,
|
||||
},
|
||||
...percentileSeries(colors, pattern, unit, title),
|
||||
];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user