@backtest-kit/pinets 3.4.0 → 3.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/index.cjs CHANGED
@@ -102,6 +102,7 @@ const cacheServices$1 = {
102
102
  };
103
103
  const connectionServices$1 = {
104
104
  pineConnectionService: Symbol("pineConnectionService"),
105
+ indicatorConnectionService: Symbol("indicatorConnectionService"),
105
106
  };
106
107
  const markdownServices$1 = {
107
108
  pineMarkdownService: Symbol("pineMarkdownService"),
@@ -328,7 +329,8 @@ class PineJobService {
328
329
  this.axisProviderService = inject(TYPES.axisProviderService);
329
330
  this.candleProviderService = inject(TYPES.candleProviderService);
330
331
  this.pineConnectionService = inject(TYPES.pineConnectionService);
331
- this.run = async (code, tickerId, timeframe = "1m", limit = 100) => {
332
+ this.indicatorConnectionService = inject(TYPES.indicatorConnectionService);
333
+ this.run = async (code, tickerId, timeframe = "1m", limit = 100, inputs = {}) => {
332
334
  this.loggerService.log("pineJobService run", {
333
335
  script: code.source,
334
336
  tickerId,
@@ -336,7 +338,18 @@ class PineJobService {
336
338
  limit,
337
339
  });
338
340
  const runner = await CREATE_RUNNER_FN(this, tickerId, timeframe, limit);
339
- return await runner.run(code.source);
341
+ const indicator = await this.indicatorConnectionService.getInstance(code.source, inputs);
342
+ const result = await runner.run(Object.keys(inputs).length ? indicator : code.source);
343
+ const plots = Object.entries(result.plots).reduce((acm, [key, value]) => {
344
+ if (key === "undefined") {
345
+ return acm;
346
+ }
347
+ return {
348
+ ...acm,
349
+ [key]: value,
350
+ };
351
+ }, {});
352
+ return { plots };
340
353
  };
341
354
  }
342
355
  }
@@ -404,11 +417,11 @@ class PineCacheService {
404
417
  }
405
418
  }
406
419
 
407
- const require$1 = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
420
+ const require$2 = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
408
421
  const REQUIRE_PINE_FACTORY = () => {
409
422
  try {
410
423
  // @ts-ignore
411
- const { PineTS } = require$1("pinets");
424
+ const { PineTS } = require$2("pinets");
412
425
  // @ts-ignore
413
426
  return PineTS;
414
427
  }
@@ -448,7 +461,7 @@ class PineConnectionService {
448
461
  this.PineFactory = await LOAD_PINE_FACTORY_FN();
449
462
  }
450
463
  if (!this.PineFactory) {
451
- throw new Error("PineTS import failed. Call usePine to provide a PineTS class to @backtest-kit/pinets.");
464
+ throw new Error("PineTS import failed (usePine). Call usePine to provide a PineTS class to @backtest-kit/pinets.");
452
465
  }
453
466
  return Reflect.construct(this.PineFactory, args);
454
467
  };
@@ -575,6 +588,68 @@ class PineMarkdownService {
575
588
  }
576
589
  }
577
590
 
591
+ const require$1 = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
592
+ const REQUIRE_INDICATOR_FACTORY = () => {
593
+ try {
594
+ // @ts-ignore
595
+ const { Indicator } = require$1("pinets");
596
+ // @ts-ignore
597
+ return Indicator;
598
+ }
599
+ catch {
600
+ return null;
601
+ }
602
+ };
603
+ const IMPORT_INDICATOR_FACTORY = async () => {
604
+ try {
605
+ // @ts-ignore
606
+ const { Indicator } = await import('pinets');
607
+ // @ts-ignore
608
+ return Indicator;
609
+ }
610
+ catch {
611
+ return null;
612
+ }
613
+ };
614
+ const LOAD_INDICATOR_FACTORY_FN = functoolsKit.singleshot(async () => {
615
+ let ctor = null;
616
+ if (ctor = REQUIRE_INDICATOR_FACTORY()) {
617
+ return ctor;
618
+ }
619
+ if (ctor = await IMPORT_INDICATOR_FACTORY()) {
620
+ return ctor;
621
+ }
622
+ throw new Error("PineTS import failed (useIndicator). Call useIndicator to provide a PineTS class to @backtest-kit/pinets.");
623
+ });
624
+ class IndicatorConnectionService {
625
+ constructor() {
626
+ this.loggerService = inject(TYPES.loggerService);
627
+ this.getInstance = async (...args) => {
628
+ this.loggerService.log("indicatorConnectionService getInstance", {
629
+ args,
630
+ });
631
+ if (!this.IndicatorFactory) {
632
+ this.IndicatorFactory = await LOAD_INDICATOR_FACTORY_FN();
633
+ }
634
+ if (!this.IndicatorFactory) {
635
+ throw new Error("PineTS import failed. Call useIndicator to provide a PineTS class to @backtest-kit/pinets.");
636
+ }
637
+ return Reflect.construct(this.IndicatorFactory, args);
638
+ };
639
+ this.useIndicator = (ctor) => {
640
+ this.loggerService.log("indicatorConnectionService useIndicator", {
641
+ ctor,
642
+ });
643
+ this.IndicatorFactory = ctor;
644
+ };
645
+ this.clear = () => {
646
+ this.loggerService.log("indicatorConnectionService clear");
647
+ LOAD_INDICATOR_FACTORY_FN.clear();
648
+ this.IndicatorFactory = null;
649
+ };
650
+ }
651
+ }
652
+
578
653
  {
579
654
  provide(TYPES.loggerService, () => new LoggerService());
580
655
  }
@@ -596,6 +671,7 @@ class PineMarkdownService {
596
671
  }
597
672
  {
598
673
  provide(TYPES.pineConnectionService, () => new PineConnectionService());
674
+ provide(TYPES.indicatorConnectionService, () => new IndicatorConnectionService());
599
675
  }
600
676
  {
601
677
  provide(TYPES.pineMarkdownService, () => new PineMarkdownService());
@@ -622,6 +698,7 @@ const cacheServices = {
622
698
  };
623
699
  const connectionServices = {
624
700
  pineConnectionService: inject(TYPES.pineConnectionService),
701
+ indicatorConnectionService: inject(TYPES.indicatorConnectionService),
625
702
  };
626
703
  const markdownServices = {
627
704
  pineMarkdownService: inject(TYPES.pineMarkdownService),
@@ -638,14 +715,22 @@ const pine = {
638
715
  };
639
716
  init();
640
717
 
641
- const METHOD_NAME_USE_PINE = "pine.usePine";
718
+ const METHOD_NAME_USE_PINE$1 = "pine.usePine";
642
719
  function usePine(ctor) {
643
- pine.loggerService.log(METHOD_NAME_USE_PINE, {
720
+ pine.loggerService.log(METHOD_NAME_USE_PINE$1, {
644
721
  ctor,
645
722
  });
646
723
  pine.pineConnectionService.usePine(ctor);
647
724
  }
648
725
 
726
+ const METHOD_NAME_USE_PINE = "indicator.useIndicator";
727
+ function useIndicator(ctor) {
728
+ pine.loggerService.log(METHOD_NAME_USE_PINE, {
729
+ ctor,
730
+ });
731
+ pine.indicatorConnectionService.useIndicator(ctor);
732
+ }
733
+
649
734
  const METHOD_NAME_RUN$2 = "run.run";
650
735
  const GET_SOURCE_FN$2 = async (source) => {
651
736
  if (File.isFile(source)) {
@@ -657,14 +742,14 @@ const GET_SOURCE_FN$2 = async (source) => {
657
742
  }
658
743
  throw new Error("Source must be a File or Code instance");
659
744
  };
660
- const BASE_RUNNER_FN$1 = async (script, symbol, timeframe, limit) => await pine.pineJobService.run(script, symbol, timeframe, limit);
745
+ const BASE_RUNNER_FN$1 = async (script, symbol, timeframe, limit, inputs) => await pine.pineJobService.run(script, symbol, timeframe, limit, inputs);
661
746
  const VALIDATE_NO_TRADING_FN$1 = () => {
662
747
  if (backtestKit.ExecutionContextService.hasContext()) {
663
748
  throw new Error(functoolsKit.str.newline("Time overrides are not allowed when running scripts in a trading context.", "Please remove the 'when' parameter from the run function call."));
664
749
  }
665
750
  };
666
- const CREATE_INFERENCE_FN$1 = (script, symbol, timeframe, limit, exchangeName, when) => {
667
- let fn = () => BASE_RUNNER_FN$1(script, symbol, timeframe, limit);
751
+ const CREATE_INFERENCE_FN$1 = (script, symbol, timeframe, limit, inputs, exchangeName, when) => {
752
+ let fn = () => BASE_RUNNER_FN$1(script, symbol, timeframe, limit, inputs);
668
753
  if (exchangeName) {
669
754
  fn = ExchangeContextService.runWithContext(fn, { exchangeName });
670
755
  }
@@ -677,14 +762,14 @@ const CREATE_INFERENCE_FN$1 = (script, symbol, timeframe, limit, exchangeName, w
677
762
  }
678
763
  return fn;
679
764
  };
680
- const RUN_INFERENCE_FN$1 = async (script, symbol, timeframe, limit, exchangeName, when) => {
765
+ const RUN_INFERENCE_FN$1 = async (script, symbol, timeframe, limit, inputs, exchangeName, when) => {
681
766
  if (when) {
682
767
  VALIDATE_NO_TRADING_FN$1();
683
768
  }
684
- const inference = CREATE_INFERENCE_FN$1(script, symbol, timeframe, limit, exchangeName, when);
769
+ const inference = CREATE_INFERENCE_FN$1(script, symbol, timeframe, limit, inputs, exchangeName, when);
685
770
  return await inference();
686
771
  };
687
- async function run(source, { symbol, timeframe, limit }, exchangeName, when) {
772
+ async function run(source, { symbol, timeframe, limit, inputs = {} }, exchangeName, when) {
688
773
  pine.loggerService.info(METHOD_NAME_RUN$2, {
689
774
  source,
690
775
  symbol,
@@ -692,7 +777,7 @@ async function run(source, { symbol, timeframe, limit }, exchangeName, when) {
692
777
  limit,
693
778
  });
694
779
  const script = await GET_SOURCE_FN$2(source);
695
- const { plots } = await RUN_INFERENCE_FN$1(script, symbol, timeframe, limit, exchangeName, when);
780
+ const { plots } = await RUN_INFERENCE_FN$1(script, symbol, timeframe, limit, inputs, exchangeName, when);
696
781
  return plots;
697
782
  }
698
783
 
@@ -760,14 +845,14 @@ const SIGNAL_SCHEMA = {
760
845
  transform: (v) => v || DEFAULT_ESTIMATED_TIME,
761
846
  },
762
847
  };
763
- async function getSignal(source, { symbol, timeframe, limit }) {
848
+ async function getSignal(source, { symbol, timeframe, limit, inputs = {} }) {
764
849
  pine.loggerService.info(METHOD_NAME_RUN, {
765
850
  source,
766
851
  symbol,
767
852
  timeframe,
768
853
  limit,
769
854
  });
770
- const { plots } = await pine.pineJobService.run(await GET_SOURCE_FN$1(source), symbol, timeframe, limit);
855
+ const { plots } = await pine.pineJobService.run(await GET_SOURCE_FN$1(source), symbol, timeframe, limit, inputs);
771
856
  const resultId = functoolsKit.randomString();
772
857
  const data = pine.pineDataService.extract(plots, SIGNAL_SCHEMA);
773
858
  return toSignalDto(resultId, data);
@@ -796,14 +881,14 @@ const GET_SOURCE_FN = async (source) => {
796
881
  }
797
882
  throw new Error("Source must be a File or Code instance");
798
883
  };
799
- const BASE_RUNNER_FN = async (script, symbol, timeframe, limit) => await pine.pineJobService.run(script, symbol, timeframe, limit);
884
+ const BASE_RUNNER_FN = async (script, symbol, timeframe, limit, inputs) => await pine.pineJobService.run(script, symbol, timeframe, limit, inputs);
800
885
  const VALIDATE_NO_TRADING_FN = () => {
801
886
  if (backtestKit.ExecutionContextService.hasContext()) {
802
887
  throw new Error(functoolsKit.str.newline("Time overrides are not allowed when running scripts in a trading context.", "Please remove the 'when' parameter from the run function call."));
803
888
  }
804
889
  };
805
- const CREATE_INFERENCE_FN = (script, symbol, timeframe, limit, exchangeName, when) => {
806
- let fn = () => BASE_RUNNER_FN(script, symbol, timeframe, limit);
890
+ const CREATE_INFERENCE_FN = (script, symbol, timeframe, limit, inputs, exchangeName, when) => {
891
+ let fn = () => BASE_RUNNER_FN(script, symbol, timeframe, limit, inputs);
807
892
  if (exchangeName) {
808
893
  fn = ExchangeContextService.runWithContext(fn, { exchangeName });
809
894
  }
@@ -816,11 +901,11 @@ const CREATE_INFERENCE_FN = (script, symbol, timeframe, limit, exchangeName, whe
816
901
  }
817
902
  return fn;
818
903
  };
819
- const RUN_INFERENCE_FN = async (script, symbol, timeframe, limit, exchangeName, when) => {
904
+ const RUN_INFERENCE_FN = async (script, symbol, timeframe, limit, inputs, exchangeName, when) => {
820
905
  if (when) {
821
906
  VALIDATE_NO_TRADING_FN();
822
907
  }
823
- const inference = CREATE_INFERENCE_FN(script, symbol, timeframe, limit, exchangeName, when);
908
+ const inference = CREATE_INFERENCE_FN(script, symbol, timeframe, limit, inputs, exchangeName, when);
824
909
  return await inference();
825
910
  };
826
911
  async function toMarkdown(signalId, plots, mapping, limit = Number.POSITIVE_INFINITY) {
@@ -832,7 +917,7 @@ async function toMarkdown(signalId, plots, mapping, limit = Number.POSITIVE_INFI
832
917
  });
833
918
  return await pine.pineMarkdownService.getReport(signalId, plots, mapping, limit);
834
919
  }
835
- async function markdown(signalId, source, { symbol, timeframe, limit }, mapping, exchangeName, when) {
920
+ async function markdown(signalId, source, { symbol, timeframe, limit, inputs = {} }, mapping, exchangeName, when) {
836
921
  pine.loggerService.log(MARKDOWN_METHOD_NAME, {
837
922
  signalId,
838
923
  mapping,
@@ -840,7 +925,7 @@ async function markdown(signalId, source, { symbol, timeframe, limit }, mapping,
840
925
  exchangeName,
841
926
  when,
842
927
  });
843
- const { plots } = await RUN_INFERENCE_FN(await GET_SOURCE_FN(source), symbol, timeframe, limit, exchangeName, when);
928
+ const { plots } = await RUN_INFERENCE_FN(await GET_SOURCE_FN(source), symbol, timeframe, limit, inputs, exchangeName, when);
844
929
  return await pine.pineMarkdownService.getReport(signalId, plots, mapping, Number.POSITIVE_INFINITY);
845
930
  }
846
931
 
@@ -856,4 +941,5 @@ exports.run = run;
856
941
  exports.setLogger = setLogger;
857
942
  exports.toMarkdown = toMarkdown;
858
943
  exports.toSignalDto = toSignalDto;
944
+ exports.useIndicator = useIndicator;
859
945
  exports.usePine = usePine;
package/build/index.mjs CHANGED
@@ -99,6 +99,7 @@ const cacheServices$1 = {
99
99
  };
100
100
  const connectionServices$1 = {
101
101
  pineConnectionService: Symbol("pineConnectionService"),
102
+ indicatorConnectionService: Symbol("indicatorConnectionService"),
102
103
  };
103
104
  const markdownServices$1 = {
104
105
  pineMarkdownService: Symbol("pineMarkdownService"),
@@ -325,7 +326,8 @@ class PineJobService {
325
326
  this.axisProviderService = inject(TYPES.axisProviderService);
326
327
  this.candleProviderService = inject(TYPES.candleProviderService);
327
328
  this.pineConnectionService = inject(TYPES.pineConnectionService);
328
- this.run = async (code, tickerId, timeframe = "1m", limit = 100) => {
329
+ this.indicatorConnectionService = inject(TYPES.indicatorConnectionService);
330
+ this.run = async (code, tickerId, timeframe = "1m", limit = 100, inputs = {}) => {
329
331
  this.loggerService.log("pineJobService run", {
330
332
  script: code.source,
331
333
  tickerId,
@@ -333,7 +335,18 @@ class PineJobService {
333
335
  limit,
334
336
  });
335
337
  const runner = await CREATE_RUNNER_FN(this, tickerId, timeframe, limit);
336
- return await runner.run(code.source);
338
+ const indicator = await this.indicatorConnectionService.getInstance(code.source, inputs);
339
+ const result = await runner.run(Object.keys(inputs).length ? indicator : code.source);
340
+ const plots = Object.entries(result.plots).reduce((acm, [key, value]) => {
341
+ if (key === "undefined") {
342
+ return acm;
343
+ }
344
+ return {
345
+ ...acm,
346
+ [key]: value,
347
+ };
348
+ }, {});
349
+ return { plots };
337
350
  };
338
351
  }
339
352
  }
@@ -401,11 +414,11 @@ class PineCacheService {
401
414
  }
402
415
  }
403
416
 
404
- const require = createRequire(import.meta.url);
417
+ const require$1 = createRequire(import.meta.url);
405
418
  const REQUIRE_PINE_FACTORY = () => {
406
419
  try {
407
420
  // @ts-ignore
408
- const { PineTS } = require("pinets");
421
+ const { PineTS } = require$1("pinets");
409
422
  // @ts-ignore
410
423
  return PineTS;
411
424
  }
@@ -445,7 +458,7 @@ class PineConnectionService {
445
458
  this.PineFactory = await LOAD_PINE_FACTORY_FN();
446
459
  }
447
460
  if (!this.PineFactory) {
448
- throw new Error("PineTS import failed. Call usePine to provide a PineTS class to @backtest-kit/pinets.");
461
+ throw new Error("PineTS import failed (usePine). Call usePine to provide a PineTS class to @backtest-kit/pinets.");
449
462
  }
450
463
  return Reflect.construct(this.PineFactory, args);
451
464
  };
@@ -572,6 +585,68 @@ class PineMarkdownService {
572
585
  }
573
586
  }
574
587
 
588
+ const require = createRequire(import.meta.url);
589
+ const REQUIRE_INDICATOR_FACTORY = () => {
590
+ try {
591
+ // @ts-ignore
592
+ const { Indicator } = require("pinets");
593
+ // @ts-ignore
594
+ return Indicator;
595
+ }
596
+ catch {
597
+ return null;
598
+ }
599
+ };
600
+ const IMPORT_INDICATOR_FACTORY = async () => {
601
+ try {
602
+ // @ts-ignore
603
+ const { Indicator } = await import('pinets');
604
+ // @ts-ignore
605
+ return Indicator;
606
+ }
607
+ catch {
608
+ return null;
609
+ }
610
+ };
611
+ const LOAD_INDICATOR_FACTORY_FN = singleshot(async () => {
612
+ let ctor = null;
613
+ if (ctor = REQUIRE_INDICATOR_FACTORY()) {
614
+ return ctor;
615
+ }
616
+ if (ctor = await IMPORT_INDICATOR_FACTORY()) {
617
+ return ctor;
618
+ }
619
+ throw new Error("PineTS import failed (useIndicator). Call useIndicator to provide a PineTS class to @backtest-kit/pinets.");
620
+ });
621
+ class IndicatorConnectionService {
622
+ constructor() {
623
+ this.loggerService = inject(TYPES.loggerService);
624
+ this.getInstance = async (...args) => {
625
+ this.loggerService.log("indicatorConnectionService getInstance", {
626
+ args,
627
+ });
628
+ if (!this.IndicatorFactory) {
629
+ this.IndicatorFactory = await LOAD_INDICATOR_FACTORY_FN();
630
+ }
631
+ if (!this.IndicatorFactory) {
632
+ throw new Error("PineTS import failed. Call useIndicator to provide a PineTS class to @backtest-kit/pinets.");
633
+ }
634
+ return Reflect.construct(this.IndicatorFactory, args);
635
+ };
636
+ this.useIndicator = (ctor) => {
637
+ this.loggerService.log("indicatorConnectionService useIndicator", {
638
+ ctor,
639
+ });
640
+ this.IndicatorFactory = ctor;
641
+ };
642
+ this.clear = () => {
643
+ this.loggerService.log("indicatorConnectionService clear");
644
+ LOAD_INDICATOR_FACTORY_FN.clear();
645
+ this.IndicatorFactory = null;
646
+ };
647
+ }
648
+ }
649
+
575
650
  {
576
651
  provide(TYPES.loggerService, () => new LoggerService());
577
652
  }
@@ -593,6 +668,7 @@ class PineMarkdownService {
593
668
  }
594
669
  {
595
670
  provide(TYPES.pineConnectionService, () => new PineConnectionService());
671
+ provide(TYPES.indicatorConnectionService, () => new IndicatorConnectionService());
596
672
  }
597
673
  {
598
674
  provide(TYPES.pineMarkdownService, () => new PineMarkdownService());
@@ -619,6 +695,7 @@ const cacheServices = {
619
695
  };
620
696
  const connectionServices = {
621
697
  pineConnectionService: inject(TYPES.pineConnectionService),
698
+ indicatorConnectionService: inject(TYPES.indicatorConnectionService),
622
699
  };
623
700
  const markdownServices = {
624
701
  pineMarkdownService: inject(TYPES.pineMarkdownService),
@@ -635,14 +712,22 @@ const pine = {
635
712
  };
636
713
  init();
637
714
 
638
- const METHOD_NAME_USE_PINE = "pine.usePine";
715
+ const METHOD_NAME_USE_PINE$1 = "pine.usePine";
639
716
  function usePine(ctor) {
640
- pine.loggerService.log(METHOD_NAME_USE_PINE, {
717
+ pine.loggerService.log(METHOD_NAME_USE_PINE$1, {
641
718
  ctor,
642
719
  });
643
720
  pine.pineConnectionService.usePine(ctor);
644
721
  }
645
722
 
723
+ const METHOD_NAME_USE_PINE = "indicator.useIndicator";
724
+ function useIndicator(ctor) {
725
+ pine.loggerService.log(METHOD_NAME_USE_PINE, {
726
+ ctor,
727
+ });
728
+ pine.indicatorConnectionService.useIndicator(ctor);
729
+ }
730
+
646
731
  const METHOD_NAME_RUN$2 = "run.run";
647
732
  const GET_SOURCE_FN$2 = async (source) => {
648
733
  if (File.isFile(source)) {
@@ -654,14 +739,14 @@ const GET_SOURCE_FN$2 = async (source) => {
654
739
  }
655
740
  throw new Error("Source must be a File or Code instance");
656
741
  };
657
- const BASE_RUNNER_FN$1 = async (script, symbol, timeframe, limit) => await pine.pineJobService.run(script, symbol, timeframe, limit);
742
+ const BASE_RUNNER_FN$1 = async (script, symbol, timeframe, limit, inputs) => await pine.pineJobService.run(script, symbol, timeframe, limit, inputs);
658
743
  const VALIDATE_NO_TRADING_FN$1 = () => {
659
744
  if (ExecutionContextService.hasContext()) {
660
745
  throw new Error(str.newline("Time overrides are not allowed when running scripts in a trading context.", "Please remove the 'when' parameter from the run function call."));
661
746
  }
662
747
  };
663
- const CREATE_INFERENCE_FN$1 = (script, symbol, timeframe, limit, exchangeName, when) => {
664
- let fn = () => BASE_RUNNER_FN$1(script, symbol, timeframe, limit);
748
+ const CREATE_INFERENCE_FN$1 = (script, symbol, timeframe, limit, inputs, exchangeName, when) => {
749
+ let fn = () => BASE_RUNNER_FN$1(script, symbol, timeframe, limit, inputs);
665
750
  if (exchangeName) {
666
751
  fn = ExchangeContextService.runWithContext(fn, { exchangeName });
667
752
  }
@@ -674,14 +759,14 @@ const CREATE_INFERENCE_FN$1 = (script, symbol, timeframe, limit, exchangeName, w
674
759
  }
675
760
  return fn;
676
761
  };
677
- const RUN_INFERENCE_FN$1 = async (script, symbol, timeframe, limit, exchangeName, when) => {
762
+ const RUN_INFERENCE_FN$1 = async (script, symbol, timeframe, limit, inputs, exchangeName, when) => {
678
763
  if (when) {
679
764
  VALIDATE_NO_TRADING_FN$1();
680
765
  }
681
- const inference = CREATE_INFERENCE_FN$1(script, symbol, timeframe, limit, exchangeName, when);
766
+ const inference = CREATE_INFERENCE_FN$1(script, symbol, timeframe, limit, inputs, exchangeName, when);
682
767
  return await inference();
683
768
  };
684
- async function run(source, { symbol, timeframe, limit }, exchangeName, when) {
769
+ async function run(source, { symbol, timeframe, limit, inputs = {} }, exchangeName, when) {
685
770
  pine.loggerService.info(METHOD_NAME_RUN$2, {
686
771
  source,
687
772
  symbol,
@@ -689,7 +774,7 @@ async function run(source, { symbol, timeframe, limit }, exchangeName, when) {
689
774
  limit,
690
775
  });
691
776
  const script = await GET_SOURCE_FN$2(source);
692
- const { plots } = await RUN_INFERENCE_FN$1(script, symbol, timeframe, limit, exchangeName, when);
777
+ const { plots } = await RUN_INFERENCE_FN$1(script, symbol, timeframe, limit, inputs, exchangeName, when);
693
778
  return plots;
694
779
  }
695
780
 
@@ -757,14 +842,14 @@ const SIGNAL_SCHEMA = {
757
842
  transform: (v) => v || DEFAULT_ESTIMATED_TIME,
758
843
  },
759
844
  };
760
- async function getSignal(source, { symbol, timeframe, limit }) {
845
+ async function getSignal(source, { symbol, timeframe, limit, inputs = {} }) {
761
846
  pine.loggerService.info(METHOD_NAME_RUN, {
762
847
  source,
763
848
  symbol,
764
849
  timeframe,
765
850
  limit,
766
851
  });
767
- const { plots } = await pine.pineJobService.run(await GET_SOURCE_FN$1(source), symbol, timeframe, limit);
852
+ const { plots } = await pine.pineJobService.run(await GET_SOURCE_FN$1(source), symbol, timeframe, limit, inputs);
768
853
  const resultId = randomString();
769
854
  const data = pine.pineDataService.extract(plots, SIGNAL_SCHEMA);
770
855
  return toSignalDto(resultId, data);
@@ -793,14 +878,14 @@ const GET_SOURCE_FN = async (source) => {
793
878
  }
794
879
  throw new Error("Source must be a File or Code instance");
795
880
  };
796
- const BASE_RUNNER_FN = async (script, symbol, timeframe, limit) => await pine.pineJobService.run(script, symbol, timeframe, limit);
881
+ const BASE_RUNNER_FN = async (script, symbol, timeframe, limit, inputs) => await pine.pineJobService.run(script, symbol, timeframe, limit, inputs);
797
882
  const VALIDATE_NO_TRADING_FN = () => {
798
883
  if (ExecutionContextService.hasContext()) {
799
884
  throw new Error(str.newline("Time overrides are not allowed when running scripts in a trading context.", "Please remove the 'when' parameter from the run function call."));
800
885
  }
801
886
  };
802
- const CREATE_INFERENCE_FN = (script, symbol, timeframe, limit, exchangeName, when) => {
803
- let fn = () => BASE_RUNNER_FN(script, symbol, timeframe, limit);
887
+ const CREATE_INFERENCE_FN = (script, symbol, timeframe, limit, inputs, exchangeName, when) => {
888
+ let fn = () => BASE_RUNNER_FN(script, symbol, timeframe, limit, inputs);
804
889
  if (exchangeName) {
805
890
  fn = ExchangeContextService.runWithContext(fn, { exchangeName });
806
891
  }
@@ -813,11 +898,11 @@ const CREATE_INFERENCE_FN = (script, symbol, timeframe, limit, exchangeName, whe
813
898
  }
814
899
  return fn;
815
900
  };
816
- const RUN_INFERENCE_FN = async (script, symbol, timeframe, limit, exchangeName, when) => {
901
+ const RUN_INFERENCE_FN = async (script, symbol, timeframe, limit, inputs, exchangeName, when) => {
817
902
  if (when) {
818
903
  VALIDATE_NO_TRADING_FN();
819
904
  }
820
- const inference = CREATE_INFERENCE_FN(script, symbol, timeframe, limit, exchangeName, when);
905
+ const inference = CREATE_INFERENCE_FN(script, symbol, timeframe, limit, inputs, exchangeName, when);
821
906
  return await inference();
822
907
  };
823
908
  async function toMarkdown(signalId, plots, mapping, limit = Number.POSITIVE_INFINITY) {
@@ -829,7 +914,7 @@ async function toMarkdown(signalId, plots, mapping, limit = Number.POSITIVE_INFI
829
914
  });
830
915
  return await pine.pineMarkdownService.getReport(signalId, plots, mapping, limit);
831
916
  }
832
- async function markdown(signalId, source, { symbol, timeframe, limit }, mapping, exchangeName, when) {
917
+ async function markdown(signalId, source, { symbol, timeframe, limit, inputs = {} }, mapping, exchangeName, when) {
833
918
  pine.loggerService.log(MARKDOWN_METHOD_NAME, {
834
919
  signalId,
835
920
  mapping,
@@ -837,8 +922,8 @@ async function markdown(signalId, source, { symbol, timeframe, limit }, mapping,
837
922
  exchangeName,
838
923
  when,
839
924
  });
840
- const { plots } = await RUN_INFERENCE_FN(await GET_SOURCE_FN(source), symbol, timeframe, limit, exchangeName, when);
925
+ const { plots } = await RUN_INFERENCE_FN(await GET_SOURCE_FN(source), symbol, timeframe, limit, inputs, exchangeName, when);
841
926
  return await pine.pineMarkdownService.getReport(signalId, plots, mapping, Number.POSITIVE_INFINITY);
842
927
  }
843
928
 
844
- export { AXIS_SYMBOL, Code, File, dumpPlotData, extract, getSignal, pine as lib, markdown, run, setLogger, toMarkdown, toSignalDto, usePine };
929
+ export { AXIS_SYMBOL, Code, File, dumpPlotData, extract, getSignal, pine as lib, markdown, run, setLogger, toMarkdown, toSignalDto, useIndicator, usePine };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backtest-kit/pinets",
3
- "version": "3.4.0",
3
+ "version": "3.5.0",
4
4
  "description": "Run TradingView Pine Script strategies in Node.js self hosted environment. Execute existing Pine Script indicators and generate trading signals with 1:1 syntax compatibility via PineTS runtime.",
5
5
  "author": {
6
6
  "name": "Petr Tripolsky",
@@ -69,12 +69,12 @@
69
69
  "ts-morph": "27.0.2",
70
70
  "tslib": "2.7.0",
71
71
  "typedoc": "0.27.9",
72
- "backtest-kit": "3.4.0",
72
+ "backtest-kit": "3.5.0",
73
73
  "worker-testbed": "1.0.12"
74
74
  },
75
75
  "peerDependencies": {
76
- "backtest-kit": "^3.4.0",
77
- "pinets": "^0.8.11",
76
+ "backtest-kit": "^3.5.0",
77
+ "pinets": "^0.8.12",
78
78
  "typescript": "^5.0.0"
79
79
  },
80
80
  "dependencies": {
package/types.d.ts CHANGED
@@ -29,19 +29,27 @@ type PlotRecord = {
29
29
  plots: PlotModel;
30
30
  };
31
31
 
32
+ type TIndicatorCtor = new (source: string, inputs?: Record<string, any>) => IIndicator;
33
+ interface IIndicator {
34
+ source: string;
35
+ inputs: Record<string, any>;
36
+ }
37
+
32
38
  interface IProvider {
33
39
  getMarketData(tickerId: string, timeframe: string, limit?: number, sDate?: number, eDate?: number): Promise<any>;
34
40
  getSymbolInfo(tickerId: string): Promise<any>;
35
41
  }
36
42
 
37
- type TPineCtor = (source: IProvider, tickerId: string, timeframe: string, limit: number) => IPine;
43
+ type TPineCtor = new (source: IProvider, tickerId: string, timeframe: string, limit: number) => IPine;
38
44
  interface IPine {
39
45
  ready(): Promise<void>;
40
- run(code: string): Promise<PlotRecord>;
46
+ run(code: string | IIndicator): Promise<PlotRecord>;
41
47
  }
42
48
 
43
49
  declare function usePine<T = TPineCtor>(ctor: T): void;
44
50
 
51
+ declare function useIndicator<T = TIndicatorCtor>(ctor: T): void;
52
+
45
53
  type ExchangeName = string;
46
54
  interface IExchangeContext {
47
55
  exchangeName: ExchangeName;
@@ -51,8 +59,9 @@ interface IRunParams$1 {
51
59
  symbol: string;
52
60
  timeframe: CandleInterval;
53
61
  limit: number;
62
+ inputs?: Record<string, any>;
54
63
  }
55
- declare function run(source: File | Code, { symbol, timeframe, limit }: IRunParams$1, exchangeName?: ExchangeName, when?: Date): Promise<PlotModel>;
64
+ declare function run(source: File | Code, { symbol, timeframe, limit, inputs }: IRunParams$1, exchangeName?: ExchangeName, when?: Date): Promise<PlotModel>;
56
65
 
57
66
  type PlotExtractConfig<T = number> = {
58
67
  plot: string;
@@ -85,8 +94,9 @@ interface IParams {
85
94
  symbol: string;
86
95
  timeframe: CandleInterval;
87
96
  limit: number;
97
+ inputs?: Record<string, any>;
88
98
  }
89
- declare function getSignal(source: File | Code, { symbol, timeframe, limit }: IParams): Promise<ISignalDto | null>;
99
+ declare function getSignal(source: File | Code, { symbol, timeframe, limit, inputs }: IParams): Promise<ISignalDto | null>;
90
100
 
91
101
  type ResultId$3 = string | number;
92
102
  declare function dumpPlotData<M extends PlotMapping>(signalId: ResultId$3, plots: PlotModel, mapping: M, taName: string, outputDir?: string): Promise<void>;
@@ -96,9 +106,10 @@ interface IRunParams {
96
106
  symbol: string;
97
107
  timeframe: CandleInterval;
98
108
  limit: number;
109
+ inputs?: Record<string, any>;
99
110
  }
100
111
  declare function toMarkdown<M extends PlotMapping>(signalId: ResultId$2, plots: PlotModel, mapping: M, limit?: number): Promise<string>;
101
- declare function markdown<M extends PlotMapping>(signalId: ResultId$2, source: File | Code, { symbol, timeframe, limit }: IRunParams, mapping: M, exchangeName?: ExchangeName, when?: Date): Promise<string>;
112
+ declare function markdown<M extends PlotMapping>(signalId: ResultId$2, source: File | Code, { symbol, timeframe, limit, inputs }: IRunParams, mapping: M, exchangeName?: ExchangeName, when?: Date): Promise<string>;
102
113
 
103
114
  type ResultId$1 = string | number;
104
115
  interface SignalData {
@@ -160,17 +171,26 @@ declare class CandleProviderService implements IProvider {
160
171
  declare class PineConnectionService {
161
172
  private readonly loggerService;
162
173
  private PineFactory;
163
- getInstance: (...args: Parameters<TPineCtor>) => Promise<IPine>;
174
+ getInstance: (...args: ConstructorParameters<TPineCtor>) => Promise<IPine>;
164
175
  usePine: (ctor: TPineCtor) => void;
165
176
  clear: () => void;
166
177
  }
167
178
 
179
+ declare class IndicatorConnectionService {
180
+ private readonly loggerService;
181
+ private IndicatorFactory;
182
+ getInstance: (...args: ConstructorParameters<TIndicatorCtor>) => Promise<IIndicator>;
183
+ useIndicator: (ctor: TIndicatorCtor) => void;
184
+ clear: () => void;
185
+ }
186
+
168
187
  declare class PineJobService {
169
188
  readonly loggerService: LoggerService;
170
189
  readonly axisProviderService: AxisProviderService;
171
190
  readonly candleProviderService: CandleProviderService;
172
191
  readonly pineConnectionService: PineConnectionService;
173
- run: (code: Code, tickerId: string, timeframe?: CandleInterval, limit?: number) => Promise<PlotRecord>;
192
+ readonly indicatorConnectionService: IndicatorConnectionService;
193
+ run: (code: Code, tickerId: string, timeframe?: CandleInterval, limit?: number, inputs?: Record<string, any>) => Promise<PlotRecord>;
174
194
  }
175
195
 
176
196
  declare class PineCacheService {
@@ -194,6 +214,7 @@ declare class PineMarkdownService {
194
214
  declare const pine: {
195
215
  pineMarkdownService: PineMarkdownService;
196
216
  pineConnectionService: PineConnectionService;
217
+ indicatorConnectionService: IndicatorConnectionService;
197
218
  pineCacheService: PineCacheService;
198
219
  pineDataService: PineDataService;
199
220
  pineJobService: PineJobService;
@@ -205,4 +226,4 @@ declare const pine: {
205
226
  };
206
227
  };
207
228
 
208
- export { AXIS_SYMBOL, type CandleModel, Code, File, type ILogger, type IPine, type IProvider, type PlotExtractConfig, type PlotMapping, type PlotModel, type PlotRecord, type SymbolInfoModel, type TPineCtor, dumpPlotData, extract, getSignal, pine as lib, markdown, run, setLogger, toMarkdown, toSignalDto, usePine };
229
+ export { AXIS_SYMBOL, type CandleModel, Code, File, type IIndicator, type ILogger, type IPine, type IProvider, type PlotExtractConfig, type PlotMapping, type PlotModel, type PlotRecord, type SymbolInfoModel, type TIndicatorCtor, type TPineCtor, dumpPlotData, extract, getSignal, pine as lib, markdown, run, setLogger, toMarkdown, toSignalDto, useIndicator, usePine };