wickchart 1.2.0 → 1.3.0

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/types/core.d.ts CHANGED
@@ -747,6 +747,162 @@ export declare function normalizeScenario(spec: any): null | {
747
747
  color: string | null;
748
748
  label: string;
749
749
  };
750
+ /**
751
+ * Validate a risk plan: entry + stop define 1R (the risk unit per trade);
752
+ * reward levels are drawn at R multiples beyond the entry. Invalid input
753
+ * is dropped, never thrown — same contract as setOverlays/setScenario.
754
+ *
755
+ * { entry: 64500, stop: 63800, // stop < entry ⇒ long; else short
756
+ * multiples: [1, 2, 3], // R-multiple levels (default [1,2,3])
757
+ * targets: [65900, 67300], // alternative: explicit prices → kR
758
+ * label: 'breakout plan' } // ≤ 40 chars
759
+ *
760
+ * Explicit `targets` are converted to their (signed) R multiple; levels on
761
+ * the wrong side of the entry (negative or ~zero R) are dropped. `multiples`
762
+ * win when both are given. At most 8 levels, each ≤ 20R.
763
+ *
764
+ * @param {any} spec
765
+ * @returns {null|{entry: number, stop: number, risk: number,
766
+ * direction: 'long'|'short', levels: {k: number, price: number}[],
767
+ * maxK: number, label: string}}
768
+ */
769
+ export declare function normalizeRiskPlan(spec: any): null | {
770
+ entry: number;
771
+ stop: number;
772
+ risk: number;
773
+ direction: 'long' | 'short';
774
+ levels: {
775
+ k: number;
776
+ price: number;
777
+ }[];
778
+ maxK: number;
779
+ label: string;
780
+ };
781
+ /**
782
+ * Turn a bar window into an ordered story: the annotation events (pivot
783
+ * highs/lows, volume spikes, gaps, RSI divergences) plus derived **legs** —
784
+ * the move between consecutive opposite pivots ("+12.4% over 38 bars").
785
+ * The timeline drives the bar-walk player and any caption UI.
786
+ *
787
+ * @param {Bar[]} bars full dataset
788
+ * @param {number} i0 first index of the window
789
+ * @param {number} i1 last index of the window
790
+ * @param {{pivot?: number, volMult?: number, gapMult?: number, rsiPeriod?: number}} [opts]
791
+ * pivot window defaults to 8 (denser than the annotations overlay's 20)
792
+ * @returns {{i: number, time: number, type: string, side: string, note: string,
793
+ * legPct?: number, legBars?: number}[]} sorted by index, capped at 60
794
+ */
795
+ export declare function narrateWindow(bars: Bar[], i0: number, i1: number, opts?: {
796
+ pivot?: number;
797
+ volMult?: number;
798
+ gapMult?: number;
799
+ rsiPeriod?: number;
800
+ }): {
801
+ i: number;
802
+ time: number;
803
+ type: string;
804
+ side: string;
805
+ note: string;
806
+ legPct?: number;
807
+ legBars?: number;
808
+ }[];
809
+ /**
810
+ * Stats for a brushed bar range: net move (open of the first bar → close
811
+ * of the last), extremes, and summed volume. Powers the brush-selection
812
+ * overlay and its `wick:brush` event.
813
+ *
814
+ * @param {Bar[]} bars full dataset
815
+ * @param {number} i0 first selected index
816
+ * @param {number} i1 last selected index
817
+ * @returns {null|{bars: number, from: {index: number, time: number},
818
+ * to: {index: number, time: number}, firstOpen: number,
819
+ * lastClose: number, delta: number, deltaPct: number,
820
+ * high: number, low: number, volume: number}}
821
+ */
822
+ export declare function brushStats(bars: Bar[], i0: number, i1: number): null | {
823
+ bars: number;
824
+ from: {
825
+ index: number;
826
+ time: number;
827
+ };
828
+ to: {
829
+ index: number;
830
+ time: number;
831
+ };
832
+ firstOpen: number;
833
+ lastClose: number;
834
+ delta: number;
835
+ deltaPct: number;
836
+ high: number;
837
+ low: number;
838
+ volume: number;
839
+ };
840
+ /** Smoothest cheap easing for viewport pans: slow in, slow out. */
841
+ export declare function easeInOutCubic(t: any): number;
842
+ /**
843
+ * Validate one story scene. Every field is optional except that a scene
844
+ * must be an object; omitted fields simply don't change that aspect of
845
+ * the chart when played. `scenario`/`riskPlan` use a 'clear' sentinel for
846
+ * explicit "remove it" (null input means clear too when the KEY is present).
847
+ *
848
+ * { title: 'The breakout', note: 'What happened…',
849
+ * range: { from, to }, // times (s or ms) — the camera pans there
850
+ * indicators: 'sma:20 rsi:14', // optional indicator string
851
+ * type: 'candles', // optional series type
852
+ * overlays: [...], // optional zones/levels (normalizeOverlays)
853
+ * scenario: {...} | null, // set / clear a scenario
854
+ * riskPlan: {...} | null, // set / clear a risk plan
855
+ * dwell: 2200 } // ms to hold after the pan (500–30000)
856
+ *
857
+ * @returns {object|null} normalized scene, or null for non-objects
858
+ */
859
+ export declare function normalizeScene(scene: any): object | null;
860
+ /**
861
+ * Validate a whole story: normalize each scene, drop junk, cap at 20.
862
+ * @returns {object[]} possibly empty
863
+ */
864
+ export declare function sceneList(story: any): object[];
865
+ /**
866
+ * Tracks other charts viewing the same room: last-sighting timestamps per
867
+ * peer plus the viewport each one is looking at. Pure bookkeeping — the
868
+ * transport (BroadcastChannel, WebSocket, …) lives in the component/app.
869
+ *
870
+ * Peers expire `ttl` ms after their last sighting, so a closed tab fades
871
+ * out of the room without an explicit goodbye.
872
+ */
873
+ export declare class PresenceTracker {
874
+ ttl: number;
875
+ /** @type {Map<string, {id: string, name: string|null, range: {from:number,to:number}|null, at: number}>} */
876
+ peers: Map<string, {
877
+ id: string;
878
+ name: string | null;
879
+ range: {
880
+ from: number;
881
+ to: number;
882
+ } | null;
883
+ at: number;
884
+ }>;
885
+ /** @param {number} [ttl=12000] ms a peer survives without a sighting */
886
+ constructor(ttl?: number);
887
+ /**
888
+ * Record a sighting. `patch.range` ({from,to} times) is validated and
889
+ * normalized; a sighting without a range keeps the previous one.
890
+ * @returns {boolean} true when this sighting is a join (new peer)
891
+ */
892
+ track(id: any, patch?: {}, now?: number): boolean;
893
+ /** @returns {object|null} the removed peer entry, or null when unknown */
894
+ drop(id: any): object | null;
895
+ /** Expire peers not seen within the ttl.
896
+ * @returns {object[]} the peer entries that left */
897
+ sweep(now?: number): object[];
898
+ /** @returns {{id: string, name: string|null, range: object|null, at: number}[]} copies, oldest sighting first */
899
+ list(): {
900
+ id: string;
901
+ name: string | null;
902
+ range: object | null;
903
+ at: number;
904
+ }[];
905
+ }
750
906
  export declare const tfLabelOf: (dtMs: any) => string;
751
907
  /**
752
908
  * Compact, LLM-friendly summary of a bar window: structured fields plus a
@@ -1,3 +1,4 @@
1
+ import { PresenceTracker } from './core.js';
1
2
  declare const HTMLElementBase: {
2
3
  new (): {};
3
4
  };
@@ -108,6 +109,10 @@ declare class WickChart extends HTMLElementBase {
108
109
  at: number;
109
110
  };
110
111
  _ghostTimer: number;
112
+ _coviewLabel: any;
113
+ _presence: PresenceTracker;
114
+ _coviewBeat: number;
115
+ _coviewViewLast: number;
111
116
  _sonify: boolean;
112
117
  _actx: any;
113
118
  _lastToneIdx: number;
@@ -120,6 +125,36 @@ declare class WickChart extends HTMLElementBase {
120
125
  done: boolean;
121
126
  };
122
127
  _measuring: boolean;
128
+ _walkTimer: number;
129
+ _storyToken: number;
130
+ _story: object[];
131
+ _brush: boolean;
132
+ _brushSel: {
133
+ i0: number;
134
+ i1: number;
135
+ stats: {
136
+ bars: number;
137
+ from: {
138
+ index: number;
139
+ time: number;
140
+ };
141
+ to: {
142
+ index: number;
143
+ time: number;
144
+ };
145
+ firstOpen: number;
146
+ lastClose: number;
147
+ delta: number;
148
+ deltaPct: number;
149
+ high: number;
150
+ low: number;
151
+ volume: number;
152
+ };
153
+ };
154
+ _brushDrag: {
155
+ i0: any;
156
+ i1: any;
157
+ };
123
158
  _ind: {
124
159
  overlays: any[];
125
160
  panes: any[];
@@ -154,6 +189,18 @@ declare class WickChart extends HTMLElementBase {
154
189
  color: string | null;
155
190
  label: string;
156
191
  };
192
+ _riskPlan: {
193
+ entry: number;
194
+ stop: number;
195
+ risk: number;
196
+ direction: 'long' | 'short';
197
+ levels: {
198
+ k: number;
199
+ price: number;
200
+ }[];
201
+ maxK: number;
202
+ label: string;
203
+ };
157
204
  _onResize: () => void;
158
205
  _onPointerDown: (e: any) => void;
159
206
  _onPointerMove: (e: any) => void;
@@ -427,6 +474,26 @@ declare class WickChart extends HTMLElementBase {
427
474
  clearScenario(): void;
428
475
  /** @returns {object|null} a copy of the active scenario */
429
476
  get scenario(): object | null;
477
+ /**
478
+ * Risk plan: an R-multiple grid anchored at entry/stop. 1R = |entry −
479
+ * stop| (the risk unit); reward lines are drawn at kR beyond the entry
480
+ * with the risk/reward zones shaded, so sizing and take-profit choices
481
+ * read directly off the chart.
482
+ *
483
+ * chart.setRiskPlan({ entry: 64500, stop: 63800, multiples: [1, 2, 3] });
484
+ * chart.setRiskPlan({ entry: 64500, stop: 63800, targets: [65900, 67300] });
485
+ *
486
+ * Direction is derived (stop below entry ⇒ long). Targets convert to
487
+ * their R multiple; `multiples` win when both are given. Invalid specs
488
+ * clear the plan (replace semantics, like setScenario); excluded from
489
+ * getState/setState — it is app state, not chart state.
490
+ * @param {object} spec
491
+ * @returns {object|null} the normalized plan, or null when invalid
492
+ */
493
+ setRiskPlan(spec: object): object | null;
494
+ clearRiskPlan(): void;
495
+ /** @returns {object|null} a copy of the active risk plan */
496
+ get riskPlan(): object | null;
430
497
  /** σ-cone for the active scenario, cached per data version. */
431
498
  _scenarioConeCache(): any;
432
499
  /** Tool manifest for LLM control — JSON-safe copy of AI_TOOLS. */
@@ -582,11 +649,126 @@ declare class WickChart extends HTMLElementBase {
582
649
  _emitCrosshair(hover: any): void;
583
650
  /** Join/leave the co-view channel named by the `co-view` attribute. */
584
651
  _setupCoView(): void;
652
+ /** Broadcast our visible range for presence; throttled unless forced. */
653
+ _coviewSendView(force: any): void;
585
654
  _coviewSend(msg: any): void;
586
655
  _onCoMessage(m: any): void;
587
656
  /** Dispatch `wick:name` (canonical) plus the deprecated `hab:name` alias,
588
657
  * so 0.x listeners keep working until 2.0. */
589
658
  _fire(name: any, detail: any): void;
659
+ /**
660
+ * Live co-view peers: who else is in the room and the time window each
661
+ * one is looking at — [{ id, name, range: {from, to}, at }], oldest
662
+ * sighting first. Peers fade out ~12 s after their last sighting.
663
+ * @returns {object[]}
664
+ */
665
+ getPeers(): object[];
666
+ /**
667
+ * Narrated timeline for a window (default: the visible range) — pivot
668
+ * highs/lows, volume spikes, gaps, RSI divergences plus derived legs
669
+ * ("+12.4% over 38 bars"), sorted by index. Pure data, perfect for
670
+ * caption UIs or the walk player.
671
+ * chart.narrate(); // visible range
672
+ * chart.narrate({ from, to }); // times in ms (s accepted)
673
+ * @param {{from?: number, to?: number}} [range]
674
+ * @returns {{i: number, time: number, type: string, side: string, note: string,
675
+ * legPct?: number, legBars?: number}[]}
676
+ */
677
+ narrate(range?: {
678
+ from?: number;
679
+ to?: number;
680
+ }): {
681
+ i: number;
682
+ time: number;
683
+ type: string;
684
+ side: string;
685
+ note: string;
686
+ legPct?: number;
687
+ legBars?: number;
688
+ }[];
689
+ /**
690
+ * Walk the chart through history like a story: the viewport slides
691
+ * from `from` to `to` while `wick:walk` events announce every step and
692
+ * the narrator's events (spikes, gaps, pivots, legs) as they're crossed.
693
+ * Any user interaction — pointer, wheel, keys, double-click — stops it.
694
+ * chart.walk({ from: 0, to: 500, speed: 120, step: 10 });
695
+ * chart.addEventListener('wick:walk', (e) => showCaption(e.detail));
696
+ * // detail: { phase: 'step'|'end'|'stop', index, events: [...], from, to }
697
+ * @param {{from?: number, to?: number, speed?: number, step?: number}} [opts]
698
+ * from/to are bar indices (default: last ~500 bars → the end)
699
+ * @returns {boolean} true when the walk started
700
+ */
701
+ walk(opts?: {
702
+ from?: number;
703
+ to?: number;
704
+ speed?: number;
705
+ step?: number;
706
+ }): boolean;
707
+ /**
708
+ * Stop the running walk (if any). Fires a final `wick:walk`
709
+ * { phase: 'stop' } unless called internally.
710
+ */
711
+ stopWalk(silent: any): void;
712
+ /**
713
+ * Commit a brush selection over [i0, i1]: stores it (draws the band
714
+ * and delta chip) and fires `wick:brush` with the range statistics.
715
+ * @param {number} i0 first index
716
+ * @param {number} i1 last index
717
+ */
718
+ _brushFinish(i0: number, i1: number): void;
719
+ /** Clear the committed brush selection (if any). Escape does the same. */
720
+ clearBrush(): void;
721
+ /** @returns {object|null} the committed selection { i0, i1, stats } */
722
+ get brushSelection(): object | null;
723
+ /**
724
+ * Capture the current chart state as a story scene: view, series type,
725
+ * indicators, overlays, scenario and risk plan, plus a title/note.
726
+ * Build guided tours by capturing several and playing them back.
727
+ * const story = [
728
+ * chart.captureScene('Overview', 'The full picture'),
729
+ * { title: 'The breakout', range: { from, to }, indicators: 'sma:20' },
730
+ * ];
731
+ * chart.playStory(story);
732
+ * @param {string} [title]
733
+ * @param {string} [note]
734
+ * @returns {object} scene (plain data — snapshot of the moment)
735
+ */
736
+ captureScene(title?: string, note?: string): object;
737
+ /** @returns {object[]|null} a copy of the last played story */
738
+ getStory(): object[] | null;
739
+ /**
740
+ * Play a story: each scene applies its state (type / indicators /
741
+ * overlays / scenario / risk plan — set or clear), the camera eases
742
+ * to its range, then holds for its dwell. `wick:story` events narrate:
743
+ * { phase: 'scene' | 'end' | 'stop', index, total, scene, title, note }
744
+ * Any user interaction — pointer, wheel, keys, double-click — stops it.
745
+ * @param {object[]} story scenes (invalid entries dropped, max 20)
746
+ * @param {{dwell?: number, panMs?: number, loop?: boolean}} [opts]
747
+ * panMs clamps 100–5000 (default 900); loop replays forever
748
+ * @returns {boolean} true when playback started
749
+ */
750
+ playStory(story: object[], opts?: {
751
+ dwell?: number;
752
+ panMs?: number;
753
+ loop?: boolean;
754
+ }): boolean;
755
+ /**
756
+ * Stop story playback (if running). Fires a final `wick:story`
757
+ * { phase: 'stop' } unless called internally.
758
+ */
759
+ stopStory(silent: any): void;
760
+ /** Apply a scene's state (only the fields it carries). */
761
+ _applyScene(sc: any): void;
762
+ /** Map a scene's time range to bar indices (null when not applicable). */
763
+ _sceneTarget(sc: any): {
764
+ i0: number;
765
+ i1: number;
766
+ };
767
+ /** Ease the viewport to { i0, i1 } over `ms`; resolves early if the
768
+ * token changes (superseded or stopped). rAF when available. */
769
+ _storyTween(target: any, ms: any, token: any): Promise<any>;
770
+ /** Interrupt narrated playback (walk / story) on user input. */
771
+ _stopPlayback(): void;
590
772
  _emitRange(): void;
591
773
  }
592
774
  export default WickChart;