mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-04-24 14:49:58 -07:00
global: snapshot part 10
This commit is contained in:
@@ -31,21 +31,44 @@ import { colors } from "../../utils/colors.js";
|
||||
// ============================================================================
|
||||
|
||||
/**
|
||||
* @param {{ transferVolume: TransferVolumePattern, coindaysDestroyed: CountPattern<number> }} activity
|
||||
* Volume folder with optional profitability (in profit + in loss per window)
|
||||
* @param {{ transferVolume: TransferVolumePattern }} activity
|
||||
* @param {Color} color
|
||||
* @param {(name: string) => string} title
|
||||
* @param {boolean} [withProfitability]
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
function volumeFolder(activity, color, title, withProfitability) {
|
||||
const tv = activity.transferVolume;
|
||||
return {
|
||||
name: "Volume",
|
||||
tree: [
|
||||
...satsBtcUsdFullTree({ pattern: tv, title: title("Sent Volume"), color }),
|
||||
...(withProfitability ? [{
|
||||
name: "Profitability",
|
||||
tree: ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: title(`Sent Volume Profitability (${w.title})`),
|
||||
bottom: [
|
||||
...satsBtcUsd({ pattern: tv.inProfit.sum[w.key], name: "In Profit", color: colors.profit }),
|
||||
...satsBtcUsd({ pattern: tv.inLoss.sum[w.key], name: "In Loss", color: colors.loss }),
|
||||
],
|
||||
})),
|
||||
}] : []),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Full activity items: volume (with profitability), coindays, dormancy
|
||||
* @param {FullActivityPattern} activity
|
||||
* @param {Color} color
|
||||
* @param {(name: string) => string} title
|
||||
* @returns {PartialOptionsTree}
|
||||
*/
|
||||
function volumeAndCoinsTree(activity, color, title) {
|
||||
function fullVolumeTree(activity, color, title) {
|
||||
return [
|
||||
{
|
||||
name: "Volume",
|
||||
tree: satsBtcUsdFullTree({
|
||||
pattern: activity.transferVolume,
|
||||
title: title("Sent Volume"),
|
||||
color,
|
||||
}),
|
||||
},
|
||||
volumeFolder(activity, color, title, true),
|
||||
{
|
||||
name: "Coindays Destroyed",
|
||||
tree: chartsFromCount({
|
||||
@@ -55,47 +78,6 @@ function volumeAndCoinsTree(activity, color, title) {
|
||||
color,
|
||||
}),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sent in profit/loss breakdown tree (shared by full and mid-level activity)
|
||||
* @param {TransferVolumePattern} sent
|
||||
* @param {(name: string) => string} title
|
||||
* @returns {PartialOptionsTree}
|
||||
*/
|
||||
function sentProfitLossTree(sent, title) {
|
||||
return [
|
||||
{
|
||||
name: "Sent In Profit",
|
||||
tree: satsBtcUsdFullTree({
|
||||
pattern: sent.inProfit,
|
||||
title: title("Sent Volume In Profit"),
|
||||
color: colors.profit,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "Sent In Loss",
|
||||
tree: satsBtcUsdFullTree({
|
||||
pattern: sent.inLoss,
|
||||
title: title("Sent Volume In Loss"),
|
||||
color: colors.loss,
|
||||
}),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Volume and coins tree with dormancy, and sent in profit/loss (All/STH/LTH)
|
||||
* @param {FullActivityPattern} activity
|
||||
* @param {Color} color
|
||||
* @param {(name: string) => string} title
|
||||
* @returns {PartialOptionsTree}
|
||||
*/
|
||||
function fullVolumeTree(activity, color, title) {
|
||||
return [
|
||||
...volumeAndCoinsTree(activity, color, title),
|
||||
...sentProfitLossTree(activity.transferVolume, title),
|
||||
{
|
||||
name: "Dormancy",
|
||||
tree: averagesArray({
|
||||
@@ -255,8 +237,16 @@ export function createActivitySectionWithActivity({ cohort, title }) {
|
||||
return {
|
||||
name: "Activity",
|
||||
tree: [
|
||||
...volumeAndCoinsTree(tree.activity, color, title),
|
||||
...sentProfitLossTree(tree.activity.transferVolume, title),
|
||||
volumeFolder(tree.activity, color, title, true),
|
||||
{
|
||||
name: "Coindays Destroyed",
|
||||
tree: chartsFromCount({
|
||||
pattern: tree.activity.coindaysDestroyed,
|
||||
title: title("Coindays Destroyed"),
|
||||
unit: Unit.coindays,
|
||||
color,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "SOPR",
|
||||
title: title("SOPR (24h)"),
|
||||
@@ -365,33 +355,79 @@ export function createGroupedActivitySectionWithAdjusted({ list, all, title }) {
|
||||
tree: [
|
||||
{
|
||||
name: "Volume",
|
||||
tree: ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: title(`Sent Volume (${w.title})`),
|
||||
bottom: flatMapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
satsBtcUsd({ pattern: tree.activity.transferVolume.sum[w.key], name, color }),
|
||||
),
|
||||
})),
|
||||
tree: [
|
||||
...ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: title(`Sent Volume (${w.title})`),
|
||||
bottom: flatMapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
satsBtcUsd({ pattern: tree.activity.transferVolume.sum[w.key], name, color }),
|
||||
),
|
||||
})),
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: title("Cumulative Sent Volume"),
|
||||
bottom: flatMapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
satsBtcUsd({ pattern: tree.activity.transferVolume.cumulative, name, color }),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "In Profit",
|
||||
tree: [
|
||||
...ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: title(`Sent In Profit (${w.title})`),
|
||||
bottom: flatMapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
satsBtcUsd({ pattern: tree.activity.transferVolume.inProfit.sum[w.key], name, color }),
|
||||
),
|
||||
})),
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: title("Cumulative Sent In Profit"),
|
||||
bottom: flatMapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
satsBtcUsd({ pattern: tree.activity.transferVolume.inProfit.cumulative, name, color }),
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "In Loss",
|
||||
tree: [
|
||||
...ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: title(`Sent In Loss (${w.title})`),
|
||||
bottom: flatMapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
satsBtcUsd({ pattern: tree.activity.transferVolume.inLoss.sum[w.key], name, color }),
|
||||
),
|
||||
})),
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: title("Cumulative Sent In Loss"),
|
||||
bottom: flatMapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
satsBtcUsd({ pattern: tree.activity.transferVolume.inLoss.cumulative, name, color }),
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Sent In Profit",
|
||||
tree: ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: title(`Sent In Profit (${w.title})`),
|
||||
bottom: flatMapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
satsBtcUsd({ pattern: tree.activity.transferVolume.inProfit.sum[w.key], name, color }),
|
||||
),
|
||||
})),
|
||||
},
|
||||
{
|
||||
name: "Sent In Loss",
|
||||
tree: ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: title(`Sent In Loss (${w.title})`),
|
||||
bottom: flatMapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
satsBtcUsd({ pattern: tree.activity.transferVolume.inLoss.sum[w.key], name, color }),
|
||||
),
|
||||
})),
|
||||
name: "Coindays Destroyed",
|
||||
tree: [
|
||||
...ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: title(`Coindays Destroyed (${w.title})`),
|
||||
bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
line({ series: tree.activity.coindaysDestroyed.sum[w.key], name, color, unit: Unit.coindays }),
|
||||
),
|
||||
})),
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: title("Cumulative Coindays Destroyed"),
|
||||
bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
line({ series: tree.activity.coindaysDestroyed.cumulative, name, color, unit: Unit.coindays }),
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Dormancy",
|
||||
@@ -419,22 +455,7 @@ export function createGroupedActivitySectionWithAdjusted({ list, all, title }) {
|
||||
name: w.name,
|
||||
title: title(`Sell Side Risk (${w.title})`),
|
||||
bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
line({
|
||||
series: tree.realized.sellSideRiskRatio[w.key].ratio,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.ratio,
|
||||
}),
|
||||
),
|
||||
})),
|
||||
},
|
||||
{
|
||||
name: "Coindays Destroyed",
|
||||
tree: ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: title(`Coindays Destroyed (${w.title})`),
|
||||
bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
line({ series: tree.activity.coindaysDestroyed.sum[w.key], name, color, unit: Unit.coindays }),
|
||||
line({ series: tree.realized.sellSideRiskRatio[w.key].ratio, name, color, unit: Unit.ratio }),
|
||||
),
|
||||
})),
|
||||
},
|
||||
@@ -453,33 +474,79 @@ export function createGroupedActivitySection({ list, all, title }) {
|
||||
tree: [
|
||||
{
|
||||
name: "Volume",
|
||||
tree: ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: title(`Sent Volume (${w.title})`),
|
||||
bottom: flatMapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
satsBtcUsd({ pattern: tree.activity.transferVolume.sum[w.key], name, color }),
|
||||
),
|
||||
})),
|
||||
tree: [
|
||||
...ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: title(`Sent Volume (${w.title})`),
|
||||
bottom: flatMapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
satsBtcUsd({ pattern: tree.activity.transferVolume.sum[w.key], name, color }),
|
||||
),
|
||||
})),
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: title("Cumulative Sent Volume"),
|
||||
bottom: flatMapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
satsBtcUsd({ pattern: tree.activity.transferVolume.cumulative, name, color }),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "In Profit",
|
||||
tree: [
|
||||
...ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: title(`Sent In Profit (${w.title})`),
|
||||
bottom: flatMapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
satsBtcUsd({ pattern: tree.activity.transferVolume.inProfit.sum[w.key], name, color }),
|
||||
),
|
||||
})),
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: title("Cumulative Sent In Profit"),
|
||||
bottom: flatMapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
satsBtcUsd({ pattern: tree.activity.transferVolume.inProfit.cumulative, name, color }),
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "In Loss",
|
||||
tree: [
|
||||
...ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: title(`Sent In Loss (${w.title})`),
|
||||
bottom: flatMapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
satsBtcUsd({ pattern: tree.activity.transferVolume.inLoss.sum[w.key], name, color }),
|
||||
),
|
||||
})),
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: title("Cumulative Sent In Loss"),
|
||||
bottom: flatMapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
satsBtcUsd({ pattern: tree.activity.transferVolume.inLoss.cumulative, name, color }),
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Sent In Profit",
|
||||
tree: ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: title(`Sent In Profit (${w.title})`),
|
||||
bottom: flatMapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
satsBtcUsd({ pattern: tree.activity.transferVolume.inProfit.sum[w.key], name, color }),
|
||||
),
|
||||
})),
|
||||
},
|
||||
{
|
||||
name: "Sent In Loss",
|
||||
tree: ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: title(`Sent In Loss (${w.title})`),
|
||||
bottom: flatMapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
satsBtcUsd({ pattern: tree.activity.transferVolume.inLoss.sum[w.key], name, color }),
|
||||
),
|
||||
})),
|
||||
name: "Coindays Destroyed",
|
||||
tree: [
|
||||
...ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: title(`Coindays Destroyed (${w.title})`),
|
||||
bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
line({ series: tree.activity.coindaysDestroyed.sum[w.key], name, color, unit: Unit.coindays }),
|
||||
),
|
||||
})),
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: title("Cumulative Coindays Destroyed"),
|
||||
bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
line({ series: tree.activity.coindaysDestroyed.cumulative, name, color, unit: Unit.coindays }),
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Dormancy",
|
||||
@@ -501,22 +568,7 @@ export function createGroupedActivitySection({ list, all, title }) {
|
||||
name: w.name,
|
||||
title: title(`Sell Side Risk (${w.title})`),
|
||||
bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
line({
|
||||
series: tree.realized.sellSideRiskRatio[w.key].ratio,
|
||||
name,
|
||||
color,
|
||||
unit: Unit.ratio,
|
||||
}),
|
||||
),
|
||||
})),
|
||||
},
|
||||
{
|
||||
name: "Coindays Destroyed",
|
||||
tree: ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: title(`Coindays Destroyed (${w.title})`),
|
||||
bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
line({ series: tree.activity.coindaysDestroyed.sum[w.key], name, color, unit: Unit.coindays }),
|
||||
line({ series: tree.realized.sellSideRiskRatio[w.key].ratio, name, color, unit: Unit.ratio }),
|
||||
),
|
||||
})),
|
||||
},
|
||||
@@ -558,13 +610,22 @@ export function createGroupedActivitySectionWithActivity({ list, all, title }) {
|
||||
},
|
||||
{
|
||||
name: "Coindays Destroyed",
|
||||
tree: ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: title(`Coindays Destroyed (${w.title})`),
|
||||
bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
line({ series: tree.activity.coindaysDestroyed.sum[w.key], name, color, unit: Unit.coindays }),
|
||||
),
|
||||
})),
|
||||
tree: [
|
||||
...ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: title(`Coindays Destroyed (${w.title})`),
|
||||
bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
line({ series: tree.activity.coindaysDestroyed.sum[w.key], name, color, unit: Unit.coindays }),
|
||||
),
|
||||
})),
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: title("Cumulative Coindays Destroyed"),
|
||||
bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
line({ series: tree.activity.coindaysDestroyed.cumulative, name, color, unit: Unit.coindays }),
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@@ -912,7 +912,7 @@ function groupedRealizedSubfolderFull(list, all, title) {
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Gross",
|
||||
name: "Gross P&L",
|
||||
tree: [
|
||||
...ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
|
||||
@@ -1,19 +1,11 @@
|
||||
/**
|
||||
* Capitalization section builders
|
||||
*
|
||||
* Structure:
|
||||
* - Total: Realized Cap (USD)
|
||||
* - Profitability: Invested Capital (Total + In Profit + In Loss) [full only]
|
||||
* - MVRV: Market Value to Realized Value ratio
|
||||
* - % of Own Market Cap [full only]
|
||||
* - Change: Rolling window absolute changes
|
||||
* - Growth Rate: Rolling window rate of change
|
||||
*/
|
||||
|
||||
import { Unit } from "../../utils/units.js";
|
||||
import { colors } from "../../utils/colors.js";
|
||||
import { ROLLING_WINDOWS, line, baseline, mapWindows, sumsTreeBaseline, rollingPercentRatioTree, percentRatio, percentRatioBaseline } from "../series.js";
|
||||
import { createRatioChart, mapCohortsWithAll, flatMapCohortsWithAll } from "../shared.js";
|
||||
import { ratioBottomSeries, mapCohortsWithAll, flatMapCohortsWithAll } from "../shared.js";
|
||||
|
||||
// ============================================================================
|
||||
// Shared building blocks
|
||||
@@ -41,6 +33,13 @@ function singleDeltaItems(tree, title) {
|
||||
*/
|
||||
function groupedDeltaAndMvrv(list, all, title) {
|
||||
return [
|
||||
{
|
||||
name: "MVRV",
|
||||
title: title("MVRV"),
|
||||
bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
baseline({ series: tree.realized.mvrv, name, color, unit: Unit.ratio, base: 1 }),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "Change",
|
||||
tree: ROLLING_WINDOWS.map((w) => ({
|
||||
@@ -61,13 +60,6 @@ function groupedDeltaAndMvrv(list, all, title) {
|
||||
),
|
||||
})),
|
||||
},
|
||||
{
|
||||
name: "MVRV",
|
||||
title: title("MVRV"),
|
||||
bottom: mapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
baseline({ series: tree.realized.mvrv, name, color, unit: Unit.ratio, base: 1 }),
|
||||
),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@@ -95,7 +87,7 @@ export function createValuationSectionFull({ cohort, title }) {
|
||||
line({ series: tree.unrealized.investedCapital.inLoss.usd, name: "In Loss", color: colors.loss, unit: Unit.usd }),
|
||||
],
|
||||
},
|
||||
{ name: "MVRV", title: title("MVRV"), bottom: [baseline({ series: tree.realized.mvrv, name: "MVRV", unit: Unit.ratio, base: 1 })] },
|
||||
{ name: "MVRV", title: title("MVRV"), bottom: ratioBottomSeries(tree.realized.price) },
|
||||
{ name: "% of Own Market Cap", title: title("Realized Cap (% of Own Market Cap)"), bottom: percentRatioBaseline({ pattern: tree.realized.cap.toOwnMcap, name: "Rel. to Own Market Cap", color }) },
|
||||
...singleDeltaItems(tree, title),
|
||||
],
|
||||
@@ -172,6 +164,7 @@ export function createGroupedValuationSectionWithOwnMarketCap({ list, all, title
|
||||
line({ series: tree.unrealized.investedCapital.inLoss.usd, name, color, unit: Unit.usd }),
|
||||
),
|
||||
},
|
||||
...groupedDeltaAndMvrv(list, all, title),
|
||||
{
|
||||
name: "% of Own Market Cap",
|
||||
title: title("Realized Cap (% of Own Market Cap)"),
|
||||
@@ -179,7 +172,6 @@ export function createGroupedValuationSectionWithOwnMarketCap({ list, all, title
|
||||
percentRatio({ pattern: tree.realized.cap.toOwnMcap, name, color }),
|
||||
),
|
||||
},
|
||||
...groupedDeltaAndMvrv(list, all, title),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user