@tradejs/cli 1.0.12 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,96 @@
1
+ Business Source License 1.1
2
+
3
+ Parameters
4
+
5
+ Licensor: TradeJS-Dev
6
+
7
+ Licensed Work: The software contained in this package, version 2.0.0 and later.
8
+
9
+ Additional Use Grant: You may make production use of the Licensed Work,
10
+ provided that you do not use the Licensed Work to offer a Competing Offering
11
+ to third parties.
12
+
13
+ A "Competing Offering" is a product or hosted or managed service whose primary
14
+ purpose is to provide a substantial portion of the functionality of TradeJS,
15
+ including strategy authoring, backtesting, live signal generation, automated
16
+ trading, or machine-learning training and inference workflows.
17
+
18
+ Internal use by you or your organization, including use for your own trading,
19
+ research, analytics, or operations, is not a Competing Offering.
20
+
21
+ Change Date: 2030-07-17
22
+
23
+ Change License: GNU Affero General Public License Version 3
24
+
25
+ For commercial licensing of a Competing Offering, contact the TradeJS-Dev
26
+ maintainers through https://github.com/TradeJS-Dev/TradeJS.
27
+
28
+ License text copyright © 2017 MariaDB Corporation Ab, All Rights Reserved.
29
+ "Business Source License" is a trademark of MariaDB Corporation Ab.
30
+
31
+ Terms
32
+
33
+ The Licensor hereby grants you the right to copy, modify, create derivative
34
+ works, redistribute, and make non-production use of the Licensed Work. The
35
+ Licensor may make an Additional Use Grant, above, permitting limited
36
+ production use.
37
+
38
+ Effective on the Change Date, or the fourth anniversary of the first publicly
39
+ available distribution of a specific version of the Licensed Work under this
40
+ License, whichever comes first, the Licensor hereby grants you rights under
41
+ the terms of the Change License, and the rights granted in the paragraph
42
+ above terminate.
43
+
44
+ If your use of the Licensed Work does not comply with the requirements
45
+ currently in effect as described in this License, you must purchase a
46
+ commercial license from the Licensor, its affiliated entities, or authorized
47
+ resellers, or you must refrain from using the Licensed Work.
48
+
49
+ All copies of the original and modified Licensed Work, and derivative works
50
+ of the Licensed Work, are subject to this License. This License applies
51
+ separately for each version of the Licensed Work and the Change Date may vary
52
+ for each version of the Licensed Work released by Licensor.
53
+
54
+ You must conspicuously display this License on each original or modified copy
55
+ of the Licensed Work. If you receive the Licensed Work in original or
56
+ modified form from a third party, the terms and conditions set forth in this
57
+ License apply to your use of that work.
58
+
59
+ Any use of the Licensed Work in violation of this License will automatically
60
+ terminate your rights under this License for the current and all other
61
+ versions of the Licensed Work.
62
+
63
+ This License does not grant you any right in any trademark or logo of
64
+ Licensor or its affiliates (provided that you may use a trademark or logo of
65
+ Licensor as expressly required by this License).
66
+
67
+ TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON
68
+ AN “AS IS” BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS,
69
+ EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF
70
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND
71
+ TITLE.
72
+
73
+ MariaDB hereby grants you permission to use this License’s text to license
74
+ your works, and to refer to it using the trademark “Business Source License”,
75
+ as long as you comply with the Covenants of Licensor below.
76
+
77
+ Covenants of Licensor
78
+
79
+ In consideration of the right to use this License’s text and the “Business
80
+ Source License” name and trademark, Licensor covenants to MariaDB, and to all
81
+ other recipients of the licensed work to be provided by Licensor:
82
+
83
+ 1. To specify as the Change License the GPL Version 2.0 or any later version,
84
+ or a license that is compatible with GPL Version 2.0 or a later version,
85
+ where “compatible” means that software provided under the Change License can
86
+ be included in a program with software provided under GPL Version 2.0 or a
87
+ later version. Licensor may specify additional Change Licenses without
88
+ limitation.
89
+
90
+ 2. To either: (a) specify an additional grant of rights to use that does not
91
+ impose any additional restriction on the right granted in this License, as
92
+ the Additional Use Grant; or (b) insert the text “None”.
93
+
94
+ 3. To specify a Change Date.
95
+
96
+ 4. Not to modify this License in any other way.
package/README.md CHANGED
@@ -1,12 +1,20 @@
1
1
  # @tradejs/cli
2
2
 
3
- Official CLI for the TradeJS open-source framework.
3
+ Official CLI for the TradeJS TypeScript framework.
4
4
 
5
5
  - Homepage: https://tradejs.dev
6
6
  - Documentation: https://docs.tradejs.dev
7
7
  - CLI API docs: https://docs.tradejs.dev/api/cli
8
8
  - Quickstart: https://docs.tradejs.dev/getting-started/quickstart
9
9
 
10
+ ## License
11
+
12
+ Version 2.0.0 and later is licensed under Business Source License 1.1. The
13
+ Additional Use Grant permits internal and other non-competing production use;
14
+ providing a competing product or service requires a commercial license.
15
+ Earlier releases remain MIT-licensed. See the
16
+ [TradeJS licensing policy](https://github.com/TradeJS-Dev/TradeJS/blob/stable/LICENSING.md).
17
+
10
18
  ## Where It Fits
11
19
 
12
20
  `@tradejs/cli` is the operational entrypoint for the standard external TradeJS project flow:
package/dist/cli.js CHANGED
@@ -6151,7 +6151,6 @@ var init_aiTrainResearch = __esm({
6151
6151
  "INTERVAL",
6152
6152
  "DERIVATIVES_CONTEXT_ENABLED",
6153
6153
  "DERIVATIVES_CONTEXT_TARGET_ENABLED",
6154
- "DERIVATIVES_CONTEXT_INTERVALS",
6155
6154
  "DERIVATIVES_CONTEXT_LOOKBACK_HOURS",
6156
6155
  "DERIVATIVES_CONTEXT_EXTRA_REFERENCE_SYMBOLS",
6157
6156
  "DERIVATIVES_CONTEXT_EXCHANGE_PRIORITY",
@@ -8591,7 +8590,7 @@ var init_binanceMarketContextBackfill = __esm({
8591
8590
  });
8592
8591
 
8593
8592
  // src/lib/derivativesContextBackfill.ts
8594
- var import_chalk7, import_progress4, import_async, import_constants5, import_indicators, import_timescale2, import_userSettings2, HOUR_MS, DAY_MS6, DEFAULT_LOOKBACK_HOURS, DEFAULT_INTERVALS, coinalyzeIntervalMap, derivativesIntervalMs, lastRequestTs, asInt2, parseList, parseBooleanFlag, normalizeSymbols, isDerivativesTargetContextEnabled, resolveDerivativesContextBackfillSymbols, chunkArray, isBacktestDerivativesContextEnabled, isSignalsDerivativesContextEnabled, isDerivativesContextBackfillEnabled, shouldBackfillDerivativesContextForBacktest, shouldBackfillDerivativesContextForSignals, resolveDerivativesContextIntervals, resolveDerivativesContextLookbackMs, resolveDerivativesContextBackfillWindow, resolveDerivativesContextIntervalBackfillWindow, resolveDerivativesContextMissingFetchFromMs, countBackfillWindows, buildBackfillWindows, hasDerivativesWindowCoverage, resolveDerivativesContextMissingCoverageFetchFromMs, groupDerivativesContextMissingFetchRanges, coverageKey, extendEdges, getCoinalyzeApiKey, getCoinalyzeBaseUrl, getRequestDelayMs, getRequestTimeoutMs, networkErrorCodes, getNestedErrorValue, getCoinalyzeErrorCause, isRetryableCoinalyzeFetchError, formatCoinalyzeRequestError, fetchJsonWithRateLimit, fetchCoinalyzeMarkets, selectBestMarket, buildMatches, normalizeMetricPoint, toSeriesMap, fetchMetricBatch, skippedBackfillResult2, backfillDerivativesContext, backfillDerivativesContextForBacktest, backfillDerivativesContextForSignals;
8593
+ var import_chalk7, import_progress4, import_async, import_constants5, import_indicators, import_timescale2, import_userSettings2, HOUR_MS, DAY_MS6, DEFAULT_LOOKBACK_HOURS, SOURCE_INTERVALS, coinalyzeIntervalMap, derivativesIntervalMs, lastRequestTs, asInt2, parseList, parseBooleanFlag, normalizeSymbols, isDerivativesTargetContextEnabled, resolveDerivativesContextBackfillSymbols, chunkArray, isBacktestDerivativesContextEnabled, isSignalsDerivativesContextEnabled, isDerivativesContextBackfillEnabled, shouldBackfillDerivativesContextForBacktest, shouldBackfillDerivativesContextForSignals, resolveDerivativesContextIntervals, resolveDerivativesContextLookbackMs, resolveDerivativesContextBackfillWindow, resolveDerivativesContextIntervalBackfillWindow, resolveDerivativesContextMissingFetchFromMs, resolveDerivativesContextFetchFromMs, countBackfillWindows, buildBackfillWindows, hasDerivativesWindowCoverage, resolveDerivativesContextMissingCoverageFetchFromMs, groupDerivativesContextMissingFetchRanges, getMissingClosedDerivativesSymbols, coverageKey, extendEdges, getCoinalyzeApiKey, getCoinalyzeBaseUrl, getRequestDelayMs, getRequestTimeoutMs, networkErrorCodes, getNestedErrorValue, getCoinalyzeErrorCause, isRetryableCoinalyzeFetchError, formatCoinalyzeRequestError, fetchJsonWithRateLimit, fetchCoinalyzeMarkets, selectBestMarket, buildMatches, normalizeMetricPoint, toSeriesMap, fetchMetricBatch, skippedBackfillResult2, backfillDerivativesContext, backfillDerivativesContextForBacktest, backfillDerivativesContextForSignals;
8595
8594
  var init_derivativesContextBackfill = __esm({
8596
8595
  "src/lib/derivativesContextBackfill.ts"() {
8597
8596
  "use strict";
@@ -8605,7 +8604,7 @@ var init_derivativesContextBackfill = __esm({
8605
8604
  HOUR_MS = 60 * 60 * 1e3;
8606
8605
  DAY_MS6 = 24 * HOUR_MS;
8607
8606
  DEFAULT_LOOKBACK_HOURS = 48;
8608
- DEFAULT_INTERVALS = ["15m", "1h"];
8607
+ SOURCE_INTERVALS = ["15m"];
8609
8608
  coinalyzeIntervalMap = {
8610
8609
  "15m": "15min",
8611
8610
  "1h": "1hour"
@@ -8666,10 +8665,7 @@ var init_derivativesContextBackfill = __esm({
8666
8665
  shouldBackfillDerivativesContextForBacktest = (params) => !params.cacheOnly && (params.aiEnabled || params.mlEnabled) && isBacktestDerivativesContextEnabled();
8667
8666
  shouldBackfillDerivativesContextForSignals = (params) => !params.cacheOnly && isSignalsDerivativesContextEnabled();
8668
8667
  resolveDerivativesContextIntervals = () => {
8669
- const intervals = (0, import_indicators.normalizeDerivativesIntervals)(
8670
- process.env.DERIVATIVES_CONTEXT_INTERVALS
8671
- );
8672
- return intervals.length ? intervals : DEFAULT_INTERVALS;
8668
+ return [...SOURCE_INTERVALS];
8673
8669
  };
8674
8670
  resolveDerivativesContextLookbackMs = () => {
8675
8671
  const hours = Number(process.env.DERIVATIVES_CONTEXT_LOOKBACK_HOURS);
@@ -8704,9 +8700,10 @@ var init_derivativesContextBackfill = __esm({
8704
8700
  };
8705
8701
  resolveDerivativesContextIntervalBackfillWindow = (params) => {
8706
8702
  const intervalMs2 = derivativesIntervalMs(params.interval);
8703
+ const alignedToMs = params.closedOnly ? (0, import_indicators.getLastClosedDerivativesBarStartMs)(params.toMs, params.interval) : Math.floor(params.toMs / intervalMs2) * intervalMs2;
8707
8704
  return {
8708
8705
  fromMs: Math.floor(params.fromMs / intervalMs2) * intervalMs2,
8709
- toMs: Math.floor(params.toMs / intervalMs2) * intervalMs2,
8706
+ toMs: alignedToMs,
8710
8707
  intervalMs: intervalMs2
8711
8708
  };
8712
8709
  };
@@ -8724,6 +8721,7 @@ var init_derivativesContextBackfill = __esm({
8724
8721
  }
8725
8722
  return null;
8726
8723
  };
8724
+ resolveDerivativesContextFetchFromMs = (params) => resolveDerivativesContextMissingFetchFromMs(params) ?? (params.refreshClosedTail ? params.toMs : null);
8727
8725
  countBackfillWindows = (params) => {
8728
8726
  let count = 0;
8729
8727
  let cursor = params.fromMs;
@@ -8787,6 +8785,14 @@ var init_derivativesContextBackfill = __esm({
8787
8785
  }
8788
8786
  return [...groups.entries()].sort(([a], [b]) => a - b).map(([fromMs, items]) => ({ fromMs, items }));
8789
8787
  };
8788
+ getMissingClosedDerivativesSymbols = (params) => {
8789
+ const availableSymbols = new Set(
8790
+ params.rows.filter((row) => row.ts.getTime() === params.expectedTimestamp).map((row) => row.symbol.trim().toUpperCase())
8791
+ );
8792
+ return params.symbols.filter(
8793
+ (symbol) => !availableSymbols.has(symbol.trim().toUpperCase())
8794
+ );
8795
+ };
8790
8796
  coverageKey = (params) => [
8791
8797
  params.symbol.trim().toUpperCase(),
8792
8798
  params.interval,
@@ -9066,7 +9072,8 @@ var init_derivativesContextBackfill = __esm({
9066
9072
  ...resolveDerivativesContextIntervalBackfillWindow({
9067
9073
  fromMs,
9068
9074
  toMs: safeEndMs,
9069
- interval: interval5
9075
+ interval: interval5,
9076
+ closedOnly: mode === "signals"
9070
9077
  })
9071
9078
  })).filter((item) => item.toMs > item.fromMs);
9072
9079
  if (!intervalWindows.length) {
@@ -9125,7 +9132,7 @@ var init_derivativesContextBackfill = __esm({
9125
9132
  }),
9126
9133
  0
9127
9134
  );
9128
- const allBackfillWindowsCached = intervalWindows.every((window2) => {
9135
+ const allBackfillWindowsCached = mode !== "signals" && intervalWindows.every((window2) => {
9129
9136
  const edgesBySymbol = edgesByInterval.get(window2.interval);
9130
9137
  const coverageKeys = coverageKeysByInterval.get(window2.interval);
9131
9138
  const backfillWindows = buildBackfillWindows({
@@ -9255,7 +9262,7 @@ var init_derivativesContextBackfill = __esm({
9255
9262
  return null;
9256
9263
  }
9257
9264
  const normalizedSymbol = item.symbol.toUpperCase();
9258
- const coverageFromMs = resolveDerivativesContextMissingCoverageFetchFromMs({
9265
+ const coverageFromMs = mode === "signals" ? cursor : resolveDerivativesContextMissingCoverageFetchFromMs({
9259
9266
  ranges: coverageRangesBySymbol.get(normalizedSymbol),
9260
9267
  fromMs: cursor,
9261
9268
  toMs,
@@ -9265,16 +9272,20 @@ var init_derivativesContextBackfill = __esm({
9265
9272
  return null;
9266
9273
  }
9267
9274
  const edges = edgesBySymbol.get(normalizedSymbol);
9268
- const edgesFromMs = resolveDerivativesContextMissingFetchFromMs({
9275
+ const edgesFromMs = resolveDerivativesContextFetchFromMs({
9269
9276
  edges,
9270
9277
  fromMs: cursor,
9271
9278
  toMs,
9272
- intervalMs: intervalMs2
9279
+ intervalMs: intervalMs2,
9280
+ refreshClosedTail: mode === "signals"
9273
9281
  });
9274
9282
  if (edgesFromMs == null) {
9275
9283
  return null;
9276
9284
  }
9277
- return { item, fromMs: Math.max(coverageFromMs, edgesFromMs) };
9285
+ return {
9286
+ item,
9287
+ fromMs: Math.max(coverageFromMs, edgesFromMs)
9288
+ };
9278
9289
  }).filter(
9279
9290
  (item) => item != null
9280
9291
  );
@@ -9288,85 +9299,113 @@ var init_derivativesContextBackfill = __esm({
9288
9299
  const marketSymbols = missingBatch.map(
9289
9300
  (item) => item.marketSymbol
9290
9301
  );
9291
- const oiMap = await fetchMetricBatch({
9292
- endpoint: oiPath,
9293
- metric: "oi",
9294
- marketSymbols,
9295
- apiKey,
9296
- interval: interval5,
9297
- fromMs: group.fromMs,
9298
- toMs
9299
- });
9300
- const fundingMap = await fetchMetricBatch({
9301
- endpoint: fundingPath,
9302
- metric: "funding",
9303
- marketSymbols,
9304
- apiKey,
9305
- interval: interval5,
9306
- fromMs: group.fromMs,
9307
- toMs
9308
- });
9309
- const liqMap = await fetchMetricBatch({
9310
- endpoint: liqPath,
9311
- metric: "liq",
9312
- marketSymbols,
9313
- apiKey,
9314
- interval: interval5,
9315
- fromMs: group.fromMs,
9316
- toMs
9317
- });
9318
- const rows = missingBatch.flatMap((item) => {
9319
- const marketSymbol = item.marketSymbol.toUpperCase();
9320
- const points = (0, import_indicators.mergeCoinalyzeMetrics)({
9321
- symbol: item.symbol,
9322
- oiRaw: oiMap.get(marketSymbol) ?? [],
9323
- fundingRaw: fundingMap.get(marketSymbol) ?? [],
9324
- liqRaw: liqMap.get(marketSymbol) ?? []
9302
+ const maxClosedBarAttempts = mode === "signals" ? asInt2(
9303
+ process.env.DERIVATIVES_CONTEXT_CLOSED_BAR_MAX_ATTEMPTS,
9304
+ 3
9305
+ ) : 1;
9306
+ const closedBarRetryDelayMs = asInt2(
9307
+ process.env.DERIVATIVES_CONTEXT_CLOSED_BAR_RETRY_DELAY_MS,
9308
+ 2e3
9309
+ );
9310
+ let rows = [];
9311
+ let missingClosedSymbols = [];
9312
+ for (let attempt = 1; attempt <= maxClosedBarAttempts; attempt += 1) {
9313
+ const oiMap = await fetchMetricBatch({
9314
+ endpoint: oiPath,
9315
+ metric: "oi",
9316
+ marketSymbols,
9317
+ apiKey,
9318
+ interval: interval5,
9319
+ fromMs: group.fromMs,
9320
+ toMs
9321
+ });
9322
+ const fundingMap = await fetchMetricBatch({
9323
+ endpoint: fundingPath,
9324
+ metric: "funding",
9325
+ marketSymbols,
9326
+ apiKey,
9327
+ interval: interval5,
9328
+ fromMs: group.fromMs,
9329
+ toMs
9330
+ });
9331
+ const liqMap = await fetchMetricBatch({
9332
+ endpoint: liqPath,
9333
+ metric: "liq",
9334
+ marketSymbols,
9335
+ apiKey,
9336
+ interval: interval5,
9337
+ fromMs: group.fromMs,
9338
+ toMs
9325
9339
  });
9326
- return (0, import_indicators.coinalyzePointsToRows)(points, interval5, "coinalyze");
9327
- });
9340
+ rows = missingBatch.flatMap((item) => {
9341
+ const marketSymbol = item.marketSymbol.toUpperCase();
9342
+ const points = (0, import_indicators.mergeCoinalyzeMetrics)({
9343
+ symbol: item.symbol,
9344
+ oiRaw: oiMap.get(marketSymbol) ?? [],
9345
+ fundingRaw: fundingMap.get(marketSymbol) ?? [],
9346
+ liqRaw: liqMap.get(marketSymbol) ?? []
9347
+ });
9348
+ return (0, import_indicators.coinalyzePointsToRows)(points, interval5, "coinalyze");
9349
+ });
9350
+ missingClosedSymbols = mode === "signals" ? getMissingClosedDerivativesSymbols({
9351
+ symbols: missingBatch.map((item) => item.symbol),
9352
+ rows,
9353
+ expectedTimestamp: toMs
9354
+ }) : [];
9355
+ if (!missingClosedSymbols.length) break;
9356
+ if (attempt < maxClosedBarAttempts) {
9357
+ await (0, import_async.delay)(closedBarRetryDelayMs);
9358
+ }
9359
+ }
9360
+ if (missingClosedSymbols.length) {
9361
+ throw new Error(
9362
+ `Coinalyze closed ${interval5} bar ${new Date(toMs).toISOString()} unavailable for ${missingClosedSymbols.join(",")}`
9363
+ );
9364
+ }
9328
9365
  if (rows.length) {
9329
9366
  await (0, import_timescale2.upsertDerivatives)(rows);
9330
9367
  totalRows += rows.length;
9331
9368
  }
9332
- const rowsCountBySymbol = /* @__PURE__ */ new Map();
9333
- for (const row of rows) {
9334
- const symbol = row.symbol.toUpperCase();
9335
- rowsCountBySymbol.set(
9336
- symbol,
9337
- (rowsCountBySymbol.get(symbol) ?? 0) + 1
9338
- );
9339
- }
9340
- const coverageRows = missingBatch.map((item) => {
9341
- const normalizedSymbol = item.symbol.toUpperCase();
9342
- const rowsCount = rowsCountBySymbol.get(normalizedSymbol) ?? 0;
9343
- return {
9344
- source: "coinalyze",
9345
- symbol: item.symbol,
9346
- interval: interval5,
9347
- fromMs: cursor,
9348
- toMs,
9349
- rowsCount
9350
- };
9351
- });
9352
- await (0, import_timescale2.upsertDerivativesBackfillCoverage)(coverageRows);
9353
- for (const coverageRow of coverageRows) {
9354
- const symbol = coverageRow.symbol.toUpperCase();
9355
- edgesBySymbol.set(
9356
- symbol,
9357
- extendEdges(edgesBySymbol.get(symbol), cursor, toMs)
9358
- );
9359
- coverageKeys.add(
9360
- coverageKey({
9369
+ if (mode === "backtest") {
9370
+ const rowsCountBySymbol = /* @__PURE__ */ new Map();
9371
+ for (const row of rows) {
9372
+ const symbol = row.symbol.toUpperCase();
9373
+ rowsCountBySymbol.set(
9361
9374
  symbol,
9375
+ (rowsCountBySymbol.get(symbol) ?? 0) + 1
9376
+ );
9377
+ }
9378
+ const coverageRows = missingBatch.map((item) => {
9379
+ const normalizedSymbol = item.symbol.toUpperCase();
9380
+ const rowsCount = rowsCountBySymbol.get(normalizedSymbol) ?? 0;
9381
+ return {
9382
+ source: "coinalyze",
9383
+ symbol: item.symbol,
9362
9384
  interval: interval5,
9363
9385
  fromMs: cursor,
9364
- toMs
9365
- })
9366
- );
9367
- const coverageRanges = coverageRangesBySymbol.get(symbol) ?? [];
9368
- coverageRanges.push({ fromMs: cursor, toMs });
9369
- coverageRangesBySymbol.set(symbol, coverageRanges);
9386
+ toMs,
9387
+ rowsCount
9388
+ };
9389
+ });
9390
+ await (0, import_timescale2.upsertDerivativesBackfillCoverage)(coverageRows);
9391
+ for (const coverageRow of coverageRows) {
9392
+ const symbol = coverageRow.symbol.toUpperCase();
9393
+ edgesBySymbol.set(
9394
+ symbol,
9395
+ extendEdges(edgesBySymbol.get(symbol), cursor, toMs)
9396
+ );
9397
+ coverageKeys.add(
9398
+ coverageKey({
9399
+ symbol,
9400
+ interval: interval5,
9401
+ fromMs: cursor,
9402
+ toMs
9403
+ })
9404
+ );
9405
+ const coverageRanges = coverageRangesBySymbol.get(symbol) ?? [];
9406
+ coverageRanges.push({ fromMs: cursor, toMs });
9407
+ coverageRangesBySymbol.set(symbol, coverageRanges);
9408
+ }
9370
9409
  }
9371
9410
  }
9372
9411
  }
@@ -18915,11 +18954,11 @@ var init_derivativesIngest = __esm({
18915
18954
  import_userSettings4 = require("@tradejs/infra/userSettings");
18916
18955
  import_connectors9 = require("@tradejs/connectors");
18917
18956
  import_args12.default.example(
18918
- "yarn ts-node ./src/scripts/derivativesIngest --provider coinalyze --symbols BTCUSDT,ETHUSDT --intervals 15m,1h --days 120",
18957
+ "yarn ts-node ./src/scripts/derivativesIngest --provider coinalyze --symbols BTCUSDT,ETHUSDT --intervals 15m --days 120",
18919
18958
  "Ingest market features (derivatives/spread) into Timescale by provider"
18920
18959
  );
18921
18960
  import_args12.default.option(["s", "symbols"], "Comma-separated symbols", "BTCUSDT,ETHUSDT");
18922
- import_args12.default.option(["t", "intervals"], "Comma-separated intervals: 15m,1h", "15m,1h");
18961
+ import_args12.default.option(["t", "intervals"], "Comma-separated intervals: 15m,1h", "15m");
18923
18962
  import_args12.default.option(["d", "days"], "Lookback in days", 120);
18924
18963
  import_args12.default.option(
18925
18964
  ["p", "provider"],
@@ -18956,9 +18995,16 @@ var init_derivativesIngest = __esm({
18956
18995
  let totalSpreadRows = 0;
18957
18996
  for (const symbol of symbols) {
18958
18997
  for (const interval5 of intervals) {
18998
+ const lastClosedStartMs = (0, import_indicators2.getLastClosedDerivativesBarStartMs)(
18999
+ now2,
19000
+ interval5
19001
+ );
18959
19002
  let cursor = fromMs;
18960
- while (cursor < now2) {
18961
- const toMs = Math.min(now2, cursor + batchDays * 24 * 60 * 60 * 1e3);
19003
+ while (cursor < lastClosedStartMs) {
19004
+ const toMs = Math.min(
19005
+ lastClosedStartMs,
19006
+ cursor + batchDays * 24 * 60 * 60 * 1e3
19007
+ );
18962
19008
  process.stdout.write(
18963
19009
  `\r${import_chalk20.default.cyan(providerName)} ${import_chalk20.default.yellow(symbol)} ${interval5} ${new Date(cursor).toISOString()} .. ${new Date(toMs).toISOString()} `
18964
19010
  );
@@ -19018,7 +19064,7 @@ var init_derivativesIngestCoinalyzeAll = __esm({
19018
19064
  "1h": "1hour"
19019
19065
  };
19020
19066
  import_args13.default.example(
19021
- "yarn ts-node ./src/scripts/derivativesIngestCoinalyzeAll --days 120 --intervals 15m,1h",
19067
+ "yarn ts-node ./src/scripts/derivativesIngestCoinalyzeAll --days 120 --intervals 15m",
19022
19068
  "Fetch derivatives for all getTickers symbols matched to Coinalyze markets"
19023
19069
  );
19024
19070
  import_args13.default.option(["U", "user"], "User settings profile name from Redis", "root");
@@ -19026,7 +19072,7 @@ var init_derivativesIngestCoinalyzeAll = __esm({
19026
19072
  import_args13.default.option(["e", "exclude"], "Comma-separated exclude symbols");
19027
19073
  import_args13.default.option(["l", "tickersLimit"], "Tickers limit");
19028
19074
  import_args13.default.option(["c", "chunk"], "Chunk selector, e.g. 1/4");
19029
- import_args13.default.option(["i", "intervals"], "Intervals: 15m,1h", "15m,1h");
19075
+ import_args13.default.option(["i", "intervals"], "Intervals: 15m,1h", "15m");
19030
19076
  import_args13.default.option(["d", "days"], "Lookback in days", 120);
19031
19077
  import_args13.default.option(["b", "batchDays"], "Request chunk size in days", 120);
19032
19078
  import_args13.default.option(
@@ -19295,12 +19341,16 @@ var init_derivativesIngestCoinalyzeAll = __esm({
19295
19341
  let totalRows = 0;
19296
19342
  let failedWindows = 0;
19297
19343
  for (const interval5 of intervals) {
19344
+ const lastClosedStartMs = (0, import_indicators3.getLastClosedDerivativesBarStartMs)(now2, interval5);
19298
19345
  for (let batchIdx = 0; batchIdx < symbolBatches.length; batchIdx += 1) {
19299
19346
  const batch = symbolBatches[batchIdx];
19300
19347
  const marketSymbols = batch.map((item) => item.marketSymbol);
19301
19348
  let cursor = fromMs;
19302
- while (cursor < now2) {
19303
- const toMs = Math.min(now2, cursor + batchDays * 24 * 60 * 60 * 1e3);
19349
+ while (cursor < lastClosedStartMs) {
19350
+ const toMs = Math.min(
19351
+ lastClosedStartMs,
19352
+ cursor + batchDays * 24 * 60 * 60 * 1e3
19353
+ );
19304
19354
  try {
19305
19355
  const oiMap = await fetchMetricBatch2({
19306
19356
  endpoint: oiPath,
@@ -273,7 +273,6 @@ var RESEARCH_CONTEXT_ENV_KEYS = [
273
273
  "INTERVAL",
274
274
  "DERIVATIVES_CONTEXT_ENABLED",
275
275
  "DERIVATIVES_CONTEXT_TARGET_ENABLED",
276
- "DERIVATIVES_CONTEXT_INTERVALS",
277
276
  "DERIVATIVES_CONTEXT_LOOKBACK_HOURS",
278
277
  "DERIVATIVES_CONTEXT_EXTRA_REFERENCE_SYMBOLS",
279
278
  "DERIVATIVES_CONTEXT_EXCHANGE_PRIORITY",