@tradejs/cli 1.0.9 → 1.0.11
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/README.md +13 -4
- package/dist/cli.js +29531 -13520
- package/dist/lib/aiPocketSearch.js +1316 -0
- package/dist/lib/aiTrainCharts.js +711 -0
- package/dist/lib/aiTrainDataset.js +40 -0
- package/dist/lib/aiTrainEvaluationDump.js +76 -0
- package/dist/lib/aiTrainMetrics.js +148 -8
- package/dist/lib/aiTrainOptions.js +130 -0
- package/dist/lib/aiTrainQuarantine.js +199 -0
- package/dist/lib/aiTrainResearch.js +467 -0
- package/dist/lib/binanceMarketContextBackfill.js +841 -0
- package/dist/lib/binanceMarketData.js +392 -0
- package/dist/lib/coinMarketCapContextBackfill.js +1126 -0
- package/dist/lib/continuity.js +60 -0
- package/dist/lib/derivativesContextBackfill.js +490 -77
- package/dist/lib/executionCalibration.js +705 -0
- package/dist/lib/marketContextPrepare.js +4441 -0
- package/dist/lib/paritySummary.js +101 -0
- package/dist/lib/runBot.js +1 -0
- package/dist/lib/runEnvironment.js +2259 -0
- package/dist/lib/runFormatting.js +1657 -0
- package/dist/lib/runtimeDebugEvidence.js +526 -0
- package/dist/lib/runtimeModeConfig.js +53 -0
- package/dist/lib/runtimeParity.js +88 -11
- package/dist/lib/runtimeParityDetails.js +751 -0
- package/dist/lib/runtimeRedis.js +157 -0
- package/dist/lib/runtimeSignalsLoader.js +40 -6
- package/dist/lib/runtimeSignalsStorage.js +45 -22
- package/dist/lib/runtimeStrategyBacktest.js +134 -0
- package/dist/lib/runtimeTradeSync.js +263 -0
- package/dist/lib/telegramReports.js +9 -0
- package/dist/lib/tickerUniverseCache.js +116 -0
- package/dist/lib/timeWindow.js +3 -3
- package/dist/scripts/agentRun.js +14 -8
- package/dist/scripts/aiExport.js +1721 -7
- package/dist/scripts/aiExportSelect.js +16 -4
- package/dist/scripts/aiPocketSearch.js +4190 -0
- package/dist/scripts/aiTrain.js +1866 -87
- package/dist/scripts/backtest.js +4534 -803
- package/dist/scripts/binanceMarketIngest.js +565 -0
- package/dist/scripts/bot.js +16 -2
- package/dist/scripts/candlesMigrateProvider.js +32 -22
- package/dist/scripts/cleanDir.js +15 -2
- package/dist/scripts/cleanRedis.js +15 -2
- package/dist/scripts/cleanTests.js +38 -2
- package/dist/scripts/cleanupMarketContext.js +69 -0
- package/dist/scripts/continuity.js +148 -67
- package/dist/scripts/derivativesIngest.js +14 -4
- package/dist/scripts/derivativesIngestCoinalyzeAll.js +14 -4
- package/dist/scripts/doctor.js +14 -4
- package/dist/scripts/executionCalibration.js +792 -0
- package/dist/scripts/infraCommon.js +18 -5
- package/dist/scripts/infraDown.js +14 -4
- package/dist/scripts/infraInit.js +32 -9
- package/dist/scripts/infraUp.js +24 -6
- package/dist/scripts/marketWs.js +222 -0
- package/dist/scripts/migration.js +15 -2
- package/dist/scripts/mlExport.js +1698 -10
- package/dist/scripts/mlExportSelect.js +16 -4
- package/dist/scripts/mlInspect.js +13 -3
- package/dist/scripts/mlTrainLatestSelect.js +14 -4
- package/dist/scripts/replay.js +8867 -0
- package/dist/scripts/replayRunner.js +8870 -0
- package/dist/scripts/replayRuntimeEvidence.js +868 -0
- package/dist/scripts/researchAuto.js +286 -59
- package/dist/scripts/results.js +3 -3
- package/dist/scripts/runtimeEvidence.js +605 -0
- package/dist/scripts/runtimeParity.js +6400 -1043
- package/dist/scripts/serverHealth.js +588 -0
- package/dist/scripts/signals.js +5628 -511
- package/dist/scripts/signalsDaemon.js +6205 -0
- package/dist/scripts/signalsSummary.js +833 -248
- package/dist/scripts/test-ml.js +25 -3
- package/dist/scripts/test.js +26 -1
- package/dist/scripts/user-add.js +15 -2
- package/dist/workers/testerWorker.js +130 -21
- package/package.json +18 -12
|
@@ -0,0 +1,1126 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/lib/coinMarketCapContextBackfill.ts
|
|
31
|
+
var coinMarketCapContextBackfill_exports = {};
|
|
32
|
+
__export(coinMarketCapContextBackfill_exports, {
|
|
33
|
+
backfillCoinMarketCapContext: () => backfillCoinMarketCapContext,
|
|
34
|
+
backfillCoinMarketCapContextForBacktest: () => backfillCoinMarketCapContextForBacktest,
|
|
35
|
+
backfillCoinMarketCapContextForReplay: () => backfillCoinMarketCapContextForReplay,
|
|
36
|
+
backfillCoinMarketCapContextForSignals: () => backfillCoinMarketCapContextForSignals,
|
|
37
|
+
coinMarketCapExchangeQuotesPayloadToLiquidityRows: () => coinMarketCapExchangeQuotesPayloadToLiquidityRows,
|
|
38
|
+
coinMarketCapFearGreedPayloadToRows: () => coinMarketCapFearGreedPayloadToRows,
|
|
39
|
+
coinMarketCapGlobalPayloadToRows: () => coinMarketCapGlobalPayloadToRows,
|
|
40
|
+
coinMarketCapHistoricalQuotesPayloadToRows: () => coinMarketCapHistoricalQuotesPayloadToRows,
|
|
41
|
+
coinMarketCapIndexPayloadToRows: () => coinMarketCapIndexPayloadToRows,
|
|
42
|
+
coverageRowsToKeySet: () => coverageRowsToKeySet,
|
|
43
|
+
isMarketContextBackfillCoverageDataBearing: () => isMarketContextBackfillCoverageDataBearing,
|
|
44
|
+
resolveCoinMarketCapBackfillWindow: () => resolveCoinMarketCapBackfillWindow,
|
|
45
|
+
shouldBackfillCoinMarketCapContextForBacktest: () => shouldBackfillCoinMarketCapContextForBacktest,
|
|
46
|
+
shouldBackfillCoinMarketCapContextForReplay: () => shouldBackfillCoinMarketCapContextForReplay,
|
|
47
|
+
shouldBackfillCoinMarketCapContextForSignals: () => shouldBackfillCoinMarketCapContextForSignals
|
|
48
|
+
});
|
|
49
|
+
module.exports = __toCommonJS(coinMarketCapContextBackfill_exports);
|
|
50
|
+
var import_chalk = __toESM(require("chalk"));
|
|
51
|
+
var import_async = require("@tradejs/core/async");
|
|
52
|
+
var import_timescale = require("@tradejs/infra/timescale");
|
|
53
|
+
var import_userSettings = require("@tradejs/infra/userSettings");
|
|
54
|
+
var DAY_MS = 864e5;
|
|
55
|
+
var DEFAULT_CONTEXT_MAX_AGE_MS = 48 * 60 * 6e4;
|
|
56
|
+
var DEFAULT_FEAR_GREED_STALE_RETRY_MS = 60 * 6e4;
|
|
57
|
+
var SOURCE_GLOBAL_DAILY = "coinmarketcap_global";
|
|
58
|
+
var SOURCE_REFERENCE = "coinmarketcap_reference_asset";
|
|
59
|
+
var SOURCE_EXCHANGE_LIQUIDITY = "coinmarketcap_exchange_liquidity";
|
|
60
|
+
var SOURCE_FEAR_GREED = "coinmarketcap_fear_greed";
|
|
61
|
+
var SOURCE_INDEX = "coinmarketcap_index";
|
|
62
|
+
var COVERAGE_SCOPE_ALL = "all";
|
|
63
|
+
var REFERENCE_ASSETS = [
|
|
64
|
+
{ symbol: "BTCUSDT", cmcId: 1 },
|
|
65
|
+
{ symbol: "ETHUSDT", cmcId: 1027 }
|
|
66
|
+
];
|
|
67
|
+
var CMC_INDEXES = [
|
|
68
|
+
{ slug: "cmc100", path: "/v3/index/cmc100-historical" },
|
|
69
|
+
{ slug: "cmc20", path: "/v3/index/cmc20-historical" }
|
|
70
|
+
];
|
|
71
|
+
var DEFAULT_EXCHANGE_SLUGS = [
|
|
72
|
+
"binance",
|
|
73
|
+
"coinbase-exchange",
|
|
74
|
+
"okx",
|
|
75
|
+
"bybit",
|
|
76
|
+
"kraken"
|
|
77
|
+
];
|
|
78
|
+
var lastRequestTs = 0;
|
|
79
|
+
var asInt = (value, fallback) => {
|
|
80
|
+
const parsed = Number.parseInt(String(value ?? ""), 10);
|
|
81
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
82
|
+
};
|
|
83
|
+
var parseEnabledFlag = (value, defaultValue) => {
|
|
84
|
+
const normalized = String(value ?? "").trim().toLowerCase();
|
|
85
|
+
if (!normalized) return defaultValue;
|
|
86
|
+
if (["1", "true", "yes", "on"].includes(normalized)) return true;
|
|
87
|
+
if (["0", "false", "no", "off"].includes(normalized)) return false;
|
|
88
|
+
return defaultValue;
|
|
89
|
+
};
|
|
90
|
+
var getBaseUrl = () => process.env.COINMARKETCAP_BASE_URL?.trim() || "https://pro-api.coinmarketcap.com";
|
|
91
|
+
var getRequestDelayMs = () => asInt(process.env.COINMARKETCAP_MIN_REQUEST_DELAY_MS, 2100);
|
|
92
|
+
var getMaxRetries = () => asInt(process.env.COINMARKETCAP_MAX_RETRIES, 4);
|
|
93
|
+
var getMaxBackfillDays = () => asInt(process.env.COINMARKETCAP_CONTEXT_BACKFILL_MAX_DAYS, 1098);
|
|
94
|
+
var getHistoricalAccessMonths = () => asInt(process.env.COINMARKETCAP_CONTEXT_HISTORICAL_ACCESS_MONTHS, 36);
|
|
95
|
+
var isExchangeLiquidityBackfillEnabled = () => parseEnabledFlag(
|
|
96
|
+
process.env.COINMARKETCAP_CONTEXT_EXCHANGE_LIQUIDITY_ENABLED,
|
|
97
|
+
true
|
|
98
|
+
);
|
|
99
|
+
var isFearGreedBackfillEnabled = () => parseEnabledFlag(process.env.COINMARKETCAP_CONTEXT_FEAR_GREED_ENABLED, true);
|
|
100
|
+
var isVerboseRequestLoggingEnabled = () => parseEnabledFlag(process.env.COINMARKETCAP_CONTEXT_VERBOSE_REQUESTS, false);
|
|
101
|
+
var getFearGreedPageSize = () => asInt(process.env.COINMARKETCAP_CONTEXT_FEAR_GREED_PAGE_SIZE, 500);
|
|
102
|
+
var getContextMaxAgeMs = () => asInt(
|
|
103
|
+
process.env.COINMARKETCAP_CONTEXT_MAX_AGE_MS,
|
|
104
|
+
DEFAULT_CONTEXT_MAX_AGE_MS
|
|
105
|
+
);
|
|
106
|
+
var getFearGreedStaleRetryMs = () => asInt(
|
|
107
|
+
process.env.COINMARKETCAP_CONTEXT_FEAR_GREED_STALE_RETRY_MS,
|
|
108
|
+
DEFAULT_FEAR_GREED_STALE_RETRY_MS
|
|
109
|
+
);
|
|
110
|
+
var getExchangeSlugs = () => {
|
|
111
|
+
const raw = process.env.COINMARKETCAP_CONTEXT_EXCHANGE_SLUGS;
|
|
112
|
+
const values = String(raw ?? "").split(",").map((item) => item.trim().toLowerCase()).filter(Boolean);
|
|
113
|
+
return values.length ? [...new Set(values)] : [...DEFAULT_EXCHANGE_SLUGS];
|
|
114
|
+
};
|
|
115
|
+
var toFiniteNumberOrNull = (value) => {
|
|
116
|
+
const numeric = typeof value === "number" ? value : typeof value === "string" && value.trim() ? Number(value) : Number.NaN;
|
|
117
|
+
return Number.isFinite(numeric) ? numeric : null;
|
|
118
|
+
};
|
|
119
|
+
var toIntOrNull = (value) => {
|
|
120
|
+
const numeric = toFiniteNumberOrNull(value);
|
|
121
|
+
return numeric == null ? null : Math.trunc(numeric);
|
|
122
|
+
};
|
|
123
|
+
var getRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
124
|
+
var getNestedRecord = (value, key) => getRecord(value[key]);
|
|
125
|
+
var safeDivide = (numerator, denominator) => numerator != null && denominator != null && denominator > 0 ? numerator / denominator : null;
|
|
126
|
+
var sumFinite = (values) => values.reduce((sum, value) => sum + (value ?? 0), 0);
|
|
127
|
+
var toExchangeLiquidityRegime = ({
|
|
128
|
+
binanceVolumeShare,
|
|
129
|
+
topExchangeVolumeShare
|
|
130
|
+
}) => {
|
|
131
|
+
if (binanceVolumeShare == null && topExchangeVolumeShare == null) {
|
|
132
|
+
return "unknown";
|
|
133
|
+
}
|
|
134
|
+
if ((binanceVolumeShare ?? 0) >= 0.55) return "binance_led";
|
|
135
|
+
if ((topExchangeVolumeShare ?? 0) >= 0.6) return "concentrated";
|
|
136
|
+
return "balanced";
|
|
137
|
+
};
|
|
138
|
+
var normalizeFearGreedClassification = (value) => {
|
|
139
|
+
const normalized = String(value ?? "").trim().toLowerCase();
|
|
140
|
+
if (normalized === "extreme fear") return "Extreme Fear";
|
|
141
|
+
if (normalized === "fear") return "Fear";
|
|
142
|
+
if (normalized === "neutral") return "Neutral";
|
|
143
|
+
if (normalized === "greed") return "Greed";
|
|
144
|
+
if (normalized === "extreme greed") return "Extreme Greed";
|
|
145
|
+
return "Unknown";
|
|
146
|
+
};
|
|
147
|
+
var toFearGreedRegime = ({
|
|
148
|
+
value,
|
|
149
|
+
classification
|
|
150
|
+
}) => {
|
|
151
|
+
if (value == null) return "unknown";
|
|
152
|
+
if (classification === "Extreme Fear" || value <= 24) return "capitulation";
|
|
153
|
+
if (classification === "Fear" || value <= 44) return "risk_off";
|
|
154
|
+
if (classification === "Extreme Greed" || value >= 75) return "euphoric";
|
|
155
|
+
if (classification === "Greed" || value >= 55) return "risk_on";
|
|
156
|
+
if (classification === "Neutral" || value >= 45 && value <= 54) {
|
|
157
|
+
return "neutral";
|
|
158
|
+
}
|
|
159
|
+
return "unknown";
|
|
160
|
+
};
|
|
161
|
+
var dayStartUtc = (ms) => {
|
|
162
|
+
const date = new Date(ms);
|
|
163
|
+
return Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate());
|
|
164
|
+
};
|
|
165
|
+
var toIsoDate = (ms) => new Date(ms).toISOString().slice(0, 10);
|
|
166
|
+
var subtractUtcMonths = (ms, months) => {
|
|
167
|
+
const date = new Date(ms);
|
|
168
|
+
const targetMonthIndex = date.getUTCMonth() - months;
|
|
169
|
+
const targetMonthStart = new Date(
|
|
170
|
+
Date.UTC(
|
|
171
|
+
date.getUTCFullYear(),
|
|
172
|
+
targetMonthIndex,
|
|
173
|
+
1,
|
|
174
|
+
date.getUTCHours(),
|
|
175
|
+
date.getUTCMinutes(),
|
|
176
|
+
date.getUTCSeconds(),
|
|
177
|
+
date.getUTCMilliseconds()
|
|
178
|
+
)
|
|
179
|
+
);
|
|
180
|
+
const daysInTargetMonth = new Date(
|
|
181
|
+
Date.UTC(
|
|
182
|
+
targetMonthStart.getUTCFullYear(),
|
|
183
|
+
targetMonthStart.getUTCMonth() + 1,
|
|
184
|
+
0
|
|
185
|
+
)
|
|
186
|
+
).getUTCDate();
|
|
187
|
+
targetMonthStart.setUTCDate(Math.min(date.getUTCDate(), daysInTargetMonth));
|
|
188
|
+
return targetMonthStart.getTime();
|
|
189
|
+
};
|
|
190
|
+
var nextUtcDayStart = (ms) => dayStartUtc(ms) + DAY_MS;
|
|
191
|
+
var resolveCoinMarketCapBackfillWindow = (params) => {
|
|
192
|
+
const warmupMs = asInt(process.env.COINMARKETCAP_CONTEXT_BACKFILL_WARMUP_DAYS, 35) * DAY_MS;
|
|
193
|
+
const maxWindowMs = getMaxBackfillDays() * DAY_MS;
|
|
194
|
+
const requestedStart = params.preloadStartMs ?? params.startMs - warmupMs;
|
|
195
|
+
const cappedStart = Math.max(requestedStart, params.endMs - maxWindowMs);
|
|
196
|
+
const accessFloorMs = nextUtcDayStart(
|
|
197
|
+
subtractUtcMonths(params.nowMs ?? Date.now(), getHistoricalAccessMonths())
|
|
198
|
+
);
|
|
199
|
+
return {
|
|
200
|
+
fromMs: dayStartUtc(Math.max(cappedStart, accessFloorMs)),
|
|
201
|
+
toMs: dayStartUtc(params.endMs)
|
|
202
|
+
};
|
|
203
|
+
};
|
|
204
|
+
var hasIntervalCoverage = (coverage, fromMs, toMs, intervalMs) => {
|
|
205
|
+
if (!coverage) return false;
|
|
206
|
+
const expectedRows = Math.max(1, Math.floor((toMs - fromMs) / intervalMs));
|
|
207
|
+
return coverage.firstMs <= fromMs + intervalMs && coverage.lastMs >= toMs - intervalMs && coverage.rows >= Math.floor(expectedRows * 0.9);
|
|
208
|
+
};
|
|
209
|
+
var hasDailyCoverage = (coverage, fromMs, toMs) => hasIntervalCoverage(coverage, fromMs, toMs, DAY_MS);
|
|
210
|
+
var coverageKey = (params) => [
|
|
211
|
+
params.source.trim().toLowerCase(),
|
|
212
|
+
params.scope.trim().toLowerCase(),
|
|
213
|
+
params.interval.trim().toLowerCase(),
|
|
214
|
+
Math.trunc(params.fromMs),
|
|
215
|
+
Math.trunc(params.toMs)
|
|
216
|
+
].join(":");
|
|
217
|
+
var isMarketContextBackfillCoverageDataBearing = (row) => Number(row.rowsCount ?? 0) > 0;
|
|
218
|
+
var coverageRowsToKeySet = (rows) => new Set(
|
|
219
|
+
rows.filter(isMarketContextBackfillCoverageDataBearing).map(
|
|
220
|
+
(row) => coverageKey({
|
|
221
|
+
source: row.source,
|
|
222
|
+
scope: row.scope,
|
|
223
|
+
interval: row.interval,
|
|
224
|
+
fromMs: row.fromMs,
|
|
225
|
+
toMs: row.toMs
|
|
226
|
+
})
|
|
227
|
+
)
|
|
228
|
+
);
|
|
229
|
+
var hasBackfillCoverage = (coverageKeys, params) => coverageKeys.has(coverageKey(params));
|
|
230
|
+
var hasRecentBackfillCoverage = (rows, params) => {
|
|
231
|
+
const key = coverageKey(params);
|
|
232
|
+
return rows.some((row) => {
|
|
233
|
+
if (!isMarketContextBackfillCoverageDataBearing(row)) return false;
|
|
234
|
+
if (coverageKey({
|
|
235
|
+
source: row.source,
|
|
236
|
+
scope: row.scope,
|
|
237
|
+
interval: row.interval,
|
|
238
|
+
fromMs: row.fromMs,
|
|
239
|
+
toMs: row.toMs
|
|
240
|
+
}) !== key) {
|
|
241
|
+
return false;
|
|
242
|
+
}
|
|
243
|
+
const checkedAtMs = Number(row.checkedAtMs);
|
|
244
|
+
return Number.isFinite(checkedAtMs) && Math.max(0, params.atMs - checkedAtMs) <= params.maxAgeMs;
|
|
245
|
+
});
|
|
246
|
+
};
|
|
247
|
+
var formatCoverageRange = (coverage) => {
|
|
248
|
+
if (!coverage) return "coverage=none";
|
|
249
|
+
return `coverage=${new Date(coverage.firstMs).toISOString()}..${new Date(
|
|
250
|
+
coverage.lastMs
|
|
251
|
+
).toISOString()} rows=${coverage.rows}`;
|
|
252
|
+
};
|
|
253
|
+
var formatReferenceCoverage = (coverage) => REFERENCE_ASSETS.map(
|
|
254
|
+
(asset) => `${asset.symbol}:${formatCoverageRange(coverage.get(asset.symbol))}`
|
|
255
|
+
).join(" ");
|
|
256
|
+
var resolveBackfillStatus = ({
|
|
257
|
+
enabled = true,
|
|
258
|
+
cached,
|
|
259
|
+
covered
|
|
260
|
+
}) => {
|
|
261
|
+
if (!enabled) return "disabled";
|
|
262
|
+
if (cached) return "cached";
|
|
263
|
+
if (covered) return "backfill_covered";
|
|
264
|
+
return "fetch_pending";
|
|
265
|
+
};
|
|
266
|
+
var formatSourceStatus = ({
|
|
267
|
+
name,
|
|
268
|
+
status,
|
|
269
|
+
rows,
|
|
270
|
+
coverage
|
|
271
|
+
}) => `${name}=${status}${rows == null ? "" : ` rows=${rows}`} ${coverage}`;
|
|
272
|
+
var createCoinMarketCapFetchStats = () => ({
|
|
273
|
+
requests: 0,
|
|
274
|
+
totalCredits: 0,
|
|
275
|
+
byPath: /* @__PURE__ */ new Map()
|
|
276
|
+
});
|
|
277
|
+
var getCoinMarketCapPathLabel = (path) => {
|
|
278
|
+
if (path.includes("global-metrics")) return "global";
|
|
279
|
+
if (path.includes("cryptocurrency/quotes")) return "reference";
|
|
280
|
+
if (path.includes("exchange/quotes")) return "exchangeLiquidity";
|
|
281
|
+
if (path.includes("fear-and-greed")) return "fearGreed";
|
|
282
|
+
if (path.includes("cmc100")) return "cmc100";
|
|
283
|
+
if (path.includes("cmc20")) return "cmc20";
|
|
284
|
+
return path;
|
|
285
|
+
};
|
|
286
|
+
var recordCoinMarketCapFetchStats = (stats, path, creditCount) => {
|
|
287
|
+
if (!stats) return;
|
|
288
|
+
const existing = stats.byPath.get(path) ?? { requests: 0, credits: 0 };
|
|
289
|
+
existing.requests += 1;
|
|
290
|
+
existing.credits += creditCount ?? 0;
|
|
291
|
+
stats.requests += 1;
|
|
292
|
+
stats.totalCredits += creditCount ?? 0;
|
|
293
|
+
stats.byPath.set(path, existing);
|
|
294
|
+
};
|
|
295
|
+
var formatCoinMarketCapFetchStats = (stats) => {
|
|
296
|
+
const byPath = [...stats.byPath.entries()].map(
|
|
297
|
+
([path, item]) => `${getCoinMarketCapPathLabel(path)}=${item.requests}req/${item.credits}cr`
|
|
298
|
+
).join(" ");
|
|
299
|
+
return `apiRequests=${stats.requests} apiCredits=${stats.totalCredits}${byPath ? ` ${byPath}` : ""}`;
|
|
300
|
+
};
|
|
301
|
+
var formatRowTimeRange = (rows) => {
|
|
302
|
+
if (!rows.length) return "range=none";
|
|
303
|
+
const times = rows.map((row) => row.ts.getTime()).filter((value) => Number.isFinite(value));
|
|
304
|
+
if (!times.length) return "range=none";
|
|
305
|
+
return `range=${new Date(Math.min(...times)).toISOString()}..${new Date(
|
|
306
|
+
Math.max(...times)
|
|
307
|
+
).toISOString()}`;
|
|
308
|
+
};
|
|
309
|
+
var logBackfillSourceDone = (params) => {
|
|
310
|
+
console.log(
|
|
311
|
+
import_chalk.default.gray(
|
|
312
|
+
`coinmarketcap context source done: ${params.name}=${params.status} rows=${params.rows}${params.details ? ` ${params.details}` : ""}`
|
|
313
|
+
)
|
|
314
|
+
);
|
|
315
|
+
};
|
|
316
|
+
var markBackfillCoverage = (rows) => (0, import_timescale.upsertMarketContextBackfillCoverage)(rows);
|
|
317
|
+
var coinMarketCapFetch = async (params) => {
|
|
318
|
+
const url = new URL(`${getBaseUrl()}${params.path}`);
|
|
319
|
+
for (const [key, value] of Object.entries(params.searchParams)) {
|
|
320
|
+
url.searchParams.set(key, value);
|
|
321
|
+
}
|
|
322
|
+
for (let attempt = 0; attempt <= getMaxRetries(); attempt += 1) {
|
|
323
|
+
const waitMs = Math.max(
|
|
324
|
+
0,
|
|
325
|
+
lastRequestTs + getRequestDelayMs() - Date.now()
|
|
326
|
+
);
|
|
327
|
+
if (waitMs > 0) {
|
|
328
|
+
await (0, import_async.delay)(waitMs);
|
|
329
|
+
}
|
|
330
|
+
lastRequestTs = Date.now();
|
|
331
|
+
const response = await fetch(url.toString(), {
|
|
332
|
+
headers: {
|
|
333
|
+
accept: "application/json",
|
|
334
|
+
"X-CMC_PRO_API_KEY": params.apiKey
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
if (response.ok) {
|
|
338
|
+
const payload = await response.json();
|
|
339
|
+
const creditCount = toFiniteNumberOrNull(
|
|
340
|
+
getRecord(getRecord(payload).status).credit_count
|
|
341
|
+
);
|
|
342
|
+
recordCoinMarketCapFetchStats(params.stats, params.path, creditCount);
|
|
343
|
+
if (creditCount != null && isVerboseRequestLoggingEnabled()) {
|
|
344
|
+
console.log(
|
|
345
|
+
import_chalk.default.gray(`coinmarketcap ${params.path}: credits=${creditCount}`)
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
return payload;
|
|
349
|
+
}
|
|
350
|
+
const text = await response.text();
|
|
351
|
+
const retryAfterRaw = Number(response.headers.get("retry-after") ?? "");
|
|
352
|
+
const retryAfterMs = Number.isFinite(retryAfterRaw) && retryAfterRaw > 0 ? retryAfterRaw * 1e3 : null;
|
|
353
|
+
const transient = response.status === 429 || response.status >= 500;
|
|
354
|
+
if (attempt < getMaxRetries() && transient) {
|
|
355
|
+
await (0, import_async.delay)(retryAfterMs ?? Math.min(3e4, 1e3 * 2 ** attempt));
|
|
356
|
+
continue;
|
|
357
|
+
}
|
|
358
|
+
throw new Error(`CoinMarketCap ${params.path} ${response.status}: ${text}`);
|
|
359
|
+
}
|
|
360
|
+
return null;
|
|
361
|
+
};
|
|
362
|
+
var fetchCoinMarketCapIndexRows = async (params) => {
|
|
363
|
+
const rows = [];
|
|
364
|
+
const maxChunkDays = 9;
|
|
365
|
+
const totalChunks = Math.max(
|
|
366
|
+
1,
|
|
367
|
+
Math.ceil(
|
|
368
|
+
(params.toMs - params.fromMs + DAY_MS) / ((maxChunkDays + 1) * DAY_MS)
|
|
369
|
+
)
|
|
370
|
+
);
|
|
371
|
+
let chunk = 0;
|
|
372
|
+
for (let cursorMs = params.fromMs; cursorMs <= params.toMs; cursorMs += (maxChunkDays + 1) * DAY_MS) {
|
|
373
|
+
chunk += 1;
|
|
374
|
+
const chunkEndMs = Math.min(params.toMs, cursorMs + maxChunkDays * DAY_MS);
|
|
375
|
+
const payload = await coinMarketCapFetch({
|
|
376
|
+
path: params.index.path,
|
|
377
|
+
apiKey: params.apiKey,
|
|
378
|
+
stats: params.stats,
|
|
379
|
+
searchParams: {
|
|
380
|
+
time_start: new Date(cursorMs).toISOString(),
|
|
381
|
+
time_end: new Date(chunkEndMs).toISOString(),
|
|
382
|
+
interval: "daily"
|
|
383
|
+
}
|
|
384
|
+
});
|
|
385
|
+
rows.push(
|
|
386
|
+
...coinMarketCapIndexPayloadToRows(payload, params.index.slug, "1d")
|
|
387
|
+
);
|
|
388
|
+
if (chunk === 1 || chunk === totalChunks || chunk % 25 === 0) {
|
|
389
|
+
console.log(
|
|
390
|
+
import_chalk.default.gray(
|
|
391
|
+
`coinmarketcap index backfill progress: ${params.index.slug} chunk=${chunk}/${totalChunks} rows=${rows.length} chunkRange=${new Date(cursorMs).toISOString()}..${new Date(chunkEndMs).toISOString()}`
|
|
392
|
+
)
|
|
393
|
+
);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
return { rows, chunks: chunk };
|
|
397
|
+
};
|
|
398
|
+
var coinMarketCapGlobalPayloadToRows = (payload, source = SOURCE_GLOBAL_DAILY) => {
|
|
399
|
+
const quotes = getRecord(getRecord(payload).data).quotes;
|
|
400
|
+
if (!Array.isArray(quotes)) return [];
|
|
401
|
+
return quotes.map((item) => {
|
|
402
|
+
const record = getRecord(item);
|
|
403
|
+
const quote = getRecord(getRecord(record.quote).USD);
|
|
404
|
+
const tsRaw = record.timestamp ?? quote.timestamp;
|
|
405
|
+
const ts = typeof tsRaw === "string" ? new Date(tsRaw) : null;
|
|
406
|
+
if (!ts || Number.isNaN(ts.getTime())) return null;
|
|
407
|
+
const totalMarketCapUsd = toFiniteNumberOrNull(quote.total_market_cap);
|
|
408
|
+
const altMarketCapUsd = toFiniteNumberOrNull(quote.altcoin_market_cap);
|
|
409
|
+
const btcToAltMarketCapRatio = totalMarketCapUsd != null && altMarketCapUsd != null && altMarketCapUsd > 0 ? Math.max(0, totalMarketCapUsd - altMarketCapUsd) / altMarketCapUsd : null;
|
|
410
|
+
return {
|
|
411
|
+
source,
|
|
412
|
+
ts,
|
|
413
|
+
updatedAt: ts,
|
|
414
|
+
activeCryptocurrencies: toIntOrNull(record.active_cryptocurrencies),
|
|
415
|
+
activeExchanges: toIntOrNull(record.active_exchanges),
|
|
416
|
+
activeMarketPairs: toIntOrNull(record.active_market_pairs),
|
|
417
|
+
markets: toIntOrNull(record.active_exchanges),
|
|
418
|
+
totalMarketCapUsd,
|
|
419
|
+
totalVolumeUsd: toFiniteNumberOrNull(quote.total_volume_24h),
|
|
420
|
+
totalVolumeReportedUsd: toFiniteNumberOrNull(
|
|
421
|
+
quote.total_volume_24h_reported
|
|
422
|
+
),
|
|
423
|
+
btcDominancePct: toFiniteNumberOrNull(record.btc_dominance),
|
|
424
|
+
ethDominancePct: toFiniteNumberOrNull(record.eth_dominance),
|
|
425
|
+
altMarketCapUsd,
|
|
426
|
+
altVolumeUsd: toFiniteNumberOrNull(quote.altcoin_volume_24h),
|
|
427
|
+
altVolumeReportedUsd: toFiniteNumberOrNull(
|
|
428
|
+
quote.altcoin_volume_24h_reported
|
|
429
|
+
),
|
|
430
|
+
btcToAltMarketCapRatio,
|
|
431
|
+
marketCapChangePct24hUsd: null
|
|
432
|
+
};
|
|
433
|
+
}).filter((row) => row != null);
|
|
434
|
+
};
|
|
435
|
+
var normalizeAssetDataItems = (payload) => {
|
|
436
|
+
const data = getRecord(payload).data;
|
|
437
|
+
if (!data) return [];
|
|
438
|
+
if (Array.isArray(data)) {
|
|
439
|
+
return data.filter(
|
|
440
|
+
(item) => item != null && typeof item === "object" && !Array.isArray(item)
|
|
441
|
+
);
|
|
442
|
+
}
|
|
443
|
+
if (typeof data === "object" && !Array.isArray(data)) {
|
|
444
|
+
const record = data;
|
|
445
|
+
if (Array.isArray(record.quotes)) return [record];
|
|
446
|
+
return Object.values(record).filter(
|
|
447
|
+
(item) => item != null && typeof item === "object" && !Array.isArray(item)
|
|
448
|
+
);
|
|
449
|
+
}
|
|
450
|
+
return [];
|
|
451
|
+
};
|
|
452
|
+
var coinMarketCapHistoricalQuotesPayloadToRows = (payload, interval = "1d") => {
|
|
453
|
+
return normalizeAssetDataItems(payload).flatMap((asset) => {
|
|
454
|
+
const cmcId = toIntOrNull(asset.id);
|
|
455
|
+
const symbolRaw = String(asset.symbol ?? "").trim().toUpperCase();
|
|
456
|
+
const target = REFERENCE_ASSETS.find(
|
|
457
|
+
(item) => item.cmcId === cmcId || item.symbol.replace("USDT", "") === symbolRaw
|
|
458
|
+
);
|
|
459
|
+
const quotes = Array.isArray(asset.quotes) ? asset.quotes : Object.values(getRecord(asset.quotes));
|
|
460
|
+
if (!target || !cmcId || !quotes.length) return [];
|
|
461
|
+
return quotes.map((item) => {
|
|
462
|
+
const record = getRecord(item);
|
|
463
|
+
const quote = getNestedRecord(getRecord(record.quote), "USD");
|
|
464
|
+
const tsRaw = record.timestamp ?? quote.timestamp ?? quote.last_updated;
|
|
465
|
+
const ts = typeof tsRaw === "string" ? new Date(tsRaw) : null;
|
|
466
|
+
if (!ts || Number.isNaN(ts.getTime())) return null;
|
|
467
|
+
return {
|
|
468
|
+
source: SOURCE_REFERENCE,
|
|
469
|
+
symbol: target.symbol,
|
|
470
|
+
cmcId,
|
|
471
|
+
interval,
|
|
472
|
+
ts,
|
|
473
|
+
openUsd: null,
|
|
474
|
+
highUsd: null,
|
|
475
|
+
lowUsd: null,
|
|
476
|
+
closeUsd: toFiniteNumberOrNull(quote.price),
|
|
477
|
+
volumeUsd: toFiniteNumberOrNull(quote.volume_24h),
|
|
478
|
+
marketCapUsd: toFiniteNumberOrNull(quote.market_cap)
|
|
479
|
+
};
|
|
480
|
+
}).filter((row) => row != null);
|
|
481
|
+
});
|
|
482
|
+
};
|
|
483
|
+
var coinMarketCapExchangeQuotesPayloadToLiquidityRows = (payload, interval = "1d") => {
|
|
484
|
+
const data = getRecord(payload).data;
|
|
485
|
+
const exchangeItems = Array.isArray(data) ? data : Object.values(getRecord(data));
|
|
486
|
+
const rowsByTs = /* @__PURE__ */ new Map();
|
|
487
|
+
for (const item of exchangeItems) {
|
|
488
|
+
const exchange = getRecord(item);
|
|
489
|
+
const slug = String(exchange.slug ?? "").trim().toLowerCase();
|
|
490
|
+
const name = String(exchange.name ?? "").trim().toLowerCase();
|
|
491
|
+
const quotes = Array.isArray(exchange.quotes) ? exchange.quotes : Array.isArray(exchange.quote) ? exchange.quote : [exchange];
|
|
492
|
+
for (const quoteItem of quotes) {
|
|
493
|
+
const record = getRecord(quoteItem);
|
|
494
|
+
const quote = getNestedRecord(getNestedRecord(record, "quote"), "USD");
|
|
495
|
+
const fallbackQuote = getNestedRecord(exchange, "quote");
|
|
496
|
+
const usd = Object.keys(quote).length ? quote : getNestedRecord(fallbackQuote, "USD");
|
|
497
|
+
const tsRaw = record.timestamp ?? usd.timestamp ?? usd.last_updated ?? exchange.last_updated;
|
|
498
|
+
const ts = typeof tsRaw === "string" ? new Date(tsRaw) : null;
|
|
499
|
+
if (!ts || Number.isNaN(ts.getTime())) continue;
|
|
500
|
+
const volumeUsd = toFiniteNumberOrNull(usd.volume_24h) ?? toFiniteNumberOrNull(usd.volume_24h_adjusted) ?? toFiniteNumberOrNull(usd.spot_volume_usd) ?? toFiniteNumberOrNull(usd.reported_volume_24h);
|
|
501
|
+
const key = ts.getTime();
|
|
502
|
+
const rows = rowsByTs.get(key) ?? [];
|
|
503
|
+
rows.push({ slug, name, volumeUsd });
|
|
504
|
+
rowsByTs.set(key, rows);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
return [...rowsByTs.entries()].sort(([left], [right]) => left - right).map(([ts, exchanges]) => {
|
|
508
|
+
const volumes = exchanges.map((exchange) => exchange.volumeUsd);
|
|
509
|
+
const totalVolumeUsd = sumFinite(volumes);
|
|
510
|
+
const binanceVolumeUsd = exchanges.find(
|
|
511
|
+
(exchange) => exchange.slug === "binance" || exchange.name.includes("binance")
|
|
512
|
+
)?.volumeUsd ?? null;
|
|
513
|
+
const topExchangeVolume = Math.max(
|
|
514
|
+
...volumes.map((value) => value ?? 0),
|
|
515
|
+
0
|
|
516
|
+
);
|
|
517
|
+
const binanceVolumeShare = safeDivide(binanceVolumeUsd, totalVolumeUsd);
|
|
518
|
+
const topExchangeVolumeShare = safeDivide(
|
|
519
|
+
topExchangeVolume,
|
|
520
|
+
totalVolumeUsd
|
|
521
|
+
);
|
|
522
|
+
return {
|
|
523
|
+
source: SOURCE_EXCHANGE_LIQUIDITY,
|
|
524
|
+
interval,
|
|
525
|
+
ts: new Date(ts),
|
|
526
|
+
exchangesCount: exchanges.length,
|
|
527
|
+
totalVolumeUsd,
|
|
528
|
+
binanceVolumeUsd,
|
|
529
|
+
binanceVolumeShare,
|
|
530
|
+
topExchangeVolumeShare,
|
|
531
|
+
liquidityRegime: toExchangeLiquidityRegime({
|
|
532
|
+
binanceVolumeShare,
|
|
533
|
+
topExchangeVolumeShare
|
|
534
|
+
})
|
|
535
|
+
};
|
|
536
|
+
});
|
|
537
|
+
};
|
|
538
|
+
var coinMarketCapFearGreedPayloadToRows = (payload) => {
|
|
539
|
+
const data = getRecord(payload).data;
|
|
540
|
+
const items = Array.isArray(data) ? data : [];
|
|
541
|
+
return items.map((item) => {
|
|
542
|
+
const record = getRecord(item);
|
|
543
|
+
const timestampValue = toFiniteNumberOrNull(record.timestamp);
|
|
544
|
+
const value = toIntOrNull(record.value);
|
|
545
|
+
if (timestampValue == null || value == null) return null;
|
|
546
|
+
const ts = new Date(timestampValue * 1e3);
|
|
547
|
+
if (Number.isNaN(ts.getTime())) return null;
|
|
548
|
+
const classification = normalizeFearGreedClassification(
|
|
549
|
+
record.value_classification
|
|
550
|
+
);
|
|
551
|
+
return {
|
|
552
|
+
source: SOURCE_FEAR_GREED,
|
|
553
|
+
interval: "1d",
|
|
554
|
+
ts,
|
|
555
|
+
value,
|
|
556
|
+
classification,
|
|
557
|
+
sentimentRegime: toFearGreedRegime({ value, classification })
|
|
558
|
+
};
|
|
559
|
+
}).filter((row) => row != null);
|
|
560
|
+
};
|
|
561
|
+
var toCmcIndexWeightPct = (value) => {
|
|
562
|
+
if (typeof value === "string") {
|
|
563
|
+
return toFiniteNumberOrNull(value.replace("%", ""));
|
|
564
|
+
}
|
|
565
|
+
return toFiniteNumberOrNull(value);
|
|
566
|
+
};
|
|
567
|
+
var coinMarketCapIndexPayloadToRows = (payload, indexSlug, interval = "1d") => {
|
|
568
|
+
const data = getRecord(payload).data;
|
|
569
|
+
const items = Array.isArray(data) ? data : [];
|
|
570
|
+
return items.map((item) => {
|
|
571
|
+
const record = getRecord(item);
|
|
572
|
+
const tsRaw = record.update_time ?? record.timestamp;
|
|
573
|
+
const ts = typeof tsRaw === "string" ? new Date(tsRaw) : null;
|
|
574
|
+
const value = toFiniteNumberOrNull(record.value);
|
|
575
|
+
if (!ts || Number.isNaN(ts.getTime()) || value == null) return null;
|
|
576
|
+
const constituentsRaw = Array.isArray(record.constituents) ? record.constituents : [];
|
|
577
|
+
const constituents = constituentsRaw.map((constituent) => {
|
|
578
|
+
const constituentRecord = getRecord(constituent);
|
|
579
|
+
return {
|
|
580
|
+
id: toIntOrNull(constituentRecord.id),
|
|
581
|
+
name: typeof constituentRecord.name === "string" ? constituentRecord.name : null,
|
|
582
|
+
symbol: typeof constituentRecord.symbol === "string" ? constituentRecord.symbol : null,
|
|
583
|
+
url: typeof constituentRecord.url === "string" ? constituentRecord.url : null,
|
|
584
|
+
weightPct: toCmcIndexWeightPct(constituentRecord.weight),
|
|
585
|
+
priceUsd: toFiniteNumberOrNull(constituentRecord.priceUsd),
|
|
586
|
+
units: toFiniteNumberOrNull(constituentRecord.units)
|
|
587
|
+
};
|
|
588
|
+
});
|
|
589
|
+
const topConstituent = constituents.reduce((top, constituent) => {
|
|
590
|
+
if (top == null) return constituent;
|
|
591
|
+
return (constituent.weightPct ?? Number.NEGATIVE_INFINITY) > (top.weightPct ?? Number.NEGATIVE_INFINITY) ? constituent : top;
|
|
592
|
+
}, null);
|
|
593
|
+
return {
|
|
594
|
+
source: SOURCE_INDEX,
|
|
595
|
+
indexSlug,
|
|
596
|
+
interval,
|
|
597
|
+
ts,
|
|
598
|
+
value,
|
|
599
|
+
constituentsCount: constituents.length,
|
|
600
|
+
topConstituentSymbol: topConstituent?.symbol ?? null,
|
|
601
|
+
topConstituentWeightPct: topConstituent?.weightPct ?? null,
|
|
602
|
+
constituents
|
|
603
|
+
};
|
|
604
|
+
}).filter((row) => row != null);
|
|
605
|
+
};
|
|
606
|
+
var shouldBackfillCoinMarketCapContextForMode = ({
|
|
607
|
+
mode,
|
|
608
|
+
aiEnabled,
|
|
609
|
+
cacheOnly,
|
|
610
|
+
mlEnabled
|
|
611
|
+
}) => parseEnabledFlag(
|
|
612
|
+
process.env.COINMARKETCAP_CONTEXT_BACKFILL_ENABLED,
|
|
613
|
+
mode === "backtest" ? (Boolean(aiEnabled) || Boolean(mlEnabled)) && !cacheOnly : !cacheOnly
|
|
614
|
+
);
|
|
615
|
+
var shouldBackfillCoinMarketCapContextForBacktest = (params) => shouldBackfillCoinMarketCapContextForMode({
|
|
616
|
+
mode: "backtest",
|
|
617
|
+
...params
|
|
618
|
+
});
|
|
619
|
+
var shouldBackfillCoinMarketCapContextForReplay = (params) => shouldBackfillCoinMarketCapContextForMode({
|
|
620
|
+
mode: "replay",
|
|
621
|
+
...params
|
|
622
|
+
});
|
|
623
|
+
var shouldBackfillCoinMarketCapContextForSignals = (params) => shouldBackfillCoinMarketCapContextForMode({
|
|
624
|
+
mode: "signals",
|
|
625
|
+
...params
|
|
626
|
+
});
|
|
627
|
+
var skippedResult = (cached = false) => ({
|
|
628
|
+
skipped: true,
|
|
629
|
+
globalRows: 0,
|
|
630
|
+
referenceRows: 0,
|
|
631
|
+
breadthRows: 0,
|
|
632
|
+
exchangeLiquidityRows: 0,
|
|
633
|
+
fearGreedRows: 0,
|
|
634
|
+
indexRows: 0,
|
|
635
|
+
cached
|
|
636
|
+
});
|
|
637
|
+
var backfillCoinMarketCapContext = async (params) => {
|
|
638
|
+
if (!Number.isFinite(params.startMs) || !Number.isFinite(params.endMs)) {
|
|
639
|
+
return skippedResult();
|
|
640
|
+
}
|
|
641
|
+
const { fromMs, toMs } = resolveCoinMarketCapBackfillWindow(params);
|
|
642
|
+
if (toMs <= fromMs) return skippedResult();
|
|
643
|
+
const exchangeLiquidityEnabled = isExchangeLiquidityBackfillEnabled();
|
|
644
|
+
const fearGreedEnabled = isFearGreedBackfillEnabled();
|
|
645
|
+
await (0, import_timescale.waitForDbReady)();
|
|
646
|
+
const [
|
|
647
|
+
globalDailyCoverage,
|
|
648
|
+
referenceDailyCoverage,
|
|
649
|
+
exchangeLiquidityCoverage,
|
|
650
|
+
fearGreedCoverage,
|
|
651
|
+
indexCoverage,
|
|
652
|
+
globalDailyBackfillCoverage,
|
|
653
|
+
referenceDailyBackfillCoverage,
|
|
654
|
+
exchangeLiquidityBackfillCoverage,
|
|
655
|
+
fearGreedBackfillCoverage,
|
|
656
|
+
indexBackfillCoverage
|
|
657
|
+
] = await Promise.all([
|
|
658
|
+
(0, import_timescale.getMarketGlobalContextCoverage)({
|
|
659
|
+
source: SOURCE_GLOBAL_DAILY,
|
|
660
|
+
startMs: fromMs,
|
|
661
|
+
endMs: toMs
|
|
662
|
+
}),
|
|
663
|
+
(0, import_timescale.getMarketReferenceAssetContextCoverage)({
|
|
664
|
+
source: SOURCE_REFERENCE,
|
|
665
|
+
symbols: REFERENCE_ASSETS.map((item) => item.symbol),
|
|
666
|
+
interval: "1d",
|
|
667
|
+
startMs: fromMs,
|
|
668
|
+
endMs: toMs
|
|
669
|
+
}),
|
|
670
|
+
exchangeLiquidityEnabled ? (0, import_timescale.getMarketCmcExchangeLiquidityContextCoverage)({
|
|
671
|
+
source: SOURCE_EXCHANGE_LIQUIDITY,
|
|
672
|
+
interval: "1d",
|
|
673
|
+
startMs: fromMs,
|
|
674
|
+
endMs: toMs
|
|
675
|
+
}) : Promise.resolve(null),
|
|
676
|
+
fearGreedEnabled ? (0, import_timescale.getMarketCmcFearGreedContextCoverage)({
|
|
677
|
+
source: SOURCE_FEAR_GREED,
|
|
678
|
+
interval: "1d",
|
|
679
|
+
startMs: fromMs,
|
|
680
|
+
endMs: toMs
|
|
681
|
+
}) : Promise.resolve(null),
|
|
682
|
+
(0, import_timescale.getMarketCmcIndexContextCoverage)({
|
|
683
|
+
source: SOURCE_INDEX,
|
|
684
|
+
indexSlugs: CMC_INDEXES.map((index) => index.slug),
|
|
685
|
+
interval: "1d",
|
|
686
|
+
startMs: fromMs,
|
|
687
|
+
endMs: toMs
|
|
688
|
+
}),
|
|
689
|
+
(0, import_timescale.getMarketContextBackfillCoverage)({
|
|
690
|
+
source: SOURCE_GLOBAL_DAILY,
|
|
691
|
+
scopes: [COVERAGE_SCOPE_ALL],
|
|
692
|
+
interval: "1d",
|
|
693
|
+
fromMs,
|
|
694
|
+
toMs
|
|
695
|
+
}),
|
|
696
|
+
(0, import_timescale.getMarketContextBackfillCoverage)({
|
|
697
|
+
source: SOURCE_REFERENCE,
|
|
698
|
+
scopes: REFERENCE_ASSETS.map((item) => item.symbol),
|
|
699
|
+
interval: "1d",
|
|
700
|
+
fromMs,
|
|
701
|
+
toMs
|
|
702
|
+
}),
|
|
703
|
+
exchangeLiquidityEnabled ? (0, import_timescale.getMarketContextBackfillCoverage)({
|
|
704
|
+
source: SOURCE_EXCHANGE_LIQUIDITY,
|
|
705
|
+
scopes: [COVERAGE_SCOPE_ALL],
|
|
706
|
+
interval: "1d",
|
|
707
|
+
fromMs,
|
|
708
|
+
toMs
|
|
709
|
+
}) : Promise.resolve([]),
|
|
710
|
+
fearGreedEnabled ? (0, import_timescale.getMarketContextBackfillCoverage)({
|
|
711
|
+
source: SOURCE_FEAR_GREED,
|
|
712
|
+
scopes: [COVERAGE_SCOPE_ALL],
|
|
713
|
+
interval: "1d",
|
|
714
|
+
fromMs,
|
|
715
|
+
toMs
|
|
716
|
+
}) : Promise.resolve([]),
|
|
717
|
+
(0, import_timescale.getMarketContextBackfillCoverage)({
|
|
718
|
+
source: SOURCE_INDEX,
|
|
719
|
+
scopes: CMC_INDEXES.map((index) => index.slug),
|
|
720
|
+
interval: "1d",
|
|
721
|
+
fromMs,
|
|
722
|
+
toMs
|
|
723
|
+
})
|
|
724
|
+
]);
|
|
725
|
+
const globalDailyBackfillKeys = coverageRowsToKeySet(
|
|
726
|
+
globalDailyBackfillCoverage
|
|
727
|
+
);
|
|
728
|
+
const referenceDailyBackfillKeys = coverageRowsToKeySet(
|
|
729
|
+
referenceDailyBackfillCoverage
|
|
730
|
+
);
|
|
731
|
+
const exchangeLiquidityBackfillKeys = coverageRowsToKeySet(
|
|
732
|
+
exchangeLiquidityBackfillCoverage
|
|
733
|
+
);
|
|
734
|
+
const fearGreedBackfillKeys = coverageRowsToKeySet(fearGreedBackfillCoverage);
|
|
735
|
+
const indexBackfillKeys = coverageRowsToKeySet(indexBackfillCoverage);
|
|
736
|
+
const globalDailyCached = hasDailyCoverage(globalDailyCoverage, fromMs, toMs);
|
|
737
|
+
const referencesDailyCached = REFERENCE_ASSETS.every(
|
|
738
|
+
(asset) => hasDailyCoverage(referenceDailyCoverage.get(asset.symbol), fromMs, toMs)
|
|
739
|
+
);
|
|
740
|
+
const exchangeLiquidityCached = !exchangeLiquidityEnabled || hasDailyCoverage(exchangeLiquidityCoverage, fromMs, toMs);
|
|
741
|
+
const fearGreedCached = !fearGreedEnabled || hasDailyCoverage(fearGreedCoverage, fromMs, toMs);
|
|
742
|
+
const indexesCached = CMC_INDEXES.every(
|
|
743
|
+
(index) => hasDailyCoverage(indexCoverage.get(index.slug), fromMs, toMs)
|
|
744
|
+
);
|
|
745
|
+
const globalDailyBackfillCovered = hasBackfillCoverage(
|
|
746
|
+
globalDailyBackfillKeys,
|
|
747
|
+
{
|
|
748
|
+
source: SOURCE_GLOBAL_DAILY,
|
|
749
|
+
scope: COVERAGE_SCOPE_ALL,
|
|
750
|
+
interval: "1d",
|
|
751
|
+
fromMs,
|
|
752
|
+
toMs
|
|
753
|
+
}
|
|
754
|
+
);
|
|
755
|
+
const referencesDailyBackfillCovered = REFERENCE_ASSETS.every(
|
|
756
|
+
(asset) => hasBackfillCoverage(referenceDailyBackfillKeys, {
|
|
757
|
+
source: SOURCE_REFERENCE,
|
|
758
|
+
scope: asset.symbol,
|
|
759
|
+
interval: "1d",
|
|
760
|
+
fromMs,
|
|
761
|
+
toMs
|
|
762
|
+
})
|
|
763
|
+
);
|
|
764
|
+
const exchangeLiquidityBackfillCovered = hasBackfillCoverage(
|
|
765
|
+
exchangeLiquidityBackfillKeys,
|
|
766
|
+
{
|
|
767
|
+
source: SOURCE_EXCHANGE_LIQUIDITY,
|
|
768
|
+
scope: COVERAGE_SCOPE_ALL,
|
|
769
|
+
interval: "1d",
|
|
770
|
+
fromMs,
|
|
771
|
+
toMs
|
|
772
|
+
}
|
|
773
|
+
);
|
|
774
|
+
const fearGreedBackfillParams = {
|
|
775
|
+
source: SOURCE_FEAR_GREED,
|
|
776
|
+
scope: COVERAGE_SCOPE_ALL,
|
|
777
|
+
interval: "1d",
|
|
778
|
+
fromMs,
|
|
779
|
+
toMs
|
|
780
|
+
};
|
|
781
|
+
const fearGreedBackfillWasCovered = hasBackfillCoverage(
|
|
782
|
+
fearGreedBackfillKeys,
|
|
783
|
+
fearGreedBackfillParams
|
|
784
|
+
);
|
|
785
|
+
const fearGreedLatestAgeMs = fearGreedCoverage == null ? Number.POSITIVE_INFINITY : Math.max(0, params.endMs - fearGreedCoverage.lastMs);
|
|
786
|
+
const refreshStaleFearGreed = params.refreshStaleFearGreed === true && fearGreedLatestAgeMs > getContextMaxAgeMs();
|
|
787
|
+
const fearGreedBackfillCovered = fearGreedBackfillWasCovered && (!refreshStaleFearGreed || hasRecentBackfillCoverage(fearGreedBackfillCoverage, {
|
|
788
|
+
...fearGreedBackfillParams,
|
|
789
|
+
atMs: params.endMs,
|
|
790
|
+
maxAgeMs: getFearGreedStaleRetryMs()
|
|
791
|
+
}));
|
|
792
|
+
const indexesBackfillCovered = CMC_INDEXES.every(
|
|
793
|
+
(index) => hasBackfillCoverage(indexBackfillKeys, {
|
|
794
|
+
source: SOURCE_INDEX,
|
|
795
|
+
scope: index.slug,
|
|
796
|
+
interval: "1d",
|
|
797
|
+
fromMs,
|
|
798
|
+
toMs
|
|
799
|
+
})
|
|
800
|
+
);
|
|
801
|
+
const globalDailyReady = globalDailyCached || globalDailyBackfillCovered;
|
|
802
|
+
const referencesDailyReady = referencesDailyCached || referencesDailyBackfillCovered;
|
|
803
|
+
const exchangeLiquidityReady = exchangeLiquidityCached || !exchangeLiquidityEnabled || exchangeLiquidityBackfillCovered;
|
|
804
|
+
const fearGreedReady = fearGreedCached || !fearGreedEnabled || fearGreedBackfillCovered;
|
|
805
|
+
const indexesReady = indexesCached || indexesBackfillCovered;
|
|
806
|
+
let globalStatus = resolveBackfillStatus({
|
|
807
|
+
cached: globalDailyCached,
|
|
808
|
+
covered: globalDailyBackfillCovered
|
|
809
|
+
});
|
|
810
|
+
let referenceStatus = resolveBackfillStatus({
|
|
811
|
+
cached: referencesDailyCached,
|
|
812
|
+
covered: referencesDailyBackfillCovered
|
|
813
|
+
});
|
|
814
|
+
let exchangeLiquidityStatus = resolveBackfillStatus({
|
|
815
|
+
enabled: exchangeLiquidityEnabled,
|
|
816
|
+
cached: exchangeLiquidityCached,
|
|
817
|
+
covered: exchangeLiquidityBackfillCovered
|
|
818
|
+
});
|
|
819
|
+
let fearGreedStatus = resolveBackfillStatus({
|
|
820
|
+
enabled: fearGreedEnabled,
|
|
821
|
+
cached: fearGreedCached,
|
|
822
|
+
covered: fearGreedBackfillCovered
|
|
823
|
+
});
|
|
824
|
+
let indexStatus = resolveBackfillStatus({
|
|
825
|
+
cached: indexesCached,
|
|
826
|
+
covered: indexesBackfillCovered
|
|
827
|
+
});
|
|
828
|
+
console.log(
|
|
829
|
+
import_chalk.default.gray(
|
|
830
|
+
[
|
|
831
|
+
"coinmarketcap context status:",
|
|
832
|
+
formatSourceStatus({
|
|
833
|
+
name: "global",
|
|
834
|
+
status: globalStatus,
|
|
835
|
+
coverage: formatCoverageRange(globalDailyCoverage)
|
|
836
|
+
}),
|
|
837
|
+
formatSourceStatus({
|
|
838
|
+
name: "reference",
|
|
839
|
+
status: referenceStatus,
|
|
840
|
+
coverage: formatReferenceCoverage(referenceDailyCoverage)
|
|
841
|
+
}),
|
|
842
|
+
formatSourceStatus({
|
|
843
|
+
name: "exchangeLiquidity",
|
|
844
|
+
status: exchangeLiquidityStatus,
|
|
845
|
+
coverage: formatCoverageRange(exchangeLiquidityCoverage)
|
|
846
|
+
}),
|
|
847
|
+
formatSourceStatus({
|
|
848
|
+
name: "fearGreed",
|
|
849
|
+
status: fearGreedStatus,
|
|
850
|
+
coverage: formatCoverageRange(fearGreedCoverage)
|
|
851
|
+
}),
|
|
852
|
+
formatSourceStatus({
|
|
853
|
+
name: "indexes",
|
|
854
|
+
status: indexStatus,
|
|
855
|
+
coverage: CMC_INDEXES.map(
|
|
856
|
+
(index) => `${index.slug}:${formatCoverageRange(indexCoverage.get(index.slug))}`
|
|
857
|
+
).join(" ")
|
|
858
|
+
})
|
|
859
|
+
].join(" ")
|
|
860
|
+
)
|
|
861
|
+
);
|
|
862
|
+
if (globalDailyReady && referencesDailyReady && exchangeLiquidityReady && fearGreedReady && indexesReady) {
|
|
863
|
+
console.log(
|
|
864
|
+
import_chalk.default.gray(
|
|
865
|
+
`coinmarketcap context cached: ${new Date(fromMs).toISOString()}..${new Date(toMs).toISOString()}`
|
|
866
|
+
)
|
|
867
|
+
);
|
|
868
|
+
return skippedResult(true);
|
|
869
|
+
}
|
|
870
|
+
const settings = await (0, import_userSettings.getUserSettings)(params.userName);
|
|
871
|
+
const apiKey = settings.COINMARKETCAP_API_KEY.trim();
|
|
872
|
+
if (!apiKey) {
|
|
873
|
+
throw new Error(
|
|
874
|
+
`Missing COINMARKETCAP_API_KEY for historical context backfill (user=${params.userName})`
|
|
875
|
+
);
|
|
876
|
+
}
|
|
877
|
+
let globalRows = 0;
|
|
878
|
+
let referenceRows = 0;
|
|
879
|
+
let exchangeLiquidityRows = 0;
|
|
880
|
+
let fearGreedRows = 0;
|
|
881
|
+
let indexRows = 0;
|
|
882
|
+
const fetchStats = createCoinMarketCapFetchStats();
|
|
883
|
+
const pendingSources = [
|
|
884
|
+
!globalDailyReady ? "global" : null,
|
|
885
|
+
!referencesDailyReady ? "reference" : null,
|
|
886
|
+
exchangeLiquidityEnabled && !exchangeLiquidityReady ? "exchangeLiquidity" : null,
|
|
887
|
+
fearGreedEnabled && !fearGreedReady ? "fearGreed" : null,
|
|
888
|
+
!indexesReady ? "indexes" : null
|
|
889
|
+
].filter((source) => source != null);
|
|
890
|
+
console.log(
|
|
891
|
+
import_chalk.default.cyan(
|
|
892
|
+
`coinmarketcap context backfill: window=${new Date(fromMs).toISOString()}..${new Date(toMs).toISOString()} days=${Math.floor((toMs - fromMs) / DAY_MS) + 1} fetch=${pendingSources.join(",")}`
|
|
893
|
+
)
|
|
894
|
+
);
|
|
895
|
+
if (!globalDailyReady) {
|
|
896
|
+
const payload = await coinMarketCapFetch({
|
|
897
|
+
path: "/v1/global-metrics/quotes/historical",
|
|
898
|
+
apiKey,
|
|
899
|
+
stats: fetchStats,
|
|
900
|
+
searchParams: {
|
|
901
|
+
time_start: toIsoDate(fromMs),
|
|
902
|
+
time_end: toIsoDate(toMs),
|
|
903
|
+
interval: "1d",
|
|
904
|
+
convert: "USD"
|
|
905
|
+
}
|
|
906
|
+
});
|
|
907
|
+
const rows = coinMarketCapGlobalPayloadToRows(payload, SOURCE_GLOBAL_DAILY);
|
|
908
|
+
await (0, import_timescale.upsertMarketGlobalContextRows)(rows);
|
|
909
|
+
await markBackfillCoverage([
|
|
910
|
+
{
|
|
911
|
+
source: SOURCE_GLOBAL_DAILY,
|
|
912
|
+
scope: COVERAGE_SCOPE_ALL,
|
|
913
|
+
interval: "1d",
|
|
914
|
+
fromMs,
|
|
915
|
+
toMs,
|
|
916
|
+
rowsCount: rows.length
|
|
917
|
+
}
|
|
918
|
+
]);
|
|
919
|
+
globalRows += rows.length;
|
|
920
|
+
globalStatus = "fetched";
|
|
921
|
+
logBackfillSourceDone({
|
|
922
|
+
name: "global",
|
|
923
|
+
status: globalStatus,
|
|
924
|
+
rows: rows.length,
|
|
925
|
+
details: formatRowTimeRange(rows)
|
|
926
|
+
});
|
|
927
|
+
}
|
|
928
|
+
if (!referencesDailyReady) {
|
|
929
|
+
const payload = await coinMarketCapFetch({
|
|
930
|
+
path: "/v3/cryptocurrency/quotes/historical",
|
|
931
|
+
apiKey,
|
|
932
|
+
stats: fetchStats,
|
|
933
|
+
searchParams: {
|
|
934
|
+
id: REFERENCE_ASSETS.map((item) => item.cmcId).join(","),
|
|
935
|
+
time_start: toIsoDate(fromMs),
|
|
936
|
+
time_end: toIsoDate(toMs),
|
|
937
|
+
interval: "1d",
|
|
938
|
+
convert: "USD"
|
|
939
|
+
}
|
|
940
|
+
});
|
|
941
|
+
const rows = coinMarketCapHistoricalQuotesPayloadToRows(payload, "1d");
|
|
942
|
+
await (0, import_timescale.upsertMarketReferenceAssetContextRows)(rows);
|
|
943
|
+
await markBackfillCoverage(
|
|
944
|
+
REFERENCE_ASSETS.map((asset) => ({
|
|
945
|
+
source: SOURCE_REFERENCE,
|
|
946
|
+
scope: asset.symbol,
|
|
947
|
+
interval: "1d",
|
|
948
|
+
fromMs,
|
|
949
|
+
toMs,
|
|
950
|
+
rowsCount: rows.filter((row) => row.symbol === asset.symbol).length
|
|
951
|
+
}))
|
|
952
|
+
);
|
|
953
|
+
referenceRows += rows.length;
|
|
954
|
+
referenceStatus = "fetched";
|
|
955
|
+
logBackfillSourceDone({
|
|
956
|
+
name: "reference",
|
|
957
|
+
status: referenceStatus,
|
|
958
|
+
rows: rows.length,
|
|
959
|
+
details: `${REFERENCE_ASSETS.map(
|
|
960
|
+
(asset) => `${asset.symbol}:${rows.filter((row) => row.symbol === asset.symbol).length}`
|
|
961
|
+
).join(" ")} ${formatRowTimeRange(rows)}`
|
|
962
|
+
});
|
|
963
|
+
}
|
|
964
|
+
if (exchangeLiquidityEnabled && !exchangeLiquidityReady) {
|
|
965
|
+
const payload = await coinMarketCapFetch({
|
|
966
|
+
path: "/v1/exchange/quotes/historical",
|
|
967
|
+
apiKey,
|
|
968
|
+
stats: fetchStats,
|
|
969
|
+
searchParams: {
|
|
970
|
+
slug: getExchangeSlugs().join(","),
|
|
971
|
+
time_start: toIsoDate(fromMs),
|
|
972
|
+
time_end: toIsoDate(toMs),
|
|
973
|
+
interval: "1d",
|
|
974
|
+
convert: "USD"
|
|
975
|
+
}
|
|
976
|
+
});
|
|
977
|
+
const rows = coinMarketCapExchangeQuotesPayloadToLiquidityRows(
|
|
978
|
+
payload,
|
|
979
|
+
"1d"
|
|
980
|
+
);
|
|
981
|
+
await (0, import_timescale.upsertMarketCmcExchangeLiquidityContextRows)(rows);
|
|
982
|
+
await markBackfillCoverage([
|
|
983
|
+
{
|
|
984
|
+
source: SOURCE_EXCHANGE_LIQUIDITY,
|
|
985
|
+
scope: COVERAGE_SCOPE_ALL,
|
|
986
|
+
interval: "1d",
|
|
987
|
+
fromMs,
|
|
988
|
+
toMs,
|
|
989
|
+
rowsCount: rows.length
|
|
990
|
+
}
|
|
991
|
+
]);
|
|
992
|
+
exchangeLiquidityRows = rows.length;
|
|
993
|
+
exchangeLiquidityStatus = "fetched";
|
|
994
|
+
logBackfillSourceDone({
|
|
995
|
+
name: "exchangeLiquidity",
|
|
996
|
+
status: exchangeLiquidityStatus,
|
|
997
|
+
rows: rows.length,
|
|
998
|
+
details: formatRowTimeRange(rows)
|
|
999
|
+
});
|
|
1000
|
+
}
|
|
1001
|
+
if (fearGreedEnabled && !fearGreedReady) {
|
|
1002
|
+
const rows = [];
|
|
1003
|
+
const pageSize = getFearGreedPageSize();
|
|
1004
|
+
let pages = 0;
|
|
1005
|
+
for (let start = 1; ; start += pageSize) {
|
|
1006
|
+
const payload = await coinMarketCapFetch({
|
|
1007
|
+
path: "/v3/fear-and-greed/historical",
|
|
1008
|
+
apiKey,
|
|
1009
|
+
stats: fetchStats,
|
|
1010
|
+
searchParams: {
|
|
1011
|
+
start: String(start),
|
|
1012
|
+
limit: String(pageSize)
|
|
1013
|
+
}
|
|
1014
|
+
});
|
|
1015
|
+
pages += 1;
|
|
1016
|
+
const pageRows = coinMarketCapFearGreedPayloadToRows(payload);
|
|
1017
|
+
if (!pageRows.length) break;
|
|
1018
|
+
const matchingRows = pageRows.filter((row) => {
|
|
1019
|
+
const ts = row.ts.getTime();
|
|
1020
|
+
return ts >= fromMs && ts <= toMs;
|
|
1021
|
+
});
|
|
1022
|
+
rows.push(...matchingRows);
|
|
1023
|
+
const oldestPageTs = Math.min(...pageRows.map((row) => row.ts.getTime()));
|
|
1024
|
+
if (oldestPageTs < fromMs || pageRows.length < pageSize) break;
|
|
1025
|
+
}
|
|
1026
|
+
await (0, import_timescale.upsertMarketCmcFearGreedContextRows)(rows);
|
|
1027
|
+
await markBackfillCoverage([
|
|
1028
|
+
{
|
|
1029
|
+
source: SOURCE_FEAR_GREED,
|
|
1030
|
+
scope: COVERAGE_SCOPE_ALL,
|
|
1031
|
+
interval: "1d",
|
|
1032
|
+
fromMs,
|
|
1033
|
+
toMs,
|
|
1034
|
+
rowsCount: rows.length
|
|
1035
|
+
}
|
|
1036
|
+
]);
|
|
1037
|
+
fearGreedRows = rows.length;
|
|
1038
|
+
fearGreedStatus = "fetched";
|
|
1039
|
+
logBackfillSourceDone({
|
|
1040
|
+
name: "fearGreed",
|
|
1041
|
+
status: fearGreedStatus,
|
|
1042
|
+
rows: rows.length,
|
|
1043
|
+
details: `pages=${pages} ${formatRowTimeRange(rows)}`
|
|
1044
|
+
});
|
|
1045
|
+
}
|
|
1046
|
+
if (!indexesReady) {
|
|
1047
|
+
const rowsByIndex = await Promise.all(
|
|
1048
|
+
CMC_INDEXES.map(async (index) => ({
|
|
1049
|
+
index,
|
|
1050
|
+
result: await fetchCoinMarketCapIndexRows({
|
|
1051
|
+
apiKey,
|
|
1052
|
+
index,
|
|
1053
|
+
fromMs,
|
|
1054
|
+
toMs,
|
|
1055
|
+
stats: fetchStats
|
|
1056
|
+
})
|
|
1057
|
+
}))
|
|
1058
|
+
);
|
|
1059
|
+
const rows = rowsByIndex.flatMap((item) => item.result.rows);
|
|
1060
|
+
await (0, import_timescale.upsertMarketCmcIndexContextRows)(rows);
|
|
1061
|
+
await markBackfillCoverage(
|
|
1062
|
+
rowsByIndex.map(({ index, result }) => ({
|
|
1063
|
+
source: SOURCE_INDEX,
|
|
1064
|
+
scope: index.slug,
|
|
1065
|
+
interval: "1d",
|
|
1066
|
+
fromMs,
|
|
1067
|
+
toMs,
|
|
1068
|
+
rowsCount: result.rows.length
|
|
1069
|
+
}))
|
|
1070
|
+
);
|
|
1071
|
+
indexRows = rows.length;
|
|
1072
|
+
indexStatus = "fetched";
|
|
1073
|
+
logBackfillSourceDone({
|
|
1074
|
+
name: "indexes",
|
|
1075
|
+
status: indexStatus,
|
|
1076
|
+
rows: rows.length,
|
|
1077
|
+
details: `${rowsByIndex.map(
|
|
1078
|
+
({ index, result }) => `${index.slug}:${result.rows.length} rows/${result.chunks} chunks`
|
|
1079
|
+
).join(" ")} ${formatRowTimeRange(rows)}`
|
|
1080
|
+
});
|
|
1081
|
+
}
|
|
1082
|
+
console.log(
|
|
1083
|
+
import_chalk.default.green(
|
|
1084
|
+
[
|
|
1085
|
+
"coinmarketcap context backfill done:",
|
|
1086
|
+
`global=${globalStatus} rows=${globalRows}`,
|
|
1087
|
+
`reference=${referenceStatus} rows=${referenceRows}`,
|
|
1088
|
+
`exchangeLiquidity=${exchangeLiquidityStatus} rows=${exchangeLiquidityRows}`,
|
|
1089
|
+
`fearGreed=${fearGreedStatus} rows=${fearGreedRows}`,
|
|
1090
|
+
`indexes=${indexStatus} rows=${indexRows}`,
|
|
1091
|
+
formatCoinMarketCapFetchStats(fetchStats)
|
|
1092
|
+
].join(" ")
|
|
1093
|
+
)
|
|
1094
|
+
);
|
|
1095
|
+
return {
|
|
1096
|
+
skipped: false,
|
|
1097
|
+
globalRows,
|
|
1098
|
+
referenceRows,
|
|
1099
|
+
breadthRows: 0,
|
|
1100
|
+
exchangeLiquidityRows,
|
|
1101
|
+
fearGreedRows,
|
|
1102
|
+
indexRows,
|
|
1103
|
+
cached: false
|
|
1104
|
+
};
|
|
1105
|
+
};
|
|
1106
|
+
var backfillCoinMarketCapContextForBacktest = backfillCoinMarketCapContext;
|
|
1107
|
+
var backfillCoinMarketCapContextForReplay = backfillCoinMarketCapContext;
|
|
1108
|
+
var backfillCoinMarketCapContextForSignals = (params) => backfillCoinMarketCapContext({ ...params, refreshStaleFearGreed: true });
|
|
1109
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1110
|
+
0 && (module.exports = {
|
|
1111
|
+
backfillCoinMarketCapContext,
|
|
1112
|
+
backfillCoinMarketCapContextForBacktest,
|
|
1113
|
+
backfillCoinMarketCapContextForReplay,
|
|
1114
|
+
backfillCoinMarketCapContextForSignals,
|
|
1115
|
+
coinMarketCapExchangeQuotesPayloadToLiquidityRows,
|
|
1116
|
+
coinMarketCapFearGreedPayloadToRows,
|
|
1117
|
+
coinMarketCapGlobalPayloadToRows,
|
|
1118
|
+
coinMarketCapHistoricalQuotesPayloadToRows,
|
|
1119
|
+
coinMarketCapIndexPayloadToRows,
|
|
1120
|
+
coverageRowsToKeySet,
|
|
1121
|
+
isMarketContextBackfillCoverageDataBearing,
|
|
1122
|
+
resolveCoinMarketCapBackfillWindow,
|
|
1123
|
+
shouldBackfillCoinMarketCapContextForBacktest,
|
|
1124
|
+
shouldBackfillCoinMarketCapContextForReplay,
|
|
1125
|
+
shouldBackfillCoinMarketCapContextForSignals
|
|
1126
|
+
});
|