pinets 0.1.1 → 0.1.33
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 +8 -0
- package/dist/pinets.dev.browser.js +10700 -0
- package/dist/pinets.dev.cjs +818 -273
- package/dist/pinets.dev.cjs.map +1 -1
- package/dist/pinets.dev.es.js +872 -172
- 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 +24 -2
- package/dist/types/PineTS.class.d.ts +1 -1
- package/dist/types/marketData/Binance/BinanceProvider.class.d.ts +3 -0
- package/dist/types/namespaces/Core.d.ts +3 -0
- package/dist/types/namespaces/PineArray.d.ts +62 -0
- package/dist/types/namespaces/PineColor.d.ts +0 -0
- package/dist/types/namespaces/PineMath.d.ts +20 -19
- package/dist/types/namespaces/PineRequest.d.ts +2 -1
- package/dist/types/namespaces/TechnicalAnalysis.d.ts +5 -1
- package/dist/types/transpiler/ScopeManager.class.d.ts +2 -0
- package/package.json +2 -2
package/dist/pinets.dev.es.js
CHANGED
|
@@ -1,24 +1,42 @@
|
|
|
1
|
+
|
|
2
|
+
/*
|
|
3
|
+
* Copyright (C) 2025 Alaa-eddine KADDOURI
|
|
4
|
+
*
|
|
5
|
+
* This program is free software: you can redistribute it and/or modify
|
|
6
|
+
* it under the terms of the GNU Affero General Public License as published by
|
|
7
|
+
* the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
* (at your option) any later version.
|
|
9
|
+
*
|
|
10
|
+
* This program is distributed in the hope that it will be useful,
|
|
11
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
* GNU Affero General Public License for more details.
|
|
14
|
+
*
|
|
15
|
+
* You should have received a copy of the GNU Affero General Public License
|
|
16
|
+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
+
*/
|
|
1
18
|
import * as acorn from 'acorn';
|
|
2
19
|
import * as walk from 'acorn-walk';
|
|
3
20
|
import * as astring from 'astring';
|
|
4
21
|
|
|
5
|
-
var __defProp$
|
|
6
|
-
var __defNormalProp$
|
|
7
|
-
var __publicField$
|
|
22
|
+
var __defProp$7 = Object.defineProperty;
|
|
23
|
+
var __defNormalProp$7 = (obj, key, value) => key in obj ? __defProp$7(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
24
|
+
var __publicField$7 = (obj, key, value) => __defNormalProp$7(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
8
25
|
class ScopeManager {
|
|
9
26
|
constructor() {
|
|
10
|
-
__publicField$
|
|
11
|
-
__publicField$
|
|
12
|
-
__publicField$
|
|
13
|
-
__publicField$
|
|
14
|
-
__publicField$
|
|
15
|
-
__publicField$
|
|
16
|
-
__publicField$
|
|
17
|
-
__publicField$
|
|
18
|
-
__publicField$
|
|
27
|
+
__publicField$7(this, "scopes", []);
|
|
28
|
+
__publicField$7(this, "scopeTypes", []);
|
|
29
|
+
__publicField$7(this, "scopeCounts", /* @__PURE__ */ new Map());
|
|
30
|
+
__publicField$7(this, "contextBoundVars", /* @__PURE__ */ new Set());
|
|
31
|
+
__publicField$7(this, "arrayPatternElements", /* @__PURE__ */ new Set());
|
|
32
|
+
__publicField$7(this, "rootParams", /* @__PURE__ */ new Set());
|
|
33
|
+
__publicField$7(this, "varKinds", /* @__PURE__ */ new Map());
|
|
34
|
+
__publicField$7(this, "loopVars", /* @__PURE__ */ new Set());
|
|
35
|
+
__publicField$7(this, "loopVarNames", /* @__PURE__ */ new Map());
|
|
19
36
|
// Map original names to transformed names
|
|
20
|
-
__publicField$
|
|
21
|
-
__publicField$
|
|
37
|
+
__publicField$7(this, "paramIdCounter", 0);
|
|
38
|
+
__publicField$7(this, "cacheIdCounter", 0);
|
|
39
|
+
__publicField$7(this, "tempVarCounter", 0);
|
|
22
40
|
this.pushScope("glb");
|
|
23
41
|
}
|
|
24
42
|
get nextParamIdArg() {
|
|
@@ -27,6 +45,12 @@ class ScopeManager {
|
|
|
27
45
|
name: `'p${this.paramIdCounter++}'`
|
|
28
46
|
};
|
|
29
47
|
}
|
|
48
|
+
get nextCacheIdArg() {
|
|
49
|
+
return {
|
|
50
|
+
type: "Identifier",
|
|
51
|
+
name: `'cache_${this.cacheIdCounter++}'`
|
|
52
|
+
};
|
|
53
|
+
}
|
|
30
54
|
pushScope(type) {
|
|
31
55
|
this.scopes.push(/* @__PURE__ */ new Map());
|
|
32
56
|
this.scopeTypes.push(type);
|
|
@@ -204,6 +228,9 @@ function transformMemberExpression(memberNode, originalParamName, scopeManager)
|
|
|
204
228
|
}
|
|
205
229
|
function transformVariableDeclaration(varNode, scopeManager) {
|
|
206
230
|
varNode.declarations.forEach((decl) => {
|
|
231
|
+
if (decl.init.name == "na") {
|
|
232
|
+
decl.init.name = "NaN";
|
|
233
|
+
}
|
|
207
234
|
const isContextProperty = decl.init && decl.init.type === "MemberExpression" && decl.init.object && (decl.init.object.name === "context" || decl.init.object.name === CONTEXT_NAME || decl.init.object.name === "context2");
|
|
208
235
|
const isSubContextProperty = decl.init && decl.init.type === "MemberExpression" && decl.init.object?.object && (decl.init.object.object.name === "context" || decl.init.object.object.name === CONTEXT_NAME || decl.init.object.object.name === "context2");
|
|
209
236
|
const isArrowFunction = decl.init && decl.init.type === "ArrowFunctionExpression";
|
|
@@ -366,7 +393,28 @@ function transformVariableDeclaration(varNode, scopeManager) {
|
|
|
366
393
|
}
|
|
367
394
|
};
|
|
368
395
|
if (isArrayPatternVar) {
|
|
369
|
-
assignmentExpr.expression.right.object.property.name +=
|
|
396
|
+
assignmentExpr.expression.right.object.property.name += `?.[0][${decl.init.property.value}]`;
|
|
397
|
+
const obj = assignmentExpr.expression.right.object;
|
|
398
|
+
assignmentExpr.expression.right = {
|
|
399
|
+
type: "CallExpression",
|
|
400
|
+
callee: {
|
|
401
|
+
type: "MemberExpression",
|
|
402
|
+
object: {
|
|
403
|
+
type: "Identifier",
|
|
404
|
+
name: CONTEXT_NAME
|
|
405
|
+
},
|
|
406
|
+
property: {
|
|
407
|
+
type: "Identifier",
|
|
408
|
+
name: "init"
|
|
409
|
+
},
|
|
410
|
+
computed: false
|
|
411
|
+
},
|
|
412
|
+
arguments: [
|
|
413
|
+
targetVarRef,
|
|
414
|
+
obj
|
|
415
|
+
/*, decl.init.property.value*/
|
|
416
|
+
]
|
|
417
|
+
};
|
|
370
418
|
}
|
|
371
419
|
if (isArrowFunction) {
|
|
372
420
|
scopeManager.pushScope("fn");
|
|
@@ -519,14 +567,22 @@ function transformAssignmentExpression(node, scopeManager) {
|
|
|
519
567
|
{ parent: node.right, inNamespaceCall: false },
|
|
520
568
|
{
|
|
521
569
|
Identifier(node2, state, c) {
|
|
570
|
+
if (node2.name == "na") {
|
|
571
|
+
node2.name = "NaN";
|
|
572
|
+
}
|
|
522
573
|
node2.parent = state.parent;
|
|
523
574
|
transformIdentifier(node2, scopeManager);
|
|
524
575
|
const isBinaryOperation = node2.parent && node2.parent.type === "BinaryExpression";
|
|
525
576
|
const isConditional = node2.parent && node2.parent.type === "ConditionalExpression";
|
|
526
|
-
|
|
577
|
+
const isContextBound = scopeManager.isContextBound(node2.name) && !scopeManager.isRootParam(node2.name);
|
|
578
|
+
const hasArrayAccess = node2.parent && node2.parent.type === "MemberExpression" && node2.parent.computed && node2.parent.object === node2;
|
|
579
|
+
const isParamCall = node2.parent && node2.parent._isParamCall;
|
|
580
|
+
const isMemberExpression = node2.parent && node2.parent.type === "MemberExpression";
|
|
581
|
+
const isReserved = node2.name === "NaN";
|
|
582
|
+
if (isContextBound || isConditional || isBinaryOperation) {
|
|
527
583
|
if (node2.type === "MemberExpression") {
|
|
528
584
|
transformArrayIndex(node2, scopeManager);
|
|
529
|
-
} else if (node2.type === "Identifier") {
|
|
585
|
+
} else if (node2.type === "Identifier" && !isMemberExpression && !hasArrayAccess && !isParamCall && !isReserved) {
|
|
530
586
|
addArrayAccess(node2);
|
|
531
587
|
}
|
|
532
588
|
}
|
|
@@ -708,6 +764,10 @@ function transformReturnStatement(node, scopeManager) {
|
|
|
708
764
|
}
|
|
709
765
|
function transformIdentifierForParam(node, scopeManager) {
|
|
710
766
|
if (node.type === "Identifier") {
|
|
767
|
+
if (node.name === "na") {
|
|
768
|
+
node.name = "NaN";
|
|
769
|
+
return node;
|
|
770
|
+
}
|
|
711
771
|
if (scopeManager.isLoopVariable(node.name)) {
|
|
712
772
|
return node;
|
|
713
773
|
}
|
|
@@ -761,40 +821,58 @@ function transformIdentifierForParam(node, scopeManager) {
|
|
|
761
821
|
}
|
|
762
822
|
return node;
|
|
763
823
|
}
|
|
824
|
+
function getParamFromUnaryExpression(node, scopeManager, namespace) {
|
|
825
|
+
const transformedArgument = transformOperand(node.argument, scopeManager, namespace);
|
|
826
|
+
const unaryExpr = {
|
|
827
|
+
type: "UnaryExpression",
|
|
828
|
+
operator: node.operator,
|
|
829
|
+
prefix: node.prefix,
|
|
830
|
+
argument: transformedArgument,
|
|
831
|
+
start: node.start,
|
|
832
|
+
end: node.end
|
|
833
|
+
};
|
|
834
|
+
return unaryExpr;
|
|
835
|
+
}
|
|
764
836
|
function transformOperand(node, scopeManager, namespace = "") {
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
if (node.type === "MemberExpression") {
|
|
769
|
-
const transformedObject = node.object.type === "Identifier" ? transformIdentifierForParam(node.object, scopeManager) : node.object;
|
|
770
|
-
return {
|
|
771
|
-
type: "MemberExpression",
|
|
772
|
-
object: transformedObject,
|
|
773
|
-
property: node.property,
|
|
774
|
-
computed: node.computed
|
|
775
|
-
};
|
|
776
|
-
} else if (node.type === "Identifier") {
|
|
777
|
-
if (scopeManager.isLoopVariable(node.name)) {
|
|
778
|
-
return node;
|
|
837
|
+
switch (node.type) {
|
|
838
|
+
case "BinaryExpression": {
|
|
839
|
+
return getParamFromBinaryExpression(node, scopeManager, namespace);
|
|
779
840
|
}
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
return
|
|
841
|
+
case "MemberExpression": {
|
|
842
|
+
const transformedObject = node.object.type === "Identifier" ? transformIdentifierForParam(node.object, scopeManager) : node.object;
|
|
843
|
+
return {
|
|
844
|
+
type: "MemberExpression",
|
|
845
|
+
object: transformedObject,
|
|
846
|
+
property: node.property,
|
|
847
|
+
computed: node.computed
|
|
848
|
+
};
|
|
849
|
+
}
|
|
850
|
+
case "Identifier": {
|
|
851
|
+
if (scopeManager.isLoopVariable(node.name)) {
|
|
852
|
+
return node;
|
|
853
|
+
}
|
|
854
|
+
const isMemberExprProperty = node.parent && node.parent.type === "MemberExpression" && node.parent.property === node;
|
|
855
|
+
if (isMemberExprProperty) {
|
|
856
|
+
return node;
|
|
857
|
+
}
|
|
858
|
+
const transformedObject = transformIdentifierForParam(node, scopeManager);
|
|
859
|
+
return {
|
|
860
|
+
type: "MemberExpression",
|
|
861
|
+
object: transformedObject,
|
|
862
|
+
property: {
|
|
863
|
+
type: "Literal",
|
|
864
|
+
value: 0
|
|
865
|
+
},
|
|
866
|
+
computed: true
|
|
867
|
+
};
|
|
868
|
+
}
|
|
869
|
+
case "UnaryExpression": {
|
|
870
|
+
return getParamFromUnaryExpression(node, scopeManager, namespace);
|
|
783
871
|
}
|
|
784
|
-
const transformedObject = transformIdentifierForParam(node, scopeManager);
|
|
785
|
-
return {
|
|
786
|
-
type: "MemberExpression",
|
|
787
|
-
object: transformedObject,
|
|
788
|
-
property: {
|
|
789
|
-
type: "Literal",
|
|
790
|
-
value: 0
|
|
791
|
-
},
|
|
792
|
-
computed: true
|
|
793
|
-
};
|
|
794
872
|
}
|
|
795
873
|
return node;
|
|
796
874
|
}
|
|
797
|
-
function
|
|
875
|
+
function getParamFromBinaryExpression(node, scopeManager, namespace) {
|
|
798
876
|
const transformedLeft = transformOperand(node.left, scopeManager, namespace);
|
|
799
877
|
const transformedRight = transformOperand(node.right, scopeManager, namespace);
|
|
800
878
|
const binaryExpr = {
|
|
@@ -815,28 +893,89 @@ function transformBinaryExpression(node, scopeManager, namespace) {
|
|
|
815
893
|
transformMemberExpression(node2, "", scopeManager);
|
|
816
894
|
}
|
|
817
895
|
});
|
|
896
|
+
return binaryExpr;
|
|
897
|
+
}
|
|
898
|
+
function getParamFromLogicalExpression(node, scopeManager, namespace) {
|
|
899
|
+
const transformedLeft = transformOperand(node.left, scopeManager, namespace);
|
|
900
|
+
const transformedRight = transformOperand(node.right, scopeManager, namespace);
|
|
901
|
+
const logicalExpr = {
|
|
902
|
+
type: "LogicalExpression",
|
|
903
|
+
operator: node.operator,
|
|
904
|
+
left: transformedLeft,
|
|
905
|
+
right: transformedRight,
|
|
906
|
+
start: node.start,
|
|
907
|
+
end: node.end
|
|
908
|
+
};
|
|
909
|
+
walk.recursive(logicalExpr, scopeManager, {
|
|
910
|
+
CallExpression(node2, scopeManager2) {
|
|
911
|
+
if (!node2._transformed) {
|
|
912
|
+
transformCallExpression(node2, scopeManager2);
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
});
|
|
916
|
+
return logicalExpr;
|
|
917
|
+
}
|
|
918
|
+
function getParamFromConditionalExpression(node, scopeManager, namespace) {
|
|
919
|
+
walk.recursive(
|
|
920
|
+
node,
|
|
921
|
+
{ parent: node, inNamespaceCall: false },
|
|
922
|
+
{
|
|
923
|
+
Identifier(node2, state, c) {
|
|
924
|
+
if (node2.name == "NaN") return;
|
|
925
|
+
if (node2.name == "na") {
|
|
926
|
+
node2.name = "NaN";
|
|
927
|
+
return;
|
|
928
|
+
}
|
|
929
|
+
node2.parent = state.parent;
|
|
930
|
+
transformIdentifier(node2, scopeManager);
|
|
931
|
+
const isBinaryOperation = node2.parent && node2.parent.type === "BinaryExpression";
|
|
932
|
+
const isConditional = node2.parent && node2.parent.type === "ConditionalExpression";
|
|
933
|
+
if (isConditional || isBinaryOperation) {
|
|
934
|
+
if (node2.type === "MemberExpression") {
|
|
935
|
+
transformArrayIndex(node2, scopeManager);
|
|
936
|
+
} else if (node2.type === "Identifier") {
|
|
937
|
+
addArrayAccess(node2);
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
},
|
|
941
|
+
MemberExpression(node2, state, c) {
|
|
942
|
+
transformArrayIndex(node2, scopeManager);
|
|
943
|
+
if (node2.object) {
|
|
944
|
+
c(node2.object, { parent: node2, inNamespaceCall: state.inNamespaceCall });
|
|
945
|
+
}
|
|
946
|
+
},
|
|
947
|
+
CallExpression(node2, state, c) {
|
|
948
|
+
const isNamespaceCall = node2.callee && node2.callee.type === "MemberExpression" && node2.callee.object && node2.callee.object.type === "Identifier" && scopeManager.isContextBound(node2.callee.object.name);
|
|
949
|
+
transformCallExpression(node2, scopeManager);
|
|
950
|
+
node2.arguments.forEach((arg) => c(arg, { parent: node2, inNamespaceCall: isNamespaceCall || state.inNamespaceCall }));
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
);
|
|
818
954
|
return {
|
|
819
955
|
type: "CallExpression",
|
|
820
956
|
callee: {
|
|
821
957
|
type: "MemberExpression",
|
|
822
|
-
object: {
|
|
823
|
-
|
|
824
|
-
name: namespace
|
|
825
|
-
},
|
|
826
|
-
property: {
|
|
827
|
-
type: "Identifier",
|
|
828
|
-
name: "param"
|
|
829
|
-
},
|
|
830
|
-
computed: false
|
|
958
|
+
object: { type: "Identifier", name: namespace },
|
|
959
|
+
property: { type: "Identifier", name: "param" }
|
|
831
960
|
},
|
|
832
|
-
arguments: [
|
|
961
|
+
arguments: [node, UNDEFINED_ARG, scopeManager.nextParamIdArg],
|
|
833
962
|
_transformed: true,
|
|
834
963
|
_isParamCall: true
|
|
835
964
|
};
|
|
836
965
|
}
|
|
837
966
|
function transformFunctionArgument(arg, namespace, scopeManager) {
|
|
838
|
-
|
|
839
|
-
|
|
967
|
+
switch (arg?.type) {
|
|
968
|
+
case "BinaryExpression":
|
|
969
|
+
arg = getParamFromBinaryExpression(arg, scopeManager, namespace);
|
|
970
|
+
break;
|
|
971
|
+
case "LogicalExpression":
|
|
972
|
+
arg = getParamFromLogicalExpression(arg, scopeManager, namespace);
|
|
973
|
+
break;
|
|
974
|
+
case "ConditionalExpression":
|
|
975
|
+
return getParamFromConditionalExpression(arg, scopeManager, namespace);
|
|
976
|
+
case "UnaryExpression":
|
|
977
|
+
arg = getParamFromUnaryExpression(arg, scopeManager, namespace);
|
|
978
|
+
break;
|
|
840
979
|
}
|
|
841
980
|
const isArrayAccess = arg.type === "MemberExpression" && arg.computed && arg.property;
|
|
842
981
|
if (isArrayAccess) {
|
|
@@ -901,6 +1040,10 @@ function transformFunctionArgument(arg, namespace, scopeManager) {
|
|
|
901
1040
|
});
|
|
902
1041
|
}
|
|
903
1042
|
if (arg.type === "Identifier") {
|
|
1043
|
+
if (arg.name === "na") {
|
|
1044
|
+
arg.name = "NaN";
|
|
1045
|
+
return arg;
|
|
1046
|
+
}
|
|
904
1047
|
if (scopeManager.isContextBound(arg.name) && !scopeManager.isRootParam(arg.name)) {
|
|
905
1048
|
return {
|
|
906
1049
|
type: "CallExpression",
|
|
@@ -944,18 +1087,18 @@ function transformFunctionArgument(arg, namespace, scopeManager) {
|
|
|
944
1087
|
_isParamCall: true
|
|
945
1088
|
};
|
|
946
1089
|
}
|
|
947
|
-
function transformCallExpression(node, scopeManager) {
|
|
1090
|
+
function transformCallExpression(node, scopeManager, namespace) {
|
|
948
1091
|
if (node._transformed) {
|
|
949
1092
|
return;
|
|
950
1093
|
}
|
|
951
1094
|
const isNamespaceCall = node.callee && node.callee.type === "MemberExpression" && node.callee.object && node.callee.object.type === "Identifier" && (scopeManager.isContextBound(node.callee.object.name) || node.callee.object.name === "math" || node.callee.object.name === "ta");
|
|
952
1095
|
if (isNamespaceCall) {
|
|
953
|
-
const
|
|
1096
|
+
const namespace2 = node.callee.object.name;
|
|
954
1097
|
node.arguments = node.arguments.map((arg) => {
|
|
955
1098
|
if (arg._isParamCall) {
|
|
956
1099
|
return arg;
|
|
957
1100
|
}
|
|
958
|
-
return transformFunctionArgument(arg,
|
|
1101
|
+
return transformFunctionArgument(arg, namespace2, scopeManager);
|
|
959
1102
|
});
|
|
960
1103
|
node._transformed = true;
|
|
961
1104
|
} else if (node.callee && node.callee.type === "Identifier") {
|
|
@@ -969,13 +1112,29 @@ function transformCallExpression(node, scopeManager) {
|
|
|
969
1112
|
}
|
|
970
1113
|
node.arguments.forEach((arg) => {
|
|
971
1114
|
walk.recursive(arg, scopeManager, {
|
|
972
|
-
|
|
1115
|
+
Identifier(node2, state, c) {
|
|
1116
|
+
node2.parent = state.parent;
|
|
1117
|
+
transformIdentifier(node2, scopeManager);
|
|
1118
|
+
const isBinaryOperation = node2.parent && node2.parent.type === "BinaryExpression";
|
|
1119
|
+
const isConditional = node2.parent && node2.parent.type === "ConditionalExpression";
|
|
1120
|
+
if (isConditional || isBinaryOperation) {
|
|
1121
|
+
if (node2.type === "MemberExpression") {
|
|
1122
|
+
transformArrayIndex(node2, scopeManager);
|
|
1123
|
+
} else if (node2.type === "Identifier") {
|
|
1124
|
+
addArrayAccess(node2);
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
},
|
|
1128
|
+
CallExpression(node2, state, c) {
|
|
973
1129
|
if (!node2._transformed) {
|
|
974
1130
|
transformCallExpression(node2, state);
|
|
975
1131
|
}
|
|
976
1132
|
},
|
|
977
|
-
MemberExpression(node2) {
|
|
1133
|
+
MemberExpression(node2, state, c) {
|
|
978
1134
|
transformMemberExpression(node2, "", scopeManager);
|
|
1135
|
+
if (node2.object) {
|
|
1136
|
+
c(node2.object, { parent: node2, inNamespaceCall: state.inNamespaceCall });
|
|
1137
|
+
}
|
|
979
1138
|
}
|
|
980
1139
|
});
|
|
981
1140
|
});
|
|
@@ -1336,9 +1495,9 @@ function transpile(fn) {
|
|
|
1336
1495
|
return _wraperFunction(this);
|
|
1337
1496
|
}
|
|
1338
1497
|
|
|
1339
|
-
var __defProp$
|
|
1340
|
-
var __defNormalProp$
|
|
1341
|
-
var __publicField$
|
|
1498
|
+
var __defProp$6 = Object.defineProperty;
|
|
1499
|
+
var __defNormalProp$6 = (obj, key, value) => key in obj ? __defProp$6(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
1500
|
+
var __publicField$6 = (obj, key, value) => __defNormalProp$6(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
1342
1501
|
class PineTS {
|
|
1343
1502
|
constructor(source, tickerId, timeframe, limit, sDate, eDate) {
|
|
1344
1503
|
this.source = source;
|
|
@@ -1347,25 +1506,25 @@ class PineTS {
|
|
|
1347
1506
|
this.limit = limit;
|
|
1348
1507
|
this.sDate = sDate;
|
|
1349
1508
|
this.eDate = eDate;
|
|
1350
|
-
__publicField$
|
|
1509
|
+
__publicField$6(this, "data", []);
|
|
1351
1510
|
//#region [Pine Script built-in variables]
|
|
1352
|
-
__publicField$
|
|
1353
|
-
__publicField$
|
|
1354
|
-
__publicField$
|
|
1355
|
-
__publicField$
|
|
1356
|
-
__publicField$
|
|
1357
|
-
__publicField$
|
|
1358
|
-
__publicField$
|
|
1359
|
-
__publicField$
|
|
1360
|
-
__publicField$
|
|
1361
|
-
__publicField$
|
|
1511
|
+
__publicField$6(this, "open", []);
|
|
1512
|
+
__publicField$6(this, "high", []);
|
|
1513
|
+
__publicField$6(this, "low", []);
|
|
1514
|
+
__publicField$6(this, "close", []);
|
|
1515
|
+
__publicField$6(this, "volume", []);
|
|
1516
|
+
__publicField$6(this, "hl2", []);
|
|
1517
|
+
__publicField$6(this, "hlc3", []);
|
|
1518
|
+
__publicField$6(this, "ohlc4", []);
|
|
1519
|
+
__publicField$6(this, "openTime", []);
|
|
1520
|
+
__publicField$6(this, "closeTime", []);
|
|
1362
1521
|
//#endregion
|
|
1363
1522
|
//#region run context
|
|
1364
|
-
__publicField$
|
|
1523
|
+
__publicField$6(this, "_periods");
|
|
1365
1524
|
//#endregion
|
|
1366
1525
|
//public fn: Function;
|
|
1367
|
-
__publicField$
|
|
1368
|
-
__publicField$
|
|
1526
|
+
__publicField$6(this, "_readyPromise", null);
|
|
1527
|
+
__publicField$6(this, "_ready", false);
|
|
1369
1528
|
this._readyPromise = new Promise((resolve) => {
|
|
1370
1529
|
this.loadMarketData(source, tickerId, timeframe, limit, sDate, eDate).then((data) => {
|
|
1371
1530
|
const marketData = data.reverse();
|
|
@@ -1411,13 +1570,22 @@ class PineTS {
|
|
|
1411
1570
|
if (!this._readyPromise) throw new Error("PineTS is not ready");
|
|
1412
1571
|
return this._readyPromise;
|
|
1413
1572
|
}
|
|
1414
|
-
async run(
|
|
1573
|
+
async run(pineTSCode, n, useTACache) {
|
|
1415
1574
|
await this.ready();
|
|
1416
1575
|
if (!n) n = this._periods;
|
|
1417
|
-
const context = new Context(
|
|
1418
|
-
|
|
1576
|
+
const context = new Context({
|
|
1577
|
+
marketData: this.data,
|
|
1578
|
+
source: this.source,
|
|
1579
|
+
tickerId: this.tickerId,
|
|
1580
|
+
timeframe: this.timeframe,
|
|
1581
|
+
limit: this.limit,
|
|
1582
|
+
sDate: this.sDate,
|
|
1583
|
+
eDate: this.eDate
|
|
1584
|
+
});
|
|
1585
|
+
context.pineTSCode = pineTSCode;
|
|
1586
|
+
context.useTACache = useTACache;
|
|
1419
1587
|
const transformer = transpile.bind(this);
|
|
1420
|
-
let
|
|
1588
|
+
let transpiledFn = transformer(pineTSCode);
|
|
1421
1589
|
const contextVarNames = ["const", "var", "let", "params"];
|
|
1422
1590
|
for (let i = this._periods - n, idx = n - 1; i < this._periods; i++, idx--) {
|
|
1423
1591
|
context.idx = i;
|
|
@@ -1431,7 +1599,7 @@ class PineTS {
|
|
|
1431
1599
|
context.data.ohlc4 = this.ohlc4.slice(idx);
|
|
1432
1600
|
context.data.openTime = this.openTime.slice(idx);
|
|
1433
1601
|
context.data.closeTime = this.closeTime.slice(idx);
|
|
1434
|
-
const result = await
|
|
1602
|
+
const result = await transpiledFn(context);
|
|
1435
1603
|
if (typeof result === "object") {
|
|
1436
1604
|
if (typeof context.result !== "object") {
|
|
1437
1605
|
context.result = {};
|
|
@@ -1462,13 +1630,30 @@ class PineTS {
|
|
|
1462
1630
|
}
|
|
1463
1631
|
}
|
|
1464
1632
|
|
|
1465
|
-
var __defProp$
|
|
1466
|
-
var __defNormalProp$
|
|
1467
|
-
var __publicField$
|
|
1633
|
+
var __defProp$5 = Object.defineProperty;
|
|
1634
|
+
var __defNormalProp$5 = (obj, key, value) => key in obj ? __defProp$5(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
1635
|
+
var __publicField$5 = (obj, key, value) => __defNormalProp$5(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
1468
1636
|
class Core {
|
|
1469
1637
|
constructor(context) {
|
|
1470
1638
|
this.context = context;
|
|
1471
|
-
__publicField$
|
|
1639
|
+
__publicField$5(this, "color", {
|
|
1640
|
+
param: (source, index = 0) => {
|
|
1641
|
+
if (Array.isArray(source)) {
|
|
1642
|
+
return source[index];
|
|
1643
|
+
}
|
|
1644
|
+
return source;
|
|
1645
|
+
},
|
|
1646
|
+
rgb: (r, g, b, a) => a ? `rgba(${r}, ${g}, ${b}, ${a})` : `rgb(${r}, ${g}, ${b})`,
|
|
1647
|
+
new: (color, a) => {
|
|
1648
|
+
if (color && color.startsWith("#")) {
|
|
1649
|
+
const hex = color.slice(1);
|
|
1650
|
+
const r = parseInt(hex.slice(0, 2), 16);
|
|
1651
|
+
const g = parseInt(hex.slice(2, 4), 16);
|
|
1652
|
+
const b = parseInt(hex.slice(4, 6), 16);
|
|
1653
|
+
return a ? `rgba(${r}, ${g}, ${b}, ${a})` : `rgb(${r}, ${g}, ${b})`;
|
|
1654
|
+
}
|
|
1655
|
+
return a ? `rgba(${color}, ${a})` : color;
|
|
1656
|
+
},
|
|
1472
1657
|
white: "white",
|
|
1473
1658
|
lime: "lime",
|
|
1474
1659
|
green: "green",
|
|
@@ -1500,7 +1685,7 @@ class Core {
|
|
|
1500
1685
|
this.context.plots[title].data.push({
|
|
1501
1686
|
time: this.context.marketData[this.context.marketData.length - this.context.idx - 1].openTime,
|
|
1502
1687
|
value: series[0],
|
|
1503
|
-
options: this.extractPlotOptions(options)
|
|
1688
|
+
options: { ...this.extractPlotOptions(options), style: "char" }
|
|
1504
1689
|
});
|
|
1505
1690
|
}
|
|
1506
1691
|
plot(series, title, options) {
|
|
@@ -1517,11 +1702,9 @@ class Core {
|
|
|
1517
1702
|
return Array.isArray(series) ? isNaN(series[0]) : isNaN(series);
|
|
1518
1703
|
}
|
|
1519
1704
|
nz(series, replacement = 0) {
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
return isNaN(series) ? replacement : series;
|
|
1524
|
-
}
|
|
1705
|
+
const val = Array.isArray(series) ? series[0] : series;
|
|
1706
|
+
const rep = Array.isArray(series) ? replacement[0] : replacement;
|
|
1707
|
+
return isNaN(val) ? rep : val;
|
|
1525
1708
|
}
|
|
1526
1709
|
}
|
|
1527
1710
|
|
|
@@ -1579,75 +1762,95 @@ class Input {
|
|
|
1579
1762
|
}
|
|
1580
1763
|
}
|
|
1581
1764
|
|
|
1582
|
-
var __defProp$
|
|
1583
|
-
var __defNormalProp$
|
|
1584
|
-
var __publicField$
|
|
1765
|
+
var __defProp$4 = Object.defineProperty;
|
|
1766
|
+
var __defNormalProp$4 = (obj, key, value) => key in obj ? __defProp$4(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
1767
|
+
var __publicField$4 = (obj, key, value) => __defNormalProp$4(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
1585
1768
|
class PineMath {
|
|
1586
1769
|
constructor(context) {
|
|
1587
1770
|
this.context = context;
|
|
1588
|
-
__publicField$
|
|
1771
|
+
__publicField$4(this, "_cache", {});
|
|
1589
1772
|
}
|
|
1590
|
-
param(source, index
|
|
1773
|
+
param(source, index, name) {
|
|
1774
|
+
if (!this.context.params[name]) this.context.params[name] = [];
|
|
1591
1775
|
if (Array.isArray(source)) {
|
|
1592
|
-
|
|
1776
|
+
if (index) {
|
|
1777
|
+
this.context.params[name] = source.slice(index);
|
|
1778
|
+
this.context.params[name].length = source.length;
|
|
1779
|
+
return this.context.params[name];
|
|
1780
|
+
}
|
|
1781
|
+
this.context.params[name] = source.slice(0);
|
|
1782
|
+
return this.context.params[name];
|
|
1783
|
+
} else {
|
|
1784
|
+
this.context.params[name][0] = source;
|
|
1785
|
+
return this.context.params[name];
|
|
1593
1786
|
}
|
|
1594
|
-
return source;
|
|
1595
1787
|
}
|
|
1596
|
-
abs(
|
|
1597
|
-
return Math.abs(
|
|
1788
|
+
abs(source) {
|
|
1789
|
+
return Math.abs(source[0]);
|
|
1598
1790
|
}
|
|
1599
|
-
pow(
|
|
1600
|
-
return Math.pow(
|
|
1791
|
+
pow(source, power) {
|
|
1792
|
+
return Math.pow(source[0], power[0]);
|
|
1601
1793
|
}
|
|
1602
|
-
sqrt(
|
|
1603
|
-
return Math.sqrt(
|
|
1794
|
+
sqrt(source) {
|
|
1795
|
+
return Math.sqrt(source[0]);
|
|
1604
1796
|
}
|
|
1605
|
-
log(
|
|
1606
|
-
return Math.log(
|
|
1797
|
+
log(source) {
|
|
1798
|
+
return Math.log(source[0]);
|
|
1607
1799
|
}
|
|
1608
|
-
ln(
|
|
1609
|
-
return Math.log(
|
|
1800
|
+
ln(source) {
|
|
1801
|
+
return Math.log(source[0]);
|
|
1610
1802
|
}
|
|
1611
|
-
exp(
|
|
1612
|
-
return Math.exp(
|
|
1803
|
+
exp(source) {
|
|
1804
|
+
return Math.exp(source[0]);
|
|
1613
1805
|
}
|
|
1614
|
-
floor(
|
|
1615
|
-
return Math.floor(
|
|
1806
|
+
floor(source) {
|
|
1807
|
+
return Math.floor(source[0]);
|
|
1616
1808
|
}
|
|
1617
|
-
ceil(
|
|
1618
|
-
return Math.ceil(
|
|
1809
|
+
ceil(source) {
|
|
1810
|
+
return Math.ceil(source[0]);
|
|
1619
1811
|
}
|
|
1620
|
-
round(
|
|
1621
|
-
return Math.round(
|
|
1812
|
+
round(source) {
|
|
1813
|
+
return Math.round(source[0]);
|
|
1622
1814
|
}
|
|
1623
1815
|
random() {
|
|
1624
1816
|
return Math.random();
|
|
1625
1817
|
}
|
|
1626
|
-
max(...
|
|
1627
|
-
|
|
1818
|
+
max(...source) {
|
|
1819
|
+
const arg = source.map((e) => Array.isArray(e) ? e[0] : e);
|
|
1820
|
+
return Math.max(...arg);
|
|
1628
1821
|
}
|
|
1629
|
-
min(...
|
|
1630
|
-
|
|
1822
|
+
min(...source) {
|
|
1823
|
+
const arg = source.map((e) => Array.isArray(e) ? e[0] : e);
|
|
1824
|
+
return Math.min(...arg);
|
|
1631
1825
|
}
|
|
1632
|
-
|
|
1633
|
-
|
|
1826
|
+
//sum of last n values
|
|
1827
|
+
sum(source, length) {
|
|
1828
|
+
const len = Array.isArray(length) ? length[0] : length;
|
|
1829
|
+
if (Array.isArray(source)) {
|
|
1830
|
+
return source.slice(0, len).reduce((a, b) => a + b, 0);
|
|
1831
|
+
}
|
|
1832
|
+
return source;
|
|
1833
|
+
}
|
|
1834
|
+
sin(source) {
|
|
1835
|
+
return Math.sin(source[0]);
|
|
1634
1836
|
}
|
|
1635
|
-
cos(
|
|
1636
|
-
return Math.cos(
|
|
1837
|
+
cos(source) {
|
|
1838
|
+
return Math.cos(source[0]);
|
|
1637
1839
|
}
|
|
1638
|
-
tan(
|
|
1639
|
-
return Math.tan(
|
|
1840
|
+
tan(source) {
|
|
1841
|
+
return Math.tan(source[0]);
|
|
1640
1842
|
}
|
|
1641
|
-
|
|
1642
|
-
return Math.
|
|
1843
|
+
acos(source) {
|
|
1844
|
+
return Math.acos(source[0]);
|
|
1643
1845
|
}
|
|
1644
|
-
|
|
1645
|
-
return Math.
|
|
1846
|
+
asin(source) {
|
|
1847
|
+
return Math.asin(source[0]);
|
|
1646
1848
|
}
|
|
1647
|
-
atan(
|
|
1648
|
-
return Math.atan(
|
|
1849
|
+
atan(source) {
|
|
1850
|
+
return Math.atan(source[0]);
|
|
1649
1851
|
}
|
|
1650
|
-
avg(...
|
|
1852
|
+
avg(...sources) {
|
|
1853
|
+
const args = sources.map((e) => Array.isArray(e) ? e[0] : e);
|
|
1651
1854
|
return args.reduce((a, b) => {
|
|
1652
1855
|
const aVal = Array.isArray(a) ? a[0] : a;
|
|
1653
1856
|
const bVal = Array.isArray(b) ? b[0] : b;
|
|
@@ -1656,13 +1859,14 @@ class PineMath {
|
|
|
1656
1859
|
}
|
|
1657
1860
|
}
|
|
1658
1861
|
|
|
1659
|
-
var __defProp$
|
|
1660
|
-
var __defNormalProp$
|
|
1661
|
-
var __publicField$
|
|
1862
|
+
var __defProp$3 = Object.defineProperty;
|
|
1863
|
+
var __defNormalProp$3 = (obj, key, value) => key in obj ? __defProp$3(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
1864
|
+
var __publicField$3 = (obj, key, value) => __defNormalProp$3(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
1865
|
+
const TIMEFRAMES = ["1", "3", "5", "15", "30", "45", "60", "120", "180", "240", "D", "W", "M"];
|
|
1662
1866
|
class PineRequest {
|
|
1663
1867
|
constructor(context) {
|
|
1664
1868
|
this.context = context;
|
|
1665
|
-
__publicField$
|
|
1869
|
+
__publicField$3(this, "_cache", {});
|
|
1666
1870
|
}
|
|
1667
1871
|
param(source, index, name) {
|
|
1668
1872
|
if (!this.context.params[name]) this.context.params[name] = [];
|
|
@@ -1678,8 +1882,42 @@ class PineRequest {
|
|
|
1678
1882
|
return [source, name];
|
|
1679
1883
|
}
|
|
1680
1884
|
}
|
|
1681
|
-
async security(symbol, timeframe, expression) {
|
|
1682
|
-
|
|
1885
|
+
async security(symbol, timeframe, expression, gaps = false, lookahead = false, ignore_invalid_symbol = false, currency = null, calc_bars_count = null) {
|
|
1886
|
+
const _symbol = symbol[0];
|
|
1887
|
+
const _timeframe = timeframe[0];
|
|
1888
|
+
const _expression = expression[0];
|
|
1889
|
+
const _expression_name = expression[1];
|
|
1890
|
+
const ctxTimeframeIdx = TIMEFRAMES.indexOf(this.context.timeframe);
|
|
1891
|
+
const reqTimeframeIdx = TIMEFRAMES.indexOf(_timeframe);
|
|
1892
|
+
if (ctxTimeframeIdx == -1 || reqTimeframeIdx == -1) {
|
|
1893
|
+
throw new Error("Invalid timeframe");
|
|
1894
|
+
}
|
|
1895
|
+
if (ctxTimeframeIdx > reqTimeframeIdx) {
|
|
1896
|
+
throw new Error("Only higher timeframes are supported for now");
|
|
1897
|
+
}
|
|
1898
|
+
if (ctxTimeframeIdx === reqTimeframeIdx) {
|
|
1899
|
+
return _expression;
|
|
1900
|
+
}
|
|
1901
|
+
const myOpenTime = this.context.data.openTime[0];
|
|
1902
|
+
const myCloseTime = this.context.data.closeTime[0];
|
|
1903
|
+
if (this.context.cache[_expression_name]) {
|
|
1904
|
+
const secContext2 = this.context.cache[_expression_name];
|
|
1905
|
+
const secContextIdx2 = this._findSecContextIdx(myOpenTime, myCloseTime, secContext2.data.openTime, secContext2.data.closeTime, lookahead);
|
|
1906
|
+
return secContextIdx2 == -1 ? NaN : secContext2.params[_expression_name][secContextIdx2];
|
|
1907
|
+
}
|
|
1908
|
+
const pineTS = new PineTS(this.context.source, _symbol, _timeframe, this.context.limit || 1e3, this.context.sDate, this.context.eDate);
|
|
1909
|
+
const secContext = await pineTS.run(this.context.pineTSCode);
|
|
1910
|
+
this.context.cache[_expression_name] = secContext;
|
|
1911
|
+
const secContextIdx = this._findSecContextIdx(myOpenTime, myCloseTime, secContext.data.openTime, secContext.data.closeTime, lookahead);
|
|
1912
|
+
return secContextIdx == -1 ? NaN : secContext.params[_expression_name][secContextIdx];
|
|
1913
|
+
}
|
|
1914
|
+
_findSecContextIdx(myOpenTime, myCloseTime, openTime, closeTime, lookahead = false) {
|
|
1915
|
+
for (let i = 0; i < openTime.length; i++) {
|
|
1916
|
+
if (openTime[i] <= myOpenTime && myCloseTime <= closeTime[i]) {
|
|
1917
|
+
return i + (lookahead ? 1 : 2);
|
|
1918
|
+
}
|
|
1919
|
+
}
|
|
1920
|
+
return -1;
|
|
1683
1921
|
}
|
|
1684
1922
|
}
|
|
1685
1923
|
|
|
@@ -1700,6 +1938,7 @@ class TechnicalAnalysis {
|
|
|
1700
1938
|
if (Array.isArray(source)) {
|
|
1701
1939
|
if (index) {
|
|
1702
1940
|
this.context.params[name] = source.slice(index);
|
|
1941
|
+
this.context.params[name].length = source.length;
|
|
1703
1942
|
return this.context.params[name];
|
|
1704
1943
|
}
|
|
1705
1944
|
this.context.params[name] = source.slice(0);
|
|
@@ -1715,9 +1954,21 @@ class TechnicalAnalysis {
|
|
|
1715
1954
|
const idx = this.context.idx;
|
|
1716
1955
|
return this.context.precision(result[idx]);
|
|
1717
1956
|
}
|
|
1718
|
-
sma(source, _period) {
|
|
1957
|
+
sma(source, _period, _cacheId) {
|
|
1719
1958
|
const period = Array.isArray(_period) ? _period[0] : _period;
|
|
1720
|
-
const
|
|
1959
|
+
const reversedSource = source.slice(0).reverse();
|
|
1960
|
+
if (this.context.useTACache && _cacheId) {
|
|
1961
|
+
if (!this.context.cache[_cacheId]) {
|
|
1962
|
+
this.context.cache[_cacheId] = {};
|
|
1963
|
+
}
|
|
1964
|
+
const cacheObj = this.context.cache[_cacheId];
|
|
1965
|
+
if (cacheObj) {
|
|
1966
|
+
const result2 = sma_cache(reversedSource, period, cacheObj);
|
|
1967
|
+
const idx2 = this.context.idx;
|
|
1968
|
+
return this.context.precision(result2[idx2]);
|
|
1969
|
+
}
|
|
1970
|
+
}
|
|
1971
|
+
const result = sma(reversedSource, period);
|
|
1721
1972
|
const idx = this.context.idx;
|
|
1722
1973
|
return this.context.precision(result[idx]);
|
|
1723
1974
|
}
|
|
@@ -1833,6 +2084,44 @@ class TechnicalAnalysis {
|
|
|
1833
2084
|
const idx = this.context.idx;
|
|
1834
2085
|
return [[this.context.precision(supertrend[idx]), direction[idx]]];
|
|
1835
2086
|
}
|
|
2087
|
+
crossover(source1, source2) {
|
|
2088
|
+
const current1 = Array.isArray(source1) ? source1[0] : source1;
|
|
2089
|
+
const current2 = Array.isArray(source2) ? source2[0] : source2;
|
|
2090
|
+
const prev1 = Array.isArray(source1) ? source1[1] : this.context.data.series[source1][1];
|
|
2091
|
+
const prev2 = Array.isArray(source2) ? source2[1] : this.context.data.series[source2][1];
|
|
2092
|
+
return prev1 < prev2 && current1 > current2;
|
|
2093
|
+
}
|
|
2094
|
+
crossunder(source1, source2) {
|
|
2095
|
+
const current1 = Array.isArray(source1) ? source1[0] : source1;
|
|
2096
|
+
const current2 = Array.isArray(source2) ? source2[0] : source2;
|
|
2097
|
+
const prev1 = Array.isArray(source1) ? source1[1] : this.context.data.series[source1][1];
|
|
2098
|
+
const prev2 = Array.isArray(source2) ? source2[1] : this.context.data.series[source2][1];
|
|
2099
|
+
return prev1 > prev2 && current1 < current2;
|
|
2100
|
+
}
|
|
2101
|
+
pivothigh(source, _leftbars, _rightbars) {
|
|
2102
|
+
if (_rightbars == void 0) {
|
|
2103
|
+
_rightbars = _leftbars;
|
|
2104
|
+
_leftbars = source;
|
|
2105
|
+
source = this.context.data.high;
|
|
2106
|
+
}
|
|
2107
|
+
const leftbars = Array.isArray(_leftbars) ? _leftbars[0] : _leftbars;
|
|
2108
|
+
const rightbars = Array.isArray(_rightbars) ? _rightbars[0] : _rightbars;
|
|
2109
|
+
const result = pivothigh(source.slice(0).reverse(), leftbars, rightbars);
|
|
2110
|
+
const idx = this.context.idx;
|
|
2111
|
+
return this.context.precision(result[idx]);
|
|
2112
|
+
}
|
|
2113
|
+
pivotlow(source, _leftbars, _rightbars) {
|
|
2114
|
+
if (_rightbars == void 0) {
|
|
2115
|
+
_rightbars = _leftbars;
|
|
2116
|
+
_leftbars = source;
|
|
2117
|
+
source = this.context.data.low;
|
|
2118
|
+
}
|
|
2119
|
+
const leftbars = Array.isArray(_leftbars) ? _leftbars[0] : _leftbars;
|
|
2120
|
+
const rightbars = Array.isArray(_rightbars) ? _rightbars[0] : _rightbars;
|
|
2121
|
+
const result = pivotlow(source.slice(0).reverse(), leftbars, rightbars);
|
|
2122
|
+
const idx = this.context.idx;
|
|
2123
|
+
return this.context.precision(result[idx]);
|
|
2124
|
+
}
|
|
1836
2125
|
}
|
|
1837
2126
|
function atr(high, low, close, period) {
|
|
1838
2127
|
const tr = new Array(high.length);
|
|
@@ -1906,6 +2195,35 @@ function rma(source, period) {
|
|
|
1906
2195
|
}
|
|
1907
2196
|
return result;
|
|
1908
2197
|
}
|
|
2198
|
+
function sma_cache(source, period, cacheObj) {
|
|
2199
|
+
const result = cacheObj.previousResult || new Array(source.length).fill(NaN);
|
|
2200
|
+
const lastProcessedIndex = cacheObj.lastProcessedIndex || -1;
|
|
2201
|
+
let previousSum = cacheObj.previousSum || 0;
|
|
2202
|
+
if (lastProcessedIndex === -1 || source.length !== lastProcessedIndex + 1) {
|
|
2203
|
+
previousSum = 0;
|
|
2204
|
+
for (let i = 0; i < period; i++) {
|
|
2205
|
+
previousSum += source[i] || 0;
|
|
2206
|
+
}
|
|
2207
|
+
result[period - 1] = previousSum / period;
|
|
2208
|
+
for (let i = 0; i < period - 1; i++) {
|
|
2209
|
+
result[i] = NaN;
|
|
2210
|
+
}
|
|
2211
|
+
for (let i = period; i < source.length; i++) {
|
|
2212
|
+
previousSum = previousSum - (source[i - period] || 0) + (source[i] || 0);
|
|
2213
|
+
result[i] = previousSum / period;
|
|
2214
|
+
}
|
|
2215
|
+
} else if (source.length === lastProcessedIndex + 2) {
|
|
2216
|
+
const newIndex = source.length - 1;
|
|
2217
|
+
previousSum = previousSum - (source[newIndex - period] || 0) + (source[newIndex] || 0);
|
|
2218
|
+
result[newIndex] = previousSum / period;
|
|
2219
|
+
} else {
|
|
2220
|
+
return sma(source, period);
|
|
2221
|
+
}
|
|
2222
|
+
cacheObj.previousSum = previousSum;
|
|
2223
|
+
cacheObj.lastProcessedIndex = source.length - 1;
|
|
2224
|
+
cacheObj.previousResult = result;
|
|
2225
|
+
return result;
|
|
2226
|
+
}
|
|
1909
2227
|
function sma(source, period) {
|
|
1910
2228
|
const result = new Array(source.length).fill(NaN);
|
|
1911
2229
|
for (let i = period - 1; i < source.length; i++) {
|
|
@@ -2024,7 +2342,7 @@ function lowest(source, length) {
|
|
|
2024
2342
|
let min = Infinity;
|
|
2025
2343
|
for (let j = 0; j < length; j++) {
|
|
2026
2344
|
const value = source[i - j];
|
|
2027
|
-
if (isNaN(value)) {
|
|
2345
|
+
if (isNaN(value) || value === void 0) {
|
|
2028
2346
|
min = min === Infinity ? NaN : min;
|
|
2029
2347
|
} else {
|
|
2030
2348
|
min = Math.min(min, value);
|
|
@@ -2143,14 +2461,263 @@ function calculateSupertrend(high, low, close, factor, atrPeriod) {
|
|
|
2143
2461
|
}
|
|
2144
2462
|
return [supertrend, direction];
|
|
2145
2463
|
}
|
|
2464
|
+
function pivothigh(source, leftbars, rightbars) {
|
|
2465
|
+
const result = new Array(source.length).fill(NaN);
|
|
2466
|
+
for (let i = leftbars + rightbars; i < source.length; i++) {
|
|
2467
|
+
const pivot = source[i - rightbars];
|
|
2468
|
+
let isPivot = true;
|
|
2469
|
+
for (let j = 1; j <= leftbars; j++) {
|
|
2470
|
+
if (source[i - rightbars - j] >= pivot) {
|
|
2471
|
+
isPivot = false;
|
|
2472
|
+
break;
|
|
2473
|
+
}
|
|
2474
|
+
}
|
|
2475
|
+
if (isPivot) {
|
|
2476
|
+
for (let j = 1; j <= rightbars; j++) {
|
|
2477
|
+
if (source[i - rightbars + j] >= pivot) {
|
|
2478
|
+
isPivot = false;
|
|
2479
|
+
break;
|
|
2480
|
+
}
|
|
2481
|
+
}
|
|
2482
|
+
}
|
|
2483
|
+
if (isPivot) {
|
|
2484
|
+
result[i] = pivot;
|
|
2485
|
+
}
|
|
2486
|
+
}
|
|
2487
|
+
return result;
|
|
2488
|
+
}
|
|
2489
|
+
function pivotlow(source, leftbars, rightbars) {
|
|
2490
|
+
const result = new Array(source.length).fill(NaN);
|
|
2491
|
+
for (let i = leftbars + rightbars; i < source.length; i++) {
|
|
2492
|
+
const pivot = source[i - rightbars];
|
|
2493
|
+
let isPivot = true;
|
|
2494
|
+
for (let j = 1; j <= leftbars; j++) {
|
|
2495
|
+
if (source[i - rightbars - j] <= pivot) {
|
|
2496
|
+
isPivot = false;
|
|
2497
|
+
break;
|
|
2498
|
+
}
|
|
2499
|
+
}
|
|
2500
|
+
if (isPivot) {
|
|
2501
|
+
for (let j = 1; j <= rightbars; j++) {
|
|
2502
|
+
if (source[i - rightbars + j] <= pivot) {
|
|
2503
|
+
isPivot = false;
|
|
2504
|
+
break;
|
|
2505
|
+
}
|
|
2506
|
+
}
|
|
2507
|
+
}
|
|
2508
|
+
if (isPivot) {
|
|
2509
|
+
result[i] = pivot;
|
|
2510
|
+
}
|
|
2511
|
+
}
|
|
2512
|
+
return result;
|
|
2513
|
+
}
|
|
2146
2514
|
|
|
2147
|
-
var __defProp = Object.defineProperty;
|
|
2148
|
-
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
2149
|
-
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
2515
|
+
var __defProp$2 = Object.defineProperty;
|
|
2516
|
+
var __defNormalProp$2 = (obj, key, value) => key in obj ? __defProp$2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
2517
|
+
var __publicField$2 = (obj, key, value) => __defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
2518
|
+
class PineArrayObject {
|
|
2519
|
+
constructor(array) {
|
|
2520
|
+
this.array = array;
|
|
2521
|
+
}
|
|
2522
|
+
}
|
|
2523
|
+
class PineArray {
|
|
2524
|
+
constructor(context) {
|
|
2525
|
+
this.context = context;
|
|
2526
|
+
__publicField$2(this, "_cache", {});
|
|
2527
|
+
}
|
|
2528
|
+
param(source, index = 0) {
|
|
2529
|
+
if (Array.isArray(source)) {
|
|
2530
|
+
return source[index];
|
|
2531
|
+
}
|
|
2532
|
+
return source;
|
|
2533
|
+
}
|
|
2534
|
+
/**
|
|
2535
|
+
* This function simulates PineScript's array.get() function
|
|
2536
|
+
* @param id - the array object to get the value from
|
|
2537
|
+
* @param index - the index of the value to get
|
|
2538
|
+
* @returns the value at the given index
|
|
2539
|
+
*/
|
|
2540
|
+
get(id, index) {
|
|
2541
|
+
return id.array[index];
|
|
2542
|
+
}
|
|
2543
|
+
set(id, index, value) {
|
|
2544
|
+
id.array[index] = value;
|
|
2545
|
+
}
|
|
2546
|
+
push(id, value) {
|
|
2547
|
+
id.array.push(value);
|
|
2548
|
+
}
|
|
2549
|
+
// Basic statistics
|
|
2550
|
+
sum(id) {
|
|
2551
|
+
return id.array.reduce((a, b) => a + (isNaN(b) ? 0 : b), 0);
|
|
2552
|
+
}
|
|
2553
|
+
avg(id) {
|
|
2554
|
+
return this.sum(id) / id.array.length;
|
|
2555
|
+
}
|
|
2556
|
+
min(id, nth = 0) {
|
|
2557
|
+
const sorted = [...id.array].sort((a, b) => a - b);
|
|
2558
|
+
return sorted[nth] ?? this.context.NA;
|
|
2559
|
+
}
|
|
2560
|
+
max(id, nth = 0) {
|
|
2561
|
+
const sorted = [...id.array].sort((a, b) => b - a);
|
|
2562
|
+
return sorted[nth] ?? this.context.NA;
|
|
2563
|
+
}
|
|
2564
|
+
size(id) {
|
|
2565
|
+
return id.array.length;
|
|
2566
|
+
}
|
|
2567
|
+
// Array creation
|
|
2568
|
+
new_bool(size, initial_value = false) {
|
|
2569
|
+
return new PineArrayObject(Array(size).fill(initial_value));
|
|
2570
|
+
}
|
|
2571
|
+
new_float(size, initial_value = NaN) {
|
|
2572
|
+
return new PineArrayObject(Array(size).fill(initial_value));
|
|
2573
|
+
}
|
|
2574
|
+
new_int(size, initial_value = 0) {
|
|
2575
|
+
return new PineArrayObject(Array(size).fill(Math.round(initial_value)));
|
|
2576
|
+
}
|
|
2577
|
+
new_string(size, initial_value = "") {
|
|
2578
|
+
return new PineArrayObject(Array(size).fill(initial_value));
|
|
2579
|
+
}
|
|
2580
|
+
new(size, initial_value) {
|
|
2581
|
+
return new PineArrayObject(Array(size).fill(initial_value));
|
|
2582
|
+
}
|
|
2583
|
+
// Array operations
|
|
2584
|
+
slice(id, start, end) {
|
|
2585
|
+
const adjustedEnd = end !== void 0 ? end + 1 : void 0;
|
|
2586
|
+
return new PineArrayObject(id.array.slice(start, adjustedEnd));
|
|
2587
|
+
}
|
|
2588
|
+
reverse(id) {
|
|
2589
|
+
id.array.reverse();
|
|
2590
|
+
}
|
|
2591
|
+
includes(id, value) {
|
|
2592
|
+
return id.array.includes(value);
|
|
2593
|
+
}
|
|
2594
|
+
indexof(id, value) {
|
|
2595
|
+
return id.array.indexOf(value);
|
|
2596
|
+
}
|
|
2597
|
+
lastindexof(id, value) {
|
|
2598
|
+
return id.array.lastIndexOf(value);
|
|
2599
|
+
}
|
|
2600
|
+
// More complex functions
|
|
2601
|
+
stdev(id, biased = true) {
|
|
2602
|
+
const mean = this.avg(id);
|
|
2603
|
+
const deviations = id.array.map((x) => Math.pow(x - mean, 2));
|
|
2604
|
+
const divisor = biased ? id.array.length : id.array.length - 1;
|
|
2605
|
+
return Math.sqrt(this.sum(new PineArrayObject(deviations)) / divisor);
|
|
2606
|
+
}
|
|
2607
|
+
variance(id, biased = true) {
|
|
2608
|
+
const mean = this.avg(id);
|
|
2609
|
+
const deviations = id.array.map((x) => Math.pow(x - mean, 2));
|
|
2610
|
+
const divisor = biased ? id.array.length : id.array.length - 1;
|
|
2611
|
+
return this.sum(new PineArrayObject(deviations)) / divisor;
|
|
2612
|
+
}
|
|
2613
|
+
covariance(arr1, arr2, biased = true) {
|
|
2614
|
+
if (arr1.array.length !== arr2.array.length || arr1.array.length < 2) return NaN;
|
|
2615
|
+
const divisor = biased ? arr1.array.length : arr1.array.length - 1;
|
|
2616
|
+
const mean1 = this.avg(arr1);
|
|
2617
|
+
const mean2 = this.avg(arr2);
|
|
2618
|
+
let sum = 0;
|
|
2619
|
+
for (let i = 0; i < arr1.array.length; i++) {
|
|
2620
|
+
sum += (arr1.array[i] - mean1) * (arr2.array[i] - mean2);
|
|
2621
|
+
}
|
|
2622
|
+
return sum / divisor;
|
|
2623
|
+
}
|
|
2624
|
+
// Additional utility methods
|
|
2625
|
+
first(id) {
|
|
2626
|
+
return id.array.length > 0 ? id.array[0] : this.context.NA;
|
|
2627
|
+
}
|
|
2628
|
+
last(id) {
|
|
2629
|
+
return id.array.length > 0 ? id.array[id.array.length - 1] : this.context.NA;
|
|
2630
|
+
}
|
|
2631
|
+
clear(id) {
|
|
2632
|
+
id.array.length = 0;
|
|
2633
|
+
}
|
|
2634
|
+
join(id, separator = ",") {
|
|
2635
|
+
return id.array.join(separator);
|
|
2636
|
+
}
|
|
2637
|
+
/** Array Manipulation Functions */
|
|
2638
|
+
abs(id) {
|
|
2639
|
+
return new PineArrayObject(id.array.map((val) => Math.abs(val)));
|
|
2640
|
+
}
|
|
2641
|
+
concat(id, other) {
|
|
2642
|
+
id.array.push(...other.array);
|
|
2643
|
+
return id;
|
|
2644
|
+
}
|
|
2645
|
+
copy(id) {
|
|
2646
|
+
return new PineArrayObject([...id.array]);
|
|
2647
|
+
}
|
|
2648
|
+
every(id, callback) {
|
|
2649
|
+
return id.array.every(callback);
|
|
2650
|
+
}
|
|
2651
|
+
fill(id, value, start = 0, end) {
|
|
2652
|
+
const length = id.array.length;
|
|
2653
|
+
const adjustedEnd = end !== void 0 ? Math.min(end, length) : length;
|
|
2654
|
+
for (let i = start; i < adjustedEnd; i++) {
|
|
2655
|
+
id.array[i] = value;
|
|
2656
|
+
}
|
|
2657
|
+
}
|
|
2658
|
+
from(source) {
|
|
2659
|
+
return new PineArrayObject([...source]);
|
|
2660
|
+
}
|
|
2661
|
+
insert(id, index, value) {
|
|
2662
|
+
id.array.splice(index, 0, value);
|
|
2663
|
+
}
|
|
2664
|
+
pop(id) {
|
|
2665
|
+
return id.array.pop();
|
|
2666
|
+
}
|
|
2667
|
+
range(id) {
|
|
2668
|
+
return this.max(id) - this.min(id);
|
|
2669
|
+
}
|
|
2670
|
+
remove(id, index) {
|
|
2671
|
+
if (index >= 0 && index < id.array.length) {
|
|
2672
|
+
return id.array.splice(index, 1)[0];
|
|
2673
|
+
}
|
|
2674
|
+
return this.context.NA;
|
|
2675
|
+
}
|
|
2676
|
+
shift(id) {
|
|
2677
|
+
return id.array.shift();
|
|
2678
|
+
}
|
|
2679
|
+
sort(id, order = "asc") {
|
|
2680
|
+
id.array.sort((a, b) => order === "asc" ? a - b : b - a);
|
|
2681
|
+
}
|
|
2682
|
+
sort_indices(id, comparator) {
|
|
2683
|
+
const indices = id.array.map((_, index) => index);
|
|
2684
|
+
indices.sort((a, b) => {
|
|
2685
|
+
const valA = id.array[a];
|
|
2686
|
+
const valB = id.array[b];
|
|
2687
|
+
return comparator ? comparator(valA, valB) : valA - valB;
|
|
2688
|
+
});
|
|
2689
|
+
return new PineArrayObject(indices);
|
|
2690
|
+
}
|
|
2691
|
+
standardize(id) {
|
|
2692
|
+
const mean = this.avg(id);
|
|
2693
|
+
const stdev = this.stdev(id);
|
|
2694
|
+
if (stdev === 0) {
|
|
2695
|
+
return new PineArrayObject(id.array.map(() => 0));
|
|
2696
|
+
}
|
|
2697
|
+
return new PineArrayObject(id.array.map((x) => (x - mean) / stdev));
|
|
2698
|
+
}
|
|
2699
|
+
unshift(id, value) {
|
|
2700
|
+
id.array.unshift(value);
|
|
2701
|
+
}
|
|
2702
|
+
some(id, callback) {
|
|
2703
|
+
return id.array.some(callback);
|
|
2704
|
+
}
|
|
2705
|
+
}
|
|
2706
|
+
|
|
2707
|
+
var __defProp$1 = Object.defineProperty;
|
|
2708
|
+
var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
2709
|
+
var __publicField$1 = (obj, key, value) => __defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
2150
2710
|
class Context {
|
|
2151
|
-
constructor(
|
|
2152
|
-
|
|
2153
|
-
|
|
2711
|
+
constructor({
|
|
2712
|
+
marketData,
|
|
2713
|
+
source,
|
|
2714
|
+
tickerId,
|
|
2715
|
+
timeframe,
|
|
2716
|
+
limit,
|
|
2717
|
+
sDate,
|
|
2718
|
+
eDate
|
|
2719
|
+
}) {
|
|
2720
|
+
__publicField$1(this, "data", {
|
|
2154
2721
|
open: [],
|
|
2155
2722
|
high: [],
|
|
2156
2723
|
low: [],
|
|
@@ -2160,23 +2727,43 @@ class Context {
|
|
|
2160
2727
|
hlc3: [],
|
|
2161
2728
|
ohlc4: []
|
|
2162
2729
|
});
|
|
2163
|
-
__publicField(this, "
|
|
2164
|
-
__publicField(this, "
|
|
2165
|
-
__publicField(this, "
|
|
2166
|
-
__publicField(this, "
|
|
2167
|
-
__publicField(this, "
|
|
2168
|
-
__publicField(this, "
|
|
2169
|
-
__publicField(this, "
|
|
2170
|
-
__publicField(this, "
|
|
2171
|
-
__publicField(this, "
|
|
2172
|
-
__publicField(this, "
|
|
2173
|
-
__publicField(this, "
|
|
2174
|
-
__publicField(this, "
|
|
2175
|
-
__publicField(this, "
|
|
2730
|
+
__publicField$1(this, "cache", {});
|
|
2731
|
+
__publicField$1(this, "useTACache", false);
|
|
2732
|
+
__publicField$1(this, "NA", NaN);
|
|
2733
|
+
__publicField$1(this, "math");
|
|
2734
|
+
__publicField$1(this, "ta");
|
|
2735
|
+
__publicField$1(this, "input");
|
|
2736
|
+
__publicField$1(this, "request");
|
|
2737
|
+
__publicField$1(this, "array");
|
|
2738
|
+
__publicField$1(this, "core");
|
|
2739
|
+
__publicField$1(this, "lang");
|
|
2740
|
+
__publicField$1(this, "idx", 0);
|
|
2741
|
+
__publicField$1(this, "params", {});
|
|
2742
|
+
__publicField$1(this, "const", {});
|
|
2743
|
+
__publicField$1(this, "var", {});
|
|
2744
|
+
__publicField$1(this, "let", {});
|
|
2745
|
+
__publicField$1(this, "result");
|
|
2746
|
+
__publicField$1(this, "plots", {});
|
|
2747
|
+
__publicField$1(this, "marketData");
|
|
2748
|
+
__publicField$1(this, "source");
|
|
2749
|
+
__publicField$1(this, "tickerId");
|
|
2750
|
+
__publicField$1(this, "timeframe", "");
|
|
2751
|
+
__publicField$1(this, "limit");
|
|
2752
|
+
__publicField$1(this, "sDate");
|
|
2753
|
+
__publicField$1(this, "eDate");
|
|
2754
|
+
__publicField$1(this, "pineTSCode");
|
|
2755
|
+
this.marketData = marketData;
|
|
2756
|
+
this.source = source;
|
|
2757
|
+
this.tickerId = tickerId;
|
|
2758
|
+
this.timeframe = timeframe;
|
|
2759
|
+
this.limit = limit;
|
|
2760
|
+
this.sDate = sDate;
|
|
2761
|
+
this.eDate = eDate;
|
|
2176
2762
|
this.math = new PineMath(this);
|
|
2177
2763
|
this.ta = new TechnicalAnalysis(this);
|
|
2178
2764
|
this.input = new Input(this);
|
|
2179
2765
|
this.request = new PineRequest(this);
|
|
2766
|
+
this.array = new PineArray(this);
|
|
2180
2767
|
const core = new Core(this);
|
|
2181
2768
|
this.core = {
|
|
2182
2769
|
plotchar: core.plotchar.bind(core),
|
|
@@ -2212,12 +2799,13 @@ class Context {
|
|
|
2212
2799
|
return trg;
|
|
2213
2800
|
}
|
|
2214
2801
|
/**
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2802
|
+
* this function is used to set the floating point precision of a number
|
|
2803
|
+
* by default it is set to 10 decimals which is the same as pine script
|
|
2804
|
+
* @param n - the number to be precision
|
|
2805
|
+
* @param decimals - the number of decimals to precision to
|
|
2806
|
+
|
|
2807
|
+
* @returns the precision number
|
|
2808
|
+
*/
|
|
2221
2809
|
precision(n, decimals = 10) {
|
|
2222
2810
|
if (typeof n !== "number" || isNaN(n)) return n;
|
|
2223
2811
|
return Number(n.toFixed(decimals));
|
|
@@ -2236,6 +2824,7 @@ class Context {
|
|
|
2236
2824
|
if (Array.isArray(source)) {
|
|
2237
2825
|
if (index) {
|
|
2238
2826
|
this.params[name] = source.slice(index);
|
|
2827
|
+
this.params[name].length = source.length;
|
|
2239
2828
|
return this.params[name];
|
|
2240
2829
|
}
|
|
2241
2830
|
this.params[name] = source.slice(0);
|
|
@@ -2248,6 +2837,9 @@ class Context {
|
|
|
2248
2837
|
//#endregion
|
|
2249
2838
|
}
|
|
2250
2839
|
|
|
2840
|
+
var __defProp = Object.defineProperty;
|
|
2841
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
2842
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
2251
2843
|
const BINANCE_API_URL = "https://api.binance.com/api/v3";
|
|
2252
2844
|
const timeframe_to_binance = {
|
|
2253
2845
|
"1": "1m",
|
|
@@ -2270,6 +2862,8 @@ const timeframe_to_binance = {
|
|
|
2270
2862
|
// 3 hours (not directly supported by Binance, needs custom handling)
|
|
2271
2863
|
"240": "4h",
|
|
2272
2864
|
// 4 hours
|
|
2865
|
+
"4H": "4h",
|
|
2866
|
+
// 4 hours
|
|
2273
2867
|
"1D": "1d",
|
|
2274
2868
|
// 1 day
|
|
2275
2869
|
D: "1d",
|
|
@@ -2283,15 +2877,119 @@ const timeframe_to_binance = {
|
|
|
2283
2877
|
M: "1M"
|
|
2284
2878
|
// 1 month
|
|
2285
2879
|
};
|
|
2880
|
+
class CacheManager {
|
|
2881
|
+
constructor(cacheDuration = 5 * 60 * 1e3) {
|
|
2882
|
+
__publicField(this, "cache");
|
|
2883
|
+
__publicField(this, "cacheDuration");
|
|
2884
|
+
this.cache = /* @__PURE__ */ new Map();
|
|
2885
|
+
this.cacheDuration = cacheDuration;
|
|
2886
|
+
}
|
|
2887
|
+
generateKey(params) {
|
|
2888
|
+
return Object.entries(params).filter(([_, value]) => value !== void 0).map(([key, value]) => `${key}:${value}`).join("|");
|
|
2889
|
+
}
|
|
2890
|
+
get(params) {
|
|
2891
|
+
const key = this.generateKey(params);
|
|
2892
|
+
const cached = this.cache.get(key);
|
|
2893
|
+
if (!cached) return null;
|
|
2894
|
+
if (Date.now() - cached.timestamp > this.cacheDuration) {
|
|
2895
|
+
this.cache.delete(key);
|
|
2896
|
+
return null;
|
|
2897
|
+
}
|
|
2898
|
+
return cached.data;
|
|
2899
|
+
}
|
|
2900
|
+
set(params, data) {
|
|
2901
|
+
const key = this.generateKey(params);
|
|
2902
|
+
this.cache.set(key, {
|
|
2903
|
+
data,
|
|
2904
|
+
timestamp: Date.now()
|
|
2905
|
+
});
|
|
2906
|
+
}
|
|
2907
|
+
clear() {
|
|
2908
|
+
this.cache.clear();
|
|
2909
|
+
}
|
|
2910
|
+
// Optional: method to remove expired entries
|
|
2911
|
+
cleanup() {
|
|
2912
|
+
const now = Date.now();
|
|
2913
|
+
for (const [key, entry] of this.cache.entries()) {
|
|
2914
|
+
if (now - entry.timestamp > this.cacheDuration) {
|
|
2915
|
+
this.cache.delete(key);
|
|
2916
|
+
}
|
|
2917
|
+
}
|
|
2918
|
+
}
|
|
2919
|
+
}
|
|
2286
2920
|
class BinanceProvider {
|
|
2921
|
+
constructor() {
|
|
2922
|
+
__publicField(this, "cacheManager");
|
|
2923
|
+
this.cacheManager = new CacheManager(5 * 60 * 1e3);
|
|
2924
|
+
}
|
|
2925
|
+
async getMarketDataInterval(tickerId, timeframe, sDate, eDate) {
|
|
2926
|
+
try {
|
|
2927
|
+
const interval = timeframe_to_binance[timeframe.toUpperCase()];
|
|
2928
|
+
if (!interval) {
|
|
2929
|
+
console.error(`Unsupported timeframe: ${timeframe}`);
|
|
2930
|
+
return [];
|
|
2931
|
+
}
|
|
2932
|
+
const timeframeDurations = {
|
|
2933
|
+
"1m": 60 * 1e3,
|
|
2934
|
+
"3m": 3 * 60 * 1e3,
|
|
2935
|
+
"5m": 5 * 60 * 1e3,
|
|
2936
|
+
"15m": 15 * 60 * 1e3,
|
|
2937
|
+
"30m": 30 * 60 * 1e3,
|
|
2938
|
+
"1h": 60 * 60 * 1e3,
|
|
2939
|
+
"2h": 2 * 60 * 60 * 1e3,
|
|
2940
|
+
"4h": 4 * 60 * 60 * 1e3,
|
|
2941
|
+
"1d": 24 * 60 * 60 * 1e3,
|
|
2942
|
+
"1w": 7 * 24 * 60 * 60 * 1e3,
|
|
2943
|
+
"1M": 30 * 24 * 60 * 60 * 1e3
|
|
2944
|
+
};
|
|
2945
|
+
let allData = [];
|
|
2946
|
+
let currentStart = sDate;
|
|
2947
|
+
const endTime = eDate;
|
|
2948
|
+
const intervalDuration = timeframeDurations[interval];
|
|
2949
|
+
if (!intervalDuration) {
|
|
2950
|
+
console.error(`Duration not defined for interval: ${interval}`);
|
|
2951
|
+
return [];
|
|
2952
|
+
}
|
|
2953
|
+
while (currentStart < endTime) {
|
|
2954
|
+
const chunkEnd = Math.min(currentStart + 1e3 * intervalDuration, endTime);
|
|
2955
|
+
const data = await this.getMarketData(
|
|
2956
|
+
tickerId,
|
|
2957
|
+
timeframe,
|
|
2958
|
+
1e3,
|
|
2959
|
+
// Max allowed by Binance
|
|
2960
|
+
currentStart,
|
|
2961
|
+
chunkEnd
|
|
2962
|
+
);
|
|
2963
|
+
if (data.length === 0) break;
|
|
2964
|
+
allData = allData.concat(data);
|
|
2965
|
+
currentStart = data[data.length - 1].closeTime + 1;
|
|
2966
|
+
if (data.length < 1e3) break;
|
|
2967
|
+
}
|
|
2968
|
+
return allData;
|
|
2969
|
+
} catch (error) {
|
|
2970
|
+
console.error("Error in getMarketDataInterval:", error);
|
|
2971
|
+
return [];
|
|
2972
|
+
}
|
|
2973
|
+
}
|
|
2974
|
+
//TODO : allow querying more than 1000 klines
|
|
2975
|
+
//TODO : immplement cache
|
|
2287
2976
|
async getMarketData(tickerId, timeframe, limit, sDate, eDate) {
|
|
2288
2977
|
try {
|
|
2978
|
+
const cacheParams = { tickerId, timeframe, limit, sDate, eDate };
|
|
2979
|
+
const cachedData = this.cacheManager.get(cacheParams);
|
|
2980
|
+
if (cachedData) {
|
|
2981
|
+
console.log("cache hit", tickerId, timeframe, limit, sDate, eDate);
|
|
2982
|
+
return cachedData;
|
|
2983
|
+
}
|
|
2289
2984
|
const interval = timeframe_to_binance[timeframe.toUpperCase()];
|
|
2290
2985
|
if (!interval) {
|
|
2291
2986
|
console.error(`Unsupported timeframe: ${timeframe}`);
|
|
2292
2987
|
return [];
|
|
2293
2988
|
}
|
|
2294
2989
|
let url = `${BINANCE_API_URL}/klines?symbol=${tickerId}&interval=${interval}`;
|
|
2990
|
+
if (!limit && sDate && eDate) {
|
|
2991
|
+
return this.getMarketDataInterval(tickerId, timeframe, sDate, eDate);
|
|
2992
|
+
}
|
|
2295
2993
|
if (limit) {
|
|
2296
2994
|
url += `&limit=${limit}`;
|
|
2297
2995
|
}
|
|
@@ -2308,13 +3006,13 @@ class BinanceProvider {
|
|
|
2308
3006
|
const result = await response.json();
|
|
2309
3007
|
const data = result.map((item) => {
|
|
2310
3008
|
return {
|
|
2311
|
-
openTime: parseInt(item[0])
|
|
3009
|
+
openTime: parseInt(item[0]),
|
|
2312
3010
|
open: parseFloat(item[1]),
|
|
2313
3011
|
high: parseFloat(item[2]),
|
|
2314
3012
|
low: parseFloat(item[3]),
|
|
2315
3013
|
close: parseFloat(item[4]),
|
|
2316
3014
|
volume: parseFloat(item[5]),
|
|
2317
|
-
closeTime: parseInt(item[6])
|
|
3015
|
+
closeTime: parseInt(item[6]),
|
|
2318
3016
|
quoteAssetVolume: parseFloat(item[7]),
|
|
2319
3017
|
numberOfTrades: parseInt(item[8]),
|
|
2320
3018
|
takerBuyBaseAssetVolume: parseFloat(item[9]),
|
|
@@ -2322,6 +3020,7 @@ class BinanceProvider {
|
|
|
2322
3020
|
ignore: item[11]
|
|
2323
3021
|
};
|
|
2324
3022
|
});
|
|
3023
|
+
this.cacheManager.set(cacheParams, data);
|
|
2325
3024
|
return data;
|
|
2326
3025
|
} catch (error) {
|
|
2327
3026
|
console.error("Error in binance.klines:", error);
|
|
@@ -2332,6 +3031,7 @@ class BinanceProvider {
|
|
|
2332
3031
|
|
|
2333
3032
|
const Provider = {
|
|
2334
3033
|
Binance: new BinanceProvider()
|
|
3034
|
+
//TODO : add other providers (polygon, etc.)
|
|
2335
3035
|
};
|
|
2336
3036
|
|
|
2337
3037
|
export { Context, PineTS, Provider };
|