pinets 0.1.33 → 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.
- package/README.md +7 -5
- package/dist/pinets.dev.browser.js +619 -473
- package/dist/pinets.dev.cjs +619 -473
- package/dist/pinets.dev.cjs.map +1 -1
- package/dist/pinets.dev.es.js +617 -471
- package/dist/pinets.dev.es.js.map +1 -1
- package/dist/pinets.min.browser.js +12 -12
- package/dist/pinets.min.cjs +10 -10
- package/dist/pinets.min.es.js +2 -2
- package/dist/types/Context.class.d.ts +1 -0
- package/dist/types/PineTS.class.d.ts +0 -3
- package/dist/types/marketData/Binance/BinanceProvider.class.d.ts +4 -0
- package/dist/types/namespaces/PineMath.d.ts +1 -0
- package/dist/types/namespaces/TechnicalAnalysis.d.ts +19 -19
- package/dist/types/transpiler/ScopeManager.class.d.ts +3 -0
- package/package.json +1 -1
|
@@ -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
|
-
|
|
8378
|
-
node.argument
|
|
8379
|
-
|
|
8380
|
-
|
|
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: "
|
|
8388
|
-
|
|
8403
|
+
type: "Literal",
|
|
8404
|
+
value: 0
|
|
8389
8405
|
},
|
|
8390
|
-
computed:
|
|
8391
|
-
}
|
|
8392
|
-
|
|
8393
|
-
|
|
8394
|
-
|
|
8395
|
-
|
|
8396
|
-
|
|
8397
|
-
|
|
8398
|
-
|
|
8399
|
-
|
|
8400
|
-
|
|
8401
|
-
|
|
8402
|
-
|
|
8403
|
-
|
|
8404
|
-
|
|
8405
|
-
|
|
8406
|
-
|
|
8407
|
-
|
|
8408
|
-
|
|
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
|
-
|
|
9610
|
-
const
|
|
9611
|
-
|
|
9612
|
-
|
|
9613
|
-
|
|
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
|
-
|
|
9616
|
-
|
|
9617
|
-
|
|
9618
|
-
|
|
9619
|
-
|
|
9620
|
-
|
|
9621
|
-
|
|
9622
|
-
|
|
9623
|
-
|
|
9624
|
-
|
|
9625
|
-
|
|
9626
|
-
}
|
|
9627
|
-
|
|
9628
|
-
|
|
9629
|
-
|
|
9630
|
-
|
|
9631
|
-
|
|
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
|
-
|
|
9634
|
-
const
|
|
9635
|
-
|
|
9636
|
-
|
|
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
|
-
|
|
9641
|
-
const
|
|
9642
|
-
|
|
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
|
|
9647
|
-
const
|
|
9648
|
-
|
|
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
|
-
|
|
9653
|
-
const
|
|
9654
|
-
|
|
9655
|
-
|
|
9656
|
-
|
|
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
|
-
|
|
9659
|
-
const
|
|
9660
|
-
|
|
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
|
-
|
|
9665
|
-
const
|
|
9666
|
-
|
|
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
|
-
|
|
9671
|
-
const
|
|
9672
|
-
|
|
9673
|
-
|
|
9674
|
-
|
|
9675
|
-
|
|
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
|
-
|
|
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
|
-
|
|
9686
|
-
const
|
|
9687
|
-
|
|
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
|
-
|
|
9692
|
-
const
|
|
9693
|
-
|
|
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
|
-
|
|
9698
|
-
const
|
|
9699
|
-
|
|
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
|
-
|
|
9704
|
-
const
|
|
9705
|
-
|
|
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
|
-
|
|
9710
|
-
const
|
|
9711
|
-
|
|
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
|
-
|
|
9716
|
-
const
|
|
9717
|
-
|
|
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
|
-
|
|
9723
|
-
const
|
|
9724
|
-
|
|
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
|
-
|
|
9730
|
-
const
|
|
9731
|
-
|
|
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
|
-
|
|
9737
|
-
const
|
|
9738
|
-
|
|
9739
|
-
|
|
9740
|
-
|
|
9741
|
-
|
|
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
|
-
|
|
10646
|
-
if (
|
|
10647
|
-
|
|
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 = {
|