@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 +109 -23
- package/build/index.mjs +109 -24
- package/package.json +4 -4
- package/types.d.ts +29 -8
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.
|
|
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
|
-
|
|
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$
|
|
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$
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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.
|
|
72
|
+
"backtest-kit": "3.5.0",
|
|
73
73
|
"worker-testbed": "1.0.12"
|
|
74
74
|
},
|
|
75
75
|
"peerDependencies": {
|
|
76
|
-
"backtest-kit": "^3.
|
|
77
|
-
"pinets": "^0.8.
|
|
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:
|
|
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
|
-
|
|
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 };
|