pinets 0.1.34 → 0.2.1

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.
@@ -7693,6 +7693,7 @@
7693
7693
  __publicField$7(this, "paramIdCounter", 0);
7694
7694
  __publicField$7(this, "cacheIdCounter", 0);
7695
7695
  __publicField$7(this, "tempVarCounter", 0);
7696
+ __publicField$7(this, "taCallIdCounter", 0);
7696
7697
  this.pushScope("glb");
7697
7698
  }
7698
7699
  get nextParamIdArg() {
@@ -7707,6 +7708,12 @@
7707
7708
  name: `'cache_${this.cacheIdCounter++}'`
7708
7709
  };
7709
7710
  }
7711
+ getNextTACallId() {
7712
+ return {
7713
+ type: "Literal",
7714
+ value: `_ta${this.taCallIdCounter++}`
7715
+ };
7716
+ }
7710
7717
  pushScope(type) {
7711
7718
  this.scopes.push(/* @__PURE__ */ new Map());
7712
7719
  this.scopeTypes.push(type);
@@ -7728,6 +7735,14 @@
7728
7735
  this.rootParams.add(name);
7729
7736
  }
7730
7737
  }
7738
+ removeContextBoundVar(name) {
7739
+ if (this.contextBoundVars.has(name)) {
7740
+ this.contextBoundVars.delete(name);
7741
+ if (this.rootParams.has(name)) {
7742
+ this.rootParams.delete(name);
7743
+ }
7744
+ }
7745
+ }
7731
7746
  addArrayPatternElement(name) {
7732
7747
  this.arrayPatternElements.add(name);
7733
7748
  }
@@ -8374,38 +8389,46 @@
8374
8389
  return prop;
8375
8390
  });
8376
8391
  } else if (node.argument.type === "Identifier") {
8377
- const [scopedName, kind] = scopeManager.getVariable(node.argument.name);
8378
- node.argument = {
8379
- type: "MemberExpression",
8380
- object: {
8392
+ transformIdentifier(node.argument, scopeManager);
8393
+ if (node.argument.type === "Identifier") {
8394
+ addArrayAccess(node.argument);
8395
+ }
8396
+ }
8397
+ if (curScope === "fn") {
8398
+ if (node.argument.type === "Identifier" && scopeManager.isContextBound(node.argument.name) && !scopeManager.isRootParam(node.argument.name)) {
8399
+ node.argument = {
8381
8400
  type: "MemberExpression",
8382
- object: {
8383
- type: "Identifier",
8384
- name: CONTEXT_NAME
8385
- },
8401
+ object: node.argument,
8386
8402
  property: {
8387
- type: "Identifier",
8388
- name: kind
8403
+ type: "Literal",
8404
+ value: 0
8389
8405
  },
8390
- computed: false
8391
- },
8392
- property: {
8393
- type: "Identifier",
8394
- name: scopedName
8395
- },
8396
- computed: false
8397
- };
8398
- node.argument = {
8399
- type: "MemberExpression",
8400
- object: node.argument,
8401
- property: {
8402
- type: "Literal",
8403
- value: 0
8404
- },
8405
- computed: true
8406
- };
8407
- }
8408
- if (curScope === "fn") {
8406
+ computed: true
8407
+ };
8408
+ } else if (node.argument.type === "MemberExpression") {
8409
+ if (node.argument.object.type === "Identifier" && scopeManager.isContextBound(node.argument.object.name) && !scopeManager.isRootParam(node.argument.object.name)) {
8410
+ if (!node.argument._indexTransformed) {
8411
+ transformArrayIndex(node.argument, scopeManager);
8412
+ node.argument._indexTransformed = true;
8413
+ }
8414
+ }
8415
+ } else if (node.argument.type === "BinaryExpression" || node.argument.type === "LogicalExpression" || node.argument.type === "ConditionalExpression" || node.argument.type === "CallExpression") {
8416
+ recursive(node.argument, scopeManager, {
8417
+ Identifier(node2, state) {
8418
+ transformIdentifier(node2, state);
8419
+ if (node2.type === "Identifier" && !node2._arrayAccessed) {
8420
+ addArrayAccess(node2);
8421
+ node2._arrayAccessed = true;
8422
+ }
8423
+ },
8424
+ MemberExpression(node2) {
8425
+ transformMemberExpression(node2, "", scopeManager);
8426
+ },
8427
+ CallExpression(node2, state) {
8428
+ transformCallExpression(node2, state);
8429
+ }
8430
+ });
8431
+ }
8409
8432
  node.argument = {
8410
8433
  type: "CallExpression",
8411
8434
  callee: {
@@ -8756,6 +8779,9 @@
8756
8779
  }
8757
8780
  return transformFunctionArgument(arg, namespace2, scopeManager);
8758
8781
  });
8782
+ if (namespace2 === "ta") {
8783
+ node.arguments.push(scopeManager.getNextTACallId());
8784
+ }
8759
8785
  node._transformed = true;
8760
8786
  } else if (node.callee && node.callee.type === "Identifier") {
8761
8787
  node.arguments = node.arguments.map((arg) => {
@@ -8796,9 +8822,11 @@
8796
8822
  });
8797
8823
  }
8798
8824
  function transformFunctionDeclaration(node, scopeManager) {
8825
+ const boundParamNames = [];
8799
8826
  node.params.forEach((param) => {
8800
8827
  if (param.type === "Identifier") {
8801
8828
  scopeManager.addContextBoundVar(param.name, false);
8829
+ boundParamNames.push(param.name);
8802
8830
  }
8803
8831
  });
8804
8832
  if (node.body && node.body.type === "BlockStatement") {
@@ -9146,14 +9174,43 @@
9146
9174
  transformIfStatement(node, state, c);
9147
9175
  }
9148
9176
  });
9177
+ transformEqualityChecks(ast);
9149
9178
  const transformedCode = generate(ast);
9150
9179
  const _wraperFunction = new Function("", `return ${transformedCode}`);
9151
9180
  return _wraperFunction(this);
9152
9181
  }
9182
+ function transformEqualityChecks(ast) {
9183
+ simple(ast, {
9184
+ BinaryExpression(node) {
9185
+ if (node.operator === "==" || node.operator === "===") {
9186
+ const leftOperand = node.left;
9187
+ const rightOperand = node.right;
9188
+ Object.assign(node, {
9189
+ type: "CallExpression",
9190
+ callee: {
9191
+ type: "MemberExpression",
9192
+ object: {
9193
+ type: "Identifier",
9194
+ name: "$.math"
9195
+ },
9196
+ property: {
9197
+ type: "Identifier",
9198
+ name: "__eq"
9199
+ },
9200
+ computed: false
9201
+ },
9202
+ arguments: [leftOperand, rightOperand],
9203
+ _transformed: true
9204
+ });
9205
+ }
9206
+ }
9207
+ });
9208
+ }
9153
9209
 
9154
9210
  var __defProp$6 = Object.defineProperty;
9155
9211
  var __defNormalProp$6 = (obj, key, value) => key in obj ? __defProp$6(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
9156
9212
  var __publicField$6 = (obj, key, value) => __defNormalProp$6(obj, typeof key !== "symbol" ? key + "" : key, value);
9213
+ const MAX_PERIODS = 5e3;
9157
9214
  class PineTS {
9158
9215
  constructor(source, tickerId, timeframe, limit, sDate, eDate) {
9159
9216
  this.source = source;
@@ -9183,7 +9240,7 @@
9183
9240
  __publicField$6(this, "_ready", false);
9184
9241
  this._readyPromise = new Promise((resolve) => {
9185
9242
  this.loadMarketData(source, tickerId, timeframe, limit, sDate, eDate).then((data) => {
9186
- const marketData = data.reverse();
9243
+ const marketData = data.slice(0, MAX_PERIODS);
9187
9244
  this._periods = marketData.length;
9188
9245
  this.data = marketData;
9189
9246
  const _open = marketData.map((d) => d.open);
@@ -9242,19 +9299,28 @@
9242
9299
  context.useTACache = useTACache;
9243
9300
  const transformer = transpile.bind(this);
9244
9301
  let transpiledFn = transformer(pineTSCode);
9302
+ context.data.close = [];
9303
+ context.data.open = [];
9304
+ context.data.high = [];
9305
+ context.data.low = [];
9306
+ context.data.volume = [];
9307
+ context.data.hl2 = [];
9308
+ context.data.hlc3 = [];
9309
+ context.data.ohlc4 = [];
9310
+ context.data.openTime = [];
9311
+ context.data.closeTime = [];
9245
9312
  const contextVarNames = ["const", "var", "let", "params"];
9246
- for (let i = this._periods - n, idx = n - 1; i < this._periods; i++, idx--) {
9313
+ for (let i = this._periods - n; i < this._periods; i++) {
9247
9314
  context.idx = i;
9248
- context.data.close = this.close.slice(idx);
9249
- context.data.open = this.open.slice(idx);
9250
- context.data.high = this.high.slice(idx);
9251
- context.data.low = this.low.slice(idx);
9252
- context.data.volume = this.volume.slice(idx);
9253
- context.data.hl2 = this.hl2.slice(idx);
9254
- context.data.hlc3 = this.hlc3.slice(idx);
9255
- context.data.ohlc4 = this.ohlc4.slice(idx);
9256
- context.data.openTime = this.openTime.slice(idx);
9257
- context.data.closeTime = this.closeTime.slice(idx);
9315
+ context.data.close.unshift(this.close[i]);
9316
+ context.data.open.unshift(this.open[i]);
9317
+ context.data.high.unshift(this.high[i]);
9318
+ context.data.low.unshift(this.low[i]);
9319
+ context.data.volume.unshift(this.volume[i]);
9320
+ context.data.hl2.unshift(this.hl2[i]);
9321
+ context.data.hlc3.unshift(this.hlc3[i]);
9322
+ context.data.ohlc4.unshift(this.ohlc4[i]);
9323
+ context.data.openTime.unshift(this.openTime[i]);
9258
9324
  const result = await transpiledFn(context);
9259
9325
  if (typeof result === "object") {
9260
9326
  if (typeof context.result !== "object") {
@@ -9441,6 +9507,9 @@
9441
9507
  return this.context.params[name];
9442
9508
  }
9443
9509
  }
9510
+ __eq(a, b) {
9511
+ return Math.abs(a - b) < 1e-8;
9512
+ }
9444
9513
  abs(source) {
9445
9514
  return Math.abs(source[0]);
9446
9515
  }
@@ -9604,141 +9673,532 @@
9604
9673
  return this.context.params[name];
9605
9674
  }
9606
9675
  }
9607
- ema(source, _period) {
9676
+ ema(source, _period, _callId) {
9608
9677
  const period = Array.isArray(_period) ? _period[0] : _period;
9609
- const result = ema(source.slice(0).reverse(), period);
9610
- const idx = this.context.idx;
9611
- return this.context.precision(result[idx]);
9612
- }
9613
- sma(source, _period, _cacheId) {
9678
+ if (!this.context.taState) this.context.taState = {};
9679
+ const stateKey = _callId || `ema_${period}`;
9680
+ if (!this.context.taState[stateKey]) {
9681
+ this.context.taState[stateKey] = { prevEma: null, initSum: 0, initCount: 0 };
9682
+ }
9683
+ const state = this.context.taState[stateKey];
9684
+ const currentValue = source[0];
9685
+ if (state.initCount < period) {
9686
+ state.initSum += currentValue;
9687
+ state.initCount++;
9688
+ if (state.initCount === period) {
9689
+ state.prevEma = state.initSum / period;
9690
+ return this.context.precision(state.prevEma);
9691
+ }
9692
+ return NaN;
9693
+ }
9694
+ const alpha = 2 / (period + 1);
9695
+ const ema2 = currentValue * alpha + state.prevEma * (1 - alpha);
9696
+ state.prevEma = ema2;
9697
+ return this.context.precision(ema2);
9698
+ }
9699
+ sma(source, _period, _callId) {
9614
9700
  const period = Array.isArray(_period) ? _period[0] : _period;
9615
- const reversedSource = source.slice(0).reverse();
9616
- if (this.context.useTACache && _cacheId) {
9617
- if (!this.context.cache[_cacheId]) {
9618
- this.context.cache[_cacheId] = {};
9619
- }
9620
- const cacheObj = this.context.cache[_cacheId];
9621
- if (cacheObj) {
9622
- const result2 = sma_cache(reversedSource, period, cacheObj);
9623
- const idx2 = this.context.idx;
9624
- return this.context.precision(result2[idx2]);
9625
- }
9626
- }
9627
- const result = sma(reversedSource, period);
9628
- const idx = this.context.idx;
9629
- return this.context.precision(result[idx]);
9630
- }
9631
- vwma(source, _period) {
9701
+ if (!this.context.taState) this.context.taState = {};
9702
+ const stateKey = _callId || `sma_${period}`;
9703
+ if (!this.context.taState[stateKey]) {
9704
+ this.context.taState[stateKey] = { window: [], sum: 0 };
9705
+ }
9706
+ const state = this.context.taState[stateKey];
9707
+ const currentValue = source[0] || 0;
9708
+ state.window.unshift(currentValue);
9709
+ state.sum += currentValue;
9710
+ if (state.window.length < period) {
9711
+ return NaN;
9712
+ }
9713
+ if (state.window.length > period) {
9714
+ const oldValue = state.window.pop();
9715
+ state.sum -= oldValue;
9716
+ }
9717
+ const sma2 = state.sum / period;
9718
+ return this.context.precision(sma2);
9719
+ }
9720
+ vwma(source, _period, _callId) {
9632
9721
  const period = Array.isArray(_period) ? _period[0] : _period;
9633
- const volume = this.context.data.volume;
9634
- const result = vwma(source.slice(0).reverse(), volume.slice(0).reverse(), period);
9635
- const idx = this.context.idx;
9636
- return this.context.precision(result[idx]);
9722
+ if (!this.context.taState) this.context.taState = {};
9723
+ const stateKey = _callId || `vwma_${period}`;
9724
+ if (!this.context.taState[stateKey]) {
9725
+ this.context.taState[stateKey] = { window: [], volumeWindow: [] };
9726
+ }
9727
+ const state = this.context.taState[stateKey];
9728
+ const currentValue = source[0];
9729
+ const currentVolume = this.context.data.volume[0];
9730
+ state.window.unshift(currentValue);
9731
+ state.volumeWindow.unshift(currentVolume);
9732
+ if (state.window.length < period) {
9733
+ return NaN;
9734
+ }
9735
+ if (state.window.length > period) {
9736
+ state.window.pop();
9737
+ state.volumeWindow.pop();
9738
+ }
9739
+ let sumVolPrice = 0;
9740
+ let sumVol = 0;
9741
+ for (let i = 0; i < period; i++) {
9742
+ sumVolPrice += state.window[i] * state.volumeWindow[i];
9743
+ sumVol += state.volumeWindow[i];
9744
+ }
9745
+ const vwma2 = sumVolPrice / sumVol;
9746
+ return this.context.precision(vwma2);
9637
9747
  }
9638
- wma(source, _period) {
9748
+ wma(source, _period, _callId) {
9639
9749
  const period = Array.isArray(_period) ? _period[0] : _period;
9640
- const result = wma(source.slice(0).reverse(), period);
9641
- const idx = this.context.idx;
9642
- return this.context.precision(result[idx]);
9750
+ if (!this.context.taState) this.context.taState = {};
9751
+ const stateKey = _callId || `wma_${period}`;
9752
+ if (!this.context.taState[stateKey]) {
9753
+ this.context.taState[stateKey] = { window: [] };
9754
+ }
9755
+ const state = this.context.taState[stateKey];
9756
+ const currentValue = source[0];
9757
+ state.window.unshift(currentValue);
9758
+ if (state.window.length < period) {
9759
+ return NaN;
9760
+ }
9761
+ if (state.window.length > period) {
9762
+ state.window.pop();
9763
+ }
9764
+ let numerator = 0;
9765
+ let denominator = 0;
9766
+ for (let i = 0; i < period; i++) {
9767
+ const weight = period - i;
9768
+ numerator += state.window[i] * weight;
9769
+ denominator += weight;
9770
+ }
9771
+ const wma2 = numerator / denominator;
9772
+ return this.context.precision(wma2);
9643
9773
  }
9644
- hma(source, _period) {
9774
+ hma(source, _period, _callId) {
9645
9775
  const period = Array.isArray(_period) ? _period[0] : _period;
9646
- const result = hma(source.slice(0).reverse(), period);
9647
- const idx = this.context.idx;
9648
- return this.context.precision(result[idx]);
9776
+ const halfPeriod = Math.floor(period / 2);
9777
+ const sqrtPeriod = Math.floor(Math.sqrt(period));
9778
+ const wma1 = this.wma(source, halfPeriod, _callId ? `${_callId}_wma1` : void 0);
9779
+ const wma2 = this.wma(source, period, _callId ? `${_callId}_wma2` : void 0);
9780
+ if (isNaN(wma1) || isNaN(wma2)) {
9781
+ return NaN;
9782
+ }
9783
+ if (!this.context.taState) this.context.taState = {};
9784
+ const stateKey = _callId || `hma_raw_${period}`;
9785
+ if (!this.context.taState[stateKey]) {
9786
+ this.context.taState[stateKey] = [];
9787
+ }
9788
+ const rawHma = 2 * wma1 - wma2;
9789
+ this.context.taState[stateKey].unshift(rawHma);
9790
+ const hmaStateKey = _callId ? `${_callId}_hma_final` : `hma_final_${period}`;
9791
+ if (!this.context.taState[hmaStateKey]) {
9792
+ this.context.taState[hmaStateKey] = { window: [] };
9793
+ }
9794
+ const state = this.context.taState[hmaStateKey];
9795
+ state.window.unshift(rawHma);
9796
+ if (state.window.length < sqrtPeriod) {
9797
+ return NaN;
9798
+ }
9799
+ if (state.window.length > sqrtPeriod) {
9800
+ state.window.pop();
9801
+ }
9802
+ let numerator = 0;
9803
+ let denominator = 0;
9804
+ for (let i = 0; i < sqrtPeriod; i++) {
9805
+ const weight = sqrtPeriod - i;
9806
+ numerator += state.window[i] * weight;
9807
+ denominator += weight;
9808
+ }
9809
+ const hma2 = numerator / denominator;
9810
+ return this.context.precision(hma2);
9649
9811
  }
9650
- rma(source, _period) {
9812
+ rma(source, _period, _callId) {
9651
9813
  const period = Array.isArray(_period) ? _period[0] : _period;
9652
- const result = rma(source.slice(0).reverse(), period);
9653
- const idx = this.context.idx;
9654
- return this.context.precision(result[idx]);
9655
- }
9656
- change(source, _length = 1) {
9814
+ if (!this.context.taState) this.context.taState = {};
9815
+ const stateKey = _callId || `rma_${period}`;
9816
+ if (!this.context.taState[stateKey]) {
9817
+ this.context.taState[stateKey] = { prevRma: null, initSum: 0, initCount: 0 };
9818
+ }
9819
+ const state = this.context.taState[stateKey];
9820
+ const currentValue = source[0] || 0;
9821
+ if (state.initCount < period) {
9822
+ state.initSum += currentValue;
9823
+ state.initCount++;
9824
+ if (state.initCount === period) {
9825
+ state.prevRma = state.initSum / period;
9826
+ return this.context.precision(state.prevRma);
9827
+ }
9828
+ return NaN;
9829
+ }
9830
+ const alpha = 1 / period;
9831
+ const rma2 = currentValue * alpha + state.prevRma * (1 - alpha);
9832
+ state.prevRma = rma2;
9833
+ return this.context.precision(rma2);
9834
+ }
9835
+ change(source, _length = 1, _callId) {
9657
9836
  const length = Array.isArray(_length) ? _length[0] : _length;
9658
- const result = change(source.slice(0).reverse(), length);
9659
- const idx = this.context.idx;
9660
- return this.context.precision(result[idx]);
9837
+ if (!this.context.taState) this.context.taState = {};
9838
+ const stateKey = _callId || `change_${length}`;
9839
+ if (!this.context.taState[stateKey]) {
9840
+ this.context.taState[stateKey] = { window: [] };
9841
+ }
9842
+ const state = this.context.taState[stateKey];
9843
+ const currentValue = source[0];
9844
+ state.window.unshift(currentValue);
9845
+ if (state.window.length <= length) {
9846
+ return NaN;
9847
+ }
9848
+ if (state.window.length > length + 1) {
9849
+ state.window.pop();
9850
+ }
9851
+ const change2 = currentValue - state.window[length];
9852
+ return this.context.precision(change2);
9661
9853
  }
9662
- rsi(source, _period) {
9854
+ rsi(source, _period, _callId) {
9663
9855
  const period = Array.isArray(_period) ? _period[0] : _period;
9664
- const result = rsi(source.slice(0).reverse(), period);
9665
- const idx = this.context.idx;
9666
- return this.context.precision(result[idx]);
9856
+ if (!this.context.taState) this.context.taState = {};
9857
+ const stateKey = _callId || `rsi_${period}`;
9858
+ if (!this.context.taState[stateKey]) {
9859
+ this.context.taState[stateKey] = {
9860
+ prevValue: null,
9861
+ avgGain: 0,
9862
+ avgLoss: 0,
9863
+ initGains: [],
9864
+ initLosses: []
9865
+ };
9866
+ }
9867
+ const state = this.context.taState[stateKey];
9868
+ const currentValue = source[0];
9869
+ if (state.prevValue !== null) {
9870
+ const diff = currentValue - state.prevValue;
9871
+ const gain = diff > 0 ? diff : 0;
9872
+ const loss = diff < 0 ? -diff : 0;
9873
+ if (state.initGains.length < period) {
9874
+ state.initGains.push(gain);
9875
+ state.initLosses.push(loss);
9876
+ if (state.initGains.length === period) {
9877
+ state.avgGain = state.initGains.reduce((a, b) => a + b, 0) / period;
9878
+ state.avgLoss = state.initLosses.reduce((a, b) => a + b, 0) / period;
9879
+ state.prevValue = currentValue;
9880
+ const rsi3 = state.avgLoss === 0 ? 100 : 100 - 100 / (1 + state.avgGain / state.avgLoss);
9881
+ return this.context.precision(rsi3);
9882
+ }
9883
+ state.prevValue = currentValue;
9884
+ return NaN;
9885
+ }
9886
+ state.avgGain = (state.avgGain * (period - 1) + gain) / period;
9887
+ state.avgLoss = (state.avgLoss * (period - 1) + loss) / period;
9888
+ const rsi2 = state.avgLoss === 0 ? 100 : 100 - 100 / (1 + state.avgGain / state.avgLoss);
9889
+ state.prevValue = currentValue;
9890
+ return this.context.precision(rsi2);
9891
+ }
9892
+ state.prevValue = currentValue;
9893
+ return NaN;
9667
9894
  }
9668
- atr(_period) {
9895
+ atr(_period, _callId) {
9669
9896
  const period = Array.isArray(_period) ? _period[0] : _period;
9670
- const high = this.context.data.high.slice().reverse();
9671
- const low = this.context.data.low.slice().reverse();
9672
- const close = this.context.data.close.slice().reverse();
9673
- const result = atr(high, low, close, period);
9674
- const idx = this.context.idx;
9675
- return this.context.precision(result[idx]);
9897
+ if (!this.context.taState) this.context.taState = {};
9898
+ const stateKey = _callId || `atr_${period}`;
9899
+ if (!this.context.taState[stateKey]) {
9900
+ this.context.taState[stateKey] = {
9901
+ prevAtr: null,
9902
+ initSum: 0,
9903
+ initCount: 0,
9904
+ prevClose: null
9905
+ };
9906
+ }
9907
+ const state = this.context.taState[stateKey];
9908
+ const high = this.context.data.high[0];
9909
+ const low = this.context.data.low[0];
9910
+ const close = this.context.data.close[0];
9911
+ let tr;
9912
+ if (state.prevClose !== null) {
9913
+ const hl = high - low;
9914
+ const hc = Math.abs(high - state.prevClose);
9915
+ const lc = Math.abs(low - state.prevClose);
9916
+ tr = Math.max(hl, hc, lc);
9917
+ } else {
9918
+ tr = high - low;
9919
+ }
9920
+ state.prevClose = close;
9921
+ if (state.initCount < period) {
9922
+ state.initSum += tr;
9923
+ state.initCount++;
9924
+ if (state.initCount === period) {
9925
+ state.prevAtr = state.initSum / period;
9926
+ return this.context.precision(state.prevAtr);
9927
+ }
9928
+ return NaN;
9929
+ }
9930
+ const atr2 = (state.prevAtr * (period - 1) + tr) / period;
9931
+ state.prevAtr = atr2;
9932
+ return this.context.precision(atr2);
9676
9933
  }
9677
- mom(source, _length) {
9934
+ mom(source, _length, _callId) {
9678
9935
  const length = Array.isArray(_length) ? _length[0] : _length;
9679
- const result = mom(source.slice(0).reverse(), length);
9680
- const idx = this.context.idx;
9681
- return this.context.precision(result[idx]);
9936
+ return this.change(source, length);
9682
9937
  }
9683
- roc(source, _length) {
9938
+ roc(source, _length, _callId) {
9684
9939
  const length = Array.isArray(_length) ? _length[0] : _length;
9685
- const result = roc(source.slice(0).reverse(), length);
9686
- const idx = this.context.idx;
9687
- return this.context.precision(result[idx]);
9940
+ if (!this.context.taState) this.context.taState = {};
9941
+ const stateKey = _callId || `roc_${length}`;
9942
+ if (!this.context.taState[stateKey]) {
9943
+ this.context.taState[stateKey] = { window: [] };
9944
+ }
9945
+ const state = this.context.taState[stateKey];
9946
+ const currentValue = source[0];
9947
+ state.window.unshift(currentValue);
9948
+ if (state.window.length <= length) {
9949
+ return NaN;
9950
+ }
9951
+ if (state.window.length > length + 1) {
9952
+ state.window.pop();
9953
+ }
9954
+ const prevValue = state.window[length];
9955
+ const roc2 = (currentValue - prevValue) / prevValue * 100;
9956
+ return this.context.precision(roc2);
9688
9957
  }
9689
- dev(source, _length) {
9958
+ dev(source, _length, _callId) {
9690
9959
  const length = Array.isArray(_length) ? _length[0] : _length;
9691
- const result = dev(source.slice(0).reverse(), length);
9692
- const idx = this.context.idx;
9693
- return this.context.precision(result[idx]);
9960
+ if (!this.context.taState) this.context.taState = {};
9961
+ const stateKey = _callId || `dev_${length}`;
9962
+ if (!this.context.taState[stateKey]) {
9963
+ this.context.taState[stateKey] = { window: [], sum: 0 };
9964
+ }
9965
+ const state = this.context.taState[stateKey];
9966
+ const currentValue = source[0] || 0;
9967
+ state.window.unshift(currentValue);
9968
+ state.sum += currentValue;
9969
+ if (state.window.length < length) {
9970
+ return NaN;
9971
+ }
9972
+ if (state.window.length > length) {
9973
+ const oldValue = state.window.pop();
9974
+ state.sum -= oldValue;
9975
+ }
9976
+ const mean = state.sum / length;
9977
+ let sumDeviation = 0;
9978
+ for (let i = 0; i < length; i++) {
9979
+ sumDeviation += Math.abs(state.window[i] - mean);
9980
+ }
9981
+ const dev2 = sumDeviation / length;
9982
+ return this.context.precision(dev2);
9694
9983
  }
9695
- variance(source, _length) {
9984
+ variance(source, _length, _callId) {
9696
9985
  const length = Array.isArray(_length) ? _length[0] : _length;
9697
- const result = variance(source.slice(0).reverse(), length);
9698
- const idx = this.context.idx;
9699
- return this.context.precision(result[idx]);
9986
+ if (!this.context.taState) this.context.taState = {};
9987
+ const stateKey = _callId || `variance_${length}`;
9988
+ if (!this.context.taState[stateKey]) {
9989
+ this.context.taState[stateKey] = { window: [] };
9990
+ }
9991
+ const state = this.context.taState[stateKey];
9992
+ const currentValue = source[0];
9993
+ state.window.unshift(currentValue);
9994
+ if (state.window.length < length) {
9995
+ return NaN;
9996
+ }
9997
+ if (state.window.length > length) {
9998
+ state.window.pop();
9999
+ }
10000
+ let sum = 0;
10001
+ let sumSquares = 0;
10002
+ for (let i = 0; i < length; i++) {
10003
+ sum += state.window[i];
10004
+ sumSquares += state.window[i] * state.window[i];
10005
+ }
10006
+ const mean = sum / length;
10007
+ const variance2 = sumSquares / length - mean * mean;
10008
+ return this.context.precision(variance2);
9700
10009
  }
9701
- highest(source, _length) {
10010
+ highest(source, _length, _callId) {
9702
10011
  const length = Array.isArray(_length) ? _length[0] : _length;
9703
- const result = highest(source.slice(0).reverse(), length);
9704
- const idx = this.context.idx;
9705
- return this.context.precision(result[idx]);
10012
+ if (!this.context.taState) this.context.taState = {};
10013
+ const stateKey = _callId || `highest_${length}`;
10014
+ if (!this.context.taState[stateKey]) {
10015
+ this.context.taState[stateKey] = { window: [] };
10016
+ }
10017
+ const state = this.context.taState[stateKey];
10018
+ const currentValue = source[0];
10019
+ state.window.unshift(currentValue);
10020
+ if (state.window.length < length) {
10021
+ return NaN;
10022
+ }
10023
+ if (state.window.length > length) {
10024
+ state.window.pop();
10025
+ }
10026
+ const max = Math.max(...state.window.filter((v) => !isNaN(v)));
10027
+ return this.context.precision(max);
9706
10028
  }
9707
- lowest(source, _length) {
10029
+ lowest(source, _length, _callId) {
9708
10030
  const length = Array.isArray(_length) ? _length[0] : _length;
9709
- const result = lowest(source.slice(0).reverse(), length);
9710
- const idx = this.context.idx;
9711
- return this.context.precision(result[idx]);
10031
+ if (!this.context.taState) this.context.taState = {};
10032
+ const stateKey = _callId || `lowest_${length}`;
10033
+ if (!this.context.taState[stateKey]) {
10034
+ this.context.taState[stateKey] = { window: [] };
10035
+ }
10036
+ const state = this.context.taState[stateKey];
10037
+ const currentValue = source[0];
10038
+ state.window.unshift(currentValue);
10039
+ if (state.window.length < length) {
10040
+ return NaN;
10041
+ }
10042
+ if (state.window.length > length) {
10043
+ state.window.pop();
10044
+ }
10045
+ const validValues = state.window.filter((v) => !isNaN(v) && v !== void 0);
10046
+ const min = validValues.length > 0 ? Math.min(...validValues) : NaN;
10047
+ return this.context.precision(min);
9712
10048
  }
9713
- median(source, _length) {
10049
+ median(source, _length, _callId) {
9714
10050
  const length = Array.isArray(_length) ? _length[0] : _length;
9715
- const result = median(source.slice(0).reverse(), length);
9716
- const idx = this.context.idx;
9717
- return this.context.precision(result[idx]);
10051
+ if (!this.context.taState) this.context.taState = {};
10052
+ const stateKey = _callId || `median_${length}`;
10053
+ if (!this.context.taState[stateKey]) {
10054
+ this.context.taState[stateKey] = { window: [] };
10055
+ }
10056
+ const state = this.context.taState[stateKey];
10057
+ const currentValue = source[0];
10058
+ state.window.unshift(currentValue);
10059
+ if (state.window.length < length) {
10060
+ return NaN;
10061
+ }
10062
+ if (state.window.length > length) {
10063
+ state.window.pop();
10064
+ }
10065
+ const sorted = state.window.slice().sort((a, b) => a - b);
10066
+ const mid = Math.floor(length / 2);
10067
+ const median2 = length % 2 === 0 ? (sorted[mid - 1] + sorted[mid]) / 2 : sorted[mid];
10068
+ return this.context.precision(median2);
9718
10069
  }
9719
- stdev(source, _length, _bias = true) {
10070
+ stdev(source, _length, _bias = true, _callId) {
9720
10071
  const length = Array.isArray(_length) ? _length[0] : _length;
9721
10072
  const bias = Array.isArray(_bias) ? _bias[0] : _bias;
9722
- const result = stdev(source.slice(0).reverse(), length, bias);
9723
- const idx = this.context.idx;
9724
- return this.context.precision(result[idx]);
10073
+ if (!this.context.taState) this.context.taState = {};
10074
+ const stateKey = _callId || `stdev_${length}_${bias}`;
10075
+ if (!this.context.taState[stateKey]) {
10076
+ this.context.taState[stateKey] = { window: [], sum: 0 };
10077
+ }
10078
+ const state = this.context.taState[stateKey];
10079
+ const currentValue = source[0];
10080
+ state.window.unshift(currentValue);
10081
+ state.sum += currentValue;
10082
+ if (state.window.length < length) {
10083
+ return NaN;
10084
+ }
10085
+ if (state.window.length > length) {
10086
+ const oldValue = state.window.pop();
10087
+ state.sum -= oldValue;
10088
+ }
10089
+ const mean = state.sum / length;
10090
+ let sumSquaredDiff = 0;
10091
+ for (let i = 0; i < length; i++) {
10092
+ sumSquaredDiff += Math.pow(state.window[i] - mean, 2);
10093
+ }
10094
+ const divisor = bias ? length : length - 1;
10095
+ const stdev2 = Math.sqrt(sumSquaredDiff / divisor);
10096
+ return this.context.precision(stdev2);
9725
10097
  }
9726
- linreg(source, _length, _offset) {
10098
+ linreg(source, _length, _offset, _callId) {
9727
10099
  const length = Array.isArray(_length) ? _length[0] : _length;
9728
10100
  const offset = Array.isArray(_offset) ? _offset[0] : _offset;
9729
- const result = linreg(source.slice(0).reverse(), length, offset);
9730
- const idx = this.context.idx;
9731
- return this.context.precision(result[idx]);
10101
+ if (!this.context.taState) this.context.taState = {};
10102
+ const stateKey = _callId || `linreg_${length}_${offset}`;
10103
+ if (!this.context.taState[stateKey]) {
10104
+ this.context.taState[stateKey] = { window: [] };
10105
+ }
10106
+ const state = this.context.taState[stateKey];
10107
+ const currentValue = source[0];
10108
+ state.window.unshift(currentValue);
10109
+ if (state.window.length < length) {
10110
+ return NaN;
10111
+ }
10112
+ if (state.window.length > length) {
10113
+ state.window.pop();
10114
+ }
10115
+ let sumX = 0;
10116
+ let sumY = 0;
10117
+ let sumXY = 0;
10118
+ let sumXX = 0;
10119
+ const n = length;
10120
+ for (let j = 0; j < length; j++) {
10121
+ const x = length - 1 - j;
10122
+ const y = state.window[j];
10123
+ sumX += x;
10124
+ sumY += y;
10125
+ sumXY += x * y;
10126
+ sumXX += x * x;
10127
+ }
10128
+ const denominator = n * sumXX - sumX * sumX;
10129
+ if (denominator === 0) {
10130
+ return NaN;
10131
+ }
10132
+ const slope = (n * sumXY - sumX * sumY) / denominator;
10133
+ const intercept = (sumY - slope * sumX) / n;
10134
+ const linRegValue = intercept + slope * (length - 1 - offset);
10135
+ return this.context.precision(linRegValue);
9732
10136
  }
9733
- supertrend(_factor, _atrPeriod) {
10137
+ supertrend(_factor, _atrPeriod, _callId) {
9734
10138
  const factor = Array.isArray(_factor) ? _factor[0] : _factor;
9735
10139
  const atrPeriod = Array.isArray(_atrPeriod) ? _atrPeriod[0] : _atrPeriod;
9736
- const high = this.context.data.high.slice().reverse();
9737
- const low = this.context.data.low.slice().reverse();
9738
- const close = this.context.data.close.slice().reverse();
9739
- const [supertrend, direction] = calculateSupertrend(high, low, close, factor, atrPeriod);
9740
- const idx = this.context.idx;
9741
- return [[this.context.precision(supertrend[idx]), direction[idx]]];
10140
+ if (!this.context.taState) this.context.taState = {};
10141
+ const stateKey = `supertrend_${factor}_${atrPeriod}`;
10142
+ if (!this.context.taState[stateKey]) {
10143
+ this.context.taState[stateKey] = {
10144
+ prevUpperBand: null,
10145
+ prevLowerBand: null,
10146
+ prevSupertrend: null,
10147
+ prevDirection: null
10148
+ };
10149
+ }
10150
+ const state = this.context.taState[stateKey];
10151
+ const high = this.context.data.high[0];
10152
+ const low = this.context.data.low[0];
10153
+ const close = this.context.data.close[0];
10154
+ const atrValue = this.atr(atrPeriod, _callId ? `${_callId}_atr` : void 0);
10155
+ if (isNaN(atrValue)) {
10156
+ return [[NaN, 0]];
10157
+ }
10158
+ const hl2 = (high + low) / 2;
10159
+ let upperBand = hl2 + factor * atrValue;
10160
+ let lowerBand = hl2 - factor * atrValue;
10161
+ if (state.prevUpperBand !== null) {
10162
+ if (upperBand < state.prevUpperBand || this.context.data.close[1] > state.prevUpperBand) {
10163
+ upperBand = upperBand;
10164
+ } else {
10165
+ upperBand = state.prevUpperBand;
10166
+ }
10167
+ if (lowerBand > state.prevLowerBand || this.context.data.close[1] < state.prevLowerBand) {
10168
+ lowerBand = lowerBand;
10169
+ } else {
10170
+ lowerBand = state.prevLowerBand;
10171
+ }
10172
+ }
10173
+ let direction;
10174
+ let supertrend;
10175
+ if (state.prevSupertrend === null) {
10176
+ direction = close <= upperBand ? -1 : 1;
10177
+ supertrend = direction === -1 ? upperBand : lowerBand;
10178
+ } else {
10179
+ if (state.prevSupertrend === state.prevUpperBand) {
10180
+ if (close > upperBand) {
10181
+ direction = 1;
10182
+ supertrend = lowerBand;
10183
+ } else {
10184
+ direction = -1;
10185
+ supertrend = upperBand;
10186
+ }
10187
+ } else {
10188
+ if (close < lowerBand) {
10189
+ direction = -1;
10190
+ supertrend = upperBand;
10191
+ } else {
10192
+ direction = 1;
10193
+ supertrend = lowerBand;
10194
+ }
10195
+ }
10196
+ }
10197
+ state.prevUpperBand = upperBand;
10198
+ state.prevLowerBand = lowerBand;
10199
+ state.prevSupertrend = supertrend;
10200
+ state.prevDirection = direction;
10201
+ return [[this.context.precision(supertrend), direction]];
9742
10202
  }
9743
10203
  crossover(source1, source2) {
9744
10204
  const current1 = Array.isArray(source1) ? source1[0] : source1;
@@ -9779,344 +10239,6 @@
9779
10239
  return this.context.precision(result[idx]);
9780
10240
  }
9781
10241
  }
9782
- function atr(high, low, close, period) {
9783
- const tr = new Array(high.length);
9784
- tr[0] = high[0] - low[0];
9785
- for (let i = 1; i < high.length; i++) {
9786
- const hl = high[i] - low[i];
9787
- const hc = Math.abs(high[i] - close[i - 1]);
9788
- const lc = Math.abs(low[i] - close[i - 1]);
9789
- tr[i] = Math.max(hl, hc, lc);
9790
- }
9791
- const atr2 = new Array(high.length).fill(NaN);
9792
- let sum = 0;
9793
- for (let i = 0; i < period; i++) {
9794
- sum += tr[i];
9795
- }
9796
- atr2[period - 1] = sum / period;
9797
- for (let i = period; i < tr.length; i++) {
9798
- atr2[i] = (atr2[i - 1] * (period - 1) + tr[i]) / period;
9799
- }
9800
- return atr2;
9801
- }
9802
- function ema(source, period) {
9803
- const result = new Array(source.length).fill(NaN);
9804
- const alpha = 2 / (period + 1);
9805
- let sum = 0;
9806
- for (let i = 0; i < period; i++) {
9807
- sum += source[i] || 0;
9808
- }
9809
- result[period - 1] = sum / period;
9810
- for (let i = period; i < source.length; i++) {
9811
- result[i] = source[i] * alpha + result[i - 1] * (1 - alpha);
9812
- }
9813
- return result;
9814
- }
9815
- function rsi(source, period) {
9816
- const result = new Array(source.length).fill(NaN);
9817
- const gains = new Array(source.length).fill(0);
9818
- const losses = new Array(source.length).fill(0);
9819
- for (let i = 1; i < source.length; i++) {
9820
- const diff = source[i] - source[i - 1];
9821
- gains[i] = diff > 0 ? diff : 0;
9822
- losses[i] = diff < 0 ? -diff : 0;
9823
- }
9824
- let avgGain = 0;
9825
- let avgLoss = 0;
9826
- for (let i = 1; i <= period; i++) {
9827
- avgGain += gains[i];
9828
- avgLoss += losses[i];
9829
- }
9830
- avgGain /= period;
9831
- avgLoss /= period;
9832
- result[period] = avgLoss === 0 ? 100 : 100 - 100 / (1 + avgGain / avgLoss);
9833
- for (let i = period + 1; i < source.length; i++) {
9834
- avgGain = (avgGain * (period - 1) + gains[i]) / period;
9835
- avgLoss = (avgLoss * (period - 1) + losses[i]) / period;
9836
- result[i] = avgLoss === 0 ? 100 : 100 - 100 / (1 + avgGain / avgLoss);
9837
- }
9838
- return result;
9839
- }
9840
- function rma(source, period) {
9841
- const result = new Array(source.length).fill(NaN);
9842
- const alpha = 1 / period;
9843
- let sum = 0;
9844
- for (let i = 0; i < period; i++) {
9845
- sum += source[i] || 0;
9846
- }
9847
- result[period - 1] = sum / period;
9848
- for (let i = period; i < source.length; i++) {
9849
- const currentValue = source[i] || 0;
9850
- result[i] = currentValue * alpha + result[i - 1] * (1 - alpha);
9851
- }
9852
- return result;
9853
- }
9854
- function sma_cache(source, period, cacheObj) {
9855
- const result = cacheObj.previousResult || new Array(source.length).fill(NaN);
9856
- const lastProcessedIndex = cacheObj.lastProcessedIndex || -1;
9857
- let previousSum = cacheObj.previousSum || 0;
9858
- if (lastProcessedIndex === -1 || source.length !== lastProcessedIndex + 1) {
9859
- previousSum = 0;
9860
- for (let i = 0; i < period; i++) {
9861
- previousSum += source[i] || 0;
9862
- }
9863
- result[period - 1] = previousSum / period;
9864
- for (let i = 0; i < period - 1; i++) {
9865
- result[i] = NaN;
9866
- }
9867
- for (let i = period; i < source.length; i++) {
9868
- previousSum = previousSum - (source[i - period] || 0) + (source[i] || 0);
9869
- result[i] = previousSum / period;
9870
- }
9871
- } else if (source.length === lastProcessedIndex + 2) {
9872
- const newIndex = source.length - 1;
9873
- previousSum = previousSum - (source[newIndex - period] || 0) + (source[newIndex] || 0);
9874
- result[newIndex] = previousSum / period;
9875
- } else {
9876
- return sma(source, period);
9877
- }
9878
- cacheObj.previousSum = previousSum;
9879
- cacheObj.lastProcessedIndex = source.length - 1;
9880
- cacheObj.previousResult = result;
9881
- return result;
9882
- }
9883
- function sma(source, period) {
9884
- const result = new Array(source.length).fill(NaN);
9885
- for (let i = period - 1; i < source.length; i++) {
9886
- let sum = 0;
9887
- for (let j = 0; j < period; j++) {
9888
- sum += source[i - j] || 0;
9889
- }
9890
- result[i] = sum / period;
9891
- }
9892
- return result;
9893
- }
9894
- function vwma(source, volume, period) {
9895
- const result = new Array(source.length).fill(NaN);
9896
- for (let i = period - 1; i < source.length; i++) {
9897
- let sumVol = 0;
9898
- let sumVolPrice = 0;
9899
- for (let j = 0; j < period; j++) {
9900
- sumVol += volume[i - j];
9901
- sumVolPrice += source[i - j] * volume[i - j];
9902
- }
9903
- result[i] = sumVolPrice / sumVol;
9904
- }
9905
- return result;
9906
- }
9907
- function hma(source, period) {
9908
- const halfPeriod = Math.floor(period / 2);
9909
- const wma1 = wma(source, halfPeriod);
9910
- const wma2 = wma(source, period);
9911
- const rawHma = wma1.map((value, index) => 2 * value - wma2[index]);
9912
- const sqrtPeriod = Math.floor(Math.sqrt(period));
9913
- const result = wma(rawHma, sqrtPeriod);
9914
- return result;
9915
- }
9916
- function wma(source, period) {
9917
- const result = new Array(source.length);
9918
- for (let i = period - 1; i < source.length; i++) {
9919
- let numerator = 0;
9920
- let denominator = 0;
9921
- for (let j = 0; j < period; j++) {
9922
- numerator += source[i - j] * (period - j);
9923
- denominator += period - j;
9924
- }
9925
- result[i] = numerator / denominator;
9926
- }
9927
- for (let i = 0; i < period - 1; i++) {
9928
- result[i] = NaN;
9929
- }
9930
- return result;
9931
- }
9932
- function change(source, length = 1) {
9933
- const result = new Array(source.length).fill(NaN);
9934
- for (let i = length; i < source.length; i++) {
9935
- result[i] = source[i] - source[i - length];
9936
- }
9937
- return result;
9938
- }
9939
- function mom(source, length) {
9940
- const result = new Array(source.length).fill(NaN);
9941
- for (let i = length; i < source.length; i++) {
9942
- result[i] = source[i] - source[i - length];
9943
- }
9944
- return result;
9945
- }
9946
- function roc(source, length) {
9947
- const result = new Array(source.length).fill(NaN);
9948
- for (let i = length; i < source.length; i++) {
9949
- result[i] = (source[i] - source[i - length]) / source[i - length] * 100;
9950
- }
9951
- return result;
9952
- }
9953
- function dev(source, length) {
9954
- const result = new Array(source.length).fill(NaN);
9955
- const smaValues = sma(source, length);
9956
- for (let i = length - 1; i < source.length; i++) {
9957
- let sumDeviation = 0;
9958
- for (let j = 0; j < length; j++) {
9959
- sumDeviation += Math.abs(source[i - j] - smaValues[i]);
9960
- }
9961
- result[i] = sumDeviation / length;
9962
- }
9963
- return result;
9964
- }
9965
- function variance(source, length) {
9966
- const result = new Array(source.length).fill(NaN);
9967
- for (let i = length - 1; i < source.length; i++) {
9968
- let sum = 0;
9969
- let sumSquares = 0;
9970
- for (let j = 0; j < length; j++) {
9971
- sum += source[i - j];
9972
- sumSquares += source[i - j] * source[i - j];
9973
- }
9974
- const mean = sum / length;
9975
- result[i] = sumSquares / length - mean * mean;
9976
- }
9977
- return result;
9978
- }
9979
- function highest(source, length) {
9980
- const result = new Array(source.length).fill(NaN);
9981
- for (let i = length - 1; i < source.length; i++) {
9982
- let max = -Infinity;
9983
- for (let j = 0; j < length; j++) {
9984
- const value = source[i - j];
9985
- if (isNaN(value)) {
9986
- max = max === -Infinity ? NaN : max;
9987
- } else {
9988
- max = Math.max(max, value);
9989
- }
9990
- }
9991
- result[i] = max;
9992
- }
9993
- return result;
9994
- }
9995
- function lowest(source, length) {
9996
- const result = new Array(source.length).fill(NaN);
9997
- for (let i = length - 1; i < source.length; i++) {
9998
- let min = Infinity;
9999
- for (let j = 0; j < length; j++) {
10000
- const value = source[i - j];
10001
- if (isNaN(value) || value === void 0) {
10002
- min = min === Infinity ? NaN : min;
10003
- } else {
10004
- min = Math.min(min, value);
10005
- }
10006
- }
10007
- result[i] = min;
10008
- }
10009
- return result;
10010
- }
10011
- function median(source, length) {
10012
- const result = new Array(source.length).fill(NaN);
10013
- for (let i = length - 1; i < source.length; i++) {
10014
- const window = source.slice(i - length + 1, i + 1);
10015
- const sorted = window.slice().sort((a, b) => a - b);
10016
- const mid = Math.floor(length / 2);
10017
- result[i] = length % 2 === 0 ? (sorted[mid - 1] + sorted[mid]) / 2 : sorted[mid];
10018
- }
10019
- return result;
10020
- }
10021
- function stdev(source, length, biased = true) {
10022
- const result = new Array(source.length).fill(NaN);
10023
- const smaValues = sma(source, length);
10024
- for (let i = length - 1; i < source.length; i++) {
10025
- let sum = 0;
10026
- for (let j = 0; j < length; j++) {
10027
- sum += Math.pow(source[i - j] - smaValues[i], 2);
10028
- }
10029
- const divisor = biased ? length : length - 1;
10030
- result[i] = Math.sqrt(sum / divisor);
10031
- }
10032
- return result;
10033
- }
10034
- function linreg(source, length, offset) {
10035
- const size = source.length;
10036
- const output = new Array(size).fill(NaN);
10037
- for (let i = length - 1; i < size; i++) {
10038
- let sumX = 0;
10039
- let sumY = 0;
10040
- let sumXY = 0;
10041
- let sumXX = 0;
10042
- const n = length;
10043
- for (let j = 0; j < length; j++) {
10044
- const x = j;
10045
- const y = source[i - length + 1 + j];
10046
- sumX += x;
10047
- sumY += y;
10048
- sumXY += x * y;
10049
- sumXX += x * x;
10050
- }
10051
- const denominator = n * sumXX - sumX * sumX;
10052
- if (denominator === 0) {
10053
- output[i] = NaN;
10054
- continue;
10055
- }
10056
- const slope = (n * sumXY - sumX * sumY) / denominator;
10057
- const intercept = (sumY - slope * sumX) / n;
10058
- const linRegValue = intercept + slope * (length - 1 - offset);
10059
- output[i] = linRegValue;
10060
- }
10061
- return output;
10062
- }
10063
- function calculateSupertrend(high, low, close, factor, atrPeriod) {
10064
- const length = high.length;
10065
- const supertrend = new Array(length).fill(NaN);
10066
- const direction = new Array(length).fill(0);
10067
- const atrValues = atr(high, low, close, atrPeriod);
10068
- const upperBand = new Array(length).fill(NaN);
10069
- const lowerBand = new Array(length).fill(NaN);
10070
- for (let i = 0; i < length; i++) {
10071
- const hl2 = (high[i] + low[i]) / 2;
10072
- const atrValue = atrValues[i];
10073
- if (!isNaN(atrValue)) {
10074
- upperBand[i] = hl2 + factor * atrValue;
10075
- lowerBand[i] = hl2 - factor * atrValue;
10076
- }
10077
- }
10078
- let prevUpperBand = upperBand[atrPeriod];
10079
- let prevLowerBand = lowerBand[atrPeriod];
10080
- let prevSupertrend = close[atrPeriod] <= prevUpperBand ? prevUpperBand : prevLowerBand;
10081
- let prevDirection = close[atrPeriod] <= prevUpperBand ? -1 : 1;
10082
- supertrend[atrPeriod] = prevSupertrend;
10083
- direction[atrPeriod] = prevDirection;
10084
- for (let i = atrPeriod + 1; i < length; i++) {
10085
- let currentUpperBand = upperBand[i];
10086
- if (currentUpperBand < prevUpperBand || close[i - 1] > prevUpperBand) {
10087
- upperBand[i] = currentUpperBand;
10088
- } else {
10089
- upperBand[i] = prevUpperBand;
10090
- }
10091
- let currentLowerBand = lowerBand[i];
10092
- if (currentLowerBand > prevLowerBand || close[i - 1] < prevLowerBand) {
10093
- lowerBand[i] = currentLowerBand;
10094
- } else {
10095
- lowerBand[i] = prevLowerBand;
10096
- }
10097
- if (prevSupertrend === prevUpperBand) {
10098
- if (close[i] > upperBand[i]) {
10099
- direction[i] = 1;
10100
- supertrend[i] = lowerBand[i];
10101
- } else {
10102
- direction[i] = -1;
10103
- supertrend[i] = upperBand[i];
10104
- }
10105
- } else {
10106
- if (close[i] < lowerBand[i]) {
10107
- direction[i] = -1;
10108
- supertrend[i] = upperBand[i];
10109
- } else {
10110
- direction[i] = 1;
10111
- supertrend[i] = lowerBand[i];
10112
- }
10113
- }
10114
- prevUpperBand = upperBand[i];
10115
- prevLowerBand = lowerBand[i];
10116
- prevSupertrend = supertrend[i];
10117
- }
10118
- return [supertrend, direction];
10119
- }
10120
10242
  function pivothigh(source, leftbars, rightbars) {
10121
10243
  const result = new Array(source.length).fill(NaN);
10122
10244
  for (let i = leftbars + rightbars; i < source.length; i++) {
@@ -10384,6 +10506,8 @@
10384
10506
  ohlc4: []
10385
10507
  });
10386
10508
  __publicField$1(this, "cache", {});
10509
+ __publicField$1(this, "taState", {});
10510
+ // State for incremental TA calculations
10387
10511
  __publicField$1(this, "useTACache", false);
10388
10512
  __publicField$1(this, "NA", NaN);
10389
10513
  __publicField$1(this, "math");
@@ -10619,7 +10743,6 @@
10619
10743
  if (data.length === 0) break;
10620
10744
  allData = allData.concat(data);
10621
10745
  currentStart = data[data.length - 1].closeTime + 1;
10622
- if (data.length < 1e3) break;
10623
10746
  }
10624
10747
  return allData;
10625
10748
  } catch (error) {
@@ -10627,8 +10750,6 @@
10627
10750
  return [];
10628
10751
  }
10629
10752
  }
10630
- //TODO : allow querying more than 1000 klines
10631
- //TODO : immplement cache
10632
10753
  async getMarketData(tickerId, timeframe, limit, sDate, eDate) {
10633
10754
  try {
10634
10755
  const cacheParams = { tickerId, timeframe, limit, sDate, eDate };
@@ -10642,12 +10763,16 @@
10642
10763
  console.error(`Unsupported timeframe: ${timeframe}`);
10643
10764
  return [];
10644
10765
  }
10645
- let url = `${BINANCE_API_URL}/klines?symbol=${tickerId}&interval=${interval}`;
10646
- if (!limit && sDate && eDate) {
10647
- return this.getMarketDataInterval(tickerId, timeframe, sDate, eDate);
10766
+ const needsPagination = this.shouldPaginate(timeframe, limit, sDate, eDate);
10767
+ if (needsPagination && sDate && eDate) {
10768
+ const allData = await this.getMarketDataInterval(tickerId, timeframe, sDate, eDate);
10769
+ const result2 = limit ? allData.slice(0, limit) : allData;
10770
+ this.cacheManager.set(cacheParams, result2);
10771
+ return result2;
10648
10772
  }
10773
+ let url = `${BINANCE_API_URL}/klines?symbol=${tickerId}&interval=${interval}`;
10649
10774
  if (limit) {
10650
- url += `&limit=${limit}`;
10775
+ url += `&limit=${Math.min(limit, 1e3)}`;
10651
10776
  }
10652
10777
  if (sDate) {
10653
10778
  url += `&startTime=${sDate}`;
@@ -10683,6 +10808,36 @@
10683
10808
  return [];
10684
10809
  }
10685
10810
  }
10811
+ /**
10812
+ * Determines if pagination is needed based on the parameters
10813
+ */
10814
+ shouldPaginate(timeframe, limit, sDate, eDate) {
10815
+ if (limit && limit > 1e3) {
10816
+ return true;
10817
+ }
10818
+ if (sDate && eDate) {
10819
+ const interval = timeframe_to_binance[timeframe.toUpperCase()];
10820
+ const timeframeDurations = {
10821
+ "1m": 60 * 1e3,
10822
+ "3m": 3 * 60 * 1e3,
10823
+ "5m": 5 * 60 * 1e3,
10824
+ "15m": 15 * 60 * 1e3,
10825
+ "30m": 30 * 60 * 1e3,
10826
+ "1h": 60 * 60 * 1e3,
10827
+ "2h": 2 * 60 * 60 * 1e3,
10828
+ "4h": 4 * 60 * 60 * 1e3,
10829
+ "1d": 24 * 60 * 60 * 1e3,
10830
+ "1w": 7 * 24 * 60 * 60 * 1e3,
10831
+ "1M": 30 * 24 * 60 * 60 * 1e3
10832
+ };
10833
+ const intervalDuration = timeframeDurations[interval];
10834
+ if (intervalDuration) {
10835
+ const requiredCandles = Math.ceil((eDate - sDate) / intervalDuration);
10836
+ return requiredCandles > 1e3;
10837
+ }
10838
+ }
10839
+ return false;
10840
+ }
10686
10841
  }
10687
10842
 
10688
10843
  const Provider = {