bitview: reorg part 9

This commit is contained in:
nym21
2025-10-01 23:17:48 +02:00
parent 62d4b35c93
commit c4ce718bb2
102 changed files with 1654 additions and 1798 deletions
+38 -50
View File
@@ -1,4 +1,4 @@
/** @import { IChartApi, ISeriesApi as _ISeriesApi, SeriesDefinition, SingleValueData as _SingleValueData, CandlestickData as _CandlestickData, BaselineData as _BaselineData, HistogramData as _HistogramData, SeriesType, IPaneApi, LineSeriesPartialOptions as _LineSeriesPartialOptions, HistogramSeriesPartialOptions as _HistogramSeriesPartialOptions, BaselineSeriesPartialOptions as _BaselineSeriesPartialOptions, CandlestickSeriesPartialOptions as _CandlestickSeriesPartialOptions, WhitespaceData, DeepPartial, ChartOptions, Time, LineData as _LineData } from '../../packages/lightweight-charts/5.0.8/dist/typings' */
/** @import { IChartApi, ISeriesApi as _ISeriesApi, SeriesDefinition, SingleValueData as _SingleValueData, CandlestickData as _CandlestickData, BaselineData as _BaselineData, HistogramData as _HistogramData, SeriesType, IPaneApi, LineSeriesPartialOptions as _LineSeriesPartialOptions, HistogramSeriesPartialOptions as _HistogramSeriesPartialOptions, BaselineSeriesPartialOptions as _BaselineSeriesPartialOptions, CandlestickSeriesPartialOptions as _CandlestickSeriesPartialOptions, WhitespaceData, DeepPartial, ChartOptions, Time, LineData as _LineData } from '../../modules/lightweight-charts/5.0.8/dist/typings' */
import {
createChart,
@@ -6,8 +6,8 @@ import {
HistogramSeries,
LineSeries,
BaselineSeries,
// } from "../packages/lightweight-charts/5.0.8/dist/lightweight-charts.standalone.development.mjs";
} from "../../packages/lightweight-charts/5.0.8/dist/lightweight-charts.standalone.production.mjs";
// } from "../modules/lightweight-charts/5.0.8/dist/lightweight-charts.standalone.development.mjs";
} from "../../modules/lightweight-charts/5.0.8/dist/lightweight-charts.standalone.production.mjs";
import {
createHorizontalChoiceField,
@@ -17,6 +17,8 @@ import {
import { createOklchToRGBA } from "./oklch";
import { throttle } from "../timing";
import { serdeBool } from "../serde";
import { stringToId } from "../format";
import { style } from "../elements";
/**
* @typedef {Object} Valued
@@ -50,7 +52,7 @@ import { serdeBool } from "../serde";
* @typedef {_BaselineData<number>} BaselineData
* @typedef {_HistogramData<number>} HistogramData
*
* @typedef {function({ iseries: ISeries; unit: Unit; index: Index }): void} SetDataCallback
* @typedef {function({ iseries: ISeries; unit: Unit; index: IndexName }): void} SetDataCallback
*/
const oklchToRGBA = createOklchToRGBA();
@@ -63,10 +65,8 @@ const lineWidth = /** @type {any} */ (1.5);
* @param {HTMLElement} args.parent
* @param {Signals} args.signals
* @param {Colors} args.colors
* @param {Utilities} args.utils
* @param {Elements} args.elements
* @param {VecsResources} args.vecsResources
* @param {Accessor<Index>} args.index
* @param {Resources} args.resources
* @param {Accessor<IndexName>} args.index
* @param {((unknownTimeScaleCallback: VoidFunction) => void)} [args.timeScaleSetCallback]
* @param {true} [args.fitContent]
* @param {{unit: Unit; blueprints: AnySeriesBlueprint[]}[]} [args.config]
@@ -75,11 +75,9 @@ function createChartElement({
parent,
signals,
colors,
utils,
elements,
id: chartId,
index,
vecsResources,
resources,
timeScaleSetCallback,
fitContent,
config,
@@ -88,20 +86,14 @@ function createChartElement({
div.classList.add("chart");
parent.append(div);
const legendTop = createLegend({
utils,
signals,
});
const legendTop = createLegend(signals);
div.append(legendTop.element);
const chartDiv = window.document.createElement("div");
chartDiv.classList.add("lightweight-chart");
div.append(chartDiv);
const legendBottom = createLegend({
utils,
signals,
});
const legendBottom = createLegend(signals);
div.append(legendBottom.element);
/** @type {IChartApi} */
@@ -110,7 +102,7 @@ function createChartElement({
/** @satisfies {DeepPartial<ChartOptions>} */ ({
autoSize: true,
layout: {
fontFamily: elements.style.fontFamily,
fontFamily: style.fontFamily,
background: { color: "transparent" },
attributionLogo: false,
colorSpace: "display-p3",
@@ -185,24 +177,24 @@ function createChartElement({
signals.createEffect(index, (index) => {
const minBarSpacing =
index === /** @satisfies {MonthIndex} */ (7)
index === "monthindex"
? 1
: index === /** @satisfies {QuarterIndex} */ (19)
: index === "quarterindex"
? 2
: index === /** @satisfies {SemesterIndex} */ (20)
: index === "semesterindex"
? 3
: index === /** @satisfies {YearIndex} */ (24)
: index === "yearindex"
? 6
: index === /** @satisfies {DecadeIndex} */ (1)
: index === "decadeindex"
? 60
: 0.5;
ichart.applyOptions({
timeScale: {
timeVisible:
index === /** @satisfies {Height} */ (5) ||
index === /** @satisfies {DifficultyEpoch} */ (2) ||
index === /** @satisfies {HalvingEpoch} */ (4),
index === "height" ||
index === "difficultyepoch" ||
index === "halvingepoch",
...(!fitContent
? {
minBarSpacing,
@@ -212,7 +204,7 @@ function createChartElement({
});
});
const activeResources = /** @type {Set<VecResource>} */ (new Set());
const activeResources = /** @type {Set<MetricResource>} */ (new Set());
ichart.subscribeCrosshairMove(
throttle(() => {
activeResources.forEach((v) => {
@@ -292,7 +284,7 @@ function createChartElement({
createChild(pane) {
const { field, selected } = createHorizontalChoiceField({
choices: /** @type {const} */ (["lin", "log"]),
id: utils.stringToId(`${id} ${paneIndex} ${unit}`),
id: stringToId(`${id} ${paneIndex} ${unit}`),
defaultValue:
unit === "usd" && seriesType !== "Baseline" ? "log" : "lin",
key: `${id}-price-scale-${paneIndex}`,
@@ -340,7 +332,7 @@ function createChartElement({
data,
}) {
return signals.createRoot((dispose) => {
const id = `${utils.stringToId(name)}-${paneIndex}`;
const id = `${stringToId(name)}-${paneIndex}`;
const active = signals.createSignal(defaultActive ?? true, {
save: {
@@ -361,7 +353,7 @@ function createChartElement({
iseries.setSeriesOrder(order);
/** @type {VecResource | undefined} */
/** @type {MetricResource | undefined} */
let _valuesResource;
/** @type {Series} */
@@ -383,15 +375,13 @@ function createChartElement({
if (metric) {
signals.createEffect(index, (index) => {
const timeResource = vecsResources.getOrCreate(
const timeResource = resources.metrics.getOrCreate(
index === "height" ? "timestamp_fixed" : "timestamp",
index,
index === /** @satisfies {Height} */ (5)
? "timestamp_fixed"
: "timestamp",
);
timeResource.fetch();
const valuesResource = vecsResources.getOrCreate(index, metric);
const valuesResource = resources.metrics.getOrCreate(metric, index);
_valuesResource = valuesResource;
series.url.set(() => valuesResource.url);
@@ -401,11 +391,11 @@ function createChartElement({
valuesResource.fetch();
activeResources.add(valuesResource);
const fetchedKey = vecsResources.defaultFetchedKey;
const fetchedKey = resources.metrics.genKey();
signals.createEffect(
() => ({
_indexes: timeResource.fetched().get(fetchedKey)?.vec(),
values: valuesResource.fetched().get(fetchedKey)?.vec(),
_indexes: timeResource.fetched().get(fetchedKey)?.data(),
values: valuesResource.fetched().get(fetchedKey)?.data(),
}),
({ _indexes, values }) => {
if (!_indexes?.length || !values?.length) return;
@@ -477,10 +467,10 @@ function createChartElement({
timeScaleSetCallback?.(() => {
if (
index === /** @satisfies {QuarterIndex} */ (19) ||
index === /** @satisfies {SemesterIndex} */ (20) ||
index === /** @satisfies {YearIndex} */ (24) ||
index === /** @satisfies {DecadeIndex} */ (1)
index === "quarterindex" ||
index === "semesterindex" ||
index === "yearindex" ||
index === "decadeindex"
) {
ichart.timeScale().setVisibleLogicalRange({
from: -1,
@@ -836,11 +826,9 @@ function createChartElement({
}
/**
* @param {Object} args
* @param {Signals} args.signals
* @param {Utilities} args.utils
* @param {Signals} signals
*/
function createLegend({ signals, utils }) {
function createLegend(signals) {
const element = window.document.createElement("legend");
const hovered = signals.createSignal(/** @type {Series | null} */ (null));
@@ -874,8 +862,8 @@ function createLegend({ signals, utils }) {
legends[order] = div;
const { input, label } = createLabeledInput({
inputId: utils.stringToId(`legend-${series.id}`),
inputName: utils.stringToId(`selected-${series.id}`),
inputId: stringToId(`legend-${series.id}`),
inputName: stringToId(`selected-${series.id}`),
inputValue: "value",
title: "Click to toggle",
inputChecked: series.active(),
+22 -19
View File
@@ -1,21 +1,24 @@
import { getElementById } from "./dom";
export default {
head: window.document.getElementsByTagName("head")[0],
body: window.document.body,
main: getElementById("main"),
aside: getElementById("aside"),
asideLabel: getElementById("aside-selector-label"),
navLabel: getElementById(`nav-selector-label`),
searchLabel: getElementById(`search-selector-label`),
search: getElementById("search"),
nav: getElementById("nav"),
searchInput: /** @type {HTMLInputElement} */ (getElementById("search-input")),
searchResults: getElementById("search-results"),
selectors: getElementById("frame-selectors"),
style: getComputedStyle(window.document.documentElement),
charts: getElementById("charts"),
table: getElementById("table"),
explorer: getElementById("explorer"),
simulation: getElementById("simulation"),
};
export const style = getComputedStyle(window.document.documentElement);
export const headElement = window.document.getElementsByTagName("head")[0];
export const bodyElement = window.document.body;
export const mainElement = getElementById("main");
export const asideElement = getElementById("aside");
export const searchElement = getElementById("search");
export const navElement = getElementById("nav");
export const chartElement = getElementById("chart");
export const tableElement = getElementById("table");
export const explorerElement = getElementById("explorer");
export const simulationElement = getElementById("simulation");
export const asideLabelElement = getElementById("aside-selector-label");
export const navLabelElement = getElementById(`nav-selector-label`);
export const searchLabelElement = getElementById(`search-selector-label`);
export const searchInput = /** @type {HTMLInputElement} */ (
getElementById("search-input")
);
export const searchResultsElement = getElementById("search-results");
export const frameSelectorsElement = getElementById("frame-selectors");
+11 -23
View File
@@ -1,24 +1,12 @@
const localhost = window.location.hostname === "localhost";
const standalone =
export const localhost = window.location.hostname === "localhost";
export const standalone =
"standalone" in window.navigator && !!window.navigator.standalone;
const userAgent = navigator.userAgent.toLowerCase();
const isChrome = userAgent.includes("chrome");
const safari = userAgent.includes("safari");
const safariOnly = safari && !isChrome;
const macOS = userAgent.includes("mac os");
const iphone = userAgent.includes("iphone");
const ipad = userAgent.includes("ipad");
const ios = iphone || ipad;
export default {
standalone,
userAgent,
isChrome,
safari,
safariOnly,
macOS,
iphone,
ipad,
ios,
localhost,
};
export const userAgent = navigator.userAgent.toLowerCase();
export const isChrome = userAgent.includes("chrome");
export const safari = userAgent.includes("safari");
export const safariOnly = safari && !isChrome;
export const macOS = userAgent.includes("mac os");
export const iphone = userAgent.includes("iphone");
export const ipad = userAgent.includes("ipad");
export const ios = iphone || ipad;
export const canShare = "canShare" in navigator;
@@ -3,7 +3,7 @@
* @param {number} [digits]
* @param {Intl.NumberFormatOptions} [options]
*/
function numberToUSFormat(value, digits, options) {
export function numberToUSNumber(value, digits, options) {
return value.toLocaleString("en-us", {
...options,
minimumFractionDigits: digits,
@@ -11,23 +11,18 @@ function numberToUSFormat(value, digits, options) {
});
}
export const locale = {
numberToUSFormat,
};
export const numberToDollars = new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD",
minimumFractionDigits: 2,
maximumFractionDigits: 2,
});
export const formatters = {
dollars: new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD",
minimumFractionDigits: 2,
maximumFractionDigits: 2,
}),
percentage: new Intl.NumberFormat("en-US", {
style: "percent",
minimumFractionDigits: 2,
maximumFractionDigits: 2,
}),
};
export const numberToPercentage = new Intl.NumberFormat("en-US", {
style: "percent",
minimumFractionDigits: 2,
maximumFractionDigits: 2,
});
/**
* @param {string} s
+8 -19
View File
@@ -7,26 +7,16 @@ import {
import { serdeUnit } from "../serde";
import { pushHistory, resetParams } from "../url";
import { readStored, writeToStorage } from "../storage";
import { stringToId } from "../format";
/**
* @param {Object} args
* @param {Colors} args.colors
* @param {Signals} args.signals
* @param {Env} args.env
* @param {Utilities} args.utils
* @param {MetricToIndexes} args.metricToIndexes
* @param {Pools} args.pools
* @param {BRK} args.brk
* @param {Signal<string | null>} args.qrcode
*/
export function initOptions({
colors,
signals,
env,
utils,
qrcode,
metricToIndexes,
pools,
}) {
export function initOptions({ colors, signals, brk, qrcode }) {
const LS_SELECTED_KEY = `selected_path`;
const urlPath_ = window.document.location.pathname
@@ -42,10 +32,8 @@ export function initOptions({
const selected = signals.createSignal(/** @type {any} */ (undefined));
const partialOptions = createPartialOptions({
env,
colors,
metricToIndexes,
pools,
brk,
});
/** @type {Option[]} */
@@ -58,7 +46,8 @@ export function initOptions({
*/
function arrayToRecord(arr = []) {
return (arr || []).reduce((record, blueprint) => {
if (env.localhost && !(blueprint.metric in metricToIndexes)) {
if (!brk.hasMetric(blueprint.metric)) {
// if (localhost && !brk.hasMetric(blueprint.metric)) {
throw Error(`${blueprint.metric} not recognized`);
}
const unit = blueprint.unit ?? serdeUnit.deserialize(blueprint.metric);
@@ -186,7 +175,7 @@ export function initOptions({
/** @type {HTMLDivElement | HTMLDetailsElement | null} */ (null),
);
const serName = utils.stringToId(anyPartial.name);
const serName = stringToId(anyPartial.name);
const path = [...parentPath, serName];
const childOptionsCount = recursiveProcessPartialTree(
anyPartial.tree,
@@ -250,7 +239,7 @@ export function initOptions({
const option = /** @type {Option} */ (anyPartial);
const name = option.name;
const path = [...parentPath, utils.stringToId(option.name)];
const path = [...parentPath, stringToId(option.name)];
if ("kind" in anyPartial && anyPartial.kind === "explorer") {
Object.assign(
+194 -265
View File
@@ -1,5 +1,3 @@
// @ts_check
/**
* @typedef {Object} BaseSeriesBlueprint
* @property {string} title
@@ -119,70 +117,15 @@
*
*/
import { localhost } from "../env";
/**
* @param {Object} args
* @param {Env} args.env
* @param {Colors} args.colors
* @param {MetricToIndexes} args.metricToIndexes
* @param {Pools} args.pools
* @param {BRK} args.brk
* @returns {PartialOptionsTree}
*/
export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
/**
* @template {string} S
* @typedef {Extract<Metric, `${S}${string}`>} StartsWith
*/
/**
* @template {string} S
* @typedef {Extract<Metric, `${string}${S}`>} EndsWith
*/
/**
* @template {string} K
* @template {string} S
* @typedef {K extends `${S}${infer Rest}` ? Rest : never} WithoutPrefix
*/
/**
* @template {string} K
* @template {string} S
* @typedef {K extends `${infer Rest}${S}` ? Rest : never} WithoutSuffix
*/
/**
* @template {string} K
* @template {string} S
* @typedef {K extends `${infer _Prefix}${S}${infer _Suffix}` ? never : K} ExcludeSubstring
*/
/**
* @typedef {"cumulative_"} CumulativePrefix
* @typedef {"_30d_delta"} _30DChageSubString
* @typedef {StartsWith<CumulativePrefix>} CumulativeMetric
* @typedef {ExcludeSubstring<WithoutPrefix<CumulativeMetric, CumulativePrefix>, _30DChageSubString>} CumulativeMetricBase
* @typedef {"_avg"} AverageSuffix
* @typedef {EndsWith<AverageSuffix>} MetricAverage
* @typedef {WithoutSuffix<MetricAverage, AverageSuffix>} MetricAverageBase
* @typedef {"_median"} MedianSuffix
* @typedef {EndsWith<MedianSuffix>} MetricMedian
* @typedef {WithoutSuffix<MetricMedian, MedianSuffix>} MetricMedianBase
* @typedef {"_pct90"} _pct90Suffix
* @typedef {EndsWith<_pct90Suffix>} MetricPct90
* @typedef {WithoutSuffix<MetricPct90, _pct90Suffix>} MetricPct90Base
* @typedef {"_pct75"} _pct75Suffix
* @typedef {EndsWith<_pct75Suffix>} MetricPct75
* @typedef {WithoutSuffix<MetricPct75, _pct75Suffix>} MetricPct75Base
* @typedef {"_pct25"} _pct25Suffix
* @typedef {EndsWith<_pct25Suffix>} MetricPct25
* @typedef {WithoutSuffix<MetricPct25, _pct25Suffix>} MetricPct25Base
* @typedef {"_pct10"} _pct10Suffix
* @typedef {EndsWith<_pct10Suffix>} MetricPct10
* @typedef {WithoutSuffix<MetricPct10, _pct10Suffix>} MetricPct10Base
* @typedef {"_max"} MaxSuffix
* @typedef {EndsWith<MaxSuffix>} MetricMax
* @typedef {WithoutSuffix<MetricMax, MaxSuffix>} MetricMaxBase
* @typedef {"_min"} MinSuffix
* @typedef {EndsWith<MinSuffix>} MetricMin
* @typedef {WithoutSuffix<MetricMin, MinSuffix>} MetricMinBase
*/
export function createPartialOptions({ colors, brk }) {
/**
* @param {string} id
* @param {boolean} compoundAdjective
@@ -681,7 +624,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
}
/**
* @param {MetricAverageBase} metric
* @param {Metric} metric
*/
function createAverageSeries(metric) {
return /** @satisfies {AnyFetchedSeriesBlueprint} */ ({
@@ -692,7 +635,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
/**
* @param {Object} args
* @param {CumulativeMetricBase} args.metric
* @param {Metric} args.metric
* @param {Color} [args.sumColor]
* @param {Color} [args.cumulativeColor]
* @param {string} [args.common]
@@ -719,14 +662,14 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
/**
* @param {Object} args
* @param {CumulativeMetricBase} args.metric
* @param {Metric} args.metric
* @param {string} [args.title]
* @param {Color} [args.color]
*/
function createSumSeries({ metric, title = "", color }) {
const metric_sum = `${metric}_sum`;
return /** @satisfies {AnyFetchedSeriesBlueprint} */ ({
metric: metric_sum in metricToIndexes ? metric_sum : metric,
metric: brk.hasMetric(metric_sum) ? metric_sum : metric,
title: `Sum ${title}`,
color: color ?? colors.red,
});
@@ -734,7 +677,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
/**
* @param {Object} args
* @param {CumulativeMetricBase} args.metric
* @param {Metric} args.metric
* @param {string} [args.title]
* @param {Color} [args.color]
*/
@@ -748,7 +691,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
}
/**
* @param {MetricMinBase & MetricMaxBase & MetricPct90Base & MetricPct75Base & MetricMedianBase & MetricPct25Base & MetricPct10Base} metric
* @param {Metric} metric
*/
function createMinMaxPercentilesSeries(metric) {
return /** @satisfies {AnyFetchedSeriesBlueprint[]} */ ([
@@ -798,7 +741,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
}
/**
* @param {MetricAverageBase & CumulativeMetricBase & MetricMinBase & MetricMaxBase & MetricPct90Base & MetricPct75Base & MetricMedianBase & MetricPct25Base & MetricPct10Base} metric
* @param {Metric} metric
*/
function createSumCumulativeMinMaxPercentilesSeries(metric) {
return [
@@ -808,7 +751,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
}
/**
* @param {MetricAverageBase & CumulativeMetricBase & MetricMinBase & MetricMaxBase & MetricPct90Base & MetricPct75Base & MetricMedianBase & MetricPct25Base & MetricPct10Base} metric
* @param {Metric} metric
*/
function createAverageSumCumulativeMinMaxPercentilesSeries(metric) {
return [
@@ -819,7 +762,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
/**
* @param {Object} args
* @param {Metric & MetricAverageBase & CumulativeMetricBase & MetricMinBase & MetricMaxBase & MetricPct90Base & MetricPct75Base & MetricMedianBase & MetricPct25Base & MetricPct10Base} args.metric
* @param {Metric} args.metric
* @param {string} args.name
*/
function createBaseAverageSumCumulativeMinMaxPercentilesSeries({
@@ -835,12 +778,6 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
];
}
/**
* @typedef {"_ratio_zscore"} RatioZScoreCapSuffix
* @typedef {EndsWith<RatioZScoreCapSuffix>} MetricRatioZScoreCap
* @typedef {WithoutSuffix<MetricRatioZScoreCap, RatioZScoreCapSuffix>} MetricRatioZScoreCapBase
*/
const percentiles = [
{
name: "pct1",
@@ -906,7 +843,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
* @param {string} args.name
* @param {string} args.legend
* @param {string} args.title
* @param {MetricRatioZScoreCapBase} args.metric
* @param {Metric} args.metric
* @param {Color} [args.color]
*/
function createPriceWithRatioOptions({ name, title, legend, metric, color }) {
@@ -931,7 +868,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
name: legend,
color,
}),
...(`${metric}_ratio_p1sd_usd` in metricToIndexes
...(brk.hasMetric(`${metric}_ratio_p1sd_usd`)
? percentiles.map(({ name, color }) =>
createBaseSeries({
metric: `${metric}_ratio_${name}_usd`,
@@ -954,7 +891,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
baseValue: { price: 1 },
},
}),
...(`${metric}_ratio_p1sd` in metricToIndexes
...(brk.hasMetric(`${metric}_ratio_p1sd`)
? percentiles.map(({ name, color }) =>
createBaseSeries({
metric: `${metric}_ratio_${name}`,
@@ -967,7 +904,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
}),
)
: []),
...(`${metric}_ratio_sma` in metricToIndexes
...(brk.hasMetric(`${metric}_ratio_sma`)
? ratioAverages.map(({ name, metric: metricAddon, color }) =>
createBaseSeries({
metric: `${metric}_ratio_${metricAddon}`,
@@ -986,7 +923,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
}),
],
},
...(`${metric}_ratio_zscore` in metricToIndexes
...(brk.hasMetric(`${metric}_ratio_zscore`)
? [
{
name: "ZScores",
@@ -1178,18 +1115,12 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
];
}
/**
* @typedef {"_supply_in_profit"} SupplyInProfitSuffix
* @typedef {EndsWith<SupplyInProfitSuffix>} MetricSupplyInProfit
* @typedef {WithoutSuffix<MetricSupplyInProfit, SupplyInProfitSuffix>} CohortId
*/
/**
* @typedef {Object} UTXOGroupObject
* @property {string} args.name
* @property {string} args.title
* @property {Color} args.color
* @property {"" | CohortId} args.id
* @property {string} args.id
*/
/**
@@ -1200,13 +1131,11 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
*/
/**
* @template {"" | CohortId} T
* @param {T} id
* @param {string} id
*/
const fixId = (id) =>
id !== ""
? /** @type {Exclude<"" | `${T}_`, "_">} */ (`${id}_`)
: /** @type {const} */ ("");
function fixId(id) {
return id !== "" ? `${id}_` : "";
}
/**
* @param {UTXOGroupObject | UTXOGroupsObject} args
@@ -1465,11 +1394,11 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
]);
}),
},
...(list.filter(({ id }) => `${fixId(id)}addr_count` in metricToIndexes)
...(list.filter(({ id }) => brk.hasMetric(`${fixId(id)}addr_count`))
.length > ("list" in args ? 1 : 0)
? !("list" in args) ||
list.filter(
({ id }) => `${fixId(id)}empty_addr_count` in metricToIndexes,
list.filter(({ id }) =>
brk.hasMetric(`${fixId(id)}empty_addr_count`),
).length <= 1
? [
{
@@ -1478,7 +1407,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
bottom: list.flatMap(({ name, color, id: _id }) => {
const id = fixId(_id);
return [
...(`${id}addr_count` in metricToIndexes
...(brk.hasMetric(`${id}addr_count`)
? /** @type {const} */ ([
createBaseSeries({
metric: `${id}addr_count`,
@@ -1487,7 +1416,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
}),
])
: []),
...(`${id}empty_addr_count` in metricToIndexes
...(brk.hasMetric(`${id}empty_addr_count`)
? /** @type {const} */ ([
createBaseSeries({
metric: `${id}empty_addr_count`,
@@ -1509,9 +1438,8 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
name: "loaded",
title: `Loaded Address Count ${title}`,
bottom: list
.filter(
({ id }) =>
`${fixId(id)}addr_count` in metricToIndexes,
.filter(({ id }) =>
brk.hasMetric(`${fixId(id)}addr_count`),
)
.flatMap(({ name, color, id: _id }) => {
const id = fixId(_id);
@@ -1524,19 +1452,16 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
];
}),
},
...(list.filter(
({ id }) =>
`${fixId(id)}empty_addr_count` in metricToIndexes,
...(list.filter(({ id }) =>
brk.hasMetric(`${fixId(id)}empty_addr_count`),
).length
? [
{
name: "empty",
title: `Empty Address Count ${title}`,
bottom: list
.filter(
({ id }) =>
`${fixId(id)}empty_addr_count` in
metricToIndexes,
.filter(({ id }) =>
brk.hasMetric(`${fixId(id)}empty_addr_count`),
)
.flatMap(({ name, color, id: _id }) => {
const id = fixId(_id);
@@ -1622,7 +1547,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
]
: []),
...(!("list" in args) &&
`${id}realized_cap_rel_to_own_market_cap` in metricToIndexes
brk.hasMetric(`${id}realized_cap_rel_to_own_market_cap`)
? [
/** @satisfies {FetchedBaselineSeriesBlueprint} */ ({
type: "Baseline",
@@ -1658,8 +1583,9 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
color: colors.red,
defaultActive: false,
}),
...(`${fixId(args.id)}realized_profit_to_loss_ratio` in
metricToIndexes
...(brk.hasMetric(
`${fixId(args.id)}realized_profit_to_loss_ratio`,
)
? [
createBaseSeries({
metric: `${fixId(
@@ -1797,7 +1723,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
},
},
}),
...(asoprKey in metricToIndexes
...(brk.hasMetric(asoprKey)
? [
/** @satisfies {FetchedBaselineSeriesBlueprint} */ ({
type: "Baseline",
@@ -1825,7 +1751,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
},
},
}),
...(asoprKey in metricToIndexes
...(brk.hasMetric(asoprKey)
? [
/** @satisfies {FetchedBaselineSeriesBlueprint} */ ({
type: "Baseline",
@@ -1853,7 +1779,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
},
},
}),
...(asoprKey in metricToIndexes
...(brk.hasMetric(asoprKey)
? [
/** @satisfies {FetchedBaselineSeriesBlueprint} */ ({
type: "Baseline",
@@ -1940,8 +1866,9 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
name,
color,
}),
...(`${id}realized_profit_to_loss_ratio` in
metricToIndexes
...(brk.hasMetric(
`${id}realized_profit_to_loss_ratio`,
)
? [
createBaseSeries({
metric: `${id}realized_profit_to_loss_ratio`,
@@ -2102,7 +2029,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
name,
metric: `${fixId(id)}adjusted_sopr`,
}))
.filter(({ metric }) => metric in metricToIndexes);
.filter(({ metric }) => brk.hasMetric(metric));
return reducedList.length
? [
@@ -2180,7 +2107,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
name: "normal",
color: colors.emerald,
}),
...(adjKey in metricToIndexes
...(brk.hasMetric(adjKey)
? [
createBaseSeries({
metric: adjKey,
@@ -2204,7 +2131,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
name: "normal",
color: colors.red,
}),
...(adjKey in metricToIndexes
...(brk.hasMetric(adjKey)
? [
createBaseSeries({
metric: adjKey,
@@ -2239,9 +2166,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
name,
metric: `${fixId(id)}adjusted_value_created`,
}))
.filter(
({ metric }) => metric in metricToIndexes,
);
.filter(({ metric }) => brk.hasMetric(metric));
return reducedList.length
? [
{
@@ -2282,9 +2207,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
name,
metric: `${fixId(id)}adjusted_value_destroyed`,
}))
.filter(
({ metric }) => metric in metricToIndexes,
);
.filter(({ metric }) => brk.hasMetric(metric));
return reducedList.length
? [
{
@@ -2361,10 +2284,11 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
name: "Negative Loss",
color: colors.red,
}),
...(`${fixId(
args.id,
)}unrealized_profit_rel_to_own_market_cap` in
metricToIndexes
...(brk.hasMetric(
`${fixId(
args.id,
)}unrealized_profit_rel_to_own_market_cap`,
)
? [
createBaseSeries({
metric: `${fixId(
@@ -2397,10 +2321,11 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
}),
]
: []),
...(`${fixId(
args.id,
)}unrealized_profit_rel_to_own_total_unrealized_pnl` in
metricToIndexes
...(brk.hasMetric(
`${fixId(
args.id,
)}unrealized_profit_rel_to_own_total_unrealized_pnl`,
)
? [
createBaseSeries({
metric: `${fixId(
@@ -2505,8 +2430,9 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
title: useGroupName ? name : "Net",
color: useGroupName ? color : undefined,
}),
...(`${fixId(id)}net_unrealized_pnl_rel_to_own_market_cap` in
metricToIndexes
...(brk.hasMetric(
`${fixId(id)}net_unrealized_pnl_rel_to_own_market_cap`,
)
? [
/** @satisfies {FetchedBaselineSeriesBlueprint} */ ({
type: "Baseline",
@@ -2521,10 +2447,11 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
}),
]
: []),
...(`${fixId(
id,
)}net_unrealized_pnl_rel_to_own_total_unrealized_pnl` in
metricToIndexes
...(brk.hasMetric(
`${fixId(
id,
)}net_unrealized_pnl_rel_to_own_total_unrealized_pnl`,
)
? [
/** @satisfies {FetchedBaselineSeriesBlueprint} */ ({
type: "Baseline",
@@ -2660,7 +2587,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
}
return [
...(env.localhost
...(localhost
? /** @type {const} */ ([
{
name: "Explorer",
@@ -2792,7 +2719,7 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
createPriceLine({
unit: "percentage",
}),
...(cagr in metricToIndexes
...(brk.hasMetric(cagr)
? [
/** @satisfies {FetchedBaselineSeriesBlueprint} */ ({
metric: cagr,
@@ -3652,133 +3579,135 @@ export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
},
{
name: "Pools",
tree: Object.entries(pools).map(([_id, name]) => {
const id = /** @type {Pool} */ (_id);
return {
name,
tree: [
{
name: "Dominance",
title: `Mining Dominance of ${name}`,
bottom: [
createBaseSeries({
metric: `${id}_1d_dominance`,
name: "1d",
color: colors.rose,
defaultActive: false,
}),
createBaseSeries({
metric: `${id}_1w_dominance`,
name: "1w",
color: colors.red,
defaultActive: false,
}),
createBaseSeries({
metric: `${id}_1m_dominance`,
name: "1m",
}),
createBaseSeries({
metric: `${id}_1y_dominance`,
name: "1y",
color: colors.lime,
defaultActive: false,
}),
createBaseSeries({
metric: `${id}_dominance`,
name: "all time",
color: colors.teal,
defaultActive: false,
}),
],
},
{
name: "Blocks mined",
title: `Blocks mined by ${name}`,
bottom: [
createBaseSeries({
metric: `${id}_blocks_mined`,
name: "Sum",
}),
createBaseSeries({
metric: `${id}_blocks_mined_cumulative`,
name: "Cumulative",
color: colors.blue,
}),
createBaseSeries({
metric: `${id}_1w_blocks_mined`,
name: "1w Sum",
color: colors.red,
defaultActive: false,
}),
createBaseSeries({
metric: `${id}_1m_blocks_mined`,
name: "1m Sum",
color: colors.pink,
defaultActive: false,
}),
createBaseSeries({
metric: `${id}_1y_blocks_mined`,
name: "1y Sum",
color: colors.purple,
defaultActive: false,
}),
],
},
{
name: "Rewards",
title: `Rewards collected by ${name}`,
bottom: [
{
metricAddon: "coinbase",
cumulativeColor: colors.red,
sumColor: colors.orange,
},
{
metricAddon: "subsidy",
cumulativeColor: colors.emerald,
sumColor: colors.lime,
},
{
metricAddon: "fee",
cumulativeColor: colors.indigo,
sumColor: colors.cyan,
},
].flatMap(
({ metricAddon, sumColor, cumulativeColor }) => [
...createSumCumulativeSeries({
metric: `${id}_${metricAddon}`,
common: metricAddon,
sumColor,
cumulativeColor,
tree: Object.entries(brk.POOL_ID_TO_POOL_NAME).map(
([_id, name]) => {
const id = /** @type {PoolId} */ (_id);
return {
name,
tree: [
{
name: "Dominance",
title: `Mining Dominance of ${name}`,
bottom: [
createBaseSeries({
metric: `${id}_1d_dominance`,
name: "1d",
color: colors.rose,
defaultActive: false,
}),
...createSumCumulativeSeries({
metric: `${id}_${metricAddon}_btc`,
common: metricAddon,
sumColor,
cumulativeColor,
createBaseSeries({
metric: `${id}_1w_dominance`,
name: "1w",
color: colors.red,
defaultActive: false,
}),
...createSumCumulativeSeries({
metric: `${id}_${metricAddon}_usd`,
common: metricAddon,
sumColor,
cumulativeColor,
createBaseSeries({
metric: `${id}_1m_dominance`,
name: "1m",
}),
createBaseSeries({
metric: `${id}_1y_dominance`,
name: "1y",
color: colors.lime,
defaultActive: false,
}),
createBaseSeries({
metric: `${id}_dominance`,
name: "all time",
color: colors.teal,
defaultActive: false,
}),
],
),
},
{
name: "Days since block",
title: `Days since ${name} mined a block`,
bottom: [
createBaseSeries({
metric: `${id}_days_since_block`,
name: "Since block",
}),
],
},
],
};
}),
},
{
name: "Blocks mined",
title: `Blocks mined by ${name}`,
bottom: [
createBaseSeries({
metric: `${id}_blocks_mined`,
name: "Sum",
}),
createBaseSeries({
metric: `${id}_blocks_mined_cumulative`,
name: "Cumulative",
color: colors.blue,
}),
createBaseSeries({
metric: `${id}_1w_blocks_mined`,
name: "1w Sum",
color: colors.red,
defaultActive: false,
}),
createBaseSeries({
metric: `${id}_1m_blocks_mined`,
name: "1m Sum",
color: colors.pink,
defaultActive: false,
}),
createBaseSeries({
metric: `${id}_1y_blocks_mined`,
name: "1y Sum",
color: colors.purple,
defaultActive: false,
}),
],
},
{
name: "Rewards",
title: `Rewards collected by ${name}`,
bottom: [
{
metricAddon: "coinbase",
cumulativeColor: colors.red,
sumColor: colors.orange,
},
{
metricAddon: "subsidy",
cumulativeColor: colors.emerald,
sumColor: colors.lime,
},
{
metricAddon: "fee",
cumulativeColor: colors.indigo,
sumColor: colors.cyan,
},
].flatMap(
({ metricAddon, sumColor, cumulativeColor }) => [
...createSumCumulativeSeries({
metric: `${id}_${metricAddon}`,
common: metricAddon,
sumColor,
cumulativeColor,
}),
...createSumCumulativeSeries({
metric: `${id}_${metricAddon}_btc`,
common: metricAddon,
sumColor,
cumulativeColor,
}),
...createSumCumulativeSeries({
metric: `${id}_${metricAddon}_usd`,
common: metricAddon,
sumColor,
cumulativeColor,
}),
],
),
},
{
name: "Days since block",
title: `Days since ${name} mined a block`,
bottom: [
createBaseSeries({
metric: `${id}_days_since_block`,
name: "Since block",
}),
],
},
],
};
},
),
},
],
},
+22 -24
View File
@@ -113,59 +113,57 @@ export const serdeBool = {
export const serdeChartableIndex = {
/**
* @param {number} v
* @returns {SerializedChartableIndex | null}
* @param {IndexName} v
* @returns {ChartableIndexName | null}
*/
serialize(v) {
switch (v) {
case /** @satisfies {DateIndex} */ (0):
case "dateindex":
return "date";
case /** @satisfies {DecadeIndex} */ (1):
case "decadeindex":
return "decade";
case /** @satisfies {DifficultyEpoch} */ (2):
case "difficultyepoch":
return "epoch";
// case /** @satisfies {HalvingEpoch} */ (4):
// return "halving";
case /** @satisfies {Height} */ (5):
case "height":
return "timestamp";
case /** @satisfies {MonthIndex} */ (7):
case "monthindex":
return "month";
case /** @satisfies {QuarterIndex} */ (19):
case "quarterindex":
return "quarter";
case /** @satisfies {SemesterIndex} */ (20):
case "semesterindex":
return "semester";
case /** @satisfies {WeekIndex} */ (23):
case "weekindex":
return "week";
case /** @satisfies {YearIndex} */ (24):
case "yearindex":
return "year";
default:
return null;
}
},
/**
* @param {SerializedChartableIndex} v
* @returns {Index}
* @param {ChartableIndexName} v
* @returns {IndexName}
*/
deserialize(v) {
switch (v) {
case "timestamp":
return /** @satisfies {Height} */ (5);
return "height";
case "date":
return /** @satisfies {DateIndex} */ (0);
return "dateindex";
case "week":
return /** @satisfies {WeekIndex} */ (23);
return "weekindex";
case "epoch":
return /** @satisfies {DifficultyEpoch} */ (2);
return "difficultyepoch";
case "month":
return /** @satisfies {MonthIndex} */ (7);
return "monthindex";
case "quarter":
return /** @satisfies {QuarterIndex} */ (19);
return "quarterindex";
case "semester":
return /** @satisfies {SemesterIndex} */ (20);
return "semesterindex";
case "year":
return /** @satisfies {YearIndex} */ (24);
return "yearindex";
case "decade":
return /** @satisfies {DecadeIndex} */ (1);
return "decadeindex";
default:
throw Error("todo");
}