@baron1996/klinecharts-adapter 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js +2 -2
- package/dist/conversion/overlays.d.ts +1 -1
- package/dist/conversion/overlays.d.ts.map +1 -1
- package/dist/conversion/overlays.js +24 -20
- package/dist/conversion/price.d.ts +6 -0
- package/dist/conversion/price.d.ts.map +1 -0
- package/dist/conversion/price.js +48 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,3 +8,9 @@ npm install @baron1996/klinecharts-adapter
|
|
|
8
8
|
|
|
9
9
|
The adapter loads only scene-owned static data, maps supported panes, indicators and
|
|
10
10
|
overlays, and owns chart lifecycle and touch gestures. KLineCharts is the only engine.
|
|
11
|
+
|
|
12
|
+
At the engine-to-Scene boundary, every price-bearing Overlay coordinate is normalized
|
|
13
|
+
to `scene.symbol.pricePrecision` (validated as `0..16`). Normalization uses the finite
|
|
14
|
+
JavaScript Number's decimal representation, rounds to nearest with exact halfway values
|
|
15
|
+
away from zero, rejects non-finite input/output, and canonicalizes negative zero to
|
|
16
|
+
positive zero. Timestamp coordinates are never changed by this rule.
|
package/dist/adapter.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,UAAU,EACV,cAAc,EACd,YAAY,EACZ,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAEN,UAAU,EACV,MAAM,+BAA+B,CAAC;AAUvC,OAAO,EAIN,KAAK,oBAAoB,EAGzB,MAAM,0BAA0B,CAAC;AAGlC,MAAM,WAAW,wBAAwB;IACxC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACtC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,eAAe;IAC/B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,UAAU,EAAE,SAAS,wBAAwB,EAAE,CAAC;IACzD,QAAQ,CAAC,QAAQ,EAAE,SAAS,YAAY,EAAE,CAAC;IAC3C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;CACrC;AAED,MAAM,MAAM,iBAAiB,GAC1B;IAAE,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAA;CAAE,GACpE;IAAE,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAA;CAAE,GACpE;IAAE,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GACzD;IAAE,QAAQ,CAAC,IAAI,EAAE,kBAAkB,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAC1D;IAAE,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,SAAS,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,EAAE,CAAA;CAAE,CAAC;AAE9F,MAAM,MAAM,yBAAyB,GAAG,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAC;AAE3E,MAAM,WAAW,qBAAsB,SAAQ,oBAAoB;CAAG;AAEtE;;;GAGG;AACH,qBAAa,uBAAuB;;IAkBnC,OAAO;WAea,MAAM,CACzB,SAAS,EAAE,WAAW,EACtB,KAAK,EAAE,OAAO,GACZ,OAAO,CAAC,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,UAAU,EACV,cAAc,EACd,YAAY,EACZ,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAEN,UAAU,EACV,MAAM,+BAA+B,CAAC;AAUvC,OAAO,EAIN,KAAK,oBAAoB,EAGzB,MAAM,0BAA0B,CAAC;AAGlC,MAAM,WAAW,wBAAwB;IACxC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACtC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,eAAe;IAC/B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,UAAU,EAAE,SAAS,wBAAwB,EAAE,CAAC;IACzD,QAAQ,CAAC,QAAQ,EAAE,SAAS,YAAY,EAAE,CAAC;IAC3C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;CACrC;AAED,MAAM,MAAM,iBAAiB,GAC1B;IAAE,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAA;CAAE,GACpE;IAAE,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAA;CAAE,GACpE;IAAE,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GACzD;IAAE,QAAQ,CAAC,IAAI,EAAE,kBAAkB,CAAC;IAAC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAC1D;IAAE,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,SAAS,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,EAAE,CAAA;CAAE,CAAC;AAE9F,MAAM,MAAM,yBAAyB,GAAG,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAC;AAE3E,MAAM,WAAW,qBAAsB,SAAQ,oBAAoB;CAAG;AAEtE;;;GAGG;AACH,qBAAa,uBAAuB;;IAkBnC,OAAO;WAea,MAAM,CACzB,SAAS,EAAE,WAAW,EACtB,KAAK,EAAE,OAAO,GACZ,OAAO,CAAC,uBAAuB,CAAC;IA+H5B,SAAS,CAAC,QAAQ,EAAE,yBAAyB,GAAG,MAAM,IAAI;IAQ1D,WAAW,IAAI,UAAU;IA0BzB,UAAU,CAAC,KAAK,EAAE,YAAY,GAAG,YAAY;IA4B7C,aAAa,CAAC,KAAK,EAAE,YAAY,GAAG,YAAY;IA6BhD,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAuBlC,mBAAmB,CAAC,OAAO,EAAE,qBAAqB,GAAG,MAAM;IAyB3D,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;IAIhD,YAAY,IAAI,SAAS,YAAY,EAAE;IAIvC,OAAO,IAAI,eAAe;IA8B1B,OAAO,IAAI,IAAI;CAUtB"}
|
package/dist/adapter.js
CHANGED
|
@@ -79,7 +79,7 @@ export class KLineChartsSceneAdapter {
|
|
|
79
79
|
const existingIndex = this.#scene.overlays.findIndex((overlay) => overlay.id === source.id);
|
|
80
80
|
const path = existingIndex < 0 ? `/overlays/${this.#scene.overlays.length}` : `/overlays/${existingIndex}`;
|
|
81
81
|
const currentSource = existingIndex < 0 ? source : this.#scene.overlays[existingIndex];
|
|
82
|
-
const overlay = fromEngineOverlay(engineOverlay, currentSource, this.#idMap, path);
|
|
82
|
+
const overlay = fromEngineOverlay(engineOverlay, currentSource, this.#idMap, path, this.#scene.symbol.pricePrecision);
|
|
83
83
|
const overlays = structuredClone(this.#scene.overlays);
|
|
84
84
|
if (existingIndex < 0) {
|
|
85
85
|
overlays.push(overlay);
|
|
@@ -145,7 +145,7 @@ export class KLineChartsSceneAdapter {
|
|
|
145
145
|
if (engine === undefined) {
|
|
146
146
|
throw new SceneError('EXPORT_INVALID', `/overlays/${index}`, `KLineCharts lost Overlay ${source.id}.`);
|
|
147
147
|
}
|
|
148
|
-
return fromEngineOverlay(engine, source, this.#idMap, `/overlays/${index}
|
|
148
|
+
return fromEngineOverlay(engine, source, this.#idMap, `/overlays/${index}`, this.#scene.symbol.pricePrecision);
|
|
149
149
|
});
|
|
150
150
|
return parseChartScene({
|
|
151
151
|
...structuredClone(this.#scene),
|
|
@@ -25,7 +25,7 @@ export interface EngineOverlayCallbacks {
|
|
|
25
25
|
export declare function toEngineOverlay(overlay: SceneOverlay, idMap: EngineIdMap, path: string, callbacks?: EngineOverlayCallbacks): OverlayCreate;
|
|
26
26
|
/** 创建尚无几何点的交互式 Overlay,不把临时状态写入 Scene。 */
|
|
27
27
|
export declare function toEngineOverlayDrawing(source: OverlayDrawingSource, idMap: EngineIdMap, callbacks: EngineOverlayCallbacks): OverlayCreate;
|
|
28
|
-
export declare function fromEngineOverlay(engine: Overlay, source: OverlaySourceSnapshot, idMap: EngineIdMap, path: string): SceneOverlay;
|
|
28
|
+
export declare function fromEngineOverlay(engine: Overlay, source: OverlaySourceSnapshot, idMap: EngineIdMap, path: string, pricePrecision: number): SceneOverlay;
|
|
29
29
|
/** 在引擎内创建场景的全部标注。 */
|
|
30
30
|
export declare function createSceneOverlays(scene: ChartScene, chart: {
|
|
31
31
|
createOverlay(value: OverlayCreate): string | null | Array<string | null>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"overlays.d.ts","sourceRoot":"","sources":["../../src/conversion/overlays.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,UAAU,EACV,YAAY,EACZ,MAAM,+BAA+B,CAAC;AAEvC,OAAO,KAAK,EAEX,OAAO,EACP,aAAa,EAGb,MAAM,aAAa,CAAC;AAErB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"overlays.d.ts","sourceRoot":"","sources":["../../src/conversion/overlays.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,UAAU,EACV,YAAY,EACZ,MAAM,+BAA+B,CAAC;AAEvC,OAAO,KAAK,EAEX,OAAO,EACP,aAAa,EAGb,MAAM,aAAa,CAAC;AAErB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAO/C,MAAM,WAAW,qBAAqB;IACrC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC,MAAM,CAAC,CAAC;IACpC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC,MAAM,CAAC,CAAC;IACpC,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC,QAAQ,CAAC,CAAC;IACxC,QAAQ,CAAC,QAAQ,CAAC,EAAE,YAAY,CAAC,UAAU,CAAC,CAAC;CAC7C;AAED,MAAM,WAAW,oBAAqB,SAAQ,qBAAqB;IAClE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,sBAAsB;IACtC,QAAQ,CAAC,SAAS,CAAC,EAAE,WAAW,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,CAAC;IAC7D,QAAQ,CAAC,gBAAgB,CAAC,EAAE,WAAW,CAAC,aAAa,CAAC,kBAAkB,CAAC,CAAC,CAAC;IAC3E,QAAQ,CAAC,SAAS,CAAC,EAAE,WAAW,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,CAAC;IAC7D,QAAQ,CAAC,UAAU,CAAC,EAAE,WAAW,CAAC,aAAa,CAAC,YAAY,CAAC,CAAC,CAAC;CAC/D;AAmGD,wBAAgB,eAAe,CAC9B,OAAO,EAAE,YAAY,EACrB,KAAK,EAAE,WAAW,EAClB,IAAI,EAAE,MAAM,EACZ,SAAS,GAAE,sBAA2B,GACpC,aAAa,CAwBf;AAED,0CAA0C;AAC1C,wBAAgB,sBAAsB,CACrC,MAAM,EAAE,oBAAoB,EAC5B,KAAK,EAAE,WAAW,EAClB,SAAS,EAAE,sBAAsB,GAC/B,aAAa,CAsBf;AAiED,wBAAgB,iBAAiB,CAChC,MAAM,EAAE,OAAO,EACf,MAAM,EAAE,qBAAqB,EAC7B,KAAK,EAAE,WAAW,EAClB,IAAI,EAAE,MAAM,EACZ,cAAc,EAAE,MAAM,GACpB,YAAY,CAiHd;AAED,qBAAqB;AACrB,wBAAgB,mBAAmB,CAClC,KAAK,EAAE,UAAU,EACjB,KAAK,EAAE;IAAE,aAAa,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,GAAG,IAAI,GAAG,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,CAAA;CAAE,EACpF,KAAK,EAAE,WAAW,EAClB,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,YAAY,KAAK,sBAAsB,GAC3D,IAAI,CAiBN"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { SceneError } from '@baron1996/kline-scene-schema';
|
|
2
2
|
import { requireMappedId } from './id-map.js';
|
|
3
|
+
import { normalizePriceValue } from './price.js';
|
|
3
4
|
import { isSupportedOverlay } from '../registry/overlays.js';
|
|
4
5
|
function toLineStyle(style) {
|
|
5
6
|
return {
|
|
@@ -143,7 +144,7 @@ export function toEngineOverlayDrawing(source, idMap, callbacks) {
|
|
|
143
144
|
}
|
|
144
145
|
return value;
|
|
145
146
|
}
|
|
146
|
-
function requirePoint(points, index, requireTimestamp, requireValue, path) {
|
|
147
|
+
function requirePoint(points, index, requireTimestamp, requireValue, path, pricePrecision) {
|
|
147
148
|
const point = points[index];
|
|
148
149
|
if (point === undefined ||
|
|
149
150
|
(requireTimestamp && !Number.isSafeInteger(point.timestamp)) ||
|
|
@@ -152,7 +153,9 @@ function requirePoint(points, index, requireTimestamp, requireValue, path) {
|
|
|
152
153
|
}
|
|
153
154
|
return {
|
|
154
155
|
timestamp: point.timestamp ?? 0,
|
|
155
|
-
value:
|
|
156
|
+
value: requireValue
|
|
157
|
+
? normalizePriceValue(point.value, pricePrecision, `${path}/value`)
|
|
158
|
+
: 0,
|
|
156
159
|
};
|
|
157
160
|
}
|
|
158
161
|
function requireText(overlay, path) {
|
|
@@ -187,23 +190,24 @@ function baseFromEngine(engine, source, idMap, path) {
|
|
|
187
190
|
}
|
|
188
191
|
return base;
|
|
189
192
|
}
|
|
190
|
-
export function fromEngineOverlay(engine, source, idMap, path) {
|
|
193
|
+
export function fromEngineOverlay(engine, source, idMap, path, pricePrecision) {
|
|
191
194
|
const base = baseFromEngine(engine, source, idMap, path);
|
|
192
195
|
const points = engine.points;
|
|
196
|
+
const readPoint = (index, requireTimestamp, requireValue, pointPath) => requirePoint(points, index, requireTimestamp, requireValue, pointPath, pricePrecision);
|
|
193
197
|
switch (base.type) {
|
|
194
198
|
case 'horizontalStraightLine':
|
|
195
199
|
case 'priceLine': {
|
|
196
|
-
const point =
|
|
200
|
+
const point = readPoint(0, false, true, `${path}/anchor`);
|
|
197
201
|
return { ...base, anchor: { value: point.value } };
|
|
198
202
|
}
|
|
199
203
|
case 'verticalStraightLine': {
|
|
200
|
-
const point =
|
|
204
|
+
const point = readPoint(0, true, false, `${path}/anchor`);
|
|
201
205
|
return { ...base, anchor: { timestamp: point.timestamp } };
|
|
202
206
|
}
|
|
203
207
|
case 'horizontalRayLine':
|
|
204
208
|
case 'horizontalSegment': {
|
|
205
|
-
const start =
|
|
206
|
-
const end =
|
|
209
|
+
const start = readPoint(0, true, true, `${path}/points/0`);
|
|
210
|
+
const end = readPoint(1, true, true, `${path}/points/1`);
|
|
207
211
|
return {
|
|
208
212
|
...base,
|
|
209
213
|
value: start.value,
|
|
@@ -213,8 +217,8 @@ export function fromEngineOverlay(engine, source, idMap, path) {
|
|
|
213
217
|
}
|
|
214
218
|
case 'verticalRayLine':
|
|
215
219
|
case 'verticalSegment': {
|
|
216
|
-
const start =
|
|
217
|
-
const end =
|
|
220
|
+
const start = readPoint(0, true, true, `${path}/points/0`);
|
|
221
|
+
const end = readPoint(1, true, true, `${path}/points/1`);
|
|
218
222
|
return {
|
|
219
223
|
...base,
|
|
220
224
|
timestamp: start.timestamp,
|
|
@@ -229,8 +233,8 @@ export function fromEngineOverlay(engine, source, idMap, path) {
|
|
|
229
233
|
return {
|
|
230
234
|
...base,
|
|
231
235
|
points: [
|
|
232
|
-
|
|
233
|
-
|
|
236
|
+
readPoint(0, true, true, `${path}/points/0`),
|
|
237
|
+
readPoint(1, true, true, `${path}/points/1`),
|
|
234
238
|
],
|
|
235
239
|
};
|
|
236
240
|
}
|
|
@@ -239,14 +243,14 @@ export function fromEngineOverlay(engine, source, idMap, path) {
|
|
|
239
243
|
return {
|
|
240
244
|
...base,
|
|
241
245
|
points: [
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
246
|
+
readPoint(0, true, true, `${path}/points/0`),
|
|
247
|
+
readPoint(1, true, true, `${path}/points/1`),
|
|
248
|
+
readPoint(2, true, true, `${path}/points/2`),
|
|
245
249
|
],
|
|
246
250
|
};
|
|
247
251
|
}
|
|
248
252
|
case 'brush': {
|
|
249
|
-
const converted = points.map((_point, index) =>
|
|
253
|
+
const converted = points.map((_point, index) => readPoint(index, true, true, `${path}/points/${index}`));
|
|
250
254
|
const first = converted[0];
|
|
251
255
|
if (first === undefined) {
|
|
252
256
|
throw new SceneError('EXPORT_INVALID', `${path}/points`, 'Brush has no points.');
|
|
@@ -257,7 +261,7 @@ export function fromEngineOverlay(engine, source, idMap, path) {
|
|
|
257
261
|
};
|
|
258
262
|
}
|
|
259
263
|
case 'simpleTag': {
|
|
260
|
-
const point =
|
|
264
|
+
const point = readPoint(0, false, true, `${path}/anchor`);
|
|
261
265
|
return {
|
|
262
266
|
...base,
|
|
263
267
|
anchor: { value: point.value },
|
|
@@ -269,20 +273,20 @@ export function fromEngineOverlay(engine, source, idMap, path) {
|
|
|
269
273
|
case 'text':
|
|
270
274
|
return {
|
|
271
275
|
...base,
|
|
272
|
-
point:
|
|
276
|
+
point: readPoint(0, true, true, `${path}/point`),
|
|
273
277
|
text: requireText(engine, `${path}/text`),
|
|
274
278
|
};
|
|
275
279
|
case 'rectangle':
|
|
276
280
|
case 'arrow':
|
|
277
281
|
return {
|
|
278
282
|
...base,
|
|
279
|
-
start:
|
|
280
|
-
end:
|
|
283
|
+
start: readPoint(0, true, true, `${path}/start`),
|
|
284
|
+
end: readPoint(1, true, true, `${path}/end`),
|
|
281
285
|
};
|
|
282
286
|
case 'crossLine':
|
|
283
287
|
return {
|
|
284
288
|
...base,
|
|
285
|
-
point:
|
|
289
|
+
point: readPoint(0, true, true, `${path}/point`),
|
|
286
290
|
};
|
|
287
291
|
}
|
|
288
292
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"price.d.ts","sourceRoot":"","sources":["../../src/conversion/price.ts"],"names":[],"mappings":"AAwBA;;;GAGG;AACH,wBAAgB,mBAAmB,CAClC,KAAK,EAAE,MAAM,EACb,cAAc,EAAE,MAAM,EACtB,IAAI,EAAE,MAAM,GACV,MAAM,CAkCR"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { SceneError } from '@baron1996/kline-scene-schema';
|
|
2
|
+
function toDecimalParts(value) {
|
|
3
|
+
const [coefficient = '', exponentText] = value.toString().toLowerCase().split('e');
|
|
4
|
+
const [integer = '', fraction = ''] = coefficient.split('.');
|
|
5
|
+
return {
|
|
6
|
+
digits: BigInt(`${integer}${fraction}`),
|
|
7
|
+
exponent: Number(exponentText ?? 0) - fraction.length,
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
function toDecimalNumber(value, precision, negative) {
|
|
11
|
+
const digits = value.toString().padStart(precision + 1, '0');
|
|
12
|
+
const unsigned = precision === 0
|
|
13
|
+
? digits
|
|
14
|
+
: `${digits.slice(0, -precision)}.${digits.slice(-precision)}`;
|
|
15
|
+
return Number(`${negative ? '-' : ''}${unsigned}`);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* 把引擎价格按 Scene 标的精度做十进制舍入。
|
|
19
|
+
* 舍入规则为最接近,恰好半值时远离零;任何负零统一输出正零。
|
|
20
|
+
*/
|
|
21
|
+
export function normalizePriceValue(value, pricePrecision, path) {
|
|
22
|
+
if (!Number.isFinite(value)) {
|
|
23
|
+
throw new SceneError('EXPORT_INVALID', path, 'KLineCharts returned a non-finite price value.');
|
|
24
|
+
}
|
|
25
|
+
if (!Number.isInteger(pricePrecision) ||
|
|
26
|
+
pricePrecision < 0 ||
|
|
27
|
+
pricePrecision > 16) {
|
|
28
|
+
throw new SceneError('EXPORT_INVALID', '/symbol/pricePrecision', 'Scene pricePrecision must be an integer from 0 through 16.');
|
|
29
|
+
}
|
|
30
|
+
const negative = value < 0;
|
|
31
|
+
const { digits, exponent } = toDecimalParts(Math.abs(value));
|
|
32
|
+
const scaledExponent = exponent + pricePrecision;
|
|
33
|
+
let rounded;
|
|
34
|
+
if (scaledExponent >= 0) {
|
|
35
|
+
rounded = digits * (10n ** BigInt(scaledExponent));
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
const divisor = 10n ** BigInt(-scaledExponent);
|
|
39
|
+
const quotient = digits / divisor;
|
|
40
|
+
const remainder = digits % divisor;
|
|
41
|
+
rounded = quotient + (remainder * 2n >= divisor ? 1n : 0n);
|
|
42
|
+
}
|
|
43
|
+
const normalized = toDecimalNumber(rounded, pricePrecision, negative);
|
|
44
|
+
if (!Number.isFinite(normalized)) {
|
|
45
|
+
throw new SceneError('EXPORT_INVALID', path, 'Normalized price value is not finite.');
|
|
46
|
+
}
|
|
47
|
+
return Object.is(normalized, -0) ? 0 : normalized;
|
|
48
|
+
}
|
package/dist/version.d.ts
CHANGED
package/dist/version.js
CHANGED