mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-08-11 17:53:07 -07:00
next: ai part 11
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
import { brk } from "../../utils/client.js";
|
||||
import { colors } from "../../utils/colors.js";
|
||||
import { Unit } from "../../utils/units.js";
|
||||
import { line } from "../series.js";
|
||||
|
||||
const FLOOR_PERCENTILES = /** @type {const} */ ([
|
||||
{ key: "pct95", name: "P95" },
|
||||
{ key: "pct98", name: "P98" },
|
||||
{ key: "pct99", name: "P99" },
|
||||
{ key: "pct995", name: "P99.5" },
|
||||
{ key: "pct999", name: "P99.9" },
|
||||
]);
|
||||
|
||||
const LEVEL_PERCENTILES = /** @type {const} */ ([
|
||||
{ key: "pct10", name: "P10" },
|
||||
{ key: "pct20", name: "P20" },
|
||||
{ key: "pct30", name: "P30" },
|
||||
{ key: "pct40", name: "P40" },
|
||||
{ key: "pct50", name: "P50" },
|
||||
{ key: "pct60", name: "P60" },
|
||||
{ key: "pct70", name: "P70" },
|
||||
{ key: "pct80", name: "P80" },
|
||||
{ key: "pct90", name: "P90" },
|
||||
]);
|
||||
|
||||
/**
|
||||
* @typedef {Object} BedrockMode
|
||||
* @property {string} name
|
||||
* @property {AnySeriesPattern} inLoss
|
||||
* @property {{
|
||||
* floor: Record<string, AnySeriesPattern>,
|
||||
* level: Record<string, AnySeriesPattern>,
|
||||
* lossThreshold: Record<string, AnySeriesPattern>,
|
||||
* }} tree
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {BedrockMode} mode
|
||||
* @param {AnySeriesPattern} ath
|
||||
* @returns {PartialChartOption}
|
||||
*/
|
||||
function modeChart(mode, ath) {
|
||||
return {
|
||||
name: mode.name,
|
||||
title: `Bitcoin Bedrock Model: ${mode.name}`,
|
||||
top: [
|
||||
...FLOOR_PERCENTILES.map((percentile, index) =>
|
||||
line({
|
||||
series: mode.tree.floor[percentile.key],
|
||||
name: percentile.name,
|
||||
color: colors.bedrock.percentiles[index],
|
||||
unit: Unit.usd,
|
||||
}),
|
||||
),
|
||||
...LEVEL_PERCENTILES.map((percentile, index) =>
|
||||
line({
|
||||
series: mode.tree.level[percentile.key],
|
||||
name: `L${percentile.name.slice(1)}`,
|
||||
color: colors.bedrock.levels[index],
|
||||
unit: Unit.usd,
|
||||
style: 1,
|
||||
}),
|
||||
),
|
||||
line({
|
||||
series: ath,
|
||||
name: "L100",
|
||||
color: colors.bedrock.levels[9],
|
||||
unit: Unit.usd,
|
||||
style: 1,
|
||||
}),
|
||||
],
|
||||
bottom: [
|
||||
line({
|
||||
series: mode.inLoss,
|
||||
name: "Loss",
|
||||
color: colors.default,
|
||||
defaultActive: false,
|
||||
unit: Unit.ratio,
|
||||
}),
|
||||
...FLOOR_PERCENTILES.map((percentile, index) =>
|
||||
line({
|
||||
series: mode.tree.lossThreshold[percentile.key],
|
||||
name: percentile.name,
|
||||
color: colors.bedrock.percentiles[index],
|
||||
defaultActive: false,
|
||||
unit: Unit.ratio,
|
||||
}),
|
||||
),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Bedrock model section.
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
export function createBedrockSection() {
|
||||
const { bedrock, market, cohorts, cointime, coinflow } = brk.series;
|
||||
const horizonModes = /** @type {const} */ ([
|
||||
{ key: "coinflow8y", horizon: "_8y", name: "Coinflow 8Y" },
|
||||
{ key: "coinflow4y", horizon: "_4y", name: "Coinflow 4Y" },
|
||||
{ key: "coinflow2y", horizon: "_2y", name: "Coinflow 2Y" },
|
||||
{ key: "coinflow1y", horizon: "_1y", name: "Coinflow 1Y" },
|
||||
{ key: "coinflow6m", horizon: "_6m", name: "Coinflow 6M" },
|
||||
{ key: "coinflow3m", horizon: "_3m", name: "Coinflow 3M" },
|
||||
{ key: "coinflow1m", horizon: "_1m", name: "Coinflow 1M" },
|
||||
]).map((mode) => ({
|
||||
name: mode.name,
|
||||
tree: bedrock[mode.key],
|
||||
inLoss: coinflow.horizon[mode.horizon].supply.inLoss.share,
|
||||
}));
|
||||
|
||||
const modes = /** @type {readonly BedrockMode[]} */ ([
|
||||
{
|
||||
name: "Raw",
|
||||
tree: bedrock.raw,
|
||||
inLoss: cohorts.utxo.all.supply.inLoss.share.ratio,
|
||||
},
|
||||
{
|
||||
name: "Cointime",
|
||||
tree: bedrock.cointime,
|
||||
inLoss: cointime.supply.active.inLoss.share,
|
||||
},
|
||||
{
|
||||
name: "Coinflow",
|
||||
tree: bedrock.coinflow,
|
||||
inLoss: coinflow.supply.mobile.inLoss.share,
|
||||
},
|
||||
...horizonModes,
|
||||
]);
|
||||
|
||||
return {
|
||||
name: "Bedrock",
|
||||
tree: modes.map((mode) => modeChart(mode, market.ath.high.usd)),
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
import { brk } from "../../utils/client.js";
|
||||
import { colors } from "../../utils/colors.js";
|
||||
import { Unit } from "../../utils/units.js";
|
||||
import { baseline, histogram } from "../series.js";
|
||||
import {
|
||||
percentileBands,
|
||||
priceBands,
|
||||
priceRatioPercentilesTree,
|
||||
} from "../shared.js";
|
||||
|
||||
/**
|
||||
* Create Rarity Meter model section.
|
||||
* @returns {PartialOptionsGroup}
|
||||
*/
|
||||
export function createRarityMeterSection() {
|
||||
const { rarityMeter } = brk.series.indicators;
|
||||
const { all, sth, lth } = brk.series.cohorts.utxo;
|
||||
|
||||
return {
|
||||
name: "Rarity Meter",
|
||||
tree: [
|
||||
.../** @type {const} */ ([
|
||||
{ key: "full", name: "Full", title: "Bitcoin Rarity Meter: Full" },
|
||||
{ key: "local", name: "Local", title: "Bitcoin Rarity Meter: Local" },
|
||||
{ key: "cycle", name: "Cycle", title: "Bitcoin Rarity Meter: Cycle" },
|
||||
]).map((variant) => {
|
||||
const meter = rarityMeter[variant.key];
|
||||
return {
|
||||
name: variant.name,
|
||||
title: variant.title,
|
||||
top: priceBands(percentileBands(meter), { defaultActive: true }),
|
||||
bottom: [
|
||||
histogram({
|
||||
series: meter.index,
|
||||
name: "Index",
|
||||
unit: Unit.count,
|
||||
colorFn: (value) =>
|
||||
/** @type {const} */ ([
|
||||
colors.ratioPct._0_5,
|
||||
colors.ratioPct._1,
|
||||
colors.ratioPct._2,
|
||||
colors.ratioPct._5,
|
||||
colors.transparent,
|
||||
colors.ratioPct._95,
|
||||
colors.ratioPct._98,
|
||||
colors.ratioPct._99,
|
||||
colors.ratioPct._99_5,
|
||||
])[value + 4],
|
||||
}),
|
||||
baseline({
|
||||
series: meter.score,
|
||||
name: "Score",
|
||||
unit: Unit.count,
|
||||
color: [colors.ratioPct._99, colors.ratioPct._1],
|
||||
defaultActive: false,
|
||||
}),
|
||||
],
|
||||
};
|
||||
}),
|
||||
{
|
||||
name: "Components",
|
||||
tree: [
|
||||
{
|
||||
name: "Realized Price",
|
||||
title: "Realized Price",
|
||||
pattern: all.realized.price,
|
||||
legend: "Realized",
|
||||
color: colors.realized,
|
||||
},
|
||||
{
|
||||
name: "Capitalized Price",
|
||||
title: "Capitalized Price",
|
||||
pattern: all.realized.capitalized.price,
|
||||
legend: "Capitalized",
|
||||
color: colors.capitalized,
|
||||
},
|
||||
{
|
||||
name: "STH RP",
|
||||
title: "STH Realized Price",
|
||||
pattern: sth.realized.price,
|
||||
legend: "Realized",
|
||||
color: colors.realized,
|
||||
},
|
||||
{
|
||||
name: "STH CP",
|
||||
title: "STH Capitalized Price",
|
||||
pattern: sth.realized.capitalized.price,
|
||||
legend: "Capitalized",
|
||||
color: colors.capitalized,
|
||||
},
|
||||
{
|
||||
name: "LTH RP",
|
||||
title: "LTH Realized Price",
|
||||
pattern: lth.realized.price,
|
||||
legend: "Realized",
|
||||
color: colors.realized,
|
||||
},
|
||||
{
|
||||
name: "LTH CP",
|
||||
title: "LTH Capitalized Price",
|
||||
pattern: lth.realized.capitalized.price,
|
||||
legend: "Capitalized",
|
||||
color: colors.capitalized,
|
||||
},
|
||||
].map((component) => {
|
||||
const [, ratioChart] = priceRatioPercentilesTree({
|
||||
pattern: component.pattern,
|
||||
title: component.title,
|
||||
legend: component.legend,
|
||||
color: component.color,
|
||||
defaultActivePercentiles: true,
|
||||
});
|
||||
return { ...ratioChart, name: component.name };
|
||||
}),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user