@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 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.
@@ -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;IAyH5B,SAAS,CAAC,QAAQ,EAAE,yBAAyB,GAAG,MAAM,IAAI;IAQ1D,WAAW,IAAI,UAAU;IAoBzB,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"}
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;AAM/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;AA8DD,wBAAgB,iBAAiB,CAChC,MAAM,EAAE,OAAO,EACf,MAAM,EAAE,qBAAqB,EAC7B,KAAK,EAAE,WAAW,EAClB,IAAI,EAAE,MAAM,GACV,YAAY,CAoGd;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
+ {"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: point.value ?? 0,
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 = requirePoint(points, 0, false, true, `${path}/anchor`);
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 = requirePoint(points, 0, true, false, `${path}/anchor`);
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 = requirePoint(points, 0, true, true, `${path}/points/0`);
206
- const end = requirePoint(points, 1, true, true, `${path}/points/1`);
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 = requirePoint(points, 0, true, true, `${path}/points/0`);
217
- const end = requirePoint(points, 1, true, true, `${path}/points/1`);
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
- requirePoint(points, 0, true, true, `${path}/points/0`),
233
- requirePoint(points, 1, true, true, `${path}/points/1`),
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
- requirePoint(points, 0, true, true, `${path}/points/0`),
243
- requirePoint(points, 1, true, true, `${path}/points/1`),
244
- requirePoint(points, 2, true, true, `${path}/points/2`),
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) => requirePoint(points, index, true, true, `${path}/points/${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 = requirePoint(points, 0, false, true, `${path}/anchor`);
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: requirePoint(points, 0, true, true, `${path}/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: requirePoint(points, 0, true, true, `${path}/start`),
280
- end: requirePoint(points, 1, true, true, `${path}/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: requirePoint(points, 0, true, true, `${path}/point`),
289
+ point: readPoint(0, true, true, `${path}/point`),
286
290
  };
287
291
  }
288
292
  }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * 把引擎价格按 Scene 标的精度做十进制舍入。
3
+ * 舍入规则为最接近,恰好半值时远离零;任何负零统一输出正零。
4
+ */
5
+ export declare function normalizePriceValue(value: number, pricePrecision: number, path: string): number;
6
+ //# sourceMappingURL=price.d.ts.map
@@ -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
@@ -1,5 +1,5 @@
1
1
  /** Adapter 包版本。 */
2
- export declare const ADAPTER_PACKAGE_VERSION: "0.1.0";
2
+ export declare const ADAPTER_PACKAGE_VERSION: "0.1.1";
3
3
  /** 唯一允许加载的 KLineCharts 引擎版本。 */
4
4
  export declare const KLINECHARTS_ENGINE_VERSION: "10.0.0";
5
5
  /** 与 ChartScene 绑定的 Runtime 协议版本。 */
package/dist/version.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /** Adapter 包版本。 */
2
- export const ADAPTER_PACKAGE_VERSION = '0.1.0';
2
+ export const ADAPTER_PACKAGE_VERSION = '0.1.1';
3
3
  /** 唯一允许加载的 KLineCharts 引擎版本。 */
4
4
  export const KLINECHARTS_ENGINE_VERSION = '10.0.0';
5
5
  /** 与 ChartScene 绑定的 Runtime 协议版本。 */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@baron1996/klinecharts-adapter",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Controlled ChartScene adapter for KLineCharts.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",