pinets 0.1.34 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -5
- package/dist/pinets.dev.browser.js +639 -484
- package/dist/pinets.dev.cjs +639 -484
- package/dist/pinets.dev.cjs.map +1 -1
- package/dist/pinets.dev.es.js +637 -482
- package/dist/pinets.dev.es.js.map +1 -1
- package/dist/pinets.min.browser.js +12 -12
- package/dist/pinets.min.cjs +12 -12
- 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.
|
|
9243
|
+
const marketData = data.slice(0, MAX_PERIODS);
|
|
9187
9244
|
this._periods = marketData.length;
|
|
9188
9245
|
this.data = marketData;
|
|
9189
9246
|
const _open = marketData.map((d) => d.open);
|
|
@@ -9242,19 +9299,28 @@
|
|
|
9242
9299
|
context.useTACache = useTACache;
|
|
9243
9300
|
const transformer = transpile.bind(this);
|
|
9244
9301
|
let transpiledFn = transformer(pineTSCode);
|
|
9302
|
+
context.data.close = [];
|
|
9303
|
+
context.data.open = [];
|
|
9304
|
+
context.data.high = [];
|
|
9305
|
+
context.data.low = [];
|
|
9306
|
+
context.data.volume = [];
|
|
9307
|
+
context.data.hl2 = [];
|
|
9308
|
+
context.data.hlc3 = [];
|
|
9309
|
+
context.data.ohlc4 = [];
|
|
9310
|
+
context.data.openTime = [];
|
|
9311
|
+
context.data.closeTime = [];
|
|
9245
9312
|
const contextVarNames = ["const", "var", "let", "params"];
|
|
9246
|
-
for (let i = this._periods - n
|
|
9313
|
+
for (let i = this._periods - n; i < this._periods; i++) {
|
|
9247
9314
|
context.idx = i;
|
|
9248
|
-
context.data.close
|
|
9249
|
-
context.data.open
|
|
9250
|
-
context.data.high
|
|
9251
|
-
context.data.low
|
|
9252
|
-
context.data.volume
|
|
9253
|
-
context.data.hl2
|
|
9254
|
-
context.data.hlc3
|
|
9255
|
-
context.data.ohlc4
|
|
9256
|
-
context.data.openTime
|
|
9257
|
-
context.data.closeTime = this.closeTime.slice(idx);
|
|
9315
|
+
context.data.close.unshift(this.close[i]);
|
|
9316
|
+
context.data.open.unshift(this.open[i]);
|
|
9317
|
+
context.data.high.unshift(this.high[i]);
|
|
9318
|
+
context.data.low.unshift(this.low[i]);
|
|
9319
|
+
context.data.volume.unshift(this.volume[i]);
|
|
9320
|
+
context.data.hl2.unshift(this.hl2[i]);
|
|
9321
|
+
context.data.hlc3.unshift(this.hlc3[i]);
|
|
9322
|
+
context.data.ohlc4.unshift(this.ohlc4[i]);
|
|
9323
|
+
context.data.openTime.unshift(this.openTime[i]);
|
|
9258
9324
|
const result = await transpiledFn(context);
|
|
9259
9325
|
if (typeof result === "object") {
|
|
9260
9326
|
if (typeof context.result !== "object") {
|
|
@@ -9441,6 +9507,9 @@
|
|
|
9441
9507
|
return this.context.params[name];
|
|
9442
9508
|
}
|
|
9443
9509
|
}
|
|
9510
|
+
__eq(a, b) {
|
|
9511
|
+
return Math.abs(a - b) < 1e-8;
|
|
9512
|
+
}
|
|
9444
9513
|
abs(source) {
|
|
9445
9514
|
return Math.abs(source[0]);
|
|
9446
9515
|
}
|
|
@@ -9604,141 +9673,532 @@
|
|
|
9604
9673
|
return this.context.params[name];
|
|
9605
9674
|
}
|
|
9606
9675
|
}
|
|
9607
|
-
ema(source, _period) {
|
|
9676
|
+
ema(source, _period, _callId) {
|
|
9608
9677
|
const period = Array.isArray(_period) ? _period[0] : _period;
|
|
9609
|
-
|
|
9610
|
-
const
|
|
9611
|
-
|
|
9612
|
-
|
|
9613
|
-
|
|
9678
|
+
if (!this.context.taState) this.context.taState = {};
|
|
9679
|
+
const stateKey = _callId || `ema_${period}`;
|
|
9680
|
+
if (!this.context.taState[stateKey]) {
|
|
9681
|
+
this.context.taState[stateKey] = { prevEma: null, initSum: 0, initCount: 0 };
|
|
9682
|
+
}
|
|
9683
|
+
const state = this.context.taState[stateKey];
|
|
9684
|
+
const currentValue = source[0];
|
|
9685
|
+
if (state.initCount < period) {
|
|
9686
|
+
state.initSum += currentValue;
|
|
9687
|
+
state.initCount++;
|
|
9688
|
+
if (state.initCount === period) {
|
|
9689
|
+
state.prevEma = state.initSum / period;
|
|
9690
|
+
return this.context.precision(state.prevEma);
|
|
9691
|
+
}
|
|
9692
|
+
return NaN;
|
|
9693
|
+
}
|
|
9694
|
+
const alpha = 2 / (period + 1);
|
|
9695
|
+
const ema2 = currentValue * alpha + state.prevEma * (1 - alpha);
|
|
9696
|
+
state.prevEma = ema2;
|
|
9697
|
+
return this.context.precision(ema2);
|
|
9698
|
+
}
|
|
9699
|
+
sma(source, _period, _callId) {
|
|
9614
9700
|
const period = Array.isArray(_period) ? _period[0] : _period;
|
|
9615
|
-
|
|
9616
|
-
|
|
9617
|
-
|
|
9618
|
-
|
|
9619
|
-
|
|
9620
|
-
|
|
9621
|
-
|
|
9622
|
-
|
|
9623
|
-
|
|
9624
|
-
|
|
9625
|
-
|
|
9626
|
-
}
|
|
9627
|
-
|
|
9628
|
-
|
|
9629
|
-
|
|
9630
|
-
|
|
9631
|
-
|
|
9701
|
+
if (!this.context.taState) this.context.taState = {};
|
|
9702
|
+
const stateKey = _callId || `sma_${period}`;
|
|
9703
|
+
if (!this.context.taState[stateKey]) {
|
|
9704
|
+
this.context.taState[stateKey] = { window: [], sum: 0 };
|
|
9705
|
+
}
|
|
9706
|
+
const state = this.context.taState[stateKey];
|
|
9707
|
+
const currentValue = source[0] || 0;
|
|
9708
|
+
state.window.unshift(currentValue);
|
|
9709
|
+
state.sum += currentValue;
|
|
9710
|
+
if (state.window.length < period) {
|
|
9711
|
+
return NaN;
|
|
9712
|
+
}
|
|
9713
|
+
if (state.window.length > period) {
|
|
9714
|
+
const oldValue = state.window.pop();
|
|
9715
|
+
state.sum -= oldValue;
|
|
9716
|
+
}
|
|
9717
|
+
const sma2 = state.sum / period;
|
|
9718
|
+
return this.context.precision(sma2);
|
|
9719
|
+
}
|
|
9720
|
+
vwma(source, _period, _callId) {
|
|
9632
9721
|
const period = Array.isArray(_period) ? _period[0] : _period;
|
|
9633
|
-
|
|
9634
|
-
const
|
|
9635
|
-
|
|
9636
|
-
|
|
9722
|
+
if (!this.context.taState) this.context.taState = {};
|
|
9723
|
+
const stateKey = _callId || `vwma_${period}`;
|
|
9724
|
+
if (!this.context.taState[stateKey]) {
|
|
9725
|
+
this.context.taState[stateKey] = { window: [], volumeWindow: [] };
|
|
9726
|
+
}
|
|
9727
|
+
const state = this.context.taState[stateKey];
|
|
9728
|
+
const currentValue = source[0];
|
|
9729
|
+
const currentVolume = this.context.data.volume[0];
|
|
9730
|
+
state.window.unshift(currentValue);
|
|
9731
|
+
state.volumeWindow.unshift(currentVolume);
|
|
9732
|
+
if (state.window.length < period) {
|
|
9733
|
+
return NaN;
|
|
9734
|
+
}
|
|
9735
|
+
if (state.window.length > period) {
|
|
9736
|
+
state.window.pop();
|
|
9737
|
+
state.volumeWindow.pop();
|
|
9738
|
+
}
|
|
9739
|
+
let sumVolPrice = 0;
|
|
9740
|
+
let sumVol = 0;
|
|
9741
|
+
for (let i = 0; i < period; i++) {
|
|
9742
|
+
sumVolPrice += state.window[i] * state.volumeWindow[i];
|
|
9743
|
+
sumVol += state.volumeWindow[i];
|
|
9744
|
+
}
|
|
9745
|
+
const vwma2 = sumVolPrice / sumVol;
|
|
9746
|
+
return this.context.precision(vwma2);
|
|
9637
9747
|
}
|
|
9638
|
-
wma(source, _period) {
|
|
9748
|
+
wma(source, _period, _callId) {
|
|
9639
9749
|
const period = Array.isArray(_period) ? _period[0] : _period;
|
|
9640
|
-
|
|
9641
|
-
const
|
|
9642
|
-
|
|
9750
|
+
if (!this.context.taState) this.context.taState = {};
|
|
9751
|
+
const stateKey = _callId || `wma_${period}`;
|
|
9752
|
+
if (!this.context.taState[stateKey]) {
|
|
9753
|
+
this.context.taState[stateKey] = { window: [] };
|
|
9754
|
+
}
|
|
9755
|
+
const state = this.context.taState[stateKey];
|
|
9756
|
+
const currentValue = source[0];
|
|
9757
|
+
state.window.unshift(currentValue);
|
|
9758
|
+
if (state.window.length < period) {
|
|
9759
|
+
return NaN;
|
|
9760
|
+
}
|
|
9761
|
+
if (state.window.length > period) {
|
|
9762
|
+
state.window.pop();
|
|
9763
|
+
}
|
|
9764
|
+
let numerator = 0;
|
|
9765
|
+
let denominator = 0;
|
|
9766
|
+
for (let i = 0; i < period; i++) {
|
|
9767
|
+
const weight = period - i;
|
|
9768
|
+
numerator += state.window[i] * weight;
|
|
9769
|
+
denominator += weight;
|
|
9770
|
+
}
|
|
9771
|
+
const wma2 = numerator / denominator;
|
|
9772
|
+
return this.context.precision(wma2);
|
|
9643
9773
|
}
|
|
9644
|
-
hma(source, _period) {
|
|
9774
|
+
hma(source, _period, _callId) {
|
|
9645
9775
|
const period = Array.isArray(_period) ? _period[0] : _period;
|
|
9646
|
-
const
|
|
9647
|
-
const
|
|
9648
|
-
|
|
9776
|
+
const halfPeriod = Math.floor(period / 2);
|
|
9777
|
+
const sqrtPeriod = Math.floor(Math.sqrt(period));
|
|
9778
|
+
const wma1 = this.wma(source, halfPeriod, _callId ? `${_callId}_wma1` : void 0);
|
|
9779
|
+
const wma2 = this.wma(source, period, _callId ? `${_callId}_wma2` : void 0);
|
|
9780
|
+
if (isNaN(wma1) || isNaN(wma2)) {
|
|
9781
|
+
return NaN;
|
|
9782
|
+
}
|
|
9783
|
+
if (!this.context.taState) this.context.taState = {};
|
|
9784
|
+
const stateKey = _callId || `hma_raw_${period}`;
|
|
9785
|
+
if (!this.context.taState[stateKey]) {
|
|
9786
|
+
this.context.taState[stateKey] = [];
|
|
9787
|
+
}
|
|
9788
|
+
const rawHma = 2 * wma1 - wma2;
|
|
9789
|
+
this.context.taState[stateKey].unshift(rawHma);
|
|
9790
|
+
const hmaStateKey = _callId ? `${_callId}_hma_final` : `hma_final_${period}`;
|
|
9791
|
+
if (!this.context.taState[hmaStateKey]) {
|
|
9792
|
+
this.context.taState[hmaStateKey] = { window: [] };
|
|
9793
|
+
}
|
|
9794
|
+
const state = this.context.taState[hmaStateKey];
|
|
9795
|
+
state.window.unshift(rawHma);
|
|
9796
|
+
if (state.window.length < sqrtPeriod) {
|
|
9797
|
+
return NaN;
|
|
9798
|
+
}
|
|
9799
|
+
if (state.window.length > sqrtPeriod) {
|
|
9800
|
+
state.window.pop();
|
|
9801
|
+
}
|
|
9802
|
+
let numerator = 0;
|
|
9803
|
+
let denominator = 0;
|
|
9804
|
+
for (let i = 0; i < sqrtPeriod; i++) {
|
|
9805
|
+
const weight = sqrtPeriod - i;
|
|
9806
|
+
numerator += state.window[i] * weight;
|
|
9807
|
+
denominator += weight;
|
|
9808
|
+
}
|
|
9809
|
+
const hma2 = numerator / denominator;
|
|
9810
|
+
return this.context.precision(hma2);
|
|
9649
9811
|
}
|
|
9650
|
-
rma(source, _period) {
|
|
9812
|
+
rma(source, _period, _callId) {
|
|
9651
9813
|
const period = Array.isArray(_period) ? _period[0] : _period;
|
|
9652
|
-
|
|
9653
|
-
const
|
|
9654
|
-
|
|
9655
|
-
|
|
9656
|
-
|
|
9814
|
+
if (!this.context.taState) this.context.taState = {};
|
|
9815
|
+
const stateKey = _callId || `rma_${period}`;
|
|
9816
|
+
if (!this.context.taState[stateKey]) {
|
|
9817
|
+
this.context.taState[stateKey] = { prevRma: null, initSum: 0, initCount: 0 };
|
|
9818
|
+
}
|
|
9819
|
+
const state = this.context.taState[stateKey];
|
|
9820
|
+
const currentValue = source[0] || 0;
|
|
9821
|
+
if (state.initCount < period) {
|
|
9822
|
+
state.initSum += currentValue;
|
|
9823
|
+
state.initCount++;
|
|
9824
|
+
if (state.initCount === period) {
|
|
9825
|
+
state.prevRma = state.initSum / period;
|
|
9826
|
+
return this.context.precision(state.prevRma);
|
|
9827
|
+
}
|
|
9828
|
+
return NaN;
|
|
9829
|
+
}
|
|
9830
|
+
const alpha = 1 / period;
|
|
9831
|
+
const rma2 = currentValue * alpha + state.prevRma * (1 - alpha);
|
|
9832
|
+
state.prevRma = rma2;
|
|
9833
|
+
return this.context.precision(rma2);
|
|
9834
|
+
}
|
|
9835
|
+
change(source, _length = 1, _callId) {
|
|
9657
9836
|
const length = Array.isArray(_length) ? _length[0] : _length;
|
|
9658
|
-
|
|
9659
|
-
const
|
|
9660
|
-
|
|
9837
|
+
if (!this.context.taState) this.context.taState = {};
|
|
9838
|
+
const stateKey = _callId || `change_${length}`;
|
|
9839
|
+
if (!this.context.taState[stateKey]) {
|
|
9840
|
+
this.context.taState[stateKey] = { window: [] };
|
|
9841
|
+
}
|
|
9842
|
+
const state = this.context.taState[stateKey];
|
|
9843
|
+
const currentValue = source[0];
|
|
9844
|
+
state.window.unshift(currentValue);
|
|
9845
|
+
if (state.window.length <= length) {
|
|
9846
|
+
return NaN;
|
|
9847
|
+
}
|
|
9848
|
+
if (state.window.length > length + 1) {
|
|
9849
|
+
state.window.pop();
|
|
9850
|
+
}
|
|
9851
|
+
const change2 = currentValue - state.window[length];
|
|
9852
|
+
return this.context.precision(change2);
|
|
9661
9853
|
}
|
|
9662
|
-
rsi(source, _period) {
|
|
9854
|
+
rsi(source, _period, _callId) {
|
|
9663
9855
|
const period = Array.isArray(_period) ? _period[0] : _period;
|
|
9664
|
-
|
|
9665
|
-
const
|
|
9666
|
-
|
|
9856
|
+
if (!this.context.taState) this.context.taState = {};
|
|
9857
|
+
const stateKey = _callId || `rsi_${period}`;
|
|
9858
|
+
if (!this.context.taState[stateKey]) {
|
|
9859
|
+
this.context.taState[stateKey] = {
|
|
9860
|
+
prevValue: null,
|
|
9861
|
+
avgGain: 0,
|
|
9862
|
+
avgLoss: 0,
|
|
9863
|
+
initGains: [],
|
|
9864
|
+
initLosses: []
|
|
9865
|
+
};
|
|
9866
|
+
}
|
|
9867
|
+
const state = this.context.taState[stateKey];
|
|
9868
|
+
const currentValue = source[0];
|
|
9869
|
+
if (state.prevValue !== null) {
|
|
9870
|
+
const diff = currentValue - state.prevValue;
|
|
9871
|
+
const gain = diff > 0 ? diff : 0;
|
|
9872
|
+
const loss = diff < 0 ? -diff : 0;
|
|
9873
|
+
if (state.initGains.length < period) {
|
|
9874
|
+
state.initGains.push(gain);
|
|
9875
|
+
state.initLosses.push(loss);
|
|
9876
|
+
if (state.initGains.length === period) {
|
|
9877
|
+
state.avgGain = state.initGains.reduce((a, b) => a + b, 0) / period;
|
|
9878
|
+
state.avgLoss = state.initLosses.reduce((a, b) => a + b, 0) / period;
|
|
9879
|
+
state.prevValue = currentValue;
|
|
9880
|
+
const rsi3 = state.avgLoss === 0 ? 100 : 100 - 100 / (1 + state.avgGain / state.avgLoss);
|
|
9881
|
+
return this.context.precision(rsi3);
|
|
9882
|
+
}
|
|
9883
|
+
state.prevValue = currentValue;
|
|
9884
|
+
return NaN;
|
|
9885
|
+
}
|
|
9886
|
+
state.avgGain = (state.avgGain * (period - 1) + gain) / period;
|
|
9887
|
+
state.avgLoss = (state.avgLoss * (period - 1) + loss) / period;
|
|
9888
|
+
const rsi2 = state.avgLoss === 0 ? 100 : 100 - 100 / (1 + state.avgGain / state.avgLoss);
|
|
9889
|
+
state.prevValue = currentValue;
|
|
9890
|
+
return this.context.precision(rsi2);
|
|
9891
|
+
}
|
|
9892
|
+
state.prevValue = currentValue;
|
|
9893
|
+
return NaN;
|
|
9667
9894
|
}
|
|
9668
|
-
atr(_period) {
|
|
9895
|
+
atr(_period, _callId) {
|
|
9669
9896
|
const period = Array.isArray(_period) ? _period[0] : _period;
|
|
9670
|
-
|
|
9671
|
-
const
|
|
9672
|
-
|
|
9673
|
-
|
|
9674
|
-
|
|
9675
|
-
|
|
9897
|
+
if (!this.context.taState) this.context.taState = {};
|
|
9898
|
+
const stateKey = _callId || `atr_${period}`;
|
|
9899
|
+
if (!this.context.taState[stateKey]) {
|
|
9900
|
+
this.context.taState[stateKey] = {
|
|
9901
|
+
prevAtr: null,
|
|
9902
|
+
initSum: 0,
|
|
9903
|
+
initCount: 0,
|
|
9904
|
+
prevClose: null
|
|
9905
|
+
};
|
|
9906
|
+
}
|
|
9907
|
+
const state = this.context.taState[stateKey];
|
|
9908
|
+
const high = this.context.data.high[0];
|
|
9909
|
+
const low = this.context.data.low[0];
|
|
9910
|
+
const close = this.context.data.close[0];
|
|
9911
|
+
let tr;
|
|
9912
|
+
if (state.prevClose !== null) {
|
|
9913
|
+
const hl = high - low;
|
|
9914
|
+
const hc = Math.abs(high - state.prevClose);
|
|
9915
|
+
const lc = Math.abs(low - state.prevClose);
|
|
9916
|
+
tr = Math.max(hl, hc, lc);
|
|
9917
|
+
} else {
|
|
9918
|
+
tr = high - low;
|
|
9919
|
+
}
|
|
9920
|
+
state.prevClose = close;
|
|
9921
|
+
if (state.initCount < period) {
|
|
9922
|
+
state.initSum += tr;
|
|
9923
|
+
state.initCount++;
|
|
9924
|
+
if (state.initCount === period) {
|
|
9925
|
+
state.prevAtr = state.initSum / period;
|
|
9926
|
+
return this.context.precision(state.prevAtr);
|
|
9927
|
+
}
|
|
9928
|
+
return NaN;
|
|
9929
|
+
}
|
|
9930
|
+
const atr2 = (state.prevAtr * (period - 1) + tr) / period;
|
|
9931
|
+
state.prevAtr = atr2;
|
|
9932
|
+
return this.context.precision(atr2);
|
|
9676
9933
|
}
|
|
9677
|
-
mom(source, _length) {
|
|
9934
|
+
mom(source, _length, _callId) {
|
|
9678
9935
|
const length = Array.isArray(_length) ? _length[0] : _length;
|
|
9679
|
-
|
|
9680
|
-
const idx = this.context.idx;
|
|
9681
|
-
return this.context.precision(result[idx]);
|
|
9936
|
+
return this.change(source, length);
|
|
9682
9937
|
}
|
|
9683
|
-
roc(source, _length) {
|
|
9938
|
+
roc(source, _length, _callId) {
|
|
9684
9939
|
const length = Array.isArray(_length) ? _length[0] : _length;
|
|
9685
|
-
|
|
9686
|
-
const
|
|
9687
|
-
|
|
9940
|
+
if (!this.context.taState) this.context.taState = {};
|
|
9941
|
+
const stateKey = _callId || `roc_${length}`;
|
|
9942
|
+
if (!this.context.taState[stateKey]) {
|
|
9943
|
+
this.context.taState[stateKey] = { window: [] };
|
|
9944
|
+
}
|
|
9945
|
+
const state = this.context.taState[stateKey];
|
|
9946
|
+
const currentValue = source[0];
|
|
9947
|
+
state.window.unshift(currentValue);
|
|
9948
|
+
if (state.window.length <= length) {
|
|
9949
|
+
return NaN;
|
|
9950
|
+
}
|
|
9951
|
+
if (state.window.length > length + 1) {
|
|
9952
|
+
state.window.pop();
|
|
9953
|
+
}
|
|
9954
|
+
const prevValue = state.window[length];
|
|
9955
|
+
const roc2 = (currentValue - prevValue) / prevValue * 100;
|
|
9956
|
+
return this.context.precision(roc2);
|
|
9688
9957
|
}
|
|
9689
|
-
dev(source, _length) {
|
|
9958
|
+
dev(source, _length, _callId) {
|
|
9690
9959
|
const length = Array.isArray(_length) ? _length[0] : _length;
|
|
9691
|
-
|
|
9692
|
-
const
|
|
9693
|
-
|
|
9960
|
+
if (!this.context.taState) this.context.taState = {};
|
|
9961
|
+
const stateKey = _callId || `dev_${length}`;
|
|
9962
|
+
if (!this.context.taState[stateKey]) {
|
|
9963
|
+
this.context.taState[stateKey] = { window: [], sum: 0 };
|
|
9964
|
+
}
|
|
9965
|
+
const state = this.context.taState[stateKey];
|
|
9966
|
+
const currentValue = source[0] || 0;
|
|
9967
|
+
state.window.unshift(currentValue);
|
|
9968
|
+
state.sum += currentValue;
|
|
9969
|
+
if (state.window.length < length) {
|
|
9970
|
+
return NaN;
|
|
9971
|
+
}
|
|
9972
|
+
if (state.window.length > length) {
|
|
9973
|
+
const oldValue = state.window.pop();
|
|
9974
|
+
state.sum -= oldValue;
|
|
9975
|
+
}
|
|
9976
|
+
const mean = state.sum / length;
|
|
9977
|
+
let sumDeviation = 0;
|
|
9978
|
+
for (let i = 0; i < length; i++) {
|
|
9979
|
+
sumDeviation += Math.abs(state.window[i] - mean);
|
|
9980
|
+
}
|
|
9981
|
+
const dev2 = sumDeviation / length;
|
|
9982
|
+
return this.context.precision(dev2);
|
|
9694
9983
|
}
|
|
9695
|
-
variance(source, _length) {
|
|
9984
|
+
variance(source, _length, _callId) {
|
|
9696
9985
|
const length = Array.isArray(_length) ? _length[0] : _length;
|
|
9697
|
-
|
|
9698
|
-
const
|
|
9699
|
-
|
|
9986
|
+
if (!this.context.taState) this.context.taState = {};
|
|
9987
|
+
const stateKey = _callId || `variance_${length}`;
|
|
9988
|
+
if (!this.context.taState[stateKey]) {
|
|
9989
|
+
this.context.taState[stateKey] = { window: [] };
|
|
9990
|
+
}
|
|
9991
|
+
const state = this.context.taState[stateKey];
|
|
9992
|
+
const currentValue = source[0];
|
|
9993
|
+
state.window.unshift(currentValue);
|
|
9994
|
+
if (state.window.length < length) {
|
|
9995
|
+
return NaN;
|
|
9996
|
+
}
|
|
9997
|
+
if (state.window.length > length) {
|
|
9998
|
+
state.window.pop();
|
|
9999
|
+
}
|
|
10000
|
+
let sum = 0;
|
|
10001
|
+
let sumSquares = 0;
|
|
10002
|
+
for (let i = 0; i < length; i++) {
|
|
10003
|
+
sum += state.window[i];
|
|
10004
|
+
sumSquares += state.window[i] * state.window[i];
|
|
10005
|
+
}
|
|
10006
|
+
const mean = sum / length;
|
|
10007
|
+
const variance2 = sumSquares / length - mean * mean;
|
|
10008
|
+
return this.context.precision(variance2);
|
|
9700
10009
|
}
|
|
9701
|
-
highest(source, _length) {
|
|
10010
|
+
highest(source, _length, _callId) {
|
|
9702
10011
|
const length = Array.isArray(_length) ? _length[0] : _length;
|
|
9703
|
-
|
|
9704
|
-
const
|
|
9705
|
-
|
|
10012
|
+
if (!this.context.taState) this.context.taState = {};
|
|
10013
|
+
const stateKey = _callId || `highest_${length}`;
|
|
10014
|
+
if (!this.context.taState[stateKey]) {
|
|
10015
|
+
this.context.taState[stateKey] = { window: [] };
|
|
10016
|
+
}
|
|
10017
|
+
const state = this.context.taState[stateKey];
|
|
10018
|
+
const currentValue = source[0];
|
|
10019
|
+
state.window.unshift(currentValue);
|
|
10020
|
+
if (state.window.length < length) {
|
|
10021
|
+
return NaN;
|
|
10022
|
+
}
|
|
10023
|
+
if (state.window.length > length) {
|
|
10024
|
+
state.window.pop();
|
|
10025
|
+
}
|
|
10026
|
+
const max = Math.max(...state.window.filter((v) => !isNaN(v)));
|
|
10027
|
+
return this.context.precision(max);
|
|
9706
10028
|
}
|
|
9707
|
-
lowest(source, _length) {
|
|
10029
|
+
lowest(source, _length, _callId) {
|
|
9708
10030
|
const length = Array.isArray(_length) ? _length[0] : _length;
|
|
9709
|
-
|
|
9710
|
-
const
|
|
9711
|
-
|
|
10031
|
+
if (!this.context.taState) this.context.taState = {};
|
|
10032
|
+
const stateKey = _callId || `lowest_${length}`;
|
|
10033
|
+
if (!this.context.taState[stateKey]) {
|
|
10034
|
+
this.context.taState[stateKey] = { window: [] };
|
|
10035
|
+
}
|
|
10036
|
+
const state = this.context.taState[stateKey];
|
|
10037
|
+
const currentValue = source[0];
|
|
10038
|
+
state.window.unshift(currentValue);
|
|
10039
|
+
if (state.window.length < length) {
|
|
10040
|
+
return NaN;
|
|
10041
|
+
}
|
|
10042
|
+
if (state.window.length > length) {
|
|
10043
|
+
state.window.pop();
|
|
10044
|
+
}
|
|
10045
|
+
const validValues = state.window.filter((v) => !isNaN(v) && v !== void 0);
|
|
10046
|
+
const min = validValues.length > 0 ? Math.min(...validValues) : NaN;
|
|
10047
|
+
return this.context.precision(min);
|
|
9712
10048
|
}
|
|
9713
|
-
median(source, _length) {
|
|
10049
|
+
median(source, _length, _callId) {
|
|
9714
10050
|
const length = Array.isArray(_length) ? _length[0] : _length;
|
|
9715
|
-
|
|
9716
|
-
const
|
|
9717
|
-
|
|
10051
|
+
if (!this.context.taState) this.context.taState = {};
|
|
10052
|
+
const stateKey = _callId || `median_${length}`;
|
|
10053
|
+
if (!this.context.taState[stateKey]) {
|
|
10054
|
+
this.context.taState[stateKey] = { window: [] };
|
|
10055
|
+
}
|
|
10056
|
+
const state = this.context.taState[stateKey];
|
|
10057
|
+
const currentValue = source[0];
|
|
10058
|
+
state.window.unshift(currentValue);
|
|
10059
|
+
if (state.window.length < length) {
|
|
10060
|
+
return NaN;
|
|
10061
|
+
}
|
|
10062
|
+
if (state.window.length > length) {
|
|
10063
|
+
state.window.pop();
|
|
10064
|
+
}
|
|
10065
|
+
const sorted = state.window.slice().sort((a, b) => a - b);
|
|
10066
|
+
const mid = Math.floor(length / 2);
|
|
10067
|
+
const median2 = length % 2 === 0 ? (sorted[mid - 1] + sorted[mid]) / 2 : sorted[mid];
|
|
10068
|
+
return this.context.precision(median2);
|
|
9718
10069
|
}
|
|
9719
|
-
stdev(source, _length, _bias = true) {
|
|
10070
|
+
stdev(source, _length, _bias = true, _callId) {
|
|
9720
10071
|
const length = Array.isArray(_length) ? _length[0] : _length;
|
|
9721
10072
|
const bias = Array.isArray(_bias) ? _bias[0] : _bias;
|
|
9722
|
-
|
|
9723
|
-
const
|
|
9724
|
-
|
|
10073
|
+
if (!this.context.taState) this.context.taState = {};
|
|
10074
|
+
const stateKey = _callId || `stdev_${length}_${bias}`;
|
|
10075
|
+
if (!this.context.taState[stateKey]) {
|
|
10076
|
+
this.context.taState[stateKey] = { window: [], sum: 0 };
|
|
10077
|
+
}
|
|
10078
|
+
const state = this.context.taState[stateKey];
|
|
10079
|
+
const currentValue = source[0];
|
|
10080
|
+
state.window.unshift(currentValue);
|
|
10081
|
+
state.sum += currentValue;
|
|
10082
|
+
if (state.window.length < length) {
|
|
10083
|
+
return NaN;
|
|
10084
|
+
}
|
|
10085
|
+
if (state.window.length > length) {
|
|
10086
|
+
const oldValue = state.window.pop();
|
|
10087
|
+
state.sum -= oldValue;
|
|
10088
|
+
}
|
|
10089
|
+
const mean = state.sum / length;
|
|
10090
|
+
let sumSquaredDiff = 0;
|
|
10091
|
+
for (let i = 0; i < length; i++) {
|
|
10092
|
+
sumSquaredDiff += Math.pow(state.window[i] - mean, 2);
|
|
10093
|
+
}
|
|
10094
|
+
const divisor = bias ? length : length - 1;
|
|
10095
|
+
const stdev2 = Math.sqrt(sumSquaredDiff / divisor);
|
|
10096
|
+
return this.context.precision(stdev2);
|
|
9725
10097
|
}
|
|
9726
|
-
linreg(source, _length, _offset) {
|
|
10098
|
+
linreg(source, _length, _offset, _callId) {
|
|
9727
10099
|
const length = Array.isArray(_length) ? _length[0] : _length;
|
|
9728
10100
|
const offset = Array.isArray(_offset) ? _offset[0] : _offset;
|
|
9729
|
-
|
|
9730
|
-
const
|
|
9731
|
-
|
|
10101
|
+
if (!this.context.taState) this.context.taState = {};
|
|
10102
|
+
const stateKey = _callId || `linreg_${length}_${offset}`;
|
|
10103
|
+
if (!this.context.taState[stateKey]) {
|
|
10104
|
+
this.context.taState[stateKey] = { window: [] };
|
|
10105
|
+
}
|
|
10106
|
+
const state = this.context.taState[stateKey];
|
|
10107
|
+
const currentValue = source[0];
|
|
10108
|
+
state.window.unshift(currentValue);
|
|
10109
|
+
if (state.window.length < length) {
|
|
10110
|
+
return NaN;
|
|
10111
|
+
}
|
|
10112
|
+
if (state.window.length > length) {
|
|
10113
|
+
state.window.pop();
|
|
10114
|
+
}
|
|
10115
|
+
let sumX = 0;
|
|
10116
|
+
let sumY = 0;
|
|
10117
|
+
let sumXY = 0;
|
|
10118
|
+
let sumXX = 0;
|
|
10119
|
+
const n = length;
|
|
10120
|
+
for (let j = 0; j < length; j++) {
|
|
10121
|
+
const x = length - 1 - j;
|
|
10122
|
+
const y = state.window[j];
|
|
10123
|
+
sumX += x;
|
|
10124
|
+
sumY += y;
|
|
10125
|
+
sumXY += x * y;
|
|
10126
|
+
sumXX += x * x;
|
|
10127
|
+
}
|
|
10128
|
+
const denominator = n * sumXX - sumX * sumX;
|
|
10129
|
+
if (denominator === 0) {
|
|
10130
|
+
return NaN;
|
|
10131
|
+
}
|
|
10132
|
+
const slope = (n * sumXY - sumX * sumY) / denominator;
|
|
10133
|
+
const intercept = (sumY - slope * sumX) / n;
|
|
10134
|
+
const linRegValue = intercept + slope * (length - 1 - offset);
|
|
10135
|
+
return this.context.precision(linRegValue);
|
|
9732
10136
|
}
|
|
9733
|
-
supertrend(_factor, _atrPeriod) {
|
|
10137
|
+
supertrend(_factor, _atrPeriod, _callId) {
|
|
9734
10138
|
const factor = Array.isArray(_factor) ? _factor[0] : _factor;
|
|
9735
10139
|
const atrPeriod = Array.isArray(_atrPeriod) ? _atrPeriod[0] : _atrPeriod;
|
|
9736
|
-
|
|
9737
|
-
const
|
|
9738
|
-
|
|
9739
|
-
|
|
9740
|
-
|
|
9741
|
-
|
|
10140
|
+
if (!this.context.taState) this.context.taState = {};
|
|
10141
|
+
const stateKey = `supertrend_${factor}_${atrPeriod}`;
|
|
10142
|
+
if (!this.context.taState[stateKey]) {
|
|
10143
|
+
this.context.taState[stateKey] = {
|
|
10144
|
+
prevUpperBand: null,
|
|
10145
|
+
prevLowerBand: null,
|
|
10146
|
+
prevSupertrend: null,
|
|
10147
|
+
prevDirection: null
|
|
10148
|
+
};
|
|
10149
|
+
}
|
|
10150
|
+
const state = this.context.taState[stateKey];
|
|
10151
|
+
const high = this.context.data.high[0];
|
|
10152
|
+
const low = this.context.data.low[0];
|
|
10153
|
+
const close = this.context.data.close[0];
|
|
10154
|
+
const atrValue = this.atr(atrPeriod, _callId ? `${_callId}_atr` : void 0);
|
|
10155
|
+
if (isNaN(atrValue)) {
|
|
10156
|
+
return [[NaN, 0]];
|
|
10157
|
+
}
|
|
10158
|
+
const hl2 = (high + low) / 2;
|
|
10159
|
+
let upperBand = hl2 + factor * atrValue;
|
|
10160
|
+
let lowerBand = hl2 - factor * atrValue;
|
|
10161
|
+
if (state.prevUpperBand !== null) {
|
|
10162
|
+
if (upperBand < state.prevUpperBand || this.context.data.close[1] > state.prevUpperBand) {
|
|
10163
|
+
upperBand = upperBand;
|
|
10164
|
+
} else {
|
|
10165
|
+
upperBand = state.prevUpperBand;
|
|
10166
|
+
}
|
|
10167
|
+
if (lowerBand > state.prevLowerBand || this.context.data.close[1] < state.prevLowerBand) {
|
|
10168
|
+
lowerBand = lowerBand;
|
|
10169
|
+
} else {
|
|
10170
|
+
lowerBand = state.prevLowerBand;
|
|
10171
|
+
}
|
|
10172
|
+
}
|
|
10173
|
+
let direction;
|
|
10174
|
+
let supertrend;
|
|
10175
|
+
if (state.prevSupertrend === null) {
|
|
10176
|
+
direction = close <= upperBand ? -1 : 1;
|
|
10177
|
+
supertrend = direction === -1 ? upperBand : lowerBand;
|
|
10178
|
+
} else {
|
|
10179
|
+
if (state.prevSupertrend === state.prevUpperBand) {
|
|
10180
|
+
if (close > upperBand) {
|
|
10181
|
+
direction = 1;
|
|
10182
|
+
supertrend = lowerBand;
|
|
10183
|
+
} else {
|
|
10184
|
+
direction = -1;
|
|
10185
|
+
supertrend = upperBand;
|
|
10186
|
+
}
|
|
10187
|
+
} else {
|
|
10188
|
+
if (close < lowerBand) {
|
|
10189
|
+
direction = -1;
|
|
10190
|
+
supertrend = upperBand;
|
|
10191
|
+
} else {
|
|
10192
|
+
direction = 1;
|
|
10193
|
+
supertrend = lowerBand;
|
|
10194
|
+
}
|
|
10195
|
+
}
|
|
10196
|
+
}
|
|
10197
|
+
state.prevUpperBand = upperBand;
|
|
10198
|
+
state.prevLowerBand = lowerBand;
|
|
10199
|
+
state.prevSupertrend = supertrend;
|
|
10200
|
+
state.prevDirection = direction;
|
|
10201
|
+
return [[this.context.precision(supertrend), direction]];
|
|
9742
10202
|
}
|
|
9743
10203
|
crossover(source1, source2) {
|
|
9744
10204
|
const current1 = Array.isArray(source1) ? source1[0] : source1;
|
|
@@ -9779,344 +10239,6 @@
|
|
|
9779
10239
|
return this.context.precision(result[idx]);
|
|
9780
10240
|
}
|
|
9781
10241
|
}
|
|
9782
|
-
function atr(high, low, close, period) {
|
|
9783
|
-
const tr = new Array(high.length);
|
|
9784
|
-
tr[0] = high[0] - low[0];
|
|
9785
|
-
for (let i = 1; i < high.length; i++) {
|
|
9786
|
-
const hl = high[i] - low[i];
|
|
9787
|
-
const hc = Math.abs(high[i] - close[i - 1]);
|
|
9788
|
-
const lc = Math.abs(low[i] - close[i - 1]);
|
|
9789
|
-
tr[i] = Math.max(hl, hc, lc);
|
|
9790
|
-
}
|
|
9791
|
-
const atr2 = new Array(high.length).fill(NaN);
|
|
9792
|
-
let sum = 0;
|
|
9793
|
-
for (let i = 0; i < period; i++) {
|
|
9794
|
-
sum += tr[i];
|
|
9795
|
-
}
|
|
9796
|
-
atr2[period - 1] = sum / period;
|
|
9797
|
-
for (let i = period; i < tr.length; i++) {
|
|
9798
|
-
atr2[i] = (atr2[i - 1] * (period - 1) + tr[i]) / period;
|
|
9799
|
-
}
|
|
9800
|
-
return atr2;
|
|
9801
|
-
}
|
|
9802
|
-
function ema(source, period) {
|
|
9803
|
-
const result = new Array(source.length).fill(NaN);
|
|
9804
|
-
const alpha = 2 / (period + 1);
|
|
9805
|
-
let sum = 0;
|
|
9806
|
-
for (let i = 0; i < period; i++) {
|
|
9807
|
-
sum += source[i] || 0;
|
|
9808
|
-
}
|
|
9809
|
-
result[period - 1] = sum / period;
|
|
9810
|
-
for (let i = period; i < source.length; i++) {
|
|
9811
|
-
result[i] = source[i] * alpha + result[i - 1] * (1 - alpha);
|
|
9812
|
-
}
|
|
9813
|
-
return result;
|
|
9814
|
-
}
|
|
9815
|
-
function rsi(source, period) {
|
|
9816
|
-
const result = new Array(source.length).fill(NaN);
|
|
9817
|
-
const gains = new Array(source.length).fill(0);
|
|
9818
|
-
const losses = new Array(source.length).fill(0);
|
|
9819
|
-
for (let i = 1; i < source.length; i++) {
|
|
9820
|
-
const diff = source[i] - source[i - 1];
|
|
9821
|
-
gains[i] = diff > 0 ? diff : 0;
|
|
9822
|
-
losses[i] = diff < 0 ? -diff : 0;
|
|
9823
|
-
}
|
|
9824
|
-
let avgGain = 0;
|
|
9825
|
-
let avgLoss = 0;
|
|
9826
|
-
for (let i = 1; i <= period; i++) {
|
|
9827
|
-
avgGain += gains[i];
|
|
9828
|
-
avgLoss += losses[i];
|
|
9829
|
-
}
|
|
9830
|
-
avgGain /= period;
|
|
9831
|
-
avgLoss /= period;
|
|
9832
|
-
result[period] = avgLoss === 0 ? 100 : 100 - 100 / (1 + avgGain / avgLoss);
|
|
9833
|
-
for (let i = period + 1; i < source.length; i++) {
|
|
9834
|
-
avgGain = (avgGain * (period - 1) + gains[i]) / period;
|
|
9835
|
-
avgLoss = (avgLoss * (period - 1) + losses[i]) / period;
|
|
9836
|
-
result[i] = avgLoss === 0 ? 100 : 100 - 100 / (1 + avgGain / avgLoss);
|
|
9837
|
-
}
|
|
9838
|
-
return result;
|
|
9839
|
-
}
|
|
9840
|
-
function rma(source, period) {
|
|
9841
|
-
const result = new Array(source.length).fill(NaN);
|
|
9842
|
-
const alpha = 1 / period;
|
|
9843
|
-
let sum = 0;
|
|
9844
|
-
for (let i = 0; i < period; i++) {
|
|
9845
|
-
sum += source[i] || 0;
|
|
9846
|
-
}
|
|
9847
|
-
result[period - 1] = sum / period;
|
|
9848
|
-
for (let i = period; i < source.length; i++) {
|
|
9849
|
-
const currentValue = source[i] || 0;
|
|
9850
|
-
result[i] = currentValue * alpha + result[i - 1] * (1 - alpha);
|
|
9851
|
-
}
|
|
9852
|
-
return result;
|
|
9853
|
-
}
|
|
9854
|
-
function sma_cache(source, period, cacheObj) {
|
|
9855
|
-
const result = cacheObj.previousResult || new Array(source.length).fill(NaN);
|
|
9856
|
-
const lastProcessedIndex = cacheObj.lastProcessedIndex || -1;
|
|
9857
|
-
let previousSum = cacheObj.previousSum || 0;
|
|
9858
|
-
if (lastProcessedIndex === -1 || source.length !== lastProcessedIndex + 1) {
|
|
9859
|
-
previousSum = 0;
|
|
9860
|
-
for (let i = 0; i < period; i++) {
|
|
9861
|
-
previousSum += source[i] || 0;
|
|
9862
|
-
}
|
|
9863
|
-
result[period - 1] = previousSum / period;
|
|
9864
|
-
for (let i = 0; i < period - 1; i++) {
|
|
9865
|
-
result[i] = NaN;
|
|
9866
|
-
}
|
|
9867
|
-
for (let i = period; i < source.length; i++) {
|
|
9868
|
-
previousSum = previousSum - (source[i - period] || 0) + (source[i] || 0);
|
|
9869
|
-
result[i] = previousSum / period;
|
|
9870
|
-
}
|
|
9871
|
-
} else if (source.length === lastProcessedIndex + 2) {
|
|
9872
|
-
const newIndex = source.length - 1;
|
|
9873
|
-
previousSum = previousSum - (source[newIndex - period] || 0) + (source[newIndex] || 0);
|
|
9874
|
-
result[newIndex] = previousSum / period;
|
|
9875
|
-
} else {
|
|
9876
|
-
return sma(source, period);
|
|
9877
|
-
}
|
|
9878
|
-
cacheObj.previousSum = previousSum;
|
|
9879
|
-
cacheObj.lastProcessedIndex = source.length - 1;
|
|
9880
|
-
cacheObj.previousResult = result;
|
|
9881
|
-
return result;
|
|
9882
|
-
}
|
|
9883
|
-
function sma(source, period) {
|
|
9884
|
-
const result = new Array(source.length).fill(NaN);
|
|
9885
|
-
for (let i = period - 1; i < source.length; i++) {
|
|
9886
|
-
let sum = 0;
|
|
9887
|
-
for (let j = 0; j < period; j++) {
|
|
9888
|
-
sum += source[i - j] || 0;
|
|
9889
|
-
}
|
|
9890
|
-
result[i] = sum / period;
|
|
9891
|
-
}
|
|
9892
|
-
return result;
|
|
9893
|
-
}
|
|
9894
|
-
function vwma(source, volume, period) {
|
|
9895
|
-
const result = new Array(source.length).fill(NaN);
|
|
9896
|
-
for (let i = period - 1; i < source.length; i++) {
|
|
9897
|
-
let sumVol = 0;
|
|
9898
|
-
let sumVolPrice = 0;
|
|
9899
|
-
for (let j = 0; j < period; j++) {
|
|
9900
|
-
sumVol += volume[i - j];
|
|
9901
|
-
sumVolPrice += source[i - j] * volume[i - j];
|
|
9902
|
-
}
|
|
9903
|
-
result[i] = sumVolPrice / sumVol;
|
|
9904
|
-
}
|
|
9905
|
-
return result;
|
|
9906
|
-
}
|
|
9907
|
-
function hma(source, period) {
|
|
9908
|
-
const halfPeriod = Math.floor(period / 2);
|
|
9909
|
-
const wma1 = wma(source, halfPeriod);
|
|
9910
|
-
const wma2 = wma(source, period);
|
|
9911
|
-
const rawHma = wma1.map((value, index) => 2 * value - wma2[index]);
|
|
9912
|
-
const sqrtPeriod = Math.floor(Math.sqrt(period));
|
|
9913
|
-
const result = wma(rawHma, sqrtPeriod);
|
|
9914
|
-
return result;
|
|
9915
|
-
}
|
|
9916
|
-
function wma(source, period) {
|
|
9917
|
-
const result = new Array(source.length);
|
|
9918
|
-
for (let i = period - 1; i < source.length; i++) {
|
|
9919
|
-
let numerator = 0;
|
|
9920
|
-
let denominator = 0;
|
|
9921
|
-
for (let j = 0; j < period; j++) {
|
|
9922
|
-
numerator += source[i - j] * (period - j);
|
|
9923
|
-
denominator += period - j;
|
|
9924
|
-
}
|
|
9925
|
-
result[i] = numerator / denominator;
|
|
9926
|
-
}
|
|
9927
|
-
for (let i = 0; i < period - 1; i++) {
|
|
9928
|
-
result[i] = NaN;
|
|
9929
|
-
}
|
|
9930
|
-
return result;
|
|
9931
|
-
}
|
|
9932
|
-
function change(source, length = 1) {
|
|
9933
|
-
const result = new Array(source.length).fill(NaN);
|
|
9934
|
-
for (let i = length; i < source.length; i++) {
|
|
9935
|
-
result[i] = source[i] - source[i - length];
|
|
9936
|
-
}
|
|
9937
|
-
return result;
|
|
9938
|
-
}
|
|
9939
|
-
function mom(source, length) {
|
|
9940
|
-
const result = new Array(source.length).fill(NaN);
|
|
9941
|
-
for (let i = length; i < source.length; i++) {
|
|
9942
|
-
result[i] = source[i] - source[i - length];
|
|
9943
|
-
}
|
|
9944
|
-
return result;
|
|
9945
|
-
}
|
|
9946
|
-
function roc(source, length) {
|
|
9947
|
-
const result = new Array(source.length).fill(NaN);
|
|
9948
|
-
for (let i = length; i < source.length; i++) {
|
|
9949
|
-
result[i] = (source[i] - source[i - length]) / source[i - length] * 100;
|
|
9950
|
-
}
|
|
9951
|
-
return result;
|
|
9952
|
-
}
|
|
9953
|
-
function dev(source, length) {
|
|
9954
|
-
const result = new Array(source.length).fill(NaN);
|
|
9955
|
-
const smaValues = sma(source, length);
|
|
9956
|
-
for (let i = length - 1; i < source.length; i++) {
|
|
9957
|
-
let sumDeviation = 0;
|
|
9958
|
-
for (let j = 0; j < length; j++) {
|
|
9959
|
-
sumDeviation += Math.abs(source[i - j] - smaValues[i]);
|
|
9960
|
-
}
|
|
9961
|
-
result[i] = sumDeviation / length;
|
|
9962
|
-
}
|
|
9963
|
-
return result;
|
|
9964
|
-
}
|
|
9965
|
-
function variance(source, length) {
|
|
9966
|
-
const result = new Array(source.length).fill(NaN);
|
|
9967
|
-
for (let i = length - 1; i < source.length; i++) {
|
|
9968
|
-
let sum = 0;
|
|
9969
|
-
let sumSquares = 0;
|
|
9970
|
-
for (let j = 0; j < length; j++) {
|
|
9971
|
-
sum += source[i - j];
|
|
9972
|
-
sumSquares += source[i - j] * source[i - j];
|
|
9973
|
-
}
|
|
9974
|
-
const mean = sum / length;
|
|
9975
|
-
result[i] = sumSquares / length - mean * mean;
|
|
9976
|
-
}
|
|
9977
|
-
return result;
|
|
9978
|
-
}
|
|
9979
|
-
function highest(source, length) {
|
|
9980
|
-
const result = new Array(source.length).fill(NaN);
|
|
9981
|
-
for (let i = length - 1; i < source.length; i++) {
|
|
9982
|
-
let max = -Infinity;
|
|
9983
|
-
for (let j = 0; j < length; j++) {
|
|
9984
|
-
const value = source[i - j];
|
|
9985
|
-
if (isNaN(value)) {
|
|
9986
|
-
max = max === -Infinity ? NaN : max;
|
|
9987
|
-
} else {
|
|
9988
|
-
max = Math.max(max, value);
|
|
9989
|
-
}
|
|
9990
|
-
}
|
|
9991
|
-
result[i] = max;
|
|
9992
|
-
}
|
|
9993
|
-
return result;
|
|
9994
|
-
}
|
|
9995
|
-
function lowest(source, length) {
|
|
9996
|
-
const result = new Array(source.length).fill(NaN);
|
|
9997
|
-
for (let i = length - 1; i < source.length; i++) {
|
|
9998
|
-
let min = Infinity;
|
|
9999
|
-
for (let j = 0; j < length; j++) {
|
|
10000
|
-
const value = source[i - j];
|
|
10001
|
-
if (isNaN(value) || value === void 0) {
|
|
10002
|
-
min = min === Infinity ? NaN : min;
|
|
10003
|
-
} else {
|
|
10004
|
-
min = Math.min(min, value);
|
|
10005
|
-
}
|
|
10006
|
-
}
|
|
10007
|
-
result[i] = min;
|
|
10008
|
-
}
|
|
10009
|
-
return result;
|
|
10010
|
-
}
|
|
10011
|
-
function median(source, length) {
|
|
10012
|
-
const result = new Array(source.length).fill(NaN);
|
|
10013
|
-
for (let i = length - 1; i < source.length; i++) {
|
|
10014
|
-
const window = source.slice(i - length + 1, i + 1);
|
|
10015
|
-
const sorted = window.slice().sort((a, b) => a - b);
|
|
10016
|
-
const mid = Math.floor(length / 2);
|
|
10017
|
-
result[i] = length % 2 === 0 ? (sorted[mid - 1] + sorted[mid]) / 2 : sorted[mid];
|
|
10018
|
-
}
|
|
10019
|
-
return result;
|
|
10020
|
-
}
|
|
10021
|
-
function stdev(source, length, biased = true) {
|
|
10022
|
-
const result = new Array(source.length).fill(NaN);
|
|
10023
|
-
const smaValues = sma(source, length);
|
|
10024
|
-
for (let i = length - 1; i < source.length; i++) {
|
|
10025
|
-
let sum = 0;
|
|
10026
|
-
for (let j = 0; j < length; j++) {
|
|
10027
|
-
sum += Math.pow(source[i - j] - smaValues[i], 2);
|
|
10028
|
-
}
|
|
10029
|
-
const divisor = biased ? length : length - 1;
|
|
10030
|
-
result[i] = Math.sqrt(sum / divisor);
|
|
10031
|
-
}
|
|
10032
|
-
return result;
|
|
10033
|
-
}
|
|
10034
|
-
function linreg(source, length, offset) {
|
|
10035
|
-
const size = source.length;
|
|
10036
|
-
const output = new Array(size).fill(NaN);
|
|
10037
|
-
for (let i = length - 1; i < size; i++) {
|
|
10038
|
-
let sumX = 0;
|
|
10039
|
-
let sumY = 0;
|
|
10040
|
-
let sumXY = 0;
|
|
10041
|
-
let sumXX = 0;
|
|
10042
|
-
const n = length;
|
|
10043
|
-
for (let j = 0; j < length; j++) {
|
|
10044
|
-
const x = j;
|
|
10045
|
-
const y = source[i - length + 1 + j];
|
|
10046
|
-
sumX += x;
|
|
10047
|
-
sumY += y;
|
|
10048
|
-
sumXY += x * y;
|
|
10049
|
-
sumXX += x * x;
|
|
10050
|
-
}
|
|
10051
|
-
const denominator = n * sumXX - sumX * sumX;
|
|
10052
|
-
if (denominator === 0) {
|
|
10053
|
-
output[i] = NaN;
|
|
10054
|
-
continue;
|
|
10055
|
-
}
|
|
10056
|
-
const slope = (n * sumXY - sumX * sumY) / denominator;
|
|
10057
|
-
const intercept = (sumY - slope * sumX) / n;
|
|
10058
|
-
const linRegValue = intercept + slope * (length - 1 - offset);
|
|
10059
|
-
output[i] = linRegValue;
|
|
10060
|
-
}
|
|
10061
|
-
return output;
|
|
10062
|
-
}
|
|
10063
|
-
function calculateSupertrend(high, low, close, factor, atrPeriod) {
|
|
10064
|
-
const length = high.length;
|
|
10065
|
-
const supertrend = new Array(length).fill(NaN);
|
|
10066
|
-
const direction = new Array(length).fill(0);
|
|
10067
|
-
const atrValues = atr(high, low, close, atrPeriod);
|
|
10068
|
-
const upperBand = new Array(length).fill(NaN);
|
|
10069
|
-
const lowerBand = new Array(length).fill(NaN);
|
|
10070
|
-
for (let i = 0; i < length; i++) {
|
|
10071
|
-
const hl2 = (high[i] + low[i]) / 2;
|
|
10072
|
-
const atrValue = atrValues[i];
|
|
10073
|
-
if (!isNaN(atrValue)) {
|
|
10074
|
-
upperBand[i] = hl2 + factor * atrValue;
|
|
10075
|
-
lowerBand[i] = hl2 - factor * atrValue;
|
|
10076
|
-
}
|
|
10077
|
-
}
|
|
10078
|
-
let prevUpperBand = upperBand[atrPeriod];
|
|
10079
|
-
let prevLowerBand = lowerBand[atrPeriod];
|
|
10080
|
-
let prevSupertrend = close[atrPeriod] <= prevUpperBand ? prevUpperBand : prevLowerBand;
|
|
10081
|
-
let prevDirection = close[atrPeriod] <= prevUpperBand ? -1 : 1;
|
|
10082
|
-
supertrend[atrPeriod] = prevSupertrend;
|
|
10083
|
-
direction[atrPeriod] = prevDirection;
|
|
10084
|
-
for (let i = atrPeriod + 1; i < length; i++) {
|
|
10085
|
-
let currentUpperBand = upperBand[i];
|
|
10086
|
-
if (currentUpperBand < prevUpperBand || close[i - 1] > prevUpperBand) {
|
|
10087
|
-
upperBand[i] = currentUpperBand;
|
|
10088
|
-
} else {
|
|
10089
|
-
upperBand[i] = prevUpperBand;
|
|
10090
|
-
}
|
|
10091
|
-
let currentLowerBand = lowerBand[i];
|
|
10092
|
-
if (currentLowerBand > prevLowerBand || close[i - 1] < prevLowerBand) {
|
|
10093
|
-
lowerBand[i] = currentLowerBand;
|
|
10094
|
-
} else {
|
|
10095
|
-
lowerBand[i] = prevLowerBand;
|
|
10096
|
-
}
|
|
10097
|
-
if (prevSupertrend === prevUpperBand) {
|
|
10098
|
-
if (close[i] > upperBand[i]) {
|
|
10099
|
-
direction[i] = 1;
|
|
10100
|
-
supertrend[i] = lowerBand[i];
|
|
10101
|
-
} else {
|
|
10102
|
-
direction[i] = -1;
|
|
10103
|
-
supertrend[i] = upperBand[i];
|
|
10104
|
-
}
|
|
10105
|
-
} else {
|
|
10106
|
-
if (close[i] < lowerBand[i]) {
|
|
10107
|
-
direction[i] = -1;
|
|
10108
|
-
supertrend[i] = upperBand[i];
|
|
10109
|
-
} else {
|
|
10110
|
-
direction[i] = 1;
|
|
10111
|
-
supertrend[i] = lowerBand[i];
|
|
10112
|
-
}
|
|
10113
|
-
}
|
|
10114
|
-
prevUpperBand = upperBand[i];
|
|
10115
|
-
prevLowerBand = lowerBand[i];
|
|
10116
|
-
prevSupertrend = supertrend[i];
|
|
10117
|
-
}
|
|
10118
|
-
return [supertrend, direction];
|
|
10119
|
-
}
|
|
10120
10242
|
function pivothigh(source, leftbars, rightbars) {
|
|
10121
10243
|
const result = new Array(source.length).fill(NaN);
|
|
10122
10244
|
for (let i = leftbars + rightbars; i < source.length; i++) {
|
|
@@ -10384,6 +10506,8 @@
|
|
|
10384
10506
|
ohlc4: []
|
|
10385
10507
|
});
|
|
10386
10508
|
__publicField$1(this, "cache", {});
|
|
10509
|
+
__publicField$1(this, "taState", {});
|
|
10510
|
+
// State for incremental TA calculations
|
|
10387
10511
|
__publicField$1(this, "useTACache", false);
|
|
10388
10512
|
__publicField$1(this, "NA", NaN);
|
|
10389
10513
|
__publicField$1(this, "math");
|
|
@@ -10619,7 +10743,6 @@
|
|
|
10619
10743
|
if (data.length === 0) break;
|
|
10620
10744
|
allData = allData.concat(data);
|
|
10621
10745
|
currentStart = data[data.length - 1].closeTime + 1;
|
|
10622
|
-
if (data.length < 1e3) break;
|
|
10623
10746
|
}
|
|
10624
10747
|
return allData;
|
|
10625
10748
|
} catch (error) {
|
|
@@ -10627,8 +10750,6 @@
|
|
|
10627
10750
|
return [];
|
|
10628
10751
|
}
|
|
10629
10752
|
}
|
|
10630
|
-
//TODO : allow querying more than 1000 klines
|
|
10631
|
-
//TODO : immplement cache
|
|
10632
10753
|
async getMarketData(tickerId, timeframe, limit, sDate, eDate) {
|
|
10633
10754
|
try {
|
|
10634
10755
|
const cacheParams = { tickerId, timeframe, limit, sDate, eDate };
|
|
@@ -10642,12 +10763,16 @@
|
|
|
10642
10763
|
console.error(`Unsupported timeframe: ${timeframe}`);
|
|
10643
10764
|
return [];
|
|
10644
10765
|
}
|
|
10645
|
-
|
|
10646
|
-
if (
|
|
10647
|
-
|
|
10766
|
+
const needsPagination = this.shouldPaginate(timeframe, limit, sDate, eDate);
|
|
10767
|
+
if (needsPagination && sDate && eDate) {
|
|
10768
|
+
const allData = await this.getMarketDataInterval(tickerId, timeframe, sDate, eDate);
|
|
10769
|
+
const result2 = limit ? allData.slice(0, limit) : allData;
|
|
10770
|
+
this.cacheManager.set(cacheParams, result2);
|
|
10771
|
+
return result2;
|
|
10648
10772
|
}
|
|
10773
|
+
let url = `${BINANCE_API_URL}/klines?symbol=${tickerId}&interval=${interval}`;
|
|
10649
10774
|
if (limit) {
|
|
10650
|
-
url += `&limit=${limit}`;
|
|
10775
|
+
url += `&limit=${Math.min(limit, 1e3)}`;
|
|
10651
10776
|
}
|
|
10652
10777
|
if (sDate) {
|
|
10653
10778
|
url += `&startTime=${sDate}`;
|
|
@@ -10683,6 +10808,36 @@
|
|
|
10683
10808
|
return [];
|
|
10684
10809
|
}
|
|
10685
10810
|
}
|
|
10811
|
+
/**
|
|
10812
|
+
* Determines if pagination is needed based on the parameters
|
|
10813
|
+
*/
|
|
10814
|
+
shouldPaginate(timeframe, limit, sDate, eDate) {
|
|
10815
|
+
if (limit && limit > 1e3) {
|
|
10816
|
+
return true;
|
|
10817
|
+
}
|
|
10818
|
+
if (sDate && eDate) {
|
|
10819
|
+
const interval = timeframe_to_binance[timeframe.toUpperCase()];
|
|
10820
|
+
const timeframeDurations = {
|
|
10821
|
+
"1m": 60 * 1e3,
|
|
10822
|
+
"3m": 3 * 60 * 1e3,
|
|
10823
|
+
"5m": 5 * 60 * 1e3,
|
|
10824
|
+
"15m": 15 * 60 * 1e3,
|
|
10825
|
+
"30m": 30 * 60 * 1e3,
|
|
10826
|
+
"1h": 60 * 60 * 1e3,
|
|
10827
|
+
"2h": 2 * 60 * 60 * 1e3,
|
|
10828
|
+
"4h": 4 * 60 * 60 * 1e3,
|
|
10829
|
+
"1d": 24 * 60 * 60 * 1e3,
|
|
10830
|
+
"1w": 7 * 24 * 60 * 60 * 1e3,
|
|
10831
|
+
"1M": 30 * 24 * 60 * 60 * 1e3
|
|
10832
|
+
};
|
|
10833
|
+
const intervalDuration = timeframeDurations[interval];
|
|
10834
|
+
if (intervalDuration) {
|
|
10835
|
+
const requiredCandles = Math.ceil((eDate - sDate) / intervalDuration);
|
|
10836
|
+
return requiredCandles > 1e3;
|
|
10837
|
+
}
|
|
10838
|
+
}
|
|
10839
|
+
return false;
|
|
10840
|
+
}
|
|
10686
10841
|
}
|
|
10687
10842
|
|
|
10688
10843
|
const Provider = {
|