kibo: fix simulation

This commit is contained in:
nym21
2025-04-04 17:44:22 +02:00
parent 118c87faf7
commit be632aaf37
10 changed files with 605 additions and 394 deletions

File diff suppressed because one or more lines are too long

View File

@@ -3,6 +3,7 @@
// import { CanvasRenderingTarget2D } from "fancy-canvas";
type CanvasRenderingTarget2D = any;
declare const baselineSeries: SeriesDefinition<"Baseline">;
declare const candlestickSeries: SeriesDefinition<"Candlestick">;
declare const lineSeries: SeriesDefinition<"Line">;
export declare const customSeriesDefaultOptions: CustomSeriesOptions;
@@ -4032,6 +4033,10 @@ export type UTCTimestamp = Nominal<number, "UTCTimestamp">;
*/
export type VisiblePriceScaleOptions = PriceScaleOptions;
export { candlestickSeries as CandlestickSeries, lineSeries as LineSeries };
export {
baselineSeries as BaselineSeries,
candlestickSeries as CandlestickSeries,
lineSeries as LineSeries,
};
export {};

View File

@@ -1,6 +1,6 @@
// @ts-check
/** @import {IChartApi, ISeriesApi, SeriesDefinition, SingleValueData as _SingleValueData, CandlestickData as _CandlestickData} from './v5.0.5-treeshaked/types' */
/** @import {IChartApi, ISeriesApi, SeriesDefinition, SingleValueData as _SingleValueData, CandlestickData as _CandlestickData, BaselineData, SeriesType} from './v5.0.5-treeshaked/types' */
/**
* @typedef {[number, number, number, number]} OHLCTuple
@@ -47,7 +47,7 @@ export default import("./v5.0.5-treeshaked/script.js").then((lc) => {
autoSize: true,
layout: {
fontFamily: "Geist mono",
fontSize: 14,
fontSize: 13,
background: { color: "transparent" },
attributionLogo: false,
colorSpace: "display-p3",
@@ -130,15 +130,18 @@ export default import("./v5.0.5-treeshaked/script.js").then((lc) => {
* @param {VecsResources} args.vecsResources
* @param {Owner | null} [args.owner]
* @param {true} [args.fitContentOnResize]
* @param {{unit: Unit; blueprints: AnySeriesBlueprint[]}[]} [args.config]
*/
function createChartElement({
parent,
signals,
colors,
utils,
id,
vecsResources,
owner: _owner,
fitContentOnResize,
config,
}) {
let owner = _owner || signals.getOwner();
@@ -179,7 +182,7 @@ export default import("./v5.0.5-treeshaked/script.js").then((lc) => {
* @param {ISeriesApi<SeriesType>} series
* @param {VecResource} valuesResource
*/
function createSetDataEffect(series, valuesResource) {
function createSetFetchedDataEffect(series, valuesResource) {
signals.runWithOwner(owner, () =>
signals.createEffect(
() => [timeResource?.fetched(), valuesResource.fetched()],
@@ -244,7 +247,7 @@ export default import("./v5.0.5-treeshaked/script.js").then((lc) => {
}),
);
return {
const chart = {
inner: () => ichart,
/**
* @param {Object} args
@@ -272,12 +275,23 @@ export default import("./v5.0.5-treeshaked/script.js").then((lc) => {
colors,
utils,
});
if (fitContentOnResize) {
ichart.applyOptions({
handleScroll: false,
handleScale: false,
timeScale: {
minBarSpacing: 0.001,
},
});
}
},
/**
* @param {Object} args
* @param {VecId} args.vecId
* @param {string} args.name
* @param {Unit} args.unit
* @param {VecId} [args.vecId]
* @param {Accessor<CandlestickData[]>} [args.data]
* @param {number} [args.paneIndex]
* @param {boolean} [args.defaultActive]
*/
@@ -287,15 +301,12 @@ export default import("./v5.0.5-treeshaked/script.js").then((lc) => {
unit,
paneIndex: _paneIndex,
defaultActive,
data,
}) {
const paneIndex = _paneIndex ?? 0;
if (!ichart || !timeResource) throw Error("Chart not fully set");
const valuesResource = vecsResources.getOrCreate(vecIndex, vecId);
valuesResource.fetch();
activeResources.push(valuesResource);
const green = colors.green();
const red = colors.red();
const series = ichart.addSeries(
@@ -311,31 +322,56 @@ export default import("./v5.0.5-treeshaked/script.js").then((lc) => {
paneIndex,
);
let url = /** @type {string | undefined} */ (undefined);
if (vecId) {
const valuesResource = vecsResources.getOrCreate(vecIndex, vecId);
valuesResource.fetch();
activeResources.push(valuesResource);
createSetFetchedDataEffect(series, valuesResource);
url = valuesResource.url;
} else if (data) {
signals.runWithOwner(owner, () =>
signals.createEffect(data, (data) => {
series.setData(data);
}),
);
}
legend.add({
series,
name,
defaultActive,
colors: [colors.green, colors.red],
url: valuesResource.url,
url,
});
createPaneHeightObserver({
ichart,
paneIndex,
signals,
utils,
});
createPriceScaleSelectorIfNeeded({
ichart,
paneIndex,
seriesType: "Candlestick",
signals,
id,
unit,
utils,
});
createSetDataEffect(series, valuesResource);
return series;
},
/**
* @param {Object} args
* @param {VecId} args.vecId
* @param {string} args.name
* @param {Unit} args.unit
* @param {Accessor<LineData[]>} [args.data]
* @param {VecId} [args.vecId]
* @param {Color} [args.color]
* @param {number} [args.paneIndex]
* @param {boolean} [args.defaultActive]
@@ -347,15 +383,12 @@ export default import("./v5.0.5-treeshaked/script.js").then((lc) => {
color,
paneIndex: _paneIndex,
defaultActive,
data,
}) {
if (!ichart || !timeResource) throw Error("Chart not fully set");
const paneIndex = _paneIndex ?? 0;
const valuesResource = vecsResources.getOrCreate(vecIndex, vecId);
valuesResource.fetch();
activeResources.push(valuesResource);
color ||= colors.orange;
const series = ichart.addSeries(
@@ -369,16 +402,34 @@ export default import("./v5.0.5-treeshaked/script.js").then((lc) => {
paneIndex,
);
let url = /** @type {string | undefined} */ (undefined);
if (vecId) {
const valuesResource = vecsResources.getOrCreate(vecIndex, vecId);
valuesResource.fetch();
activeResources.push(valuesResource);
createSetFetchedDataEffect(series, valuesResource);
url = valuesResource.url;
} else if (data) {
signals.runWithOwner(owner, () =>
signals.createEffect(data, (data) => {
series.setData(data);
ichart
?.timeScale()
.setVisibleLogicalRange({ from: -1, to: data.length });
}),
);
}
legend.add({
series,
colors: [color],
name,
defaultActive,
url: valuesResource.url,
url,
});
createSetDataEffect(series, valuesResource);
createPaneHeightObserver({
ichart,
paneIndex,
@@ -390,6 +441,94 @@ export default import("./v5.0.5-treeshaked/script.js").then((lc) => {
ichart,
paneIndex,
signals,
seriesType: "Line",
id,
unit,
utils,
});
return series;
},
/**
* @param {Object} args
* @param {string} args.name
* @param {Unit} args.unit
* @param {Accessor<BaselineData[]>} [args.data]
* @param {VecId} [args.vecId]
* @param {number} [args.paneIndex]
* @param {boolean} [args.defaultActive]
*/
addBaselineSeries({
vecId,
name,
unit,
paneIndex: _paneIndex,
defaultActive,
data,
}) {
if (!ichart || !timeResource) throw Error("Chart not fully set");
const paneIndex = _paneIndex ?? 0;
const series = ichart.addSeries(
/** @type {SeriesDefinition<'Baseline'>} */ (lc.BaselineSeries),
{
lineWidth: /** @type {any} */ (1.5),
visible: defaultActive !== false,
topLineColor: colors.green(),
bottomLineColor: colors.red(),
baseValue: {
price: 0,
},
baseLineStyle: 0,
baseLineWidth: 1,
baseLineVisible: true,
lineVisible: true,
},
paneIndex,
);
let url = /** @type {string | undefined} */ (undefined);
if (vecId) {
const valuesResource = vecsResources.getOrCreate(vecIndex, vecId);
valuesResource.fetch();
activeResources.push(valuesResource);
createSetFetchedDataEffect(series, valuesResource);
url = valuesResource.url;
} else if (data) {
signals.runWithOwner(owner, () =>
signals.createEffect(data, (data) => {
series.setData(data);
ichart
?.timeScale()
.setVisibleLogicalRange({ from: -1, to: data.length });
}),
);
}
legend.add({
series,
colors: [colors.green, colors.red],
name,
defaultActive,
url,
});
createPaneHeightObserver({
ichart,
paneIndex,
signals,
utils,
});
createPriceScaleSelectorIfNeeded({
ichart,
paneIndex,
signals,
seriesType: "Baseline",
id,
unit,
utils,
});
@@ -410,6 +549,41 @@ export default import("./v5.0.5-treeshaked/script.js").then((lc) => {
legend.reset();
},
};
config?.forEach(({ unit, blueprints }, paneIndex) => {
chart.create({ index: /** @satisfies {Dateindex} */ (1) });
blueprints.forEach((blueprint) => {
if (blueprint.type === "Candlestick") {
chart.addCandlestickSeries({
name: blueprint.title,
unit,
data: blueprint.data,
defaultActive: blueprint.defaultActive,
paneIndex,
});
} else if (blueprint.type === "Baseline") {
chart.addBaselineSeries({
name: blueprint.title,
unit,
data: blueprint.data,
defaultActive: blueprint.defaultActive,
paneIndex,
});
} else {
chart.addLineSeries({
name: blueprint.title,
unit,
data: blueprint.data,
defaultActive: blueprint.defaultActive,
paneIndex,
color: blueprint.color,
});
}
});
});
return chart;
}
return {
@@ -678,7 +852,6 @@ function createPaneHeightObserver({ ichart, paneIndex, signals, utils }) {
if (!paneIndex) return;
const owner = signals.getOwner();
if (!owner) throw Error("Expect owner");
const one = "1";
@@ -729,6 +902,8 @@ function createPaneHeightObserver({ ichart, paneIndex, signals, utils }) {
* @param {Object} args
* @param {IChartApi} args.ichart
* @param {Unit} args.unit
* @param {string} args.id
* @param {SeriesType} args.seriesType
* @param {number} args.paneIndex
* @param {Signals} args.signals
* @param {Utilities} args.utils
@@ -737,11 +912,12 @@ function createPriceScaleSelectorIfNeeded({
ichart,
unit,
paneIndex,
id,
seriesType,
signals,
utils,
}) {
const owner = signals.getOwner();
if (!owner) throw Error("Expect owner");
setTimeout(
() => {
@@ -759,16 +935,20 @@ function createPriceScaleSelectorIfNeeded({
return;
}
console.log(id);
const choices = /**@type {const} */ (["lin", "log"]);
/** @typedef {(typeof choices)[number]} Choices */
const serializedValue = signals.createSignal(
/** @satisfies {Choices} */ (paneIndex ? "lin" : "log"),
/** @satisfies {Choices} */ (
unit === "US Dollars" && seriesType !== "Baseline" ? "log" : "lin"
),
{
save: {
...utils.serde.string,
keyPrefix: "charts",
key: `price-scale-${paneIndex}`,
keyPrefix: "",
key: `${id}-price-scale-${paneIndex}`,
},
},
);
@@ -777,7 +957,7 @@ function createPriceScaleSelectorIfNeeded({
title: unit,
selected: serializedValue(),
choices: choices,
id: unit,
id: `${id}-${unit.replace(" ", "-")}`,
signals,
});
@@ -789,6 +969,7 @@ function createPriceScaleSelectorIfNeeded({
const element = window.document.createElement(tagName);
element.dataset.size = "xs";
element.id = `${id}-price-scale-${paneIndex}`;
element.append(field);
const mode = signals.createMemo(() => {