mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-04-24 06:39:58 -07:00
website: snapshot
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -1,15 +1,15 @@
|
||||
import {
|
||||
fromSumStatsPattern,
|
||||
fromBaseStatsPattern,
|
||||
fromFullStatsPattern,
|
||||
fromStatsPattern,
|
||||
fromCoinbasePattern,
|
||||
fromValuePattern,
|
||||
fromBitcoinPatternWithUnit,
|
||||
fromCountPattern,
|
||||
fromSupplyPattern,
|
||||
chartsFromFull,
|
||||
chartsFromSum,
|
||||
chartsFromCount,
|
||||
chartsFromValue,
|
||||
chartsFromValueFull,
|
||||
} from "./series.js";
|
||||
import { colors } from "../chart/colors.js";
|
||||
import { Unit } from "../utils/units.js";
|
||||
|
||||
/**
|
||||
* @template {(arg: any, ...args: any[]) => any} F
|
||||
@@ -29,6 +29,18 @@ const bind = (fn) =>
|
||||
(...args) => fn(colors, ...args)
|
||||
);
|
||||
|
||||
/**
|
||||
* Create distribution series for btc/sats/usd from a value pattern with stats (average + percentiles)
|
||||
* @param {FullValuePattern | SumValuePattern} source
|
||||
* @returns {AnyFetchedSeriesBlueprint[]}
|
||||
*/
|
||||
const distributionBtcSatsUsd = (source) => [
|
||||
...fromStatsPattern(colors, { pattern: source.bitcoin, unit: Unit.btc }),
|
||||
...fromStatsPattern(colors, { pattern: source.sats, unit: Unit.sats }),
|
||||
...fromStatsPattern(colors, { pattern: source.dollars, unit: Unit.usd }),
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* Create a context object with all dependencies for building partial options
|
||||
* @param {Object} args
|
||||
@@ -38,14 +50,16 @@ export function createContext({ brk }) {
|
||||
return {
|
||||
colors,
|
||||
brk,
|
||||
fromSumStatsPattern: bind(fromSumStatsPattern),
|
||||
// Series helpers (return series arrays for a single chart)
|
||||
fromBaseStatsPattern: bind(fromBaseStatsPattern),
|
||||
fromFullStatsPattern: bind(fromFullStatsPattern),
|
||||
fromStatsPattern: bind(fromStatsPattern),
|
||||
fromCoinbasePattern: bind(fromCoinbasePattern),
|
||||
fromValuePattern,
|
||||
fromBitcoinPatternWithUnit,
|
||||
fromCountPattern,
|
||||
fromSupplyPattern,
|
||||
distributionBtcSatsUsd,
|
||||
// Chart helpers (return chart trees for Sum/Distribution/Cumulative folders)
|
||||
chartsFromFull: bind(chartsFromFull),
|
||||
chartsFromSum: bind(chartsFromSum),
|
||||
chartsFromCount,
|
||||
chartsFromValue,
|
||||
chartsFromValueFull: bind(chartsFromValueFull),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -223,7 +223,6 @@ function createRealizedCapWithExtras(ctx, list, args, useGroupName) {
|
||||
unit: Unit.usd,
|
||||
defaultActive: false,
|
||||
}),
|
||||
priceLine({ ctx, unit: Unit.usd, defaultActive: false }),
|
||||
]
|
||||
: []),
|
||||
// RealizedPattern (address cohorts) doesn't have realizedCapRelToOwnMarketCap
|
||||
@@ -437,9 +436,25 @@ function createRealizedPnlSection(ctx, args, title) {
|
||||
name: "Peak Regret",
|
||||
title: title("Peak Regret"),
|
||||
bottom: [
|
||||
line({ metric: realized.peakRegret.sum, name: "Sum", color: colors.red, unit: Unit.usd }),
|
||||
line({ metric: realized.peakRegret.cumulative, name: "Cumulative", color: colors.red, unit: Unit.usd, defaultActive: false }),
|
||||
baseline({ metric: realized.peakRegretRelToRealizedCap, name: "Rel. to Realized Cap", color: colors.orange, unit: Unit.pctRcap }),
|
||||
line({
|
||||
metric: realized.peakRegret.sum,
|
||||
name: "Sum",
|
||||
color: colors.red,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
line({
|
||||
metric: realized.peakRegret.cumulative,
|
||||
name: "Cumulative",
|
||||
color: colors.red,
|
||||
unit: Unit.usd,
|
||||
defaultActive: false,
|
||||
}),
|
||||
baseline({
|
||||
metric: realized.peakRegretRelToRealizedCap,
|
||||
name: "Rel. to Realized Cap",
|
||||
color: colors.orange,
|
||||
unit: Unit.pctRcap,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -449,35 +464,109 @@ function createRealizedPnlSection(ctx, args, title) {
|
||||
name: "In Profit",
|
||||
title: title("Sent In Profit"),
|
||||
bottom: [
|
||||
line({ metric: realized.sentInProfit.bitcoin.sum, name: "Sum", color: colors.green, unit: Unit.btc }),
|
||||
line({ metric: realized.sentInProfit.bitcoin.cumulative, name: "Cumulative", color: colors.green, unit: Unit.btc, defaultActive: false }),
|
||||
line({ metric: realized.sentInProfit.sats.sum, name: "Sum", color: colors.green, unit: Unit.sats }),
|
||||
line({ metric: realized.sentInProfit.sats.cumulative, name: "Cumulative", color: colors.green, unit: Unit.sats, defaultActive: false }),
|
||||
line({ metric: realized.sentInProfit.dollars.sum, name: "Sum", color: colors.green, unit: Unit.usd }),
|
||||
line({ metric: realized.sentInProfit.dollars.cumulative, name: "Cumulative", color: colors.green, unit: Unit.usd, defaultActive: false }),
|
||||
line({
|
||||
metric: realized.sentInProfit.bitcoin.sum,
|
||||
name: "Sum",
|
||||
color: colors.green,
|
||||
unit: Unit.btc,
|
||||
}),
|
||||
line({
|
||||
metric: realized.sentInProfit.bitcoin.cumulative,
|
||||
name: "Cumulative",
|
||||
color: colors.green,
|
||||
unit: Unit.btc,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: realized.sentInProfit.sats.sum,
|
||||
name: "Sum",
|
||||
color: colors.green,
|
||||
unit: Unit.sats,
|
||||
}),
|
||||
line({
|
||||
metric: realized.sentInProfit.sats.cumulative,
|
||||
name: "Cumulative",
|
||||
color: colors.green,
|
||||
unit: Unit.sats,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: realized.sentInProfit.dollars.sum,
|
||||
name: "Sum",
|
||||
color: colors.green,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
line({
|
||||
metric: realized.sentInProfit.dollars.cumulative,
|
||||
name: "Cumulative",
|
||||
color: colors.green,
|
||||
unit: Unit.usd,
|
||||
defaultActive: false,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "In Loss",
|
||||
title: title("Sent In Loss"),
|
||||
bottom: [
|
||||
line({ metric: realized.sentInLoss.bitcoin.sum, name: "Sum", color: colors.red, unit: Unit.btc }),
|
||||
line({ metric: realized.sentInLoss.bitcoin.cumulative, name: "Cumulative", color: colors.red, unit: Unit.btc, defaultActive: false }),
|
||||
line({ metric: realized.sentInLoss.sats.sum, name: "Sum", color: colors.red, unit: Unit.sats }),
|
||||
line({ metric: realized.sentInLoss.sats.cumulative, name: "Cumulative", color: colors.red, unit: Unit.sats, defaultActive: false }),
|
||||
line({ metric: realized.sentInLoss.dollars.sum, name: "Sum", color: colors.red, unit: Unit.usd }),
|
||||
line({ metric: realized.sentInLoss.dollars.cumulative, name: "Cumulative", color: colors.red, unit: Unit.usd, defaultActive: false }),
|
||||
line({
|
||||
metric: realized.sentInLoss.bitcoin.sum,
|
||||
name: "Sum",
|
||||
color: colors.red,
|
||||
unit: Unit.btc,
|
||||
}),
|
||||
line({
|
||||
metric: realized.sentInLoss.bitcoin.cumulative,
|
||||
name: "Cumulative",
|
||||
color: colors.red,
|
||||
unit: Unit.btc,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: realized.sentInLoss.sats.sum,
|
||||
name: "Sum",
|
||||
color: colors.red,
|
||||
unit: Unit.sats,
|
||||
}),
|
||||
line({
|
||||
metric: realized.sentInLoss.sats.cumulative,
|
||||
name: "Cumulative",
|
||||
color: colors.red,
|
||||
unit: Unit.sats,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: realized.sentInLoss.dollars.sum,
|
||||
name: "Sum",
|
||||
color: colors.red,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
line({
|
||||
metric: realized.sentInLoss.dollars.cumulative,
|
||||
name: "Cumulative",
|
||||
color: colors.red,
|
||||
unit: Unit.usd,
|
||||
defaultActive: false,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "In Profit 14d EMA",
|
||||
title: title("Sent In Profit 14d EMA"),
|
||||
bottom: satsBtcUsd({ pattern: realized.sentInProfit14dEma, name: "14d EMA", color: colors.green }),
|
||||
bottom: satsBtcUsd({
|
||||
pattern: realized.sentInProfit14dEma,
|
||||
name: "14d EMA",
|
||||
color: colors.green,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "In Loss 14d EMA",
|
||||
title: title("Sent In Loss 14d EMA"),
|
||||
bottom: satsBtcUsd({ pattern: realized.sentInLoss14dEma, name: "14d EMA", color: colors.red }),
|
||||
bottom: satsBtcUsd({
|
||||
pattern: realized.sentInLoss14dEma,
|
||||
name: "14d EMA",
|
||||
color: colors.red,
|
||||
}),
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -493,9 +582,27 @@ function createRealizedPnlSection(ctx, args, title) {
|
||||
*/
|
||||
function createGroupedRealizedPnlSection(ctx, list, title) {
|
||||
const pnlConfigs = /** @type {const} */ ([
|
||||
{ name: "Profit", sum: "realizedProfit", ema: "realizedProfit7dEma", rel: "realizedProfitRelToRealizedCap", isNet: false },
|
||||
{ name: "Loss", sum: "realizedLoss", ema: "realizedLoss7dEma", rel: "realizedLossRelToRealizedCap", isNet: false },
|
||||
{ name: "Net P&L", sum: "netRealizedPnl", ema: "netRealizedPnl7dEma", rel: "netRealizedPnlRelToRealizedCap", isNet: true },
|
||||
{
|
||||
name: "Profit",
|
||||
sum: "realizedProfit",
|
||||
ema: "realizedProfit7dEma",
|
||||
rel: "realizedProfitRelToRealizedCap",
|
||||
isNet: false,
|
||||
},
|
||||
{
|
||||
name: "Loss",
|
||||
sum: "realizedLoss",
|
||||
ema: "realizedLoss7dEma",
|
||||
rel: "realizedLossRelToRealizedCap",
|
||||
isNet: false,
|
||||
},
|
||||
{
|
||||
name: "Net P&L",
|
||||
sum: "netRealizedPnl",
|
||||
ema: "netRealizedPnl7dEma",
|
||||
rel: "netRealizedPnlRelToRealizedCap",
|
||||
isNet: true,
|
||||
},
|
||||
]);
|
||||
|
||||
return [
|
||||
@@ -507,10 +614,19 @@ function createGroupedRealizedPnlSection(ctx, list, title) {
|
||||
title: title(`Realized ${name}`),
|
||||
bottom: [
|
||||
...list.flatMap(({ color, name, tree }) => [
|
||||
(isNet ? baseline : line)({ metric: tree.realized[sum].sum, name, color, unit: Unit.usd }),
|
||||
baseline({ metric: tree.realized[rel].sum, name, color, unit: Unit.pctRcap }),
|
||||
(isNet ? baseline : line)({
|
||||
metric: tree.realized[sum].sum,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
baseline({
|
||||
metric: tree.realized[rel].sum,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.pctRcap,
|
||||
}),
|
||||
]),
|
||||
priceLine({ ctx, unit: Unit.usd }),
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -518,9 +634,13 @@ function createGroupedRealizedPnlSection(ctx, list, title) {
|
||||
title: title(`Realized ${name} 7d EMA`),
|
||||
bottom: [
|
||||
...list.map(({ color, name, tree }) =>
|
||||
(isNet ? baseline : line)({ metric: tree.realized[ema], name, color, unit: Unit.usd }),
|
||||
(isNet ? baseline : line)({
|
||||
metric: tree.realized[ema],
|
||||
name,
|
||||
color,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
),
|
||||
priceLine({ ctx, unit: Unit.usd }),
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -532,21 +652,36 @@ function createGroupedRealizedPnlSection(ctx, list, title) {
|
||||
name: "Sum",
|
||||
title: title("Peak Regret"),
|
||||
bottom: list.flatMap(({ color, name, tree }) => [
|
||||
line({ metric: tree.realized.peakRegret.sum, name, color, unit: Unit.usd }),
|
||||
line({
|
||||
metric: tree.realized.peakRegret.sum,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
]),
|
||||
},
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: title("Peak Regret Cumulative"),
|
||||
bottom: list.flatMap(({ color, name, tree }) => [
|
||||
line({ metric: tree.realized.peakRegret.cumulative, name, color, unit: Unit.usd }),
|
||||
line({
|
||||
metric: tree.realized.peakRegret.cumulative,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
]),
|
||||
},
|
||||
{
|
||||
name: "Rel. to Realized Cap",
|
||||
title: title("Peak Regret Rel. to Realized Cap"),
|
||||
bottom: list.flatMap(({ color, name, tree }) => [
|
||||
baseline({ metric: tree.realized.peakRegretRelToRealizedCap, name, color, unit: Unit.pctRcap }),
|
||||
baseline({
|
||||
metric: tree.realized.peakRegretRelToRealizedCap,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.pctRcap,
|
||||
}),
|
||||
]),
|
||||
},
|
||||
],
|
||||
@@ -558,50 +693,118 @@ function createGroupedRealizedPnlSection(ctx, list, title) {
|
||||
name: "In Profit",
|
||||
title: title("Sent In Profit"),
|
||||
bottom: list.flatMap(({ color, name, tree }) => [
|
||||
line({ metric: tree.realized.sentInProfit.bitcoin.sum, name, color, unit: Unit.btc }),
|
||||
line({ metric: tree.realized.sentInProfit.sats.sum, name, color, unit: Unit.sats }),
|
||||
line({ metric: tree.realized.sentInProfit.dollars.sum, name, color, unit: Unit.usd }),
|
||||
line({
|
||||
metric: tree.realized.sentInProfit.bitcoin.sum,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.btc,
|
||||
}),
|
||||
line({
|
||||
metric: tree.realized.sentInProfit.sats.sum,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.sats,
|
||||
}),
|
||||
line({
|
||||
metric: tree.realized.sentInProfit.dollars.sum,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
]),
|
||||
},
|
||||
{
|
||||
name: "In Profit Cumulative",
|
||||
title: title("Sent In Profit Cumulative"),
|
||||
bottom: list.flatMap(({ color, name, tree }) => [
|
||||
line({ metric: tree.realized.sentInProfit.bitcoin.cumulative, name, color, unit: Unit.btc }),
|
||||
line({ metric: tree.realized.sentInProfit.sats.cumulative, name, color, unit: Unit.sats }),
|
||||
line({ metric: tree.realized.sentInProfit.dollars.cumulative, name, color, unit: Unit.usd }),
|
||||
line({
|
||||
metric: tree.realized.sentInProfit.bitcoin.cumulative,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.btc,
|
||||
}),
|
||||
line({
|
||||
metric: tree.realized.sentInProfit.sats.cumulative,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.sats,
|
||||
}),
|
||||
line({
|
||||
metric: tree.realized.sentInProfit.dollars.cumulative,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
]),
|
||||
},
|
||||
{
|
||||
name: "In Loss",
|
||||
title: title("Sent In Loss"),
|
||||
bottom: list.flatMap(({ color, name, tree }) => [
|
||||
line({ metric: tree.realized.sentInLoss.bitcoin.sum, name, color, unit: Unit.btc }),
|
||||
line({ metric: tree.realized.sentInLoss.sats.sum, name, color, unit: Unit.sats }),
|
||||
line({ metric: tree.realized.sentInLoss.dollars.sum, name, color, unit: Unit.usd }),
|
||||
line({
|
||||
metric: tree.realized.sentInLoss.bitcoin.sum,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.btc,
|
||||
}),
|
||||
line({
|
||||
metric: tree.realized.sentInLoss.sats.sum,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.sats,
|
||||
}),
|
||||
line({
|
||||
metric: tree.realized.sentInLoss.dollars.sum,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
]),
|
||||
},
|
||||
{
|
||||
name: "In Loss Cumulative",
|
||||
title: title("Sent In Loss Cumulative"),
|
||||
bottom: list.flatMap(({ color, name, tree }) => [
|
||||
line({ metric: tree.realized.sentInLoss.bitcoin.cumulative, name, color, unit: Unit.btc }),
|
||||
line({ metric: tree.realized.sentInLoss.sats.cumulative, name, color, unit: Unit.sats }),
|
||||
line({ metric: tree.realized.sentInLoss.dollars.cumulative, name, color, unit: Unit.usd }),
|
||||
line({
|
||||
metric: tree.realized.sentInLoss.bitcoin.cumulative,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.btc,
|
||||
}),
|
||||
line({
|
||||
metric: tree.realized.sentInLoss.sats.cumulative,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.sats,
|
||||
}),
|
||||
line({
|
||||
metric: tree.realized.sentInLoss.dollars.cumulative,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
]),
|
||||
},
|
||||
{
|
||||
name: "In Profit 14d EMA",
|
||||
title: title("Sent In Profit 14d EMA"),
|
||||
bottom: list.flatMap(({ color, name, tree }) =>
|
||||
satsBtcUsd({ pattern: tree.realized.sentInProfit14dEma, name, color }),
|
||||
satsBtcUsd({
|
||||
pattern: tree.realized.sentInProfit14dEma,
|
||||
name,
|
||||
color,
|
||||
}),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "In Loss 14d EMA",
|
||||
title: title("Sent In Loss 14d EMA"),
|
||||
bottom: list.flatMap(({ color, name, tree }) =>
|
||||
satsBtcUsd({ pattern: tree.realized.sentInLoss14dEma, name, color }),
|
||||
satsBtcUsd({
|
||||
pattern: tree.realized.sentInLoss14dEma,
|
||||
name,
|
||||
color,
|
||||
}),
|
||||
),
|
||||
},
|
||||
],
|
||||
|
||||
@@ -12,16 +12,7 @@ import {
|
||||
spendableTypeColors,
|
||||
yearColors,
|
||||
} from "../colors/index.js";
|
||||
|
||||
/**
|
||||
* @template {Record<string, any>} T
|
||||
* @param {T} obj
|
||||
* @returns {[keyof T & string, T[keyof T & string]][]}
|
||||
*/
|
||||
const entries = (obj) =>
|
||||
/** @type {[keyof T & string, T[keyof T & string]][]} */ (
|
||||
Object.entries(obj)
|
||||
);
|
||||
import { entries } from "../../utils/array.js";
|
||||
|
||||
/** @type {readonly AddressableType[]} */
|
||||
const ADDRESSABLE_TYPES = [
|
||||
|
||||
@@ -854,7 +854,6 @@ function createSingleRealizedPriceChartsBasic(ctx, cohort, title) {
|
||||
color,
|
||||
unit: Unit.ratio,
|
||||
}),
|
||||
priceLine({ ctx, unit: Unit.ratio, number: 1 }),
|
||||
],
|
||||
},
|
||||
];
|
||||
@@ -891,7 +890,6 @@ function createSingleRealizedCapSeries(ctx, cohort, { extra = [] } = {}) {
|
||||
unit: Unit.usd,
|
||||
defaultActive: false,
|
||||
}),
|
||||
priceLine({ ctx, unit: Unit.usd, defaultActive: false }),
|
||||
...extra,
|
||||
];
|
||||
}
|
||||
@@ -983,56 +981,89 @@ function createSingleRealizedPnlSection(
|
||||
title,
|
||||
{ extra = [] } = {},
|
||||
) {
|
||||
const { colors, fromCountPattern, fromBitcoinPatternWithUnit } = ctx;
|
||||
const { colors } = ctx;
|
||||
const { tree } = cohort;
|
||||
|
||||
return [
|
||||
{
|
||||
name: "P&L",
|
||||
tree: [
|
||||
{
|
||||
name: "Sum",
|
||||
title: title("Realized P&L"),
|
||||
bottom: [
|
||||
line({
|
||||
metric: tree.realized.realizedProfit.sum,
|
||||
name: "Profit",
|
||||
color: colors.green,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
line({
|
||||
metric: tree.realized.realizedProfit7dEma,
|
||||
name: "Profit 7d EMA",
|
||||
color: colors.green,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
line({
|
||||
metric: tree.realized.realizedLoss.sum,
|
||||
name: "Loss",
|
||||
color: colors.red,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
line({
|
||||
metric: tree.realized.realizedLoss7dEma,
|
||||
name: "Loss 7d EMA",
|
||||
color: colors.red,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
line({
|
||||
metric: tree.realized.negRealizedLoss.sum,
|
||||
name: "Negative Loss",
|
||||
color: colors.red,
|
||||
unit: Unit.usd,
|
||||
defaultActive: false,
|
||||
}),
|
||||
...extra,
|
||||
line({
|
||||
metric: tree.realized.totalRealizedPnl,
|
||||
name: "Total",
|
||||
color: colors.default,
|
||||
unit: Unit.usd,
|
||||
defaultActive: false,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: title("Realized P&L (Total)"),
|
||||
bottom: [
|
||||
line({
|
||||
metric: tree.realized.realizedProfit.cumulative,
|
||||
name: "Profit",
|
||||
color: colors.green,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
line({
|
||||
metric: tree.realized.realizedLoss.cumulative,
|
||||
name: "Loss",
|
||||
color: colors.red,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
line({
|
||||
metric: tree.realized.negRealizedLoss.cumulative,
|
||||
name: "Negative Loss",
|
||||
color: colors.red,
|
||||
unit: Unit.usd,
|
||||
defaultActive: false,
|
||||
}),
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "P&L Relative",
|
||||
title: title("Realized P&L"),
|
||||
bottom: [
|
||||
...fromCountPattern({
|
||||
pattern: tree.realized.realizedProfit,
|
||||
unit: Unit.usd,
|
||||
cumulativeUnit: Unit.usdCumulative,
|
||||
title: "Profit",
|
||||
color: colors.green,
|
||||
}),
|
||||
line({
|
||||
metric: tree.realized.realizedProfit7dEma,
|
||||
name: "Profit 7d EMA",
|
||||
color: colors.green,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
...fromCountPattern({
|
||||
pattern: tree.realized.realizedLoss,
|
||||
unit: Unit.usd,
|
||||
cumulativeUnit: Unit.usdCumulative,
|
||||
title: "Loss",
|
||||
color: colors.red,
|
||||
}),
|
||||
line({
|
||||
metric: tree.realized.realizedLoss7dEma,
|
||||
name: "Loss 7d EMA",
|
||||
color: colors.red,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
...fromBitcoinPatternWithUnit({
|
||||
pattern: tree.realized.negRealizedLoss,
|
||||
unit: Unit.usd,
|
||||
cumulativeUnit: Unit.usdCumulative,
|
||||
title: "Negative Loss",
|
||||
color: colors.red,
|
||||
defaultActive: false,
|
||||
}),
|
||||
...extra,
|
||||
line({
|
||||
metric: tree.realized.totalRealizedPnl,
|
||||
name: "Total",
|
||||
color: colors.default,
|
||||
unit: Unit.usd,
|
||||
defaultActive: false,
|
||||
}),
|
||||
baseline({
|
||||
metric: tree.realized.realizedProfitRelToRealizedCap.sum,
|
||||
name: "Profit",
|
||||
@@ -1059,57 +1090,67 @@ function createSingleRealizedPnlSection(
|
||||
unit: Unit.pctRcap,
|
||||
defaultActive: false,
|
||||
}),
|
||||
priceLine({ ctx, unit: Unit.pctRcap }),
|
||||
priceLine({ ctx, unit: Unit.usd, defaultActive: false }),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Net P&L",
|
||||
title: title("Net Realized P&L"),
|
||||
bottom: [
|
||||
...fromCountPattern({
|
||||
pattern: tree.realized.netRealizedPnl,
|
||||
unit: Unit.usd,
|
||||
cumulativeUnit: Unit.usdCumulative,
|
||||
title: "Net",
|
||||
}),
|
||||
baseline({
|
||||
metric: tree.realized.netRealizedPnl7dEma,
|
||||
name: "Net 7d EMA",
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
baseline({
|
||||
metric: tree.realized.netRealizedPnlCumulative30dDelta,
|
||||
name: "Cumulative 30d Change",
|
||||
unit: Unit.usd,
|
||||
defaultActive: false,
|
||||
}),
|
||||
baseline({
|
||||
metric: tree.realized.netRealizedPnlRelToRealizedCap.sum,
|
||||
name: "Net",
|
||||
unit: Unit.pctRcap,
|
||||
}),
|
||||
baseline({
|
||||
metric: tree.realized.netRealizedPnlRelToRealizedCap.cumulative,
|
||||
name: "Net Cumulative",
|
||||
unit: Unit.pctRcap,
|
||||
defaultActive: false,
|
||||
}),
|
||||
baseline({
|
||||
metric:
|
||||
tree.realized.netRealizedPnlCumulative30dDeltaRelToRealizedCap,
|
||||
name: "Cumulative 30d Change",
|
||||
unit: Unit.pctRcap,
|
||||
defaultActive: false,
|
||||
}),
|
||||
baseline({
|
||||
metric: tree.realized.netRealizedPnlCumulative30dDeltaRelToMarketCap,
|
||||
name: "Cumulative 30d Change",
|
||||
unit: Unit.pctMcap,
|
||||
}),
|
||||
priceLine({ ctx, unit: Unit.pctMcap }),
|
||||
priceLine({ ctx, unit: Unit.pctRcap }),
|
||||
priceLine({ ctx, unit: Unit.usd }),
|
||||
tree: [
|
||||
{
|
||||
name: "Sum",
|
||||
title: title("Net Realized P&L"),
|
||||
bottom: [
|
||||
baseline({
|
||||
metric: tree.realized.netRealizedPnl.sum,
|
||||
name: "Net",
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
baseline({
|
||||
metric: tree.realized.netRealizedPnl7dEma,
|
||||
name: "Net 7d EMA",
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
baseline({
|
||||
metric: tree.realized.netRealizedPnlRelToRealizedCap.sum,
|
||||
name: "Net",
|
||||
unit: Unit.pctRcap,
|
||||
}),
|
||||
baseline({
|
||||
metric:
|
||||
tree.realized.netRealizedPnlCumulative30dDeltaRelToMarketCap,
|
||||
name: "30d Change",
|
||||
unit: Unit.pctMcap,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: title("Net Realized P&L (Total)"),
|
||||
bottom: [
|
||||
baseline({
|
||||
metric: tree.realized.netRealizedPnl.cumulative,
|
||||
name: "Net",
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
baseline({
|
||||
metric: tree.realized.netRealizedPnlCumulative30dDelta,
|
||||
name: "30d Change",
|
||||
unit: Unit.usd,
|
||||
defaultActive: false,
|
||||
}),
|
||||
baseline({
|
||||
metric: tree.realized.netRealizedPnlRelToRealizedCap.cumulative,
|
||||
name: "Net",
|
||||
unit: Unit.pctRcap,
|
||||
}),
|
||||
baseline({
|
||||
metric:
|
||||
tree.realized.netRealizedPnlCumulative30dDeltaRelToRealizedCap,
|
||||
name: "30d Change",
|
||||
unit: Unit.pctRcap,
|
||||
defaultActive: false,
|
||||
}),
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -1119,35 +1160,109 @@ function createSingleRealizedPnlSection(
|
||||
name: "In Profit",
|
||||
title: title("Sent In Profit"),
|
||||
bottom: [
|
||||
line({ metric: tree.realized.sentInProfit.bitcoin.sum, name: "Sum", color: colors.green, unit: Unit.btc }),
|
||||
line({ metric: tree.realized.sentInProfit.bitcoin.cumulative, name: "Cumulative", color: colors.green, unit: Unit.btc, defaultActive: false }),
|
||||
line({ metric: tree.realized.sentInProfit.sats.sum, name: "Sum", color: colors.green, unit: Unit.sats }),
|
||||
line({ metric: tree.realized.sentInProfit.sats.cumulative, name: "Cumulative", color: colors.green, unit: Unit.sats, defaultActive: false }),
|
||||
line({ metric: tree.realized.sentInProfit.dollars.sum, name: "Sum", color: colors.green, unit: Unit.usd }),
|
||||
line({ metric: tree.realized.sentInProfit.dollars.cumulative, name: "Cumulative", color: colors.green, unit: Unit.usd, defaultActive: false }),
|
||||
line({
|
||||
metric: tree.realized.sentInProfit.bitcoin.sum,
|
||||
name: "Sum",
|
||||
color: colors.green,
|
||||
unit: Unit.btc,
|
||||
}),
|
||||
line({
|
||||
metric: tree.realized.sentInProfit.bitcoin.cumulative,
|
||||
name: "Cumulative",
|
||||
color: colors.green,
|
||||
unit: Unit.btc,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: tree.realized.sentInProfit.sats.sum,
|
||||
name: "Sum",
|
||||
color: colors.green,
|
||||
unit: Unit.sats,
|
||||
}),
|
||||
line({
|
||||
metric: tree.realized.sentInProfit.sats.cumulative,
|
||||
name: "Cumulative",
|
||||
color: colors.green,
|
||||
unit: Unit.sats,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: tree.realized.sentInProfit.dollars.sum,
|
||||
name: "Sum",
|
||||
color: colors.green,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
line({
|
||||
metric: tree.realized.sentInProfit.dollars.cumulative,
|
||||
name: "Cumulative",
|
||||
color: colors.green,
|
||||
unit: Unit.usd,
|
||||
defaultActive: false,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "In Loss",
|
||||
title: title("Sent In Loss"),
|
||||
bottom: [
|
||||
line({ metric: tree.realized.sentInLoss.bitcoin.sum, name: "Sum", color: colors.red, unit: Unit.btc }),
|
||||
line({ metric: tree.realized.sentInLoss.bitcoin.cumulative, name: "Cumulative", color: colors.red, unit: Unit.btc, defaultActive: false }),
|
||||
line({ metric: tree.realized.sentInLoss.sats.sum, name: "Sum", color: colors.red, unit: Unit.sats }),
|
||||
line({ metric: tree.realized.sentInLoss.sats.cumulative, name: "Cumulative", color: colors.red, unit: Unit.sats, defaultActive: false }),
|
||||
line({ metric: tree.realized.sentInLoss.dollars.sum, name: "Sum", color: colors.red, unit: Unit.usd }),
|
||||
line({ metric: tree.realized.sentInLoss.dollars.cumulative, name: "Cumulative", color: colors.red, unit: Unit.usd, defaultActive: false }),
|
||||
line({
|
||||
metric: tree.realized.sentInLoss.bitcoin.sum,
|
||||
name: "Sum",
|
||||
color: colors.red,
|
||||
unit: Unit.btc,
|
||||
}),
|
||||
line({
|
||||
metric: tree.realized.sentInLoss.bitcoin.cumulative,
|
||||
name: "Cumulative",
|
||||
color: colors.red,
|
||||
unit: Unit.btc,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: tree.realized.sentInLoss.sats.sum,
|
||||
name: "Sum",
|
||||
color: colors.red,
|
||||
unit: Unit.sats,
|
||||
}),
|
||||
line({
|
||||
metric: tree.realized.sentInLoss.sats.cumulative,
|
||||
name: "Cumulative",
|
||||
color: colors.red,
|
||||
unit: Unit.sats,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: tree.realized.sentInLoss.dollars.sum,
|
||||
name: "Sum",
|
||||
color: colors.red,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
line({
|
||||
metric: tree.realized.sentInLoss.dollars.cumulative,
|
||||
name: "Cumulative",
|
||||
color: colors.red,
|
||||
unit: Unit.usd,
|
||||
defaultActive: false,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "In Profit 14d EMA",
|
||||
title: title("Sent In Profit 14d EMA"),
|
||||
bottom: satsBtcUsd({ pattern: tree.realized.sentInProfit14dEma, name: "14d EMA", color: colors.green }),
|
||||
bottom: satsBtcUsd({
|
||||
pattern: tree.realized.sentInProfit14dEma,
|
||||
name: "14d EMA",
|
||||
color: colors.green,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "In Loss 14d EMA",
|
||||
title: title("Sent In Loss 14d EMA"),
|
||||
bottom: satsBtcUsd({ pattern: tree.realized.sentInLoss14dEma, name: "14d EMA", color: colors.red }),
|
||||
bottom: satsBtcUsd({
|
||||
pattern: tree.realized.sentInLoss14dEma,
|
||||
name: "14d EMA",
|
||||
color: colors.red,
|
||||
}),
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -1171,9 +1286,27 @@ function createGroupedRealizedPnlSections(
|
||||
{ ratioMetrics } = {},
|
||||
) {
|
||||
const pnlConfigs = /** @type {const} */ ([
|
||||
{ name: "Profit", sum: "realizedProfit", ema: "realizedProfit7dEma", rel: "realizedProfitRelToRealizedCap", isNet: false },
|
||||
{ name: "Loss", sum: "realizedLoss", ema: "realizedLoss7dEma", rel: "realizedLossRelToRealizedCap", isNet: false },
|
||||
{ name: "Net P&L", sum: "netRealizedPnl", ema: "netRealizedPnl7dEma", rel: "netRealizedPnlRelToRealizedCap", isNet: true },
|
||||
{
|
||||
name: "Profit",
|
||||
sum: "realizedProfit",
|
||||
ema: "realizedProfit7dEma",
|
||||
rel: "realizedProfitRelToRealizedCap",
|
||||
isNet: false,
|
||||
},
|
||||
{
|
||||
name: "Loss",
|
||||
sum: "realizedLoss",
|
||||
ema: "realizedLoss7dEma",
|
||||
rel: "realizedLossRelToRealizedCap",
|
||||
isNet: false,
|
||||
},
|
||||
{
|
||||
name: "Net P&L",
|
||||
sum: "netRealizedPnl",
|
||||
ema: "netRealizedPnl7dEma",
|
||||
rel: "netRealizedPnlRelToRealizedCap",
|
||||
isNet: true,
|
||||
},
|
||||
]);
|
||||
|
||||
return [
|
||||
@@ -1185,10 +1318,19 @@ function createGroupedRealizedPnlSections(
|
||||
title: title(`Realized ${name}`),
|
||||
bottom: [
|
||||
...list.flatMap(({ color, name, tree }) => [
|
||||
(isNet ? baseline : line)({ metric: tree.realized[sum].sum, name, color, unit: Unit.usd }),
|
||||
baseline({ metric: tree.realized[rel].sum, name, color, unit: Unit.pctRcap }),
|
||||
(isNet ? baseline : line)({
|
||||
metric: tree.realized[sum].sum,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
baseline({
|
||||
metric: tree.realized[rel].sum,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.pctRcap,
|
||||
}),
|
||||
]),
|
||||
priceLine({ ctx, unit: Unit.usd }),
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -1196,9 +1338,13 @@ function createGroupedRealizedPnlSections(
|
||||
title: title(`Realized ${name} 7d EMA`),
|
||||
bottom: [
|
||||
...list.map(({ color, name, tree }) =>
|
||||
(isNet ? baseline : line)({ metric: tree.realized[ema], name, color, unit: Unit.usd }),
|
||||
(isNet ? baseline : line)({
|
||||
metric: tree.realized[ema],
|
||||
name,
|
||||
color,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
),
|
||||
priceLine({ ctx, unit: Unit.usd }),
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -1207,7 +1353,12 @@ function createGroupedRealizedPnlSections(
|
||||
name: "Total P&L",
|
||||
title: title("Total Realized P&L"),
|
||||
bottom: list.flatMap((cohort) => [
|
||||
line({ metric: cohort.tree.realized.totalRealizedPnl, name: cohort.name, color: cohort.color, unit: Unit.usd }),
|
||||
line({
|
||||
metric: cohort.tree.realized.totalRealizedPnl,
|
||||
name: cohort.name,
|
||||
color: cohort.color,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
...(ratioMetrics ? ratioMetrics(cohort) : []),
|
||||
]),
|
||||
},
|
||||
@@ -1250,7 +1401,6 @@ function createGroupedRealizedPnlSections(
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
]),
|
||||
priceLine({ ctx, unit: Unit.usd }),
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -1280,9 +1430,6 @@ function createGroupedRealizedPnlSections(
|
||||
unit: Unit.pctMcap,
|
||||
}),
|
||||
]),
|
||||
priceLine({ ctx, unit: Unit.usd }),
|
||||
priceLine({ ctx, unit: Unit.pctMcap }),
|
||||
priceLine({ ctx, unit: Unit.pctRcap }),
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -1294,50 +1441,118 @@ function createGroupedRealizedPnlSections(
|
||||
name: "In Profit",
|
||||
title: title("Sent In Profit"),
|
||||
bottom: list.flatMap(({ color, name, tree }) => [
|
||||
line({ metric: tree.realized.sentInProfit.bitcoin.sum, name, color, unit: Unit.btc }),
|
||||
line({ metric: tree.realized.sentInProfit.sats.sum, name, color, unit: Unit.sats }),
|
||||
line({ metric: tree.realized.sentInProfit.dollars.sum, name, color, unit: Unit.usd }),
|
||||
line({
|
||||
metric: tree.realized.sentInProfit.bitcoin.sum,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.btc,
|
||||
}),
|
||||
line({
|
||||
metric: tree.realized.sentInProfit.sats.sum,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.sats,
|
||||
}),
|
||||
line({
|
||||
metric: tree.realized.sentInProfit.dollars.sum,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
]),
|
||||
},
|
||||
{
|
||||
name: "In Profit Cumulative",
|
||||
title: title("Sent In Profit Cumulative"),
|
||||
bottom: list.flatMap(({ color, name, tree }) => [
|
||||
line({ metric: tree.realized.sentInProfit.bitcoin.cumulative, name, color, unit: Unit.btc }),
|
||||
line({ metric: tree.realized.sentInProfit.sats.cumulative, name, color, unit: Unit.sats }),
|
||||
line({ metric: tree.realized.sentInProfit.dollars.cumulative, name, color, unit: Unit.usd }),
|
||||
line({
|
||||
metric: tree.realized.sentInProfit.bitcoin.cumulative,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.btc,
|
||||
}),
|
||||
line({
|
||||
metric: tree.realized.sentInProfit.sats.cumulative,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.sats,
|
||||
}),
|
||||
line({
|
||||
metric: tree.realized.sentInProfit.dollars.cumulative,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
]),
|
||||
},
|
||||
{
|
||||
name: "In Loss",
|
||||
title: title("Sent In Loss"),
|
||||
bottom: list.flatMap(({ color, name, tree }) => [
|
||||
line({ metric: tree.realized.sentInLoss.bitcoin.sum, name, color, unit: Unit.btc }),
|
||||
line({ metric: tree.realized.sentInLoss.sats.sum, name, color, unit: Unit.sats }),
|
||||
line({ metric: tree.realized.sentInLoss.dollars.sum, name, color, unit: Unit.usd }),
|
||||
line({
|
||||
metric: tree.realized.sentInLoss.bitcoin.sum,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.btc,
|
||||
}),
|
||||
line({
|
||||
metric: tree.realized.sentInLoss.sats.sum,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.sats,
|
||||
}),
|
||||
line({
|
||||
metric: tree.realized.sentInLoss.dollars.sum,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
]),
|
||||
},
|
||||
{
|
||||
name: "In Loss Cumulative",
|
||||
title: title("Sent In Loss Cumulative"),
|
||||
bottom: list.flatMap(({ color, name, tree }) => [
|
||||
line({ metric: tree.realized.sentInLoss.bitcoin.cumulative, name, color, unit: Unit.btc }),
|
||||
line({ metric: tree.realized.sentInLoss.sats.cumulative, name, color, unit: Unit.sats }),
|
||||
line({ metric: tree.realized.sentInLoss.dollars.cumulative, name, color, unit: Unit.usd }),
|
||||
line({
|
||||
metric: tree.realized.sentInLoss.bitcoin.cumulative,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.btc,
|
||||
}),
|
||||
line({
|
||||
metric: tree.realized.sentInLoss.sats.cumulative,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.sats,
|
||||
}),
|
||||
line({
|
||||
metric: tree.realized.sentInLoss.dollars.cumulative,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
]),
|
||||
},
|
||||
{
|
||||
name: "In Profit 14d EMA",
|
||||
title: title("Sent In Profit 14d EMA"),
|
||||
bottom: list.flatMap(({ color, name, tree }) =>
|
||||
satsBtcUsd({ pattern: tree.realized.sentInProfit14dEma, name, color }),
|
||||
satsBtcUsd({
|
||||
pattern: tree.realized.sentInProfit14dEma,
|
||||
name,
|
||||
color,
|
||||
}),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "In Loss 14d EMA",
|
||||
title: title("Sent In Loss 14d EMA"),
|
||||
bottom: list.flatMap(({ color, name, tree }) =>
|
||||
satsBtcUsd({ pattern: tree.realized.sentInLoss14dEma, name, color }),
|
||||
satsBtcUsd({
|
||||
pattern: tree.realized.sentInLoss14dEma,
|
||||
name,
|
||||
color,
|
||||
}),
|
||||
),
|
||||
},
|
||||
],
|
||||
@@ -1687,7 +1902,6 @@ function createNetUnrealizedPnlRelToOwnPnlMetrics(ctx, rel) {
|
||||
name: "Net",
|
||||
unit: Unit.pctOwnPnl,
|
||||
}),
|
||||
priceLine({ ctx, unit: Unit.pctOwnPnl }),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -1785,7 +1999,6 @@ function createNuplChart(ctx, rel, title) {
|
||||
name: "NUPL",
|
||||
unit: Unit.ratio,
|
||||
}),
|
||||
priceLine({ ctx, unit: Unit.ratio }),
|
||||
],
|
||||
};
|
||||
}
|
||||
@@ -1880,10 +2093,7 @@ function createSingleInvestedCapitalRelativeChart(ctx, rel, title) {
|
||||
return {
|
||||
name: "Relative",
|
||||
title: title("Invested Capital In Profit & Loss %"),
|
||||
bottom: [
|
||||
...createInvestedCapitalRelMetrics(ctx, rel),
|
||||
priceLine({ ctx, unit: Unit.pctRcap }),
|
||||
],
|
||||
bottom: [...createInvestedCapitalRelMetrics(ctx, rel)],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1938,7 +2148,6 @@ function createGroupedNuplChart(ctx, list, title) {
|
||||
unit: Unit.ratio,
|
||||
}),
|
||||
),
|
||||
priceLine({ ctx, unit: Unit.ratio }),
|
||||
],
|
||||
};
|
||||
}
|
||||
@@ -2023,20 +2232,12 @@ function createUnrealizedSection({
|
||||
{
|
||||
name: "P&L",
|
||||
title: title("Unrealized P&L"),
|
||||
bottom: [
|
||||
...createUnrealizedPnlBaseMetrics(ctx, tree),
|
||||
...pnl,
|
||||
priceLine({ ctx, unit: Unit.usd, defaultActive: false }),
|
||||
],
|
||||
bottom: [...createUnrealizedPnlBaseMetrics(ctx, tree), ...pnl],
|
||||
},
|
||||
{
|
||||
name: "Net P&L",
|
||||
title: title("Net Unrealized P&L"),
|
||||
bottom: [
|
||||
createNetUnrealizedPnlBaseMetric(tree),
|
||||
...netPnl,
|
||||
priceLine({ ctx, unit: Unit.usd }),
|
||||
],
|
||||
bottom: [createNetUnrealizedPnlBaseMetric(tree), ...netPnl],
|
||||
},
|
||||
investedCapitalFolder,
|
||||
{
|
||||
@@ -2256,14 +2457,16 @@ function createSingleUnrealizedSectionAll(ctx, cohort, title) {
|
||||
pnl: [
|
||||
...createUnrealizedPnlRelToMarketCapMetrics(ctx, tree.relative),
|
||||
...createUnrealizedPnlRelToOwnPnlMetrics(ctx, tree.relative),
|
||||
priceLine({ ctx, unit: Unit.pctMcap, defaultActive: false }),
|
||||
],
|
||||
netPnl: [
|
||||
...createNetUnrealizedPnlRelToMarketCapMetrics(tree.relative),
|
||||
...createNetUnrealizedPnlRelToOwnPnlMetrics(ctx, tree.relative),
|
||||
priceLine({ ctx, unit: Unit.pctMcap }),
|
||||
],
|
||||
investedCapitalFolder: createSingleInvestedCapitalFolderFull(ctx, tree, title),
|
||||
investedCapitalFolder: createSingleInvestedCapitalFolderFull(
|
||||
ctx,
|
||||
tree,
|
||||
title,
|
||||
),
|
||||
charts: [
|
||||
createNuplChart(ctx, tree.relative, title),
|
||||
createPeakRegretChartWithMarketCap(ctx, tree, title),
|
||||
@@ -2287,15 +2490,17 @@ function createSingleUnrealizedSectionFull(ctx, cohort, title) {
|
||||
...createUnrealizedPnlRelToMarketCapMetrics(ctx, tree.relative),
|
||||
...createUnrealizedPnlRelToOwnMarketCapMetrics(ctx, tree.relative),
|
||||
...createUnrealizedPnlRelToOwnPnlMetrics(ctx, tree.relative),
|
||||
priceLine({ ctx, unit: Unit.pctMcap, defaultActive: false }),
|
||||
],
|
||||
netPnl: [
|
||||
...createNetUnrealizedPnlRelToMarketCapMetrics(tree.relative),
|
||||
...createNetUnrealizedPnlRelToOwnMarketCapMetrics(ctx, tree.relative),
|
||||
...createNetUnrealizedPnlRelToOwnPnlMetrics(ctx, tree.relative),
|
||||
priceLine({ ctx, unit: Unit.pctMcap }),
|
||||
],
|
||||
investedCapitalFolder: createSingleInvestedCapitalFolderFull(ctx, tree, title),
|
||||
investedCapitalFolder: createSingleInvestedCapitalFolderFull(
|
||||
ctx,
|
||||
tree,
|
||||
title,
|
||||
),
|
||||
charts: [
|
||||
createNuplChart(ctx, tree.relative, title),
|
||||
createPeakRegretChartWithMarketCap(ctx, tree, title),
|
||||
@@ -2315,15 +2520,13 @@ function createSingleUnrealizedSectionWithMarketCap(ctx, cohort, title) {
|
||||
ctx,
|
||||
tree,
|
||||
title,
|
||||
pnl: [
|
||||
...createUnrealizedPnlRelToMarketCapMetrics(ctx, tree.relative),
|
||||
priceLine({ ctx, unit: Unit.pctMcap, defaultActive: false }),
|
||||
],
|
||||
netPnl: [
|
||||
...createNetUnrealizedPnlRelToMarketCapMetrics(tree.relative),
|
||||
priceLine({ ctx, unit: Unit.pctMcap }),
|
||||
],
|
||||
investedCapitalFolder: createSingleInvestedCapitalFolderFull(ctx, tree, title),
|
||||
pnl: [...createUnrealizedPnlRelToMarketCapMetrics(ctx, tree.relative)],
|
||||
netPnl: [...createNetUnrealizedPnlRelToMarketCapMetrics(tree.relative)],
|
||||
investedCapitalFolder: createSingleInvestedCapitalFolderFull(
|
||||
ctx,
|
||||
tree,
|
||||
title,
|
||||
),
|
||||
charts: [
|
||||
createNuplChart(ctx, tree.relative, title),
|
||||
createPeakRegretChartWithMarketCap(ctx, tree, title),
|
||||
@@ -2343,15 +2546,13 @@ function createSingleUnrealizedSectionWithMarketCapOnly(ctx, cohort, title) {
|
||||
ctx,
|
||||
tree,
|
||||
title,
|
||||
pnl: [
|
||||
...createUnrealizedPnlRelToMarketCapMetrics(ctx, tree.relative),
|
||||
priceLine({ ctx, unit: Unit.pctMcap, defaultActive: false }),
|
||||
],
|
||||
netPnl: [
|
||||
...createNetUnrealizedPnlRelToMarketCapMetrics(tree.relative),
|
||||
priceLine({ ctx, unit: Unit.pctMcap }),
|
||||
],
|
||||
investedCapitalFolder: createSingleInvestedCapitalFolderFull(ctx, tree, title),
|
||||
pnl: [...createUnrealizedPnlRelToMarketCapMetrics(ctx, tree.relative)],
|
||||
netPnl: [...createNetUnrealizedPnlRelToMarketCapMetrics(tree.relative)],
|
||||
investedCapitalFolder: createSingleInvestedCapitalFolderFull(
|
||||
ctx,
|
||||
tree,
|
||||
title,
|
||||
),
|
||||
charts: [createNuplChart(ctx, tree.relative, title)],
|
||||
});
|
||||
}
|
||||
@@ -2368,15 +2569,13 @@ function createSingleUnrealizedSectionMinAge(ctx, cohort, title) {
|
||||
ctx,
|
||||
tree,
|
||||
title,
|
||||
pnl: [
|
||||
...createUnrealizedPnlRelToMarketCapMetrics(ctx, tree.relative),
|
||||
priceLine({ ctx, unit: Unit.pctMcap, defaultActive: false }),
|
||||
],
|
||||
netPnl: [
|
||||
...createNetUnrealizedPnlRelToMarketCapMetrics(tree.relative),
|
||||
priceLine({ ctx, unit: Unit.pctMcap }),
|
||||
],
|
||||
investedCapitalFolder: createSingleInvestedCapitalFolderFull(ctx, tree, title),
|
||||
pnl: [...createUnrealizedPnlRelToMarketCapMetrics(ctx, tree.relative)],
|
||||
netPnl: [...createNetUnrealizedPnlRelToMarketCapMetrics(tree.relative)],
|
||||
investedCapitalFolder: createSingleInvestedCapitalFolderFull(
|
||||
ctx,
|
||||
tree,
|
||||
title,
|
||||
),
|
||||
charts: [
|
||||
createNuplChart(ctx, tree.relative, title),
|
||||
createPeakRegretChartWithMarketCap(ctx, tree, title),
|
||||
@@ -2603,7 +2802,11 @@ function createSingleUnrealizedSectionWithNupl({ ctx, cohort, title }) {
|
||||
...createNetUnrealizedPnlRelToOwnMarketCapMetrics(ctx, tree.relative),
|
||||
...createNetUnrealizedPnlRelToOwnPnlMetrics(ctx, tree.relative),
|
||||
],
|
||||
investedCapitalFolder: createSingleInvestedCapitalFolderFull(ctx, tree, title),
|
||||
investedCapitalFolder: createSingleInvestedCapitalFolderFull(
|
||||
ctx,
|
||||
tree,
|
||||
title,
|
||||
),
|
||||
charts: [
|
||||
createNuplChart(ctx, tree.relative, title),
|
||||
createPeakRegretChartWithMarketCap(ctx, tree, title),
|
||||
@@ -2669,7 +2872,11 @@ function createSingleUnrealizedSectionAgeRange(ctx, cohort, title) {
|
||||
...createNetUnrealizedPnlRelToOwnMarketCapMetrics(ctx, tree.relative),
|
||||
...createNetUnrealizedPnlRelToOwnPnlMetrics(ctx, tree.relative),
|
||||
],
|
||||
investedCapitalFolder: createSingleInvestedCapitalFolderFull(ctx, tree, title),
|
||||
investedCapitalFolder: createSingleInvestedCapitalFolderFull(
|
||||
ctx,
|
||||
tree,
|
||||
title,
|
||||
),
|
||||
charts: [createPeakRegretChart(ctx, tree, title)],
|
||||
});
|
||||
}
|
||||
@@ -2895,7 +3102,7 @@ function createGroupedCostBasisSectionWithPercentiles(ctx, list, title) {
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
function createActivitySection({ ctx, cohort, title, valueMetrics = [] }) {
|
||||
const { colors, fromCountPattern, fromBitcoinPatternWithUnit } = ctx;
|
||||
const { colors } = ctx;
|
||||
const { tree, color } = cohort;
|
||||
|
||||
return {
|
||||
@@ -2903,41 +3110,70 @@ function createActivitySection({ ctx, cohort, title, valueMetrics = [] }) {
|
||||
tree: [
|
||||
{
|
||||
name: "Sent",
|
||||
title: title("Sent"),
|
||||
bottom: [
|
||||
...fromCountPattern({
|
||||
pattern: tree.activity.sent.sats,
|
||||
unit: Unit.sats,
|
||||
cumulativeUnit: Unit.satsCumulative,
|
||||
color: color,
|
||||
}),
|
||||
...fromBitcoinPatternWithUnit({
|
||||
pattern: tree.activity.sent.bitcoin,
|
||||
unit: Unit.btc,
|
||||
cumulativeUnit: Unit.btcCumulative,
|
||||
color: color,
|
||||
}),
|
||||
...fromCountPattern({
|
||||
pattern: tree.activity.sent.dollars,
|
||||
unit: Unit.usd,
|
||||
cumulativeUnit: Unit.usdCumulative,
|
||||
color: color,
|
||||
}),
|
||||
line({
|
||||
metric: tree.activity.sent14dEma.sats,
|
||||
name: "14d EMA",
|
||||
unit: Unit.sats,
|
||||
}),
|
||||
line({
|
||||
metric: tree.activity.sent14dEma.bitcoin,
|
||||
name: "14d EMA",
|
||||
unit: Unit.btc,
|
||||
}),
|
||||
line({
|
||||
metric: tree.activity.sent14dEma.dollars,
|
||||
name: "14d EMA",
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
tree: [
|
||||
{
|
||||
name: "Sum",
|
||||
title: title("Sent"),
|
||||
bottom: [
|
||||
line({
|
||||
metric: tree.activity.sent.sats.sum,
|
||||
name: "sum",
|
||||
color,
|
||||
unit: Unit.sats,
|
||||
}),
|
||||
line({
|
||||
metric: tree.activity.sent.bitcoin.sum,
|
||||
name: "sum",
|
||||
color,
|
||||
unit: Unit.btc,
|
||||
}),
|
||||
line({
|
||||
metric: tree.activity.sent.dollars.sum,
|
||||
name: "sum",
|
||||
color,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
line({
|
||||
metric: tree.activity.sent14dEma.sats,
|
||||
name: "14d EMA",
|
||||
unit: Unit.sats,
|
||||
}),
|
||||
line({
|
||||
metric: tree.activity.sent14dEma.bitcoin,
|
||||
name: "14d EMA",
|
||||
unit: Unit.btc,
|
||||
}),
|
||||
line({
|
||||
metric: tree.activity.sent14dEma.dollars,
|
||||
name: "14d EMA",
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: title("Sent (Total)"),
|
||||
bottom: [
|
||||
line({
|
||||
metric: tree.activity.sent.sats.cumulative,
|
||||
name: "all-time",
|
||||
color,
|
||||
unit: Unit.sats,
|
||||
}),
|
||||
line({
|
||||
metric: tree.activity.sent.bitcoin.cumulative,
|
||||
name: "all-time",
|
||||
color,
|
||||
unit: Unit.btc,
|
||||
}),
|
||||
line({
|
||||
metric: tree.activity.sent.dollars.cumulative,
|
||||
name: "all-time",
|
||||
color,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -104,7 +104,6 @@ export function createMomentumSection(ctx, indicators) {
|
||||
name: "Histogram",
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
priceLine({ ctx, unit: Unit.usd }),
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
@@ -64,7 +64,6 @@ export function createReturnsSection(ctx, returns) {
|
||||
}),
|
||||
]
|
||||
: []),
|
||||
priceLine({ ctx, unit: Unit.percentage }),
|
||||
],
|
||||
};
|
||||
};
|
||||
@@ -121,7 +120,6 @@ export function createReturnsSection(ctx, returns) {
|
||||
color: colors.blue,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
priceLine({ ctx, unit: Unit.percentage }),
|
||||
],
|
||||
},
|
||||
// Short-term (1d, 1w, 1m)
|
||||
|
||||
@@ -88,7 +88,6 @@ export function createVolatilitySection(ctx, { volatility, range }) {
|
||||
color: colors.lime,
|
||||
unit: Unit.ratio,
|
||||
}),
|
||||
priceLine({ ctx, unit: Unit.ratio }),
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -113,7 +112,6 @@ export function createVolatilitySection(ctx, { volatility, range }) {
|
||||
color: colors.lime,
|
||||
unit: Unit.ratio,
|
||||
}),
|
||||
priceLine({ ctx, unit: Unit.ratio }),
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
/** Mining section - Network security and miner economics */
|
||||
|
||||
import { Unit } from "../utils/units.js";
|
||||
import { priceLine } from "./constants.js";
|
||||
import { entries, includes } from "../utils/array.js";
|
||||
import { colorAt } from "../chart/colors.js";
|
||||
import { line, baseline, dots, dotted } from "./series.js";
|
||||
import { satsBtcUsd } from "./shared.js";
|
||||
import { fromCountPattern } from "./series.js";
|
||||
import {
|
||||
satsBtcUsd,
|
||||
satsBtcUsdFrom,
|
||||
satsBtcUsdFromFull,
|
||||
revenueBtcSatsUsd,
|
||||
} from "./shared.js";
|
||||
|
||||
/** Major pools to show in Compare section (by current hashrate dominance) */
|
||||
const MAJOR_POOL_IDS = [
|
||||
const MAJOR_POOL_IDS = /** @type {const} */ ([
|
||||
"foundryusa", // ~32% - largest pool
|
||||
"antpool", // ~18% - Bitmain-owned
|
||||
"viabtc", // ~14% - independent
|
||||
@@ -16,14 +21,14 @@ const MAJOR_POOL_IDS = [
|
||||
"braiinspool", // formerly Slush Pool
|
||||
"spiderpool", // growing Asian pool
|
||||
"ocean", // decentralization-focused
|
||||
];
|
||||
]);
|
||||
|
||||
/**
|
||||
* AntPool & friends - pools sharing AntPool's block templates
|
||||
* Based on b10c's research: https://b10c.me/blog/015-bitcoin-mining-centralization/
|
||||
* Collectively ~35-40% of network hashrate
|
||||
*/
|
||||
const ANTPOOL_AND_FRIENDS_IDS = [
|
||||
const ANTPOOL_AND_FRIENDS_IDS = /** @type {const} */ ([
|
||||
"antpool", // Bitmain-owned, template source
|
||||
"poolin", // shares AntPool templates
|
||||
"btccom", // CloverPool (formerly BTC.com)
|
||||
@@ -34,7 +39,7 @@ const ANTPOOL_AND_FRIENDS_IDS = [
|
||||
"sigmapoolcom", // SigmaPool
|
||||
"rawpool", // shares AntPool templates
|
||||
"luxor", // shares AntPool templates
|
||||
];
|
||||
]);
|
||||
|
||||
/**
|
||||
* Create Mining section
|
||||
@@ -42,143 +47,164 @@ const ANTPOOL_AND_FRIENDS_IDS = [
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
export function createMiningSection(ctx) {
|
||||
const {
|
||||
colors,
|
||||
brk,
|
||||
fromSumStatsPattern,
|
||||
fromCoinbasePattern,
|
||||
fromValuePattern,
|
||||
} = ctx;
|
||||
const { colors, brk, distributionBtcSatsUsd } = ctx;
|
||||
const { blocks, transactions, pools } = brk.metrics;
|
||||
|
||||
// Build pools tree dynamically
|
||||
const poolEntries = Object.entries(pools.vecs);
|
||||
const poolsTree = poolEntries.map(([key, pool]) => {
|
||||
const poolName =
|
||||
brk.POOL_ID_TO_POOL_NAME[
|
||||
/** @type {keyof typeof brk.POOL_ID_TO_POOL_NAME} */ (key.toLowerCase())
|
||||
] || key;
|
||||
return {
|
||||
name: poolName,
|
||||
tree: [
|
||||
{
|
||||
name: "Dominance",
|
||||
title: `Dominance: ${poolName}`,
|
||||
bottom: [
|
||||
dots({
|
||||
metric: pool._24hDominance,
|
||||
name: "24h",
|
||||
color: colors.pink,
|
||||
unit: Unit.percentage,
|
||||
defaultActive: false,
|
||||
// Pre-compute pool entries with resolved names
|
||||
const poolData = entries(pools.vecs).map(([id, pool]) => ({
|
||||
id,
|
||||
name: brk.POOL_ID_TO_POOL_NAME[id],
|
||||
pool,
|
||||
}));
|
||||
|
||||
// Filtered pool groups for comparisons
|
||||
const majorPools = poolData.filter((p) => includes(MAJOR_POOL_IDS, p.id));
|
||||
const antpoolFriends = poolData.filter((p) =>
|
||||
includes(ANTPOOL_AND_FRIENDS_IDS, p.id),
|
||||
);
|
||||
|
||||
// Build individual pool trees
|
||||
const poolsTree = poolData.map(({ name, pool }) => ({
|
||||
name,
|
||||
tree: [
|
||||
{
|
||||
name: "Dominance",
|
||||
title: `Dominance: ${name}`,
|
||||
bottom: [
|
||||
dots({
|
||||
metric: pool._24hDominance,
|
||||
name: "24h",
|
||||
color: colors.time._24h,
|
||||
unit: Unit.percentage,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: pool._1wDominance,
|
||||
name: "1w",
|
||||
color: colors.time._1w,
|
||||
unit: Unit.percentage,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: pool._1mDominance,
|
||||
name: "1m",
|
||||
color: colors.time._1m,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
line({
|
||||
metric: pool._1yDominance,
|
||||
name: "1y",
|
||||
color: colors.time._1y,
|
||||
unit: Unit.percentage,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: pool.dominance,
|
||||
name: "All Time",
|
||||
color: colors.time.all,
|
||||
unit: Unit.percentage,
|
||||
defaultActive: false,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Blocks Mined",
|
||||
tree: [
|
||||
{
|
||||
name: "Sum",
|
||||
title: `Blocks Mined: ${name}`,
|
||||
bottom: [
|
||||
line({
|
||||
metric: pool.blocksMined.sum,
|
||||
name: "sum",
|
||||
unit: Unit.count,
|
||||
}),
|
||||
line({
|
||||
metric: pool._24hBlocksMined,
|
||||
name: "24h",
|
||||
color: colors.time._24h,
|
||||
unit: Unit.count,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: pool._1wBlocksMined,
|
||||
name: "1w",
|
||||
color: colors.time._1w,
|
||||
unit: Unit.count,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: pool._1mBlocksMined,
|
||||
name: "1m",
|
||||
color: colors.time._1m,
|
||||
unit: Unit.count,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: pool._1yBlocksMined,
|
||||
name: "1y",
|
||||
color: colors.time._1y,
|
||||
unit: Unit.count,
|
||||
defaultActive: false,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: `Blocks Mined: ${name} (Total)`,
|
||||
bottom: [
|
||||
line({
|
||||
metric: pool.blocksMined.cumulative,
|
||||
name: "all-time",
|
||||
unit: Unit.count,
|
||||
}),
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Rewards",
|
||||
tree: [
|
||||
{
|
||||
name: "Sum",
|
||||
title: `Rewards: ${name}`,
|
||||
bottom: revenueBtcSatsUsd(colors, {
|
||||
coinbase: pool.coinbase,
|
||||
subsidy: pool.subsidy,
|
||||
fee: pool.fee,
|
||||
key: "sum",
|
||||
}),
|
||||
line({
|
||||
metric: pool._1wDominance,
|
||||
name: "1w",
|
||||
color: colors.red,
|
||||
unit: Unit.percentage,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: `Rewards: ${name} (Total)`,
|
||||
bottom: revenueBtcSatsUsd(colors, {
|
||||
coinbase: pool.coinbase,
|
||||
subsidy: pool.subsidy,
|
||||
fee: pool.fee,
|
||||
key: "cumulative",
|
||||
}),
|
||||
line({
|
||||
metric: pool._1mDominance,
|
||||
name: "1m",
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
line({
|
||||
metric: pool._1yDominance,
|
||||
name: "1y",
|
||||
color: colors.lime,
|
||||
unit: Unit.percentage,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: pool.dominance,
|
||||
name: "All Time",
|
||||
color: colors.teal,
|
||||
unit: Unit.percentage,
|
||||
defaultActive: false,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Blocks Mined",
|
||||
title: `Blocks Mined: ${poolName}`,
|
||||
bottom: [
|
||||
...fromCountPattern({
|
||||
pattern: pool.blocksMined,
|
||||
unit: Unit.count,
|
||||
cumulativeUnit: Unit.countCumulative,
|
||||
}),
|
||||
line({
|
||||
metric: pool._24hBlocksMined,
|
||||
name: "24h",
|
||||
color: colors.pink,
|
||||
unit: Unit.count,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: pool._1wBlocksMined,
|
||||
name: "1w",
|
||||
color: colors.red,
|
||||
unit: Unit.count,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: pool._1mBlocksMined,
|
||||
name: "1m",
|
||||
color: colors.orange,
|
||||
unit: Unit.count,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: pool._1yBlocksMined,
|
||||
name: "1y",
|
||||
color: colors.purple,
|
||||
unit: Unit.count,
|
||||
defaultActive: false,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Rewards",
|
||||
title: `Rewards: ${poolName}`,
|
||||
bottom: [
|
||||
...fromValuePattern({
|
||||
pattern: pool.coinbase,
|
||||
title: "coinbase",
|
||||
color: colors.orange,
|
||||
}),
|
||||
...fromValuePattern({
|
||||
pattern: pool.subsidy,
|
||||
title: "subsidy",
|
||||
color: colors.lime,
|
||||
}),
|
||||
...fromValuePattern({
|
||||
pattern: pool.fee,
|
||||
title: "fee",
|
||||
color: colors.cyan,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Since Last Block",
|
||||
title: `Since Last Block: ${poolName}`,
|
||||
bottom: [
|
||||
line({
|
||||
metric: pool.blocksSinceBlock,
|
||||
name: "Elapsed",
|
||||
unit: Unit.blocks,
|
||||
}),
|
||||
line({
|
||||
metric: pool.daysSinceBlock,
|
||||
name: "Elapsed",
|
||||
unit: Unit.days,
|
||||
}),
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
});
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Since Last Block",
|
||||
title: `Since Last Block: ${name}`,
|
||||
bottom: [
|
||||
line({
|
||||
metric: pool.blocksSinceBlock,
|
||||
name: "Elapsed",
|
||||
unit: Unit.blocks,
|
||||
}),
|
||||
line({
|
||||
metric: pool.daysSinceBlock,
|
||||
name: "Elapsed",
|
||||
unit: Unit.days,
|
||||
}),
|
||||
],
|
||||
},
|
||||
],
|
||||
}));
|
||||
|
||||
return {
|
||||
name: "Mining",
|
||||
@@ -196,28 +222,28 @@ export function createMiningSection(ctx) {
|
||||
line({
|
||||
metric: blocks.mining.hashRate1wSma,
|
||||
name: "1w SMA",
|
||||
color: colors.red,
|
||||
color: colors.time._1w,
|
||||
unit: Unit.hashRate,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: blocks.mining.hashRate1mSma,
|
||||
name: "1m SMA",
|
||||
color: colors.orange,
|
||||
color: colors.time._1m,
|
||||
unit: Unit.hashRate,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: blocks.mining.hashRate2mSma,
|
||||
name: "2m SMA",
|
||||
color: colors.yellow,
|
||||
color: colors.orange,
|
||||
unit: Unit.hashRate,
|
||||
defaultActive: false,
|
||||
}),
|
||||
line({
|
||||
metric: blocks.mining.hashRate1ySma,
|
||||
name: "1y SMA",
|
||||
color: colors.lime,
|
||||
color: colors.time._1y,
|
||||
unit: Unit.hashRate,
|
||||
defaultActive: false,
|
||||
}),
|
||||
@@ -265,7 +291,6 @@ export function createMiningSection(ctx) {
|
||||
name: "Change",
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
priceLine({ ctx, number: 0, unit: Unit.percentage }),
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -293,72 +318,182 @@ export function createMiningSection(ctx) {
|
||||
{
|
||||
name: "Revenue",
|
||||
tree: [
|
||||
{
|
||||
name: "Compare",
|
||||
tree: [
|
||||
{
|
||||
name: "Sum",
|
||||
title: "Revenue Comparison",
|
||||
bottom: revenueBtcSatsUsd(colors, {
|
||||
coinbase: blocks.rewards.coinbase,
|
||||
subsidy: blocks.rewards.subsidy,
|
||||
fee: transactions.fees.fee,
|
||||
key: "sum",
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: "Revenue Comparison (Total)",
|
||||
bottom: revenueBtcSatsUsd(colors, {
|
||||
coinbase: blocks.rewards.coinbase,
|
||||
subsidy: blocks.rewards.subsidy,
|
||||
fee: transactions.fees.fee,
|
||||
key: "cumulative",
|
||||
}),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Coinbase",
|
||||
title: "Coinbase Rewards",
|
||||
bottom: [
|
||||
...fromCoinbasePattern({ pattern: blocks.rewards.coinbase }),
|
||||
...satsBtcUsd({
|
||||
pattern: blocks.rewards._24hCoinbaseSum,
|
||||
name: "24h sum",
|
||||
color: colors.pink,
|
||||
defaultActive: false,
|
||||
}),
|
||||
tree: [
|
||||
{
|
||||
name: "Sum",
|
||||
title: "Coinbase Rewards",
|
||||
bottom: [
|
||||
...satsBtcUsdFromFull({
|
||||
source: blocks.rewards.coinbase,
|
||||
key: "base",
|
||||
name: "sum",
|
||||
}),
|
||||
...satsBtcUsdFrom({
|
||||
source: blocks.rewards.coinbase,
|
||||
key: "sum",
|
||||
name: "sum",
|
||||
}),
|
||||
...satsBtcUsd({
|
||||
pattern: blocks.rewards._24hCoinbaseSum,
|
||||
name: "24h",
|
||||
color: colors.time._24h,
|
||||
defaultActive: false,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Distribution",
|
||||
title: "Coinbase Rewards Distribution",
|
||||
bottom: distributionBtcSatsUsd(blocks.rewards.coinbase),
|
||||
},
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: "Coinbase Rewards (Total)",
|
||||
bottom: satsBtcUsdFrom({
|
||||
source: blocks.rewards.coinbase,
|
||||
key: "cumulative",
|
||||
name: "all-time",
|
||||
}),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Subsidy",
|
||||
title: "Block Subsidy",
|
||||
bottom: [
|
||||
...fromCoinbasePattern({ pattern: blocks.rewards.subsidy }),
|
||||
line({
|
||||
metric: blocks.rewards.subsidyDominance,
|
||||
name: "Dominance",
|
||||
color: colors.purple,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
line({
|
||||
metric: blocks.rewards.subsidyUsd1ySma,
|
||||
name: "1y SMA",
|
||||
color: colors.lime,
|
||||
unit: Unit.usd,
|
||||
defaultActive: false,
|
||||
}),
|
||||
tree: [
|
||||
{
|
||||
name: "Sum",
|
||||
title: "Block Subsidy",
|
||||
bottom: [
|
||||
...satsBtcUsdFromFull({
|
||||
source: blocks.rewards.subsidy,
|
||||
key: "base",
|
||||
name: "sum",
|
||||
}),
|
||||
...satsBtcUsdFrom({
|
||||
source: blocks.rewards.subsidy,
|
||||
key: "sum",
|
||||
name: "sum",
|
||||
}),
|
||||
line({
|
||||
metric: blocks.rewards.subsidyUsd1ySma,
|
||||
name: "1y SMA",
|
||||
color: colors.time._1y,
|
||||
unit: Unit.usd,
|
||||
defaultActive: false,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Distribution",
|
||||
title: "Block Subsidy Distribution",
|
||||
bottom: distributionBtcSatsUsd(blocks.rewards.subsidy),
|
||||
},
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: "Block Subsidy (Total)",
|
||||
bottom: satsBtcUsdFrom({
|
||||
source: blocks.rewards.subsidy,
|
||||
key: "cumulative",
|
||||
name: "all-time",
|
||||
}),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Fees",
|
||||
title: "Transaction Fee Revenue",
|
||||
tree: [
|
||||
{
|
||||
name: "Sum",
|
||||
title: "Transaction Fee Revenue",
|
||||
bottom: satsBtcUsdFrom({
|
||||
source: transactions.fees.fee,
|
||||
key: "sum",
|
||||
name: "sum",
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "Distribution",
|
||||
title: "Transaction Fee Revenue Distribution",
|
||||
bottom: distributionBtcSatsUsd(transactions.fees.fee),
|
||||
},
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: "Transaction Fee Revenue (Total)",
|
||||
bottom: satsBtcUsdFrom({
|
||||
source: transactions.fees.fee,
|
||||
key: "cumulative",
|
||||
name: "all-time",
|
||||
}),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Dominance",
|
||||
title: "Revenue Dominance",
|
||||
bottom: [
|
||||
...fromSumStatsPattern({
|
||||
pattern: transactions.fees.fee.bitcoin,
|
||||
unit: Unit.btc,
|
||||
cumulativeUnit: Unit.btcCumulative,
|
||||
}),
|
||||
...fromSumStatsPattern({
|
||||
pattern: transactions.fees.fee.sats,
|
||||
unit: Unit.sats,
|
||||
cumulativeUnit: Unit.satsCumulative,
|
||||
}),
|
||||
...fromSumStatsPattern({
|
||||
pattern: transactions.fees.fee.dollars,
|
||||
unit: Unit.usd,
|
||||
cumulativeUnit: Unit.usdCumulative,
|
||||
line({
|
||||
metric: blocks.rewards.subsidyDominance,
|
||||
name: "Subsidy",
|
||||
color: colors.lime,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
line({
|
||||
metric: blocks.rewards.feeDominance,
|
||||
name: "Dominance",
|
||||
color: colors.purple,
|
||||
name: "Fees",
|
||||
color: colors.cyan,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Unclaimed",
|
||||
title: "Unclaimed Rewards",
|
||||
bottom: fromValuePattern({
|
||||
pattern: blocks.rewards.unclaimedRewards,
|
||||
}),
|
||||
tree: [
|
||||
{
|
||||
name: "Sum",
|
||||
title: "Unclaimed Rewards",
|
||||
bottom: satsBtcUsdFrom({
|
||||
source: blocks.rewards.unclaimedRewards,
|
||||
key: "sum",
|
||||
name: "sum",
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: "Unclaimed Rewards (Total)",
|
||||
bottom: satsBtcUsdFrom({
|
||||
source: blocks.rewards.unclaimedRewards,
|
||||
key: "cumulative",
|
||||
name: "all-time",
|
||||
}),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -383,12 +518,6 @@ export function createMiningSection(ctx) {
|
||||
color: colors.emerald,
|
||||
unit: Unit.usdPerPhsPerDay,
|
||||
}),
|
||||
line({
|
||||
metric: blocks.mining.hashPriceRebound,
|
||||
name: "Rebound",
|
||||
color: colors.yellow,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
dotted({
|
||||
metric: blocks.mining.hashPriceThsMin,
|
||||
name: "TH/s Min",
|
||||
@@ -419,12 +548,6 @@ export function createMiningSection(ctx) {
|
||||
color: colors.orange,
|
||||
unit: Unit.satsPerPhsPerDay,
|
||||
}),
|
||||
line({
|
||||
metric: blocks.mining.hashValueRebound,
|
||||
name: "Rebound",
|
||||
color: colors.yellow,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
dotted({
|
||||
metric: blocks.mining.hashValueThsMin,
|
||||
name: "TH/s Min",
|
||||
@@ -439,6 +562,24 @@ export function createMiningSection(ctx) {
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Recovery",
|
||||
title: "Recovery",
|
||||
bottom: [
|
||||
line({
|
||||
metric: blocks.mining.hashPriceRebound,
|
||||
name: "Hash Price",
|
||||
color: colors.emerald,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
line({
|
||||
metric: blocks.mining.hashValueRebound,
|
||||
name: "Hash Value",
|
||||
color: colors.orange,
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -458,7 +599,6 @@ export function createMiningSection(ctx) {
|
||||
line({
|
||||
metric: blocks.halving.daysBeforeNextHalving,
|
||||
name: "Remaining",
|
||||
color: colors.blue,
|
||||
unit: Unit.days,
|
||||
}),
|
||||
],
|
||||
@@ -487,41 +627,39 @@ export function createMiningSection(ctx) {
|
||||
tree: [
|
||||
{
|
||||
name: "Dominance",
|
||||
title: "Dominance: Major Pools",
|
||||
bottom: poolEntries
|
||||
.filter(([key]) => MAJOR_POOL_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()
|
||||
)
|
||||
] || key;
|
||||
return line({
|
||||
metric: pool._1mDominance,
|
||||
name: poolName,
|
||||
unit: Unit.percentage,
|
||||
});
|
||||
title: "Dominance: Major Pools (1m)",
|
||||
bottom: majorPools.map((p, i) =>
|
||||
line({
|
||||
metric: p.pool._1mDominance,
|
||||
name: p.name,
|
||||
color: colorAt(i),
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "Blocks Mined",
|
||||
title: "Blocks Mined: Major Pools (1m)",
|
||||
bottom: poolEntries
|
||||
.filter(([key]) => MAJOR_POOL_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()
|
||||
)
|
||||
] || key;
|
||||
return line({
|
||||
metric: pool._1mBlocksMined,
|
||||
name: poolName,
|
||||
unit: Unit.count,
|
||||
});
|
||||
bottom: majorPools.map((p, i) =>
|
||||
line({
|
||||
metric: p.pool._1mBlocksMined,
|
||||
name: p.name,
|
||||
color: colorAt(i),
|
||||
unit: Unit.count,
|
||||
}),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "Total Rewards",
|
||||
title: "Total Rewards: Major Pools",
|
||||
bottom: majorPools.flatMap((p, i) =>
|
||||
satsBtcUsdFrom({
|
||||
source: p.pool.coinbase,
|
||||
key: "sum",
|
||||
name: p.name,
|
||||
color: colorAt(i),
|
||||
}),
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -531,51 +669,45 @@ export function createMiningSection(ctx) {
|
||||
tree: [
|
||||
{
|
||||
name: "Dominance",
|
||||
title: "Dominance: AntPool & Friends",
|
||||
bottom: poolEntries
|
||||
.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()
|
||||
)
|
||||
] || key;
|
||||
return line({
|
||||
metric: pool._1mDominance,
|
||||
name: poolName,
|
||||
unit: Unit.percentage,
|
||||
});
|
||||
title: "Dominance: AntPool & Friends (1m)",
|
||||
bottom: antpoolFriends.map((p, i) =>
|
||||
line({
|
||||
metric: p.pool._1mDominance,
|
||||
name: p.name,
|
||||
color: colorAt(i),
|
||||
unit: Unit.percentage,
|
||||
}),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "Blocks Mined",
|
||||
title: "Blocks Mined: AntPool & Friends (1m)",
|
||||
bottom: poolEntries
|
||||
.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()
|
||||
)
|
||||
] || key;
|
||||
return line({
|
||||
metric: pool._1mBlocksMined,
|
||||
name: poolName,
|
||||
unit: Unit.count,
|
||||
});
|
||||
bottom: antpoolFriends.map((p, i) =>
|
||||
line({
|
||||
metric: p.pool._1mBlocksMined,
|
||||
name: p.name,
|
||||
color: colorAt(i),
|
||||
unit: Unit.count,
|
||||
}),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "Total Rewards",
|
||||
title: "Total Rewards: AntPool & Friends",
|
||||
bottom: antpoolFriends.flatMap((p, i) =>
|
||||
satsBtcUsdFrom({
|
||||
source: p.pool.coinbase,
|
||||
key: "sum",
|
||||
name: p.name,
|
||||
color: colorAt(i),
|
||||
}),
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
// Individual pools
|
||||
// All pools
|
||||
{
|
||||
name: "Individual",
|
||||
name: "All Pools",
|
||||
tree: poolsTree,
|
||||
},
|
||||
],
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -50,62 +50,18 @@ export function price({
|
||||
* @param {StatsPattern<any> | BaseStatsPattern<any> | FullStatsPattern<any> | AnyStatsPattern} pattern
|
||||
* @param {Unit} unit
|
||||
* @param {string} title
|
||||
* @param {{ type?: "Dots" }} [options]
|
||||
* @returns {AnyFetchedSeriesBlueprint[]}
|
||||
*/
|
||||
function percentileSeries(colors, pattern, unit, title, { type } = {}) {
|
||||
function percentileSeries(colors, pattern, unit, title) {
|
||||
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,
|
||||
},
|
||||
dots({ metric: pattern.max, name: `${title} max`.trim(), color: stat.max, unit, defaultActive: false }),
|
||||
dots({ metric: pattern.min, name: `${title} min`.trim(), color: stat.min, unit, defaultActive: false }),
|
||||
dots({ metric: pattern.median, name: `${title} median`.trim(), color: stat.median, unit, defaultActive: false }),
|
||||
dots({ metric: pattern.pct75, name: `${title} pct75`.trim(), color: stat.pct75, unit, defaultActive: false }),
|
||||
dots({ metric: pattern.pct25, name: `${title} pct25`.trim(), color: stat.pct25, unit, defaultActive: false }),
|
||||
dots({ metric: pattern.pct90, name: `${title} pct90`.trim(), color: stat.pct90, unit, defaultActive: false }),
|
||||
dots({ metric: pattern.pct10, name: `${title} pct10`.trim(), color: stat.pct10, unit, defaultActive: false }),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -305,36 +261,6 @@ export function histogram({
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create series from patterns with sum + cumulative + percentiles (NO base)
|
||||
* @param {Colors} colors
|
||||
* @param {Object} args
|
||||
* @param {AnyStatsPattern} args.pattern
|
||||
* @param {Unit} args.unit
|
||||
* @param {Unit} args.cumulativeUnit
|
||||
* @param {string} [args.title]
|
||||
* @returns {AnyFetchedSeriesBlueprint[]}
|
||||
*/
|
||||
export function fromSumStatsPattern(colors, { pattern, unit, cumulativeUnit, title = "" }) {
|
||||
const { stat } = colors;
|
||||
return [
|
||||
{ metric: pattern.average, title: `${title} avg`.trim(), unit },
|
||||
{
|
||||
metric: pattern.sum,
|
||||
title: title || "sum",
|
||||
color: stat.sum,
|
||||
unit,
|
||||
defaultActive: false,
|
||||
},
|
||||
{
|
||||
metric: pattern.cumulative,
|
||||
title: title || "cumulative",
|
||||
unit: cumulativeUnit,
|
||||
},
|
||||
...percentileSeries(colors, pattern, unit, title),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create series from a BaseStatsPattern (base + avg + percentiles, NO sum)
|
||||
* @param {Colors} colors
|
||||
@@ -352,59 +278,23 @@ export function fromBaseStatsPattern(
|
||||
) {
|
||||
const { stat } = colors;
|
||||
return [
|
||||
{ metric: pattern.base, title: title || "base", color: baseColor, unit },
|
||||
{
|
||||
dots({ metric: pattern.base, name: title || "base", color: baseColor, unit }),
|
||||
dots({
|
||||
metric: pattern.average,
|
||||
title: `${title} avg`.trim(),
|
||||
name: `${title} avg`.trim(),
|
||||
color: stat.avg,
|
||||
unit,
|
||||
defaultActive: avgActive,
|
||||
},
|
||||
}),
|
||||
...percentileSeries(colors, pattern, unit, title),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create series from a FullStatsPattern (base + sum + cumulative + avg + percentiles)
|
||||
* Create series from any pattern with avg + percentiles (works with StatsPattern, SumStatsPattern, etc.)
|
||||
* @param {Colors} colors
|
||||
* @param {Object} args
|
||||
* @param {FullStatsPattern<any>} args.pattern
|
||||
* @param {Unit} args.unit
|
||||
* @param {Unit} args.cumulativeUnit
|
||||
* @param {string} [args.title]
|
||||
* @returns {AnyFetchedSeriesBlueprint[]}
|
||||
*/
|
||||
export function fromFullStatsPattern(colors, { pattern, unit, cumulativeUnit, title = "" }) {
|
||||
const { stat } = colors;
|
||||
return [
|
||||
{ metric: pattern.base, title: title || "base", unit },
|
||||
{
|
||||
metric: pattern.sum,
|
||||
title: title || "sum",
|
||||
color: stat.sum,
|
||||
unit,
|
||||
},
|
||||
{
|
||||
metric: pattern.cumulative,
|
||||
title: title || "cumulative",
|
||||
unit: cumulativeUnit,
|
||||
},
|
||||
{
|
||||
metric: pattern.average,
|
||||
title: `${title} avg`.trim(),
|
||||
color: stat.avg,
|
||||
unit,
|
||||
defaultActive: false,
|
||||
},
|
||||
...percentileSeries(colors, pattern, unit, title),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create series from a StatsPattern (avg + percentiles, NO base)
|
||||
* @param {Colors} colors
|
||||
* @param {Object} args
|
||||
* @param {StatsPattern<any>} args.pattern
|
||||
* @param {StatsPattern<any> | BaseStatsPattern<any> | FullStatsPattern<any> | AnyStatsPattern} args.pattern
|
||||
* @param {Unit} args.unit
|
||||
* @param {string} [args.title]
|
||||
* @returns {AnyFetchedSeriesBlueprint[]}
|
||||
@@ -417,178 +307,7 @@ export function fromStatsPattern(colors, { pattern, unit, title = "" }) {
|
||||
title: `${title} avg`.trim(),
|
||||
unit,
|
||||
},
|
||||
...percentileSeries(colors, pattern, unit, title, { type: "Dots" }),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create series from AnyFullStatsPattern (base + sum + cumulative + avg + percentiles)
|
||||
* @param {Colors} colors
|
||||
* @param {Object} args
|
||||
* @param {AnyFullStatsPattern} args.pattern
|
||||
* @param {Unit} args.unit
|
||||
* @param {Unit} args.cumulativeUnit
|
||||
* @param {string} [args.title]
|
||||
* @returns {AnyFetchedSeriesBlueprint[]}
|
||||
*/
|
||||
export function fromAnyFullStatsPattern(colors, { pattern, unit, cumulativeUnit, title = "" }) {
|
||||
const { stat } = colors;
|
||||
return [
|
||||
...fromBaseStatsPattern(colors, { pattern, unit, title }),
|
||||
{
|
||||
metric: pattern.sum,
|
||||
title: title || "sum",
|
||||
color: stat.sum,
|
||||
unit,
|
||||
},
|
||||
{
|
||||
metric: pattern.cumulative,
|
||||
title: title || "cumulative",
|
||||
unit: cumulativeUnit,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create series from a CoinbasePattern ({ sats, bitcoin, dollars } each with stats + sum + cumulative)
|
||||
* @param {Colors} colors
|
||||
* @param {Object} args
|
||||
* @param {CoinbasePattern} args.pattern
|
||||
* @param {string} [args.title]
|
||||
* @returns {AnyFetchedSeriesBlueprint[]}
|
||||
*/
|
||||
export function fromCoinbasePattern(colors, { pattern, title = "" }) {
|
||||
return [
|
||||
...fromAnyFullStatsPattern(colors, {
|
||||
pattern: pattern.bitcoin,
|
||||
unit: Unit.btc,
|
||||
cumulativeUnit: Unit.btcCumulative,
|
||||
title,
|
||||
}),
|
||||
...fromAnyFullStatsPattern(colors, {
|
||||
pattern: pattern.sats,
|
||||
unit: Unit.sats,
|
||||
cumulativeUnit: Unit.satsCumulative,
|
||||
title,
|
||||
}),
|
||||
...fromAnyFullStatsPattern(colors, {
|
||||
pattern: pattern.dollars,
|
||||
unit: Unit.usd,
|
||||
cumulativeUnit: Unit.usdCumulative,
|
||||
title,
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create series from a ValuePattern ({ sats, bitcoin, dollars } each as CountPattern with sum + cumulative)
|
||||
* @param {Object} args
|
||||
* @param {ValuePattern} args.pattern
|
||||
* @param {string} [args.title]
|
||||
* @param {Color} [args.color]
|
||||
* @returns {AnyFetchedSeriesBlueprint[]}
|
||||
*/
|
||||
export function fromValuePattern({ pattern, title = "", color }) {
|
||||
return [
|
||||
{
|
||||
metric: pattern.bitcoin.sum,
|
||||
title: title || "sum",
|
||||
color,
|
||||
unit: Unit.btc,
|
||||
},
|
||||
{
|
||||
metric: pattern.bitcoin.cumulative,
|
||||
title: title || "cumulative",
|
||||
color,
|
||||
unit: Unit.btcCumulative,
|
||||
},
|
||||
{
|
||||
metric: pattern.sats.sum,
|
||||
title: title || "sum",
|
||||
color,
|
||||
unit: Unit.sats,
|
||||
},
|
||||
{
|
||||
metric: pattern.sats.cumulative,
|
||||
title: title || "cumulative",
|
||||
color,
|
||||
unit: Unit.satsCumulative,
|
||||
},
|
||||
{
|
||||
metric: pattern.dollars.sum,
|
||||
title: title || "sum",
|
||||
color,
|
||||
unit: Unit.usd,
|
||||
},
|
||||
{
|
||||
metric: pattern.dollars.cumulative,
|
||||
title: title || "cumulative",
|
||||
color,
|
||||
unit: Unit.usdCumulative,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create sum/cumulative series from a BitcoinPattern ({ sum, cumulative }) with explicit unit and colors
|
||||
* @param {Object} args
|
||||
* @param {{ sum: AnyMetricPattern, cumulative: AnyMetricPattern }} args.pattern
|
||||
* @param {Unit} args.unit
|
||||
* @param {Unit} args.cumulativeUnit
|
||||
* @param {string} [args.title]
|
||||
* @param {Color} [args.color]
|
||||
* @param {boolean} [args.defaultActive]
|
||||
* @returns {AnyFetchedSeriesBlueprint[]}
|
||||
*/
|
||||
export function fromBitcoinPatternWithUnit({
|
||||
pattern,
|
||||
unit,
|
||||
cumulativeUnit,
|
||||
title = "",
|
||||
color,
|
||||
defaultActive,
|
||||
}) {
|
||||
return [
|
||||
{
|
||||
metric: pattern.sum,
|
||||
title: title || "sum",
|
||||
color,
|
||||
unit,
|
||||
defaultActive,
|
||||
},
|
||||
{
|
||||
metric: pattern.cumulative,
|
||||
title: title || "cumulative",
|
||||
color,
|
||||
unit: cumulativeUnit,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create sum/cumulative series from a CountPattern with explicit unit and colors
|
||||
* @param {Object} args
|
||||
* @param {CountPattern<any>} args.pattern
|
||||
* @param {Unit} args.unit
|
||||
* @param {Unit} args.cumulativeUnit
|
||||
* @param {string} [args.title]
|
||||
* @param {Color} [args.color]
|
||||
* @returns {AnyFetchedSeriesBlueprint[]}
|
||||
*/
|
||||
export function fromCountPattern({ pattern, unit, cumulativeUnit, title = "", color }) {
|
||||
return [
|
||||
{
|
||||
metric: pattern.sum,
|
||||
title: title || "sum",
|
||||
color,
|
||||
unit,
|
||||
},
|
||||
{
|
||||
metric: pattern.cumulative,
|
||||
title: title || "cumulative",
|
||||
color,
|
||||
unit: cumulativeUnit,
|
||||
},
|
||||
...percentileSeries(colors, pattern, unit, title),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -622,3 +341,207 @@ export function fromSupplyPattern({ pattern, title, color }) {
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Chart-generating helpers (return PartialOptionsTree for folder structures)
|
||||
// ============================================================================
|
||||
// These split patterns into separate Sum/Distribution/Cumulative charts
|
||||
|
||||
/**
|
||||
* Create distribution series (avg + percentiles)
|
||||
* @param {Colors} colors
|
||||
* @param {StatsPattern<any> | BaseStatsPattern<any> | FullStatsPattern<any> | AnyStatsPattern} pattern
|
||||
* @param {Unit} unit
|
||||
* @returns {AnyFetchedSeriesBlueprint[]}
|
||||
*/
|
||||
function distributionSeries(colors, pattern, unit) {
|
||||
const { stat } = colors;
|
||||
return [
|
||||
dots({ metric: pattern.average, name: "avg", color: stat.avg, unit }),
|
||||
dots({ metric: pattern.median, name: "median", color: stat.median, unit, defaultActive: false }),
|
||||
dots({ metric: pattern.max, name: "max", color: stat.max, unit, defaultActive: false }),
|
||||
dots({ metric: pattern.min, name: "min", color: stat.min, unit, defaultActive: false }),
|
||||
dots({ metric: pattern.pct75, name: "pct75", color: stat.pct75, unit, defaultActive: false }),
|
||||
dots({ metric: pattern.pct25, name: "pct25", color: stat.pct25, unit, defaultActive: false }),
|
||||
dots({ metric: pattern.pct90, name: "pct90", color: stat.pct90, unit, defaultActive: false }),
|
||||
dots({ metric: pattern.pct10, name: "pct10", color: stat.pct10, unit, defaultActive: false }),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create btc/sats/usd series from metrics
|
||||
* @param {Object} args
|
||||
* @param {{ bitcoin: AnyMetricPattern, sats: AnyMetricPattern, dollars: AnyMetricPattern }} args.metrics
|
||||
* @param {string} args.name
|
||||
* @param {Color} [args.color]
|
||||
* @param {boolean} [args.defaultActive]
|
||||
* @returns {AnyFetchedSeriesBlueprint[]}
|
||||
*/
|
||||
function btcSatsUsdSeries({ metrics, name, color, defaultActive }) {
|
||||
return [
|
||||
{ metric: metrics.bitcoin, title: name, color, unit: Unit.btc, defaultActive },
|
||||
{ metric: metrics.sats, title: name, color, unit: Unit.sats, defaultActive },
|
||||
{ metric: metrics.dollars, title: name, color, unit: Unit.usd, defaultActive },
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Split pattern with base + sum + distribution + cumulative into 3 charts
|
||||
* @param {Colors} colors
|
||||
* @param {Object} args
|
||||
* @param {FullStatsPattern<any>} args.pattern
|
||||
* @param {string} args.title
|
||||
* @param {Unit} args.unit
|
||||
* @returns {PartialOptionsTree}
|
||||
*/
|
||||
export function chartsFromFull(colors, { pattern, title, unit }) {
|
||||
return [
|
||||
{
|
||||
name: "Sum",
|
||||
title,
|
||||
bottom: [
|
||||
{ metric: pattern.base, title: "sum", unit },
|
||||
{ metric: pattern.sum, title: "sum", unit },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Distribution",
|
||||
title: `${title} Distribution`,
|
||||
bottom: distributionSeries(colors, pattern, unit),
|
||||
},
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: `${title} (Total)`,
|
||||
bottom: [{ metric: pattern.cumulative, title: "all-time", unit }],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Split pattern with sum + distribution + cumulative into 3 charts (no base)
|
||||
* @param {Colors} colors
|
||||
* @param {Object} args
|
||||
* @param {AnyStatsPattern} args.pattern
|
||||
* @param {string} args.title
|
||||
* @param {Unit} args.unit
|
||||
* @returns {PartialOptionsTree}
|
||||
*/
|
||||
export function chartsFromSum(colors, { pattern, title, unit }) {
|
||||
const { stat } = colors;
|
||||
return [
|
||||
{
|
||||
name: "Sum",
|
||||
title,
|
||||
bottom: [{ metric: pattern.sum, title: "sum", color: stat.sum, unit }],
|
||||
},
|
||||
{
|
||||
name: "Distribution",
|
||||
title: `${title} Distribution`,
|
||||
bottom: distributionSeries(colors, pattern, unit),
|
||||
},
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: `${title} (Total)`,
|
||||
bottom: [{ metric: pattern.cumulative, title: "all-time", unit }],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Split pattern with sum + cumulative into 2 charts
|
||||
* @param {Object} args
|
||||
* @param {CountPattern<any>} args.pattern
|
||||
* @param {string} args.title
|
||||
* @param {Unit} args.unit
|
||||
* @param {Color} [args.color]
|
||||
* @returns {PartialOptionsTree}
|
||||
*/
|
||||
export function chartsFromCount({ pattern, title, unit, color }) {
|
||||
return [
|
||||
{
|
||||
name: "Sum",
|
||||
title,
|
||||
bottom: [{ metric: pattern.sum, title: "sum", color, unit }],
|
||||
},
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: `${title} (Total)`,
|
||||
bottom: [{ metric: pattern.cumulative, title: "all-time", color, unit }],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Split value pattern (btc/sats/usd with sum + cumulative) into 2 charts
|
||||
* @param {Object} args
|
||||
* @param {ValuePattern} args.pattern
|
||||
* @param {string} args.title
|
||||
* @param {Color} [args.color]
|
||||
* @returns {PartialOptionsTree}
|
||||
*/
|
||||
export function chartsFromValue({ pattern, title, color }) {
|
||||
return [
|
||||
{
|
||||
name: "Sum",
|
||||
title,
|
||||
bottom: btcSatsUsdSeries({
|
||||
metrics: { bitcoin: pattern.bitcoin.sum, sats: pattern.sats.sum, dollars: pattern.dollars.sum },
|
||||
name: "sum",
|
||||
color,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: `${title} (Total)`,
|
||||
bottom: btcSatsUsdSeries({
|
||||
metrics: { bitcoin: pattern.bitcoin.cumulative, sats: pattern.sats.cumulative, dollars: pattern.dollars.cumulative },
|
||||
name: "all-time",
|
||||
color,
|
||||
}),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Split btc/sats/usd pattern with full stats into 3 charts
|
||||
* @param {Colors} colors
|
||||
* @param {Object} args
|
||||
* @param {CoinbasePattern} args.pattern
|
||||
* @param {string} args.title
|
||||
* @returns {PartialOptionsTree}
|
||||
*/
|
||||
export function chartsFromValueFull(colors, { pattern, title }) {
|
||||
return [
|
||||
{
|
||||
name: "Sum",
|
||||
title,
|
||||
bottom: [
|
||||
...btcSatsUsdSeries({
|
||||
metrics: { bitcoin: pattern.bitcoin.base, sats: pattern.sats.base, dollars: pattern.dollars.base },
|
||||
name: "sum",
|
||||
}),
|
||||
...btcSatsUsdSeries({
|
||||
metrics: { bitcoin: pattern.bitcoin.sum, sats: pattern.sats.sum, dollars: pattern.dollars.sum },
|
||||
name: "sum",
|
||||
}),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Distribution",
|
||||
title: `${title} Distribution`,
|
||||
bottom: [
|
||||
...distributionSeries(colors, pattern.bitcoin, Unit.btc),
|
||||
...distributionSeries(colors, pattern.sats, Unit.sats),
|
||||
...distributionSeries(colors, pattern.dollars, Unit.usd),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: `${title} (Total)`,
|
||||
bottom: btcSatsUsdSeries({
|
||||
metrics: { bitcoin: pattern.bitcoin.cumulative, sats: pattern.sats.cumulative, dollars: pattern.dollars.cumulative },
|
||||
name: "all-time",
|
||||
}),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@@ -23,21 +23,65 @@ export const formatCohortTitle = (cohortTitle) =>
|
||||
*/
|
||||
export function satsBtcUsd({ pattern, name, color, defaultActive }) {
|
||||
return [
|
||||
line({
|
||||
metric: pattern.bitcoin,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.btc,
|
||||
defaultActive,
|
||||
}),
|
||||
line({ metric: pattern.bitcoin, name, color, unit: Unit.btc, defaultActive }),
|
||||
line({ metric: pattern.sats, name, color, unit: Unit.sats, defaultActive }),
|
||||
line({
|
||||
metric: pattern.dollars,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.usd,
|
||||
defaultActive,
|
||||
}),
|
||||
line({ metric: pattern.dollars, name, color, unit: Unit.usd, defaultActive }),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create sats/btc/usd series from any value pattern using sum or cumulative key
|
||||
* @param {Object} args
|
||||
* @param {AnyValuePatternType} args.source
|
||||
* @param {'sum' | 'cumulative'} args.key
|
||||
* @param {string} args.name
|
||||
* @param {Color} [args.color]
|
||||
* @param {boolean} [args.defaultActive]
|
||||
* @returns {FetchedLineSeriesBlueprint[]}
|
||||
*/
|
||||
export function satsBtcUsdFrom({ source, key, name, color, defaultActive }) {
|
||||
return satsBtcUsd({
|
||||
pattern: { bitcoin: source.bitcoin[key], sats: source.sats[key], dollars: source.dollars[key] },
|
||||
name,
|
||||
color,
|
||||
defaultActive,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create sats/btc/usd series from a full value pattern using base or average key
|
||||
* @param {Object} args
|
||||
* @param {FullValuePattern} args.source
|
||||
* @param {'base' | 'average'} args.key
|
||||
* @param {string} args.name
|
||||
* @param {Color} [args.color]
|
||||
* @param {boolean} [args.defaultActive]
|
||||
* @returns {FetchedLineSeriesBlueprint[]}
|
||||
*/
|
||||
export function satsBtcUsdFromFull({ source, key, name, color, defaultActive }) {
|
||||
return satsBtcUsd({
|
||||
pattern: { bitcoin: source.bitcoin[key], sats: source.sats[key], dollars: source.dollars[key] },
|
||||
name,
|
||||
color,
|
||||
defaultActive,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create coinbase/subsidy/fee series from separate sources
|
||||
* @param {Colors} colors
|
||||
* @param {Object} args
|
||||
* @param {AnyValuePatternType} args.coinbase
|
||||
* @param {AnyValuePatternType} args.subsidy
|
||||
* @param {AnyValuePatternType} args.fee
|
||||
* @param {'sum' | 'cumulative'} args.key
|
||||
* @returns {FetchedLineSeriesBlueprint[]}
|
||||
*/
|
||||
export function revenueBtcSatsUsd(colors, { coinbase, subsidy, fee, key }) {
|
||||
return [
|
||||
...satsBtcUsdFrom({ source: coinbase, key, name: "Coinbase", color: colors.orange }),
|
||||
...satsBtcUsdFrom({ source: subsidy, key, name: "Subsidy", color: colors.lime }),
|
||||
...satsBtcUsdFrom({ source: fee, key, name: "Fees", color: colors.cyan }),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user