@backtest-kit/pinets 0.0.1 → 0.0.3
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 +173 -0
- package/build/index.mjs +175 -3
- package/package.json +1 -1
- package/types.d.ts +30 -15
package/build/index.cjs
CHANGED
|
@@ -99,6 +99,9 @@ const cacheServices$1 = {
|
|
|
99
99
|
const connectionServices$1 = {
|
|
100
100
|
pineConnectionService: Symbol("pineConnectionService"),
|
|
101
101
|
};
|
|
102
|
+
const markdownServices$1 = {
|
|
103
|
+
pineMarkdownService: Symbol("pineMarkdownService"),
|
|
104
|
+
};
|
|
102
105
|
const TYPES = {
|
|
103
106
|
...baseServices,
|
|
104
107
|
...providerServices$1,
|
|
@@ -106,6 +109,7 @@ const TYPES = {
|
|
|
106
109
|
...dataServices$1,
|
|
107
110
|
...cacheServices$1,
|
|
108
111
|
...connectionServices$1,
|
|
112
|
+
...markdownServices$1,
|
|
109
113
|
};
|
|
110
114
|
|
|
111
115
|
const MS_PER_MINUTE = 60000;
|
|
@@ -438,6 +442,155 @@ class PineConnectionService {
|
|
|
438
442
|
}
|
|
439
443
|
}
|
|
440
444
|
|
|
445
|
+
const TABLE_ROWS_LIMIT = 48;
|
|
446
|
+
const GET_METHOD_CONTEXT_FN = () => {
|
|
447
|
+
if (backtestKit.MethodContextService.hasContext()) {
|
|
448
|
+
const { exchangeName, frameName, strategyName } = backtestKit.lib.methodContextService.context;
|
|
449
|
+
return { exchangeName, frameName, strategyName };
|
|
450
|
+
}
|
|
451
|
+
return {
|
|
452
|
+
strategyName: "",
|
|
453
|
+
exchangeName: "",
|
|
454
|
+
frameName: "",
|
|
455
|
+
};
|
|
456
|
+
};
|
|
457
|
+
const GET_EXECUTION_CONTEXT_FN = () => {
|
|
458
|
+
if (backtestKit.ExecutionContextService.hasContext()) {
|
|
459
|
+
const { when } = backtestKit.lib.executionContextService.context;
|
|
460
|
+
return { when: when.toISOString() };
|
|
461
|
+
}
|
|
462
|
+
return {
|
|
463
|
+
when: "",
|
|
464
|
+
};
|
|
465
|
+
};
|
|
466
|
+
const DEFAULT_FORMAT = (v) => v !== null ? Number(v).toFixed(4) : "N/A";
|
|
467
|
+
function isUnsafe(value) {
|
|
468
|
+
if (value === null)
|
|
469
|
+
return true;
|
|
470
|
+
if (typeof value !== "number")
|
|
471
|
+
return true;
|
|
472
|
+
if (isNaN(value))
|
|
473
|
+
return true;
|
|
474
|
+
if (!isFinite(value))
|
|
475
|
+
return true;
|
|
476
|
+
return false;
|
|
477
|
+
}
|
|
478
|
+
function extractRowAtIndex(plots, keys, index) {
|
|
479
|
+
let time = null;
|
|
480
|
+
for (const key of keys) {
|
|
481
|
+
const plotData = plots[key]?.data;
|
|
482
|
+
if (plotData && plotData[index]) {
|
|
483
|
+
time = plotData[index].time;
|
|
484
|
+
break;
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
if (time === null)
|
|
488
|
+
return null;
|
|
489
|
+
const row = { time };
|
|
490
|
+
for (const key of keys) {
|
|
491
|
+
const plotData = plots[key]?.data;
|
|
492
|
+
if (plotData && plotData[index]) {
|
|
493
|
+
const value = plotData[index].value;
|
|
494
|
+
row[key] = isUnsafe(value) ? null : value;
|
|
495
|
+
}
|
|
496
|
+
else {
|
|
497
|
+
row[key] = null;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
return row;
|
|
501
|
+
}
|
|
502
|
+
function isRowWarmedUp(row, keys) {
|
|
503
|
+
for (const key of keys) {
|
|
504
|
+
if (!row[key]) {
|
|
505
|
+
return false;
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
return true;
|
|
509
|
+
}
|
|
510
|
+
function generateMarkdownTable(rows, keys, signalId) {
|
|
511
|
+
let markdown = "";
|
|
512
|
+
const { when: createdAt } = GET_EXECUTION_CONTEXT_FN();
|
|
513
|
+
markdown += `# PineScript Technical Analysis Dump\n\n`;
|
|
514
|
+
markdown += `**Signal ID**: ${String(signalId)}\n`;
|
|
515
|
+
if (createdAt) {
|
|
516
|
+
markdown += `**Current datetime**: ${String(createdAt)}\n`;
|
|
517
|
+
}
|
|
518
|
+
markdown += "\n";
|
|
519
|
+
const header = `| Timestamp | ${keys.join(" | ")} |\n`;
|
|
520
|
+
const separator = `| --- | ${keys.map(() => "---").join(" | ")} |\n`;
|
|
521
|
+
markdown += header;
|
|
522
|
+
markdown += separator;
|
|
523
|
+
for (const row of rows) {
|
|
524
|
+
const timestamp = new Date(row.time).toISOString();
|
|
525
|
+
const cells = keys.map((key) => DEFAULT_FORMAT(row[key]));
|
|
526
|
+
markdown += `| ${timestamp} | ${cells.join(" | ")} |\n`;
|
|
527
|
+
}
|
|
528
|
+
return markdown;
|
|
529
|
+
}
|
|
530
|
+
class PineMarkdownService {
|
|
531
|
+
constructor() {
|
|
532
|
+
this.loggerService = inject(TYPES.loggerService);
|
|
533
|
+
this.getData = (plots) => {
|
|
534
|
+
this.loggerService.log("pineMarkdownService getReport", {
|
|
535
|
+
plotCount: Object.keys(plots).length,
|
|
536
|
+
});
|
|
537
|
+
const keys = Object.keys(plots);
|
|
538
|
+
if (keys.length === 0) {
|
|
539
|
+
return;
|
|
540
|
+
}
|
|
541
|
+
const firstPlot = plots[keys[0]];
|
|
542
|
+
const dataLength = firstPlot?.data?.length ?? 0;
|
|
543
|
+
if (dataLength === 0) {
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
const rows = [];
|
|
547
|
+
let warmupComplete = false;
|
|
548
|
+
for (let i = 0; i < dataLength; i++) {
|
|
549
|
+
const row = extractRowAtIndex(plots, keys, i);
|
|
550
|
+
if (!row)
|
|
551
|
+
continue;
|
|
552
|
+
if (!warmupComplete) {
|
|
553
|
+
if (isRowWarmedUp(row, keys)) {
|
|
554
|
+
warmupComplete = true;
|
|
555
|
+
}
|
|
556
|
+
else {
|
|
557
|
+
continue;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
rows.push(row);
|
|
561
|
+
}
|
|
562
|
+
return rows.slice(-TABLE_ROWS_LIMIT);
|
|
563
|
+
};
|
|
564
|
+
this.getReport = (signalId, plots) => {
|
|
565
|
+
this.loggerService.log("pineMarkdownService getReport", {
|
|
566
|
+
signalId,
|
|
567
|
+
plotCount: Object.keys(plots).length,
|
|
568
|
+
});
|
|
569
|
+
const rows = this.getData(plots);
|
|
570
|
+
const keys = Object.keys(plots);
|
|
571
|
+
return generateMarkdownTable(rows, keys, signalId);
|
|
572
|
+
};
|
|
573
|
+
this.dump = async (signalId, plots, taName, outputDir = `./dump/ta/${taName}`) => {
|
|
574
|
+
this.loggerService.log("pineMarkdownService dumpSignal", {
|
|
575
|
+
signalId,
|
|
576
|
+
plotCount: Object.keys(plots).length,
|
|
577
|
+
outputDir,
|
|
578
|
+
});
|
|
579
|
+
const content = this.getReport(signalId, plots);
|
|
580
|
+
const { exchangeName, frameName, strategyName } = GET_METHOD_CONTEXT_FN();
|
|
581
|
+
await backtestKit.Markdown.writeData(taName, content, {
|
|
582
|
+
path: outputDir,
|
|
583
|
+
file: `${String(signalId)}.md`,
|
|
584
|
+
symbol: "",
|
|
585
|
+
signalId: String(signalId),
|
|
586
|
+
strategyName,
|
|
587
|
+
exchangeName,
|
|
588
|
+
frameName,
|
|
589
|
+
});
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
|
|
441
594
|
{
|
|
442
595
|
provide(TYPES.loggerService, () => new LoggerService());
|
|
443
596
|
}
|
|
@@ -457,6 +610,9 @@ class PineConnectionService {
|
|
|
457
610
|
{
|
|
458
611
|
provide(TYPES.pineConnectionService, () => new PineConnectionService());
|
|
459
612
|
}
|
|
613
|
+
{
|
|
614
|
+
provide(TYPES.pineMarkdownService, () => new PineMarkdownService());
|
|
615
|
+
}
|
|
460
616
|
|
|
461
617
|
const commonServices = {
|
|
462
618
|
loggerService: inject(TYPES.loggerService),
|
|
@@ -477,6 +633,9 @@ const cacheServices = {
|
|
|
477
633
|
const connectionServices = {
|
|
478
634
|
pineConnectionService: inject(TYPES.pineConnectionService),
|
|
479
635
|
};
|
|
636
|
+
const markdownServices = {
|
|
637
|
+
pineMarkdownService: inject(TYPES.pineMarkdownService),
|
|
638
|
+
};
|
|
480
639
|
const pine = {
|
|
481
640
|
...commonServices,
|
|
482
641
|
...providerServices,
|
|
@@ -484,6 +643,7 @@ const pine = {
|
|
|
484
643
|
...dataServices,
|
|
485
644
|
...cacheServices,
|
|
486
645
|
...connectionServices,
|
|
646
|
+
...markdownServices,
|
|
487
647
|
};
|
|
488
648
|
init();
|
|
489
649
|
|
|
@@ -548,6 +708,7 @@ const SIGNAL_SCHEMA = {
|
|
|
548
708
|
function toSignalDto(data) {
|
|
549
709
|
if (data.position === 1) {
|
|
550
710
|
return {
|
|
711
|
+
id: functoolsKit.randomString(),
|
|
551
712
|
position: "long",
|
|
552
713
|
priceOpen: data.priceOpen,
|
|
553
714
|
priceTakeProfit: data.priceTakeProfit,
|
|
@@ -557,6 +718,7 @@ function toSignalDto(data) {
|
|
|
557
718
|
}
|
|
558
719
|
if (data.position === -1) {
|
|
559
720
|
return {
|
|
721
|
+
id: functoolsKit.randomString(),
|
|
560
722
|
position: "short",
|
|
561
723
|
priceOpen: data.priceOpen,
|
|
562
724
|
priceTakeProfit: data.priceTakeProfit,
|
|
@@ -578,9 +740,20 @@ async function getSignal(source, { symbol, timeframe, limit }) {
|
|
|
578
740
|
return toSignalDto(data);
|
|
579
741
|
}
|
|
580
742
|
|
|
743
|
+
const DUMP_SIGNAL_METHOD_NAME = "dump.dumpSignal";
|
|
744
|
+
async function dumpPineData(signalId, plots, taName, outputDir = "./dump/ta") {
|
|
745
|
+
pine.loggerService.log(DUMP_SIGNAL_METHOD_NAME, {
|
|
746
|
+
signalId,
|
|
747
|
+
plotCount: Object.keys(plots).length,
|
|
748
|
+
outputDir,
|
|
749
|
+
});
|
|
750
|
+
return await pine.pineMarkdownService.dump(signalId, plots, taName, outputDir);
|
|
751
|
+
}
|
|
752
|
+
|
|
581
753
|
exports.AXIS_SYMBOL = AXIS_SYMBOL;
|
|
582
754
|
exports.Code = Code;
|
|
583
755
|
exports.File = File;
|
|
756
|
+
exports.dumpPineData = dumpPineData;
|
|
584
757
|
exports.getSignal = getSignal;
|
|
585
758
|
exports.lib = pine;
|
|
586
759
|
exports.run = run;
|
package/build/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { join } from 'path';
|
|
2
|
-
import { getDate, getRawCandles } from 'backtest-kit';
|
|
2
|
+
import { getDate, getRawCandles, Markdown, MethodContextService, lib, ExecutionContextService } from 'backtest-kit';
|
|
3
3
|
import { createActivator } from 'di-kit';
|
|
4
|
-
import { singleshot, memoize } from 'functools-kit';
|
|
4
|
+
import { singleshot, memoize, randomString } from 'functools-kit';
|
|
5
5
|
import fs from 'fs/promises';
|
|
6
6
|
import { createRequire } from 'module';
|
|
7
7
|
|
|
@@ -96,6 +96,9 @@ const cacheServices$1 = {
|
|
|
96
96
|
const connectionServices$1 = {
|
|
97
97
|
pineConnectionService: Symbol("pineConnectionService"),
|
|
98
98
|
};
|
|
99
|
+
const markdownServices$1 = {
|
|
100
|
+
pineMarkdownService: Symbol("pineMarkdownService"),
|
|
101
|
+
};
|
|
99
102
|
const TYPES = {
|
|
100
103
|
...baseServices,
|
|
101
104
|
...providerServices$1,
|
|
@@ -103,6 +106,7 @@ const TYPES = {
|
|
|
103
106
|
...dataServices$1,
|
|
104
107
|
...cacheServices$1,
|
|
105
108
|
...connectionServices$1,
|
|
109
|
+
...markdownServices$1,
|
|
106
110
|
};
|
|
107
111
|
|
|
108
112
|
const MS_PER_MINUTE = 60000;
|
|
@@ -435,6 +439,155 @@ class PineConnectionService {
|
|
|
435
439
|
}
|
|
436
440
|
}
|
|
437
441
|
|
|
442
|
+
const TABLE_ROWS_LIMIT = 48;
|
|
443
|
+
const GET_METHOD_CONTEXT_FN = () => {
|
|
444
|
+
if (MethodContextService.hasContext()) {
|
|
445
|
+
const { exchangeName, frameName, strategyName } = lib.methodContextService.context;
|
|
446
|
+
return { exchangeName, frameName, strategyName };
|
|
447
|
+
}
|
|
448
|
+
return {
|
|
449
|
+
strategyName: "",
|
|
450
|
+
exchangeName: "",
|
|
451
|
+
frameName: "",
|
|
452
|
+
};
|
|
453
|
+
};
|
|
454
|
+
const GET_EXECUTION_CONTEXT_FN = () => {
|
|
455
|
+
if (ExecutionContextService.hasContext()) {
|
|
456
|
+
const { when } = lib.executionContextService.context;
|
|
457
|
+
return { when: when.toISOString() };
|
|
458
|
+
}
|
|
459
|
+
return {
|
|
460
|
+
when: "",
|
|
461
|
+
};
|
|
462
|
+
};
|
|
463
|
+
const DEFAULT_FORMAT = (v) => v !== null ? Number(v).toFixed(4) : "N/A";
|
|
464
|
+
function isUnsafe(value) {
|
|
465
|
+
if (value === null)
|
|
466
|
+
return true;
|
|
467
|
+
if (typeof value !== "number")
|
|
468
|
+
return true;
|
|
469
|
+
if (isNaN(value))
|
|
470
|
+
return true;
|
|
471
|
+
if (!isFinite(value))
|
|
472
|
+
return true;
|
|
473
|
+
return false;
|
|
474
|
+
}
|
|
475
|
+
function extractRowAtIndex(plots, keys, index) {
|
|
476
|
+
let time = null;
|
|
477
|
+
for (const key of keys) {
|
|
478
|
+
const plotData = plots[key]?.data;
|
|
479
|
+
if (plotData && plotData[index]) {
|
|
480
|
+
time = plotData[index].time;
|
|
481
|
+
break;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
if (time === null)
|
|
485
|
+
return null;
|
|
486
|
+
const row = { time };
|
|
487
|
+
for (const key of keys) {
|
|
488
|
+
const plotData = plots[key]?.data;
|
|
489
|
+
if (plotData && plotData[index]) {
|
|
490
|
+
const value = plotData[index].value;
|
|
491
|
+
row[key] = isUnsafe(value) ? null : value;
|
|
492
|
+
}
|
|
493
|
+
else {
|
|
494
|
+
row[key] = null;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
return row;
|
|
498
|
+
}
|
|
499
|
+
function isRowWarmedUp(row, keys) {
|
|
500
|
+
for (const key of keys) {
|
|
501
|
+
if (!row[key]) {
|
|
502
|
+
return false;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
return true;
|
|
506
|
+
}
|
|
507
|
+
function generateMarkdownTable(rows, keys, signalId) {
|
|
508
|
+
let markdown = "";
|
|
509
|
+
const { when: createdAt } = GET_EXECUTION_CONTEXT_FN();
|
|
510
|
+
markdown += `# PineScript Technical Analysis Dump\n\n`;
|
|
511
|
+
markdown += `**Signal ID**: ${String(signalId)}\n`;
|
|
512
|
+
if (createdAt) {
|
|
513
|
+
markdown += `**Current datetime**: ${String(createdAt)}\n`;
|
|
514
|
+
}
|
|
515
|
+
markdown += "\n";
|
|
516
|
+
const header = `| Timestamp | ${keys.join(" | ")} |\n`;
|
|
517
|
+
const separator = `| --- | ${keys.map(() => "---").join(" | ")} |\n`;
|
|
518
|
+
markdown += header;
|
|
519
|
+
markdown += separator;
|
|
520
|
+
for (const row of rows) {
|
|
521
|
+
const timestamp = new Date(row.time).toISOString();
|
|
522
|
+
const cells = keys.map((key) => DEFAULT_FORMAT(row[key]));
|
|
523
|
+
markdown += `| ${timestamp} | ${cells.join(" | ")} |\n`;
|
|
524
|
+
}
|
|
525
|
+
return markdown;
|
|
526
|
+
}
|
|
527
|
+
class PineMarkdownService {
|
|
528
|
+
constructor() {
|
|
529
|
+
this.loggerService = inject(TYPES.loggerService);
|
|
530
|
+
this.getData = (plots) => {
|
|
531
|
+
this.loggerService.log("pineMarkdownService getReport", {
|
|
532
|
+
plotCount: Object.keys(plots).length,
|
|
533
|
+
});
|
|
534
|
+
const keys = Object.keys(plots);
|
|
535
|
+
if (keys.length === 0) {
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
const firstPlot = plots[keys[0]];
|
|
539
|
+
const dataLength = firstPlot?.data?.length ?? 0;
|
|
540
|
+
if (dataLength === 0) {
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
const rows = [];
|
|
544
|
+
let warmupComplete = false;
|
|
545
|
+
for (let i = 0; i < dataLength; i++) {
|
|
546
|
+
const row = extractRowAtIndex(plots, keys, i);
|
|
547
|
+
if (!row)
|
|
548
|
+
continue;
|
|
549
|
+
if (!warmupComplete) {
|
|
550
|
+
if (isRowWarmedUp(row, keys)) {
|
|
551
|
+
warmupComplete = true;
|
|
552
|
+
}
|
|
553
|
+
else {
|
|
554
|
+
continue;
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
rows.push(row);
|
|
558
|
+
}
|
|
559
|
+
return rows.slice(-TABLE_ROWS_LIMIT);
|
|
560
|
+
};
|
|
561
|
+
this.getReport = (signalId, plots) => {
|
|
562
|
+
this.loggerService.log("pineMarkdownService getReport", {
|
|
563
|
+
signalId,
|
|
564
|
+
plotCount: Object.keys(plots).length,
|
|
565
|
+
});
|
|
566
|
+
const rows = this.getData(plots);
|
|
567
|
+
const keys = Object.keys(plots);
|
|
568
|
+
return generateMarkdownTable(rows, keys, signalId);
|
|
569
|
+
};
|
|
570
|
+
this.dump = async (signalId, plots, taName, outputDir = `./dump/ta/${taName}`) => {
|
|
571
|
+
this.loggerService.log("pineMarkdownService dumpSignal", {
|
|
572
|
+
signalId,
|
|
573
|
+
plotCount: Object.keys(plots).length,
|
|
574
|
+
outputDir,
|
|
575
|
+
});
|
|
576
|
+
const content = this.getReport(signalId, plots);
|
|
577
|
+
const { exchangeName, frameName, strategyName } = GET_METHOD_CONTEXT_FN();
|
|
578
|
+
await Markdown.writeData(taName, content, {
|
|
579
|
+
path: outputDir,
|
|
580
|
+
file: `${String(signalId)}.md`,
|
|
581
|
+
symbol: "",
|
|
582
|
+
signalId: String(signalId),
|
|
583
|
+
strategyName,
|
|
584
|
+
exchangeName,
|
|
585
|
+
frameName,
|
|
586
|
+
});
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
|
|
438
591
|
{
|
|
439
592
|
provide(TYPES.loggerService, () => new LoggerService());
|
|
440
593
|
}
|
|
@@ -454,6 +607,9 @@ class PineConnectionService {
|
|
|
454
607
|
{
|
|
455
608
|
provide(TYPES.pineConnectionService, () => new PineConnectionService());
|
|
456
609
|
}
|
|
610
|
+
{
|
|
611
|
+
provide(TYPES.pineMarkdownService, () => new PineMarkdownService());
|
|
612
|
+
}
|
|
457
613
|
|
|
458
614
|
const commonServices = {
|
|
459
615
|
loggerService: inject(TYPES.loggerService),
|
|
@@ -474,6 +630,9 @@ const cacheServices = {
|
|
|
474
630
|
const connectionServices = {
|
|
475
631
|
pineConnectionService: inject(TYPES.pineConnectionService),
|
|
476
632
|
};
|
|
633
|
+
const markdownServices = {
|
|
634
|
+
pineMarkdownService: inject(TYPES.pineMarkdownService),
|
|
635
|
+
};
|
|
477
636
|
const pine = {
|
|
478
637
|
...commonServices,
|
|
479
638
|
...providerServices,
|
|
@@ -481,6 +640,7 @@ const pine = {
|
|
|
481
640
|
...dataServices,
|
|
482
641
|
...cacheServices,
|
|
483
642
|
...connectionServices,
|
|
643
|
+
...markdownServices,
|
|
484
644
|
};
|
|
485
645
|
init();
|
|
486
646
|
|
|
@@ -545,6 +705,7 @@ const SIGNAL_SCHEMA = {
|
|
|
545
705
|
function toSignalDto(data) {
|
|
546
706
|
if (data.position === 1) {
|
|
547
707
|
return {
|
|
708
|
+
id: randomString(),
|
|
548
709
|
position: "long",
|
|
549
710
|
priceOpen: data.priceOpen,
|
|
550
711
|
priceTakeProfit: data.priceTakeProfit,
|
|
@@ -554,6 +715,7 @@ function toSignalDto(data) {
|
|
|
554
715
|
}
|
|
555
716
|
if (data.position === -1) {
|
|
556
717
|
return {
|
|
718
|
+
id: randomString(),
|
|
557
719
|
position: "short",
|
|
558
720
|
priceOpen: data.priceOpen,
|
|
559
721
|
priceTakeProfit: data.priceTakeProfit,
|
|
@@ -575,4 +737,14 @@ async function getSignal(source, { symbol, timeframe, limit }) {
|
|
|
575
737
|
return toSignalDto(data);
|
|
576
738
|
}
|
|
577
739
|
|
|
578
|
-
|
|
740
|
+
const DUMP_SIGNAL_METHOD_NAME = "dump.dumpSignal";
|
|
741
|
+
async function dumpPineData(signalId, plots, taName, outputDir = "./dump/ta") {
|
|
742
|
+
pine.loggerService.log(DUMP_SIGNAL_METHOD_NAME, {
|
|
743
|
+
signalId,
|
|
744
|
+
plotCount: Object.keys(plots).length,
|
|
745
|
+
outputDir,
|
|
746
|
+
});
|
|
747
|
+
return await pine.pineMarkdownService.dump(signalId, plots, taName, outputDir);
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
export { AXIS_SYMBOL, Code, File, dumpPineData, getSignal, pine as lib, run, setLogger, usePine };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@backtest-kit/pinets",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
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",
|
package/types.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { TPineCtor as TPineCtor$1 } from 'src/interface/Pine.interface';
|
|
2
1
|
import { CandleInterval, ISignalDto } from 'backtest-kit';
|
|
3
2
|
|
|
4
3
|
declare class Code {
|
|
@@ -18,8 +17,6 @@ declare class File {
|
|
|
18
17
|
static isFile: (value: unknown) => value is File;
|
|
19
18
|
}
|
|
20
19
|
|
|
21
|
-
declare function usePine<T = TPineCtor$1>(ctor: T): void;
|
|
22
|
-
|
|
23
20
|
type PlotData = {
|
|
24
21
|
time: number;
|
|
25
22
|
value: number;
|
|
@@ -32,6 +29,19 @@ type PlotRecord = {
|
|
|
32
29
|
plots: PlotModel;
|
|
33
30
|
};
|
|
34
31
|
|
|
32
|
+
interface IProvider {
|
|
33
|
+
getMarketData(tickerId: string, timeframe: string, limit?: number, sDate?: number, eDate?: number): Promise<any>;
|
|
34
|
+
getSymbolInfo(tickerId: string): Promise<any>;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
type TPineCtor = (source: IProvider, tickerId: string, timeframe: string, limit: number) => IPine;
|
|
38
|
+
interface IPine {
|
|
39
|
+
ready(): Promise<void>;
|
|
40
|
+
run(code: string): Promise<PlotRecord>;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
declare function usePine<T = TPineCtor>(ctor: T): void;
|
|
44
|
+
|
|
35
45
|
type PlotExtractConfig<T = number> = {
|
|
36
46
|
plot: string;
|
|
37
47
|
barsBack?: number;
|
|
@@ -72,6 +82,9 @@ interface IParams {
|
|
|
72
82
|
}
|
|
73
83
|
declare function getSignal(source: File | Code, { symbol, timeframe, limit }: IParams): Promise<ISignalDto | null>;
|
|
74
84
|
|
|
85
|
+
type ResultId$1 = string | number;
|
|
86
|
+
declare function dumpPineData(signalId: ResultId$1, plots: PlotModel, taName: string, outputDir?: string): Promise<void>;
|
|
87
|
+
|
|
75
88
|
interface CandleModel {
|
|
76
89
|
openTime: number;
|
|
77
90
|
open: number;
|
|
@@ -91,17 +104,6 @@ interface SymbolInfoModel {
|
|
|
91
104
|
timezone: string;
|
|
92
105
|
}
|
|
93
106
|
|
|
94
|
-
interface IProvider {
|
|
95
|
-
getMarketData(tickerId: string, timeframe: string, limit?: number, sDate?: number, eDate?: number): Promise<any>;
|
|
96
|
-
getSymbolInfo(tickerId: string): Promise<any>;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
type TPineCtor = (source: IProvider, tickerId: string, timeframe: string, limit: number) => IPine;
|
|
100
|
-
interface IPine {
|
|
101
|
-
ready(): Promise<void>;
|
|
102
|
-
run(code: string): Promise<PlotRecord>;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
107
|
declare const AXIS_SYMBOL = "_AXIS";
|
|
106
108
|
declare class AxisProviderService implements IProvider {
|
|
107
109
|
private readonly loggerService;
|
|
@@ -146,7 +148,20 @@ declare class PineCacheService {
|
|
|
146
148
|
clear: (path?: string, baseDir?: string) => Promise<void>;
|
|
147
149
|
}
|
|
148
150
|
|
|
151
|
+
type ResultId = string | number;
|
|
152
|
+
interface IPlotRow {
|
|
153
|
+
time: number;
|
|
154
|
+
[key: string]: number | null;
|
|
155
|
+
}
|
|
156
|
+
declare class PineMarkdownService {
|
|
157
|
+
private readonly loggerService;
|
|
158
|
+
getData: (plots: PlotModel) => IPlotRow[];
|
|
159
|
+
getReport: (signalId: ResultId, plots: PlotModel) => string;
|
|
160
|
+
dump: (signalId: ResultId, plots: PlotModel, taName: string, outputDir?: string) => Promise<void>;
|
|
161
|
+
}
|
|
162
|
+
|
|
149
163
|
declare const pine: {
|
|
164
|
+
pineMarkdownService: PineMarkdownService;
|
|
150
165
|
pineConnectionService: PineConnectionService;
|
|
151
166
|
pineCacheService: PineCacheService;
|
|
152
167
|
pineDataService: PineDataService;
|
|
@@ -156,4 +171,4 @@ declare const pine: {
|
|
|
156
171
|
loggerService: LoggerService;
|
|
157
172
|
};
|
|
158
173
|
|
|
159
|
-
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, getSignal, pine as lib, run, setLogger, usePine };
|
|
174
|
+
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, dumpPineData, getSignal, pine as lib, run, setLogger, usePine };
|