@backtest-kit/pinets 3.0.1 → 3.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 CHANGED
@@ -3,6 +3,7 @@
3
3
  var path = require('path');
4
4
  var backtestKit = require('backtest-kit');
5
5
  var diKit = require('di-kit');
6
+ var diScoped = require('di-scoped');
6
7
  var functoolsKit = require('functools-kit');
7
8
  var fs = require('fs/promises');
8
9
  var module$1 = require('module');
@@ -82,6 +83,7 @@ const { provide, inject, init, override } = diKit.createActivator("pine");
82
83
 
83
84
  const baseServices = {
84
85
  loggerService: Symbol("loggerService"),
86
+ contextService: Symbol("contextService"),
85
87
  };
86
88
  const providerServices$1 = {
87
89
  axisProviderService: Symbol("axisProviderService"),
@@ -230,9 +232,28 @@ class AxisProviderService {
230
232
  }
231
233
  }
232
234
 
235
+ const ContextService = diScoped.scoped(class {
236
+ constructor(context) {
237
+ this.context = context;
238
+ }
239
+ });
240
+
241
+ const GET_RAW_CANDLES_FN = async (self, symbol, interval, limit, sDate, eDate) => {
242
+ if (ContextService.hasContext()) {
243
+ return await backtestKit.Exchange.getRawCandles(symbol, interval, self.contextService.context, limit, sDate, eDate);
244
+ }
245
+ if (!backtestKit.MethodContextService.hasContext()) {
246
+ throw new Error("MethodContextService context is required to get market data for pinets if exchangeName?: string is not specified");
247
+ }
248
+ if (!backtestKit.ExecutionContextService.hasContext()) {
249
+ throw new Error("ExecutionContextService context is required to get market data for pinets if exchangeName?: string is not specified");
250
+ }
251
+ return await backtestKit.getRawCandles(symbol, interval, limit, sDate, eDate);
252
+ };
233
253
  class CandleProviderService {
234
254
  constructor() {
235
255
  this.loggerService = inject(TYPES.loggerService);
256
+ this.contextService = inject(TYPES.contextService);
236
257
  }
237
258
  async getMarketData(tickerId, timeframe, limit, sDate, eDate) {
238
259
  this.loggerService.log("candleProviderService getMarketData", {
@@ -245,7 +266,7 @@ class CandleProviderService {
245
266
  const symbol = tickerId
246
267
  .toUpperCase()
247
268
  .replace(/^BINANCE:|^BYBIT:|^OKX:/, "");
248
- const rawCandles = await backtestKit.getRawCandles(symbol, timeframe, limit, sDate, eDate);
269
+ const rawCandles = await GET_RAW_CANDLES_FN(this, symbol, timeframe, limit, sDate, eDate);
249
270
  const candles = rawCandles.map((c) => ({
250
271
  openTime: c.timestamp,
251
272
  open: c.open,
@@ -553,6 +574,7 @@ class PineMarkdownService {
553
574
 
554
575
  {
555
576
  provide(TYPES.loggerService, () => new LoggerService());
577
+ provide(TYPES.contextService, () => new ContextService());
556
578
  }
557
579
  {
558
580
  provide(TYPES.axisProviderService, () => new AxisProviderService());
@@ -576,6 +598,7 @@ class PineMarkdownService {
576
598
 
577
599
  const commonServices = {
578
600
  loggerService: inject(TYPES.loggerService),
601
+ contextService: inject(TYPES.contextService),
579
602
  };
580
603
  const providerServices = {
581
604
  axisProviderService: inject(TYPES.axisProviderService),
@@ -626,7 +649,13 @@ const GET_SOURCE_FN$1 = async (source) => {
626
649
  }
627
650
  throw new Error("Source must be a File or Code instance");
628
651
  };
629
- async function run(source, { symbol, timeframe, limit }) {
652
+ const RUN_INFERENCE_FN = async (script, symbol, timeframe, limit, exchangeName) => {
653
+ if (exchangeName) {
654
+ return await ContextService.runInContext(async () => await pine.pineJobService.run(script, symbol, timeframe, limit), { exchangeName });
655
+ }
656
+ return await pine.pineJobService.run(script, symbol, timeframe, limit);
657
+ };
658
+ async function run(source, { symbol, timeframe, limit }, exchangeName) {
630
659
  pine.loggerService.info(METHOD_NAME_RUN$2, {
631
660
  source,
632
661
  symbol,
@@ -634,7 +663,7 @@ async function run(source, { symbol, timeframe, limit }) {
634
663
  limit,
635
664
  });
636
665
  const script = await GET_SOURCE_FN$1(source);
637
- const { plots } = await pine.pineJobService.run(script, symbol, timeframe, limit);
666
+ const { plots } = await RUN_INFERENCE_FN(script, symbol, timeframe, limit, exchangeName);
638
667
  return plots;
639
668
  }
640
669
 
@@ -726,6 +755,16 @@ async function dumpPlotData(signalId, plots, mapping, taName, outputDir = "./dum
726
755
  return await pine.pineMarkdownService.dump(signalId, plots, mapping, taName, outputDir);
727
756
  }
728
757
 
758
+ const TO_MARKDOWN_METHOD_NAME = "markdown.toMarkdown";
759
+ async function toMarkdown(signalId, plots, mapping) {
760
+ pine.loggerService.log(TO_MARKDOWN_METHOD_NAME, {
761
+ signalId,
762
+ plotCount: Object.keys(plots).length,
763
+ mapping,
764
+ });
765
+ return await pine.pineMarkdownService.getReport(signalId, plots, mapping);
766
+ }
767
+
729
768
  exports.AXIS_SYMBOL = AXIS_SYMBOL;
730
769
  exports.Code = Code;
731
770
  exports.File = File;
@@ -735,5 +774,6 @@ exports.getSignal = getSignal;
735
774
  exports.lib = pine;
736
775
  exports.run = run;
737
776
  exports.setLogger = setLogger;
777
+ exports.toMarkdown = toMarkdown;
738
778
  exports.toSignalDto = toSignalDto;
739
779
  exports.usePine = usePine;
package/build/index.mjs CHANGED
@@ -1,6 +1,7 @@
1
1
  import { join } from 'path';
2
- import { getDate, getRawCandles, Markdown, MethodContextService, lib, ExecutionContextService } from 'backtest-kit';
2
+ import { getDate, Exchange, MethodContextService, ExecutionContextService, getRawCandles, Markdown, lib } from 'backtest-kit';
3
3
  import { createActivator } from 'di-kit';
4
+ import { scoped } from 'di-scoped';
4
5
  import { singleshot, memoize, randomString } from 'functools-kit';
5
6
  import fs from 'fs/promises';
6
7
  import { createRequire } from 'module';
@@ -79,6 +80,7 @@ const { provide, inject, init, override } = createActivator("pine");
79
80
 
80
81
  const baseServices = {
81
82
  loggerService: Symbol("loggerService"),
83
+ contextService: Symbol("contextService"),
82
84
  };
83
85
  const providerServices$1 = {
84
86
  axisProviderService: Symbol("axisProviderService"),
@@ -227,9 +229,28 @@ class AxisProviderService {
227
229
  }
228
230
  }
229
231
 
232
+ const ContextService = scoped(class {
233
+ constructor(context) {
234
+ this.context = context;
235
+ }
236
+ });
237
+
238
+ const GET_RAW_CANDLES_FN = async (self, symbol, interval, limit, sDate, eDate) => {
239
+ if (ContextService.hasContext()) {
240
+ return await Exchange.getRawCandles(symbol, interval, self.contextService.context, limit, sDate, eDate);
241
+ }
242
+ if (!MethodContextService.hasContext()) {
243
+ throw new Error("MethodContextService context is required to get market data for pinets if exchangeName?: string is not specified");
244
+ }
245
+ if (!ExecutionContextService.hasContext()) {
246
+ throw new Error("ExecutionContextService context is required to get market data for pinets if exchangeName?: string is not specified");
247
+ }
248
+ return await getRawCandles(symbol, interval, limit, sDate, eDate);
249
+ };
230
250
  class CandleProviderService {
231
251
  constructor() {
232
252
  this.loggerService = inject(TYPES.loggerService);
253
+ this.contextService = inject(TYPES.contextService);
233
254
  }
234
255
  async getMarketData(tickerId, timeframe, limit, sDate, eDate) {
235
256
  this.loggerService.log("candleProviderService getMarketData", {
@@ -242,7 +263,7 @@ class CandleProviderService {
242
263
  const symbol = tickerId
243
264
  .toUpperCase()
244
265
  .replace(/^BINANCE:|^BYBIT:|^OKX:/, "");
245
- const rawCandles = await getRawCandles(symbol, timeframe, limit, sDate, eDate);
266
+ const rawCandles = await GET_RAW_CANDLES_FN(this, symbol, timeframe, limit, sDate, eDate);
246
267
  const candles = rawCandles.map((c) => ({
247
268
  openTime: c.timestamp,
248
269
  open: c.open,
@@ -550,6 +571,7 @@ class PineMarkdownService {
550
571
 
551
572
  {
552
573
  provide(TYPES.loggerService, () => new LoggerService());
574
+ provide(TYPES.contextService, () => new ContextService());
553
575
  }
554
576
  {
555
577
  provide(TYPES.axisProviderService, () => new AxisProviderService());
@@ -573,6 +595,7 @@ class PineMarkdownService {
573
595
 
574
596
  const commonServices = {
575
597
  loggerService: inject(TYPES.loggerService),
598
+ contextService: inject(TYPES.contextService),
576
599
  };
577
600
  const providerServices = {
578
601
  axisProviderService: inject(TYPES.axisProviderService),
@@ -623,7 +646,13 @@ const GET_SOURCE_FN$1 = async (source) => {
623
646
  }
624
647
  throw new Error("Source must be a File or Code instance");
625
648
  };
626
- async function run(source, { symbol, timeframe, limit }) {
649
+ const RUN_INFERENCE_FN = async (script, symbol, timeframe, limit, exchangeName) => {
650
+ if (exchangeName) {
651
+ return await ContextService.runInContext(async () => await pine.pineJobService.run(script, symbol, timeframe, limit), { exchangeName });
652
+ }
653
+ return await pine.pineJobService.run(script, symbol, timeframe, limit);
654
+ };
655
+ async function run(source, { symbol, timeframe, limit }, exchangeName) {
627
656
  pine.loggerService.info(METHOD_NAME_RUN$2, {
628
657
  source,
629
658
  symbol,
@@ -631,7 +660,7 @@ async function run(source, { symbol, timeframe, limit }) {
631
660
  limit,
632
661
  });
633
662
  const script = await GET_SOURCE_FN$1(source);
634
- const { plots } = await pine.pineJobService.run(script, symbol, timeframe, limit);
663
+ const { plots } = await RUN_INFERENCE_FN(script, symbol, timeframe, limit, exchangeName);
635
664
  return plots;
636
665
  }
637
666
 
@@ -723,4 +752,14 @@ async function dumpPlotData(signalId, plots, mapping, taName, outputDir = "./dum
723
752
  return await pine.pineMarkdownService.dump(signalId, plots, mapping, taName, outputDir);
724
753
  }
725
754
 
726
- export { AXIS_SYMBOL, Code, File, dumpPlotData, extract, getSignal, pine as lib, run, setLogger, toSignalDto, usePine };
755
+ const TO_MARKDOWN_METHOD_NAME = "markdown.toMarkdown";
756
+ async function toMarkdown(signalId, plots, mapping) {
757
+ pine.loggerService.log(TO_MARKDOWN_METHOD_NAME, {
758
+ signalId,
759
+ plotCount: Object.keys(plots).length,
760
+ mapping,
761
+ });
762
+ return await pine.pineMarkdownService.getReport(signalId, plots, mapping);
763
+ }
764
+
765
+ export { AXIS_SYMBOL, Code, File, dumpPlotData, extract, getSignal, pine as lib, run, setLogger, toMarkdown, toSignalDto, usePine };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backtest-kit/pinets",
3
- "version": "3.0.1",
3
+ "version": "3.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
@@ -42,12 +42,17 @@ interface IPine {
42
42
 
43
43
  declare function usePine<T = TPineCtor>(ctor: T): void;
44
44
 
45
+ type ExchangeName = string;
46
+ interface IContext {
47
+ exchangeName: ExchangeName;
48
+ }
49
+
45
50
  interface IRunParams {
46
51
  symbol: string;
47
52
  timeframe: CandleInterval;
48
53
  limit: number;
49
54
  }
50
- declare function run(source: File | Code, { symbol, timeframe, limit }: IRunParams): Promise<PlotModel>;
55
+ declare function run(source: File | Code, { symbol, timeframe, limit }: IRunParams, exchangeName?: ExchangeName): Promise<PlotModel>;
51
56
 
52
57
  type PlotExtractConfig<T = number> = {
53
58
  plot: string;
@@ -83,8 +88,11 @@ interface IParams {
83
88
  }
84
89
  declare function getSignal(source: File | Code, { symbol, timeframe, limit }: IParams): Promise<ISignalDto | null>;
85
90
 
91
+ type ResultId$3 = string | number;
92
+ declare function dumpPlotData<M extends PlotMapping>(signalId: ResultId$3, plots: PlotModel, mapping: M, taName: string, outputDir?: string): Promise<void>;
93
+
86
94
  type ResultId$2 = string | number;
87
- declare function dumpPlotData<M extends PlotMapping>(signalId: ResultId$2, plots: PlotModel, mapping: M, taName: string, outputDir?: string): Promise<void>;
95
+ declare function toMarkdown<M extends PlotMapping>(signalId: ResultId$2, plots: PlotModel, mapping: M): Promise<string>;
88
96
 
89
97
  type ResultId$1 = string | number;
90
98
  interface SignalData {
@@ -135,7 +143,10 @@ declare class LoggerService implements ILogger {
135
143
  }
136
144
 
137
145
  declare class CandleProviderService implements IProvider {
138
- private readonly loggerService;
146
+ readonly loggerService: LoggerService;
147
+ readonly contextService: {
148
+ readonly context: IContext;
149
+ };
139
150
  getMarketData(tickerId: string, timeframe: string, limit?: number, sDate?: number, eDate?: number): Promise<any[]>;
140
151
  getSymbolInfo(tickerId: string): Promise<any>;
141
152
  }
@@ -183,6 +194,9 @@ declare const pine: {
183
194
  axisProviderService: AxisProviderService;
184
195
  candleProviderService: CandleProviderService;
185
196
  loggerService: LoggerService;
197
+ contextService: {
198
+ readonly context: IContext;
199
+ };
186
200
  };
187
201
 
188
- 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 };
202
+ 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, toMarkdown, toSignalDto, usePine };