mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-07-01 06:19:02 -07:00
global: snapshot part 16
This commit is contained in:
@@ -41,7 +41,7 @@ function volumeTree(tv, color, title) {
|
||||
return [
|
||||
...satsBtcUsdFullTree({
|
||||
pattern: tv,
|
||||
title: title("Sent Volume"),
|
||||
title: title("Transfer Volume"),
|
||||
color,
|
||||
}),
|
||||
{
|
||||
@@ -49,7 +49,7 @@ function volumeTree(tv, color, title) {
|
||||
tree: [
|
||||
...ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: title(`Sent Volume Profitability (${w.title})`),
|
||||
title: title(`Transfer Volume Profitability (${w.title})`),
|
||||
bottom: [
|
||||
...satsBtcUsd({
|
||||
pattern: tv.inProfit.sum[w.key],
|
||||
@@ -65,7 +65,7 @@ function volumeTree(tv, color, title) {
|
||||
})),
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: title("Cumulative Sent Volume Profitability"),
|
||||
title: title("Cumulative Transfer Volume"),
|
||||
bottom: [
|
||||
...satsBtcUsd({
|
||||
pattern: tv.inProfit.cumulative,
|
||||
@@ -83,7 +83,7 @@ function volumeTree(tv, color, title) {
|
||||
name: "In Profit",
|
||||
tree: satsBtcUsdFullTree({
|
||||
pattern: tv.inProfit,
|
||||
title: title("Sent In Profit"),
|
||||
title: title("Transfer Volume In Profit"),
|
||||
color: colors.profit,
|
||||
}),
|
||||
},
|
||||
@@ -91,7 +91,7 @@ function volumeTree(tv, color, title) {
|
||||
name: "In Loss",
|
||||
tree: satsBtcUsdFullTree({
|
||||
pattern: tv.inLoss,
|
||||
title: title("Sent In Loss"),
|
||||
title: title("Transfer Volume In Loss"),
|
||||
color: colors.loss,
|
||||
}),
|
||||
},
|
||||
@@ -360,7 +360,7 @@ export function createActivitySectionMinimal({ cohort, title }) {
|
||||
name: "Activity",
|
||||
tree: satsBtcUsdFullTree({
|
||||
pattern: cohort.tree.activity.transferVolume,
|
||||
title: title("Volume"),
|
||||
title: title("Transfer Volume"),
|
||||
}),
|
||||
};
|
||||
}
|
||||
@@ -399,7 +399,7 @@ function groupedProfitabilityArray(list, all, title, getInProfit, getInLoss) {
|
||||
list,
|
||||
all,
|
||||
title,
|
||||
metricTitle: "Sent In Profit",
|
||||
metricTitle: "Transfer Volume In Profit",
|
||||
getMetric: (c) => getInProfit(c),
|
||||
}),
|
||||
},
|
||||
@@ -409,7 +409,7 @@ function groupedProfitabilityArray(list, all, title, getInProfit, getInLoss) {
|
||||
list,
|
||||
all,
|
||||
title,
|
||||
metricTitle: "Sent In Loss",
|
||||
metricTitle: "Transfer Volume In Loss",
|
||||
getMetric: (c) => getInLoss(c),
|
||||
}),
|
||||
},
|
||||
@@ -431,7 +431,7 @@ function groupedVolumeTree(list, all, title, getTransferVolume) {
|
||||
list,
|
||||
all,
|
||||
title,
|
||||
metricTitle: "Sent Volume",
|
||||
metricTitle: "Transfer Volume",
|
||||
getMetric: (c) => getTransferVolume(c),
|
||||
}),
|
||||
...groupedProfitabilityArray(
|
||||
|
||||
@@ -136,7 +136,7 @@ function groupedWeightFolder({ list, all, getAvgPrice, getInProfit, getInLoss, g
|
||||
return [
|
||||
{
|
||||
name: "Average",
|
||||
title: title(`${avgTitle} Comparison`),
|
||||
title: title(`Cost Basis ${avgTitle} (${weightLabel})`),
|
||||
top: mapCohortsWithAll(list, all, (c) =>
|
||||
price({ series: getAvgPrice(c), name: c.name, color: c.color }),
|
||||
),
|
||||
|
||||
@@ -82,7 +82,7 @@ function singleDeltaItems(delta, unit, title, name) {
|
||||
{
|
||||
...rollingPercentRatioTree({
|
||||
windows: delta.rate,
|
||||
title: title(`${name} Rate`),
|
||||
title: title(`${name} Growth Rate`),
|
||||
}),
|
||||
name: "Growth Rate",
|
||||
},
|
||||
@@ -121,7 +121,7 @@ function groupedDeltaItems(list, all, getDelta, unit, title, name) {
|
||||
name: "Growth Rate",
|
||||
tree: ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: title(`${name} Rate (${w.title})`),
|
||||
title: title(`${name} Growth Rate (${w.title})`),
|
||||
bottom: flatMapCohortsWithAll(list, all, (c) =>
|
||||
percentRatioBaseline({
|
||||
pattern: getDelta(c).rate[w.key],
|
||||
@@ -212,17 +212,18 @@ function ownSupplyChart(supply, title) {
|
||||
* Count folder (UTXO or Address) with value + change
|
||||
* @param {{ base: AnySeriesPattern, delta: DeltaPattern }} pattern
|
||||
* @param {string} name
|
||||
* @param {string} chartTitle
|
||||
* @param {Color} color
|
||||
* @param {(name: string) => string} title
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
function countFolder(pattern, name, color, title) {
|
||||
function countFolder(pattern, name, chartTitle, color, title) {
|
||||
return {
|
||||
name,
|
||||
tree: [
|
||||
{
|
||||
name: "Count",
|
||||
title: title(name),
|
||||
title: title(chartTitle),
|
||||
bottom: [
|
||||
line({
|
||||
series: pattern.base,
|
||||
@@ -259,7 +260,7 @@ export function createHoldingsSection({ cohort, title }) {
|
||||
...singleDeltaItems(supply.delta, Unit.sats, title, "Change"),
|
||||
],
|
||||
},
|
||||
countFolder(cohort.tree.outputs.unspentCount, "UTXOs", cohort.color, title),
|
||||
countFolder(cohort.tree.outputs.unspentCount, "UTXOs", "UTXO Count", cohort.color, title),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -283,8 +284,8 @@ export function createHoldingsSectionAll({ cohort, title }) {
|
||||
...singleDeltaItems(supply.delta, Unit.sats, title, "Change"),
|
||||
],
|
||||
},
|
||||
countFolder(cohort.tree.outputs.unspentCount, "UTXOs", cohort.color, title),
|
||||
countFolder(cohort.addressCount, "Addresses", cohort.color, title),
|
||||
countFolder(cohort.tree.outputs.unspentCount, "UTXOs", "UTXO Count", cohort.color, title),
|
||||
countFolder(cohort.addressCount, "Addresses", "Address Count", cohort.color, title),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -309,7 +310,7 @@ export function createHoldingsSectionWithRelative({ cohort, title }) {
|
||||
...singleDeltaItems(supply.delta, Unit.sats, title, "Change"),
|
||||
],
|
||||
},
|
||||
countFolder(cohort.tree.outputs.unspentCount, "UTXOs", cohort.color, title),
|
||||
countFolder(cohort.tree.outputs.unspentCount, "UTXOs", "UTXO Count", cohort.color, title),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -333,7 +334,7 @@ export function createHoldingsSectionWithOwnSupply({ cohort, title }) {
|
||||
...singleDeltaItems(supply.delta, Unit.sats, title, "Change"),
|
||||
],
|
||||
},
|
||||
countFolder(cohort.tree.outputs.unspentCount, "UTXOs", cohort.color, title),
|
||||
countFolder(cohort.tree.outputs.unspentCount, "UTXOs", "UTXO Count", cohort.color, title),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -356,7 +357,7 @@ export function createHoldingsSectionWithProfitLoss({ cohort, title }) {
|
||||
...singleDeltaItems(supply.delta, Unit.sats, title, "Change"),
|
||||
],
|
||||
},
|
||||
countFolder(cohort.tree.outputs.unspentCount, "UTXOs", cohort.color, title),
|
||||
countFolder(cohort.tree.outputs.unspentCount, "UTXOs", "UTXO Count", cohort.color, title),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -379,8 +380,8 @@ export function createHoldingsSectionAddress({ cohort, title }) {
|
||||
...singleDeltaItems(supply.delta, Unit.sats, title, "Change"),
|
||||
],
|
||||
},
|
||||
countFolder(cohort.tree.outputs.unspentCount, "UTXOs", cohort.color, title),
|
||||
countFolder(cohort.addressCount, "Addresses", cohort.color, title),
|
||||
countFolder(cohort.tree.outputs.unspentCount, "UTXOs", "UTXO Count", cohort.color, title),
|
||||
countFolder(cohort.addressCount, "Addresses", "Address Count", cohort.color, title),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -402,8 +403,8 @@ export function createHoldingsSectionAddressAmount({ cohort, title }) {
|
||||
...singleDeltaItems(supply.delta, Unit.sats, title, "Change"),
|
||||
],
|
||||
},
|
||||
countFolder(cohort.tree.outputs.unspentCount, "UTXOs", cohort.color, title),
|
||||
countFolder(cohort.addressCount, "Addresses", cohort.color, title),
|
||||
countFolder(cohort.tree.outputs.unspentCount, "UTXOs", "UTXO Count", cohort.color, title),
|
||||
countFolder(cohort.addressCount, "Addresses", "Address Count", cohort.color, title),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -65,6 +65,7 @@ import {
|
||||
createProfitabilitySectionWithInvestedCapitalPct,
|
||||
createProfitabilitySectionLongTerm,
|
||||
createGroupedProfitabilitySection,
|
||||
createGroupedProfitabilitySectionWithProfitLoss,
|
||||
createGroupedProfitabilitySectionWithNupl,
|
||||
createGroupedProfitabilitySectionWithInvestedCapitalPct,
|
||||
} from "./profitability.js";
|
||||
@@ -91,7 +92,7 @@ export { buildCohortData } from "./data.js";
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
export function createCohortFolderAll(cohort) {
|
||||
const title = formatCohortTitle(cohort.name);
|
||||
const title = formatCohortTitle(cohort.title);
|
||||
return {
|
||||
name: cohort.name || "all",
|
||||
tree: [
|
||||
@@ -111,7 +112,7 @@ export function createCohortFolderAll(cohort) {
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
export function createCohortFolderFull(cohort) {
|
||||
const title = formatCohortTitle(cohort.name);
|
||||
const title = formatCohortTitle(cohort.title);
|
||||
return {
|
||||
name: cohort.name || "all",
|
||||
tree: [
|
||||
@@ -131,7 +132,7 @@ export function createCohortFolderFull(cohort) {
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
export function createCohortFolderWithAdjusted(cohort) {
|
||||
const title = formatCohortTitle(cohort.name);
|
||||
const title = formatCohortTitle(cohort.title);
|
||||
return {
|
||||
name: cohort.name || "all",
|
||||
tree: [
|
||||
@@ -150,7 +151,7 @@ export function createCohortFolderWithAdjusted(cohort) {
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
export function createCohortFolderWithNupl(cohort) {
|
||||
const title = formatCohortTitle(cohort.name);
|
||||
const title = formatCohortTitle(cohort.title);
|
||||
return {
|
||||
name: cohort.name || "all",
|
||||
tree: [
|
||||
@@ -170,7 +171,7 @@ export function createCohortFolderWithNupl(cohort) {
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
export function createCohortFolderLongTerm(cohort) {
|
||||
const title = formatCohortTitle(cohort.name);
|
||||
const title = formatCohortTitle(cohort.title);
|
||||
return {
|
||||
name: cohort.name || "all",
|
||||
tree: [
|
||||
@@ -190,7 +191,7 @@ export function createCohortFolderLongTerm(cohort) {
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
export function createCohortFolderAgeRange(cohort) {
|
||||
const title = formatCohortTitle(cohort.name);
|
||||
const title = formatCohortTitle(cohort.title);
|
||||
return {
|
||||
name: cohort.name || "all",
|
||||
tree: [
|
||||
@@ -210,7 +211,7 @@ export function createCohortFolderAgeRange(cohort) {
|
||||
*/
|
||||
export function createCohortFolderAgeRangeWithMatured(cohort) {
|
||||
const folder = createCohortFolderAgeRange(cohort);
|
||||
const title = formatCohortTitle(cohort.name);
|
||||
const title = formatCohortTitle(cohort.title);
|
||||
folder.tree.push({
|
||||
name: "Matured",
|
||||
tree: satsBtcUsdFullTree({
|
||||
@@ -227,7 +228,7 @@ export function createCohortFolderAgeRangeWithMatured(cohort) {
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
export function createCohortFolderBasicWithMarketCap(cohort) {
|
||||
const title = formatCohortTitle(cohort.name);
|
||||
const title = formatCohortTitle(cohort.title);
|
||||
return {
|
||||
name: cohort.name || "all",
|
||||
tree: [
|
||||
@@ -247,7 +248,7 @@ export function createCohortFolderBasicWithMarketCap(cohort) {
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
export function createCohortFolderAddress(cohort) {
|
||||
const title = formatCohortTitle(cohort.name);
|
||||
const title = formatCohortTitle(cohort.title);
|
||||
return {
|
||||
name: cohort.name || "all",
|
||||
tree: [
|
||||
@@ -266,7 +267,7 @@ export function createCohortFolderAddress(cohort) {
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
export function createCohortFolderWithoutRelative(cohort) {
|
||||
const title = formatCohortTitle(cohort.name);
|
||||
const title = formatCohortTitle(cohort.title);
|
||||
return {
|
||||
name: cohort.name || "all",
|
||||
tree: [
|
||||
@@ -285,7 +286,7 @@ export function createCohortFolderWithoutRelative(cohort) {
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
export function createAddressCohortFolder(cohort) {
|
||||
const title = formatCohortTitle(cohort.name);
|
||||
const title = formatCohortTitle(cohort.title);
|
||||
return {
|
||||
name: cohort.name || "all",
|
||||
tree: [
|
||||
@@ -455,7 +456,7 @@ export function createGroupedCohortFolderAddress({
|
||||
...createGroupedHoldingsSectionAddress({ list, all, title }),
|
||||
createGroupedValuationSection({ list, all, title }),
|
||||
createGroupedPricesSection({ list, all, title }),
|
||||
createGroupedProfitabilitySection({
|
||||
createGroupedProfitabilitySectionWithProfitLoss({
|
||||
list,
|
||||
all,
|
||||
title,
|
||||
|
||||
@@ -100,6 +100,19 @@ function relPnlChart(profit, loss, name, title) {
|
||||
};
|
||||
}
|
||||
|
||||
/** @param {{ percent: AnySeriesPattern, ratio: AnySeriesPattern }} net @param {{ percent: AnySeriesPattern, ratio: AnySeriesPattern }} profit @param {{ percent: AnySeriesPattern, ratio: AnySeriesPattern }} loss @param {string} name @param {(name: string) => string} title */
|
||||
function relPnlChartWithNet(net, profit, loss, name, title) {
|
||||
return {
|
||||
name,
|
||||
title: title(`Unrealized P&L (${name})`),
|
||||
bottom: [
|
||||
...percentRatioBaseline({ pattern: net, name: "Net" }),
|
||||
...percentRatio({ pattern: profit, name: "Profit", color: colors.profit }),
|
||||
...percentRatio({ pattern: loss, name: "Loss", color: colors.loss }),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Core unrealized items: Overview + Net + NUPL + Profit + Loss
|
||||
* @param {{ profit: { usd: AnySeriesPattern }, loss: { usd: AnySeriesPattern, negative: AnySeriesPattern }, netPnl: { usd: AnySeriesPattern }, nupl: NuplPattern }} u
|
||||
@@ -142,6 +155,30 @@ function unrealizedCore(u, title) {
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Core unrealized items + Gross
|
||||
* @param {{ profit: { usd: AnySeriesPattern }, loss: { usd: AnySeriesPattern, negative: AnySeriesPattern }, netPnl: { usd: AnySeriesPattern }, grossPnl: { usd: AnySeriesPattern }, nupl: NuplPattern }} u
|
||||
* @param {(name: string) => string} title
|
||||
* @returns {PartialOptionsTree}
|
||||
*/
|
||||
function unrealizedCoreWithGross(u, title) {
|
||||
return [
|
||||
...unrealizedCore(u, title),
|
||||
{
|
||||
name: "Gross",
|
||||
title: title("Gross Unrealized P&L"),
|
||||
bottom: [
|
||||
line({
|
||||
series: u.grossPnl.usd,
|
||||
name: "Gross",
|
||||
color: colors.gross,
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* % of Own P&L chart
|
||||
* @param {AllRelativePattern | FullRelativePattern} u
|
||||
@@ -173,7 +210,7 @@ function ownPnlChart(u, title) {
|
||||
/** @param {AllRelativePattern} u @param {(name: string) => string} title */
|
||||
function unrealizedTreeAll(u, title) {
|
||||
return [
|
||||
...unrealizedCore(u, title),
|
||||
...unrealizedCoreWithGross(u, title),
|
||||
ownPnlChart(u, title),
|
||||
relPnlChart(u.profit.toMcap, u.loss.toMcap, "% of Market Cap", title),
|
||||
];
|
||||
@@ -182,22 +219,17 @@ function unrealizedTreeAll(u, title) {
|
||||
/** @param {FullRelativePattern} u @param {(name: string) => string} title */
|
||||
function unrealizedTreeFull(u, title) {
|
||||
return [
|
||||
...unrealizedCore(u, title),
|
||||
...unrealizedCoreWithGross(u, title),
|
||||
ownPnlChart(u, title),
|
||||
relPnlChart(u.profit.toMcap, u.loss.toMcap, "% of Market Cap", title),
|
||||
relPnlChart(
|
||||
u.profit.toOwnMcap,
|
||||
u.loss.toOwnMcap,
|
||||
"% of Own Market Cap",
|
||||
title,
|
||||
),
|
||||
relPnlChartWithNet(u.netPnl.toOwnMcap, u.profit.toOwnMcap, u.loss.toOwnMcap, "% of Own Market Cap", title),
|
||||
];
|
||||
}
|
||||
|
||||
/** @param {FullRelativePattern} u @param {(name: string) => string} title */
|
||||
function unrealizedTreeLongTerm(u, title) {
|
||||
return [
|
||||
...unrealizedCore(u, title),
|
||||
...unrealizedCoreWithGross(u, title),
|
||||
ownPnlChart(u, title),
|
||||
{
|
||||
name: "% of Market Cap",
|
||||
@@ -208,12 +240,7 @@ function unrealizedTreeLongTerm(u, title) {
|
||||
color: colors.loss,
|
||||
}),
|
||||
},
|
||||
relPnlChart(
|
||||
u.profit.toOwnMcap,
|
||||
u.loss.toOwnMcap,
|
||||
"% of Own Market Cap",
|
||||
title,
|
||||
),
|
||||
relPnlChartWithNet(u.netPnl.toOwnMcap, u.profit.toOwnMcap, u.loss.toOwnMcap, "% of Own Market Cap", title),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -300,7 +327,7 @@ function realizedMetricFolder({ pattern, metricTitle, color, title }) {
|
||||
})),
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: title(`Realized ${metricTitle} (Total)`),
|
||||
title: title(`Cumulative Realized ${metricTitle}`),
|
||||
bottom: [
|
||||
line({
|
||||
series: pattern.cumulative.usd,
|
||||
@@ -350,7 +377,7 @@ function realizedNetFolder({ netPnl, title, extraChange = [] }) {
|
||||
})),
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: title("Net Realized P&L (Total)"),
|
||||
title: title("Cumulative Net Realized P&L"),
|
||||
bottom: [
|
||||
baseline({
|
||||
series: netPnl.cumulative.usd,
|
||||
@@ -372,7 +399,7 @@ function realizedNetFolder({ netPnl, title, extraChange = [] }) {
|
||||
tree: [
|
||||
...rollingPercentRatioTree({
|
||||
windows: netPnl.delta.rate,
|
||||
title: title("Net Realized P&L Rate"),
|
||||
title: title("Net Realized P&L Growth Rate"),
|
||||
}).tree,
|
||||
...extraChange,
|
||||
],
|
||||
@@ -587,7 +614,7 @@ function realizedSubfolderFull(r, title) {
|
||||
})),
|
||||
{
|
||||
name: "Cumulative",
|
||||
title: title("Peak Regret (Total)"),
|
||||
title: title("Cumulative Peak Regret"),
|
||||
bottom: [
|
||||
line({
|
||||
series: r.peakRegret.cumulative.usd,
|
||||
@@ -706,6 +733,16 @@ export function createProfitabilitySectionWithProfitLoss({ cohort, title }) {
|
||||
{
|
||||
name: "Unrealized",
|
||||
tree: [
|
||||
{
|
||||
name: "Overview",
|
||||
title: title("Unrealized P&L"),
|
||||
bottom: [
|
||||
line({ series: u.profit.usd, name: "Profit", color: colors.profit, unit: Unit.usd }),
|
||||
line({ series: u.loss.negative, name: "Neg. Loss", color: colors.loss, unit: Unit.usd }),
|
||||
line({ series: u.loss.usd, name: "Loss", color: colors.loss, unit: Unit.usd, defaultActive: false }),
|
||||
priceLine({ unit: Unit.usd }),
|
||||
],
|
||||
},
|
||||
{ name: "NUPL", title: title("NUPL"), bottom: nuplSeries(u.nupl) },
|
||||
{
|
||||
name: "Profit",
|
||||
@@ -840,32 +877,49 @@ export function createProfitabilitySectionWithInvestedCapitalPct({
|
||||
* @param {(name: string) => string} title
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
/**
|
||||
* Grouped realized profit + loss items
|
||||
* @param {readonly UtxoCohortObject[]} list
|
||||
* @param {CohortAll} all
|
||||
* @param {(name: string) => string} title
|
||||
* @returns {PartialOptionsTree}
|
||||
*/
|
||||
function groupedRealizedProfitLossItems(list, all, title) {
|
||||
return [
|
||||
{ name: "Profit", tree: groupedWindowsCumulativeUsd({ list, all, title, metricTitle: "Realized Profit", getMetric: (c) => c.tree.realized.profit }) },
|
||||
{ name: "Loss", tree: groupedWindowsCumulativeUsd({ list, all, title, metricTitle: "Realized Loss", getMetric: (c) => c.tree.realized.loss }) },
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Grouped realized net item
|
||||
* @param {readonly (CohortAgeRange | CohortWithAdjusted | CohortAll | CohortFull | CohortLongTerm)[]} list
|
||||
* @param {CohortAll} all
|
||||
* @param {(name: string) => string} title
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
function groupedRealizedNetItem(list, all, title) {
|
||||
return { name: "Net", tree: groupedWindowsCumulativeUsd({ list, all, title, metricTitle: "Net Realized P&L", getMetric: (c) => c.tree.realized.netPnl, seriesFn: baseline }) };
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {readonly UtxoCohortObject[]} list
|
||||
* @param {CohortAll} all
|
||||
* @param {(name: string) => string} title
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
function groupedRealizedSubfolder(list, all, title) {
|
||||
return {
|
||||
name: "Realized",
|
||||
tree: [
|
||||
{
|
||||
name: "Profit",
|
||||
tree: groupedWindowsCumulativeUsd({
|
||||
list,
|
||||
all,
|
||||
title,
|
||||
metricTitle: "Realized Profit",
|
||||
getMetric: (c) => c.tree.realized.profit,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "Loss",
|
||||
tree: groupedWindowsCumulativeUsd({
|
||||
list,
|
||||
all,
|
||||
title,
|
||||
metricTitle: "Realized Loss",
|
||||
getMetric: (c) => c.tree.realized.loss,
|
||||
}),
|
||||
},
|
||||
],
|
||||
};
|
||||
return { name: "Realized", tree: groupedRealizedProfitLossItems(list, all, title) };
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {readonly (CohortAgeRange | CohortWithAdjusted)[]} list
|
||||
* @param {CohortAll} all
|
||||
* @param {(name: string) => string} title
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
function groupedRealizedSubfolderMid(list, all, title) {
|
||||
return { name: "Realized", tree: [groupedRealizedNetItem(list, all, title), ...groupedRealizedProfitLossItems(list, all, title)] };
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -896,7 +950,7 @@ function groupedRealizedNetPnlDeltaItems(list, all, title) {
|
||||
name: "Growth Rate",
|
||||
tree: ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: title(`Net Realized P&L Rate (${w.title})`),
|
||||
title: title(`Net Realized P&L Growth Rate (${w.title})`),
|
||||
bottom: flatMapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
percentRatioBaseline({
|
||||
pattern: tree.realized.netPnl.delta.rate[w.key],
|
||||
@@ -934,26 +988,7 @@ function groupedRealizedSubfolderFull(list, all, title) {
|
||||
...groupedRealizedNetPnlDeltaItems(list, all, title),
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "Profit",
|
||||
tree: groupedWindowsCumulativeUsd({
|
||||
list,
|
||||
all,
|
||||
title,
|
||||
metricTitle: "Realized Profit",
|
||||
getMetric: (c) => c.tree.realized.profit,
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "Loss",
|
||||
tree: groupedWindowsCumulativeUsd({
|
||||
list,
|
||||
all,
|
||||
title,
|
||||
metricTitle: "Realized Loss",
|
||||
getMetric: (c) => c.tree.realized.loss,
|
||||
}),
|
||||
},
|
||||
...groupedRealizedProfitLossItems(list, all, title),
|
||||
{
|
||||
name: "Gross",
|
||||
tree: groupedWindowsCumulativeUsd({
|
||||
@@ -1234,7 +1269,7 @@ export function createGroupedProfitabilitySectionWithInvestedCapitalPct({
|
||||
name: "Profitability",
|
||||
tree: [
|
||||
{ name: "Unrealized", tree: groupedUnrealizedMid(list, all, title) },
|
||||
groupedRealizedSubfolder(list, all, title),
|
||||
groupedRealizedSubfolderMid(list, all, title),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ import { ratioBottomSeries, mapCohortsWithAll, flatMapCohortsWithAll } from "../
|
||||
function singleDeltaItems(tree, title) {
|
||||
return [
|
||||
{ ...sumsTreeBaseline({ windows: mapWindows(tree.realized.cap.delta.absolute, (c) => c.usd), title: title("Realized Cap Change"), unit: Unit.usd }), name: "Change" },
|
||||
{ ...rollingPercentRatioTree({ windows: tree.realized.cap.delta.rate, title: title("Realized Cap Rate") }), name: "Growth Rate" },
|
||||
{ ...rollingPercentRatioTree({ windows: tree.realized.cap.delta.rate, title: title("Realized Cap Growth Rate") }), name: "Growth Rate" },
|
||||
];
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ function groupedDeltaAndMvrv(list, all, title) {
|
||||
name: "Growth Rate",
|
||||
tree: ROLLING_WINDOWS.map((w) => ({
|
||||
name: w.name,
|
||||
title: title(`Realized Cap Rate (${w.title})`),
|
||||
title: title(`Realized Cap Growth Rate (${w.title})`),
|
||||
bottom: flatMapCohortsWithAll(list, all, ({ name, color, tree }) =>
|
||||
percentRatioBaseline({ pattern: tree.realized.cap.delta.rate[w.key], name, color }),
|
||||
),
|
||||
|
||||
@@ -5,13 +5,13 @@ import { entries, includes } from "../utils/array.js";
|
||||
import { colors } from "../utils/colors.js";
|
||||
import {
|
||||
line,
|
||||
baseline,
|
||||
dots,
|
||||
dotted,
|
||||
distributionBtcSatsUsd,
|
||||
statsAtWindow,
|
||||
ROLLING_WINDOWS,
|
||||
percentRatio,
|
||||
percentRatioBaseline,
|
||||
chartsFromCount,
|
||||
} from "./series.js";
|
||||
import {
|
||||
@@ -438,7 +438,7 @@ export function createMiningSection() {
|
||||
{
|
||||
name: "Adjustment",
|
||||
title: "Difficulty Adjustment",
|
||||
bottom: [baseline({ series: blocks.difficulty.adjustment.percent, name: "Change", unit: Unit.percentage })],
|
||||
bottom: percentRatioBaseline({ pattern: blocks.difficulty.adjustment, name: "Change" }),
|
||||
},
|
||||
{
|
||||
name: "Countdown",
|
||||
|
||||
@@ -15,40 +15,31 @@ function walkSeries(node, map, path) {
|
||||
for (const [key, value] of Object.entries(node)) {
|
||||
const kn = key.toLowerCase();
|
||||
if (
|
||||
key === "lookback" ||
|
||||
key === "cumulativeMarketCap" ||
|
||||
key === "sd24h" ||
|
||||
key === "spot" ||
|
||||
key === "ohlc" ||
|
||||
key === "state" ||
|
||||
key === "emaSlow" ||
|
||||
key === "emaFast" ||
|
||||
key.endsWith("Raw") ||
|
||||
key.endsWith("Cents") ||
|
||||
key.endsWith("State") ||
|
||||
key.endsWith("Start") ||
|
||||
kn === "cents" ||
|
||||
kn === "bps" ||
|
||||
kn === "mvrv" ||
|
||||
kn === "constants" ||
|
||||
kn === "blockhash" ||
|
||||
kn === "date" ||
|
||||
kn === "ohlc" ||
|
||||
kn === "split" ||
|
||||
kn === "outpoint" ||
|
||||
kn === "positions" ||
|
||||
kn === "heighttopool" ||
|
||||
kn === "txid" ||
|
||||
kn === "spot" ||
|
||||
kn.startsWith("timestamp") ||
|
||||
kn.startsWith("satdays") ||
|
||||
kn.startsWith("satblocks") ||
|
||||
kn.startsWith("coinyears") ||
|
||||
kn.endsWith("index") ||
|
||||
kn.endsWith("indexes")
|
||||
)
|
||||
continue;
|
||||
walkSeries(/** @type {TreeNode | null | undefined} */ (value), map, [
|
||||
...path,
|
||||
key,
|
||||
]);
|
||||
const newPath = [...path, key];
|
||||
const joined = newPath.join(".");
|
||||
if (
|
||||
joined.endsWith(".count.total.average") ||
|
||||
joined.endsWith(".versions.v1.average") ||
|
||||
joined.endsWith(".versions.v2.average") ||
|
||||
joined.endsWith(".versions.v3.average")
|
||||
)
|
||||
continue;
|
||||
walkSeries(/** @type {TreeNode | null | undefined} */ (value), map, newPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user