@tradejs/cli 2.0.5 → 2.0.7

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.
@@ -34,9 +34,9 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
34
34
  ));
35
35
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
36
36
 
37
- // ../../node_modules/debug/node_modules/ms/index.js
37
+ // ../../node_modules/ms/index.js
38
38
  var require_ms = __commonJS({
39
- "../../node_modules/debug/node_modules/ms/index.js"(exports2, module2) {
39
+ "../../node_modules/ms/index.js"(exports2, module2) {
40
40
  "use strict";
41
41
  var s = 1e3;
42
42
  var m = s * 60;
@@ -151,9 +151,9 @@ var require_ms = __commonJS({
151
151
  }
152
152
  });
153
153
 
154
- // ../../node_modules/debug/src/common.js
154
+ // ../../node_modules/list-it/node_modules/debug/src/common.js
155
155
  var require_common = __commonJS({
156
- "../../node_modules/debug/src/common.js"(exports2, module2) {
156
+ "../../node_modules/list-it/node_modules/debug/src/common.js"(exports2, module2) {
157
157
  "use strict";
158
158
  function setup(env) {
159
159
  createDebug.debug = createDebug;
@@ -255,50 +255,64 @@ var require_common = __commonJS({
255
255
  createDebug.namespaces = namespaces;
256
256
  createDebug.names = [];
257
257
  createDebug.skips = [];
258
- let i;
259
- const split = (typeof namespaces === "string" ? namespaces : "").split(/[\s,]+/);
260
- const len = split.length;
261
- for (i = 0; i < len; i++) {
262
- if (!split[i]) {
263
- continue;
258
+ const split = (typeof namespaces === "string" ? namespaces : "").trim().replace(/\s+/g, ",").split(",").filter(Boolean);
259
+ for (const ns of split) {
260
+ if (ns[0] === "-") {
261
+ createDebug.skips.push(ns.slice(1));
262
+ } else {
263
+ createDebug.names.push(ns);
264
264
  }
265
- namespaces = split[i].replace(/\*/g, ".*?");
266
- if (namespaces[0] === "-") {
267
- createDebug.skips.push(new RegExp("^" + namespaces.slice(1) + "$"));
265
+ }
266
+ }
267
+ function matchesTemplate(search, template) {
268
+ let searchIndex = 0;
269
+ let templateIndex = 0;
270
+ let starIndex = -1;
271
+ let matchIndex = 0;
272
+ while (searchIndex < search.length) {
273
+ if (templateIndex < template.length && (template[templateIndex] === search[searchIndex] || template[templateIndex] === "*")) {
274
+ if (template[templateIndex] === "*") {
275
+ starIndex = templateIndex;
276
+ matchIndex = searchIndex;
277
+ templateIndex++;
278
+ } else {
279
+ searchIndex++;
280
+ templateIndex++;
281
+ }
282
+ } else if (starIndex !== -1) {
283
+ templateIndex = starIndex + 1;
284
+ matchIndex++;
285
+ searchIndex = matchIndex;
268
286
  } else {
269
- createDebug.names.push(new RegExp("^" + namespaces + "$"));
287
+ return false;
270
288
  }
271
289
  }
290
+ while (templateIndex < template.length && template[templateIndex] === "*") {
291
+ templateIndex++;
292
+ }
293
+ return templateIndex === template.length;
272
294
  }
273
295
  function disable() {
274
296
  const namespaces = [
275
- ...createDebug.names.map(toNamespace),
276
- ...createDebug.skips.map(toNamespace).map((namespace) => "-" + namespace)
297
+ ...createDebug.names,
298
+ ...createDebug.skips.map((namespace) => "-" + namespace)
277
299
  ].join(",");
278
300
  createDebug.enable("");
279
301
  return namespaces;
280
302
  }
281
303
  function enabled(name) {
282
- if (name[name.length - 1] === "*") {
283
- return true;
284
- }
285
- let i;
286
- let len;
287
- for (i = 0, len = createDebug.skips.length; i < len; i++) {
288
- if (createDebug.skips[i].test(name)) {
304
+ for (const skip of createDebug.skips) {
305
+ if (matchesTemplate(name, skip)) {
289
306
  return false;
290
307
  }
291
308
  }
292
- for (i = 0, len = createDebug.names.length; i < len; i++) {
293
- if (createDebug.names[i].test(name)) {
309
+ for (const ns of createDebug.names) {
310
+ if (matchesTemplate(name, ns)) {
294
311
  return true;
295
312
  }
296
313
  }
297
314
  return false;
298
315
  }
299
- function toNamespace(regexp) {
300
- return regexp.toString().substring(2, regexp.toString().length - 2).replace(/\.\*\?$/, "*");
301
- }
302
316
  function coerce(val) {
303
317
  if (val instanceof Error) {
304
318
  return val.stack || val.message;
@@ -315,9 +329,9 @@ var require_common = __commonJS({
315
329
  }
316
330
  });
317
331
 
318
- // ../../node_modules/debug/src/browser.js
332
+ // ../../node_modules/list-it/node_modules/debug/src/browser.js
319
333
  var require_browser = __commonJS({
320
- "../../node_modules/debug/src/browser.js"(exports2, module2) {
334
+ "../../node_modules/list-it/node_modules/debug/src/browser.js"(exports2, module2) {
321
335
  "use strict";
322
336
  exports2.formatArgs = formatArgs;
323
337
  exports2.save = save;
@@ -418,10 +432,11 @@ var require_browser = __commonJS({
418
432
  if (typeof navigator !== "undefined" && navigator.userAgent && navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/)) {
419
433
  return false;
420
434
  }
435
+ let m;
421
436
  return typeof document !== "undefined" && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance || // Is firebug? http://stackoverflow.com/a/398120/376773
422
437
  typeof window !== "undefined" && window.console && (window.console.firebug || window.console.exception && window.console.table) || // Is firefox >= v31?
423
438
  // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages
424
- typeof navigator !== "undefined" && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31 || // Double check webkit in userAgent just in case we are in a worker
439
+ typeof navigator !== "undefined" && navigator.userAgent && (m = navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/)) && parseInt(m[1], 10) >= 31 || // Double check webkit in userAgent just in case we are in a worker
425
440
  typeof navigator !== "undefined" && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/);
426
441
  }
427
442
  function formatArgs(args2) {
@@ -459,7 +474,7 @@ var require_browser = __commonJS({
459
474
  function load() {
460
475
  let r;
461
476
  try {
462
- r = exports2.storage.getItem("debug");
477
+ r = exports2.storage.getItem("debug") || exports2.storage.getItem("DEBUG");
463
478
  } catch (error) {
464
479
  }
465
480
  if (!r && typeof process !== "undefined" && "env" in process) {
@@ -600,9 +615,9 @@ var require_supports_color = __commonJS({
600
615
  }
601
616
  });
602
617
 
603
- // ../../node_modules/debug/src/node.js
618
+ // ../../node_modules/list-it/node_modules/debug/src/node.js
604
619
  var require_node = __commonJS({
605
- "../../node_modules/debug/src/node.js"(exports2, module2) {
620
+ "../../node_modules/list-it/node_modules/debug/src/node.js"(exports2, module2) {
606
621
  "use strict";
607
622
  var tty = require("tty");
608
623
  var util = require("util");
@@ -743,7 +758,7 @@ var require_node = __commonJS({
743
758
  return (/* @__PURE__ */ new Date()).toISOString() + " ";
744
759
  }
745
760
  function log(...args2) {
746
- return process.stderr.write(util.format(...args2) + "\n");
761
+ return process.stderr.write(util.formatWithOptions(exports2.inspectOpts, ...args2) + "\n");
747
762
  }
748
763
  function save(namespaces) {
749
764
  if (namespaces) {
@@ -775,9 +790,9 @@ var require_node = __commonJS({
775
790
  }
776
791
  });
777
792
 
778
- // ../../node_modules/debug/src/index.js
793
+ // ../../node_modules/list-it/node_modules/debug/src/index.js
779
794
  var require_src = __commonJS({
780
- "../../node_modules/debug/src/index.js"(exports2, module2) {
795
+ "../../node_modules/list-it/node_modules/debug/src/index.js"(exports2, module2) {
781
796
  "use strict";
782
797
  if (typeof process === "undefined" || process.type === "renderer" || process.browser === true || process.__nwjs) {
783
798
  module2.exports = require_browser();
@@ -1666,6 +1681,7 @@ var import_time3 = require("@tradejs/core/time");
1666
1681
  var import_backtestArtifacts = require("@tradejs/infra/backtestArtifacts");
1667
1682
  var import_redis4 = require("@tradejs/infra/redis");
1668
1683
  var import_tradingAccounts = require("@tradejs/infra/tradingAccounts");
1684
+ var import_types3 = require("@tradejs/types");
1669
1685
 
1670
1686
  // src/lib/runFormatting.ts
1671
1687
  var import_date_fns = require("date-fns");
@@ -1695,6 +1711,7 @@ var import_progress = __toESM(require("progress"));
1695
1711
  var import_chalk = __toESM(require("chalk"));
1696
1712
  var import_connectors = require("@tradejs/connectors");
1697
1713
  var import_connectors2 = require("@tradejs/node/connectors");
1714
+ var import_strategies = require("@tradejs/node/strategies");
1698
1715
  var import_timescale = require("@tradejs/infra/timescale");
1699
1716
 
1700
1717
  // src/lib/binanceMarketData.ts
@@ -1704,14 +1721,6 @@ var MARKET_FEATURE_INTERVAL_MS = {
1704
1721
  "15m": 9e5,
1705
1722
  "1h": 36e5
1706
1723
  };
1707
- var STABLE_QUOTE_SYMBOLS = /* @__PURE__ */ new Set([
1708
- "USDCUSDT",
1709
- "FDUSDUSDT",
1710
- "TUSDUSDT",
1711
- "BUSDUSDT",
1712
- "USDPUSDT",
1713
- "DAIUSDT"
1714
- ]);
1715
1724
  var safeDivide = (num, den) => Number.isFinite(num) && Number.isFinite(den) && den !== 0 ? num / den : null;
1716
1725
  var mean = (values) => values.length ? values.reduce((sum, value) => sum + value, 0) / values.length : null;
1717
1726
  var standardDeviation = (values) => {
@@ -1755,9 +1764,6 @@ var classifyBtcAltRegime = ({
1755
1764
  if (btcVsAltReturn24h < -5e-3) return "alt_lead";
1756
1765
  return "neutral";
1757
1766
  };
1758
- var selectBreadthUniverseFromTickers = (tickers, limit) => tickers.filter(
1759
- (ticker) => ticker.symbol.endsWith("USDT") && ticker.symbol !== "BTCUSDT" && !STABLE_QUOTE_SYMBOLS.has(ticker.symbol) && Number.isFinite(ticker.turnover24h) && ticker.turnover24h > 0
1760
- ).sort((a, b) => b.turnover24h - a.turnover24h).slice(0, Math.max(0, limit)).map((ticker) => ticker.symbol);
1761
1767
  var aggregateAggTradesToRows = ({
1762
1768
  symbol,
1763
1769
  interval: interval2,
@@ -2239,10 +2245,6 @@ var backfillBinanceMarketContext = async (params, enabled) => {
2239
2245
  process.env.BINANCE_MARKET_CONTEXT_TRADE_FLOW_SYMBOL_LIMIT,
2240
2246
  2
2241
2247
  );
2242
- const breadthLimit = asInt(
2243
- process.env.BINANCE_MARKET_CONTEXT_BREADTH_LIMIT,
2244
- 30
2245
- );
2246
2248
  const breadthChunkDays = asFloat(
2247
2249
  process.env.BINANCE_MARKET_CONTEXT_BREADTH_CHUNK_DAYS,
2248
2250
  30
@@ -2365,88 +2367,113 @@ var backfillBinanceMarketContext = async (params, enabled) => {
2365
2367
  }
2366
2368
  }
2367
2369
  if (includeBreadth) {
2368
- const tickers = await connector.getTickers();
2369
- const breadthSymbols = selectBreadthUniverseFromTickers(
2370
- tickers,
2371
- breadthLimit
2372
- );
2373
- const universe = `binance_top${breadthSymbols.length}_usdt`;
2374
- const coverage = await (0, import_timescale.getMarketBreadthCoverage)({
2375
- universe,
2376
- interval: interval2,
2377
- startMs: breadthStartMs,
2378
- endMs
2379
- });
2380
- const hasBtcAltMetrics = (coverage?.btcAltMetricsRows ?? 0) >= Math.floor(Math.max(1, coverage?.rows ?? 0) * 0.9);
2381
- if (!hasCoverage({
2382
- coverage,
2370
+ const breadthUniverses = (0, import_strategies.getBinanceBreadthUniverses)();
2371
+ const breadthSymbols = breadthUniverses.at(-1)?.symbols ?? [];
2372
+ const connectorInterval = marketIntervalToConnectorInterval(interval2);
2373
+ const chunks = buildBreadthBackfillChunks({
2383
2374
  startMs: breadthStartMs,
2384
2375
  endMs,
2385
- intervalMs
2386
- }) || !hasBtcAltMetrics) {
2387
- const connectorInterval = marketIntervalToConnectorInterval(interval2);
2388
- const chunks = buildBreadthBackfillChunks({
2389
- startMs: breadthStartMs,
2390
- endMs,
2391
- intervalMs,
2392
- chunkDays: breadthChunkDays
2393
- });
2394
- const missingChunks = filterMissingBreadthBackfillChunks({
2395
- chunks,
2396
- coverage,
2397
- intervalMs
2398
- });
2399
- const bar = new import_progress.default(
2400
- "breadth :current/:total [:bar][:percent] :etas(s) candles=:candles skip=:skip chunk=:chunk :symbol",
2401
- {
2402
- total: Math.max(1, missingChunks.length * breadthSymbols.length),
2403
- width: 24
2404
- }
2405
- );
2406
- let candlesRead = 0;
2407
- let skippedBreadthChunks = chunks.length - missingChunks.length;
2408
- if (!missingChunks.length) {
2409
- bar.tick(1, {
2410
- candles: 0,
2411
- skip: skippedBreadthChunks,
2412
- chunk: "cached",
2413
- symbol: universe
2376
+ intervalMs,
2377
+ chunkDays: breadthChunkDays
2378
+ });
2379
+ const plans = await Promise.all(
2380
+ breadthUniverses.map(async (definition) => {
2381
+ const coverage = await (0, import_timescale.getMarketBreadthCoverage)({
2382
+ universe: definition.universe,
2383
+ interval: interval2,
2384
+ startMs: breadthStartMs,
2385
+ endMs
2414
2386
  });
2415
- }
2416
- for (let chunkIndex = 0; chunkIndex < missingChunks.length; chunkIndex += 1) {
2417
- const chunk = missingChunks[chunkIndex];
2418
- const originalChunkIndex = chunks.findIndex(
2419
- (item) => item.startMs === chunk.startMs && item.endMs === chunk.endMs
2420
- );
2421
- const candlesBySymbol = {};
2422
- for (const symbol of breadthSymbols) {
2423
- const candles = await connector.kline({
2424
- symbol,
2425
- interval: connectorInterval,
2426
- start: chunk.fetchStartMs,
2427
- end: chunk.endMs,
2428
- silent: true
2429
- });
2430
- candlesBySymbol[symbol] = candles;
2431
- candlesRead += candles.length;
2432
- bar.tick(1, {
2433
- candles: candlesRead,
2434
- skip: skippedBreadthChunks,
2435
- chunk: `${originalChunkIndex + 1}/${chunks.length}`,
2436
- symbol
2437
- });
2387
+ const hasBtcAltMetrics = (coverage?.btcAltMetricsRows ?? 0) >= Math.floor(Math.max(1, coverage?.rows ?? 0) * 0.9);
2388
+ const missingChunks2 = !hasCoverage({
2389
+ coverage,
2390
+ startMs: breadthStartMs,
2391
+ endMs,
2392
+ intervalMs
2393
+ }) || !hasBtcAltMetrics ? !hasBtcAltMetrics ? chunks : filterMissingBreadthBackfillChunks({
2394
+ chunks,
2395
+ coverage,
2396
+ intervalMs
2397
+ }) : [];
2398
+ if (!missingChunks2.length) {
2399
+ console.log(
2400
+ import_chalk.default.gray(`breadth cached: universe=${definition.universe}`)
2401
+ );
2438
2402
  }
2439
- const btcCandles = await connector.kline({
2440
- symbol: "BTCUSDT",
2403
+ return {
2404
+ definition,
2405
+ missingChunkKeys: new Set(
2406
+ missingChunks2.map((chunk) => `${chunk.startMs}:${chunk.endMs}`)
2407
+ )
2408
+ };
2409
+ })
2410
+ );
2411
+ const missingChunks = chunks.filter((chunk) => {
2412
+ const key = `${chunk.startMs}:${chunk.endMs}`;
2413
+ return plans.some((plan) => plan.missingChunkKeys.has(key));
2414
+ });
2415
+ const bar = new import_progress.default(
2416
+ "breadth :current/:total [:bar][:percent] :etas(s) candles=:candles skip=:skip chunk=:chunk :symbol",
2417
+ {
2418
+ total: Math.max(1, missingChunks.length * breadthSymbols.length),
2419
+ width: 24
2420
+ }
2421
+ );
2422
+ let candlesRead = 0;
2423
+ const skippedBreadthChunks = chunks.length - missingChunks.length;
2424
+ if (!missingChunks.length) {
2425
+ bar.tick(1, {
2426
+ candles: 0,
2427
+ skip: skippedBreadthChunks,
2428
+ chunk: "cached",
2429
+ symbol: "top5/top10/top30/top50/top100"
2430
+ });
2431
+ }
2432
+ for (let chunkIndex = 0; chunkIndex < missingChunks.length; chunkIndex += 1) {
2433
+ const chunk = missingChunks[chunkIndex];
2434
+ const originalChunkIndex = chunks.findIndex(
2435
+ (item) => item.startMs === chunk.startMs && item.endMs === chunk.endMs
2436
+ );
2437
+ const candlesBySymbol = {};
2438
+ for (const symbol of breadthSymbols) {
2439
+ const candles = await connector.kline({
2440
+ symbol,
2441
2441
  interval: connectorInterval,
2442
2442
  start: chunk.fetchStartMs,
2443
2443
  end: chunk.endMs,
2444
2444
  silent: true
2445
2445
  });
2446
+ candlesBySymbol[symbol] = candles;
2447
+ candlesRead += candles.length;
2448
+ bar.tick(1, {
2449
+ candles: candlesRead,
2450
+ skip: skippedBreadthChunks,
2451
+ chunk: `${originalChunkIndex + 1}/${chunks.length}`,
2452
+ symbol
2453
+ });
2454
+ }
2455
+ const btcCandles = await connector.kline({
2456
+ symbol: "BTCUSDT",
2457
+ interval: connectorInterval,
2458
+ start: chunk.fetchStartMs,
2459
+ end: chunk.endMs,
2460
+ silent: true
2461
+ });
2462
+ const chunkKey = `${chunk.startMs}:${chunk.endMs}`;
2463
+ for (const plan of plans) {
2464
+ if (!plan.missingChunkKeys.has(chunkKey)) {
2465
+ continue;
2466
+ }
2467
+ const symbolSet = new Set(plan.definition.symbols);
2468
+ const universeCandles = Object.fromEntries(
2469
+ Object.entries(candlesBySymbol).filter(
2470
+ ([symbol]) => symbolSet.has(symbol)
2471
+ )
2472
+ );
2446
2473
  const rows = buildMarketBreadthRows({
2447
- universe,
2474
+ universe: plan.definition.universe,
2448
2475
  interval: interval2,
2449
- candlesBySymbol,
2476
+ candlesBySymbol: universeCandles,
2450
2477
  btcCandles
2451
2478
  }).filter((row) => {
2452
2479
  const ts = row.ts.getTime();
@@ -2455,8 +2482,6 @@ var backfillBinanceMarketContext = async (params, enabled) => {
2455
2482
  await (0, import_timescale.upsertMarketBreadthRows)(rows);
2456
2483
  breadthRows += rows.length;
2457
2484
  }
2458
- } else {
2459
- console.log(import_chalk.default.gray(`breadth cached: universe=${universe}`));
2460
2485
  }
2461
2486
  }
2462
2487
  console.log(
@@ -4651,11 +4676,30 @@ var resolveTimeWindow = ({
4651
4676
  endTime,
4652
4677
  nowMs = Date.now(),
4653
4678
  defaultStartMs,
4654
- defaultEndMs
4679
+ defaultEndMs,
4680
+ closedIntervalMs
4655
4681
  }) => {
4656
4682
  const explicitStart = toEpochMs(startTime);
4657
4683
  const explicitEnd = toEpochMs(endTime);
4658
4684
  const resolvedDays = toPositiveNumber(days);
4685
+ const alignToLastClosedInterval = (window2, preserveDuration) => {
4686
+ if (typeof closedIntervalMs !== "number" || !Number.isFinite(closedIntervalMs) || closedIntervalMs <= 0) {
4687
+ return window2;
4688
+ }
4689
+ const alignedEnd = Math.floor(window2.end / closedIntervalMs) * closedIntervalMs - 1;
4690
+ const delta = window2.end - alignedEnd;
4691
+ const alignedStart = preserveDuration ? window2.start - delta : window2.start;
4692
+ if (alignedStart >= alignedEnd) {
4693
+ throw new Error(
4694
+ `Invalid closed-candle window: start (${alignedStart}) must be less than end (${alignedEnd})`
4695
+ );
4696
+ }
4697
+ return {
4698
+ ...window2,
4699
+ start: Math.trunc(alignedStart),
4700
+ end: Math.trunc(alignedEnd)
4701
+ };
4702
+ };
4659
4703
  if (explicitStart != null || explicitEnd != null) {
4660
4704
  const resolvedStart = explicitStart ?? (resolvedDays != null && explicitEnd != null ? explicitEnd - resolvedDays * ONE_DAY_MS : defaultStartMs);
4661
4705
  const resolvedEnd2 = explicitEnd ?? (resolvedDays != null && explicitStart != null ? explicitStart + resolvedDays * ONE_DAY_MS : defaultEndMs ?? nowMs);
@@ -4664,11 +4708,14 @@ var resolveTimeWindow = ({
4664
4708
  `Invalid time window: startTime (${resolvedStart}) must be less than endTime (${resolvedEnd2})`
4665
4709
  );
4666
4710
  }
4667
- return {
4668
- start: resolvedStart,
4669
- end: resolvedEnd2,
4670
- source: "explicit"
4671
- };
4711
+ return alignToLastClosedInterval(
4712
+ {
4713
+ start: resolvedStart,
4714
+ end: resolvedEnd2,
4715
+ source: "explicit"
4716
+ },
4717
+ explicitStart == null && resolvedDays != null
4718
+ );
4672
4719
  }
4673
4720
  if (resolvedDays != null) {
4674
4721
  const resolvedEnd2 = defaultEndMs ?? nowMs;
@@ -4678,11 +4725,14 @@ var resolveTimeWindow = ({
4678
4725
  `Invalid time window: computed start (${resolvedStart}) must be less than end (${resolvedEnd2})`
4679
4726
  );
4680
4727
  }
4681
- return {
4682
- start: Math.trunc(resolvedStart),
4683
- end: Math.trunc(resolvedEnd2),
4684
- source: "days"
4685
- };
4728
+ return alignToLastClosedInterval(
4729
+ {
4730
+ start: Math.trunc(resolvedStart),
4731
+ end: Math.trunc(resolvedEnd2),
4732
+ source: "days"
4733
+ },
4734
+ true
4735
+ );
4686
4736
  }
4687
4737
  const resolvedEnd = defaultEndMs ?? nowMs;
4688
4738
  if (defaultStartMs >= resolvedEnd) {
@@ -4690,11 +4740,14 @@ var resolveTimeWindow = ({
4690
4740
  `Invalid default time window: start (${defaultStartMs}) must be less than end (${resolvedEnd})`
4691
4741
  );
4692
4742
  }
4693
- return {
4694
- start: Math.trunc(defaultStartMs),
4695
- end: Math.trunc(resolvedEnd),
4696
- source: "default"
4697
- };
4743
+ return alignToLastClosedInterval(
4744
+ {
4745
+ start: Math.trunc(defaultStartMs),
4746
+ end: Math.trunc(resolvedEnd),
4747
+ source: "default"
4748
+ },
4749
+ true
4750
+ );
4698
4751
  };
4699
4752
 
4700
4753
  // src/lib/runtimeStrategyBacktest.ts
@@ -5080,7 +5133,8 @@ var prepareRunEnvironment = async ({
5080
5133
  accountId,
5081
5134
  deploymentId,
5082
5135
  assetClasses,
5083
- deployment
5136
+ deployment,
5137
+ closedIntervalMs
5084
5138
  }) => {
5085
5139
  const connectorName = await resolveRunConnectorName({
5086
5140
  value: connector,
@@ -5143,7 +5197,8 @@ var prepareRunEnvironment = async ({
5143
5197
  startTime,
5144
5198
  endTime,
5145
5199
  defaultStartMs: (0, import_time2.getTimestamp)(import_constants2.BACKTEST_DEFAULT_DAYS),
5146
- defaultEndMs: (0, import_time2.getTimestamp)()
5200
+ defaultEndMs: (0, import_time2.getTimestamp)(),
5201
+ closedIntervalMs
5147
5202
  });
5148
5203
  const preloadStart = (0, import_time2.getBacktestPreloadStart)(
5149
5204
  window2.start,
@@ -5538,8 +5593,12 @@ var markBacktestRunStatus = async ({
5538
5593
  };
5539
5594
 
5540
5595
  // src/lib/backtest/runState.ts
5596
+ var import_types2 = require("@tradejs/types");
5541
5597
  var successTests = 0;
5542
5598
  var errorTests = 0;
5599
+ var warningCounts = {
5600
+ [import_types2.BACKTEST_WARNING_CODES.TAKE_PROFIT_CROSSED_BEFORE_ENTRY]: 0
5601
+ };
5543
5602
  var errorMessages = [];
5544
5603
  var topResults = [];
5545
5604
  var bestTickerResults = /* @__PURE__ */ new Map();
@@ -5586,6 +5645,14 @@ var addResultToAggregate = (aggregate, result) => {
5586
5645
  aggregate.winRateCount += 1;
5587
5646
  }
5588
5647
  };
5648
+ var addResultWarnings = (result) => {
5649
+ for (const code of Object.values(import_types2.BACKTEST_WARNING_CODES)) {
5650
+ const count = Number(result.warningCounts?.[code] ?? 0);
5651
+ if (Number.isFinite(count) && count > 0) {
5652
+ warningCounts[code] = (warningCounts[code] ?? 0) + count;
5653
+ }
5654
+ }
5655
+ };
5589
5656
  var getAggregateAverageProfit = (aggregate) => aggregate.count > 0 ? aggregate.netProfitSum / aggregate.count : 0;
5590
5657
  var getAggregateWinRate = (aggregate) => {
5591
5658
  const closedTrades = aggregate.wins + aggregate.losses;
@@ -5611,6 +5678,7 @@ var markTestsStarted = () => {
5611
5678
  var getRunCounters = () => ({
5612
5679
  successTests,
5613
5680
  errorTests,
5681
+ warningCounts: { ...warningCounts },
5614
5682
  errors: [...errorMessages]
5615
5683
  });
5616
5684
  var getTopResults = () => topResults;
@@ -5624,6 +5692,7 @@ var setBestTickerResultForSymbol = (symbol, result) => {
5624
5692
  };
5625
5693
  var recordResultAggregates = (result) => {
5626
5694
  addResultToAggregate(progressStats, result);
5695
+ addResultWarnings(result);
5627
5696
  const configId = result.test.configId || result.test.name;
5628
5697
  const existing = configResultBuckets.get(configId);
5629
5698
  const bucket = existing ?? {
@@ -5673,6 +5742,9 @@ var setRuntimeCompareContext = ({
5673
5742
  var resetRunState = () => {
5674
5743
  successTests = 0;
5675
5744
  errorTests = 0;
5745
+ for (const code of Object.values(import_types2.BACKTEST_WARNING_CODES)) {
5746
+ warningCounts[code] = 0;
5747
+ }
5676
5748
  errorMessages.length = 0;
5677
5749
  topResults = [];
5678
5750
  bestTickerResults.clear();
@@ -6269,9 +6341,15 @@ var printRunOutro = () => {
6269
6341
  import_chalk9.default.gray(`backtest total: done in ${formatDuration(getRunStartedAt())}`)
6270
6342
  );
6271
6343
  console.log("");
6272
- const { successTests: successTests2, errorTests: errorTests2 } = getRunCounters();
6344
+ const { successTests: successTests2, errorTests: errorTests2, warningCounts: warningCounts2 } = getRunCounters();
6273
6345
  console.log(`${import_chalk9.default.green("SUCCESS TESTS")}: ${successTests2}`);
6274
6346
  console.log(`${import_chalk9.default.red("ERRORS")}: ${errorTests2}`);
6347
+ const takeProfitCrossedWarnings = warningCounts2[import_types3.BACKTEST_WARNING_CODES.TAKE_PROFIT_CROSSED_BEFORE_ENTRY] ?? 0;
6348
+ console.log(
6349
+ import_chalk9.default.yellow(
6350
+ `WARNINGS (${import_types3.BACKTEST_WARNING_CODES.TAKE_PROFIT_CROSSED_BEFORE_ENTRY}): ${takeProfitCrossedWarnings}`
6351
+ )
6352
+ );
6275
6353
  console.log("");
6276
6354
  };
6277
6355