pinets 0.1.1 → 0.1.31

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;
9475
+ }
9476
+ sin(source) {
9477
+ return Math.sin(source[0]);
9380
9478
  }
9381
- cos(a) {
9382
- return Math.cos(a);
9479
+ cos(source) {
9480
+ return Math.cos(source[0]);
9383
9481
  }
9384
- tan(a) {
9385
- return Math.tan(a);
9482
+ tan(source) {
9483
+ return Math.tan(source[0]);
9386
9484
  }
9387
- asin(a) {
9388
- return Math.asin(a);
9485
+ acos(source) {
9486
+ return Math.acos(source[0]);
9389
9487
  }
9390
- acos(a) {
9391
- return Math.acos(a);
9488
+ asin(source) {
9489
+ return Math.asin(source[0]);
9392
9490
  }
9393
- atan(a) {
9394
- return Math.atan(a);
9491
+ atan(source) {
9492
+ return Math.atan(source[0]);
9395
9493
  }
9396
- avg(...args) {
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
  }
@@ -9679,6 +9796,35 @@ function rma(source, period) {
9679
9796
  }
9680
9797
  return result;
9681
9798
  }
9799
+ function sma_cache(source, period, cacheObj) {
9800
+ const result = cacheObj.previousResult || new Array(source.length).fill(NaN);
9801
+ const lastProcessedIndex = cacheObj.lastProcessedIndex || -1;
9802
+ let previousSum = cacheObj.previousSum || 0;
9803
+ if (lastProcessedIndex === -1 || source.length !== lastProcessedIndex + 1) {
9804
+ previousSum = 0;
9805
+ for (let i = 0; i < period; i++) {
9806
+ previousSum += source[i] || 0;
9807
+ }
9808
+ result[period - 1] = previousSum / period;
9809
+ for (let i = 0; i < period - 1; i++) {
9810
+ result[i] = NaN;
9811
+ }
9812
+ for (let i = period; i < source.length; i++) {
9813
+ previousSum = previousSum - (source[i - period] || 0) + (source[i] || 0);
9814
+ result[i] = previousSum / period;
9815
+ }
9816
+ } else if (source.length === lastProcessedIndex + 2) {
9817
+ const newIndex = source.length - 1;
9818
+ previousSum = previousSum - (source[newIndex - period] || 0) + (source[newIndex] || 0);
9819
+ result[newIndex] = previousSum / period;
9820
+ } else {
9821
+ return sma(source, period);
9822
+ }
9823
+ cacheObj.previousSum = previousSum;
9824
+ cacheObj.lastProcessedIndex = source.length - 1;
9825
+ cacheObj.previousResult = result;
9826
+ return result;
9827
+ }
9682
9828
  function sma(source, period) {
9683
9829
  const result = new Array(source.length).fill(NaN);
9684
9830
  for (let i = period - 1; i < source.length; i++) {
@@ -9797,7 +9943,7 @@ function lowest(source, length) {
9797
9943
  let min = Infinity;
9798
9944
  for (let j = 0; j < length; j++) {
9799
9945
  const value = source[i - j];
9800
- if (isNaN(value)) {
9946
+ if (isNaN(value) || value === void 0) {
9801
9947
  min = min === Infinity ? NaN : min;
9802
9948
  } else {
9803
9949
  min = Math.min(min, value);
@@ -9917,22 +10063,196 @@ function calculateSupertrend(high, low, close, factor, atrPeriod) {
9917
10063
  return [supertrend, direction];
9918
10064
  }
9919
10065
 
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
- };
10066
+ class PineArrayObject {
10067
+ constructor(array) {
10068
+ this.array = array;
10069
+ }
10070
+ }
10071
+ class PineArray {
10072
+ constructor(context) {
10073
+ this.context = context;
10074
+ }
10075
+ _cache = {};
10076
+ param(source, index = 0) {
10077
+ if (Array.isArray(source)) {
10078
+ return source[index];
10079
+ }
10080
+ return source;
10081
+ }
10082
+ /**
10083
+ * This function simulates PineScript's array.get() function
10084
+ * @param id - the array object to get the value from
10085
+ * @param index - the index of the value to get
10086
+ * @returns the value at the given index
10087
+ */
10088
+ get(id, index) {
10089
+ return id.array[index];
10090
+ }
10091
+ set(id, index, value) {
10092
+ id.array[index] = value;
10093
+ }
10094
+ push(id, value) {
10095
+ id.array.push(value);
10096
+ }
10097
+ // Basic statistics
10098
+ sum(id) {
10099
+ return id.array.reduce((a, b) => a + (isNaN(b) ? 0 : b), 0);
9935
10100
  }
10101
+ avg(id) {
10102
+ return this.sum(id) / id.array.length;
10103
+ }
10104
+ min(id, nth = 0) {
10105
+ const sorted = [...id.array].sort((a, b) => a - b);
10106
+ return sorted[nth] ?? this.context.NA;
10107
+ }
10108
+ max(id, nth = 0) {
10109
+ const sorted = [...id.array].sort((a, b) => b - a);
10110
+ return sorted[nth] ?? this.context.NA;
10111
+ }
10112
+ size(id) {
10113
+ return id.array.length;
10114
+ }
10115
+ // Array creation
10116
+ new_bool(size, initial_value = false) {
10117
+ return new PineArrayObject(Array(size).fill(initial_value));
10118
+ }
10119
+ new_float(size, initial_value = NaN) {
10120
+ return new PineArrayObject(Array(size).fill(initial_value));
10121
+ }
10122
+ new_int(size, initial_value = 0) {
10123
+ return new PineArrayObject(Array(size).fill(Math.round(initial_value)));
10124
+ }
10125
+ new_string(size, initial_value = "") {
10126
+ return new PineArrayObject(Array(size).fill(initial_value));
10127
+ }
10128
+ new(size, initial_value) {
10129
+ return new PineArrayObject(Array(size).fill(initial_value));
10130
+ }
10131
+ // Array operations
10132
+ slice(id, start, end) {
10133
+ const adjustedEnd = end !== void 0 ? end + 1 : void 0;
10134
+ return new PineArrayObject(id.array.slice(start, adjustedEnd));
10135
+ }
10136
+ reverse(id) {
10137
+ id.array.reverse();
10138
+ }
10139
+ includes(id, value) {
10140
+ return id.array.includes(value);
10141
+ }
10142
+ indexof(id, value) {
10143
+ return id.array.indexOf(value);
10144
+ }
10145
+ lastindexof(id, value) {
10146
+ return id.array.lastIndexOf(value);
10147
+ }
10148
+ // More complex functions
10149
+ stdev(id, biased = true) {
10150
+ const mean = this.avg(id);
10151
+ const deviations = id.array.map((x) => Math.pow(x - mean, 2));
10152
+ const divisor = biased ? id.array.length : id.array.length - 1;
10153
+ return Math.sqrt(this.sum(new PineArrayObject(deviations)) / divisor);
10154
+ }
10155
+ variance(id, biased = true) {
10156
+ const mean = this.avg(id);
10157
+ const deviations = id.array.map((x) => Math.pow(x - mean, 2));
10158
+ const divisor = biased ? id.array.length : id.array.length - 1;
10159
+ return this.sum(new PineArrayObject(deviations)) / divisor;
10160
+ }
10161
+ covariance(arr1, arr2, biased = true) {
10162
+ if (arr1.array.length !== arr2.array.length || arr1.array.length < 2) return NaN;
10163
+ const divisor = biased ? arr1.array.length : arr1.array.length - 1;
10164
+ const mean1 = this.avg(arr1);
10165
+ const mean2 = this.avg(arr2);
10166
+ let sum = 0;
10167
+ for (let i = 0; i < arr1.array.length; i++) {
10168
+ sum += (arr1.array[i] - mean1) * (arr2.array[i] - mean2);
10169
+ }
10170
+ return sum / divisor;
10171
+ }
10172
+ // Additional utility methods
10173
+ first(id) {
10174
+ return id.array.length > 0 ? id.array[0] : this.context.NA;
10175
+ }
10176
+ last(id) {
10177
+ return id.array.length > 0 ? id.array[id.array.length - 1] : this.context.NA;
10178
+ }
10179
+ clear(id) {
10180
+ id.array.length = 0;
10181
+ }
10182
+ join(id, separator = ",") {
10183
+ return id.array.join(separator);
10184
+ }
10185
+ /** Array Manipulation Functions */
10186
+ abs(id) {
10187
+ return new PineArrayObject(id.array.map((val) => Math.abs(val)));
10188
+ }
10189
+ concat(id, other) {
10190
+ id.array.push(...other.array);
10191
+ return id;
10192
+ }
10193
+ copy(id) {
10194
+ return new PineArrayObject([...id.array]);
10195
+ }
10196
+ every(id, callback) {
10197
+ return id.array.every(callback);
10198
+ }
10199
+ fill(id, value, start = 0, end) {
10200
+ const length = id.array.length;
10201
+ const adjustedEnd = end !== void 0 ? Math.min(end, length) : length;
10202
+ for (let i = start; i < adjustedEnd; i++) {
10203
+ id.array[i] = value;
10204
+ }
10205
+ }
10206
+ from(source) {
10207
+ return new PineArrayObject([...source]);
10208
+ }
10209
+ insert(id, index, value) {
10210
+ id.array.splice(index, 0, value);
10211
+ }
10212
+ pop(id) {
10213
+ return id.array.pop();
10214
+ }
10215
+ range(id) {
10216
+ return this.max(id) - this.min(id);
10217
+ }
10218
+ remove(id, index) {
10219
+ if (index >= 0 && index < id.array.length) {
10220
+ return id.array.splice(index, 1)[0];
10221
+ }
10222
+ return this.context.NA;
10223
+ }
10224
+ shift(id) {
10225
+ return id.array.shift();
10226
+ }
10227
+ sort(id, order = "asc") {
10228
+ id.array.sort((a, b) => order === "asc" ? a - b : b - a);
10229
+ }
10230
+ sort_indices(id, comparator) {
10231
+ const indices = id.array.map((_, index) => index);
10232
+ indices.sort((a, b) => {
10233
+ const valA = id.array[a];
10234
+ const valB = id.array[b];
10235
+ return comparator ? comparator(valA, valB) : valA - valB;
10236
+ });
10237
+ return new PineArrayObject(indices);
10238
+ }
10239
+ standardize(id) {
10240
+ const mean = this.avg(id);
10241
+ const stdev = this.stdev(id);
10242
+ if (stdev === 0) {
10243
+ return new PineArrayObject(id.array.map(() => 0));
10244
+ }
10245
+ return new PineArrayObject(id.array.map((x) => (x - mean) / stdev));
10246
+ }
10247
+ unshift(id, value) {
10248
+ id.array.unshift(value);
10249
+ }
10250
+ some(id, callback) {
10251
+ return id.array.some(callback);
10252
+ }
10253
+ }
10254
+
10255
+ class Context {
9936
10256
  data = {
9937
10257
  open: [],
9938
10258
  high: [],
@@ -9943,11 +10263,16 @@ class Context {
9943
10263
  hlc3: [],
9944
10264
  ohlc4: []
9945
10265
  };
10266
+ cache = {};
10267
+ useTACache = false;
10268
+ NA = NaN;
9946
10269
  math;
9947
10270
  ta;
9948
10271
  input;
9949
10272
  request;
10273
+ array;
9950
10274
  core;
10275
+ lang;
9951
10276
  idx = 0;
9952
10277
  params = {};
9953
10278
  const = {};
@@ -9955,7 +10280,44 @@ class Context {
9955
10280
  let = {};
9956
10281
  result = void 0;
9957
10282
  plots = {};
10283
+ marketData;
10284
+ source;
10285
+ tickerId;
9958
10286
  timeframe = "";
10287
+ limit;
10288
+ sDate;
10289
+ eDate;
10290
+ pineTSCode;
10291
+ constructor({
10292
+ marketData,
10293
+ source,
10294
+ tickerId,
10295
+ timeframe,
10296
+ limit,
10297
+ sDate,
10298
+ eDate
10299
+ }) {
10300
+ this.marketData = marketData;
10301
+ this.source = source;
10302
+ this.tickerId = tickerId;
10303
+ this.timeframe = timeframe;
10304
+ this.limit = limit;
10305
+ this.sDate = sDate;
10306
+ this.eDate = eDate;
10307
+ this.math = new PineMath(this);
10308
+ this.ta = new TechnicalAnalysis(this);
10309
+ this.input = new Input(this);
10310
+ this.request = new PineRequest(this);
10311
+ this.array = new PineArray(this);
10312
+ const core = new Core(this);
10313
+ this.core = {
10314
+ plotchar: core.plotchar.bind(core),
10315
+ na: core.na.bind(core),
10316
+ color: core.color,
10317
+ plot: core.plot.bind(core),
10318
+ nz: core.nz.bind(core)
10319
+ };
10320
+ }
9959
10321
  //#region [Runtime functions] ===========================
9960
10322
  /**
9961
10323
  * this function is used to initialize the target variable with the source array
@@ -9982,12 +10344,13 @@ class Context {
9982
10344
  return trg;
9983
10345
  }
9984
10346
  /**
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
- */
10347
+ * this function is used to set the floating point precision of a number
10348
+ * by default it is set to 10 decimals which is the same as pine script
10349
+ * @param n - the number to be precision
10350
+ * @param decimals - the number of decimals to precision to
10351
+
10352
+ * @returns the precision number
10353
+ */
9991
10354
  precision(n, decimals = 10) {
9992
10355
  if (typeof n !== "number" || isNaN(n)) return n;
9993
10356
  return Number(n.toFixed(decimals));
@@ -10006,6 +10369,7 @@ class Context {
10006
10369
  if (Array.isArray(source)) {
10007
10370
  if (index) {
10008
10371
  this.params[name] = source.slice(index);
10372
+ this.params[name].length = source.length;
10009
10373
  return this.params[name];
10010
10374
  }
10011
10375
  this.params[name] = source.slice(0);
@@ -10018,22 +10382,7 @@ class Context {
10018
10382
  //#endregion
10019
10383
  }
10020
10384
 
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";
10385
+ const BINANCE_API_URL = "https://api.binance.com/api/v3";
10037
10386
  const timeframe_to_binance = {
10038
10387
  "1": "1m",
10039
10388
  // 1 minute
@@ -10055,6 +10404,8 @@ const timeframe_to_binance = {
10055
10404
  // 3 hours (not directly supported by Binance, needs custom handling)
10056
10405
  "240": "4h",
10057
10406
  // 4 hours
10407
+ "4H": "4h",
10408
+ // 4 hours
10058
10409
  "1D": "1d",
10059
10410
  // 1 day
10060
10411
  D: "1d",
@@ -10068,15 +10419,119 @@ const timeframe_to_binance = {
10068
10419
  M: "1M"
10069
10420
  // 1 month
10070
10421
  };
10422
+ class CacheManager {
10423
+ cache;
10424
+ cacheDuration;
10425
+ constructor(cacheDuration = 5 * 60 * 1e3) {
10426
+ this.cache = /* @__PURE__ */ new Map();
10427
+ this.cacheDuration = cacheDuration;
10428
+ }
10429
+ generateKey(params) {
10430
+ return Object.entries(params).filter(([_, value]) => value !== void 0).map(([key, value]) => `${key}:${value}`).join("|");
10431
+ }
10432
+ get(params) {
10433
+ const key = this.generateKey(params);
10434
+ const cached = this.cache.get(key);
10435
+ if (!cached) return null;
10436
+ if (Date.now() - cached.timestamp > this.cacheDuration) {
10437
+ this.cache.delete(key);
10438
+ return null;
10439
+ }
10440
+ return cached.data;
10441
+ }
10442
+ set(params, data) {
10443
+ const key = this.generateKey(params);
10444
+ this.cache.set(key, {
10445
+ data,
10446
+ timestamp: Date.now()
10447
+ });
10448
+ }
10449
+ clear() {
10450
+ this.cache.clear();
10451
+ }
10452
+ // Optional: method to remove expired entries
10453
+ cleanup() {
10454
+ const now = Date.now();
10455
+ for (const [key, entry] of this.cache.entries()) {
10456
+ if (now - entry.timestamp > this.cacheDuration) {
10457
+ this.cache.delete(key);
10458
+ }
10459
+ }
10460
+ }
10461
+ }
10071
10462
  class BinanceProvider {
10463
+ cacheManager;
10464
+ constructor() {
10465
+ this.cacheManager = new CacheManager(5 * 60 * 1e3);
10466
+ }
10467
+ async getMarketDataInterval(tickerId, timeframe, sDate, eDate) {
10468
+ try {
10469
+ const interval = timeframe_to_binance[timeframe.toUpperCase()];
10470
+ if (!interval) {
10471
+ console.error(`Unsupported timeframe: ${timeframe}`);
10472
+ return [];
10473
+ }
10474
+ const timeframeDurations = {
10475
+ "1m": 60 * 1e3,
10476
+ "3m": 3 * 60 * 1e3,
10477
+ "5m": 5 * 60 * 1e3,
10478
+ "15m": 15 * 60 * 1e3,
10479
+ "30m": 30 * 60 * 1e3,
10480
+ "1h": 60 * 60 * 1e3,
10481
+ "2h": 2 * 60 * 60 * 1e3,
10482
+ "4h": 4 * 60 * 60 * 1e3,
10483
+ "1d": 24 * 60 * 60 * 1e3,
10484
+ "1w": 7 * 24 * 60 * 60 * 1e3,
10485
+ "1M": 30 * 24 * 60 * 60 * 1e3
10486
+ };
10487
+ let allData = [];
10488
+ let currentStart = sDate;
10489
+ const endTime = eDate;
10490
+ const intervalDuration = timeframeDurations[interval];
10491
+ if (!intervalDuration) {
10492
+ console.error(`Duration not defined for interval: ${interval}`);
10493
+ return [];
10494
+ }
10495
+ while (currentStart < endTime) {
10496
+ const chunkEnd = Math.min(currentStart + 1e3 * intervalDuration, endTime);
10497
+ const data = await this.getMarketData(
10498
+ tickerId,
10499
+ timeframe,
10500
+ 1e3,
10501
+ // Max allowed by Binance
10502
+ currentStart,
10503
+ chunkEnd
10504
+ );
10505
+ if (data.length === 0) break;
10506
+ allData = allData.concat(data);
10507
+ currentStart = data[data.length - 1].closeTime + 1;
10508
+ if (data.length < 1e3) break;
10509
+ }
10510
+ return allData;
10511
+ } catch (error) {
10512
+ console.error("Error in getMarketDataInterval:", error);
10513
+ return [];
10514
+ }
10515
+ }
10516
+ //TODO : allow querying more than 1000 klines
10517
+ //TODO : immplement cache
10072
10518
  async getMarketData(tickerId, timeframe, limit, sDate, eDate) {
10073
10519
  try {
10520
+ const cacheParams = { tickerId, timeframe, limit, sDate, eDate };
10521
+ const cachedData = this.cacheManager.get(cacheParams);
10522
+ if (cachedData) {
10523
+ console.log("cache hit", tickerId, timeframe, limit, sDate, eDate);
10524
+ return cachedData;
10525
+ }
10074
10526
  const interval = timeframe_to_binance[timeframe.toUpperCase()];
10075
10527
  if (!interval) {
10076
10528
  console.error(`Unsupported timeframe: ${timeframe}`);
10077
10529
  return [];
10078
10530
  }
10079
10531
  let url = `${BINANCE_API_URL}/klines?symbol=${tickerId}&interval=${interval}`;
10532
+ if (!limit && sDate && eDate) {
10533
+ return this.getMarketDataInterval(tickerId, timeframe, sDate, eDate);
10534
+ }
10080
10535
  if (limit) {
10081
10536
  url += `&limit=${limit}`;
10082
10537
  }
@@ -10093,13 +10548,13 @@ class BinanceProvider {
10093
10548
  const result = await response.json();
10094
10549
  const data = result.map((item) => {
10095
10550
  return {
10096
- openTime: parseInt(item[0]) / 1e3,
10551
+ openTime: parseInt(item[0]),
10097
10552
  open: parseFloat(item[1]),
10098
10553
  high: parseFloat(item[2]),
10099
10554
  low: parseFloat(item[3]),
10100
10555
  close: parseFloat(item[4]),
10101
10556
  volume: parseFloat(item[5]),
10102
- closeTime: parseInt(item[6]) / 1e3,
10557
+ closeTime: parseInt(item[6]),
10103
10558
  quoteAssetVolume: parseFloat(item[7]),
10104
10559
  numberOfTrades: parseInt(item[8]),
10105
10560
  takerBuyBaseAssetVolume: parseFloat(item[9]),
@@ -10107,6 +10562,7 @@ class BinanceProvider {
10107
10562
  ignore: item[11]
10108
10563
  };
10109
10564
  });
10565
+ this.cacheManager.set(cacheParams, data);
10110
10566
  return data;
10111
10567
  } catch (error) {
10112
10568
  console.error("Error in binance.klines:", error);
@@ -10117,6 +10573,7 @@ class BinanceProvider {
10117
10573
 
10118
10574
  const Provider = {
10119
10575
  Binance: new BinanceProvider()
10576
+ //TODO : add other providers (polygon, etc.)
10120
10577
  };
10121
10578
 
10122
10579
  exports.Context = Context;