@hoox-sh/pynets 0.6.0 → 0.7.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hoox-sh/pynets",
3
- "version": "0.6.0",
3
+ "version": "0.7.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.6.0";
15
+ export const VERSION = "0.7.0";
16
16
 
17
17
  type Rgb = readonly [number, number, number];
18
18
 
@@ -193,6 +193,24 @@ const TA_METHODS = new Set([
193
193
  "regime_adaptive_signal",
194
194
  "strategy_score",
195
195
  "breakout_detection",
196
+ "ema_cross_signal",
197
+ "momentum_filter",
198
+ "volatility_regime",
199
+ "economic_impact_score",
200
+ "contrarian_signal",
201
+ "drawdown_recovery_level",
202
+ "pivot_point_levels",
203
+ "volatility_regime_score",
204
+ "correlation_filter",
205
+ "advanced_breakout_detector",
206
+ "pullback_bounce_level",
207
+ "divergence_detector",
208
+ "momentum_divergence",
209
+ "spread_analysis",
210
+ "employment_cycle_indicator",
211
+ "gdp_growth_proxy",
212
+ "inflation_proxy_indicator",
213
+ "expected_value",
196
214
  ]);
197
215
 
198
216
  /**
@@ -405,6 +423,13 @@ export function emitCall(ctx: EmitCtx, node: Call, visit: VisitFn): string {
405
423
  ctx.needsHeikinashi = true;
406
424
  return `"__HEIKINASHI__"`;
407
425
  }
426
+ if (name === "ticker_inherit") {
427
+ const fallback = pick(args, 0, ["ticker", "tickerid", "from", "source"], `"SYMBOL"`);
428
+ return pick(args, 1, ["symbol", "ticker"], fallback);
429
+ }
430
+ if (name === "renko" || name === "kagi" || name === "linebreak" || name === "pointfigure") {
431
+ return pick(args, 0, ["ticker", "tickerid", "symbol"], `"SYMBOL"`);
432
+ }
408
433
  // ticker.new / standard / modify / renko / kagi / linebreak / pointfigure → first arg or "SYMBOL"
409
434
  if (name.startsWith("ticker_")) {
410
435
  return pick(args, 0, ["symbol", "ticker", "tickerid"], `"SYMBOL"`);
@@ -617,6 +642,11 @@ function cellOf(expr: string): string {
617
642
  return `((__c) => (__c == null ? null : typeof __c === "boolean" ? (__c ? 1 : 0) : (typeof __c === "number" && Number.isFinite(__c) ? __c : null)))(${expr})`;
618
643
  }
619
644
 
645
+ /** Cell or array/object (history-based TA args keep arrays intact). */
646
+ function arrOrCell(expr: string): string {
647
+ return `((__v) => (__v != null && typeof __v === "object" ? __v : (__v == null ? null : typeof __v === "boolean" ? (__v ? 1 : 0) : (typeof __v === "number" && Number.isFinite(__v) ? __v : null))))(${expr})`;
648
+ }
649
+
620
650
  /**
621
651
  * Interpret `wave10bList`: a Pine array becomes its cells; a scalar is a
622
652
  * one-element list. Bar-mode series stay one element.
@@ -2394,8 +2424,11 @@ function emitTa(ctx: EmitCtx, method: string, args: CallArgs): string {
2394
2424
  ]);
2395
2425
  }
2396
2426
  if (method === "market_structure_pivot") {
2397
- return taPure("marketStructurePivot", [
2398
- cellOf(pick(args, 2, ["close"], "null")),
2427
+ return taCall("marketStructurePivot", site, [
2428
+ arrOrCell(pick(args, 0, ["high_list", "high"], "null")),
2429
+ arrOrCell(pick(args, 1, ["low_list", "low"], "null")),
2430
+ cellOf(pick(args, 3, ["period", "left", "left_bars"], "null")),
2431
+ cellOf(pick(args, 4, ["mode", "right", "right_bars"], "null")),
2399
2432
  ]);
2400
2433
  }
2401
2434
  if (method === "position_sizing_score") {
@@ -2416,13 +2449,13 @@ function emitTa(ctx: EmitCtx, method: string, args: CallArgs): string {
2416
2449
  ]);
2417
2450
  }
2418
2451
  if (method === "optimal_entry_zone") {
2419
- return taPure("optimalEntryZone", [cellOf(pick(args, 2, ["close"], "null"))]);
2452
+ return taCall("optimalEntryZone", site, [arrOrCell(pick(args, 2, ["close"], "null"))]);
2420
2453
  }
2421
2454
  if (method === "mean_reversion_entry") {
2422
- return taPure("meanReversionEntry", [
2423
- cellOf(pick(args, 0, ["price", "source"], "null")),
2424
- cellOf(pick(args, 1, ["sma"], "null")),
2425
- cellOf(pick(args, 2, ["stdev", "std_dev"], "null")),
2455
+ return taCall("meanReversionEntry", site, [
2456
+ arrOrCell(pick(args, 0, ["price", "source"], "null")),
2457
+ arrOrCell(pick(args, 1, ["sma"], "null")),
2458
+ arrOrCell(pick(args, 2, ["stdev", "std_dev"], "null")),
2426
2459
  cellOf(pick(args, 4, ["threshold"], "null")),
2427
2460
  ]);
2428
2461
  }
@@ -2466,5 +2499,133 @@ function emitTa(ctx: EmitCtx, method: string, args: CallArgs): string {
2466
2499
  cellOf(pick(args, 2, ["support"], "null")),
2467
2500
  ]);
2468
2501
  }
2502
+ if (method === "ema_cross_signal") {
2503
+ return taCall("emaCrossSignal", site, [
2504
+ cellOf(pick(args, 0, ["fast_ema", "fast"], "null")),
2505
+ cellOf(pick(args, 1, ["slow_ema", "slow"], "null")),
2506
+ cellOf(pick(args, 2, ["threshold"], "null")),
2507
+ ]);
2508
+ }
2509
+ if (method === "momentum_filter") {
2510
+ return taCall("momentumFilter", site, [
2511
+ cellOf(pick(args, 0, ["momentum_raw", "momentum"], "null")),
2512
+ cellOf(pick(args, 1, ["volume"], "null")),
2513
+ cellOf(pick(args, 2, ["period", "length"], "null")),
2514
+ ]);
2515
+ }
2516
+ if (method === "volatility_regime") {
2517
+ return taCall("volatilityRegime", site, [
2518
+ cellOf(pick(args, 0, ["atr_series", "atr", "source"], "null")),
2519
+ cellOf(pick(args, 1, ["period", "length"], "null")),
2520
+ ]);
2521
+ }
2522
+ if (method === "economic_impact_score") {
2523
+ return taPure("economicImpactScore", [
2524
+ cellOf(pick(args, 0, ["price_change"], "null")),
2525
+ cellOf(pick(args, 1, ["volatility"], "null")),
2526
+ cellOf(pick(args, 2, ["volume_change"], "null")),
2527
+ ]);
2528
+ }
2529
+ if (method === "contrarian_signal") {
2530
+ return taPure("contrarianSignal", [
2531
+ cellOf(pick(args, 0, ["sentiment"], "null")),
2532
+ cellOf(pick(args, 1, ["volatility"], "null")),
2533
+ cellOf(pick(args, 2, ["time_since_extreme", "time_extreme"], "null")),
2534
+ ]);
2535
+ }
2536
+ if (method === "drawdown_recovery_level") {
2537
+ return taPure("drawdownRecoveryLevel", [
2538
+ cellOf(pick(args, 0, ["peak"], "null")),
2539
+ cellOf(pick(args, 1, ["current", "current_price"], "null")),
2540
+ cellOf(pick(args, 2, ["recovery_factor", "factor"], "null")),
2541
+ cellOf(pick(args, 3, ["period", "length"], "null")),
2542
+ ]);
2543
+ }
2544
+ if (method === "pivot_point_levels") {
2545
+ return taPure("pivotPointLevels", [
2546
+ cellOf(pick(args, 0, ["high"], "null")),
2547
+ cellOf(pick(args, 1, ["low"], "null")),
2548
+ cellOf(pick(args, 2, ["close"], "null")),
2549
+ pick(args, 3, ["traditional"], "true"),
2550
+ ]);
2551
+ }
2552
+ if (method === "volatility_regime_score") {
2553
+ return taCall("volatilityRegimeScore", site, [
2554
+ arrOrCell(pick(args, 0, ["atr_list", "atr"], "null")),
2555
+ arrOrCell(pick(args, 1, ["volatility_list", "volatility"], "null")),
2556
+ arrOrCell(pick(args, 2, ["vix_list", "vix"], "null")),
2557
+ cellOf(pick(args, 3, ["threshold"], "null")),
2558
+ ]);
2559
+ }
2560
+ if (method === "correlation_filter") {
2561
+ return taCall("correlationFilter", site, [
2562
+ arrOrCell(pick(args, 0, ["signal1_list", "signal1", "s1"], "null")),
2563
+ arrOrCell(pick(args, 1, ["signal2_list", "signal2", "s2"], "null")),
2564
+ arrOrCell(pick(args, 2, ["signal3_list", "signal3", "s3"], "null")),
2565
+ cellOf(pick(args, 4, ["threshold"], "null")),
2566
+ ]);
2567
+ }
2568
+ if (method === "advanced_breakout_detector") {
2569
+ return taCall("advancedBreakoutDetector", site, [
2570
+ arrOrCell(pick(args, 0, ["price_list", "price"], "null")),
2571
+ arrOrCell(pick(args, 1, ["volume_list", "volume"], "null")),
2572
+ cellOf(pick(args, 2, ["level"], "null")),
2573
+ cellOf(pick(args, 3, ["lookback"], "null")),
2574
+ cellOf(pick(args, 4, ["volume_multiplier", "vol_mult"], "null")),
2575
+ ]);
2576
+ }
2577
+ if (method === "pullback_bounce_level") {
2578
+ return taCall("pullbackBounceLevel", site, [
2579
+ arrOrCell(pick(args, 0, ["high_list", "high"], "null")),
2580
+ arrOrCell(pick(args, 1, ["low_list", "low"], "null")),
2581
+ cellOf(pick(args, 3, ["trend_direction", "trend_dir"], "null")),
2582
+ cellOf(pick(args, 4, ["lookback"], "null")),
2583
+ ]);
2584
+ }
2585
+ if (method === "divergence_detector") {
2586
+ return taCall("divergenceDetector", site, [
2587
+ arrOrCell(pick(args, 0, ["price_list", "price"], "null")),
2588
+ arrOrCell(pick(args, 1, ["indicator_list", "indicator"], "null")),
2589
+ cellOf(pick(args, 2, ["lookback"], "null")),
2590
+ ]);
2591
+ }
2592
+ if (method === "momentum_divergence") {
2593
+ return taCall("momentumDivergence", site, [
2594
+ arrOrCell(pick(args, 0, ["price"], "null")),
2595
+ arrOrCell(pick(args, 1, ["momentum_fast", "mom_fast"], "null")),
2596
+ arrOrCell(pick(args, 2, ["momentum_slow", "mom_slow"], "null")),
2597
+ ]);
2598
+ }
2599
+ if (method === "spread_analysis") {
2600
+ return taCall("spreadAnalysis", site, [
2601
+ arrOrCell(pick(args, 0, ["bid"], "null")),
2602
+ arrOrCell(pick(args, 1, ["ask"], "null")),
2603
+ cellOf(pick(args, 2, ["period", "length"], "null")),
2604
+ ]);
2605
+ }
2606
+ if (method === "employment_cycle_indicator") {
2607
+ return taCall("employmentCycleIndicator", site, [
2608
+ arrOrCell(pick(args, 0, ["cyclical_stocks", "cyclical"], "null")),
2609
+ arrOrCell(pick(args, 1, ["defensive_stocks", "defensive"], "null")),
2610
+ arrOrCell(pick(args, 2, ["unemployment_proxy", "unemployment"], "null")),
2611
+ ]);
2612
+ }
2613
+ if (method === "gdp_growth_proxy") {
2614
+ return taCall("gdpGrowthProxy", site, [
2615
+ arrOrCell(pick(args, 0, ["market_breadth", "breadth"], "null")),
2616
+ arrOrCell(pick(args, 1, ["market_volume", "volume"], "null")),
2617
+ arrOrCell(pick(args, 2, ["price_momentum", "momentum"], "null")),
2618
+ ]);
2619
+ }
2620
+ if (method === "inflation_proxy_indicator") {
2621
+ return taCall("inflationProxyIndicator", site, [
2622
+ arrOrCell(pick(args, 0, ["usd_index", "usd"], "null")),
2623
+ arrOrCell(pick(args, 1, ["commodity_prices", "commodities"], "null")),
2624
+ arrOrCell(pick(args, 2, ["bond_yields", "yields"], "null")),
2625
+ ]);
2626
+ }
2627
+ if (method === "expected_value") {
2628
+ return `__h.ta.expectedValue(${cellList(pick(args, 0, ["returns"], "null"))}, ${cellList(pick(args, 1, ["probabilities", "probs"], "null"))})`;
2629
+ }
2469
2630
  return "null";
2470
2631
  }
@@ -2105,6 +2105,39 @@ function evalCall(node: Call, env: Env): Value {
2105
2105
  evalAsString(callArg(node.args, 3, ["adjust", "adjustment"]), env) ?? undefined,
2106
2106
  });
2107
2107
  }
2108
+ if (fname === "ticker.inherit") {
2109
+ const first = evalExpr(callArg(node.args, 0, ["ticker", "tickerid", "from", "source"]), env);
2110
+ const secondArg = callArg(node.args, 1, ["symbol", "ticker"]);
2111
+ if (secondArg != null) {
2112
+ const target = evalExpr(secondArg, env);
2113
+ const sym =
2114
+ target instanceof TickerId
2115
+ ? target.symbol
2116
+ : typeof target === "string"
2117
+ ? target
2118
+ : env.symbol;
2119
+ if (first instanceof TickerId) return tickerNew(sym, first.session, first.adjust);
2120
+ return tickerNew(sym);
2121
+ }
2122
+ if (first instanceof TickerId) {
2123
+ return tickerNew(first.symbol, first.session, first.adjust);
2124
+ }
2125
+ const sym = typeof first === "string" ? first : env.symbol;
2126
+ return tickerNew(sym);
2127
+ }
2128
+ if (
2129
+ fname === "renko" ||
2130
+ fname === "kagi" ||
2131
+ fname === "linebreak" ||
2132
+ fname === "pointfigure"
2133
+ ) {
2134
+ const raw = evalExpr(callArg(node.args, 0, ["ticker", "tickerid", "symbol"]), env);
2135
+ const sym = raw instanceof TickerId ? raw.symbol : typeof raw === "string" ? raw : env.symbol;
2136
+ if (fname === "renko") return tickerRenko(sym);
2137
+ if (fname === "kagi") return tickerKagi(sym);
2138
+ if (fname === "linebreak") return tickerLinebreak(sym);
2139
+ return tickerPointfigure(sym);
2140
+ }
2108
2141
  const mapVal = evalMapCall(fname, node, env);
2109
2142
  if (mapVal !== undefined) return mapVal;
2110
2143
  const matrixVal = evalMatrixCall(fname, node, env);
@@ -3841,7 +3874,7 @@ function evalExtraTa(fname: string | null, node: Call, env: Env, site: string):
3841
3874
  lenArg(node, env, 1),
3842
3875
  );
3843
3876
  }
3844
- if (fname === "ta.percentile_nearest_rank") {
3877
+ if (fname === "ta.percentile_nearest_rank" || fname === "percentile_nearest_rank") {
3845
3878
  if (typeof ta.percentileNearest !== "function") return NA;
3846
3879
  return (ta.percentileNearest as TaEngine["percentileNearest"]).call(
3847
3880
  env.ta,
@@ -3851,7 +3884,7 @@ function evalExtraTa(fname: string | null, node: Call, env: Env, site: string):
3851
3884
  lenOrDefault(node, env, 2, ["percentage", "percent"], 50),
3852
3885
  );
3853
3886
  }
3854
- if (fname === "ta.percentile_linear_interpolation") {
3887
+ if (fname === "ta.percentile_linear_interpolation" || fname === "percentile_linear_interpolation") {
3855
3888
  if (typeof ta.percentileLinear !== "function") return NA;
3856
3889
  return (ta.percentileLinear as TaEngine["percentileLinear"]).call(
3857
3890
  env.ta,
@@ -4049,17 +4082,6 @@ function evalExtraTa(fname: string | null, node: Call, env: Env, site: string):
4049
4082
  const r = env.ta.aroon(site, num(env.ctx.high), num(env.ctx.low), length ?? 14);
4050
4083
  return tupleOf([r.down, r.up]);
4051
4084
  }
4052
- if (fname === "ta.pivot_point_levels") {
4053
- if (typeof ta.pivotPoints !== "function") return NA;
4054
- const r = (ta.pivotPoints as TaEngine["pivotPoints"]).call(
4055
- env.ta,
4056
- site,
4057
- num(env.ctx.high),
4058
- num(env.ctx.low),
4059
- num(env.ctx.close),
4060
- );
4061
- return tupleOf([r.pp, r.r1, r.s1, r.r2, r.s2]);
4062
- }
4063
4085
  if (fname === "ta.uo" || fname === "uo") {
4064
4086
  return env.ta.uo(
4065
4087
  site,
@@ -4300,6 +4322,8 @@ function evalExtraTa(fname: string | null, node: Call, env: Env, site: string):
4300
4322
  unwrap(evalExpr(wave10aArg(index, names, message), env));
4301
4323
  const wave11n = (index: number, names: string[], message: string): Cell =>
4302
4324
  num(wave11(index, names, message));
4325
+ const wave13v = (index: number, names: string[], message: string): Value =>
4326
+ evalExpr(wave10aArg(index, names, message), env);
4303
4327
  if (fname === "ta.sma_weighted" || fname === "sma_weighted") {
4304
4328
  const msg = "ta.sma_weighted() requires at least 2 arguments: series, period";
4305
4329
  const weightArg = callArg(node.args, 2, ["weight_type", "weight"]);
@@ -4566,12 +4590,12 @@ function evalExtraTa(fname: string | null, node: Call, env: Env, site: string):
4566
4590
  }
4567
4591
  if (fname === "ta.market_structure_pivot" || fname === "market_structure_pivot") {
4568
4592
  const msg = "ta.market_structure_pivot() requires 5 arguments";
4569
- wave11n(0, ["high"], msg);
4570
- wave11n(1, ["low"], msg);
4571
- const close = wave11n(2, ["close"], msg);
4572
- wave11n(3, ["left", "left_bars"], msg);
4573
- wave11n(4, ["right", "right_bars"], msg);
4574
- return env.ta.marketStructurePivot(close);
4593
+ const high = wave13v(0, ["high_list", "high"], msg);
4594
+ const low = wave13v(1, ["low_list", "low"], msg);
4595
+ wave13v(2, ["close_list", "close"], msg);
4596
+ const period = wave11n(3, ["period", "left", "left_bars"], msg);
4597
+ const mode = wave11n(4, ["mode", "right", "right_bars"], msg);
4598
+ return env.ta.marketStructurePivot(site, high, low, period, mode);
4575
4599
  }
4576
4600
  if (fname === "ta.position_sizing_score" || fname === "position_sizing_score") {
4577
4601
  const msg = "ta.position_sizing_score() requires 4 arguments";
@@ -4596,16 +4620,17 @@ function evalExtraTa(fname: string | null, node: Call, env: Env, site: string):
4596
4620
  const msg = "ta.optimal_entry_zone() requires 4 arguments";
4597
4621
  wave11n(0, ["high"], msg);
4598
4622
  wave11n(1, ["low"], msg);
4599
- const close = wave11n(2, ["close"], msg);
4623
+ const close = wave13v(2, ["close"], msg);
4600
4624
  wave11n(3, ["period", "length"], msg);
4601
- return env.ta.optimalEntryZone(close);
4625
+ return env.ta.optimalEntryZone(site, close);
4602
4626
  }
4603
4627
  if (fname === "ta.mean_reversion_entry" || fname === "mean_reversion_entry") {
4604
4628
  const msg = "ta.mean_reversion_entry() requires 5 arguments";
4605
4629
  return env.ta.meanReversionEntry(
4606
- wave11n(0, ["price", "source"], msg),
4607
- wave11n(1, ["sma"], msg),
4608
- wave11n(2, ["stdev", "std_dev"], msg),
4630
+ site,
4631
+ wave13v(0, ["price", "source"], msg),
4632
+ wave13v(1, ["sma"], msg),
4633
+ wave13v(2, ["stdev", "std_dev"], msg),
4609
4634
  wave11n(4, ["threshold"], msg),
4610
4635
  );
4611
4636
  }
@@ -4655,6 +4680,186 @@ function evalExtraTa(fname: string | null, node: Call, env: Env, site: string):
4655
4680
  );
4656
4681
  }
4657
4682
  // wave12-end
4683
+ // wave13-start — history / sentiment / macro analytics
4684
+ if (fname === "ta.ema_cross_signal" || fname === "ema_cross_signal") {
4685
+ const msg = "ta.ema_cross_signal() requires 3 arguments";
4686
+ return env.ta.emaCrossSignal(
4687
+ site,
4688
+ wave11n(0, ["fast_ema", "fast"], msg),
4689
+ wave11n(1, ["slow_ema", "slow"], msg),
4690
+ wave11n(2, ["threshold"], msg),
4691
+ );
4692
+ }
4693
+ if (fname === "ta.momentum_filter" || fname === "momentum_filter") {
4694
+ const msg = "ta.momentum_filter() requires 3 arguments";
4695
+ return env.ta.momentumFilter(
4696
+ site,
4697
+ wave11n(0, ["momentum_raw", "momentum"], msg),
4698
+ wave11n(1, ["volume"], msg),
4699
+ wave11n(2, ["period", "length"], msg),
4700
+ );
4701
+ }
4702
+ if (fname === "ta.volatility_regime" || fname === "volatility_regime") {
4703
+ const msg = "ta.volatility_regime() requires 2 arguments";
4704
+ return env.ta.volatilityRegime(
4705
+ site,
4706
+ wave11n(0, ["atr_series", "atr", "source"], msg),
4707
+ wave11n(1, ["period", "length"], msg),
4708
+ );
4709
+ }
4710
+ if (fname === "ta.economic_impact_score" || fname === "economic_impact_score") {
4711
+ const msg = "ta.economic_impact_score() requires 3 arguments";
4712
+ return env.ta.economicImpactScore(
4713
+ wave11n(0, ["price_change"], msg),
4714
+ wave11n(1, ["volatility"], msg),
4715
+ wave11n(2, ["volume_change"], msg),
4716
+ );
4717
+ }
4718
+ if (fname === "ta.contrarian_signal" || fname === "contrarian_signal") {
4719
+ const msg = "ta.contrarian_signal() requires 3 arguments";
4720
+ return env.ta.contrarianSignal(
4721
+ wave11n(0, ["sentiment"], msg),
4722
+ wave11n(1, ["volatility"], msg),
4723
+ wave11n(2, ["time_since_extreme", "time_extreme"], msg),
4724
+ );
4725
+ }
4726
+ if (fname === "ta.drawdown_recovery_level" || fname === "drawdown_recovery_level") {
4727
+ const msg = "ta.drawdown_recovery_level() requires 4 arguments";
4728
+ return env.ta.drawdownRecoveryLevel(
4729
+ wave11n(0, ["peak"], msg),
4730
+ wave11n(1, ["current", "current_price"], msg),
4731
+ wave11n(2, ["recovery_factor", "factor"], msg),
4732
+ wave11n(3, ["period", "length"], msg),
4733
+ );
4734
+ }
4735
+ if (fname === "ta.pivot_point_levels" || fname === "pivot_point_levels") {
4736
+ const msg = "ta.pivot_point_levels() requires type+anchor or high, low, close";
4737
+ const first = wave11(0, ["type", "high"], msg);
4738
+ if (typeof first === "string") {
4739
+ // Reference Pine type+anchor form → context pivot-point tuple.
4740
+ if (typeof env.ta.pivotPoints !== "function") return NA;
4741
+ const r = (env.ta.pivotPoints as TaEngine["pivotPoints"]).call(
4742
+ env.ta,
4743
+ site,
4744
+ num(env.ctx.high),
4745
+ num(env.ctx.low),
4746
+ num(env.ctx.close),
4747
+ );
4748
+ return tupleOf([r.pp, r.r1, r.s1, r.r2, r.s2]);
4749
+ }
4750
+ const tradArg = callArg(node.args, 3, ["traditional"]);
4751
+ const traditional = tradArg == null ? true : unwrap(evalExpr(tradArg, env)) !== 0;
4752
+ return env.ta.pivotPointLevels(
4753
+ num(first),
4754
+ wave11n(1, ["low"], msg),
4755
+ wave11n(2, ["close"], msg),
4756
+ traditional,
4757
+ );
4758
+ }
4759
+ if (fname === "ta.volatility_regime_score" || fname === "volatility_regime_score") {
4760
+ const msg = "ta.volatility_regime_score() requires 4 arguments";
4761
+ return env.ta.volatilityRegimeScore(
4762
+ site,
4763
+ wave13v(0, ["atr_list", "atr"], msg),
4764
+ wave13v(1, ["volatility_list", "volatility"], msg),
4765
+ wave13v(2, ["vix_list", "vix"], msg),
4766
+ wave11n(3, ["threshold"], msg),
4767
+ );
4768
+ }
4769
+ if (fname === "ta.correlation_filter" || fname === "correlation_filter") {
4770
+ const msg = "ta.correlation_filter() requires 5 arguments";
4771
+ const s1 = wave13v(0, ["signal1_list", "signal1", "s1"], msg);
4772
+ const s2 = wave13v(1, ["signal2_list", "signal2", "s2"], msg);
4773
+ const s3 = wave13v(2, ["signal3_list", "signal3", "s3"], msg);
4774
+ wave11n(3, ["num_signals"], msg);
4775
+ return env.ta.correlationFilter(site, s1, s2, s3, wave11n(4, ["threshold"], msg));
4776
+ }
4777
+ if (fname === "ta.advanced_breakout_detector" || fname === "advanced_breakout_detector") {
4778
+ const msg = "ta.advanced_breakout_detector() requires 5 arguments";
4779
+ return env.ta.advancedBreakoutDetector(
4780
+ site,
4781
+ wave13v(0, ["price_list", "price"], msg),
4782
+ wave13v(1, ["volume_list", "volume"], msg),
4783
+ wave11n(2, ["level"], msg),
4784
+ wave11n(3, ["lookback"], msg),
4785
+ wave11n(4, ["volume_multiplier", "vol_mult"], msg),
4786
+ );
4787
+ }
4788
+ if (fname === "ta.pullback_bounce_level" || fname === "pullback_bounce_level") {
4789
+ const msg = "ta.pullback_bounce_level() requires 5 arguments";
4790
+ const high = wave13v(0, ["high_list", "high"], msg);
4791
+ const low = wave13v(1, ["low_list", "low"], msg);
4792
+ wave13v(2, ["close_list", "close"], msg);
4793
+ return env.ta.pullbackBounceLevel(
4794
+ site,
4795
+ high,
4796
+ low,
4797
+ wave11n(3, ["trend_direction", "trend_dir"], msg),
4798
+ wave11n(4, ["lookback"], msg),
4799
+ );
4800
+ }
4801
+ if (fname === "ta.divergence_detector" || fname === "divergence_detector") {
4802
+ const msg = "ta.divergence_detector() requires 3 arguments";
4803
+ return env.ta.divergenceDetector(
4804
+ site,
4805
+ wave13v(0, ["price_list", "price"], msg),
4806
+ wave13v(1, ["indicator_list", "indicator"], msg),
4807
+ wave11n(2, ["lookback"], msg),
4808
+ );
4809
+ }
4810
+ if (fname === "ta.momentum_divergence" || fname === "momentum_divergence") {
4811
+ const msg = "ta.momentum_divergence() requires 3 arguments";
4812
+ return env.ta.momentumDivergence(
4813
+ site,
4814
+ wave13v(0, ["price"], msg),
4815
+ wave13v(1, ["momentum_fast", "mom_fast"], msg),
4816
+ wave13v(2, ["momentum_slow", "mom_slow"], msg),
4817
+ );
4818
+ }
4819
+ if (fname === "ta.spread_analysis" || fname === "spread_analysis") {
4820
+ const msg = "ta.spread_analysis() requires 3 arguments";
4821
+ return env.ta.spreadAnalysis(
4822
+ site,
4823
+ wave13v(0, ["bid"], msg),
4824
+ wave13v(1, ["ask"], msg),
4825
+ wave11n(2, ["period", "length"], msg),
4826
+ );
4827
+ }
4828
+ if (fname === "ta.employment_cycle_indicator" || fname === "employment_cycle_indicator") {
4829
+ const msg = "ta.employment_cycle_indicator() requires 3 arguments";
4830
+ return env.ta.employmentCycleIndicator(
4831
+ site,
4832
+ wave13v(0, ["cyclical_stocks", "cyclical"], msg),
4833
+ wave13v(1, ["defensive_stocks", "defensive"], msg),
4834
+ wave13v(2, ["unemployment_proxy", "unemployment"], msg),
4835
+ );
4836
+ }
4837
+ if (fname === "ta.gdp_growth_proxy" || fname === "gdp_growth_proxy") {
4838
+ const msg = "ta.gdp_growth_proxy() requires 3 arguments";
4839
+ return env.ta.gdpGrowthProxy(
4840
+ site,
4841
+ wave13v(0, ["market_breadth", "breadth"], msg),
4842
+ wave13v(1, ["market_volume", "volume"], msg),
4843
+ wave13v(2, ["price_momentum", "momentum"], msg),
4844
+ );
4845
+ }
4846
+ if (fname === "ta.inflation_proxy_indicator" || fname === "inflation_proxy_indicator") {
4847
+ const msg = "ta.inflation_proxy_indicator() requires 3 arguments";
4848
+ return env.ta.inflationProxyIndicator(
4849
+ site,
4850
+ wave13v(0, ["usd_index", "usd"], msg),
4851
+ wave13v(1, ["commodity_prices", "commodities"], msg),
4852
+ wave13v(2, ["bond_yields", "yields"], msg),
4853
+ );
4854
+ }
4855
+ if (fname === "ta.expected_value" || fname === "expected_value") {
4856
+ const msg = "ta.expected_value() requires 2 arguments: returns, probabilities";
4857
+ const returns = numericList(evalExpr(wave10aArg(0, ["returns"], msg), env));
4858
+ const probs = numericList(evalExpr(wave10aArg(1, ["probabilities", "probs"], msg), env));
4859
+ if (returns.length !== probs.length) runtimeError("Returns and probabilities must have same length");
4860
+ return env.ta.expectedValue(returns, probs);
4861
+ }
4862
+ // wave13-end
4658
4863
  return undefined;
4659
4864
  }
4660
4865
 
@@ -4676,6 +4881,21 @@ function seriesLastCell(value: Value): number | null {
4676
4881
  return typeof v === "number" && Number.isFinite(v) ? v : null;
4677
4882
  }
4678
4883
 
4884
+ /** Python `_expect_list` for array-valued args: numeric cells only. */
4885
+ function numericList(value: Value): number[] {
4886
+ const arr = asArray(value);
4887
+ if (arr == null) {
4888
+ const v = unwrap(value);
4889
+ return typeof v === "number" && Number.isFinite(v) ? [v] : [];
4890
+ }
4891
+ const out: number[] = [];
4892
+ for (let i = 0; i < arr.size(); i++) {
4893
+ const v = arr.get(i);
4894
+ if (typeof v === "number" && Number.isFinite(v)) out.push(v);
4895
+ }
4896
+ return out;
4897
+ }
4898
+
4679
4899
  function asArray(value: Value): PineArray | null {
4680
4900
  if (value instanceof PineArray) return value;
4681
4901
  if (value && typeof value === "object" && "__array" in value) {