@tradejs/node 1.0.2 → 1.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/dist/{ai-NNJ3RLLL.mjs → ai-MDMDKAEE.mjs} +3 -3
- package/dist/backtest.d.mts +1 -1
- package/dist/backtest.d.ts +1 -1
- package/dist/backtest.js +214 -131
- package/dist/backtest.mjs +52 -25
- package/dist/{chunk-DE7ADBIR.mjs → chunk-3C76HVLA.mjs} +1 -0
- package/dist/{chunk-OB4CSYDJ.mjs → chunk-7ICOZAKA.mjs} +0 -4
- package/dist/chunk-CK2PW4L5.mjs +243 -0
- package/dist/{chunk-PXJJPAQT.mjs → chunk-H4HXW3EZ.mjs} +1 -1
- package/dist/chunk-MGFEID6K.mjs +268 -0
- package/dist/{chunk-ZIMX3JX2.mjs → chunk-QDYCJ2OK.mjs} +1 -1
- package/dist/cli.js +131 -99
- package/dist/cli.mjs +17 -15
- package/dist/connectors.d.mts +9 -9
- package/dist/connectors.d.ts +9 -9
- package/dist/connectors.js +110 -70
- package/dist/connectors.mjs +2 -2
- package/dist/pine.d.mts +0 -1
- package/dist/pine.d.ts +0 -1
- package/dist/pine.js +0 -4
- package/dist/pine.mjs +1 -1
- package/dist/registry.d.mts +10 -10
- package/dist/registry.d.ts +10 -10
- package/dist/registry.js +122 -81
- package/dist/registry.mjs +2 -2
- package/dist/strategies.js +132 -86
- package/dist/strategies.mjs +15 -10
- package/package.json +4 -4
- package/dist/chunk-E2QNOA5M.mjs +0 -227
- package/dist/chunk-MHCXPD2B.mjs +0 -201
package/dist/backtest.mjs
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
import {
|
|
2
2
|
BUILTIN_CONNECTOR_NAMES,
|
|
3
3
|
getConnectorCreatorByName
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-MGFEID6K.mjs";
|
|
5
5
|
import {
|
|
6
6
|
buildMlPayload
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-H4HXW3EZ.mjs";
|
|
8
8
|
import {
|
|
9
9
|
require_lodash
|
|
10
10
|
} from "./chunk-GKDBAF3A.mjs";
|
|
11
11
|
import {
|
|
12
12
|
getStrategyCreator
|
|
13
|
-
} from "./chunk-
|
|
14
|
-
import
|
|
13
|
+
} from "./chunk-CK2PW4L5.mjs";
|
|
14
|
+
import {
|
|
15
|
+
getTradejsProjectCwd
|
|
16
|
+
} from "./chunk-3C76HVLA.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
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
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
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
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
|
|
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,243 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getTradejsProjectCwd,
|
|
3
|
+
importTradejsModule,
|
|
4
|
+
loadTradejsConfig,
|
|
5
|
+
resolvePluginModuleSpecifier
|
|
6
|
+
} from "./chunk-3C76HVLA.mjs";
|
|
7
|
+
|
|
8
|
+
// src/strategy/manifests.ts
|
|
9
|
+
import {
|
|
10
|
+
registerIndicatorEntries,
|
|
11
|
+
resetIndicatorRegistryCache
|
|
12
|
+
} from "@tradejs/core/indicators";
|
|
13
|
+
import { logger } from "@tradejs/infra/logger";
|
|
14
|
+
var createStrategyRegistryState = () => ({
|
|
15
|
+
strategyCreators: /* @__PURE__ */ new Map(),
|
|
16
|
+
strategyManifestsMap: /* @__PURE__ */ new Map(),
|
|
17
|
+
pluginsLoadPromise: null
|
|
18
|
+
});
|
|
19
|
+
var registryStateByProjectRoot = /* @__PURE__ */ new Map();
|
|
20
|
+
var getStrategyRegistryState = (cwd = getTradejsProjectCwd()) => {
|
|
21
|
+
const projectRoot = getTradejsProjectCwd(cwd);
|
|
22
|
+
let state = registryStateByProjectRoot.get(projectRoot);
|
|
23
|
+
if (!state) {
|
|
24
|
+
state = createStrategyRegistryState();
|
|
25
|
+
registryStateByProjectRoot.set(projectRoot, state);
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
projectRoot,
|
|
29
|
+
state
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
var toUniqueModules = (modules = []) => [
|
|
33
|
+
...new Set(modules.map((moduleName) => moduleName.trim()).filter(Boolean))
|
|
34
|
+
];
|
|
35
|
+
var getConfiguredPluginModuleNames = async (cwd = getTradejsProjectCwd()) => {
|
|
36
|
+
const config = await loadTradejsConfig(cwd);
|
|
37
|
+
return {
|
|
38
|
+
strategyModules: toUniqueModules(config.strategies),
|
|
39
|
+
indicatorModules: toUniqueModules(config.indicators)
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
var extractModuleEntries = (moduleExport, key) => {
|
|
43
|
+
if (!moduleExport || typeof moduleExport !== "object") {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
const candidate = moduleExport;
|
|
47
|
+
if (Array.isArray(candidate[key])) {
|
|
48
|
+
return candidate[key];
|
|
49
|
+
}
|
|
50
|
+
const defaultExport = candidate.default;
|
|
51
|
+
if (defaultExport && Array.isArray(defaultExport[key])) {
|
|
52
|
+
return defaultExport[key];
|
|
53
|
+
}
|
|
54
|
+
return null;
|
|
55
|
+
};
|
|
56
|
+
var extractStrategyPluginDefinition = (moduleExport) => {
|
|
57
|
+
const strategyEntries = extractModuleEntries(
|
|
58
|
+
moduleExport,
|
|
59
|
+
"strategyEntries"
|
|
60
|
+
);
|
|
61
|
+
return strategyEntries ? { strategyEntries } : null;
|
|
62
|
+
};
|
|
63
|
+
var extractIndicatorPluginDefinition = (moduleExport) => {
|
|
64
|
+
const indicatorEntries = extractModuleEntries(
|
|
65
|
+
moduleExport,
|
|
66
|
+
"indicatorEntries"
|
|
67
|
+
);
|
|
68
|
+
return indicatorEntries ? { indicatorEntries } : null;
|
|
69
|
+
};
|
|
70
|
+
var registerEntries = (entries, source, state) => {
|
|
71
|
+
for (const entry of entries) {
|
|
72
|
+
const strategyName = entry.manifest?.name;
|
|
73
|
+
if (!strategyName) {
|
|
74
|
+
logger.warn("Skip strategy entry without name from %s", source);
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
if (state.strategyCreators.has(strategyName)) {
|
|
78
|
+
logger.warn(
|
|
79
|
+
'Skip duplicate strategy "%s" from %s: already registered',
|
|
80
|
+
strategyName,
|
|
81
|
+
source
|
|
82
|
+
);
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
state.strategyCreators.set(strategyName, entry.creator);
|
|
86
|
+
state.strategyManifestsMap.set(strategyName, entry.manifest);
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
var importStrategyPluginModule = async (moduleName) => {
|
|
90
|
+
if (typeof importTradejsModule === "function") {
|
|
91
|
+
return importTradejsModule(moduleName);
|
|
92
|
+
}
|
|
93
|
+
return import(
|
|
94
|
+
/* webpackIgnore: true */
|
|
95
|
+
moduleName
|
|
96
|
+
);
|
|
97
|
+
};
|
|
98
|
+
var ensureStrategyPluginsLoaded = async (cwd = getTradejsProjectCwd()) => {
|
|
99
|
+
const { projectRoot, state } = getStrategyRegistryState(cwd);
|
|
100
|
+
if (!state.pluginsLoadPromise) {
|
|
101
|
+
resetIndicatorRegistryCache(projectRoot);
|
|
102
|
+
state.pluginsLoadPromise = (async () => {
|
|
103
|
+
const { strategyModules, indicatorModules } = await getConfiguredPluginModuleNames(projectRoot);
|
|
104
|
+
const strategySet = new Set(strategyModules);
|
|
105
|
+
const indicatorSet = new Set(indicatorModules);
|
|
106
|
+
const pluginModuleNames = [
|
|
107
|
+
.../* @__PURE__ */ new Set([...strategyModules, ...indicatorModules])
|
|
108
|
+
];
|
|
109
|
+
if (!pluginModuleNames.length) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
for (const moduleName of pluginModuleNames) {
|
|
113
|
+
try {
|
|
114
|
+
const resolvedModuleName = resolvePluginModuleSpecifier(
|
|
115
|
+
moduleName,
|
|
116
|
+
projectRoot
|
|
117
|
+
);
|
|
118
|
+
const moduleExport = await importStrategyPluginModule(resolvedModuleName);
|
|
119
|
+
if (strategySet.has(moduleName)) {
|
|
120
|
+
const pluginDefinition = extractStrategyPluginDefinition(moduleExport);
|
|
121
|
+
if (!pluginDefinition) {
|
|
122
|
+
logger.warn(
|
|
123
|
+
'Skip strategy plugin "%s": export { strategyEntries } is missing',
|
|
124
|
+
moduleName
|
|
125
|
+
);
|
|
126
|
+
} else {
|
|
127
|
+
registerEntries(
|
|
128
|
+
pluginDefinition.strategyEntries,
|
|
129
|
+
moduleName,
|
|
130
|
+
state
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
if (indicatorSet.has(moduleName)) {
|
|
135
|
+
const indicatorPluginDefinition = extractIndicatorPluginDefinition(moduleExport);
|
|
136
|
+
if (!indicatorPluginDefinition) {
|
|
137
|
+
logger.warn(
|
|
138
|
+
'Skip indicator plugin "%s": export { indicatorEntries } is missing',
|
|
139
|
+
moduleName
|
|
140
|
+
);
|
|
141
|
+
} else {
|
|
142
|
+
registerIndicatorEntries(
|
|
143
|
+
indicatorPluginDefinition.indicatorEntries,
|
|
144
|
+
moduleName,
|
|
145
|
+
projectRoot
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
if (!strategySet.has(moduleName) && !indicatorSet.has(moduleName)) {
|
|
150
|
+
logger.warn(
|
|
151
|
+
'Skip plugin "%s": no strategy/indicator sections requested in config',
|
|
152
|
+
moduleName
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
} catch (error) {
|
|
156
|
+
logger.warn(
|
|
157
|
+
'Failed to load plugin "%s": %s',
|
|
158
|
+
moduleName,
|
|
159
|
+
String(error)
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
})();
|
|
164
|
+
}
|
|
165
|
+
await state.pluginsLoadPromise;
|
|
166
|
+
};
|
|
167
|
+
var ensureIndicatorPluginsLoaded = async (cwd = getTradejsProjectCwd()) => ensureStrategyPluginsLoaded(cwd);
|
|
168
|
+
var getStrategyCreator = async (name, cwd = getTradejsProjectCwd()) => {
|
|
169
|
+
await ensureStrategyPluginsLoaded(cwd);
|
|
170
|
+
const { state } = getStrategyRegistryState(cwd);
|
|
171
|
+
return state.strategyCreators.get(name);
|
|
172
|
+
};
|
|
173
|
+
var getAvailableStrategyNames = async (cwd = getTradejsProjectCwd()) => {
|
|
174
|
+
await ensureStrategyPluginsLoaded(cwd);
|
|
175
|
+
const { state } = getStrategyRegistryState(cwd);
|
|
176
|
+
return [...state.strategyCreators.keys()].sort((a, b) => a.localeCompare(b));
|
|
177
|
+
};
|
|
178
|
+
var getRegisteredStrategies = (cwd = getTradejsProjectCwd()) => {
|
|
179
|
+
const { state } = getStrategyRegistryState(cwd);
|
|
180
|
+
return Object.fromEntries(state.strategyCreators.entries());
|
|
181
|
+
};
|
|
182
|
+
var getRegisteredManifests = (cwd = getTradejsProjectCwd()) => {
|
|
183
|
+
const { state } = getStrategyRegistryState(cwd);
|
|
184
|
+
return [...state.strategyManifestsMap.values()];
|
|
185
|
+
};
|
|
186
|
+
var getStrategyManifest = (name, cwd = getTradejsProjectCwd()) => {
|
|
187
|
+
if (!name) {
|
|
188
|
+
return void 0;
|
|
189
|
+
}
|
|
190
|
+
const { state } = getStrategyRegistryState(cwd);
|
|
191
|
+
return state.strategyManifestsMap.get(name);
|
|
192
|
+
};
|
|
193
|
+
var isKnownStrategy = (name, cwd = getTradejsProjectCwd()) => {
|
|
194
|
+
const { state } = getStrategyRegistryState(cwd);
|
|
195
|
+
return state.strategyCreators.has(name);
|
|
196
|
+
};
|
|
197
|
+
var registerStrategyEntries = (entries, cwd = getTradejsProjectCwd()) => {
|
|
198
|
+
const { state } = getStrategyRegistryState(cwd);
|
|
199
|
+
registerEntries(entries, "runtime", state);
|
|
200
|
+
};
|
|
201
|
+
var resetStrategyRegistryCache = (cwd) => {
|
|
202
|
+
const normalizedCwd = String(cwd ?? "").trim();
|
|
203
|
+
if (!normalizedCwd) {
|
|
204
|
+
registryStateByProjectRoot.clear();
|
|
205
|
+
resetIndicatorRegistryCache();
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
const projectRoot = getTradejsProjectCwd(normalizedCwd);
|
|
209
|
+
registryStateByProjectRoot.delete(projectRoot);
|
|
210
|
+
resetIndicatorRegistryCache(projectRoot);
|
|
211
|
+
};
|
|
212
|
+
var strategies = new Proxy(
|
|
213
|
+
{},
|
|
214
|
+
{
|
|
215
|
+
get: (_target, property) => {
|
|
216
|
+
if (typeof property !== "string") {
|
|
217
|
+
return void 0;
|
|
218
|
+
}
|
|
219
|
+
return getStrategyRegistryState().state.strategyCreators.get(property);
|
|
220
|
+
},
|
|
221
|
+
ownKeys: () => {
|
|
222
|
+
return [...getStrategyRegistryState().state.strategyCreators.keys()];
|
|
223
|
+
},
|
|
224
|
+
getOwnPropertyDescriptor: () => ({
|
|
225
|
+
enumerable: true,
|
|
226
|
+
configurable: true
|
|
227
|
+
})
|
|
228
|
+
}
|
|
229
|
+
);
|
|
230
|
+
|
|
231
|
+
export {
|
|
232
|
+
ensureStrategyPluginsLoaded,
|
|
233
|
+
ensureIndicatorPluginsLoaded,
|
|
234
|
+
getStrategyCreator,
|
|
235
|
+
getAvailableStrategyNames,
|
|
236
|
+
getRegisteredStrategies,
|
|
237
|
+
getRegisteredManifests,
|
|
238
|
+
getStrategyManifest,
|
|
239
|
+
isKnownStrategy,
|
|
240
|
+
registerStrategyEntries,
|
|
241
|
+
resetStrategyRegistryCache,
|
|
242
|
+
strategies
|
|
243
|
+
};
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getTradejsProjectCwd,
|
|
3
|
+
importTradejsModule,
|
|
4
|
+
loadTradejsConfig,
|
|
5
|
+
resolvePluginModuleSpecifier
|
|
6
|
+
} from "./chunk-3C76HVLA.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) => {
|
|
133
|
+
if (typeof importTradejsModule === "function") {
|
|
134
|
+
return importTradejsModule(moduleName);
|
|
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(resolvedModuleName);
|
|
157
|
+
const pluginDefinition = extractConnectorPluginDefinition(moduleExport);
|
|
158
|
+
if (!pluginDefinition) {
|
|
159
|
+
logger.warn(
|
|
160
|
+
'Skip connector plugin "%s": export { connectorEntries } is missing',
|
|
161
|
+
moduleName
|
|
162
|
+
);
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
registerEntries(pluginDefinition.connectorEntries, moduleName, state);
|
|
166
|
+
} catch (error) {
|
|
167
|
+
logger.warn(
|
|
168
|
+
'Failed to load connector plugin "%s": %s',
|
|
169
|
+
moduleName,
|
|
170
|
+
String(error)
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
})();
|
|
175
|
+
}
|
|
176
|
+
await state.pluginsLoadPromise;
|
|
177
|
+
};
|
|
178
|
+
var getConnectorCreatorByName = async (connectorName, cwd = getTradejsProjectCwd()) => {
|
|
179
|
+
await ensureConnectorPluginsLoaded(cwd);
|
|
180
|
+
const { state } = getConnectorRegistryState(cwd);
|
|
181
|
+
const raw = String(connectorName ?? "").trim();
|
|
182
|
+
if (!raw) {
|
|
183
|
+
return void 0;
|
|
184
|
+
}
|
|
185
|
+
const direct = state.connectorCreators.get(raw);
|
|
186
|
+
if (direct) {
|
|
187
|
+
return direct;
|
|
188
|
+
}
|
|
189
|
+
const existing = findConnectorNameInsensitive(raw, state.connectorCreators);
|
|
190
|
+
if (!existing) {
|
|
191
|
+
return void 0;
|
|
192
|
+
}
|
|
193
|
+
return state.connectorCreators.get(existing);
|
|
194
|
+
};
|
|
195
|
+
var getConnectorNameByProvider = async (provider, cwd = getTradejsProjectCwd()) => {
|
|
196
|
+
await ensureConnectorPluginsLoaded(cwd);
|
|
197
|
+
const { state } = getConnectorRegistryState(cwd);
|
|
198
|
+
const normalized = normalizeProvider(provider);
|
|
199
|
+
if (!normalized) {
|
|
200
|
+
return void 0;
|
|
201
|
+
}
|
|
202
|
+
return state.providerToConnectorName.get(normalized);
|
|
203
|
+
};
|
|
204
|
+
var getConnectorCreatorByProvider = async (provider, cwd = getTradejsProjectCwd()) => {
|
|
205
|
+
await ensureConnectorPluginsLoaded(cwd);
|
|
206
|
+
const { state } = getConnectorRegistryState(cwd);
|
|
207
|
+
const normalized = normalizeProvider(provider);
|
|
208
|
+
if (!normalized) {
|
|
209
|
+
return void 0;
|
|
210
|
+
}
|
|
211
|
+
const connectorName = state.providerToConnectorName.get(normalized);
|
|
212
|
+
if (!connectorName) {
|
|
213
|
+
return void 0;
|
|
214
|
+
}
|
|
215
|
+
return state.connectorCreators.get(connectorName);
|
|
216
|
+
};
|
|
217
|
+
var resolveConnectorName = async (providerOrName, cwd = getTradejsProjectCwd()) => {
|
|
218
|
+
await ensureConnectorPluginsLoaded(cwd);
|
|
219
|
+
const { state } = getConnectorRegistryState(cwd);
|
|
220
|
+
const raw = String(providerOrName ?? "").trim();
|
|
221
|
+
if (!raw) {
|
|
222
|
+
return void 0;
|
|
223
|
+
}
|
|
224
|
+
const byProvider = state.providerToConnectorName.get(normalizeProvider(raw));
|
|
225
|
+
if (byProvider) {
|
|
226
|
+
return byProvider;
|
|
227
|
+
}
|
|
228
|
+
return state.connectorCreators.get(raw) && raw ? raw : findConnectorNameInsensitive(raw, state.connectorCreators) ?? void 0;
|
|
229
|
+
};
|
|
230
|
+
var getAvailableConnectorNames = async (cwd = getTradejsProjectCwd()) => {
|
|
231
|
+
await ensureConnectorPluginsLoaded(cwd);
|
|
232
|
+
const { state } = getConnectorRegistryState(cwd);
|
|
233
|
+
return [...state.connectorCreators.keys()].sort((a, b) => a.localeCompare(b));
|
|
234
|
+
};
|
|
235
|
+
var getAvailableConnectorProviders = async (cwd = getTradejsProjectCwd()) => {
|
|
236
|
+
await ensureConnectorPluginsLoaded(cwd);
|
|
237
|
+
const { state } = getConnectorRegistryState(cwd);
|
|
238
|
+
return [...state.providerToConnectorName.keys()].sort(
|
|
239
|
+
(a, b) => a.localeCompare(b)
|
|
240
|
+
);
|
|
241
|
+
};
|
|
242
|
+
var registerConnectorEntries = (entries, cwd = getTradejsProjectCwd()) => {
|
|
243
|
+
const { state } = getConnectorRegistryState(cwd);
|
|
244
|
+
registerEntries(entries, "runtime", state);
|
|
245
|
+
};
|
|
246
|
+
var resetConnectorRegistryCache = (cwd) => {
|
|
247
|
+
const normalizedCwd = String(cwd ?? "").trim();
|
|
248
|
+
if (!normalizedCwd) {
|
|
249
|
+
registryStateByProjectRoot.clear();
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
registryStateByProjectRoot.delete(getTradejsProjectCwd(normalizedCwd));
|
|
253
|
+
};
|
|
254
|
+
var DEFAULT_CONNECTOR_NAME = BUILTIN_CONNECTOR_NAMES.ByBit;
|
|
255
|
+
|
|
256
|
+
export {
|
|
257
|
+
BUILTIN_CONNECTOR_NAMES,
|
|
258
|
+
ensureConnectorPluginsLoaded,
|
|
259
|
+
getConnectorCreatorByName,
|
|
260
|
+
getConnectorNameByProvider,
|
|
261
|
+
getConnectorCreatorByProvider,
|
|
262
|
+
resolveConnectorName,
|
|
263
|
+
getAvailableConnectorNames,
|
|
264
|
+
getAvailableConnectorProviders,
|
|
265
|
+
registerConnectorEntries,
|
|
266
|
+
resetConnectorRegistryCache,
|
|
267
|
+
DEFAULT_CONNECTOR_NAME
|
|
268
|
+
};
|