@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/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 import_constants8 = require("@tradejs/core/constants");
47
+ var import_constants9 = require("@tradejs/core/constants");
48
48
  var import_strategies7 = require("@tradejs/core/strategies");
49
- var import_time2 = require("@tradejs/core/time");
50
- var import_logger13 = require("@tradejs/infra/logger");
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 import_aiLanguages = require("@tradejs/core/aiLanguages");
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 [{ HumanMessage, SystemMessage }, model, settings] = await Promise.all([
1646
- import("@langchain/core/messages"),
1647
- getAiModel(options.userName, options.model),
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
- let response;
1671
- try {
1672
- response = await model.invoke(messages);
1673
- } catch (error) {
1674
- throw getAiInvocationError(error);
1675
- }
1676
- const responseContent = normalizeResponseContent(response?.content);
1677
- if (isEmptyResponseContent(responseContent)) {
1678
- throw new Error("AI provider returned an empty chat completion");
1679
- }
1680
- const parsed = parseAIResponse(responseContent);
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
- import_logger12.logger.warn(
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
- import_logger12.logger.warn("Skip connector entry without name from %s", source);
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
- import_logger12.logger.warn(
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
- import_logger12.logger.warn(
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(connectorName, entry.creator);
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
- import_logger12.logger.warn(
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
- import_logger12.logger.warn(
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 import_constants6 = require("@tradejs/core/constants");
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 ?? import_constants6.FEE_PERCENT;
6591
- const takerRate = hasConfiguredFees ? Number(config.TAKER_FEE_RATE) : exchangeFees?.takerRate ?? import_constants6.FEE_PERCENT;
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, import_constants6.BACKTEST_BASE_SLIPPAGE_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
- import_constants6.BACKTEST_SPREAD_SLIPPAGE_MULTIPLIER
6942
+ import_constants7.BACKTEST_SPREAD_SLIPPAGE_MULTIPLIER
6616
6943
  ),
6617
6944
  marketImpactBps: finiteOr(
6618
6945
  config.SLIPPAGE_MARKET_IMPACT_BPS,
6619
- import_constants6.BACKTEST_MARKET_IMPACT_BPS
6946
+ import_constants7.BACKTEST_MARKET_IMPACT_BPS
6620
6947
  ),
6621
6948
  delayRiskMultiplier: finiteOr(
6622
6949
  config.SLIPPAGE_DELAY_RISK_MULTIPLIER,
6623
- import_constants6.BACKTEST_DELAY_RISK_MULTIPLIER
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 import_constants7 = require("@tradejs/core/constants");
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 ?? import_constants7.FEE_PERCENT;
6669
- const takerFeeRate = executionCostModel?.fees.takerRate ?? import_constants7.FEE_PERCENT;
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 = import_constants7.INITIAL_BACKTEST_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 - import_constants7.INITIAL_BACKTEST_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 import_constants8.BACKTEST_EXECUTION_INTERVAL;
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 (!import_constants8.BACKTEST_LOWER_TIMEFRAME_EXECUTION_ENABLED) {
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
- import_logger13.logger.warn(
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, import_time2.getBacktestPreloadStart)(start);
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, import_time2.getBacktestPreloadStart)(start);
9013
+ const preloadStart = (0, import_time3.getBacktestPreloadStart)(start);
8687
9014
  const progress = createBacktestProgress({
8688
9015
  testName: first.name,
8689
9016
  symbol,