@tradejs/node 1.0.2 → 1.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/dist/backtest.mjs CHANGED
@@ -1,17 +1,19 @@
1
1
  import {
2
2
  BUILTIN_CONNECTOR_NAMES,
3
3
  getConnectorCreatorByName
4
- } from "./chunk-E2QNOA5M.mjs";
4
+ } from "./chunk-CIY64D57.mjs";
5
5
  import {
6
6
  buildMlPayload
7
- } from "./chunk-PXJJPAQT.mjs";
7
+ } from "./chunk-RBE4PZER.mjs";
8
8
  import {
9
9
  require_lodash
10
10
  } from "./chunk-GKDBAF3A.mjs";
11
11
  import {
12
12
  getStrategyCreator
13
- } from "./chunk-MHCXPD2B.mjs";
14
- import "./chunk-DE7ADBIR.mjs";
13
+ } from "./chunk-ZY6ULOWK.mjs";
14
+ import {
15
+ getTradejsProjectCwd
16
+ } from "./chunk-P2ZUWONT.mjs";
15
17
  import {
16
18
  __toESM
17
19
  } from "./chunk-6DZX6EAA.mjs";
@@ -231,10 +233,25 @@ var createTestConnector = (connector, context) => {
231
233
 
232
234
  // src/testing.ts
233
235
  var preloadStart = getTimestamp(PRELOAD_DAYS);
234
- var coinKlineCache = /* @__PURE__ */ new Map();
235
- var btcKlineCache = /* @__PURE__ */ new Map();
236
- var btcBinanceKlineCache = /* @__PURE__ */ new Map();
237
- var btcCoinbaseKlineCache = /* @__PURE__ */ new Map();
236
+ var createTestingKlineCacheState = () => ({
237
+ coinKlineCache: /* @__PURE__ */ new Map(),
238
+ btcKlineCache: /* @__PURE__ */ new Map(),
239
+ btcBinanceKlineCache: /* @__PURE__ */ new Map(),
240
+ btcCoinbaseKlineCache: /* @__PURE__ */ new Map()
241
+ });
242
+ var testingKlineCacheStateByProjectRoot = /* @__PURE__ */ new Map();
243
+ var getTestingKlineCacheState = (cwd = getTradejsProjectCwd()) => {
244
+ const projectRoot = getTradejsProjectCwd(cwd);
245
+ let state = testingKlineCacheStateByProjectRoot.get(projectRoot);
246
+ if (!state) {
247
+ state = createTestingKlineCacheState();
248
+ testingKlineCacheStateByProjectRoot.set(projectRoot, state);
249
+ }
250
+ return {
251
+ projectRoot,
252
+ state
253
+ };
254
+ };
238
255
  var getKlineCacheKey = (params) => {
239
256
  const { userName, connectorName, symbol, end, interval, cacheOnly } = params;
240
257
  return [
@@ -247,11 +264,15 @@ var getKlineCacheKey = (params) => {
247
264
  cacheOnly ? 1 : 0
248
265
  ].join(":");
249
266
  };
250
- var resetTestingKlineCache = () => {
251
- coinKlineCache.clear();
252
- btcKlineCache.clear();
253
- btcBinanceKlineCache.clear();
254
- btcCoinbaseKlineCache.clear();
267
+ var resetTestingKlineCache = (cwd) => {
268
+ const normalizedCwd = String(cwd ?? "").trim();
269
+ if (!normalizedCwd) {
270
+ testingKlineCacheStateByProjectRoot.clear();
271
+ return;
272
+ }
273
+ testingKlineCacheStateByProjectRoot.delete(
274
+ getTradejsProjectCwd(normalizedCwd)
275
+ );
255
276
  };
256
277
  var testing = async ({
257
278
  userName,
@@ -269,22 +290,28 @@ var testing = async ({
269
290
  if (!start) {
270
291
  throw new Error("no start");
271
292
  }
272
- const connectorCreator = await getConnectorCreatorByName(connectorName);
293
+ const { projectRoot, state } = getTestingKlineCacheState();
294
+ const connectorCreator = await getConnectorCreatorByName(
295
+ connectorName,
296
+ projectRoot
297
+ );
273
298
  if (!connectorCreator) {
274
299
  throw new Error(`Unknown connector: ${connectorName}`);
275
300
  }
276
301
  const connector = await connectorCreator({
277
302
  userName
278
303
  });
279
- const strategyCreator = await getStrategyCreator(strategyName);
304
+ const strategyCreator = await getStrategyCreator(strategyName, projectRoot);
280
305
  if (!strategyCreator) {
281
306
  throw new Error(`Unknown strategy: ${strategyName}`);
282
307
  }
283
308
  const binanceCreator = await getConnectorCreatorByName(
284
- BUILTIN_CONNECTOR_NAMES.Binance
309
+ BUILTIN_CONNECTOR_NAMES.Binance,
310
+ projectRoot
285
311
  );
286
312
  const coinbaseCreator = await getConnectorCreatorByName(
287
- BUILTIN_CONNECTOR_NAMES.Coinbase
313
+ BUILTIN_CONNECTOR_NAMES.Coinbase,
314
+ projectRoot
288
315
  );
289
316
  if (!binanceCreator || !coinbaseCreator) {
290
317
  logger.warn(
@@ -310,8 +337,8 @@ var testing = async ({
310
337
  interval,
311
338
  cacheOnly
312
339
  });
313
- const cachedCoinData = coinKlineCache.get(coinCacheKey);
314
- const cachedBtcData = btcKlineCache.get(btcCacheKey);
340
+ const cachedCoinData = state.coinKlineCache.get(coinCacheKey);
341
+ const cachedBtcData = state.btcKlineCache.get(btcCacheKey);
315
342
  const btcBinanceCacheKey = getKlineCacheKey({
316
343
  userName,
317
344
  connectorName: binanceCreator ? BUILTIN_CONNECTOR_NAMES.Binance : connectorName,
@@ -328,8 +355,8 @@ var testing = async ({
328
355
  interval,
329
356
  cacheOnly
330
357
  });
331
- const cachedBtcBinanceData = btcBinanceKlineCache.get(btcBinanceCacheKey);
332
- const cachedBtcCoinbaseData = btcCoinbaseKlineCache.get(btcCoinbaseCacheKey);
358
+ const cachedBtcBinanceData = state.btcBinanceKlineCache.get(btcBinanceCacheKey);
359
+ const cachedBtcCoinbaseData = state.btcCoinbaseKlineCache.get(btcCoinbaseCacheKey);
333
360
  const [data, btcData, btcBinanceData, btcCoinbaseData] = await Promise.all([
334
361
  cachedCoinData ? Promise.resolve(cachedCoinData) : connector.kline({
335
362
  symbol,
@@ -383,16 +410,16 @@ var testing = async ({
383
410
  })
384
411
  ]);
385
412
  if (!cachedCoinData) {
386
- coinKlineCache.set(coinCacheKey, data);
413
+ state.coinKlineCache.set(coinCacheKey, data);
387
414
  }
388
415
  if (!cachedBtcData) {
389
- btcKlineCache.set(btcCacheKey, btcData);
416
+ state.btcKlineCache.set(btcCacheKey, btcData);
390
417
  }
391
418
  if (!cachedBtcBinanceData) {
392
- btcBinanceKlineCache.set(btcBinanceCacheKey, btcBinanceData);
419
+ state.btcBinanceKlineCache.set(btcBinanceCacheKey, btcBinanceData);
393
420
  }
394
421
  if (!cachedBtcCoinbaseData) {
395
- btcCoinbaseKlineCache.set(btcCoinbaseCacheKey, btcCoinbaseData);
422
+ state.btcCoinbaseKlineCache.set(btcCoinbaseCacheKey, btcCoinbaseData);
396
423
  }
397
424
  const prevDataRaw = data.filter(
398
425
  (candle) => candle.timestamp >= preloadStart && candle.timestamp < start
@@ -5,8 +5,6 @@ import {
5
5
  // src/pine.ts
6
6
  import fs from "fs";
7
7
  import path from "path";
8
-
9
- // src/pineShared.ts
10
8
  var getPinePlotSeries = (context, plotName) => {
11
9
  const name = String(plotName || "").trim();
12
10
  if (!name) return [];
@@ -29,8 +27,6 @@ var asPineBoolean = (value) => {
29
27
  if (typeof value === "number") return Number.isFinite(value) && value !== 0;
30
28
  return false;
31
29
  };
32
-
33
- // src/pine.ts
34
30
  var loadPinets = () => {
35
31
  const resolvedPath = __require.resolve("pinets");
36
32
  const cjsPath = resolvedPath.includes("pinets.min.browser") ? resolvedPath.replace(/pinets\.min\.browser(\.es)?\.js$/, "pinets.min.cjs") : resolvedPath;
@@ -0,0 +1,271 @@
1
+ import {
2
+ getTradejsProjectCwd,
3
+ importTradejsModule,
4
+ loadTradejsConfig,
5
+ resolvePluginModuleSpecifier
6
+ } from "./chunk-P2ZUWONT.mjs";
7
+
8
+ // src/connectorsRegistry.ts
9
+ import { logger } from "@tradejs/infra/logger";
10
+ var createConnectorRegistryState = () => ({
11
+ connectorCreators: /* @__PURE__ */ new Map(),
12
+ providerToConnectorName: /* @__PURE__ */ new Map(),
13
+ pluginsLoadPromise: null
14
+ });
15
+ var registryStateByProjectRoot = /* @__PURE__ */ new Map();
16
+ var getConnectorRegistryState = (cwd = getTradejsProjectCwd()) => {
17
+ const projectRoot = getTradejsProjectCwd(cwd);
18
+ let state = registryStateByProjectRoot.get(projectRoot);
19
+ if (!state) {
20
+ state = createConnectorRegistryState();
21
+ registryStateByProjectRoot.set(projectRoot, state);
22
+ }
23
+ return {
24
+ projectRoot,
25
+ state
26
+ };
27
+ };
28
+ var BUILTIN_CONNECTOR_NAMES = {
29
+ ByBit: "ByBit",
30
+ Binance: "Binance",
31
+ Coinbase: "Coinbase",
32
+ Test: "Test"
33
+ };
34
+ var normalizeProvider = (value) => String(value ?? "").trim().toLowerCase();
35
+ var toUniqueModules = (modules = []) => [
36
+ ...new Set(modules.map((moduleName) => moduleName.trim()).filter(Boolean))
37
+ ];
38
+ var findConnectorNameInsensitive = (name, connectorCreators) => {
39
+ const normalized = name.trim().toLowerCase();
40
+ if (!normalized) {
41
+ return null;
42
+ }
43
+ for (const existingName of connectorCreators.keys()) {
44
+ if (existingName.toLowerCase() === normalized) {
45
+ return existingName;
46
+ }
47
+ }
48
+ return null;
49
+ };
50
+ var normalizeProviders = (providers, connectorName) => {
51
+ const list = Array.isArray(providers) ? providers.map((item) => normalizeProvider(item)).filter(Boolean) : [];
52
+ const deduped = [...new Set(list)];
53
+ if (deduped.length) {
54
+ return deduped;
55
+ }
56
+ return [normalizeProvider(connectorName)];
57
+ };
58
+ var registerProvider = (provider, connectorName, source, providerToConnectorName) => {
59
+ const existing = providerToConnectorName.get(provider);
60
+ if (existing && existing !== connectorName) {
61
+ logger.warn(
62
+ 'Skip duplicate connector provider "%s" from %s: already mapped to %s',
63
+ provider,
64
+ source,
65
+ existing
66
+ );
67
+ return;
68
+ }
69
+ providerToConnectorName.set(provider, connectorName);
70
+ };
71
+ var registerEntry = (entry, source, state) => {
72
+ const connectorName = String(entry?.name ?? "").trim();
73
+ if (!connectorName) {
74
+ logger.warn("Skip connector entry without name from %s", source);
75
+ return;
76
+ }
77
+ if (typeof entry.creator !== "function") {
78
+ logger.warn(
79
+ 'Skip connector entry "%s" from %s: creator must be a function',
80
+ connectorName,
81
+ source
82
+ );
83
+ return;
84
+ }
85
+ const existingByName = findConnectorNameInsensitive(
86
+ connectorName,
87
+ state.connectorCreators
88
+ );
89
+ if (existingByName) {
90
+ logger.warn(
91
+ 'Skip duplicate connector "%s" from %s: already registered as %s',
92
+ connectorName,
93
+ source,
94
+ existingByName
95
+ );
96
+ return;
97
+ }
98
+ state.connectorCreators.set(connectorName, entry.creator);
99
+ const providers = normalizeProviders(entry.providers, connectorName);
100
+ for (const provider of providers) {
101
+ registerProvider(
102
+ provider,
103
+ connectorName,
104
+ source,
105
+ state.providerToConnectorName
106
+ );
107
+ }
108
+ };
109
+ var registerEntries = (entries, source, state) => {
110
+ for (const entry of entries) {
111
+ registerEntry(entry, source, state);
112
+ }
113
+ };
114
+ var extractConnectorPluginDefinition = (moduleExport) => {
115
+ if (!moduleExport || typeof moduleExport !== "object") {
116
+ return null;
117
+ }
118
+ const candidate = moduleExport;
119
+ if (Array.isArray(candidate.connectorEntries)) {
120
+ return {
121
+ connectorEntries: candidate.connectorEntries
122
+ };
123
+ }
124
+ const defaultExport = candidate.default;
125
+ if (defaultExport && Array.isArray(defaultExport.connectorEntries)) {
126
+ return {
127
+ connectorEntries: defaultExport.connectorEntries
128
+ };
129
+ }
130
+ return null;
131
+ };
132
+ var importConnectorPluginModule = async (moduleName, cwd = getTradejsProjectCwd()) => {
133
+ if (typeof importTradejsModule === "function") {
134
+ return importTradejsModule(moduleName, cwd);
135
+ }
136
+ return import(
137
+ /* webpackIgnore: true */
138
+ moduleName
139
+ );
140
+ };
141
+ var ensureConnectorPluginsLoaded = async (cwd = getTradejsProjectCwd()) => {
142
+ const { projectRoot, state } = getConnectorRegistryState(cwd);
143
+ if (!state.pluginsLoadPromise) {
144
+ state.pluginsLoadPromise = (async () => {
145
+ const config = await loadTradejsConfig(projectRoot);
146
+ const connectorModules = toUniqueModules(config.connectors);
147
+ if (!connectorModules.length) {
148
+ return;
149
+ }
150
+ for (const moduleName of connectorModules) {
151
+ try {
152
+ const resolvedModuleName = resolvePluginModuleSpecifier(
153
+ moduleName,
154
+ projectRoot
155
+ );
156
+ const moduleExport = await importConnectorPluginModule(
157
+ resolvedModuleName,
158
+ projectRoot
159
+ );
160
+ const pluginDefinition = extractConnectorPluginDefinition(moduleExport);
161
+ if (!pluginDefinition) {
162
+ logger.warn(
163
+ 'Skip connector plugin "%s": export { connectorEntries } is missing',
164
+ moduleName
165
+ );
166
+ continue;
167
+ }
168
+ registerEntries(pluginDefinition.connectorEntries, moduleName, state);
169
+ } catch (error) {
170
+ logger.warn(
171
+ 'Failed to load connector plugin "%s": %s',
172
+ moduleName,
173
+ String(error)
174
+ );
175
+ }
176
+ }
177
+ })();
178
+ }
179
+ await state.pluginsLoadPromise;
180
+ };
181
+ var getConnectorCreatorByName = async (connectorName, cwd = getTradejsProjectCwd()) => {
182
+ await ensureConnectorPluginsLoaded(cwd);
183
+ const { state } = getConnectorRegistryState(cwd);
184
+ const raw = String(connectorName ?? "").trim();
185
+ if (!raw) {
186
+ return void 0;
187
+ }
188
+ const direct = state.connectorCreators.get(raw);
189
+ if (direct) {
190
+ return direct;
191
+ }
192
+ const existing = findConnectorNameInsensitive(raw, state.connectorCreators);
193
+ if (!existing) {
194
+ return void 0;
195
+ }
196
+ return state.connectorCreators.get(existing);
197
+ };
198
+ var getConnectorNameByProvider = async (provider, cwd = getTradejsProjectCwd()) => {
199
+ await ensureConnectorPluginsLoaded(cwd);
200
+ const { state } = getConnectorRegistryState(cwd);
201
+ const normalized = normalizeProvider(provider);
202
+ if (!normalized) {
203
+ return void 0;
204
+ }
205
+ return state.providerToConnectorName.get(normalized);
206
+ };
207
+ var getConnectorCreatorByProvider = async (provider, cwd = getTradejsProjectCwd()) => {
208
+ await ensureConnectorPluginsLoaded(cwd);
209
+ const { state } = getConnectorRegistryState(cwd);
210
+ const normalized = normalizeProvider(provider);
211
+ if (!normalized) {
212
+ return void 0;
213
+ }
214
+ const connectorName = state.providerToConnectorName.get(normalized);
215
+ if (!connectorName) {
216
+ return void 0;
217
+ }
218
+ return state.connectorCreators.get(connectorName);
219
+ };
220
+ var resolveConnectorName = async (providerOrName, cwd = getTradejsProjectCwd()) => {
221
+ await ensureConnectorPluginsLoaded(cwd);
222
+ const { state } = getConnectorRegistryState(cwd);
223
+ const raw = String(providerOrName ?? "").trim();
224
+ if (!raw) {
225
+ return void 0;
226
+ }
227
+ const byProvider = state.providerToConnectorName.get(normalizeProvider(raw));
228
+ if (byProvider) {
229
+ return byProvider;
230
+ }
231
+ return state.connectorCreators.get(raw) && raw ? raw : findConnectorNameInsensitive(raw, state.connectorCreators) ?? void 0;
232
+ };
233
+ var getAvailableConnectorNames = async (cwd = getTradejsProjectCwd()) => {
234
+ await ensureConnectorPluginsLoaded(cwd);
235
+ const { state } = getConnectorRegistryState(cwd);
236
+ return [...state.connectorCreators.keys()].sort((a, b) => a.localeCompare(b));
237
+ };
238
+ var getAvailableConnectorProviders = async (cwd = getTradejsProjectCwd()) => {
239
+ await ensureConnectorPluginsLoaded(cwd);
240
+ const { state } = getConnectorRegistryState(cwd);
241
+ return [...state.providerToConnectorName.keys()].sort(
242
+ (a, b) => a.localeCompare(b)
243
+ );
244
+ };
245
+ var registerConnectorEntries = (entries, cwd = getTradejsProjectCwd()) => {
246
+ const { state } = getConnectorRegistryState(cwd);
247
+ registerEntries(entries, "runtime", state);
248
+ };
249
+ var resetConnectorRegistryCache = (cwd) => {
250
+ const normalizedCwd = String(cwd ?? "").trim();
251
+ if (!normalizedCwd) {
252
+ registryStateByProjectRoot.clear();
253
+ return;
254
+ }
255
+ registryStateByProjectRoot.delete(getTradejsProjectCwd(normalizedCwd));
256
+ };
257
+ var DEFAULT_CONNECTOR_NAME = BUILTIN_CONNECTOR_NAMES.ByBit;
258
+
259
+ export {
260
+ BUILTIN_CONNECTOR_NAMES,
261
+ ensureConnectorPluginsLoaded,
262
+ getConnectorCreatorByName,
263
+ getConnectorNameByProvider,
264
+ getConnectorCreatorByProvider,
265
+ resolveConnectorName,
266
+ getAvailableConnectorNames,
267
+ getAvailableConnectorProviders,
268
+ registerConnectorEntries,
269
+ resetConnectorRegistryCache,
270
+ DEFAULT_CONNECTOR_NAME
271
+ };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getStrategyManifest
3
- } from "./chunk-MHCXPD2B.mjs";
3
+ } from "./chunk-ZY6ULOWK.mjs";
4
4
 
5
5
  // src/ai.ts
6
6
  import { setData, redisKeys } from "@tradejs/infra/redis";
@@ -15,6 +15,7 @@ var TS_MODULE_RE = /\.(cts|mts|ts)$/i;
15
15
  var cachedByCwd = /* @__PURE__ */ new Map();
16
16
  var announcedConfigFile = /* @__PURE__ */ new Set();
17
17
  var tsNodeRegistered = false;
18
+ var tsconfigPathsRegisteredByCwd = /* @__PURE__ */ new Set();
18
19
  var getTradejsProjectCwd = (cwd) => {
19
20
  const explicit = String(cwd ?? "").trim();
20
21
  if (explicit) {
@@ -56,6 +57,28 @@ var ensureTsNodeRegistered = async () => {
56
57
  });
57
58
  tsNodeRegistered = true;
58
59
  };
60
+ var ensureTsconfigPathsRegistered = async (cwd = getTradejsProjectCwd()) => {
61
+ const projectRoot = getTradejsProjectCwd(cwd);
62
+ if (tsconfigPathsRegisteredByCwd.has(projectRoot)) {
63
+ return;
64
+ }
65
+ const tsconfigPathsModule = await import("tsconfig-paths");
66
+ const loadConfig = tsconfigPathsModule.loadConfig;
67
+ const register = tsconfigPathsModule.register;
68
+ if (typeof loadConfig !== "function" || typeof register !== "function") {
69
+ return;
70
+ }
71
+ const loadedConfig = loadConfig(projectRoot);
72
+ if (loadedConfig.resultType !== "success") {
73
+ return;
74
+ }
75
+ register({
76
+ baseUrl: loadedConfig.absoluteBaseUrl,
77
+ paths: loadedConfig.paths,
78
+ addMatchAll: false
79
+ });
80
+ tsconfigPathsRegisteredByCwd.add(projectRoot);
81
+ };
59
82
  var toImportSpecifier = (moduleName) => {
60
83
  if (moduleName.startsWith("file://")) {
61
84
  return moduleName;
@@ -83,6 +106,7 @@ var importConfigFile = async (configFilePath) => {
83
106
  if (ext === ".ts" || ext === ".mts") {
84
107
  const requireFn = getRequireFn(path.dirname(configFilePath));
85
108
  await ensureTsNodeRegistered();
109
+ await ensureTsconfigPathsRegistered(path.dirname(configFilePath));
86
110
  return requireFn(configFilePath);
87
111
  }
88
112
  return import(
@@ -90,7 +114,7 @@ var importConfigFile = async (configFilePath) => {
90
114
  configFileUrl
91
115
  );
92
116
  };
93
- var importTradejsModule = async (moduleName) => {
117
+ var importTradejsModule = async (moduleName, cwd = getTradejsProjectCwd()) => {
94
118
  const normalized = String(moduleName ?? "").trim();
95
119
  if (!normalized) {
96
120
  return {};
@@ -104,13 +128,15 @@ var importTradejsModule = async (moduleName) => {
104
128
  }
105
129
  }
106
130
  const requireFn = getRequireFn(
107
- path.isAbsolute(modulePath) ? path.dirname(modulePath) : getTradejsProjectCwd()
131
+ path.isAbsolute(modulePath) ? path.dirname(modulePath) : cwd
108
132
  );
109
133
  if (isTsModulePath(modulePath)) {
110
134
  await ensureTsNodeRegistered();
135
+ await ensureTsconfigPathsRegistered(cwd);
111
136
  return requireFn(modulePath);
112
137
  }
113
138
  if (isBareModuleSpecifier(normalized)) {
139
+ await ensureTsconfigPathsRegistered(cwd);
114
140
  return requireFn(normalized);
115
141
  }
116
142
  try {
@@ -121,6 +147,7 @@ var importTradejsModule = async (moduleName) => {
121
147
  } catch (error) {
122
148
  if (isTsModulePath(modulePath)) {
123
149
  await ensureTsNodeRegistered();
150
+ await ensureTsconfigPathsRegistered(cwd);
124
151
  return requireFn(modulePath);
125
152
  }
126
153
  throw error;
@@ -198,6 +225,7 @@ var loadTradejsConfig = async (cwd = getTradejsProjectCwd()) => {
198
225
  };
199
226
 
200
227
  export {
228
+ getTradejsProjectCwd,
201
229
  importTradejsModule,
202
230
  resolvePluginModuleSpecifier,
203
231
  loadTradejsConfig
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getStrategyManifest
3
- } from "./chunk-MHCXPD2B.mjs";
3
+ } from "./chunk-ZY6ULOWK.mjs";
4
4
 
5
5
  // src/strategyAdapters/ml.ts
6
6
  var defaultMlAdapter = {