@hoox-sh/pynets 0.5.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/dist/index.js CHANGED
@@ -6287,6 +6287,7 @@ class PinescriptLexerBase extends cn {
6287
6287
  _lastPendingTokenType = 0;
6288
6288
  _lastPendingTokenTypeFromDefaultChannel = 0;
6289
6289
  _numOpens = 0;
6290
+ _numSqBrackets = 0;
6290
6291
  _indentLengthStack = [];
6291
6292
  _inputStarted = false;
6292
6293
  constructor(input) {
@@ -6340,6 +6341,7 @@ class PinescriptLexerBase extends cn {
6340
6341
  this._lastPendingTokenType = 0;
6341
6342
  this._lastPendingTokenTypeFromDefaultChannel = 0;
6342
6343
  this._numOpens = 0;
6344
+ this._numSqBrackets = 0;
6343
6345
  this._indentLengthStack = [];
6344
6346
  this._inputStarted = false;
6345
6347
  }
@@ -6356,11 +6358,17 @@ class PinescriptLexerBase extends cn {
6356
6358
  const tok = this._currentToken;
6357
6359
  const tokType = tok.type;
6358
6360
  const C = this.C;
6359
- if (tokType === C.LPAR || tokType === C.LSQB) {
6361
+ if (tokType === C.LPAR) {
6360
6362
  this._numOpens += 1;
6361
6363
  this._addPendingToken(tok);
6362
- } else if (tokType === C.RPAR || tokType === C.RSQB) {
6363
- this._numOpens -= 1;
6364
+ } else if (tokType === C.LSQB) {
6365
+ this._numSqBrackets += 1;
6366
+ this._addPendingToken(tok);
6367
+ } else if (tokType === C.RPAR) {
6368
+ this._numOpens = Math.max(0, this._numOpens - 1);
6369
+ this._addPendingToken(tok);
6370
+ } else if (tokType === C.RSQB) {
6371
+ this._numSqBrackets = Math.max(0, this._numSqBrackets - 1);
6364
6372
  this._addPendingToken(tok);
6365
6373
  } else if (tokType === C.NEWLINE) {
6366
6374
  this._handleNEWLINEToken();
@@ -6452,7 +6460,7 @@ class PinescriptLexerBase extends cn {
6452
6460
  return tok;
6453
6461
  }
6454
6462
  _handleNEWLINEToken() {
6455
- if (this._numOpens > 0 || this._operators.has(this._lastPendingTokenTypeFromDefaultChannel)) {
6463
+ if (this._numOpens > 0 || this._numSqBrackets > 0 || this._operators.has(this._lastPendingTokenTypeFromDefaultChannel)) {
6456
6464
  this._hideAndAddPendingToken(this._currentToken);
6457
6465
  return;
6458
6466
  }
@@ -36320,6 +36328,13 @@ function mathRandom(a, b, rng = Math.random) {
36320
36328
  // src/runtime/time.ts
36321
36329
  var MIN_T = -62167219200;
36322
36330
  var MAX_T = 253402300799;
36331
+ function timestampToMs(ts) {
36332
+ if (ts == null || !Number.isFinite(ts))
36333
+ return null;
36334
+ if (Math.abs(ts) < 100000000000)
36335
+ return ts * 1000;
36336
+ return ts;
36337
+ }
36323
36338
  function utcPartsFromMs(ms) {
36324
36339
  let t = Number.isFinite(ms) ? Math.trunc(ms / 1000) : 0;
36325
36340
  if (t < MIN_T)
@@ -37176,8 +37191,7 @@ function coerceFormatTimeMs(time) {
37176
37191
  if (typeof raw !== "number" || !Number.isFinite(raw))
37177
37192
  return null;
37178
37193
  let ts = Math.trunc(raw);
37179
- if (ts > 0 && ts < 10000000000)
37180
- ts *= 1000;
37194
+ ts = timestampToMs(ts) ?? ts;
37181
37195
  return ts;
37182
37196
  }
37183
37197
  function formatOffsetHours(offsetHours) {
@@ -37930,6 +37944,48 @@ class StrategyBook {
37930
37944
  bar: isFiniteNumber2(bar) ? bar : 0
37931
37945
  });
37932
37946
  }
37947
+ placeClose(bar, id, price, opts) {
37948
+ const b = isFiniteNumber2(bar) ? bar : 0;
37949
+ const oid = String(id ?? "");
37950
+ this.ensureSanePosition();
37951
+ if (this.position.qty !== 0) {
37952
+ const absPos = Math.abs(this.position.qty);
37953
+ const rawQty = opts?.qty;
37954
+ const q = isFiniteNumber2(rawQty) && rawQty >= 0 ? Math.min(Math.abs(rawQty), absPos) : absPos;
37955
+ if (q > 0) {
37956
+ this.upsertPending({
37957
+ id: `${oid}:close`,
37958
+ direction: this.position.qty > 0 ? "short" : "long",
37959
+ qty: q,
37960
+ limit: null,
37961
+ stop: null,
37962
+ bar: b,
37963
+ kind: "close",
37964
+ deferred: true,
37965
+ comment: opts?.comment
37966
+ });
37967
+ }
37968
+ }
37969
+ this.close(b, oid);
37970
+ }
37971
+ placeCloseAll(bar, price, opts) {
37972
+ const b = isFiniteNumber2(bar) ? bar : 0;
37973
+ this.ensureSanePosition();
37974
+ if (this.position.qty !== 0) {
37975
+ this.upsertPending({
37976
+ id: "close_all",
37977
+ direction: this.position.qty > 0 ? "short" : "long",
37978
+ qty: Math.abs(this.position.qty),
37979
+ limit: null,
37980
+ stop: null,
37981
+ bar: b,
37982
+ kind: "close_all",
37983
+ deferred: true,
37984
+ comment: opts?.comment
37985
+ });
37986
+ }
37987
+ this.events.push({ type: "close_all", id: "", bar: b });
37988
+ }
37933
37989
  exit(bar, id, qty) {
37934
37990
  const event = {
37935
37991
  type: "exit",
@@ -37967,10 +38023,46 @@ class StrategyBook {
37967
38023
  placeEntry(bar, id, direction, qty, opts) {
37968
38024
  const limit = optLevel(opts?.limit);
37969
38025
  const stop = optLevel(opts?.stop);
38026
+ const b = isFiniteNumber2(bar) ? bar : 0;
38027
+ const oid = String(id ?? "");
37970
38028
  if (limit == null && stop == null) {
38029
+ if (opts?.deferMarket === true) {
38030
+ const dir = mapDirection(direction);
38031
+ const absQty = isFiniteNumber2(qty) ? Math.abs(qty) : Number.NaN;
38032
+ if (!isFiniteNumber2(absQty) || absQty <= 0)
38033
+ return;
38034
+ const t = isFiniteNumber2(opts?.time) ? opts.time : b;
38035
+ if (!this.riskAllowsEntry(dir, t))
38036
+ return;
38037
+ const mark = isFiniteNumber2(opts?.price) ? opts.price : 0;
38038
+ const capped = this.capQtyByMaxPosition(absQty, mark);
38039
+ if (!isFiniteNumber2(capped) || capped <= 0)
38040
+ return;
38041
+ const signed = dir === "long" ? capped : -capped;
38042
+ if (this.position.qty !== 0 && Math.sign(this.position.qty) === Math.sign(signed)) {
38043
+ const maxEntries = this.pyramiding !== undefined ? this.pyramiding + 1 : 1;
38044
+ const allowed = this.pyramiding !== undefined && this.pyramiding > 0 && this.opentrades < maxEntries;
38045
+ if (!allowed)
38046
+ return;
38047
+ }
38048
+ this.upsertPending({
38049
+ id: oid,
38050
+ direction: dir,
38051
+ qty: capped,
38052
+ limit: null,
38053
+ stop: null,
38054
+ bar: b,
38055
+ kind: "entry",
38056
+ is_entry: true,
38057
+ deferred: true,
38058
+ comment: opts?.comment
38059
+ });
38060
+ this.events.push({ type: "order", id: oid, direction: dir, qty: capped, bar: b });
38061
+ return;
38062
+ }
37971
38063
  if (isFiniteNumber2(opts?.price)) {
37972
38064
  const dir = mapDirection(direction);
37973
- const t = isFiniteNumber2(opts?.time) ? opts.time : bar;
38065
+ const t = isFiniteNumber2(opts?.time) ? opts.time : b;
37974
38066
  this.updateEquityExtremes(opts.price);
37975
38067
  if (!this.riskAllowsEntry(dir, t))
37976
38068
  return;
@@ -37981,8 +38073,6 @@ class StrategyBook {
37981
38073
  }
37982
38074
  const dir = mapDirection(direction);
37983
38075
  const absQty = isFiniteNumber2(qty) ? Math.abs(qty) : Number.NaN;
37984
- const b = isFiniteNumber2(bar) ? bar : 0;
37985
- const oid = String(id ?? "");
37986
38076
  this.entry(b, oid, dir, isFiniteNumber2(absQty) ? absQty : qty);
37987
38077
  if (!isFiniteNumber2(absQty) || absQty === 0)
37988
38078
  return;
@@ -38032,6 +38122,23 @@ class StrategyBook {
38032
38122
  return;
38033
38123
  const t = isFiniteNumber2(opts?.time) ? opts.time : b;
38034
38124
  if (limitP == null && stopP == null && !hasTrail) {
38125
+ if (opts?.deferMarket === true) {
38126
+ const closeDir = isLong ? "short" : "long";
38127
+ this.upsertPending({
38128
+ id: `${oid}:close`,
38129
+ direction: closeDir,
38130
+ qty,
38131
+ limit: null,
38132
+ stop: null,
38133
+ bar: b,
38134
+ kind: "exit",
38135
+ from_entry: fromEntry,
38136
+ deferred: true,
38137
+ comment: opts?.comment
38138
+ });
38139
+ this.events.push({ type: "order", id: oid, direction: closeDir, qty, bar: b });
38140
+ return;
38141
+ }
38035
38142
  if (!isFiniteNumber2(opts?.price))
38036
38143
  return;
38037
38144
  this.reducePosition(b, oid, opts.price, qty, fromEntry, t, opts?.comment);
@@ -38102,13 +38209,24 @@ class StrategyBook {
38102
38209
  for (const order of this.pending.slice()) {
38103
38210
  if (!this.pending.some((p) => p.id === order.id))
38104
38211
  continue;
38212
+ if (order.bar === b)
38213
+ continue;
38105
38214
  if (order.kind === "exit" && isTrailOrder(order))
38106
38215
  updateTrailStop(order, high, low);
38107
- const px = triggerPrice(order, open, high, low);
38216
+ let px;
38217
+ if (order.limit == null && order.stop == null)
38218
+ px = open;
38219
+ else
38220
+ px = triggerPrice(order, open, high, low);
38108
38221
  if (px == null)
38109
38222
  continue;
38110
38223
  this.dropPending(order.id);
38111
- const applied = order.kind === "exit" ? this.applyExitFill(b, order, px) : this.applyEntryFill(b, order.id, order.direction, order.qty, px, false);
38224
+ const emitEntry = order.kind === "entry" || order.deferred === true;
38225
+ const barTime = isFiniteNumber2(ohlc.time) ? ohlc.time : undefined;
38226
+ const applied = order.kind === "exit" ? this.applyExitFill(b, order, px, barTime) : order.kind === "close" || order.kind === "close_all" ? this.applyCloseFill(b, order, px, barTime) : this.applyEntryFill(b, order.id, order.direction, order.qty, px, emitEntry, {
38227
+ comment: order.comment,
38228
+ time: barTime
38229
+ });
38112
38230
  if (applied) {
38113
38231
  const q = Math.abs(this.fills.at(-1)?.qty ?? order.qty);
38114
38232
  this.ocaAfterFill(order, q, b);
@@ -38124,6 +38242,22 @@ class StrategyBook {
38124
38242
  }
38125
38243
  return filled;
38126
38244
  }
38245
+ applyCloseFill(bar, order, price, time) {
38246
+ this.ensureSanePosition();
38247
+ if (this.position.qty === 0 || !isFiniteNumber2(price))
38248
+ return false;
38249
+ const absPos = Math.abs(this.position.qty);
38250
+ const rawQty = order.qty;
38251
+ const q = isFiniteNumber2(rawQty) && rawQty > 0 ? Math.min(Math.abs(rawQty), absPos) : absPos;
38252
+ if (!(q > 0))
38253
+ return false;
38254
+ if (q < absPos - 0.000000000001) {
38255
+ this.reducePosition(bar, order.id, price, q, order.from_entry ?? null, time, order.comment);
38256
+ } else {
38257
+ this.flattenAt(bar, order.id, price, time, order.comment);
38258
+ }
38259
+ return true;
38260
+ }
38127
38261
  fillEntry(bar, id, direction, qty, price, opts) {
38128
38262
  this.applyEntryFill(bar, id, direction, qty, price, true, opts);
38129
38263
  }
@@ -38246,9 +38380,9 @@ class StrategyBook {
38246
38380
  return { status: "invalid", qty: 0 };
38247
38381
  return { status: "ok", qty: Math.min(rawQty, target) };
38248
38382
  }
38249
- applyExitFill(bar, order, price) {
38383
+ applyExitFill(bar, order, price, time) {
38250
38384
  const t = isFiniteNumber2(bar) ? bar : 0;
38251
- return this.reducePosition(t, order.id, price, order.qty, order.from_entry ?? null, t);
38385
+ return this.reducePosition(t, order.id, price, order.qty, order.from_entry ?? null, time);
38252
38386
  }
38253
38387
  closeLeg(ot, bar, exitPrice, qty, profit, commission, exitTime, exitComment) {
38254
38388
  const closed = {
@@ -40255,6 +40389,37 @@ function pushCap(window2, value, cap) {
40255
40389
  function finiteOut2(value) {
40256
40390
  return Number.isFinite(value) ? value : null;
40257
40391
  }
40392
+ function smaOf(series, period) {
40393
+ if (period <= 0 || series.length < period)
40394
+ return null;
40395
+ const window2 = series.slice(series.length - period);
40396
+ let sum = 0;
40397
+ for (const v of window2) {
40398
+ if (v === null)
40399
+ return null;
40400
+ sum += v;
40401
+ }
40402
+ return sum / period;
40403
+ }
40404
+ function stdevOf(series, period) {
40405
+ if (period <= 1 || series.length < period)
40406
+ return null;
40407
+ const window2 = series.slice(series.length - period);
40408
+ const vals = [];
40409
+ for (const v of window2) {
40410
+ if (v === null)
40411
+ return null;
40412
+ vals.push(v);
40413
+ }
40414
+ let mean = 0;
40415
+ for (const v of vals)
40416
+ mean += v;
40417
+ mean /= period;
40418
+ let sumSq = 0;
40419
+ for (const v of vals)
40420
+ sumSq += (v - mean) ** 2;
40421
+ return Math.sqrt(sumSq / period);
40422
+ }
40258
40423
  var PERIOD_INT_EPS = 0.000000001;
40259
40424
  function pinePeriod(period) {
40260
40425
  if (!Number.isFinite(period) || period <= 0)
@@ -40353,6 +40518,26 @@ class TaEngine {
40353
40518
  emvSites = new Map;
40354
40519
  fractalSites = new Map;
40355
40520
  zigzagSites = new Map;
40521
+ engulfingSites = new Map;
40522
+ insideBarSites = new Map;
40523
+ doubleTopBottomSites = new Map;
40524
+ rsiDivergenceSites = new Map;
40525
+ stochSmoothSites = new Map;
40526
+ smaWeightedSites = new Map;
40527
+ betaSites = new Map;
40528
+ rSquaredSites = new Map;
40529
+ comovementSites = new Map;
40530
+ skewnessSites = new Map;
40531
+ kurtosisSites = new Map;
40532
+ vwmSites = new Map;
40533
+ meanReversionSites = new Map;
40534
+ marketConditionSites = new Map;
40535
+ orderFlowSites = new Map;
40536
+ liquiditySites = new Map;
40537
+ emaCrossSites = new Map;
40538
+ historySites = new Map;
40539
+ momentumFilterSites = new Map;
40540
+ volatilityRegimeSites = new Map;
40356
40541
  sma(site, source, period) {
40357
40542
  const n = pinePeriod(period);
40358
40543
  if (n === null)
@@ -40427,18 +40612,24 @@ class TaEngine {
40427
40612
  st = {
40428
40613
  period: n,
40429
40614
  prevClose: null,
40615
+ started: false,
40430
40616
  rma: { period: n, rma: null, seeded: false, started: false, seedBuf: [] }
40431
40617
  };
40432
40618
  this.atrSites.set(site, st);
40433
40619
  }
40434
- const prev = st.prevClose;
40435
- st.prevClose = finiteCell2(close);
40436
- if (prev === null)
40437
- return null;
40438
40620
  const h = finiteCell2(high);
40439
40621
  const l = finiteCell2(low);
40440
40622
  const c = finiteCell2(close);
40441
- if (h === null || l === null || c === null)
40623
+ if (!st.started) {
40624
+ st.started = true;
40625
+ st.prevClose = c;
40626
+ if (h === null || l === null)
40627
+ return null;
40628
+ return rmaStep(st.rma, h - l, n);
40629
+ }
40630
+ const prev = st.prevClose;
40631
+ st.prevClose = c;
40632
+ if (prev === null || h === null || l === null || c === null)
40442
40633
  return null;
40443
40634
  const tr = Math.max(h - l, Math.abs(h - prev), Math.abs(l - prev));
40444
40635
  return rmaStep(st.rma, tr, n);
@@ -40604,7 +40795,7 @@ class TaEngine {
40604
40795
  }
40605
40796
  if (st.window.length < n || st.count !== n)
40606
40797
  return null;
40607
- let v = (st.sumsq - st.sum * st.sum / n) / (n - 1);
40798
+ let v = (st.sumsq - st.sum * st.sum / n) / n;
40608
40799
  if (v < 0)
40609
40800
  v = 0;
40610
40801
  return Math.sqrt(v);
@@ -41810,7 +42001,7 @@ class TaEngine {
41810
42001
  }
41811
42002
  if (st.window.length < n || st.count !== n)
41812
42003
  return null;
41813
- let v = (st.sumsq - st.sum * st.sum / n) / (n - 1);
42004
+ let v = (st.sumsq - st.sum * st.sum / n) / n;
41814
42005
  if (v < 0)
41815
42006
  v = 0;
41816
42007
  return v;
@@ -42414,20 +42605,17 @@ class TaEngine {
42414
42605
  st = { samples: [] };
42415
42606
  this.zigzagSites.set(site, st);
42416
42607
  }
42417
- pushCap(st.samples, finiteCell2(source), 1000);
42418
- const finite = [];
42419
- for (const v of st.samples) {
42420
- if (v !== null)
42421
- finite.push(v);
42422
- }
42423
- if (finite.length < 2)
42608
+ const x = finiteCell2(source);
42609
+ if (x !== null)
42610
+ pushCap(st.samples, x, 2);
42611
+ if (st.samples.length < 2)
42424
42612
  return { high: null, low: null, dir: 0 };
42425
- const oldest = finite[0];
42426
- const second = finite[1];
42427
- const recentHigh = Math.max(oldest, second);
42428
- const recentLow = Math.min(oldest, second);
42613
+ const oldest = st.samples[0];
42614
+ const newest = st.samples[1];
42615
+ const recentHigh = Math.max(oldest, newest);
42616
+ const recentLow = Math.min(oldest, newest);
42429
42617
  const pct = recentLow !== 0 ? (recentHigh - recentLow) / recentLow * 100 : 0;
42430
- const direction = recentHigh === oldest ? 1 : -1;
42618
+ const direction = recentHigh === newest ? 1 : -1;
42431
42619
  return { high: recentHigh, low: recentLow, dir: pct > th ? 1 : direction };
42432
42620
  }
42433
42621
  accelSites = new Map;
@@ -42494,30 +42682,31 @@ class TaEngine {
42494
42682
  }
42495
42683
  accelerationFactor(site, source, period) {
42496
42684
  const p = this.coerceTaInt(period, "ta.acceleration_factor() requires 2 arguments");
42497
- let samples = this.accelSites.get(site);
42685
+ if (p <= 0)
42686
+ return 0;
42687
+ const key = `${site}:${p}`;
42688
+ let samples = this.accelSites.get(key);
42498
42689
  if (samples === undefined) {
42499
42690
  samples = [];
42500
- this.accelSites.set(site, samples);
42691
+ this.accelSites.set(key, samples);
42501
42692
  }
42502
- pushCap(samples, finiteCell2(source), 1000);
42503
- if (samples.length < p + 1 || p <= 0)
42693
+ pushCap(samples, finiteCell2(source), p + 1);
42694
+ if (samples.length < p + 1)
42504
42695
  return 0;
42505
- const oldest = samples.slice(0, p + 1);
42506
- const newestFirst = [];
42507
- for (let i = oldest.length - 1;i >= 0; i--) {
42508
- const v = oldest[i];
42696
+ const clean = [];
42697
+ for (const v of samples) {
42509
42698
  if (v !== null)
42510
- newestFirst.push(v);
42699
+ clean.push(v);
42511
42700
  }
42512
- if (newestFirst.length < 2)
42701
+ if (clean.length < 2)
42513
42702
  return 0;
42514
42703
  let oldSum = 0;
42515
- for (let i = 0;i < newestFirst.length - 1; i++)
42516
- oldSum += newestFirst[i];
42517
- const oldMom = oldSum / (newestFirst.length - 1);
42704
+ for (let i = 0;i < clean.length - 1; i++)
42705
+ oldSum += clean[i];
42706
+ const oldMom = oldSum / (clean.length - 1);
42707
+ const newMom = clean[clean.length - 1];
42518
42708
  if (Math.abs(oldMom) < 0.0000000001)
42519
42709
  return 0;
42520
- const newMom = newestFirst[newestFirst.length - 1];
42521
42710
  let acc = (newMom - oldMom) / oldMom;
42522
42711
  if (acc < -2)
42523
42712
  acc = -2;
@@ -42525,32 +42714,141 @@ class TaEngine {
42525
42714
  acc = 2;
42526
42715
  return finiteOut2(acc);
42527
42716
  }
42528
- stochSmooth(high, low, close, period, smoothK, smoothD) {
42717
+ stochSmooth(site, high, low, close, period, smoothK, smoothD) {
42529
42718
  const p = this.coerceTaInt(period, "ta.stoch_smooth period must be integer");
42530
42719
  const k = this.coerceTaInt(smoothK, "ta.stoch_smooth smooth_k must be integer");
42531
42720
  const d = this.coerceTaInt(smoothD, "ta.stoch_smooth smooth_d must be integer");
42532
- if (p <= 0)
42533
- throw new Error("max() iterable argument is empty");
42534
- if (high === null || low === null || close === null) {
42535
- throw new Error("unsupported operand type(s) for /: 'float' and 'NoneType'");
42536
- }
42537
- if (k !== 1 || d !== 1)
42721
+ if (p < 1 || k < 1 || d < 1)
42538
42722
  return null;
42539
- const span = high - low;
42540
- const stoch = span !== 0 ? 100 * (close - low) / span : 50;
42541
- return finiteOut2(stoch);
42542
- }
42543
- engulfing() {
42544
- return taRec({ is_bullish: false, is_bearish: false, pattern_strength: 0 });
42545
- }
42546
- insideBarPattern() {
42547
- return false;
42723
+ const key = `${site}:${p}:${k}:${d}`;
42724
+ let st = this.stochSmoothSites.get(key);
42725
+ if (st === undefined) {
42726
+ st = { highs: [], lows: [] };
42727
+ this.stochSmoothSites.set(key, st);
42728
+ }
42729
+ pushCap(st.highs, finiteCell2(high), p);
42730
+ pushCap(st.lows, finiteCell2(low), p);
42731
+ const c = finiteCell2(close);
42732
+ let stoch = null;
42733
+ if (c !== null && !st.highs.includes(null) && !st.lows.includes(null)) {
42734
+ let hh = -Infinity;
42735
+ let ll = Infinity;
42736
+ for (const v of st.highs) {
42737
+ if (v > hh)
42738
+ hh = v;
42739
+ }
42740
+ for (const v of st.lows) {
42741
+ if (v < ll)
42742
+ ll = v;
42743
+ }
42744
+ stoch = hh === ll ? 50 : 100 * (c - ll) / (hh - ll);
42745
+ }
42746
+ const kVal = this.ema(`${site}:k`, stoch, k);
42747
+ return this.ema(`${site}:d`, kVal, d);
42748
+ }
42749
+ engulfing(site, open, close) {
42750
+ const off = taRec({ is_bullish: false, is_bearish: false, pattern_strength: 0 });
42751
+ let st = this.engulfingSites.get(site);
42752
+ if (st === undefined) {
42753
+ st = { prevOpen: null, prevClose: null, count: 0 };
42754
+ this.engulfingSites.set(site, st);
42755
+ }
42756
+ const o = finiteCell2(open);
42757
+ const c = finiteCell2(close);
42758
+ st.count += 1;
42759
+ const prevO = st.prevOpen;
42760
+ const prevC = st.prevClose;
42761
+ if (st.count < 2 || prevO === null || prevC === null || o === null || c === null) {
42762
+ st.prevOpen = o;
42763
+ st.prevClose = c;
42764
+ return off;
42765
+ }
42766
+ const isBullish = o < prevC && c > prevO && c > o;
42767
+ const isBearish = o > prevC && c < prevO && c < o;
42768
+ let engulf = 0;
42769
+ if (isBullish) {
42770
+ engulf = Math.max(0, Math.min(1, (c - prevO) / Math.abs(prevO - prevC + 0.0001)));
42771
+ } else if (isBearish) {
42772
+ engulf = Math.max(0, Math.min(1, (prevO - c) / Math.abs(prevC - prevO + 0.0001)));
42773
+ }
42774
+ st.prevOpen = o;
42775
+ st.prevClose = c;
42776
+ return taRec({
42777
+ is_bullish: isBullish,
42778
+ is_bearish: isBearish,
42779
+ pattern_strength: finiteOut2(engulf) ?? 0
42780
+ });
42548
42781
  }
42549
- doubleTopBottom(period) {
42550
- if (period === null || !Number.isFinite(period)) {
42551
- throw new Error("period must be integer. Got: na");
42782
+ insideBarPattern(site, high, low) {
42783
+ let st = this.insideBarSites.get(site);
42784
+ if (st === undefined) {
42785
+ st = { prevHigh: null, prevLow: null, count: 0 };
42786
+ this.insideBarSites.set(site, st);
42787
+ }
42788
+ const currH = high !== null && Number.isFinite(high) ? high : 0;
42789
+ const currL = low !== null && Number.isFinite(low) ? low : 0;
42790
+ st.count += 1;
42791
+ const prevH = st.prevHigh;
42792
+ const prevL = st.prevLow;
42793
+ if (st.count < 2 || prevH === null || prevL === null) {
42794
+ st.prevHigh = currH;
42795
+ st.prevLow = currL;
42796
+ return false;
42797
+ }
42798
+ const val = currH < prevH && currL > prevL;
42799
+ st.prevHigh = currH;
42800
+ st.prevLow = currL;
42801
+ return val;
42802
+ }
42803
+ doubleTopBottom(site, high, low, period) {
42804
+ const n = this.coerceTaInt(period, "period must be integer");
42805
+ const off = taRec({ pattern_type: "none", strength: 0, breakout_level: 0 });
42806
+ if (n < 1)
42807
+ return off;
42808
+ const key = `${site}:${n}`;
42809
+ let st = this.doubleTopBottomSites.get(key);
42810
+ if (st === undefined) {
42811
+ st = { highs: [], lows: [], count: 0 };
42812
+ this.doubleTopBottomSites.set(key, st);
42813
+ }
42814
+ pushCap(st.highs, finiteCell2(high), n);
42815
+ pushCap(st.lows, finiteCell2(low), n);
42816
+ st.count += 1;
42817
+ if (st.count < n)
42818
+ return off;
42819
+ const recentHigh = [];
42820
+ for (const v of st.highs) {
42821
+ if (v !== null)
42822
+ recentHigh.push(v);
42823
+ }
42824
+ const recentLow = [];
42825
+ for (const v of st.lows) {
42826
+ if (v !== null)
42827
+ recentLow.push(v);
42828
+ }
42829
+ if (recentHigh.length < 3 || recentLow.length === 0)
42830
+ return off;
42831
+ const peaks = [recentHigh[0]];
42832
+ for (let i = 1;i < recentHigh.length - 1; i++) {
42833
+ if (recentHigh[i] > recentHigh[i - 1] && recentHigh[i] > recentHigh[i + 1]) {
42834
+ peaks.push(recentHigh[i]);
42835
+ }
42836
+ }
42837
+ if (peaks.length < 2)
42838
+ return off;
42839
+ const peakDiff = Math.abs(peaks[peaks.length - 1] - peaks[peaks.length - 2]);
42840
+ const avgPeak = (peaks[peaks.length - 1] + peaks[peaks.length - 2]) / 2;
42841
+ const strength = avgPeak > 0 ? 1 - Math.min(1, peakDiff / avgPeak) : 0;
42842
+ let minLow = recentLow[0];
42843
+ for (const v of recentLow) {
42844
+ if (v < minLow)
42845
+ minLow = v;
42552
42846
  }
42553
- return taRec({ pattern_type: "none", strength: 0, breakout_level: 0 });
42847
+ return taRec({
42848
+ pattern_type: "double_top",
42849
+ strength: finiteOut2(strength) ?? 0,
42850
+ breakout_level: finiteOut2(minLow - avgPeak * 0.1) ?? 0
42851
+ });
42554
42852
  }
42555
42853
  cumDeltaSites = new Map;
42556
42854
  volMomSites = new Map;
@@ -42571,6 +42869,39 @@ class TaEngine {
42571
42869
  const v = buf[-i - 1];
42572
42870
  return v === undefined ? null : v;
42573
42871
  }
42872
+ hist(site, value) {
42873
+ let h = this.historySites.get(site);
42874
+ if (h === undefined) {
42875
+ h = [];
42876
+ this.historySites.set(site, h);
42877
+ }
42878
+ h.unshift(finiteCell2(value));
42879
+ if (h.length > 1000)
42880
+ h.pop();
42881
+ return h;
42882
+ }
42883
+ windowFor(site, value) {
42884
+ if (Array.isArray(value)) {
42885
+ return value.map((v) => typeof v === "number" ? finiteCell2(v) : null);
42886
+ }
42887
+ if (value != null && typeof value === "object" && typeof value.size === "function" && typeof value.get === "function") {
42888
+ const arr = value;
42889
+ const out = [];
42890
+ const n = arr.size();
42891
+ for (let i = 0;i < n; i++) {
42892
+ const v = arr.get(i);
42893
+ out.push(typeof v === "number" ? finiteCell2(v) : null);
42894
+ }
42895
+ return out;
42896
+ }
42897
+ return this.hist(site, typeof value === "number" ? value : null);
42898
+ }
42899
+ static oldest(h) {
42900
+ return h.length === 0 ? null : h[h.length - 1];
42901
+ }
42902
+ static secondOldest(h) {
42903
+ return h.length < 2 ? null : h[h.length - 2];
42904
+ }
42574
42905
  voi(buy, sell) {
42575
42906
  const b = buy !== null && Number.isFinite(buy) ? buy : 0;
42576
42907
  const s = sell !== null && Number.isFinite(sell) ? sell : 0;
@@ -42603,96 +42934,91 @@ class TaEngine {
42603
42934
  }
42604
42935
  return taRec({ is_oversold: rsi < os, is_overbought: rsi > ob, rsi });
42605
42936
  }
42606
- rsiDivergence(series, period) {
42937
+ rsiDivergence(site, value, period) {
42607
42938
  const p = this.asCount(period);
42608
- if (p === null || series.length === 0)
42609
- return null;
42610
- let last = 0;
42611
- for (let i = 0;i < series.length; i++) {
42612
- if (i < p) {
42613
- last = 0;
42614
- continue;
42615
- }
42616
- const start = Math.max(0, i - p);
42617
- const vals = [];
42618
- for (let j = start;j <= i; j++) {
42619
- const v = series[j];
42620
- if (v !== null && Number.isFinite(v))
42621
- vals.push(v);
42622
- }
42623
- if (vals.length < 2) {
42624
- last = 0;
42625
- continue;
42626
- }
42627
- let rsiMin = vals[0];
42628
- let rsiMax = vals[0];
42629
- for (const v of vals) {
42630
- if (v < rsiMin)
42631
- rsiMin = v;
42632
- if (v > rsiMax)
42633
- rsiMax = v;
42634
- }
42635
- const cur = series[i];
42636
- const range = rsiMax - rsiMin;
42637
- if (cur === null || !Number.isFinite(cur) || range <= 0) {
42638
- last = 0;
42639
- continue;
42640
- }
42641
- last = (cur - rsiMin) / range * 2 - 1;
42939
+ if (p === null || p < 1)
42940
+ return 0;
42941
+ let st = this.rsiDivergenceSites.get(`${site}:${p}`);
42942
+ if (st === undefined) {
42943
+ st = { window: [], count: 0 };
42944
+ this.rsiDivergenceSites.set(`${site}:${p}`, st);
42945
+ }
42946
+ const x = finiteCell2(value);
42947
+ pushCap(st.window, x, p + 1);
42948
+ st.count += 1;
42949
+ if (st.count <= p)
42950
+ return 0;
42951
+ const vals = [];
42952
+ for (const v of st.window) {
42953
+ if (v !== null)
42954
+ vals.push(v);
42642
42955
  }
42643
- return finiteOut2(last);
42956
+ if (vals.length < 2 || x === null)
42957
+ return 0;
42958
+ let lo = vals[0];
42959
+ let hi = vals[0];
42960
+ for (const v of vals) {
42961
+ if (v < lo)
42962
+ lo = v;
42963
+ if (v > hi)
42964
+ hi = v;
42965
+ }
42966
+ const range = hi - lo;
42967
+ return range > 0 ? finiteOut2((x - lo) / range * 2 - 1) : 0;
42644
42968
  }
42645
42969
  cumulativeDelta(site, close, volume, period) {
42646
42970
  const n = this.asCount(period);
42647
- let st = this.cumDeltaSites.get(site);
42971
+ if (n === null || n <= 0)
42972
+ return 0;
42973
+ let st = this.cumDeltaSites.get(`${site}:${n}`);
42648
42974
  if (st === undefined) {
42649
- st = { close: [], volume: [] };
42650
- this.cumDeltaSites.set(site, st);
42975
+ st = { closes: [], volumes: [], count: 0 };
42976
+ this.cumDeltaSites.set(`${site}:${n}`, st);
42651
42977
  }
42652
- this.pushChrono(st.close, close);
42653
- this.pushChrono(st.volume, volume);
42654
- if (n === null || n <= 0 || st.close.length < n || st.volume.length < n)
42978
+ const c = finiteCell2(close);
42979
+ const v = volume !== null && Number.isFinite(volume) ? volume : 0;
42980
+ pushCap(st.closes, c, n + 1);
42981
+ pushCap(st.volumes, v, n);
42982
+ st.count += 1;
42983
+ if (st.count < n)
42655
42984
  return 0;
42656
42985
  let delta = 0;
42657
- for (let i = -n;i < 0; i++) {
42658
- const cRaw = this.nf(st.close, i);
42659
- const vRaw = this.nf(st.volume, i);
42660
- const c = cRaw !== null && Number.isFinite(cRaw) ? cRaw : 0;
42661
- const v = vRaw !== null && Number.isFinite(vRaw) ? vRaw : 0;
42662
- if (i > -n) {
42663
- const prevRaw = this.nf(st.close, i - 1);
42664
- const prev = prevRaw !== null && Number.isFinite(prevRaw) ? prevRaw : c;
42665
- if (c > prev)
42666
- delta += v;
42667
- else if (c < prev)
42668
- delta -= v;
42669
- }
42986
+ const offset = st.closes.length - st.volumes.length;
42987
+ for (let j = 0;j < st.volumes.length; j++) {
42988
+ const idx = offset + j;
42989
+ const cRaw = st.closes[idx] ?? null;
42990
+ const cur = cRaw ?? 0;
42991
+ const prevRaw = idx === 0 ? null : st.closes[idx - 1] ?? null;
42992
+ const prev = prevRaw ?? cur;
42993
+ const vol = st.volumes[j];
42994
+ if (cur > prev)
42995
+ delta += vol;
42996
+ else if (cur < prev)
42997
+ delta -= vol;
42670
42998
  }
42671
42999
  return finiteOut2(delta);
42672
43000
  }
42673
43001
  volumeMomentum(site, volume, period) {
42674
43002
  const n = this.asCount(period);
42675
- let st = this.volMomSites.get(site);
42676
- if (st === undefined) {
42677
- st = { volume: [] };
42678
- this.volMomSites.set(site, st);
42679
- }
42680
- this.pushChrono(st.volume, volume);
42681
- if (n === null || n <= 0 || st.volume.length < n + 1)
43003
+ if (n === null || n <= 0)
42682
43004
  return 0;
42683
- const clean = [];
42684
- for (let i = 0;i < n + 1; i++) {
42685
- const v = st.volume[i];
42686
- if (v !== null && Number.isFinite(v))
42687
- clean.push(v);
43005
+ let st = this.volMomSites.get(`${site}:${n}`);
43006
+ if (st === undefined) {
43007
+ st = { window: [] };
43008
+ this.volMomSites.set(`${site}:${n}`, st);
42688
43009
  }
42689
- if (clean.length < 2)
43010
+ if (volume !== null && Number.isFinite(volume))
43011
+ pushCap(st.window, volume, n + 1);
43012
+ if (st.window.length < n + 1)
42690
43013
  return 0;
42691
- const newVol = clean[0];
42692
- let sum = 0;
42693
- for (let i = 1;i < clean.length; i++)
42694
- sum += clean[i];
42695
- const oldVol = sum / (clean.length - 1);
43014
+ let oldSum = 0;
43015
+ for (let i = 0;i < n; i++)
43016
+ oldSum += st.window[i];
43017
+ let newSum = 0;
43018
+ for (let i = 1;i <= n; i++)
43019
+ newSum += st.window[i];
43020
+ const oldVol = oldSum / n;
43021
+ const newVol = newSum / n;
42696
43022
  if (oldVol === 0)
42697
43023
  return 0;
42698
43024
  let momentum = (newVol - oldVol) / oldVol * 100;
@@ -42702,6 +43028,1167 @@ class TaEngine {
42702
43028
  momentum = -100;
42703
43029
  return finiteOut2(momentum);
42704
43030
  }
43031
+ smaWeighted(site, source, period, weightType) {
43032
+ const n = this.coerceTaInt(period, "period must be integer");
43033
+ const kind = typeof weightType === "string" ? weightType : "linear";
43034
+ if (n < 1)
43035
+ return null;
43036
+ const key = `${site}:${n}:${kind}`;
43037
+ let st = this.smaWeightedSites.get(key);
43038
+ if (st === undefined) {
43039
+ const weights = [];
43040
+ for (let i = 0;i < n; i++) {
43041
+ if (kind === "quadratic")
43042
+ weights.push((i + 1) ** 2);
43043
+ else if (kind === "sqrt")
43044
+ weights.push((i + 1) ** 0.5);
43045
+ else
43046
+ weights.push(i + 1);
43047
+ }
43048
+ st = { window: [], weights, wsum: weights.reduce((a, b) => a + b, 0) };
43049
+ this.smaWeightedSites.set(key, st);
43050
+ }
43051
+ pushCap(st.window, finiteCell2(source), n);
43052
+ if (st.window.length < n || st.window.includes(null) || st.wsum <= 0)
43053
+ return null;
43054
+ let total = 0;
43055
+ for (let i = 0;i < n; i++)
43056
+ total += st.window[i] * st.weights[i];
43057
+ return finiteOut2(total / st.wsum);
43058
+ }
43059
+ apo(site, source, fast, slow) {
43060
+ const f = this.coerceTaInt(fast, "ta.apo fast_period must be integer");
43061
+ const s = this.coerceTaInt(slow, "ta.apo slow_period must be integer");
43062
+ const fastV = this.ema(`${site}:fast`, source, f);
43063
+ const slowV = this.ema(`${site}:slow`, source, s);
43064
+ if (fastV === null || slowV === null)
43065
+ return null;
43066
+ return finiteOut2(fastV - slowV);
43067
+ }
43068
+ atrNormalized(site, high, low, close, period) {
43069
+ const n = this.coerceTaInt(period, "period must be integer");
43070
+ const atr = this.atr(`${site}:atr`, high, low, close, n);
43071
+ const c = finiteCell2(close);
43072
+ if (c === null || c === 0 || atr === null)
43073
+ return null;
43074
+ return finiteOut2(atr / c * 100);
43075
+ }
43076
+ pairWindow(sites, site, a, b, length) {
43077
+ const n = this.coerceTaInt(length, "length must be integer");
43078
+ if (n < 1)
43079
+ return null;
43080
+ const key = `${site}:${n}`;
43081
+ let st = sites.get(key);
43082
+ if (st === undefined) {
43083
+ st = { a: [], b: [] };
43084
+ sites.set(key, st);
43085
+ }
43086
+ pushCap(st.a, finiteCell2(a), n);
43087
+ pushCap(st.b, finiteCell2(b), n);
43088
+ if (st.a.length < n || st.b.length < n)
43089
+ return null;
43090
+ const left = [];
43091
+ const right = [];
43092
+ for (let i = 0;i < n; i++) {
43093
+ const av = st.a[i];
43094
+ const bv = st.b[i];
43095
+ if (av === null || bv === null)
43096
+ return null;
43097
+ left.push(av);
43098
+ right.push(bv);
43099
+ }
43100
+ return { a: left, b: right };
43101
+ }
43102
+ beta(site, a, b, length) {
43103
+ const pair = this.pairWindow(this.betaSites, site, a, b, length);
43104
+ if (pair === null)
43105
+ return null;
43106
+ const n = pair.a.length;
43107
+ let meanA = 0;
43108
+ let meanB = 0;
43109
+ for (let i = 0;i < n; i++) {
43110
+ meanA += pair.a[i];
43111
+ meanB += pair.b[i];
43112
+ }
43113
+ meanA /= n;
43114
+ meanB /= n;
43115
+ let covariance = 0;
43116
+ let varianceB = 0;
43117
+ for (let i = 0;i < n; i++) {
43118
+ covariance += (pair.a[i] - meanA) * (pair.b[i] - meanB);
43119
+ varianceB += (pair.b[i] - meanB) ** 2;
43120
+ }
43121
+ covariance /= n;
43122
+ varianceB /= n;
43123
+ if (varianceB === 0)
43124
+ return 0;
43125
+ return finiteOut2(covariance / varianceB);
43126
+ }
43127
+ rSquared(site, a, b, length) {
43128
+ const pair = this.pairWindow(this.rSquaredSites, site, a, b, length);
43129
+ if (pair === null)
43130
+ return null;
43131
+ const n = pair.a.length;
43132
+ let meanA = 0;
43133
+ let meanB = 0;
43134
+ for (let i = 0;i < n; i++) {
43135
+ meanA += pair.a[i];
43136
+ meanB += pair.b[i];
43137
+ }
43138
+ meanA /= n;
43139
+ meanB /= n;
43140
+ let covariance = 0;
43141
+ let varA = 0;
43142
+ let varB = 0;
43143
+ for (let i = 0;i < n; i++) {
43144
+ covariance += (pair.a[i] - meanA) * (pair.b[i] - meanB);
43145
+ varA += (pair.a[i] - meanA) ** 2;
43146
+ varB += (pair.b[i] - meanB) ** 2;
43147
+ }
43148
+ covariance /= n;
43149
+ varA /= n;
43150
+ varB /= n;
43151
+ if (varA === 0 || varB === 0)
43152
+ return 0;
43153
+ const corr = covariance / Math.sqrt(varA * varB);
43154
+ return finiteOut2(Math.max(0, Math.min(1, corr ** 2)));
43155
+ }
43156
+ comovement(site, a, b, length) {
43157
+ const pair = this.pairWindow(this.comovementSites, site, a, b, length);
43158
+ if (pair === null)
43159
+ return null;
43160
+ const n = pair.a.length;
43161
+ if (n <= 1)
43162
+ return 0;
43163
+ let same = 0;
43164
+ for (let i = 1;i < n; i++) {
43165
+ if ((pair.a[i] - pair.a[i - 1]) * (pair.b[i] - pair.b[i - 1]) > 0)
43166
+ same += 1;
43167
+ }
43168
+ return finiteOut2(same / (n - 1) * 100);
43169
+ }
43170
+ skewness(site, source, period) {
43171
+ const n = this.coerceTaInt(period, "period must be integer");
43172
+ if (n < 1)
43173
+ return null;
43174
+ const key = `${site}:${n}`;
43175
+ let st = this.skewnessSites.get(key);
43176
+ if (st === undefined) {
43177
+ st = { window: [] };
43178
+ this.skewnessSites.set(key, st);
43179
+ }
43180
+ pushCap(st.window, finiteCell2(source), n);
43181
+ if (st.window.length < n || st.window.includes(null))
43182
+ return null;
43183
+ const vals = st.window;
43184
+ let mean = 0;
43185
+ for (const v of vals)
43186
+ mean += v;
43187
+ mean /= n;
43188
+ let variance = 0;
43189
+ for (const v of vals)
43190
+ variance += (v - mean) ** 2;
43191
+ variance /= n;
43192
+ const std = Math.sqrt(variance);
43193
+ if (std === 0)
43194
+ return 0;
43195
+ let third = 0;
43196
+ for (const v of vals)
43197
+ third += (v - mean) ** 3;
43198
+ return finiteOut2(third / (n * std ** 3));
43199
+ }
43200
+ kurtosis(site, source, period) {
43201
+ const n = this.coerceTaInt(period, "period must be integer");
43202
+ if (n < 1)
43203
+ return null;
43204
+ const key = `${site}:${n}`;
43205
+ let st = this.kurtosisSites.get(key);
43206
+ if (st === undefined) {
43207
+ st = { window: [] };
43208
+ this.kurtosisSites.set(key, st);
43209
+ }
43210
+ pushCap(st.window, finiteCell2(source), n);
43211
+ if (st.window.length < n || st.window.includes(null))
43212
+ return null;
43213
+ const vals = st.window;
43214
+ let mean = 0;
43215
+ for (const v of vals)
43216
+ mean += v;
43217
+ mean /= n;
43218
+ let variance = 0;
43219
+ for (const v of vals)
43220
+ variance += (v - mean) ** 2;
43221
+ variance /= n;
43222
+ const std = Math.sqrt(variance);
43223
+ if (std === 0)
43224
+ return 0;
43225
+ let fourth = 0;
43226
+ for (const v of vals)
43227
+ fourth += (v - mean) ** 4;
43228
+ fourth /= n;
43229
+ return finiteOut2(fourth / std ** 4 - 3);
43230
+ }
43231
+ volumeWeightedMomentum(site, source, volume, length) {
43232
+ const n = this.coerceTaInt(length, "length must be integer");
43233
+ if (n < 1)
43234
+ return null;
43235
+ const key = `${site}:${n}`;
43236
+ let st = this.vwmSites.get(key);
43237
+ if (st === undefined) {
43238
+ st = { prices: [], volumes: [] };
43239
+ this.vwmSites.set(key, st);
43240
+ }
43241
+ pushCap(st.prices, finiteCell2(source), n + 1);
43242
+ pushCap(st.volumes, finiteCell2(volume), n);
43243
+ if (st.volumes.length < n)
43244
+ return null;
43245
+ const ps = st.prices;
43246
+ const vs = st.volumes;
43247
+ let momentumSum = 0;
43248
+ let volumeSum = 0;
43249
+ for (let i = 0;i < n; i++) {
43250
+ const pEnd = ps.length - 1 - i;
43251
+ const pPrev = pEnd - 1;
43252
+ if (pPrev < 0)
43253
+ break;
43254
+ const pv = ps[pEnd] ?? null;
43255
+ const pp = ps[pPrev] ?? null;
43256
+ if (pv === null || pp === null)
43257
+ return null;
43258
+ const vol = vs[vs.length - 1 - i] ?? 0;
43259
+ momentumSum += (pv - pp) * vol;
43260
+ volumeSum += vol;
43261
+ }
43262
+ return finiteOut2(volumeSum > 0 ? momentumSum / volumeSum : 0);
43263
+ }
43264
+ parkinson(high, low) {
43265
+ const h = finiteCell2(high);
43266
+ const l = finiteCell2(low);
43267
+ if (h === null || l === null || h <= l)
43268
+ return null;
43269
+ const ratio = h / l;
43270
+ if (ratio <= 0)
43271
+ return null;
43272
+ return finiteOut2(Math.sqrt(Math.log(ratio) ** 2 / (4 * Math.log(2))));
43273
+ }
43274
+ garmanKlass(high, low, close, open) {
43275
+ const h = finiteCell2(high);
43276
+ const l = finiteCell2(low);
43277
+ const c = finiteCell2(close);
43278
+ const o = finiteCell2(open);
43279
+ if (h === null || l === null || c === null || o === null)
43280
+ return null;
43281
+ if (h <= l || h <= 0 || c <= 0)
43282
+ return null;
43283
+ const term1 = 0.5 * Math.log(h / l) ** 2;
43284
+ const term2 = (2 * Math.log(2) - 1) * Math.log(c / o) ** 2;
43285
+ return finiteOut2(Math.sqrt(term1 - term2));
43286
+ }
43287
+ meanReversionScore(site, close, sma, stdev, period) {
43288
+ const n = this.coerceTaInt(period, "ta.mean_reversion_score() requires 4 arguments");
43289
+ if (n <= 0)
43290
+ return 50;
43291
+ let st = this.meanReversionSites.get(`${site}:${n}`);
43292
+ if (st === undefined) {
43293
+ st = { countClose: 0, countSma: 0 };
43294
+ this.meanReversionSites.set(`${site}:${n}`, st);
43295
+ }
43296
+ st.countClose += 1;
43297
+ st.countSma += 1;
43298
+ if (st.countClose < n || st.countSma < n)
43299
+ return 50;
43300
+ const c = finiteCell2(close) ?? 100;
43301
+ const s = finiteCell2(sma) ?? 100;
43302
+ const sd = finiteCell2(stdev) ?? 1;
43303
+ if (sd === 0)
43304
+ return 50;
43305
+ return Math.max(0, Math.min(100, Math.min(100, Math.abs(c - s) / sd * 20)));
43306
+ }
43307
+ marketCondition(site, close, atr, smaPeriod, stdevPeriod) {
43308
+ const smaN = this.coerceTaInt(smaPeriod, "sma period must be integer");
43309
+ const stdevN = this.coerceTaInt(stdevPeriod, "stdev period must be integer");
43310
+ if (smaN <= 0 || stdevN <= 0)
43311
+ return "ranging";
43312
+ const win = Math.max(smaN, stdevN);
43313
+ const key = `${site}:${smaN}:${stdevN}`;
43314
+ let st = this.marketConditionSites.get(key);
43315
+ if (st === undefined) {
43316
+ st = { window: [], atr: null };
43317
+ this.marketConditionSites.set(key, st);
43318
+ }
43319
+ pushCap(st.window, finiteCell2(close), win);
43320
+ st.atr = finiteCell2(atr);
43321
+ if (st.window.length < win)
43322
+ return "ranging";
43323
+ const cLast = st.window[st.window.length - 1] ?? null;
43324
+ const currentClose = cLast ?? 0;
43325
+ const currentAtr = st.atr ?? 1;
43326
+ const currentSma = smaOf(st.window, smaN) ?? currentClose;
43327
+ const stdevVal = stdevOf(st.window, stdevN);
43328
+ if (stdevVal !== null && stdevVal > currentAtr * 1.5)
43329
+ return "volatile";
43330
+ if (currentClose > currentSma && currentAtr > 0.5)
43331
+ return "trending_up";
43332
+ if (currentClose < currentSma && currentAtr > 0.5)
43333
+ return "trending_down";
43334
+ return "ranging";
43335
+ }
43336
+ orderFlowImbalance(site, high, low, close, volume, period) {
43337
+ const n = this.coerceTaInt(period, "period must be integer");
43338
+ if (n <= 0)
43339
+ return 0;
43340
+ const key = `${site}:${n}`;
43341
+ let st = this.orderFlowSites.get(key);
43342
+ if (st === undefined) {
43343
+ st = { highs: [], lows: [], closes: [], volumes: [] };
43344
+ this.orderFlowSites.set(key, st);
43345
+ }
43346
+ pushCap(st.highs, finiteCell2(high), n);
43347
+ pushCap(st.lows, finiteCell2(low), n);
43348
+ pushCap(st.closes, finiteCell2(close), n);
43349
+ pushCap(st.volumes, finiteCell2(volume), n);
43350
+ if (st.highs.length < n || st.lows.length < n)
43351
+ return 0;
43352
+ let buy = 0;
43353
+ let sell = 0;
43354
+ for (let i = 0;i < n; i++) {
43355
+ const h = st.highs[i] ?? 0;
43356
+ const l = st.lows[i] ?? 0;
43357
+ const c = st.closes[i] ?? 0;
43358
+ const v = st.volumes[i] ?? 0;
43359
+ if (h > l) {
43360
+ const mid = (h + l) / 2;
43361
+ if (c > mid)
43362
+ buy += v;
43363
+ else
43364
+ sell += v;
43365
+ }
43366
+ }
43367
+ const total = buy + sell;
43368
+ if (total === 0)
43369
+ return 0;
43370
+ return finiteOut2((buy - sell) / total) ?? 0;
43371
+ }
43372
+ liquidityScore(site, volume, volatility, spread, period) {
43373
+ const n = this.coerceTaInt(period, "period must be integer");
43374
+ if (n <= 0)
43375
+ return 50;
43376
+ const key = `${site}:${n}`;
43377
+ let st = this.liquiditySites.get(key);
43378
+ if (st === undefined) {
43379
+ st = { volume: [], volatility: [], spread: [] };
43380
+ this.liquiditySites.set(key, st);
43381
+ }
43382
+ pushCap(st.volume, finiteCell2(volume), n);
43383
+ pushCap(st.volatility, finiteCell2(volatility), n);
43384
+ pushCap(st.spread, finiteCell2(spread), n);
43385
+ if (st.volume.length < n || st.volatility.length < n || st.spread.length < n)
43386
+ return 50;
43387
+ let volSum = 0;
43388
+ for (const v of st.volume)
43389
+ if (v !== null)
43390
+ volSum += v;
43391
+ let volaSum = 0;
43392
+ for (const v of st.volatility)
43393
+ if (v !== null)
43394
+ volaSum += v;
43395
+ let spreadSum = 0;
43396
+ for (const v of st.spread)
43397
+ if (v !== null)
43398
+ spreadSum += v;
43399
+ const volScore = Math.min(100, volSum / n / 100);
43400
+ const volatilityScore = Math.max(0, 100 - volaSum / n * 50);
43401
+ const spreadScore = Math.max(0, 100 - spreadSum / n * 100);
43402
+ const liquidity = volScore * 0.4 + volatilityScore * 0.3 + spreadScore * 0.3;
43403
+ return Math.max(0, Math.min(100, liquidity));
43404
+ }
43405
+ fearGreedIndex(rsi, vix, putCall, breadth) {
43406
+ const r = finiteCell2(rsi) ?? 50;
43407
+ const v = finiteCell2(vix) ?? 2;
43408
+ const pc = finiteCell2(putCall) ?? 1;
43409
+ const b = finiteCell2(breadth) ?? 0.5;
43410
+ const index = ((r - 50) * 1 + (v - 2) * 10 + (1 - pc) * 50 + (b - 0.5) * 100) / 4;
43411
+ return Math.max(-100, Math.min(100, index));
43412
+ }
43413
+ crowdSentiment(priceAgreement, volumeAgreement, timeAgreement) {
43414
+ const p = finiteCell2(priceAgreement) ?? 0.5;
43415
+ const v = finiteCell2(volumeAgreement) ?? 0.5;
43416
+ const t = finiteCell2(timeAgreement) ?? 0.5;
43417
+ return Math.max(0, Math.min(100, (p + v + t) / 3 * 100));
43418
+ }
43419
+ smartMoneyFlow(priceChange, volume, timeHigh, timeLow) {
43420
+ const p = finiteCell2(priceChange) ?? 0;
43421
+ const v = finiteCell2(volume) ?? 1000;
43422
+ const th = finiteCell2(timeHigh) ?? 10;
43423
+ const tl = finiteCell2(timeLow) ?? 10;
43424
+ const volFactor = Math.min(1, v / 5000);
43425
+ let flow = 0;
43426
+ if (p > 0 && th < tl)
43427
+ flow = volFactor * 0.8;
43428
+ else if (p < 0 && tl < th)
43429
+ flow = -volFactor * 0.8;
43430
+ return Math.max(-1, Math.min(1, flow));
43431
+ }
43432
+ trendConfirmationScore(momentum, trendAlignment, strength, rsi, rsiAlignment) {
43433
+ const m = finiteCell2(momentum) ?? 0;
43434
+ const t = finiteCell2(trendAlignment) ?? 0;
43435
+ const s = finiteCell2(strength) ?? 1;
43436
+ const r = finiteCell2(rsi) ?? 50;
43437
+ const a = finiteCell2(rsiAlignment) ?? 0;
43438
+ const momentumScore = Math.min(100, Math.abs(m) * 20);
43439
+ const trendScore = Math.max(0, (t + 1) / 2 * 100);
43440
+ const strengthScore = Math.min(100, s * 50);
43441
+ const rsiScore = Math.abs(r - 50) > 10 ? Math.min(100, Math.abs(r - 50) * 2) : 40;
43442
+ const alignmentBonus = Math.abs(a) > 0.5 ? 20 : 0;
43443
+ const total = momentumScore * 0.25 + trendScore * 0.3 + strengthScore * 0.25 + rsiScore * 0.15 + alignmentBonus;
43444
+ return Math.max(0, Math.min(100, total));
43445
+ }
43446
+ maxLossLevel(entry, account, maxLossPct) {
43447
+ const e = finiteCell2(entry) ?? 100;
43448
+ const a = finiteCell2(account) ?? 1e4;
43449
+ const p = finiteCell2(maxLossPct) ?? 1;
43450
+ const maxLossAmount = a * (p / 100);
43451
+ if (e <= 0)
43452
+ return 0;
43453
+ const shares = a / e;
43454
+ const stop = shares > 0 ? e - maxLossAmount / shares : 0;
43455
+ return Math.max(0, stop);
43456
+ }
43457
+ profitLockLevel(entry, current, trailPct, direction) {
43458
+ const e = finiteCell2(entry) ?? 100;
43459
+ const c = finiteCell2(current) ?? 100;
43460
+ const t = Math.max(0, Math.min(1, finiteCell2(trailPct) ?? 0.05));
43461
+ const d = finiteCell2(direction) ?? 1;
43462
+ return d > 0 ? Math.max(e * 0.9, c - c * t) : Math.min(e * 1.1, c + c * t);
43463
+ }
43464
+ positionSizing(account, riskPct, entry, stop) {
43465
+ const a = finiteCell2(account) ?? 1e4;
43466
+ const r = finiteCell2(riskPct) ?? 0.01;
43467
+ const e = finiteCell2(entry) ?? 100;
43468
+ const s = finiteCell2(stop) ?? 95;
43469
+ const riskAmount = a * (r / 100);
43470
+ const distance = e - s;
43471
+ if (Math.abs(distance) < 0.0000000001)
43472
+ return 0;
43473
+ return Math.max(0, riskAmount / Math.abs(distance));
43474
+ }
43475
+ kellyCriterion(winRate, avgWin, avgLoss) {
43476
+ const wr = Math.max(0, Math.min(1, finiteCell2(winRate) ?? 0.5));
43477
+ const win = finiteCell2(avgWin) ?? 1;
43478
+ const loss = finiteCell2(avgLoss) ?? 1;
43479
+ if (Math.abs(win) < 0.0000000001)
43480
+ return 0;
43481
+ const kelly = (wr * win - (1 - wr) * loss) / win;
43482
+ return Math.max(0, kelly);
43483
+ }
43484
+ riskRewardRatio(entry, stop, target) {
43485
+ const e = finiteCell2(entry) ?? 0;
43486
+ const s = finiteCell2(stop) ?? 0;
43487
+ const t = finiteCell2(target) ?? 0;
43488
+ const risk = e - s;
43489
+ if (Math.abs(risk) < 0.0000000001)
43490
+ return null;
43491
+ return finiteOut2((t - e) / risk);
43492
+ }
43493
+ riskRewardAsymmetry(entry, stop, target, winRate) {
43494
+ const e = finiteCell2(entry) ?? 0;
43495
+ const s = finiteCell2(stop) ?? 0;
43496
+ const t = finiteCell2(target) ?? 0;
43497
+ const wr = finiteCell2(winRate) ?? 0.5;
43498
+ const risk = Math.abs(e - s);
43499
+ const reward = Math.abs(t - e);
43500
+ const ratio = risk > 0 ? reward / risk : 0;
43501
+ const ev = wr * reward - (1 - wr) * risk;
43502
+ const kelly = ratio > 0 ? wr - (1 - wr) / ratio : 0;
43503
+ return taRec({
43504
+ ratio: finiteOut2(ratio) ?? 0,
43505
+ expected_value: finiteOut2(ev) ?? 0,
43506
+ kelly_percent: finiteOut2(kelly * 100) ?? 0,
43507
+ is_favorable: ev > 0,
43508
+ risk_reward_ratio: finiteOut2(ratio) ?? 0,
43509
+ kelly_percentage: finiteOut2(kelly * 100) ?? 0
43510
+ });
43511
+ }
43512
+ probabilityOfMovement(current, target, atr, period) {
43513
+ const c = finiteCell2(current) ?? 100;
43514
+ const t = finiteCell2(target) ?? 100;
43515
+ const a = finiteCell2(atr) ?? 1;
43516
+ const n = this.coerceTaInt(period, "period must be integer");
43517
+ if (Math.abs(c) < 0.0000000001 || Math.abs(a) < 0.0000000001)
43518
+ return 0.5;
43519
+ const distance = Math.abs(t - c);
43520
+ const expected = a * Math.sqrt(n);
43521
+ if (expected === 0)
43522
+ return 0.5;
43523
+ return Math.max(0, Math.min(1, Math.min(1, distance / expected) * 0.8 + 0.1));
43524
+ }
43525
+ trendStrength(price, adx, rsi) {
43526
+ const a = finiteCell2(adx) ?? 20;
43527
+ const r = finiteCell2(rsi) ?? 50;
43528
+ const adxNorm = Math.min(100, Math.max(0, a));
43529
+ const rsiExtremeness = Math.abs(r - 50) / 50;
43530
+ return Math.min(100, Math.max(0, adxNorm * 0.6 + rsiExtremeness * 40));
43531
+ }
43532
+ breakevenLevel(entry, qty, commission, slippage, direction) {
43533
+ const e = finiteCell2(entry) ?? 0;
43534
+ const q = finiteCell2(qty) ?? 0;
43535
+ const comm = finiteCell2(commission) ?? 0;
43536
+ const slip = finiteCell2(slippage) ?? 0;
43537
+ const d = finiteCell2(direction) ?? 1;
43538
+ const cost = comm + slip * q;
43539
+ const impact = q > 0 ? cost / q : 0;
43540
+ const breakeven = d >= 0 ? e + impact : e - impact;
43541
+ const move = e > 0 ? Math.abs(breakeven - e) / e * 100 : 0;
43542
+ return taRec({
43543
+ breakeven_price: finiteOut2(breakeven) ?? 0,
43544
+ total_cost: finiteOut2(cost) ?? 0,
43545
+ move_required_percent: finiteOut2(move) ?? 0
43546
+ });
43547
+ }
43548
+ marketStructurePivot(site, high, low, period, mode) {
43549
+ const hh = this.windowFor(`${site}:high`, high);
43550
+ const ll = this.windowFor(`${site}:low`, low);
43551
+ const n = this.coerceTaInt(period, "ta.market_structure_pivot() requires 5 arguments");
43552
+ const m = this.coerceTaInt(mode, "ta.market_structure_pivot() requires 5 arguments");
43553
+ const off = taRec({ pivot_price: 100, strength: 50, structure: "neutral" });
43554
+ if (hh.length === 0 || ll.length === 0 || hh.length < n || n <= 0)
43555
+ return off;
43556
+ const hVals = hh.slice(-n).filter((v) => v !== null);
43557
+ const lVals = ll.slice(-n).filter((v) => v !== null);
43558
+ if (hVals.length === 0 || lVals.length === 0)
43559
+ return off;
43560
+ let pivotHigh = hVals[0];
43561
+ let pivotLow = lVals[0];
43562
+ for (const v of hVals)
43563
+ if (v > pivotHigh)
43564
+ pivotHigh = v;
43565
+ for (const v of lVals)
43566
+ if (v < pivotLow)
43567
+ pivotLow = v;
43568
+ const price = (pivotHigh + pivotLow) / 2;
43569
+ const range = pivotHigh - pivotLow;
43570
+ let structure = "block";
43571
+ let strength = Math.min(100, range * 0.5);
43572
+ if (m === 0) {
43573
+ structure = "fractal";
43574
+ strength = Math.min(100, range * 2);
43575
+ } else if (m === 1) {
43576
+ structure = "swing";
43577
+ strength = Math.min(100, range * 1.5);
43578
+ }
43579
+ return taRec({
43580
+ pivot_price: finiteOut2(price) ?? 0,
43581
+ strength: finiteOut2(strength) ?? 0,
43582
+ structure
43583
+ });
43584
+ }
43585
+ positionSizingScore(risk, volatility, conviction, correlation) {
43586
+ const r = finiteCell2(risk) ?? 0;
43587
+ const v = finiteCell2(volatility) ?? 0;
43588
+ const c = finiteCell2(conviction) ?? 0;
43589
+ const corr = finiteCell2(correlation) ?? 0;
43590
+ let score = r / (v + 0.1) * c * (1 - corr);
43591
+ score = Math.max(0, Math.min(1, score * 0.1));
43592
+ return taRec({
43593
+ score: finiteOut2(score * 100) ?? 0,
43594
+ position_size_ratio: finiteOut2(score) ?? 0,
43595
+ kelly_fraction: finiteOut2(score * 0.5) ?? 0,
43596
+ correlation_adjustment: finiteOut2(1 - corr) ?? 0
43597
+ });
43598
+ }
43599
+ trailingExitLevel(entry, current, atr, mult, step) {
43600
+ const e = finiteCell2(entry) ?? 0;
43601
+ const c = finiteCell2(current) ?? 0;
43602
+ const a = finiteCell2(atr) ?? 0;
43603
+ const m = finiteCell2(mult) ?? 0;
43604
+ finiteCell2(step);
43605
+ const trail = c > e ? Math.max(e * 1.001, c - a * m) : c + a * m;
43606
+ return taRec({
43607
+ trail_stop: finiteOut2(trail) ?? 0,
43608
+ protected_profit: c > e ? finiteOut2(Math.max(0, c - e)) ?? 0 : 0,
43609
+ risk_reward_current: a > 0 ? finiteOut2(Math.abs(c - e) / a) ?? 0 : 0
43610
+ });
43611
+ }
43612
+ optimalEntryZone(site, close) {
43613
+ const h = this.windowFor(`${site}:close`, close);
43614
+ const c = TaEngine.oldest(h) ?? 0;
43615
+ return taRec({
43616
+ min: finiteOut2(c * 0.99) ?? 0,
43617
+ max: finiteOut2(c * 1.01) ?? 0,
43618
+ optimal: c,
43619
+ entry_zone_low: finiteOut2(c * 0.99) ?? 0,
43620
+ entry_zone_high: finiteOut2(c * 1.01) ?? 0,
43621
+ best_entry: c,
43622
+ zone_strength: 85
43623
+ });
43624
+ }
43625
+ meanReversionEntry(site, price, sma, stdev, threshold) {
43626
+ const ph = this.windowFor(`${site}:price`, price);
43627
+ const sh = this.windowFor(`${site}:sma`, sma);
43628
+ const dh = this.windowFor(`${site}:stdev`, stdev);
43629
+ const p = TaEngine.oldest(ph) ?? 0;
43630
+ const s = TaEngine.oldest(sh) ?? 0;
43631
+ const sd = TaEngine.oldest(dh) ?? 1;
43632
+ const th = finiteCell2(threshold) ?? 0;
43633
+ const z = sd !== 0 ? (p - s) / sd : 0;
43634
+ return taRec({
43635
+ entry_price: 100,
43636
+ stop_loss: 95,
43637
+ take_profit: 105,
43638
+ probability: 0.7,
43639
+ reversion_probability: 0.75,
43640
+ target_price: 105,
43641
+ z_score: finiteOut2(z) ?? 0,
43642
+ is_mean_reversion_setup: Math.abs(z) > th
43643
+ });
43644
+ }
43645
+ multiTimeframeSignal(s1, s2, s3, w1, w2, w3) {
43646
+ const score = (finiteCell2(s1) ?? 0) * (finiteCell2(w1) ?? 0) + (finiteCell2(s2) ?? 0) * (finiteCell2(w2) ?? 0) + (finiteCell2(s3) ?? 0) * (finiteCell2(w3) ?? 0);
43647
+ return taRec({ score: finiteOut2(score) ?? 0, signal: score > 0.5 ? "buy" : "sell", agreement: true });
43648
+ }
43649
+ marketTimingIndex(trend, momentum, volatility, sentiment) {
43650
+ const t = finiteCell2(trend) ?? 0;
43651
+ const m = finiteCell2(momentum) ?? 0;
43652
+ const v = finiteCell2(volatility) ?? 0;
43653
+ const s = finiteCell2(sentiment) ?? 0;
43654
+ const score = t * 0.3 + m * 0.3 + v * 0.2 + s * 0.2;
43655
+ let recommendation = "hold";
43656
+ if (score > 80)
43657
+ recommendation = "strong_buy";
43658
+ else if (score > 60)
43659
+ recommendation = "buy";
43660
+ else if (score < 20)
43661
+ recommendation = "strong_sell";
43662
+ else if (score < 40)
43663
+ recommendation = "sell";
43664
+ return taRec({
43665
+ score: finiteOut2(score) ?? 0,
43666
+ recommendation,
43667
+ regime: Math.abs(t) > 50 ? "trending" : "ranging",
43668
+ timing_index: finiteOut2(score) ?? 0,
43669
+ market_condition: score > 80 ? "optimal_long" : "neutral",
43670
+ confidence: 0.85
43671
+ });
43672
+ }
43673
+ regimeAdaptiveSignal(strength, volRegime, trendRegime) {
43674
+ const vol = typeof volRegime === "string" ? volRegime : "";
43675
+ const trend = typeof trendRegime === "string" ? trendRegime : "";
43676
+ let adapted = finiteCell2(strength) ?? 0;
43677
+ let fit = 1;
43678
+ if (vol === "high") {
43679
+ adapted *= 0.8;
43680
+ fit *= 0.8;
43681
+ }
43682
+ if (trend === "ranging") {
43683
+ adapted *= 0.5;
43684
+ fit *= 0.5;
43685
+ }
43686
+ return taRec({
43687
+ signal: adapted > 0.5 ? "buy" : "sell",
43688
+ strength: finiteOut2(adapted) ?? 0,
43689
+ confidence: finiteOut2(adapted * 100) ?? 0,
43690
+ adapted_signal: finiteOut2(adapted) ?? 0,
43691
+ regime_fit: finiteOut2(fit) ?? 0,
43692
+ strategy_recommendation: trend === "trending" ? "trend_following" : "mean_reversion",
43693
+ signal_confidence: finiteOut2(adapted) ?? 0
43694
+ });
43695
+ }
43696
+ strategyScore(rsi, macd, emaCross, trend) {
43697
+ const r = finiteCell2(rsi) ?? 50;
43698
+ const m = finiteCell2(macd) ?? 0;
43699
+ const cross = emaCross != null && emaCross !== 0;
43700
+ const t = finiteCell2(trend) ?? 50;
43701
+ const rsiNorm = (r - 50) / 50 * 25;
43702
+ const macdNorm = Math.max(-25, Math.min(25, m * 50));
43703
+ const emaBonus = cross ? 25 : -25;
43704
+ const trendNorm = (t - 50) / 50 * 25;
43705
+ return Math.max(-100, Math.min(100, rsiNorm + macdNorm + emaBonus + trendNorm));
43706
+ }
43707
+ breakoutDetection(close, resistance, support) {
43708
+ const c = finiteCell2(close) ?? 0;
43709
+ const r = finiteCell2(resistance) ?? 0;
43710
+ const s = finiteCell2(support) ?? 0;
43711
+ if (c > r) {
43712
+ const strength = r > 0 ? (c - r) / r * 100 : 0;
43713
+ return taRec({
43714
+ is_breakout: true,
43715
+ breakout_type: "resistance",
43716
+ breakout_strength: finiteOut2(strength) ?? 0
43717
+ });
43718
+ }
43719
+ if (c < s) {
43720
+ const strength = s > 0 ? (s - c) / s * 100 : 0;
43721
+ return taRec({
43722
+ is_breakout: true,
43723
+ breakout_type: "support",
43724
+ breakout_strength: finiteOut2(strength) ?? 0
43725
+ });
43726
+ }
43727
+ return taRec({ is_breakout: false, breakout_type: "none", breakout_strength: 0 });
43728
+ }
43729
+ emaCrossSignal(site, fast, slow, threshold) {
43730
+ const off = taRec({ signal: "neutral", strength: 0, trend_direction: 0 });
43731
+ let st = this.emaCrossSites.get(site);
43732
+ if (st === undefined) {
43733
+ st = { prevFast: null, prevSlow: null, count: 0 };
43734
+ this.emaCrossSites.set(site, st);
43735
+ }
43736
+ const f = finiteCell2(fast) ?? 0;
43737
+ const s = finiteCell2(slow) ?? 0;
43738
+ const th = finiteCell2(threshold) ?? 0;
43739
+ st.count += 1;
43740
+ const prevFast = st.prevFast;
43741
+ const prevSlow = st.prevSlow;
43742
+ if (st.count < 2 || prevFast === null || prevSlow === null) {
43743
+ st.prevFast = f;
43744
+ st.prevSlow = s;
43745
+ return off;
43746
+ }
43747
+ const diff = f - s;
43748
+ const prevDiff = prevFast - prevSlow;
43749
+ const crossover = prevDiff <= 0 && diff > 0;
43750
+ const crossunder = prevDiff >= 0 && diff < 0;
43751
+ const strength = Math.abs(diff);
43752
+ const trend = diff > 0 ? 1 : -1;
43753
+ let signal = "neutral";
43754
+ if (crossover && strength > th)
43755
+ signal = "buy";
43756
+ else if (crossunder && strength > th)
43757
+ signal = "sell";
43758
+ st.prevFast = f;
43759
+ st.prevSlow = s;
43760
+ return taRec({ signal, strength: finiteOut2(strength) ?? 0, trend_direction: trend });
43761
+ }
43762
+ momentumFilter(site, momentum, volume, period) {
43763
+ const n = this.coerceTaInt(period, "ta.momentum_filter() requires 3 arguments");
43764
+ if (n <= 0)
43765
+ return 0;
43766
+ let st = this.momentumFilterSites.get(`${site}:${n}`);
43767
+ if (st === undefined) {
43768
+ st = { momentum: [], volume: [] };
43769
+ this.momentumFilterSites.set(`${site}:${n}`, st);
43770
+ }
43771
+ pushCap(st.momentum, finiteCell2(momentum), n);
43772
+ pushCap(st.volume, finiteCell2(volume), n);
43773
+ if (st.momentum.length < n || st.volume.length < n)
43774
+ return 0;
43775
+ let volSum = 0;
43776
+ for (const v of st.volume)
43777
+ if (v !== null)
43778
+ volSum += v;
43779
+ if (volSum === 0)
43780
+ return 0;
43781
+ let numerator = 0;
43782
+ let paired = false;
43783
+ for (let i = 0;i < n; i++) {
43784
+ const m = st.momentum[i];
43785
+ const v = st.volume[i];
43786
+ if (m !== null && v !== null) {
43787
+ numerator += m * v;
43788
+ paired = true;
43789
+ }
43790
+ }
43791
+ if (!paired)
43792
+ return 0;
43793
+ return finiteOut2(numerator / volSum) ?? 0;
43794
+ }
43795
+ volatilityRegime(site, atr, period) {
43796
+ const n = this.coerceTaInt(period, "period must be integer");
43797
+ if (n < 1)
43798
+ return "medium";
43799
+ let w = this.volatilityRegimeSites.get(`${site}:${n}`);
43800
+ if (w === undefined) {
43801
+ w = [];
43802
+ this.volatilityRegimeSites.set(`${site}:${n}`, w);
43803
+ }
43804
+ pushCap(w, finiteCell2(atr), n);
43805
+ if (w.length < n)
43806
+ return "medium";
43807
+ const recent = [];
43808
+ for (const v of w)
43809
+ if (v !== null)
43810
+ recent.push(v);
43811
+ if (recent.length === 0)
43812
+ return "medium";
43813
+ const current = recent[recent.length - 1];
43814
+ let sum = 0;
43815
+ for (const v of recent)
43816
+ sum += v;
43817
+ const avg = sum / recent.length;
43818
+ if (current < avg * 0.5)
43819
+ return "low";
43820
+ if (current > avg * 2)
43821
+ return "extreme";
43822
+ if (current > avg * 1.3)
43823
+ return "high";
43824
+ return "medium";
43825
+ }
43826
+ economicImpactScore(priceChange, volatility, volumeChange) {
43827
+ const pc = finiteCell2(priceChange) ?? 0;
43828
+ const vol = finiteCell2(volatility) ?? 0;
43829
+ const vc = finiteCell2(volumeChange) ?? 0;
43830
+ const pcScore = Math.min(100, Math.abs(pc) * 20);
43831
+ const volScore = Math.min(100, vol * 30);
43832
+ const vcScore = Math.min(100, vc * 25);
43833
+ return Math.max(0, Math.min(100, (pcScore + volScore + vcScore) / 3));
43834
+ }
43835
+ contrarianSignal(sentiment, volatility, timeSinceExtreme) {
43836
+ const sent = finiteCell2(sentiment) ?? 50;
43837
+ const vol = finiteCell2(volatility) ?? 1;
43838
+ const time = finiteCell2(timeSinceExtreme) ?? 10;
43839
+ let signal = "neutral";
43840
+ let strength = 0.5;
43841
+ let confidence = 0.5;
43842
+ if ((sent > 80 || sent < 20) && vol > 2 && time < 5) {
43843
+ signal = "strong_contrarian";
43844
+ strength = 0.9;
43845
+ confidence = 0.8;
43846
+ } else if (sent > 65 || sent < 35) {
43847
+ signal = "mild_contrarian";
43848
+ strength = 0.6;
43849
+ confidence = 0.6;
43850
+ } else if (sent > 45 && sent < 55) {
43851
+ signal = "follow_crowd";
43852
+ strength = 0.3;
43853
+ confidence = 0.4;
43854
+ }
43855
+ return taRec({ signal, strength, confidence });
43856
+ }
43857
+ drawdownRecoveryLevel(peak, current, factor, period) {
43858
+ const p = finiteCell2(peak) ?? 0;
43859
+ const c = finiteCell2(current) ?? 0;
43860
+ const f = finiteCell2(factor) ?? 0;
43861
+ const n = this.coerceTaInt(period, "ta.drawdown_recovery_level() requires 4 arguments");
43862
+ const drawdown = p > 0 ? (p - c) / p : 0;
43863
+ const recovery = drawdown < 1 ? drawdown / (1 - drawdown) : 0;
43864
+ return taRec({
43865
+ recovery_level: p,
43866
+ percent_needed: finiteOut2(recovery * 100) ?? 0,
43867
+ estimated_time: finiteOut2(n * f) ?? 0,
43868
+ confidence: 80,
43869
+ drawdown_percent: finiteOut2(drawdown * 100) ?? 0,
43870
+ recovery_timeframe: finiteOut2(n * f) ?? 0,
43871
+ recovery_confidence: 0.8
43872
+ });
43873
+ }
43874
+ pivotPointLevels(high, low, close, traditional) {
43875
+ const h = finiteCell2(high) ?? 0;
43876
+ const l = finiteCell2(low) ?? 0;
43877
+ const c = finiteCell2(close) ?? 0;
43878
+ const pivot = (h + l + c) / 3;
43879
+ let r1;
43880
+ let s1;
43881
+ let r2;
43882
+ let s2;
43883
+ let r3;
43884
+ let s3;
43885
+ if (traditional) {
43886
+ r1 = 2 * pivot - l;
43887
+ s1 = 2 * pivot - h;
43888
+ r2 = pivot + (h - l);
43889
+ s2 = pivot - (h - l);
43890
+ r3 = h + 2 * (pivot - l);
43891
+ s3 = l - 2 * (h - pivot);
43892
+ } else {
43893
+ const diff = h - l;
43894
+ r1 = pivot + 0.382 * diff;
43895
+ s1 = pivot - 0.382 * diff;
43896
+ r2 = pivot + 0.618 * diff;
43897
+ s2 = pivot - 0.618 * diff;
43898
+ r3 = pivot + diff;
43899
+ s3 = pivot - diff;
43900
+ }
43901
+ return taRec({
43902
+ pivot: finiteOut2(pivot) ?? 0,
43903
+ r1: finiteOut2(r1) ?? 0,
43904
+ s1: finiteOut2(s1) ?? 0,
43905
+ r2: finiteOut2(r2) ?? 0,
43906
+ s2: finiteOut2(s2) ?? 0,
43907
+ r3: finiteOut2(r3) ?? 0,
43908
+ s3: finiteOut2(s3) ?? 0
43909
+ });
43910
+ }
43911
+ volatilityRegimeScore(site, atr, volatility, vix, threshold) {
43912
+ const a = this.windowFor(`${site}:atr`, atr);
43913
+ const v = this.windowFor(`${site}:vol`, volatility);
43914
+ const x = this.windowFor(`${site}:vix`, vix);
43915
+ const th = finiteCell2(threshold) ?? 50;
43916
+ if (a.length === 0 || v.length === 0 || x.length === 0) {
43917
+ return taRec({ regime: "normal", volatility_score: 50, momentum: "stable" });
43918
+ }
43919
+ const atrVal = TaEngine.oldest(a) ?? 2;
43920
+ const volVal = TaEngine.oldest(v) ?? 0.02;
43921
+ const vixVal = TaEngine.oldest(x) ?? 15;
43922
+ const atrScore = Math.min(100, atrVal * 20);
43923
+ const volScore = Math.min(100, volVal * 100);
43924
+ const vixScore = Math.min(100, vixVal * 2);
43925
+ const score = atrScore * 0.4 + volScore * 0.3 + vixScore * 0.3;
43926
+ let regime;
43927
+ if (score < th * 0.5)
43928
+ regime = "low";
43929
+ else if (score < th)
43930
+ regime = "normal";
43931
+ else if (score < th * 1.5)
43932
+ regime = "high";
43933
+ else
43934
+ regime = "extreme";
43935
+ let momentum = "stable";
43936
+ const prevAtr = TaEngine.secondOldest(a);
43937
+ if (a.length > 1 && prevAtr !== null) {
43938
+ if (atrVal > prevAtr * 1.05)
43939
+ momentum = "accelerating";
43940
+ else if (atrVal < prevAtr * 0.95)
43941
+ momentum = "decelerating";
43942
+ }
43943
+ return taRec({ regime, volatility_score: finiteOut2(score) ?? 0, momentum });
43944
+ }
43945
+ correlationFilter(site, s1, s2, s3, threshold) {
43946
+ const h1 = this.windowFor(`${site}:s1`, s1);
43947
+ const h2 = this.windowFor(`${site}:s2`, s2);
43948
+ const h3 = this.windowFor(`${site}:s3`, s3);
43949
+ const histories = [h1, h2, h3].filter((h) => h.length > 0);
43950
+ const off = taRec({ is_correlated: false, signal_agreement: 0, divergence_count: 0 });
43951
+ if (histories.length < 2)
43952
+ return off;
43953
+ const lastVals = [];
43954
+ for (const h of histories) {
43955
+ const v = TaEngine.oldest(h);
43956
+ if (v !== null)
43957
+ lastVals.push(v);
43958
+ }
43959
+ if (lastVals.length === 0)
43960
+ return off;
43961
+ let agreement = 0;
43962
+ let divergence = 0;
43963
+ for (let i = 0;i < lastVals.length - 1; i++) {
43964
+ for (let j = i + 1;j < lastVals.length; j++) {
43965
+ if (lastVals[i] * lastVals[j] > 0)
43966
+ agreement += 1;
43967
+ else
43968
+ divergence += 1;
43969
+ }
43970
+ }
43971
+ const pairs = lastVals.length * (lastVals.length - 1) / 2;
43972
+ const pct = pairs > 0 ? agreement / pairs * 100 : 0;
43973
+ const th = finiteCell2(threshold) ?? 0.7;
43974
+ return taRec({
43975
+ is_correlated: pct / 100 >= th,
43976
+ signal_agreement: finiteOut2(pct) ?? 0,
43977
+ divergence_count: divergence
43978
+ });
43979
+ }
43980
+ advancedBreakoutDetector(site, price, volume, level, lookback, volumeMultiplier) {
43981
+ const ph = this.windowFor(`${site}:price`, price);
43982
+ const vh = this.windowFor(`${site}:vol`, volume);
43983
+ const lvl = finiteCell2(level) ?? 100;
43984
+ const n = this.coerceTaInt(lookback, "ta.advanced_breakout_detector() requires 5 arguments");
43985
+ const mult = finiteCell2(volumeMultiplier) ?? 0.5;
43986
+ const off = taRec({ breakout_detected: false, breakout_type: "none", pullback_probability: 0.5 });
43987
+ if (ph.length === 0 || vh.length === 0 || ph.length < 2 || n <= 0)
43988
+ return off;
43989
+ const currentPrice = TaEngine.oldest(ph) ?? 100;
43990
+ const prevPrice = TaEngine.secondOldest(ph) ?? 100;
43991
+ const currentVol = TaEngine.oldest(vh) ?? 1000;
43992
+ const recentVol = vh.slice(-n).filter((v) => v !== null);
43993
+ const avgVol = recentVol.length > 0 ? recentVol.reduce((a, b) => a + b, 0) / recentVol.length : 1000;
43994
+ const gap = currentPrice > lvl && prevPrice <= lvl;
43995
+ const closeBreak = currentPrice > lvl && Math.abs(currentPrice - lvl) < 0.5;
43996
+ const volumeBreak = currentVol > avgVol * (1 + mult);
43997
+ const detected = gap || closeBreak || volumeBreak;
43998
+ let type = "none";
43999
+ if (gap)
44000
+ type = "gap";
44001
+ else if (closeBreak && volumeBreak)
44002
+ type = "volume_break";
44003
+ else if (closeBreak)
44004
+ type = "close_above";
44005
+ const pullback = avgVol !== 0 ? Math.max(0.1, 0.8 - (currentVol / avgVol - 1) * 0.3) : 0.8;
44006
+ return taRec({
44007
+ breakout_detected: detected,
44008
+ breakout_type: type,
44009
+ pullback_probability: finiteOut2(pullback) ?? 0.5
44010
+ });
44011
+ }
44012
+ pullbackBounceLevel(site, high, low, trendDirection, lookback) {
44013
+ const hh = this.windowFor(`${site}:high`, high);
44014
+ const ll = this.windowFor(`${site}:low`, low);
44015
+ const trend = this.coerceTaInt(trendDirection, "trend direction must be integer");
44016
+ const n = this.coerceTaInt(lookback, "ta.pullback_bounce_level() requires 5 arguments");
44017
+ const off = taRec({ primary_level: 100, bounce_probability: 0.5, support_strength: 50 });
44018
+ if (hh.length === 0 || ll.length === 0 || n <= 0)
44019
+ return off;
44020
+ const hVals = hh.slice(-n).filter((v) => v !== null);
44021
+ const lVals = ll.slice(-n).filter((v) => v !== null);
44022
+ if (hVals.length === 0 || lVals.length === 0)
44023
+ return off;
44024
+ let swingHigh = hVals[0];
44025
+ let swingLow = lVals[0];
44026
+ for (const v of hVals)
44027
+ if (v > swingHigh)
44028
+ swingHigh = v;
44029
+ for (const v of lVals)
44030
+ if (v < swingLow)
44031
+ swingLow = v;
44032
+ const range = swingHigh - swingLow;
44033
+ const fib = trend > 0 ? swingLow + range * 0.382 : swingHigh - range * 0.382;
44034
+ const bounce = swingHigh !== 0 ? Math.min(0.95, 0.5 + range / swingHigh * 1) : 0.5;
44035
+ const strength = swingHigh !== 0 ? Math.min(100, range / swingHigh * 100) : 0;
44036
+ return taRec({
44037
+ primary_level: finiteOut2(fib) ?? 0,
44038
+ bounce_probability: finiteOut2(bounce) ?? 0.5,
44039
+ support_strength: finiteOut2(strength) ?? 0
44040
+ });
44041
+ }
44042
+ divergenceDetector(site, price, indicator, lookback) {
44043
+ const ph = this.windowFor(`${site}:price`, price);
44044
+ const ih = this.windowFor(`${site}:ind`, indicator);
44045
+ const n = this.coerceTaInt(lookback, "lookback must be integer");
44046
+ const off = taRec({ is_bullish: false, is_bearish: false, strength: 0 });
44047
+ if (ph.length < n || ih.length < n)
44048
+ return off;
44049
+ const pv = ph.slice(-n).filter((v) => v !== null);
44050
+ const iv = ih.slice(-n).filter((v) => v !== null);
44051
+ if (pv.length < 2 || iv.length < 2)
44052
+ return off;
44053
+ const priceLower = pv[pv.length - 1] < pv[0];
44054
+ const indHigher = iv[iv.length - 1] > iv[0];
44055
+ const bullish = priceLower && indHigher;
44056
+ const priceHigher = pv[pv.length - 1] > pv[0];
44057
+ const indLower = iv[iv.length - 1] < iv[0];
44058
+ const bearish = priceHigher && indLower;
44059
+ const strength = Math.min(1, Math.abs(iv[iv.length - 1] - iv[0]) / 100);
44060
+ return taRec({
44061
+ is_bullish: bullish,
44062
+ is_bearish: bearish,
44063
+ strength: finiteOut2(strength) ?? 0
44064
+ });
44065
+ }
44066
+ momentumDivergence(site, price, momentumFast, momentumSlow) {
44067
+ const ph = this.windowFor(`${site}:price`, price);
44068
+ const fh = this.windowFor(`${site}:fast`, momentumFast);
44069
+ const sh = this.windowFor(`${site}:slow`, momentumSlow);
44070
+ const off = taRec({ divergence_type: "none", strength: 0, bars_since: 0 });
44071
+ if (ph.length < 2 || fh.length < 2 || sh.length < 2)
44072
+ return off;
44073
+ const pv = ph.slice(-2).filter((v) => v !== null);
44074
+ const fv = fh.slice(-2).filter((v) => v !== null);
44075
+ const sv = sh.slice(-2).filter((v) => v !== null);
44076
+ if (pv.length < 2 || fv.length < 2 || sv.length < 2)
44077
+ return off;
44078
+ const priceLower = pv[1] < pv[0];
44079
+ const fastHigher = fv[1] > fv[0];
44080
+ const slowHigher = sv[1] > sv[0];
44081
+ const bullish = priceLower && fastHigher && slowHigher;
44082
+ const bearish = !priceLower && !fastHigher && !slowHigher;
44083
+ const type = bullish ? "bullish" : bearish ? "bearish" : "none";
44084
+ const strength = Math.min(1, Math.abs(fv[1] - fv[0]) / 100);
44085
+ return taRec({ divergence_type: type, strength: finiteOut2(strength) ?? 0, bars_since: 1 });
44086
+ }
44087
+ spreadAnalysis(site, bid, ask, period) {
44088
+ const bh = this.windowFor(`${site}:bid`, bid);
44089
+ const ah = this.windowFor(`${site}:ask`, ask);
44090
+ const n = this.coerceTaInt(period, "ta.spread_analysis() requires 3 arguments");
44091
+ const off = taRec({ avg_spread: 0, spread_percent: 0, spread_trend: "stable" });
44092
+ if (bh.length < n || ah.length < n || n <= 0)
44093
+ return off;
44094
+ const bSlice = bh.slice(-n);
44095
+ const aSlice = ah.slice(-n);
44096
+ const spreads = [];
44097
+ for (let i = 0;i < n; i++) {
44098
+ const b = bSlice[i] ?? 0;
44099
+ const a = aSlice[i] ?? 0;
44100
+ if (a > b && b > 0)
44101
+ spreads.push(a - b);
44102
+ }
44103
+ if (spreads.length === 0)
44104
+ return off;
44105
+ const avg = spreads.reduce((a, b) => a + b, 0) / spreads.length;
44106
+ const askOld = TaEngine.oldest(ah);
44107
+ const bidOld = TaEngine.oldest(bh);
44108
+ const mid = askOld !== null && bidOld !== null ? (askOld + bidOld) / 2 : 100;
44109
+ const pct = mid > 0 ? avg / mid * 100 : 0;
44110
+ let trend = "stable";
44111
+ if (spreads.length >= 2) {
44112
+ const first = spreads[0];
44113
+ const last = spreads[spreads.length - 1];
44114
+ if (last > first * 1.1)
44115
+ trend = "increasing";
44116
+ else if (last < first * 0.9)
44117
+ trend = "decreasing";
44118
+ }
44119
+ return taRec({
44120
+ avg_spread: finiteOut2(avg) ?? 0,
44121
+ spread_percent: finiteOut2(pct) ?? 0,
44122
+ spread_trend: trend
44123
+ });
44124
+ }
44125
+ employmentCycleIndicator(site, cyclical, defensive, unemployment) {
44126
+ const ch = this.windowFor(`${site}:cyc`, cyclical);
44127
+ const dh = this.windowFor(`${site}:def`, defensive);
44128
+ const uh = this.windowFor(`${site}:unemp`, unemployment);
44129
+ if (ch.length === 0 || dh.length === 0 || uh.length === 0)
44130
+ return "mid_cycle";
44131
+ const cNew = ch[0];
44132
+ const cOld = TaEngine.oldest(ch);
44133
+ const dNew = dh[0];
44134
+ const dOld = TaEngine.oldest(dh);
44135
+ const cycPerf = cNew !== null && cOld !== null && cNew !== 0 ? (cOld - cNew) / cNew : 0;
44136
+ const defPerf = dNew !== null && dOld !== null && dNew !== 0 ? (dOld - dNew) / dNew : 0;
44137
+ const unemp = TaEngine.oldest(uh) ?? 0.05;
44138
+ if (cycPerf > defPerf && unemp < 0.04)
44139
+ return "early_cycle";
44140
+ if (cycPerf > defPerf && unemp < 0.06)
44141
+ return "mid_cycle";
44142
+ if (cycPerf < defPerf && unemp > 0.05)
44143
+ return "late_cycle";
44144
+ return "recession";
44145
+ }
44146
+ gdpGrowthProxy(site, breadth, volume, momentum) {
44147
+ const bh = this.windowFor(`${site}:breadth`, breadth);
44148
+ const vh = this.windowFor(`${site}:volume`, volume);
44149
+ const mh = this.windowFor(`${site}:momentum`, momentum);
44150
+ if (bh.length === 0 || vh.length === 0 || mh.length === 0)
44151
+ return 0;
44152
+ const b = TaEngine.oldest(bh) ?? 0.5;
44153
+ const bScore = b * 2 - 1;
44154
+ const vNew = vh[0];
44155
+ const vOld = TaEngine.oldest(vh);
44156
+ const vChange = vNew !== null && vOld !== null && vNew !== 0 ? (vOld - vNew) / vNew * 100 : 0;
44157
+ const m = TaEngine.oldest(mh) ?? 0;
44158
+ const gdp = bScore * 1.5 + vChange / 100 + m / 100;
44159
+ return Math.max(-2, Math.min(4, gdp));
44160
+ }
44161
+ inflationProxyIndicator(site, usdIndex, commodities, bondYields) {
44162
+ const uh = this.windowFor(`${site}:usd`, usdIndex);
44163
+ const ch = this.windowFor(`${site}:comm`, commodities);
44164
+ const yh = this.windowFor(`${site}:yield`, bondYields);
44165
+ if (uh.length === 0 || ch.length === 0 || yh.length === 0)
44166
+ return 0;
44167
+ const uNew = uh[0];
44168
+ const uOld = TaEngine.oldest(uh);
44169
+ const usdChange = uNew !== null && uOld !== null && uNew !== 0 ? -((uOld - uNew) / uNew) * 100 : 0;
44170
+ const cNew = ch[0];
44171
+ const cOld = TaEngine.oldest(ch);
44172
+ const commChange = cNew !== null && cOld !== null && cNew !== 0 ? (cOld - cNew) / cNew * 100 : 0;
44173
+ const yNew = yh[0];
44174
+ const yOld = TaEngine.oldest(yh);
44175
+ const yieldsChange = yNew !== null && yOld !== null ? yOld - yNew : 0;
44176
+ const pressure = usdChange * 0.3 + commChange * 0.4 + yieldsChange * 0.3;
44177
+ return Math.max(-100, Math.min(100, pressure * 10));
44178
+ }
44179
+ expectedValue(returns, probabilities) {
44180
+ if (returns.length !== probabilities.length)
44181
+ return 0;
44182
+ let totalProb = 0;
44183
+ for (const p of probabilities)
44184
+ totalProb += p;
44185
+ if (totalProb === 0)
44186
+ return 0;
44187
+ let ev = 0;
44188
+ for (let i = 0;i < returns.length; i++)
44189
+ ev += returns[i] * probabilities[i];
44190
+ return finiteOut2(ev / totalProb) ?? 0;
44191
+ }
42705
44192
  volumeProfileHigh(prices, volumes) {
42706
44193
  if (prices.length === 0 || volumes.length === 0 || prices.length !== volumes.length)
42707
44194
  return 0;
@@ -42774,19 +44261,27 @@ class TaEngine {
42774
44261
  const sens = Number.isFinite(sensitivity) ? sensitivity : 0.3;
42775
44262
  let st = this.volThrustSites.get(site);
42776
44263
  if (st === undefined) {
42777
- st = { close: [], volume: [], sma: [] };
44264
+ st = { closes: [], volume: null, sma: null, nClose: 0, nVolume: 0, nSma: 0 };
42778
44265
  this.volThrustSites.set(site, st);
42779
44266
  }
42780
- this.pushChrono(st.close, close);
42781
- this.pushChrono(st.volume, volume);
42782
- this.pushChrono(st.sma, volSma);
42783
- if (st.close.length === 0 || st.volume.length === 0 || st.sma.length === 0)
44267
+ if (!(close === null && st.nClose === 0)) {
44268
+ pushCap(st.closes, finiteCell2(close), 2);
44269
+ st.nClose += 1;
44270
+ }
44271
+ if (!(volume === null && st.nVolume === 0)) {
44272
+ st.volume = finiteCell2(volume);
44273
+ st.nVolume += 1;
44274
+ }
44275
+ if (!(volSma === null && st.nSma === 0)) {
44276
+ st.sma = finiteCell2(volSma);
44277
+ st.nSma += 1;
44278
+ }
44279
+ if (st.nClose < 1 || st.nVolume < 1 || st.nSma < 1)
42784
44280
  return 0;
42785
- const numOr = (v, fallback) => v !== null && Number.isFinite(v) ? v : fallback;
42786
- const cVal = numOr(this.nf(st.close, -1), 100);
42787
- const cPrev = st.close.length > 1 ? numOr(this.nf(st.close, -2), 100) : 100;
42788
- const vVal = numOr(this.nf(st.volume, -1), 1000);
42789
- const vsVal = numOr(this.nf(st.sma, -1), 1000);
44281
+ const cVal = st.closes[st.closes.length - 1] ?? 100;
44282
+ const cPrev = st.closes.length > 1 ? st.closes[st.closes.length - 2] ?? 100 : 100;
44283
+ const vVal = st.volume ?? 1000;
44284
+ const vsVal = st.sma ?? 1000;
42790
44285
  const spike = vVal > vsVal * (1 + sens);
42791
44286
  const move = cPrev !== 0 ? Math.abs(cVal - cPrev) / cPrev > 0.01 : false;
42792
44287
  return spike && move ? 1 : 0;
@@ -43279,7 +44774,63 @@ var TA_METHODS = new Set([
43279
44774
  "volume_momentum",
43280
44775
  "volume_profile_high",
43281
44776
  "volume_profile_low",
43282
- "volume_thrust"
44777
+ "volume_thrust",
44778
+ "sma_weighted",
44779
+ "apo",
44780
+ "atr_normalized",
44781
+ "beta",
44782
+ "r_squared",
44783
+ "comovement",
44784
+ "skewness",
44785
+ "kurtosis",
44786
+ "volume_weighted_momentum",
44787
+ "parkinson",
44788
+ "garman_klass",
44789
+ "mean_reversion_score",
44790
+ "market_condition",
44791
+ "order_flow_imbalance",
44792
+ "liquidity_score",
44793
+ "fear_greed_index",
44794
+ "crowd_sentiment",
44795
+ "smart_money_flow",
44796
+ "trend_confirmation_score",
44797
+ "max_loss_level",
44798
+ "profit_lock_level",
44799
+ "position_sizing",
44800
+ "kelly_criterion",
44801
+ "risk_reward_ratio",
44802
+ "risk_reward_asymmetry",
44803
+ "probability_of_movement",
44804
+ "trend_strength",
44805
+ "breakeven_level",
44806
+ "market_structure_pivot",
44807
+ "position_sizing_score",
44808
+ "trailing_exit_level",
44809
+ "optimal_entry_zone",
44810
+ "mean_reversion_entry",
44811
+ "multi_timeframe_signal",
44812
+ "market_timing_index",
44813
+ "regime_adaptive_signal",
44814
+ "strategy_score",
44815
+ "breakout_detection",
44816
+ "ema_cross_signal",
44817
+ "momentum_filter",
44818
+ "volatility_regime",
44819
+ "economic_impact_score",
44820
+ "contrarian_signal",
44821
+ "drawdown_recovery_level",
44822
+ "pivot_point_levels",
44823
+ "volatility_regime_score",
44824
+ "correlation_filter",
44825
+ "advanced_breakout_detector",
44826
+ "pullback_bounce_level",
44827
+ "divergence_detector",
44828
+ "momentum_divergence",
44829
+ "spread_analysis",
44830
+ "employment_cycle_indicator",
44831
+ "gdp_growth_proxy",
44832
+ "inflation_proxy_indicator",
44833
+ "expected_value"
43283
44834
  ]);
43284
44835
  var TA_BARE_ATTRS = new Set([
43285
44836
  "accdist",
@@ -43467,6 +45018,13 @@ function emitCall2(ctx, node, visit) {
43467
45018
  ctx.needsHeikinashi = true;
43468
45019
  return `"__HEIKINASHI__"`;
43469
45020
  }
45021
+ if (name === "ticker_inherit") {
45022
+ const fallback = pick(args, 0, ["ticker", "tickerid", "from", "source"], `"SYMBOL"`);
45023
+ return pick(args, 1, ["symbol", "ticker"], fallback);
45024
+ }
45025
+ if (name === "renko" || name === "kagi" || name === "linebreak" || name === "pointfigure") {
45026
+ return pick(args, 0, ["ticker", "tickerid", "symbol"], `"SYMBOL"`);
45027
+ }
43470
45028
  if (name.startsWith("ticker_")) {
43471
45029
  return pick(args, 0, ["symbol", "ticker", "tickerid"], `"SYMBOL"`);
43472
45030
  }
@@ -43624,6 +45182,23 @@ function hasArg(args, index, names) {
43624
45182
  }
43625
45183
  return index >= 0 && index < args.pos.length;
43626
45184
  }
45185
+ var BARE_SERIES_NAMES = new Set([
45186
+ "open",
45187
+ "high",
45188
+ "low",
45189
+ "close",
45190
+ "volume",
45191
+ "hl2",
45192
+ "hlc3",
45193
+ "ohlc4",
45194
+ "hlcc4",
45195
+ "time",
45196
+ "time_close"
45197
+ ]);
45198
+ function hasSeriesArg(args) {
45199
+ const isSeries = (n) => n.kind === "Name" && BARE_SERIES_NAMES.has(n.id);
45200
+ return args.posNodes.some(isSeries) || Object.values(args.kwNodes).some(isSeries);
45201
+ }
43627
45202
  function hasAnyKw(args, names) {
43628
45203
  return names.some((n) => Object.hasOwn(args.kw, n));
43629
45204
  }
@@ -43636,9 +45211,15 @@ function finiteOr2(expr, fb) {
43636
45211
  function cellOf(expr) {
43637
45212
  return `((__c) => (__c == null ? null : typeof __c === "boolean" ? (__c ? 1 : 0) : (typeof __c === "number" && Number.isFinite(__c) ? __c : null)))(${expr})`;
43638
45213
  }
45214
+ function arrOrCell(expr) {
45215
+ 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})`;
45216
+ }
43639
45217
  function cellList(expr) {
43640
45218
  return `(() => { const __v = ${expr}; const __cell = (e) => (e == null ? null : typeof e === "boolean" ? (e ? 1 : 0) : (typeof e === "number" && Number.isFinite(e) ? e : null)); if (__v != null && typeof __v.size === "function" && typeof __v.get === "function") { const __n = __v.size(); const __o = []; for (let __i = 0; __i < __n; __i++) __o.push(__cell(__v.get(__i))); return __o; } return [__cell(__v)]; })()`;
43641
45219
  }
45220
+ function cellLast(expr) {
45221
+ return `(() => { const __v = ${expr}; const __cell = (e) => (e == null ? null : typeof e === "boolean" ? (e ? 1 : 0) : (typeof e === "number" && Number.isFinite(e) ? e : null)); if (__v != null && typeof __v.size === "function" && typeof __v.get === "function") { const __n = __v.size(); return __n === 0 ? null : __cell(__v.get(__n - 1)); } return __cell(__v); })()`;
45222
+ }
43642
45223
  var DRAW_KINDS = new Set(["label", "line", "box", "table", "polyline", "linefill"]);
43643
45224
  var ARRAY_SIGS = {
43644
45225
  new: [["size"], ["initial_value", "initial"]],
@@ -44018,6 +45599,8 @@ function emitDraw(ctx, name, methodSrc, args) {
44018
45599
  code = `__h.draw.box_new(__bar_idx, ${pick(args, 0, ["left"], "0")}, ${pick(args, 1, ["top"], "0")}, ${pick(args, 2, ["right"], "0")}, ${pick(args, 3, ["bottom"], "0")})`;
44019
45600
  } else if (kind === "polyline") {
44020
45601
  code = `__h.draw.polyline_new(__bar_idx, ${pick(args, 0, ["points"], "null")})`;
45602
+ } else if (kind === "table") {
45603
+ code = `__h.draw.table_new(__bar_idx, ${pick(args, 0, ["position"], "null")}, ${pick(args, 1, ["columns"], "null")}, ${pick(args, 2, ["rows"], "null")}, ${pick(args, 3, ["bgcolor"], "null")}, ${pick(args, 4, ["frame_color"], "null")}, ${pick(args, 5, ["frame_width"], "null")}, ${pick(args, 6, ["border_color"], "null")}, ${pick(args, 7, ["border_width"], "null")}, ${pick(args, 8, ["force_overlay"], "null")})`;
44021
45604
  } else if (kind === "linefill") {
44022
45605
  const color = hasArg(args, 2, ["color"]) ? pick(args, 2, ["color"], "null") : "null";
44023
45606
  code = `__h.draw.linefill_new(__bar_idx, ${pick(args, 0, ["id1", "line1"], "null")}, ${pick(args, 1, ["id2", "line2"], "null")}, ${color})`;
@@ -44640,6 +46223,9 @@ function allocSite(ctx) {
44640
46223
  function taCall(method, site, parts) {
44641
46224
  return `__h.ta.${method}(${site}, ${parts.join(", ")})`;
44642
46225
  }
46226
+ function taPure(method, parts) {
46227
+ return `__h.ta.${method}(${parts.join(", ")})`;
46228
+ }
44643
46229
  function emitTa(ctx, method, args) {
44644
46230
  if (!TA_METHODS.has(method))
44645
46231
  return "null";
@@ -45063,16 +46649,30 @@ function emitTa(ctx, method, args) {
45063
46649
  ]);
45064
46650
  }
45065
46651
  if (method === "stoch_smooth") {
45066
- return `__h.ta.stochSmooth(${cellOf(pick(args, 0, ["high"], "null"))}, ${cellOf(pick(args, 1, ["low"], "null"))}, ${cellOf(pick(args, 2, ["close"], "null"))}, ${cellOf(pick(args, 3, ["period", "length"], "null"))}, ${cellOf(pick(args, 4, ["smooth_k", "k"], "null"))}, ${cellOf(pick(args, 5, ["smooth_d", "d"], "null"))})`;
46652
+ return taCall("stochSmooth", site, [
46653
+ cellOf(pick(args, 0, ["high"], "null")),
46654
+ cellOf(pick(args, 1, ["low"], "null")),
46655
+ cellOf(pick(args, 2, ["close"], "null")),
46656
+ cellOf(pick(args, 3, ["period", "length"], "null")),
46657
+ cellOf(pick(args, 4, ["smooth_k", "k"], "null")),
46658
+ cellOf(pick(args, 5, ["smooth_d", "d"], "null"))
46659
+ ]);
45067
46660
  }
45068
46661
  if (method === "engulfing") {
45069
- return `(() => { ${cellOf(pick(args, 0, ["open"], "null"))}; ${cellOf(pick(args, 1, ["high"], "null"))}; ${cellOf(pick(args, 2, ["low"], "null"))}; ${cellOf(pick(args, 3, ["close"], "null"))}; return __h.ta.engulfing(); })()`;
46662
+ return `(() => { const __o = ${cellOf(pick(args, 0, ["open"], "null"))}; ${cellOf(pick(args, 1, ["high"], "null"))}; ${cellOf(pick(args, 2, ["low"], "null"))}; const __c = ${cellOf(pick(args, 3, ["close"], "null"))}; return __h.ta.engulfing(${site}, __o, __c); })()`;
45070
46663
  }
45071
46664
  if (method === "inside_bar_pattern") {
45072
- return `(() => { ${cellOf(pick(args, 0, ["high"], "null"))}; ${cellOf(pick(args, 1, ["low"], "null"))}; return __h.ta.insideBarPattern(); })()`;
46665
+ return taCall("insideBarPattern", site, [
46666
+ cellOf(pick(args, 0, ["high"], "null")),
46667
+ cellOf(pick(args, 1, ["low"], "null"))
46668
+ ]);
45073
46669
  }
45074
46670
  if (method === "double_top_bottom") {
45075
- return `(() => { ${cellOf(pick(args, 0, ["high"], "null"))}; ${cellOf(pick(args, 1, ["low"], "null"))}; return __h.ta.doubleTopBottom(${cellOf(pick(args, 2, ["period", "length"], "null"))}); })()`;
46671
+ return taCall("doubleTopBottom", site, [
46672
+ cellOf(pick(args, 0, ["high"], "null")),
46673
+ cellOf(pick(args, 1, ["low"], "null")),
46674
+ cellOf(pick(args, 2, ["period", "length"], "null"))
46675
+ ]);
45076
46676
  }
45077
46677
  if (method === "voi") {
45078
46678
  return `__h.ta.voi(${cellOf(pick(args, 0, ["buy_volume", "buy"], "null"))}, ${cellOf(pick(args, 1, ["sell_volume", "sell"], "null"))})`;
@@ -45085,7 +46685,10 @@ function emitTa(ctx, method, args) {
45085
46685
  }
45086
46686
  if (method === "rsi_divergence") {
45087
46687
  const period = `((__n) => (__n == null ? 0 : __n))(${cellOf(pick(args, 1, ["period", "length"], "null"))})`;
45088
- return `__h.ta.rsiDivergence(${cellList(pick(args, 0, ["rsi_series", "series", "source"], "null"))}, ${period})`;
46688
+ return taCall("rsiDivergence", site, [
46689
+ cellLast(pick(args, 0, ["rsi_series", "series", "source"], "null")),
46690
+ period
46691
+ ]);
45089
46692
  }
45090
46693
  if (method === "cumulative_delta") {
45091
46694
  return taCall("cumulativeDelta", site, [
@@ -45120,6 +46723,368 @@ function emitTa(ctx, method, args) {
45120
46723
  `((__n) => (__n == null ? 0.3 : __n))(${sens})`
45121
46724
  ]);
45122
46725
  }
46726
+ if (method === "sma_weighted") {
46727
+ return taCall("smaWeighted", site, [
46728
+ cellOf(pick(args, 0, ["series", "source"], "null")),
46729
+ cellOf(pick(args, 1, ["period", "length"], "null")),
46730
+ pick(args, 2, ["weight_type", "weight"], '"linear"')
46731
+ ]);
46732
+ }
46733
+ if (method === "apo") {
46734
+ return taCall("apo", site, [
46735
+ cellOf(pick(args, 0, ["series", "source"], "null")),
46736
+ cellOf(pick(args, 1, ["fast_period", "fast"], "null")),
46737
+ cellOf(pick(args, 2, ["slow_period", "slow"], "null"))
46738
+ ]);
46739
+ }
46740
+ if (method === "atr_normalized") {
46741
+ return taCall("atrNormalized", site, [
46742
+ cellOf(pick(args, 0, ["high"], "null")),
46743
+ cellOf(pick(args, 1, ["low"], "null")),
46744
+ cellOf(pick(args, 2, ["close"], "null")),
46745
+ cellOf(pick(args, 3, ["period", "length"], "null"))
46746
+ ]);
46747
+ }
46748
+ if (method === "beta" || method === "r_squared" || method === "comovement") {
46749
+ const fn = method === "beta" ? "beta" : method === "r_squared" ? "rSquared" : "comovement";
46750
+ return taCall(fn, site, [
46751
+ cellOf(pick(args, 0, ["series1", "series", "source"], "null")),
46752
+ cellOf(pick(args, 1, ["series2", "series_b", "other"], "null")),
46753
+ cellOf(pick(args, 2, ["length", "period"], "null"))
46754
+ ]);
46755
+ }
46756
+ if (method === "skewness" || method === "kurtosis") {
46757
+ return taCall(method, site, [
46758
+ cellOf(pick(args, 0, ["series", "source"], "null")),
46759
+ cellOf(pick(args, 1, ["period", "length"], "null"))
46760
+ ]);
46761
+ }
46762
+ if (method === "volume_weighted_momentum") {
46763
+ return taCall("volumeWeightedMomentum", site, [
46764
+ cellOf(pick(args, 0, ["series", "source"], "null")),
46765
+ cellOf(pick(args, 1, ["volume"], "null")),
46766
+ cellOf(pick(args, 2, ["length", "period"], "null"))
46767
+ ]);
46768
+ }
46769
+ if (method === "parkinson") {
46770
+ if (hasSeriesArg(args))
46771
+ return "null";
46772
+ return `__h.ta.parkinson(${cellOf(pick(args, 0, ["high"], "null"))}, ${cellOf(pick(args, 1, ["low"], "null"))})`;
46773
+ }
46774
+ if (method === "garman_klass") {
46775
+ if (hasSeriesArg(args))
46776
+ return "null";
46777
+ return `__h.ta.garmanKlass(${cellOf(pick(args, 0, ["high"], "null"))}, ${cellOf(pick(args, 1, ["low"], "null"))}, ${cellOf(pick(args, 2, ["close"], "null"))}, ${cellOf(pick(args, 3, ["open"], "null"))})`;
46778
+ }
46779
+ if (method === "mean_reversion_score") {
46780
+ return taCall("meanReversionScore", site, [
46781
+ cellOf(pick(args, 0, ["close", "source"], "null")),
46782
+ cellOf(pick(args, 1, ["sma"], "null")),
46783
+ cellOf(pick(args, 2, ["stdev", "std_dev"], "null")),
46784
+ cellOf(pick(args, 3, ["period", "length"], "null"))
46785
+ ]);
46786
+ }
46787
+ if (method === "market_condition") {
46788
+ return taCall("marketCondition", site, [
46789
+ cellOf(pick(args, 0, ["close", "source"], "null")),
46790
+ cellOf(pick(args, 1, ["atr"], "null")),
46791
+ cellOf(pick(args, 2, ["sma_period", "sma_length"], "null")),
46792
+ cellOf(pick(args, 3, ["stdev_period", "stdev_length"], "null"))
46793
+ ]);
46794
+ }
46795
+ if (method === "order_flow_imbalance") {
46796
+ return taCall("orderFlowImbalance", site, [
46797
+ cellOf(pick(args, 0, ["high"], "null")),
46798
+ cellOf(pick(args, 1, ["low"], "null")),
46799
+ cellOf(pick(args, 2, ["close"], "null")),
46800
+ cellOf(pick(args, 3, ["volume"], "null")),
46801
+ cellOf(pick(args, 4, ["period", "length"], "null"))
46802
+ ]);
46803
+ }
46804
+ if (method === "liquidity_score") {
46805
+ return taCall("liquidityScore", site, [
46806
+ cellOf(pick(args, 0, ["volume"], "null")),
46807
+ cellOf(pick(args, 1, ["volatility"], "null")),
46808
+ cellOf(pick(args, 2, ["spread"], "null")),
46809
+ cellOf(pick(args, 3, ["period", "length"], "null"))
46810
+ ]);
46811
+ }
46812
+ if (method === "fear_greed_index") {
46813
+ return `__h.ta.fearGreedIndex(${cellOf(pick(args, 0, ["rsi"], "null"))}, ${cellOf(pick(args, 1, ["vix"], "null"))}, ${cellOf(pick(args, 2, ["put_call", "put_call_ratio"], "null"))}, ${cellOf(pick(args, 3, ["breadth"], "null"))})`;
46814
+ }
46815
+ if (method === "crowd_sentiment") {
46816
+ return `__h.ta.crowdSentiment(${cellOf(pick(args, 0, ["price_agreement", "price"], "null"))}, ${cellOf(pick(args, 1, ["volume_agreement", "volume"], "null"))}, ${cellOf(pick(args, 2, ["time_agreement", "time"], "null"))})`;
46817
+ }
46818
+ if (method === "smart_money_flow") {
46819
+ return `__h.ta.smartMoneyFlow(${cellOf(pick(args, 0, ["price_change"], "null"))}, ${cellOf(pick(args, 1, ["volume"], "null"))}, ${cellOf(pick(args, 2, ["time_since_high", "time_high"], "null"))}, ${cellOf(pick(args, 3, ["time_since_low", "time_low"], "null"))})`;
46820
+ }
46821
+ if (method === "trend_confirmation_score") {
46822
+ return `__h.ta.trendConfirmationScore(${cellOf(pick(args, 0, ["momentum"], "null"))}, ${cellOf(pick(args, 1, ["trend_alignment"], "null"))}, ${cellOf(pick(args, 2, ["strength"], "null"))}, ${cellOf(pick(args, 3, ["rsi"], "null"))}, ${cellOf(pick(args, 4, ["rsi_alignment"], "null"))})`;
46823
+ }
46824
+ if (method === "max_loss_level") {
46825
+ return `__h.ta.maxLossLevel(${cellOf(pick(args, 0, ["entry", "entry_price"], "null"))}, ${cellOf(pick(args, 1, ["account", "account_size"], "null"))}, ${cellOf(pick(args, 2, ["max_loss_pct", "max_loss_percent"], "null"))})`;
46826
+ }
46827
+ if (method === "profit_lock_level") {
46828
+ return `__h.ta.profitLockLevel(${cellOf(pick(args, 0, ["entry", "entry_price"], "null"))}, ${cellOf(pick(args, 1, ["current", "current_price"], "null"))}, ${cellOf(pick(args, 2, ["trail_pct", "trail_percent"], "null"))}, ${cellOf(pick(args, 3, ["direction"], "null"))})`;
46829
+ }
46830
+ if (method === "position_sizing") {
46831
+ return taPure("positionSizing", [
46832
+ cellOf(pick(args, 0, ["account", "account_size"], "null")),
46833
+ cellOf(pick(args, 1, ["risk_pct", "risk_percent"], "null")),
46834
+ cellOf(pick(args, 2, ["entry", "entry_price"], "null")),
46835
+ cellOf(pick(args, 3, ["stop", "stop_price"], "null"))
46836
+ ]);
46837
+ }
46838
+ if (method === "kelly_criterion") {
46839
+ return taPure("kellyCriterion", [
46840
+ cellOf(pick(args, 0, ["win_rate"], "null")),
46841
+ cellOf(pick(args, 1, ["avg_win"], "null")),
46842
+ cellOf(pick(args, 2, ["avg_loss"], "null"))
46843
+ ]);
46844
+ }
46845
+ if (method === "risk_reward_ratio") {
46846
+ return taPure("riskRewardRatio", [
46847
+ cellOf(pick(args, 0, ["entry", "entry_price"], "null")),
46848
+ cellOf(pick(args, 1, ["stop", "stop_price"], "null")),
46849
+ cellOf(pick(args, 2, ["target", "target_price"], "null"))
46850
+ ]);
46851
+ }
46852
+ if (method === "risk_reward_asymmetry") {
46853
+ return taPure("riskRewardAsymmetry", [
46854
+ cellOf(pick(args, 0, ["entry", "entry_price"], "null")),
46855
+ cellOf(pick(args, 1, ["stop", "stop_price"], "null")),
46856
+ cellOf(pick(args, 2, ["target", "target_price"], "null")),
46857
+ cellOf(pick(args, 3, ["win_rate"], "null"))
46858
+ ]);
46859
+ }
46860
+ if (method === "probability_of_movement") {
46861
+ return taPure("probabilityOfMovement", [
46862
+ cellOf(pick(args, 0, ["current", "current_price"], "null")),
46863
+ cellOf(pick(args, 1, ["target", "target_price"], "null")),
46864
+ cellOf(pick(args, 2, ["atr"], "null")),
46865
+ cellOf(pick(args, 3, ["period", "length"], "null"))
46866
+ ]);
46867
+ }
46868
+ if (method === "trend_strength") {
46869
+ return taPure("trendStrength", [
46870
+ cellOf(pick(args, 0, ["price", "source"], "null")),
46871
+ cellOf(pick(args, 1, ["adx"], "null")),
46872
+ cellOf(pick(args, 2, ["rsi"], "null"))
46873
+ ]);
46874
+ }
46875
+ if (method === "breakeven_level") {
46876
+ return taPure("breakevenLevel", [
46877
+ cellOf(pick(args, 0, ["entry", "entry_price"], "null")),
46878
+ cellOf(pick(args, 1, ["quantity", "qty"], "null")),
46879
+ cellOf(pick(args, 2, ["commission"], "null")),
46880
+ cellOf(pick(args, 3, ["slippage"], "null")),
46881
+ cellOf(pick(args, 4, ["direction"], "null"))
46882
+ ]);
46883
+ }
46884
+ if (method === "market_structure_pivot") {
46885
+ return taCall("marketStructurePivot", site, [
46886
+ arrOrCell(pick(args, 0, ["high_list", "high"], "null")),
46887
+ arrOrCell(pick(args, 1, ["low_list", "low"], "null")),
46888
+ cellOf(pick(args, 3, ["period", "left", "left_bars"], "null")),
46889
+ cellOf(pick(args, 4, ["mode", "right", "right_bars"], "null"))
46890
+ ]);
46891
+ }
46892
+ if (method === "position_sizing_score") {
46893
+ return taPure("positionSizingScore", [
46894
+ cellOf(pick(args, 0, ["account_risk", "risk"], "null")),
46895
+ cellOf(pick(args, 1, ["volatility"], "null")),
46896
+ cellOf(pick(args, 2, ["conviction"], "null")),
46897
+ cellOf(pick(args, 3, ["correlation"], "null"))
46898
+ ]);
46899
+ }
46900
+ if (method === "trailing_exit_level") {
46901
+ return taPure("trailingExitLevel", [
46902
+ cellOf(pick(args, 0, ["entry", "entry_price"], "null")),
46903
+ cellOf(pick(args, 1, ["current", "current_price"], "null")),
46904
+ cellOf(pick(args, 2, ["atr"], "null")),
46905
+ cellOf(pick(args, 3, ["multiplier", "mult"], "null")),
46906
+ cellOf(pick(args, 4, ["step"], "null"))
46907
+ ]);
46908
+ }
46909
+ if (method === "optimal_entry_zone") {
46910
+ return taCall("optimalEntryZone", site, [arrOrCell(pick(args, 2, ["close"], "null"))]);
46911
+ }
46912
+ if (method === "mean_reversion_entry") {
46913
+ return taCall("meanReversionEntry", site, [
46914
+ arrOrCell(pick(args, 0, ["price", "source"], "null")),
46915
+ arrOrCell(pick(args, 1, ["sma"], "null")),
46916
+ arrOrCell(pick(args, 2, ["stdev", "std_dev"], "null")),
46917
+ cellOf(pick(args, 4, ["threshold"], "null"))
46918
+ ]);
46919
+ }
46920
+ if (method === "multi_timeframe_signal") {
46921
+ return taPure("multiTimeframeSignal", [
46922
+ cellOf(pick(args, 0, ["s1", "signal1"], "null")),
46923
+ cellOf(pick(args, 1, ["s2", "signal2"], "null")),
46924
+ cellOf(pick(args, 2, ["s3", "signal3"], "null")),
46925
+ cellOf(pick(args, 3, ["w1", "weight1"], "null")),
46926
+ cellOf(pick(args, 4, ["w2", "weight2"], "null")),
46927
+ cellOf(pick(args, 5, ["w3", "weight3"], "null"))
46928
+ ]);
46929
+ }
46930
+ if (method === "market_timing_index") {
46931
+ return taPure("marketTimingIndex", [
46932
+ cellOf(pick(args, 0, ["trend"], "null")),
46933
+ cellOf(pick(args, 1, ["momentum"], "null")),
46934
+ cellOf(pick(args, 2, ["volatility"], "null")),
46935
+ cellOf(pick(args, 3, ["sentiment"], "null"))
46936
+ ]);
46937
+ }
46938
+ if (method === "regime_adaptive_signal") {
46939
+ return taPure("regimeAdaptiveSignal", [
46940
+ cellOf(pick(args, 0, ["signal_strength", "strength"], "null")),
46941
+ pick(args, 1, ["volatility_regime", "vol_regime"], '""'),
46942
+ pick(args, 2, ["trend_regime"], '""')
46943
+ ]);
46944
+ }
46945
+ if (method === "strategy_score") {
46946
+ return taPure("strategyScore", [
46947
+ cellOf(pick(args, 0, ["rsi"], "null")),
46948
+ cellOf(pick(args, 1, ["macd"], "null")),
46949
+ cellOf(pick(args, 2, ["ema_cross"], "null")),
46950
+ cellOf(pick(args, 3, ["trend"], "null"))
46951
+ ]);
46952
+ }
46953
+ if (method === "breakout_detection") {
46954
+ return taPure("breakoutDetection", [
46955
+ cellOf(pick(args, 0, ["close", "source"], "null")),
46956
+ cellOf(pick(args, 1, ["resistance"], "null")),
46957
+ cellOf(pick(args, 2, ["support"], "null"))
46958
+ ]);
46959
+ }
46960
+ if (method === "ema_cross_signal") {
46961
+ return taCall("emaCrossSignal", site, [
46962
+ cellOf(pick(args, 0, ["fast_ema", "fast"], "null")),
46963
+ cellOf(pick(args, 1, ["slow_ema", "slow"], "null")),
46964
+ cellOf(pick(args, 2, ["threshold"], "null"))
46965
+ ]);
46966
+ }
46967
+ if (method === "momentum_filter") {
46968
+ return taCall("momentumFilter", site, [
46969
+ cellOf(pick(args, 0, ["momentum_raw", "momentum"], "null")),
46970
+ cellOf(pick(args, 1, ["volume"], "null")),
46971
+ cellOf(pick(args, 2, ["period", "length"], "null"))
46972
+ ]);
46973
+ }
46974
+ if (method === "volatility_regime") {
46975
+ return taCall("volatilityRegime", site, [
46976
+ cellOf(pick(args, 0, ["atr_series", "atr", "source"], "null")),
46977
+ cellOf(pick(args, 1, ["period", "length"], "null"))
46978
+ ]);
46979
+ }
46980
+ if (method === "economic_impact_score") {
46981
+ return taPure("economicImpactScore", [
46982
+ cellOf(pick(args, 0, ["price_change"], "null")),
46983
+ cellOf(pick(args, 1, ["volatility"], "null")),
46984
+ cellOf(pick(args, 2, ["volume_change"], "null"))
46985
+ ]);
46986
+ }
46987
+ if (method === "contrarian_signal") {
46988
+ return taPure("contrarianSignal", [
46989
+ cellOf(pick(args, 0, ["sentiment"], "null")),
46990
+ cellOf(pick(args, 1, ["volatility"], "null")),
46991
+ cellOf(pick(args, 2, ["time_since_extreme", "time_extreme"], "null"))
46992
+ ]);
46993
+ }
46994
+ if (method === "drawdown_recovery_level") {
46995
+ return taPure("drawdownRecoveryLevel", [
46996
+ cellOf(pick(args, 0, ["peak"], "null")),
46997
+ cellOf(pick(args, 1, ["current", "current_price"], "null")),
46998
+ cellOf(pick(args, 2, ["recovery_factor", "factor"], "null")),
46999
+ cellOf(pick(args, 3, ["period", "length"], "null"))
47000
+ ]);
47001
+ }
47002
+ if (method === "pivot_point_levels") {
47003
+ return taPure("pivotPointLevels", [
47004
+ cellOf(pick(args, 0, ["high"], "null")),
47005
+ cellOf(pick(args, 1, ["low"], "null")),
47006
+ cellOf(pick(args, 2, ["close"], "null")),
47007
+ pick(args, 3, ["traditional"], "true")
47008
+ ]);
47009
+ }
47010
+ if (method === "volatility_regime_score") {
47011
+ return taCall("volatilityRegimeScore", site, [
47012
+ arrOrCell(pick(args, 0, ["atr_list", "atr"], "null")),
47013
+ arrOrCell(pick(args, 1, ["volatility_list", "volatility"], "null")),
47014
+ arrOrCell(pick(args, 2, ["vix_list", "vix"], "null")),
47015
+ cellOf(pick(args, 3, ["threshold"], "null"))
47016
+ ]);
47017
+ }
47018
+ if (method === "correlation_filter") {
47019
+ return taCall("correlationFilter", site, [
47020
+ arrOrCell(pick(args, 0, ["signal1_list", "signal1", "s1"], "null")),
47021
+ arrOrCell(pick(args, 1, ["signal2_list", "signal2", "s2"], "null")),
47022
+ arrOrCell(pick(args, 2, ["signal3_list", "signal3", "s3"], "null")),
47023
+ cellOf(pick(args, 4, ["threshold"], "null"))
47024
+ ]);
47025
+ }
47026
+ if (method === "advanced_breakout_detector") {
47027
+ return taCall("advancedBreakoutDetector", site, [
47028
+ arrOrCell(pick(args, 0, ["price_list", "price"], "null")),
47029
+ arrOrCell(pick(args, 1, ["volume_list", "volume"], "null")),
47030
+ cellOf(pick(args, 2, ["level"], "null")),
47031
+ cellOf(pick(args, 3, ["lookback"], "null")),
47032
+ cellOf(pick(args, 4, ["volume_multiplier", "vol_mult"], "null"))
47033
+ ]);
47034
+ }
47035
+ if (method === "pullback_bounce_level") {
47036
+ return taCall("pullbackBounceLevel", site, [
47037
+ arrOrCell(pick(args, 0, ["high_list", "high"], "null")),
47038
+ arrOrCell(pick(args, 1, ["low_list", "low"], "null")),
47039
+ cellOf(pick(args, 3, ["trend_direction", "trend_dir"], "null")),
47040
+ cellOf(pick(args, 4, ["lookback"], "null"))
47041
+ ]);
47042
+ }
47043
+ if (method === "divergence_detector") {
47044
+ return taCall("divergenceDetector", site, [
47045
+ arrOrCell(pick(args, 0, ["price_list", "price"], "null")),
47046
+ arrOrCell(pick(args, 1, ["indicator_list", "indicator"], "null")),
47047
+ cellOf(pick(args, 2, ["lookback"], "null"))
47048
+ ]);
47049
+ }
47050
+ if (method === "momentum_divergence") {
47051
+ return taCall("momentumDivergence", site, [
47052
+ arrOrCell(pick(args, 0, ["price"], "null")),
47053
+ arrOrCell(pick(args, 1, ["momentum_fast", "mom_fast"], "null")),
47054
+ arrOrCell(pick(args, 2, ["momentum_slow", "mom_slow"], "null"))
47055
+ ]);
47056
+ }
47057
+ if (method === "spread_analysis") {
47058
+ return taCall("spreadAnalysis", site, [
47059
+ arrOrCell(pick(args, 0, ["bid"], "null")),
47060
+ arrOrCell(pick(args, 1, ["ask"], "null")),
47061
+ cellOf(pick(args, 2, ["period", "length"], "null"))
47062
+ ]);
47063
+ }
47064
+ if (method === "employment_cycle_indicator") {
47065
+ return taCall("employmentCycleIndicator", site, [
47066
+ arrOrCell(pick(args, 0, ["cyclical_stocks", "cyclical"], "null")),
47067
+ arrOrCell(pick(args, 1, ["defensive_stocks", "defensive"], "null")),
47068
+ arrOrCell(pick(args, 2, ["unemployment_proxy", "unemployment"], "null"))
47069
+ ]);
47070
+ }
47071
+ if (method === "gdp_growth_proxy") {
47072
+ return taCall("gdpGrowthProxy", site, [
47073
+ arrOrCell(pick(args, 0, ["market_breadth", "breadth"], "null")),
47074
+ arrOrCell(pick(args, 1, ["market_volume", "volume"], "null")),
47075
+ arrOrCell(pick(args, 2, ["price_momentum", "momentum"], "null"))
47076
+ ]);
47077
+ }
47078
+ if (method === "inflation_proxy_indicator") {
47079
+ return taCall("inflationProxyIndicator", site, [
47080
+ arrOrCell(pick(args, 0, ["usd_index", "usd"], "null")),
47081
+ arrOrCell(pick(args, 1, ["commodity_prices", "commodities"], "null")),
47082
+ arrOrCell(pick(args, 2, ["bond_yields", "yields"], "null"))
47083
+ ]);
47084
+ }
47085
+ if (method === "expected_value") {
47086
+ return `__h.ta.expectedValue(${cellList(pick(args, 0, ["returns"], "null"))}, ${cellList(pick(args, 1, ["probabilities", "probs"], "null"))})`;
47087
+ }
45123
47088
  return "null";
45124
47089
  }
45125
47090
 
@@ -46588,6 +48553,8 @@ function naNum(v) {
46588
48553
  function hold(v) {
46589
48554
  if (v != null && typeof v === "object")
46590
48555
  return v;
48556
+ if (typeof v === "string")
48557
+ return v;
46591
48558
  return naNum(v);
46592
48559
  }
46593
48560
  function pineEq(a, b) {
@@ -47280,8 +49247,23 @@ class CompileDraw {
47280
49247
  putNum(extra, "bottom", bottom);
47281
49248
  return this.book.boxNew(n3(bar, 0), Object.keys(extra).length ? extra : undefined);
47282
49249
  }
47283
- table_new(bar) {
47284
- return this.book.tableNew(n3(bar, 0));
49250
+ table_new(bar, position, columns, rows, bgcolor, frameColor, frameWidth, borderColor, borderWidth, forceOverlay) {
49251
+ const extra = {};
49252
+ if (position != null)
49253
+ extra.position = position;
49254
+ putNum(extra, "columns", columns);
49255
+ putNum(extra, "rows", rows);
49256
+ if (bgcolor != null)
49257
+ extra.bgcolor = bgcolor;
49258
+ if (frameColor != null)
49259
+ extra.frame_color = frameColor;
49260
+ putNum(extra, "frame_width", frameWidth);
49261
+ if (borderColor != null)
49262
+ extra.border_color = borderColor;
49263
+ putNum(extra, "border_width", borderWidth);
49264
+ if (forceOverlay != null)
49265
+ extra.force_overlay = forceOverlay;
49266
+ return this.book.tableNew(n3(bar, 0), Object.keys(extra).length ? extra : undefined);
47285
49267
  }
47286
49268
  polyline_new(bar, points) {
47287
49269
  const pts = pointsOf(points);
@@ -47915,7 +49897,8 @@ class CompileStrategy {
47915
49897
  open: num(open, this.mark),
47916
49898
  high: num(high, this.mark),
47917
49899
  low: num(low, this.mark),
47918
- close: this.mark
49900
+ close: this.mark,
49901
+ time: this.time
47919
49902
  };
47920
49903
  this.ohlc = ohlc;
47921
49904
  this.book.processPending(this.bar, ohlc);
@@ -47931,17 +49914,18 @@ class CompileStrategy {
47931
49914
  stop: opts?.stop,
47932
49915
  comment: opts?.comment,
47933
49916
  oca_name: opts?.oca_name,
47934
- oca_type: opts?.oca_type
49917
+ oca_type: opts?.oca_type,
49918
+ deferMarket: true
47935
49919
  });
47936
49920
  }
47937
49921
  close(id, qty) {
47938
- this.book.fillClose(this.bar, str(id, ""), this.mark, {
49922
+ this.book.placeClose(this.bar, str(id, ""), this.mark, {
47939
49923
  time: this.time,
47940
49924
  qty: optNum(qty)
47941
49925
  });
47942
49926
  }
47943
49927
  close_all(_comment) {
47944
- this.book.closeAll(this.bar, this.mark);
49928
+ this.book.placeCloseAll(this.bar, this.mark);
47945
49929
  }
47946
49930
  order(id, direction, qty, opts) {
47947
49931
  this.entry(id, direction, qty, opts);
@@ -47958,7 +49942,8 @@ class CompileStrategy {
47958
49942
  ...parsed,
47959
49943
  price: this.mark,
47960
49944
  time: this.time,
47961
- ohlc: this.ohlc
49945
+ ohlc: this.ohlc,
49946
+ deferMarket: true
47962
49947
  });
47963
49948
  }
47964
49949
  position_size() {
@@ -48326,8 +50311,8 @@ function createCompileHelpers(opts) {
48326
50311
  second: calendarSecond,
48327
50312
  dayofweek: calendarDayofweek,
48328
50313
  timestamp,
48329
- weekOfYear,
48330
- timeTradingDay,
50314
+ weekOfYear: calendarWeekOfYear,
50315
+ timeTradingDay: calendarTimeTradingDay,
48331
50316
  timeframeInSeconds: timeframeInSecondsOrDaily,
48332
50317
  timeframeChange,
48333
50318
  timeClose,
@@ -48451,7 +50436,20 @@ function NA_FN(v) {
48451
50436
  function utcPart(t, key) {
48452
50437
  if (typeof t !== "number" || !Number.isFinite(t))
48453
50438
  return null;
48454
- return utcPartsFromMs(t)[key];
50439
+ const ms = timestampToMs(t);
50440
+ return ms == null ? null : utcPartsFromMs(ms)[key];
50441
+ }
50442
+ function calendarWeekOfYear(t) {
50443
+ if (typeof t !== "number" || !Number.isFinite(t))
50444
+ return null;
50445
+ const ms = timestampToMs(t);
50446
+ return ms == null ? null : weekOfYear(ms);
50447
+ }
50448
+ function calendarTimeTradingDay(t) {
50449
+ if (typeof t !== "number" || !Number.isFinite(t))
50450
+ return null;
50451
+ const ms = timestampToMs(t);
50452
+ return ms == null ? null : timeTradingDay(ms);
48455
50453
  }
48456
50454
  function calendarYear(t) {
48457
50455
  return utcPart(t, "year");
@@ -48885,7 +50883,8 @@ function interpretTree(tree, ohlcv, host) {
48885
50883
  open: num2(env.ctx.open) ?? undefined,
48886
50884
  high: num2(env.ctx.high) ?? undefined,
48887
50885
  low: num2(env.ctx.low) ?? undefined,
48888
- close: num2(env.ctx.close) ?? undefined
50886
+ close: num2(env.ctx.close) ?? undefined,
50887
+ time: num2(env.ctx.time) ?? undefined
48889
50888
  });
48890
50889
  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);
48891
50890
  try {
@@ -49216,6 +51215,27 @@ function seriesGet(env, id) {
49216
51215
  }
49217
51216
  return env.series.get(id);
49218
51217
  }
51218
+ var BARE_SERIES_NAMES2 = new Set([
51219
+ "open",
51220
+ "high",
51221
+ "low",
51222
+ "close",
51223
+ "volume",
51224
+ "hl2",
51225
+ "hlc3",
51226
+ "ohlc4",
51227
+ "hlcc4",
51228
+ "time",
51229
+ "time_close"
51230
+ ]);
51231
+ function isSeriesExpr(value, env) {
51232
+ if (value.kind === "Name") {
51233
+ if (BARE_SERIES_NAMES2.has(value.id))
51234
+ return true;
51235
+ return seriesGet(env, value.id) != null;
51236
+ }
51237
+ return false;
51238
+ }
49219
51239
  function bindName(env, id, value) {
49220
51240
  const stored = typeof value === "number" && !Number.isFinite(value) ? NA : value;
49221
51241
  const frame = activeFrame(env);
@@ -50399,6 +52419,33 @@ function evalCall(node, env) {
50399
52419
  adjust: evalAsString(callArg(node.args, 3, ["adjust", "adjustment"]), env) ?? undefined
50400
52420
  });
50401
52421
  }
52422
+ if (fname === "ticker.inherit") {
52423
+ const first = evalExpr(callArg(node.args, 0, ["ticker", "tickerid", "from", "source"]), env);
52424
+ const secondArg = callArg(node.args, 1, ["symbol", "ticker"]);
52425
+ if (secondArg != null) {
52426
+ const target = evalExpr(secondArg, env);
52427
+ const sym = target instanceof TickerId ? target.symbol : typeof target === "string" ? target : env.symbol;
52428
+ if (first instanceof TickerId)
52429
+ return tickerNew(sym, first.session, first.adjust);
52430
+ return tickerNew(sym);
52431
+ }
52432
+ if (first instanceof TickerId) {
52433
+ return tickerNew(first.symbol, first.session, first.adjust);
52434
+ }
52435
+ const sym = typeof first === "string" ? first : env.symbol;
52436
+ return tickerNew(sym);
52437
+ }
52438
+ if (fname === "renko" || fname === "kagi" || fname === "linebreak" || fname === "pointfigure") {
52439
+ const raw = evalExpr(callArg(node.args, 0, ["ticker", "tickerid", "symbol"]), env);
52440
+ const sym = raw instanceof TickerId ? raw.symbol : typeof raw === "string" ? raw : env.symbol;
52441
+ if (fname === "renko")
52442
+ return tickerRenko(sym);
52443
+ if (fname === "kagi")
52444
+ return tickerKagi(sym);
52445
+ if (fname === "linebreak")
52446
+ return tickerLinebreak(sym);
52447
+ return tickerPointfigure(sym);
52448
+ }
50402
52449
  const mapVal = evalMapCall(fname, node, env);
50403
52450
  if (mapVal !== undefined)
50404
52451
  return mapVal;
@@ -50433,7 +52480,8 @@ function evalCall(node, env) {
50433
52480
  price: mark == null || !Number.isFinite(mark) ? undefined : mark,
50434
52481
  time: num2(env.ctx.time) ?? undefined,
50435
52482
  oca_name: evalAsString(callArg(node.args, -1, ["oca_name"]), env),
50436
- oca_type: evalAsString(callArg(node.args, -1, ["oca_type"]), env)
52483
+ oca_type: evalAsString(callArg(node.args, -1, ["oca_type"]), env),
52484
+ deferMarket: true
50437
52485
  });
50438
52486
  return NA;
50439
52487
  }
@@ -50442,13 +52490,10 @@ function evalCall(node, env) {
50442
52490
  const mark = num2(env.ctx.close);
50443
52491
  const qtyExpr = callArg(node.args, 1, ["qty"]);
50444
52492
  const qtyRaw = qtyExpr == null ? undefined : unwrap2(evalExpr(qtyExpr, env));
50445
- if (env.book.position.qty !== 0 && mark != null) {
50446
- env.book.fillClose(env.barIndex, id, mark, {
50447
- time: num2(env.ctx.time) ?? undefined,
50448
- qty: qtyRaw ?? undefined
50449
- });
50450
- } else
50451
- env.book.close(env.barIndex, id);
52493
+ env.book.placeClose(env.barIndex, id, mark ?? undefined, {
52494
+ time: num2(env.ctx.time) ?? undefined,
52495
+ qty: qtyRaw ?? undefined
52496
+ });
50452
52497
  return NA;
50453
52498
  }
50454
52499
  if (fname === "strategy.exit") {
@@ -50468,17 +52513,12 @@ function evalCall(node, env) {
50468
52513
  trail_offset: unwrap2(evalExpr(callArg(node.args, 10, ["trail_offset"]), env)),
50469
52514
  price: num2(env.ctx.close) ?? undefined,
50470
52515
  time: num2(env.ctx.time) ?? undefined,
50471
- ohlc: {
50472
- open: num2(env.ctx.open) ?? undefined,
50473
- high: num2(env.ctx.high) ?? undefined,
50474
- low: num2(env.ctx.low) ?? undefined,
50475
- close: num2(env.ctx.close) ?? undefined
50476
- }
52516
+ deferMarket: true
50477
52517
  });
50478
52518
  return NA;
50479
52519
  }
50480
52520
  if (fname === "strategy.close_all") {
50481
- env.book.closeAll(env.barIndex, num2(env.ctx.close) ?? undefined);
52521
+ env.book.placeCloseAll(env.barIndex, num2(env.ctx.close) ?? undefined);
50482
52522
  return NA;
50483
52523
  }
50484
52524
  if (fname === "strategy.cancel") {
@@ -50500,7 +52540,8 @@ function evalCall(node, env) {
50500
52540
  price: num2(env.ctx.close) ?? undefined,
50501
52541
  time: num2(env.ctx.time) ?? undefined,
50502
52542
  oca_name: evalAsString(callArg(node.args, -1, ["oca_name"]), env),
50503
- oca_type: evalAsString(callArg(node.args, -1, ["oca_type"]), env)
52543
+ oca_type: evalAsString(callArg(node.args, -1, ["oca_type"]), env),
52544
+ deferMarket: true
50504
52545
  });
50505
52546
  return NA;
50506
52547
  }
@@ -51725,12 +53766,12 @@ function evalExtraTa(fname, node, env, site) {
51725
53766
  return NA;
51726
53767
  return ta.percentrank.call(env.ta, site, srcArg(node, env), lenArg(node, env, 1));
51727
53768
  }
51728
- if (fname === "ta.percentile_nearest_rank") {
53769
+ if (fname === "ta.percentile_nearest_rank" || fname === "percentile_nearest_rank") {
51729
53770
  if (typeof ta.percentileNearest !== "function")
51730
53771
  return NA;
51731
53772
  return ta.percentileNearest.call(env.ta, site, srcArg(node, env), lenArg(node, env, 1), lenOrDefault(node, env, 2, ["percentage", "percent"], 50));
51732
53773
  }
51733
- if (fname === "ta.percentile_linear_interpolation") {
53774
+ if (fname === "ta.percentile_linear_interpolation" || fname === "percentile_linear_interpolation") {
51734
53775
  if (typeof ta.percentileLinear !== "function")
51735
53776
  return NA;
51736
53777
  return ta.percentileLinear.call(env.ta, site, srcArg(node, env), lenArg(node, env, 1), lenOrDefault(node, env, 2, ["percentage", "percent"], 50));
@@ -51845,12 +53886,6 @@ function evalExtraTa(fname, node, env, site) {
51845
53886
  const r = env.ta.aroon(site, num2(env.ctx.high), num2(env.ctx.low), length ?? 14);
51846
53887
  return tupleOf([r.down, r.up]);
51847
53888
  }
51848
- if (fname === "ta.pivot_point_levels") {
51849
- if (typeof ta.pivotPoints !== "function")
51850
- return NA;
51851
- const r = ta.pivotPoints.call(env.ta, site, num2(env.ctx.high), num2(env.ctx.low), num2(env.ctx.close));
51852
- return tupleOf([r.pp, r.r1, r.s1, r.r2, r.s2]);
51853
- }
51854
53889
  if (fname === "ta.uo" || fname === "uo") {
51855
53890
  return env.ta.uo(site, num2(env.ctx.high), num2(env.ctx.low), num2(env.ctx.close), lenArg(node, env, 0), lenArg(node, env, 1), lenArg(node, env, 2));
51856
53891
  }
@@ -51905,11 +53940,11 @@ function evalExtraTa(fname, node, env, site) {
51905
53940
  }
51906
53941
  if (fname === "ta.engulfing" || fname === "engulfing") {
51907
53942
  const msg = "ta.engulfing() requires 4 arguments: open, high, low, close";
51908
- unwrap2(evalExpr(wave10aArg(0, ["open"], msg), env));
53943
+ const open = unwrap2(evalExpr(wave10aArg(0, ["open"], msg), env));
51909
53944
  unwrap2(evalExpr(wave10aArg(1, ["high"], msg), env));
51910
53945
  unwrap2(evalExpr(wave10aArg(2, ["low"], msg), env));
51911
- unwrap2(evalExpr(wave10aArg(3, ["close"], msg), env));
51912
- return env.ta.engulfing();
53946
+ const close = unwrap2(evalExpr(wave10aArg(3, ["close"], msg), env));
53947
+ return env.ta.engulfing(site, open, close);
51913
53948
  }
51914
53949
  if (fname === "ta.gap_detector" || fname === "gap_detector") {
51915
53950
  const msg = "ta.gap_detector() requires 3 arguments: high, low, prev_close";
@@ -51917,15 +53952,15 @@ function evalExtraTa(fname, node, env, site) {
51917
53952
  }
51918
53953
  if (fname === "ta.double_top_bottom" || fname === "double_top_bottom") {
51919
53954
  const msg = "ta.double_top_bottom() requires 3 arguments";
51920
- unwrap2(evalExpr(wave10aArg(0, ["high"], msg), env));
51921
- unwrap2(evalExpr(wave10aArg(1, ["low"], msg), env));
51922
- return env.ta.doubleTopBottom(unwrap2(evalExpr(wave10aArg(2, ["period", "length"], msg), env)));
53955
+ const high = unwrap2(evalExpr(wave10aArg(0, ["high"], msg), env));
53956
+ const low = unwrap2(evalExpr(wave10aArg(1, ["low"], msg), env));
53957
+ return env.ta.doubleTopBottom(site, high, low, unwrap2(evalExpr(wave10aArg(2, ["period", "length"], msg), env)));
51923
53958
  }
51924
53959
  if (fname === "ta.inside_bar_pattern" || fname === "inside_bar_pattern") {
51925
53960
  const msg = "ta.inside_bar_pattern() requires 2 arguments";
51926
- unwrap2(evalExpr(wave10aArg(0, ["high"], msg), env));
51927
- unwrap2(evalExpr(wave10aArg(1, ["low"], msg), env));
51928
- return env.ta.insideBarPattern();
53961
+ const high = unwrap2(evalExpr(wave10aArg(0, ["high"], msg), env));
53962
+ const low = unwrap2(evalExpr(wave10aArg(1, ["low"], msg), env));
53963
+ return env.ta.insideBarPattern(site, high, low);
51929
53964
  }
51930
53965
  if (fname === "ta.macd_signal" || fname === "macd_signal") {
51931
53966
  const msg = "ta.macd_signal() requires 2 arguments: macd_line, signal_line";
@@ -51937,7 +53972,7 @@ function evalExtraTa(fname, node, env, site) {
51937
53972
  }
51938
53973
  if (fname === "ta.stoch_smooth" || fname === "stoch_smooth") {
51939
53974
  const msg = "ta.stoch_smooth() requires 6 arguments: high, low, close, period, smooth_k, smooth_d";
51940
- return env.ta.stochSmooth(unwrap2(evalExpr(wave10aArg(0, ["high"], msg), env)), unwrap2(evalExpr(wave10aArg(1, ["low"], msg), env)), unwrap2(evalExpr(wave10aArg(2, ["close"], msg), env)), unwrap2(evalExpr(wave10aArg(3, ["period", "length"], msg), env)), unwrap2(evalExpr(wave10aArg(4, ["smooth_k", "k"], msg), env)), unwrap2(evalExpr(wave10aArg(5, ["smooth_d", "d"], msg), env)));
53975
+ return env.ta.stochSmooth(site, unwrap2(evalExpr(wave10aArg(0, ["high"], msg), env)), unwrap2(evalExpr(wave10aArg(1, ["low"], msg), env)), unwrap2(evalExpr(wave10aArg(2, ["close"], msg), env)), unwrap2(evalExpr(wave10aArg(3, ["period", "length"], msg), env)), unwrap2(evalExpr(wave10aArg(4, ["smooth_k", "k"], msg), env)), unwrap2(evalExpr(wave10aArg(5, ["smooth_d", "d"], msg), env)));
51941
53976
  }
51942
53977
  const wave10bList = (index, names) => {
51943
53978
  const raw = evalExpr(callArg(node.args, index, names), env);
@@ -51962,7 +53997,7 @@ function evalExtraTa(fname, node, env, site) {
51962
53997
  }
51963
53998
  if (fname === "ta.rsi_divergence" || fname === "rsi_divergence") {
51964
53999
  const period = unwrap2(evalExpr(callArg(node.args, 1, ["period", "length"]), env));
51965
- return env.ta.rsiDivergence(wave10bList(0, ["rsi_series", "series", "source"]), period ?? 0);
54000
+ return env.ta.rsiDivergence(site, seriesLastCell(evalExpr(callArg(node.args, 0, ["rsi_series", "series", "source"]), env)), period ?? 0);
51966
54001
  }
51967
54002
  if (fname === "ta.cumulative_delta" || fname === "cumulative_delta") {
51968
54003
  const period = unwrap2(evalExpr(callArg(node.args, 2, ["period", "length"]), env));
@@ -51984,6 +54019,281 @@ function evalExtraTa(fname, node, env, site) {
51984
54019
  const sens = unwrap2(evalExpr(callArg(node.args, 3, ["sensitivity"]), env));
51985
54020
  return env.ta.volumeThrust(site, unwrap2(evalExpr(callArg(node.args, 0, ["close", "source"]), env)), unwrap2(evalExpr(callArg(node.args, 1, ["volume"]), env)), unwrap2(evalExpr(callArg(node.args, 2, ["volume_sma", "vol_sma", "sma"]), env)), sens ?? 0.3);
51986
54021
  }
54022
+ const wave11 = (index, names, message) => unwrap2(evalExpr(wave10aArg(index, names, message), env));
54023
+ const wave11n = (index, names, message) => num2(wave11(index, names, message));
54024
+ const wave13v = (index, names, message) => evalExpr(wave10aArg(index, names, message), env);
54025
+ if (fname === "ta.sma_weighted" || fname === "sma_weighted") {
54026
+ const msg = "ta.sma_weighted() requires at least 2 arguments: series, period";
54027
+ const weightArg = callArg(node.args, 2, ["weight_type", "weight"]);
54028
+ return env.ta.smaWeighted(site, wave11n(0, ["series", "source"], msg), wave11n(1, ["period", "length"], msg), weightArg == null ? "linear" : evalExpr(weightArg, env));
54029
+ }
54030
+ if (fname === "ta.apo" || fname === "apo") {
54031
+ const msg = "ta.apo() requires 3 arguments: series, fast_period, slow_period";
54032
+ return env.ta.apo(site, wave11n(0, ["series", "source"], msg), wave11n(1, ["fast_period", "fast"], msg), wave11n(2, ["slow_period", "slow"], msg));
54033
+ }
54034
+ if (fname === "ta.atr_normalized" || fname === "atr_normalized") {
54035
+ const msg = "ta.atr_normalized() requires 4 arguments: high, low, close, period";
54036
+ return env.ta.atrNormalized(site, wave11n(0, ["high"], msg), wave11n(1, ["low"], msg), wave11n(2, ["close"], msg), wave11n(3, ["period", "length"], msg));
54037
+ }
54038
+ if (fname === "ta.beta" || fname === "beta") {
54039
+ const msg = "ta.beta() requires 3 arguments: series1, series2, length";
54040
+ return env.ta.beta(site, wave11n(0, ["series1", "series", "source"], msg), wave11n(1, ["series2", "series_b", "other"], msg), wave11n(2, ["length", "period"], msg));
54041
+ }
54042
+ if (fname === "ta.r_squared" || fname === "r_squared") {
54043
+ const msg = "ta.r_squared() requires 3 arguments: series1, series2, length";
54044
+ return env.ta.rSquared(site, wave11n(0, ["series1", "series", "source"], msg), wave11n(1, ["series2", "series_b", "other"], msg), wave11n(2, ["length", "period"], msg));
54045
+ }
54046
+ if (fname === "ta.comovement" || fname === "comovement") {
54047
+ const msg = "ta.comovement() requires 3 arguments: series1, series2, length";
54048
+ return env.ta.comovement(site, wave11n(0, ["series1", "series", "source"], msg), wave11n(1, ["series2", "series_b", "other"], msg), wave11n(2, ["length", "period"], msg));
54049
+ }
54050
+ if (fname === "ta.skewness" || fname === "skewness") {
54051
+ const msg = "ta.skewness() requires 2 arguments: series, period";
54052
+ return env.ta.skewness(site, wave11n(0, ["series", "source"], msg), wave11n(1, ["period", "length"], msg));
54053
+ }
54054
+ if (fname === "ta.kurtosis" || fname === "kurtosis") {
54055
+ const msg = "ta.kurtosis() requires 2 arguments: series, period";
54056
+ return env.ta.kurtosis(site, wave11n(0, ["series", "source"], msg), wave11n(1, ["period", "length"], msg));
54057
+ }
54058
+ if (fname === "ta.volume_weighted_momentum" || fname === "volume_weighted_momentum") {
54059
+ const msg = "ta.volume_weighted_momentum() requires 3 arguments: series, volume, length";
54060
+ return env.ta.volumeWeightedMomentum(site, wave11n(0, ["series", "source"], msg), wave11n(1, ["volume"], msg), wave11n(2, ["length", "period"], msg));
54061
+ }
54062
+ if (fname === "ta.parkinson" || fname === "parkinson") {
54063
+ const msg = "ta.parkinson() requires 2 arguments: high, low";
54064
+ const highArg = wave10aArg(0, ["high"], msg);
54065
+ const lowArg = wave10aArg(1, ["low"], msg);
54066
+ if (isSeriesExpr(highArg, env) || isSeriesExpr(lowArg, env))
54067
+ return NA;
54068
+ return env.ta.parkinson(num2(evalExpr(highArg, env)), num2(evalExpr(lowArg, env)));
54069
+ }
54070
+ if (fname === "ta.garman_klass" || fname === "garman_klass") {
54071
+ const msg = "ta.garman_klass() requires 4 arguments: high, low, close, open";
54072
+ const argsExpr = [
54073
+ wave10aArg(0, ["high"], msg),
54074
+ wave10aArg(1, ["low"], msg),
54075
+ wave10aArg(2, ["close"], msg),
54076
+ wave10aArg(3, ["open"], msg)
54077
+ ];
54078
+ if (argsExpr.some((a) => isSeriesExpr(a, env)))
54079
+ return NA;
54080
+ return env.ta.garmanKlass(num2(evalExpr(argsExpr[0], env)), num2(evalExpr(argsExpr[1], env)), num2(evalExpr(argsExpr[2], env)), num2(evalExpr(argsExpr[3], env)));
54081
+ }
54082
+ if (fname === "ta.mean_reversion_score" || fname === "mean_reversion_score") {
54083
+ const msg = "ta.mean_reversion_score() requires 4 arguments";
54084
+ return env.ta.meanReversionScore(site, wave11n(0, ["close", "source"], msg), wave11n(1, ["sma"], msg), wave11n(2, ["stdev", "std_dev"], msg), wave11n(3, ["period", "length"], msg));
54085
+ }
54086
+ if (fname === "ta.market_condition" || fname === "market_condition") {
54087
+ const msg = "ta.market_condition() requires 4 arguments";
54088
+ return env.ta.marketCondition(site, wave11n(0, ["close", "source"], msg), wave11n(1, ["atr"], msg), wave11n(2, ["sma_period", "sma_length"], msg), wave11n(3, ["stdev_period", "stdev_length"], msg));
54089
+ }
54090
+ if (fname === "ta.order_flow_imbalance" || fname === "order_flow_imbalance") {
54091
+ const msg = "ta.order_flow_imbalance() requires 5 arguments";
54092
+ return env.ta.orderFlowImbalance(site, wave11n(0, ["high"], msg), wave11n(1, ["low"], msg), wave11n(2, ["close"], msg), wave11n(3, ["volume"], msg), wave11n(4, ["period", "length"], msg));
54093
+ }
54094
+ if (fname === "ta.liquidity_score" || fname === "liquidity_score") {
54095
+ const msg = "ta.liquidity_score() requires 4 arguments";
54096
+ return env.ta.liquidityScore(site, wave11n(0, ["volume"], msg), wave11n(1, ["volatility"], msg), wave11n(2, ["spread"], msg), wave11n(3, ["period", "length"], msg));
54097
+ }
54098
+ if (fname === "ta.fear_greed_index" || fname === "fear_greed_index") {
54099
+ const msg = "ta.fear_greed_index() requires 4 arguments";
54100
+ return env.ta.fearGreedIndex(wave11n(0, ["rsi"], msg), wave11n(1, ["vix"], msg), wave11n(2, ["put_call", "put_call_ratio"], msg), wave11n(3, ["breadth"], msg));
54101
+ }
54102
+ if (fname === "ta.crowd_sentiment" || fname === "crowd_sentiment") {
54103
+ const msg = "ta.crowd_sentiment() requires 3 arguments";
54104
+ return env.ta.crowdSentiment(wave11n(0, ["price_agreement", "price"], msg), wave11n(1, ["volume_agreement", "volume"], msg), wave11n(2, ["time_agreement", "time"], msg));
54105
+ }
54106
+ if (fname === "ta.smart_money_flow" || fname === "smart_money_flow") {
54107
+ const msg = "ta.smart_money_flow() requires 4 arguments";
54108
+ return env.ta.smartMoneyFlow(wave11n(0, ["price_change"], msg), wave11n(1, ["volume"], msg), wave11n(2, ["time_since_high", "time_high"], msg), wave11n(3, ["time_since_low", "time_low"], msg));
54109
+ }
54110
+ if (fname === "ta.trend_confirmation_score" || fname === "trend_confirmation_score") {
54111
+ const msg = "ta.trend_confirmation_score() requires 6 arguments";
54112
+ wave11n(5, ["support_distance"], msg);
54113
+ return env.ta.trendConfirmationScore(wave11n(0, ["momentum"], msg), wave11n(1, ["trend_alignment"], msg), wave11n(2, ["strength"], msg), wave11n(3, ["rsi"], msg), wave11n(4, ["rsi_alignment"], msg));
54114
+ }
54115
+ if (fname === "ta.max_loss_level" || fname === "max_loss_level") {
54116
+ const msg = "ta.max_loss_level() requires 3 arguments";
54117
+ return env.ta.maxLossLevel(wave11n(0, ["entry", "entry_price"], msg), wave11n(1, ["account", "account_size"], msg), wave11n(2, ["max_loss_pct", "max_loss_percent"], msg));
54118
+ }
54119
+ if (fname === "ta.profit_lock_level" || fname === "profit_lock_level") {
54120
+ const msg = "ta.profit_lock_level() requires 4 arguments";
54121
+ return env.ta.profitLockLevel(wave11n(0, ["entry", "entry_price"], msg), wave11n(1, ["current", "current_price"], msg), wave11n(2, ["trail_pct", "trail_percent"], msg), wave11n(3, ["direction"], msg));
54122
+ }
54123
+ const wave12s = (index, names, message) => evalExpr(wave10aArg(index, names, message), env);
54124
+ if (fname === "ta.position_sizing" || fname === "position_sizing") {
54125
+ const msg = "ta.position_sizing() requires 4 arguments";
54126
+ return env.ta.positionSizing(wave11n(0, ["account", "account_size"], msg), wave11n(1, ["risk_pct", "risk_percent"], msg), wave11n(2, ["entry", "entry_price"], msg), wave11n(3, ["stop", "stop_price"], msg));
54127
+ }
54128
+ if (fname === "ta.kelly_criterion" || fname === "kelly_criterion") {
54129
+ const msg = "ta.kelly_criterion() requires 3 arguments";
54130
+ return env.ta.kellyCriterion(wave11n(0, ["win_rate"], msg), wave11n(1, ["avg_win"], msg), wave11n(2, ["avg_loss"], msg));
54131
+ }
54132
+ if (fname === "ta.risk_reward_ratio" || fname === "risk_reward_ratio") {
54133
+ const msg = "ta.risk_reward_ratio() requires 3 arguments";
54134
+ return env.ta.riskRewardRatio(wave11n(0, ["entry", "entry_price"], msg), wave11n(1, ["stop", "stop_price"], msg), wave11n(2, ["target", "target_price"], msg));
54135
+ }
54136
+ if (fname === "ta.risk_reward_asymmetry" || fname === "risk_reward_asymmetry") {
54137
+ const msg = "ta.risk_reward_asymmetry() requires 4 arguments";
54138
+ return env.ta.riskRewardAsymmetry(wave11n(0, ["entry", "entry_price"], msg), wave11n(1, ["stop", "stop_price"], msg), wave11n(2, ["target", "target_price"], msg), wave11n(3, ["win_rate"], msg));
54139
+ }
54140
+ if (fname === "ta.probability_of_movement" || fname === "probability_of_movement") {
54141
+ const msg = "ta.probability_of_movement() requires 4 arguments";
54142
+ return env.ta.probabilityOfMovement(wave11n(0, ["current", "current_price"], msg), wave11n(1, ["target", "target_price"], msg), wave11n(2, ["atr"], msg), wave11n(3, ["period", "length"], msg));
54143
+ }
54144
+ if (fname === "ta.trend_strength" || fname === "trend_strength") {
54145
+ const msg = "ta.trend_strength() requires 3 arguments";
54146
+ return env.ta.trendStrength(wave11n(0, ["price", "source"], msg), wave11n(1, ["adx"], msg), wave11n(2, ["rsi"], msg));
54147
+ }
54148
+ if (fname === "ta.breakeven_level" || fname === "breakeven_level") {
54149
+ const msg = "ta.breakeven_level() requires 5 arguments";
54150
+ return env.ta.breakevenLevel(wave11n(0, ["entry", "entry_price"], msg), wave11n(1, ["quantity", "qty"], msg), wave11n(2, ["commission"], msg), wave11n(3, ["slippage"], msg), wave11n(4, ["direction"], msg));
54151
+ }
54152
+ if (fname === "ta.market_structure_pivot" || fname === "market_structure_pivot") {
54153
+ const msg = "ta.market_structure_pivot() requires 5 arguments";
54154
+ const high = wave13v(0, ["high_list", "high"], msg);
54155
+ const low = wave13v(1, ["low_list", "low"], msg);
54156
+ wave13v(2, ["close_list", "close"], msg);
54157
+ const period = wave11n(3, ["period", "left", "left_bars"], msg);
54158
+ const mode = wave11n(4, ["mode", "right", "right_bars"], msg);
54159
+ return env.ta.marketStructurePivot(site, high, low, period, mode);
54160
+ }
54161
+ if (fname === "ta.position_sizing_score" || fname === "position_sizing_score") {
54162
+ const msg = "ta.position_sizing_score() requires 4 arguments";
54163
+ return env.ta.positionSizingScore(wave11n(0, ["account_risk", "risk"], msg), wave11n(1, ["volatility"], msg), wave11n(2, ["conviction"], msg), wave11n(3, ["correlation"], msg));
54164
+ }
54165
+ if (fname === "ta.trailing_exit_level" || fname === "trailing_exit_level") {
54166
+ const msg = "ta.trailing_exit_level() requires 5 arguments";
54167
+ return env.ta.trailingExitLevel(wave11n(0, ["entry", "entry_price"], msg), wave11n(1, ["current", "current_price"], msg), wave11n(2, ["atr"], msg), wave11n(3, ["multiplier", "mult"], msg), wave11n(4, ["step"], msg));
54168
+ }
54169
+ if (fname === "ta.optimal_entry_zone" || fname === "optimal_entry_zone") {
54170
+ const msg = "ta.optimal_entry_zone() requires 4 arguments";
54171
+ wave11n(0, ["high"], msg);
54172
+ wave11n(1, ["low"], msg);
54173
+ const close = wave13v(2, ["close"], msg);
54174
+ wave11n(3, ["period", "length"], msg);
54175
+ return env.ta.optimalEntryZone(site, close);
54176
+ }
54177
+ if (fname === "ta.mean_reversion_entry" || fname === "mean_reversion_entry") {
54178
+ const msg = "ta.mean_reversion_entry() requires 5 arguments";
54179
+ return env.ta.meanReversionEntry(site, wave13v(0, ["price", "source"], msg), wave13v(1, ["sma"], msg), wave13v(2, ["stdev", "std_dev"], msg), wave11n(4, ["threshold"], msg));
54180
+ }
54181
+ if (fname === "ta.multi_timeframe_signal" || fname === "multi_timeframe_signal") {
54182
+ const msg = "ta.multi_timeframe_signal() requires 6 arguments";
54183
+ return env.ta.multiTimeframeSignal(wave11n(0, ["s1", "signal1"], msg), wave11n(1, ["s2", "signal2"], msg), wave11n(2, ["s3", "signal3"], msg), wave11n(3, ["w1", "weight1"], msg), wave11n(4, ["w2", "weight2"], msg), wave11n(5, ["w3", "weight3"], msg));
54184
+ }
54185
+ if (fname === "ta.market_timing_index" || fname === "market_timing_index") {
54186
+ const msg = "ta.market_timing_index() requires 4 arguments";
54187
+ return env.ta.marketTimingIndex(wave11n(0, ["trend"], msg), wave11n(1, ["momentum"], msg), wave11n(2, ["volatility"], msg), wave11n(3, ["sentiment"], msg));
54188
+ }
54189
+ if (fname === "ta.regime_adaptive_signal" || fname === "regime_adaptive_signal") {
54190
+ const msg = "ta.regime_adaptive_signal() requires 4 arguments";
54191
+ const strength = wave11n(0, ["signal_strength", "strength"], msg);
54192
+ const volRegime = wave12s(1, ["volatility_regime", "vol_regime"], msg);
54193
+ const trendRegime = wave12s(2, ["trend_regime"], msg);
54194
+ wave11n(3, ["period", "length"], msg);
54195
+ return env.ta.regimeAdaptiveSignal(strength, volRegime, trendRegime);
54196
+ }
54197
+ if (fname === "ta.strategy_score" || fname === "strategy_score") {
54198
+ const msg = "ta.strategy_score() requires 4 arguments";
54199
+ return env.ta.strategyScore(wave11n(0, ["rsi"], msg), wave11n(1, ["macd"], msg), wave11n(2, ["ema_cross"], msg), wave11n(3, ["trend"], msg));
54200
+ }
54201
+ if (fname === "ta.breakout_detection" || fname === "breakout_detection") {
54202
+ const msg = "ta.breakout_detection() requires 3 arguments";
54203
+ return env.ta.breakoutDetection(wave11n(0, ["close", "source"], msg), wave11n(1, ["resistance"], msg), wave11n(2, ["support"], msg));
54204
+ }
54205
+ if (fname === "ta.ema_cross_signal" || fname === "ema_cross_signal") {
54206
+ const msg = "ta.ema_cross_signal() requires 3 arguments";
54207
+ return env.ta.emaCrossSignal(site, wave11n(0, ["fast_ema", "fast"], msg), wave11n(1, ["slow_ema", "slow"], msg), wave11n(2, ["threshold"], msg));
54208
+ }
54209
+ if (fname === "ta.momentum_filter" || fname === "momentum_filter") {
54210
+ const msg = "ta.momentum_filter() requires 3 arguments";
54211
+ return env.ta.momentumFilter(site, wave11n(0, ["momentum_raw", "momentum"], msg), wave11n(1, ["volume"], msg), wave11n(2, ["period", "length"], msg));
54212
+ }
54213
+ if (fname === "ta.volatility_regime" || fname === "volatility_regime") {
54214
+ const msg = "ta.volatility_regime() requires 2 arguments";
54215
+ return env.ta.volatilityRegime(site, wave11n(0, ["atr_series", "atr", "source"], msg), wave11n(1, ["period", "length"], msg));
54216
+ }
54217
+ if (fname === "ta.economic_impact_score" || fname === "economic_impact_score") {
54218
+ const msg = "ta.economic_impact_score() requires 3 arguments";
54219
+ return env.ta.economicImpactScore(wave11n(0, ["price_change"], msg), wave11n(1, ["volatility"], msg), wave11n(2, ["volume_change"], msg));
54220
+ }
54221
+ if (fname === "ta.contrarian_signal" || fname === "contrarian_signal") {
54222
+ const msg = "ta.contrarian_signal() requires 3 arguments";
54223
+ return env.ta.contrarianSignal(wave11n(0, ["sentiment"], msg), wave11n(1, ["volatility"], msg), wave11n(2, ["time_since_extreme", "time_extreme"], msg));
54224
+ }
54225
+ if (fname === "ta.drawdown_recovery_level" || fname === "drawdown_recovery_level") {
54226
+ const msg = "ta.drawdown_recovery_level() requires 4 arguments";
54227
+ return env.ta.drawdownRecoveryLevel(wave11n(0, ["peak"], msg), wave11n(1, ["current", "current_price"], msg), wave11n(2, ["recovery_factor", "factor"], msg), wave11n(3, ["period", "length"], msg));
54228
+ }
54229
+ if (fname === "ta.pivot_point_levels" || fname === "pivot_point_levels") {
54230
+ const msg = "ta.pivot_point_levels() requires type+anchor or high, low, close";
54231
+ const first = wave11(0, ["type", "high"], msg);
54232
+ if (typeof first === "string") {
54233
+ if (typeof env.ta.pivotPoints !== "function")
54234
+ return NA;
54235
+ const r = env.ta.pivotPoints.call(env.ta, site, num2(env.ctx.high), num2(env.ctx.low), num2(env.ctx.close));
54236
+ return tupleOf([r.pp, r.r1, r.s1, r.r2, r.s2]);
54237
+ }
54238
+ const tradArg = callArg(node.args, 3, ["traditional"]);
54239
+ const traditional = tradArg == null ? true : unwrap2(evalExpr(tradArg, env)) !== 0;
54240
+ return env.ta.pivotPointLevels(num2(first), wave11n(1, ["low"], msg), wave11n(2, ["close"], msg), traditional);
54241
+ }
54242
+ if (fname === "ta.volatility_regime_score" || fname === "volatility_regime_score") {
54243
+ const msg = "ta.volatility_regime_score() requires 4 arguments";
54244
+ return env.ta.volatilityRegimeScore(site, wave13v(0, ["atr_list", "atr"], msg), wave13v(1, ["volatility_list", "volatility"], msg), wave13v(2, ["vix_list", "vix"], msg), wave11n(3, ["threshold"], msg));
54245
+ }
54246
+ if (fname === "ta.correlation_filter" || fname === "correlation_filter") {
54247
+ const msg = "ta.correlation_filter() requires 5 arguments";
54248
+ const s1 = wave13v(0, ["signal1_list", "signal1", "s1"], msg);
54249
+ const s2 = wave13v(1, ["signal2_list", "signal2", "s2"], msg);
54250
+ const s3 = wave13v(2, ["signal3_list", "signal3", "s3"], msg);
54251
+ wave11n(3, ["num_signals"], msg);
54252
+ return env.ta.correlationFilter(site, s1, s2, s3, wave11n(4, ["threshold"], msg));
54253
+ }
54254
+ if (fname === "ta.advanced_breakout_detector" || fname === "advanced_breakout_detector") {
54255
+ const msg = "ta.advanced_breakout_detector() requires 5 arguments";
54256
+ return env.ta.advancedBreakoutDetector(site, wave13v(0, ["price_list", "price"], msg), wave13v(1, ["volume_list", "volume"], msg), wave11n(2, ["level"], msg), wave11n(3, ["lookback"], msg), wave11n(4, ["volume_multiplier", "vol_mult"], msg));
54257
+ }
54258
+ if (fname === "ta.pullback_bounce_level" || fname === "pullback_bounce_level") {
54259
+ const msg = "ta.pullback_bounce_level() requires 5 arguments";
54260
+ const high = wave13v(0, ["high_list", "high"], msg);
54261
+ const low = wave13v(1, ["low_list", "low"], msg);
54262
+ wave13v(2, ["close_list", "close"], msg);
54263
+ return env.ta.pullbackBounceLevel(site, high, low, wave11n(3, ["trend_direction", "trend_dir"], msg), wave11n(4, ["lookback"], msg));
54264
+ }
54265
+ if (fname === "ta.divergence_detector" || fname === "divergence_detector") {
54266
+ const msg = "ta.divergence_detector() requires 3 arguments";
54267
+ return env.ta.divergenceDetector(site, wave13v(0, ["price_list", "price"], msg), wave13v(1, ["indicator_list", "indicator"], msg), wave11n(2, ["lookback"], msg));
54268
+ }
54269
+ if (fname === "ta.momentum_divergence" || fname === "momentum_divergence") {
54270
+ const msg = "ta.momentum_divergence() requires 3 arguments";
54271
+ return env.ta.momentumDivergence(site, wave13v(0, ["price"], msg), wave13v(1, ["momentum_fast", "mom_fast"], msg), wave13v(2, ["momentum_slow", "mom_slow"], msg));
54272
+ }
54273
+ if (fname === "ta.spread_analysis" || fname === "spread_analysis") {
54274
+ const msg = "ta.spread_analysis() requires 3 arguments";
54275
+ return env.ta.spreadAnalysis(site, wave13v(0, ["bid"], msg), wave13v(1, ["ask"], msg), wave11n(2, ["period", "length"], msg));
54276
+ }
54277
+ if (fname === "ta.employment_cycle_indicator" || fname === "employment_cycle_indicator") {
54278
+ const msg = "ta.employment_cycle_indicator() requires 3 arguments";
54279
+ return env.ta.employmentCycleIndicator(site, wave13v(0, ["cyclical_stocks", "cyclical"], msg), wave13v(1, ["defensive_stocks", "defensive"], msg), wave13v(2, ["unemployment_proxy", "unemployment"], msg));
54280
+ }
54281
+ if (fname === "ta.gdp_growth_proxy" || fname === "gdp_growth_proxy") {
54282
+ const msg = "ta.gdp_growth_proxy() requires 3 arguments";
54283
+ return env.ta.gdpGrowthProxy(site, wave13v(0, ["market_breadth", "breadth"], msg), wave13v(1, ["market_volume", "volume"], msg), wave13v(2, ["price_momentum", "momentum"], msg));
54284
+ }
54285
+ if (fname === "ta.inflation_proxy_indicator" || fname === "inflation_proxy_indicator") {
54286
+ const msg = "ta.inflation_proxy_indicator() requires 3 arguments";
54287
+ return env.ta.inflationProxyIndicator(site, wave13v(0, ["usd_index", "usd"], msg), wave13v(1, ["commodity_prices", "commodities"], msg), wave13v(2, ["bond_yields", "yields"], msg));
54288
+ }
54289
+ if (fname === "ta.expected_value" || fname === "expected_value") {
54290
+ const msg = "ta.expected_value() requires 2 arguments: returns, probabilities";
54291
+ const returns = numericList(evalExpr(wave10aArg(0, ["returns"], msg), env));
54292
+ const probs = numericList(evalExpr(wave10aArg(1, ["probabilities", "probs"], msg), env));
54293
+ if (returns.length !== probs.length)
54294
+ runtimeError("Returns and probabilities must have same length");
54295
+ return env.ta.expectedValue(returns, probs);
54296
+ }
51987
54297
  return;
51988
54298
  }
51989
54299
  function hlc3(env) {
@@ -51994,6 +54304,28 @@ function hlc3(env) {
51994
54304
  return c ?? h ?? l;
51995
54305
  return (h + l + c) / 3;
51996
54306
  }
54307
+ function seriesLastCell(value) {
54308
+ if (value instanceof PineArray) {
54309
+ const n = value.size();
54310
+ return n > 0 ? value.get(n - 1) : null;
54311
+ }
54312
+ const v = unwrap2(value);
54313
+ return typeof v === "number" && Number.isFinite(v) ? v : null;
54314
+ }
54315
+ function numericList(value) {
54316
+ const arr = asArray2(value);
54317
+ if (arr == null) {
54318
+ const v = unwrap2(value);
54319
+ return typeof v === "number" && Number.isFinite(v) ? [v] : [];
54320
+ }
54321
+ const out = [];
54322
+ for (let i = 0;i < arr.size(); i++) {
54323
+ const v = arr.get(i);
54324
+ if (typeof v === "number" && Number.isFinite(v))
54325
+ out.push(v);
54326
+ }
54327
+ return out;
54328
+ }
51997
54329
  function asArray2(value) {
51998
54330
  if (value instanceof PineArray)
51999
54331
  return value;
@@ -52892,8 +55224,22 @@ function evalDrawingCall(fname, node, env) {
52892
55224
  const position = drawRaw(node, env, 0, ["position"]);
52893
55225
  if (position !== undefined)
52894
55226
  extra.position = position;
52895
- putFinite(extra, "rows", drawNum(node, env, 1, ["rows"]));
52896
- putFinite(extra, "columns", drawNum(node, env, 2, ["columns"]));
55227
+ putFinite(extra, "columns", drawNum(node, env, 1, ["columns"]));
55228
+ putFinite(extra, "rows", drawNum(node, env, 2, ["rows"]));
55229
+ const bgcolor = drawRaw(node, env, 3, ["bgcolor"]);
55230
+ if (bgcolor !== undefined)
55231
+ extra.bgcolor = bgcolor;
55232
+ const frameColor = drawRaw(node, env, 4, ["frame_color"]);
55233
+ if (frameColor !== undefined)
55234
+ extra.frame_color = frameColor;
55235
+ putFinite(extra, "frame_width", drawNum(node, env, 5, ["frame_width"]));
55236
+ const borderColor = drawRaw(node, env, 6, ["border_color"]);
55237
+ if (borderColor !== undefined)
55238
+ extra.border_color = borderColor;
55239
+ putFinite(extra, "border_width", drawNum(node, env, 7, ["border_width"]));
55240
+ const forceOverlay = drawRaw(node, env, 8, ["force_overlay"]);
55241
+ if (forceOverlay !== undefined)
55242
+ extra.force_overlay = forceOverlay;
52897
55243
  return book.tableNew(env.barIndex, extra);
52898
55244
  }
52899
55245
  if (fname === "polyline.new") {
@@ -53940,8 +56286,8 @@ function syminfoPrefix2(node, env) {
53940
56286
  function calendarMs(node, env) {
53941
56287
  const arg = callArg(node.args, 0, ["time", "timestamp"]);
53942
56288
  if (arg == null)
53943
- return num2(env.ctx.time);
53944
- return unwrap2(evalExpr(arg, env));
56289
+ return timestampToMs(num2(env.ctx.time));
56290
+ return timestampToMs(unwrap2(evalExpr(arg, env)));
53945
56291
  }
53946
56292
  function evalLibraryMemberCall(mod, attr, node, env) {
53947
56293
  const exp = mod.get(attr);