@hoox-sh/pynets 0.8.0 → 0.9.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 +17 -0
- package/README.md +9 -1
- package/dist/browser.js +30 -30
- package/dist/index.js +33 -9
- package/package.json +1 -1
- package/src/cli/rich.ts +1 -1
- package/src/runtime/interpret.ts +46 -8
package/dist/index.js
CHANGED
|
@@ -50909,7 +50909,8 @@ class Runtime {
|
|
|
50909
50909
|
broker: extraOpts?.broker ?? this.broker,
|
|
50910
50910
|
timeframe: extraOpts?.timeframe ?? this.timeframe,
|
|
50911
50911
|
libraries: extraOpts?.libraries != null ? resolveLibraries(extraOpts.libraries) : this.libraries,
|
|
50912
|
-
timeout_seconds: extraOpts?.timeout_seconds ?? this.timeout_seconds
|
|
50912
|
+
timeout_seconds: extraOpts?.timeout_seconds ?? this.timeout_seconds,
|
|
50913
|
+
realtime_last_bar: extraOpts?.realtimeLastBar ?? false
|
|
50913
50914
|
};
|
|
50914
50915
|
if (mode === "compile") {
|
|
50915
50916
|
return runCompiled(source, ohlcv, host);
|
|
@@ -50972,7 +50973,7 @@ class RuntimeStream {
|
|
|
50972
50973
|
return;
|
|
50973
50974
|
this.bars.push(bar);
|
|
50974
50975
|
try {
|
|
50975
|
-
const out = this.runtime.run(this.source, this.bars);
|
|
50976
|
+
const out = this.runtime.run(this.source, this.bars, { realtimeLastBar: true });
|
|
50976
50977
|
for (const h of this.barHandlers)
|
|
50977
50978
|
h(out);
|
|
50978
50979
|
return out;
|
|
@@ -50983,9 +50984,29 @@ class RuntimeStream {
|
|
|
50983
50984
|
return;
|
|
50984
50985
|
}
|
|
50985
50986
|
}
|
|
50987
|
+
get barCount() {
|
|
50988
|
+
return this.bars.length;
|
|
50989
|
+
}
|
|
50990
|
+
updateLastBar(bar) {
|
|
50991
|
+
if (this.closed)
|
|
50992
|
+
throw new Error("stream is closed");
|
|
50993
|
+
if (this.bars.length === 0)
|
|
50994
|
+
throw new Error("updateLastBar requires at least one bar");
|
|
50995
|
+
this.bars[this.bars.length - 1] = bar;
|
|
50996
|
+
const out = this.runtime.run(this.source, this.bars, { realtimeLastBar: true });
|
|
50997
|
+
for (const h of this.barHandlers)
|
|
50998
|
+
h(out);
|
|
50999
|
+
return out;
|
|
51000
|
+
}
|
|
51001
|
+
appendBars(bars) {
|
|
51002
|
+
let out;
|
|
51003
|
+
for (const bar of bars)
|
|
51004
|
+
out = this.push(bar);
|
|
51005
|
+
return out;
|
|
51006
|
+
}
|
|
50986
51007
|
close() {
|
|
50987
51008
|
this.closed = true;
|
|
50988
|
-
const out = this.runtime.run(this.source, this.bars);
|
|
51009
|
+
const out = this.runtime.run(this.source, this.bars, { realtimeLastBar: false });
|
|
50989
51010
|
for (const h of this.endHandlers)
|
|
50990
51011
|
h(out);
|
|
50991
51012
|
return out;
|
|
@@ -51032,7 +51053,8 @@ function interpretTree(tree, ohlcv, host) {
|
|
|
51032
51053
|
pendingExports: new Map,
|
|
51033
51054
|
onceFired: new WeakSet,
|
|
51034
51055
|
maxBarsBackDecls: [],
|
|
51035
|
-
alertFireBars: new Map
|
|
51056
|
+
alertFireBars: new Map,
|
|
51057
|
+
realtimeLastBar: host?.realtime_last_bar ?? false
|
|
51036
51058
|
};
|
|
51037
51059
|
const n = ohlcv.length;
|
|
51038
51060
|
let runError;
|
|
@@ -51781,7 +51803,8 @@ function evalExpr(node, env) {
|
|
|
51781
51803
|
return NA;
|
|
51782
51804
|
for (const s of onceNode.body)
|
|
51783
51805
|
execStmt(s, env);
|
|
51784
|
-
env
|
|
51806
|
+
if (barIsConfirmed(env))
|
|
51807
|
+
env.onceFired.add(onceNode);
|
|
51785
51808
|
return NA;
|
|
51786
51809
|
}
|
|
51787
51810
|
case "BoolOp":
|
|
@@ -52128,20 +52151,21 @@ function evalAttribute(node, env) {
|
|
|
52128
52151
|
}
|
|
52129
52152
|
if (node.value.id === "barstate") {
|
|
52130
52153
|
const last = env.barIndex === env.barCount - 1;
|
|
52154
|
+
const realtime = env.realtimeLastBar && last;
|
|
52131
52155
|
if (node.attr === "isfirst")
|
|
52132
52156
|
return env.barIndex === 0 ? 1 : 0;
|
|
52133
52157
|
if (node.attr === "islast")
|
|
52134
52158
|
return last ? 1 : 0;
|
|
52135
52159
|
if (node.attr === "ishistory")
|
|
52136
|
-
return
|
|
52160
|
+
return realtime ? 0 : 1;
|
|
52137
52161
|
if (node.attr === "isconfirmed")
|
|
52138
52162
|
return 1;
|
|
52139
52163
|
if (node.attr === "isnew")
|
|
52140
52164
|
return 1;
|
|
52141
52165
|
if (node.attr === "isrealtime")
|
|
52142
|
-
return 0;
|
|
52166
|
+
return realtime ? 1 : 0;
|
|
52143
52167
|
if (node.attr === "islastconfirmedhistory")
|
|
52144
|
-
return last ? 1 : 0;
|
|
52168
|
+
return realtime ? 0 : last ? 1 : 0;
|
|
52145
52169
|
}
|
|
52146
52170
|
if (node.value.id === "session") {
|
|
52147
52171
|
if (node.attr === "regular")
|
|
@@ -52910,7 +52934,7 @@ function formatRunError(err) {
|
|
|
52910
52934
|
return "runtime error";
|
|
52911
52935
|
}
|
|
52912
52936
|
function isPlainInputs(extra) {
|
|
52913
|
-
return !("inputs" in extra) && !("broker" in extra) && !("timeframe" in extra) && !("libraries" in extra) && !("mode" in extra) && !("timeout_seconds" in 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);
|
|
52914
52938
|
}
|
|
52915
52939
|
function resolveLibraries(libs) {
|
|
52916
52940
|
if (libs instanceof LibraryRegistry)
|
package/package.json
CHANGED
package/src/cli/rich.ts
CHANGED
package/src/runtime/interpret.ts
CHANGED
|
@@ -218,6 +218,8 @@ export interface RuntimeOptions {
|
|
|
218
218
|
mode?: RuntimeMode;
|
|
219
219
|
/** Wall-clock budget in seconds. Interpret only; checked every 32 bars. */
|
|
220
220
|
timeout_seconds?: number | null;
|
|
221
|
+
/** Visit the last bar as a forming realtime bar (`barstate.isrealtime`). */
|
|
222
|
+
realtimeLastBar?: boolean;
|
|
221
223
|
}
|
|
222
224
|
|
|
223
225
|
export interface RuntimeFill {
|
|
@@ -362,6 +364,8 @@ interface Env {
|
|
|
362
364
|
maxBarsBackDecls: Array<{ var: Value; num: Value }>;
|
|
363
365
|
/** Python `_alert_fire_bars` keys for `alert` / `alertcondition` freq gating. */
|
|
364
366
|
alertFireBars: Map<string, number>;
|
|
367
|
+
/** Visit the last bar as a forming realtime bar (`barstate.isrealtime`). */
|
|
368
|
+
realtimeLastBar: boolean;
|
|
365
369
|
}
|
|
366
370
|
|
|
367
371
|
export class Runtime {
|
|
@@ -420,6 +424,7 @@ export class Runtime {
|
|
|
420
424
|
timeframe: extraOpts?.timeframe ?? this.timeframe,
|
|
421
425
|
libraries: extraOpts?.libraries != null ? resolveLibraries(extraOpts.libraries) : this.libraries,
|
|
422
426
|
timeout_seconds: extraOpts?.timeout_seconds ?? this.timeout_seconds,
|
|
427
|
+
realtime_last_bar: extraOpts?.realtimeLastBar ?? false,
|
|
423
428
|
};
|
|
424
429
|
if (mode === "compile") {
|
|
425
430
|
return runCompiled(source, ohlcv, host);
|
|
@@ -494,7 +499,7 @@ export class RuntimeStream {
|
|
|
494
499
|
if (this.closed) return undefined;
|
|
495
500
|
this.bars.push(bar);
|
|
496
501
|
try {
|
|
497
|
-
const out = this.runtime.run(this.source, this.bars);
|
|
502
|
+
const out = this.runtime.run(this.source, this.bars, { realtimeLastBar: true });
|
|
498
503
|
for (const h of this.barHandlers) h(out);
|
|
499
504
|
return out;
|
|
500
505
|
} catch (err) {
|
|
@@ -504,9 +509,35 @@ export class RuntimeStream {
|
|
|
504
509
|
}
|
|
505
510
|
}
|
|
506
511
|
|
|
512
|
+
/** Number of committed bars in the stream. */
|
|
513
|
+
get barCount(): number {
|
|
514
|
+
return this.bars.length;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/**
|
|
518
|
+
* Re-tick the forming last bar with revised OHLCV. The bar count does not
|
|
519
|
+
* advance; the script is re-evaluated from bar 0, so `once`, TA state, and
|
|
520
|
+
* broker fills naturally roll back to the revised history.
|
|
521
|
+
*/
|
|
522
|
+
updateLastBar(bar: OHLCVBar): RuntimeResult {
|
|
523
|
+
if (this.closed) throw new Error("stream is closed");
|
|
524
|
+
if (this.bars.length === 0) throw new Error("updateLastBar requires at least one bar");
|
|
525
|
+
this.bars[this.bars.length - 1] = bar;
|
|
526
|
+
const out = this.runtime.run(this.source, this.bars, { realtimeLastBar: true });
|
|
527
|
+
for (const h of this.barHandlers) h(out);
|
|
528
|
+
return out;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
/** Append several bars, returning the final result. */
|
|
532
|
+
appendBars(bars: OHLCVBar[]): RuntimeResult | undefined {
|
|
533
|
+
let out: RuntimeResult | undefined;
|
|
534
|
+
for (const bar of bars) out = this.push(bar);
|
|
535
|
+
return out;
|
|
536
|
+
}
|
|
537
|
+
|
|
507
538
|
close(): RuntimeResult {
|
|
508
539
|
this.closed = true;
|
|
509
|
-
const out = this.runtime.run(this.source, this.bars);
|
|
540
|
+
const out = this.runtime.run(this.source, this.bars, { realtimeLastBar: false });
|
|
510
541
|
for (const h of this.endHandlers) h(out);
|
|
511
542
|
return out;
|
|
512
543
|
}
|
|
@@ -528,6 +559,7 @@ export function interpretTree(
|
|
|
528
559
|
timeframe?: string | null;
|
|
529
560
|
libraries?: LibraryRegistry;
|
|
530
561
|
timeout_seconds?: number | null;
|
|
562
|
+
realtime_last_bar?: boolean;
|
|
531
563
|
},
|
|
532
564
|
): RuntimeResult {
|
|
533
565
|
const scriptNode = tree.kind === "Script" ? (tree as Script) : null;
|
|
@@ -566,6 +598,7 @@ export function interpretTree(
|
|
|
566
598
|
onceFired: new WeakSet(),
|
|
567
599
|
maxBarsBackDecls: [],
|
|
568
600
|
alertFireBars: new Map(),
|
|
601
|
+
realtimeLastBar: host?.realtime_last_bar ?? false,
|
|
569
602
|
};
|
|
570
603
|
|
|
571
604
|
const n = ohlcv.length;
|
|
@@ -1319,7 +1352,9 @@ function evalExpr(node: expr | undefined, env: Env): Value {
|
|
|
1319
1352
|
const t = onceNode.test == null ? 1 : unwrap(evalExpr(onceNode.test, env));
|
|
1320
1353
|
if (!isTruthy(t)) return NA;
|
|
1321
1354
|
for (const s of onceNode.body) execStmt(s, env);
|
|
1322
|
-
|
|
1355
|
+
// Python commits the fired flag only on a confirmed bar; unconfirmed
|
|
1356
|
+
// realtime ticks may re-run the body (rollback semantics).
|
|
1357
|
+
if (barIsConfirmed(env)) env.onceFired.add(onceNode);
|
|
1323
1358
|
return NA;
|
|
1324
1359
|
}
|
|
1325
1360
|
case "BoolOp":
|
|
@@ -1652,13 +1687,14 @@ function evalAttribute(node: Attribute, env: Env): Value {
|
|
|
1652
1687
|
}
|
|
1653
1688
|
if (node.value.id === "barstate") {
|
|
1654
1689
|
const last = env.barIndex === env.barCount - 1;
|
|
1690
|
+
const realtime = env.realtimeLastBar && last;
|
|
1655
1691
|
if (node.attr === "isfirst") return env.barIndex === 0 ? 1 : 0;
|
|
1656
1692
|
if (node.attr === "islast") return last ? 1 : 0;
|
|
1657
|
-
if (node.attr === "ishistory") return
|
|
1693
|
+
if (node.attr === "ishistory") return realtime ? 0 : 1;
|
|
1658
1694
|
if (node.attr === "isconfirmed") return 1;
|
|
1659
1695
|
if (node.attr === "isnew") return 1;
|
|
1660
|
-
if (node.attr === "isrealtime") return 0;
|
|
1661
|
-
if (node.attr === "islastconfirmedhistory") return last ? 1 : 0;
|
|
1696
|
+
if (node.attr === "isrealtime") return realtime ? 1 : 0;
|
|
1697
|
+
if (node.attr === "islastconfirmedhistory") return realtime ? 0 : last ? 1 : 0;
|
|
1662
1698
|
}
|
|
1663
1699
|
if (node.value.id === "session") {
|
|
1664
1700
|
if (node.attr === "regular") return "regular";
|
|
@@ -2470,7 +2506,8 @@ function isPlainInputs(extra: RuntimeOptions | InputOverrides): extra is InputOv
|
|
|
2470
2506
|
!("timeframe" in extra) &&
|
|
2471
2507
|
!("libraries" in extra) &&
|
|
2472
2508
|
!("mode" in extra) &&
|
|
2473
|
-
!("timeout_seconds" in extra)
|
|
2509
|
+
!("timeout_seconds" in extra) &&
|
|
2510
|
+
!("realtimeLastBar" in extra)
|
|
2474
2511
|
);
|
|
2475
2512
|
}
|
|
2476
2513
|
|
|
@@ -2623,6 +2660,7 @@ function runAuto(
|
|
|
2623
2660
|
timeframe?: string | null;
|
|
2624
2661
|
libraries?: LibraryRegistry;
|
|
2625
2662
|
timeout_seconds?: number | null;
|
|
2663
|
+
realtime_last_bar?: boolean;
|
|
2626
2664
|
},
|
|
2627
2665
|
): RuntimeResult {
|
|
2628
2666
|
const elig = compileEligible(source);
|
|
@@ -2700,7 +2738,7 @@ function normalizeAlertFreq(freq: unknown): string {
|
|
|
2700
2738
|
return ALERT_FREQ_ALIASES[s] ?? (s ? s : FREQ_ONCE_PER_BAR);
|
|
2701
2739
|
}
|
|
2702
2740
|
|
|
2703
|
-
/** Python `_bar_is_confirmed
|
|
2741
|
+
/** Python `_bar_is_confirmed`: single-tick runs are confirmed (ticks>1 not modeled). */
|
|
2704
2742
|
function barIsConfirmed(_env: Env): boolean {
|
|
2705
2743
|
return true;
|
|
2706
2744
|
}
|