@backtest-kit/pinets 0.0.3 → 0.0.5
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 +41 -31
- package/build/index.mjs +39 -31
- package/package.json +1 -1
- package/types.d.ts +22 -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
|
@@ -501,7 +501,7 @@ function extractRowAtIndex(plots, keys, index) {
|
|
|
501
501
|
}
|
|
502
502
|
function isRowWarmedUp(row, keys) {
|
|
503
503
|
for (const key of keys) {
|
|
504
|
-
if (
|
|
504
|
+
if (isUnsafe(row[key])) {
|
|
505
505
|
return false;
|
|
506
506
|
}
|
|
507
507
|
}
|
|
@@ -536,12 +536,12 @@ class PineMarkdownService {
|
|
|
536
536
|
});
|
|
537
537
|
const keys = Object.keys(plots);
|
|
538
538
|
if (keys.length === 0) {
|
|
539
|
-
return;
|
|
539
|
+
return [];
|
|
540
540
|
}
|
|
541
541
|
const firstPlot = plots[keys[0]];
|
|
542
542
|
const dataLength = firstPlot?.data?.length ?? 0;
|
|
543
543
|
if (dataLength === 0) {
|
|
544
|
-
return;
|
|
544
|
+
return [];
|
|
545
545
|
}
|
|
546
546
|
const rows = [];
|
|
547
547
|
let warmupComplete = false;
|
|
@@ -655,7 +655,7 @@ function usePine(ctor) {
|
|
|
655
655
|
pine.pineConnectionService.usePine(ctor);
|
|
656
656
|
}
|
|
657
657
|
|
|
658
|
-
const METHOD_NAME_RUN$
|
|
658
|
+
const METHOD_NAME_RUN$2 = "run.run";
|
|
659
659
|
const GET_SOURCE_FN$1 = async (source) => {
|
|
660
660
|
if (File.isFile(source)) {
|
|
661
661
|
const code = await pine.pineCacheService.readFile(source.path, source.baseDir);
|
|
@@ -666,16 +666,23 @@ const GET_SOURCE_FN$1 = async (source) => {
|
|
|
666
666
|
}
|
|
667
667
|
throw new Error("Source must be a File or Code instance");
|
|
668
668
|
};
|
|
669
|
-
async function run(source, { symbol, timeframe,
|
|
670
|
-
pine.loggerService.info(METHOD_NAME_RUN$
|
|
669
|
+
async function run(source, { symbol, timeframe, limit }) {
|
|
670
|
+
pine.loggerService.info(METHOD_NAME_RUN$2, {
|
|
671
671
|
source,
|
|
672
672
|
symbol,
|
|
673
673
|
timeframe,
|
|
674
|
-
mapping,
|
|
675
674
|
limit,
|
|
676
675
|
});
|
|
677
676
|
const script = await GET_SOURCE_FN$1(source);
|
|
678
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
|
+
});
|
|
679
686
|
return pine.pineDataService.extract(plots, mapping);
|
|
680
687
|
}
|
|
681
688
|
|
|
@@ -683,28 +690,6 @@ function setLogger(logger) {
|
|
|
683
690
|
pine.loggerService.setLogger(logger);
|
|
684
691
|
}
|
|
685
692
|
|
|
686
|
-
const METHOD_NAME_RUN = "strategy.getSignal";
|
|
687
|
-
const DEFAULT_ESTIMATED_TIME = 240;
|
|
688
|
-
const GET_SOURCE_FN = async (source) => {
|
|
689
|
-
if (File.isFile(source)) {
|
|
690
|
-
const code = await pine.pineCacheService.readFile(source.path, source.baseDir);
|
|
691
|
-
return Code.fromString(code);
|
|
692
|
-
}
|
|
693
|
-
if (Code.isCode(source)) {
|
|
694
|
-
return source;
|
|
695
|
-
}
|
|
696
|
-
throw new Error("Source must be a File or Code instance");
|
|
697
|
-
};
|
|
698
|
-
const SIGNAL_SCHEMA = {
|
|
699
|
-
position: "Signal",
|
|
700
|
-
priceOpen: "Close",
|
|
701
|
-
priceTakeProfit: "TakeProfit",
|
|
702
|
-
priceStopLoss: "StopLoss",
|
|
703
|
-
minuteEstimatedTime: {
|
|
704
|
-
plot: "EstimatedTime",
|
|
705
|
-
transform: (v) => v || DEFAULT_ESTIMATED_TIME,
|
|
706
|
-
},
|
|
707
|
-
};
|
|
708
693
|
function toSignalDto(data) {
|
|
709
694
|
if (data.position === 1) {
|
|
710
695
|
return {
|
|
@@ -728,6 +713,29 @@ function toSignalDto(data) {
|
|
|
728
713
|
}
|
|
729
714
|
return null;
|
|
730
715
|
}
|
|
716
|
+
|
|
717
|
+
const METHOD_NAME_RUN = "strategy.getSignal";
|
|
718
|
+
const DEFAULT_ESTIMATED_TIME = 240;
|
|
719
|
+
const GET_SOURCE_FN = async (source) => {
|
|
720
|
+
if (File.isFile(source)) {
|
|
721
|
+
const code = await pine.pineCacheService.readFile(source.path, source.baseDir);
|
|
722
|
+
return Code.fromString(code);
|
|
723
|
+
}
|
|
724
|
+
if (Code.isCode(source)) {
|
|
725
|
+
return source;
|
|
726
|
+
}
|
|
727
|
+
throw new Error("Source must be a File or Code instance");
|
|
728
|
+
};
|
|
729
|
+
const SIGNAL_SCHEMA = {
|
|
730
|
+
position: "Signal",
|
|
731
|
+
priceOpen: "Close",
|
|
732
|
+
priceTakeProfit: "TakeProfit",
|
|
733
|
+
priceStopLoss: "StopLoss",
|
|
734
|
+
minuteEstimatedTime: {
|
|
735
|
+
plot: "EstimatedTime",
|
|
736
|
+
transform: (v) => v || DEFAULT_ESTIMATED_TIME,
|
|
737
|
+
},
|
|
738
|
+
};
|
|
731
739
|
async function getSignal(source, { symbol, timeframe, limit }) {
|
|
732
740
|
pine.loggerService.info(METHOD_NAME_RUN, {
|
|
733
741
|
source,
|
|
@@ -741,7 +749,7 @@ async function getSignal(source, { symbol, timeframe, limit }) {
|
|
|
741
749
|
}
|
|
742
750
|
|
|
743
751
|
const DUMP_SIGNAL_METHOD_NAME = "dump.dumpSignal";
|
|
744
|
-
async function
|
|
752
|
+
async function dumpPlotData(signalId, plots, taName, outputDir = "./dump/ta") {
|
|
745
753
|
pine.loggerService.log(DUMP_SIGNAL_METHOD_NAME, {
|
|
746
754
|
signalId,
|
|
747
755
|
plotCount: Object.keys(plots).length,
|
|
@@ -753,9 +761,11 @@ async function dumpPineData(signalId, plots, taName, outputDir = "./dump/ta") {
|
|
|
753
761
|
exports.AXIS_SYMBOL = AXIS_SYMBOL;
|
|
754
762
|
exports.Code = Code;
|
|
755
763
|
exports.File = File;
|
|
756
|
-
exports.
|
|
764
|
+
exports.dumpPlotData = dumpPlotData;
|
|
765
|
+
exports.extract = extract;
|
|
757
766
|
exports.getSignal = getSignal;
|
|
758
767
|
exports.lib = pine;
|
|
759
768
|
exports.run = run;
|
|
760
769
|
exports.setLogger = setLogger;
|
|
770
|
+
exports.toSignalDto = toSignalDto;
|
|
761
771
|
exports.usePine = usePine;
|
package/build/index.mjs
CHANGED
|
@@ -498,7 +498,7 @@ function extractRowAtIndex(plots, keys, index) {
|
|
|
498
498
|
}
|
|
499
499
|
function isRowWarmedUp(row, keys) {
|
|
500
500
|
for (const key of keys) {
|
|
501
|
-
if (
|
|
501
|
+
if (isUnsafe(row[key])) {
|
|
502
502
|
return false;
|
|
503
503
|
}
|
|
504
504
|
}
|
|
@@ -533,12 +533,12 @@ class PineMarkdownService {
|
|
|
533
533
|
});
|
|
534
534
|
const keys = Object.keys(plots);
|
|
535
535
|
if (keys.length === 0) {
|
|
536
|
-
return;
|
|
536
|
+
return [];
|
|
537
537
|
}
|
|
538
538
|
const firstPlot = plots[keys[0]];
|
|
539
539
|
const dataLength = firstPlot?.data?.length ?? 0;
|
|
540
540
|
if (dataLength === 0) {
|
|
541
|
-
return;
|
|
541
|
+
return [];
|
|
542
542
|
}
|
|
543
543
|
const rows = [];
|
|
544
544
|
let warmupComplete = false;
|
|
@@ -652,7 +652,7 @@ function usePine(ctor) {
|
|
|
652
652
|
pine.pineConnectionService.usePine(ctor);
|
|
653
653
|
}
|
|
654
654
|
|
|
655
|
-
const METHOD_NAME_RUN$
|
|
655
|
+
const METHOD_NAME_RUN$2 = "run.run";
|
|
656
656
|
const GET_SOURCE_FN$1 = async (source) => {
|
|
657
657
|
if (File.isFile(source)) {
|
|
658
658
|
const code = await pine.pineCacheService.readFile(source.path, source.baseDir);
|
|
@@ -663,16 +663,23 @@ const GET_SOURCE_FN$1 = async (source) => {
|
|
|
663
663
|
}
|
|
664
664
|
throw new Error("Source must be a File or Code instance");
|
|
665
665
|
};
|
|
666
|
-
async function run(source, { symbol, timeframe,
|
|
667
|
-
pine.loggerService.info(METHOD_NAME_RUN$
|
|
666
|
+
async function run(source, { symbol, timeframe, limit }) {
|
|
667
|
+
pine.loggerService.info(METHOD_NAME_RUN$2, {
|
|
668
668
|
source,
|
|
669
669
|
symbol,
|
|
670
670
|
timeframe,
|
|
671
|
-
mapping,
|
|
672
671
|
limit,
|
|
673
672
|
});
|
|
674
673
|
const script = await GET_SOURCE_FN$1(source);
|
|
675
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
|
+
});
|
|
676
683
|
return pine.pineDataService.extract(plots, mapping);
|
|
677
684
|
}
|
|
678
685
|
|
|
@@ -680,28 +687,6 @@ function setLogger(logger) {
|
|
|
680
687
|
pine.loggerService.setLogger(logger);
|
|
681
688
|
}
|
|
682
689
|
|
|
683
|
-
const METHOD_NAME_RUN = "strategy.getSignal";
|
|
684
|
-
const DEFAULT_ESTIMATED_TIME = 240;
|
|
685
|
-
const GET_SOURCE_FN = async (source) => {
|
|
686
|
-
if (File.isFile(source)) {
|
|
687
|
-
const code = await pine.pineCacheService.readFile(source.path, source.baseDir);
|
|
688
|
-
return Code.fromString(code);
|
|
689
|
-
}
|
|
690
|
-
if (Code.isCode(source)) {
|
|
691
|
-
return source;
|
|
692
|
-
}
|
|
693
|
-
throw new Error("Source must be a File or Code instance");
|
|
694
|
-
};
|
|
695
|
-
const SIGNAL_SCHEMA = {
|
|
696
|
-
position: "Signal",
|
|
697
|
-
priceOpen: "Close",
|
|
698
|
-
priceTakeProfit: "TakeProfit",
|
|
699
|
-
priceStopLoss: "StopLoss",
|
|
700
|
-
minuteEstimatedTime: {
|
|
701
|
-
plot: "EstimatedTime",
|
|
702
|
-
transform: (v) => v || DEFAULT_ESTIMATED_TIME,
|
|
703
|
-
},
|
|
704
|
-
};
|
|
705
690
|
function toSignalDto(data) {
|
|
706
691
|
if (data.position === 1) {
|
|
707
692
|
return {
|
|
@@ -725,6 +710,29 @@ function toSignalDto(data) {
|
|
|
725
710
|
}
|
|
726
711
|
return null;
|
|
727
712
|
}
|
|
713
|
+
|
|
714
|
+
const METHOD_NAME_RUN = "strategy.getSignal";
|
|
715
|
+
const DEFAULT_ESTIMATED_TIME = 240;
|
|
716
|
+
const GET_SOURCE_FN = async (source) => {
|
|
717
|
+
if (File.isFile(source)) {
|
|
718
|
+
const code = await pine.pineCacheService.readFile(source.path, source.baseDir);
|
|
719
|
+
return Code.fromString(code);
|
|
720
|
+
}
|
|
721
|
+
if (Code.isCode(source)) {
|
|
722
|
+
return source;
|
|
723
|
+
}
|
|
724
|
+
throw new Error("Source must be a File or Code instance");
|
|
725
|
+
};
|
|
726
|
+
const SIGNAL_SCHEMA = {
|
|
727
|
+
position: "Signal",
|
|
728
|
+
priceOpen: "Close",
|
|
729
|
+
priceTakeProfit: "TakeProfit",
|
|
730
|
+
priceStopLoss: "StopLoss",
|
|
731
|
+
minuteEstimatedTime: {
|
|
732
|
+
plot: "EstimatedTime",
|
|
733
|
+
transform: (v) => v || DEFAULT_ESTIMATED_TIME,
|
|
734
|
+
},
|
|
735
|
+
};
|
|
728
736
|
async function getSignal(source, { symbol, timeframe, limit }) {
|
|
729
737
|
pine.loggerService.info(METHOD_NAME_RUN, {
|
|
730
738
|
source,
|
|
@@ -738,7 +746,7 @@ async function getSignal(source, { symbol, timeframe, limit }) {
|
|
|
738
746
|
}
|
|
739
747
|
|
|
740
748
|
const DUMP_SIGNAL_METHOD_NAME = "dump.dumpSignal";
|
|
741
|
-
async function
|
|
749
|
+
async function dumpPlotData(signalId, plots, taName, outputDir = "./dump/ta") {
|
|
742
750
|
pine.loggerService.log(DUMP_SIGNAL_METHOD_NAME, {
|
|
743
751
|
signalId,
|
|
744
752
|
plotCount: Object.keys(plots).length,
|
|
@@ -747,4 +755,4 @@ async function dumpPineData(signalId, plots, taName, outputDir = "./dump/ta") {
|
|
|
747
755
|
return await pine.pineMarkdownService.dump(signalId, plots, taName, outputDir);
|
|
748
756
|
}
|
|
749
757
|
|
|
750
|
-
export { AXIS_SYMBOL, Code, File,
|
|
758
|
+
export { AXIS_SYMBOL, Code, File, dumpPlotData, extract, getSignal, pine as lib, run, setLogger, toSignalDto, 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.5",
|
|
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,19 @@ 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>;
|
|
88
|
+
|
|
89
|
+
interface SignalData {
|
|
90
|
+
position: number;
|
|
91
|
+
priceOpen: number;
|
|
92
|
+
priceTakeProfit: number;
|
|
93
|
+
priceStopLoss: number;
|
|
94
|
+
minuteEstimatedTime: number;
|
|
95
|
+
}
|
|
96
|
+
interface Signal extends ISignalDto {
|
|
97
|
+
id: string;
|
|
98
|
+
}
|
|
99
|
+
declare function toSignalDto(data: SignalData): Signal | null;
|
|
87
100
|
|
|
88
101
|
interface CandleModel {
|
|
89
102
|
openTime: number;
|
|
@@ -171,4 +184,4 @@ declare const pine: {
|
|
|
171
184
|
loggerService: LoggerService;
|
|
172
185
|
};
|
|
173
186
|
|
|
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,
|
|
187
|
+
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, toSignalDto, usePine };
|