mirror of
https://github.com/bitcoinresearchkit/brk.git
synced 2026-06-21 03:58:24 -07:00
app: charts: add unit and price mode switch
This commit is contained in:
@@ -48,6 +48,8 @@
|
||||
|
||||
- General
|
||||
- Added chart scroll button for nice animations à la Wicked
|
||||
- Added scale mode switch (Linear/Logarithmic) at the bottom right of all charts
|
||||
- Added unit at the top left of all charts
|
||||
- Added a backup API in case the main one fails or is offline
|
||||
- Complete redesign of the datasets object
|
||||
- Removed import of routes in JSON in favor for hardcoded typed routes in string format which resulted in:
|
||||
|
||||
+1
-1
@@ -35,7 +35,7 @@
|
||||
"pwa-asset-generator": "^6.3.1",
|
||||
"rollup-plugin-visualizer": "^5.12.0",
|
||||
"tailwindcss": "^3.4.6",
|
||||
"typescript": "^5.5.3",
|
||||
"typescript": "^5.5.4",
|
||||
"unplugin-auto-import": "^0.18.0",
|
||||
"unplugin-icons": "^0.19.0",
|
||||
"vite": "^5.3.4",
|
||||
|
||||
Generated
+7
-7
@@ -59,8 +59,8 @@ devDependencies:
|
||||
specifier: ^3.4.6
|
||||
version: 3.4.6
|
||||
typescript:
|
||||
specifier: ^5.5.3
|
||||
version: 5.5.3
|
||||
specifier: ^5.5.4
|
||||
version: 5.5.4
|
||||
unplugin-auto-import:
|
||||
specifier: ^0.18.0
|
||||
version: 0.18.0(rollup@2.79.1)
|
||||
@@ -2635,7 +2635,7 @@ packages:
|
||||
hasBin: true
|
||||
dependencies:
|
||||
caniuse-lite: 1.0.30001643
|
||||
electron-to-chromium: 1.4.832
|
||||
electron-to-chromium: 1.5.0
|
||||
node-releases: 2.0.18
|
||||
update-browserslist-db: 1.1.0(browserslist@4.23.2)
|
||||
dev: true
|
||||
@@ -3101,8 +3101,8 @@ packages:
|
||||
jake: 10.9.2
|
||||
dev: true
|
||||
|
||||
/electron-to-chromium@1.4.832:
|
||||
resolution: {integrity: sha512-cTen3SB0H2SGU7x467NRe1eVcQgcuS6jckKfWJHia2eo0cHIGOqHoAxevIYZD4eRHcWjkvFzo93bi3vJ9W+1lA==}
|
||||
/electron-to-chromium@1.5.0:
|
||||
resolution: {integrity: sha512-Vb3xHHYnLseK8vlMJQKJYXJ++t4u1/qJ3vykuVrVjvdiOEhYyT1AuP4x03G8EnPmYvYOhe9T+dADTmthjRQMkA==}
|
||||
dev: true
|
||||
|
||||
/emoji-regex@8.0.0:
|
||||
@@ -5651,8 +5651,8 @@ packages:
|
||||
possible-typed-array-names: 1.0.0
|
||||
dev: true
|
||||
|
||||
/typescript@5.5.3:
|
||||
resolution: {integrity: sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==}
|
||||
/typescript@5.5.4:
|
||||
resolution: {integrity: sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==}
|
||||
engines: {node: '>=14.17'}
|
||||
hasBin: true
|
||||
dev: true
|
||||
|
||||
@@ -1,82 +1,369 @@
|
||||
import { requestIdleCallbackPossible } from "/src/env";
|
||||
import { applySeriesList } from "/src/scripts/presets/apply";
|
||||
import { chunkIdToIndex } from "/src/scripts/datasets/resource";
|
||||
import { createChart } from "/src/scripts/lightweightCharts/create";
|
||||
import { createSeriesGroup } from "/src/scripts/lightweightCharts/group";
|
||||
import { setMinMaxMarkers } from "/src/scripts/lightweightCharts/markers";
|
||||
import {
|
||||
debouncedUpdateVisiblePriceSeriesType,
|
||||
updateVisiblePriceSeriesType,
|
||||
} from "/src/scripts/lightweightCharts/price";
|
||||
import {
|
||||
initTimeScale,
|
||||
setInitialTimeRange,
|
||||
} from "/src/scripts/lightweightCharts/time";
|
||||
import { setWhitespace } from "/src/scripts/lightweightCharts/whitespace";
|
||||
import { SeriesType } from "/src/scripts/presets/enums";
|
||||
import { colors } from "/src/scripts/utils/colors";
|
||||
import { debounce } from "/src/scripts/utils/debounce";
|
||||
import { createSL } from "/src/scripts/utils/selectableList/static";
|
||||
import { webSockets } from "/src/scripts/ws";
|
||||
import { classPropToString } from "/src/solid/classes";
|
||||
import { createRWS } from "/src/solid/rws";
|
||||
|
||||
import { RadioGroup } from "../../settings";
|
||||
|
||||
export function Chart({
|
||||
charts,
|
||||
parentDiv,
|
||||
presets,
|
||||
datasets,
|
||||
legendSetter,
|
||||
dark,
|
||||
activeDatasets,
|
||||
activeIds,
|
||||
charts,
|
||||
chartsDrawn,
|
||||
dark,
|
||||
datasets,
|
||||
exactRange,
|
||||
firstChartSetter,
|
||||
index,
|
||||
lastActiveIndex,
|
||||
lastChartIndex,
|
||||
legendSetter,
|
||||
preset: presetAccessor,
|
||||
priceSeriesType,
|
||||
seriesConfigs,
|
||||
seriesCount,
|
||||
}: {
|
||||
charts: RWS<IChartApi[]>;
|
||||
parentDiv: RWS<HTMLDivElement | undefined>;
|
||||
presets: Presets;
|
||||
datasets: Datasets;
|
||||
legendSetter: Setter<SeriesLegend[]>;
|
||||
dark: Accessor<boolean>;
|
||||
activeDatasets: ReadWriteSignal<ResourceDataset<any, any>[]>;
|
||||
activeIds: RWS<number[]>;
|
||||
charts: ReadWriteSignal<
|
||||
{
|
||||
chart: RWS<IChartApi | undefined>;
|
||||
whitespace: RWS<ISeriesApiAny | undefined>;
|
||||
}[]
|
||||
>;
|
||||
chartsDrawn: Accessor<ReadWriteSignal<boolean>[]>;
|
||||
dark: Accessor<boolean>;
|
||||
datasets: Datasets;
|
||||
exactRange: ReadWriteSignal<TimeRange>;
|
||||
firstChartSetter: Setter<IChartApi | undefined>;
|
||||
index: Accessor<number>;
|
||||
lastActiveIndex: Accessor<number | undefined>;
|
||||
lastChartIndex: Accessor<number>;
|
||||
legendSetter: Setter<SeriesLegend[]>;
|
||||
preset: Accessor<Preset>;
|
||||
priceSeriesType: ReadWriteSignal<PriceSeriesType>;
|
||||
seriesConfigs: SeriesConfig[];
|
||||
seriesCount: Accessor<number>;
|
||||
}) {
|
||||
const wasIdle = createRWS(false);
|
||||
const div = createRWS<HTMLDivElement | undefined>(undefined);
|
||||
const chartIndex = index();
|
||||
|
||||
if (requestIdleCallbackPossible) {
|
||||
const idleCallback = requestIdleCallback(() => {
|
||||
wasIdle.set(true);
|
||||
});
|
||||
const isDrawn = chartsDrawn()[chartIndex];
|
||||
const isLastDrawn = createMemo(
|
||||
() => chartsDrawn().findLastIndex((drawn) => drawn()) === chartIndex,
|
||||
);
|
||||
|
||||
onCleanup(() => {
|
||||
cancelIdleCallback(idleCallback);
|
||||
});
|
||||
} else {
|
||||
const timeout = setTimeout(() => {
|
||||
wasIdle.set(true);
|
||||
}, 500);
|
||||
|
||||
onCleanup(() => {
|
||||
clearTimeout(timeout);
|
||||
});
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
createEffect(() => {
|
||||
const preset = presets.selected();
|
||||
const div = parentDiv();
|
||||
|
||||
if (!wasIdle() || !div) return;
|
||||
|
||||
untrack(() => {
|
||||
try {
|
||||
console.log(`preset: ${preset.id}`);
|
||||
applySeriesList({
|
||||
charts,
|
||||
parentDiv: div,
|
||||
datasets,
|
||||
preset,
|
||||
legendSetter,
|
||||
dark,
|
||||
activeIds,
|
||||
priceScaleOptions: preset.priceScaleOptions,
|
||||
top: preset.top,
|
||||
bottom: preset.bottom,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("chart: render: failed", error);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
onCleanup(() =>
|
||||
charts.set((charts) => {
|
||||
charts.forEach((chart) => {
|
||||
chart.remove();
|
||||
});
|
||||
|
||||
return [];
|
||||
}),
|
||||
);
|
||||
const chartPriceModeKey = `chart-price-mode-${chartIndex}` as const;
|
||||
const chartPriceMode = createSL(["Linear", "Log"] as const, {
|
||||
saveable: {
|
||||
key: chartPriceModeKey,
|
||||
mode: "localStorage",
|
||||
},
|
||||
defaultValue: chartIndex === 0 ? "Log" : "Linear",
|
||||
});
|
||||
|
||||
return <></>;
|
||||
createEffect(
|
||||
on([div, () => charts()[chartIndex]], ([div, chartConfig]) => {
|
||||
console.log({
|
||||
div,
|
||||
chartConfig,
|
||||
});
|
||||
|
||||
if (!div || !chartConfig) return;
|
||||
|
||||
const preset = presetAccessor();
|
||||
const scale = preset.scale;
|
||||
|
||||
const chart = createChart({
|
||||
scale,
|
||||
element: div,
|
||||
dark,
|
||||
});
|
||||
|
||||
if (!chart) {
|
||||
console.log("chart: undefined");
|
||||
return;
|
||||
}
|
||||
|
||||
const whitespace = setWhitespace(chart, scale);
|
||||
|
||||
batch(() => {
|
||||
chartConfig.chart.set(chart);
|
||||
chartConfig.whitespace.set(whitespace);
|
||||
|
||||
if (chartIndex === 0) {
|
||||
firstChartSetter(chart);
|
||||
}
|
||||
});
|
||||
|
||||
const range = exactRange();
|
||||
|
||||
setInitialTimeRange({ chart, range });
|
||||
|
||||
if (chartIndex === 0) {
|
||||
initTimeScale({
|
||||
scale,
|
||||
chart,
|
||||
activeIds,
|
||||
exactRange,
|
||||
});
|
||||
|
||||
if (range) {
|
||||
updateVisiblePriceSeriesType({
|
||||
scale,
|
||||
chart,
|
||||
priceSeriesType,
|
||||
timeRange: range,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const chartLegend: SeriesLegend[] = [];
|
||||
|
||||
onCleanup(() => {
|
||||
chartLegend.length = 0;
|
||||
});
|
||||
|
||||
const markerCallback = () =>
|
||||
setMinMaxMarkers({
|
||||
scale,
|
||||
visibleRange: exactRange(),
|
||||
legendList: chartLegend,
|
||||
dark,
|
||||
activeIds: activeIds,
|
||||
});
|
||||
|
||||
const debouncedSetMinMaxMarkers = requestIdleCallbackPossible
|
||||
? () => requestIdleCallback(markerCallback)
|
||||
: debounce(
|
||||
markerCallback,
|
||||
seriesCount() * 10 + scale === "date" ? 50 : 100,
|
||||
);
|
||||
|
||||
createEffect(on([exactRange, dark], debouncedSetMinMaxMarkers));
|
||||
|
||||
if (chartIndex === 0) {
|
||||
const datasetPath: AnyDatasetPath = `/${scale}-to-price`;
|
||||
|
||||
const dataset = datasets.getOrImport(scale, datasetPath);
|
||||
|
||||
// Don't trigger reactivity by design
|
||||
activeDatasets().push(dataset);
|
||||
|
||||
const title = "Price";
|
||||
|
||||
function createPriceSeries(seriesType: PriceSeriesType) {
|
||||
let seriesConfig: SeriesConfig;
|
||||
|
||||
if (seriesType === "Candlestick") {
|
||||
seriesConfig = {
|
||||
datasetPath,
|
||||
title,
|
||||
seriesType: SeriesType.Candlestick,
|
||||
};
|
||||
} else {
|
||||
seriesConfig = {
|
||||
datasetPath,
|
||||
title,
|
||||
color: colors.white,
|
||||
};
|
||||
}
|
||||
|
||||
const priceSeries = createSeriesGroup({
|
||||
scale,
|
||||
datasets,
|
||||
index: -1,
|
||||
activeIds,
|
||||
seriesConfig,
|
||||
chart,
|
||||
chartLegend,
|
||||
lastActiveIndex,
|
||||
preset,
|
||||
disabled: () => priceSeriesType() !== seriesType,
|
||||
debouncedSetMinMaxMarkers,
|
||||
dark,
|
||||
});
|
||||
|
||||
createEffect(() => {
|
||||
const latest = webSockets.liveKrakenCandle.latest();
|
||||
|
||||
if (!latest) return;
|
||||
|
||||
const index = chunkIdToIndex(scale, latest.year);
|
||||
|
||||
const series = priceSeries.seriesList.at(index)?.();
|
||||
|
||||
series?.update(latest);
|
||||
});
|
||||
|
||||
return priceSeries;
|
||||
}
|
||||
|
||||
const priceCandlestickLegend = createPriceSeries("Candlestick");
|
||||
const priceLineLegend = createPriceSeries("Line");
|
||||
|
||||
createEffect(() => {
|
||||
priceCandlestickLegend.visible.set(priceLineLegend.visible());
|
||||
});
|
||||
|
||||
createEffect(() => {
|
||||
priceLineLegend.visible.set(priceCandlestickLegend.visible());
|
||||
});
|
||||
}
|
||||
|
||||
[...seriesConfigs].reverse().forEach((seriesConfig, index) => {
|
||||
const dataset = datasets.getOrImport(scale, seriesConfig.datasetPath);
|
||||
|
||||
// Don't trigger reactivity by design
|
||||
activeDatasets().push(dataset);
|
||||
|
||||
createSeriesGroup({
|
||||
scale,
|
||||
datasets,
|
||||
activeIds,
|
||||
index,
|
||||
seriesConfig,
|
||||
chartLegend,
|
||||
chart,
|
||||
preset,
|
||||
lastActiveIndex,
|
||||
debouncedSetMinMaxMarkers,
|
||||
dark,
|
||||
});
|
||||
});
|
||||
|
||||
chartLegend.forEach((legend) => {
|
||||
createEffect(on(legend.visible, debouncedSetMinMaxMarkers));
|
||||
});
|
||||
|
||||
legendSetter((l) => {
|
||||
for (let i = 0; i < chartLegend.length; i++) {
|
||||
l.splice(0, 0, chartLegend[i]);
|
||||
}
|
||||
return l;
|
||||
});
|
||||
|
||||
createEffect(() =>
|
||||
isDrawn.set(() => chartLegend.some((legend) => legend.drawn())),
|
||||
);
|
||||
|
||||
createEffect(() =>
|
||||
chart.timeScale().applyOptions({
|
||||
visible: isLastDrawn(),
|
||||
}),
|
||||
);
|
||||
|
||||
createEffect(() =>
|
||||
chart.priceScale("right").applyOptions({
|
||||
mode: chartPriceMode.selected() === "Linear" ? 0 : 1,
|
||||
}),
|
||||
);
|
||||
|
||||
chart.timeScale().subscribeVisibleLogicalRangeChange((logicalRange) => {
|
||||
if (!logicalRange) return;
|
||||
|
||||
// Must be the chart with the visible timeScale
|
||||
if (chartIndex === lastChartIndex()) {
|
||||
debouncedUpdateVisiblePriceSeriesType({
|
||||
scale,
|
||||
chart,
|
||||
logicalRange,
|
||||
priceSeriesType,
|
||||
});
|
||||
}
|
||||
|
||||
for (
|
||||
let otherChartIndex = 0;
|
||||
otherChartIndex <= lastChartIndex();
|
||||
otherChartIndex++
|
||||
) {
|
||||
if (chartIndex !== otherChartIndex) {
|
||||
const chart = charts()[otherChartIndex].chart();
|
||||
|
||||
chart?.timeScale().setVisibleLogicalRange(logicalRange);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
chart.subscribeCrosshairMove(({ time, sourceEvent }) => {
|
||||
// Don't override crosshair position from scroll event
|
||||
if (time && !sourceEvent) return;
|
||||
|
||||
for (
|
||||
let otherChartIndex = 0;
|
||||
otherChartIndex <= lastChartIndex();
|
||||
otherChartIndex++
|
||||
) {
|
||||
const { whitespace: _whitespace, chart: _otherChart } =
|
||||
charts()[otherChartIndex];
|
||||
|
||||
const otherChart = _otherChart();
|
||||
const whitespace = _whitespace();
|
||||
|
||||
if (otherChart && whitespace && chartIndex !== otherChartIndex) {
|
||||
if (time) {
|
||||
otherChart.setCrosshairPosition(NaN, time, whitespace);
|
||||
} else {
|
||||
// No time when mouse goes outside the chart
|
||||
otherChart.clearCrosshairPosition();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Trigger reactivity now
|
||||
activeDatasets.set((l) => l);
|
||||
}),
|
||||
);
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
height: isLastDrawn() ? "100%" : "calc(100% - 62px)",
|
||||
"margin-bottom": isLastDrawn() ? "" : "-2px",
|
||||
}}
|
||||
class={classPropToString([
|
||||
isDrawn()
|
||||
? ["max-h-full", !isLastDrawn() ? "border-b" : "mb-[-2px]"]
|
||||
: "max-h-0",
|
||||
"border-lighter relative h-full min-h-0 w-full cursor-crosshair",
|
||||
])}
|
||||
>
|
||||
<div ref={div.set} class="size-full" />
|
||||
|
||||
<Show when={isDrawn()}>
|
||||
<div class="text-low-contrast absolute left-0 top-0 px-2 py-1.5 text-xs">
|
||||
{chartIndex === 0
|
||||
? ("US Dollars" satisfies Unit)
|
||||
: presetAccessor().unit}
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
bottom: `${isLastDrawn() ? 32 : 0}px`,
|
||||
right: `77px`,
|
||||
}}
|
||||
class="text-low-contrast absolute z-50 px-3 py-0.5"
|
||||
>
|
||||
<RadioGroup size="xs" title={chartPriceModeKey} sl={chartPriceMode} />
|
||||
</div>
|
||||
</Show>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
import { chunkIdToIndex } from "/src/scripts/datasets/resource";
|
||||
import {
|
||||
getInitialTimeRange,
|
||||
setActiveIds,
|
||||
} from "/src/scripts/lightweightCharts/time";
|
||||
import { createRWS } from "/src/solid/rws";
|
||||
|
||||
import { Chart } from "./chart";
|
||||
|
||||
export function Charts({
|
||||
firstChartSetter,
|
||||
preset,
|
||||
datasets,
|
||||
legendSetter,
|
||||
dark,
|
||||
activeIds,
|
||||
}: {
|
||||
firstChartSetter: Setter<IChartApi | undefined>;
|
||||
preset: Accessor<Preset>;
|
||||
datasets: Datasets;
|
||||
legendSetter: Setter<SeriesLegend[]>;
|
||||
dark: Accessor<boolean>;
|
||||
activeIds: RWS<number[]>;
|
||||
}) {
|
||||
const scale = createMemo(() => preset().scale);
|
||||
const exactRange = createRWS(getInitialTimeRange(scale()));
|
||||
const priceSeriesType = createRWS<PriceSeriesType>("Candlestick");
|
||||
const activeDatasets = createRWS([] as ResourceDataset<any, any>[], {
|
||||
equals: false,
|
||||
});
|
||||
const chartSeriesConfigs = createRWS([] as SeriesConfig[][], {
|
||||
equals: false,
|
||||
});
|
||||
const charts = createRWS(
|
||||
[] as {
|
||||
chart: RWS<IChartApi | undefined>;
|
||||
whitespace: RWS<ISeriesApiAny | undefined>;
|
||||
}[],
|
||||
{
|
||||
equals: false,
|
||||
},
|
||||
);
|
||||
const lastChartIndex = createMemo(() => chartSeriesConfigs().length - 1);
|
||||
const seriesCount = createMemo(() =>
|
||||
chartSeriesConfigs().reduce(
|
||||
(acc, l) => (acc += l.length),
|
||||
1, // Because of price series
|
||||
),
|
||||
);
|
||||
const lastActiveIndex = createMemo(() => {
|
||||
const last = activeIds().at(-1);
|
||||
return last !== undefined
|
||||
? chunkIdToIndex(preset().scale, last)
|
||||
: undefined;
|
||||
});
|
||||
const chartsDrawn = createMemo(() =>
|
||||
chartSeriesConfigs().map((_) => createRWS(true)),
|
||||
);
|
||||
|
||||
createEffect(
|
||||
on([activeIds, activeDatasets], ([ids, activeDatasets]) => {
|
||||
for (let i = 0; i < ids.length; i++) {
|
||||
const id = ids[i];
|
||||
for (let j = 0; j < activeDatasets.length; j++) {
|
||||
activeDatasets[j].fetch(id);
|
||||
}
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
createEffect(
|
||||
on(preset, (preset) => {
|
||||
const scale = preset.scale;
|
||||
|
||||
exactRange.set(getInitialTimeRange(scale));
|
||||
|
||||
chartSeriesConfigs.set(
|
||||
[preset.top || [], preset.bottom].flatMap((list) =>
|
||||
list ? [list] : [],
|
||||
),
|
||||
);
|
||||
|
||||
charts.set(() =>
|
||||
new Array(chartSeriesConfigs().length).fill(undefined).map(() => ({
|
||||
chart: createRWS(undefined as IChartApi | undefined),
|
||||
whitespace: createRWS(undefined as ISeriesApiAny | undefined),
|
||||
})),
|
||||
);
|
||||
|
||||
setActiveIds({
|
||||
exactRange: exactRange(),
|
||||
activeIds,
|
||||
});
|
||||
|
||||
legendSetter(() => []);
|
||||
}),
|
||||
);
|
||||
|
||||
onCleanup(() => {
|
||||
firstChartSetter(undefined);
|
||||
|
||||
charts().map(({ chart, whitespace }) => {
|
||||
chart()?.remove();
|
||||
chart.set(undefined);
|
||||
whitespace.set(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
return (
|
||||
<For
|
||||
each={chartSeriesConfigs().filter(
|
||||
(configs, index) => index === 0 || configs.length !== 0,
|
||||
)}
|
||||
>
|
||||
{(seriesConfigs, index) => (
|
||||
<Chart
|
||||
activeDatasets={activeDatasets}
|
||||
activeIds={activeIds}
|
||||
charts={charts}
|
||||
chartsDrawn={chartsDrawn}
|
||||
dark={dark}
|
||||
datasets={datasets}
|
||||
exactRange={exactRange}
|
||||
firstChartSetter={firstChartSetter}
|
||||
index={index}
|
||||
lastActiveIndex={lastActiveIndex}
|
||||
lastChartIndex={lastChartIndex}
|
||||
legendSetter={legendSetter}
|
||||
preset={preset}
|
||||
priceSeriesType={priceSeriesType}
|
||||
seriesConfigs={seriesConfigs}
|
||||
seriesCount={seriesCount}
|
||||
/>
|
||||
)}
|
||||
</For>
|
||||
);
|
||||
}
|
||||
@@ -6,26 +6,26 @@ import { GENESIS_DAY } from "../../../../../scripts/lightweightCharts/whitespace
|
||||
import { Box } from "../../box";
|
||||
import { Scrollable } from "../../scrollable";
|
||||
|
||||
const MULTIPLIER = 0.0025;
|
||||
const DELAY = 25;
|
||||
const DELAY = 1;
|
||||
const MULTIPLIER = DELAY / 1000;
|
||||
const LEFT = -1;
|
||||
const RIGHT = 1;
|
||||
|
||||
export function TimeScale({
|
||||
scale,
|
||||
charts,
|
||||
firstChart,
|
||||
}: {
|
||||
scale: Accessor<ResourceScale>;
|
||||
charts: RWS<IChartApi[]>;
|
||||
firstChart: RWS<IChartApi | undefined>;
|
||||
}) {
|
||||
const today = new Date();
|
||||
|
||||
const disabled = createMemo(() => charts().length === 0);
|
||||
const disabled = createMemo(() => !firstChart());
|
||||
|
||||
const scrollDirection = createRWS(0);
|
||||
|
||||
const timeScale = createMemo(() => {
|
||||
const chart = charts().at(0);
|
||||
const chart = firstChart();
|
||||
if (!chart) return undefined;
|
||||
return chart.timeScale();
|
||||
});
|
||||
@@ -86,21 +86,25 @@ export function TimeScale({
|
||||
<Button
|
||||
minWidth
|
||||
disabled={disabled}
|
||||
onClick={() => setTimeScale({ scale: scale(), charts })}
|
||||
onClick={() => setTimeScale({ scale: scale(), timeScale })}
|
||||
>
|
||||
All Time
|
||||
</Button>
|
||||
<Button
|
||||
minWidth
|
||||
disabled={disabled}
|
||||
onClick={() => setTimeScale({ scale: scale(), charts, days: 7 })}
|
||||
onClick={() =>
|
||||
setTimeScale({ scale: scale(), timeScale, days: 7 })
|
||||
}
|
||||
>
|
||||
1 Week
|
||||
</Button>
|
||||
<Button
|
||||
minWidth
|
||||
disabled={disabled}
|
||||
onClick={() => setTimeScale({ scale: scale(), charts, days: 30 })}
|
||||
onClick={() =>
|
||||
setTimeScale({ scale: scale(), timeScale, days: 30 })
|
||||
}
|
||||
>
|
||||
1 Month
|
||||
</Button>
|
||||
@@ -108,7 +112,7 @@ export function TimeScale({
|
||||
minWidth
|
||||
disabled={disabled}
|
||||
onClick={() =>
|
||||
setTimeScale({ scale: scale(), charts, days: 3 * 30 })
|
||||
setTimeScale({ scale: scale(), timeScale, days: 3 * 30 })
|
||||
}
|
||||
>
|
||||
3 Months
|
||||
@@ -117,7 +121,7 @@ export function TimeScale({
|
||||
minWidth
|
||||
disabled={disabled}
|
||||
onClick={() =>
|
||||
setTimeScale({ scale: scale(), charts, days: 6 * 30 })
|
||||
setTimeScale({ scale: scale(), timeScale, days: 6 * 30 })
|
||||
}
|
||||
>
|
||||
6 Months
|
||||
@@ -128,7 +132,7 @@ export function TimeScale({
|
||||
onClick={() =>
|
||||
setTimeScale({
|
||||
scale: scale(),
|
||||
charts,
|
||||
timeScale,
|
||||
days: Math.ceil(
|
||||
(today.getTime() -
|
||||
new Date(`${today.getUTCFullYear()}-01-01`).getTime()) /
|
||||
@@ -143,7 +147,7 @@ export function TimeScale({
|
||||
minWidth
|
||||
disabled={disabled}
|
||||
onClick={() =>
|
||||
setTimeScale({ scale: scale(), charts, days: 365 })
|
||||
setTimeScale({ scale: scale(), timeScale, days: 365 })
|
||||
}
|
||||
>
|
||||
1 Year
|
||||
@@ -152,7 +156,7 @@ export function TimeScale({
|
||||
minWidth
|
||||
disabled={disabled}
|
||||
onClick={() =>
|
||||
setTimeScale({ scale: scale(), charts, days: 2 * 365 })
|
||||
setTimeScale({ scale: scale(), timeScale, days: 2 * 365 })
|
||||
}
|
||||
>
|
||||
2 Years
|
||||
@@ -161,7 +165,7 @@ export function TimeScale({
|
||||
minWidth
|
||||
disabled={disabled}
|
||||
onClick={() =>
|
||||
setTimeScale({ scale: scale(), charts, days: 4 * 365 })
|
||||
setTimeScale({ scale: scale(), timeScale, days: 4 * 365 })
|
||||
}
|
||||
>
|
||||
4 Years
|
||||
@@ -170,7 +174,7 @@ export function TimeScale({
|
||||
minWidth
|
||||
disabled={disabled}
|
||||
onClick={() =>
|
||||
setTimeScale({ scale: scale(), charts, days: 8 * 365 })
|
||||
setTimeScale({ scale: scale(), timeScale, days: 8 * 365 })
|
||||
}
|
||||
>
|
||||
8 Years
|
||||
@@ -187,7 +191,9 @@ export function TimeScale({
|
||||
<Button
|
||||
minWidth
|
||||
disabled={disabled}
|
||||
onClick={() => setTimeScale({ scale: scale(), charts, year })}
|
||||
onClick={() =>
|
||||
setTimeScale({ scale: scale(), timeScale, year })
|
||||
}
|
||||
>
|
||||
{year}
|
||||
</Button>
|
||||
@@ -214,7 +220,7 @@ export function TimeScale({
|
||||
onClick={() =>
|
||||
setTimeScale({
|
||||
scale: scale(),
|
||||
charts,
|
||||
timeScale,
|
||||
range: {
|
||||
from: i * 100_000,
|
||||
to: (i + 0.5) * 100_000,
|
||||
@@ -282,13 +288,13 @@ function Button({
|
||||
}
|
||||
|
||||
function setTimeScale({
|
||||
charts,
|
||||
timeScale,
|
||||
scale,
|
||||
days,
|
||||
year,
|
||||
range,
|
||||
}: {
|
||||
charts: RWS<IChartApi[]>;
|
||||
timeScale: Accessor<ITimeScaleApi<Time> | undefined>;
|
||||
scale: ResourceScale;
|
||||
days?: number;
|
||||
year?: number;
|
||||
@@ -307,22 +313,16 @@ function setTimeScale({
|
||||
from = new Date(GENESIS_DAY);
|
||||
}
|
||||
|
||||
charts()
|
||||
.at(0)
|
||||
?.timeScale()
|
||||
.setVisibleRange({
|
||||
from: (from.getTime() / 1000) as Time,
|
||||
to: (to.getTime() / 1000) as Time,
|
||||
});
|
||||
timeScale()?.setVisibleRange({
|
||||
from: (from.getTime() / 1000) as Time,
|
||||
to: (to.getTime() / 1000) as Time,
|
||||
});
|
||||
} else if (scale === "height") {
|
||||
if (range) {
|
||||
charts()
|
||||
.at(0)
|
||||
?.timeScale()
|
||||
.setVisibleRange({
|
||||
from: range.from as Time,
|
||||
to: range.to as Time,
|
||||
});
|
||||
timeScale()?.setVisibleRange({
|
||||
from: range.from as Time,
|
||||
to: range.to as Time,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { classPropToString } from "/src/solid/classes";
|
||||
import { createWasIdleAccessor } from "/src/solid/idle";
|
||||
import { createRWS } from "/src/solid/rws";
|
||||
|
||||
import { Box } from "../box";
|
||||
@@ -24,18 +25,18 @@ export function ChartFrame({
|
||||
dark: Accessor<boolean>;
|
||||
standalone: boolean;
|
||||
}) {
|
||||
const legend = createRWS<SeriesLegend[]>([]);
|
||||
const legend = createRWS<SeriesLegend[]>([], { equals: false });
|
||||
|
||||
const charts = createRWS<IChartApi[]>([]);
|
||||
|
||||
const div = createRWS<HTMLDivElement | undefined>(undefined);
|
||||
const firstChart = createRWS<IChartApi | undefined>(undefined);
|
||||
|
||||
const scale = createMemo(() => presets.selected().scale);
|
||||
|
||||
const activeIds = createRWS([] as number[], { equals: false });
|
||||
|
||||
const Chart = lazy(() =>
|
||||
import("./components/chart").then((d) => ({ default: d.Chart })),
|
||||
const wasIdle = createWasIdleAccessor();
|
||||
|
||||
const Charts = lazy(() =>
|
||||
import("./components/charts").then((d) => ({ default: d.Charts })),
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -49,7 +50,13 @@ export function ChartFrame({
|
||||
display: (hide ? hide() : false) ? "none" : undefined,
|
||||
}}
|
||||
>
|
||||
<Box flex={false} dark padded={false} classes="short:hidden">
|
||||
<Box
|
||||
flex={false}
|
||||
dark
|
||||
padded={false}
|
||||
spaced={false}
|
||||
classes="short:hidden"
|
||||
>
|
||||
<Title presets={presets} />
|
||||
|
||||
<div class="border-lighter border-t" />
|
||||
@@ -68,19 +75,20 @@ export function ChartFrame({
|
||||
</div>
|
||||
</Box>
|
||||
|
||||
<div ref={div.set} class="-mr-2 -mt-2 flex min-h-0 flex-1 flex-col">
|
||||
<Chart
|
||||
parentDiv={div}
|
||||
charts={charts}
|
||||
datasets={datasets}
|
||||
legendSetter={legend.set}
|
||||
presets={presets}
|
||||
dark={dark}
|
||||
activeIds={activeIds}
|
||||
/>
|
||||
<div class="-mr-2 -mt-2 flex min-h-0 flex-1 flex-col">
|
||||
<Show when={wasIdle()}>
|
||||
<Charts
|
||||
firstChartSetter={firstChart.set}
|
||||
datasets={datasets}
|
||||
legendSetter={legend.set}
|
||||
preset={presets.selected}
|
||||
dark={dark}
|
||||
activeIds={activeIds}
|
||||
/>
|
||||
</Show>
|
||||
</div>
|
||||
|
||||
<TimeScale charts={charts} scale={scale} />
|
||||
<TimeScale firstChart={firstChart} scale={scale} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ export function SettingsFrame({
|
||||
<div class="space-y-4">
|
||||
<Title>General</Title>
|
||||
|
||||
<RadioGroup
|
||||
<FieldRadioGroup
|
||||
title="Theme"
|
||||
ariaTitle="App's theme"
|
||||
description="Options for the app's theme"
|
||||
@@ -47,14 +47,14 @@ export function SettingsFrame({
|
||||
<div class="space-y-4">
|
||||
<Title>Background</Title>
|
||||
|
||||
<RadioGroup
|
||||
<FieldRadioGroup
|
||||
title="Mode"
|
||||
ariaTitle="Background mode"
|
||||
description="Options for how the background in displayed"
|
||||
sl={backgroundMode}
|
||||
/>
|
||||
|
||||
<RadioGroup
|
||||
<FieldRadioGroup
|
||||
title="Opacity"
|
||||
ariaTitle="Background mode"
|
||||
description="Options for the opacity of the text in the background"
|
||||
@@ -232,7 +232,7 @@ function Title({ children }: ParentProps) {
|
||||
return <p class="text-base font-medium">{children}</p>;
|
||||
}
|
||||
|
||||
function RadioGroup<
|
||||
export function FieldRadioGroup<
|
||||
T extends
|
||||
| string
|
||||
| {
|
||||
@@ -256,31 +256,54 @@ function RadioGroup<
|
||||
|
||||
<p class="pb-1 text-sm opacity-50">{description}</p>
|
||||
|
||||
<div class="border-superlight -mx-2 mt-2 flex gap-1.5 rounded-lg border bg-stone-400/30 p-1.5 backdrop-blur-[2px] dark:bg-stone-950/75">
|
||||
<For each={sl.list()}>
|
||||
{(value) => (
|
||||
<label
|
||||
class={classPropToString([
|
||||
value === sl.selected()
|
||||
? "border-lighter bg-orange-50/75 shadow dark:bg-orange-200/10"
|
||||
: "border-transparent",
|
||||
"flex flex-1 cursor-pointer select-none items-center justify-center rounded-md border px-3 py-1.5 font-medium hover:bg-orange-50 focus:outline-none active:scale-95 active:bg-orange-50 dark:hover:bg-orange-200/20 dark:active:bg-orange-200/10",
|
||||
])}
|
||||
>
|
||||
<input
|
||||
type="radio"
|
||||
name={`${title}-option`}
|
||||
value={typeof value === "object" ? value.value : value}
|
||||
class="sr-only"
|
||||
onClick={() => {
|
||||
sl.select(value);
|
||||
}}
|
||||
/>
|
||||
<span>{typeof value === "object" ? value.text : value}</span>
|
||||
</label>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
<RadioGroup sl={sl} title={title} />
|
||||
</fieldset>
|
||||
);
|
||||
}
|
||||
|
||||
export function RadioGroup<
|
||||
T extends
|
||||
| string
|
||||
| {
|
||||
text: string;
|
||||
value: number;
|
||||
},
|
||||
>({ title, sl, size }: { title: string; sl: SL<T>; size?: Size }) {
|
||||
return (
|
||||
<div
|
||||
class={classPropToString([
|
||||
size === "xs" && "gap-0.5 rounded-md border p-0.5 text-xs",
|
||||
size === "sm" && "gap-1 rounded-md border p-1 text-sm",
|
||||
(!size || size === "base") && "gap-1.5 rounded-lg border p-1.5",
|
||||
"border-superlight -mx-2 mt-2 flex bg-stone-400/30 backdrop-blur-[2px] dark:bg-stone-950/75",
|
||||
])}
|
||||
>
|
||||
<For each={sl.list()}>
|
||||
{(value) => (
|
||||
<label
|
||||
class={classPropToString([
|
||||
size === "xs" && "rounded px-1.5 py-0",
|
||||
size === "sm" && "rounded px-2 py-1",
|
||||
(!size || size === "base") && "rounded-md px-3 py-1.5",
|
||||
value === sl.selected()
|
||||
? "border-lighter bg-orange-50/75 shadow dark:bg-orange-200/10"
|
||||
: "border-transparent",
|
||||
"flex flex-1 cursor-pointer select-none items-center justify-center border font-medium hover:bg-orange-50 focus:outline-none active:scale-95 active:bg-orange-50 dark:hover:bg-orange-200/20 dark:active:bg-orange-200/10",
|
||||
])}
|
||||
>
|
||||
<input
|
||||
type="radio"
|
||||
name={`${title}-option`}
|
||||
value={typeof value === "object" ? value.value : value}
|
||||
class="sr-only"
|
||||
onClick={() => {
|
||||
sl.select(value);
|
||||
}}
|
||||
/>
|
||||
<span>{typeof value === "object" ? value.text : value}</span>
|
||||
</label>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Vendored
+2
@@ -5,3 +5,5 @@ type FrameName =
|
||||
| "Search"
|
||||
| "History"
|
||||
| "Settings";
|
||||
|
||||
type Size = "xs" | "sm" | "base" | "lg" | "xl";
|
||||
|
||||
@@ -8,17 +8,15 @@ import { colors } from "../utils/colors";
|
||||
import { valueToString } from "../utils/locale";
|
||||
import { HorzScaleBehaviorHeight } from "./horzScaleBehavior";
|
||||
|
||||
export function createChart(
|
||||
scale: ResourceScale,
|
||||
element: HTMLElement,
|
||||
{
|
||||
dark,
|
||||
priceScaleOptions,
|
||||
}: {
|
||||
dark: Accessor<boolean>;
|
||||
priceScaleOptions?: DeepPartialPriceScaleOptions;
|
||||
},
|
||||
) {
|
||||
export function createChart({
|
||||
scale,
|
||||
element,
|
||||
dark,
|
||||
}: {
|
||||
scale: ResourceScale;
|
||||
element: HTMLElement;
|
||||
dark: Accessor<boolean>;
|
||||
}) {
|
||||
console.log(`chart: create (scale: ${scale})`);
|
||||
|
||||
const options: DeepPartialChartOptions = {
|
||||
@@ -63,11 +61,9 @@ export function createChart(
|
||||
}
|
||||
|
||||
chart.priceScale("right").applyOptions({
|
||||
...priceScaleOptions,
|
||||
scaleMargins: {
|
||||
top: 0.075,
|
||||
bottom: 0.075,
|
||||
...priceScaleOptions?.scaleMargins,
|
||||
},
|
||||
minimumWidth: 78,
|
||||
});
|
||||
|
||||
@@ -0,0 +1,209 @@
|
||||
import { createRWS } from "/src/solid/rws";
|
||||
|
||||
import { chunkIdToIndex } from "../datasets/resource";
|
||||
import { SeriesType } from "../presets/enums";
|
||||
import { stringToId } from "../utils/id";
|
||||
import { createBaseLineSeries, DEFAULT_BASELINE_COLORS } from "./baseLine";
|
||||
import { createCandlesticksSeries } from "./candlesticks";
|
||||
import { createHistogramSeries } from "./histogram";
|
||||
import { createSeriesLegend } from "./legend";
|
||||
import { createLineSeries } from "./line";
|
||||
|
||||
export function createSeriesGroup<Scale extends ResourceScale>({
|
||||
scale,
|
||||
datasets,
|
||||
activeIds,
|
||||
seriesConfig,
|
||||
preset,
|
||||
chartLegend,
|
||||
chart,
|
||||
index: seriesIndex,
|
||||
disabled,
|
||||
lastActiveIndex,
|
||||
debouncedSetMinMaxMarkers,
|
||||
dark,
|
||||
}: {
|
||||
scale: Scale;
|
||||
datasets: Datasets;
|
||||
activeIds: Accessor<number[]>;
|
||||
seriesConfig: SeriesConfig;
|
||||
preset: Preset;
|
||||
chart: IChartApi;
|
||||
index: number;
|
||||
chartLegend: SeriesLegend[];
|
||||
lastActiveIndex: Accessor<number | undefined>;
|
||||
disabled?: Accessor<boolean>;
|
||||
debouncedSetMinMaxMarkers: VoidFunction;
|
||||
dark: Accessor<boolean>;
|
||||
}) {
|
||||
const {
|
||||
datasetPath,
|
||||
title,
|
||||
colors,
|
||||
color,
|
||||
defaultVisible,
|
||||
seriesType: type,
|
||||
options,
|
||||
priceScaleOptions,
|
||||
} = seriesConfig;
|
||||
|
||||
const dataset = datasets.getOrImport(
|
||||
scale,
|
||||
datasetPath as DatasetPath<Scale>,
|
||||
);
|
||||
|
||||
const seriesList: RWS<
|
||||
ISeriesApi<"Baseline" | "Line" | "Histogram" | "Candlestick"> | undefined
|
||||
>[] = new Array(dataset.fetchedJSONs.length);
|
||||
|
||||
const legend = createSeriesLegend({
|
||||
scale,
|
||||
id: stringToId(title),
|
||||
presetId: preset.id,
|
||||
title,
|
||||
seriesList,
|
||||
color: colors || color || DEFAULT_BASELINE_COLORS,
|
||||
defaultVisible,
|
||||
disabled,
|
||||
dataset,
|
||||
});
|
||||
|
||||
chartLegend.push(legend);
|
||||
|
||||
dataset.fetchedJSONs.forEach((json, index) => {
|
||||
const series: (typeof seriesList)[number] = createRWS(undefined);
|
||||
|
||||
seriesList[index] = series;
|
||||
|
||||
createEffect(() => {
|
||||
const values = json.vec();
|
||||
|
||||
if (!values) return;
|
||||
|
||||
if (seriesIndex > 0) {
|
||||
let previous = chartLegend.at(seriesIndex - 1)?.seriesList[index];
|
||||
|
||||
if (!previous?.()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
untrack(() => {
|
||||
let s = series();
|
||||
|
||||
if (!s) {
|
||||
switch (type) {
|
||||
case SeriesType.Based: {
|
||||
s = createBaseLineSeries({
|
||||
chart,
|
||||
dark,
|
||||
color,
|
||||
topColor: seriesConfig.topColor,
|
||||
bottomColor: seriesConfig.bottomColor,
|
||||
options,
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
case SeriesType.Candlestick: {
|
||||
const candlestickSeries = createCandlesticksSeries({
|
||||
chart,
|
||||
options,
|
||||
dark,
|
||||
});
|
||||
|
||||
s = candlestickSeries[0];
|
||||
|
||||
if (!colors && !color) {
|
||||
legend.color = candlestickSeries[1];
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case SeriesType.Histogram: {
|
||||
s = createHistogramSeries({
|
||||
chart,
|
||||
options,
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
case SeriesType.Line: {
|
||||
s = createLineSeries({
|
||||
chart,
|
||||
color,
|
||||
dark,
|
||||
options,
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (priceScaleOptions) {
|
||||
s.priceScale().applyOptions(priceScaleOptions);
|
||||
}
|
||||
|
||||
series.set(s);
|
||||
}
|
||||
|
||||
s.setData(values);
|
||||
|
||||
debouncedSetMinMaxMarkers();
|
||||
});
|
||||
});
|
||||
|
||||
createEffect(() => {
|
||||
const s = series();
|
||||
const currentVec = dataset.fetchedJSONs.at(index)?.vec();
|
||||
const nextVec = dataset.fetchedJSONs.at(index + 1)?.vec();
|
||||
|
||||
if (s && currentVec?.length && nextVec?.length) {
|
||||
s.update(nextVec[0]);
|
||||
}
|
||||
});
|
||||
|
||||
const isLast = createMemo(() => {
|
||||
const last = lastActiveIndex();
|
||||
return last !== undefined && last === index;
|
||||
});
|
||||
|
||||
createEffect(() => {
|
||||
series()?.applyOptions({
|
||||
lastValueVisible: legend.drawn() && isLast(),
|
||||
});
|
||||
});
|
||||
|
||||
const inRange = createMemo(() => {
|
||||
const range = activeIds();
|
||||
|
||||
if (range.length) {
|
||||
const start = chunkIdToIndex(scale, range.at(0)!);
|
||||
const end = chunkIdToIndex(scale, range.at(-1)!);
|
||||
|
||||
if (index >= start && index <= end) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
const visible = createMemo((previous: boolean) => {
|
||||
if (legend.disabled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return previous || inRange();
|
||||
}, false);
|
||||
|
||||
createEffect(() => {
|
||||
series()?.applyOptions({
|
||||
visible: legend.drawn() && visible(),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return legend;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
import { dateFromTime, getNumberOfDaysBetweenTwoDates } from "../utils/date";
|
||||
import { debounce } from "../utils/debounce";
|
||||
|
||||
export const debouncedUpdateVisiblePriceSeriesType = debounce(
|
||||
updateVisiblePriceSeriesType,
|
||||
50,
|
||||
);
|
||||
|
||||
export function updateVisiblePriceSeriesType({
|
||||
scale,
|
||||
chart,
|
||||
logicalRange,
|
||||
timeRange,
|
||||
priceSeriesType,
|
||||
}: {
|
||||
scale: ResourceScale;
|
||||
chart: IChartApi;
|
||||
logicalRange?: LogicalRange;
|
||||
timeRange?: TimeRange;
|
||||
priceSeriesType: RWS<PriceSeriesType>;
|
||||
}) {
|
||||
try {
|
||||
const width = chart.timeScale().width();
|
||||
|
||||
let ratio: number;
|
||||
|
||||
if (logicalRange) {
|
||||
ratio = (logicalRange.to - logicalRange.from) / width;
|
||||
} else if (timeRange) {
|
||||
if (scale === "date") {
|
||||
ratio = getNumberOfDaysBetweenTwoDates(
|
||||
dateFromTime(timeRange.from),
|
||||
dateFromTime(timeRange.to),
|
||||
);
|
||||
} else {
|
||||
ratio = ((timeRange.to as number) - (timeRange.from as number)) / width;
|
||||
}
|
||||
} else {
|
||||
throw Error();
|
||||
}
|
||||
|
||||
if (ratio <= 0.5) {
|
||||
priceSeriesType.set("Candlestick");
|
||||
} else {
|
||||
priceSeriesType.set("Line");
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
@@ -15,6 +15,7 @@ export function createPresets(scale: ResourceScale): PartialPresetFolder {
|
||||
name: `Total Non Empty Addresses`,
|
||||
title: `Total Non Empty Address`,
|
||||
description: "",
|
||||
unit: "Count",
|
||||
icon: IconTablerWallet,
|
||||
bottom: [
|
||||
{
|
||||
@@ -29,6 +30,7 @@ export function createPresets(scale: ResourceScale): PartialPresetFolder {
|
||||
name: `New Addresses`,
|
||||
title: `New Addresses`,
|
||||
description: "",
|
||||
unit: "Count",
|
||||
icon: IconTablerSparkles,
|
||||
bottom: [
|
||||
{
|
||||
@@ -43,6 +45,7 @@ export function createPresets(scale: ResourceScale): PartialPresetFolder {
|
||||
name: `Total Addresses Created`,
|
||||
title: `Total Addresses Created`,
|
||||
description: "",
|
||||
unit: "Count",
|
||||
icon: IconTablerArchive,
|
||||
bottom: [
|
||||
{
|
||||
@@ -57,6 +60,7 @@ export function createPresets(scale: ResourceScale): PartialPresetFolder {
|
||||
name: `Total Empty Addresses`,
|
||||
title: `Total Empty Addresses`,
|
||||
description: "",
|
||||
unit: "Count",
|
||||
icon: IconTablerTrash,
|
||||
bottom: [
|
||||
{
|
||||
@@ -177,8 +181,9 @@ export function createAddressCountPreset({
|
||||
scale,
|
||||
name: `Address Count`,
|
||||
title: `${name} Address Count`,
|
||||
icon: IconTablerAddressBook,
|
||||
description: "",
|
||||
unit: "Count",
|
||||
icon: IconTablerAddressBook,
|
||||
bottom: [addressCount],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,669 +0,0 @@
|
||||
import { requestIdleCallbackPossible } from "/src/env";
|
||||
import { createRWS } from "/src/solid/rws";
|
||||
|
||||
import { chunkIdToIndex } from "../datasets/resource";
|
||||
import {
|
||||
createBaseLineSeries,
|
||||
DEFAULT_BASELINE_COLORS,
|
||||
} from "../lightweightCharts/baseLine";
|
||||
import { createCandlesticksSeries } from "../lightweightCharts/candlesticks";
|
||||
import { createChart } from "../lightweightCharts/create";
|
||||
import { createHistogramSeries } from "../lightweightCharts/histogram";
|
||||
import { createSeriesLegend } from "../lightweightCharts/legend";
|
||||
import { createLineSeries } from "../lightweightCharts/line";
|
||||
import { setMinMaxMarkers } from "../lightweightCharts/markers";
|
||||
import {
|
||||
getInitialTimeRange,
|
||||
initTimeScale,
|
||||
setActiveIds,
|
||||
setInitialTimeRange,
|
||||
} from "../lightweightCharts/time";
|
||||
import { setWhitespace } from "../lightweightCharts/whitespace";
|
||||
import { colors } from "../utils/colors";
|
||||
import { dateFromTime, getNumberOfDaysBetweenTwoDates } from "../utils/date";
|
||||
import { debounce } from "../utils/debounce";
|
||||
import { stringToId } from "../utils/id";
|
||||
import { webSockets } from "../ws";
|
||||
import { SeriesType } from "./enums";
|
||||
|
||||
export function applySeriesList({
|
||||
parentDiv,
|
||||
charts: reactiveChartList,
|
||||
top,
|
||||
bottom,
|
||||
preset,
|
||||
priceScaleOptions,
|
||||
datasets,
|
||||
priceDataset,
|
||||
priceOptions,
|
||||
legendSetter,
|
||||
dark,
|
||||
activeIds,
|
||||
}: {
|
||||
charts: RWS<IChartApi[]>;
|
||||
parentDiv: HTMLDivElement;
|
||||
preset: Preset;
|
||||
legendSetter: Setter<SeriesLegend[]>;
|
||||
priceDataset?: AnyDatasetPath;
|
||||
priceOptions?: PriceSeriesOptions;
|
||||
// priceScaleOptions?: DeepPartialPriceScaleOptions;
|
||||
// top?: SeriesConfig<Scale>[];
|
||||
// bottom?: SeriesConfig<Scale>[];
|
||||
datasets: Datasets;
|
||||
dark: Accessor<boolean>;
|
||||
activeIds: RWS<number[]>;
|
||||
} & PresetParams) {
|
||||
// ---
|
||||
// Reset states
|
||||
// ---
|
||||
|
||||
legendSetter([]);
|
||||
|
||||
reactiveChartList.set((charts) => {
|
||||
charts.forEach((chart) => {
|
||||
chart.remove();
|
||||
});
|
||||
|
||||
return [];
|
||||
});
|
||||
|
||||
parentDiv.replaceChildren();
|
||||
|
||||
// ---
|
||||
// Done
|
||||
// ---
|
||||
|
||||
const scale = preset.scale;
|
||||
|
||||
const presetLegend: SeriesLegend[] = [];
|
||||
|
||||
const priceSeriesType = createRWS<PriceSeriesType>("Candlestick");
|
||||
|
||||
const activeDatasets: ResourceDataset<any, any>[] = [];
|
||||
|
||||
const lastActiveIndex = createMemo(() => {
|
||||
const last = activeIds().at(-1);
|
||||
return last !== undefined ? chunkIdToIndex(scale, last) : undefined;
|
||||
});
|
||||
|
||||
const exactRange = createRWS(getInitialTimeRange(scale));
|
||||
|
||||
setActiveIds({
|
||||
exactRange: exactRange(),
|
||||
activeIds: activeIds,
|
||||
});
|
||||
|
||||
const seriesNumber = 1 + (top || []).length + (bottom || []).length;
|
||||
|
||||
const charts = [top || [], bottom]
|
||||
.flatMap((list) => (list ? [list] : []))
|
||||
.flatMap((seriesConfigList, chartIndex) => {
|
||||
if (chartIndex !== 0 && seriesConfigList.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const div = document.createElement("div");
|
||||
|
||||
div.className = "w-full cursor-crosshair min-h-0 border-lighter h-full";
|
||||
|
||||
parentDiv.appendChild(div);
|
||||
|
||||
const chart = createChart(scale, div, {
|
||||
dark,
|
||||
priceScaleOptions,
|
||||
});
|
||||
|
||||
if (!chart) {
|
||||
console.log("chart: undefined");
|
||||
return [];
|
||||
}
|
||||
|
||||
const whitespace = setWhitespace(chart, scale);
|
||||
|
||||
const range = exactRange();
|
||||
|
||||
setInitialTimeRange({ chart, range });
|
||||
|
||||
if (chartIndex === 0) {
|
||||
initTimeScale({
|
||||
scale,
|
||||
chart,
|
||||
activeIds: activeIds,
|
||||
exactRange,
|
||||
});
|
||||
|
||||
if (range) {
|
||||
updateVisiblePriceSeriesType({
|
||||
scale,
|
||||
chart,
|
||||
priceSeriesType,
|
||||
timeRange: range,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// const whitespace = new Array<ISeriesApi<"Line"> | undefined>(
|
||||
// scale === "date"
|
||||
// ? whitespaceDateDatasets.length
|
||||
// : whitespaceHeightDatasets.length,
|
||||
// ).fill(undefined);
|
||||
|
||||
// function createWhitespaceSeriesIfNeeded(index: number) {
|
||||
// console.log(index);
|
||||
// if (index >= 0 && index < whitespace.length && !whitespace[index]) {
|
||||
// const series = createLineSeries(chart);
|
||||
// whitespace[index] = series;
|
||||
|
||||
// if (scale === "date") {
|
||||
// series.setData(whitespaceDateDatasets[index]);
|
||||
// } else {
|
||||
// series.setData(whitespaceHeightDatasets[index]);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// createEffect(() => {
|
||||
// const ids = activeIds();
|
||||
// console.log(ids);
|
||||
|
||||
// const idsLength = ids.length;
|
||||
// for (let i = 0; i < idsLength; i++) {
|
||||
// const id = ids[i];
|
||||
|
||||
// const whitespaceIndex = chunkIdToIndex(
|
||||
// scale,
|
||||
// scale === "date"
|
||||
// ? id - whitespaceStartDateYear
|
||||
// : id - whitespaceHeightStart,
|
||||
// );
|
||||
|
||||
// if (i === 0) {
|
||||
// createWhitespaceSeriesIfNeeded(whitespaceIndex - 1);
|
||||
// }
|
||||
|
||||
// createWhitespaceSeriesIfNeeded(whitespaceIndex);
|
||||
|
||||
// if (i === idsLength - 1) {
|
||||
// createWhitespaceSeriesIfNeeded(whitespaceIndex + 1);
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
|
||||
const chartLegend: SeriesLegend[] = [];
|
||||
|
||||
onCleanup(() => {
|
||||
chartLegend.length = 0;
|
||||
});
|
||||
|
||||
const markerCallback = () =>
|
||||
setMinMaxMarkers({
|
||||
scale,
|
||||
visibleRange: exactRange(),
|
||||
legendList: chartLegend,
|
||||
dark,
|
||||
activeIds: activeIds,
|
||||
});
|
||||
|
||||
const debouncedSetMinMaxMarkers = requestIdleCallbackPossible
|
||||
? () => requestIdleCallback(markerCallback)
|
||||
: debounce(
|
||||
markerCallback,
|
||||
seriesNumber * 10 + scale === "date" ? 50 : 100,
|
||||
);
|
||||
|
||||
createEffect(on([exactRange, dark], debouncedSetMinMaxMarkers));
|
||||
|
||||
if (chartIndex === 0) {
|
||||
const datasetPath =
|
||||
priceDataset || (`/${scale}-to-price` satisfies AnyDatasetPath);
|
||||
|
||||
const dataset = datasets.getOrImport(scale, datasetPath);
|
||||
|
||||
activeDatasets.push(dataset);
|
||||
|
||||
const title = priceOptions?.title || "Price";
|
||||
|
||||
const priceScaleOptions: DeepPartialPriceScaleOptions = {
|
||||
mode: 1,
|
||||
...priceOptions?.priceScaleOptions,
|
||||
};
|
||||
|
||||
function createPriceSeries(seriesType: PriceSeriesType) {
|
||||
let seriesConfig: SeriesConfig;
|
||||
|
||||
if (seriesType === "Candlestick") {
|
||||
seriesConfig = {
|
||||
// @ts-ignore
|
||||
datasetPath,
|
||||
title,
|
||||
seriesType: SeriesType.Candlestick,
|
||||
options: priceOptions,
|
||||
priceScaleOptions,
|
||||
};
|
||||
} else {
|
||||
seriesConfig = {
|
||||
// @ts-ignore
|
||||
datasetPath,
|
||||
title,
|
||||
color: colors.white,
|
||||
options: priceOptions?.seriesOptions,
|
||||
priceScaleOptions,
|
||||
};
|
||||
}
|
||||
|
||||
const priceSeries = createSeriesGroup({
|
||||
scale,
|
||||
datasets,
|
||||
index: -1,
|
||||
activeIds,
|
||||
seriesConfig,
|
||||
chart,
|
||||
chartLegend,
|
||||
lastActiveIndex,
|
||||
preset,
|
||||
disabled: () => priceSeriesType() !== seriesType,
|
||||
debouncedSetMinMaxMarkers,
|
||||
dark,
|
||||
});
|
||||
|
||||
createEffect(() => {
|
||||
const latest = webSockets.liveKrakenCandle.latest();
|
||||
|
||||
if (!latest) return;
|
||||
|
||||
const index = chunkIdToIndex(scale, latest.year);
|
||||
|
||||
const series = priceSeries.seriesList.at(index)?.();
|
||||
|
||||
series?.update(latest);
|
||||
});
|
||||
|
||||
return priceSeries;
|
||||
}
|
||||
|
||||
const priceCandlestickLegend = createPriceSeries("Candlestick");
|
||||
const priceLineLegend = createPriceSeries("Line");
|
||||
|
||||
createEffect(() => {
|
||||
priceCandlestickLegend.visible.set(priceLineLegend.visible());
|
||||
});
|
||||
|
||||
createEffect(() => {
|
||||
priceLineLegend.visible.set(priceCandlestickLegend.visible());
|
||||
});
|
||||
}
|
||||
|
||||
[...seriesConfigList].reverse().forEach((seriesConfig, index) => {
|
||||
const dataset = datasets.getOrImport(scale, seriesConfig.datasetPath);
|
||||
|
||||
activeDatasets.push(dataset);
|
||||
|
||||
createSeriesGroup({
|
||||
scale,
|
||||
datasets,
|
||||
activeIds: activeIds,
|
||||
index,
|
||||
seriesConfig,
|
||||
chartLegend,
|
||||
chart,
|
||||
preset,
|
||||
lastActiveIndex,
|
||||
debouncedSetMinMaxMarkers,
|
||||
dark,
|
||||
});
|
||||
});
|
||||
|
||||
chartLegend.forEach((legend) => {
|
||||
presetLegend.splice(0, 0, legend);
|
||||
|
||||
createEffect(on(legend.visible, debouncedSetMinMaxMarkers));
|
||||
});
|
||||
|
||||
return [
|
||||
{
|
||||
scale,
|
||||
div,
|
||||
chart,
|
||||
whitespace,
|
||||
legendList: chartLegend,
|
||||
debouncedSetMinMaxMarkers,
|
||||
},
|
||||
];
|
||||
}) satisfies ChartObject[];
|
||||
|
||||
createEffect(() => {
|
||||
const visibleCharts: typeof charts = [];
|
||||
|
||||
charts.forEach((chart) => {
|
||||
if (chart.legendList.some((legend) => legend.drawn())) {
|
||||
chart.div.style.border = "";
|
||||
chart.div.style.maxHeight = "100%";
|
||||
visibleCharts.push(chart);
|
||||
} else {
|
||||
// chart.div.style.height = "100%";
|
||||
chart.div.style.maxHeight = "0px";
|
||||
chart.div.style.border = "none";
|
||||
}
|
||||
});
|
||||
|
||||
visibleCharts.forEach(({ div, chart }, index) => {
|
||||
const last = index === visibleCharts.length - 1;
|
||||
|
||||
div.style.height = last ? "100%" : "calc(100% - 62px)";
|
||||
div.style.borderBottomWidth = last ? "none" : "1px";
|
||||
div.style.marginBottom = last ? "" : "-2px";
|
||||
|
||||
chart.timeScale().applyOptions({
|
||||
visible: last,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const debouncedUpdateVisiblePriceSeriesType = debounce(
|
||||
updateVisiblePriceSeriesType,
|
||||
50,
|
||||
);
|
||||
|
||||
const activeDatasetsLength = activeDatasets.length;
|
||||
createEffect(() => {
|
||||
const range = activeIds();
|
||||
|
||||
untrack(() => {
|
||||
for (let i = 0; i < range.length; i++) {
|
||||
const id = range[i];
|
||||
for (let j = 0; j < activeDatasetsLength; j++) {
|
||||
activeDatasets[j].fetch(id);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const lastChartIndex = charts.length - 1;
|
||||
|
||||
for (let i = 0; i < charts.length; i++) {
|
||||
const chart = charts[i].chart;
|
||||
|
||||
chart.timeScale().subscribeVisibleLogicalRangeChange((logicalRange) => {
|
||||
if (!logicalRange) return;
|
||||
|
||||
// Must be the chart with the visible timeScale
|
||||
if (i === lastChartIndex) {
|
||||
debouncedUpdateVisiblePriceSeriesType({
|
||||
scale,
|
||||
chart,
|
||||
logicalRange,
|
||||
priceSeriesType,
|
||||
});
|
||||
}
|
||||
|
||||
for (let j = 0; j <= lastChartIndex; j++) {
|
||||
if (i !== j) {
|
||||
charts[j].chart.timeScale().setVisibleLogicalRange(logicalRange);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
chart.subscribeCrosshairMove(({ time, sourceEvent }) => {
|
||||
// Don't override crosshair position from scroll event
|
||||
if (time && !sourceEvent) return;
|
||||
|
||||
for (let j = 0; j <= lastChartIndex; j++) {
|
||||
const whitespace = charts[j].whitespace;
|
||||
const otherChart = charts[j].chart;
|
||||
|
||||
if (whitespace && i !== j) {
|
||||
if (time) {
|
||||
otherChart.setCrosshairPosition(NaN, time, whitespace);
|
||||
} else {
|
||||
// No time when mouse goes outside the chart
|
||||
otherChart.clearCrosshairPosition();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
legendSetter(presetLegend);
|
||||
|
||||
reactiveChartList.set(() => charts.map(({ chart }) => chart));
|
||||
}
|
||||
|
||||
export function updateVisiblePriceSeriesType({
|
||||
scale,
|
||||
chart,
|
||||
logicalRange,
|
||||
timeRange,
|
||||
priceSeriesType,
|
||||
}: {
|
||||
scale: ResourceScale;
|
||||
chart: IChartApi;
|
||||
logicalRange?: LogicalRange;
|
||||
timeRange?: TimeRange;
|
||||
priceSeriesType: RWS<PriceSeriesType>;
|
||||
}) {
|
||||
try {
|
||||
const width = chart.timeScale().width();
|
||||
|
||||
let ratio: number;
|
||||
|
||||
if (logicalRange) {
|
||||
ratio = (logicalRange.to - logicalRange.from) / width;
|
||||
} else if (timeRange) {
|
||||
if (scale === "date") {
|
||||
ratio = getNumberOfDaysBetweenTwoDates(
|
||||
dateFromTime(timeRange.from),
|
||||
dateFromTime(timeRange.to),
|
||||
);
|
||||
} else {
|
||||
ratio = ((timeRange.to as number) - (timeRange.from as number)) / width;
|
||||
}
|
||||
} else {
|
||||
throw Error();
|
||||
}
|
||||
|
||||
if (ratio <= 0.5) {
|
||||
priceSeriesType.set("Candlestick");
|
||||
} else {
|
||||
priceSeriesType.set("Line");
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
|
||||
function createSeriesGroup<Scale extends ResourceScale>({
|
||||
scale,
|
||||
datasets,
|
||||
activeIds,
|
||||
seriesConfig,
|
||||
preset,
|
||||
chartLegend,
|
||||
chart,
|
||||
index: seriesIndex,
|
||||
disabled,
|
||||
lastActiveIndex,
|
||||
debouncedSetMinMaxMarkers,
|
||||
dark,
|
||||
}: {
|
||||
scale: Scale;
|
||||
datasets: Datasets;
|
||||
activeIds: Accessor<number[]>;
|
||||
seriesConfig: SeriesConfig;
|
||||
preset: Preset;
|
||||
chart: IChartApi;
|
||||
index: number;
|
||||
chartLegend: SeriesLegend[];
|
||||
lastActiveIndex: Accessor<number | undefined>;
|
||||
disabled?: Accessor<boolean>;
|
||||
debouncedSetMinMaxMarkers: VoidFunction;
|
||||
dark: Accessor<boolean>;
|
||||
}) {
|
||||
const {
|
||||
datasetPath,
|
||||
title,
|
||||
colors,
|
||||
color,
|
||||
defaultVisible,
|
||||
seriesType: type,
|
||||
options,
|
||||
priceScaleOptions,
|
||||
} = seriesConfig;
|
||||
|
||||
const dataset = datasets.getOrImport(
|
||||
scale,
|
||||
datasetPath as DatasetPath<Scale>,
|
||||
);
|
||||
|
||||
const seriesList: RWS<
|
||||
ISeriesApi<"Baseline" | "Line" | "Histogram" | "Candlestick"> | undefined
|
||||
>[] = new Array(dataset.fetchedJSONs.length);
|
||||
|
||||
const legend = createSeriesLegend({
|
||||
scale,
|
||||
id: stringToId(title),
|
||||
presetId: preset.id,
|
||||
title,
|
||||
seriesList,
|
||||
color: colors || color || DEFAULT_BASELINE_COLORS,
|
||||
defaultVisible,
|
||||
disabled,
|
||||
dataset,
|
||||
});
|
||||
|
||||
chartLegend.push(legend);
|
||||
|
||||
dataset.fetchedJSONs.forEach((json, index) => {
|
||||
const series: (typeof seriesList)[number] = createRWS(undefined);
|
||||
|
||||
seriesList[index] = series;
|
||||
|
||||
createEffect(() => {
|
||||
const values = json.vec();
|
||||
|
||||
if (!values) return;
|
||||
|
||||
if (seriesIndex > 0) {
|
||||
let previous = chartLegend.at(seriesIndex - 1)?.seriesList[index];
|
||||
|
||||
if (!previous?.()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
untrack(() => {
|
||||
let s = series();
|
||||
|
||||
if (!s) {
|
||||
switch (type) {
|
||||
case SeriesType.Based: {
|
||||
s = createBaseLineSeries({
|
||||
chart,
|
||||
dark,
|
||||
color,
|
||||
topColor: seriesConfig.topColor,
|
||||
bottomColor: seriesConfig.bottomColor,
|
||||
options,
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
case SeriesType.Candlestick: {
|
||||
const candlestickSeries = createCandlesticksSeries({
|
||||
chart,
|
||||
options,
|
||||
dark,
|
||||
});
|
||||
|
||||
s = candlestickSeries[0];
|
||||
|
||||
if (!colors && !color) {
|
||||
legend.color = candlestickSeries[1];
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case SeriesType.Histogram: {
|
||||
s = createHistogramSeries({
|
||||
chart,
|
||||
options,
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
case SeriesType.Line: {
|
||||
s = createLineSeries({
|
||||
chart,
|
||||
color,
|
||||
dark,
|
||||
options,
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (priceScaleOptions) {
|
||||
s.priceScale().applyOptions(priceScaleOptions);
|
||||
}
|
||||
|
||||
series.set(s);
|
||||
}
|
||||
|
||||
s.setData(values);
|
||||
|
||||
debouncedSetMinMaxMarkers();
|
||||
});
|
||||
});
|
||||
|
||||
createEffect(() => {
|
||||
const s = series();
|
||||
const currentVec = dataset.fetchedJSONs.at(index)?.vec();
|
||||
const nextVec = dataset.fetchedJSONs.at(index + 1)?.vec();
|
||||
|
||||
if (s && currentVec?.length && nextVec?.length) {
|
||||
s.update(nextVec[0]);
|
||||
}
|
||||
});
|
||||
|
||||
const isLast = createMemo(() => {
|
||||
const last = lastActiveIndex();
|
||||
return last !== undefined && last === index;
|
||||
});
|
||||
|
||||
createEffect(() => {
|
||||
series()?.applyOptions({
|
||||
lastValueVisible: legend.drawn() && isLast(),
|
||||
});
|
||||
});
|
||||
|
||||
const inRange = createMemo(() => {
|
||||
const range = activeIds();
|
||||
|
||||
if (range.length) {
|
||||
const start = chunkIdToIndex(scale, range.at(0)!);
|
||||
const end = chunkIdToIndex(scale, range.at(-1)!);
|
||||
|
||||
if (index >= start && index <= end) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
const visible = createMemo((previous: boolean) => {
|
||||
if (legend.disabled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return previous || inRange();
|
||||
}, false);
|
||||
|
||||
createEffect(() => {
|
||||
series()?.applyOptions({
|
||||
visible: legend.drawn() && visible(),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return legend;
|
||||
}
|
||||
@@ -13,6 +13,7 @@ export function createPresets(scale: ResourceScale) {
|
||||
name: "Height",
|
||||
title: "Block Height",
|
||||
description: "",
|
||||
unit: "Height",
|
||||
bottom: [
|
||||
{
|
||||
title: "Height",
|
||||
@@ -31,6 +32,7 @@ export function createPresets(scale: ResourceScale) {
|
||||
name: "Daily Sum",
|
||||
title: "Daily Sum Of Blocks Mined",
|
||||
description: "",
|
||||
unit: "Count",
|
||||
bottom: [
|
||||
{
|
||||
title: "Target",
|
||||
@@ -64,6 +66,7 @@ export function createPresets(scale: ResourceScale) {
|
||||
name: "Weekly Sum",
|
||||
title: "Weekly Sum Of Blocks Mined",
|
||||
description: "",
|
||||
unit: "Count",
|
||||
bottom: [
|
||||
{
|
||||
title: "Target",
|
||||
@@ -86,6 +89,7 @@ export function createPresets(scale: ResourceScale) {
|
||||
name: "Monthly Sum",
|
||||
title: "Monthly Sum Of Blocks Mined",
|
||||
description: "",
|
||||
unit: "Count",
|
||||
bottom: [
|
||||
{
|
||||
title: "Target",
|
||||
@@ -108,6 +112,7 @@ export function createPresets(scale: ResourceScale) {
|
||||
name: "Yearly Sum",
|
||||
title: "Yearly Sum Of Blocks Mined",
|
||||
description: "",
|
||||
unit: "Count",
|
||||
bottom: [
|
||||
{
|
||||
title: "Target",
|
||||
@@ -130,6 +135,7 @@ export function createPresets(scale: ResourceScale) {
|
||||
name: "Total",
|
||||
title: "Total Blocks Mined",
|
||||
description: "",
|
||||
unit: "Count",
|
||||
bottom: [
|
||||
{
|
||||
title: "Mined",
|
||||
@@ -147,6 +153,7 @@ export function createPresets(scale: ResourceScale) {
|
||||
scale,
|
||||
title: "Block Size",
|
||||
color: colors.darkWhite,
|
||||
unit: "Megabytes",
|
||||
keySum: "/date-to-block-size-1d-sum",
|
||||
keyAverage: "/date-to-block-size-1d-average",
|
||||
keyMax: "/date-to-block-size-1d-max",
|
||||
@@ -165,6 +172,7 @@ export function createPresets(scale: ResourceScale) {
|
||||
scale,
|
||||
title: "Block Weight",
|
||||
color: colors.darkWhite,
|
||||
unit: "Weight",
|
||||
keyAverage: "/date-to-block-weight-1d-average",
|
||||
keyMax: "/date-to-block-weight-1d-max",
|
||||
key90p: "/date-to-block-weight-1d-90p",
|
||||
@@ -182,6 +190,7 @@ export function createPresets(scale: ResourceScale) {
|
||||
scale,
|
||||
title: "Block VBytes",
|
||||
color: colors.darkWhite,
|
||||
unit: "Virtual Bytes",
|
||||
keyAverage: "/date-to-block-vbytes-1d-average",
|
||||
keyMax: "/date-to-block-vbytes-1d-max",
|
||||
key90p: "/date-to-block-vbytes-1d-90p",
|
||||
@@ -199,6 +208,7 @@ export function createPresets(scale: ResourceScale) {
|
||||
scale,
|
||||
title: "Block Interval",
|
||||
color: colors.darkWhite,
|
||||
unit: "Seconds",
|
||||
keyAverage: "/date-to-block-interval-1d-average",
|
||||
keyMax: "/date-to-block-interval-1d-max",
|
||||
key90p: "/date-to-block-interval-1d-90p",
|
||||
@@ -217,9 +227,10 @@ export function createPresets(scale: ResourceScale) {
|
||||
name: "Size",
|
||||
title: "Block Size",
|
||||
description: "",
|
||||
unit: "Megabytes",
|
||||
bottom: [
|
||||
{
|
||||
title: "Size (MB)",
|
||||
title: "Size",
|
||||
color: colors.darkWhite,
|
||||
datasetPath: `/height-to-block-size`,
|
||||
},
|
||||
@@ -231,9 +242,10 @@ export function createPresets(scale: ResourceScale) {
|
||||
name: "Weight",
|
||||
title: "Block Weight",
|
||||
description: "",
|
||||
unit: "Weight",
|
||||
bottom: [
|
||||
{
|
||||
title: "Weight (MB)",
|
||||
title: "Weight",
|
||||
color: colors.darkWhite,
|
||||
datasetPath: `/height-to-block-weight`,
|
||||
},
|
||||
@@ -245,6 +257,7 @@ export function createPresets(scale: ResourceScale) {
|
||||
name: "VBytes",
|
||||
title: "Block VBytes",
|
||||
description: "",
|
||||
unit: "Virtual Bytes",
|
||||
bottom: [
|
||||
{
|
||||
title: "VBytes",
|
||||
@@ -259,9 +272,10 @@ export function createPresets(scale: ResourceScale) {
|
||||
name: "Interval",
|
||||
title: "Block Interval",
|
||||
description: "",
|
||||
unit: "Seconds",
|
||||
bottom: [
|
||||
{
|
||||
title: "Interval (s)",
|
||||
title: "Interval",
|
||||
color: colors.darkWhite,
|
||||
datasetPath: `/height-to-block-interval`,
|
||||
},
|
||||
@@ -274,9 +288,10 @@ export function createPresets(scale: ResourceScale) {
|
||||
name: "Cumulative Size",
|
||||
title: "Cumulative Block Size",
|
||||
description: "",
|
||||
unit: "Megabytes",
|
||||
bottom: [
|
||||
{
|
||||
title: "Size (MB)",
|
||||
title: "Size",
|
||||
color: colors.darkWhite,
|
||||
datasetPath: `/${scale}-to-cumulative-block-size`,
|
||||
},
|
||||
|
||||
@@ -15,6 +15,7 @@ export function createPresets(scale: ResourceScale) {
|
||||
name: "All",
|
||||
title: "All Cointime Prices",
|
||||
description: "",
|
||||
unit: "US Dollars",
|
||||
top: [
|
||||
{
|
||||
title: "Vaulted Price",
|
||||
@@ -52,6 +53,7 @@ export function createPresets(scale: ResourceScale) {
|
||||
name: "Price",
|
||||
title: "Active Price",
|
||||
description: "",
|
||||
unit: "US Dollars",
|
||||
top: [
|
||||
{
|
||||
title: "Active Price",
|
||||
@@ -78,6 +80,7 @@ export function createPresets(scale: ResourceScale) {
|
||||
name: "Price",
|
||||
title: "Vaulted Price",
|
||||
description: "",
|
||||
unit: "US Dollars",
|
||||
top: [
|
||||
{
|
||||
title: "Vaulted Price",
|
||||
@@ -104,6 +107,7 @@ export function createPresets(scale: ResourceScale) {
|
||||
name: "Price",
|
||||
title: "True Market Mean",
|
||||
description: "",
|
||||
unit: "US Dollars",
|
||||
top: [
|
||||
{
|
||||
title: "True Market Mean",
|
||||
@@ -130,6 +134,7 @@ export function createPresets(scale: ResourceScale) {
|
||||
name: "Price",
|
||||
title: "Cointime Price",
|
||||
description: "",
|
||||
unit: "US Dollars",
|
||||
top: [
|
||||
{
|
||||
title: "Cointime",
|
||||
@@ -158,9 +163,7 @@ export function createPresets(scale: ResourceScale) {
|
||||
name: "All",
|
||||
title: "Cointime Capitalizations",
|
||||
description: "",
|
||||
priceScaleOptions: {
|
||||
mode: 1,
|
||||
},
|
||||
unit: "US Dollars",
|
||||
bottom: [
|
||||
{
|
||||
title: "Market Cap",
|
||||
@@ -190,9 +193,7 @@ export function createPresets(scale: ResourceScale) {
|
||||
name: "Thermo Cap",
|
||||
title: "Thermo Cap",
|
||||
description: "",
|
||||
priceScaleOptions: {
|
||||
mode: 1,
|
||||
},
|
||||
unit: "US Dollars",
|
||||
bottom: [
|
||||
{
|
||||
title: "Thermo Cap",
|
||||
@@ -207,10 +208,7 @@ export function createPresets(scale: ResourceScale) {
|
||||
name: "Investor Cap",
|
||||
title: "Investor Cap",
|
||||
description: "",
|
||||
|
||||
priceScaleOptions: {
|
||||
mode: 1,
|
||||
},
|
||||
unit: "US Dollars",
|
||||
bottom: [
|
||||
{
|
||||
title: "Investor Cap",
|
||||
@@ -223,8 +221,9 @@ export function createPresets(scale: ResourceScale) {
|
||||
scale,
|
||||
icon: IconTablerDivide,
|
||||
name: "Thermo Cap To Investor Cap Ratio",
|
||||
title: "Thermo Cap To Investor Cap Ratio (%)",
|
||||
title: "Thermo Cap To Investor Cap Ratio",
|
||||
description: "",
|
||||
unit: "Percentage",
|
||||
bottom: [
|
||||
{
|
||||
title: "Ratio",
|
||||
@@ -244,6 +243,7 @@ export function createPresets(scale: ResourceScale) {
|
||||
name: "All",
|
||||
title: "All Coinblocks",
|
||||
description: "",
|
||||
unit: "Coinblocks",
|
||||
bottom: [
|
||||
{
|
||||
title: "Coinblocks Created",
|
||||
@@ -268,6 +268,7 @@ export function createPresets(scale: ResourceScale) {
|
||||
name: "Created",
|
||||
title: "Coinblocks Created",
|
||||
description: "",
|
||||
unit: "Coinblocks",
|
||||
bottom: [
|
||||
{
|
||||
title: "Coinblocks Created",
|
||||
@@ -282,7 +283,7 @@ export function createPresets(scale: ResourceScale) {
|
||||
name: "Destroyed",
|
||||
title: "Coinblocks Destroyed",
|
||||
description: "",
|
||||
|
||||
unit: "Coinblocks",
|
||||
bottom: [
|
||||
{
|
||||
title: "Coinblocks Destroyed",
|
||||
@@ -297,6 +298,7 @@ export function createPresets(scale: ResourceScale) {
|
||||
name: "Stored",
|
||||
title: "Coinblocks Stored",
|
||||
description: "",
|
||||
unit: "Coinblocks",
|
||||
bottom: [
|
||||
{
|
||||
title: "Coinblocks Stored",
|
||||
@@ -316,6 +318,7 @@ export function createPresets(scale: ResourceScale) {
|
||||
name: "All",
|
||||
title: "All Cumulative Coinblocks",
|
||||
description: "",
|
||||
unit: "Coinblocks",
|
||||
bottom: [
|
||||
{
|
||||
title: "Cumulative Coinblocks Created",
|
||||
@@ -340,6 +343,7 @@ export function createPresets(scale: ResourceScale) {
|
||||
name: "Created",
|
||||
title: "Cumulative Coinblocks Created",
|
||||
description: "",
|
||||
unit: "Coinblocks",
|
||||
bottom: [
|
||||
{
|
||||
title: "Cumulative Coinblocks Created",
|
||||
@@ -354,6 +358,7 @@ export function createPresets(scale: ResourceScale) {
|
||||
name: "Destroyed",
|
||||
title: "Cumulative Coinblocks Destroyed",
|
||||
description: "",
|
||||
unit: "Coinblocks",
|
||||
bottom: [
|
||||
{
|
||||
title: "Cumulative Coinblocks Destroyed",
|
||||
@@ -368,6 +373,7 @@ export function createPresets(scale: ResourceScale) {
|
||||
name: "Stored",
|
||||
title: "Cumulative Coinblocks Stored",
|
||||
description: "",
|
||||
unit: "Coinblocks",
|
||||
bottom: [
|
||||
{
|
||||
title: "Cumulative Coinblocks Stored",
|
||||
@@ -387,6 +393,7 @@ export function createPresets(scale: ResourceScale) {
|
||||
name: "Liveliness - Activity",
|
||||
title: "Liveliness (Activity)",
|
||||
description: "",
|
||||
unit: "",
|
||||
bottom: [
|
||||
{
|
||||
title: "Liveliness",
|
||||
@@ -401,6 +408,7 @@ export function createPresets(scale: ResourceScale) {
|
||||
name: "Vaultedness",
|
||||
title: "Vaultedness",
|
||||
description: "",
|
||||
unit: "",
|
||||
bottom: [
|
||||
{
|
||||
title: "Vaultedness",
|
||||
@@ -415,6 +423,7 @@ export function createPresets(scale: ResourceScale) {
|
||||
name: "Versus",
|
||||
title: "Liveliness V. Vaultedness",
|
||||
description: "",
|
||||
unit: "",
|
||||
bottom: [
|
||||
{
|
||||
title: "Liveliness",
|
||||
@@ -434,6 +443,7 @@ export function createPresets(scale: ResourceScale) {
|
||||
name: "Activity To Vaultedness Ratio",
|
||||
title: "Activity To Vaultedness Ratio",
|
||||
description: "",
|
||||
unit: "Percentage",
|
||||
bottom: [
|
||||
{
|
||||
title: "Activity To Vaultedness Ratio",
|
||||
@@ -448,15 +458,16 @@ export function createPresets(scale: ResourceScale) {
|
||||
name: "Concurrent Liveliness - Supply Adjusted Coindays Destroyed",
|
||||
title: "Concurrent Liveliness - Supply Adjusted Coindays Destroyed",
|
||||
description: "",
|
||||
unit: "",
|
||||
bottom: [
|
||||
{
|
||||
title: "Concurrent Liveliness 14d Median",
|
||||
color: colors.darkLiveliness,
|
||||
color: colors.liveliness,
|
||||
datasetPath: `/${scale}-to-concurrent-liveliness-2w-median`,
|
||||
},
|
||||
{
|
||||
title: "Concurrent Liveliness",
|
||||
color: colors.liveliness,
|
||||
color: colors.darkLiveliness,
|
||||
datasetPath: `/${scale}-to-concurrent-liveliness`,
|
||||
},
|
||||
],
|
||||
@@ -467,6 +478,7 @@ export function createPresets(scale: ResourceScale) {
|
||||
name: "Liveliness Incremental Change",
|
||||
title: "Liveliness Incremental Change",
|
||||
description: "",
|
||||
unit: "",
|
||||
bottom: [
|
||||
{
|
||||
title: "Liveliness Incremental Change",
|
||||
@@ -493,6 +505,7 @@ export function createPresets(scale: ResourceScale) {
|
||||
name: "Vaulted",
|
||||
title: "Vaulted Supply",
|
||||
description: "",
|
||||
unit: "Bitcoin",
|
||||
bottom: [
|
||||
{
|
||||
title: "Vaulted Supply",
|
||||
@@ -507,7 +520,7 @@ export function createPresets(scale: ResourceScale) {
|
||||
name: "Active",
|
||||
title: "Active Supply",
|
||||
description: "",
|
||||
|
||||
unit: "Bitcoin",
|
||||
bottom: [
|
||||
{
|
||||
title: "Active Supply",
|
||||
@@ -522,7 +535,7 @@ export function createPresets(scale: ResourceScale) {
|
||||
name: "Vaulted V. Active",
|
||||
title: "Vaulted V. Active",
|
||||
description: "",
|
||||
|
||||
unit: "Bitcoin",
|
||||
bottom: [
|
||||
{
|
||||
title: "Circulating Supply",
|
||||
@@ -577,6 +590,7 @@ export function createPresets(scale: ResourceScale) {
|
||||
name: "Vaulted Net Change",
|
||||
title: "Vaulted Supply Net Change",
|
||||
description: "",
|
||||
unit: "Bitcoin",
|
||||
bottom: [
|
||||
{
|
||||
title: "Vaulted Supply Net Change",
|
||||
@@ -591,6 +605,7 @@ export function createPresets(scale: ResourceScale) {
|
||||
name: "Active Net Change",
|
||||
title: "Active Supply Net Change",
|
||||
description: "",
|
||||
unit: "Bitcoin",
|
||||
bottom: [
|
||||
{
|
||||
title: "Active Supply Net Change",
|
||||
@@ -605,6 +620,7 @@ export function createPresets(scale: ResourceScale) {
|
||||
name: "Active VS. Vaulted 90D Net Change",
|
||||
title: "Active VS. Vaulted 90 Day Supply Net Change",
|
||||
description: "",
|
||||
unit: "Bitcoin",
|
||||
bottom: [
|
||||
{
|
||||
title: "Active Supply Net Change",
|
||||
@@ -724,6 +740,7 @@ export function createPresets(scale: ResourceScale) {
|
||||
name: "In Profit",
|
||||
title: "Cointime Supply In Profit",
|
||||
description: "",
|
||||
unit: "Bitcoin",
|
||||
bottom: [
|
||||
{
|
||||
title: "Circulating Supply",
|
||||
@@ -748,6 +765,7 @@ export function createPresets(scale: ResourceScale) {
|
||||
name: "In Loss",
|
||||
title: "Cointime Supply In Loss",
|
||||
description: "",
|
||||
unit: "Bitcoin",
|
||||
bottom: [
|
||||
{
|
||||
title: "Circulating Supply",
|
||||
@@ -772,11 +790,9 @@ export function createPresets(scale: ResourceScale) {
|
||||
scale,
|
||||
icon: IconTablerBuildingFactory,
|
||||
name: "Cointime Yearly Inflation Rate",
|
||||
title: "Cointime-Adjusted Yearly Inflation Rate (%)",
|
||||
title: "Cointime-Adjusted Yearly Inflation Rate",
|
||||
description: "",
|
||||
priceScaleOptions: {
|
||||
mode: 1,
|
||||
},
|
||||
unit: "Percentage",
|
||||
bottom: [
|
||||
{
|
||||
title: "Cointime Adjusted",
|
||||
@@ -796,9 +812,7 @@ export function createPresets(scale: ResourceScale) {
|
||||
name: "Cointime Velocity",
|
||||
title: "Cointime-Adjusted Transactions Velocity",
|
||||
description: "",
|
||||
priceScaleOptions: {
|
||||
mode: 1,
|
||||
},
|
||||
unit: "",
|
||||
bottom: [
|
||||
{
|
||||
title: "Cointime Adjusted",
|
||||
|
||||
@@ -18,6 +18,7 @@ export function createPresets(scale: ResourceScale) {
|
||||
title: `Hodl Supply`,
|
||||
description: "",
|
||||
icon: IconTablerRipple,
|
||||
unit: "Bitcoin",
|
||||
bottom: [
|
||||
{
|
||||
title: `24h`,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { averages } from "/src/scripts/datasets/date";
|
||||
import { colors } from "/src/scripts/utils/colors";
|
||||
|
||||
import { SeriesType } from "../../enums";
|
||||
import { createRatioFolder } from "../../templates/ratio";
|
||||
|
||||
export function createPresets(scale: ResourceScale): PartialPresetFolder {
|
||||
@@ -14,6 +13,7 @@ export function createPresets(scale: ResourceScale): PartialPresetFolder {
|
||||
name: "All",
|
||||
title: "All Moving Averages",
|
||||
description: "",
|
||||
unit: "US Dollars",
|
||||
top: averages.map((average) => ({
|
||||
title: average.key.toUpperCase(),
|
||||
color: colors[`_${average.key}`],
|
||||
@@ -52,9 +52,10 @@ function createPresetFolder({
|
||||
{
|
||||
scale,
|
||||
name: "Average",
|
||||
description: "",
|
||||
icon: IconTablerMathAvg,
|
||||
title,
|
||||
description: "",
|
||||
unit: "US Dollars",
|
||||
icon: IconTablerMathAvg,
|
||||
top: [
|
||||
{
|
||||
title: `SMA`,
|
||||
|
||||
@@ -13,6 +13,7 @@ export function createPresets(scale: ResourceScale) {
|
||||
name: "Price",
|
||||
title: "Market Price",
|
||||
description: "",
|
||||
unit: "US Dollars",
|
||||
},
|
||||
{
|
||||
scale,
|
||||
@@ -20,6 +21,7 @@ export function createPresets(scale: ResourceScale) {
|
||||
name: "Capitalization",
|
||||
title: "Market Capitalization",
|
||||
description: "",
|
||||
unit: "US Dollars",
|
||||
bottom: [
|
||||
{
|
||||
title: "Market Cap.",
|
||||
|
||||
@@ -56,9 +56,10 @@ function createPreset({
|
||||
description: "",
|
||||
icon: IconTablerReceiptTax,
|
||||
title: `${title} Return`,
|
||||
unit: "Percentage",
|
||||
bottom: [
|
||||
{
|
||||
title: `Return (%)`,
|
||||
title: `Return`,
|
||||
seriesType: SeriesType.Based,
|
||||
datasetPath: `/date-to-price-${key}-return`,
|
||||
},
|
||||
|
||||
@@ -17,8 +17,9 @@ export function createPresets(scale: ResourceScale) {
|
||||
scale,
|
||||
icon: IconTablerCoinBitcoin,
|
||||
name: "In Bitcoin",
|
||||
title: "Last Coinbase (In Bitcoin)",
|
||||
title: "Last Coinbase In Bitcoin",
|
||||
description: "",
|
||||
unit: "US Dollars",
|
||||
bottom: [
|
||||
{
|
||||
title: "Last",
|
||||
@@ -31,8 +32,9 @@ export function createPresets(scale: ResourceScale) {
|
||||
scale,
|
||||
icon: IconTablerCoin,
|
||||
name: "In Dollars",
|
||||
title: "Last Coinbase (In Dollars)",
|
||||
title: "Last Coinbase In Dollars",
|
||||
description: "",
|
||||
unit: "US Dollars",
|
||||
bottom: [
|
||||
{
|
||||
title: "Last",
|
||||
@@ -51,11 +53,12 @@ export function createPresets(scale: ResourceScale) {
|
||||
scale,
|
||||
icon: IconTablerMoneybag,
|
||||
name: "In Bitcoin",
|
||||
title: "Daily Sum Of Bitcoin Coinbases",
|
||||
title: "Daily Sum Of Coinbases In Bitcoin",
|
||||
description: "",
|
||||
unit: "Bitcoin",
|
||||
bottom: [
|
||||
{
|
||||
title: "Coinbases (Bitcoin)",
|
||||
title: "Sum",
|
||||
color: colors.bitcoin,
|
||||
datasetPath: `/${scale}-to-coinbase`,
|
||||
},
|
||||
@@ -65,11 +68,12 @@ export function createPresets(scale: ResourceScale) {
|
||||
scale,
|
||||
icon: IconTablerCash,
|
||||
name: "In Dollars",
|
||||
title: "Daily Sum Of Dollar Coinbases",
|
||||
title: "Daily Sum Of Coinbases In Dollars",
|
||||
description: "",
|
||||
unit: "US Dollars",
|
||||
bottom: [
|
||||
{
|
||||
title: "Coinbases (Dollars)",
|
||||
title: "Sum",
|
||||
color: colors.dollars,
|
||||
datasetPath: `/${scale}-to-coinbase-in-dollars`,
|
||||
},
|
||||
@@ -85,11 +89,12 @@ export function createPresets(scale: ResourceScale) {
|
||||
scale,
|
||||
icon: IconTablerMoneybag,
|
||||
name: "In Bitcoin",
|
||||
title: "Yearly Sum Of Bitcoin Coinbases",
|
||||
title: "Yearly Sum Of Coinbases In Bitcoin",
|
||||
description: "",
|
||||
unit: "Bitcoin",
|
||||
bottom: [
|
||||
{
|
||||
title: "Coinbases (Bitcoin)",
|
||||
title: "Sum",
|
||||
color: colors.bitcoin,
|
||||
datasetPath: `/${scale}-to-coinbase-1y-sum`,
|
||||
},
|
||||
@@ -99,11 +104,12 @@ export function createPresets(scale: ResourceScale) {
|
||||
scale,
|
||||
icon: IconTablerCash,
|
||||
name: "In Dollars",
|
||||
title: "Yearly Sum Of Dollar Coinbases",
|
||||
title: "Yearly Sum Of Coinbases In Dollars",
|
||||
description: "",
|
||||
unit: "US Dollars",
|
||||
bottom: [
|
||||
{
|
||||
title: "Coinbases (Dollars)",
|
||||
title: "Sum",
|
||||
color: colors.dollars,
|
||||
datasetPath: `/${scale}-to-coinbase-in-dollars-1y-sum`,
|
||||
},
|
||||
@@ -119,11 +125,12 @@ export function createPresets(scale: ResourceScale) {
|
||||
scale,
|
||||
icon: IconTablerMoneybag,
|
||||
name: "In Bitcoin",
|
||||
title: "Cumulative Bitcoin Coinbases",
|
||||
title: "Cumulative Coinbases In Bitcoin",
|
||||
description: "",
|
||||
unit: "Bitcoin",
|
||||
bottom: [
|
||||
{
|
||||
title: "Coinbases (Bitcoin)",
|
||||
title: "Coinbases",
|
||||
color: colors.bitcoin,
|
||||
datasetPath: `/${scale}-to-cumulative-coinbase`,
|
||||
},
|
||||
@@ -133,11 +140,12 @@ export function createPresets(scale: ResourceScale) {
|
||||
scale,
|
||||
icon: IconTablerCash,
|
||||
name: "In Dollars",
|
||||
title: "Cumulative Dollar Coinbases",
|
||||
title: "Cumulative Coinbases In Dollars",
|
||||
description: "",
|
||||
unit: "US Dollars",
|
||||
bottom: [
|
||||
{
|
||||
title: "Coinbases (Dollars)",
|
||||
title: "Coinbases",
|
||||
color: colors.dollars,
|
||||
datasetPath: `/${scale}-to-cumulative-coinbase-in-dollars`,
|
||||
},
|
||||
@@ -162,8 +170,9 @@ export function createPresets(scale: ResourceScale) {
|
||||
scale,
|
||||
icon: IconTablerCoinBitcoin,
|
||||
name: "In Bitcoin",
|
||||
title: "Last Subsidy (In Bitcoin)",
|
||||
title: "Last Subsidy In Bitcoin",
|
||||
description: "",
|
||||
unit: "Bitcoin",
|
||||
bottom: [
|
||||
{
|
||||
title: "Last",
|
||||
@@ -176,8 +185,9 @@ export function createPresets(scale: ResourceScale) {
|
||||
scale,
|
||||
icon: IconTablerCoin,
|
||||
name: "In Dollars",
|
||||
title: "Last Subsidy (In Dollars)",
|
||||
title: "Last Subsidy In Dollars",
|
||||
description: "",
|
||||
unit: "US Dollars",
|
||||
bottom: [
|
||||
{
|
||||
title: "Last",
|
||||
@@ -196,11 +206,12 @@ export function createPresets(scale: ResourceScale) {
|
||||
scale,
|
||||
icon: IconTablerMoneybag,
|
||||
name: "In Bitcoin",
|
||||
title: "Daily Sum Of Bitcoin Subsidies",
|
||||
title: "Daily Sum Of Subsidies In Bitcoin",
|
||||
description: "",
|
||||
unit: "Bitcoin",
|
||||
bottom: [
|
||||
{
|
||||
title: "Subsidies (Bitcoin)",
|
||||
title: "Sum",
|
||||
color: colors.bitcoin,
|
||||
datasetPath: `/${scale}-to-subsidy`,
|
||||
},
|
||||
@@ -210,11 +221,12 @@ export function createPresets(scale: ResourceScale) {
|
||||
scale,
|
||||
icon: IconTablerCash,
|
||||
name: "In Dollars",
|
||||
title: "Daily Sum Of Dollar Subsidies",
|
||||
title: "Daily Sum Of Subsidies In Dollars",
|
||||
description: "",
|
||||
unit: "US Dollars",
|
||||
bottom: [
|
||||
{
|
||||
title: "Subsidies (Dollars)",
|
||||
title: "Sum",
|
||||
color: colors.dollars,
|
||||
datasetPath: `/${scale}-to-subsidy-in-dollars`,
|
||||
},
|
||||
@@ -230,11 +242,12 @@ export function createPresets(scale: ResourceScale) {
|
||||
scale,
|
||||
icon: IconTablerMoneybag,
|
||||
name: "In Bitcoin",
|
||||
title: "Yearly Sum Of Bitcoin Subsidies",
|
||||
title: "Yearly Sum Of Subsidies In Bitcoin",
|
||||
description: "",
|
||||
unit: "Bitcoin",
|
||||
bottom: [
|
||||
{
|
||||
title: "Subsidies (Bitcoin)",
|
||||
title: "Sum",
|
||||
color: colors.bitcoin,
|
||||
datasetPath: `/${scale}-to-subsidy-1y-sum`,
|
||||
},
|
||||
@@ -244,11 +257,12 @@ export function createPresets(scale: ResourceScale) {
|
||||
scale,
|
||||
icon: IconTablerCash,
|
||||
name: "In Dollars",
|
||||
title: "Yearly Sum Of Dollar Subsidies",
|
||||
title: "Yearly Sum Of Subsidies In Dollars",
|
||||
description: "",
|
||||
unit: "US Dollars",
|
||||
bottom: [
|
||||
{
|
||||
title: "Subsidies (Dollars)",
|
||||
title: "Sum",
|
||||
color: colors.dollars,
|
||||
datasetPath: `/${scale}-to-subsidy-in-dollars-1y-sum`,
|
||||
},
|
||||
@@ -264,11 +278,12 @@ export function createPresets(scale: ResourceScale) {
|
||||
scale,
|
||||
icon: IconTablerMoneybag,
|
||||
name: "In Bitcoin",
|
||||
title: "Cumulative Bitcoin Subsidies",
|
||||
title: "Cumulative Subsidies In Bitcoin",
|
||||
description: "",
|
||||
unit: "Bitcoin",
|
||||
bottom: [
|
||||
{
|
||||
title: "Subsidies (Bitcoin)",
|
||||
title: "Subsidies",
|
||||
color: colors.bitcoin,
|
||||
datasetPath: `/${scale}-to-cumulative-subsidy`,
|
||||
},
|
||||
@@ -278,11 +293,12 @@ export function createPresets(scale: ResourceScale) {
|
||||
scale,
|
||||
icon: IconTablerCash,
|
||||
name: "In Dollars",
|
||||
title: "Cumulative Dollar Subsidies",
|
||||
title: "Cumulative Subsidies In Dollars",
|
||||
description: "",
|
||||
unit: "US Dollars",
|
||||
bottom: [
|
||||
{
|
||||
title: "Subsidies (Dollars)",
|
||||
title: "Subsidies",
|
||||
color: colors.dollars,
|
||||
datasetPath: `/${scale}-to-cumulative-subsidy-in-dollars`,
|
||||
},
|
||||
@@ -307,8 +323,9 @@ export function createPresets(scale: ResourceScale) {
|
||||
scale,
|
||||
icon: IconTablerCoinBitcoin,
|
||||
name: "In Bitcoin",
|
||||
title: "Last Fees (In Bitcoin)",
|
||||
title: "Last Fees In Bitcoin",
|
||||
description: "",
|
||||
unit: "Bitcoin",
|
||||
bottom: [
|
||||
{
|
||||
title: "Last",
|
||||
@@ -321,8 +338,9 @@ export function createPresets(scale: ResourceScale) {
|
||||
scale,
|
||||
icon: IconTablerCoin,
|
||||
name: "In Dollars",
|
||||
title: "Last Fees (In Dollars)",
|
||||
title: "Last Fees In Dollars",
|
||||
description: "",
|
||||
unit: "US Dollars",
|
||||
bottom: [
|
||||
{
|
||||
title: "Last",
|
||||
@@ -341,11 +359,12 @@ export function createPresets(scale: ResourceScale) {
|
||||
scale,
|
||||
icon: IconTablerMoneybag,
|
||||
name: "In Bitcoin",
|
||||
title: "Daily Sum Of Bitcoin Fees",
|
||||
title: "Daily Sum Of Fees In Bitcoin",
|
||||
description: "",
|
||||
unit: "Bitcoin",
|
||||
bottom: [
|
||||
{
|
||||
title: "Fees (Bitcoin)",
|
||||
title: "Sum",
|
||||
color: colors.bitcoin,
|
||||
datasetPath: `/${scale}-to-fees`,
|
||||
},
|
||||
@@ -355,11 +374,12 @@ export function createPresets(scale: ResourceScale) {
|
||||
scale,
|
||||
icon: IconTablerCash,
|
||||
name: "In Dollars",
|
||||
title: "Daily Sum Of Dollar Fees",
|
||||
title: "Daily Sum Of Fees In Dollars",
|
||||
description: "",
|
||||
unit: "US Dollars",
|
||||
bottom: [
|
||||
{
|
||||
title: "Fees (Dollars)",
|
||||
title: "Sum",
|
||||
color: colors.dollars,
|
||||
datasetPath: `/${scale}-to-fees-in-dollars`,
|
||||
},
|
||||
@@ -375,11 +395,12 @@ export function createPresets(scale: ResourceScale) {
|
||||
scale,
|
||||
icon: IconTablerMoneybag,
|
||||
name: "In Bitcoin",
|
||||
title: "Yearly Sum Of Bitcoin Fees",
|
||||
title: "Yearly Sum Of Fees In Bitcoin",
|
||||
description: "",
|
||||
unit: "Bitcoin",
|
||||
bottom: [
|
||||
{
|
||||
title: "Fees (Bitcoin)",
|
||||
title: "Sum",
|
||||
color: colors.bitcoin,
|
||||
datasetPath: `/${scale}-to-fees-1y-sum`,
|
||||
},
|
||||
@@ -389,11 +410,12 @@ export function createPresets(scale: ResourceScale) {
|
||||
scale,
|
||||
icon: IconTablerCash,
|
||||
name: "In Dollars",
|
||||
title: "Yearly Sum Of Dollar Fees",
|
||||
title: "Yearly Sum Of Fees In Dollars",
|
||||
description: "",
|
||||
unit: "US Dollars",
|
||||
bottom: [
|
||||
{
|
||||
title: "Fees (Dollars)",
|
||||
title: "Sum",
|
||||
color: colors.dollars,
|
||||
datasetPath: `/${scale}-to-fees-in-dollars-1y-sum`,
|
||||
},
|
||||
@@ -409,11 +431,12 @@ export function createPresets(scale: ResourceScale) {
|
||||
scale,
|
||||
icon: IconTablerMoneybag,
|
||||
name: "In Bitcoin",
|
||||
title: "Cumulative Bitcoin Fees",
|
||||
title: "Cumulative Fees In Bitcoin",
|
||||
description: "",
|
||||
unit: "Bitcoin",
|
||||
bottom: [
|
||||
{
|
||||
title: "Fees (Bitcoin)",
|
||||
title: "Fees",
|
||||
color: colors.bitcoin,
|
||||
datasetPath: `/${scale}-to-cumulative-fees`,
|
||||
},
|
||||
@@ -423,11 +446,12 @@ export function createPresets(scale: ResourceScale) {
|
||||
scale,
|
||||
icon: IconTablerCash,
|
||||
name: "In Dollars",
|
||||
title: "Cumulative Dollar Fees",
|
||||
title: "Cumulative Fees In Dollars",
|
||||
description: "",
|
||||
unit: "US Dollars",
|
||||
bottom: [
|
||||
{
|
||||
title: "Fees (Dollars)",
|
||||
title: "Fees",
|
||||
color: colors.dollars,
|
||||
datasetPath: `/${scale}-to-cumulative-fees-in-dollars`,
|
||||
},
|
||||
@@ -446,14 +470,15 @@ export function createPresets(scale: ResourceScale) {
|
||||
name: "Subsidy V. Fees",
|
||||
title: "Subsidy V. Fees",
|
||||
description: "",
|
||||
unit: "Percentage",
|
||||
bottom: [
|
||||
{
|
||||
title: "Subsidy (%)",
|
||||
title: "Subsidy",
|
||||
color: colors.bitcoin,
|
||||
datasetPath: `/${scale}-to-subsidy-to-coinbase-ratio`,
|
||||
},
|
||||
{
|
||||
title: "Fees (%)",
|
||||
title: "Fees",
|
||||
color: colors.darkBitcoin,
|
||||
datasetPath: `/${scale}-to-fees-to-coinbase-ratio`,
|
||||
},
|
||||
@@ -468,9 +493,7 @@ export function createPresets(scale: ResourceScale) {
|
||||
name: "Puell Multiple",
|
||||
title: "Puell Multiple",
|
||||
description: "",
|
||||
priceScaleOptions: {
|
||||
mode: 1,
|
||||
},
|
||||
unit: "",
|
||||
bottom: [
|
||||
{
|
||||
title: "Multiple",
|
||||
@@ -484,11 +507,9 @@ export function createPresets(scale: ResourceScale) {
|
||||
scale,
|
||||
icon: IconTablerPick,
|
||||
name: "Hash Rate",
|
||||
title: "Hash Rate (EH/s)",
|
||||
title: "Hash Rate",
|
||||
description: "",
|
||||
priceScaleOptions: {
|
||||
mode: 1,
|
||||
},
|
||||
unit: "ExaHash / Second",
|
||||
bottom: [
|
||||
{
|
||||
title: "1M SMA",
|
||||
@@ -511,11 +532,9 @@ export function createPresets(scale: ResourceScale) {
|
||||
scale,
|
||||
icon: IconTablerRibbonHealth,
|
||||
name: "Hash Ribbon",
|
||||
title: "Hash Ribbon (EH/s)",
|
||||
title: "Hash Ribbon",
|
||||
description: "",
|
||||
priceScaleOptions: {
|
||||
mode: 1,
|
||||
},
|
||||
unit: "ExaHash / Second",
|
||||
bottom: [
|
||||
{
|
||||
title: "1M SMA",
|
||||
@@ -535,15 +554,16 @@ export function createPresets(scale: ResourceScale) {
|
||||
name: "Hash Price",
|
||||
title: "Hash Price",
|
||||
description: "",
|
||||
unit: "Dollars / (PetaHash / Second)",
|
||||
bottom: [
|
||||
{
|
||||
title: "Price ($/PH/s)",
|
||||
title: "Price",
|
||||
color: colors.dollars,
|
||||
datasetPath: `/date-to-hash-price`,
|
||||
},
|
||||
],
|
||||
},
|
||||
] satisfies PartialPreset[])
|
||||
] as const satisfies PartialPreset[])
|
||||
: []),
|
||||
|
||||
{
|
||||
@@ -552,9 +572,7 @@ export function createPresets(scale: ResourceScale) {
|
||||
name: "Difficulty",
|
||||
title: "Difficulty",
|
||||
description: "",
|
||||
priceScaleOptions: {
|
||||
mode: 1,
|
||||
},
|
||||
unit: "",
|
||||
bottom: [
|
||||
{
|
||||
title: "Difficulty",
|
||||
@@ -572,9 +590,10 @@ export function createPresets(scale: ResourceScale) {
|
||||
name: "Difficulty Adjustment",
|
||||
title: "Difficulty Adjustment",
|
||||
description: "",
|
||||
unit: "Percentage",
|
||||
bottom: [
|
||||
{
|
||||
title: "Adjustment (%)",
|
||||
title: "Adjustment",
|
||||
seriesType: SeriesType.Based,
|
||||
datasetPath: `/${scale}-to-difficulty-adjustment`,
|
||||
},
|
||||
@@ -589,9 +608,7 @@ export function createPresets(scale: ResourceScale) {
|
||||
name: "Annualized Issuance",
|
||||
title: "Annualized Issuance",
|
||||
description: "",
|
||||
priceScaleOptions: {
|
||||
mode: 1,
|
||||
},
|
||||
unit: "Bitcoin",
|
||||
bottom: [
|
||||
{
|
||||
title: "Issuance",
|
||||
@@ -607,12 +624,10 @@ export function createPresets(scale: ResourceScale) {
|
||||
name: "Yearly Inflation Rate",
|
||||
title: "Yearly Inflation Rate",
|
||||
description: "",
|
||||
priceScaleOptions: {
|
||||
mode: 1,
|
||||
},
|
||||
unit: "Percentage",
|
||||
bottom: [
|
||||
{
|
||||
title: "Rate (%)",
|
||||
title: "Rate",
|
||||
color: colors.bitcoin,
|
||||
datasetPath: `/${scale}-to-yearly-inflation-rate`,
|
||||
},
|
||||
|
||||
@@ -21,6 +21,7 @@ export function createCohortPresetPricesPaidFolder({
|
||||
name: `Average`,
|
||||
title: `${title} Average Price Paid - Realized Price`,
|
||||
description: "",
|
||||
unit: "US Dollars",
|
||||
icon: () => IconTablerMathAvg,
|
||||
top: [
|
||||
{
|
||||
@@ -36,6 +37,7 @@ export function createCohortPresetPricesPaidFolder({
|
||||
title: `${title} deciles`,
|
||||
icon: () => IconTablerSquareHalf,
|
||||
description: "",
|
||||
unit: "US Dollars",
|
||||
top: percentiles
|
||||
.filter(({ value }) => Number(value) % 10 === 0)
|
||||
.map(({ name, id }) => {
|
||||
@@ -54,6 +56,7 @@ export function createCohortPresetPricesPaidFolder({
|
||||
name: percentile.name,
|
||||
title: `${title} ${percentile.title}`,
|
||||
description: "",
|
||||
unit: "US Dollars",
|
||||
icon: () => IconTablerSquareHalf,
|
||||
top: [
|
||||
{
|
||||
|
||||
@@ -25,6 +25,7 @@ export function createCohortPresetRealizedFolder({
|
||||
name: `Price`,
|
||||
title: `${title} Realized Price`,
|
||||
description: "",
|
||||
unit: "US Dollars",
|
||||
icon: () => IconTablerTag,
|
||||
top: [
|
||||
{
|
||||
@@ -46,6 +47,7 @@ export function createCohortPresetRealizedFolder({
|
||||
name: `Capitalization`,
|
||||
title: `${title} Realized Capitalization`,
|
||||
description: "",
|
||||
unit: "US Dollars",
|
||||
icon: () => IconTablerPigMoney,
|
||||
bottom: [
|
||||
{
|
||||
@@ -70,6 +72,7 @@ export function createCohortPresetRealizedFolder({
|
||||
name: `Capitalization 1M Net Change`,
|
||||
title: `${title} Realized Capitalization 1 Month Net Change`,
|
||||
description: "",
|
||||
unit: "US Dollars",
|
||||
icon: () => IconTablerStatusChange,
|
||||
bottom: [
|
||||
{
|
||||
@@ -84,6 +87,7 @@ export function createCohortPresetRealizedFolder({
|
||||
name: `Profit`,
|
||||
title: `${title} Realized Profit`,
|
||||
description: "",
|
||||
unit: "US Dollars",
|
||||
icon: () => IconTablerCash,
|
||||
bottom: [
|
||||
{
|
||||
@@ -98,6 +102,7 @@ export function createCohortPresetRealizedFolder({
|
||||
name: "Loss",
|
||||
title: `${title} Realized Loss`,
|
||||
description: "",
|
||||
unit: "US Dollars",
|
||||
icon: () => IconTablerCoffin,
|
||||
bottom: [
|
||||
{
|
||||
@@ -112,6 +117,7 @@ export function createCohortPresetRealizedFolder({
|
||||
name: `PNL`,
|
||||
title: `${title} Realized Profit And Loss`,
|
||||
description: "",
|
||||
unit: "US Dollars",
|
||||
icon: () => IconTablerArrowsVertical,
|
||||
bottom: [
|
||||
{
|
||||
@@ -133,6 +139,7 @@ export function createCohortPresetRealizedFolder({
|
||||
name: `Net PNL`,
|
||||
title: `${title} Net Realized Profit And Loss`,
|
||||
description: "",
|
||||
unit: "US Dollars",
|
||||
icon: () => IconTablerScale,
|
||||
bottom: [
|
||||
{
|
||||
@@ -147,6 +154,7 @@ export function createCohortPresetRealizedFolder({
|
||||
name: `Net PNL Relative To Market Cap`,
|
||||
title: `${title} Net Realized Profit And Loss Relative To Market Capitalization`,
|
||||
description: "",
|
||||
unit: "Percentage",
|
||||
icon: () => IconTablerDivide,
|
||||
bottom: [
|
||||
{
|
||||
@@ -161,6 +169,7 @@ export function createCohortPresetRealizedFolder({
|
||||
name: `Cumulative Profit`,
|
||||
title: `${title} Cumulative Realized Profit`,
|
||||
description: "",
|
||||
unit: "US Dollars",
|
||||
icon: () => IconTablerSum,
|
||||
bottom: [
|
||||
{
|
||||
@@ -175,6 +184,7 @@ export function createCohortPresetRealizedFolder({
|
||||
name: "Cumulative Loss",
|
||||
title: `${title} Cumulative Realized Loss`,
|
||||
description: "",
|
||||
unit: "US Dollars",
|
||||
icon: () => IconTablerSum,
|
||||
bottom: [
|
||||
{
|
||||
@@ -189,6 +199,7 @@ export function createCohortPresetRealizedFolder({
|
||||
name: `Cumulative Net PNL`,
|
||||
title: `${title} Cumulative Net Realized Profit And Loss`,
|
||||
description: "",
|
||||
unit: "US Dollars",
|
||||
icon: () => IconTablerSum,
|
||||
bottom: [
|
||||
{
|
||||
@@ -203,6 +214,7 @@ export function createCohortPresetRealizedFolder({
|
||||
name: `Cumulative Net PNL 30 Day Change`,
|
||||
title: `${title} Cumulative Net Realized Profit And Loss 30 Day Change`,
|
||||
description: "",
|
||||
unit: "US Dollars",
|
||||
icon: () => IconTablerTimeDuration30,
|
||||
bottom: [
|
||||
{
|
||||
@@ -217,6 +229,7 @@ export function createCohortPresetRealizedFolder({
|
||||
name: `Value Created`,
|
||||
title: `${title} Value Created`,
|
||||
description: "",
|
||||
unit: "US Dollars",
|
||||
icon: () => IconTablerPlus,
|
||||
bottom: [
|
||||
{
|
||||
@@ -231,6 +244,7 @@ export function createCohortPresetRealizedFolder({
|
||||
name: `Value Destroyed`,
|
||||
title: `${title} Value Destroyed`,
|
||||
description: "",
|
||||
unit: "US Dollars",
|
||||
icon: () => IconTablerMinus,
|
||||
bottom: [
|
||||
{
|
||||
@@ -245,6 +259,7 @@ export function createCohortPresetRealizedFolder({
|
||||
name: `Spent Output Profit Ratio - SOPR`,
|
||||
title: `${title} Spent Output Profit Ratio`,
|
||||
description: "",
|
||||
unit: "Percentage",
|
||||
icon: () => IconTablerMathXDivideY,
|
||||
bottom: [
|
||||
{
|
||||
|
||||
@@ -27,7 +27,7 @@ export function createCohortPresetSupplyFolder({
|
||||
title: `${title} Profit And Loss`,
|
||||
icon: () => IconTablerArrowsCross,
|
||||
description: "",
|
||||
|
||||
unit: "US Dollars",
|
||||
bottom: [
|
||||
{
|
||||
title: "In Profit",
|
||||
@@ -60,6 +60,7 @@ export function createCohortPresetSupplyFolder({
|
||||
title: `${title} Total supply`,
|
||||
icon: () => IconTablerSum,
|
||||
description: "",
|
||||
unit: "Bitcoin",
|
||||
bottom: [
|
||||
{
|
||||
title: "Supply",
|
||||
@@ -73,6 +74,7 @@ export function createCohortPresetSupplyFolder({
|
||||
name: "In Profit",
|
||||
title: `${title} Supply In Profit`,
|
||||
description: "",
|
||||
unit: "Bitcoin",
|
||||
icon: () => IconTablerTrendingUp,
|
||||
bottom: [
|
||||
{
|
||||
@@ -87,6 +89,7 @@ export function createCohortPresetSupplyFolder({
|
||||
name: "In Loss",
|
||||
title: `${title} Supply In Loss`,
|
||||
description: "",
|
||||
unit: "Bitcoin",
|
||||
icon: () => IconTablerTrendingDown,
|
||||
bottom: [
|
||||
{
|
||||
@@ -106,6 +109,7 @@ export function createCohortPresetSupplyFolder({
|
||||
name: "All",
|
||||
title: `${title} Profit And Loss Relative To Circulating Supply`,
|
||||
description: "",
|
||||
unit: "Percentage",
|
||||
icon: () => IconTablerArrowsCross,
|
||||
bottom: [
|
||||
{
|
||||
@@ -138,6 +142,7 @@ export function createCohortPresetSupplyFolder({
|
||||
name: `Total`,
|
||||
title: `${title} Total supply Relative To Circulating Supply`,
|
||||
description: "",
|
||||
unit: "Percentage",
|
||||
icon: () => IconTablerSum,
|
||||
bottom: [
|
||||
{
|
||||
@@ -152,6 +157,7 @@ export function createCohortPresetSupplyFolder({
|
||||
name: "In Profit",
|
||||
title: `${title} Supply In Profit Relative To Circulating Supply`,
|
||||
description: "",
|
||||
unit: "Percentage",
|
||||
icon: () => IconTablerTrendingUp,
|
||||
bottom: [
|
||||
{
|
||||
@@ -166,6 +172,7 @@ export function createCohortPresetSupplyFolder({
|
||||
name: "In Loss",
|
||||
title: `${title} Supply In Loss Relative To Circulating Supply`,
|
||||
description: "",
|
||||
unit: "Percentage",
|
||||
icon: () => IconTablerTrendingDown,
|
||||
bottom: [
|
||||
{
|
||||
@@ -185,6 +192,7 @@ export function createCohortPresetSupplyFolder({
|
||||
name: "All",
|
||||
title: `${title} Supply In Profit And Loss Relative To Own Supply`,
|
||||
description: "",
|
||||
unit: "Percentage",
|
||||
icon: () => IconTablerArrowsCross,
|
||||
bottom: [
|
||||
{
|
||||
@@ -221,6 +229,7 @@ export function createCohortPresetSupplyFolder({
|
||||
name: "In Profit",
|
||||
title: `${title} Supply In Profit Relative To Own Supply`,
|
||||
description: "",
|
||||
unit: "Percentage",
|
||||
icon: () => IconTablerTrendingUp,
|
||||
bottom: [
|
||||
{
|
||||
@@ -235,6 +244,7 @@ export function createCohortPresetSupplyFolder({
|
||||
name: "In Loss",
|
||||
title: `${title} Supply In Loss Relative To Own Supply`,
|
||||
description: "",
|
||||
unit: "Percentage",
|
||||
icon: () => IconTablerTrendingDown,
|
||||
bottom: [
|
||||
{
|
||||
|
||||
@@ -24,6 +24,7 @@ export function createCohortPresetUnrealizedFolder({
|
||||
name: `Profit`,
|
||||
title: `${title} Unrealized Profit`,
|
||||
description: "",
|
||||
unit: "US Dollars",
|
||||
icon: () => IconTablerMoodDollar,
|
||||
bottom: [
|
||||
{
|
||||
@@ -38,6 +39,7 @@ export function createCohortPresetUnrealizedFolder({
|
||||
name: "Loss",
|
||||
title: `${title} Unrealized Loss`,
|
||||
description: "",
|
||||
unit: "US Dollars",
|
||||
icon: () => IconTablerMoodSadDizzy,
|
||||
bottom: [
|
||||
{
|
||||
@@ -52,6 +54,7 @@ export function createCohortPresetUnrealizedFolder({
|
||||
name: `PNL`,
|
||||
title: `${title} Unrealized Profit And Loss`,
|
||||
description: "",
|
||||
unit: "US Dollars",
|
||||
icon: () => IconTablerArrowsVertical,
|
||||
bottom: [
|
||||
{
|
||||
@@ -73,6 +76,7 @@ export function createCohortPresetUnrealizedFolder({
|
||||
name: `Net PNL`,
|
||||
title: `${title} Net Unrealized Profit And Loss`,
|
||||
description: "",
|
||||
unit: "US Dollars",
|
||||
icon: () => IconTablerScale,
|
||||
bottom: [
|
||||
{
|
||||
@@ -87,6 +91,7 @@ export function createCohortPresetUnrealizedFolder({
|
||||
name: `Net PNL Relative To Market Cap`,
|
||||
title: `${title} Net Unrealized Profit And Loss Relative To Total Market Capitalization`,
|
||||
description: "",
|
||||
unit: "Percentage",
|
||||
icon: () => IconTablerDivide,
|
||||
bottom: [
|
||||
{
|
||||
|
||||
@@ -21,6 +21,7 @@ export function createCohortPresetUTXOFolder({
|
||||
name: `Count`,
|
||||
title: `${title} Unspent Transaction Outputs Count`,
|
||||
description: "",
|
||||
unit: "Count",
|
||||
icon: () => IconTablerTicket,
|
||||
bottom: [
|
||||
{
|
||||
|
||||
@@ -20,9 +20,10 @@ export function createRatioFolder({
|
||||
{
|
||||
scale,
|
||||
name: "Basic",
|
||||
description: "",
|
||||
icon: IconTablerMathXDivideY,
|
||||
title: `Market Price To ${title} Ratio`,
|
||||
unit: "Ratio",
|
||||
description: "",
|
||||
top: [
|
||||
{
|
||||
title: `SMA`,
|
||||
@@ -55,6 +56,7 @@ export function createRatioFolder({
|
||||
name: "Averages",
|
||||
description: "",
|
||||
icon: IconTablerMathAvg,
|
||||
unit: "Ratio",
|
||||
title: `Market Price To ${title} Ratio Averages`,
|
||||
top: [
|
||||
{
|
||||
@@ -98,9 +100,10 @@ export function createRatioFolder({
|
||||
{
|
||||
scale,
|
||||
name: "Momentum Oscillator",
|
||||
description: "",
|
||||
icon: IconTablerWaveSine,
|
||||
title: `Market Price To ${title} Ratio 1Y SMA Momentum Oscillator`,
|
||||
description: "",
|
||||
unit: "Ratio",
|
||||
icon: IconTablerWaveSine,
|
||||
top: [
|
||||
{
|
||||
title: `SMA`,
|
||||
@@ -129,9 +132,10 @@ export function createRatioFolder({
|
||||
{
|
||||
scale,
|
||||
name: "Top Percentiles",
|
||||
description: "",
|
||||
icon: IconTablerJetpack,
|
||||
title: `Market Price To ${title} Ratio Top Percentiles`,
|
||||
description: "",
|
||||
unit: "Ratio",
|
||||
top: [
|
||||
{
|
||||
title: `SMA`,
|
||||
@@ -165,9 +169,10 @@ export function createRatioFolder({
|
||||
{
|
||||
scale,
|
||||
name: "Bottom Percentiles",
|
||||
description: "",
|
||||
icon: IconTablerScubaMask,
|
||||
title: `Market Price To ${title} Ratio Bottom Percentiles`,
|
||||
description: "",
|
||||
unit: "Ratio",
|
||||
top: [
|
||||
{
|
||||
title: `SMA`,
|
||||
@@ -201,9 +206,10 @@ export function createRatioFolder({
|
||||
{
|
||||
scale,
|
||||
name: "Top Probabilities",
|
||||
description: "",
|
||||
icon: IconTablerRocket,
|
||||
title: `${title} Top Probabilities`,
|
||||
description: "",
|
||||
unit: "US Dollars",
|
||||
top: [
|
||||
{
|
||||
title: `0.1%`,
|
||||
@@ -225,9 +231,10 @@ export function createRatioFolder({
|
||||
{
|
||||
scale,
|
||||
name: "Bottom Probabilities",
|
||||
description: "",
|
||||
icon: IconTablerSubmarine,
|
||||
title: `${title} Bottom Probabilities`,
|
||||
description: "",
|
||||
unit: "US Dollars",
|
||||
top: [
|
||||
{
|
||||
title: `0.1%`,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export function createRecapPresets({
|
||||
scale,
|
||||
unit,
|
||||
title,
|
||||
keyAverage,
|
||||
color,
|
||||
@@ -15,6 +16,7 @@ export function createRecapPresets({
|
||||
scale: ResourceScale;
|
||||
title: string;
|
||||
color: Color;
|
||||
unit: Unit;
|
||||
keySum?: AnyDatasetPath;
|
||||
keyAverage?: AnyDatasetPath;
|
||||
keyMax?: AnyDatasetPath;
|
||||
@@ -34,6 +36,7 @@ export function createRecapPresets({
|
||||
name: "Daily Sum",
|
||||
title: `${title} Daily Sum`,
|
||||
description: "",
|
||||
unit,
|
||||
bottom: [
|
||||
{
|
||||
title: "Sum",
|
||||
@@ -52,6 +55,7 @@ export function createRecapPresets({
|
||||
name: "Daily Average",
|
||||
title: `${title} Daily Average`,
|
||||
description: "",
|
||||
unit,
|
||||
bottom: [
|
||||
{
|
||||
title: "Average",
|
||||
@@ -70,6 +74,7 @@ export function createRecapPresets({
|
||||
name: "Daily Percentiles",
|
||||
title: `${title} Daily Percentiles`,
|
||||
description: "",
|
||||
unit,
|
||||
bottom: [
|
||||
...(keyMax
|
||||
? [
|
||||
@@ -146,6 +151,7 @@ export function createRecapPresets({
|
||||
name: "Daily Max",
|
||||
title: `${title} Daily Max`,
|
||||
description: "",
|
||||
unit,
|
||||
bottom: [
|
||||
{
|
||||
title: "Max",
|
||||
@@ -164,6 +170,7 @@ export function createRecapPresets({
|
||||
name: "Daily 90th Percentile",
|
||||
title: `${title} Daily 90th Percentile`,
|
||||
description: "",
|
||||
unit,
|
||||
bottom: [
|
||||
{
|
||||
title: "90%",
|
||||
@@ -182,6 +189,7 @@ export function createRecapPresets({
|
||||
name: "Daily 75th Percentile",
|
||||
title: `${title} Size 75th Percentile`,
|
||||
description: "",
|
||||
unit,
|
||||
bottom: [
|
||||
{
|
||||
title: "75%",
|
||||
@@ -200,6 +208,7 @@ export function createRecapPresets({
|
||||
name: "Daily Median",
|
||||
title: `${title} Daily Median`,
|
||||
description: "",
|
||||
unit,
|
||||
bottom: [
|
||||
{
|
||||
title: "Median",
|
||||
@@ -218,6 +227,7 @@ export function createRecapPresets({
|
||||
name: "Daily 25th Percentile",
|
||||
title: `${title} Daily 25th Percentile`,
|
||||
description: "",
|
||||
unit,
|
||||
bottom: [
|
||||
{
|
||||
title: "25%",
|
||||
@@ -236,6 +246,7 @@ export function createRecapPresets({
|
||||
name: "Daily 10th Percentile",
|
||||
title: `${title} Daily 10th Percentile`,
|
||||
description: "",
|
||||
unit,
|
||||
bottom: [
|
||||
{
|
||||
title: "10%",
|
||||
@@ -254,6 +265,7 @@ export function createRecapPresets({
|
||||
name: "Daily Min",
|
||||
title: `${title} Daily Min`,
|
||||
description: "",
|
||||
unit,
|
||||
bottom: [
|
||||
{
|
||||
title: "Min",
|
||||
|
||||
@@ -10,6 +10,7 @@ export function createPresets(scale: ResourceScale) {
|
||||
name: "Count",
|
||||
title: "Transaction Count",
|
||||
description: "",
|
||||
unit: "Count",
|
||||
bottom: [
|
||||
{
|
||||
title: "1M SMA",
|
||||
@@ -38,6 +39,7 @@ export function createPresets(scale: ResourceScale) {
|
||||
name: "In Bitcoin",
|
||||
title: "Transaction Volume",
|
||||
description: "",
|
||||
unit: "Bitcoin",
|
||||
bottom: [
|
||||
{
|
||||
title: "1M SMA",
|
||||
@@ -62,9 +64,7 @@ export function createPresets(scale: ResourceScale) {
|
||||
name: "In Dollars",
|
||||
title: "Transaction Volume In Dollars",
|
||||
description: "",
|
||||
priceScaleOptions: {
|
||||
mode: 1,
|
||||
},
|
||||
unit: "US Dollars",
|
||||
bottom: [
|
||||
{
|
||||
title: "1M SMA",
|
||||
@@ -95,6 +95,7 @@ export function createPresets(scale: ResourceScale) {
|
||||
name: "In Bitcoin",
|
||||
title: "Annualized Transaction Volume",
|
||||
description: "",
|
||||
unit: "Bitcoin",
|
||||
bottom: [
|
||||
{
|
||||
title: "Volume",
|
||||
@@ -109,6 +110,7 @@ export function createPresets(scale: ResourceScale) {
|
||||
name: "In Dollars",
|
||||
title: "Annualized Transaction Volume In Dollars",
|
||||
description: "",
|
||||
unit: "US Dollars",
|
||||
bottom: [
|
||||
{
|
||||
title: "Volume",
|
||||
@@ -125,6 +127,7 @@ export function createPresets(scale: ResourceScale) {
|
||||
name: "Velocity",
|
||||
title: "Transactions Velocity",
|
||||
description: "",
|
||||
unit: "",
|
||||
bottom: [
|
||||
{
|
||||
title: "Transactions Velocity",
|
||||
@@ -139,6 +142,7 @@ export function createPresets(scale: ResourceScale) {
|
||||
name: "Per Second",
|
||||
title: "Transactions Per Second",
|
||||
description: "",
|
||||
unit: "Transactions",
|
||||
bottom: [
|
||||
{
|
||||
title: "1M SMA",
|
||||
|
||||
Vendored
+18
-11
@@ -1,13 +1,30 @@
|
||||
interface PresetParams {
|
||||
priceScaleOptions?: DeepPartialPriceScaleOptions;
|
||||
top?: SeriesConfig[];
|
||||
bottom?: SeriesConfig[];
|
||||
}
|
||||
|
||||
type Unit =
|
||||
| "US Dollars"
|
||||
| "Bitcoin"
|
||||
| "Percentage"
|
||||
| "Height"
|
||||
| "Count"
|
||||
| "Megabytes"
|
||||
| "Transactions"
|
||||
| "Weight"
|
||||
| "Ratio"
|
||||
| "Virtual Bytes"
|
||||
| "Seconds"
|
||||
| "Coinblocks"
|
||||
| "ExaHash / Second"
|
||||
| "Dollars / (PetaHash / Second)"
|
||||
| "";
|
||||
|
||||
type PartialPreset = {
|
||||
scale: ResourceScale;
|
||||
icon?: () => JSXElement;
|
||||
name: string;
|
||||
unit: Unit;
|
||||
title: string;
|
||||
description: string;
|
||||
} & PresetParams;
|
||||
@@ -24,16 +41,6 @@ type FilePath = {
|
||||
name: string;
|
||||
}[];
|
||||
|
||||
// type ApplyPreset = (params: {
|
||||
// charts: RWS<IChartApi[]>;
|
||||
// parentDiv: HTMLDivElement;
|
||||
// datasets: Datasets;
|
||||
// preset: Preset;
|
||||
// legendSetter: Setter<SeriesLegend[]>;
|
||||
// dark: Accessor<boolean>;
|
||||
// activeIds: RWS<number[]>;
|
||||
// }) => void;
|
||||
|
||||
interface PartialPresetFolder {
|
||||
name: string;
|
||||
tree: PartialPresetTree;
|
||||
|
||||
@@ -49,10 +49,10 @@ export const createStaticList = <T, L extends T[] = T[]>(
|
||||
|
||||
return found;
|
||||
} else {
|
||||
const index = savedIndex ?? parameters.selectedIndex;
|
||||
const value = index !== undefined ? l.at(index) : undefined;
|
||||
return (
|
||||
l.at(savedIndex ?? parameters.selectedIndex!) ??
|
||||
parameters.defaultValue ??
|
||||
l[parameters.defaultIndex || 0]
|
||||
value ?? parameters.defaultValue ?? l[parameters.defaultIndex || 0]
|
||||
);
|
||||
}
|
||||
}),
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import { requestIdleCallbackPossible } from "../env";
|
||||
import { createRWS } from "./rws";
|
||||
|
||||
export function createWasIdleAccessor() {
|
||||
const wasIdle = createRWS(false);
|
||||
|
||||
if (requestIdleCallbackPossible) {
|
||||
const idleCallback = requestIdleCallback(() => {
|
||||
wasIdle.set(true);
|
||||
});
|
||||
|
||||
onCleanup(() => {
|
||||
cancelIdleCallback(idleCallback);
|
||||
});
|
||||
} else {
|
||||
const timeout = setTimeout(() => {
|
||||
wasIdle.set(true);
|
||||
}, 500);
|
||||
|
||||
onCleanup(() => {
|
||||
clearTimeout(timeout);
|
||||
});
|
||||
}
|
||||
|
||||
return wasIdle as Accessor<boolean>;
|
||||
}
|
||||
+1
@@ -4,6 +4,7 @@ type ISeriesApi<T extends SeriesType> =
|
||||
import("lightweight-charts").ISeriesApi<T>;
|
||||
type SeriesOptionsMap = import("lightweight-charts").SeriesOptionsMap;
|
||||
type ISeriesApiAny = ISeriesApi<keyof SeriesOptionsMap>;
|
||||
type ITimeScaleApi<Time> = import("lightweight-charts").ITimeScaleApi<Time>;
|
||||
type IPriceLine = import("lightweight-charts").IPriceLine;
|
||||
type ChartOptions = import("lightweight-charts").ChartOptions;
|
||||
type DeepPartial<T> = import("lightweight-charts").DeepPartial<T>;
|
||||
|
||||
Vendored
-22
@@ -1,22 +0,0 @@
|
||||
name: Rust
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Build
|
||||
run: cargo build --verbose
|
||||
- name: Run tests
|
||||
run: cargo test --verbose
|
||||
Reference in New Issue
Block a user