Files
brk/website/scripts/options/frameworks/coinflow.js
T
2026-08-05 14:50:47 +02:00

279 lines
7.5 KiB
JavaScript

import { brk } from "../../utils/client.js";
import { colors } from "../../utils/colors.js";
import { Unit } from "../../utils/units.js";
import { ageRanges } from "../age-ranges.js";
import { line, price } from "../series.js";
import { satsBtcUsd, simplePriceRatioTree } from "../shared.js";
/**
* @typedef {Object} CoinflowAgeRange
* @property {string} name
* @property {Color} color
* @property {{
* mobility: AnySeriesPattern,
* spendingRate: AnySeriesPattern,
* spendingExposure: AnySeriesPattern,
* supply: { mobile: AnyValuePattern, immobile: AnyValuePattern },
* }} tree
*/
/**
* @param {readonly CoinflowAgeRange[]} ranges
* @param {"mobility" | "spendingRate" | "spendingExposure"} key
* @param {string} name
* @returns {PartialChartOption}
*/
function ageRangeRatioChart(ranges, key, name) {
return {
name,
title: `${name} by UTXO Age`,
bottom: ranges.map((range) =>
line({
series: range.tree[key],
name: range.name,
color: range.color,
unit: Unit.ratio,
}),
),
};
}
/**
* @param {readonly CoinflowAgeRange[]} ranges
* @param {"mobile" | "immobile"} key
* @param {string} name
* @returns {PartialChartOption}
*/
function ageRangeSupplyChart(ranges, key, name) {
return {
name,
title: `${name} Supply by UTXO Age`,
bottom: ranges.flatMap((range) =>
satsBtcUsd({
pattern: range.tree.supply[key],
name: range.name,
color: range.color,
}),
),
};
}
/**
* Create Coinflow section.
* @returns {PartialOptionsGroup}
*/
export function createCoinflowSection() {
const { coinflow } = brk.series.frameworks;
const ranges = ageRanges.map(({ key, ...range }) => ({
...range,
tree: coinflow.ageRange[key],
}));
const terms = [
{ name: "STH", color: colors.term.short, tree: coinflow.sth },
{ name: "LTH", color: colors.term.long, tree: coinflow.lth },
];
const cohorts = [
{ name: "All", color: colors.loss, tree: coinflow },
...terms,
];
const frameworkCohorts = [
{ name: "All", color: colors.coinflow, tree: coinflow },
...terms,
];
const horizons = /** @type {const} */ ([
{ key: "_8y", name: "8Y" },
{ key: "_4y", name: "4Y" },
{ key: "_2y", name: "2Y" },
{ key: "_1y", name: "1Y" },
{ key: "_6m", name: "6M" },
{ key: "_3m", name: "3M" },
{ key: "_1m", name: "1M" },
]).map((horizon, index, all) => ({
...horizon,
color: colors.at(index, all.length),
}));
return {
name: "Coinflow",
tree: [
{
name: "Price",
tree: [
{
name: "Compare",
title: "Coinflow Price by Holder Term",
top: frameworkCohorts.map(({ name, color, tree }) =>
price({
series: tree.price,
name,
color,
}),
),
bottom: frameworkCohorts.map(({ name, color, tree }) =>
line({
series: tree.price.ratio,
name: `Spot / ${name}`,
color,
unit: Unit.ratio,
}),
),
},
...frameworkCohorts.map(({ name, color, tree }) => {
const title =
name === "All" ? "Coinflow Price" : `${name} Coinflow Price`;
const [chart] = simplePriceRatioTree({
pattern: tree.price,
title,
legend: name,
color,
});
return { ...chart, name };
}),
],
},
{
name: "Capitalization",
tree: [
{
name: "Compare",
title: "Coinflow Cap by Holder Term",
bottom: frameworkCohorts.map(({ name, color, tree }) =>
line({
series: tree.cap.usd,
name,
color,
unit: Unit.usd,
}),
),
},
...frameworkCohorts.map(({ name, color, tree }) => ({
name,
title: name === "All" ? "Coinflow Cap" : `${name} Coinflow Cap`,
bottom: [
line({
series: tree.cap.usd,
name,
color,
unit: Unit.usd,
}),
],
})),
],
},
{
name: "Supply",
tree: [
{
name: "Overview",
title: "Mobile vs Immobile Supply",
bottom: [
...satsBtcUsd({
pattern: coinflow.supply.mobile,
name: "Mobile",
color: colors.mobile,
}),
...satsBtcUsd({
pattern: coinflow.supply.immobile,
name: "Immobile",
color: colors.immobile,
}),
],
},
{
name: "By Holder Term",
tree: [
{
name: "Mobile",
title: "Mobile Supply by Holder Term",
bottom: terms.flatMap(({ name, color, tree }) =>
satsBtcUsd({
pattern: tree.supply.mobile,
name,
color,
}),
),
},
{
name: "Immobile",
title: "Immobile Supply by Holder Term",
bottom: terms.flatMap(({ name, color, tree }) =>
satsBtcUsd({
pattern: tree.supply.immobile,
name,
color,
}),
),
},
],
},
{
name: "By UTXO Age",
tree: [
ageRangeSupplyChart(ranges, "mobile", "Mobile"),
ageRangeSupplyChart(ranges, "immobile", "Immobile"),
],
},
{
name: "In Loss",
tree: [
{
name: "By Holder Term",
title: "Mobile Supply in Loss by Holder Term",
bottom: cohorts.map(({ name, color, tree }) =>
line({
series: tree.supply.mobile.inLoss.share,
name,
color,
unit: Unit.ratio,
}),
),
},
{
name: "By Horizon",
tree: cohorts.map(({ name, tree }) => ({
name,
title:
name === "All"
? "Mobile Supply in Loss by Horizon"
: `${name} Mobile Supply in Loss by Horizon`,
bottom: horizons.map((horizon) =>
line({
series:
tree.horizon[horizon.key].supply.inLoss.share,
name: horizon.name,
color: horizon.color,
unit: Unit.ratio,
}),
),
})),
},
],
},
],
},
{
name: "Activity",
tree: [
{
name: "By UTXO Age",
tree: [
ageRangeRatioChart(ranges, "mobility", "Mobility"),
ageRangeRatioChart(
ranges,
"spendingRate",
"Spending Rate",
),
ageRangeRatioChart(
ranges,
"spendingExposure",
"Spending Exposure",
),
],
},
],
},
],
};
}