@tradejs/cli 2.0.6 → 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.
@@ -520,8 +520,10 @@ var REPLAY_RUNTIME_COMPARISON_HEADERS = [
520
520
  import_chalk.default.yellow("RT TRADES"),
521
521
  import_chalk.default.yellow("RT PNL"),
522
522
  import_chalk.default.green("MATCHED"),
523
+ import_chalk.default.red("ORDER FAILED"),
523
524
  import_chalk.default.yellow("RT ONLY"),
524
- import_chalk.default.magenta("BT ONLY")
525
+ import_chalk.default.magenta("BT ONLY"),
526
+ import_chalk.default.red("UNMATCHED")
525
527
  ];
526
528
  var REPLAY_RESULTS_CONFIG = "replay";
527
529
  var REPLAY_RUNTIME_COMPARE_TOLERANCE_BARS = 1;
@@ -30,6 +30,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/scripts/resolveExportStrategy.ts
31
31
  var resolveExportStrategy_exports = {};
32
32
  __export(resolveExportStrategy_exports, {
33
+ ALL_EXPORT_STRATEGIES: () => ALL_EXPORT_STRATEGIES,
33
34
  resolveExportStrategy: () => resolveExportStrategy
34
35
  });
35
36
  module.exports = __toCommonJS(resolveExportStrategy_exports);
@@ -100,12 +101,14 @@ var selectStrategy = async (promptLabel = "Select strategy", options) => {
100
101
  };
101
102
 
102
103
  // src/scripts/resolveExportStrategy.ts
104
+ var ALL_EXPORT_STRATEGIES = "all";
103
105
  var resolveExportStrategy = async ({
104
106
  explicitStrategy,
105
107
  outDir,
106
108
  datasetLabel,
107
109
  promptLabel,
108
- listStrategies
110
+ listStrategies,
111
+ includeAllOption = false
109
112
  }) => {
110
113
  const raw = String(explicitStrategy || "").trim();
111
114
  if (raw) {
@@ -123,12 +126,14 @@ var resolveExportStrategy = async ({
123
126
  `Multiple ${datasetLabel} chunk strategies found in ${outDir}: ${availableStrategies.join(", ")}. Pass --strategy.`
124
127
  );
125
128
  }
129
+ const promptStrategies = includeAllOption ? [...availableStrategies, ALL_EXPORT_STRATEGIES] : availableStrategies;
126
130
  return selectStrategy(promptLabel, {
127
- strategies: availableStrategies,
131
+ strategies: promptStrategies,
128
132
  defaultStrategy: availableStrategies[0]
129
133
  });
130
134
  };
131
135
  // Annotate the CommonJS export names for ESM import in node:
132
136
  0 && (module.exports = {
137
+ ALL_EXPORT_STRATEGIES,
133
138
  resolveExportStrategy
134
139
  });
@@ -2256,11 +2256,30 @@ var resolveTimeWindow = ({
2256
2256
  endTime,
2257
2257
  nowMs = Date.now(),
2258
2258
  defaultStartMs,
2259
- defaultEndMs
2259
+ defaultEndMs,
2260
+ closedIntervalMs
2260
2261
  }) => {
2261
2262
  const explicitStart = toEpochMs(startTime);
2262
2263
  const explicitEnd = toEpochMs(endTime);
2263
2264
  const resolvedDays = toPositiveNumber(days);
2265
+ const alignToLastClosedInterval = (window2, preserveDuration) => {
2266
+ if (typeof closedIntervalMs !== "number" || !Number.isFinite(closedIntervalMs) || closedIntervalMs <= 0) {
2267
+ return window2;
2268
+ }
2269
+ const alignedEnd = Math.floor(window2.end / closedIntervalMs) * closedIntervalMs - 1;
2270
+ const delta = window2.end - alignedEnd;
2271
+ const alignedStart = preserveDuration ? window2.start - delta : window2.start;
2272
+ if (alignedStart >= alignedEnd) {
2273
+ throw new Error(
2274
+ `Invalid closed-candle window: start (${alignedStart}) must be less than end (${alignedEnd})`
2275
+ );
2276
+ }
2277
+ return {
2278
+ ...window2,
2279
+ start: Math.trunc(alignedStart),
2280
+ end: Math.trunc(alignedEnd)
2281
+ };
2282
+ };
2264
2283
  if (explicitStart != null || explicitEnd != null) {
2265
2284
  const resolvedStart = explicitStart ?? (resolvedDays != null && explicitEnd != null ? explicitEnd - resolvedDays * ONE_DAY_MS : defaultStartMs);
2266
2285
  const resolvedEnd2 = explicitEnd ?? (resolvedDays != null && explicitStart != null ? explicitStart + resolvedDays * ONE_DAY_MS : defaultEndMs ?? nowMs);
@@ -2269,11 +2288,14 @@ var resolveTimeWindow = ({
2269
2288
  `Invalid time window: startTime (${resolvedStart}) must be less than endTime (${resolvedEnd2})`
2270
2289
  );
2271
2290
  }
2272
- return {
2273
- start: resolvedStart,
2274
- end: resolvedEnd2,
2275
- source: "explicit"
2276
- };
2291
+ return alignToLastClosedInterval(
2292
+ {
2293
+ start: resolvedStart,
2294
+ end: resolvedEnd2,
2295
+ source: "explicit"
2296
+ },
2297
+ explicitStart == null && resolvedDays != null
2298
+ );
2277
2299
  }
2278
2300
  if (resolvedDays != null) {
2279
2301
  const resolvedEnd2 = defaultEndMs ?? nowMs;
@@ -2283,11 +2305,14 @@ var resolveTimeWindow = ({
2283
2305
  `Invalid time window: computed start (${resolvedStart}) must be less than end (${resolvedEnd2})`
2284
2306
  );
2285
2307
  }
2286
- return {
2287
- start: Math.trunc(resolvedStart),
2288
- end: Math.trunc(resolvedEnd2),
2289
- source: "days"
2290
- };
2308
+ return alignToLastClosedInterval(
2309
+ {
2310
+ start: Math.trunc(resolvedStart),
2311
+ end: Math.trunc(resolvedEnd2),
2312
+ source: "days"
2313
+ },
2314
+ true
2315
+ );
2291
2316
  }
2292
2317
  const resolvedEnd = defaultEndMs ?? nowMs;
2293
2318
  if (defaultStartMs >= resolvedEnd) {
@@ -2295,11 +2320,14 @@ var resolveTimeWindow = ({
2295
2320
  `Invalid default time window: start (${defaultStartMs}) must be less than end (${resolvedEnd})`
2296
2321
  );
2297
2322
  }
2298
- return {
2299
- start: Math.trunc(defaultStartMs),
2300
- end: Math.trunc(resolvedEnd),
2301
- source: "default"
2302
- };
2323
+ return alignToLastClosedInterval(
2324
+ {
2325
+ start: Math.trunc(defaultStartMs),
2326
+ end: Math.trunc(resolvedEnd),
2327
+ source: "default"
2328
+ },
2329
+ true
2330
+ );
2303
2331
  };
2304
2332
 
2305
2333
  // src/lib/runFormatting.ts
@@ -2329,6 +2357,7 @@ var import_progress = __toESM(require("progress"));
2329
2357
  var import_chalk = __toESM(require("chalk"));
2330
2358
  var import_connectors = require("@tradejs/connectors");
2331
2359
  var import_connectors2 = require("@tradejs/node/connectors");
2360
+ var import_strategies = require("@tradejs/node/strategies");
2332
2361
  var import_timescale = require("@tradejs/infra/timescale");
2333
2362
 
2334
2363
  // src/lib/binanceMarketData.ts
@@ -2338,14 +2367,6 @@ var MARKET_FEATURE_INTERVAL_MS = {
2338
2367
  "15m": 9e5,
2339
2368
  "1h": 36e5
2340
2369
  };
2341
- var STABLE_QUOTE_SYMBOLS = /* @__PURE__ */ new Set([
2342
- "USDCUSDT",
2343
- "FDUSDUSDT",
2344
- "TUSDUSDT",
2345
- "BUSDUSDT",
2346
- "USDPUSDT",
2347
- "DAIUSDT"
2348
- ]);
2349
2370
  var safeDivide = (num, den) => Number.isFinite(num) && Number.isFinite(den) && den !== 0 ? num / den : null;
2350
2371
  var mean = (values) => values.length ? values.reduce((sum, value) => sum + value, 0) / values.length : null;
2351
2372
  var standardDeviation = (values) => {
@@ -2389,9 +2410,6 @@ var classifyBtcAltRegime = ({
2389
2410
  if (btcVsAltReturn24h < -5e-3) return "alt_lead";
2390
2411
  return "neutral";
2391
2412
  };
2392
- var selectBreadthUniverseFromTickers = (tickers, limit) => tickers.filter(
2393
- (ticker) => ticker.symbol.endsWith("USDT") && ticker.symbol !== "BTCUSDT" && !STABLE_QUOTE_SYMBOLS.has(ticker.symbol) && Number.isFinite(ticker.turnover24h) && ticker.turnover24h > 0
2394
- ).sort((a, b) => b.turnover24h - a.turnover24h).slice(0, Math.max(0, limit)).map((ticker) => ticker.symbol);
2395
2413
  var aggregateAggTradesToRows = ({
2396
2414
  symbol,
2397
2415
  interval: interval2,
@@ -2873,10 +2891,6 @@ var backfillBinanceMarketContext = async (params, enabled) => {
2873
2891
  process.env.BINANCE_MARKET_CONTEXT_TRADE_FLOW_SYMBOL_LIMIT,
2874
2892
  2
2875
2893
  );
2876
- const breadthLimit = asInt(
2877
- process.env.BINANCE_MARKET_CONTEXT_BREADTH_LIMIT,
2878
- 30
2879
- );
2880
2894
  const breadthChunkDays = asFloat(
2881
2895
  process.env.BINANCE_MARKET_CONTEXT_BREADTH_CHUNK_DAYS,
2882
2896
  30
@@ -2999,88 +3013,113 @@ var backfillBinanceMarketContext = async (params, enabled) => {
2999
3013
  }
3000
3014
  }
3001
3015
  if (includeBreadth) {
3002
- const tickers = await connector.getTickers();
3003
- const breadthSymbols = selectBreadthUniverseFromTickers(
3004
- tickers,
3005
- breadthLimit
3006
- );
3007
- const universe = `binance_top${breadthSymbols.length}_usdt`;
3008
- const coverage = await (0, import_timescale.getMarketBreadthCoverage)({
3009
- universe,
3010
- interval: interval2,
3011
- startMs: breadthStartMs,
3012
- endMs
3013
- });
3014
- const hasBtcAltMetrics = (coverage?.btcAltMetricsRows ?? 0) >= Math.floor(Math.max(1, coverage?.rows ?? 0) * 0.9);
3015
- if (!hasCoverage({
3016
- coverage,
3016
+ const breadthUniverses = (0, import_strategies.getBinanceBreadthUniverses)();
3017
+ const breadthSymbols = breadthUniverses.at(-1)?.symbols ?? [];
3018
+ const connectorInterval = marketIntervalToConnectorInterval(interval2);
3019
+ const chunks = buildBreadthBackfillChunks({
3017
3020
  startMs: breadthStartMs,
3018
3021
  endMs,
3019
- intervalMs
3020
- }) || !hasBtcAltMetrics) {
3021
- const connectorInterval = marketIntervalToConnectorInterval(interval2);
3022
- const chunks = buildBreadthBackfillChunks({
3023
- startMs: breadthStartMs,
3024
- endMs,
3025
- intervalMs,
3026
- chunkDays: breadthChunkDays
3027
- });
3028
- const missingChunks = filterMissingBreadthBackfillChunks({
3029
- chunks,
3030
- coverage,
3031
- intervalMs
3032
- });
3033
- const bar = new import_progress.default(
3034
- "breadth :current/:total [:bar][:percent] :etas(s) candles=:candles skip=:skip chunk=:chunk :symbol",
3035
- {
3036
- total: Math.max(1, missingChunks.length * breadthSymbols.length),
3037
- width: 24
3038
- }
3039
- );
3040
- let candlesRead = 0;
3041
- let skippedBreadthChunks = chunks.length - missingChunks.length;
3042
- if (!missingChunks.length) {
3043
- bar.tick(1, {
3044
- candles: 0,
3045
- skip: skippedBreadthChunks,
3046
- chunk: "cached",
3047
- symbol: universe
3022
+ intervalMs,
3023
+ chunkDays: breadthChunkDays
3024
+ });
3025
+ const plans = await Promise.all(
3026
+ breadthUniverses.map(async (definition) => {
3027
+ const coverage = await (0, import_timescale.getMarketBreadthCoverage)({
3028
+ universe: definition.universe,
3029
+ interval: interval2,
3030
+ startMs: breadthStartMs,
3031
+ endMs
3048
3032
  });
3049
- }
3050
- for (let chunkIndex = 0; chunkIndex < missingChunks.length; chunkIndex += 1) {
3051
- const chunk = missingChunks[chunkIndex];
3052
- const originalChunkIndex = chunks.findIndex(
3053
- (item) => item.startMs === chunk.startMs && item.endMs === chunk.endMs
3054
- );
3055
- const candlesBySymbol = {};
3056
- for (const symbol of breadthSymbols) {
3057
- const candles = await connector.kline({
3058
- symbol,
3059
- interval: connectorInterval,
3060
- start: chunk.fetchStartMs,
3061
- end: chunk.endMs,
3062
- silent: true
3063
- });
3064
- candlesBySymbol[symbol] = candles;
3065
- candlesRead += candles.length;
3066
- bar.tick(1, {
3067
- candles: candlesRead,
3068
- skip: skippedBreadthChunks,
3069
- chunk: `${originalChunkIndex + 1}/${chunks.length}`,
3070
- symbol
3071
- });
3033
+ const hasBtcAltMetrics = (coverage?.btcAltMetricsRows ?? 0) >= Math.floor(Math.max(1, coverage?.rows ?? 0) * 0.9);
3034
+ const missingChunks2 = !hasCoverage({
3035
+ coverage,
3036
+ startMs: breadthStartMs,
3037
+ endMs,
3038
+ intervalMs
3039
+ }) || !hasBtcAltMetrics ? !hasBtcAltMetrics ? chunks : filterMissingBreadthBackfillChunks({
3040
+ chunks,
3041
+ coverage,
3042
+ intervalMs
3043
+ }) : [];
3044
+ if (!missingChunks2.length) {
3045
+ console.log(
3046
+ import_chalk.default.gray(`breadth cached: universe=${definition.universe}`)
3047
+ );
3072
3048
  }
3073
- const btcCandles = await connector.kline({
3074
- symbol: "BTCUSDT",
3049
+ return {
3050
+ definition,
3051
+ missingChunkKeys: new Set(
3052
+ missingChunks2.map((chunk) => `${chunk.startMs}:${chunk.endMs}`)
3053
+ )
3054
+ };
3055
+ })
3056
+ );
3057
+ const missingChunks = chunks.filter((chunk) => {
3058
+ const key = `${chunk.startMs}:${chunk.endMs}`;
3059
+ return plans.some((plan) => plan.missingChunkKeys.has(key));
3060
+ });
3061
+ const bar = new import_progress.default(
3062
+ "breadth :current/:total [:bar][:percent] :etas(s) candles=:candles skip=:skip chunk=:chunk :symbol",
3063
+ {
3064
+ total: Math.max(1, missingChunks.length * breadthSymbols.length),
3065
+ width: 24
3066
+ }
3067
+ );
3068
+ let candlesRead = 0;
3069
+ const skippedBreadthChunks = chunks.length - missingChunks.length;
3070
+ if (!missingChunks.length) {
3071
+ bar.tick(1, {
3072
+ candles: 0,
3073
+ skip: skippedBreadthChunks,
3074
+ chunk: "cached",
3075
+ symbol: "top5/top10/top30/top50/top100"
3076
+ });
3077
+ }
3078
+ for (let chunkIndex = 0; chunkIndex < missingChunks.length; chunkIndex += 1) {
3079
+ const chunk = missingChunks[chunkIndex];
3080
+ const originalChunkIndex = chunks.findIndex(
3081
+ (item) => item.startMs === chunk.startMs && item.endMs === chunk.endMs
3082
+ );
3083
+ const candlesBySymbol = {};
3084
+ for (const symbol of breadthSymbols) {
3085
+ const candles = await connector.kline({
3086
+ symbol,
3075
3087
  interval: connectorInterval,
3076
3088
  start: chunk.fetchStartMs,
3077
3089
  end: chunk.endMs,
3078
3090
  silent: true
3079
3091
  });
3092
+ candlesBySymbol[symbol] = candles;
3093
+ candlesRead += candles.length;
3094
+ bar.tick(1, {
3095
+ candles: candlesRead,
3096
+ skip: skippedBreadthChunks,
3097
+ chunk: `${originalChunkIndex + 1}/${chunks.length}`,
3098
+ symbol
3099
+ });
3100
+ }
3101
+ const btcCandles = await connector.kline({
3102
+ symbol: "BTCUSDT",
3103
+ interval: connectorInterval,
3104
+ start: chunk.fetchStartMs,
3105
+ end: chunk.endMs,
3106
+ silent: true
3107
+ });
3108
+ const chunkKey = `${chunk.startMs}:${chunk.endMs}`;
3109
+ for (const plan of plans) {
3110
+ if (!plan.missingChunkKeys.has(chunkKey)) {
3111
+ continue;
3112
+ }
3113
+ const symbolSet = new Set(plan.definition.symbols);
3114
+ const universeCandles = Object.fromEntries(
3115
+ Object.entries(candlesBySymbol).filter(
3116
+ ([symbol]) => symbolSet.has(symbol)
3117
+ )
3118
+ );
3080
3119
  const rows = buildMarketBreadthRows({
3081
- universe,
3120
+ universe: plan.definition.universe,
3082
3121
  interval: interval2,
3083
- candlesBySymbol,
3122
+ candlesBySymbol: universeCandles,
3084
3123
  btcCandles
3085
3124
  }).filter((row) => {
3086
3125
  const ts = row.ts.getTime();
@@ -3089,8 +3128,6 @@ var backfillBinanceMarketContext = async (params, enabled) => {
3089
3128
  await (0, import_timescale.upsertMarketBreadthRows)(rows);
3090
3129
  breadthRows += rows.length;
3091
3130
  }
3092
- } else {
3093
- console.log(import_chalk.default.gray(`breadth cached: universe=${universe}`));
3094
3131
  }
3095
3132
  }
3096
3133
  console.log(