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.
@@ -37,6 +37,7 @@ class ScopeManager {
37
37
  __publicField$7(this, "paramIdCounter", 0);
38
38
  __publicField$7(this, "cacheIdCounter", 0);
39
39
  __publicField$7(this, "tempVarCounter", 0);
40
+ __publicField$7(this, "taCallIdCounter", 0);
40
41
  this.pushScope("glb");
41
42
  }
42
43
  get nextParamIdArg() {
@@ -51,6 +52,12 @@ class ScopeManager {
51
52
  name: `'cache_${this.cacheIdCounter++}'`
52
53
  };
53
54
  }
55
+ getNextTACallId() {
56
+ return {
57
+ type: "Literal",
58
+ value: `_ta${this.taCallIdCounter++}`
59
+ };
60
+ }
54
61
  pushScope(type) {
55
62
  this.scopes.push(/* @__PURE__ */ new Map());
56
63
  this.scopeTypes.push(type);
@@ -72,6 +79,14 @@ class ScopeManager {
72
79
  this.rootParams.add(name);
73
80
  }
74
81
  }
82
+ removeContextBoundVar(name) {
83
+ if (this.contextBoundVars.has(name)) {
84
+ this.contextBoundVars.delete(name);
85
+ if (this.rootParams.has(name)) {
86
+ this.rootParams.delete(name);
87
+ }
88
+ }
89
+ }
75
90
  addArrayPatternElement(name) {
76
91
  this.arrayPatternElements.add(name);
77
92
  }
@@ -718,38 +733,46 @@ function transformReturnStatement(node, scopeManager) {
718
733
  return prop;
719
734
  });
720
735
  } else if (node.argument.type === "Identifier") {
721
- const [scopedName, kind] = scopeManager.getVariable(node.argument.name);
722
- node.argument = {
723
- type: "MemberExpression",
724
- object: {
736
+ transformIdentifier(node.argument, scopeManager);
737
+ if (node.argument.type === "Identifier") {
738
+ addArrayAccess(node.argument);
739
+ }
740
+ }
741
+ if (curScope === "fn") {
742
+ if (node.argument.type === "Identifier" && scopeManager.isContextBound(node.argument.name) && !scopeManager.isRootParam(node.argument.name)) {
743
+ node.argument = {
725
744
  type: "MemberExpression",
726
- object: {
727
- type: "Identifier",
728
- name: CONTEXT_NAME
729
- },
745
+ object: node.argument,
730
746
  property: {
731
- type: "Identifier",
732
- name: kind
747
+ type: "Literal",
748
+ value: 0
733
749
  },
734
- computed: false
735
- },
736
- property: {
737
- type: "Identifier",
738
- name: scopedName
739
- },
740
- computed: false
741
- };
742
- node.argument = {
743
- type: "MemberExpression",
744
- object: node.argument,
745
- property: {
746
- type: "Literal",
747
- value: 0
748
- },
749
- computed: true
750
- };
751
- }
752
- if (curScope === "fn") {
750
+ computed: true
751
+ };
752
+ } else if (node.argument.type === "MemberExpression") {
753
+ if (node.argument.object.type === "Identifier" && scopeManager.isContextBound(node.argument.object.name) && !scopeManager.isRootParam(node.argument.object.name)) {
754
+ if (!node.argument._indexTransformed) {
755
+ transformArrayIndex(node.argument, scopeManager);
756
+ node.argument._indexTransformed = true;
757
+ }
758
+ }
759
+ } else if (node.argument.type === "BinaryExpression" || node.argument.type === "LogicalExpression" || node.argument.type === "ConditionalExpression" || node.argument.type === "CallExpression") {
760
+ walk.recursive(node.argument, scopeManager, {
761
+ Identifier(node2, state) {
762
+ transformIdentifier(node2, state);
763
+ if (node2.type === "Identifier" && !node2._arrayAccessed) {
764
+ addArrayAccess(node2);
765
+ node2._arrayAccessed = true;
766
+ }
767
+ },
768
+ MemberExpression(node2) {
769
+ transformMemberExpression(node2, "", scopeManager);
770
+ },
771
+ CallExpression(node2, state) {
772
+ transformCallExpression(node2, state);
773
+ }
774
+ });
775
+ }
753
776
  node.argument = {
754
777
  type: "CallExpression",
755
778
  callee: {
@@ -1100,6 +1123,9 @@ function transformCallExpression(node, scopeManager, namespace) {
1100
1123
  }
1101
1124
  return transformFunctionArgument(arg, namespace2, scopeManager);
1102
1125
  });
1126
+ if (namespace2 === "ta") {
1127
+ node.arguments.push(scopeManager.getNextTACallId());
1128
+ }
1103
1129
  node._transformed = true;
1104
1130
  } else if (node.callee && node.callee.type === "Identifier") {
1105
1131
  node.arguments = node.arguments.map((arg) => {
@@ -1140,9 +1166,11 @@ function transformCallExpression(node, scopeManager, namespace) {
1140
1166
  });
1141
1167
  }
1142
1168
  function transformFunctionDeclaration(node, scopeManager) {
1169
+ const boundParamNames = [];
1143
1170
  node.params.forEach((param) => {
1144
1171
  if (param.type === "Identifier") {
1145
1172
  scopeManager.addContextBoundVar(param.name, false);
1173
+ boundParamNames.push(param.name);
1146
1174
  }
1147
1175
  });
1148
1176
  if (node.body && node.body.type === "BlockStatement") {
@@ -1490,14 +1518,43 @@ function transpile(fn) {
1490
1518
  transformIfStatement(node, state, c);
1491
1519
  }
1492
1520
  });
1521
+ transformEqualityChecks(ast);
1493
1522
  const transformedCode = astring.generate(ast);
1494
1523
  const _wraperFunction = new Function("", `return ${transformedCode}`);
1495
1524
  return _wraperFunction(this);
1496
1525
  }
1526
+ function transformEqualityChecks(ast) {
1527
+ walk.simple(ast, {
1528
+ BinaryExpression(node) {
1529
+ if (node.operator === "==" || node.operator === "===") {
1530
+ const leftOperand = node.left;
1531
+ const rightOperand = node.right;
1532
+ Object.assign(node, {
1533
+ type: "CallExpression",
1534
+ callee: {
1535
+ type: "MemberExpression",
1536
+ object: {
1537
+ type: "Identifier",
1538
+ name: "math"
1539
+ },
1540
+ property: {
1541
+ type: "Identifier",
1542
+ name: "__eq"
1543
+ },
1544
+ computed: false
1545
+ },
1546
+ arguments: [leftOperand, rightOperand],
1547
+ _transformed: true
1548
+ });
1549
+ }
1550
+ }
1551
+ });
1552
+ }
1497
1553
 
1498
1554
  var __defProp$6 = Object.defineProperty;
1499
1555
  var __defNormalProp$6 = (obj, key, value) => key in obj ? __defProp$6(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
1500
1556
  var __publicField$6 = (obj, key, value) => __defNormalProp$6(obj, typeof key !== "symbol" ? key + "" : key, value);
1557
+ const MAX_PERIODS = 5e3;
1501
1558
  class PineTS {
1502
1559
  constructor(source, tickerId, timeframe, limit, sDate, eDate) {
1503
1560
  this.source = source;
@@ -1527,7 +1584,7 @@ class PineTS {
1527
1584
  __publicField$6(this, "_ready", false);
1528
1585
  this._readyPromise = new Promise((resolve) => {
1529
1586
  this.loadMarketData(source, tickerId, timeframe, limit, sDate, eDate).then((data) => {
1530
- const marketData = data.reverse();
1587
+ const marketData = data.reverse().slice(0, MAX_PERIODS);
1531
1588
  this._periods = marketData.length;
1532
1589
  this.data = marketData;
1533
1590
  const _open = marketData.map((d) => d.open);
@@ -1785,6 +1842,9 @@ class PineMath {
1785
1842
  return this.context.params[name];
1786
1843
  }
1787
1844
  }
1845
+ __eq(a, b) {
1846
+ return Math.abs(a - b) < 1e-8;
1847
+ }
1788
1848
  abs(source) {
1789
1849
  return Math.abs(source[0]);
1790
1850
  }
@@ -1948,141 +2008,532 @@ class TechnicalAnalysis {
1948
2008
  return this.context.params[name];
1949
2009
  }
1950
2010
  }
1951
- ema(source, _period) {
1952
- const period = Array.isArray(_period) ? _period[0] : _period;
1953
- const result = ema(source.slice(0).reverse(), period);
1954
- const idx = this.context.idx;
1955
- return this.context.precision(result[idx]);
1956
- }
1957
- sma(source, _period, _cacheId) {
2011
+ ema(source, _period, _callId) {
1958
2012
  const period = Array.isArray(_period) ? _period[0] : _period;
1959
- const reversedSource = source.slice(0).reverse();
1960
- if (this.context.useTACache && _cacheId) {
1961
- if (!this.context.cache[_cacheId]) {
1962
- this.context.cache[_cacheId] = {};
1963
- }
1964
- const cacheObj = this.context.cache[_cacheId];
1965
- if (cacheObj) {
1966
- const result2 = sma_cache(reversedSource, period, cacheObj);
1967
- const idx2 = this.context.idx;
1968
- return this.context.precision(result2[idx2]);
2013
+ if (!this.context.taState) this.context.taState = {};
2014
+ const stateKey = _callId || `ema_${period}`;
2015
+ if (!this.context.taState[stateKey]) {
2016
+ this.context.taState[stateKey] = { prevEma: null, initSum: 0, initCount: 0 };
2017
+ }
2018
+ const state = this.context.taState[stateKey];
2019
+ const currentValue = source[0];
2020
+ if (state.initCount < period) {
2021
+ state.initSum += currentValue;
2022
+ state.initCount++;
2023
+ if (state.initCount === period) {
2024
+ state.prevEma = state.initSum / period;
2025
+ return this.context.precision(state.prevEma);
1969
2026
  }
2027
+ return NaN;
1970
2028
  }
1971
- const result = sma(reversedSource, period);
1972
- const idx = this.context.idx;
1973
- return this.context.precision(result[idx]);
2029
+ const alpha = 2 / (period + 1);
2030
+ const ema2 = currentValue * alpha + state.prevEma * (1 - alpha);
2031
+ state.prevEma = ema2;
2032
+ return this.context.precision(ema2);
1974
2033
  }
1975
- vwma(source, _period) {
2034
+ sma(source, _period, _callId) {
1976
2035
  const period = Array.isArray(_period) ? _period[0] : _period;
1977
- const volume = this.context.data.volume;
1978
- const result = vwma(source.slice(0).reverse(), volume.slice(0).reverse(), period);
1979
- const idx = this.context.idx;
1980
- return this.context.precision(result[idx]);
2036
+ if (!this.context.taState) this.context.taState = {};
2037
+ const stateKey = _callId || `sma_${period}`;
2038
+ if (!this.context.taState[stateKey]) {
2039
+ this.context.taState[stateKey] = { window: [], sum: 0 };
2040
+ }
2041
+ const state = this.context.taState[stateKey];
2042
+ const currentValue = source[0] || 0;
2043
+ state.window.unshift(currentValue);
2044
+ state.sum += currentValue;
2045
+ if (state.window.length < period) {
2046
+ return NaN;
2047
+ }
2048
+ if (state.window.length > period) {
2049
+ const oldValue = state.window.pop();
2050
+ state.sum -= oldValue;
2051
+ }
2052
+ const sma2 = state.sum / period;
2053
+ return this.context.precision(sma2);
2054
+ }
2055
+ vwma(source, _period, _callId) {
2056
+ const period = Array.isArray(_period) ? _period[0] : _period;
2057
+ if (!this.context.taState) this.context.taState = {};
2058
+ const stateKey = _callId || `vwma_${period}`;
2059
+ if (!this.context.taState[stateKey]) {
2060
+ this.context.taState[stateKey] = { window: [], volumeWindow: [] };
2061
+ }
2062
+ const state = this.context.taState[stateKey];
2063
+ const currentValue = source[0];
2064
+ const currentVolume = this.context.data.volume[0];
2065
+ state.window.unshift(currentValue);
2066
+ state.volumeWindow.unshift(currentVolume);
2067
+ if (state.window.length < period) {
2068
+ return NaN;
2069
+ }
2070
+ if (state.window.length > period) {
2071
+ state.window.pop();
2072
+ state.volumeWindow.pop();
2073
+ }
2074
+ let sumVolPrice = 0;
2075
+ let sumVol = 0;
2076
+ for (let i = 0; i < period; i++) {
2077
+ sumVolPrice += state.window[i] * state.volumeWindow[i];
2078
+ sumVol += state.volumeWindow[i];
2079
+ }
2080
+ const vwma2 = sumVolPrice / sumVol;
2081
+ return this.context.precision(vwma2);
1981
2082
  }
1982
- wma(source, _period) {
2083
+ wma(source, _period, _callId) {
1983
2084
  const period = Array.isArray(_period) ? _period[0] : _period;
1984
- const result = wma(source.slice(0).reverse(), period);
1985
- const idx = this.context.idx;
1986
- return this.context.precision(result[idx]);
2085
+ if (!this.context.taState) this.context.taState = {};
2086
+ const stateKey = _callId || `wma_${period}`;
2087
+ if (!this.context.taState[stateKey]) {
2088
+ this.context.taState[stateKey] = { window: [] };
2089
+ }
2090
+ const state = this.context.taState[stateKey];
2091
+ const currentValue = source[0];
2092
+ state.window.unshift(currentValue);
2093
+ if (state.window.length < period) {
2094
+ return NaN;
2095
+ }
2096
+ if (state.window.length > period) {
2097
+ state.window.pop();
2098
+ }
2099
+ let numerator = 0;
2100
+ let denominator = 0;
2101
+ for (let i = 0; i < period; i++) {
2102
+ const weight = period - i;
2103
+ numerator += state.window[i] * weight;
2104
+ denominator += weight;
2105
+ }
2106
+ const wma2 = numerator / denominator;
2107
+ return this.context.precision(wma2);
1987
2108
  }
1988
- hma(source, _period) {
2109
+ hma(source, _period, _callId) {
1989
2110
  const period = Array.isArray(_period) ? _period[0] : _period;
1990
- const result = hma(source.slice(0).reverse(), period);
1991
- const idx = this.context.idx;
1992
- return this.context.precision(result[idx]);
2111
+ const halfPeriod = Math.floor(period / 2);
2112
+ const sqrtPeriod = Math.floor(Math.sqrt(period));
2113
+ const wma1 = this.wma(source, halfPeriod, _callId ? `${_callId}_wma1` : void 0);
2114
+ const wma2 = this.wma(source, period, _callId ? `${_callId}_wma2` : void 0);
2115
+ if (isNaN(wma1) || isNaN(wma2)) {
2116
+ return NaN;
2117
+ }
2118
+ if (!this.context.taState) this.context.taState = {};
2119
+ const stateKey = _callId || `hma_raw_${period}`;
2120
+ if (!this.context.taState[stateKey]) {
2121
+ this.context.taState[stateKey] = [];
2122
+ }
2123
+ const rawHma = 2 * wma1 - wma2;
2124
+ this.context.taState[stateKey].unshift(rawHma);
2125
+ const hmaStateKey = _callId ? `${_callId}_hma_final` : `hma_final_${period}`;
2126
+ if (!this.context.taState[hmaStateKey]) {
2127
+ this.context.taState[hmaStateKey] = { window: [] };
2128
+ }
2129
+ const state = this.context.taState[hmaStateKey];
2130
+ state.window.unshift(rawHma);
2131
+ if (state.window.length < sqrtPeriod) {
2132
+ return NaN;
2133
+ }
2134
+ if (state.window.length > sqrtPeriod) {
2135
+ state.window.pop();
2136
+ }
2137
+ let numerator = 0;
2138
+ let denominator = 0;
2139
+ for (let i = 0; i < sqrtPeriod; i++) {
2140
+ const weight = sqrtPeriod - i;
2141
+ numerator += state.window[i] * weight;
2142
+ denominator += weight;
2143
+ }
2144
+ const hma2 = numerator / denominator;
2145
+ return this.context.precision(hma2);
1993
2146
  }
1994
- rma(source, _period) {
2147
+ rma(source, _period, _callId) {
1995
2148
  const period = Array.isArray(_period) ? _period[0] : _period;
1996
- const result = rma(source.slice(0).reverse(), period);
1997
- const idx = this.context.idx;
1998
- return this.context.precision(result[idx]);
2149
+ if (!this.context.taState) this.context.taState = {};
2150
+ const stateKey = _callId || `rma_${period}`;
2151
+ if (!this.context.taState[stateKey]) {
2152
+ this.context.taState[stateKey] = { prevRma: null, initSum: 0, initCount: 0 };
2153
+ }
2154
+ const state = this.context.taState[stateKey];
2155
+ const currentValue = source[0] || 0;
2156
+ if (state.initCount < period) {
2157
+ state.initSum += currentValue;
2158
+ state.initCount++;
2159
+ if (state.initCount === period) {
2160
+ state.prevRma = state.initSum / period;
2161
+ return this.context.precision(state.prevRma);
2162
+ }
2163
+ return NaN;
2164
+ }
2165
+ const alpha = 1 / period;
2166
+ const rma2 = currentValue * alpha + state.prevRma * (1 - alpha);
2167
+ state.prevRma = rma2;
2168
+ return this.context.precision(rma2);
1999
2169
  }
2000
- change(source, _length = 1) {
2170
+ change(source, _length = 1, _callId) {
2001
2171
  const length = Array.isArray(_length) ? _length[0] : _length;
2002
- const result = change(source.slice(0).reverse(), length);
2003
- const idx = this.context.idx;
2004
- return this.context.precision(result[idx]);
2172
+ if (!this.context.taState) this.context.taState = {};
2173
+ const stateKey = _callId || `change_${length}`;
2174
+ if (!this.context.taState[stateKey]) {
2175
+ this.context.taState[stateKey] = { window: [] };
2176
+ }
2177
+ const state = this.context.taState[stateKey];
2178
+ const currentValue = source[0];
2179
+ state.window.unshift(currentValue);
2180
+ if (state.window.length <= length) {
2181
+ return NaN;
2182
+ }
2183
+ if (state.window.length > length + 1) {
2184
+ state.window.pop();
2185
+ }
2186
+ const change2 = currentValue - state.window[length];
2187
+ return this.context.precision(change2);
2005
2188
  }
2006
- rsi(source, _period) {
2189
+ rsi(source, _period, _callId) {
2007
2190
  const period = Array.isArray(_period) ? _period[0] : _period;
2008
- const result = rsi(source.slice(0).reverse(), period);
2009
- const idx = this.context.idx;
2010
- return this.context.precision(result[idx]);
2191
+ if (!this.context.taState) this.context.taState = {};
2192
+ const stateKey = _callId || `rsi_${period}`;
2193
+ if (!this.context.taState[stateKey]) {
2194
+ this.context.taState[stateKey] = {
2195
+ prevValue: null,
2196
+ avgGain: 0,
2197
+ avgLoss: 0,
2198
+ initGains: [],
2199
+ initLosses: []
2200
+ };
2201
+ }
2202
+ const state = this.context.taState[stateKey];
2203
+ const currentValue = source[0];
2204
+ if (state.prevValue !== null) {
2205
+ const diff = currentValue - state.prevValue;
2206
+ const gain = diff > 0 ? diff : 0;
2207
+ const loss = diff < 0 ? -diff : 0;
2208
+ if (state.initGains.length < period) {
2209
+ state.initGains.push(gain);
2210
+ state.initLosses.push(loss);
2211
+ if (state.initGains.length === period) {
2212
+ state.avgGain = state.initGains.reduce((a, b) => a + b, 0) / period;
2213
+ state.avgLoss = state.initLosses.reduce((a, b) => a + b, 0) / period;
2214
+ state.prevValue = currentValue;
2215
+ const rsi3 = state.avgLoss === 0 ? 100 : 100 - 100 / (1 + state.avgGain / state.avgLoss);
2216
+ return this.context.precision(rsi3);
2217
+ }
2218
+ state.prevValue = currentValue;
2219
+ return NaN;
2220
+ }
2221
+ state.avgGain = (state.avgGain * (period - 1) + gain) / period;
2222
+ state.avgLoss = (state.avgLoss * (period - 1) + loss) / period;
2223
+ const rsi2 = state.avgLoss === 0 ? 100 : 100 - 100 / (1 + state.avgGain / state.avgLoss);
2224
+ state.prevValue = currentValue;
2225
+ return this.context.precision(rsi2);
2226
+ }
2227
+ state.prevValue = currentValue;
2228
+ return NaN;
2011
2229
  }
2012
- atr(_period) {
2230
+ atr(_period, _callId) {
2013
2231
  const period = Array.isArray(_period) ? _period[0] : _period;
2014
- const high = this.context.data.high.slice().reverse();
2015
- const low = this.context.data.low.slice().reverse();
2016
- const close = this.context.data.close.slice().reverse();
2017
- const result = atr(high, low, close, period);
2018
- const idx = this.context.idx;
2019
- return this.context.precision(result[idx]);
2232
+ if (!this.context.taState) this.context.taState = {};
2233
+ const stateKey = _callId || `atr_${period}`;
2234
+ if (!this.context.taState[stateKey]) {
2235
+ this.context.taState[stateKey] = {
2236
+ prevAtr: null,
2237
+ initSum: 0,
2238
+ initCount: 0,
2239
+ prevClose: null
2240
+ };
2241
+ }
2242
+ const state = this.context.taState[stateKey];
2243
+ const high = this.context.data.high[0];
2244
+ const low = this.context.data.low[0];
2245
+ const close = this.context.data.close[0];
2246
+ let tr;
2247
+ if (state.prevClose !== null) {
2248
+ const hl = high - low;
2249
+ const hc = Math.abs(high - state.prevClose);
2250
+ const lc = Math.abs(low - state.prevClose);
2251
+ tr = Math.max(hl, hc, lc);
2252
+ } else {
2253
+ tr = high - low;
2254
+ }
2255
+ state.prevClose = close;
2256
+ if (state.initCount < period) {
2257
+ state.initSum += tr;
2258
+ state.initCount++;
2259
+ if (state.initCount === period) {
2260
+ state.prevAtr = state.initSum / period;
2261
+ return this.context.precision(state.prevAtr);
2262
+ }
2263
+ return NaN;
2264
+ }
2265
+ const atr2 = (state.prevAtr * (period - 1) + tr) / period;
2266
+ state.prevAtr = atr2;
2267
+ return this.context.precision(atr2);
2020
2268
  }
2021
- mom(source, _length) {
2269
+ mom(source, _length, _callId) {
2022
2270
  const length = Array.isArray(_length) ? _length[0] : _length;
2023
- const result = mom(source.slice(0).reverse(), length);
2024
- const idx = this.context.idx;
2025
- return this.context.precision(result[idx]);
2271
+ return this.change(source, length);
2026
2272
  }
2027
- roc(source, _length) {
2273
+ roc(source, _length, _callId) {
2028
2274
  const length = Array.isArray(_length) ? _length[0] : _length;
2029
- const result = roc(source.slice(0).reverse(), length);
2030
- const idx = this.context.idx;
2031
- return this.context.precision(result[idx]);
2275
+ if (!this.context.taState) this.context.taState = {};
2276
+ const stateKey = _callId || `roc_${length}`;
2277
+ if (!this.context.taState[stateKey]) {
2278
+ this.context.taState[stateKey] = { window: [] };
2279
+ }
2280
+ const state = this.context.taState[stateKey];
2281
+ const currentValue = source[0];
2282
+ state.window.unshift(currentValue);
2283
+ if (state.window.length <= length) {
2284
+ return NaN;
2285
+ }
2286
+ if (state.window.length > length + 1) {
2287
+ state.window.pop();
2288
+ }
2289
+ const prevValue = state.window[length];
2290
+ const roc2 = (currentValue - prevValue) / prevValue * 100;
2291
+ return this.context.precision(roc2);
2032
2292
  }
2033
- dev(source, _length) {
2293
+ dev(source, _length, _callId) {
2034
2294
  const length = Array.isArray(_length) ? _length[0] : _length;
2035
- const result = dev(source.slice(0).reverse(), length);
2036
- const idx = this.context.idx;
2037
- return this.context.precision(result[idx]);
2295
+ if (!this.context.taState) this.context.taState = {};
2296
+ const stateKey = _callId || `dev_${length}`;
2297
+ if (!this.context.taState[stateKey]) {
2298
+ this.context.taState[stateKey] = { window: [], sum: 0 };
2299
+ }
2300
+ const state = this.context.taState[stateKey];
2301
+ const currentValue = source[0] || 0;
2302
+ state.window.unshift(currentValue);
2303
+ state.sum += currentValue;
2304
+ if (state.window.length < length) {
2305
+ return NaN;
2306
+ }
2307
+ if (state.window.length > length) {
2308
+ const oldValue = state.window.pop();
2309
+ state.sum -= oldValue;
2310
+ }
2311
+ const mean = state.sum / length;
2312
+ let sumDeviation = 0;
2313
+ for (let i = 0; i < length; i++) {
2314
+ sumDeviation += Math.abs(state.window[i] - mean);
2315
+ }
2316
+ const dev2 = sumDeviation / length;
2317
+ return this.context.precision(dev2);
2038
2318
  }
2039
- variance(source, _length) {
2319
+ variance(source, _length, _callId) {
2040
2320
  const length = Array.isArray(_length) ? _length[0] : _length;
2041
- const result = variance(source.slice(0).reverse(), length);
2042
- const idx = this.context.idx;
2043
- return this.context.precision(result[idx]);
2321
+ if (!this.context.taState) this.context.taState = {};
2322
+ const stateKey = _callId || `variance_${length}`;
2323
+ if (!this.context.taState[stateKey]) {
2324
+ this.context.taState[stateKey] = { window: [] };
2325
+ }
2326
+ const state = this.context.taState[stateKey];
2327
+ const currentValue = source[0];
2328
+ state.window.unshift(currentValue);
2329
+ if (state.window.length < length) {
2330
+ return NaN;
2331
+ }
2332
+ if (state.window.length > length) {
2333
+ state.window.pop();
2334
+ }
2335
+ let sum = 0;
2336
+ let sumSquares = 0;
2337
+ for (let i = 0; i < length; i++) {
2338
+ sum += state.window[i];
2339
+ sumSquares += state.window[i] * state.window[i];
2340
+ }
2341
+ const mean = sum / length;
2342
+ const variance2 = sumSquares / length - mean * mean;
2343
+ return this.context.precision(variance2);
2044
2344
  }
2045
- highest(source, _length) {
2345
+ highest(source, _length, _callId) {
2046
2346
  const length = Array.isArray(_length) ? _length[0] : _length;
2047
- const result = highest(source.slice(0).reverse(), length);
2048
- const idx = this.context.idx;
2049
- return this.context.precision(result[idx]);
2347
+ if (!this.context.taState) this.context.taState = {};
2348
+ const stateKey = _callId || `highest_${length}`;
2349
+ if (!this.context.taState[stateKey]) {
2350
+ this.context.taState[stateKey] = { window: [] };
2351
+ }
2352
+ const state = this.context.taState[stateKey];
2353
+ const currentValue = source[0];
2354
+ state.window.unshift(currentValue);
2355
+ if (state.window.length < length) {
2356
+ return NaN;
2357
+ }
2358
+ if (state.window.length > length) {
2359
+ state.window.pop();
2360
+ }
2361
+ const max = Math.max(...state.window.filter((v) => !isNaN(v)));
2362
+ return this.context.precision(max);
2050
2363
  }
2051
- lowest(source, _length) {
2364
+ lowest(source, _length, _callId) {
2052
2365
  const length = Array.isArray(_length) ? _length[0] : _length;
2053
- const result = lowest(source.slice(0).reverse(), length);
2054
- const idx = this.context.idx;
2055
- return this.context.precision(result[idx]);
2366
+ if (!this.context.taState) this.context.taState = {};
2367
+ const stateKey = _callId || `lowest_${length}`;
2368
+ if (!this.context.taState[stateKey]) {
2369
+ this.context.taState[stateKey] = { window: [] };
2370
+ }
2371
+ const state = this.context.taState[stateKey];
2372
+ const currentValue = source[0];
2373
+ state.window.unshift(currentValue);
2374
+ if (state.window.length < length) {
2375
+ return NaN;
2376
+ }
2377
+ if (state.window.length > length) {
2378
+ state.window.pop();
2379
+ }
2380
+ const validValues = state.window.filter((v) => !isNaN(v) && v !== void 0);
2381
+ const min = validValues.length > 0 ? Math.min(...validValues) : NaN;
2382
+ return this.context.precision(min);
2056
2383
  }
2057
- median(source, _length) {
2384
+ median(source, _length, _callId) {
2058
2385
  const length = Array.isArray(_length) ? _length[0] : _length;
2059
- const result = median(source.slice(0).reverse(), length);
2060
- const idx = this.context.idx;
2061
- return this.context.precision(result[idx]);
2386
+ if (!this.context.taState) this.context.taState = {};
2387
+ const stateKey = _callId || `median_${length}`;
2388
+ if (!this.context.taState[stateKey]) {
2389
+ this.context.taState[stateKey] = { window: [] };
2390
+ }
2391
+ const state = this.context.taState[stateKey];
2392
+ const currentValue = source[0];
2393
+ state.window.unshift(currentValue);
2394
+ if (state.window.length < length) {
2395
+ return NaN;
2396
+ }
2397
+ if (state.window.length > length) {
2398
+ state.window.pop();
2399
+ }
2400
+ const sorted = state.window.slice().sort((a, b) => a - b);
2401
+ const mid = Math.floor(length / 2);
2402
+ const median2 = length % 2 === 0 ? (sorted[mid - 1] + sorted[mid]) / 2 : sorted[mid];
2403
+ return this.context.precision(median2);
2062
2404
  }
2063
- stdev(source, _length, _bias = true) {
2405
+ stdev(source, _length, _bias = true, _callId) {
2064
2406
  const length = Array.isArray(_length) ? _length[0] : _length;
2065
2407
  const bias = Array.isArray(_bias) ? _bias[0] : _bias;
2066
- const result = stdev(source.slice(0).reverse(), length, bias);
2067
- const idx = this.context.idx;
2068
- return this.context.precision(result[idx]);
2069
- }
2070
- linreg(source, _length, _offset) {
2408
+ if (!this.context.taState) this.context.taState = {};
2409
+ const stateKey = _callId || `stdev_${length}_${bias}`;
2410
+ if (!this.context.taState[stateKey]) {
2411
+ this.context.taState[stateKey] = { window: [], sum: 0 };
2412
+ }
2413
+ const state = this.context.taState[stateKey];
2414
+ const currentValue = source[0];
2415
+ state.window.unshift(currentValue);
2416
+ state.sum += currentValue;
2417
+ if (state.window.length < length) {
2418
+ return NaN;
2419
+ }
2420
+ if (state.window.length > length) {
2421
+ const oldValue = state.window.pop();
2422
+ state.sum -= oldValue;
2423
+ }
2424
+ const mean = state.sum / length;
2425
+ let sumSquaredDiff = 0;
2426
+ for (let i = 0; i < length; i++) {
2427
+ sumSquaredDiff += Math.pow(state.window[i] - mean, 2);
2428
+ }
2429
+ const divisor = bias ? length : length - 1;
2430
+ const stdev2 = Math.sqrt(sumSquaredDiff / divisor);
2431
+ return this.context.precision(stdev2);
2432
+ }
2433
+ linreg(source, _length, _offset, _callId) {
2071
2434
  const length = Array.isArray(_length) ? _length[0] : _length;
2072
2435
  const offset = Array.isArray(_offset) ? _offset[0] : _offset;
2073
- const result = linreg(source.slice(0).reverse(), length, offset);
2074
- const idx = this.context.idx;
2075
- return this.context.precision(result[idx]);
2436
+ if (!this.context.taState) this.context.taState = {};
2437
+ const stateKey = _callId || `linreg_${length}_${offset}`;
2438
+ if (!this.context.taState[stateKey]) {
2439
+ this.context.taState[stateKey] = { window: [] };
2440
+ }
2441
+ const state = this.context.taState[stateKey];
2442
+ const currentValue = source[0];
2443
+ state.window.unshift(currentValue);
2444
+ if (state.window.length < length) {
2445
+ return NaN;
2446
+ }
2447
+ if (state.window.length > length) {
2448
+ state.window.pop();
2449
+ }
2450
+ let sumX = 0;
2451
+ let sumY = 0;
2452
+ let sumXY = 0;
2453
+ let sumXX = 0;
2454
+ const n = length;
2455
+ for (let j = 0; j < length; j++) {
2456
+ const x = length - 1 - j;
2457
+ const y = state.window[j];
2458
+ sumX += x;
2459
+ sumY += y;
2460
+ sumXY += x * y;
2461
+ sumXX += x * x;
2462
+ }
2463
+ const denominator = n * sumXX - sumX * sumX;
2464
+ if (denominator === 0) {
2465
+ return NaN;
2466
+ }
2467
+ const slope = (n * sumXY - sumX * sumY) / denominator;
2468
+ const intercept = (sumY - slope * sumX) / n;
2469
+ const linRegValue = intercept + slope * (length - 1 - offset);
2470
+ return this.context.precision(linRegValue);
2076
2471
  }
2077
- supertrend(_factor, _atrPeriod) {
2472
+ supertrend(_factor, _atrPeriod, _callId) {
2078
2473
  const factor = Array.isArray(_factor) ? _factor[0] : _factor;
2079
2474
  const atrPeriod = Array.isArray(_atrPeriod) ? _atrPeriod[0] : _atrPeriod;
2080
- const high = this.context.data.high.slice().reverse();
2081
- const low = this.context.data.low.slice().reverse();
2082
- const close = this.context.data.close.slice().reverse();
2083
- const [supertrend, direction] = calculateSupertrend(high, low, close, factor, atrPeriod);
2084
- const idx = this.context.idx;
2085
- return [[this.context.precision(supertrend[idx]), direction[idx]]];
2475
+ if (!this.context.taState) this.context.taState = {};
2476
+ const stateKey = `supertrend_${factor}_${atrPeriod}`;
2477
+ if (!this.context.taState[stateKey]) {
2478
+ this.context.taState[stateKey] = {
2479
+ prevUpperBand: null,
2480
+ prevLowerBand: null,
2481
+ prevSupertrend: null,
2482
+ prevDirection: null
2483
+ };
2484
+ }
2485
+ const state = this.context.taState[stateKey];
2486
+ const high = this.context.data.high[0];
2487
+ const low = this.context.data.low[0];
2488
+ const close = this.context.data.close[0];
2489
+ const atrValue = this.atr(atrPeriod, _callId ? `${_callId}_atr` : void 0);
2490
+ if (isNaN(atrValue)) {
2491
+ return [[NaN, 0]];
2492
+ }
2493
+ const hl2 = (high + low) / 2;
2494
+ let upperBand = hl2 + factor * atrValue;
2495
+ let lowerBand = hl2 - factor * atrValue;
2496
+ if (state.prevUpperBand !== null) {
2497
+ if (upperBand < state.prevUpperBand || this.context.data.close[1] > state.prevUpperBand) {
2498
+ upperBand = upperBand;
2499
+ } else {
2500
+ upperBand = state.prevUpperBand;
2501
+ }
2502
+ if (lowerBand > state.prevLowerBand || this.context.data.close[1] < state.prevLowerBand) {
2503
+ lowerBand = lowerBand;
2504
+ } else {
2505
+ lowerBand = state.prevLowerBand;
2506
+ }
2507
+ }
2508
+ let direction;
2509
+ let supertrend;
2510
+ if (state.prevSupertrend === null) {
2511
+ direction = close <= upperBand ? -1 : 1;
2512
+ supertrend = direction === -1 ? upperBand : lowerBand;
2513
+ } else {
2514
+ if (state.prevSupertrend === state.prevUpperBand) {
2515
+ if (close > upperBand) {
2516
+ direction = 1;
2517
+ supertrend = lowerBand;
2518
+ } else {
2519
+ direction = -1;
2520
+ supertrend = upperBand;
2521
+ }
2522
+ } else {
2523
+ if (close < lowerBand) {
2524
+ direction = -1;
2525
+ supertrend = upperBand;
2526
+ } else {
2527
+ direction = 1;
2528
+ supertrend = lowerBand;
2529
+ }
2530
+ }
2531
+ }
2532
+ state.prevUpperBand = upperBand;
2533
+ state.prevLowerBand = lowerBand;
2534
+ state.prevSupertrend = supertrend;
2535
+ state.prevDirection = direction;
2536
+ return [[this.context.precision(supertrend), direction]];
2086
2537
  }
2087
2538
  crossover(source1, source2) {
2088
2539
  const current1 = Array.isArray(source1) ? source1[0] : source1;
@@ -2123,344 +2574,6 @@ class TechnicalAnalysis {
2123
2574
  return this.context.precision(result[idx]);
2124
2575
  }
2125
2576
  }
2126
- function atr(high, low, close, period) {
2127
- const tr = new Array(high.length);
2128
- tr[0] = high[0] - low[0];
2129
- for (let i = 1; i < high.length; i++) {
2130
- const hl = high[i] - low[i];
2131
- const hc = Math.abs(high[i] - close[i - 1]);
2132
- const lc = Math.abs(low[i] - close[i - 1]);
2133
- tr[i] = Math.max(hl, hc, lc);
2134
- }
2135
- const atr2 = new Array(high.length).fill(NaN);
2136
- let sum = 0;
2137
- for (let i = 0; i < period; i++) {
2138
- sum += tr[i];
2139
- }
2140
- atr2[period - 1] = sum / period;
2141
- for (let i = period; i < tr.length; i++) {
2142
- atr2[i] = (atr2[i - 1] * (period - 1) + tr[i]) / period;
2143
- }
2144
- return atr2;
2145
- }
2146
- function ema(source, period) {
2147
- const result = new Array(source.length).fill(NaN);
2148
- const alpha = 2 / (period + 1);
2149
- let sum = 0;
2150
- for (let i = 0; i < period; i++) {
2151
- sum += source[i] || 0;
2152
- }
2153
- result[period - 1] = sum / period;
2154
- for (let i = period; i < source.length; i++) {
2155
- result[i] = source[i] * alpha + result[i - 1] * (1 - alpha);
2156
- }
2157
- return result;
2158
- }
2159
- function rsi(source, period) {
2160
- const result = new Array(source.length).fill(NaN);
2161
- const gains = new Array(source.length).fill(0);
2162
- const losses = new Array(source.length).fill(0);
2163
- for (let i = 1; i < source.length; i++) {
2164
- const diff = source[i] - source[i - 1];
2165
- gains[i] = diff > 0 ? diff : 0;
2166
- losses[i] = diff < 0 ? -diff : 0;
2167
- }
2168
- let avgGain = 0;
2169
- let avgLoss = 0;
2170
- for (let i = 1; i <= period; i++) {
2171
- avgGain += gains[i];
2172
- avgLoss += losses[i];
2173
- }
2174
- avgGain /= period;
2175
- avgLoss /= period;
2176
- result[period] = avgLoss === 0 ? 100 : 100 - 100 / (1 + avgGain / avgLoss);
2177
- for (let i = period + 1; i < source.length; i++) {
2178
- avgGain = (avgGain * (period - 1) + gains[i]) / period;
2179
- avgLoss = (avgLoss * (period - 1) + losses[i]) / period;
2180
- result[i] = avgLoss === 0 ? 100 : 100 - 100 / (1 + avgGain / avgLoss);
2181
- }
2182
- return result;
2183
- }
2184
- function rma(source, period) {
2185
- const result = new Array(source.length).fill(NaN);
2186
- const alpha = 1 / period;
2187
- let sum = 0;
2188
- for (let i = 0; i < period; i++) {
2189
- sum += source[i] || 0;
2190
- }
2191
- result[period - 1] = sum / period;
2192
- for (let i = period; i < source.length; i++) {
2193
- const currentValue = source[i] || 0;
2194
- result[i] = currentValue * alpha + result[i - 1] * (1 - alpha);
2195
- }
2196
- return result;
2197
- }
2198
- function sma_cache(source, period, cacheObj) {
2199
- const result = cacheObj.previousResult || new Array(source.length).fill(NaN);
2200
- const lastProcessedIndex = cacheObj.lastProcessedIndex || -1;
2201
- let previousSum = cacheObj.previousSum || 0;
2202
- if (lastProcessedIndex === -1 || source.length !== lastProcessedIndex + 1) {
2203
- previousSum = 0;
2204
- for (let i = 0; i < period; i++) {
2205
- previousSum += source[i] || 0;
2206
- }
2207
- result[period - 1] = previousSum / period;
2208
- for (let i = 0; i < period - 1; i++) {
2209
- result[i] = NaN;
2210
- }
2211
- for (let i = period; i < source.length; i++) {
2212
- previousSum = previousSum - (source[i - period] || 0) + (source[i] || 0);
2213
- result[i] = previousSum / period;
2214
- }
2215
- } else if (source.length === lastProcessedIndex + 2) {
2216
- const newIndex = source.length - 1;
2217
- previousSum = previousSum - (source[newIndex - period] || 0) + (source[newIndex] || 0);
2218
- result[newIndex] = previousSum / period;
2219
- } else {
2220
- return sma(source, period);
2221
- }
2222
- cacheObj.previousSum = previousSum;
2223
- cacheObj.lastProcessedIndex = source.length - 1;
2224
- cacheObj.previousResult = result;
2225
- return result;
2226
- }
2227
- function sma(source, period) {
2228
- const result = new Array(source.length).fill(NaN);
2229
- for (let i = period - 1; i < source.length; i++) {
2230
- let sum = 0;
2231
- for (let j = 0; j < period; j++) {
2232
- sum += source[i - j] || 0;
2233
- }
2234
- result[i] = sum / period;
2235
- }
2236
- return result;
2237
- }
2238
- function vwma(source, volume, period) {
2239
- const result = new Array(source.length).fill(NaN);
2240
- for (let i = period - 1; i < source.length; i++) {
2241
- let sumVol = 0;
2242
- let sumVolPrice = 0;
2243
- for (let j = 0; j < period; j++) {
2244
- sumVol += volume[i - j];
2245
- sumVolPrice += source[i - j] * volume[i - j];
2246
- }
2247
- result[i] = sumVolPrice / sumVol;
2248
- }
2249
- return result;
2250
- }
2251
- function hma(source, period) {
2252
- const halfPeriod = Math.floor(period / 2);
2253
- const wma1 = wma(source, halfPeriod);
2254
- const wma2 = wma(source, period);
2255
- const rawHma = wma1.map((value, index) => 2 * value - wma2[index]);
2256
- const sqrtPeriod = Math.floor(Math.sqrt(period));
2257
- const result = wma(rawHma, sqrtPeriod);
2258
- return result;
2259
- }
2260
- function wma(source, period) {
2261
- const result = new Array(source.length);
2262
- for (let i = period - 1; i < source.length; i++) {
2263
- let numerator = 0;
2264
- let denominator = 0;
2265
- for (let j = 0; j < period; j++) {
2266
- numerator += source[i - j] * (period - j);
2267
- denominator += period - j;
2268
- }
2269
- result[i] = numerator / denominator;
2270
- }
2271
- for (let i = 0; i < period - 1; i++) {
2272
- result[i] = NaN;
2273
- }
2274
- return result;
2275
- }
2276
- function change(source, length = 1) {
2277
- const result = new Array(source.length).fill(NaN);
2278
- for (let i = length; i < source.length; i++) {
2279
- result[i] = source[i] - source[i - length];
2280
- }
2281
- return result;
2282
- }
2283
- function mom(source, length) {
2284
- const result = new Array(source.length).fill(NaN);
2285
- for (let i = length; i < source.length; i++) {
2286
- result[i] = source[i] - source[i - length];
2287
- }
2288
- return result;
2289
- }
2290
- function roc(source, length) {
2291
- const result = new Array(source.length).fill(NaN);
2292
- for (let i = length; i < source.length; i++) {
2293
- result[i] = (source[i] - source[i - length]) / source[i - length] * 100;
2294
- }
2295
- return result;
2296
- }
2297
- function dev(source, length) {
2298
- const result = new Array(source.length).fill(NaN);
2299
- const smaValues = sma(source, length);
2300
- for (let i = length - 1; i < source.length; i++) {
2301
- let sumDeviation = 0;
2302
- for (let j = 0; j < length; j++) {
2303
- sumDeviation += Math.abs(source[i - j] - smaValues[i]);
2304
- }
2305
- result[i] = sumDeviation / length;
2306
- }
2307
- return result;
2308
- }
2309
- function variance(source, length) {
2310
- const result = new Array(source.length).fill(NaN);
2311
- for (let i = length - 1; i < source.length; i++) {
2312
- let sum = 0;
2313
- let sumSquares = 0;
2314
- for (let j = 0; j < length; j++) {
2315
- sum += source[i - j];
2316
- sumSquares += source[i - j] * source[i - j];
2317
- }
2318
- const mean = sum / length;
2319
- result[i] = sumSquares / length - mean * mean;
2320
- }
2321
- return result;
2322
- }
2323
- function highest(source, length) {
2324
- const result = new Array(source.length).fill(NaN);
2325
- for (let i = length - 1; i < source.length; i++) {
2326
- let max = -Infinity;
2327
- for (let j = 0; j < length; j++) {
2328
- const value = source[i - j];
2329
- if (isNaN(value)) {
2330
- max = max === -Infinity ? NaN : max;
2331
- } else {
2332
- max = Math.max(max, value);
2333
- }
2334
- }
2335
- result[i] = max;
2336
- }
2337
- return result;
2338
- }
2339
- function lowest(source, length) {
2340
- const result = new Array(source.length).fill(NaN);
2341
- for (let i = length - 1; i < source.length; i++) {
2342
- let min = Infinity;
2343
- for (let j = 0; j < length; j++) {
2344
- const value = source[i - j];
2345
- if (isNaN(value) || value === void 0) {
2346
- min = min === Infinity ? NaN : min;
2347
- } else {
2348
- min = Math.min(min, value);
2349
- }
2350
- }
2351
- result[i] = min;
2352
- }
2353
- return result;
2354
- }
2355
- function median(source, length) {
2356
- const result = new Array(source.length).fill(NaN);
2357
- for (let i = length - 1; i < source.length; i++) {
2358
- const window = source.slice(i - length + 1, i + 1);
2359
- const sorted = window.slice().sort((a, b) => a - b);
2360
- const mid = Math.floor(length / 2);
2361
- result[i] = length % 2 === 0 ? (sorted[mid - 1] + sorted[mid]) / 2 : sorted[mid];
2362
- }
2363
- return result;
2364
- }
2365
- function stdev(source, length, biased = true) {
2366
- const result = new Array(source.length).fill(NaN);
2367
- const smaValues = sma(source, length);
2368
- for (let i = length - 1; i < source.length; i++) {
2369
- let sum = 0;
2370
- for (let j = 0; j < length; j++) {
2371
- sum += Math.pow(source[i - j] - smaValues[i], 2);
2372
- }
2373
- const divisor = biased ? length : length - 1;
2374
- result[i] = Math.sqrt(sum / divisor);
2375
- }
2376
- return result;
2377
- }
2378
- function linreg(source, length, offset) {
2379
- const size = source.length;
2380
- const output = new Array(size).fill(NaN);
2381
- for (let i = length - 1; i < size; i++) {
2382
- let sumX = 0;
2383
- let sumY = 0;
2384
- let sumXY = 0;
2385
- let sumXX = 0;
2386
- const n = length;
2387
- for (let j = 0; j < length; j++) {
2388
- const x = j;
2389
- const y = source[i - length + 1 + j];
2390
- sumX += x;
2391
- sumY += y;
2392
- sumXY += x * y;
2393
- sumXX += x * x;
2394
- }
2395
- const denominator = n * sumXX - sumX * sumX;
2396
- if (denominator === 0) {
2397
- output[i] = NaN;
2398
- continue;
2399
- }
2400
- const slope = (n * sumXY - sumX * sumY) / denominator;
2401
- const intercept = (sumY - slope * sumX) / n;
2402
- const linRegValue = intercept + slope * (length - 1 - offset);
2403
- output[i] = linRegValue;
2404
- }
2405
- return output;
2406
- }
2407
- function calculateSupertrend(high, low, close, factor, atrPeriod) {
2408
- const length = high.length;
2409
- const supertrend = new Array(length).fill(NaN);
2410
- const direction = new Array(length).fill(0);
2411
- const atrValues = atr(high, low, close, atrPeriod);
2412
- const upperBand = new Array(length).fill(NaN);
2413
- const lowerBand = new Array(length).fill(NaN);
2414
- for (let i = 0; i < length; i++) {
2415
- const hl2 = (high[i] + low[i]) / 2;
2416
- const atrValue = atrValues[i];
2417
- if (!isNaN(atrValue)) {
2418
- upperBand[i] = hl2 + factor * atrValue;
2419
- lowerBand[i] = hl2 - factor * atrValue;
2420
- }
2421
- }
2422
- let prevUpperBand = upperBand[atrPeriod];
2423
- let prevLowerBand = lowerBand[atrPeriod];
2424
- let prevSupertrend = close[atrPeriod] <= prevUpperBand ? prevUpperBand : prevLowerBand;
2425
- let prevDirection = close[atrPeriod] <= prevUpperBand ? -1 : 1;
2426
- supertrend[atrPeriod] = prevSupertrend;
2427
- direction[atrPeriod] = prevDirection;
2428
- for (let i = atrPeriod + 1; i < length; i++) {
2429
- let currentUpperBand = upperBand[i];
2430
- if (currentUpperBand < prevUpperBand || close[i - 1] > prevUpperBand) {
2431
- upperBand[i] = currentUpperBand;
2432
- } else {
2433
- upperBand[i] = prevUpperBand;
2434
- }
2435
- let currentLowerBand = lowerBand[i];
2436
- if (currentLowerBand > prevLowerBand || close[i - 1] < prevLowerBand) {
2437
- lowerBand[i] = currentLowerBand;
2438
- } else {
2439
- lowerBand[i] = prevLowerBand;
2440
- }
2441
- if (prevSupertrend === prevUpperBand) {
2442
- if (close[i] > upperBand[i]) {
2443
- direction[i] = 1;
2444
- supertrend[i] = lowerBand[i];
2445
- } else {
2446
- direction[i] = -1;
2447
- supertrend[i] = upperBand[i];
2448
- }
2449
- } else {
2450
- if (close[i] < lowerBand[i]) {
2451
- direction[i] = -1;
2452
- supertrend[i] = upperBand[i];
2453
- } else {
2454
- direction[i] = 1;
2455
- supertrend[i] = lowerBand[i];
2456
- }
2457
- }
2458
- prevUpperBand = upperBand[i];
2459
- prevLowerBand = lowerBand[i];
2460
- prevSupertrend = supertrend[i];
2461
- }
2462
- return [supertrend, direction];
2463
- }
2464
2577
  function pivothigh(source, leftbars, rightbars) {
2465
2578
  const result = new Array(source.length).fill(NaN);
2466
2579
  for (let i = leftbars + rightbars; i < source.length; i++) {
@@ -2728,6 +2841,8 @@ class Context {
2728
2841
  ohlc4: []
2729
2842
  });
2730
2843
  __publicField$1(this, "cache", {});
2844
+ __publicField$1(this, "taState", {});
2845
+ // State for incremental TA calculations
2731
2846
  __publicField$1(this, "useTACache", false);
2732
2847
  __publicField$1(this, "NA", NaN);
2733
2848
  __publicField$1(this, "math");
@@ -2963,7 +3078,6 @@ class BinanceProvider {
2963
3078
  if (data.length === 0) break;
2964
3079
  allData = allData.concat(data);
2965
3080
  currentStart = data[data.length - 1].closeTime + 1;
2966
- if (data.length < 1e3) break;
2967
3081
  }
2968
3082
  return allData;
2969
3083
  } catch (error) {
@@ -2971,8 +3085,6 @@ class BinanceProvider {
2971
3085
  return [];
2972
3086
  }
2973
3087
  }
2974
- //TODO : allow querying more than 1000 klines
2975
- //TODO : immplement cache
2976
3088
  async getMarketData(tickerId, timeframe, limit, sDate, eDate) {
2977
3089
  try {
2978
3090
  const cacheParams = { tickerId, timeframe, limit, sDate, eDate };
@@ -2986,12 +3098,16 @@ class BinanceProvider {
2986
3098
  console.error(`Unsupported timeframe: ${timeframe}`);
2987
3099
  return [];
2988
3100
  }
2989
- let url = `${BINANCE_API_URL}/klines?symbol=${tickerId}&interval=${interval}`;
2990
- if (!limit && sDate && eDate) {
2991
- return this.getMarketDataInterval(tickerId, timeframe, sDate, eDate);
3101
+ const needsPagination = this.shouldPaginate(timeframe, limit, sDate, eDate);
3102
+ if (needsPagination && sDate && eDate) {
3103
+ const allData = await this.getMarketDataInterval(tickerId, timeframe, sDate, eDate);
3104
+ const result2 = limit ? allData.slice(0, limit) : allData;
3105
+ this.cacheManager.set(cacheParams, result2);
3106
+ return result2;
2992
3107
  }
3108
+ let url = `${BINANCE_API_URL}/klines?symbol=${tickerId}&interval=${interval}`;
2993
3109
  if (limit) {
2994
- url += `&limit=${limit}`;
3110
+ url += `&limit=${Math.min(limit, 1e3)}`;
2995
3111
  }
2996
3112
  if (sDate) {
2997
3113
  url += `&startTime=${sDate}`;
@@ -3027,6 +3143,36 @@ class BinanceProvider {
3027
3143
  return [];
3028
3144
  }
3029
3145
  }
3146
+ /**
3147
+ * Determines if pagination is needed based on the parameters
3148
+ */
3149
+ shouldPaginate(timeframe, limit, sDate, eDate) {
3150
+ if (limit && limit > 1e3) {
3151
+ return true;
3152
+ }
3153
+ if (sDate && eDate) {
3154
+ const interval = timeframe_to_binance[timeframe.toUpperCase()];
3155
+ const timeframeDurations = {
3156
+ "1m": 60 * 1e3,
3157
+ "3m": 3 * 60 * 1e3,
3158
+ "5m": 5 * 60 * 1e3,
3159
+ "15m": 15 * 60 * 1e3,
3160
+ "30m": 30 * 60 * 1e3,
3161
+ "1h": 60 * 60 * 1e3,
3162
+ "2h": 2 * 60 * 60 * 1e3,
3163
+ "4h": 4 * 60 * 60 * 1e3,
3164
+ "1d": 24 * 60 * 60 * 1e3,
3165
+ "1w": 7 * 24 * 60 * 60 * 1e3,
3166
+ "1M": 30 * 24 * 60 * 60 * 1e3
3167
+ };
3168
+ const intervalDuration = timeframeDurations[interval];
3169
+ if (intervalDuration) {
3170
+ const requiredCandles = Math.ceil((eDate - sDate) / intervalDuration);
3171
+ return requiredCandles > 1e3;
3172
+ }
3173
+ }
3174
+ return false;
3175
+ }
3030
3176
  }
3031
3177
 
3032
3178
  const Provider = {