@hoox-sh/pynets 0.6.0 → 0.8.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.8.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.8.0";
16
16
 
17
17
  type Rgb = readonly [number, number, number];
18
18
 
@@ -193,6 +193,27 @@ 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",
214
+ "gamma_levels",
215
+ "intelligent_strategy_synthesizer",
216
+ "signal_confluence",
196
217
  ]);
197
218
 
198
219
  /**
@@ -405,6 +426,13 @@ export function emitCall(ctx: EmitCtx, node: Call, visit: VisitFn): string {
405
426
  ctx.needsHeikinashi = true;
406
427
  return `"__HEIKINASHI__"`;
407
428
  }
429
+ if (name === "ticker_inherit") {
430
+ const fallback = pick(args, 0, ["ticker", "tickerid", "from", "source"], `"SYMBOL"`);
431
+ return pick(args, 1, ["symbol", "ticker"], fallback);
432
+ }
433
+ if (name === "renko" || name === "kagi" || name === "linebreak" || name === "pointfigure") {
434
+ return pick(args, 0, ["ticker", "tickerid", "symbol"], `"SYMBOL"`);
435
+ }
408
436
  // ticker.new / standard / modify / renko / kagi / linebreak / pointfigure → first arg or "SYMBOL"
409
437
  if (name.startsWith("ticker_")) {
410
438
  return pick(args, 0, ["symbol", "ticker", "tickerid"], `"SYMBOL"`);
@@ -588,6 +616,10 @@ function hasSeriesArg(args: CallArgs): boolean {
588
616
  return args.posNodes.some(isSeries) || Object.values(args.kwNodes).some(isSeries);
589
617
  }
590
618
 
619
+ function isSeriesNode(n: expr | undefined): boolean {
620
+ return n != null && n.kind === "Name" && BARE_SERIES_NAMES.has(n.id);
621
+ }
622
+
591
623
  /** True when a keyword argument named one of *names* is present (interpret hasKwarg). */
592
624
  function hasAnyKw(args: CallArgs, names: string[]): boolean {
593
625
  return names.some((n) => Object.hasOwn(args.kw, n));
@@ -617,6 +649,11 @@ function cellOf(expr: string): string {
617
649
  return `((__c) => (__c == null ? null : typeof __c === "boolean" ? (__c ? 1 : 0) : (typeof __c === "number" && Number.isFinite(__c) ? __c : null)))(${expr})`;
618
650
  }
619
651
 
652
+ /** Cell or array/object (history-based TA args keep arrays intact). */
653
+ function arrOrCell(expr: string): string {
654
+ 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})`;
655
+ }
656
+
620
657
  /**
621
658
  * Interpret `wave10bList`: a Pine array becomes its cells; a scalar is a
622
659
  * one-element list. Bar-mode series stay one element.
@@ -2394,8 +2431,11 @@ function emitTa(ctx: EmitCtx, method: string, args: CallArgs): string {
2394
2431
  ]);
2395
2432
  }
2396
2433
  if (method === "market_structure_pivot") {
2397
- return taPure("marketStructurePivot", [
2398
- cellOf(pick(args, 2, ["close"], "null")),
2434
+ return taCall("marketStructurePivot", site, [
2435
+ arrOrCell(pick(args, 0, ["high_list", "high"], "null")),
2436
+ arrOrCell(pick(args, 1, ["low_list", "low"], "null")),
2437
+ cellOf(pick(args, 3, ["period", "left", "left_bars"], "null")),
2438
+ cellOf(pick(args, 4, ["mode", "right", "right_bars"], "null")),
2399
2439
  ]);
2400
2440
  }
2401
2441
  if (method === "position_sizing_score") {
@@ -2416,13 +2456,13 @@ function emitTa(ctx: EmitCtx, method: string, args: CallArgs): string {
2416
2456
  ]);
2417
2457
  }
2418
2458
  if (method === "optimal_entry_zone") {
2419
- return taPure("optimalEntryZone", [cellOf(pick(args, 2, ["close"], "null"))]);
2459
+ return taCall("optimalEntryZone", site, [arrOrCell(pick(args, 2, ["close"], "null"))]);
2420
2460
  }
2421
2461
  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")),
2462
+ return taCall("meanReversionEntry", site, [
2463
+ arrOrCell(pick(args, 0, ["price", "source"], "null")),
2464
+ arrOrCell(pick(args, 1, ["sma"], "null")),
2465
+ arrOrCell(pick(args, 2, ["stdev", "std_dev"], "null")),
2426
2466
  cellOf(pick(args, 4, ["threshold"], "null")),
2427
2467
  ]);
2428
2468
  }
@@ -2460,11 +2500,163 @@ function emitTa(ctx: EmitCtx, method: string, args: CallArgs): string {
2460
2500
  ]);
2461
2501
  }
2462
2502
  if (method === "breakout_detection") {
2503
+ const arg = (i: number, names: string[]): string =>
2504
+ isSeriesNode(args.posNodes[i]) ? "null" : cellOf(pick(args, i, names, "null"));
2463
2505
  return taPure("breakoutDetection", [
2464
- cellOf(pick(args, 0, ["close", "source"], "null")),
2465
- cellOf(pick(args, 1, ["resistance"], "null")),
2466
- cellOf(pick(args, 2, ["support"], "null")),
2506
+ arg(0, ["close", "source"]),
2507
+ arg(1, ["resistance"]),
2508
+ arg(2, ["support"]),
2509
+ ]);
2510
+ }
2511
+ if (method === "ema_cross_signal") {
2512
+ return taCall("emaCrossSignal", site, [
2513
+ cellOf(pick(args, 0, ["fast_ema", "fast"], "null")),
2514
+ cellOf(pick(args, 1, ["slow_ema", "slow"], "null")),
2515
+ cellOf(pick(args, 2, ["threshold"], "null")),
2516
+ ]);
2517
+ }
2518
+ if (method === "momentum_filter") {
2519
+ return taCall("momentumFilter", site, [
2520
+ cellOf(pick(args, 0, ["momentum_raw", "momentum"], "null")),
2521
+ cellOf(pick(args, 1, ["volume"], "null")),
2522
+ cellOf(pick(args, 2, ["period", "length"], "null")),
2523
+ ]);
2524
+ }
2525
+ if (method === "volatility_regime") {
2526
+ return taCall("volatilityRegime", site, [
2527
+ cellOf(pick(args, 0, ["atr_series", "atr", "source"], "null")),
2528
+ cellOf(pick(args, 1, ["period", "length"], "null")),
2529
+ ]);
2530
+ }
2531
+ if (method === "economic_impact_score") {
2532
+ return taPure("economicImpactScore", [
2533
+ cellOf(pick(args, 0, ["price_change"], "null")),
2534
+ cellOf(pick(args, 1, ["volatility"], "null")),
2535
+ cellOf(pick(args, 2, ["volume_change"], "null")),
2536
+ ]);
2537
+ }
2538
+ if (method === "contrarian_signal") {
2539
+ return taPure("contrarianSignal", [
2540
+ cellOf(pick(args, 0, ["sentiment"], "null")),
2541
+ cellOf(pick(args, 1, ["volatility"], "null")),
2542
+ cellOf(pick(args, 2, ["time_since_extreme", "time_extreme"], "null")),
2543
+ ]);
2544
+ }
2545
+ if (method === "drawdown_recovery_level") {
2546
+ return taPure("drawdownRecoveryLevel", [
2547
+ cellOf(pick(args, 0, ["peak"], "null")),
2548
+ cellOf(pick(args, 1, ["current", "current_price"], "null")),
2549
+ cellOf(pick(args, 2, ["recovery_factor", "factor"], "null")),
2550
+ cellOf(pick(args, 3, ["period", "length"], "null")),
2467
2551
  ]);
2468
2552
  }
2553
+ if (method === "pivot_point_levels") {
2554
+ return taPure("pivotPointLevels", [
2555
+ cellOf(pick(args, 0, ["high"], "null")),
2556
+ cellOf(pick(args, 1, ["low"], "null")),
2557
+ cellOf(pick(args, 2, ["close"], "null")),
2558
+ pick(args, 3, ["traditional"], "true"),
2559
+ ]);
2560
+ }
2561
+ if (method === "volatility_regime_score") {
2562
+ return taCall("volatilityRegimeScore", site, [
2563
+ arrOrCell(pick(args, 0, ["atr_list", "atr"], "null")),
2564
+ arrOrCell(pick(args, 1, ["volatility_list", "volatility"], "null")),
2565
+ arrOrCell(pick(args, 2, ["vix_list", "vix"], "null")),
2566
+ cellOf(pick(args, 3, ["threshold"], "null")),
2567
+ ]);
2568
+ }
2569
+ if (method === "correlation_filter") {
2570
+ return taCall("correlationFilter", site, [
2571
+ arrOrCell(pick(args, 0, ["signal1_list", "signal1", "s1"], "null")),
2572
+ arrOrCell(pick(args, 1, ["signal2_list", "signal2", "s2"], "null")),
2573
+ arrOrCell(pick(args, 2, ["signal3_list", "signal3", "s3"], "null")),
2574
+ cellOf(pick(args, 4, ["threshold"], "null")),
2575
+ ]);
2576
+ }
2577
+ if (method === "advanced_breakout_detector") {
2578
+ return taCall("advancedBreakoutDetector", site, [
2579
+ arrOrCell(pick(args, 0, ["price_list", "price"], "null")),
2580
+ arrOrCell(pick(args, 1, ["volume_list", "volume"], "null")),
2581
+ cellOf(pick(args, 2, ["level"], "null")),
2582
+ cellOf(pick(args, 3, ["lookback"], "null")),
2583
+ cellOf(pick(args, 4, ["volume_multiplier", "vol_mult"], "null")),
2584
+ ]);
2585
+ }
2586
+ if (method === "pullback_bounce_level") {
2587
+ return taCall("pullbackBounceLevel", site, [
2588
+ arrOrCell(pick(args, 0, ["high_list", "high"], "null")),
2589
+ arrOrCell(pick(args, 1, ["low_list", "low"], "null")),
2590
+ cellOf(pick(args, 3, ["trend_direction", "trend_dir"], "null")),
2591
+ cellOf(pick(args, 4, ["lookback"], "null")),
2592
+ ]);
2593
+ }
2594
+ if (method === "divergence_detector") {
2595
+ return taCall("divergenceDetector", site, [
2596
+ arrOrCell(pick(args, 0, ["price_list", "price"], "null")),
2597
+ arrOrCell(pick(args, 1, ["indicator_list", "indicator"], "null")),
2598
+ cellOf(pick(args, 2, ["lookback"], "null")),
2599
+ ]);
2600
+ }
2601
+ if (method === "momentum_divergence") {
2602
+ return taCall("momentumDivergence", site, [
2603
+ arrOrCell(pick(args, 0, ["price"], "null")),
2604
+ arrOrCell(pick(args, 1, ["momentum_fast", "mom_fast"], "null")),
2605
+ arrOrCell(pick(args, 2, ["momentum_slow", "mom_slow"], "null")),
2606
+ ]);
2607
+ }
2608
+ if (method === "spread_analysis") {
2609
+ return taCall("spreadAnalysis", site, [
2610
+ arrOrCell(pick(args, 0, ["bid"], "null")),
2611
+ arrOrCell(pick(args, 1, ["ask"], "null")),
2612
+ cellOf(pick(args, 2, ["period", "length"], "null")),
2613
+ ]);
2614
+ }
2615
+ if (method === "employment_cycle_indicator") {
2616
+ return taCall("employmentCycleIndicator", site, [
2617
+ arrOrCell(pick(args, 0, ["cyclical_stocks", "cyclical"], "null")),
2618
+ arrOrCell(pick(args, 1, ["defensive_stocks", "defensive"], "null")),
2619
+ arrOrCell(pick(args, 2, ["unemployment_proxy", "unemployment"], "null")),
2620
+ ]);
2621
+ }
2622
+ if (method === "gdp_growth_proxy") {
2623
+ return taCall("gdpGrowthProxy", site, [
2624
+ arrOrCell(pick(args, 0, ["market_breadth", "breadth"], "null")),
2625
+ arrOrCell(pick(args, 1, ["market_volume", "volume"], "null")),
2626
+ arrOrCell(pick(args, 2, ["price_momentum", "momentum"], "null")),
2627
+ ]);
2628
+ }
2629
+ if (method === "inflation_proxy_indicator") {
2630
+ return taCall("inflationProxyIndicator", site, [
2631
+ arrOrCell(pick(args, 0, ["usd_index", "usd"], "null")),
2632
+ arrOrCell(pick(args, 1, ["commodity_prices", "commodities"], "null")),
2633
+ arrOrCell(pick(args, 2, ["bond_yields", "yields"], "null")),
2634
+ ]);
2635
+ }
2636
+ if (method === "expected_value") {
2637
+ return `__h.ta.expectedValue(${cellList(pick(args, 0, ["returns"], "null"))}, ${cellList(pick(args, 1, ["probabilities", "probs"], "null"))})`;
2638
+ }
2639
+ if (method === "gamma_levels") {
2640
+ const vol = isSeriesNode(args.posNodes[0])
2641
+ ? "null"
2642
+ : cellOf(pick(args, 0, ["volatility"], "null"));
2643
+ const price = isSeriesNode(args.posNodes[1])
2644
+ ? "null"
2645
+ : cellOf(pick(args, 1, ["current_price", "price"], "null"));
2646
+ return taPure("gammaLevels", [vol, price, cellOf(pick(args, 2, ["period", "length"], "null"))]);
2647
+ }
2648
+ if (method === "intelligent_strategy_synthesizer") {
2649
+ return taCall("intelligentStrategySynthesizer", site, [
2650
+ arrOrCell(pick(args, 0, ["trend_indicators", "trend"], "null")),
2651
+ arrOrCell(pick(args, 1, ["momentum_indicators", "momentum"], "null")),
2652
+ arrOrCell(pick(args, 2, ["volatility_indicators", "volatility"], "null")),
2653
+ arrOrCell(pick(args, 3, ["volume_indicators", "volume"], "null")),
2654
+ pick(args, 4, ["market_condition", "market_regime"], '"ranging"'),
2655
+ pick(args, 5, ["risk_profile"], '"balanced"'),
2656
+ ]);
2657
+ }
2658
+ if (method === "signal_confluence") {
2659
+ return taPure("signalConfluence", [pick(args, 0, ["signals"], "null")]);
2660
+ }
2469
2661
  return "null";
2470
2662
  }
@@ -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
  }
@@ -4648,13 +4673,226 @@ function evalExtraTa(fname: string | null, node: Call, env: Env, site: string):
4648
4673
  }
4649
4674
  if (fname === "ta.breakout_detection" || fname === "breakout_detection") {
4650
4675
  const msg = "ta.breakout_detection() requires 3 arguments";
4676
+ const arg = (i: number, names: string[]): Cell => {
4677
+ const e = wave10aArg(i, names, msg);
4678
+ return isSeriesExpr(e, env) ? NA : num(evalExpr(e, env));
4679
+ };
4651
4680
  return env.ta.breakoutDetection(
4652
- wave11n(0, ["close", "source"], msg),
4653
- wave11n(1, ["resistance"], msg),
4654
- wave11n(2, ["support"], msg),
4681
+ arg(0, ["close", "source"]),
4682
+ arg(1, ["resistance"]),
4683
+ arg(2, ["support"]),
4655
4684
  );
4656
4685
  }
4657
4686
  // wave12-end
4687
+ // wave13-start — history / sentiment / macro analytics
4688
+ if (fname === "ta.ema_cross_signal" || fname === "ema_cross_signal") {
4689
+ const msg = "ta.ema_cross_signal() requires 3 arguments";
4690
+ return env.ta.emaCrossSignal(
4691
+ site,
4692
+ wave11n(0, ["fast_ema", "fast"], msg),
4693
+ wave11n(1, ["slow_ema", "slow"], msg),
4694
+ wave11n(2, ["threshold"], msg),
4695
+ );
4696
+ }
4697
+ if (fname === "ta.momentum_filter" || fname === "momentum_filter") {
4698
+ const msg = "ta.momentum_filter() requires 3 arguments";
4699
+ return env.ta.momentumFilter(
4700
+ site,
4701
+ wave11n(0, ["momentum_raw", "momentum"], msg),
4702
+ wave11n(1, ["volume"], msg),
4703
+ wave11n(2, ["period", "length"], msg),
4704
+ );
4705
+ }
4706
+ if (fname === "ta.volatility_regime" || fname === "volatility_regime") {
4707
+ const msg = "ta.volatility_regime() requires 2 arguments";
4708
+ return env.ta.volatilityRegime(
4709
+ site,
4710
+ wave11n(0, ["atr_series", "atr", "source"], msg),
4711
+ wave11n(1, ["period", "length"], msg),
4712
+ );
4713
+ }
4714
+ if (fname === "ta.economic_impact_score" || fname === "economic_impact_score") {
4715
+ const msg = "ta.economic_impact_score() requires 3 arguments";
4716
+ return env.ta.economicImpactScore(
4717
+ wave11n(0, ["price_change"], msg),
4718
+ wave11n(1, ["volatility"], msg),
4719
+ wave11n(2, ["volume_change"], msg),
4720
+ );
4721
+ }
4722
+ if (fname === "ta.contrarian_signal" || fname === "contrarian_signal") {
4723
+ const msg = "ta.contrarian_signal() requires 3 arguments";
4724
+ return env.ta.contrarianSignal(
4725
+ wave11n(0, ["sentiment"], msg),
4726
+ wave11n(1, ["volatility"], msg),
4727
+ wave11n(2, ["time_since_extreme", "time_extreme"], msg),
4728
+ );
4729
+ }
4730
+ if (fname === "ta.drawdown_recovery_level" || fname === "drawdown_recovery_level") {
4731
+ const msg = "ta.drawdown_recovery_level() requires 4 arguments";
4732
+ return env.ta.drawdownRecoveryLevel(
4733
+ wave11n(0, ["peak"], msg),
4734
+ wave11n(1, ["current", "current_price"], msg),
4735
+ wave11n(2, ["recovery_factor", "factor"], msg),
4736
+ wave11n(3, ["period", "length"], msg),
4737
+ );
4738
+ }
4739
+ if (fname === "ta.pivot_point_levels" || fname === "pivot_point_levels") {
4740
+ const msg = "ta.pivot_point_levels() requires type+anchor or high, low, close";
4741
+ const first = wave11(0, ["type", "high"], msg);
4742
+ if (typeof first === "string") {
4743
+ // Reference Pine type+anchor form → context pivot-point tuple.
4744
+ if (typeof env.ta.pivotPoints !== "function") return NA;
4745
+ const r = (env.ta.pivotPoints as TaEngine["pivotPoints"]).call(
4746
+ env.ta,
4747
+ site,
4748
+ num(env.ctx.high),
4749
+ num(env.ctx.low),
4750
+ num(env.ctx.close),
4751
+ );
4752
+ return tupleOf([r.pp, r.r1, r.s1, r.r2, r.s2]);
4753
+ }
4754
+ const tradArg = callArg(node.args, 3, ["traditional"]);
4755
+ const traditional = tradArg == null ? true : unwrap(evalExpr(tradArg, env)) !== 0;
4756
+ return env.ta.pivotPointLevels(
4757
+ num(first),
4758
+ wave11n(1, ["low"], msg),
4759
+ wave11n(2, ["close"], msg),
4760
+ traditional,
4761
+ );
4762
+ }
4763
+ if (fname === "ta.volatility_regime_score" || fname === "volatility_regime_score") {
4764
+ const msg = "ta.volatility_regime_score() requires 4 arguments";
4765
+ return env.ta.volatilityRegimeScore(
4766
+ site,
4767
+ wave13v(0, ["atr_list", "atr"], msg),
4768
+ wave13v(1, ["volatility_list", "volatility"], msg),
4769
+ wave13v(2, ["vix_list", "vix"], msg),
4770
+ wave11n(3, ["threshold"], msg),
4771
+ );
4772
+ }
4773
+ if (fname === "ta.correlation_filter" || fname === "correlation_filter") {
4774
+ const msg = "ta.correlation_filter() requires 5 arguments";
4775
+ const s1 = wave13v(0, ["signal1_list", "signal1", "s1"], msg);
4776
+ const s2 = wave13v(1, ["signal2_list", "signal2", "s2"], msg);
4777
+ const s3 = wave13v(2, ["signal3_list", "signal3", "s3"], msg);
4778
+ wave11n(3, ["num_signals"], msg);
4779
+ return env.ta.correlationFilter(site, s1, s2, s3, wave11n(4, ["threshold"], msg));
4780
+ }
4781
+ if (fname === "ta.advanced_breakout_detector" || fname === "advanced_breakout_detector") {
4782
+ const msg = "ta.advanced_breakout_detector() requires 5 arguments";
4783
+ return env.ta.advancedBreakoutDetector(
4784
+ site,
4785
+ wave13v(0, ["price_list", "price"], msg),
4786
+ wave13v(1, ["volume_list", "volume"], msg),
4787
+ wave11n(2, ["level"], msg),
4788
+ wave11n(3, ["lookback"], msg),
4789
+ wave11n(4, ["volume_multiplier", "vol_mult"], msg),
4790
+ );
4791
+ }
4792
+ if (fname === "ta.pullback_bounce_level" || fname === "pullback_bounce_level") {
4793
+ const msg = "ta.pullback_bounce_level() requires 5 arguments";
4794
+ const high = wave13v(0, ["high_list", "high"], msg);
4795
+ const low = wave13v(1, ["low_list", "low"], msg);
4796
+ wave13v(2, ["close_list", "close"], msg);
4797
+ return env.ta.pullbackBounceLevel(
4798
+ site,
4799
+ high,
4800
+ low,
4801
+ wave11n(3, ["trend_direction", "trend_dir"], msg),
4802
+ wave11n(4, ["lookback"], msg),
4803
+ );
4804
+ }
4805
+ if (fname === "ta.divergence_detector" || fname === "divergence_detector") {
4806
+ const msg = "ta.divergence_detector() requires 3 arguments";
4807
+ return env.ta.divergenceDetector(
4808
+ site,
4809
+ wave13v(0, ["price_list", "price"], msg),
4810
+ wave13v(1, ["indicator_list", "indicator"], msg),
4811
+ wave11n(2, ["lookback"], msg),
4812
+ );
4813
+ }
4814
+ if (fname === "ta.momentum_divergence" || fname === "momentum_divergence") {
4815
+ const msg = "ta.momentum_divergence() requires 3 arguments";
4816
+ return env.ta.momentumDivergence(
4817
+ site,
4818
+ wave13v(0, ["price"], msg),
4819
+ wave13v(1, ["momentum_fast", "mom_fast"], msg),
4820
+ wave13v(2, ["momentum_slow", "mom_slow"], msg),
4821
+ );
4822
+ }
4823
+ if (fname === "ta.spread_analysis" || fname === "spread_analysis") {
4824
+ const msg = "ta.spread_analysis() requires 3 arguments";
4825
+ return env.ta.spreadAnalysis(
4826
+ site,
4827
+ wave13v(0, ["bid"], msg),
4828
+ wave13v(1, ["ask"], msg),
4829
+ wave11n(2, ["period", "length"], msg),
4830
+ );
4831
+ }
4832
+ if (fname === "ta.employment_cycle_indicator" || fname === "employment_cycle_indicator") {
4833
+ const msg = "ta.employment_cycle_indicator() requires 3 arguments";
4834
+ return env.ta.employmentCycleIndicator(
4835
+ site,
4836
+ wave13v(0, ["cyclical_stocks", "cyclical"], msg),
4837
+ wave13v(1, ["defensive_stocks", "defensive"], msg),
4838
+ wave13v(2, ["unemployment_proxy", "unemployment"], msg),
4839
+ );
4840
+ }
4841
+ if (fname === "ta.gdp_growth_proxy" || fname === "gdp_growth_proxy") {
4842
+ const msg = "ta.gdp_growth_proxy() requires 3 arguments";
4843
+ return env.ta.gdpGrowthProxy(
4844
+ site,
4845
+ wave13v(0, ["market_breadth", "breadth"], msg),
4846
+ wave13v(1, ["market_volume", "volume"], msg),
4847
+ wave13v(2, ["price_momentum", "momentum"], msg),
4848
+ );
4849
+ }
4850
+ if (fname === "ta.inflation_proxy_indicator" || fname === "inflation_proxy_indicator") {
4851
+ const msg = "ta.inflation_proxy_indicator() requires 3 arguments";
4852
+ return env.ta.inflationProxyIndicator(
4853
+ site,
4854
+ wave13v(0, ["usd_index", "usd"], msg),
4855
+ wave13v(1, ["commodity_prices", "commodities"], msg),
4856
+ wave13v(2, ["bond_yields", "yields"], msg),
4857
+ );
4858
+ }
4859
+ if (fname === "ta.expected_value" || fname === "expected_value") {
4860
+ const msg = "ta.expected_value() requires 2 arguments: returns, probabilities";
4861
+ const returns = numericList(evalExpr(wave10aArg(0, ["returns"], msg), env));
4862
+ const probs = numericList(evalExpr(wave10aArg(1, ["probabilities", "probs"], msg), env));
4863
+ if (returns.length !== probs.length) runtimeError("Returns and probabilities must have same length");
4864
+ return env.ta.expectedValue(returns, probs);
4865
+ }
4866
+ // wave13-end
4867
+ // wave14-start — list-return + capstone synthesis
4868
+ if (fname === "ta.gamma_levels" || fname === "gamma_levels") {
4869
+ const msg = "ta.gamma_levels() requires 3 arguments";
4870
+ const volArg = wave10aArg(0, ["volatility"], msg);
4871
+ const priceArg = wave10aArg(1, ["current_price", "price"], msg);
4872
+ const vol = isSeriesExpr(volArg, env) ? NA : num(evalExpr(volArg, env));
4873
+ const price = isSeriesExpr(priceArg, env) ? NA : num(evalExpr(priceArg, env));
4874
+ return env.ta.gammaLevels(vol, price, wave11n(2, ["period", "length"], msg));
4875
+ }
4876
+ if (
4877
+ fname === "ta.intelligent_strategy_synthesizer" ||
4878
+ fname === "intelligent_strategy_synthesizer"
4879
+ ) {
4880
+ const msg = "ta.intelligent_strategy_synthesizer() requires 6 arguments";
4881
+ return env.ta.intelligentStrategySynthesizer(
4882
+ site,
4883
+ wave13v(0, ["trend_indicators", "trend"], msg),
4884
+ wave13v(1, ["momentum_indicators", "momentum"], msg),
4885
+ wave13v(2, ["volatility_indicators", "volatility"], msg),
4886
+ wave13v(3, ["volume_indicators", "volume"], msg),
4887
+ wave13v(4, ["market_condition", "market_regime"], msg),
4888
+ wave13v(5, ["risk_profile"], msg),
4889
+ );
4890
+ }
4891
+ if (fname === "ta.signal_confluence" || fname === "signal_confluence") {
4892
+ const msg = "ta.signal_confluence() requires 1 argument";
4893
+ return env.ta.signalConfluence(wave13v(0, ["signals"], msg));
4894
+ }
4895
+ // wave14-end
4658
4896
  return undefined;
4659
4897
  }
4660
4898
 
@@ -4676,6 +4914,21 @@ function seriesLastCell(value: Value): number | null {
4676
4914
  return typeof v === "number" && Number.isFinite(v) ? v : null;
4677
4915
  }
4678
4916
 
4917
+ /** Python `_expect_list` for array-valued args: numeric cells only. */
4918
+ function numericList(value: Value): number[] {
4919
+ const arr = asArray(value);
4920
+ if (arr == null) {
4921
+ const v = unwrap(value);
4922
+ return typeof v === "number" && Number.isFinite(v) ? [v] : [];
4923
+ }
4924
+ const out: number[] = [];
4925
+ for (let i = 0; i < arr.size(); i++) {
4926
+ const v = arr.get(i);
4927
+ if (typeof v === "number" && Number.isFinite(v)) out.push(v);
4928
+ }
4929
+ return out;
4930
+ }
4931
+
4679
4932
  function asArray(value: Value): PineArray | null {
4680
4933
  if (value instanceof PineArray) return value;
4681
4934
  if (value && typeof value === "object" && "__array" in value) {