bitview: reorg part 3

This commit is contained in:
nym21
2025-09-24 00:35:32 +02:00
parent d45686128e
commit 2279aa8f18
18 changed files with 480 additions and 445 deletions
+26 -26
View File
@@ -1,13 +1,13 @@
import { index as serdeIndex } from "./serde";
import { runWhenIdle } from "./scheduling";
import { serdeIndex } from "./serde";
import { runWhenIdle } from "./timing";
/**
* @param {Signals} signals
* @param {Utilities} utils
* @param {Env} env
* @param {VecIdToIndexes} vecIdToIndexes
* @param {MetricToIndexes} metricToIndexes
*/
export function createVecsResources(signals, utils, env, vecIdToIndexes) {
export function createVecsResources(signals, utils, env, metricToIndexes) {
const owner = signals.getOwner();
const defaultFrom = -10_000;
@@ -31,11 +31,11 @@ export function createVecsResources(signals, utils, env, vecIdToIndexes) {
/**
* @template {number | OHLCTuple} [T=number]
* @param {Index} index
* @param {VecId} id
* @param {Metric} metric
*/
function createVecResource(index, id) {
if (env.localhost && !(id in vecIdToIndexes)) {
throw Error(`${id} not recognized`);
function createVecResource(index, metric) {
if (env.localhost && !(metric in metricToIndexes)) {
throw Error(`${metric} not recognized`);
}
return signals.runWithOwner(owner, () => {
@@ -48,7 +48,7 @@ export function createVecsResources(signals, utils, env, vecIdToIndexes) {
);
return {
url: api.genUrl(index, id, defaultFrom),
url: api.genUrl(index, metric, defaultFrom),
fetched: fetchedRecord,
/**
* Defaults
@@ -92,7 +92,7 @@ export function createVecsResources(signals, utils, env, vecIdToIndexes) {
}
},
index,
id,
metric,
from,
to,
)
@@ -112,16 +112,16 @@ export function createVecsResources(signals, utils, env, vecIdToIndexes) {
/**
* @template {number | OHLCTuple} [T=number]
* @param {Index} index
* @param {VecId} id
* @param {Metric} metric
*/
getOrCreate(index, id) {
const key = `${index},${id}`;
getOrCreate(index, metric) {
const key = `${index},${metric}`;
const found = map.get(key);
if (found) {
return found;
}
const vec = createVecResource(index, id);
const vec = createVecResource(index, metric);
if (!vec) throw Error("vec is undefined");
map.set(key, /** @type {any} */ (vec));
return vec;
@@ -230,12 +230,12 @@ async function fetchApi(callback, path, mustBeArray) {
/**
* @param {Index} index
* @param {VecId} vecId
* @param {Metric} metric
* @param {number} [from]
* @param {number} [to]
*/
function genPath(index, vecId, from, to) {
let path = `/${serdeIndex.serialize(index)}-to-${vecId.replaceAll("_", "-")}?`;
function genPath(index, metric, from, to) {
let path = `/${serdeIndex.serialize(index)}-to-${metric.replaceAll("_", "-")}?`;
if (from !== undefined) {
path += `from=${from}`;
@@ -252,30 +252,30 @@ function genPath(index, vecId, from, to) {
export const api = {
/**
* @param {Index} index
* @param {VecId} vecId
* @param {Metric} metric
* @param {number} from
*/
genUrl(index, vecId, from) {
return `${API_VECS_PREFIX}${genPath(index, vecId, from)}`;
genUrl(index, metric, from) {
return `${API_VECS_PREFIX}${genPath(index, metric, from)}`;
},
/**
* @template {number | OHLCTuple} [T=number]
* @param {(v: T[]) => void} callback
* @param {Index} index
* @param {VecId} vecId
* @param {Metric} metric
* @param {number} [from]
* @param {number} [to]
*/
fetchVec(callback, index, vecId, from, to) {
return fetchApi(callback, genPath(index, vecId, from, to), true);
fetchVec(callback, index, metric, from, to) {
return fetchApi(callback, genPath(index, metric, from, to), true);
},
/**
* @template {number | OHLCTuple} [T=number]
* @param {(v: T) => void} callback
* @param {Index} index
* @param {VecId} vecId
* @param {Metric} metric
*/
fetchLast(callback, index, vecId) {
return fetchApi(callback, genPath(index, vecId, -1));
fetchLast(callback, index, metric) {
return fetchApi(callback, genPath(index, metric, -1));
},
};
+1 -1
View File
@@ -15,6 +15,6 @@ export function range(start, end) {
* @template T
* @param {T[]} array
*/
export function random(array) {
export function randomFromArray(array) {
return array[Math.floor(Math.random() * array.length)];
}
+28 -27
View File
@@ -1,12 +1,22 @@
/** @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 {
createChart,
CandlestickSeries,
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";
import {
createHorizontalChoiceField,
createLabeledInput,
createSpanName,
} from "./dom";
import { createOklchToRGBA } from "./colors";
import { throttle } from "./scheduling";
import { throttle } from "./timing";
import { serdeBool } from "./serde";
/**
* @typedef {[number, number, number, number]} OHLCTuple
@@ -45,15 +55,6 @@ import { throttle } from "./scheduling";
* @typedef {function({ iseries: ISeries; unit: Unit; index: Index }): void} SetDataCallback
*/
import {
createChart,
CandlestickSeries,
HistogramSeries,
LineSeries,
BaselineSeries,
// } from "./5.0.8/dist/lightweight-charts.standalone.development.mjs";
} from "../packages/lightweight-charts/5.0.8/dist/lightweight-charts.standalone.production.mjs";
const oklchToRGBA = createOklchToRGBA();
const lineWidth = /** @type {any} */ (1.5);
@@ -321,7 +322,7 @@ function createChartElement({
* @param {number} args.order
* @param {Color[]} args.colors
* @param {SeriesType} args.seriesType
* @param {VecId} [args.vecId]
* @param {Metric} [args.metric]
* @param {SetDataCallback} [args.setDataCallback]
* @param {Accessor<WhitespaceData<number>[]>} [args.data]
* @param {number} args.paneIndex
@@ -329,7 +330,7 @@ function createChartElement({
*/
function addSeries({
iseries,
vecId,
metric,
name,
unit,
order,
@@ -347,7 +348,7 @@ function createChartElement({
save: {
keyPrefix: "",
key: id,
...serde.boolean,
...serdeBool,
},
});
@@ -382,7 +383,7 @@ function createChartElement({
},
};
if (vecId) {
if (metric) {
signals.createEffect(index, (index) => {
const timeResource = vecsResources.getOrCreate(
index,
@@ -392,7 +393,7 @@ function createChartElement({
);
timeResource.fetch();
const valuesResource = vecsResources.getOrCreate(index, vecId);
const valuesResource = vecsResources.getOrCreate(index, metric);
_valuesResource = valuesResource;
series.url.set(() => valuesResource.url);
@@ -556,7 +557,7 @@ function createChartElement({
* @param {string} args.name
* @param {Unit} args.unit
* @param {number} args.order
* @param {VecId} [args.vecId]
* @param {Metric} [args.metric]
* @param {Accessor<CandlestickData[]>} [args.data]
* @param {number} [args.paneIndex]
* @param {boolean} [args.defaultActive]
@@ -565,7 +566,7 @@ function createChartElement({
* @param {CandlestickSeriesPartialOptions} [args.options]
*/
addCandlestickSeries({
vecId,
metric,
name,
unit,
order,
@@ -607,7 +608,7 @@ function createChartElement({
data,
setDataCallback,
defaultActive,
vecId,
metric,
});
},
/**
@@ -616,7 +617,7 @@ function createChartElement({
* @param {Unit} args.unit
* @param {number} args.order
* @param {Color} args.color
* @param {VecId} [args.vecId]
* @param {Metric} [args.metric]
* @param {Accessor<HistogramData[]>} [args.data]
* @param {number} [args.paneIndex]
* @param {boolean} [args.defaultActive]
@@ -624,7 +625,7 @@ function createChartElement({
* @param {HistogramSeriesPartialOptions} [args.options]
*/
addHistogramSeries({
vecId,
metric,
name,
unit,
color,
@@ -659,7 +660,7 @@ function createChartElement({
data,
setDataCallback,
defaultActive,
vecId,
metric,
});
},
/**
@@ -668,7 +669,7 @@ function createChartElement({
* @param {Unit} args.unit
* @param {number} args.order
* @param {Accessor<LineData[]>} [args.data]
* @param {VecId} [args.vecId]
* @param {Metric} [args.metric]
* @param {Color} [args.color]
* @param {SetDataCallback} [args.setDataCallback]
* @param {number} [args.paneIndex]
@@ -676,7 +677,7 @@ function createChartElement({
* @param {LineSeriesPartialOptions} [args.options]
*/
addLineSeries({
vecId,
metric,
name,
unit,
order,
@@ -718,7 +719,7 @@ function createChartElement({
setDataCallback,
data,
defaultActive,
vecId,
metric,
});
},
/**
@@ -727,14 +728,14 @@ function createChartElement({
* @param {Unit} args.unit
* @param {number} args.order
* @param {Accessor<BaselineData[]>} [args.data]
* @param {VecId} [args.vecId]
* @param {Metric} [args.metric]
* @param {SetDataCallback} [args.setDataCallback]
* @param {number} [args.paneIndex]
* @param {boolean} [args.defaultActive]
* @param {BaselineSeriesPartialOptions} [args.options]
*/
addBaselineSeries({
vecId,
metric,
name,
unit,
order,
@@ -784,7 +785,7 @@ function createChartElement({
unit,
data,
defaultActive,
vecId,
metric,
});
},
};
+8 -18
View File
@@ -17,28 +17,21 @@ export function todayUTC() {
/**
* @param {Date} date
*/
export function toString(date) {
return date.toJSON().split("T")[0];
}
/**
* @param {Date} date
*/
export function toDateIndex(date) {
export function dateToDateIndex(date) {
if (
date.getUTCFullYear() === 2009 &&
date.getUTCMonth() === 0 &&
date.getUTCDate() === 3
)
return 0;
return differenceBetween(date, new Date("2009-01-09"));
return differenceBetweenDates(date, new Date("2009-01-09"));
}
/**
* @param {Date} start
* @param {Date} end
*/
export function getRange(start, end) {
export function createDateRange(start, end) {
const dates = /** @type {Date[]} */ ([]);
let currentDate = new Date(start);
while (currentDate <= end) {
@@ -52,17 +45,14 @@ export function getRange(start, end) {
* @param {Date} date1
* @param {Date} date2
*/
export function differenceBetween(date1, date2) {
export function differenceBetweenDates(date1, date2) {
return Math.abs(date1.valueOf() - date2.valueOf()) / ONE_DAY_IN_MS;
}
/**
* @param {Date} oldest
* @param {Date} youngest
* @returns {number}
* @param {Date} date1
* @param {Date} date2
*/
export function getNumberOfDaysBetweenTwoDates(oldest, youngest) {
return Math.round(
Math.abs((youngest.getTime() - oldest.getTime()) / ONE_DAY_IN_MS),
);
export function roundedDifferenceBetweenDates(date1, date2) {
return Math.round(differenceBetweenDates(date1, date2));
}
+2 -2
View File
@@ -1,4 +1,4 @@
import { string as stringSerde } from "./serde";
import { serdeString } from "./serde";
/**
* @param {string} id
@@ -253,7 +253,7 @@ export function createHorizontalChoiceField({
/** @type {Signal<T[number]>} */
const selected = signals.createSignal(defaultValue, {
save: {
...stringSerde,
...serdeString,
keyPrefix: keyPrefix ?? "",
key,
saveDefaultValue: true,
+13 -10
View File
@@ -4,6 +4,9 @@ import {
createAnchorElement,
insertElementAtIndex,
} from "../dom";
import { serdeUnit } from "../serde";
import { pushHistory, resetParams } from "../url";
import { readStored, writeToStorage } from "../storage";
/**
* @param {Object} args
@@ -11,7 +14,7 @@ import {
* @param {Signals} args.signals
* @param {Env} args.env
* @param {Utilities} args.utils
* @param {VecIdToIndexes} args.vecIdToIndexes
* @param {MetricToIndexes} args.metricToIndexes
* @param {Pools} args.pools
* @param {Signal<string | null>} args.qrcode
*/
@@ -21,7 +24,7 @@ export function initOptions({
env,
utils,
qrcode,
vecIdToIndexes,
metricToIndexes,
pools,
}) {
const LS_SELECTED_KEY = `selected_path`;
@@ -31,7 +34,7 @@ export function initOptions({
.filter((v) => v);
const urlPath = urlPath_.length ? urlPath_ : undefined;
const savedPath = /** @type {string[]} */ (
JSON.parse(storage.read(LS_SELECTED_KEY) || "[]") || []
JSON.parse(readStored(LS_SELECTED_KEY) || "[]") || []
).filter((v) => v);
console.log(savedPath);
@@ -41,7 +44,7 @@ export function initOptions({
const partialOptions = createPartialOptions({
env,
colors,
vecIdToIndexes,
metricToIndexes,
pools,
});
@@ -55,10 +58,10 @@ export function initOptions({
*/
function arrayToRecord(arr = []) {
return (arr || []).reduce((record, blueprint) => {
if (env.localhost && !(blueprint.key in vecIdToIndexes)) {
throw Error(`${blueprint.key} not recognized`);
if (env.localhost && !(blueprint.metric in metricToIndexes)) {
throw Error(`${blueprint.metric} not recognized`);
}
const unit = blueprint.unit ?? utils.vecidToUnit(blueprint.key);
const unit = blueprint.unit ?? serdeUnit.deserialize(blueprint.metric);
record[unit] ??= [];
record[unit].push(blueprint);
return record;
@@ -69,9 +72,9 @@ export function initOptions({
* @param {Option} option
*/
function selectOption(option) {
utils.url.pushHistory(option.path);
utils.url.resetParams(option);
utils.storage.write(LS_SELECTED_KEY, JSON.stringify(option.path));
pushHistory(option.path);
resetParams(option);
writeToStorage(LS_SELECTED_KEY, JSON.stringify(option.path));
selected.set(option);
}
@@ -38,7 +38,7 @@
*
* @typedef {AnySeriesBlueprint["type"]} SeriesType
*
* @typedef {{ key: VecId, unit?: Unit }} FetchedAnySeriesOptions
* @typedef {{ metric: Metric, unit?: Unit }} FetchedAnySeriesOptions
*
* @typedef {BaselineSeriesBlueprint & FetchedAnySeriesOptions} FetchedBaselineSeriesBlueprint
* @typedef {CandlestickSeriesBlueprint & FetchedAnySeriesOptions} FetchedCandlestickSeriesBlueprint
@@ -123,18 +123,18 @@
* @param {Object} args
* @param {Env} args.env
* @param {Colors} args.colors
* @param {VecIdToIndexes} args.vecIdToIndexes
* @param {MetricToIndexes} args.metricToIndexes
* @param {Pools} args.pools
* @returns {PartialOptionsTree}
*/
export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
export function createPartialOptions({ env, colors, metricToIndexes, pools }) {
/**
* @template {string} S
* @typedef {Extract<VecId, `${S}${string}`>} StartsWith
* @typedef {Extract<Metric, `${S}${string}`>} StartsWith
*/
/**
* @template {string} S
* @typedef {Extract<VecId, `${string}${S}`>} EndsWith
* @typedef {Extract<Metric, `${string}${S}`>} EndsWith
*/
/**
* @template {string} K
@@ -155,32 +155,32 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
/**
* @typedef {"cumulative_"} CumulativePrefix
* @typedef {"_30d_delta"} _30DChageSubString
* @typedef {StartsWith<CumulativePrefix>} CumulativeVecId
* @typedef {ExcludeSubstring<WithoutPrefix<CumulativeVecId, CumulativePrefix>, _30DChageSubString>} CumulativeVecIdBase
* @typedef {StartsWith<CumulativePrefix>} CumulativeMetric
* @typedef {ExcludeSubstring<WithoutPrefix<CumulativeMetric, CumulativePrefix>, _30DChageSubString>} CumulativeMetricBase
* @typedef {"_avg"} AverageSuffix
* @typedef {EndsWith<AverageSuffix>} VecIdAverage
* @typedef {WithoutSuffix<VecIdAverage, AverageSuffix>} VecIdAverageBase
* @typedef {EndsWith<AverageSuffix>} MetricAverage
* @typedef {WithoutSuffix<MetricAverage, AverageSuffix>} MetricAverageBase
* @typedef {"_median"} MedianSuffix
* @typedef {EndsWith<MedianSuffix>} VecIdMedian
* @typedef {WithoutSuffix<VecIdMedian, MedianSuffix>} VecIdMedianBase
* @typedef {EndsWith<MedianSuffix>} MetricMedian
* @typedef {WithoutSuffix<MetricMedian, MedianSuffix>} MetricMedianBase
* @typedef {"_pct90"} _pct90Suffix
* @typedef {EndsWith<_pct90Suffix>} VecIdpct90
* @typedef {WithoutSuffix<VecIdpct90, _pct90Suffix>} VecIdpct90Base
* @typedef {EndsWith<_pct90Suffix>} MetricPct90
* @typedef {WithoutSuffix<MetricPct90, _pct90Suffix>} MetricPct90Base
* @typedef {"_pct75"} _pct75Suffix
* @typedef {EndsWith<_pct75Suffix>} VecIdpct75
* @typedef {WithoutSuffix<VecIdpct75, _pct75Suffix>} VecIdpct75Base
* @typedef {EndsWith<_pct75Suffix>} MetricPct75
* @typedef {WithoutSuffix<MetricPct75, _pct75Suffix>} MetricPct75Base
* @typedef {"_pct25"} _pct25Suffix
* @typedef {EndsWith<_pct25Suffix>} VecIdpct25
* @typedef {WithoutSuffix<VecIdpct25, _pct25Suffix>} VecIdpct25Base
* @typedef {EndsWith<_pct25Suffix>} MetricPct25
* @typedef {WithoutSuffix<MetricPct25, _pct25Suffix>} MetricPct25Base
* @typedef {"_pct10"} _pct10Suffix
* @typedef {EndsWith<_pct10Suffix>} VecIdpct10
* @typedef {WithoutSuffix<VecIdpct10, _pct10Suffix>} VecIdpct10Base
* @typedef {EndsWith<_pct10Suffix>} MetricPct10
* @typedef {WithoutSuffix<MetricPct10, _pct10Suffix>} MetricPct10Base
* @typedef {"_max"} MaxSuffix
* @typedef {EndsWith<MaxSuffix>} VecIdMax
* @typedef {WithoutSuffix<VecIdMax, MaxSuffix>} VecIdMaxBase
* @typedef {EndsWith<MaxSuffix>} MetricMax
* @typedef {WithoutSuffix<MetricMax, MaxSuffix>} MetricMaxBase
* @typedef {"_min"} MinSuffix
* @typedef {EndsWith<MinSuffix>} VecIdMin
* @typedef {WithoutSuffix<VecIdMin, MinSuffix>} VecIdMinBase
* @typedef {EndsWith<MinSuffix>} MetricMin
* @typedef {WithoutSuffix<MetricMin, MinSuffix>} MetricMinBase
*/
const averages = /** @type {const} */ ([
@@ -1135,7 +1135,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
/**
* @param {Object} args
* @param {VecId} args.key
* @param {Metric} args.key
* @param {string} args.name
* @param {Color} [args.color]
* @param {Unit} [args.unit]
@@ -1162,7 +1162,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
/**
* @param {Object} args
* @param {VecIdAverageBase} args.concat
* @param {MetricAverageBase} args.concat
* @param {string} [args.title]
*/
function createAverageSeries({ concat, title = "" }) {
@@ -1174,7 +1174,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
/**
* @param {Object} args
* @param {CumulativeVecIdBase} args.concat
* @param {CumulativeMetricBase} args.concat
* @param {Color} [args.sumColor]
* @param {Color} [args.cumulativeColor]
* @param {string} [args.common]
@@ -1201,14 +1201,14 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
/**
* @param {Object} args
* @param {CumulativeVecIdBase} args.key
* @param {CumulativeMetricBase} args.key
* @param {string} [args.title]
* @param {Color} [args.color]
*/
function createSumSeries({ key, title = "", color }) {
const sumKey = `${key}_sum`;
return /** @satisfies {AnyFetchedSeriesBlueprint} */ ({
key: `${key}_sum` in vecIdToIndexes ? sumKey : key,
key: `${key}_sum` in metricToIndexes ? sumKey : key,
title: `Sum ${title}`,
color: color ?? colors.red,
});
@@ -1216,7 +1216,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
/**
* @param {Object} args
* @param {CumulativeVecIdBase} args.concat
* @param {CumulativeMetricBase} args.concat
* @param {string} [args.title]
* @param {Color} [args.color]
*/
@@ -1231,7 +1231,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
/**
* @param {Object} args
* @param {VecIdMinBase & VecIdMaxBase & VecIdpct90Base & VecIdpct75Base & VecIdMedianBase & VecIdpct25Base & VecIdpct10Base} args.concat
* @param {MetricMinBase & MetricMaxBase & MetricPct90Base & MetricPct75Base & MetricMedianBase & MetricPct25Base & MetricPct10Base} args.concat
* @param {string} [args.title]
*/
function createMinMaxPercentilesSeries({ concat, title = "" }) {
@@ -1282,7 +1282,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
}
/**
* @param {VecIdAverageBase & CumulativeVecIdBase & VecIdMinBase & VecIdMaxBase & VecIdpct90Base & VecIdpct75Base & VecIdMedianBase & VecIdpct25Base & VecIdpct10Base} key
* @param {MetricAverageBase & CumulativeMetricBase & MetricMinBase & MetricMaxBase & MetricPct90Base & MetricPct75Base & MetricMedianBase & MetricPct25Base & MetricPct10Base} key
*/
function createSumCumulativeMinMaxPercentilesSeries(key) {
return [
@@ -1292,7 +1292,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
}
/**
* @param {VecIdAverageBase & CumulativeVecIdBase & VecIdMinBase & VecIdMaxBase & VecIdpct90Base & VecIdpct75Base & VecIdMedianBase & VecIdpct25Base & VecIdpct10Base} key
* @param {MetricAverageBase & CumulativeMetricBase & MetricMinBase & MetricMaxBase & MetricPct90Base & MetricPct75Base & MetricMedianBase & MetricPct25Base & MetricPct10Base} key
*/
function createAverageSumCumulativeMinMaxPercentilesSeries(key) {
return [
@@ -1303,7 +1303,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
/**
* @param {Object} args
* @param {VecId & VecIdAverageBase & CumulativeVecIdBase & VecIdMinBase & VecIdMaxBase & VecIdpct90Base & VecIdpct75Base & VecIdMedianBase & VecIdpct25Base & VecIdpct10Base} args.key
* @param {Metric & MetricAverageBase & CumulativeMetricBase & MetricMinBase & MetricMaxBase & MetricPct90Base & MetricPct75Base & MetricMedianBase & MetricPct25Base & MetricPct10Base} args.key
* @param {string} args.name
*/
function createBaseAverageSumCumulativeMinMaxPercentilesSeries({
@@ -1321,8 +1321,8 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
/**
* @typedef {"_ratio_zscore"} RatioZScoreCapSuffix
* @typedef {EndsWith<RatioZScoreCapSuffix>} VecIdRatioZScoreCap
* @typedef {WithoutSuffix<VecIdRatioZScoreCap, RatioZScoreCapSuffix>} VecIdRatioZScoreCapBase
* @typedef {EndsWith<RatioZScoreCapSuffix>} MetricRatioZScoreCap
* @typedef {WithoutSuffix<MetricRatioZScoreCap, RatioZScoreCapSuffix>} MetricRatioZScoreCapBase
*/
/**
@@ -1331,7 +1331,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
* @param {string} args.name
* @param {string} args.legend
* @param {string} args.title
* @param {VecIdRatioZScoreCapBase} args.key
* @param {MetricRatioZScoreCapBase} args.key
* @param {Color} [args.color]
*/
function createPriceWithRatioOptions({ name, title, legend, key, color }) {
@@ -1416,7 +1416,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
name: legend,
color,
}),
...(`${key}_ratio_p1sd_usd` in vecIdToIndexes
...(`${key}_ratio_p1sd_usd` in metricToIndexes
? percentiles.map(({ name, color }) =>
createBaseSeries({
key: `${key}_ratio_${name}_usd`,
@@ -1439,7 +1439,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
baseValue: { price: 1 },
},
}),
...(`${key}_ratio_p1sd` in vecIdToIndexes
...(`${key}_ratio_p1sd` in metricToIndexes
? percentiles.map(({ name, color }) =>
createBaseSeries({
key: `${key}_ratio_${name}`,
@@ -1452,7 +1452,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
}),
)
: []),
...(`${key}_ratio_sma` in vecIdToIndexes
...(`${key}_ratio_sma` in metricToIndexes
? ratioAverages.map(({ name, key: keyAddon, color }) =>
createBaseSeries({
key: `${key}_ratio_${keyAddon}`,
@@ -1471,7 +1471,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
}),
],
},
...(`${key}_ratio_zscore` in vecIdToIndexes
...(`${key}_ratio_zscore` in metricToIndexes
? [
{
name: "ZScores",
@@ -1665,8 +1665,8 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
/**
* @typedef {"_supply_in_profit"} SupplyInProfitSuffix
* @typedef {EndsWith<SupplyInProfitSuffix>} VecIdSupplyInProfit
* @typedef {WithoutSuffix<VecIdSupplyInProfit, SupplyInProfitSuffix>} CohortId
* @typedef {EndsWith<SupplyInProfitSuffix>} MetricSupplyInProfit
* @typedef {WithoutSuffix<MetricSupplyInProfit, SupplyInProfitSuffix>} CohortId
*/
/**
@@ -2014,11 +2014,11 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
}),
},
...(list.filter(
({ key }) => `${fixKey(key)}addr_count` in vecIdToIndexes,
({ key }) => `${fixKey(key)}addr_count` in metricToIndexes,
).length > ("list" in args ? 1 : 0)
? !("list" in args) ||
list.filter(
({ key }) => `${fixKey(key)}empty_addr_count` in vecIdToIndexes,
({ key }) => `${fixKey(key)}empty_addr_count` in metricToIndexes,
).length <= 1
? [
{
@@ -2027,7 +2027,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
bottom: list.flatMap(({ name, color, key: _key }) => {
const key = fixKey(_key);
return [
...(`${key}addr_count` in vecIdToIndexes
...(`${key}addr_count` in metricToIndexes
? /** @type {const} */ ([
createBaseSeries({
key: `${key}addr_count`,
@@ -2036,7 +2036,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
}),
])
: []),
...(`${key}empty_addr_count` in vecIdToIndexes
...(`${key}empty_addr_count` in metricToIndexes
? /** @type {const} */ ([
createBaseSeries({
key: `${key}empty_addr_count`,
@@ -2060,7 +2060,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
bottom: list
.filter(
({ key }) =>
`${fixKey(key)}addr_count` in vecIdToIndexes,
`${fixKey(key)}addr_count` in metricToIndexes,
)
.flatMap(({ name, color, key: _key }) => {
const key = fixKey(_key);
@@ -2075,7 +2075,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
},
...(list.filter(
({ key }) =>
`${fixKey(key)}empty_addr_count` in vecIdToIndexes,
`${fixKey(key)}empty_addr_count` in metricToIndexes,
).length
? [
{
@@ -2085,7 +2085,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
.filter(
({ key }) =>
`${fixKey(key)}empty_addr_count` in
vecIdToIndexes,
metricToIndexes,
)
.flatMap(({ name, color, key: _key }) => {
const key = fixKey(_key);
@@ -2191,7 +2191,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
defaultActive: false,
}),
...(`${fixKey(args.key)}realized_profit_to_loss_ratio` in
vecIdToIndexes
metricToIndexes
? [
createBaseSeries({
key: `${fixKey(
@@ -2329,7 +2329,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
},
},
}),
...(asoprKey in vecIdToIndexes
...(asoprKey in metricToIndexes
? [
/** @satisfies {FetchedBaselineSeriesBlueprint} */ ({
type: "Baseline",
@@ -2357,7 +2357,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
},
},
}),
...(asoprKey in vecIdToIndexes
...(asoprKey in metricToIndexes
? [
/** @satisfies {FetchedBaselineSeriesBlueprint} */ ({
type: "Baseline",
@@ -2385,7 +2385,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
},
},
}),
...(asoprKey in vecIdToIndexes
...(asoprKey in metricToIndexes
? [
/** @satisfies {FetchedBaselineSeriesBlueprint} */ ({
type: "Baseline",
@@ -2473,7 +2473,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
color,
}),
...(`${key}realized_profit_to_loss_ratio` in
vecIdToIndexes
metricToIndexes
? [
createBaseSeries({
key: `${key}realized_profit_to_loss_ratio`,
@@ -2634,7 +2634,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
name,
key: `${fixKey(key)}adjusted_sopr`,
}))
.filter(({ key }) => key in vecIdToIndexes);
.filter(({ key }) => key in metricToIndexes);
return reducedList.length
? [
@@ -2712,7 +2712,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
name: "normal",
color: colors.emerald,
}),
...(adjKey in vecIdToIndexes
...(adjKey in metricToIndexes
? [
createBaseSeries({
key: adjKey,
@@ -2738,7 +2738,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
name: "normal",
color: colors.red,
}),
...(adjKey in vecIdToIndexes
...(adjKey in metricToIndexes
? [
createBaseSeries({
key: adjKey,
@@ -2773,7 +2773,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
name,
key: `${fixKey(key)}adjusted_value_created`,
}))
.filter(({ key }) => key in vecIdToIndexes);
.filter(({ key }) => key in metricToIndexes);
return reducedList.length
? [
{
@@ -2814,7 +2814,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
name,
key: `${fixKey(key)}adjusted_value_destroyed`,
}))
.filter(({ key }) => key in vecIdToIndexes);
.filter(({ key }) => key in metricToIndexes);
return reducedList.length
? [
{
@@ -2894,7 +2894,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
...(`${fixKey(
args.key,
)}unrealized_profit_rel_to_own_market_cap` in
vecIdToIndexes
metricToIndexes
? [
createBaseSeries({
key: `${fixKey(
@@ -2930,7 +2930,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
...(`${fixKey(
args.key,
)}unrealized_profit_rel_to_own_total_unrealized_pnl` in
vecIdToIndexes
metricToIndexes
? [
createBaseSeries({
key: `${fixKey(
@@ -3037,7 +3037,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
}),
...(`${fixKey(
key,
)}net_unrealized_pnl_rel_to_own_market_cap` in vecIdToIndexes
)}net_unrealized_pnl_rel_to_own_market_cap` in metricToIndexes
? [
/** @satisfies {FetchedBaselineSeriesBlueprint} */ ({
type: "Baseline",
@@ -3055,7 +3055,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
...(`${fixKey(
key,
)}net_unrealized_pnl_rel_to_own_total_unrealized_pnl` in
vecIdToIndexes
metricToIndexes
? [
/** @satisfies {FetchedBaselineSeriesBlueprint} */ ({
type: "Baseline",
@@ -3321,7 +3321,7 @@ export function createPartialOptions({ env, colors, vecIdToIndexes, pools }) {
createPriceLine({
unit: "percentage",
}),
...(`${key}_cagr` in vecIdToIndexes
...(`${key}_cagr` in metricToIndexes
? [
/** @satisfies {FetchedBaselineSeriesBlueprint} */ ({
key: `${key}_cagr`,
+28 -13
View File
@@ -1,6 +1,6 @@
const localhost = window.location.hostname === "localhost";
export const string = {
export const serdeString = {
/**
* @param {string} v
*/
@@ -15,9 +15,9 @@ export const string = {
},
};
export const vecIds = {
export const serdeMetrics = {
/**
* @param {VecId[]} v
* @param {Metric[]} v
*/
serialize(v) {
return v.join(",");
@@ -26,11 +26,11 @@ export const vecIds = {
* @param {string} v
*/
deserialize(v) {
return /** @type {VecId[]} */ (v.split(","));
return /** @type {Metric[]} */ (v.split(","));
},
};
export const number = {
export const serdeNumber = {
/**
* @param {number} v
*/
@@ -45,7 +45,7 @@ export const number = {
},
};
export const optNumber = {
export const serdeOptNumber = {
/**
* @param {number | null} v
*/
@@ -60,9 +60,24 @@ export const optNumber = {
},
};
export const optDate = {
export const serdeDate = {
/**
* @param {Date | null} date
* @param {Date} date
*/
serialize(date) {
return date.toString();
},
/**
* @param {string} v
*/
deserialize(v) {
return new Date(v);
},
};
export const serdeOptDate = {
/**
* @param {Date | null} date
*/
serialize(date) {
return date !== null ? date.toString() : "";
@@ -75,7 +90,7 @@ export const optDate = {
},
};
export const boolean = {
export const serdeBool = {
/**
* @param {boolean} v
*/
@@ -96,7 +111,7 @@ export const boolean = {
},
};
export const index = {
export const serdeIndex = {
/**
* @param {Index} v
*/
@@ -160,7 +175,7 @@ export const index = {
},
};
export const chartableIndex = {
export const serdeChartableIndex = {
/**
* @param {number} v
* @returns {SerializedChartableIndex | null}
@@ -278,9 +293,9 @@ export const chartableIndex = {
* "" } Unit
*/
export const unit = {
export const serdeUnit = {
/**
* @param {VecId} v
* @param {string} v
*/
deserialize(v) {
/** @type {Unit | undefined} */
+50 -48
View File
@@ -1,49 +1,51 @@
export default {
/**
* @param {string} key
*/
readNumber(key) {
const saved = this.read(key);
if (saved) {
return Number(saved);
}
/**
* @param {string} key
*/
export function readStoredNumber(key) {
const saved = readStored(key);
if (saved) {
return Number(saved);
}
return null;
}
/**
* @param {string} key
*/
export function readStoredBool(key) {
const saved = readStored(key);
if (saved) {
return saved === "true" || saved === "1";
}
return null;
}
/**
* @param {string} key
*/
export function readStored(key) {
try {
return localStorage.getItem(key);
} catch (_) {
return null;
},
/**
* @param {string} key
*/
readBool(key) {
const saved = this.read(key);
if (saved) {
return saved === "true" || saved === "1";
}
return null;
},
/**
* @param {string} key
*/
read(key) {
try {
return localStorage.getItem(key);
} catch (_) {
return null;
}
},
/**
* @param {string} key
* @param {string | boolean | null | undefined} value
*/
write(key, value) {
try {
value !== undefined && value !== null
? localStorage.setItem(key, String(value))
: localStorage.removeItem(key);
} catch (_) {}
},
/**
* @param {string} key
*/
remove(key) {
this.write(key, undefined);
},
};
}
}
/**
* @param {string} key
* @param {string | boolean | null | undefined} value
*/
export function writeToStorage(key, value) {
try {
value !== undefined && value !== null
? localStorage.setItem(key, String(value))
: localStorage.removeItem(key);
} catch (_) {}
}
/**
* @param {string} key
*/
export function removeStored(key) {
writeToStorage(key, undefined);
}
+97 -91
View File
@@ -6,96 +6,102 @@ function processPathname(pathname) {
return Array.isArray(pathname) ? pathname.join("/") : pathname;
}
export default {
chartParamsWhitelist: ["from", "to"],
/**
* @param {string | string[]} pathname
*/
pushHistory(pathname) {
const urlParams = new URLSearchParams(window.location.search);
pathname = processPathname(pathname);
try {
const url = `/${pathname}?${urlParams.toString()}`;
console.log(`push history: ${url}`);
window.history.pushState(null, "", url);
} catch (_) {}
},
/**
* @param {Object} args
* @param {URLSearchParams} [args.urlParams]
* @param {string | string[]} [args.pathname]
*/
replaceHistory({ urlParams, pathname }) {
urlParams ||= new URLSearchParams(window.location.search);
pathname = processPathname(pathname);
try {
const url = `/${pathname}?${urlParams.toString()}`;
console.log(`replace history: ${url}`);
window.history.replaceState(null, "", url);
} catch (_) {}
},
/**
* @param {Option} option
*/
resetParams(option) {
const urlParams = new URLSearchParams();
if (option.kind === "chart") {
[...new URLSearchParams(window.location.search).entries()]
.filter(([key, _]) => this.chartParamsWhitelist.includes(key))
.forEach(([key, value]) => {
urlParams.set(key, value);
});
}
this.replaceHistory({ urlParams, pathname: option.path.join("/") });
},
/**
* @param {string} key
* @param {string | boolean | null | undefined} value
*/
writeParam(key, value) {
const urlParams = new URLSearchParams(window.location.search);
const chartParamsWhitelist = ["from", "to"];
if (value !== null && value !== undefined) {
urlParams.set(key, String(value));
} else {
urlParams.delete(key);
}
/**
* @param {string | string[]} pathname
*/
export function pushHistory(pathname) {
const urlParams = new URLSearchParams(window.location.search);
pathname = processPathname(pathname);
try {
const url = `/${pathname}?${urlParams.toString()}`;
console.log(`push history: ${url}`);
window.history.pushState(null, "", url);
} catch (_) {}
}
this.replaceHistory({ urlParams });
},
/**
* @param {string} key
*/
removeParam(key) {
this.writeParam(key, undefined);
},
/**
* @param {string} key
*/
readBoolParam(key) {
const param = this.readParam(key);
if (param) {
return param === "true" || param === "1";
}
return null;
},
/**
* @param {string} key
*/
readNumberParam(key) {
const param = this.readParam(key);
if (param) {
return Number(param);
}
return null;
},
/**
*
* @param {string} key
* @returns {string | null}
*/
readParam(key) {
const params = new URLSearchParams(window.location.search);
return params.get(key);
},
};
/**
* @param {Object} args
* @param {URLSearchParams} [args.urlParams]
* @param {string | string[]} [args.pathname]
*/
export function replaceHistory({ urlParams, pathname }) {
urlParams ||= new URLSearchParams(window.location.search);
pathname = processPathname(pathname);
try {
const url = `/${pathname}?${urlParams.toString()}`;
console.log(`replace history: ${url}`);
window.history.replaceState(null, "", url);
} catch (_) {}
}
/**
* @param {Option} option
*/
export function resetParams(option) {
const urlParams = new URLSearchParams();
if (option.kind === "chart") {
[...new URLSearchParams(window.location.search).entries()]
.filter(([key, _]) => chartParamsWhitelist.includes(key))
.forEach(([key, value]) => {
urlParams.set(key, value);
});
}
replaceHistory({ urlParams, pathname: option.path.join("/") });
}
/**
* @param {string} key
* @param {string | boolean | null | undefined} value
*/
export function writeParam(key, value) {
const urlParams = new URLSearchParams(window.location.search);
if (value !== null && value !== undefined) {
urlParams.set(key, String(value));
} else {
urlParams.delete(key);
}
replaceHistory({ urlParams });
}
/**
* @param {string} key
*/
export function removeParam(key) {
writeParam(key, undefined);
}
/**
* @param {string} key
*/
export function readBoolParam(key) {
const param = readParam(key);
if (param) {
return param === "true" || param === "1";
}
return null;
}
/**
* @param {string} key
*/
export function readNumberParam(key) {
const param = readParam(key);
if (param) {
return Number(param);
}
return null;
}
/**
*
* @param {string} key
* @returns {string | null}
*/
export function readParam(key) {
const params = new URLSearchParams(window.location.search);
return params.get(key);
}