@hoox-sh/pynets 0.8.0 → 0.10.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.
@@ -8,6 +8,7 @@
8
8
  */
9
9
 
10
10
  import { UdtInstance } from "./udt.ts";
11
+ import { cloneValue } from "./clone.ts";
11
12
 
12
13
  export type Cell = number | null;
13
14
 
@@ -564,6 +565,30 @@ export class PineMatrix {
564
565
  }
565
566
  }
566
567
 
568
+ /** Deep clone (realtime tick snapshots). */
569
+ clone(): PineMatrix {
570
+ const out = new PineMatrix(this.nRows, this.nCols);
571
+ out.restoreContents(this.snapshotContents());
572
+ return out;
573
+ }
574
+
575
+ /** Deep-cloned grid + dims for realtime tick rollback. */
576
+ snapshotContents(): { cells: unknown[][]; nRows: number; nCols: number } {
577
+ return {
578
+ cells: this.cells.map((row) => row.map(cloneValue)),
579
+ nRows: this.nRows,
580
+ nCols: this.nCols,
581
+ };
582
+ }
583
+
584
+ /** Replace grid + dims with clones of `snap` — same instance for aliases. */
585
+ restoreContents(snap: { cells: unknown[][]; nRows: number; nCols: number }): void {
586
+ this.cells.length = 0;
587
+ for (const row of snap.cells) this.cells.push(row.map(cloneValue));
588
+ this.nRows = snap.nRows;
589
+ this.nCols = snap.nCols;
590
+ }
591
+
567
592
  transpose(): PineMatrix {
568
593
  const t = new PineMatrix(this.nCols, this.nRows);
569
594
  for (let i = 0; i < this.nRows; i++) {
@@ -21,6 +21,12 @@ export class PineSeries {
21
21
  else this.data[this.data.length - 1] = value;
22
22
  }
23
23
 
24
+ /** Drop cells beyond `len` (realtime tick rollback). No-op when shorter. */
25
+ truncate(len: number): void {
26
+ if (len < 0) return;
27
+ if (this.data.length > len) this.data.length = len;
28
+ }
29
+
24
30
  get current(): Cell {
25
31
  return this.data.length === 0 ? NA : this.data[this.data.length - 1]!;
26
32
  }
@@ -11,6 +11,8 @@
11
11
  * fillEntry from flat / reverse leftover opens a trade; flatten moves it to closed.
12
12
  */
13
13
 
14
+ import { cloneValue } from "./clone.ts";
15
+
14
16
  export type StrategyDirection = "long" | "short";
15
17
  /** `strategy(..., avg_price_model=)` — stock FIFO reweight / sticky futures AEP. */
16
18
  export type AvgPriceModel = "stock" | "futures" | "inverse";
@@ -441,6 +443,36 @@ export class StrategyBook {
441
443
  if (settings) this.applyDeclSettings(settings);
442
444
  }
443
445
 
446
+ /**
447
+ * Snapshot all broker state (Python `strategy_state.snapshot()` analog)
448
+ * for realtime tick rollback: events, position, fills, pending orders,
449
+ * trades, and every counter.
450
+ */
451
+ snapshot(): Record<string, unknown> {
452
+ const out: Record<string, unknown> = {};
453
+ for (const [key, value] of Object.entries(this as unknown as Record<string, unknown>)) {
454
+ if (typeof value === "function") continue;
455
+ out[key] = cloneValue(value);
456
+ }
457
+ return out;
458
+ }
459
+
460
+ /** Restore from `snapshot()` — arrays refill in place so aliases keep identity. */
461
+ restore(snap: Record<string, unknown>): void {
462
+ const self = this as unknown as Record<string, unknown>;
463
+ for (const [key, value] of Object.entries(snap)) {
464
+ const live = self[key];
465
+ if (Array.isArray(live)) {
466
+ live.length = 0;
467
+ for (const item of value as unknown[]) live.push(cloneValue(item));
468
+ } else if (live !== null && typeof live === "object" && value !== null && typeof value === "object") {
469
+ Object.assign(live, value);
470
+ } else {
471
+ self[key] = cloneValue(value);
472
+ }
473
+ }
474
+ }
475
+
444
476
  /** Margin locked in open trades: sum(|entry * size|) / leverage (Python `capital_held`). */
445
477
  capitalHeld(): number {
446
478
  let notional = 0;
package/src/runtime/ta.ts CHANGED
@@ -9,6 +9,7 @@
9
9
  export type Cell = number | null;
10
10
 
11
11
  import { PineArray } from "./array.ts";
12
+ import { cloneValue } from "./clone.ts";
12
13
 
13
14
  interface SmaState {
14
15
  period: number;
@@ -602,6 +603,33 @@ export class TaEngine {
602
603
  private readonly momentumFilterSites = new Map<string, { momentum: Cell[]; volume: Cell[] }>();
603
604
  private readonly volatilityRegimeSites = new Map<string, Cell[]>();
604
605
 
606
+ /**
607
+ * Snapshot every per-site window (Python `_snapshot_ta_state`).
608
+ * Intermediate realtime ticks push one sample per call site; without
609
+ * rollback the same bar would be counted N times.
610
+ */
611
+ snapshot(): Array<[string, Map<string, unknown>]> {
612
+ const out: Array<[string, Map<string, unknown>]> = [];
613
+ for (const [key, value] of Object.entries(this as unknown as Record<string, unknown>)) {
614
+ if (!(value instanceof Map)) continue;
615
+ const copy = new Map<string, unknown>();
616
+ for (const [site, state] of value) copy.set(site, cloneValue(state));
617
+ out.push([key, copy]);
618
+ }
619
+ return out;
620
+ }
621
+
622
+ /** Copy-on-restore (Python `_restore_ta_state`); never throws. */
623
+ restore(snap: Array<[string, Map<string, unknown>]>): void {
624
+ const self = this as unknown as Record<string, unknown>;
625
+ for (const [key, copy] of snap) {
626
+ const live = self[key];
627
+ if (!(live instanceof Map)) continue;
628
+ live.clear();
629
+ for (const [site, state] of copy) live.set(site, cloneValue(state));
630
+ }
631
+ }
632
+
605
633
  sma(site: string, source: Cell, period: number): Cell {
606
634
  const n = pinePeriod(period);
607
635
  if (n === null) return null;
@@ -7,6 +7,8 @@
7
7
  * remain the source of truth — interpret registers TypeDef / EnumDef later.
8
8
  */
9
9
 
10
+ import { cloneValue } from "./clone.ts";
11
+
10
12
  export type UdtField = { name: string; default: unknown };
11
13
 
12
14
  /** Stored method; `body` is opaque (interpret owns FunctionDef). */
@@ -83,6 +85,26 @@ export class UdtInstance {
83
85
  getMethod(name: string): UdtMethod | undefined {
84
86
  return this.type.getMethod(name);
85
87
  }
88
+
89
+ /** Deep clone (realtime tick snapshots). */
90
+ clone(): UdtInstance {
91
+ const out = new UdtInstance(this.type);
92
+ out.restoreContents(this.snapshotContents());
93
+ return out;
94
+ }
95
+
96
+ /** Deep-cloned field values for realtime tick rollback. */
97
+ snapshotContents(): Array<[string, unknown]> {
98
+ const out: Array<[string, unknown]> = [];
99
+ for (const [k, v] of this.values) out.push([k, cloneValue(v)]);
100
+ return out;
101
+ }
102
+
103
+ /** Replace fields with clones of `snap` — same instance for aliases. */
104
+ restoreContents(snap: Array<[string, unknown]>): void {
105
+ this.values.clear();
106
+ for (const [k, v] of snap) this.values.set(k, cloneValue(v));
107
+ }
86
108
  }
87
109
 
88
110
  export class EnumMember {