@tradejs/cli 2.0.1 → 2.0.3

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.
@@ -2728,6 +2728,38 @@ var resolveDerivativesContextMissingFetchFromMs = (params) => {
2728
2728
  return null;
2729
2729
  };
2730
2730
  var resolveDerivativesContextFetchFromMs = (params) => resolveDerivativesContextMissingFetchFromMs(params) ?? (params.refreshClosedTail ? params.toMs : null);
2731
+ var resolveDerivativesContextRequiredFetchFromMs = (params) => {
2732
+ const dataCoverageFromMs = params.dataCoverageKeyExists ? null : params.mode === "signals" ? params.fromMs : resolveDerivativesContextMissingCoverageFetchFromMs({
2733
+ ranges: params.dataCoverageRanges,
2734
+ fromMs: params.fromMs,
2735
+ toMs: params.toMs,
2736
+ intervalMs: params.intervalMs
2737
+ });
2738
+ const edgesFromMs = dataCoverageFromMs == null ? null : resolveDerivativesContextFetchFromMs({
2739
+ edges: params.edges,
2740
+ fromMs: params.fromMs,
2741
+ toMs: params.toMs,
2742
+ intervalMs: params.intervalMs,
2743
+ refreshClosedTail: params.mode === "signals"
2744
+ });
2745
+ const dataFetchFromMs = dataCoverageFromMs != null && edgesFromMs != null ? Math.max(dataCoverageFromMs, edgesFromMs) : null;
2746
+ const confirmedLiquidationWindow = (0, import_indicators2.resolveCoinalyzeConfirmedIntradayCoverage)({
2747
+ interval: params.interval,
2748
+ fromMs: params.fromMs,
2749
+ toMs: params.toMs,
2750
+ nowMs: params.nowMs
2751
+ });
2752
+ const liquidationFetchFromMs = confirmedLiquidationWindow ? resolveDerivativesContextMissingCoverageFetchFromMs({
2753
+ ranges: params.liquidationCoverageRanges,
2754
+ fromMs: confirmedLiquidationWindow.fromMs,
2755
+ toMs: confirmedLiquidationWindow.toMs,
2756
+ intervalMs: params.intervalMs
2757
+ }) : null;
2758
+ const fetchStarts = [dataFetchFromMs, liquidationFetchFromMs].filter(
2759
+ (value) => value != null
2760
+ );
2761
+ return fetchStarts.length ? Math.min(...fetchStarts) : null;
2762
+ };
2731
2763
  var countBackfillWindows = (params) => {
2732
2764
  let count = 0;
2733
2765
  let cursor = params.fromMs;
@@ -3063,7 +3095,8 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3063
3095
  mode,
3064
3096
  startMs,
3065
3097
  endMs,
3066
- preloadStartMs: params.preloadStartMs
3098
+ preloadStartMs: params.preloadStartMs,
3099
+ nowMs: Date.now()
3067
3100
  });
3068
3101
  if (safeEndMs <= fromMs) {
3069
3102
  return skippedBackfillResult2();
@@ -3097,15 +3130,33 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3097
3130
  );
3098
3131
  const coverageKeysByInterval = /* @__PURE__ */ new Map();
3099
3132
  const coverageRangesByInterval = /* @__PURE__ */ new Map();
3133
+ const liquidationCoverageRangesByInterval = /* @__PURE__ */ new Map();
3134
+ const coverageNowMs = Date.now();
3100
3135
  await Promise.all(
3101
3136
  intervalWindows.map(async (window2) => {
3102
- const coverageRows = await (0, import_timescale2.getDerivativesBackfillCoverage)({
3103
- source: "coinalyze",
3104
- symbols,
3137
+ const confirmedLiquidationWindow = (0, import_indicators2.resolveCoinalyzeConfirmedIntradayCoverage)({
3105
3138
  interval: window2.interval,
3106
3139
  fromMs: window2.fromMs,
3107
- toMs: window2.toMs
3140
+ toMs: window2.toMs,
3141
+ nowMs: coverageNowMs
3108
3142
  });
3143
+ const [coverageRows, liquidationCoverageRows] = await Promise.all([
3144
+ (0, import_timescale2.getDerivativesBackfillCoverage)({
3145
+ source: "coinalyze",
3146
+ symbols,
3147
+ interval: window2.interval,
3148
+ fromMs: window2.fromMs,
3149
+ toMs: window2.toMs
3150
+ }),
3151
+ confirmedLiquidationWindow ? (0, import_timescale2.getDerivativesMetricCoverage)({
3152
+ source: "coinalyze",
3153
+ metric: "liquidation",
3154
+ symbols,
3155
+ interval: window2.interval,
3156
+ fromMs: confirmedLiquidationWindow.fromMs,
3157
+ toMs: confirmedLiquidationWindow.toMs
3158
+ }) : Promise.resolve([])
3159
+ ]);
3109
3160
  coverageKeysByInterval.set(
3110
3161
  window2.interval,
3111
3162
  new Set(
@@ -3127,6 +3178,17 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3127
3178
  rangesBySymbol.set(symbol, ranges);
3128
3179
  }
3129
3180
  coverageRangesByInterval.set(window2.interval, rangesBySymbol);
3181
+ const liquidationRangesBySymbol = /* @__PURE__ */ new Map();
3182
+ for (const row of liquidationCoverageRows) {
3183
+ const symbol = row.symbol.toUpperCase();
3184
+ const ranges = liquidationRangesBySymbol.get(symbol) ?? [];
3185
+ ranges.push({ fromMs: row.fromMs, toMs: row.toMs });
3186
+ liquidationRangesBySymbol.set(symbol, ranges);
3187
+ }
3188
+ liquidationCoverageRangesByInterval.set(
3189
+ window2.interval,
3190
+ liquidationRangesBySymbol
3191
+ );
3130
3192
  })
3131
3193
  );
3132
3194
  const cachedWindows = intervalWindows.reduce(
@@ -3151,7 +3213,7 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3151
3213
  (symbol) => backfillWindows.every((backfillWindow) => {
3152
3214
  const normalizedSymbol = symbol.toUpperCase();
3153
3215
  const coverageRanges = coverageRangesByInterval.get(window2.interval)?.get(normalizedSymbol) ?? [];
3154
- return hasDerivativesWindowCoverage({
3216
+ const dataCovered = hasDerivativesWindowCoverage({
3155
3217
  edges: edgesBySymbol?.get(normalizedSymbol),
3156
3218
  fromMs: backfillWindow.fromMs,
3157
3219
  toMs: backfillWindow.toMs
@@ -3168,6 +3230,19 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3168
3230
  toMs: backfillWindow.toMs
3169
3231
  })
3170
3232
  );
3233
+ const confirmedLiquidationWindow = (0, import_indicators2.resolveCoinalyzeConfirmedIntradayCoverage)({
3234
+ interval: window2.interval,
3235
+ fromMs: backfillWindow.fromMs,
3236
+ toMs: backfillWindow.toMs,
3237
+ nowMs: coverageNowMs
3238
+ });
3239
+ const liquidationCovered = confirmedLiquidationWindow == null || resolveDerivativesContextMissingCoverageFetchFromMs({
3240
+ ranges: liquidationCoverageRangesByInterval.get(window2.interval)?.get(normalizedSymbol) ?? [],
3241
+ fromMs: confirmedLiquidationWindow.fromMs,
3242
+ toMs: confirmedLiquidationWindow.toMs,
3243
+ intervalMs: window2.intervalMs
3244
+ }) == null;
3245
+ return dataCovered && liquidationCovered;
3171
3246
  })
3172
3247
  );
3173
3248
  });
@@ -3252,6 +3327,11 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3252
3327
  coverageKeysByInterval.set(interval2, coverageKeys);
3253
3328
  const coverageRangesBySymbol = coverageRangesByInterval.get(interval2) ?? /* @__PURE__ */ new Map();
3254
3329
  coverageRangesByInterval.set(interval2, coverageRangesBySymbol);
3330
+ const liquidationCoverageRangesBySymbol = liquidationCoverageRangesByInterval.get(interval2) ?? /* @__PURE__ */ new Map();
3331
+ liquidationCoverageRangesByInterval.set(
3332
+ interval2,
3333
+ liquidationCoverageRangesBySymbol
3334
+ );
3255
3335
  for (let batchIdx = 0; batchIdx < symbolBatches.length; batchIdx += 1) {
3256
3336
  const batch = symbolBatches[batchIdx];
3257
3337
  let cursor = window2.fromMs;
@@ -3264,33 +3344,25 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3264
3344
  fromMs: cursor,
3265
3345
  toMs
3266
3346
  });
3267
- if (coverageKeys.has(key)) {
3268
- return null;
3269
- }
3270
3347
  const normalizedSymbol = item.symbol.toUpperCase();
3271
- const coverageFromMs = mode === "signals" ? cursor : resolveDerivativesContextMissingCoverageFetchFromMs({
3272
- ranges: coverageRangesBySymbol.get(normalizedSymbol),
3273
- fromMs: cursor,
3274
- toMs,
3275
- intervalMs: intervalMs2
3276
- });
3277
- if (coverageFromMs == null) {
3278
- return null;
3279
- }
3280
- const edges = edgesBySymbol.get(normalizedSymbol);
3281
- const edgesFromMs = resolveDerivativesContextFetchFromMs({
3282
- edges,
3283
- fromMs: cursor,
3284
- toMs,
3285
- intervalMs: intervalMs2,
3286
- refreshClosedTail: mode === "signals"
3287
- });
3288
- if (edgesFromMs == null) {
3289
- return null;
3290
- }
3348
+ const requiredFromMs = resolveDerivativesContextRequiredFetchFromMs(
3349
+ {
3350
+ mode,
3351
+ interval: interval2,
3352
+ intervalMs: intervalMs2,
3353
+ fromMs: cursor,
3354
+ toMs,
3355
+ nowMs: coverageNowMs,
3356
+ dataCoverageKeyExists: coverageKeys.has(key),
3357
+ dataCoverageRanges: coverageRangesBySymbol.get(normalizedSymbol),
3358
+ liquidationCoverageRanges: liquidationCoverageRangesBySymbol.get(normalizedSymbol),
3359
+ edges: edgesBySymbol.get(normalizedSymbol)
3360
+ }
3361
+ );
3362
+ if (requiredFromMs == null) return null;
3291
3363
  return {
3292
3364
  item,
3293
- fromMs: Math.max(coverageFromMs, edgesFromMs)
3365
+ fromMs: requiredFromMs
3294
3366
  };
3295
3367
  }).filter(
3296
3368
  (item) => item != null
@@ -3315,6 +3387,7 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3315
3387
  );
3316
3388
  let rows = [];
3317
3389
  let missingClosedSymbols = [];
3390
+ let liquidationRowsByMarket = /* @__PURE__ */ new Map();
3318
3391
  for (let attempt = 1; attempt <= maxClosedBarAttempts; attempt += 1) {
3319
3392
  const oiMap = await fetchMetricBatch({
3320
3393
  endpoint: oiPath,
@@ -3334,7 +3407,7 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3334
3407
  fromMs: group.fromMs,
3335
3408
  toMs
3336
3409
  });
3337
- const liqMap = await fetchMetricBatch({
3410
+ liquidationRowsByMarket = await fetchMetricBatch({
3338
3411
  endpoint: liqPath,
3339
3412
  metric: "liq",
3340
3413
  marketSymbols,
@@ -3349,7 +3422,7 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3349
3422
  symbol: item.symbol,
3350
3423
  oiRaw: oiMap.get(marketSymbol) ?? [],
3351
3424
  fundingRaw: fundingMap.get(marketSymbol) ?? [],
3352
- liqRaw: liqMap.get(marketSymbol) ?? []
3425
+ liqRaw: liquidationRowsByMarket.get(marketSymbol) ?? []
3353
3426
  });
3354
3427
  return (0, import_indicators2.coinalyzePointsToRows)(points, interval2, "coinalyze");
3355
3428
  });
@@ -3372,6 +3445,42 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3372
3445
  await (0, import_timescale2.upsertDerivatives)(rows);
3373
3446
  totalRows += rows.length;
3374
3447
  }
3448
+ const confirmedLiquidationWindow = (0, import_indicators2.resolveCoinalyzeConfirmedIntradayCoverage)({
3449
+ interval: interval2,
3450
+ fromMs: group.fromMs,
3451
+ toMs,
3452
+ nowMs: coverageNowMs
3453
+ });
3454
+ if (confirmedLiquidationWindow) {
3455
+ const metricCoverageRows = missingBatch.map((item) => {
3456
+ const marketSymbol = item.marketSymbol.toUpperCase();
3457
+ const eventRowsCount = (liquidationRowsByMarket.get(marketSymbol) ?? []).filter((point) => {
3458
+ const timestamp = (0, import_indicators2.toCoinalyzeTimestampMs)(
3459
+ point.t ?? point.ts ?? point.time ?? point.timestamp
3460
+ );
3461
+ return timestamp != null && timestamp >= confirmedLiquidationWindow.fromMs && timestamp <= confirmedLiquidationWindow.toMs;
3462
+ }).length;
3463
+ return {
3464
+ source: "coinalyze",
3465
+ metric: "liquidation",
3466
+ symbol: item.symbol,
3467
+ interval: interval2,
3468
+ fromMs: confirmedLiquidationWindow.fromMs,
3469
+ toMs: confirmedLiquidationWindow.toMs,
3470
+ eventRowsCount
3471
+ };
3472
+ });
3473
+ await (0, import_timescale2.applyDerivativesMetricCoverage)(metricCoverageRows);
3474
+ for (const coverageRow of metricCoverageRows) {
3475
+ const symbol = coverageRow.symbol.toUpperCase();
3476
+ const ranges = liquidationCoverageRangesBySymbol.get(symbol) ?? [];
3477
+ ranges.push({
3478
+ fromMs: coverageRow.fromMs,
3479
+ toMs: coverageRow.toMs
3480
+ });
3481
+ liquidationCoverageRangesBySymbol.set(symbol, ranges);
3482
+ }
3483
+ }
3375
3484
  if (mode === "backtest") {
3376
3485
  const rowsCountBySymbol = /* @__PURE__ */ new Map();
3377
3486
  for (const row of rows) {
@@ -2726,6 +2726,38 @@ var resolveDerivativesContextMissingFetchFromMs = (params) => {
2726
2726
  return null;
2727
2727
  };
2728
2728
  var resolveDerivativesContextFetchFromMs = (params) => resolveDerivativesContextMissingFetchFromMs(params) ?? (params.refreshClosedTail ? params.toMs : null);
2729
+ var resolveDerivativesContextRequiredFetchFromMs = (params) => {
2730
+ const dataCoverageFromMs = params.dataCoverageKeyExists ? null : params.mode === "signals" ? params.fromMs : resolveDerivativesContextMissingCoverageFetchFromMs({
2731
+ ranges: params.dataCoverageRanges,
2732
+ fromMs: params.fromMs,
2733
+ toMs: params.toMs,
2734
+ intervalMs: params.intervalMs
2735
+ });
2736
+ const edgesFromMs = dataCoverageFromMs == null ? null : resolveDerivativesContextFetchFromMs({
2737
+ edges: params.edges,
2738
+ fromMs: params.fromMs,
2739
+ toMs: params.toMs,
2740
+ intervalMs: params.intervalMs,
2741
+ refreshClosedTail: params.mode === "signals"
2742
+ });
2743
+ const dataFetchFromMs = dataCoverageFromMs != null && edgesFromMs != null ? Math.max(dataCoverageFromMs, edgesFromMs) : null;
2744
+ const confirmedLiquidationWindow = (0, import_indicators2.resolveCoinalyzeConfirmedIntradayCoverage)({
2745
+ interval: params.interval,
2746
+ fromMs: params.fromMs,
2747
+ toMs: params.toMs,
2748
+ nowMs: params.nowMs
2749
+ });
2750
+ const liquidationFetchFromMs = confirmedLiquidationWindow ? resolveDerivativesContextMissingCoverageFetchFromMs({
2751
+ ranges: params.liquidationCoverageRanges,
2752
+ fromMs: confirmedLiquidationWindow.fromMs,
2753
+ toMs: confirmedLiquidationWindow.toMs,
2754
+ intervalMs: params.intervalMs
2755
+ }) : null;
2756
+ const fetchStarts = [dataFetchFromMs, liquidationFetchFromMs].filter(
2757
+ (value) => value != null
2758
+ );
2759
+ return fetchStarts.length ? Math.min(...fetchStarts) : null;
2760
+ };
2729
2761
  var countBackfillWindows = (params) => {
2730
2762
  let count = 0;
2731
2763
  let cursor = params.fromMs;
@@ -3061,7 +3093,8 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3061
3093
  mode,
3062
3094
  startMs,
3063
3095
  endMs,
3064
- preloadStartMs: params.preloadStartMs
3096
+ preloadStartMs: params.preloadStartMs,
3097
+ nowMs: Date.now()
3065
3098
  });
3066
3099
  if (safeEndMs <= fromMs) {
3067
3100
  return skippedBackfillResult2();
@@ -3095,15 +3128,33 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3095
3128
  );
3096
3129
  const coverageKeysByInterval = /* @__PURE__ */ new Map();
3097
3130
  const coverageRangesByInterval = /* @__PURE__ */ new Map();
3131
+ const liquidationCoverageRangesByInterval = /* @__PURE__ */ new Map();
3132
+ const coverageNowMs = Date.now();
3098
3133
  await Promise.all(
3099
3134
  intervalWindows.map(async (window2) => {
3100
- const coverageRows = await (0, import_timescale2.getDerivativesBackfillCoverage)({
3101
- source: "coinalyze",
3102
- symbols,
3135
+ const confirmedLiquidationWindow = (0, import_indicators2.resolveCoinalyzeConfirmedIntradayCoverage)({
3103
3136
  interval: window2.interval,
3104
3137
  fromMs: window2.fromMs,
3105
- toMs: window2.toMs
3138
+ toMs: window2.toMs,
3139
+ nowMs: coverageNowMs
3106
3140
  });
3141
+ const [coverageRows, liquidationCoverageRows] = await Promise.all([
3142
+ (0, import_timescale2.getDerivativesBackfillCoverage)({
3143
+ source: "coinalyze",
3144
+ symbols,
3145
+ interval: window2.interval,
3146
+ fromMs: window2.fromMs,
3147
+ toMs: window2.toMs
3148
+ }),
3149
+ confirmedLiquidationWindow ? (0, import_timescale2.getDerivativesMetricCoverage)({
3150
+ source: "coinalyze",
3151
+ metric: "liquidation",
3152
+ symbols,
3153
+ interval: window2.interval,
3154
+ fromMs: confirmedLiquidationWindow.fromMs,
3155
+ toMs: confirmedLiquidationWindow.toMs
3156
+ }) : Promise.resolve([])
3157
+ ]);
3107
3158
  coverageKeysByInterval.set(
3108
3159
  window2.interval,
3109
3160
  new Set(
@@ -3125,6 +3176,17 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3125
3176
  rangesBySymbol.set(symbol, ranges);
3126
3177
  }
3127
3178
  coverageRangesByInterval.set(window2.interval, rangesBySymbol);
3179
+ const liquidationRangesBySymbol = /* @__PURE__ */ new Map();
3180
+ for (const row of liquidationCoverageRows) {
3181
+ const symbol = row.symbol.toUpperCase();
3182
+ const ranges = liquidationRangesBySymbol.get(symbol) ?? [];
3183
+ ranges.push({ fromMs: row.fromMs, toMs: row.toMs });
3184
+ liquidationRangesBySymbol.set(symbol, ranges);
3185
+ }
3186
+ liquidationCoverageRangesByInterval.set(
3187
+ window2.interval,
3188
+ liquidationRangesBySymbol
3189
+ );
3128
3190
  })
3129
3191
  );
3130
3192
  const cachedWindows = intervalWindows.reduce(
@@ -3149,7 +3211,7 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3149
3211
  (symbol) => backfillWindows.every((backfillWindow) => {
3150
3212
  const normalizedSymbol = symbol.toUpperCase();
3151
3213
  const coverageRanges = coverageRangesByInterval.get(window2.interval)?.get(normalizedSymbol) ?? [];
3152
- return hasDerivativesWindowCoverage({
3214
+ const dataCovered = hasDerivativesWindowCoverage({
3153
3215
  edges: edgesBySymbol?.get(normalizedSymbol),
3154
3216
  fromMs: backfillWindow.fromMs,
3155
3217
  toMs: backfillWindow.toMs
@@ -3166,6 +3228,19 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3166
3228
  toMs: backfillWindow.toMs
3167
3229
  })
3168
3230
  );
3231
+ const confirmedLiquidationWindow = (0, import_indicators2.resolveCoinalyzeConfirmedIntradayCoverage)({
3232
+ interval: window2.interval,
3233
+ fromMs: backfillWindow.fromMs,
3234
+ toMs: backfillWindow.toMs,
3235
+ nowMs: coverageNowMs
3236
+ });
3237
+ const liquidationCovered = confirmedLiquidationWindow == null || resolveDerivativesContextMissingCoverageFetchFromMs({
3238
+ ranges: liquidationCoverageRangesByInterval.get(window2.interval)?.get(normalizedSymbol) ?? [],
3239
+ fromMs: confirmedLiquidationWindow.fromMs,
3240
+ toMs: confirmedLiquidationWindow.toMs,
3241
+ intervalMs: window2.intervalMs
3242
+ }) == null;
3243
+ return dataCovered && liquidationCovered;
3169
3244
  })
3170
3245
  );
3171
3246
  });
@@ -3250,6 +3325,11 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3250
3325
  coverageKeysByInterval.set(interval2, coverageKeys);
3251
3326
  const coverageRangesBySymbol = coverageRangesByInterval.get(interval2) ?? /* @__PURE__ */ new Map();
3252
3327
  coverageRangesByInterval.set(interval2, coverageRangesBySymbol);
3328
+ const liquidationCoverageRangesBySymbol = liquidationCoverageRangesByInterval.get(interval2) ?? /* @__PURE__ */ new Map();
3329
+ liquidationCoverageRangesByInterval.set(
3330
+ interval2,
3331
+ liquidationCoverageRangesBySymbol
3332
+ );
3253
3333
  for (let batchIdx = 0; batchIdx < symbolBatches.length; batchIdx += 1) {
3254
3334
  const batch = symbolBatches[batchIdx];
3255
3335
  let cursor = window2.fromMs;
@@ -3262,33 +3342,25 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3262
3342
  fromMs: cursor,
3263
3343
  toMs
3264
3344
  });
3265
- if (coverageKeys.has(key)) {
3266
- return null;
3267
- }
3268
3345
  const normalizedSymbol = item.symbol.toUpperCase();
3269
- const coverageFromMs = mode === "signals" ? cursor : resolveDerivativesContextMissingCoverageFetchFromMs({
3270
- ranges: coverageRangesBySymbol.get(normalizedSymbol),
3271
- fromMs: cursor,
3272
- toMs,
3273
- intervalMs: intervalMs2
3274
- });
3275
- if (coverageFromMs == null) {
3276
- return null;
3277
- }
3278
- const edges = edgesBySymbol.get(normalizedSymbol);
3279
- const edgesFromMs = resolveDerivativesContextFetchFromMs({
3280
- edges,
3281
- fromMs: cursor,
3282
- toMs,
3283
- intervalMs: intervalMs2,
3284
- refreshClosedTail: mode === "signals"
3285
- });
3286
- if (edgesFromMs == null) {
3287
- return null;
3288
- }
3346
+ const requiredFromMs = resolveDerivativesContextRequiredFetchFromMs(
3347
+ {
3348
+ mode,
3349
+ interval: interval2,
3350
+ intervalMs: intervalMs2,
3351
+ fromMs: cursor,
3352
+ toMs,
3353
+ nowMs: coverageNowMs,
3354
+ dataCoverageKeyExists: coverageKeys.has(key),
3355
+ dataCoverageRanges: coverageRangesBySymbol.get(normalizedSymbol),
3356
+ liquidationCoverageRanges: liquidationCoverageRangesBySymbol.get(normalizedSymbol),
3357
+ edges: edgesBySymbol.get(normalizedSymbol)
3358
+ }
3359
+ );
3360
+ if (requiredFromMs == null) return null;
3289
3361
  return {
3290
3362
  item,
3291
- fromMs: Math.max(coverageFromMs, edgesFromMs)
3363
+ fromMs: requiredFromMs
3292
3364
  };
3293
3365
  }).filter(
3294
3366
  (item) => item != null
@@ -3313,6 +3385,7 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3313
3385
  );
3314
3386
  let rows = [];
3315
3387
  let missingClosedSymbols = [];
3388
+ let liquidationRowsByMarket = /* @__PURE__ */ new Map();
3316
3389
  for (let attempt = 1; attempt <= maxClosedBarAttempts; attempt += 1) {
3317
3390
  const oiMap = await fetchMetricBatch({
3318
3391
  endpoint: oiPath,
@@ -3332,7 +3405,7 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3332
3405
  fromMs: group.fromMs,
3333
3406
  toMs
3334
3407
  });
3335
- const liqMap = await fetchMetricBatch({
3408
+ liquidationRowsByMarket = await fetchMetricBatch({
3336
3409
  endpoint: liqPath,
3337
3410
  metric: "liq",
3338
3411
  marketSymbols,
@@ -3347,7 +3420,7 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3347
3420
  symbol: item.symbol,
3348
3421
  oiRaw: oiMap.get(marketSymbol) ?? [],
3349
3422
  fundingRaw: fundingMap.get(marketSymbol) ?? [],
3350
- liqRaw: liqMap.get(marketSymbol) ?? []
3423
+ liqRaw: liquidationRowsByMarket.get(marketSymbol) ?? []
3351
3424
  });
3352
3425
  return (0, import_indicators2.coinalyzePointsToRows)(points, interval2, "coinalyze");
3353
3426
  });
@@ -3370,6 +3443,42 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
3370
3443
  await (0, import_timescale2.upsertDerivatives)(rows);
3371
3444
  totalRows += rows.length;
3372
3445
  }
3446
+ const confirmedLiquidationWindow = (0, import_indicators2.resolveCoinalyzeConfirmedIntradayCoverage)({
3447
+ interval: interval2,
3448
+ fromMs: group.fromMs,
3449
+ toMs,
3450
+ nowMs: coverageNowMs
3451
+ });
3452
+ if (confirmedLiquidationWindow) {
3453
+ const metricCoverageRows = missingBatch.map((item) => {
3454
+ const marketSymbol = item.marketSymbol.toUpperCase();
3455
+ const eventRowsCount = (liquidationRowsByMarket.get(marketSymbol) ?? []).filter((point) => {
3456
+ const timestamp = (0, import_indicators2.toCoinalyzeTimestampMs)(
3457
+ point.t ?? point.ts ?? point.time ?? point.timestamp
3458
+ );
3459
+ return timestamp != null && timestamp >= confirmedLiquidationWindow.fromMs && timestamp <= confirmedLiquidationWindow.toMs;
3460
+ }).length;
3461
+ return {
3462
+ source: "coinalyze",
3463
+ metric: "liquidation",
3464
+ symbol: item.symbol,
3465
+ interval: interval2,
3466
+ fromMs: confirmedLiquidationWindow.fromMs,
3467
+ toMs: confirmedLiquidationWindow.toMs,
3468
+ eventRowsCount
3469
+ };
3470
+ });
3471
+ await (0, import_timescale2.applyDerivativesMetricCoverage)(metricCoverageRows);
3472
+ for (const coverageRow of metricCoverageRows) {
3473
+ const symbol = coverageRow.symbol.toUpperCase();
3474
+ const ranges = liquidationCoverageRangesBySymbol.get(symbol) ?? [];
3475
+ ranges.push({
3476
+ fromMs: coverageRow.fromMs,
3477
+ toMs: coverageRow.toMs
3478
+ });
3479
+ liquidationCoverageRangesBySymbol.set(symbol, ranges);
3480
+ }
3481
+ }
3373
3482
  if (mode === "backtest") {
3374
3483
  const rowsCountBySymbol = /* @__PURE__ */ new Map();
3375
3484
  for (const row of rows) {
@@ -113,7 +113,9 @@ var import_constants = require("@tradejs/core/constants");
113
113
  var import_time2 = require("@tradejs/core/time");
114
114
  var import_redis2 = require("@tradejs/infra/redis");
115
115
  var CLOSED_PNL_LIMIT = 100;
116
+ var CLOSED_PNL_RECONCILIATION_LOOKBACK_MS = 24 * 60 * 60 * 1e3;
116
117
  var EXCHANGE_HISTORY_MAX_RANGE_MS = 7 * 24 * 60 * 60 * 1e3;
118
+ var isRuntimeTradeSyncFallbackClose = (trade) => trade.status === "closed" && trade.exitTimestamp === trade.lastSyncedAt && trade.exitPrice == null && trade.actualExitPrice == null && trade.closeFee == null && trade.fundingFee == null;
117
119
  var splitExchangeHistoryTimeRange = ({
118
120
  startTime,
119
121
  endTime,
@@ -242,7 +244,7 @@ var syncRuntimeTrades = async ({
242
244
  );
243
245
  const closedPnlRows = await loadClosedPnlRows({
244
246
  connector,
245
- startTime,
247
+ startTime: Math.max(0, startTime - CLOSED_PNL_RECONCILIATION_LOOKBACK_MS),
246
248
  endTime,
247
249
  callbacks: closedPnlCallbacks
248
250
  });
@@ -254,14 +256,14 @@ var syncRuntimeTrades = async ({
254
256
  }
255
257
  const syncedTrades = [];
256
258
  for (const trade of trades) {
257
- if (trade.status !== "active") {
259
+ if (trade.status !== "active" && !isRuntimeTradeSyncFallbackClose(trade)) {
258
260
  syncedTrades.push(trade);
259
261
  continue;
260
262
  }
261
263
  const openPosition = openPositionsBySymbol.get(trade.symbol);
262
264
  const activeOrderId = activeOrderIdBySymbol.get(trade.symbol);
263
265
  const isCurrentActiveTrade = activeOrderId === trade.orderId;
264
- if (!openPositionsReliable) {
266
+ if (trade.status === "active" && !openPositionsReliable) {
265
267
  syncedTrades.push({
266
268
  ...trade,
267
269
  status: "active",
@@ -269,7 +271,7 @@ var syncRuntimeTrades = async ({
269
271
  });
270
272
  continue;
271
273
  }
272
- if (isCurrentActiveTrade && openPosition && openPosition.direction === trade.direction) {
274
+ if (trade.status === "active" && isCurrentActiveTrade && openPosition && openPosition.direction === trade.direction) {
273
275
  const nextTrade2 = {
274
276
  ...trade,
275
277
  status: "active",
@@ -297,21 +299,24 @@ var syncRuntimeTrades = async ({
297
299
  continue;
298
300
  }
299
301
  const matchedClosedPnl = consumeClosedPnlMatch(closedPnlBuckets, trade);
302
+ if (!matchedClosedPnl) {
303
+ continue;
304
+ }
300
305
  const nextTrade = {
301
306
  ...trade,
302
307
  status: "closed",
303
- currentPrice: matchedClosedPnl?.exitPrice ?? trade.currentPrice ?? null,
304
- currentPnl: matchedClosedPnl?.closedPnl ?? trade.closedPnl ?? trade.currentPnl ?? null,
305
- closedPnl: matchedClosedPnl?.closedPnl ?? trade.closedPnl ?? trade.currentPnl ?? null,
306
- actualEntryPrice: matchedClosedPnl?.entryPrice ?? trade.actualEntryPrice ?? null,
307
- exitPrice: matchedClosedPnl?.exitPrice ?? trade.exitPrice ?? null,
308
- actualExitPrice: matchedClosedPnl?.exitPrice ?? trade.actualExitPrice ?? null,
309
- exitTimestamp: matchedClosedPnl?.closedAt ?? trade.exitTimestamp ?? endTime,
308
+ currentPrice: matchedClosedPnl.exitPrice ?? trade.currentPrice ?? null,
309
+ currentPnl: matchedClosedPnl.closedPnl,
310
+ closedPnl: matchedClosedPnl.closedPnl,
311
+ actualEntryPrice: matchedClosedPnl.entryPrice ?? trade.actualEntryPrice ?? null,
312
+ exitPrice: matchedClosedPnl.exitPrice ?? trade.exitPrice ?? null,
313
+ actualExitPrice: matchedClosedPnl.exitPrice ?? trade.actualExitPrice ?? null,
314
+ exitTimestamp: matchedClosedPnl.closedAt,
310
315
  exitType: trade.exitType ?? null,
311
- openFee: matchedClosedPnl?.openFee ?? trade.openFee ?? null,
312
- closeFee: matchedClosedPnl?.closeFee ?? trade.closeFee ?? null,
313
- fundingFee: matchedClosedPnl?.fundingFee ?? trade.fundingFee ?? null,
314
- totalFee: matchedClosedPnl?.totalFee ?? trade.totalFee ?? null,
316
+ openFee: matchedClosedPnl.openFee ?? trade.openFee ?? null,
317
+ closeFee: matchedClosedPnl.closeFee ?? trade.closeFee ?? null,
318
+ fundingFee: matchedClosedPnl.fundingFee ?? trade.fundingFee ?? null,
319
+ totalFee: matchedClosedPnl.totalFee ?? trade.totalFee ?? null,
315
320
  lastSyncedAt: endTime
316
321
  };
317
322
  await Promise.all([
@@ -904,8 +909,7 @@ var isRuntimeTradeClosedInWindow = (trade, startTime, endTime) => {
904
909
  if (trade.status !== "closed" || !isTimestampInWindow(trade.exitTimestamp, startTime, endTime)) {
905
910
  return false;
906
911
  }
907
- const looksLikeSyncFallbackClose = trade.exitTimestamp === trade.lastSyncedAt && trade.exitPrice == null && trade.actualExitPrice == null && trade.closeFee == null && trade.fundingFee == null && trade.totalFee == null;
908
- return !looksLikeSyncFallbackClose;
912
+ return !isRuntimeTradeSyncFallbackClose(trade);
909
913
  };
910
914
  var buildSummaryMessages = ({
911
915
  hours,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tradejs/cli",
3
- "version": "2.0.1",
3
+ "version": "2.0.3",
4
4
  "description": "Official CLI for the TradeJS TypeScript framework: infra setup, backtests, signals, bots, and ML workflows.",
5
5
  "keywords": [
6
6
  "tradejs",
@@ -29,14 +29,14 @@
29
29
  "tradejs": "dist/cli.js"
30
30
  },
31
31
  "dependencies": {
32
- "@tradejs/base": "^2.0.1",
33
- "@tradejs/connectors": "^2.0.1",
34
- "@tradejs/core": "^2.0.1",
35
- "@tradejs/indicators": "^2.0.1",
36
- "@tradejs/infra": "^2.0.1",
37
- "@tradejs/node": "^2.0.1",
38
- "@tradejs/strategies": "^2.0.1",
39
- "@tradejs/types": "^2.0.1",
32
+ "@tradejs/base": "^2.0.3",
33
+ "@tradejs/connectors": "^2.0.3",
34
+ "@tradejs/core": "^2.0.3",
35
+ "@tradejs/indicators": "^2.0.3",
36
+ "@tradejs/infra": "^2.0.3",
37
+ "@tradejs/node": "^2.0.3",
38
+ "@tradejs/strategies": "^2.0.3",
39
+ "@tradejs/types": "^2.0.3",
40
40
  "args": "^5.0.3",
41
41
  "bcryptjs": "^2.4.3",
42
42
  "chalk": "4.1.2",