@backtest-kit/pinets 3.0.2 → 3.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/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');
@@ -80,9 +81,12 @@ class LoggerService {
80
81
 
81
82
  const { provide, inject, init, override } = diKit.createActivator("pine");
82
83
 
83
- const baseServices = {
84
+ const baseServices$1 = {
84
85
  loggerService: Symbol("loggerService"),
85
86
  };
87
+ const contextServices$1 = {
88
+ exchangeContextService: Symbol("exchangeContextService"),
89
+ };
86
90
  const providerServices$1 = {
87
91
  axisProviderService: Symbol("axisProviderService"),
88
92
  candleProviderService: Symbol("candleProviderService"),
@@ -103,7 +107,8 @@ const markdownServices$1 = {
103
107
  pineMarkdownService: Symbol("pineMarkdownService"),
104
108
  };
105
109
  const TYPES = {
106
- ...baseServices,
110
+ ...baseServices$1,
111
+ ...contextServices$1,
107
112
  ...providerServices$1,
108
113
  ...jobServices$1,
109
114
  ...dataServices$1,
@@ -230,9 +235,28 @@ class AxisProviderService {
230
235
  }
231
236
  }
232
237
 
238
+ const ExchangeContextService = diScoped.scoped(class {
239
+ constructor(context) {
240
+ this.context = context;
241
+ }
242
+ });
243
+
244
+ const GET_RAW_CANDLES_FN = async (self, symbol, interval, limit, sDate, eDate) => {
245
+ if (ExchangeContextService.hasContext()) {
246
+ return await backtestKit.Exchange.getRawCandles(symbol, interval, self.exchangeContextService.context, limit, sDate, eDate);
247
+ }
248
+ if (!backtestKit.MethodContextService.hasContext()) {
249
+ throw new Error("MethodContextService context is required to get market data for pinets if exchangeName?: string is not specified");
250
+ }
251
+ if (!backtestKit.ExecutionContextService.hasContext()) {
252
+ throw new Error("ExecutionContextService context is required to get market data for pinets if exchangeName?: string is not specified");
253
+ }
254
+ return await backtestKit.getRawCandles(symbol, interval, limit, sDate, eDate);
255
+ };
233
256
  class CandleProviderService {
234
257
  constructor() {
235
258
  this.loggerService = inject(TYPES.loggerService);
259
+ this.exchangeContextService = inject(TYPES.exchangeContextService);
236
260
  }
237
261
  async getMarketData(tickerId, timeframe, limit, sDate, eDate) {
238
262
  this.loggerService.log("candleProviderService getMarketData", {
@@ -245,7 +269,7 @@ class CandleProviderService {
245
269
  const symbol = tickerId
246
270
  .toUpperCase()
247
271
  .replace(/^BINANCE:|^BYBIT:|^OKX:/, "");
248
- const rawCandles = await backtestKit.getRawCandles(symbol, timeframe, limit, sDate, eDate);
272
+ const rawCandles = await GET_RAW_CANDLES_FN(this, symbol, timeframe, limit, sDate, eDate);
249
273
  const candles = rawCandles.map((c) => ({
250
274
  openTime: c.timestamp,
251
275
  open: c.open,
@@ -554,6 +578,9 @@ class PineMarkdownService {
554
578
  {
555
579
  provide(TYPES.loggerService, () => new LoggerService());
556
580
  }
581
+ {
582
+ provide(TYPES.exchangeContextService, () => new ExchangeContextService());
583
+ }
557
584
  {
558
585
  provide(TYPES.axisProviderService, () => new AxisProviderService());
559
586
  provide(TYPES.candleProviderService, () => new CandleProviderService());
@@ -574,9 +601,12 @@ class PineMarkdownService {
574
601
  provide(TYPES.pineMarkdownService, () => new PineMarkdownService());
575
602
  }
576
603
 
577
- const commonServices = {
604
+ const baseServices = {
578
605
  loggerService: inject(TYPES.loggerService),
579
606
  };
607
+ const contextServices = {
608
+ exchangeContextService: inject(TYPES.exchangeContextService),
609
+ };
580
610
  const providerServices = {
581
611
  axisProviderService: inject(TYPES.axisProviderService),
582
612
  candleProviderService: inject(TYPES.candleProviderService),
@@ -597,7 +627,8 @@ const markdownServices = {
597
627
  pineMarkdownService: inject(TYPES.pineMarkdownService),
598
628
  };
599
629
  const pine = {
600
- ...commonServices,
630
+ ...contextServices,
631
+ ...baseServices,
601
632
  ...providerServices,
602
633
  ...jobServices,
603
634
  ...dataServices,
@@ -626,7 +657,26 @@ const GET_SOURCE_FN$1 = async (source) => {
626
657
  }
627
658
  throw new Error("Source must be a File or Code instance");
628
659
  };
629
- async function run(source, { symbol, timeframe, limit }) {
660
+ const BASE_RUNNER_FN = async (script, symbol, timeframe, limit) => await pine.pineJobService.run(script, symbol, timeframe, limit);
661
+ const CREATE_INFERENCE_FN = (symbol, exchangeName, when) => {
662
+ let fn = () => BASE_RUNNER_FN;
663
+ if (exchangeName) {
664
+ fn = ExchangeContextService.runWithContext(fn, { exchangeName });
665
+ }
666
+ if (when) {
667
+ fn = backtestKit.ExecutionContextService.runWithContext(fn, {
668
+ when,
669
+ symbol,
670
+ backtest: true,
671
+ });
672
+ }
673
+ return fn();
674
+ };
675
+ const RUN_INFERENCE_FN = async (script, symbol, timeframe, limit, exchangeName, when) => {
676
+ const inference = CREATE_INFERENCE_FN(symbol, exchangeName, when);
677
+ return await inference(script, symbol, timeframe, limit);
678
+ };
679
+ async function run(source, { symbol, timeframe, limit }, exchangeName, when) {
630
680
  pine.loggerService.info(METHOD_NAME_RUN$2, {
631
681
  source,
632
682
  symbol,
@@ -634,7 +684,7 @@ async function run(source, { symbol, timeframe, limit }) {
634
684
  limit,
635
685
  });
636
686
  const script = await GET_SOURCE_FN$1(source);
637
- const { plots } = await pine.pineJobService.run(script, symbol, timeframe, limit);
687
+ const { plots } = await RUN_INFERENCE_FN(script, symbol, timeframe, limit, exchangeName, when);
638
688
  return plots;
639
689
  }
640
690
 
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';
@@ -77,9 +78,12 @@ class LoggerService {
77
78
 
78
79
  const { provide, inject, init, override } = createActivator("pine");
79
80
 
80
- const baseServices = {
81
+ const baseServices$1 = {
81
82
  loggerService: Symbol("loggerService"),
82
83
  };
84
+ const contextServices$1 = {
85
+ exchangeContextService: Symbol("exchangeContextService"),
86
+ };
83
87
  const providerServices$1 = {
84
88
  axisProviderService: Symbol("axisProviderService"),
85
89
  candleProviderService: Symbol("candleProviderService"),
@@ -100,7 +104,8 @@ const markdownServices$1 = {
100
104
  pineMarkdownService: Symbol("pineMarkdownService"),
101
105
  };
102
106
  const TYPES = {
103
- ...baseServices,
107
+ ...baseServices$1,
108
+ ...contextServices$1,
104
109
  ...providerServices$1,
105
110
  ...jobServices$1,
106
111
  ...dataServices$1,
@@ -227,9 +232,28 @@ class AxisProviderService {
227
232
  }
228
233
  }
229
234
 
235
+ const ExchangeContextService = 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 (ExchangeContextService.hasContext()) {
243
+ return await Exchange.getRawCandles(symbol, interval, self.exchangeContextService.context, limit, sDate, eDate);
244
+ }
245
+ if (!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 (!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 getRawCandles(symbol, interval, limit, sDate, eDate);
252
+ };
230
253
  class CandleProviderService {
231
254
  constructor() {
232
255
  this.loggerService = inject(TYPES.loggerService);
256
+ this.exchangeContextService = inject(TYPES.exchangeContextService);
233
257
  }
234
258
  async getMarketData(tickerId, timeframe, limit, sDate, eDate) {
235
259
  this.loggerService.log("candleProviderService getMarketData", {
@@ -242,7 +266,7 @@ class CandleProviderService {
242
266
  const symbol = tickerId
243
267
  .toUpperCase()
244
268
  .replace(/^BINANCE:|^BYBIT:|^OKX:/, "");
245
- const rawCandles = await getRawCandles(symbol, timeframe, limit, sDate, eDate);
269
+ const rawCandles = await GET_RAW_CANDLES_FN(this, symbol, timeframe, limit, sDate, eDate);
246
270
  const candles = rawCandles.map((c) => ({
247
271
  openTime: c.timestamp,
248
272
  open: c.open,
@@ -551,6 +575,9 @@ class PineMarkdownService {
551
575
  {
552
576
  provide(TYPES.loggerService, () => new LoggerService());
553
577
  }
578
+ {
579
+ provide(TYPES.exchangeContextService, () => new ExchangeContextService());
580
+ }
554
581
  {
555
582
  provide(TYPES.axisProviderService, () => new AxisProviderService());
556
583
  provide(TYPES.candleProviderService, () => new CandleProviderService());
@@ -571,9 +598,12 @@ class PineMarkdownService {
571
598
  provide(TYPES.pineMarkdownService, () => new PineMarkdownService());
572
599
  }
573
600
 
574
- const commonServices = {
601
+ const baseServices = {
575
602
  loggerService: inject(TYPES.loggerService),
576
603
  };
604
+ const contextServices = {
605
+ exchangeContextService: inject(TYPES.exchangeContextService),
606
+ };
577
607
  const providerServices = {
578
608
  axisProviderService: inject(TYPES.axisProviderService),
579
609
  candleProviderService: inject(TYPES.candleProviderService),
@@ -594,7 +624,8 @@ const markdownServices = {
594
624
  pineMarkdownService: inject(TYPES.pineMarkdownService),
595
625
  };
596
626
  const pine = {
597
- ...commonServices,
627
+ ...contextServices,
628
+ ...baseServices,
598
629
  ...providerServices,
599
630
  ...jobServices,
600
631
  ...dataServices,
@@ -623,7 +654,26 @@ const GET_SOURCE_FN$1 = async (source) => {
623
654
  }
624
655
  throw new Error("Source must be a File or Code instance");
625
656
  };
626
- async function run(source, { symbol, timeframe, limit }) {
657
+ const BASE_RUNNER_FN = async (script, symbol, timeframe, limit) => await pine.pineJobService.run(script, symbol, timeframe, limit);
658
+ const CREATE_INFERENCE_FN = (symbol, exchangeName, when) => {
659
+ let fn = () => BASE_RUNNER_FN;
660
+ if (exchangeName) {
661
+ fn = ExchangeContextService.runWithContext(fn, { exchangeName });
662
+ }
663
+ if (when) {
664
+ fn = ExecutionContextService.runWithContext(fn, {
665
+ when,
666
+ symbol,
667
+ backtest: true,
668
+ });
669
+ }
670
+ return fn();
671
+ };
672
+ const RUN_INFERENCE_FN = async (script, symbol, timeframe, limit, exchangeName, when) => {
673
+ const inference = CREATE_INFERENCE_FN(symbol, exchangeName, when);
674
+ return await inference(script, symbol, timeframe, limit);
675
+ };
676
+ async function run(source, { symbol, timeframe, limit }, exchangeName, when) {
627
677
  pine.loggerService.info(METHOD_NAME_RUN$2, {
628
678
  source,
629
679
  symbol,
@@ -631,7 +681,7 @@ async function run(source, { symbol, timeframe, limit }) {
631
681
  limit,
632
682
  });
633
683
  const script = await GET_SOURCE_FN$1(source);
634
- const { plots } = await pine.pineJobService.run(script, symbol, timeframe, limit);
684
+ const { plots } = await RUN_INFERENCE_FN(script, symbol, timeframe, limit, exchangeName, when);
635
685
  return plots;
636
686
  }
637
687
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backtest-kit/pinets",
3
- "version": "3.0.2",
3
+ "version": "3.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",
@@ -72,13 +72,13 @@
72
72
  "worker-testbed": "1.0.12"
73
73
  },
74
74
  "peerDependencies": {
75
- "backtest-kit": "^3.0.5",
75
+ "backtest-kit": "^3.0.9",
76
76
  "pinets": "^0.8.6",
77
77
  "typescript": "^5.0.0"
78
78
  },
79
79
  "dependencies": {
80
80
  "di-kit": "^1.0.18",
81
- "di-scoped": "^1.0.20",
81
+ "di-scoped": "^1.0.21",
82
82
  "functools-kit": "^1.0.95",
83
83
  "get-moment-stamp": "^1.1.1"
84
84
  },
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 IExchangeContext {
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, when?: Date): Promise<PlotModel>;
51
56
 
52
57
  type PlotExtractConfig<T = number> = {
53
58
  plot: string;
@@ -138,7 +143,10 @@ declare class LoggerService implements ILogger {
138
143
  }
139
144
 
140
145
  declare class CandleProviderService implements IProvider {
141
- private readonly loggerService;
146
+ readonly loggerService: LoggerService;
147
+ readonly exchangeContextService: {
148
+ readonly context: IExchangeContext;
149
+ };
142
150
  getMarketData(tickerId: string, timeframe: string, limit?: number, sDate?: number, eDate?: number): Promise<any[]>;
143
151
  getSymbolInfo(tickerId: string): Promise<any>;
144
152
  }
@@ -186,6 +194,9 @@ declare const pine: {
186
194
  axisProviderService: AxisProviderService;
187
195
  candleProviderService: CandleProviderService;
188
196
  loggerService: LoggerService;
197
+ exchangeContextService: {
198
+ readonly context: IExchangeContext;
199
+ };
189
200
  };
190
201
 
191
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 };