@tradejs/node 3.0.1 → 3.1.1

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.
Files changed (43) hide show
  1. package/README.md +4 -0
  2. package/dist/ai.d.mts +26 -4
  3. package/dist/ai.d.ts +26 -4
  4. package/dist/ai.js +162 -165
  5. package/dist/ai.mjs +4 -1
  6. package/dist/backtest.js +515 -188
  7. package/dist/backtest.mjs +6 -4
  8. package/dist/chunk-3TWULKHV.mjs +595 -0
  9. package/dist/chunk-FB5NUEOQ.mjs +295 -0
  10. package/dist/chunk-LAJ7NA3Q.mjs +377 -0
  11. package/dist/{chunk-VRXU4E4O.mjs → chunk-SEQJ6V6B.mjs} +165 -446
  12. package/dist/{chunk-OGAWBO3Z.mjs → chunk-W26Y6IRP.mjs} +22 -3
  13. package/dist/chunk-XN7BC7XK.mjs +295 -0
  14. package/dist/cli.js +152 -161
  15. package/dist/cli.mjs +4 -2
  16. package/dist/connectors.d.mts +5 -2
  17. package/dist/connectors.d.ts +5 -2
  18. package/dist/connectors.js +329 -8
  19. package/dist/connectors.mjs +5 -1
  20. package/dist/registry-DHTLjQcr.d.mts +17 -0
  21. package/dist/registry-DHTLjQcr.d.ts +17 -0
  22. package/dist/registry.d.mts +2 -15
  23. package/dist/registry.d.ts +2 -15
  24. package/dist/registry.js +176 -161
  25. package/dist/registry.mjs +5 -2
  26. package/dist/runtimeDashboard.d.mts +12 -0
  27. package/dist/runtimeDashboard.d.ts +12 -0
  28. package/dist/runtimeDashboard.js +8502 -0
  29. package/dist/runtimeDashboard.mjs +1000 -0
  30. package/dist/runtimeStrategies.d.mts +38 -0
  31. package/dist/runtimeStrategies.d.ts +38 -0
  32. package/dist/runtimeStrategies.js +798 -0
  33. package/dist/runtimeStrategies.mjs +264 -0
  34. package/dist/runtimeTrades.d.mts +31 -0
  35. package/dist/runtimeTrades.d.ts +31 -0
  36. package/dist/runtimeTrades.js +321 -0
  37. package/dist/runtimeTrades.mjs +11 -0
  38. package/dist/strategies.d.mts +2 -2
  39. package/dist/strategies.d.ts +2 -2
  40. package/dist/strategies.js +194 -165
  41. package/dist/strategies.mjs +24 -379
  42. package/package.json +27 -6
  43. package/dist/chunk-V3YMKE4I.mjs +0 -271
package/dist/cli.js CHANGED
@@ -78,11 +78,8 @@ var import_logger5 = require("@tradejs/infra/logger");
78
78
  var import_candles = require("@tradejs/infra/timescale/candles");
79
79
 
80
80
  // src/ai.ts
81
- var import_aiLanguages = require("@tradejs/core/aiLanguages");
82
- var import_aiEndpoints = require("@tradejs/core/aiEndpoints");
83
- var import_aiModels = require("@tradejs/core/aiModels");
81
+ var import_aiLanguages2 = require("@tradejs/core/aiLanguages");
84
82
  var import_redis = require("@tradejs/infra/redis");
85
- var import_userSettings = require("@tradejs/infra/userSettings");
86
83
 
87
84
  // src/aiShared.ts
88
85
  var MAX_AI_SERIES_POINTS = 5;
@@ -955,6 +952,7 @@ var createStrategyRegistryState = () => ({
955
952
  strategyCreators: /* @__PURE__ */ new Map(),
956
953
  strategyManifestsMap: /* @__PURE__ */ new Map(),
957
954
  strategyEntriesMap: /* @__PURE__ */ new Map(),
955
+ strategySourcesMap: /* @__PURE__ */ new Map(),
958
956
  pluginsLoadPromise: null
959
957
  });
960
958
  var registryStateByProjectRoot = sharedStrategyRegistry.registryStateByProjectRoot;
@@ -1025,6 +1023,7 @@ var registerEntries = (entries, source, state) => {
1025
1023
  }
1026
1024
  state.strategyManifestsMap.set(strategyName, entry.manifest);
1027
1025
  state.strategyEntriesMap.set(strategyName, entry);
1026
+ state.strategySourcesMap.set(strategyName, source);
1028
1027
  materializeStrategyCreator(strategyName, state);
1029
1028
  }
1030
1029
  };
@@ -1207,6 +1206,139 @@ var postProcessAiAnalysisByStrategy = (signal, analysis, payload = buildAiPayloa
1207
1206
  analysis
1208
1207
  }) ?? analysis;
1209
1208
 
1209
+ // src/aiProvider.ts
1210
+ var import_aiEndpoints = require("@tradejs/core/aiEndpoints");
1211
+ var import_aiModels = require("@tradejs/core/aiModels");
1212
+ var import_aiLanguages = require("@tradejs/core/aiLanguages");
1213
+ var import_userSettings = require("@tradejs/infra/userSettings");
1214
+ var DEFAULT_AI_MODEL = "openai/gpt-5-mini";
1215
+ var userSettingsCache = /* @__PURE__ */ new Map();
1216
+ var aiModelCache = /* @__PURE__ */ new Map();
1217
+ var normalizeResponseContent = (content) => {
1218
+ if (typeof content === "string") return content;
1219
+ if (content && typeof content === "object" && !Array.isArray(content)) {
1220
+ return content;
1221
+ }
1222
+ if (Array.isArray(content)) {
1223
+ return content.map(
1224
+ (part) => typeof part?.text === "string" ? part.text : ""
1225
+ ).join("\n").trim();
1226
+ }
1227
+ return String(content ?? "");
1228
+ };
1229
+ var getAiInvocationError = (error) => {
1230
+ const details = error instanceof Error && error.message.trim() ? error.message.trim() : String(error);
1231
+ const isEmptyCompletion = error instanceof TypeError && /Cannot read properties of undefined \(reading ['"]message['"]\)/.test(
1232
+ details
1233
+ );
1234
+ const wrapped = new Error(
1235
+ isEmptyCompletion ? "AI provider returned an empty chat completion" : `AI model invocation failed: ${details}`
1236
+ );
1237
+ wrapped.cause = error;
1238
+ return wrapped;
1239
+ };
1240
+ var isEmptyResponseContent = (content) => typeof content === "string" ? content.trim().length === 0 : Object.keys(content).length === 0;
1241
+ var getAiModelCacheKey = (userName, modelName, temperature) => `${userName}::${modelName}::${temperature}`;
1242
+ var resolveAiModelName = (settings, requestedModelName) => {
1243
+ const explicitModelName = requestedModelName?.trim() ?? "";
1244
+ if (explicitModelName) return explicitModelName;
1245
+ return settings.AI_MODEL?.trim() || DEFAULT_AI_MODEL;
1246
+ };
1247
+ var getOpenRouterModelKwargs = (apiEndpoint) => {
1248
+ const endpoint = String(apiEndpoint ?? "").trim();
1249
+ if (!endpoint) return {};
1250
+ let hostname = "";
1251
+ try {
1252
+ hostname = new URL(endpoint).hostname;
1253
+ } catch {
1254
+ hostname = endpoint;
1255
+ }
1256
+ return hostname.toLowerCase().includes("openrouter") ? { provider: { ignore: ["azure"] } } : {};
1257
+ };
1258
+ var getAiUserSettings = async (userName = "root") => {
1259
+ let settingsPromise = userSettingsCache.get(userName);
1260
+ if (!settingsPromise) {
1261
+ settingsPromise = (0, import_userSettings.getUserSettings)(userName).then((settings2) => {
1262
+ const endpoint = (0, import_aiEndpoints.normalizeAiEndpoint)(settings2.AI_API_ENDPOINT);
1263
+ return {
1264
+ ...settings2,
1265
+ AI_API_ENDPOINT: endpoint,
1266
+ AI_MODEL: (0, import_aiModels.normalizeAiModel)(settings2.AI_MODEL, endpoint),
1267
+ AI_RESPONSE_LANGUAGE: (0, import_aiLanguages.normalizeAiResponseLanguage)(
1268
+ settings2.AI_RESPONSE_LANGUAGE
1269
+ )
1270
+ };
1271
+ });
1272
+ settingsPromise.catch(() => userSettingsCache.delete(userName));
1273
+ userSettingsCache.set(userName, settingsPromise);
1274
+ }
1275
+ const settings = await settingsPromise;
1276
+ if (!settings.AI_API_KEY || !settings.AI_API_ENDPOINT) {
1277
+ throw new Error(`AI settings are incomplete for user ${userName}`);
1278
+ }
1279
+ return settings;
1280
+ };
1281
+ var getAiModel = async (userName = "root", requestedModelName, temperature = 0.2) => {
1282
+ const settings = await getAiUserSettings(userName);
1283
+ const modelName = resolveAiModelName(settings, requestedModelName);
1284
+ const cacheKey = getAiModelCacheKey(userName, modelName, temperature);
1285
+ let modelPromise = aiModelCache.get(cacheKey);
1286
+ if (!modelPromise) {
1287
+ modelPromise = import("@langchain/openai").then(({ ChatOpenAI }) => {
1288
+ const modelKwargs = getOpenRouterModelKwargs(settings.AI_API_ENDPOINT);
1289
+ return new ChatOpenAI({
1290
+ temperature,
1291
+ modelName,
1292
+ apiKey: settings.AI_API_KEY,
1293
+ ...Object.keys(modelKwargs).length ? { modelKwargs } : {},
1294
+ configuration: {
1295
+ baseURL: settings.AI_API_ENDPOINT,
1296
+ defaultHeaders: {
1297
+ "HTTP-Referer": "https://tradejs.dev",
1298
+ "X-Title": "Inv"
1299
+ }
1300
+ }
1301
+ });
1302
+ });
1303
+ modelPromise.catch(() => aiModelCache.delete(cacheKey));
1304
+ aiModelCache.set(cacheKey, modelPromise);
1305
+ }
1306
+ try {
1307
+ return await modelPromise;
1308
+ } catch (error) {
1309
+ aiModelCache.delete(cacheKey);
1310
+ userSettingsCache.delete(userName);
1311
+ throw error;
1312
+ }
1313
+ };
1314
+ var invokeAiChatWithUserMessageEncoding = async ({
1315
+ messages,
1316
+ userName = "root",
1317
+ model,
1318
+ temperature = 0.2
1319
+ }, resolveUserMessageEncoding) => {
1320
+ const [{ HumanMessage, SystemMessage }, aiModel] = await Promise.all([
1321
+ import("@langchain/core/messages"),
1322
+ getAiModel(userName, model, temperature)
1323
+ ]);
1324
+ const providerMessages = messages.map(
1325
+ (message) => message.role === "system" ? new SystemMessage(message.content) : new HumanMessage(
1326
+ resolveUserMessageEncoding(message) === "text-block" ? { content: [{ type: "text", text: message.content }] } : message.content
1327
+ )
1328
+ );
1329
+ try {
1330
+ const response = await aiModel.invoke(providerMessages);
1331
+ const content = normalizeResponseContent(response?.content);
1332
+ if (isEmptyResponseContent(content)) {
1333
+ throw new Error("AI provider returned an empty chat completion");
1334
+ }
1335
+ return { content };
1336
+ } catch (error) {
1337
+ throw getAiInvocationError(error);
1338
+ }
1339
+ };
1340
+ var invokeAiPromptChat = (options) => invokeAiChatWithUserMessageEncoding(options, () => "text-block");
1341
+
1210
1342
  // src/ai.ts
1211
1343
  var parseAIResponse = (input) => {
1212
1344
  try {
@@ -1220,18 +1352,6 @@ var parseAIResponse = (input) => {
1220
1352
  return {};
1221
1353
  }
1222
1354
  };
1223
- var normalizeResponseContent = (content) => {
1224
- if (typeof content === "string" || content && typeof content === "object") {
1225
- if (typeof content !== "object" || !Array.isArray(content)) {
1226
- return content;
1227
- }
1228
- }
1229
- if (Array.isArray(content)) {
1230
- const text = content.map((part) => typeof part?.text === "string" ? part.text : "").join("\n").trim();
1231
- return text;
1232
- }
1233
- return String(content ?? "");
1234
- };
1235
1355
  var normalizeAnalysis = (raw) => {
1236
1356
  const direction = raw?.direction === "LONG" || raw?.direction === "SHORT" ? raw.direction : null;
1237
1357
  const qualityNum = typeof raw?.quality === "number" ? Math.max(1, Math.min(5, Math.round(raw.quality))) : void 0;
@@ -1437,153 +1557,24 @@ Trade payload:
1437
1557
  ${JSON.stringify(payload)}
1438
1558
  ${buildAiHumanPromptAddonByStrategy(signal, payload)}
1439
1559
  `;
1440
- var getAiInvocationError = (error) => {
1441
- const details = error instanceof Error && error.message.trim() ? error.message.trim() : String(error);
1442
- const isEmptyCompletion = error instanceof TypeError && /Cannot read properties of undefined \(reading ['"]message['"]\)/.test(
1443
- details
1444
- );
1445
- const wrapped = new Error(
1446
- isEmptyCompletion ? "AI provider returned an empty chat completion" : `AI model invocation failed: ${details}`
1447
- );
1448
- wrapped.cause = error;
1449
- return wrapped;
1450
- };
1451
- var isEmptyResponseContent = (content) => typeof content === "string" ? content.trim().length === 0 : Object.keys(content).length === 0;
1452
- var DEFAULT_AI_MODEL = "openai/gpt-5-mini";
1453
- var userSettingsCache = /* @__PURE__ */ new Map();
1454
- var aiModelCache = /* @__PURE__ */ new Map();
1455
- var getAiModelCacheKey = (userName, modelName) => `${userName}::${modelName}`;
1456
- var resolveAiModelName = (settings, requestedModelName) => {
1457
- const explicitModelName = typeof requestedModelName === "string" ? requestedModelName.trim() : "";
1458
- if (explicitModelName) {
1459
- return explicitModelName;
1460
- }
1461
- const settingsModelName = typeof settings.AI_MODEL === "string" ? settings.AI_MODEL.trim() : "";
1462
- return settingsModelName || DEFAULT_AI_MODEL;
1463
- };
1464
- var getOpenRouterModelKwargs = (apiEndpoint) => {
1465
- const endpoint = String(apiEndpoint ?? "").trim();
1466
- if (!endpoint) {
1467
- return {};
1468
- }
1469
- let hostname = "";
1470
- try {
1471
- hostname = new URL(endpoint).hostname;
1472
- } catch {
1473
- hostname = endpoint;
1474
- }
1475
- if (!hostname.toLowerCase().includes("openrouter")) {
1476
- return {};
1477
- }
1478
- return {
1479
- provider: {
1480
- ignore: ["azure"]
1481
- }
1482
- };
1483
- };
1484
- var getAiSettings = async (userName = "root") => {
1485
- let settingsPromise = userSettingsCache.get(userName);
1486
- if (!settingsPromise) {
1487
- settingsPromise = (0, import_userSettings.getUserSettings)(userName).then((settings2) => {
1488
- const endpoint = (0, import_aiEndpoints.normalizeAiEndpoint)(settings2.AI_API_ENDPOINT);
1489
- return {
1490
- ...settings2,
1491
- AI_API_ENDPOINT: endpoint,
1492
- AI_MODEL: (0, import_aiModels.normalizeAiModel)(settings2.AI_MODEL, endpoint),
1493
- AI_RESPONSE_LANGUAGE: (0, import_aiLanguages.normalizeAiResponseLanguage)(
1494
- settings2.AI_RESPONSE_LANGUAGE
1495
- )
1496
- };
1497
- });
1498
- settingsPromise.catch(() => {
1499
- userSettingsCache.delete(userName);
1500
- });
1501
- userSettingsCache.set(userName, settingsPromise);
1502
- }
1503
- const settings = await settingsPromise;
1504
- if (!settings.AI_API_KEY || !settings.AI_API_ENDPOINT) {
1505
- throw new Error(`AI settings are incomplete for user ${userName}`);
1506
- }
1507
- return settings;
1508
- };
1509
- var createAiModel = async (userName = "root", requestedModelName) => {
1510
- const settings = await getAiSettings(userName);
1511
- const modelName = resolveAiModelName(settings, requestedModelName);
1512
- const cacheKey = getAiModelCacheKey(userName, modelName);
1513
- let modelPromise = aiModelCache.get(cacheKey);
1514
- if (!modelPromise) {
1515
- modelPromise = (async () => {
1516
- const { ChatOpenAI } = await import("@langchain/openai");
1517
- const modelKwargs = getOpenRouterModelKwargs(settings.AI_API_ENDPOINT);
1518
- return new ChatOpenAI({
1519
- temperature: 0.2,
1520
- modelName,
1521
- apiKey: settings.AI_API_KEY,
1522
- ...Object.keys(modelKwargs).length ? { modelKwargs } : {},
1523
- configuration: {
1524
- baseURL: settings.AI_API_ENDPOINT,
1525
- defaultHeaders: {
1526
- "HTTP-Referer": "https://tradejs.dev",
1527
- "X-Title": "Inv"
1528
- }
1529
- }
1530
- });
1531
- })();
1532
- modelPromise.catch(() => {
1533
- aiModelCache.delete(cacheKey);
1534
- });
1535
- aiModelCache.set(cacheKey, modelPromise);
1536
- }
1537
- return modelPromise;
1538
- };
1539
- var getAiModel = async (userName = "root", requestedModelName) => {
1540
- const settings = await getAiSettings(userName);
1541
- const resolvedModelName = resolveAiModelName(settings, requestedModelName);
1542
- try {
1543
- return await createAiModel(userName, resolvedModelName);
1544
- } catch (error) {
1545
- aiModelCache.delete(getAiModelCacheKey(userName, resolvedModelName));
1546
- userSettingsCache.delete(userName);
1547
- throw error;
1548
- }
1549
- };
1550
1560
  var runAiPrompt = async ({ systemPrompt, humanPrompt }, options = {}) => {
1551
- const [{ HumanMessage, SystemMessage }, model, settings] = await Promise.all([
1552
- import("@langchain/core/messages"),
1553
- getAiModel(options.userName, options.model),
1554
- getAiSettings(options.userName)
1555
- ]);
1556
- const messages = [];
1557
- const responseLanguage = (0, import_aiLanguages.getAiResponseLanguagePromptName)(
1558
- settings.AI_RESPONSE_LANGUAGE || import_aiLanguages.DEFAULT_AI_RESPONSE_LANGUAGE
1561
+ const settings = await getAiUserSettings(options.userName);
1562
+ const responseLanguage = (0, import_aiLanguages2.getAiResponseLanguagePromptName)(
1563
+ settings.AI_RESPONSE_LANGUAGE || import_aiLanguages2.DEFAULT_AI_RESPONSE_LANGUAGE
1559
1564
  );
1560
- messages.push(new SystemMessage(systemPrompt));
1561
- messages.push(
1562
- new SystemMessage(
1563
- `Write all user-visible text fields in ${responseLanguage}. Keep field names and JSON syntax unchanged.`
1564
- )
1565
- );
1566
- messages.push(
1567
- new HumanMessage({
1568
- content: [
1569
- {
1570
- type: "text",
1571
- text: humanPrompt
1572
- }
1573
- ]
1574
- })
1575
- );
1576
- let response;
1577
- try {
1578
- response = await model.invoke(messages);
1579
- } catch (error) {
1580
- throw getAiInvocationError(error);
1581
- }
1582
- const responseContent = normalizeResponseContent(response?.content);
1583
- if (isEmptyResponseContent(responseContent)) {
1584
- throw new Error("AI provider returned an empty chat completion");
1585
- }
1586
- const parsed = parseAIResponse(responseContent);
1565
+ const response = await invokeAiPromptChat({
1566
+ userName: options.userName,
1567
+ model: options.model,
1568
+ messages: [
1569
+ { role: "system", content: systemPrompt },
1570
+ {
1571
+ role: "system",
1572
+ content: `Write all user-visible text fields in ${responseLanguage}. Keep field names and JSON syntax unchanged.`
1573
+ },
1574
+ { role: "user", content: humanPrompt }
1575
+ ]
1576
+ });
1577
+ const parsed = parseAIResponse(response.content);
1587
1578
  const normalized = normalizeAnalysis(parsed);
1588
1579
  if (!options.signal) {
1589
1580
  return normalized;
package/dist/cli.mjs CHANGED
@@ -4,9 +4,11 @@ import {
4
4
  SCREENSHOT_CONCURRENCY_LIMIT
5
5
  } from "./chunk-KMJQQ53K.mjs";
6
6
  import {
7
- askAI,
7
+ askAI
8
+ } from "./chunk-SEQJ6V6B.mjs";
9
+ import {
8
10
  ensureStrategyPluginsLoaded
9
- } from "./chunk-VRXU4E4O.mjs";
11
+ } from "./chunk-XN7BC7XK.mjs";
10
12
  import {
11
13
  getTradejsProjectCwd,
12
14
  loadTradejsConfig
@@ -1,4 +1,4 @@
1
- import { ConnectorCreator, ConnectorRegistryEntry } from '@tradejs/types';
1
+ import { ConnectorCreator, ConnectorRegistryEntry, ConnectorRuntime } from '@tradejs/types';
2
2
 
3
3
  declare const BUILTIN_CONNECTOR_NAMES: {
4
4
  readonly ByBit: "ByBit";
@@ -17,4 +17,7 @@ declare const registerConnectorEntries: (entries: readonly ConnectorRegistryEntr
17
17
  declare const resetConnectorRegistryCache: (cwd?: string) => void;
18
18
  declare const DEFAULT_CONNECTOR_NAME: "ByBit";
19
19
 
20
- export { BUILTIN_CONNECTOR_NAMES, DEFAULT_CONNECTOR_NAME, ensureConnectorPluginsLoaded, getAvailableConnectorNames, getAvailableConnectorProviders, getConnectorCreatorByName, getConnectorCreatorByProvider, getConnectorNameByProvider, registerConnectorEntries, resetConnectorRegistryCache, resolveConnectorName };
20
+ declare const connectorRuntime: ConnectorRuntime;
21
+ declare const bindConnectorRuntime: (creator: ConnectorCreator, runtime?: ConnectorRuntime) => ConnectorCreator;
22
+
23
+ export { BUILTIN_CONNECTOR_NAMES, DEFAULT_CONNECTOR_NAME, bindConnectorRuntime, connectorRuntime, ensureConnectorPluginsLoaded, getAvailableConnectorNames, getAvailableConnectorProviders, getConnectorCreatorByName, getConnectorCreatorByProvider, getConnectorNameByProvider, registerConnectorEntries, resetConnectorRegistryCache, resolveConnectorName };
@@ -1,4 +1,4 @@
1
- import { ConnectorCreator, ConnectorRegistryEntry } from '@tradejs/types';
1
+ import { ConnectorCreator, ConnectorRegistryEntry, ConnectorRuntime } from '@tradejs/types';
2
2
 
3
3
  declare const BUILTIN_CONNECTOR_NAMES: {
4
4
  readonly ByBit: "ByBit";
@@ -17,4 +17,7 @@ declare const registerConnectorEntries: (entries: readonly ConnectorRegistryEntr
17
17
  declare const resetConnectorRegistryCache: (cwd?: string) => void;
18
18
  declare const DEFAULT_CONNECTOR_NAME: "ByBit";
19
19
 
20
- export { BUILTIN_CONNECTOR_NAMES, DEFAULT_CONNECTOR_NAME, ensureConnectorPluginsLoaded, getAvailableConnectorNames, getAvailableConnectorProviders, getConnectorCreatorByName, getConnectorCreatorByProvider, getConnectorNameByProvider, registerConnectorEntries, resetConnectorRegistryCache, resolveConnectorName };
20
+ declare const connectorRuntime: ConnectorRuntime;
21
+ declare const bindConnectorRuntime: (creator: ConnectorCreator, runtime?: ConnectorRuntime) => ConnectorCreator;
22
+
23
+ export { BUILTIN_CONNECTOR_NAMES, DEFAULT_CONNECTOR_NAME, bindConnectorRuntime, connectorRuntime, ensureConnectorPluginsLoaded, getAvailableConnectorNames, getAvailableConnectorProviders, getConnectorCreatorByName, getConnectorCreatorByProvider, getConnectorNameByProvider, registerConnectorEntries, resetConnectorRegistryCache, resolveConnectorName };