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.
@@ -1,6 +1,3 @@
1
- 'use strict';
2
-
3
- Object.defineProperty(exports, '__esModule', { value: true });
4
1
 
5
2
  /*
6
3
  * Copyright (C) 2025 Alaa-eddine KADDOURI
@@ -17,7 +14,12 @@ Object.defineProperty(exports, '__esModule', { value: true });
17
14
  *
18
15
  * You should have received a copy of the GNU Affero General Public License
19
16
  * along with this program. If not, see <https://www.gnu.org/licenses/>.
20
- */// This file was generated. Do not modify manually!
17
+ */
18
+ 'use strict';
19
+
20
+ Object.defineProperty(exports, '__esModule', { value: true });
21
+
22
+ // This file was generated. Do not modify manually!
21
23
  var astralIdentifierCodes = [509, 0, 227, 0, 150, 4, 294, 9, 1368, 2, 2, 1, 6, 3, 41, 2, 5, 0, 166, 1, 574, 3, 9, 9, 7, 9, 32, 4, 318, 1, 80, 3, 71, 10, 50, 3, 123, 2, 54, 14, 32, 10, 3, 1, 11, 3, 46, 10, 8, 0, 46, 9, 7, 2, 37, 13, 2, 9, 6, 1, 45, 0, 13, 2, 49, 13, 9, 3, 2, 11, 83, 11, 7, 0, 3, 0, 158, 11, 6, 9, 7, 3, 56, 1, 2, 6, 3, 1, 3, 2, 10, 0, 11, 1, 3, 6, 4, 4, 68, 8, 2, 0, 3, 0, 2, 3, 2, 4, 2, 0, 15, 1, 83, 17, 10, 9, 5, 0, 82, 19, 13, 9, 214, 6, 3, 8, 28, 1, 83, 16, 16, 9, 82, 12, 9, 9, 7, 19, 58, 14, 5, 9, 243, 14, 166, 9, 71, 5, 2, 1, 3, 3, 2, 0, 2, 1, 13, 9, 120, 6, 3, 6, 4, 0, 29, 9, 41, 6, 2, 3, 9, 0, 10, 10, 47, 15, 343, 9, 54, 7, 2, 7, 17, 9, 57, 21, 2, 13, 123, 5, 4, 0, 2, 1, 2, 6, 2, 0, 9, 9, 49, 4, 2, 1, 2, 4, 9, 9, 330, 3, 10, 1, 2, 0, 49, 6, 4, 4, 14, 10, 5350, 0, 7, 14, 11465, 27, 2343, 9, 87, 9, 39, 4, 60, 6, 26, 9, 535, 9, 470, 0, 2, 54, 8, 3, 82, 0, 12, 1, 19628, 1, 4178, 9, 519, 45, 3, 22, 543, 4, 4, 5, 9, 7, 3, 6, 31, 3, 149, 2, 1418, 49, 513, 54, 5, 49, 9, 0, 15, 0, 23, 4, 2, 14, 1361, 6, 2, 16, 3, 6, 2, 1, 2, 4, 101, 0, 161, 6, 10, 9, 357, 0, 62, 13, 499, 13, 245, 1, 2, 9, 726, 6, 110, 6, 6, 9, 4759, 9, 787719, 239];
22
24
 
23
25
  // This file was generated. Do not modify manually!
@@ -6146,22 +6148,7 @@ function parse(input, options) {
6146
6148
  return Parser.parse(input, options)
6147
6149
  }
6148
6150
 
6149
- /*
6150
- * Copyright (C) 2025 Alaa-eddine KADDOURI
6151
- *
6152
- * This program is free software: you can redistribute it and/or modify
6153
- * it under the terms of the GNU Affero General Public License as published by
6154
- * the Free Software Foundation, either version 3 of the License, or
6155
- * (at your option) any later version.
6156
- *
6157
- * This program is distributed in the hope that it will be useful,
6158
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
6159
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
6160
- * GNU Affero General Public License for more details.
6161
- *
6162
- * You should have received a copy of the GNU Affero General Public License
6163
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
6164
- */// AST walker module for ESTree compatible trees
6151
+ // AST walker module for ESTree compatible trees
6165
6152
 
6166
6153
  // A simple walk is one where you simply specify callbacks to be
6167
6154
  // called on specific nodes. The last two arguments are optional. A
@@ -6465,22 +6452,7 @@ base.MethodDefinition = base.PropertyDefinition = base.Property = function (node
6465
6452
  if (node.value) { c(node.value, st, "Expression"); }
6466
6453
  };
6467
6454
 
6468
- /*
6469
- * Copyright (C) 2025 Alaa-eddine KADDOURI
6470
- *
6471
- * This program is free software: you can redistribute it and/or modify
6472
- * it under the terms of the GNU Affero General Public License as published by
6473
- * the Free Software Foundation, either version 3 of the License, or
6474
- * (at your option) any later version.
6475
- *
6476
- * This program is distributed in the hope that it will be useful,
6477
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
6478
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
6479
- * GNU Affero General Public License for more details.
6480
- *
6481
- * You should have received a copy of the GNU Affero General Public License
6482
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
6483
- */// Astring is a tiny and fast JavaScript code generator from an ESTree-compliant AST.
6455
+ // Astring is a tiny and fast JavaScript code generator from an ESTree-compliant AST.
6484
6456
  //
6485
6457
  // Astring was written by David Bonnet and released under an MIT license.
6486
6458
  //
@@ -7701,22 +7673,7 @@ function generate(node, options) {
7701
7673
  return state.output
7702
7674
  }
7703
7675
 
7704
- /*
7705
- * Copyright (C) 2025 Alaa-eddine KADDOURI
7706
- *
7707
- * This program is free software: you can redistribute it and/or modify
7708
- * it under the terms of the GNU Affero General Public License as published by
7709
- * the Free Software Foundation, either version 3 of the License, or
7710
- * (at your option) any later version.
7711
- *
7712
- * This program is distributed in the hope that it will be useful,
7713
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
7714
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
7715
- * GNU Affero General Public License for more details.
7716
- *
7717
- * You should have received a copy of the GNU Affero General Public License
7718
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
7719
- */class ScopeManager {
7676
+ class ScopeManager {
7720
7677
  scopes = [];
7721
7678
  scopeTypes = [];
7722
7679
  scopeCounts = /* @__PURE__ */ new Map();
@@ -7728,6 +7685,7 @@ function generate(node, options) {
7728
7685
  loopVarNames = /* @__PURE__ */ new Map();
7729
7686
  // Map original names to transformed names
7730
7687
  paramIdCounter = 0;
7688
+ cacheIdCounter = 0;
7731
7689
  tempVarCounter = 0;
7732
7690
  get nextParamIdArg() {
7733
7691
  return {
@@ -7735,6 +7693,12 @@ function generate(node, options) {
7735
7693
  name: `'p${this.paramIdCounter++}'`
7736
7694
  };
7737
7695
  }
7696
+ get nextCacheIdArg() {
7697
+ return {
7698
+ type: "Identifier",
7699
+ name: `'cache_${this.cacheIdCounter++}'`
7700
+ };
7701
+ }
7738
7702
  constructor() {
7739
7703
  this.pushScope("glb");
7740
7704
  }
@@ -7818,6 +7782,7 @@ function generate(node, options) {
7818
7782
  }
7819
7783
  }
7820
7784
 
7785
+ //!!!Warning!!! this code is not clean, it was initially written as a PoC then used as transpiler for PineTS
7821
7786
  const CONTEXT_NAME = "$";
7822
7787
  const UNDEFINED_ARG = {
7823
7788
  type: "Identifier",
@@ -7914,6 +7879,9 @@ function transformMemberExpression(memberNode, originalParamName, scopeManager)
7914
7879
  }
7915
7880
  function transformVariableDeclaration(varNode, scopeManager) {
7916
7881
  varNode.declarations.forEach((decl) => {
7882
+ if (decl.init.name == "na") {
7883
+ decl.init.name = "NaN";
7884
+ }
7917
7885
  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");
7918
7886
  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");
7919
7887
  const isArrowFunction = decl.init && decl.init.type === "ArrowFunctionExpression";
@@ -8076,7 +8044,28 @@ function transformVariableDeclaration(varNode, scopeManager) {
8076
8044
  }
8077
8045
  };
8078
8046
  if (isArrayPatternVar) {
8079
- assignmentExpr.expression.right.object.property.name += "?.[0]";
8047
+ assignmentExpr.expression.right.object.property.name += `?.[0][${decl.init.property.value}]`;
8048
+ const obj = assignmentExpr.expression.right.object;
8049
+ assignmentExpr.expression.right = {
8050
+ type: "CallExpression",
8051
+ callee: {
8052
+ type: "MemberExpression",
8053
+ object: {
8054
+ type: "Identifier",
8055
+ name: CONTEXT_NAME
8056
+ },
8057
+ property: {
8058
+ type: "Identifier",
8059
+ name: "init"
8060
+ },
8061
+ computed: false
8062
+ },
8063
+ arguments: [
8064
+ targetVarRef,
8065
+ obj
8066
+ /*, decl.init.property.value*/
8067
+ ]
8068
+ };
8080
8069
  }
8081
8070
  if (isArrowFunction) {
8082
8071
  scopeManager.pushScope("fn");
@@ -8229,14 +8218,22 @@ function transformAssignmentExpression(node, scopeManager) {
8229
8218
  { parent: node.right, inNamespaceCall: false },
8230
8219
  {
8231
8220
  Identifier(node2, state, c) {
8221
+ if (node2.name == "na") {
8222
+ node2.name = "NaN";
8223
+ }
8232
8224
  node2.parent = state.parent;
8233
8225
  transformIdentifier(node2, scopeManager);
8234
8226
  const isBinaryOperation = node2.parent && node2.parent.type === "BinaryExpression";
8235
8227
  const isConditional = node2.parent && node2.parent.type === "ConditionalExpression";
8236
- if (isConditional || isBinaryOperation) {
8228
+ const isContextBound = scopeManager.isContextBound(node2.name) && !scopeManager.isRootParam(node2.name);
8229
+ const hasArrayAccess = node2.parent && node2.parent.type === "MemberExpression" && node2.parent.computed && node2.parent.object === node2;
8230
+ const isParamCall = node2.parent && node2.parent._isParamCall;
8231
+ const isMemberExpression = node2.parent && node2.parent.type === "MemberExpression";
8232
+ const isReserved = node2.name === "NaN";
8233
+ if (isContextBound || isConditional || isBinaryOperation) {
8237
8234
  if (node2.type === "MemberExpression") {
8238
8235
  transformArrayIndex(node2, scopeManager);
8239
- } else if (node2.type === "Identifier") {
8236
+ } else if (node2.type === "Identifier" && !isMemberExpression && !hasArrayAccess && !isParamCall && !isReserved) {
8240
8237
  addArrayAccess(node2);
8241
8238
  }
8242
8239
  }
@@ -8418,6 +8415,10 @@ function transformReturnStatement(node, scopeManager) {
8418
8415
  }
8419
8416
  function transformIdentifierForParam(node, scopeManager) {
8420
8417
  if (node.type === "Identifier") {
8418
+ if (node.name === "na") {
8419
+ node.name = "NaN";
8420
+ return node;
8421
+ }
8421
8422
  if (scopeManager.isLoopVariable(node.name)) {
8422
8423
  return node;
8423
8424
  }
@@ -8471,40 +8472,58 @@ function transformIdentifierForParam(node, scopeManager) {
8471
8472
  }
8472
8473
  return node;
8473
8474
  }
8475
+ function getParamFromUnaryExpression(node, scopeManager, namespace) {
8476
+ const transformedArgument = transformOperand(node.argument, scopeManager, namespace);
8477
+ const unaryExpr = {
8478
+ type: "UnaryExpression",
8479
+ operator: node.operator,
8480
+ prefix: node.prefix,
8481
+ argument: transformedArgument,
8482
+ start: node.start,
8483
+ end: node.end
8484
+ };
8485
+ return unaryExpr;
8486
+ }
8474
8487
  function transformOperand(node, scopeManager, namespace = "") {
8475
- if (node.type === "BinaryExpression") {
8476
- return transformBinaryExpression(node, scopeManager, namespace);
8477
- }
8478
- if (node.type === "MemberExpression") {
8479
- const transformedObject = node.object.type === "Identifier" ? transformIdentifierForParam(node.object, scopeManager) : node.object;
8480
- return {
8481
- type: "MemberExpression",
8482
- object: transformedObject,
8483
- property: node.property,
8484
- computed: node.computed
8485
- };
8486
- } else if (node.type === "Identifier") {
8487
- if (scopeManager.isLoopVariable(node.name)) {
8488
- return node;
8488
+ switch (node.type) {
8489
+ case "BinaryExpression": {
8490
+ return getParamFromBinaryExpression(node, scopeManager, namespace);
8489
8491
  }
8490
- const isMemberExprProperty = node.parent && node.parent.type === "MemberExpression" && node.parent.property === node;
8491
- if (isMemberExprProperty) {
8492
- return node;
8492
+ case "MemberExpression": {
8493
+ const transformedObject = node.object.type === "Identifier" ? transformIdentifierForParam(node.object, scopeManager) : node.object;
8494
+ return {
8495
+ type: "MemberExpression",
8496
+ object: transformedObject,
8497
+ property: node.property,
8498
+ computed: node.computed
8499
+ };
8500
+ }
8501
+ case "Identifier": {
8502
+ if (scopeManager.isLoopVariable(node.name)) {
8503
+ return node;
8504
+ }
8505
+ const isMemberExprProperty = node.parent && node.parent.type === "MemberExpression" && node.parent.property === node;
8506
+ if (isMemberExprProperty) {
8507
+ return node;
8508
+ }
8509
+ const transformedObject = transformIdentifierForParam(node, scopeManager);
8510
+ return {
8511
+ type: "MemberExpression",
8512
+ object: transformedObject,
8513
+ property: {
8514
+ type: "Literal",
8515
+ value: 0
8516
+ },
8517
+ computed: true
8518
+ };
8519
+ }
8520
+ case "UnaryExpression": {
8521
+ return getParamFromUnaryExpression(node, scopeManager, namespace);
8493
8522
  }
8494
- const transformedObject = transformIdentifierForParam(node, scopeManager);
8495
- return {
8496
- type: "MemberExpression",
8497
- object: transformedObject,
8498
- property: {
8499
- type: "Literal",
8500
- value: 0
8501
- },
8502
- computed: true
8503
- };
8504
8523
  }
8505
8524
  return node;
8506
8525
  }
8507
- function transformBinaryExpression(node, scopeManager, namespace) {
8526
+ function getParamFromBinaryExpression(node, scopeManager, namespace) {
8508
8527
  const transformedLeft = transformOperand(node.left, scopeManager, namespace);
8509
8528
  const transformedRight = transformOperand(node.right, scopeManager, namespace);
8510
8529
  const binaryExpr = {
@@ -8525,28 +8544,89 @@ function transformBinaryExpression(node, scopeManager, namespace) {
8525
8544
  transformMemberExpression(node2, "", scopeManager);
8526
8545
  }
8527
8546
  });
8547
+ return binaryExpr;
8548
+ }
8549
+ function getParamFromLogicalExpression(node, scopeManager, namespace) {
8550
+ const transformedLeft = transformOperand(node.left, scopeManager, namespace);
8551
+ const transformedRight = transformOperand(node.right, scopeManager, namespace);
8552
+ const logicalExpr = {
8553
+ type: "LogicalExpression",
8554
+ operator: node.operator,
8555
+ left: transformedLeft,
8556
+ right: transformedRight,
8557
+ start: node.start,
8558
+ end: node.end
8559
+ };
8560
+ recursive(logicalExpr, scopeManager, {
8561
+ CallExpression(node2, scopeManager2) {
8562
+ if (!node2._transformed) {
8563
+ transformCallExpression(node2, scopeManager2);
8564
+ }
8565
+ }
8566
+ });
8567
+ return logicalExpr;
8568
+ }
8569
+ function getParamFromConditionalExpression(node, scopeManager, namespace) {
8570
+ recursive(
8571
+ node,
8572
+ { parent: node, inNamespaceCall: false },
8573
+ {
8574
+ Identifier(node2, state, c) {
8575
+ if (node2.name == "NaN") return;
8576
+ if (node2.name == "na") {
8577
+ node2.name = "NaN";
8578
+ return;
8579
+ }
8580
+ node2.parent = state.parent;
8581
+ transformIdentifier(node2, scopeManager);
8582
+ const isBinaryOperation = node2.parent && node2.parent.type === "BinaryExpression";
8583
+ const isConditional = node2.parent && node2.parent.type === "ConditionalExpression";
8584
+ if (isConditional || isBinaryOperation) {
8585
+ if (node2.type === "MemberExpression") {
8586
+ transformArrayIndex(node2, scopeManager);
8587
+ } else if (node2.type === "Identifier") {
8588
+ addArrayAccess(node2);
8589
+ }
8590
+ }
8591
+ },
8592
+ MemberExpression(node2, state, c) {
8593
+ transformArrayIndex(node2, scopeManager);
8594
+ if (node2.object) {
8595
+ c(node2.object, { parent: node2, inNamespaceCall: state.inNamespaceCall });
8596
+ }
8597
+ },
8598
+ CallExpression(node2, state, c) {
8599
+ const isNamespaceCall = node2.callee && node2.callee.type === "MemberExpression" && node2.callee.object && node2.callee.object.type === "Identifier" && scopeManager.isContextBound(node2.callee.object.name);
8600
+ transformCallExpression(node2, scopeManager);
8601
+ node2.arguments.forEach((arg) => c(arg, { parent: node2, inNamespaceCall: isNamespaceCall || state.inNamespaceCall }));
8602
+ }
8603
+ }
8604
+ );
8528
8605
  return {
8529
8606
  type: "CallExpression",
8530
8607
  callee: {
8531
8608
  type: "MemberExpression",
8532
- object: {
8533
- type: "Identifier",
8534
- name: namespace
8535
- },
8536
- property: {
8537
- type: "Identifier",
8538
- name: "param"
8539
- },
8540
- computed: false
8609
+ object: { type: "Identifier", name: namespace },
8610
+ property: { type: "Identifier", name: "param" }
8541
8611
  },
8542
- arguments: [binaryExpr, UNDEFINED_ARG, scopeManager.nextParamIdArg],
8612
+ arguments: [node, UNDEFINED_ARG, scopeManager.nextParamIdArg],
8543
8613
  _transformed: true,
8544
8614
  _isParamCall: true
8545
8615
  };
8546
8616
  }
8547
8617
  function transformFunctionArgument(arg, namespace, scopeManager) {
8548
- if (arg.type === "BinaryExpression") {
8549
- return transformBinaryExpression(arg, scopeManager, namespace);
8618
+ switch (arg?.type) {
8619
+ case "BinaryExpression":
8620
+ arg = getParamFromBinaryExpression(arg, scopeManager, namespace);
8621
+ break;
8622
+ case "LogicalExpression":
8623
+ arg = getParamFromLogicalExpression(arg, scopeManager, namespace);
8624
+ break;
8625
+ case "ConditionalExpression":
8626
+ return getParamFromConditionalExpression(arg, scopeManager, namespace);
8627
+ case "UnaryExpression":
8628
+ arg = getParamFromUnaryExpression(arg, scopeManager, namespace);
8629
+ break;
8550
8630
  }
8551
8631
  const isArrayAccess = arg.type === "MemberExpression" && arg.computed && arg.property;
8552
8632
  if (isArrayAccess) {
@@ -8611,6 +8691,10 @@ function transformFunctionArgument(arg, namespace, scopeManager) {
8611
8691
  });
8612
8692
  }
8613
8693
  if (arg.type === "Identifier") {
8694
+ if (arg.name === "na") {
8695
+ arg.name = "NaN";
8696
+ return arg;
8697
+ }
8614
8698
  if (scopeManager.isContextBound(arg.name) && !scopeManager.isRootParam(arg.name)) {
8615
8699
  return {
8616
8700
  type: "CallExpression",
@@ -8654,18 +8738,18 @@ function transformFunctionArgument(arg, namespace, scopeManager) {
8654
8738
  _isParamCall: true
8655
8739
  };
8656
8740
  }
8657
- function transformCallExpression(node, scopeManager) {
8741
+ function transformCallExpression(node, scopeManager, namespace) {
8658
8742
  if (node._transformed) {
8659
8743
  return;
8660
8744
  }
8661
8745
  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");
8662
8746
  if (isNamespaceCall) {
8663
- const namespace = node.callee.object.name;
8747
+ const namespace2 = node.callee.object.name;
8664
8748
  node.arguments = node.arguments.map((arg) => {
8665
8749
  if (arg._isParamCall) {
8666
8750
  return arg;
8667
8751
  }
8668
- return transformFunctionArgument(arg, namespace, scopeManager);
8752
+ return transformFunctionArgument(arg, namespace2, scopeManager);
8669
8753
  });
8670
8754
  node._transformed = true;
8671
8755
  } else if (node.callee && node.callee.type === "Identifier") {
@@ -8679,13 +8763,29 @@ function transformCallExpression(node, scopeManager) {
8679
8763
  }
8680
8764
  node.arguments.forEach((arg) => {
8681
8765
  recursive(arg, scopeManager, {
8682
- CallExpression(node2, state) {
8766
+ Identifier(node2, state, c) {
8767
+ node2.parent = state.parent;
8768
+ transformIdentifier(node2, scopeManager);
8769
+ const isBinaryOperation = node2.parent && node2.parent.type === "BinaryExpression";
8770
+ const isConditional = node2.parent && node2.parent.type === "ConditionalExpression";
8771
+ if (isConditional || isBinaryOperation) {
8772
+ if (node2.type === "MemberExpression") {
8773
+ transformArrayIndex(node2, scopeManager);
8774
+ } else if (node2.type === "Identifier") {
8775
+ addArrayAccess(node2);
8776
+ }
8777
+ }
8778
+ },
8779
+ CallExpression(node2, state, c) {
8683
8780
  if (!node2._transformed) {
8684
8781
  transformCallExpression(node2, state);
8685
8782
  }
8686
8783
  },
8687
- MemberExpression(node2) {
8784
+ MemberExpression(node2, state, c) {
8688
8785
  transformMemberExpression(node2, "", scopeManager);
8786
+ if (node2.object) {
8787
+ c(node2.object, { parent: node2, inNamespaceCall: state.inNamespaceCall });
8788
+ }
8689
8789
  }
8690
8790
  });
8691
8791
  });
@@ -9118,13 +9218,22 @@ class PineTS {
9118
9218
  if (!this._readyPromise) throw new Error("PineTS is not ready");
9119
9219
  return this._readyPromise;
9120
9220
  }
9121
- async run(fn, n) {
9221
+ async run(pineTSCode, n, useTACache) {
9122
9222
  await this.ready();
9123
9223
  if (!n) n = this._periods;
9124
- const context = new Context(this.data);
9125
- context.timeframe = this.timeframe;
9224
+ const context = new Context({
9225
+ marketData: this.data,
9226
+ source: this.source,
9227
+ tickerId: this.tickerId,
9228
+ timeframe: this.timeframe,
9229
+ limit: this.limit,
9230
+ sDate: this.sDate,
9231
+ eDate: this.eDate
9232
+ });
9233
+ context.pineTSCode = pineTSCode;
9234
+ context.useTACache = useTACache;
9126
9235
  const transformer = transpile.bind(this);
9127
- let transformedFn = transformer(fn);
9236
+ let transpiledFn = transformer(pineTSCode);
9128
9237
  const contextVarNames = ["const", "var", "let", "params"];
9129
9238
  for (let i = this._periods - n, idx = n - 1; i < this._periods; i++, idx--) {
9130
9239
  context.idx = i;
@@ -9138,7 +9247,7 @@ class PineTS {
9138
9247
  context.data.ohlc4 = this.ohlc4.slice(idx);
9139
9248
  context.data.openTime = this.openTime.slice(idx);
9140
9249
  context.data.closeTime = this.closeTime.slice(idx);
9141
- const result = await transformedFn(context);
9250
+ const result = await transpiledFn(context);
9142
9251
  if (typeof result === "object") {
9143
9252
  if (typeof context.result !== "object") {
9144
9253
  context.result = {};
@@ -9169,26 +9278,28 @@ class PineTS {
9169
9278
  }
9170
9279
  }
9171
9280
 
9172
- /*
9173
- * Copyright (C) 2025 Alaa-eddine KADDOURI
9174
- *
9175
- * This program is free software: you can redistribute it and/or modify
9176
- * it under the terms of the GNU Affero General Public License as published by
9177
- * the Free Software Foundation, either version 3 of the License, or
9178
- * (at your option) any later version.
9179
- *
9180
- * This program is distributed in the hope that it will be useful,
9181
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
9182
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9183
- * GNU Affero General Public License for more details.
9184
- *
9185
- * You should have received a copy of the GNU Affero General Public License
9186
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
9187
- */class Core {
9281
+ class Core {
9188
9282
  constructor(context) {
9189
9283
  this.context = context;
9190
9284
  }
9191
9285
  color = {
9286
+ param: (source, index = 0) => {
9287
+ if (Array.isArray(source)) {
9288
+ return source[index];
9289
+ }
9290
+ return source;
9291
+ },
9292
+ rgb: (r, g, b, a) => a ? `rgba(${r}, ${g}, ${b}, ${a})` : `rgb(${r}, ${g}, ${b})`,
9293
+ new: (color, a) => {
9294
+ if (color && color.startsWith("#")) {
9295
+ const hex = color.slice(1);
9296
+ const r = parseInt(hex.slice(0, 2), 16);
9297
+ const g = parseInt(hex.slice(2, 4), 16);
9298
+ const b = parseInt(hex.slice(4, 6), 16);
9299
+ return a ? `rgba(${r}, ${g}, ${b}, ${a})` : `rgb(${r}, ${g}, ${b})`;
9300
+ }
9301
+ return a ? `rgba(${color}, ${a})` : color;
9302
+ },
9192
9303
  white: "white",
9193
9304
  lime: "lime",
9194
9305
  green: "green",
@@ -9219,7 +9330,7 @@ class PineTS {
9219
9330
  this.context.plots[title].data.push({
9220
9331
  time: this.context.marketData[this.context.marketData.length - this.context.idx - 1].openTime,
9221
9332
  value: series[0],
9222
- options: this.extractPlotOptions(options)
9333
+ options: { ...this.extractPlotOptions(options), style: "char" }
9223
9334
  });
9224
9335
  }
9225
9336
  plot(series, title, options) {
@@ -9236,30 +9347,13 @@ class PineTS {
9236
9347
  return Array.isArray(series) ? isNaN(series[0]) : isNaN(series);
9237
9348
  }
9238
9349
  nz(series, replacement = 0) {
9239
- if (Array.isArray(series)) {
9240
- return isNaN(series[0]) ? replacement : series[0];
9241
- } else {
9242
- return isNaN(series) ? replacement : series;
9243
- }
9350
+ const val = Array.isArray(series) ? series[0] : series;
9351
+ const rep = Array.isArray(series) ? replacement[0] : replacement;
9352
+ return isNaN(val) ? rep : val;
9244
9353
  }
9245
9354
  }
9246
9355
 
9247
- /*
9248
- * Copyright (C) 2025 Alaa-eddine KADDOURI
9249
- *
9250
- * This program is free software: you can redistribute it and/or modify
9251
- * it under the terms of the GNU Affero General Public License as published by
9252
- * the Free Software Foundation, either version 3 of the License, or
9253
- * (at your option) any later version.
9254
- *
9255
- * This program is distributed in the hope that it will be useful,
9256
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
9257
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9258
- * GNU Affero General Public License for more details.
9259
- *
9260
- * You should have received a copy of the GNU Affero General Public License
9261
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
9262
- */class Input {
9356
+ class Input {
9263
9357
  constructor(context) {
9264
9358
  this.context = context;
9265
9359
  }
@@ -9313,87 +9407,92 @@ class PineTS {
9313
9407
  }
9314
9408
  }
9315
9409
 
9316
- /*
9317
- * Copyright (C) 2025 Alaa-eddine KADDOURI
9318
- *
9319
- * This program is free software: you can redistribute it and/or modify
9320
- * it under the terms of the GNU Affero General Public License as published by
9321
- * the Free Software Foundation, either version 3 of the License, or
9322
- * (at your option) any later version.
9323
- *
9324
- * This program is distributed in the hope that it will be useful,
9325
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
9326
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9327
- * GNU Affero General Public License for more details.
9328
- *
9329
- * You should have received a copy of the GNU Affero General Public License
9330
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
9331
- */class PineMath {
9410
+ class PineMath {
9332
9411
  constructor(context) {
9333
9412
  this.context = context;
9334
9413
  }
9335
9414
  _cache = {};
9336
- param(source, index = 0) {
9415
+ param(source, index, name) {
9416
+ if (!this.context.params[name]) this.context.params[name] = [];
9337
9417
  if (Array.isArray(source)) {
9338
- return source[index];
9418
+ if (index) {
9419
+ this.context.params[name] = source.slice(index);
9420
+ this.context.params[name].length = source.length;
9421
+ return this.context.params[name];
9422
+ }
9423
+ this.context.params[name] = source.slice(0);
9424
+ return this.context.params[name];
9425
+ } else {
9426
+ this.context.params[name][0] = source;
9427
+ return this.context.params[name];
9339
9428
  }
9340
- return source;
9341
9429
  }
9342
- abs(n) {
9343
- return Math.abs(n);
9430
+ abs(source) {
9431
+ return Math.abs(source[0]);
9344
9432
  }
9345
- pow(a, b) {
9346
- return Math.pow(a, b);
9433
+ pow(source, power) {
9434
+ return Math.pow(source[0], power[0]);
9347
9435
  }
9348
- sqrt(a) {
9349
- return Math.sqrt(a);
9436
+ sqrt(source) {
9437
+ return Math.sqrt(source[0]);
9350
9438
  }
9351
- log(a) {
9352
- return Math.log(a);
9439
+ log(source) {
9440
+ return Math.log(source[0]);
9353
9441
  }
9354
- ln(a) {
9355
- return Math.log(a);
9442
+ ln(source) {
9443
+ return Math.log(source[0]);
9356
9444
  }
9357
- exp(a) {
9358
- return Math.exp(a);
9445
+ exp(source) {
9446
+ return Math.exp(source[0]);
9359
9447
  }
9360
- floor(a) {
9361
- return Math.floor(a);
9448
+ floor(source) {
9449
+ return Math.floor(source[0]);
9362
9450
  }
9363
- ceil(a) {
9364
- return Math.ceil(a);
9451
+ ceil(source) {
9452
+ return Math.ceil(source[0]);
9365
9453
  }
9366
- round(a) {
9367
- return Math.round(a);
9454
+ round(source) {
9455
+ return Math.round(source[0]);
9368
9456
  }
9369
9457
  random() {
9370
9458
  return Math.random();
9371
9459
  }
9372
- max(...args) {
9373
- return Math.max(...args);
9460
+ max(...source) {
9461
+ const arg = source.map((e) => Array.isArray(e) ? e[0] : e);
9462
+ return Math.max(...arg);
9374
9463
  }
9375
- min(...args) {
9376
- return Math.min(...args);
9464
+ min(...source) {
9465
+ const arg = source.map((e) => Array.isArray(e) ? e[0] : e);
9466
+ return Math.min(...arg);
9377
9467
  }
9378
- sin(a) {
9379
- return Math.sin(a);
9468
+ //sum of last n values
9469
+ sum(source, length) {
9470
+ const len = Array.isArray(length) ? length[0] : length;
9471
+ if (Array.isArray(source)) {
9472
+ return source.slice(0, len).reduce((a, b) => a + b, 0);
9473
+ }
9474
+ return source;
9380
9475
  }
9381
- cos(a) {
9382
- return Math.cos(a);
9476
+ sin(source) {
9477
+ return Math.sin(source[0]);
9383
9478
  }
9384
- tan(a) {
9385
- return Math.tan(a);
9479
+ cos(source) {
9480
+ return Math.cos(source[0]);
9386
9481
  }
9387
- asin(a) {
9388
- return Math.asin(a);
9482
+ tan(source) {
9483
+ return Math.tan(source[0]);
9389
9484
  }
9390
- acos(a) {
9391
- return Math.acos(a);
9485
+ acos(source) {
9486
+ return Math.acos(source[0]);
9392
9487
  }
9393
- atan(a) {
9394
- return Math.atan(a);
9488
+ asin(source) {
9489
+ return Math.asin(source[0]);
9395
9490
  }
9396
- avg(...args) {
9491
+ atan(source) {
9492
+ return Math.atan(source[0]);
9493
+ }
9494
+ avg(...sources) {
9495
+ const args = sources.map((e) => Array.isArray(e) ? e[0] : e);
9397
9496
  return args.reduce((a, b) => {
9398
9497
  const aVal = Array.isArray(a) ? a[0] : a;
9399
9498
  const bVal = Array.isArray(b) ? b[0] : b;
@@ -9402,22 +9501,8 @@ class PineTS {
9402
9501
  }
9403
9502
  }
9404
9503
 
9405
- /*
9406
- * Copyright (C) 2025 Alaa-eddine KADDOURI
9407
- *
9408
- * This program is free software: you can redistribute it and/or modify
9409
- * it under the terms of the GNU Affero General Public License as published by
9410
- * the Free Software Foundation, either version 3 of the License, or
9411
- * (at your option) any later version.
9412
- *
9413
- * This program is distributed in the hope that it will be useful,
9414
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
9415
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9416
- * GNU Affero General Public License for more details.
9417
- *
9418
- * You should have received a copy of the GNU Affero General Public License
9419
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
9420
- */class PineRequest {
9504
+ const TIMEFRAMES = ["1", "3", "5", "15", "30", "45", "60", "120", "180", "240", "D", "W", "M"];
9505
+ class PineRequest {
9421
9506
  constructor(context) {
9422
9507
  this.context = context;
9423
9508
  }
@@ -9436,27 +9521,46 @@ class PineTS {
9436
9521
  return [source, name];
9437
9522
  }
9438
9523
  }
9439
- async security(symbol, timeframe, expression) {
9440
- throw new Error("Not implemented");
9524
+ async security(symbol, timeframe, expression, gaps = false, lookahead = false, ignore_invalid_symbol = false, currency = null, calc_bars_count = null) {
9525
+ const _symbol = symbol[0];
9526
+ const _timeframe = timeframe[0];
9527
+ const _expression = expression[0];
9528
+ const _expression_name = expression[1];
9529
+ const ctxTimeframeIdx = TIMEFRAMES.indexOf(this.context.timeframe);
9530
+ const reqTimeframeIdx = TIMEFRAMES.indexOf(_timeframe);
9531
+ if (ctxTimeframeIdx == -1 || reqTimeframeIdx == -1) {
9532
+ throw new Error("Invalid timeframe");
9533
+ }
9534
+ if (ctxTimeframeIdx > reqTimeframeIdx) {
9535
+ throw new Error("Only higher timeframes are supported for now");
9536
+ }
9537
+ if (ctxTimeframeIdx === reqTimeframeIdx) {
9538
+ return _expression;
9539
+ }
9540
+ const myOpenTime = this.context.data.openTime[0];
9541
+ const myCloseTime = this.context.data.closeTime[0];
9542
+ if (this.context.cache[_expression_name]) {
9543
+ const secContext2 = this.context.cache[_expression_name];
9544
+ const secContextIdx2 = this._findSecContextIdx(myOpenTime, myCloseTime, secContext2.data.openTime, secContext2.data.closeTime, lookahead);
9545
+ return secContextIdx2 == -1 ? NaN : secContext2.params[_expression_name][secContextIdx2];
9546
+ }
9547
+ const pineTS = new PineTS(this.context.source, _symbol, _timeframe, this.context.limit || 1e3, this.context.sDate, this.context.eDate);
9548
+ const secContext = await pineTS.run(this.context.pineTSCode);
9549
+ this.context.cache[_expression_name] = secContext;
9550
+ const secContextIdx = this._findSecContextIdx(myOpenTime, myCloseTime, secContext.data.openTime, secContext.data.closeTime, lookahead);
9551
+ return secContextIdx == -1 ? NaN : secContext.params[_expression_name][secContextIdx];
9552
+ }
9553
+ _findSecContextIdx(myOpenTime, myCloseTime, openTime, closeTime, lookahead = false) {
9554
+ for (let i = 0; i < openTime.length; i++) {
9555
+ if (openTime[i] <= myOpenTime && myCloseTime <= closeTime[i]) {
9556
+ return i + (lookahead ? 1 : 2);
9557
+ }
9558
+ }
9559
+ return -1;
9441
9560
  }
9442
9561
  }
9443
9562
 
9444
- /*
9445
- * Copyright (C) 2025 Alaa-eddine KADDOURI
9446
- *
9447
- * This program is free software: you can redistribute it and/or modify
9448
- * it under the terms of the GNU Affero General Public License as published by
9449
- * the Free Software Foundation, either version 3 of the License, or
9450
- * (at your option) any later version.
9451
- *
9452
- * This program is distributed in the hope that it will be useful,
9453
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
9454
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9455
- * GNU Affero General Public License for more details.
9456
- *
9457
- * You should have received a copy of the GNU Affero General Public License
9458
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
9459
- */class TechnicalAnalysis {
9563
+ class TechnicalAnalysis {
9460
9564
  constructor(context) {
9461
9565
  this.context = context;
9462
9566
  }
@@ -9473,6 +9577,7 @@ class PineTS {
9473
9577
  if (Array.isArray(source)) {
9474
9578
  if (index) {
9475
9579
  this.context.params[name] = source.slice(index);
9580
+ this.context.params[name].length = source.length;
9476
9581
  return this.context.params[name];
9477
9582
  }
9478
9583
  this.context.params[name] = source.slice(0);
@@ -9488,9 +9593,21 @@ class PineTS {
9488
9593
  const idx = this.context.idx;
9489
9594
  return this.context.precision(result[idx]);
9490
9595
  }
9491
- sma(source, _period) {
9596
+ sma(source, _period, _cacheId) {
9492
9597
  const period = Array.isArray(_period) ? _period[0] : _period;
9493
- const result = sma(source.slice(0).reverse(), period);
9598
+ const reversedSource = source.slice(0).reverse();
9599
+ if (this.context.useTACache && _cacheId) {
9600
+ if (!this.context.cache[_cacheId]) {
9601
+ this.context.cache[_cacheId] = {};
9602
+ }
9603
+ const cacheObj = this.context.cache[_cacheId];
9604
+ if (cacheObj) {
9605
+ const result2 = sma_cache(reversedSource, period, cacheObj);
9606
+ const idx2 = this.context.idx;
9607
+ return this.context.precision(result2[idx2]);
9608
+ }
9609
+ }
9610
+ const result = sma(reversedSource, period);
9494
9611
  const idx = this.context.idx;
9495
9612
  return this.context.precision(result[idx]);
9496
9613
  }
@@ -9606,6 +9723,44 @@ class PineTS {
9606
9723
  const idx = this.context.idx;
9607
9724
  return [[this.context.precision(supertrend[idx]), direction[idx]]];
9608
9725
  }
9726
+ crossover(source1, source2) {
9727
+ const current1 = Array.isArray(source1) ? source1[0] : source1;
9728
+ const current2 = Array.isArray(source2) ? source2[0] : source2;
9729
+ const prev1 = Array.isArray(source1) ? source1[1] : this.context.data.series[source1][1];
9730
+ const prev2 = Array.isArray(source2) ? source2[1] : this.context.data.series[source2][1];
9731
+ return prev1 < prev2 && current1 > current2;
9732
+ }
9733
+ crossunder(source1, source2) {
9734
+ const current1 = Array.isArray(source1) ? source1[0] : source1;
9735
+ const current2 = Array.isArray(source2) ? source2[0] : source2;
9736
+ const prev1 = Array.isArray(source1) ? source1[1] : this.context.data.series[source1][1];
9737
+ const prev2 = Array.isArray(source2) ? source2[1] : this.context.data.series[source2][1];
9738
+ return prev1 > prev2 && current1 < current2;
9739
+ }
9740
+ pivothigh(source, _leftbars, _rightbars) {
9741
+ if (_rightbars == void 0) {
9742
+ _rightbars = _leftbars;
9743
+ _leftbars = source;
9744
+ source = this.context.data.high;
9745
+ }
9746
+ const leftbars = Array.isArray(_leftbars) ? _leftbars[0] : _leftbars;
9747
+ const rightbars = Array.isArray(_rightbars) ? _rightbars[0] : _rightbars;
9748
+ const result = pivothigh(source.slice(0).reverse(), leftbars, rightbars);
9749
+ const idx = this.context.idx;
9750
+ return this.context.precision(result[idx]);
9751
+ }
9752
+ pivotlow(source, _leftbars, _rightbars) {
9753
+ if (_rightbars == void 0) {
9754
+ _rightbars = _leftbars;
9755
+ _leftbars = source;
9756
+ source = this.context.data.low;
9757
+ }
9758
+ const leftbars = Array.isArray(_leftbars) ? _leftbars[0] : _leftbars;
9759
+ const rightbars = Array.isArray(_rightbars) ? _rightbars[0] : _rightbars;
9760
+ const result = pivotlow(source.slice(0).reverse(), leftbars, rightbars);
9761
+ const idx = this.context.idx;
9762
+ return this.context.precision(result[idx]);
9763
+ }
9609
9764
  }
9610
9765
  function atr(high, low, close, period) {
9611
9766
  const tr = new Array(high.length);
@@ -9679,6 +9834,35 @@ function rma(source, period) {
9679
9834
  }
9680
9835
  return result;
9681
9836
  }
9837
+ function sma_cache(source, period, cacheObj) {
9838
+ const result = cacheObj.previousResult || new Array(source.length).fill(NaN);
9839
+ const lastProcessedIndex = cacheObj.lastProcessedIndex || -1;
9840
+ let previousSum = cacheObj.previousSum || 0;
9841
+ if (lastProcessedIndex === -1 || source.length !== lastProcessedIndex + 1) {
9842
+ previousSum = 0;
9843
+ for (let i = 0; i < period; i++) {
9844
+ previousSum += source[i] || 0;
9845
+ }
9846
+ result[period - 1] = previousSum / period;
9847
+ for (let i = 0; i < period - 1; i++) {
9848
+ result[i] = NaN;
9849
+ }
9850
+ for (let i = period; i < source.length; i++) {
9851
+ previousSum = previousSum - (source[i - period] || 0) + (source[i] || 0);
9852
+ result[i] = previousSum / period;
9853
+ }
9854
+ } else if (source.length === lastProcessedIndex + 2) {
9855
+ const newIndex = source.length - 1;
9856
+ previousSum = previousSum - (source[newIndex - period] || 0) + (source[newIndex] || 0);
9857
+ result[newIndex] = previousSum / period;
9858
+ } else {
9859
+ return sma(source, period);
9860
+ }
9861
+ cacheObj.previousSum = previousSum;
9862
+ cacheObj.lastProcessedIndex = source.length - 1;
9863
+ cacheObj.previousResult = result;
9864
+ return result;
9865
+ }
9682
9866
  function sma(source, period) {
9683
9867
  const result = new Array(source.length).fill(NaN);
9684
9868
  for (let i = period - 1; i < source.length; i++) {
@@ -9797,7 +9981,7 @@ function lowest(source, length) {
9797
9981
  let min = Infinity;
9798
9982
  for (let j = 0; j < length; j++) {
9799
9983
  const value = source[i - j];
9800
- if (isNaN(value)) {
9984
+ if (isNaN(value) || value === void 0) {
9801
9985
  min = min === Infinity ? NaN : min;
9802
9986
  } else {
9803
9987
  min = Math.min(min, value);
@@ -9916,23 +10100,247 @@ function calculateSupertrend(high, low, close, factor, atrPeriod) {
9916
10100
  }
9917
10101
  return [supertrend, direction];
9918
10102
  }
10103
+ function pivothigh(source, leftbars, rightbars) {
10104
+ const result = new Array(source.length).fill(NaN);
10105
+ for (let i = leftbars + rightbars; i < source.length; i++) {
10106
+ const pivot = source[i - rightbars];
10107
+ let isPivot = true;
10108
+ for (let j = 1; j <= leftbars; j++) {
10109
+ if (source[i - rightbars - j] >= pivot) {
10110
+ isPivot = false;
10111
+ break;
10112
+ }
10113
+ }
10114
+ if (isPivot) {
10115
+ for (let j = 1; j <= rightbars; j++) {
10116
+ if (source[i - rightbars + j] >= pivot) {
10117
+ isPivot = false;
10118
+ break;
10119
+ }
10120
+ }
10121
+ }
10122
+ if (isPivot) {
10123
+ result[i] = pivot;
10124
+ }
10125
+ }
10126
+ return result;
10127
+ }
10128
+ function pivotlow(source, leftbars, rightbars) {
10129
+ const result = new Array(source.length).fill(NaN);
10130
+ for (let i = leftbars + rightbars; i < source.length; i++) {
10131
+ const pivot = source[i - rightbars];
10132
+ let isPivot = true;
10133
+ for (let j = 1; j <= leftbars; j++) {
10134
+ if (source[i - rightbars - j] <= pivot) {
10135
+ isPivot = false;
10136
+ break;
10137
+ }
10138
+ }
10139
+ if (isPivot) {
10140
+ for (let j = 1; j <= rightbars; j++) {
10141
+ if (source[i - rightbars + j] <= pivot) {
10142
+ isPivot = false;
10143
+ break;
10144
+ }
10145
+ }
10146
+ }
10147
+ if (isPivot) {
10148
+ result[i] = pivot;
10149
+ }
10150
+ }
10151
+ return result;
10152
+ }
9919
10153
 
9920
- class Context {
9921
- constructor(marketData) {
9922
- this.marketData = marketData;
9923
- this.math = new PineMath(this);
9924
- this.ta = new TechnicalAnalysis(this);
9925
- this.input = new Input(this);
9926
- this.request = new PineRequest(this);
9927
- const core = new Core(this);
9928
- this.core = {
9929
- plotchar: core.plotchar.bind(core),
9930
- na: core.na.bind(core),
9931
- color: core.color,
9932
- plot: core.plot.bind(core),
9933
- nz: core.nz.bind(core)
9934
- };
10154
+ class PineArrayObject {
10155
+ constructor(array) {
10156
+ this.array = array;
10157
+ }
10158
+ }
10159
+ class PineArray {
10160
+ constructor(context) {
10161
+ this.context = context;
9935
10162
  }
10163
+ _cache = {};
10164
+ param(source, index = 0) {
10165
+ if (Array.isArray(source)) {
10166
+ return source[index];
10167
+ }
10168
+ return source;
10169
+ }
10170
+ /**
10171
+ * This function simulates PineScript's array.get() function
10172
+ * @param id - the array object to get the value from
10173
+ * @param index - the index of the value to get
10174
+ * @returns the value at the given index
10175
+ */
10176
+ get(id, index) {
10177
+ return id.array[index];
10178
+ }
10179
+ set(id, index, value) {
10180
+ id.array[index] = value;
10181
+ }
10182
+ push(id, value) {
10183
+ id.array.push(value);
10184
+ }
10185
+ // Basic statistics
10186
+ sum(id) {
10187
+ return id.array.reduce((a, b) => a + (isNaN(b) ? 0 : b), 0);
10188
+ }
10189
+ avg(id) {
10190
+ return this.sum(id) / id.array.length;
10191
+ }
10192
+ min(id, nth = 0) {
10193
+ const sorted = [...id.array].sort((a, b) => a - b);
10194
+ return sorted[nth] ?? this.context.NA;
10195
+ }
10196
+ max(id, nth = 0) {
10197
+ const sorted = [...id.array].sort((a, b) => b - a);
10198
+ return sorted[nth] ?? this.context.NA;
10199
+ }
10200
+ size(id) {
10201
+ return id.array.length;
10202
+ }
10203
+ // Array creation
10204
+ new_bool(size, initial_value = false) {
10205
+ return new PineArrayObject(Array(size).fill(initial_value));
10206
+ }
10207
+ new_float(size, initial_value = NaN) {
10208
+ return new PineArrayObject(Array(size).fill(initial_value));
10209
+ }
10210
+ new_int(size, initial_value = 0) {
10211
+ return new PineArrayObject(Array(size).fill(Math.round(initial_value)));
10212
+ }
10213
+ new_string(size, initial_value = "") {
10214
+ return new PineArrayObject(Array(size).fill(initial_value));
10215
+ }
10216
+ new(size, initial_value) {
10217
+ return new PineArrayObject(Array(size).fill(initial_value));
10218
+ }
10219
+ // Array operations
10220
+ slice(id, start, end) {
10221
+ const adjustedEnd = end !== void 0 ? end + 1 : void 0;
10222
+ return new PineArrayObject(id.array.slice(start, adjustedEnd));
10223
+ }
10224
+ reverse(id) {
10225
+ id.array.reverse();
10226
+ }
10227
+ includes(id, value) {
10228
+ return id.array.includes(value);
10229
+ }
10230
+ indexof(id, value) {
10231
+ return id.array.indexOf(value);
10232
+ }
10233
+ lastindexof(id, value) {
10234
+ return id.array.lastIndexOf(value);
10235
+ }
10236
+ // More complex functions
10237
+ stdev(id, biased = true) {
10238
+ const mean = this.avg(id);
10239
+ const deviations = id.array.map((x) => Math.pow(x - mean, 2));
10240
+ const divisor = biased ? id.array.length : id.array.length - 1;
10241
+ return Math.sqrt(this.sum(new PineArrayObject(deviations)) / divisor);
10242
+ }
10243
+ variance(id, biased = true) {
10244
+ const mean = this.avg(id);
10245
+ const deviations = id.array.map((x) => Math.pow(x - mean, 2));
10246
+ const divisor = biased ? id.array.length : id.array.length - 1;
10247
+ return this.sum(new PineArrayObject(deviations)) / divisor;
10248
+ }
10249
+ covariance(arr1, arr2, biased = true) {
10250
+ if (arr1.array.length !== arr2.array.length || arr1.array.length < 2) return NaN;
10251
+ const divisor = biased ? arr1.array.length : arr1.array.length - 1;
10252
+ const mean1 = this.avg(arr1);
10253
+ const mean2 = this.avg(arr2);
10254
+ let sum = 0;
10255
+ for (let i = 0; i < arr1.array.length; i++) {
10256
+ sum += (arr1.array[i] - mean1) * (arr2.array[i] - mean2);
10257
+ }
10258
+ return sum / divisor;
10259
+ }
10260
+ // Additional utility methods
10261
+ first(id) {
10262
+ return id.array.length > 0 ? id.array[0] : this.context.NA;
10263
+ }
10264
+ last(id) {
10265
+ return id.array.length > 0 ? id.array[id.array.length - 1] : this.context.NA;
10266
+ }
10267
+ clear(id) {
10268
+ id.array.length = 0;
10269
+ }
10270
+ join(id, separator = ",") {
10271
+ return id.array.join(separator);
10272
+ }
10273
+ /** Array Manipulation Functions */
10274
+ abs(id) {
10275
+ return new PineArrayObject(id.array.map((val) => Math.abs(val)));
10276
+ }
10277
+ concat(id, other) {
10278
+ id.array.push(...other.array);
10279
+ return id;
10280
+ }
10281
+ copy(id) {
10282
+ return new PineArrayObject([...id.array]);
10283
+ }
10284
+ every(id, callback) {
10285
+ return id.array.every(callback);
10286
+ }
10287
+ fill(id, value, start = 0, end) {
10288
+ const length = id.array.length;
10289
+ const adjustedEnd = end !== void 0 ? Math.min(end, length) : length;
10290
+ for (let i = start; i < adjustedEnd; i++) {
10291
+ id.array[i] = value;
10292
+ }
10293
+ }
10294
+ from(source) {
10295
+ return new PineArrayObject([...source]);
10296
+ }
10297
+ insert(id, index, value) {
10298
+ id.array.splice(index, 0, value);
10299
+ }
10300
+ pop(id) {
10301
+ return id.array.pop();
10302
+ }
10303
+ range(id) {
10304
+ return this.max(id) - this.min(id);
10305
+ }
10306
+ remove(id, index) {
10307
+ if (index >= 0 && index < id.array.length) {
10308
+ return id.array.splice(index, 1)[0];
10309
+ }
10310
+ return this.context.NA;
10311
+ }
10312
+ shift(id) {
10313
+ return id.array.shift();
10314
+ }
10315
+ sort(id, order = "asc") {
10316
+ id.array.sort((a, b) => order === "asc" ? a - b : b - a);
10317
+ }
10318
+ sort_indices(id, comparator) {
10319
+ const indices = id.array.map((_, index) => index);
10320
+ indices.sort((a, b) => {
10321
+ const valA = id.array[a];
10322
+ const valB = id.array[b];
10323
+ return comparator ? comparator(valA, valB) : valA - valB;
10324
+ });
10325
+ return new PineArrayObject(indices);
10326
+ }
10327
+ standardize(id) {
10328
+ const mean = this.avg(id);
10329
+ const stdev = this.stdev(id);
10330
+ if (stdev === 0) {
10331
+ return new PineArrayObject(id.array.map(() => 0));
10332
+ }
10333
+ return new PineArrayObject(id.array.map((x) => (x - mean) / stdev));
10334
+ }
10335
+ unshift(id, value) {
10336
+ id.array.unshift(value);
10337
+ }
10338
+ some(id, callback) {
10339
+ return id.array.some(callback);
10340
+ }
10341
+ }
10342
+
10343
+ class Context {
9936
10344
  data = {
9937
10345
  open: [],
9938
10346
  high: [],
@@ -9943,11 +10351,16 @@ class Context {
9943
10351
  hlc3: [],
9944
10352
  ohlc4: []
9945
10353
  };
10354
+ cache = {};
10355
+ useTACache = false;
10356
+ NA = NaN;
9946
10357
  math;
9947
10358
  ta;
9948
10359
  input;
9949
10360
  request;
10361
+ array;
9950
10362
  core;
10363
+ lang;
9951
10364
  idx = 0;
9952
10365
  params = {};
9953
10366
  const = {};
@@ -9955,7 +10368,44 @@ class Context {
9955
10368
  let = {};
9956
10369
  result = void 0;
9957
10370
  plots = {};
10371
+ marketData;
10372
+ source;
10373
+ tickerId;
9958
10374
  timeframe = "";
10375
+ limit;
10376
+ sDate;
10377
+ eDate;
10378
+ pineTSCode;
10379
+ constructor({
10380
+ marketData,
10381
+ source,
10382
+ tickerId,
10383
+ timeframe,
10384
+ limit,
10385
+ sDate,
10386
+ eDate
10387
+ }) {
10388
+ this.marketData = marketData;
10389
+ this.source = source;
10390
+ this.tickerId = tickerId;
10391
+ this.timeframe = timeframe;
10392
+ this.limit = limit;
10393
+ this.sDate = sDate;
10394
+ this.eDate = eDate;
10395
+ this.math = new PineMath(this);
10396
+ this.ta = new TechnicalAnalysis(this);
10397
+ this.input = new Input(this);
10398
+ this.request = new PineRequest(this);
10399
+ this.array = new PineArray(this);
10400
+ const core = new Core(this);
10401
+ this.core = {
10402
+ plotchar: core.plotchar.bind(core),
10403
+ na: core.na.bind(core),
10404
+ color: core.color,
10405
+ plot: core.plot.bind(core),
10406
+ nz: core.nz.bind(core)
10407
+ };
10408
+ }
9959
10409
  //#region [Runtime functions] ===========================
9960
10410
  /**
9961
10411
  * this function is used to initialize the target variable with the source array
@@ -9982,12 +10432,13 @@ class Context {
9982
10432
  return trg;
9983
10433
  }
9984
10434
  /**
9985
- * this function is used to set the floating point precision of a number
9986
- * by default it is set to 10 decimals which is the same as pine script
9987
- * @param n - the number to be precision
9988
- * @param decimals - the number of decimals to precision to
9989
- * @returns the precision number
9990
- */
10435
+ * this function is used to set the floating point precision of a number
10436
+ * by default it is set to 10 decimals which is the same as pine script
10437
+ * @param n - the number to be precision
10438
+ * @param decimals - the number of decimals to precision to
10439
+
10440
+ * @returns the precision number
10441
+ */
9991
10442
  precision(n, decimals = 10) {
9992
10443
  if (typeof n !== "number" || isNaN(n)) return n;
9993
10444
  return Number(n.toFixed(decimals));
@@ -10006,6 +10457,7 @@ class Context {
10006
10457
  if (Array.isArray(source)) {
10007
10458
  if (index) {
10008
10459
  this.params[name] = source.slice(index);
10460
+ this.params[name].length = source.length;
10009
10461
  return this.params[name];
10010
10462
  }
10011
10463
  this.params[name] = source.slice(0);
@@ -10018,22 +10470,7 @@ class Context {
10018
10470
  //#endregion
10019
10471
  }
10020
10472
 
10021
- /*
10022
- * Copyright (C) 2025 Alaa-eddine KADDOURI
10023
- *
10024
- * This program is free software: you can redistribute it and/or modify
10025
- * it under the terms of the GNU Affero General Public License as published by
10026
- * the Free Software Foundation, either version 3 of the License, or
10027
- * (at your option) any later version.
10028
- *
10029
- * This program is distributed in the hope that it will be useful,
10030
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
10031
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10032
- * GNU Affero General Public License for more details.
10033
- *
10034
- * You should have received a copy of the GNU Affero General Public License
10035
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
10036
- */const BINANCE_API_URL = "https://api.binance.com/api/v3";
10473
+ const BINANCE_API_URL = "https://api.binance.com/api/v3";
10037
10474
  const timeframe_to_binance = {
10038
10475
  "1": "1m",
10039
10476
  // 1 minute
@@ -10055,6 +10492,8 @@ const timeframe_to_binance = {
10055
10492
  // 3 hours (not directly supported by Binance, needs custom handling)
10056
10493
  "240": "4h",
10057
10494
  // 4 hours
10495
+ "4H": "4h",
10496
+ // 4 hours
10058
10497
  "1D": "1d",
10059
10498
  // 1 day
10060
10499
  D: "1d",
@@ -10068,15 +10507,119 @@ const timeframe_to_binance = {
10068
10507
  M: "1M"
10069
10508
  // 1 month
10070
10509
  };
10510
+ class CacheManager {
10511
+ cache;
10512
+ cacheDuration;
10513
+ constructor(cacheDuration = 5 * 60 * 1e3) {
10514
+ this.cache = /* @__PURE__ */ new Map();
10515
+ this.cacheDuration = cacheDuration;
10516
+ }
10517
+ generateKey(params) {
10518
+ return Object.entries(params).filter(([_, value]) => value !== void 0).map(([key, value]) => `${key}:${value}`).join("|");
10519
+ }
10520
+ get(params) {
10521
+ const key = this.generateKey(params);
10522
+ const cached = this.cache.get(key);
10523
+ if (!cached) return null;
10524
+ if (Date.now() - cached.timestamp > this.cacheDuration) {
10525
+ this.cache.delete(key);
10526
+ return null;
10527
+ }
10528
+ return cached.data;
10529
+ }
10530
+ set(params, data) {
10531
+ const key = this.generateKey(params);
10532
+ this.cache.set(key, {
10533
+ data,
10534
+ timestamp: Date.now()
10535
+ });
10536
+ }
10537
+ clear() {
10538
+ this.cache.clear();
10539
+ }
10540
+ // Optional: method to remove expired entries
10541
+ cleanup() {
10542
+ const now = Date.now();
10543
+ for (const [key, entry] of this.cache.entries()) {
10544
+ if (now - entry.timestamp > this.cacheDuration) {
10545
+ this.cache.delete(key);
10546
+ }
10547
+ }
10548
+ }
10549
+ }
10071
10550
  class BinanceProvider {
10551
+ cacheManager;
10552
+ constructor() {
10553
+ this.cacheManager = new CacheManager(5 * 60 * 1e3);
10554
+ }
10555
+ async getMarketDataInterval(tickerId, timeframe, sDate, eDate) {
10556
+ try {
10557
+ const interval = timeframe_to_binance[timeframe.toUpperCase()];
10558
+ if (!interval) {
10559
+ console.error(`Unsupported timeframe: ${timeframe}`);
10560
+ return [];
10561
+ }
10562
+ const timeframeDurations = {
10563
+ "1m": 60 * 1e3,
10564
+ "3m": 3 * 60 * 1e3,
10565
+ "5m": 5 * 60 * 1e3,
10566
+ "15m": 15 * 60 * 1e3,
10567
+ "30m": 30 * 60 * 1e3,
10568
+ "1h": 60 * 60 * 1e3,
10569
+ "2h": 2 * 60 * 60 * 1e3,
10570
+ "4h": 4 * 60 * 60 * 1e3,
10571
+ "1d": 24 * 60 * 60 * 1e3,
10572
+ "1w": 7 * 24 * 60 * 60 * 1e3,
10573
+ "1M": 30 * 24 * 60 * 60 * 1e3
10574
+ };
10575
+ let allData = [];
10576
+ let currentStart = sDate;
10577
+ const endTime = eDate;
10578
+ const intervalDuration = timeframeDurations[interval];
10579
+ if (!intervalDuration) {
10580
+ console.error(`Duration not defined for interval: ${interval}`);
10581
+ return [];
10582
+ }
10583
+ while (currentStart < endTime) {
10584
+ const chunkEnd = Math.min(currentStart + 1e3 * intervalDuration, endTime);
10585
+ const data = await this.getMarketData(
10586
+ tickerId,
10587
+ timeframe,
10588
+ 1e3,
10589
+ // Max allowed by Binance
10590
+ currentStart,
10591
+ chunkEnd
10592
+ );
10593
+ if (data.length === 0) break;
10594
+ allData = allData.concat(data);
10595
+ currentStart = data[data.length - 1].closeTime + 1;
10596
+ if (data.length < 1e3) break;
10597
+ }
10598
+ return allData;
10599
+ } catch (error) {
10600
+ console.error("Error in getMarketDataInterval:", error);
10601
+ return [];
10602
+ }
10603
+ }
10604
+ //TODO : allow querying more than 1000 klines
10605
+ //TODO : immplement cache
10072
10606
  async getMarketData(tickerId, timeframe, limit, sDate, eDate) {
10073
10607
  try {
10608
+ const cacheParams = { tickerId, timeframe, limit, sDate, eDate };
10609
+ const cachedData = this.cacheManager.get(cacheParams);
10610
+ if (cachedData) {
10611
+ console.log("cache hit", tickerId, timeframe, limit, sDate, eDate);
10612
+ return cachedData;
10613
+ }
10074
10614
  const interval = timeframe_to_binance[timeframe.toUpperCase()];
10075
10615
  if (!interval) {
10076
10616
  console.error(`Unsupported timeframe: ${timeframe}`);
10077
10617
  return [];
10078
10618
  }
10079
10619
  let url = `${BINANCE_API_URL}/klines?symbol=${tickerId}&interval=${interval}`;
10620
+ if (!limit && sDate && eDate) {
10621
+ return this.getMarketDataInterval(tickerId, timeframe, sDate, eDate);
10622
+ }
10080
10623
  if (limit) {
10081
10624
  url += `&limit=${limit}`;
10082
10625
  }
@@ -10093,13 +10636,13 @@ class BinanceProvider {
10093
10636
  const result = await response.json();
10094
10637
  const data = result.map((item) => {
10095
10638
  return {
10096
- openTime: parseInt(item[0]) / 1e3,
10639
+ openTime: parseInt(item[0]),
10097
10640
  open: parseFloat(item[1]),
10098
10641
  high: parseFloat(item[2]),
10099
10642
  low: parseFloat(item[3]),
10100
10643
  close: parseFloat(item[4]),
10101
10644
  volume: parseFloat(item[5]),
10102
- closeTime: parseInt(item[6]) / 1e3,
10645
+ closeTime: parseInt(item[6]),
10103
10646
  quoteAssetVolume: parseFloat(item[7]),
10104
10647
  numberOfTrades: parseInt(item[8]),
10105
10648
  takerBuyBaseAssetVolume: parseFloat(item[9]),
@@ -10107,6 +10650,7 @@ class BinanceProvider {
10107
10650
  ignore: item[11]
10108
10651
  };
10109
10652
  });
10653
+ this.cacheManager.set(cacheParams, data);
10110
10654
  return data;
10111
10655
  } catch (error) {
10112
10656
  console.error("Error in binance.klines:", error);
@@ -10117,6 +10661,7 @@ class BinanceProvider {
10117
10661
 
10118
10662
  const Provider = {
10119
10663
  Binance: new BinanceProvider()
10664
+ //TODO : add other providers (polygon, etc.)
10120
10665
  };
10121
10666
 
10122
10667
  exports.Context = Context;