Files
brk/websites/bitview/scripts/options/partial/cohorts/shared.js
2026-01-02 19:08:20 +01:00

217 lines
8.0 KiB
JavaScript

/** Shared cohort chart section builders */
/**
* Create supply section for a single cohort
* @param {PartialContext} ctx
* @param {CohortObject} cohort
* @param {string} title
* @returns {AnyFetchedSeriesBlueprint[]}
*/
export function createSingleSupplySeries(ctx, cohort, title) {
const { colors, s, createPriceLine } = ctx;
const { tree, color, name } = cohort;
return [
s({ metric: tree.supply.supply.sats, name: "Supply", color: colors.default }),
s({ metric: tree.supply.supply.bitcoin, name: "Supply", color: colors.default }),
s({ metric: tree.supply.supply.dollars, name: "Supply", color: colors.default }),
...("supplyRelToCirculatingSupply" in tree.relative
? [s({ metric: tree.relative.supplyRelToCirculatingSupply, name: "Supply", color: colors.default })]
: []),
s({ metric: tree.unrealized.supplyInProfit.sats, name: "In Profit", color: colors.green }),
s({ metric: tree.unrealized.supplyInProfit.bitcoin, name: "In Profit", color: colors.green }),
s({ metric: tree.unrealized.supplyInProfit.dollars, name: "In Profit", color: colors.green }),
s({ metric: tree.unrealized.supplyInLoss.sats, name: "In Loss", color: colors.red }),
s({ metric: tree.unrealized.supplyInLoss.bitcoin, name: "In Loss", color: colors.red }),
s({ metric: tree.unrealized.supplyInLoss.dollars, name: "In Loss", color: colors.red }),
s({ metric: tree.supply.supplyHalf.sats, name: "half", color: colors.gray, options: { lineStyle: 4 } }),
s({ metric: tree.supply.supplyHalf.bitcoin, name: "half", color: colors.gray, options: { lineStyle: 4 } }),
s({ metric: tree.supply.supplyHalf.dollars, name: "half", color: colors.gray, options: { lineStyle: 4 } }),
...("supplyInProfitRelToCirculatingSupply" in tree.relative
? [
s({ metric: tree.relative.supplyInProfitRelToCirculatingSupply, name: "In Profit", color: colors.green }),
s({ metric: tree.relative.supplyInLossRelToCirculatingSupply, name: "In Loss", color: colors.red }),
]
: []),
s({ metric: tree.relative.supplyInProfitRelToOwnSupply, name: "In Profit", color: colors.green }),
s({ metric: tree.relative.supplyInLossRelToOwnSupply, name: "In Loss", color: colors.red }),
createPriceLine({ unit: "%self", number: 100, lineStyle: 0, color: colors.default }),
createPriceLine({ unit: "%self", number: 50 }),
];
}
/**
* Create supply total series for grouped cohorts
* @param {PartialContext} ctx
* @param {readonly CohortObject[]} list
* @returns {AnyFetchedSeriesBlueprint[]}
*/
export function createGroupedSupplyTotalSeries(ctx, list) {
const { s, constant100 } = ctx;
return list.flatMap(({ color, name, tree }) => [
s({ metric: tree.supply.supply.sats, name, color }),
s({ metric: tree.supply.supply.bitcoin, name, color }),
s({ metric: tree.supply.supply.dollars, name, color }),
"supplyRelToCirculatingSupply" in tree.relative
? s({ metric: tree.relative.supplyRelToCirculatingSupply, name, color })
: s({ unit: "%all", metric: constant100, name, color }),
]);
}
/**
* Create supply in profit series for grouped cohorts
* @param {PartialContext} ctx
* @param {readonly CohortObject[]} list
* @returns {AnyFetchedSeriesBlueprint[]}
*/
export function createGroupedSupplyInProfitSeries(ctx, list) {
const { s } = ctx;
return list.flatMap(({ color, name, tree }) => [
s({ metric: tree.unrealized.supplyInProfit.sats, name, color }),
s({ metric: tree.unrealized.supplyInProfit.bitcoin, name, color }),
s({ metric: tree.unrealized.supplyInProfit.dollars, name, color }),
...("supplyInProfitRelToCirculatingSupply" in tree.relative
? [s({ metric: tree.relative.supplyInProfitRelToCirculatingSupply, name, color })]
: []),
]);
}
/**
* Create supply in loss series for grouped cohorts
* @param {PartialContext} ctx
* @param {readonly CohortObject[]} list
* @returns {AnyFetchedSeriesBlueprint[]}
*/
export function createGroupedSupplyInLossSeries(ctx, list) {
const { s } = ctx;
return list.flatMap(({ color, name, tree }) => [
s({ metric: tree.unrealized.supplyInLoss.sats, name, color }),
s({ metric: tree.unrealized.supplyInLoss.bitcoin, name, color }),
s({ metric: tree.unrealized.supplyInLoss.dollars, name, color }),
...("supplyInLossRelToCirculatingSupply" in tree.relative
? [s({ metric: tree.relative.supplyInLossRelToCirculatingSupply, name, color })]
: []),
]);
}
/**
* Create UTXO count series
* @param {PartialContext} ctx
* @param {readonly CohortObject[]} list
* @param {boolean} useGroupName
* @returns {AnyFetchedSeriesBlueprint[]}
*/
export function createUtxoCountSeries(ctx, list, useGroupName) {
const { s } = ctx;
return list.flatMap(({ color, name, tree }) => [
s({ metric: tree.supply.utxoCount, name: useGroupName ? name : "Count", color }),
]);
}
/**
* Create address count series (for address cohorts only)
* @param {PartialContext} ctx
* @param {readonly AddressCohortObject[]} list
* @param {boolean} useGroupName
* @returns {AnyFetchedSeriesBlueprint[]}
*/
export function createAddressCountSeries(ctx, list, useGroupName) {
const { s, colors } = ctx;
return list.flatMap(({ color, name, tree }) => [
s({
metric: tree.addrCount,
name: useGroupName ? name : "Count",
color: useGroupName ? color : colors.orange,
}),
]);
}
/**
* Create realized price series for grouped cohorts
* @param {PartialContext} ctx
* @param {readonly CohortObject[]} list
* @returns {AnyFetchedSeriesBlueprint[]}
*/
export function createRealizedPriceSeries(ctx, list) {
const { s } = ctx;
return list.map(({ color, name, tree }) =>
s({ metric: tree.realized.realizedPrice, name, color }),
);
}
/**
* Create realized price ratio series for grouped cohorts
* @param {PartialContext} ctx
* @param {readonly CohortObject[]} list
* @returns {AnyFetchedSeriesBlueprint[]}
*/
export function createRealizedPriceRatioSeries(ctx, list) {
const { s, createPriceLine } = ctx;
return [
...list.map(({ color, name, tree }) =>
s({ metric: tree.realized.realizedPriceExtra.ratio, name, color }),
),
createPriceLine({ unit: "ratio", number: 1 }),
];
}
/**
* Create realized capitalization series
* @param {PartialContext} ctx
* @param {readonly CohortObject[]} list
* @param {boolean} useGroupName
* @returns {AnyFetchedSeriesBlueprint[]}
*/
export function createRealizedCapSeries(ctx, list, useGroupName) {
const { s } = ctx;
return list.flatMap(({ color, name, tree }) => [
s({ metric: tree.realized.realizedCap, name: useGroupName ? name : "Capitalization", color }),
]);
}
/**
* Create price paid min/max series (available on all cohorts)
* @param {PartialContext} ctx
* @param {readonly CohortObject[]} list
* @param {boolean} useGroupName
* @returns {AnyFetchedSeriesBlueprint[]}
*/
export function createPricePaidMinMaxSeries(ctx, list, useGroupName) {
const { s } = ctx;
return list.flatMap(({ color, name, tree }) => [
s({ metric: tree.pricePaid.minPricePaid, name: useGroupName ? `${name} min` : "Min", color }),
s({ metric: tree.pricePaid.maxPricePaid, name: useGroupName ? `${name} max` : "Max", color }),
]);
}
/**
* Create price percentile series (only for cohorts with PricePaidPattern2)
* @param {PartialContext} ctx
* @param {readonly CohortWithPricePercentiles[]} list
* @param {boolean} useGroupName
* @returns {AnyFetchedSeriesBlueprint[]}
*/
export function createPricePercentilesSeries(ctx, list, useGroupName) {
const { s, colors } = ctx;
return list.flatMap(({ color, name, tree }) => {
const pp = tree.pricePaid.pricePercentiles;
return [
s({ metric: pp.pct10, name: useGroupName ? `${name} p10` : "p10", color, defaultActive: false }),
s({ metric: pp.pct25, name: useGroupName ? `${name} p25` : "p25", color, defaultActive: false }),
s({ metric: pp.pct50, name: useGroupName ? `${name} p50` : "p50", color }),
s({ metric: pp.pct75, name: useGroupName ? `${name} p75` : "p75", color, defaultActive: false }),
s({ metric: pp.pct90, name: useGroupName ? `${name} p90` : "p90", color, defaultActive: false }),
];
});
}