@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.
- package/README.md +4 -0
- package/dist/ai.d.mts +26 -4
- package/dist/ai.d.ts +26 -4
- package/dist/ai.js +162 -165
- package/dist/ai.mjs +4 -1
- package/dist/backtest.js +515 -188
- package/dist/backtest.mjs +6 -4
- package/dist/chunk-3TWULKHV.mjs +595 -0
- package/dist/chunk-FB5NUEOQ.mjs +295 -0
- package/dist/chunk-LAJ7NA3Q.mjs +377 -0
- package/dist/{chunk-VRXU4E4O.mjs → chunk-SEQJ6V6B.mjs} +165 -446
- package/dist/{chunk-OGAWBO3Z.mjs → chunk-W26Y6IRP.mjs} +22 -3
- package/dist/chunk-XN7BC7XK.mjs +295 -0
- package/dist/cli.js +152 -161
- package/dist/cli.mjs +4 -2
- package/dist/connectors.d.mts +5 -2
- package/dist/connectors.d.ts +5 -2
- package/dist/connectors.js +329 -8
- package/dist/connectors.mjs +5 -1
- package/dist/registry-DHTLjQcr.d.mts +17 -0
- package/dist/registry-DHTLjQcr.d.ts +17 -0
- package/dist/registry.d.mts +2 -15
- package/dist/registry.d.ts +2 -15
- package/dist/registry.js +176 -161
- package/dist/registry.mjs +5 -2
- package/dist/runtimeDashboard.d.mts +12 -0
- package/dist/runtimeDashboard.d.ts +12 -0
- package/dist/runtimeDashboard.js +8502 -0
- package/dist/runtimeDashboard.mjs +1000 -0
- package/dist/runtimeStrategies.d.mts +38 -0
- package/dist/runtimeStrategies.d.ts +38 -0
- package/dist/runtimeStrategies.js +798 -0
- package/dist/runtimeStrategies.mjs +264 -0
- package/dist/runtimeTrades.d.mts +31 -0
- package/dist/runtimeTrades.d.ts +31 -0
- package/dist/runtimeTrades.js +321 -0
- package/dist/runtimeTrades.mjs +11 -0
- package/dist/strategies.d.mts +2 -2
- package/dist/strategies.d.ts +2 -2
- package/dist/strategies.js +194 -165
- package/dist/strategies.mjs +24 -379
- package/package.json +27 -6
- package/dist/chunk-V3YMKE4I.mjs +0 -271
package/dist/backtest.js
CHANGED
|
@@ -44,10 +44,10 @@ __reExport(backtest_exports, require("@tradejs/core/backtest"), module.exports);
|
|
|
44
44
|
|
|
45
45
|
// src/testing.ts
|
|
46
46
|
var import_indicators3 = require("@tradejs/core/indicators");
|
|
47
|
-
var
|
|
47
|
+
var import_constants9 = require("@tradejs/core/constants");
|
|
48
48
|
var import_strategies7 = require("@tradejs/core/strategies");
|
|
49
|
-
var
|
|
50
|
-
var
|
|
49
|
+
var import_time3 = require("@tradejs/core/time");
|
|
50
|
+
var import_logger15 = require("@tradejs/infra/logger");
|
|
51
51
|
|
|
52
52
|
// src/strategyRuntime.ts
|
|
53
53
|
var import_constants5 = require("@tradejs/core/constants");
|
|
@@ -60,11 +60,8 @@ var import_constants3 = require("@tradejs/core/constants");
|
|
|
60
60
|
var import_ml2 = require("@tradejs/infra/ml");
|
|
61
61
|
|
|
62
62
|
// src/ai.ts
|
|
63
|
-
var
|
|
64
|
-
var import_aiEndpoints = require("@tradejs/core/aiEndpoints");
|
|
65
|
-
var import_aiModels = require("@tradejs/core/aiModels");
|
|
63
|
+
var import_aiLanguages2 = require("@tradejs/core/aiLanguages");
|
|
66
64
|
var import_redis = require("@tradejs/infra/redis");
|
|
67
|
-
var import_userSettings = require("@tradejs/infra/userSettings");
|
|
68
65
|
|
|
69
66
|
// src/aiShared.ts
|
|
70
67
|
var MAX_AI_SERIES_POINTS = 5;
|
|
@@ -937,6 +934,7 @@ var createStrategyRegistryState = () => ({
|
|
|
937
934
|
strategyCreators: /* @__PURE__ */ new Map(),
|
|
938
935
|
strategyManifestsMap: /* @__PURE__ */ new Map(),
|
|
939
936
|
strategyEntriesMap: /* @__PURE__ */ new Map(),
|
|
937
|
+
strategySourcesMap: /* @__PURE__ */ new Map(),
|
|
940
938
|
pluginsLoadPromise: null
|
|
941
939
|
});
|
|
942
940
|
var registryStateByProjectRoot = sharedStrategyRegistry.registryStateByProjectRoot;
|
|
@@ -1007,6 +1005,7 @@ var registerEntries = (entries, source, state) => {
|
|
|
1007
1005
|
}
|
|
1008
1006
|
state.strategyManifestsMap.set(strategyName, entry.manifest);
|
|
1009
1007
|
state.strategyEntriesMap.set(strategyName, entry);
|
|
1008
|
+
state.strategySourcesMap.set(strategyName, source);
|
|
1010
1009
|
materializeStrategyCreator(strategyName, state);
|
|
1011
1010
|
}
|
|
1012
1011
|
};
|
|
@@ -1273,6 +1272,139 @@ var postProcessLocalAiAnalysisByStrategy = (signal, analysis, payload = buildAiP
|
|
|
1273
1272
|
}) ?? strategyAnalysis;
|
|
1274
1273
|
};
|
|
1275
1274
|
|
|
1275
|
+
// src/aiProvider.ts
|
|
1276
|
+
var import_aiEndpoints = require("@tradejs/core/aiEndpoints");
|
|
1277
|
+
var import_aiModels = require("@tradejs/core/aiModels");
|
|
1278
|
+
var import_aiLanguages = require("@tradejs/core/aiLanguages");
|
|
1279
|
+
var import_userSettings = require("@tradejs/infra/userSettings");
|
|
1280
|
+
var DEFAULT_AI_MODEL = "openai/gpt-5-mini";
|
|
1281
|
+
var userSettingsCache = /* @__PURE__ */ new Map();
|
|
1282
|
+
var aiModelCache = /* @__PURE__ */ new Map();
|
|
1283
|
+
var normalizeResponseContent = (content) => {
|
|
1284
|
+
if (typeof content === "string") return content;
|
|
1285
|
+
if (content && typeof content === "object" && !Array.isArray(content)) {
|
|
1286
|
+
return content;
|
|
1287
|
+
}
|
|
1288
|
+
if (Array.isArray(content)) {
|
|
1289
|
+
return content.map(
|
|
1290
|
+
(part) => typeof part?.text === "string" ? part.text : ""
|
|
1291
|
+
).join("\n").trim();
|
|
1292
|
+
}
|
|
1293
|
+
return String(content ?? "");
|
|
1294
|
+
};
|
|
1295
|
+
var getAiInvocationError = (error) => {
|
|
1296
|
+
const details = error instanceof Error && error.message.trim() ? error.message.trim() : String(error);
|
|
1297
|
+
const isEmptyCompletion = error instanceof TypeError && /Cannot read properties of undefined \(reading ['"]message['"]\)/.test(
|
|
1298
|
+
details
|
|
1299
|
+
);
|
|
1300
|
+
const wrapped = new Error(
|
|
1301
|
+
isEmptyCompletion ? "AI provider returned an empty chat completion" : `AI model invocation failed: ${details}`
|
|
1302
|
+
);
|
|
1303
|
+
wrapped.cause = error;
|
|
1304
|
+
return wrapped;
|
|
1305
|
+
};
|
|
1306
|
+
var isEmptyResponseContent = (content) => typeof content === "string" ? content.trim().length === 0 : Object.keys(content).length === 0;
|
|
1307
|
+
var getAiModelCacheKey = (userName, modelName, temperature) => `${userName}::${modelName}::${temperature}`;
|
|
1308
|
+
var resolveAiModelName = (settings, requestedModelName) => {
|
|
1309
|
+
const explicitModelName = requestedModelName?.trim() ?? "";
|
|
1310
|
+
if (explicitModelName) return explicitModelName;
|
|
1311
|
+
return settings.AI_MODEL?.trim() || DEFAULT_AI_MODEL;
|
|
1312
|
+
};
|
|
1313
|
+
var getOpenRouterModelKwargs = (apiEndpoint) => {
|
|
1314
|
+
const endpoint = String(apiEndpoint ?? "").trim();
|
|
1315
|
+
if (!endpoint) return {};
|
|
1316
|
+
let hostname = "";
|
|
1317
|
+
try {
|
|
1318
|
+
hostname = new URL(endpoint).hostname;
|
|
1319
|
+
} catch {
|
|
1320
|
+
hostname = endpoint;
|
|
1321
|
+
}
|
|
1322
|
+
return hostname.toLowerCase().includes("openrouter") ? { provider: { ignore: ["azure"] } } : {};
|
|
1323
|
+
};
|
|
1324
|
+
var getAiUserSettings = async (userName = "root") => {
|
|
1325
|
+
let settingsPromise = userSettingsCache.get(userName);
|
|
1326
|
+
if (!settingsPromise) {
|
|
1327
|
+
settingsPromise = (0, import_userSettings.getUserSettings)(userName).then((settings2) => {
|
|
1328
|
+
const endpoint = (0, import_aiEndpoints.normalizeAiEndpoint)(settings2.AI_API_ENDPOINT);
|
|
1329
|
+
return {
|
|
1330
|
+
...settings2,
|
|
1331
|
+
AI_API_ENDPOINT: endpoint,
|
|
1332
|
+
AI_MODEL: (0, import_aiModels.normalizeAiModel)(settings2.AI_MODEL, endpoint),
|
|
1333
|
+
AI_RESPONSE_LANGUAGE: (0, import_aiLanguages.normalizeAiResponseLanguage)(
|
|
1334
|
+
settings2.AI_RESPONSE_LANGUAGE
|
|
1335
|
+
)
|
|
1336
|
+
};
|
|
1337
|
+
});
|
|
1338
|
+
settingsPromise.catch(() => userSettingsCache.delete(userName));
|
|
1339
|
+
userSettingsCache.set(userName, settingsPromise);
|
|
1340
|
+
}
|
|
1341
|
+
const settings = await settingsPromise;
|
|
1342
|
+
if (!settings.AI_API_KEY || !settings.AI_API_ENDPOINT) {
|
|
1343
|
+
throw new Error(`AI settings are incomplete for user ${userName}`);
|
|
1344
|
+
}
|
|
1345
|
+
return settings;
|
|
1346
|
+
};
|
|
1347
|
+
var getAiModel = async (userName = "root", requestedModelName, temperature = 0.2) => {
|
|
1348
|
+
const settings = await getAiUserSettings(userName);
|
|
1349
|
+
const modelName = resolveAiModelName(settings, requestedModelName);
|
|
1350
|
+
const cacheKey = getAiModelCacheKey(userName, modelName, temperature);
|
|
1351
|
+
let modelPromise = aiModelCache.get(cacheKey);
|
|
1352
|
+
if (!modelPromise) {
|
|
1353
|
+
modelPromise = import("@langchain/openai").then(({ ChatOpenAI }) => {
|
|
1354
|
+
const modelKwargs = getOpenRouterModelKwargs(settings.AI_API_ENDPOINT);
|
|
1355
|
+
return new ChatOpenAI({
|
|
1356
|
+
temperature,
|
|
1357
|
+
modelName,
|
|
1358
|
+
apiKey: settings.AI_API_KEY,
|
|
1359
|
+
...Object.keys(modelKwargs).length ? { modelKwargs } : {},
|
|
1360
|
+
configuration: {
|
|
1361
|
+
baseURL: settings.AI_API_ENDPOINT,
|
|
1362
|
+
defaultHeaders: {
|
|
1363
|
+
"HTTP-Referer": "https://tradejs.dev",
|
|
1364
|
+
"X-Title": "Inv"
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
});
|
|
1368
|
+
});
|
|
1369
|
+
modelPromise.catch(() => aiModelCache.delete(cacheKey));
|
|
1370
|
+
aiModelCache.set(cacheKey, modelPromise);
|
|
1371
|
+
}
|
|
1372
|
+
try {
|
|
1373
|
+
return await modelPromise;
|
|
1374
|
+
} catch (error) {
|
|
1375
|
+
aiModelCache.delete(cacheKey);
|
|
1376
|
+
userSettingsCache.delete(userName);
|
|
1377
|
+
throw error;
|
|
1378
|
+
}
|
|
1379
|
+
};
|
|
1380
|
+
var invokeAiChatWithUserMessageEncoding = async ({
|
|
1381
|
+
messages,
|
|
1382
|
+
userName = "root",
|
|
1383
|
+
model,
|
|
1384
|
+
temperature = 0.2
|
|
1385
|
+
}, resolveUserMessageEncoding) => {
|
|
1386
|
+
const [{ HumanMessage, SystemMessage }, aiModel] = await Promise.all([
|
|
1387
|
+
import("@langchain/core/messages"),
|
|
1388
|
+
getAiModel(userName, model, temperature)
|
|
1389
|
+
]);
|
|
1390
|
+
const providerMessages = messages.map(
|
|
1391
|
+
(message) => message.role === "system" ? new SystemMessage(message.content) : new HumanMessage(
|
|
1392
|
+
resolveUserMessageEncoding(message) === "text-block" ? { content: [{ type: "text", text: message.content }] } : message.content
|
|
1393
|
+
)
|
|
1394
|
+
);
|
|
1395
|
+
try {
|
|
1396
|
+
const response = await aiModel.invoke(providerMessages);
|
|
1397
|
+
const content = normalizeResponseContent(response?.content);
|
|
1398
|
+
if (isEmptyResponseContent(content)) {
|
|
1399
|
+
throw new Error("AI provider returned an empty chat completion");
|
|
1400
|
+
}
|
|
1401
|
+
return { content };
|
|
1402
|
+
} catch (error) {
|
|
1403
|
+
throw getAiInvocationError(error);
|
|
1404
|
+
}
|
|
1405
|
+
};
|
|
1406
|
+
var invokeAiPromptChat = (options) => invokeAiChatWithUserMessageEncoding(options, () => "text-block");
|
|
1407
|
+
|
|
1276
1408
|
// src/ai.ts
|
|
1277
1409
|
var parseAIResponse = (input) => {
|
|
1278
1410
|
try {
|
|
@@ -1286,18 +1418,6 @@ var parseAIResponse = (input) => {
|
|
|
1286
1418
|
return {};
|
|
1287
1419
|
}
|
|
1288
1420
|
};
|
|
1289
|
-
var normalizeResponseContent = (content) => {
|
|
1290
|
-
if (typeof content === "string" || content && typeof content === "object") {
|
|
1291
|
-
if (typeof content !== "object" || !Array.isArray(content)) {
|
|
1292
|
-
return content;
|
|
1293
|
-
}
|
|
1294
|
-
}
|
|
1295
|
-
if (Array.isArray(content)) {
|
|
1296
|
-
const text = content.map((part) => typeof part?.text === "string" ? part.text : "").join("\n").trim();
|
|
1297
|
-
return text;
|
|
1298
|
-
}
|
|
1299
|
-
return String(content ?? "");
|
|
1300
|
-
};
|
|
1301
1421
|
var normalizeAnalysis = (raw) => {
|
|
1302
1422
|
const direction = raw?.direction === "LONG" || raw?.direction === "SHORT" ? raw.direction : null;
|
|
1303
1423
|
const qualityNum = typeof raw?.quality === "number" ? Math.max(1, Math.min(5, Math.round(raw.quality))) : void 0;
|
|
@@ -1531,153 +1651,24 @@ Trade payload:
|
|
|
1531
1651
|
${JSON.stringify(payload)}
|
|
1532
1652
|
${buildAiHumanPromptAddonByStrategy(signal, payload)}
|
|
1533
1653
|
`;
|
|
1534
|
-
var getAiInvocationError = (error) => {
|
|
1535
|
-
const details = error instanceof Error && error.message.trim() ? error.message.trim() : String(error);
|
|
1536
|
-
const isEmptyCompletion = error instanceof TypeError && /Cannot read properties of undefined \(reading ['"]message['"]\)/.test(
|
|
1537
|
-
details
|
|
1538
|
-
);
|
|
1539
|
-
const wrapped = new Error(
|
|
1540
|
-
isEmptyCompletion ? "AI provider returned an empty chat completion" : `AI model invocation failed: ${details}`
|
|
1541
|
-
);
|
|
1542
|
-
wrapped.cause = error;
|
|
1543
|
-
return wrapped;
|
|
1544
|
-
};
|
|
1545
|
-
var isEmptyResponseContent = (content) => typeof content === "string" ? content.trim().length === 0 : Object.keys(content).length === 0;
|
|
1546
|
-
var DEFAULT_AI_MODEL = "openai/gpt-5-mini";
|
|
1547
|
-
var userSettingsCache = /* @__PURE__ */ new Map();
|
|
1548
|
-
var aiModelCache = /* @__PURE__ */ new Map();
|
|
1549
|
-
var getAiModelCacheKey = (userName, modelName) => `${userName}::${modelName}`;
|
|
1550
|
-
var resolveAiModelName = (settings, requestedModelName) => {
|
|
1551
|
-
const explicitModelName = typeof requestedModelName === "string" ? requestedModelName.trim() : "";
|
|
1552
|
-
if (explicitModelName) {
|
|
1553
|
-
return explicitModelName;
|
|
1554
|
-
}
|
|
1555
|
-
const settingsModelName = typeof settings.AI_MODEL === "string" ? settings.AI_MODEL.trim() : "";
|
|
1556
|
-
return settingsModelName || DEFAULT_AI_MODEL;
|
|
1557
|
-
};
|
|
1558
|
-
var getOpenRouterModelKwargs = (apiEndpoint) => {
|
|
1559
|
-
const endpoint = String(apiEndpoint ?? "").trim();
|
|
1560
|
-
if (!endpoint) {
|
|
1561
|
-
return {};
|
|
1562
|
-
}
|
|
1563
|
-
let hostname = "";
|
|
1564
|
-
try {
|
|
1565
|
-
hostname = new URL(endpoint).hostname;
|
|
1566
|
-
} catch {
|
|
1567
|
-
hostname = endpoint;
|
|
1568
|
-
}
|
|
1569
|
-
if (!hostname.toLowerCase().includes("openrouter")) {
|
|
1570
|
-
return {};
|
|
1571
|
-
}
|
|
1572
|
-
return {
|
|
1573
|
-
provider: {
|
|
1574
|
-
ignore: ["azure"]
|
|
1575
|
-
}
|
|
1576
|
-
};
|
|
1577
|
-
};
|
|
1578
|
-
var getAiSettings = async (userName = "root") => {
|
|
1579
|
-
let settingsPromise = userSettingsCache.get(userName);
|
|
1580
|
-
if (!settingsPromise) {
|
|
1581
|
-
settingsPromise = (0, import_userSettings.getUserSettings)(userName).then((settings2) => {
|
|
1582
|
-
const endpoint = (0, import_aiEndpoints.normalizeAiEndpoint)(settings2.AI_API_ENDPOINT);
|
|
1583
|
-
return {
|
|
1584
|
-
...settings2,
|
|
1585
|
-
AI_API_ENDPOINT: endpoint,
|
|
1586
|
-
AI_MODEL: (0, import_aiModels.normalizeAiModel)(settings2.AI_MODEL, endpoint),
|
|
1587
|
-
AI_RESPONSE_LANGUAGE: (0, import_aiLanguages.normalizeAiResponseLanguage)(
|
|
1588
|
-
settings2.AI_RESPONSE_LANGUAGE
|
|
1589
|
-
)
|
|
1590
|
-
};
|
|
1591
|
-
});
|
|
1592
|
-
settingsPromise.catch(() => {
|
|
1593
|
-
userSettingsCache.delete(userName);
|
|
1594
|
-
});
|
|
1595
|
-
userSettingsCache.set(userName, settingsPromise);
|
|
1596
|
-
}
|
|
1597
|
-
const settings = await settingsPromise;
|
|
1598
|
-
if (!settings.AI_API_KEY || !settings.AI_API_ENDPOINT) {
|
|
1599
|
-
throw new Error(`AI settings are incomplete for user ${userName}`);
|
|
1600
|
-
}
|
|
1601
|
-
return settings;
|
|
1602
|
-
};
|
|
1603
|
-
var createAiModel = async (userName = "root", requestedModelName) => {
|
|
1604
|
-
const settings = await getAiSettings(userName);
|
|
1605
|
-
const modelName = resolveAiModelName(settings, requestedModelName);
|
|
1606
|
-
const cacheKey = getAiModelCacheKey(userName, modelName);
|
|
1607
|
-
let modelPromise = aiModelCache.get(cacheKey);
|
|
1608
|
-
if (!modelPromise) {
|
|
1609
|
-
modelPromise = (async () => {
|
|
1610
|
-
const { ChatOpenAI } = await import("@langchain/openai");
|
|
1611
|
-
const modelKwargs = getOpenRouterModelKwargs(settings.AI_API_ENDPOINT);
|
|
1612
|
-
return new ChatOpenAI({
|
|
1613
|
-
temperature: 0.2,
|
|
1614
|
-
modelName,
|
|
1615
|
-
apiKey: settings.AI_API_KEY,
|
|
1616
|
-
...Object.keys(modelKwargs).length ? { modelKwargs } : {},
|
|
1617
|
-
configuration: {
|
|
1618
|
-
baseURL: settings.AI_API_ENDPOINT,
|
|
1619
|
-
defaultHeaders: {
|
|
1620
|
-
"HTTP-Referer": "https://tradejs.dev",
|
|
1621
|
-
"X-Title": "Inv"
|
|
1622
|
-
}
|
|
1623
|
-
}
|
|
1624
|
-
});
|
|
1625
|
-
})();
|
|
1626
|
-
modelPromise.catch(() => {
|
|
1627
|
-
aiModelCache.delete(cacheKey);
|
|
1628
|
-
});
|
|
1629
|
-
aiModelCache.set(cacheKey, modelPromise);
|
|
1630
|
-
}
|
|
1631
|
-
return modelPromise;
|
|
1632
|
-
};
|
|
1633
|
-
var getAiModel = async (userName = "root", requestedModelName) => {
|
|
1634
|
-
const settings = await getAiSettings(userName);
|
|
1635
|
-
const resolvedModelName = resolveAiModelName(settings, requestedModelName);
|
|
1636
|
-
try {
|
|
1637
|
-
return await createAiModel(userName, resolvedModelName);
|
|
1638
|
-
} catch (error) {
|
|
1639
|
-
aiModelCache.delete(getAiModelCacheKey(userName, resolvedModelName));
|
|
1640
|
-
userSettingsCache.delete(userName);
|
|
1641
|
-
throw error;
|
|
1642
|
-
}
|
|
1643
|
-
};
|
|
1644
1654
|
var runAiPrompt = async ({ systemPrompt, humanPrompt }, options = {}) => {
|
|
1645
|
-
const
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
getAiSettings(options.userName)
|
|
1649
|
-
]);
|
|
1650
|
-
const messages = [];
|
|
1651
|
-
const responseLanguage = (0, import_aiLanguages.getAiResponseLanguagePromptName)(
|
|
1652
|
-
settings.AI_RESPONSE_LANGUAGE || import_aiLanguages.DEFAULT_AI_RESPONSE_LANGUAGE
|
|
1653
|
-
);
|
|
1654
|
-
messages.push(new SystemMessage(systemPrompt));
|
|
1655
|
-
messages.push(
|
|
1656
|
-
new SystemMessage(
|
|
1657
|
-
`Write all user-visible text fields in ${responseLanguage}. Keep field names and JSON syntax unchanged.`
|
|
1658
|
-
)
|
|
1659
|
-
);
|
|
1660
|
-
messages.push(
|
|
1661
|
-
new HumanMessage({
|
|
1662
|
-
content: [
|
|
1663
|
-
{
|
|
1664
|
-
type: "text",
|
|
1665
|
-
text: humanPrompt
|
|
1666
|
-
}
|
|
1667
|
-
]
|
|
1668
|
-
})
|
|
1655
|
+
const settings = await getAiUserSettings(options.userName);
|
|
1656
|
+
const responseLanguage = (0, import_aiLanguages2.getAiResponseLanguagePromptName)(
|
|
1657
|
+
settings.AI_RESPONSE_LANGUAGE || import_aiLanguages2.DEFAULT_AI_RESPONSE_LANGUAGE
|
|
1669
1658
|
);
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1659
|
+
const response = await invokeAiPromptChat({
|
|
1660
|
+
userName: options.userName,
|
|
1661
|
+
model: options.model,
|
|
1662
|
+
messages: [
|
|
1663
|
+
{ role: "system", content: systemPrompt },
|
|
1664
|
+
{
|
|
1665
|
+
role: "system",
|
|
1666
|
+
content: `Write all user-visible text fields in ${responseLanguage}. Keep field names and JSON syntax unchanged.`
|
|
1667
|
+
},
|
|
1668
|
+
{ role: "user", content: humanPrompt }
|
|
1669
|
+
]
|
|
1670
|
+
});
|
|
1671
|
+
const parsed = parseAIResponse(response.content);
|
|
1681
1672
|
const normalized = normalizeAnalysis(parsed);
|
|
1682
1673
|
if (!options.signal) {
|
|
1683
1674
|
return normalized;
|
|
@@ -4435,6 +4426,7 @@ var executeEntryOrder = async ({
|
|
|
4435
4426
|
deploymentId: signal.deploymentId,
|
|
4436
4427
|
policyProfileId: signal.policyProfileId,
|
|
4437
4428
|
runtimeConfigId: signal.runtimeConfigId,
|
|
4429
|
+
runtimeReleaseVersion: signal.runtimeReleaseVersion,
|
|
4438
4430
|
runtimeLineage: signal.runtimeLineage,
|
|
4439
4431
|
...signal.aiAnalysis ? { aiAnalysis: signal.aiAnalysis } : {}
|
|
4440
4432
|
});
|
|
@@ -5359,6 +5351,9 @@ var createStrategyRuntime = ({
|
|
|
5359
5351
|
deploymentId: requestedDeploymentId,
|
|
5360
5352
|
policyProfileId,
|
|
5361
5353
|
runtimeConfigId,
|
|
5354
|
+
runtimeReleaseVersion,
|
|
5355
|
+
entriesPaused = false,
|
|
5356
|
+
runtimeLineage,
|
|
5362
5357
|
runtimeConfigSnapshot,
|
|
5363
5358
|
data,
|
|
5364
5359
|
btcData,
|
|
@@ -6017,6 +6012,7 @@ var createStrategyRuntime = ({
|
|
|
6017
6012
|
});
|
|
6018
6013
|
const signal = decision.signal;
|
|
6019
6014
|
if (signal) {
|
|
6015
|
+
if (runtimeLineage) signal.runtimeLineage = runtimeLineage;
|
|
6020
6016
|
if (universe) signal.universe = universe;
|
|
6021
6017
|
if (assetClass) signal.assetClass = assetClass;
|
|
6022
6018
|
if (accountId) signal.accountId = accountId;
|
|
@@ -6027,6 +6023,9 @@ var createStrategyRuntime = ({
|
|
|
6027
6023
|
signal.signalId = `${signal.signalId}:${runtimeConfigId}`;
|
|
6028
6024
|
}
|
|
6029
6025
|
}
|
|
6026
|
+
if (runtimeReleaseVersion) {
|
|
6027
|
+
signal.runtimeReleaseVersion = runtimeReleaseVersion;
|
|
6028
|
+
}
|
|
6030
6029
|
if (decisionHookCtx.policyProfileId) {
|
|
6031
6030
|
signal.policyProfileId = decisionHookCtx.policyProfileId;
|
|
6032
6031
|
}
|
|
@@ -6133,6 +6132,15 @@ var createStrategyRuntime = ({
|
|
|
6133
6132
|
quality,
|
|
6134
6133
|
minAiQuality
|
|
6135
6134
|
});
|
|
6135
|
+
if (entriesPaused) {
|
|
6136
|
+
const skipReason = "RUNTIME_ENTRIES_PAUSED";
|
|
6137
|
+
if (signal) {
|
|
6138
|
+
signal.orderStatus = "skipped";
|
|
6139
|
+
signal.orderSkipReason = skipReason;
|
|
6140
|
+
signal.runtimeReleaseVersion = runtimeReleaseVersion;
|
|
6141
|
+
}
|
|
6142
|
+
return signal ?? skipReason;
|
|
6143
|
+
}
|
|
6136
6144
|
if (!shouldMakeOrder) {
|
|
6137
6145
|
if (signal) {
|
|
6138
6146
|
signal.orderStatus = "skipped";
|
|
@@ -6241,7 +6249,323 @@ var createStrategyRuntime = ({
|
|
|
6241
6249
|
setStrategyRuntimeFactory(createStrategyRuntime);
|
|
6242
6250
|
|
|
6243
6251
|
// src/connectorsRegistry.ts
|
|
6252
|
+
var import_logger14 = require("@tradejs/infra/logger");
|
|
6253
|
+
|
|
6254
|
+
// src/connectorRuntime.ts
|
|
6255
|
+
var import_logger13 = require("@tradejs/infra/logger");
|
|
6256
|
+
var import_tradingAccounts = require("@tradejs/infra/tradingAccounts");
|
|
6257
|
+
|
|
6258
|
+
// src/timescaleCachedKline.ts
|
|
6259
|
+
var import_constants6 = require("@tradejs/core/constants");
|
|
6260
|
+
var import_data4 = require("@tradejs/core/data");
|
|
6261
|
+
var import_time2 = require("@tradejs/core/time");
|
|
6262
|
+
var import_async = require("@tradejs/core/async");
|
|
6244
6263
|
var import_logger12 = require("@tradejs/infra/logger");
|
|
6264
|
+
var import_candles = require("@tradejs/infra/timescale/candles");
|
|
6265
|
+
var DEFAULT_LIMIT = 1e3;
|
|
6266
|
+
var DEFAULT_CACHE_FALLBACK_WINDOW = 1e3;
|
|
6267
|
+
var DEFAULT_TIMESCALE_RETRIES = 2;
|
|
6268
|
+
var DEFAULT_TIMESCALE_RETRY_DELAY_MS = 1e3;
|
|
6269
|
+
var resolveNonNegativeInt = (value, fallback) => {
|
|
6270
|
+
const parsed = Number(value);
|
|
6271
|
+
if (!Number.isFinite(parsed) || parsed < 0) {
|
|
6272
|
+
return fallback;
|
|
6273
|
+
}
|
|
6274
|
+
return Math.floor(parsed);
|
|
6275
|
+
};
|
|
6276
|
+
var getTimescaleRetryCount = () => resolveNonNegativeInt(
|
|
6277
|
+
process.env.TIMESCALE_KLINE_RETRIES,
|
|
6278
|
+
DEFAULT_TIMESCALE_RETRIES
|
|
6279
|
+
);
|
|
6280
|
+
var getTimescaleRetryDelayMs = () => resolveNonNegativeInt(
|
|
6281
|
+
process.env.TIMESCALE_KLINE_RETRY_DELAY_MS,
|
|
6282
|
+
DEFAULT_TIMESCALE_RETRY_DELAY_MS
|
|
6283
|
+
);
|
|
6284
|
+
var intervalMsOf = (interval) => interval * 6e4;
|
|
6285
|
+
var clampToClosedCandle = (value, intervalMs) => Math.floor(value / intervalMs) * intervalMs;
|
|
6286
|
+
var normalizeRangeToClosed = (intervalMs, start, end) => {
|
|
6287
|
+
const lastClosed = Math.floor(Date.now() / intervalMs) * intervalMs;
|
|
6288
|
+
const normStart = start !== void 0 ? clampToClosedCandle(start, intervalMs) : 0;
|
|
6289
|
+
const cappedEnd = Math.min(end ?? Date.now(), lastClosed);
|
|
6290
|
+
const normEnd = clampToClosedCandle(cappedEnd, intervalMs);
|
|
6291
|
+
return { normStart, normEnd };
|
|
6292
|
+
};
|
|
6293
|
+
var rowsToKline = (rows) => rows.map(({ ts, ...data }) => ({
|
|
6294
|
+
timestamp: new Date(ts).getTime(),
|
|
6295
|
+
...data
|
|
6296
|
+
}));
|
|
6297
|
+
var createTimescaleCachedKline = ({
|
|
6298
|
+
provider,
|
|
6299
|
+
request,
|
|
6300
|
+
intervalToMinutes,
|
|
6301
|
+
limit = DEFAULT_LIMIT,
|
|
6302
|
+
cacheFallbackWindow = DEFAULT_CACHE_FALLBACK_WINDOW
|
|
6303
|
+
}) => {
|
|
6304
|
+
let isTimescaleFallbackMode = false;
|
|
6305
|
+
const runTimescaleOperation = async (operation) => {
|
|
6306
|
+
const retries = getTimescaleRetryCount();
|
|
6307
|
+
const retryDelayMs = getTimescaleRetryDelayMs();
|
|
6308
|
+
for (let attempt = 0; ; attempt += 1) {
|
|
6309
|
+
try {
|
|
6310
|
+
return await operation();
|
|
6311
|
+
} catch (error) {
|
|
6312
|
+
if (attempt >= retries) {
|
|
6313
|
+
throw error;
|
|
6314
|
+
}
|
|
6315
|
+
const waitMs = retryDelayMs * 2 ** attempt;
|
|
6316
|
+
if (waitMs > 0) {
|
|
6317
|
+
await (0, import_async.delay)(waitMs);
|
|
6318
|
+
}
|
|
6319
|
+
}
|
|
6320
|
+
}
|
|
6321
|
+
};
|
|
6322
|
+
const loadData = async (direction, pointer, limitBoundary, requestParams, intervalMs, options = {}) => {
|
|
6323
|
+
if (pointer === void 0) return { data: [], loaded: false };
|
|
6324
|
+
let accumulated = [];
|
|
6325
|
+
let fulfilled = false;
|
|
6326
|
+
let loaded = false;
|
|
6327
|
+
const shouldAccumulate = options.accumulate ?? true;
|
|
6328
|
+
while (!fulfilled) {
|
|
6329
|
+
const currentPointer = pointer;
|
|
6330
|
+
const params = {
|
|
6331
|
+
symbol: requestParams.symbol,
|
|
6332
|
+
interval: requestParams.interval,
|
|
6333
|
+
silent: requestParams.silent,
|
|
6334
|
+
end: requestParams.end
|
|
6335
|
+
};
|
|
6336
|
+
if (direction === "older") {
|
|
6337
|
+
params.end = pointer;
|
|
6338
|
+
if (limitBoundary !== void 0) params.start = limitBoundary;
|
|
6339
|
+
} else {
|
|
6340
|
+
params.start = pointer;
|
|
6341
|
+
if (limitBoundary !== void 0) params.end = limitBoundary;
|
|
6342
|
+
}
|
|
6343
|
+
const partData = await request(params);
|
|
6344
|
+
if (!partData.length) {
|
|
6345
|
+
fulfilled = true;
|
|
6346
|
+
break;
|
|
6347
|
+
}
|
|
6348
|
+
loaded = true;
|
|
6349
|
+
if (options.onPartData) {
|
|
6350
|
+
await options.onPartData(partData);
|
|
6351
|
+
}
|
|
6352
|
+
if (shouldAccumulate) {
|
|
6353
|
+
accumulated = direction === "older" ? (0, import_data4.mergeData)(partData, accumulated) : (0, import_data4.mergeData)(accumulated, partData);
|
|
6354
|
+
}
|
|
6355
|
+
const boundaryReached = limitBoundary !== void 0 && (direction === "older" && currentPointer <= limitBoundary || direction === "newer" && currentPointer >= limitBoundary);
|
|
6356
|
+
if (partData.length < limit || boundaryReached) {
|
|
6357
|
+
fulfilled = true;
|
|
6358
|
+
break;
|
|
6359
|
+
}
|
|
6360
|
+
const nextPointer = direction === "older" ? (0, import_time2.getItemTimestamp)(partData[0]) - intervalMs : (0, import_time2.getItemTimestamp)(partData[partData.length - 1]) + intervalMs;
|
|
6361
|
+
if (!Number.isFinite(nextPointer) || nextPointer === currentPointer) {
|
|
6362
|
+
fulfilled = true;
|
|
6363
|
+
break;
|
|
6364
|
+
}
|
|
6365
|
+
pointer = nextPointer;
|
|
6366
|
+
}
|
|
6367
|
+
return { data: accumulated, loaded };
|
|
6368
|
+
};
|
|
6369
|
+
const refreshTail = async ({
|
|
6370
|
+
symbol,
|
|
6371
|
+
interval,
|
|
6372
|
+
silent,
|
|
6373
|
+
tailCount = 2
|
|
6374
|
+
}) => {
|
|
6375
|
+
const intMinutes = intervalToMinutes(interval);
|
|
6376
|
+
if (!intMinutes) {
|
|
6377
|
+
import_logger12.logger.log("error", "refreshTail: invalid interval %s", interval);
|
|
6378
|
+
return;
|
|
6379
|
+
}
|
|
6380
|
+
const intervalMs = intervalMsOf(intMinutes);
|
|
6381
|
+
const lastClosed = Math.floor(Date.now() / intervalMs) * intervalMs;
|
|
6382
|
+
const tailEnd = lastClosed + intervalMs;
|
|
6383
|
+
const tailStart = tailEnd - tailCount * intervalMs;
|
|
6384
|
+
const part = await request({
|
|
6385
|
+
symbol,
|
|
6386
|
+
interval,
|
|
6387
|
+
start: tailStart,
|
|
6388
|
+
end: tailEnd,
|
|
6389
|
+
silent
|
|
6390
|
+
});
|
|
6391
|
+
if (part.length) {
|
|
6392
|
+
await runTimescaleOperation(
|
|
6393
|
+
() => (0, import_candles.upsertCandles)((0, import_candles.toRows)(provider, symbol, intMinutes, part))
|
|
6394
|
+
);
|
|
6395
|
+
}
|
|
6396
|
+
};
|
|
6397
|
+
return async ({
|
|
6398
|
+
symbol,
|
|
6399
|
+
interval,
|
|
6400
|
+
start: defaultStart,
|
|
6401
|
+
end: defaultEnd,
|
|
6402
|
+
silent = false,
|
|
6403
|
+
cacheOnly = false,
|
|
6404
|
+
warmOnly = false
|
|
6405
|
+
}) => {
|
|
6406
|
+
const intMinutes = intervalToMinutes(interval);
|
|
6407
|
+
if (!intMinutes) {
|
|
6408
|
+
import_logger12.logger.log("error", "kline: invalid interval %s", interval);
|
|
6409
|
+
return [];
|
|
6410
|
+
}
|
|
6411
|
+
if (defaultStart !== void 0 && defaultEnd !== void 0 && defaultEnd <= defaultStart) {
|
|
6412
|
+
return [];
|
|
6413
|
+
}
|
|
6414
|
+
const intervalMs = intervalMsOf(intMinutes);
|
|
6415
|
+
try {
|
|
6416
|
+
const edges = await runTimescaleOperation(
|
|
6417
|
+
() => (0, import_candles.getDataEdges)(provider, symbol, intMinutes)
|
|
6418
|
+
);
|
|
6419
|
+
let dataStart = edges.min;
|
|
6420
|
+
let dataEnd = edges.max;
|
|
6421
|
+
const { normStart, normEnd } = normalizeRangeToClosed(
|
|
6422
|
+
intervalMs,
|
|
6423
|
+
defaultStart,
|
|
6424
|
+
defaultEnd
|
|
6425
|
+
);
|
|
6426
|
+
if (cacheOnly) {
|
|
6427
|
+
const base = edges.max ?? Date.now();
|
|
6428
|
+
const s = Math.max(
|
|
6429
|
+
defaultStart ?? base - cacheFallbackWindow * intervalMs,
|
|
6430
|
+
0
|
|
6431
|
+
);
|
|
6432
|
+
const e = defaultEnd ?? base;
|
|
6433
|
+
const dbData2 = await runTimescaleOperation(
|
|
6434
|
+
() => (0, import_candles.getCandlesRange)(provider, symbol, intMinutes, s, e)
|
|
6435
|
+
);
|
|
6436
|
+
return rowsToKline(dbData2);
|
|
6437
|
+
}
|
|
6438
|
+
const needOlderData = defaultStart !== void 0 && (dataStart === void 0 || normStart < dataStart);
|
|
6439
|
+
const persistPartData = warmOnly ? (partData) => runTimescaleOperation(
|
|
6440
|
+
() => (0, import_candles.upsertCandles)((0, import_candles.toRows)(provider, symbol, intMinutes, partData))
|
|
6441
|
+
) : void 0;
|
|
6442
|
+
if (needOlderData) {
|
|
6443
|
+
const pointerForOlder = dataStart ?? normEnd ?? Date.now();
|
|
6444
|
+
const olderResult = await loadData(
|
|
6445
|
+
"older",
|
|
6446
|
+
pointerForOlder,
|
|
6447
|
+
normStart,
|
|
6448
|
+
{
|
|
6449
|
+
symbol,
|
|
6450
|
+
interval,
|
|
6451
|
+
silent,
|
|
6452
|
+
start: normStart,
|
|
6453
|
+
end: pointerForOlder
|
|
6454
|
+
},
|
|
6455
|
+
intervalMs,
|
|
6456
|
+
{
|
|
6457
|
+
accumulate: !warmOnly,
|
|
6458
|
+
onPartData: persistPartData
|
|
6459
|
+
}
|
|
6460
|
+
);
|
|
6461
|
+
if (warmOnly && olderResult.loaded) {
|
|
6462
|
+
dataStart = normStart;
|
|
6463
|
+
if (dataEnd === void 0) {
|
|
6464
|
+
dataEnd = normEnd;
|
|
6465
|
+
}
|
|
6466
|
+
} else if (olderResult.data.length) {
|
|
6467
|
+
await runTimescaleOperation(
|
|
6468
|
+
() => (0, import_candles.upsertCandles)(
|
|
6469
|
+
(0, import_candles.toRows)(provider, symbol, intMinutes, olderResult.data)
|
|
6470
|
+
)
|
|
6471
|
+
);
|
|
6472
|
+
dataStart = normStart;
|
|
6473
|
+
if (dataEnd === void 0) {
|
|
6474
|
+
dataEnd = normEnd;
|
|
6475
|
+
}
|
|
6476
|
+
}
|
|
6477
|
+
}
|
|
6478
|
+
const needNewerData = defaultEnd !== void 0 && (dataEnd === void 0 || normEnd > dataEnd);
|
|
6479
|
+
if (needNewerData) {
|
|
6480
|
+
const fallbackStart = (0, import_time2.getTimestamp)(import_constants6.PRELOAD_FALLBACK_DAYS);
|
|
6481
|
+
const pointerForNewer = dataEnd ?? (defaultStart !== void 0 ? normStart : fallbackStart) ?? 0;
|
|
6482
|
+
const newerResult = await loadData(
|
|
6483
|
+
"newer",
|
|
6484
|
+
pointerForNewer,
|
|
6485
|
+
normEnd,
|
|
6486
|
+
{
|
|
6487
|
+
symbol,
|
|
6488
|
+
interval,
|
|
6489
|
+
silent,
|
|
6490
|
+
start: pointerForNewer,
|
|
6491
|
+
end: normEnd
|
|
6492
|
+
},
|
|
6493
|
+
intervalMs,
|
|
6494
|
+
{
|
|
6495
|
+
accumulate: !warmOnly,
|
|
6496
|
+
onPartData: persistPartData
|
|
6497
|
+
}
|
|
6498
|
+
);
|
|
6499
|
+
if (warmOnly && newerResult.loaded) {
|
|
6500
|
+
dataEnd = normEnd;
|
|
6501
|
+
} else if (newerResult.data.length) {
|
|
6502
|
+
await runTimescaleOperation(
|
|
6503
|
+
() => (0, import_candles.upsertCandles)(
|
|
6504
|
+
(0, import_candles.toRows)(provider, symbol, intMinutes, newerResult.data)
|
|
6505
|
+
)
|
|
6506
|
+
);
|
|
6507
|
+
dataEnd = normEnd;
|
|
6508
|
+
}
|
|
6509
|
+
}
|
|
6510
|
+
const isRightEdgeQuery = defaultEnd === void 0 || defaultEnd && defaultEnd >= Date.now() - intervalMs;
|
|
6511
|
+
if (!cacheOnly && isRightEdgeQuery) {
|
|
6512
|
+
await refreshTail({ symbol, interval, silent });
|
|
6513
|
+
}
|
|
6514
|
+
if (warmOnly) {
|
|
6515
|
+
if (isTimescaleFallbackMode) {
|
|
6516
|
+
isTimescaleFallbackMode = false;
|
|
6517
|
+
import_logger12.logger.log("info", "TimescaleDB connection restored for kline cache");
|
|
6518
|
+
}
|
|
6519
|
+
return [];
|
|
6520
|
+
}
|
|
6521
|
+
const rangeStart = defaultStart ?? dataStart ?? 0;
|
|
6522
|
+
const rangeEnd = defaultEnd ?? dataEnd ?? Date.now();
|
|
6523
|
+
const { normStart: finalStart, normEnd: finalEnd } = normalizeRangeToClosed(intervalMs, rangeStart, rangeEnd);
|
|
6524
|
+
const dbData = await runTimescaleOperation(
|
|
6525
|
+
() => (0, import_candles.getCandlesRange)(provider, symbol, intMinutes, finalStart, finalEnd)
|
|
6526
|
+
);
|
|
6527
|
+
if (isTimescaleFallbackMode) {
|
|
6528
|
+
isTimescaleFallbackMode = false;
|
|
6529
|
+
import_logger12.logger.log("info", "TimescaleDB connection restored for kline cache");
|
|
6530
|
+
}
|
|
6531
|
+
return rowsToKline(dbData);
|
|
6532
|
+
} catch (error) {
|
|
6533
|
+
if (!isTimescaleFallbackMode) {
|
|
6534
|
+
isTimescaleFallbackMode = true;
|
|
6535
|
+
import_logger12.logger.log(
|
|
6536
|
+
"warn",
|
|
6537
|
+
"TimescaleDB unavailable for %s %s: %s. Falling back to exchange API.",
|
|
6538
|
+
symbol,
|
|
6539
|
+
interval,
|
|
6540
|
+
String(error)
|
|
6541
|
+
);
|
|
6542
|
+
}
|
|
6543
|
+
if (cacheOnly || warmOnly) {
|
|
6544
|
+
return [];
|
|
6545
|
+
}
|
|
6546
|
+
return request({
|
|
6547
|
+
symbol,
|
|
6548
|
+
interval,
|
|
6549
|
+
start: defaultStart,
|
|
6550
|
+
end: defaultEnd,
|
|
6551
|
+
silent
|
|
6552
|
+
});
|
|
6553
|
+
}
|
|
6554
|
+
};
|
|
6555
|
+
};
|
|
6556
|
+
|
|
6557
|
+
// src/connectorRuntime.ts
|
|
6558
|
+
var connectorRuntime = {
|
|
6559
|
+
logger: import_logger13.logger,
|
|
6560
|
+
resolveTradingAccount: import_tradingAccounts.resolveTradingAccount,
|
|
6561
|
+
createCachedKline: createTimescaleCachedKline
|
|
6562
|
+
};
|
|
6563
|
+
var bindConnectorRuntime = (creator, runtime = connectorRuntime) => {
|
|
6564
|
+
const boundCreator = (config) => creator(config, runtime);
|
|
6565
|
+
return boundCreator;
|
|
6566
|
+
};
|
|
6567
|
+
|
|
6568
|
+
// src/connectorsRegistry.ts
|
|
6245
6569
|
var createConnectorRegistryState = () => ({
|
|
6246
6570
|
connectorCreators: /* @__PURE__ */ new Map(),
|
|
6247
6571
|
providerToConnectorName: /* @__PURE__ */ new Map(),
|
|
@@ -6293,7 +6617,7 @@ var normalizeProviders = (providers, connectorName) => {
|
|
|
6293
6617
|
var registerProvider = (provider, connectorName, source, providerToConnectorName) => {
|
|
6294
6618
|
const existing = providerToConnectorName.get(provider);
|
|
6295
6619
|
if (existing && existing !== connectorName) {
|
|
6296
|
-
|
|
6620
|
+
import_logger14.logger.warn(
|
|
6297
6621
|
'Skip duplicate connector provider "%s" from %s: already mapped to %s',
|
|
6298
6622
|
provider,
|
|
6299
6623
|
source,
|
|
@@ -6306,11 +6630,11 @@ var registerProvider = (provider, connectorName, source, providerToConnectorName
|
|
|
6306
6630
|
var registerEntry = (entry, source, state) => {
|
|
6307
6631
|
const connectorName = String(entry?.name ?? "").trim();
|
|
6308
6632
|
if (!connectorName) {
|
|
6309
|
-
|
|
6633
|
+
import_logger14.logger.warn("Skip connector entry without name from %s", source);
|
|
6310
6634
|
return;
|
|
6311
6635
|
}
|
|
6312
6636
|
if (typeof entry.creator !== "function") {
|
|
6313
|
-
|
|
6637
|
+
import_logger14.logger.warn(
|
|
6314
6638
|
'Skip connector entry "%s" from %s: creator must be a function',
|
|
6315
6639
|
connectorName,
|
|
6316
6640
|
source
|
|
@@ -6322,7 +6646,7 @@ var registerEntry = (entry, source, state) => {
|
|
|
6322
6646
|
state.connectorCreators
|
|
6323
6647
|
);
|
|
6324
6648
|
if (existingByName) {
|
|
6325
|
-
|
|
6649
|
+
import_logger14.logger.warn(
|
|
6326
6650
|
'Skip duplicate connector "%s" from %s: already registered as %s',
|
|
6327
6651
|
connectorName,
|
|
6328
6652
|
source,
|
|
@@ -6330,7 +6654,10 @@ var registerEntry = (entry, source, state) => {
|
|
|
6330
6654
|
);
|
|
6331
6655
|
return;
|
|
6332
6656
|
}
|
|
6333
|
-
state.connectorCreators.set(
|
|
6657
|
+
state.connectorCreators.set(
|
|
6658
|
+
connectorName,
|
|
6659
|
+
bindConnectorRuntime(entry.creator)
|
|
6660
|
+
);
|
|
6334
6661
|
const providers = normalizeProviders(entry.providers, connectorName);
|
|
6335
6662
|
for (const provider of providers) {
|
|
6336
6663
|
registerProvider(
|
|
@@ -6394,7 +6721,7 @@ var ensureConnectorPluginsLoaded = async (cwd = getTradejsProjectCwd()) => {
|
|
|
6394
6721
|
);
|
|
6395
6722
|
const pluginDefinition = extractConnectorPluginDefinition(moduleExport);
|
|
6396
6723
|
if (!pluginDefinition) {
|
|
6397
|
-
|
|
6724
|
+
import_logger14.logger.warn(
|
|
6398
6725
|
'Skip connector plugin "%s": export { connectorEntries } is missing',
|
|
6399
6726
|
moduleName
|
|
6400
6727
|
);
|
|
@@ -6402,7 +6729,7 @@ var ensureConnectorPluginsLoaded = async (cwd = getTradejsProjectCwd()) => {
|
|
|
6402
6729
|
}
|
|
6403
6730
|
registerEntries2(pluginDefinition.connectorEntries, moduleName, state);
|
|
6404
6731
|
} catch (error) {
|
|
6405
|
-
|
|
6732
|
+
import_logger14.logger.warn(
|
|
6406
6733
|
'Failed to load connector plugin "%s": %s',
|
|
6407
6734
|
moduleName,
|
|
6408
6735
|
String(error)
|
|
@@ -6547,7 +6874,7 @@ var import_coreResearch = require("@tradejs/infra/coreResearch");
|
|
|
6547
6874
|
var import_ml3 = require("@tradejs/infra/ml");
|
|
6548
6875
|
|
|
6549
6876
|
// src/executionCosts.ts
|
|
6550
|
-
var
|
|
6877
|
+
var import_constants7 = require("@tradejs/core/constants");
|
|
6551
6878
|
var finiteOr = (value, fallback) => {
|
|
6552
6879
|
const parsed = Number(value);
|
|
6553
6880
|
return Number.isFinite(parsed) ? parsed : fallback;
|
|
@@ -6587,8 +6914,8 @@ var resolveExecutionCosts = async (params) => {
|
|
|
6587
6914
|
const cacheOnly = config.EXECUTION_COSTS_CACHE_ONLY === true;
|
|
6588
6915
|
const hasConfiguredFees = Number.isFinite(Number(config.MAKER_FEE_RATE)) && Number.isFinite(Number(config.TAKER_FEE_RATE));
|
|
6589
6916
|
const exchangeFees = !cacheOnly && !hasConfiguredFees && connector.getTradingFeeRate ? await loadTradingFee(connector, symbol).catch(() => null) : null;
|
|
6590
|
-
const makerRate = hasConfiguredFees ? Number(config.MAKER_FEE_RATE) : exchangeFees?.makerRate ??
|
|
6591
|
-
const takerRate = hasConfiguredFees ? Number(config.TAKER_FEE_RATE) : exchangeFees?.takerRate ??
|
|
6917
|
+
const makerRate = hasConfiguredFees ? Number(config.MAKER_FEE_RATE) : exchangeFees?.makerRate ?? import_constants7.FEE_PERCENT;
|
|
6918
|
+
const takerRate = hasConfiguredFees ? Number(config.TAKER_FEE_RATE) : exchangeFees?.takerRate ?? import_constants7.FEE_PERCENT;
|
|
6592
6919
|
const fundingEnabled = config.FUNDING_ENABLED !== false && !cacheOnly && typeof connector.getFundingRateHistory === "function";
|
|
6593
6920
|
const fundingRates = fundingEnabled ? await loadFundingRates(connector, symbol, startTime, endTime) : [];
|
|
6594
6921
|
const requestedLeverage = Math.max(1, finiteOr(config.LEVERAGE, 10));
|
|
@@ -6609,18 +6936,18 @@ var resolveExecutionCosts = async (params) => {
|
|
|
6609
6936
|
toTimestamp: fundingRates.at(-1)?.timestamp ?? null
|
|
6610
6937
|
},
|
|
6611
6938
|
slippage: {
|
|
6612
|
-
baseBps: finiteOr(config.SLIPPAGE_BASE_BPS,
|
|
6939
|
+
baseBps: finiteOr(config.SLIPPAGE_BASE_BPS, import_constants7.BACKTEST_BASE_SLIPPAGE_BPS),
|
|
6613
6940
|
spreadMultiplier: finiteOr(
|
|
6614
6941
|
config.SLIPPAGE_SPREAD_MULTIPLIER,
|
|
6615
|
-
|
|
6942
|
+
import_constants7.BACKTEST_SPREAD_SLIPPAGE_MULTIPLIER
|
|
6616
6943
|
),
|
|
6617
6944
|
marketImpactBps: finiteOr(
|
|
6618
6945
|
config.SLIPPAGE_MARKET_IMPACT_BPS,
|
|
6619
|
-
|
|
6946
|
+
import_constants7.BACKTEST_MARKET_IMPACT_BPS
|
|
6620
6947
|
),
|
|
6621
6948
|
delayRiskMultiplier: finiteOr(
|
|
6622
6949
|
config.SLIPPAGE_DELAY_RISK_MULTIPLIER,
|
|
6623
|
-
|
|
6950
|
+
import_constants7.BACKTEST_DELAY_RISK_MULTIPLIER
|
|
6624
6951
|
),
|
|
6625
6952
|
source: config.SLIPPAGE_BASE_BPS != null || config.SLIPPAGE_SPREAD_MULTIPLIER != null || config.SLIPPAGE_MARKET_IMPACT_BPS != null ? "config" : "fallback"
|
|
6626
6953
|
},
|
|
@@ -6638,7 +6965,7 @@ var resolveExecutionCosts = async (params) => {
|
|
|
6638
6965
|
|
|
6639
6966
|
// src/testConnector.ts
|
|
6640
6967
|
var import_node_crypto4 = require("crypto");
|
|
6641
|
-
var
|
|
6968
|
+
var import_constants8 = require("@tradejs/core/constants");
|
|
6642
6969
|
var import_backtest = require("@tradejs/core/backtest");
|
|
6643
6970
|
var import_trade2 = require("@tradejs/core/trade");
|
|
6644
6971
|
var import_math = require("@tradejs/core/math");
|
|
@@ -6665,14 +6992,14 @@ var createTestConnector = (connector, context) => {
|
|
|
6665
6992
|
const positionLog = [];
|
|
6666
6993
|
const fastMode = Boolean(context?.fastMode);
|
|
6667
6994
|
const executionCostModel = context?.executionCostModel;
|
|
6668
|
-
const makerFeeRate = executionCostModel?.fees.makerRate ??
|
|
6669
|
-
const takerFeeRate = executionCostModel?.fees.takerRate ??
|
|
6995
|
+
const makerFeeRate = executionCostModel?.fees.makerRate ?? import_constants8.FEE_PERCENT;
|
|
6996
|
+
const takerFeeRate = executionCostModel?.fees.takerRate ?? import_constants8.FEE_PERCENT;
|
|
6670
6997
|
const fundingRates = [...context?.fundingRates ?? []].sort(
|
|
6671
6998
|
(left, right) => left.timestamp - right.timestamp
|
|
6672
6999
|
);
|
|
6673
7000
|
const processedFundingTimestamps = /* @__PURE__ */ new Set();
|
|
6674
7001
|
let currentPosition = null;
|
|
6675
|
-
let amount =
|
|
7002
|
+
let amount = import_constants8.INITIAL_BACKTEST_AMOUNT;
|
|
6676
7003
|
let originalQty = 0;
|
|
6677
7004
|
let currentPositionProfit = 0;
|
|
6678
7005
|
let currentSignalId = null;
|
|
@@ -7115,7 +7442,7 @@ var createTestConnector = (connector, context) => {
|
|
|
7115
7442
|
profit: fullStat.netProfit
|
|
7116
7443
|
} : {
|
|
7117
7444
|
amount: (0, import_math.round)(amount),
|
|
7118
|
-
profit: (0, import_math.round)(amount -
|
|
7445
|
+
profit: (0, import_math.round)(amount - import_constants8.INITIAL_BACKTEST_AMOUNT),
|
|
7119
7446
|
orders: positionLog.length
|
|
7120
7447
|
},
|
|
7121
7448
|
orderLogId,
|
|
@@ -8143,7 +8470,7 @@ var BACKTEST_INTERVAL = "15";
|
|
|
8143
8470
|
var resolveBacktestExecutionInterval = (interval) => {
|
|
8144
8471
|
const normalized = String(interval);
|
|
8145
8472
|
if (normalized === "15") {
|
|
8146
|
-
return
|
|
8473
|
+
return import_constants9.BACKTEST_EXECUTION_INTERVAL;
|
|
8147
8474
|
}
|
|
8148
8475
|
if (normalized === "60") {
|
|
8149
8476
|
return "15";
|
|
@@ -8190,7 +8517,7 @@ var filterClosedAlignedCandles = (data, btcData, interval) => {
|
|
|
8190
8517
|
};
|
|
8191
8518
|
};
|
|
8192
8519
|
var shouldLoadBacktestExecutionCandles = (interval) => {
|
|
8193
|
-
if (!
|
|
8520
|
+
if (!import_constants9.BACKTEST_LOWER_TIMEFRAME_EXECUTION_ENABLED) {
|
|
8194
8521
|
return false;
|
|
8195
8522
|
}
|
|
8196
8523
|
const executionInterval = resolveBacktestExecutionInterval(interval);
|
|
@@ -8300,7 +8627,7 @@ var prepareTestingData = async (params) => {
|
|
|
8300
8627
|
throw new Error(`Unknown connector: ${connectorName}`);
|
|
8301
8628
|
}
|
|
8302
8629
|
if (!binanceConnector || !coinbaseConnector) {
|
|
8303
|
-
|
|
8630
|
+
import_logger15.logger.warn(
|
|
8304
8631
|
"Binance/Coinbase connectors are unavailable. Reusing %s for BTC references.",
|
|
8305
8632
|
connectorName
|
|
8306
8633
|
);
|
|
@@ -8583,7 +8910,7 @@ var testing = async (test) => {
|
|
|
8583
8910
|
if (!start) {
|
|
8584
8911
|
throw new Error("no start");
|
|
8585
8912
|
}
|
|
8586
|
-
const preloadStart = (0,
|
|
8913
|
+
const preloadStart = (0, import_time3.getBacktestPreloadStart)(start);
|
|
8587
8914
|
const progress = createBacktestProgress({
|
|
8588
8915
|
testName: name,
|
|
8589
8916
|
symbol,
|
|
@@ -8683,7 +9010,7 @@ var testingGroupInSharedCandleLoop = async (tests) => {
|
|
|
8683
9010
|
if (!start) {
|
|
8684
9011
|
throw new Error("no start");
|
|
8685
9012
|
}
|
|
8686
|
-
const preloadStart = (0,
|
|
9013
|
+
const preloadStart = (0, import_time3.getBacktestPreloadStart)(start);
|
|
8687
9014
|
const progress = createBacktestProgress({
|
|
8688
9015
|
testName: first.name,
|
|
8689
9016
|
symbol,
|