general: snapshot

This commit is contained in:
k
2024-07-03 20:40:35 +02:00
parent b7e8cbea20
commit 069311dcf3
24 changed files with 407 additions and 312 deletions

View File

@@ -4,11 +4,9 @@ export { averages } from "./consts/averages";
export function createScaleDatasets<Scale extends ResourceScale>({
scale,
setActiveResources,
groupedKeysToURLPath,
}: {
scale: Scale;
setActiveResources: Setter<Set<ResourceDataset<any, any>>>;
groupedKeysToURLPath: GroupedKeysToURLPath[Scale];
}) {
type Key = keyof typeof groupedKeysToURLPath;
@@ -23,7 +21,6 @@ export function createScaleDatasets<Scale extends ResourceScale>({
datasets[key as unknown as Exclude<Key, "ohlc">] = createResourceDataset({
scale,
path: groupedKeysToURLPath[key as Key] as any,
setActiveResources,
});
}
}
@@ -31,7 +28,6 @@ export function createScaleDatasets<Scale extends ResourceScale>({
const price = createResourceDataset<Scale, OHLC>({
scale,
path: `/${scale}-to-ohlc`,
setActiveResources,
});
Object.assign(datasets, { price });

View File

@@ -3,10 +3,8 @@ import { createResourceDataset } from "./resource";
export { averages } from "./consts/averages";
export function createDateDatasets({
setActiveResources,
groupedKeysToURLPath,
}: {
setActiveResources: Setter<Set<ResourceDataset<any, any>>>;
groupedKeysToURLPath: GroupedKeysToURLPath["date"];
}) {
type Key = keyof typeof groupedKeysToURLPath;
@@ -21,7 +19,6 @@ export function createDateDatasets({
datasets[key as Exclude<Key, "ohlc">] = createResourceDataset<"date">({
scale: "date",
path: groupedKeysToURLPath[key as Key],
setActiveResources,
});
}
}
@@ -29,7 +26,6 @@ export function createDateDatasets({
const price = createResourceDataset<"date", OHLC>({
scale: "date",
path: "/date-to-ohlc",
setActiveResources,
});
Object.assign(datasets, { price });

View File

@@ -1,10 +1,8 @@
import { createResourceDataset } from "./resource";
export function createHeightDatasets({
setActiveResources,
groupedKeysToURLPath,
}: {
setActiveResources: Setter<Set<ResourceDataset<any, any>>>;
groupedKeysToURLPath: GroupedKeysToURLPath["height"];
}) {
type Key = keyof typeof groupedKeysToURLPath;
@@ -19,7 +17,6 @@ export function createHeightDatasets({
datasets[key as Exclude<Key, "ohlc">] = createResourceDataset<"height">({
scale: "height",
path: groupedKeysToURLPath[key as Key],
setActiveResources,
});
}
}
@@ -27,7 +24,6 @@ export function createHeightDatasets({
const price = createResourceDataset<"height", OHLC>({
scale: "height",
path: "/height-to-ohlc",
setActiveResources,
});
Object.assign(datasets, { price });

View File

@@ -7,18 +7,12 @@ export const scales = ["date" as const, "height" as const];
export const HEIGHT_CHUNK_SIZE = 10_000;
export function createDatasets({
setActiveResources,
}: {
setActiveResources: Setter<Set<ResourceDataset<any, any>>>;
}) {
export function createDatasets() {
const date = createDateDatasets({
setActiveResources,
groupedKeysToURLPath: groupedKeysToURLPath.date,
});
const height = createHeightDatasets({
setActiveResources,
groupedKeysToURLPath: groupedKeysToURLPath.height,
});

View File

@@ -1,5 +1,3 @@
import { createLazyMemo } from "@solid-primitives/memo";
import {
ONE_DAY_IN_MS,
ONE_HOUR_IN_MS,
@@ -8,19 +6,12 @@ import {
import { createRWS } from "/src/solid/rws";
import { HEIGHT_CHUNK_SIZE } from ".";
import { debounce } from "../utils/debounce";
export function createResourceDataset<
Scale extends ResourceScale,
Type extends OHLC | number = number,
>({
scale,
path,
setActiveResources,
}: {
scale: Scale;
path: string;
setActiveResources: Setter<Set<ResourceDataset<any, any>>>;
}) {
>({ scale, path }: { scale: Scale; path: string }) {
type Dataset = Scale extends "date"
? FetchedDateDataset<Type>
: FetchedHeightDataset<Type>;
@@ -85,8 +76,7 @@ export function createResourceDataset<
}) as FetchedResult<Scale, Type>[];
const _fetch = async (id: number) => {
const index =
scale === "date" ? id - 2009 : Math.floor(id / HEIGHT_CHUNK_SIZE);
const index = chunkIdToIndex(scale, id);
if (
index < 0 ||
@@ -205,25 +195,43 @@ export function createResourceDataset<
fetched.loading = false;
};
const valuesCallback = (vecs: Value[][]) => {
let length = 0;
for (let i = 0; i < vecs.length; i++) {
length += vecs[i].length;
}
if (!length) return;
const array = new Array(length);
let k = 0;
for (let i = 0; i < vecs.length; i++) {
let vec = vecs[i];
for (let j = 0; j < vec.length; j++) {
array[k++] = vec[j];
}
}
if (k !== length) throw Error("e");
values.set(array);
};
const debouncedValuesCallback = debounce(valuesCallback, 100);
const values = createRWS<Value[]>([]);
createEffect(() => {
const vecs = fetchedJSONs.map((fetched) => fetched.vec() || []);
debouncedValuesCallback(vecs);
});
const resource: ResourceDataset<Scale, Type> = {
scale,
url: baseURL,
fetch: _fetch,
fetchedJSONs,
values: createLazyMemo(() => {
setActiveResources((resources) => resources.add(resource));
onCleanup(() =>
setActiveResources((resources) => {
resources.delete(resource);
return resources;
}),
);
const flat = fetchedJSONs.flatMap((fetched) => fetched.vec() || []);
return flat;
}),
values,
drop() {
fetchedJSONs.forEach((fetched) => {
fetched.at = null;
@@ -245,3 +253,7 @@ async function convertResponseToJSON<
return null;
}
}
function chunkIdToIndex(scale: ResourceScale, id: number) {
return scale === "date" ? id - 2009 : Math.floor(id / HEIGHT_CHUNK_SIZE);
}

View File

@@ -8,14 +8,6 @@ type ResourceScale = (typeof import("./index").scales)[index];
type DatasetValue<T> = T & Numbered & Valued;
interface Dataset<
Scale extends ResourceScale,
Value extends SingleValueData | CandlestickData = SingleValueData,
> {
scale: Scale;
values: Accessor<DatasetValue<Value>[]>;
}
interface ResourceDataset<
Scale extends ResourceScale,
Type extends OHLC | number = number,
@@ -27,16 +19,15 @@ interface ResourceDataset<
Value extends SingleValueData | CandlestickData = Type extends number
? SingleValueData
: CandlestickData,
> extends Dataset<Scale, Value> {
> {
scale: Scale;
url: string;
fetch: (id: number) => void;
fetchedJSONs: FetchedResult<Scale, Type>[];
values: Accessor<DatasetValue<Value>[]>;
drop: VoidFunction;
}
type AnyDataset<Scale extends ResourceScale> = Dataset<Scale> &
Partial<ResourceDataset<Scale>>;
interface FetchedResult<
Scale extends ResourceScale,
Type extends number | OHLC,

View File

@@ -1,110 +0,0 @@
import { colors } from "../../utils/colors";
import { webSockets } from "../../ws";
import { createCandlesticksSeries } from "../series/creators/candlesticks";
import { createSeriesLegend } from "../series/creators/legend";
import { createLineSeries } from "../series/creators/line";
import { initTimeScale } from "./time";
export const PRICE_SCALE_MOMENTUM_ID = "momentum";
export const applyPriceSeries = <
Scale extends ResourceScale,
T extends SingleValueData,
>({
chart,
preset,
dataset,
seriesType,
valuesSkipped,
options,
activeResources,
}: {
chart: IChartApi;
preset: Preset;
valuesSkipped: Accessor<number>;
seriesType: Accessor<"Candlestick" | "Line">;
activeResources: Accessor<Set<ResourceDataset<any, any>>>;
dataset: Dataset<Scale, T>;
options?: PriceSeriesOptions;
}) => {
const id = options?.id || "price";
const title = options?.title || "Price";
const url = "url" in dataset ? (dataset as any).url : undefined;
const priceScaleOptions: DeepPartialPriceScaleOptions = {
mode: 1,
...options?.priceScaleOptions,
};
const [ohlcSeries, ohlcColors] = createCandlesticksSeries(chart, options);
const ohlcLegend = createSeriesLegend({
id,
presetId: preset.id,
title,
color: () => ohlcColors,
series: ohlcSeries,
disabled: () => seriesType() !== "Candlestick",
url,
});
ohlcSeries.priceScale().applyOptions(priceScaleOptions);
// ---
const lineColor = colors.white;
const lineSeries = createLineSeries(chart, {
color: lineColor,
...options?.seriesOptions,
});
const lineLegend = createSeriesLegend({
id,
presetId: preset.id,
title,
color: () => lineColor,
series: lineSeries,
disabled: () => seriesType() !== "Line",
visible: ohlcLegend.visible,
url,
});
lineSeries.priceScale().applyOptions(priceScaleOptions);
// ---
// setMinMaxMarkers({
// scale: preset.scale,
// candlesticks:
// dataset?.values() || datasets[preset.scale].price.values() || ([] as any),
// range: chartState.range,
// lowerOpacity,
// });
initTimeScale({
activeResources,
});
createEffect(() => {
const values = dataset.values();
// const values = computeDrawnSeriesValues(dataset.values(), valuesSkipped());
lineSeries.setData(values);
ohlcSeries.setData(values);
});
createEffect(() => {
if (preset.scale === "date") {
const latest = webSockets.liveKrakenCandle.latest();
if (latest) {
ohlcSeries.update(latest);
lineSeries.update(latest);
}
}
});
return { ohlcLegend, lineLegend };
};

View File

@@ -1,7 +1,6 @@
import { HEIGHT_CHUNK_SIZE } from "../../datasets";
import { debounce } from "../../utils/debounce";
import { writeURLParam } from "../../utils/urlParams";
import { setMinMaxMarkers } from "./markers";
import {
chartState,
LOCAL_STORAGE_RANGE_KEY,
@@ -20,9 +19,9 @@ const debouncedUpdateURLParams = debounce((range: TimeRange | null) => {
}, 500);
export function initTimeScale({
activeResources,
activeDatasets,
}: {
activeResources: Accessor<Set<ResourceDataset<any, any>>>;
activeDatasets: Set<ResourceDataset<any, any>>;
}) {
setTimeScale(chartState.range);
@@ -46,7 +45,7 @@ export function initTimeScale({
}
ids.forEach((id) => {
activeResources().forEach((resource) => resource.fetch(id));
activeDatasets.forEach((dataset) => dataset.fetch(id));
});
}, 100);
@@ -63,6 +62,7 @@ export function initTimeScale({
chartState.range = range;
});
}, 50);
onCleanup(() => clearTimeout(timeout));
}

View File

@@ -27,17 +27,16 @@ const whitespaceDateDataset: (SingleValueData & Numbered)[] = new Array(
};
});
const whitespaceHeightDataset: (WhitespaceData & Numbered)[] = new Array(
840_000,
const heightStart = -100_000;
const whitespaceHeightDataset: (SingleValueData & Numbered)[] = new Array(
1_200_000,
)
.fill(0)
.map(
(_, index) =>
({
time: index,
number: index,
}) as any,
);
.map((_, index) => ({
time: (heightStart + index) as any,
number: heightStart + index,
value: NaN,
}));
export function setWhitespace(chart: IChartApi, scale: ResourceScale) {
const whitespaceSeries = createLineSeries(chart);

View File

@@ -1,10 +1,11 @@
import { PRICE_SCALE_MOMENTUM_ID } from "../../chart/price";
import { defaultSeriesOptions } from "./options";
type HistogramOptions = DeepPartial<
HistogramStyleOptions & SeriesOptionsCommon
>;
export const PRICE_SCALE_MOMENTUM_ID = "momentum";
export const createHistogramSeries = (
chart: IChartApi,
options?: HistogramOptions,

View File

@@ -1,19 +1,22 @@
import { createRWS } from "/src/solid/rws";
import { createChart } from "../lightweightCharts/chart/create";
import { applyPriceSeries } from "../lightweightCharts/chart/price";
import { chartState } from "../lightweightCharts/chart/state";
import { initTimeScale } from "../lightweightCharts/chart/time";
import { setWhitespace } from "../lightweightCharts/chart/whitespace";
import { createAreaSeries } from "../lightweightCharts/series/creators/area";
import {
createBaseLineSeries,
DEFAULT_BASELINE_COLORS,
} from "../lightweightCharts/series/creators/baseLine";
import { createCandlesticksSeries } from "../lightweightCharts/series/creators/candlesticks";
import { createHistogramSeries } from "../lightweightCharts/series/creators/histogram";
import { createSeriesLegend } from "../lightweightCharts/series/creators/legend";
import { createLineSeries } from "../lightweightCharts/series/creators/line";
import { colors } from "../utils/colors";
import { debounce } from "../utils/debounce";
import { stringToId } from "../utils/id";
import { webSockets } from "../ws";
export enum SeriesType {
Normal,
@@ -24,7 +27,7 @@ export enum SeriesType {
type SeriesConfig<Scale extends ResourceScale> =
| {
dataset: AnyDataset<Scale>;
dataset: ResourceDataset<Scale>;
color?: string;
colors?: undefined;
seriesType: SeriesType.Based;
@@ -33,7 +36,7 @@ type SeriesConfig<Scale extends ResourceScale> =
defaultVisible?: boolean;
}
| {
dataset: AnyDataset<Scale>;
dataset: ResourceDataset<Scale>;
color?: string;
colors?: string[];
seriesType: SeriesType.Histogram;
@@ -42,7 +45,7 @@ type SeriesConfig<Scale extends ResourceScale> =
defaultVisible?: boolean;
}
| {
dataset: AnyDataset<Scale>;
dataset: ResourceDataset<Scale>;
color: string;
colors?: undefined;
seriesType?: SeriesType.Normal | SeriesType.Area;
@@ -61,20 +64,18 @@ export function applySeriesList<Scale extends ResourceScale>({
datasets,
priceDataset,
priceOptions,
activeResources,
legendSetter,
}: {
charts: RWS<IChartApi[]>;
parentDiv: HTMLDivElement;
preset: Preset;
legendSetter: Setter<PresetLegend>;
priceDataset?: AnyDataset<Scale>;
priceDataset?: ResourceDataset<Scale>;
priceOptions?: PriceSeriesOptions;
priceScaleOptions?: DeepPartialPriceScaleOptions;
top?: SeriesConfig<Scale>[];
bottom?: SeriesConfig<Scale>[];
datasets: Datasets;
activeResources: Accessor<Set<ResourceDataset<any, any>>>;
}) {
reactiveChartList.set((charts) => {
charts.forEach((chart) => {
@@ -92,7 +93,9 @@ export function applySeriesList<Scale extends ResourceScale>({
const priceSeriesType = createRWS<"Candlestick" | "Line">("Candlestick");
const valuesSkipped = createRWS(0);
// const valuesSkipped = createRWS(0);
const activeDatasets: Set<ResourceDataset<any, any>> = new Set();
const charts = [top || [], bottom]
.flatMap((list) => (list ? [list] : []))
@@ -136,17 +139,20 @@ export function applySeriesList<Scale extends ResourceScale>({
const _legendList: PresetLegend = [];
if (index === 0) {
const dataset =
priceDataset ||
(datasets[preset.scale as Scale].price as unknown as NonNullable<
typeof priceDataset
>);
activeDatasets.add(dataset);
const price = applyPriceSeries({
chart,
preset,
seriesType: priceSeriesType,
valuesSkipped,
dataset:
priceDataset ||
(datasets[preset.scale as Scale].price as unknown as NonNullable<
typeof priceDataset
>),
activeResources,
// valuesSkipped,
dataset,
options: priceOptions,
});
@@ -168,6 +174,8 @@ export function applySeriesList<Scale extends ResourceScale>({
options,
defaultVisible,
}) => {
activeDatasets.add(dataset);
let series: ISeriesApi<
"Baseline" | "Line" | "Area" | "Histogram"
>;
@@ -216,10 +224,10 @@ export function applySeriesList<Scale extends ResourceScale>({
);
createEffect(() => {
series.setData(
dataset?.values() || [],
// computeDrawnSeriesValues(dataset?.values(), valuesSkipped()),
);
const values = dataset.values();
console.log(values.length);
series.setData(values);
});
return series;
@@ -275,11 +283,13 @@ export function applySeriesList<Scale extends ResourceScale>({
const ratio = (range.to - range.from) / width;
if (ratio <= 0.5) {
// valuesSkipped.set(0);
priceSeriesType.set("Candlestick");
} else {
priceSeriesType.set("Line");
valuesSkipped.set(Math.floor(ratio / 5));
// valuesSkipped.set(Math.floor(ratio / 1.25));
}
} catch {}
}
@@ -289,6 +299,10 @@ export function applySeriesList<Scale extends ResourceScale>({
50,
);
initTimeScale({
activeDatasets,
});
charts.forEach(({ chart }, index) => {
chart.timeScale().subscribeVisibleLogicalRangeChange((timeRange) => {
// Last chart otherwise length of timescale is Infinity
@@ -327,31 +341,270 @@ export function applySeriesList<Scale extends ResourceScale>({
reactiveChartList.set(() => charts.map(({ chart }) => chart));
}
export function computeDrawnSeriesValues<T>(
values: DatasetValue<T>[] | undefined,
valuesSkipped: number,
) {
values = values || [];
function applyPriceSeries<
Scale extends ResourceScale,
T extends OHLC | number,
>({
chart,
preset,
dataset,
seriesType,
// valuesSkipped,
options,
}: {
chart: IChartApi;
preset: Preset;
// valuesSkipped: Accessor<number>;
seriesType: Accessor<"Candlestick" | "Line">;
dataset: ResourceDataset<Scale, T>;
options?: PriceSeriesOptions;
}) {
// console.time("series add");
if (valuesSkipped === 0) {
return values;
} else {
const valuesSkippedPlus1 = valuesSkipped + 1;
const id = options?.id || "price";
const title = options?.title || "Price";
// console.log(_valuesSkippedPlus1);
const url = "url" in dataset ? (dataset as any).url : undefined;
let length = Math.floor(values.length / valuesSkippedPlus1);
const priceScaleOptions: DeepPartialPriceScaleOptions = {
mode: 1,
...options?.priceScaleOptions,
};
// console.log(length);
let [ohlcSeries, ohlcColors] = createCandlesticksSeries(chart, options);
const filteredValues = new Array(length);
const ohlcLegend = createSeriesLegend({
id,
presetId: preset.id,
title,
color: () => ohlcColors,
series: ohlcSeries,
disabled: () => seriesType() !== "Candlestick",
url,
});
for (let i = 0; i < length; i++) {
filteredValues[i] = values[i * valuesSkippedPlus1];
ohlcSeries.priceScale().applyOptions(priceScaleOptions);
// ---
const lineColor = colors.white;
let lineSeries = createLineSeries(chart, {
color: lineColor,
...options?.seriesOptions,
});
const lineLegend = createSeriesLegend({
id,
presetId: preset.id,
title,
color: () => lineColor,
series: lineSeries,
disabled: () => seriesType() !== "Line",
visible: ohlcLegend.visible,
url,
});
lineSeries.priceScale().applyOptions(priceScaleOptions);
// console.timeEnd("series add");
// lineSeries.setData(whitespaceHeightDataset);
// ohlcSeries.setData({ time: 0, value: NaN });
// ---
// setMinMaxMarkers({
// scale: preset.scale,
// candlesticks:
// dataset?.values() || datasets[preset.scale].price.values() || ([] as any),
// range: chartState.range,
// lowerOpacity,
// });
// const startingValue = {
// number: -1,
// time: -1,
// open: NaN,
// high: NaN,
// low: NaN,
// close: NaN,
// value: NaN,
// };
// lineSeries.update(startingValue);
// ohlcSeries.update(startingValue);
// const callback = (
// chunks: any[],
// valuesSkippedPlus1: number,
// length: number,
// ) => {
// console.time("t");
// console.time("a");
// // chart.removeSeries(ohlcSeries);
// // chart.removeSeries(lineSeries);
// // ohlcSeries = createCandlesticksSeries(chart, options)[0];
// // ohlcSeries.priceScale().applyOptions(priceScaleOptions);
// // lineSeries = createLineSeries(chart, {
// // color: lineColor,
// // ...options?.seriesOptions,
// // });
// // lineSeries.priceScale().applyOptions(priceScaleOptions);
// const values = new Array(length);
// let i = 0;
// for (let k = 0; k < chunks.length; k++) {
// const chunk = chunks[k];
// // const chunk =
// // fetchedJSONs[chunkIdToIndex(dataset.scale, activeRange[k])]?.vec?.() ||
// // [];
// for (let j = 0; j < chunk.length; j += valuesSkippedPlus1) {
// values[i++] = chunk[j];
// // console.log(chunk[j]);
// // callback(chunk[j]);
// // for (let i = 0; i < seriesList.length; i++) {
// // seriesList[i].update(chunk[j]);
// // }
// // const value = chunk[j];
// // console.log(value.time);
// // lineSeries.update(value);
// // ohlcSeries.update(value);
// // i++;
// }
// }
// console.log(values.length);
// console.timeEnd("t");
// lineSeries.setData(values);
// console.timeEnd("a");
// };
// const debouncedCallback = debounce(callback, 200);
createEffect(() => {
const values = dataset.values();
console.log(values.length);
lineSeries.setData(values);
ohlcSeries.setData(values);
});
// createEffect(() =>
// computeDrawnSeriesValues(
// dataset,
// valuesSkipped(),
// debouncedCallback,
// // [lineSeries, ohlcSeries],
// // (value) => {
// // try {
// // console.log(value);
// // lineSeries.update(value);
// // ohlcSeries.update(value);
// // } catch {}
// // }),
// ),
// );
createEffect(() => {
if (preset.scale === "date") {
const latest = webSockets.liveKrakenCandle.latest();
if (latest) {
ohlcSeries.update(latest);
lineSeries.update(latest);
}
}
});
// console.log(filteredValues.length);
return filteredValues;
}
return { ohlcLegend, lineLegend };
}
// // const computeDrawnSeriesValues = debounce(_computeDrawnSeriesValues, 100);
// function computeDrawnSeriesValues<
// S extends ResourceScale,
// T extends OHLC | number,
// >(
// dataset: ResourceDataset<S, T>,
// valuesSkipped: number,
// callback: (chunks: any, v: number, l: number) => void,
// // seriesList: ISeriesApi<any>[],
// ) {
// // console.time(dataset.url);
// const { fetchedJSONs, activeRange: _activeRange } = dataset;
// const activeRange = _activeRange();
// const valuesSkippedPlus1 = valuesSkipped + 1;
// if (valuesSkippedPlus1 === 1) {
// console.log("todo valuesSkippedPlus1===1, skip for now");
// }
// // for (let i = 0; i < seriesList.length; i++) {
// // seriesList[i].
// // }
// const chunks = new Array(activeRange.length);
// let length = 0;
// for (let i = 0; i < chunks.length; i++) {
// const chunk =
// fetchedJSONs[chunkIdToIndex(dataset.scale, activeRange[i])]?.vec?.() ||
// [];
// chunks[i] = chunk;
// length += Math.ceil(chunk.length / valuesSkippedPlus1);
// }
// callback(chunks, valuesSkippedPlus1, length);
// // setValues(chunks, valuesSkippedPlus1, length, callback);
// // }
// // // const debouncedSetValues = debounce(setValues, 50);
// // function setValues(
// // chunks: any[],
// // valuesSkippedPlus1: number,
// // length: number,
// // callback: (values: any[]) => void,
// // ) {
// // const values = new Array(length);
// // let i = 0;
// // for (let k = 0; k < activeRange.length; k++) {
// // const chunk = chunks[k];
// // // const chunk =
// // // fetchedJSONs[chunkIdToIndex(dataset.scale, activeRange[k])]?.vec?.() ||
// // // [];
// // for (let j = 0; j < chunk.length; j += valuesSkippedPlus1) {
// // // values[i++] = chunk[j];
// // // console.log(chunk[j]);
// // // callback(chunk[j]);
// // for (let i = 0; i < seriesList.length; i++) {
// // seriesList[i].update(chunk[j]);
// // }
// // // i++;
// // }
// // }
// // console.log(i);
// // if (i !== values.length) {
// // console.log({ n: i, values });
// // throw Error("error");
// // }
// // console.timeEnd(dataset.url);
// }

View File

@@ -24,7 +24,6 @@ type ApplyPreset = (params: {
parentDiv: HTMLDivElement;
datasets: Datasets;
preset: Preset;
activeResources: Accessor<Set<ResourceDataset<any, any>>>;
legendSetter: Setter<PresetLegend>;
}) => void;

View File

@@ -110,6 +110,7 @@ export const convertCandleToVolumeColor = (
export const colors = {
white,
black,
darkWhite,
gray,
lightBitcoin: yellow,