@backtest-kit/pinets 0.0.3 → 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 +16 -8
- package/build/index.mjs +15 -8
- 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
|
@@ -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
|
|
|
@@ -741,7 +748,7 @@ async function getSignal(source, { symbol, timeframe, limit }) {
|
|
|
741
748
|
}
|
|
742
749
|
|
|
743
750
|
const DUMP_SIGNAL_METHOD_NAME = "dump.dumpSignal";
|
|
744
|
-
async function
|
|
751
|
+
async function dumpPlotData(signalId, plots, taName, outputDir = "./dump/ta") {
|
|
745
752
|
pine.loggerService.log(DUMP_SIGNAL_METHOD_NAME, {
|
|
746
753
|
signalId,
|
|
747
754
|
plotCount: Object.keys(plots).length,
|
|
@@ -753,7 +760,8 @@ async function dumpPineData(signalId, plots, taName, outputDir = "./dump/ta") {
|
|
|
753
760
|
exports.AXIS_SYMBOL = AXIS_SYMBOL;
|
|
754
761
|
exports.Code = Code;
|
|
755
762
|
exports.File = File;
|
|
756
|
-
exports.
|
|
763
|
+
exports.dumpPlotData = dumpPlotData;
|
|
764
|
+
exports.extract = extract;
|
|
757
765
|
exports.getSignal = getSignal;
|
|
758
766
|
exports.lib = pine;
|
|
759
767
|
exports.run = run;
|
package/build/index.mjs
CHANGED
|
@@ -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
|
|
|
@@ -738,7 +745,7 @@ async function getSignal(source, { symbol, timeframe, limit }) {
|
|
|
738
745
|
}
|
|
739
746
|
|
|
740
747
|
const DUMP_SIGNAL_METHOD_NAME = "dump.dumpSignal";
|
|
741
|
-
async function
|
|
748
|
+
async function dumpPlotData(signalId, plots, taName, outputDir = "./dump/ta") {
|
|
742
749
|
pine.loggerService.log(DUMP_SIGNAL_METHOD_NAME, {
|
|
743
750
|
signalId,
|
|
744
751
|
plotCount: Object.keys(plots).length,
|
|
@@ -747,4 +754,4 @@ async function dumpPineData(signalId, plots, taName, outputDir = "./dump/ta") {
|
|
|
747
754
|
return await pine.pineMarkdownService.dump(signalId, plots, taName, outputDir);
|
|
748
755
|
}
|
|
749
756
|
|
|
750
|
-
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 };
|