@hoox-sh/pynets 0.9.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.
package/dist/index.js CHANGED
@@ -33581,6 +33581,31 @@ function tokenize(source) {
33581
33581
  }
33582
33582
  return tokens;
33583
33583
  }
33584
+ // src/runtime/clone.ts
33585
+ function cloneValue(v) {
33586
+ if (v == null)
33587
+ return v;
33588
+ if (typeof v !== "object")
33589
+ return v;
33590
+ if (Array.isArray(v))
33591
+ return v.map(cloneValue);
33592
+ if (v instanceof Map) {
33593
+ const out = new Map;
33594
+ for (const [k, x] of v)
33595
+ out.set(k, cloneValue(x));
33596
+ return out;
33597
+ }
33598
+ if (v instanceof Set)
33599
+ return new Set(v);
33600
+ const clone = v.clone;
33601
+ if (typeof clone === "function")
33602
+ return clone.call(v);
33603
+ const out = {};
33604
+ for (const [k, x] of Object.entries(v))
33605
+ out[k] = cloneValue(x);
33606
+ return out;
33607
+ }
33608
+
33584
33609
  // src/runtime/udt.ts
33585
33610
  class UdtType {
33586
33611
  name;
@@ -33637,6 +33662,22 @@ class UdtInstance {
33637
33662
  getMethod(name) {
33638
33663
  return this.type.getMethod(name);
33639
33664
  }
33665
+ clone() {
33666
+ const out = new UdtInstance(this.type);
33667
+ out.restoreContents(this.snapshotContents());
33668
+ return out;
33669
+ }
33670
+ snapshotContents() {
33671
+ const out = [];
33672
+ for (const [k, v] of this.values)
33673
+ out.push([k, cloneValue(v)]);
33674
+ return out;
33675
+ }
33676
+ restoreContents(snap) {
33677
+ this.values.clear();
33678
+ for (const [k, v] of snap)
33679
+ this.values.set(k, cloneValue(v));
33680
+ }
33640
33681
  }
33641
33682
 
33642
33683
  class EnumMember {
@@ -33910,6 +33951,19 @@ class PineArray {
33910
33951
  out.push(v);
33911
33952
  return out;
33912
33953
  }
33954
+ clone() {
33955
+ const out = new PineArray;
33956
+ out.restoreContents(this.snapshotContents());
33957
+ return out;
33958
+ }
33959
+ snapshotContents() {
33960
+ return this.cells.map(cloneValue);
33961
+ }
33962
+ restoreContents(snap) {
33963
+ this.cells.length = 0;
33964
+ for (const c of snap)
33965
+ this.cells.push(cloneValue(c));
33966
+ }
33913
33967
  reverse() {
33914
33968
  this.cells.reverse();
33915
33969
  }
@@ -36928,6 +36982,12 @@ class PineSeries {
36928
36982
  else
36929
36983
  this.data[this.data.length - 1] = value;
36930
36984
  }
36985
+ truncate(len) {
36986
+ if (len < 0)
36987
+ return;
36988
+ if (this.data.length > len)
36989
+ this.data.length = len;
36990
+ }
36931
36991
  get current() {
36932
36992
  return this.data.length === 0 ? NA : this.data[this.data.length - 1];
36933
36993
  }
@@ -37555,6 +37615,30 @@ class StrategyBook {
37555
37615
  if (settings)
37556
37616
  this.applyDeclSettings(settings);
37557
37617
  }
37618
+ snapshot() {
37619
+ const out = {};
37620
+ for (const [key, value] of Object.entries(this)) {
37621
+ if (typeof value === "function")
37622
+ continue;
37623
+ out[key] = cloneValue(value);
37624
+ }
37625
+ return out;
37626
+ }
37627
+ restore(snap) {
37628
+ const self = this;
37629
+ for (const [key, value] of Object.entries(snap)) {
37630
+ const live = self[key];
37631
+ if (Array.isArray(live)) {
37632
+ live.length = 0;
37633
+ for (const item of value)
37634
+ live.push(cloneValue(item));
37635
+ } else if (live !== null && typeof live === "object" && value !== null && typeof value === "object") {
37636
+ Object.assign(live, value);
37637
+ } else {
37638
+ self[key] = cloneValue(value);
37639
+ }
37640
+ }
37641
+ }
37558
37642
  capitalHeld() {
37559
37643
  let notional = 0;
37560
37644
  for (const t of this.openTrades) {
@@ -38847,6 +38931,25 @@ class PineMap {
38847
38931
  out.put(decodeKey(k), this.data.get(k));
38848
38932
  return out;
38849
38933
  }
38934
+ clone() {
38935
+ const out = new PineMap;
38936
+ out.restoreContents(this.snapshotContents());
38937
+ return out;
38938
+ }
38939
+ snapshotContents() {
38940
+ const data = [];
38941
+ for (const [k, v] of this.data)
38942
+ data.push([k, cloneValue(v)]);
38943
+ return { data, order: [...this.order] };
38944
+ }
38945
+ restoreContents(snap) {
38946
+ this.data.clear();
38947
+ this.order.length = 0;
38948
+ for (const [k, v] of snap.data)
38949
+ this.data.set(k, cloneValue(v));
38950
+ for (const k of snap.order)
38951
+ this.order.push(k);
38952
+ }
38850
38953
  putAll(other) {
38851
38954
  if (!(other instanceof PineMap))
38852
38955
  return;
@@ -39436,6 +39539,25 @@ class PineMatrix {
39436
39539
  row[j] = value;
39437
39540
  }
39438
39541
  }
39542
+ clone() {
39543
+ const out = new PineMatrix(this.nRows, this.nCols);
39544
+ out.restoreContents(this.snapshotContents());
39545
+ return out;
39546
+ }
39547
+ snapshotContents() {
39548
+ return {
39549
+ cells: this.cells.map((row) => row.map(cloneValue)),
39550
+ nRows: this.nRows,
39551
+ nCols: this.nCols
39552
+ };
39553
+ }
39554
+ restoreContents(snap) {
39555
+ this.cells.length = 0;
39556
+ for (const row of snap.cells)
39557
+ this.cells.push(row.map(cloneValue));
39558
+ this.nRows = snap.nRows;
39559
+ this.nCols = snap.nCols;
39560
+ }
39439
39561
  transpose() {
39440
39562
  const t = new PineMatrix(this.nCols, this.nRows);
39441
39563
  for (let i = 0;i < this.nRows; i++) {
@@ -40538,6 +40660,29 @@ class TaEngine {
40538
40660
  historySites = new Map;
40539
40661
  momentumFilterSites = new Map;
40540
40662
  volatilityRegimeSites = new Map;
40663
+ snapshot() {
40664
+ const out = [];
40665
+ for (const [key, value] of Object.entries(this)) {
40666
+ if (!(value instanceof Map))
40667
+ continue;
40668
+ const copy = new Map;
40669
+ for (const [site, state] of value)
40670
+ copy.set(site, cloneValue(state));
40671
+ out.push([key, copy]);
40672
+ }
40673
+ return out;
40674
+ }
40675
+ restore(snap) {
40676
+ const self = this;
40677
+ for (const [key, copy] of snap) {
40678
+ const live = self[key];
40679
+ if (!(live instanceof Map))
40680
+ continue;
40681
+ live.clear();
40682
+ for (const [site, state] of copy)
40683
+ live.set(site, cloneValue(state));
40684
+ }
40685
+ }
40541
40686
  sma(site, source, period) {
40542
40687
  const n = pinePeriod(period);
40543
40688
  if (n === null)
@@ -45293,6 +45438,15 @@ function unwrapSpecialize(node) {
45293
45438
  return n.value;
45294
45439
  return node;
45295
45440
  }
45441
+ function isArrayReturningCall(func) {
45442
+ const { name } = resolveCallee(func);
45443
+ if (name === "ta_gamma_levels" || name === "gamma_levels")
45444
+ return true;
45445
+ if (name === "array_from" || name === "array_copy" || name === "array_concat" || name === "array_slice") {
45446
+ return true;
45447
+ }
45448
+ return name.startsWith("array_new");
45449
+ }
45296
45450
  function collectArgs(node, visit) {
45297
45451
  const pos = [];
45298
45452
  const kw = {};
@@ -47341,6 +47495,7 @@ var RESERVED2 = new Set([
47341
47495
  function newEmitCtx() {
47342
47496
  return {
47343
47497
  arrays: new Set,
47498
+ arrayNames: new Set,
47344
47499
  plots: [],
47345
47500
  varNames: new Set,
47346
47501
  functions: [],
@@ -48155,6 +48310,9 @@ function emitAttribute(state, node) {
48155
48310
  function emitSubscript(state, node) {
48156
48311
  const slice = node.slice != null ? emitExpr2(state, node.slice) : "0";
48157
48312
  const arr = histArray(state, node.value);
48313
+ if (node.value.kind === "Name" && state.ctx.arrayNames.has(node.value.id)) {
48314
+ return `__h.array.sub(${arr}[__bar_idx], ${slice})`;
48315
+ }
48158
48316
  return `__h.hist(${arr}, __bar_idx, ${slice})`;
48159
48317
  }
48160
48318
  function histArray(state, value) {
@@ -48253,11 +48411,22 @@ function emitImport2(state, node) {
48253
48411
  function emitAssign2(state, node) {
48254
48412
  const rhs = node.value != null ? emitExpr2(state, node.value) : "null";
48255
48413
  const isVar = isVarMode2(node.mode);
48414
+ trackArrayName(state, node.target, node.value);
48256
48415
  return emitStore(state, node.target, rhs, isVar);
48257
48416
  }
48258
48417
  function emitReAssign2(state, node) {
48418
+ trackArrayName(state, node.target, node.value);
48259
48419
  return emitStore(state, node.target, emitExpr2(state, node.value), false);
48260
48420
  }
48421
+ function trackArrayName(state, target, value) {
48422
+ if (target.kind !== "Name")
48423
+ return;
48424
+ if (value != null && value.kind === "Call" && isArrayReturningCall(value.func)) {
48425
+ state.ctx.arrayNames.add(target.id);
48426
+ } else {
48427
+ state.ctx.arrayNames.delete(target.id);
48428
+ }
48429
+ }
48261
48430
  function emitAugAssign2(state, node) {
48262
48431
  if (node.target.kind === "Attribute") {
48263
48432
  return emitStore(state, node.target, emitExpr2(state, node.value), false);
@@ -48903,6 +49072,15 @@ var compileArray = {
48903
49072
  get(id, index) {
48904
49073
  return asArray(id)?.get(n2(index, 0)) ?? null;
48905
49074
  },
49075
+ sub(id, offset) {
49076
+ const a = asArray(id);
49077
+ const off = cell(offset);
49078
+ if (a == null || off == null || off < 0)
49079
+ return null;
49080
+ const size = a.size();
49081
+ const i = size - 1 - Math.trunc(off);
49082
+ return i < 0 || i >= size ? null : a.get(i);
49083
+ },
48906
49084
  set(id, index, value) {
48907
49085
  asArray(id)?.set(n2(index, 0), store(value));
48908
49086
  return id ?? null;
@@ -50903,6 +51081,12 @@ class Runtime {
50903
51081
  const extraOpts = extra && !isPlainInputs(extra) ? extra : undefined;
50904
51082
  const mode = extraOpts?.mode ?? this.mode;
50905
51083
  const inputs = mergeInputs(this.inputs, extra);
51084
+ const rt = resolveRealtimeWindow(ohlcv.length, {
51085
+ realtimeLastBar: extraOpts?.realtimeLastBar,
51086
+ realtimeTicks: extraOpts?.realtimeTicks,
51087
+ realtimeBars: extraOpts?.realtimeBars,
51088
+ realtimeFromBar: extraOpts?.realtimeFromBar
51089
+ });
50906
51090
  const host = {
50907
51091
  symbol: this.symbol,
50908
51092
  inputs,
@@ -50910,7 +51094,8 @@ class Runtime {
50910
51094
  timeframe: extraOpts?.timeframe ?? this.timeframe,
50911
51095
  libraries: extraOpts?.libraries != null ? resolveLibraries(extraOpts.libraries) : this.libraries,
50912
51096
  timeout_seconds: extraOpts?.timeout_seconds ?? this.timeout_seconds,
50913
- realtime_last_bar: extraOpts?.realtimeLastBar ?? false
51097
+ realtime_first: rt.first,
51098
+ realtime_ticks: rt.ticks
50914
51099
  };
50915
51100
  if (mode === "compile") {
50916
51101
  return runCompiled(source, ohlcv, host);
@@ -51018,6 +51203,106 @@ function interpret(source, ohlcv) {
51018
51203
  throw new Error(out.error);
51019
51204
  return { plots: out.plots };
51020
51205
  }
51206
+ function isSnapshotContainer(v) {
51207
+ return v !== null && typeof v === "object" && typeof v.snapshotContents === "function";
51208
+ }
51209
+ function isRestoreContainer(v) {
51210
+ return v !== null && typeof v === "object" && typeof v.restoreContents === "function";
51211
+ }
51212
+ function snapshotTickState(env) {
51213
+ const containers = [];
51214
+ for (const [id, v] of Object.entries(env.ctx)) {
51215
+ if (isSnapshotContainer(v)) {
51216
+ containers.push([id, v.snapshotContents()]);
51217
+ }
51218
+ }
51219
+ const plotsLens = new Map;
51220
+ for (const [key, arr] of Object.entries(env.plots))
51221
+ plotsLens.set(key, arr.length);
51222
+ const udfSites = [];
51223
+ for (const [key, f] of env.udfSites) {
51224
+ const seriesLens = new Map;
51225
+ for (const [name, s] of f.series)
51226
+ seriesLens.set(name, s.length);
51227
+ udfSites.push([key, { ...f.ctx }, new Set(f.varInited), seriesLens]);
51228
+ }
51229
+ return {
51230
+ ctx: { ...env.ctx },
51231
+ containers,
51232
+ varInited: new Set(env.varInited),
51233
+ plotsLens,
51234
+ plotMetaLen: env.plot_meta.length,
51235
+ ta: env.ta.snapshot(),
51236
+ book: env.book.snapshot(),
51237
+ udfSites
51238
+ };
51239
+ }
51240
+ function restoreTickState(env, snap) {
51241
+ const varips = env.varipNames;
51242
+ for (const key of Object.keys(env.ctx)) {
51243
+ if (!(key in snap.ctx) && !varips.has(key))
51244
+ delete env.ctx[key];
51245
+ }
51246
+ for (const [key, value] of Object.entries(snap.ctx)) {
51247
+ if (varips.has(key))
51248
+ continue;
51249
+ env.ctx[key] = value;
51250
+ }
51251
+ for (const [id, contents] of snap.containers) {
51252
+ if (varips.has(id))
51253
+ continue;
51254
+ const cur = env.ctx[id];
51255
+ if (isRestoreContainer(cur)) {
51256
+ cur.restoreContents(contents);
51257
+ }
51258
+ }
51259
+ const restoredInited = new Set(snap.varInited);
51260
+ for (const id of varips) {
51261
+ if (env.varInited.has(id))
51262
+ restoredInited.add(id);
51263
+ }
51264
+ env.varInited = restoredInited;
51265
+ for (const key of Object.keys(env.plots)) {
51266
+ const len = snap.plotsLens.get(key);
51267
+ if (len === undefined)
51268
+ delete env.plots[key];
51269
+ else if (env.plots[key].length > len)
51270
+ env.plots[key].length = len;
51271
+ }
51272
+ env.plot_meta.length = snap.plotMetaLen;
51273
+ env.ta.restore(snap.ta);
51274
+ env.book.restore(snap.book);
51275
+ const siteKeys = new Set(snap.udfSites.map(([key]) => key));
51276
+ for (const key of [...env.udfSites.keys()]) {
51277
+ if (!siteKeys.has(key))
51278
+ env.udfSites.delete(key);
51279
+ }
51280
+ for (const [key, ctx, varInited, seriesLens] of snap.udfSites) {
51281
+ const f = env.udfSites.get(key);
51282
+ if (f == null)
51283
+ continue;
51284
+ for (const k of Object.keys(f.ctx)) {
51285
+ if (!(k in ctx) && !varips.has(k))
51286
+ delete f.ctx[k];
51287
+ }
51288
+ for (const [k, v] of Object.entries(ctx)) {
51289
+ if (varips.has(k))
51290
+ continue;
51291
+ f.ctx[k] = v;
51292
+ }
51293
+ for (const [name, len] of seriesLens) {
51294
+ const s = f.series.get(name);
51295
+ if (s != null && s.length > len)
51296
+ s.truncate(len);
51297
+ }
51298
+ const restored = new Set(varInited);
51299
+ for (const id of varips) {
51300
+ if (f.varInited.has(id))
51301
+ restored.add(id);
51302
+ }
51303
+ f.varInited = restored;
51304
+ }
51305
+ }
51021
51306
  function interpretTree(tree, ohlcv, host) {
51022
51307
  const scriptNode = tree.kind === "Script" ? tree : null;
51023
51308
  const body = scriptNode?.body ?? [];
@@ -51054,7 +51339,12 @@ function interpretTree(tree, ohlcv, host) {
51054
51339
  onceFired: new WeakSet,
51055
51340
  maxBarsBackDecls: [],
51056
51341
  alertFireBars: new Map,
51057
- realtimeLastBar: host?.realtime_last_bar ?? false
51342
+ realtimeFirst: host?.realtime_first ?? null,
51343
+ realtimeTicks: Math.max(1, host?.realtime_ticks ?? 1),
51344
+ barRealtime: false,
51345
+ tickIndex: 0,
51346
+ tickCount: 1,
51347
+ varipNames: new Set
51058
51348
  };
51059
51349
  const n = ohlcv.length;
51060
51350
  let runError;
@@ -51062,8 +51352,9 @@ function interpretTree(tree, ohlcv, host) {
51062
51352
  let barsDone = n;
51063
51353
  const timeoutSec = host?.timeout_seconds;
51064
51354
  const deadline = timeoutSec != null && Number.isFinite(timeoutSec) ? performance.now() + timeoutSec * 1000 : null;
51355
+ let aborted = false;
51065
51356
  try {
51066
- for (let i = 0;i < n; i++) {
51357
+ for (let i = 0;i < n && !aborted; i++) {
51067
51358
  if (deadline != null && (i & 31) === 0 && performance.now() > deadline) {
51068
51359
  timedOut = true;
51069
51360
  barsDone = i;
@@ -51078,33 +51369,44 @@ function interpretTree(tree, ohlcv, host) {
51078
51369
  time: num2(env.ctx.time) ?? undefined
51079
51370
  });
51080
51371
  env.book.markOpenTrades(num2(env.ctx.high) ?? num2(env.ctx.close) ?? 0, num2(env.ctx.low) ?? num2(env.ctx.close) ?? 0, num2(env.ctx.close) ?? 0);
51081
- try {
51082
- for (const stmt of body) {
51083
- const decl = captureDecl(stmt);
51084
- if (decl) {
51085
- if (script_name == null)
51086
- script_name = decl.name;
51087
- if (decl.kind === "strategy") {
51088
- script_type = "strategy";
51089
- applyStrategyDecl(stmt, env);
51090
- } else if (decl.kind === "library") {
51091
- script_type = "library";
51092
- applyDrawingDecl(stmt, env);
51093
- } else {
51094
- applyDrawingDecl(stmt, env);
51372
+ const barRt = env.realtimeFirst != null && i >= env.realtimeFirst;
51373
+ const ticks = barRt ? env.realtimeTicks : 1;
51374
+ env.barRealtime = barRt;
51375
+ env.tickCount = ticks;
51376
+ for (let tick = 0;tick < ticks; tick++) {
51377
+ env.tickIndex = tick;
51378
+ const snap = barRt && tick < ticks - 1 ? snapshotTickState(env) : null;
51379
+ try {
51380
+ for (const stmt of body) {
51381
+ const decl = captureDecl(stmt);
51382
+ if (decl) {
51383
+ if (script_name == null)
51384
+ script_name = decl.name;
51385
+ if (decl.kind === "strategy") {
51386
+ script_type = "strategy";
51387
+ applyStrategyDecl(stmt, env);
51388
+ } else if (decl.kind === "library") {
51389
+ script_type = "library";
51390
+ applyDrawingDecl(stmt, env);
51391
+ } else {
51392
+ applyDrawingDecl(stmt, env);
51393
+ }
51394
+ continue;
51095
51395
  }
51096
- continue;
51396
+ execStmt(stmt, env);
51097
51397
  }
51098
- execStmt(stmt, env);
51099
- }
51100
- if (i === 0 && script_type === "library" && script_name) {
51101
- finalizeLibrary(env, script_name);
51398
+ if (i === 0 && script_type === "library" && script_name) {
51399
+ finalizeLibrary(env, script_name);
51400
+ }
51401
+ } catch (err) {
51402
+ if (err === LOOP_BREAK || err === LOOP_CONTINUE)
51403
+ break;
51404
+ runError = { error: formatRunError(err), error_kind: "runtime" };
51405
+ aborted = true;
51406
+ break;
51102
51407
  }
51103
- } catch (err) {
51104
- if (err === LOOP_BREAK || err === LOOP_CONTINUE)
51105
- continue;
51106
- runError = { error: formatRunError(err), error_kind: "runtime" };
51107
- break;
51408
+ if (snap)
51409
+ restoreTickState(env, snap);
51108
51410
  }
51109
51411
  }
51110
51412
  } catch (err) {
@@ -51371,7 +51673,7 @@ function uniquifyPlotKey(env, node, title) {
51371
51673
  return key;
51372
51674
  }
51373
51675
  function isVarMode3(stmt) {
51374
- return stmt.mode?.kind === "Var";
51676
+ return stmt.mode?.kind === "Var" || stmt.mode?.kind === "VarIp";
51375
51677
  }
51376
51678
  function isTuple(v) {
51377
51679
  return typeof v === "object" && v !== null && v.__tuple === true;
@@ -51486,6 +51788,15 @@ function evalAssign(stmt, env) {
51486
51788
  if (stmt.target.kind !== "Name")
51487
51789
  return NA;
51488
51790
  const id = stmt.target.id;
51791
+ const isVarip = stmt.mode?.kind === "VarIp";
51792
+ if (isVarip)
51793
+ env.varipNames.add(id);
51794
+ if (isVarip && env.barRealtime) {
51795
+ const value = evalExpr(stmt.value, env);
51796
+ bindName(env, id, value);
51797
+ inited.add(id);
51798
+ return value;
51799
+ }
51489
51800
  if (isVarMode3(stmt) && (inited.has(id) || env.barIndex !== 0 && ctxGet(env, id) !== undefined)) {
51490
51801
  inited.add(id);
51491
51802
  const cur = ctxGet(env, id);
@@ -51754,6 +52065,12 @@ function evalExpr(node, env) {
51754
52065
  const s = seriesGet(env, node.value.id);
51755
52066
  if (s)
51756
52067
  return s.get(offset);
52068
+ const v = ctxGet(env, node.value.id);
52069
+ if (v instanceof PineArray) {
52070
+ const size = v.size();
52071
+ const i = size - 1 - offset;
52072
+ return i < 0 || i >= size ? NA : v.get(i);
52073
+ }
51757
52074
  }
51758
52075
  return NA;
51759
52076
  }
@@ -52151,21 +52468,21 @@ function evalAttribute(node, env) {
52151
52468
  }
52152
52469
  if (node.value.id === "barstate") {
52153
52470
  const last = env.barIndex === env.barCount - 1;
52154
- const realtime = env.realtimeLastBar && last;
52471
+ const rt = env.barRealtime;
52155
52472
  if (node.attr === "isfirst")
52156
52473
  return env.barIndex === 0 ? 1 : 0;
52157
52474
  if (node.attr === "islast")
52158
52475
  return last ? 1 : 0;
52159
52476
  if (node.attr === "ishistory")
52160
- return realtime ? 0 : 1;
52477
+ return rt ? 0 : 1;
52161
52478
  if (node.attr === "isconfirmed")
52162
- return 1;
52479
+ return barIsConfirmed(env) ? 1 : 0;
52163
52480
  if (node.attr === "isnew")
52164
- return 1;
52481
+ return rt && env.tickIndex > 0 ? 0 : 1;
52165
52482
  if (node.attr === "isrealtime")
52166
- return realtime ? 1 : 0;
52483
+ return rt ? 1 : 0;
52167
52484
  if (node.attr === "islastconfirmedhistory")
52168
- return realtime ? 0 : last ? 1 : 0;
52485
+ return rt ? 0 : last ? 1 : 0;
52169
52486
  }
52170
52487
  if (node.value.id === "session") {
52171
52488
  if (node.attr === "regular")
@@ -52933,8 +53250,35 @@ function formatRunError(err) {
52933
53250
  }
52934
53251
  return "runtime error";
52935
53252
  }
53253
+ function resolveRealtimeWindow(nBars, opts) {
53254
+ const intOr = (v, fallback) => {
53255
+ if (v == null || !Number.isFinite(v))
53256
+ return fallback;
53257
+ return Math.trunc(v);
53258
+ };
53259
+ let ticks = intOr(opts.realtimeTicks, 1);
53260
+ if (ticks < 1)
53261
+ ticks = 1;
53262
+ let rtBars = intOr(opts.realtimeBars, 0);
53263
+ if (rtBars < 0)
53264
+ rtBars = 0;
53265
+ if (opts.realtimeFromBar != null) {
53266
+ let first = intOr(opts.realtimeFromBar, 0);
53267
+ if (first < 0)
53268
+ first = 0;
53269
+ if (first >= nBars)
53270
+ return { first: null, ticks };
53271
+ return { first, ticks };
53272
+ }
53273
+ if (rtBars > 0)
53274
+ return { first: nBars > rtBars ? nBars - rtBars : 0, ticks };
53275
+ if (opts.realtimeLastBar || ticks > 1) {
53276
+ return { first: nBars > 0 ? nBars - 1 : null, ticks };
53277
+ }
53278
+ return { first: null, ticks };
53279
+ }
52936
53280
  function isPlainInputs(extra) {
52937
- return !("inputs" in extra) && !("broker" in extra) && !("timeframe" in extra) && !("libraries" in extra) && !("mode" in extra) && !("timeout_seconds" in extra) && !("realtimeLastBar" in extra);
53281
+ return !("inputs" in extra) && !("broker" in extra) && !("timeframe" in extra) && !("libraries" in extra) && !("mode" in extra) && !("timeout_seconds" in extra) && !("realtimeLastBar" in extra) && !("realtimeTicks" in extra) && !("realtimeBars" in extra) && !("realtimeFromBar" in extra);
52938
53282
  }
52939
53283
  function resolveLibraries(libs) {
52940
53284
  if (libs instanceof LibraryRegistry)
@@ -53106,8 +53450,8 @@ function normalizeAlertFreq(freq) {
53106
53450
  const s = String(freq).trim().toLowerCase().replace(/ /g, "_");
53107
53451
  return ALERT_FREQ_ALIASES[s] ?? (s ? s : FREQ_ONCE_PER_BAR);
53108
53452
  }
53109
- function barIsConfirmed(_env) {
53110
- return true;
53453
+ function barIsConfirmed(env) {
53454
+ return !env.barRealtime || env.tickIndex === env.tickCount - 1;
53111
53455
  }
53112
53456
  function shouldFireAlert(env, source, title, message, freq) {
53113
53457
  const freqN = normalizeAlertFreq(freq);
@@ -56816,6 +57160,7 @@ export {
56816
57160
  mapJsonBars,
56817
57161
  parse,
56818
57162
  parseTimestampString,
57163
+ resolveRealtimeWindow,
56819
57164
  runScript,
56820
57165
  runtimeError,
56821
57166
  tickerHeikinashi,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hoox-sh/pynets",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "type": "module",
5
5
  "license": "AGPL-3.0-or-later",
6
6
  "author": "jango_blockchained",
package/src/cli/rich.ts CHANGED
@@ -12,7 +12,7 @@ const MUTED = [203, 204, 189] as const;
12
12
  const FAIL = [231, 0, 11] as const;
13
13
  const WARN = [235, 169, 65] as const;
14
14
 
15
- export const VERSION = "0.9.0";
15
+ export const VERSION = "0.10.0";
16
16
 
17
17
  type Rgb = readonly [number, number, number];
18
18
 
package/src/index.ts CHANGED
@@ -9,7 +9,7 @@
9
9
  export { dump, parse, tokenize, unparse, PinescriptSyntaxError } from "./ast/helper.ts";
10
10
  export type { ParseMode } from "./ast/helper.ts";
11
11
  export type { AST, Script, Expression } from "./ast/nodes.ts";
12
- export { Runtime, RuntimeStream, interpret, interpretTree } from "./runtime/interpret.ts";
12
+ export { Runtime, RuntimeStream, interpret, interpretTree, resolveRealtimeWindow } from "./runtime/interpret.ts";
13
13
  export type {
14
14
  DrawingEvent,
15
15
  InputOverrides,
@@ -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
  /** Numeric/na cell. Arrays may also store UDT instances, strings, and bools. */
13
14
  export type Cell = number | null;
@@ -245,6 +246,24 @@ export class PineArray {
245
246
  return out;
246
247
  }
247
248
 
249
+ /** Deep clone (realtime tick snapshots). */
250
+ clone(): PineArray {
251
+ const out = new PineArray();
252
+ out.restoreContents(this.snapshotContents());
253
+ return out;
254
+ }
255
+
256
+ /** Deep-cloned slots for realtime tick rollback. */
257
+ snapshotContents(): unknown[] {
258
+ return this.cells.map(cloneValue);
259
+ }
260
+
261
+ /** Replace slots with clones of `snap` — same instance, aliases keep identity. */
262
+ restoreContents(snap: unknown[]): void {
263
+ this.cells.length = 0;
264
+ for (const c of snap) this.cells.push(cloneValue(c));
265
+ }
266
+
248
267
  reverse(): void {
249
268
  this.cells.reverse();
250
269
  }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Copyright (C) 2024-2026 jango_blockchained
3
+ * SPDX-License-Identifier: AGPL-3.0-or-later
4
+ *
5
+ * Deep-clone runtime values for realtime tick snapshots (Python
6
+ * `_snapshot_realtime_scope` / `_snapshot_ta_state` analog). Containers
7
+ * duck-type `clone()` so this module stays import-free.
8
+ */
9
+
10
+ /** Deep clone: primitives, arrays, Map/Set, containers (via `clone()`), plain objects. */
11
+ export function cloneValue(v: unknown): unknown {
12
+ if (v == null) return v;
13
+ if (typeof v !== "object") return v;
14
+ if (Array.isArray(v)) return v.map(cloneValue);
15
+ if (v instanceof Map) {
16
+ const out = new Map<unknown, unknown>();
17
+ for (const [k, x] of v) out.set(k, cloneValue(x));
18
+ return out;
19
+ }
20
+ if (v instanceof Set) return new Set(v);
21
+ const clone = (v as { clone?: () => unknown }).clone;
22
+ if (typeof clone === "function") return clone.call(v);
23
+ const out: Record<string, unknown> = {};
24
+ for (const [k, x] of Object.entries(v)) out[k] = cloneValue(x);
25
+ return out;
26
+ }