pinets 0.1.34 → 0.2.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.
@@ -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.reverse().slice(0, MAX_PERIODS);
9187
9244
  this._periods = marketData.length;
9188
9245
  this.data = marketData;
9189
9246
  const _open = marketData.map((d) => d.open);
@@ -9441,6 +9498,9 @@
9441
9498
  return this.context.params[name];
9442
9499
  }
9443
9500
  }
9501
+ __eq(a, b) {
9502
+ return Math.abs(a - b) < 1e-8;
9503
+ }
9444
9504
  abs(source) {
9445
9505
  return Math.abs(source[0]);
9446
9506
  }
@@ -9604,141 +9664,532 @@
9604
9664
  return this.context.params[name];
9605
9665
  }
9606
9666
  }
9607
- ema(source, _period) {
9667
+ ema(source, _period, _callId) {
9608
9668
  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) {
9669
+ if (!this.context.taState) this.context.taState = {};
9670
+ const stateKey = _callId || `ema_${period}`;
9671
+ if (!this.context.taState[stateKey]) {
9672
+ this.context.taState[stateKey] = { prevEma: null, initSum: 0, initCount: 0 };
9673
+ }
9674
+ const state = this.context.taState[stateKey];
9675
+ const currentValue = source[0];
9676
+ if (state.initCount < period) {
9677
+ state.initSum += currentValue;
9678
+ state.initCount++;
9679
+ if (state.initCount === period) {
9680
+ state.prevEma = state.initSum / period;
9681
+ return this.context.precision(state.prevEma);
9682
+ }
9683
+ return NaN;
9684
+ }
9685
+ const alpha = 2 / (period + 1);
9686
+ const ema2 = currentValue * alpha + state.prevEma * (1 - alpha);
9687
+ state.prevEma = ema2;
9688
+ return this.context.precision(ema2);
9689
+ }
9690
+ sma(source, _period, _callId) {
9614
9691
  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) {
9692
+ if (!this.context.taState) this.context.taState = {};
9693
+ const stateKey = _callId || `sma_${period}`;
9694
+ if (!this.context.taState[stateKey]) {
9695
+ this.context.taState[stateKey] = { window: [], sum: 0 };
9696
+ }
9697
+ const state = this.context.taState[stateKey];
9698
+ const currentValue = source[0] || 0;
9699
+ state.window.unshift(currentValue);
9700
+ state.sum += currentValue;
9701
+ if (state.window.length < period) {
9702
+ return NaN;
9703
+ }
9704
+ if (state.window.length > period) {
9705
+ const oldValue = state.window.pop();
9706
+ state.sum -= oldValue;
9707
+ }
9708
+ const sma2 = state.sum / period;
9709
+ return this.context.precision(sma2);
9710
+ }
9711
+ vwma(source, _period, _callId) {
9632
9712
  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]);
9713
+ if (!this.context.taState) this.context.taState = {};
9714
+ const stateKey = _callId || `vwma_${period}`;
9715
+ if (!this.context.taState[stateKey]) {
9716
+ this.context.taState[stateKey] = { window: [], volumeWindow: [] };
9717
+ }
9718
+ const state = this.context.taState[stateKey];
9719
+ const currentValue = source[0];
9720
+ const currentVolume = this.context.data.volume[0];
9721
+ state.window.unshift(currentValue);
9722
+ state.volumeWindow.unshift(currentVolume);
9723
+ if (state.window.length < period) {
9724
+ return NaN;
9725
+ }
9726
+ if (state.window.length > period) {
9727
+ state.window.pop();
9728
+ state.volumeWindow.pop();
9729
+ }
9730
+ let sumVolPrice = 0;
9731
+ let sumVol = 0;
9732
+ for (let i = 0; i < period; i++) {
9733
+ sumVolPrice += state.window[i] * state.volumeWindow[i];
9734
+ sumVol += state.volumeWindow[i];
9735
+ }
9736
+ const vwma2 = sumVolPrice / sumVol;
9737
+ return this.context.precision(vwma2);
9637
9738
  }
9638
- wma(source, _period) {
9739
+ wma(source, _period, _callId) {
9639
9740
  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]);
9741
+ if (!this.context.taState) this.context.taState = {};
9742
+ const stateKey = _callId || `wma_${period}`;
9743
+ if (!this.context.taState[stateKey]) {
9744
+ this.context.taState[stateKey] = { window: [] };
9745
+ }
9746
+ const state = this.context.taState[stateKey];
9747
+ const currentValue = source[0];
9748
+ state.window.unshift(currentValue);
9749
+ if (state.window.length < period) {
9750
+ return NaN;
9751
+ }
9752
+ if (state.window.length > period) {
9753
+ state.window.pop();
9754
+ }
9755
+ let numerator = 0;
9756
+ let denominator = 0;
9757
+ for (let i = 0; i < period; i++) {
9758
+ const weight = period - i;
9759
+ numerator += state.window[i] * weight;
9760
+ denominator += weight;
9761
+ }
9762
+ const wma2 = numerator / denominator;
9763
+ return this.context.precision(wma2);
9643
9764
  }
9644
- hma(source, _period) {
9765
+ hma(source, _period, _callId) {
9645
9766
  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]);
9767
+ const halfPeriod = Math.floor(period / 2);
9768
+ const sqrtPeriod = Math.floor(Math.sqrt(period));
9769
+ const wma1 = this.wma(source, halfPeriod, _callId ? `${_callId}_wma1` : void 0);
9770
+ const wma2 = this.wma(source, period, _callId ? `${_callId}_wma2` : void 0);
9771
+ if (isNaN(wma1) || isNaN(wma2)) {
9772
+ return NaN;
9773
+ }
9774
+ if (!this.context.taState) this.context.taState = {};
9775
+ const stateKey = _callId || `hma_raw_${period}`;
9776
+ if (!this.context.taState[stateKey]) {
9777
+ this.context.taState[stateKey] = [];
9778
+ }
9779
+ const rawHma = 2 * wma1 - wma2;
9780
+ this.context.taState[stateKey].unshift(rawHma);
9781
+ const hmaStateKey = _callId ? `${_callId}_hma_final` : `hma_final_${period}`;
9782
+ if (!this.context.taState[hmaStateKey]) {
9783
+ this.context.taState[hmaStateKey] = { window: [] };
9784
+ }
9785
+ const state = this.context.taState[hmaStateKey];
9786
+ state.window.unshift(rawHma);
9787
+ if (state.window.length < sqrtPeriod) {
9788
+ return NaN;
9789
+ }
9790
+ if (state.window.length > sqrtPeriod) {
9791
+ state.window.pop();
9792
+ }
9793
+ let numerator = 0;
9794
+ let denominator = 0;
9795
+ for (let i = 0; i < sqrtPeriod; i++) {
9796
+ const weight = sqrtPeriod - i;
9797
+ numerator += state.window[i] * weight;
9798
+ denominator += weight;
9799
+ }
9800
+ const hma2 = numerator / denominator;
9801
+ return this.context.precision(hma2);
9649
9802
  }
9650
- rma(source, _period) {
9803
+ rma(source, _period, _callId) {
9651
9804
  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) {
9805
+ if (!this.context.taState) this.context.taState = {};
9806
+ const stateKey = _callId || `rma_${period}`;
9807
+ if (!this.context.taState[stateKey]) {
9808
+ this.context.taState[stateKey] = { prevRma: null, initSum: 0, initCount: 0 };
9809
+ }
9810
+ const state = this.context.taState[stateKey];
9811
+ const currentValue = source[0] || 0;
9812
+ if (state.initCount < period) {
9813
+ state.initSum += currentValue;
9814
+ state.initCount++;
9815
+ if (state.initCount === period) {
9816
+ state.prevRma = state.initSum / period;
9817
+ return this.context.precision(state.prevRma);
9818
+ }
9819
+ return NaN;
9820
+ }
9821
+ const alpha = 1 / period;
9822
+ const rma2 = currentValue * alpha + state.prevRma * (1 - alpha);
9823
+ state.prevRma = rma2;
9824
+ return this.context.precision(rma2);
9825
+ }
9826
+ change(source, _length = 1, _callId) {
9657
9827
  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]);
9828
+ if (!this.context.taState) this.context.taState = {};
9829
+ const stateKey = _callId || `change_${length}`;
9830
+ if (!this.context.taState[stateKey]) {
9831
+ this.context.taState[stateKey] = { window: [] };
9832
+ }
9833
+ const state = this.context.taState[stateKey];
9834
+ const currentValue = source[0];
9835
+ state.window.unshift(currentValue);
9836
+ if (state.window.length <= length) {
9837
+ return NaN;
9838
+ }
9839
+ if (state.window.length > length + 1) {
9840
+ state.window.pop();
9841
+ }
9842
+ const change2 = currentValue - state.window[length];
9843
+ return this.context.precision(change2);
9661
9844
  }
9662
- rsi(source, _period) {
9845
+ rsi(source, _period, _callId) {
9663
9846
  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]);
9847
+ if (!this.context.taState) this.context.taState = {};
9848
+ const stateKey = _callId || `rsi_${period}`;
9849
+ if (!this.context.taState[stateKey]) {
9850
+ this.context.taState[stateKey] = {
9851
+ prevValue: null,
9852
+ avgGain: 0,
9853
+ avgLoss: 0,
9854
+ initGains: [],
9855
+ initLosses: []
9856
+ };
9857
+ }
9858
+ const state = this.context.taState[stateKey];
9859
+ const currentValue = source[0];
9860
+ if (state.prevValue !== null) {
9861
+ const diff = currentValue - state.prevValue;
9862
+ const gain = diff > 0 ? diff : 0;
9863
+ const loss = diff < 0 ? -diff : 0;
9864
+ if (state.initGains.length < period) {
9865
+ state.initGains.push(gain);
9866
+ state.initLosses.push(loss);
9867
+ if (state.initGains.length === period) {
9868
+ state.avgGain = state.initGains.reduce((a, b) => a + b, 0) / period;
9869
+ state.avgLoss = state.initLosses.reduce((a, b) => a + b, 0) / period;
9870
+ state.prevValue = currentValue;
9871
+ const rsi3 = state.avgLoss === 0 ? 100 : 100 - 100 / (1 + state.avgGain / state.avgLoss);
9872
+ return this.context.precision(rsi3);
9873
+ }
9874
+ state.prevValue = currentValue;
9875
+ return NaN;
9876
+ }
9877
+ state.avgGain = (state.avgGain * (period - 1) + gain) / period;
9878
+ state.avgLoss = (state.avgLoss * (period - 1) + loss) / period;
9879
+ const rsi2 = state.avgLoss === 0 ? 100 : 100 - 100 / (1 + state.avgGain / state.avgLoss);
9880
+ state.prevValue = currentValue;
9881
+ return this.context.precision(rsi2);
9882
+ }
9883
+ state.prevValue = currentValue;
9884
+ return NaN;
9667
9885
  }
9668
- atr(_period) {
9886
+ atr(_period, _callId) {
9669
9887
  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]);
9888
+ if (!this.context.taState) this.context.taState = {};
9889
+ const stateKey = _callId || `atr_${period}`;
9890
+ if (!this.context.taState[stateKey]) {
9891
+ this.context.taState[stateKey] = {
9892
+ prevAtr: null,
9893
+ initSum: 0,
9894
+ initCount: 0,
9895
+ prevClose: null
9896
+ };
9897
+ }
9898
+ const state = this.context.taState[stateKey];
9899
+ const high = this.context.data.high[0];
9900
+ const low = this.context.data.low[0];
9901
+ const close = this.context.data.close[0];
9902
+ let tr;
9903
+ if (state.prevClose !== null) {
9904
+ const hl = high - low;
9905
+ const hc = Math.abs(high - state.prevClose);
9906
+ const lc = Math.abs(low - state.prevClose);
9907
+ tr = Math.max(hl, hc, lc);
9908
+ } else {
9909
+ tr = high - low;
9910
+ }
9911
+ state.prevClose = close;
9912
+ if (state.initCount < period) {
9913
+ state.initSum += tr;
9914
+ state.initCount++;
9915
+ if (state.initCount === period) {
9916
+ state.prevAtr = state.initSum / period;
9917
+ return this.context.precision(state.prevAtr);
9918
+ }
9919
+ return NaN;
9920
+ }
9921
+ const atr2 = (state.prevAtr * (period - 1) + tr) / period;
9922
+ state.prevAtr = atr2;
9923
+ return this.context.precision(atr2);
9676
9924
  }
9677
- mom(source, _length) {
9925
+ mom(source, _length, _callId) {
9678
9926
  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]);
9927
+ return this.change(source, length);
9682
9928
  }
9683
- roc(source, _length) {
9929
+ roc(source, _length, _callId) {
9684
9930
  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]);
9931
+ if (!this.context.taState) this.context.taState = {};
9932
+ const stateKey = _callId || `roc_${length}`;
9933
+ if (!this.context.taState[stateKey]) {
9934
+ this.context.taState[stateKey] = { window: [] };
9935
+ }
9936
+ const state = this.context.taState[stateKey];
9937
+ const currentValue = source[0];
9938
+ state.window.unshift(currentValue);
9939
+ if (state.window.length <= length) {
9940
+ return NaN;
9941
+ }
9942
+ if (state.window.length > length + 1) {
9943
+ state.window.pop();
9944
+ }
9945
+ const prevValue = state.window[length];
9946
+ const roc2 = (currentValue - prevValue) / prevValue * 100;
9947
+ return this.context.precision(roc2);
9688
9948
  }
9689
- dev(source, _length) {
9949
+ dev(source, _length, _callId) {
9690
9950
  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]);
9951
+ if (!this.context.taState) this.context.taState = {};
9952
+ const stateKey = _callId || `dev_${length}`;
9953
+ if (!this.context.taState[stateKey]) {
9954
+ this.context.taState[stateKey] = { window: [], sum: 0 };
9955
+ }
9956
+ const state = this.context.taState[stateKey];
9957
+ const currentValue = source[0] || 0;
9958
+ state.window.unshift(currentValue);
9959
+ state.sum += currentValue;
9960
+ if (state.window.length < length) {
9961
+ return NaN;
9962
+ }
9963
+ if (state.window.length > length) {
9964
+ const oldValue = state.window.pop();
9965
+ state.sum -= oldValue;
9966
+ }
9967
+ const mean = state.sum / length;
9968
+ let sumDeviation = 0;
9969
+ for (let i = 0; i < length; i++) {
9970
+ sumDeviation += Math.abs(state.window[i] - mean);
9971
+ }
9972
+ const dev2 = sumDeviation / length;
9973
+ return this.context.precision(dev2);
9694
9974
  }
9695
- variance(source, _length) {
9975
+ variance(source, _length, _callId) {
9696
9976
  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]);
9977
+ if (!this.context.taState) this.context.taState = {};
9978
+ const stateKey = _callId || `variance_${length}`;
9979
+ if (!this.context.taState[stateKey]) {
9980
+ this.context.taState[stateKey] = { window: [] };
9981
+ }
9982
+ const state = this.context.taState[stateKey];
9983
+ const currentValue = source[0];
9984
+ state.window.unshift(currentValue);
9985
+ if (state.window.length < length) {
9986
+ return NaN;
9987
+ }
9988
+ if (state.window.length > length) {
9989
+ state.window.pop();
9990
+ }
9991
+ let sum = 0;
9992
+ let sumSquares = 0;
9993
+ for (let i = 0; i < length; i++) {
9994
+ sum += state.window[i];
9995
+ sumSquares += state.window[i] * state.window[i];
9996
+ }
9997
+ const mean = sum / length;
9998
+ const variance2 = sumSquares / length - mean * mean;
9999
+ return this.context.precision(variance2);
9700
10000
  }
9701
- highest(source, _length) {
10001
+ highest(source, _length, _callId) {
9702
10002
  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]);
10003
+ if (!this.context.taState) this.context.taState = {};
10004
+ const stateKey = _callId || `highest_${length}`;
10005
+ if (!this.context.taState[stateKey]) {
10006
+ this.context.taState[stateKey] = { window: [] };
10007
+ }
10008
+ const state = this.context.taState[stateKey];
10009
+ const currentValue = source[0];
10010
+ state.window.unshift(currentValue);
10011
+ if (state.window.length < length) {
10012
+ return NaN;
10013
+ }
10014
+ if (state.window.length > length) {
10015
+ state.window.pop();
10016
+ }
10017
+ const max = Math.max(...state.window.filter((v) => !isNaN(v)));
10018
+ return this.context.precision(max);
9706
10019
  }
9707
- lowest(source, _length) {
10020
+ lowest(source, _length, _callId) {
9708
10021
  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]);
10022
+ if (!this.context.taState) this.context.taState = {};
10023
+ const stateKey = _callId || `lowest_${length}`;
10024
+ if (!this.context.taState[stateKey]) {
10025
+ this.context.taState[stateKey] = { window: [] };
10026
+ }
10027
+ const state = this.context.taState[stateKey];
10028
+ const currentValue = source[0];
10029
+ state.window.unshift(currentValue);
10030
+ if (state.window.length < length) {
10031
+ return NaN;
10032
+ }
10033
+ if (state.window.length > length) {
10034
+ state.window.pop();
10035
+ }
10036
+ const validValues = state.window.filter((v) => !isNaN(v) && v !== void 0);
10037
+ const min = validValues.length > 0 ? Math.min(...validValues) : NaN;
10038
+ return this.context.precision(min);
9712
10039
  }
9713
- median(source, _length) {
10040
+ median(source, _length, _callId) {
9714
10041
  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]);
10042
+ if (!this.context.taState) this.context.taState = {};
10043
+ const stateKey = _callId || `median_${length}`;
10044
+ if (!this.context.taState[stateKey]) {
10045
+ this.context.taState[stateKey] = { window: [] };
10046
+ }
10047
+ const state = this.context.taState[stateKey];
10048
+ const currentValue = source[0];
10049
+ state.window.unshift(currentValue);
10050
+ if (state.window.length < length) {
10051
+ return NaN;
10052
+ }
10053
+ if (state.window.length > length) {
10054
+ state.window.pop();
10055
+ }
10056
+ const sorted = state.window.slice().sort((a, b) => a - b);
10057
+ const mid = Math.floor(length / 2);
10058
+ const median2 = length % 2 === 0 ? (sorted[mid - 1] + sorted[mid]) / 2 : sorted[mid];
10059
+ return this.context.precision(median2);
9718
10060
  }
9719
- stdev(source, _length, _bias = true) {
10061
+ stdev(source, _length, _bias = true, _callId) {
9720
10062
  const length = Array.isArray(_length) ? _length[0] : _length;
9721
10063
  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]);
10064
+ if (!this.context.taState) this.context.taState = {};
10065
+ const stateKey = _callId || `stdev_${length}_${bias}`;
10066
+ if (!this.context.taState[stateKey]) {
10067
+ this.context.taState[stateKey] = { window: [], sum: 0 };
10068
+ }
10069
+ const state = this.context.taState[stateKey];
10070
+ const currentValue = source[0];
10071
+ state.window.unshift(currentValue);
10072
+ state.sum += currentValue;
10073
+ if (state.window.length < length) {
10074
+ return NaN;
10075
+ }
10076
+ if (state.window.length > length) {
10077
+ const oldValue = state.window.pop();
10078
+ state.sum -= oldValue;
10079
+ }
10080
+ const mean = state.sum / length;
10081
+ let sumSquaredDiff = 0;
10082
+ for (let i = 0; i < length; i++) {
10083
+ sumSquaredDiff += Math.pow(state.window[i] - mean, 2);
10084
+ }
10085
+ const divisor = bias ? length : length - 1;
10086
+ const stdev2 = Math.sqrt(sumSquaredDiff / divisor);
10087
+ return this.context.precision(stdev2);
9725
10088
  }
9726
- linreg(source, _length, _offset) {
10089
+ linreg(source, _length, _offset, _callId) {
9727
10090
  const length = Array.isArray(_length) ? _length[0] : _length;
9728
10091
  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]);
10092
+ if (!this.context.taState) this.context.taState = {};
10093
+ const stateKey = _callId || `linreg_${length}_${offset}`;
10094
+ if (!this.context.taState[stateKey]) {
10095
+ this.context.taState[stateKey] = { window: [] };
10096
+ }
10097
+ const state = this.context.taState[stateKey];
10098
+ const currentValue = source[0];
10099
+ state.window.unshift(currentValue);
10100
+ if (state.window.length < length) {
10101
+ return NaN;
10102
+ }
10103
+ if (state.window.length > length) {
10104
+ state.window.pop();
10105
+ }
10106
+ let sumX = 0;
10107
+ let sumY = 0;
10108
+ let sumXY = 0;
10109
+ let sumXX = 0;
10110
+ const n = length;
10111
+ for (let j = 0; j < length; j++) {
10112
+ const x = length - 1 - j;
10113
+ const y = state.window[j];
10114
+ sumX += x;
10115
+ sumY += y;
10116
+ sumXY += x * y;
10117
+ sumXX += x * x;
10118
+ }
10119
+ const denominator = n * sumXX - sumX * sumX;
10120
+ if (denominator === 0) {
10121
+ return NaN;
10122
+ }
10123
+ const slope = (n * sumXY - sumX * sumY) / denominator;
10124
+ const intercept = (sumY - slope * sumX) / n;
10125
+ const linRegValue = intercept + slope * (length - 1 - offset);
10126
+ return this.context.precision(linRegValue);
9732
10127
  }
9733
- supertrend(_factor, _atrPeriod) {
10128
+ supertrend(_factor, _atrPeriod, _callId) {
9734
10129
  const factor = Array.isArray(_factor) ? _factor[0] : _factor;
9735
10130
  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]]];
10131
+ if (!this.context.taState) this.context.taState = {};
10132
+ const stateKey = `supertrend_${factor}_${atrPeriod}`;
10133
+ if (!this.context.taState[stateKey]) {
10134
+ this.context.taState[stateKey] = {
10135
+ prevUpperBand: null,
10136
+ prevLowerBand: null,
10137
+ prevSupertrend: null,
10138
+ prevDirection: null
10139
+ };
10140
+ }
10141
+ const state = this.context.taState[stateKey];
10142
+ const high = this.context.data.high[0];
10143
+ const low = this.context.data.low[0];
10144
+ const close = this.context.data.close[0];
10145
+ const atrValue = this.atr(atrPeriod, _callId ? `${_callId}_atr` : void 0);
10146
+ if (isNaN(atrValue)) {
10147
+ return [[NaN, 0]];
10148
+ }
10149
+ const hl2 = (high + low) / 2;
10150
+ let upperBand = hl2 + factor * atrValue;
10151
+ let lowerBand = hl2 - factor * atrValue;
10152
+ if (state.prevUpperBand !== null) {
10153
+ if (upperBand < state.prevUpperBand || this.context.data.close[1] > state.prevUpperBand) {
10154
+ upperBand = upperBand;
10155
+ } else {
10156
+ upperBand = state.prevUpperBand;
10157
+ }
10158
+ if (lowerBand > state.prevLowerBand || this.context.data.close[1] < state.prevLowerBand) {
10159
+ lowerBand = lowerBand;
10160
+ } else {
10161
+ lowerBand = state.prevLowerBand;
10162
+ }
10163
+ }
10164
+ let direction;
10165
+ let supertrend;
10166
+ if (state.prevSupertrend === null) {
10167
+ direction = close <= upperBand ? -1 : 1;
10168
+ supertrend = direction === -1 ? upperBand : lowerBand;
10169
+ } else {
10170
+ if (state.prevSupertrend === state.prevUpperBand) {
10171
+ if (close > upperBand) {
10172
+ direction = 1;
10173
+ supertrend = lowerBand;
10174
+ } else {
10175
+ direction = -1;
10176
+ supertrend = upperBand;
10177
+ }
10178
+ } else {
10179
+ if (close < lowerBand) {
10180
+ direction = -1;
10181
+ supertrend = upperBand;
10182
+ } else {
10183
+ direction = 1;
10184
+ supertrend = lowerBand;
10185
+ }
10186
+ }
10187
+ }
10188
+ state.prevUpperBand = upperBand;
10189
+ state.prevLowerBand = lowerBand;
10190
+ state.prevSupertrend = supertrend;
10191
+ state.prevDirection = direction;
10192
+ return [[this.context.precision(supertrend), direction]];
9742
10193
  }
9743
10194
  crossover(source1, source2) {
9744
10195
  const current1 = Array.isArray(source1) ? source1[0] : source1;
@@ -9779,344 +10230,6 @@
9779
10230
  return this.context.precision(result[idx]);
9780
10231
  }
9781
10232
  }
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
10233
  function pivothigh(source, leftbars, rightbars) {
10121
10234
  const result = new Array(source.length).fill(NaN);
10122
10235
  for (let i = leftbars + rightbars; i < source.length; i++) {
@@ -10384,6 +10497,8 @@
10384
10497
  ohlc4: []
10385
10498
  });
10386
10499
  __publicField$1(this, "cache", {});
10500
+ __publicField$1(this, "taState", {});
10501
+ // State for incremental TA calculations
10387
10502
  __publicField$1(this, "useTACache", false);
10388
10503
  __publicField$1(this, "NA", NaN);
10389
10504
  __publicField$1(this, "math");
@@ -10619,7 +10734,6 @@
10619
10734
  if (data.length === 0) break;
10620
10735
  allData = allData.concat(data);
10621
10736
  currentStart = data[data.length - 1].closeTime + 1;
10622
- if (data.length < 1e3) break;
10623
10737
  }
10624
10738
  return allData;
10625
10739
  } catch (error) {
@@ -10627,8 +10741,6 @@
10627
10741
  return [];
10628
10742
  }
10629
10743
  }
10630
- //TODO : allow querying more than 1000 klines
10631
- //TODO : immplement cache
10632
10744
  async getMarketData(tickerId, timeframe, limit, sDate, eDate) {
10633
10745
  try {
10634
10746
  const cacheParams = { tickerId, timeframe, limit, sDate, eDate };
@@ -10642,12 +10754,16 @@
10642
10754
  console.error(`Unsupported timeframe: ${timeframe}`);
10643
10755
  return [];
10644
10756
  }
10645
- let url = `${BINANCE_API_URL}/klines?symbol=${tickerId}&interval=${interval}`;
10646
- if (!limit && sDate && eDate) {
10647
- return this.getMarketDataInterval(tickerId, timeframe, sDate, eDate);
10757
+ const needsPagination = this.shouldPaginate(timeframe, limit, sDate, eDate);
10758
+ if (needsPagination && sDate && eDate) {
10759
+ const allData = await this.getMarketDataInterval(tickerId, timeframe, sDate, eDate);
10760
+ const result2 = limit ? allData.slice(0, limit) : allData;
10761
+ this.cacheManager.set(cacheParams, result2);
10762
+ return result2;
10648
10763
  }
10764
+ let url = `${BINANCE_API_URL}/klines?symbol=${tickerId}&interval=${interval}`;
10649
10765
  if (limit) {
10650
- url += `&limit=${limit}`;
10766
+ url += `&limit=${Math.min(limit, 1e3)}`;
10651
10767
  }
10652
10768
  if (sDate) {
10653
10769
  url += `&startTime=${sDate}`;
@@ -10683,6 +10799,36 @@
10683
10799
  return [];
10684
10800
  }
10685
10801
  }
10802
+ /**
10803
+ * Determines if pagination is needed based on the parameters
10804
+ */
10805
+ shouldPaginate(timeframe, limit, sDate, eDate) {
10806
+ if (limit && limit > 1e3) {
10807
+ return true;
10808
+ }
10809
+ if (sDate && eDate) {
10810
+ const interval = timeframe_to_binance[timeframe.toUpperCase()];
10811
+ const timeframeDurations = {
10812
+ "1m": 60 * 1e3,
10813
+ "3m": 3 * 60 * 1e3,
10814
+ "5m": 5 * 60 * 1e3,
10815
+ "15m": 15 * 60 * 1e3,
10816
+ "30m": 30 * 60 * 1e3,
10817
+ "1h": 60 * 60 * 1e3,
10818
+ "2h": 2 * 60 * 60 * 1e3,
10819
+ "4h": 4 * 60 * 60 * 1e3,
10820
+ "1d": 24 * 60 * 60 * 1e3,
10821
+ "1w": 7 * 24 * 60 * 60 * 1e3,
10822
+ "1M": 30 * 24 * 60 * 60 * 1e3
10823
+ };
10824
+ const intervalDuration = timeframeDurations[interval];
10825
+ if (intervalDuration) {
10826
+ const requiredCandles = Math.ceil((eDate - sDate) / intervalDuration);
10827
+ return requiredCandles > 1e3;
10828
+ }
10829
+ }
10830
+ return false;
10831
+ }
10686
10832
  }
10687
10833
 
10688
10834
  const Provider = {