@backtest-kit/pinets 0.0.2 → 0.0.4
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/README.md +30 -29
- package/build/index.cjs +48 -13
- package/build/index.mjs +48 -14
- package/package.json +1 -1
- package/types.d.ts +10 -9
package/README.md
CHANGED
|
@@ -47,22 +47,22 @@ Create a Pine Script file (`strategy.pine`):
|
|
|
47
47
|
|
|
48
48
|
```pine
|
|
49
49
|
//@version=5
|
|
50
|
-
indicator("Signal Strategy")
|
|
50
|
+
indicator("Signal Strategy 100 candles of 1H timeframe")
|
|
51
51
|
|
|
52
|
-
// Indicators
|
|
53
|
-
rsi = ta.rsi(close,
|
|
54
|
-
atr = ta.atr(
|
|
55
|
-
ema_fast = ta.ema(close,
|
|
56
|
-
ema_slow = ta.ema(close,
|
|
52
|
+
// Indicators - faster settings for 1H
|
|
53
|
+
rsi = ta.rsi(close, 10)
|
|
54
|
+
atr = ta.atr(10)
|
|
55
|
+
ema_fast = ta.ema(close, 7)
|
|
56
|
+
ema_slow = ta.ema(close, 16)
|
|
57
57
|
|
|
58
58
|
// Conditions
|
|
59
|
-
long_cond = ta.crossover(ema_fast, ema_slow) and rsi <
|
|
60
|
-
short_cond = ta.crossunder(ema_fast, ema_slow) and rsi >
|
|
59
|
+
long_cond = ta.crossover(ema_fast, ema_slow) and rsi < 65
|
|
60
|
+
short_cond = ta.crossunder(ema_fast, ema_slow) and rsi > 35
|
|
61
61
|
|
|
62
|
-
// Levels
|
|
63
|
-
sl_long = close - atr *
|
|
62
|
+
// Levels - tighter SL, wider TP for better RR
|
|
63
|
+
sl_long = close - atr * 1.5
|
|
64
64
|
tp_long = close + atr * 3
|
|
65
|
-
sl_short = close + atr *
|
|
65
|
+
sl_short = close + atr * 1.5
|
|
66
66
|
tp_short = close - atr * 3
|
|
67
67
|
|
|
68
68
|
// Plots for extraction
|
|
@@ -70,7 +70,7 @@ plot(close, "Close")
|
|
|
70
70
|
plot(long_cond ? 1 : short_cond ? -1 : 0, "Signal")
|
|
71
71
|
plot(long_cond ? sl_long : sl_short, "StopLoss")
|
|
72
72
|
plot(long_cond ? tp_long : tp_short, "TakeProfit")
|
|
73
|
-
plot(
|
|
73
|
+
plot(60, "EstimatedTime") // 1 hour in minutes
|
|
74
74
|
```
|
|
75
75
|
|
|
76
76
|
Use it in your strategy:
|
|
@@ -88,7 +88,7 @@ addStrategy({
|
|
|
88
88
|
|
|
89
89
|
return await getSignal(source, {
|
|
90
90
|
symbol,
|
|
91
|
-
timeframe: '
|
|
91
|
+
timeframe: '1h',
|
|
92
92
|
limit: 100,
|
|
93
93
|
});
|
|
94
94
|
}
|
|
@@ -131,28 +131,29 @@ const signal = await getSignal(source, {
|
|
|
131
131
|
For advanced use cases, extract any Pine `plot()` with custom mapping:
|
|
132
132
|
|
|
133
133
|
```typescript
|
|
134
|
-
import { File, run } from '@backtest-kit/pinets';
|
|
134
|
+
import { File, run, extract } from '@backtest-kit/pinets';
|
|
135
135
|
|
|
136
136
|
const source = File.fromPath('indicators.pine');
|
|
137
137
|
|
|
138
|
-
const
|
|
138
|
+
const plots = await run(source, {
|
|
139
139
|
symbol: 'ETHUSDT',
|
|
140
140
|
timeframe: '1h',
|
|
141
141
|
limit: 200,
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
const data = extract(plots, {
|
|
145
|
+
// Simple: plot name -> number
|
|
146
|
+
rsi: 'RSI',
|
|
147
|
+
macd: 'MACD',
|
|
148
|
+
|
|
149
|
+
// Advanced: with transform and lookback
|
|
150
|
+
prevRsi: {
|
|
151
|
+
plot: 'RSI',
|
|
152
|
+
barsBack: 1, // Previous bar value
|
|
153
|
+
},
|
|
154
|
+
trendStrength: {
|
|
155
|
+
plot: 'ADX',
|
|
156
|
+
transform: (v) => v > 25 ? 'strong' : 'weak',
|
|
156
157
|
},
|
|
157
158
|
});
|
|
158
159
|
|
package/build/index.cjs
CHANGED
|
@@ -442,6 +442,27 @@ class PineConnectionService {
|
|
|
442
442
|
}
|
|
443
443
|
}
|
|
444
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
|
+
};
|
|
445
466
|
const DEFAULT_FORMAT = (v) => v !== null ? Number(v).toFixed(4) : "N/A";
|
|
446
467
|
function isUnsafe(value) {
|
|
447
468
|
if (value === null)
|
|
@@ -488,8 +509,13 @@ function isRowWarmedUp(row, keys) {
|
|
|
488
509
|
}
|
|
489
510
|
function generateMarkdownTable(rows, keys, signalId) {
|
|
490
511
|
let markdown = "";
|
|
512
|
+
const { when: createdAt } = GET_EXECUTION_CONTEXT_FN();
|
|
491
513
|
markdown += `# PineScript Technical Analysis Dump\n\n`;
|
|
492
|
-
markdown += `**Signal ID**: ${String(signalId)}\n
|
|
514
|
+
markdown += `**Signal ID**: ${String(signalId)}\n`;
|
|
515
|
+
if (createdAt) {
|
|
516
|
+
markdown += `**Current datetime**: ${String(createdAt)}\n`;
|
|
517
|
+
}
|
|
518
|
+
markdown += "\n";
|
|
493
519
|
const header = `| Timestamp | ${keys.join(" | ")} |\n`;
|
|
494
520
|
const separator = `| --- | ${keys.map(() => "---").join(" | ")} |\n`;
|
|
495
521
|
markdown += header;
|
|
@@ -510,12 +536,12 @@ class PineMarkdownService {
|
|
|
510
536
|
});
|
|
511
537
|
const keys = Object.keys(plots);
|
|
512
538
|
if (keys.length === 0) {
|
|
513
|
-
return;
|
|
539
|
+
return [];
|
|
514
540
|
}
|
|
515
541
|
const firstPlot = plots[keys[0]];
|
|
516
542
|
const dataLength = firstPlot?.data?.length ?? 0;
|
|
517
543
|
if (dataLength === 0) {
|
|
518
|
-
return;
|
|
544
|
+
return [];
|
|
519
545
|
}
|
|
520
546
|
const rows = [];
|
|
521
547
|
let warmupComplete = false;
|
|
@@ -533,7 +559,7 @@ class PineMarkdownService {
|
|
|
533
559
|
}
|
|
534
560
|
rows.push(row);
|
|
535
561
|
}
|
|
536
|
-
return rows;
|
|
562
|
+
return rows.slice(-TABLE_ROWS_LIMIT);
|
|
537
563
|
};
|
|
538
564
|
this.getReport = (signalId, plots) => {
|
|
539
565
|
this.loggerService.log("pineMarkdownService getReport", {
|
|
@@ -551,14 +577,15 @@ class PineMarkdownService {
|
|
|
551
577
|
outputDir,
|
|
552
578
|
});
|
|
553
579
|
const content = this.getReport(signalId, plots);
|
|
580
|
+
const { exchangeName, frameName, strategyName } = GET_METHOD_CONTEXT_FN();
|
|
554
581
|
await backtestKit.Markdown.writeData(taName, content, {
|
|
555
582
|
path: outputDir,
|
|
556
583
|
file: `${String(signalId)}.md`,
|
|
557
584
|
symbol: "",
|
|
558
585
|
signalId: String(signalId),
|
|
559
|
-
strategyName
|
|
560
|
-
exchangeName
|
|
561
|
-
frameName
|
|
586
|
+
strategyName,
|
|
587
|
+
exchangeName,
|
|
588
|
+
frameName,
|
|
562
589
|
});
|
|
563
590
|
};
|
|
564
591
|
}
|
|
@@ -628,7 +655,7 @@ function usePine(ctor) {
|
|
|
628
655
|
pine.pineConnectionService.usePine(ctor);
|
|
629
656
|
}
|
|
630
657
|
|
|
631
|
-
const METHOD_NAME_RUN$
|
|
658
|
+
const METHOD_NAME_RUN$2 = "run.run";
|
|
632
659
|
const GET_SOURCE_FN$1 = async (source) => {
|
|
633
660
|
if (File.isFile(source)) {
|
|
634
661
|
const code = await pine.pineCacheService.readFile(source.path, source.baseDir);
|
|
@@ -639,16 +666,23 @@ const GET_SOURCE_FN$1 = async (source) => {
|
|
|
639
666
|
}
|
|
640
667
|
throw new Error("Source must be a File or Code instance");
|
|
641
668
|
};
|
|
642
|
-
async function run(source, { symbol, timeframe,
|
|
643
|
-
pine.loggerService.info(METHOD_NAME_RUN$
|
|
669
|
+
async function run(source, { symbol, timeframe, limit }) {
|
|
670
|
+
pine.loggerService.info(METHOD_NAME_RUN$2, {
|
|
644
671
|
source,
|
|
645
672
|
symbol,
|
|
646
673
|
timeframe,
|
|
647
|
-
mapping,
|
|
648
674
|
limit,
|
|
649
675
|
});
|
|
650
676
|
const script = await GET_SOURCE_FN$1(source);
|
|
651
677
|
const { plots } = await pine.pineJobService.run(script, symbol, timeframe, limit);
|
|
678
|
+
return plots;
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
const METHOD_NAME_RUN$1 = "extract.extract";
|
|
682
|
+
async function extract(plots, mapping) {
|
|
683
|
+
pine.loggerService.info(METHOD_NAME_RUN$1, {
|
|
684
|
+
mapping,
|
|
685
|
+
});
|
|
652
686
|
return pine.pineDataService.extract(plots, mapping);
|
|
653
687
|
}
|
|
654
688
|
|
|
@@ -714,7 +748,7 @@ async function getSignal(source, { symbol, timeframe, limit }) {
|
|
|
714
748
|
}
|
|
715
749
|
|
|
716
750
|
const DUMP_SIGNAL_METHOD_NAME = "dump.dumpSignal";
|
|
717
|
-
async function
|
|
751
|
+
async function dumpPlotData(signalId, plots, taName, outputDir = "./dump/ta") {
|
|
718
752
|
pine.loggerService.log(DUMP_SIGNAL_METHOD_NAME, {
|
|
719
753
|
signalId,
|
|
720
754
|
plotCount: Object.keys(plots).length,
|
|
@@ -726,7 +760,8 @@ async function dumpPineData(signalId, plots, taName, outputDir = "./dump/ta") {
|
|
|
726
760
|
exports.AXIS_SYMBOL = AXIS_SYMBOL;
|
|
727
761
|
exports.Code = Code;
|
|
728
762
|
exports.File = File;
|
|
729
|
-
exports.
|
|
763
|
+
exports.dumpPlotData = dumpPlotData;
|
|
764
|
+
exports.extract = extract;
|
|
730
765
|
exports.getSignal = getSignal;
|
|
731
766
|
exports.lib = pine;
|
|
732
767
|
exports.run = run;
|
package/build/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { join } from 'path';
|
|
2
|
-
import { getDate, getRawCandles, Markdown } from 'backtest-kit';
|
|
2
|
+
import { getDate, getRawCandles, Markdown, MethodContextService, lib, ExecutionContextService } from 'backtest-kit';
|
|
3
3
|
import { createActivator } from 'di-kit';
|
|
4
4
|
import { singleshot, memoize, randomString } from 'functools-kit';
|
|
5
5
|
import fs from 'fs/promises';
|
|
@@ -439,6 +439,27 @@ class PineConnectionService {
|
|
|
439
439
|
}
|
|
440
440
|
}
|
|
441
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
|
+
};
|
|
442
463
|
const DEFAULT_FORMAT = (v) => v !== null ? Number(v).toFixed(4) : "N/A";
|
|
443
464
|
function isUnsafe(value) {
|
|
444
465
|
if (value === null)
|
|
@@ -485,8 +506,13 @@ function isRowWarmedUp(row, keys) {
|
|
|
485
506
|
}
|
|
486
507
|
function generateMarkdownTable(rows, keys, signalId) {
|
|
487
508
|
let markdown = "";
|
|
509
|
+
const { when: createdAt } = GET_EXECUTION_CONTEXT_FN();
|
|
488
510
|
markdown += `# PineScript Technical Analysis Dump\n\n`;
|
|
489
|
-
markdown += `**Signal ID**: ${String(signalId)}\n
|
|
511
|
+
markdown += `**Signal ID**: ${String(signalId)}\n`;
|
|
512
|
+
if (createdAt) {
|
|
513
|
+
markdown += `**Current datetime**: ${String(createdAt)}\n`;
|
|
514
|
+
}
|
|
515
|
+
markdown += "\n";
|
|
490
516
|
const header = `| Timestamp | ${keys.join(" | ")} |\n`;
|
|
491
517
|
const separator = `| --- | ${keys.map(() => "---").join(" | ")} |\n`;
|
|
492
518
|
markdown += header;
|
|
@@ -507,12 +533,12 @@ class PineMarkdownService {
|
|
|
507
533
|
});
|
|
508
534
|
const keys = Object.keys(plots);
|
|
509
535
|
if (keys.length === 0) {
|
|
510
|
-
return;
|
|
536
|
+
return [];
|
|
511
537
|
}
|
|
512
538
|
const firstPlot = plots[keys[0]];
|
|
513
539
|
const dataLength = firstPlot?.data?.length ?? 0;
|
|
514
540
|
if (dataLength === 0) {
|
|
515
|
-
return;
|
|
541
|
+
return [];
|
|
516
542
|
}
|
|
517
543
|
const rows = [];
|
|
518
544
|
let warmupComplete = false;
|
|
@@ -530,7 +556,7 @@ class PineMarkdownService {
|
|
|
530
556
|
}
|
|
531
557
|
rows.push(row);
|
|
532
558
|
}
|
|
533
|
-
return rows;
|
|
559
|
+
return rows.slice(-TABLE_ROWS_LIMIT);
|
|
534
560
|
};
|
|
535
561
|
this.getReport = (signalId, plots) => {
|
|
536
562
|
this.loggerService.log("pineMarkdownService getReport", {
|
|
@@ -548,14 +574,15 @@ class PineMarkdownService {
|
|
|
548
574
|
outputDir,
|
|
549
575
|
});
|
|
550
576
|
const content = this.getReport(signalId, plots);
|
|
577
|
+
const { exchangeName, frameName, strategyName } = GET_METHOD_CONTEXT_FN();
|
|
551
578
|
await Markdown.writeData(taName, content, {
|
|
552
579
|
path: outputDir,
|
|
553
580
|
file: `${String(signalId)}.md`,
|
|
554
581
|
symbol: "",
|
|
555
582
|
signalId: String(signalId),
|
|
556
|
-
strategyName
|
|
557
|
-
exchangeName
|
|
558
|
-
frameName
|
|
583
|
+
strategyName,
|
|
584
|
+
exchangeName,
|
|
585
|
+
frameName,
|
|
559
586
|
});
|
|
560
587
|
};
|
|
561
588
|
}
|
|
@@ -625,7 +652,7 @@ function usePine(ctor) {
|
|
|
625
652
|
pine.pineConnectionService.usePine(ctor);
|
|
626
653
|
}
|
|
627
654
|
|
|
628
|
-
const METHOD_NAME_RUN$
|
|
655
|
+
const METHOD_NAME_RUN$2 = "run.run";
|
|
629
656
|
const GET_SOURCE_FN$1 = async (source) => {
|
|
630
657
|
if (File.isFile(source)) {
|
|
631
658
|
const code = await pine.pineCacheService.readFile(source.path, source.baseDir);
|
|
@@ -636,16 +663,23 @@ const GET_SOURCE_FN$1 = async (source) => {
|
|
|
636
663
|
}
|
|
637
664
|
throw new Error("Source must be a File or Code instance");
|
|
638
665
|
};
|
|
639
|
-
async function run(source, { symbol, timeframe,
|
|
640
|
-
pine.loggerService.info(METHOD_NAME_RUN$
|
|
666
|
+
async function run(source, { symbol, timeframe, limit }) {
|
|
667
|
+
pine.loggerService.info(METHOD_NAME_RUN$2, {
|
|
641
668
|
source,
|
|
642
669
|
symbol,
|
|
643
670
|
timeframe,
|
|
644
|
-
mapping,
|
|
645
671
|
limit,
|
|
646
672
|
});
|
|
647
673
|
const script = await GET_SOURCE_FN$1(source);
|
|
648
674
|
const { plots } = await pine.pineJobService.run(script, symbol, timeframe, limit);
|
|
675
|
+
return plots;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
const METHOD_NAME_RUN$1 = "extract.extract";
|
|
679
|
+
async function extract(plots, mapping) {
|
|
680
|
+
pine.loggerService.info(METHOD_NAME_RUN$1, {
|
|
681
|
+
mapping,
|
|
682
|
+
});
|
|
649
683
|
return pine.pineDataService.extract(plots, mapping);
|
|
650
684
|
}
|
|
651
685
|
|
|
@@ -711,7 +745,7 @@ async function getSignal(source, { symbol, timeframe, limit }) {
|
|
|
711
745
|
}
|
|
712
746
|
|
|
713
747
|
const DUMP_SIGNAL_METHOD_NAME = "dump.dumpSignal";
|
|
714
|
-
async function
|
|
748
|
+
async function dumpPlotData(signalId, plots, taName, outputDir = "./dump/ta") {
|
|
715
749
|
pine.loggerService.log(DUMP_SIGNAL_METHOD_NAME, {
|
|
716
750
|
signalId,
|
|
717
751
|
plotCount: Object.keys(plots).length,
|
|
@@ -720,4 +754,4 @@ async function dumpPineData(signalId, plots, taName, outputDir = "./dump/ta") {
|
|
|
720
754
|
return await pine.pineMarkdownService.dump(signalId, plots, taName, outputDir);
|
|
721
755
|
}
|
|
722
756
|
|
|
723
|
-
export { AXIS_SYMBOL, Code, File,
|
|
757
|
+
export { AXIS_SYMBOL, Code, File, dumpPlotData, extract, 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.4",
|
|
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
|
@@ -42,6 +42,13 @@ interface IPine {
|
|
|
42
42
|
|
|
43
43
|
declare function usePine<T = TPineCtor>(ctor: T): void;
|
|
44
44
|
|
|
45
|
+
interface IRunParams {
|
|
46
|
+
symbol: string;
|
|
47
|
+
timeframe: CandleInterval;
|
|
48
|
+
limit: number;
|
|
49
|
+
}
|
|
50
|
+
declare function run(source: File | Code, { symbol, timeframe, limit }: IRunParams): Promise<PlotModel>;
|
|
51
|
+
|
|
45
52
|
type PlotExtractConfig<T = number> = {
|
|
46
53
|
plot: string;
|
|
47
54
|
barsBack?: number;
|
|
@@ -58,13 +65,7 @@ declare class PineDataService {
|
|
|
58
65
|
extract<M extends PlotMapping>(plots: PlotModel, mapping: M): ExtractedData<M>;
|
|
59
66
|
}
|
|
60
67
|
|
|
61
|
-
|
|
62
|
-
symbol: string;
|
|
63
|
-
timeframe: CandleInterval;
|
|
64
|
-
limit: number;
|
|
65
|
-
mapping: M;
|
|
66
|
-
}
|
|
67
|
-
declare function run<M extends PlotMapping>(source: File | Code, { symbol, timeframe, mapping, limit }: IRunParams<M>): Promise<ExtractedData<M>>;
|
|
68
|
+
declare function extract<M extends PlotMapping>(plots: PlotModel, mapping: M): Promise<ExtractedData<M>>;
|
|
68
69
|
|
|
69
70
|
interface ILogger {
|
|
70
71
|
log(topic: string, ...args: any[]): void;
|
|
@@ -83,7 +84,7 @@ interface IParams {
|
|
|
83
84
|
declare function getSignal(source: File | Code, { symbol, timeframe, limit }: IParams): Promise<ISignalDto | null>;
|
|
84
85
|
|
|
85
86
|
type ResultId$1 = string | number;
|
|
86
|
-
declare function
|
|
87
|
+
declare function dumpPlotData(signalId: ResultId$1, plots: PlotModel, taName: string, outputDir?: string): Promise<void>;
|
|
87
88
|
|
|
88
89
|
interface CandleModel {
|
|
89
90
|
openTime: number;
|
|
@@ -171,4 +172,4 @@ declare const pine: {
|
|
|
171
172
|
loggerService: LoggerService;
|
|
172
173
|
};
|
|
173
174
|
|
|
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,
|
|
175
|
+
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, run, setLogger, usePine };
|