@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/CHANGELOG.md +15 -0
- package/README.md +9 -1
- package/dist/browser.js +63 -63
- package/dist/index.js +382 -37
- package/package.json +1 -1
- package/src/cli/rich.ts +1 -1
- package/src/index.ts +1 -1
- package/src/runtime/array.ts +19 -0
- package/src/runtime/clone.ts +26 -0
- package/src/runtime/compile/collections.ts +13 -0
- package/src/runtime/compile/emit.ts +19 -1
- package/src/runtime/compile/emit_call.ts +15 -0
- package/src/runtime/compile/types.ts +2 -0
- package/src/runtime/interpret.ts +259 -40
- package/src/runtime/map.ts +25 -0
- package/src/runtime/matrix.ts +25 -0
- package/src/runtime/series.ts +6 -0
- package/src/runtime/strategy.ts +32 -0
- package/src/runtime/ta.ts +28 -0
- package/src/runtime/udt.ts +22 -0
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
51082
|
-
|
|
51083
|
-
|
|
51084
|
-
|
|
51085
|
-
|
|
51086
|
-
|
|
51087
|
-
|
|
51088
|
-
|
|
51089
|
-
|
|
51090
|
-
|
|
51091
|
-
|
|
51092
|
-
|
|
51093
|
-
|
|
51094
|
-
|
|
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
|
-
|
|
51396
|
+
execStmt(stmt, env);
|
|
51097
51397
|
}
|
|
51098
|
-
|
|
51099
|
-
|
|
51100
|
-
|
|
51101
|
-
|
|
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
|
-
|
|
51104
|
-
|
|
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
|
|
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
|
|
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
|
|
52483
|
+
return rt ? 1 : 0;
|
|
52167
52484
|
if (node.attr === "islastconfirmedhistory")
|
|
52168
|
-
return
|
|
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(
|
|
53110
|
-
return
|
|
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
package/src/cli/rich.ts
CHANGED
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,
|
package/src/runtime/array.ts
CHANGED
|
@@ -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
|
+
}
|