@tradejs/cli 2.0.1 → 2.0.2
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/dist/cli.js +250 -38
- package/dist/lib/aiTrainCharts.js +49 -2
- package/dist/lib/aiTrainMetrics.js +49 -2
- package/dist/lib/aiTrainResearch.js +58 -3
- package/dist/lib/derivativesContextBackfill.js +143 -32
- package/dist/lib/marketContextPrepare.js +141 -32
- package/dist/scripts/aiPocketSearch.js +49 -2
- package/dist/scripts/aiTrain.js +76 -3
- package/dist/scripts/backtest.js +141 -32
- package/dist/scripts/derivativesIngestCoinalyzeAll.js +30 -0
- package/dist/scripts/replay.js +141 -32
- package/dist/scripts/replayRunner.js +141 -32
- package/dist/scripts/runtimeParity.js +141 -32
- package/dist/scripts/signals.js +141 -32
- package/dist/scripts/signalsDaemon.js +141 -32
- package/package.json +9 -9
|
@@ -35,6 +35,7 @@ var divideOrNull = (num, denom) => {
|
|
|
35
35
|
var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
36
36
|
var DAYS_PER_WEEK = 7;
|
|
37
37
|
var DAYS_PER_MONTH = 30.4375;
|
|
38
|
+
var DAYS_PER_YEAR = 365;
|
|
38
39
|
var getEvaluationPeriodDays = (evaluations) => {
|
|
39
40
|
let minTimestamp = null;
|
|
40
41
|
let maxTimestamp = null;
|
|
@@ -56,6 +57,40 @@ var getEvaluationPeriodDays = (evaluations) => {
|
|
|
56
57
|
return Math.max((maxTimestamp - minTimestamp) / DAY_MS, 1);
|
|
57
58
|
};
|
|
58
59
|
var qualitySortKey = (quality) => quality == null ? Number.POSITIVE_INFINITY : quality;
|
|
60
|
+
var calculateTradePnlRiskRatios = ({
|
|
61
|
+
profits,
|
|
62
|
+
totalProfit,
|
|
63
|
+
maxDrawdown,
|
|
64
|
+
periodDays
|
|
65
|
+
}) => {
|
|
66
|
+
if (!profits.length || periodDays == null || periodDays <= 0) {
|
|
67
|
+
return {
|
|
68
|
+
sharpeRatio: null,
|
|
69
|
+
sortinoRatio: null,
|
|
70
|
+
calmarRatio: null
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
const meanProfit = totalProfit / profits.length;
|
|
74
|
+
const variance = profits.reduce((sum, profit) => {
|
|
75
|
+
const diff = profit - meanProfit;
|
|
76
|
+
return sum + diff * diff;
|
|
77
|
+
}, 0) / profits.length;
|
|
78
|
+
const stdDev = Math.sqrt(variance);
|
|
79
|
+
const downsideDeviation = Math.sqrt(
|
|
80
|
+
profits.reduce(
|
|
81
|
+
(sum, profit) => profit < 0 ? sum + profit * profit : sum,
|
|
82
|
+
0
|
|
83
|
+
) / profits.length
|
|
84
|
+
);
|
|
85
|
+
const annualizationScale = Math.sqrt(
|
|
86
|
+
profits.length / periodDays * DAYS_PER_YEAR
|
|
87
|
+
);
|
|
88
|
+
return {
|
|
89
|
+
sharpeRatio: stdDev > 0 && annualizationScale > 0 ? meanProfit / stdDev * annualizationScale : null,
|
|
90
|
+
sortinoRatio: downsideDeviation > 0 && annualizationScale > 0 ? meanProfit / downsideDeviation * annualizationScale : null,
|
|
91
|
+
calmarRatio: maxDrawdown > 0 ? totalProfit / periodDays * DAYS_PER_YEAR / maxDrawdown : null
|
|
92
|
+
};
|
|
93
|
+
};
|
|
59
94
|
var emptyRiskSummary = () => ({
|
|
60
95
|
trades: 0,
|
|
61
96
|
totalProfit: 0,
|
|
@@ -71,12 +106,15 @@ var emptyRiskSummary = () => ({
|
|
|
71
106
|
maxDrawdown: 0,
|
|
72
107
|
maxDrawdownPctOfGrossProfit: null,
|
|
73
108
|
maxDrawdownPctOfTotalProfit: null,
|
|
109
|
+
sharpeRatio: null,
|
|
110
|
+
sortinoRatio: null,
|
|
111
|
+
calmarRatio: null,
|
|
74
112
|
recoveryFactor: null,
|
|
75
113
|
ulcerIndex: null,
|
|
76
114
|
maxConsecutiveWins: 0,
|
|
77
115
|
maxConsecutiveLosses: 0
|
|
78
116
|
});
|
|
79
|
-
var summarizeApprovedRisk = (evaluations) => {
|
|
117
|
+
var summarizeApprovedRisk = (evaluations, periodDays = getEvaluationPeriodDays(evaluations)) => {
|
|
80
118
|
const approvedEvaluations = evaluations.filter((evaluation) => evaluation.aiApproved).sort((left, right) => {
|
|
81
119
|
const leftTimestamp = typeof left.timestamp === "number" && Number.isFinite(left.timestamp) ? left.timestamp : Number.POSITIVE_INFINITY;
|
|
82
120
|
const rightTimestamp = typeof right.timestamp === "number" && Number.isFinite(right.timestamp) ? right.timestamp : Number.POSITIVE_INFINITY;
|
|
@@ -99,8 +137,10 @@ var summarizeApprovedRisk = (evaluations) => {
|
|
|
99
137
|
let currentLossStreak = 0;
|
|
100
138
|
let maxConsecutiveWins = 0;
|
|
101
139
|
let maxConsecutiveLosses = 0;
|
|
140
|
+
const approvedProfits = [];
|
|
102
141
|
for (const evaluation of approvedEvaluations) {
|
|
103
142
|
const profit = evaluation.profit;
|
|
143
|
+
approvedProfits.push(profit);
|
|
104
144
|
if (profit > 0) {
|
|
105
145
|
grossProfit += profit;
|
|
106
146
|
wins += 1;
|
|
@@ -128,6 +168,12 @@ var summarizeApprovedRisk = (evaluations) => {
|
|
|
128
168
|
const totalProfit = grossProfit - grossLoss;
|
|
129
169
|
const avgWin = divideOrNull(grossProfit, wins);
|
|
130
170
|
const avgLoss = divideOrNull(grossLoss, losses);
|
|
171
|
+
const riskRatios = calculateTradePnlRiskRatios({
|
|
172
|
+
profits: approvedProfits,
|
|
173
|
+
totalProfit,
|
|
174
|
+
maxDrawdown,
|
|
175
|
+
periodDays
|
|
176
|
+
});
|
|
131
177
|
return {
|
|
132
178
|
trades: approvedEvaluations.length,
|
|
133
179
|
totalProfit,
|
|
@@ -143,6 +189,7 @@ var summarizeApprovedRisk = (evaluations) => {
|
|
|
143
189
|
maxDrawdown,
|
|
144
190
|
maxDrawdownPctOfGrossProfit: grossProfit > 0 ? maxDrawdown / grossProfit : null,
|
|
145
191
|
maxDrawdownPctOfTotalProfit: totalProfit > 0 ? maxDrawdown / totalProfit : null,
|
|
192
|
+
...riskRatios,
|
|
146
193
|
recoveryFactor: maxDrawdown > 0 ? totalProfit / maxDrawdown : null,
|
|
147
194
|
ulcerIndex: Math.sqrt(drawdownSquares / approvedEvaluations.length),
|
|
148
195
|
maxConsecutiveWins,
|
|
@@ -241,7 +288,7 @@ var summarizeAiTrainEvaluations = (evaluations) => {
|
|
|
241
288
|
avgApprovedTradesPerDay,
|
|
242
289
|
avgApprovedTradesPerWeek,
|
|
243
290
|
expectancyDelta,
|
|
244
|
-
approvedRisk: summarizeApprovedRisk(evaluations),
|
|
291
|
+
approvedRisk: summarizeApprovedRisk(evaluations, periodDays),
|
|
245
292
|
qualityBuckets: [...bucketMap.values()].sort(
|
|
246
293
|
(a, b) => qualitySortKey(a.quality) - qualitySortKey(b.quality)
|
|
247
294
|
)
|
|
@@ -52,6 +52,7 @@ var divideOrNull = (num, denom) => {
|
|
|
52
52
|
var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
53
53
|
var DAYS_PER_WEEK = 7;
|
|
54
54
|
var DAYS_PER_MONTH = 30.4375;
|
|
55
|
+
var DAYS_PER_YEAR = 365;
|
|
55
56
|
var getEvaluationPeriodDays = (evaluations) => {
|
|
56
57
|
let minTimestamp = null;
|
|
57
58
|
let maxTimestamp = null;
|
|
@@ -73,6 +74,40 @@ var getEvaluationPeriodDays = (evaluations) => {
|
|
|
73
74
|
return Math.max((maxTimestamp - minTimestamp) / DAY_MS, 1);
|
|
74
75
|
};
|
|
75
76
|
var qualitySortKey = (quality) => quality == null ? Number.POSITIVE_INFINITY : quality;
|
|
77
|
+
var calculateTradePnlRiskRatios = ({
|
|
78
|
+
profits,
|
|
79
|
+
totalProfit,
|
|
80
|
+
maxDrawdown,
|
|
81
|
+
periodDays
|
|
82
|
+
}) => {
|
|
83
|
+
if (!profits.length || periodDays == null || periodDays <= 0) {
|
|
84
|
+
return {
|
|
85
|
+
sharpeRatio: null,
|
|
86
|
+
sortinoRatio: null,
|
|
87
|
+
calmarRatio: null
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
const meanProfit = totalProfit / profits.length;
|
|
91
|
+
const variance = profits.reduce((sum, profit) => {
|
|
92
|
+
const diff = profit - meanProfit;
|
|
93
|
+
return sum + diff * diff;
|
|
94
|
+
}, 0) / profits.length;
|
|
95
|
+
const stdDev = Math.sqrt(variance);
|
|
96
|
+
const downsideDeviation = Math.sqrt(
|
|
97
|
+
profits.reduce(
|
|
98
|
+
(sum, profit) => profit < 0 ? sum + profit * profit : sum,
|
|
99
|
+
0
|
|
100
|
+
) / profits.length
|
|
101
|
+
);
|
|
102
|
+
const annualizationScale = Math.sqrt(
|
|
103
|
+
profits.length / periodDays * DAYS_PER_YEAR
|
|
104
|
+
);
|
|
105
|
+
return {
|
|
106
|
+
sharpeRatio: stdDev > 0 && annualizationScale > 0 ? meanProfit / stdDev * annualizationScale : null,
|
|
107
|
+
sortinoRatio: downsideDeviation > 0 && annualizationScale > 0 ? meanProfit / downsideDeviation * annualizationScale : null,
|
|
108
|
+
calmarRatio: maxDrawdown > 0 ? totalProfit / periodDays * DAYS_PER_YEAR / maxDrawdown : null
|
|
109
|
+
};
|
|
110
|
+
};
|
|
76
111
|
var emptyRiskSummary = () => ({
|
|
77
112
|
trades: 0,
|
|
78
113
|
totalProfit: 0,
|
|
@@ -88,12 +123,15 @@ var emptyRiskSummary = () => ({
|
|
|
88
123
|
maxDrawdown: 0,
|
|
89
124
|
maxDrawdownPctOfGrossProfit: null,
|
|
90
125
|
maxDrawdownPctOfTotalProfit: null,
|
|
126
|
+
sharpeRatio: null,
|
|
127
|
+
sortinoRatio: null,
|
|
128
|
+
calmarRatio: null,
|
|
91
129
|
recoveryFactor: null,
|
|
92
130
|
ulcerIndex: null,
|
|
93
131
|
maxConsecutiveWins: 0,
|
|
94
132
|
maxConsecutiveLosses: 0
|
|
95
133
|
});
|
|
96
|
-
var summarizeApprovedRisk = (evaluations) => {
|
|
134
|
+
var summarizeApprovedRisk = (evaluations, periodDays = getEvaluationPeriodDays(evaluations)) => {
|
|
97
135
|
const approvedEvaluations = evaluations.filter((evaluation) => evaluation.aiApproved).sort((left, right) => {
|
|
98
136
|
const leftTimestamp = typeof left.timestamp === "number" && Number.isFinite(left.timestamp) ? left.timestamp : Number.POSITIVE_INFINITY;
|
|
99
137
|
const rightTimestamp = typeof right.timestamp === "number" && Number.isFinite(right.timestamp) ? right.timestamp : Number.POSITIVE_INFINITY;
|
|
@@ -116,8 +154,10 @@ var summarizeApprovedRisk = (evaluations) => {
|
|
|
116
154
|
let currentLossStreak = 0;
|
|
117
155
|
let maxConsecutiveWins = 0;
|
|
118
156
|
let maxConsecutiveLosses = 0;
|
|
157
|
+
const approvedProfits = [];
|
|
119
158
|
for (const evaluation of approvedEvaluations) {
|
|
120
159
|
const profit = evaluation.profit;
|
|
160
|
+
approvedProfits.push(profit);
|
|
121
161
|
if (profit > 0) {
|
|
122
162
|
grossProfit += profit;
|
|
123
163
|
wins += 1;
|
|
@@ -145,6 +185,12 @@ var summarizeApprovedRisk = (evaluations) => {
|
|
|
145
185
|
const totalProfit = grossProfit - grossLoss;
|
|
146
186
|
const avgWin = divideOrNull(grossProfit, wins);
|
|
147
187
|
const avgLoss = divideOrNull(grossLoss, losses);
|
|
188
|
+
const riskRatios = calculateTradePnlRiskRatios({
|
|
189
|
+
profits: approvedProfits,
|
|
190
|
+
totalProfit,
|
|
191
|
+
maxDrawdown,
|
|
192
|
+
periodDays
|
|
193
|
+
});
|
|
148
194
|
return {
|
|
149
195
|
trades: approvedEvaluations.length,
|
|
150
196
|
totalProfit,
|
|
@@ -160,6 +206,7 @@ var summarizeApprovedRisk = (evaluations) => {
|
|
|
160
206
|
maxDrawdown,
|
|
161
207
|
maxDrawdownPctOfGrossProfit: grossProfit > 0 ? maxDrawdown / grossProfit : null,
|
|
162
208
|
maxDrawdownPctOfTotalProfit: totalProfit > 0 ? maxDrawdown / totalProfit : null,
|
|
209
|
+
...riskRatios,
|
|
163
210
|
recoveryFactor: maxDrawdown > 0 ? totalProfit / maxDrawdown : null,
|
|
164
211
|
ulcerIndex: Math.sqrt(drawdownSquares / approvedEvaluations.length),
|
|
165
212
|
maxConsecutiveWins,
|
|
@@ -258,7 +305,7 @@ var summarizeAiTrainEvaluations = (evaluations) => {
|
|
|
258
305
|
avgApprovedTradesPerDay,
|
|
259
306
|
avgApprovedTradesPerWeek,
|
|
260
307
|
expectancyDelta,
|
|
261
|
-
approvedRisk: summarizeApprovedRisk(evaluations),
|
|
308
|
+
approvedRisk: summarizeApprovedRisk(evaluations, periodDays),
|
|
262
309
|
qualityBuckets: [...bucketMap.values()].sort(
|
|
263
310
|
(a, b) => qualitySortKey(a.quality) - qualitySortKey(b.quality)
|
|
264
311
|
)
|
|
@@ -286,6 +333,12 @@ var RESEARCH_CONTEXT_ENV_KEYS = [
|
|
|
286
333
|
"COINMARKETCAP_CONTEXT_BACKFILL_MAX_DAYS",
|
|
287
334
|
"COINMARKETCAP_CONTEXT_BACKFILL_WARMUP_DAYS"
|
|
288
335
|
];
|
|
336
|
+
var DERIVATIVES_CONTEXT_DATA_MODEL = {
|
|
337
|
+
derivativesSourceIntervals: "15m",
|
|
338
|
+
derivativesDerivedIntervals: "1h",
|
|
339
|
+
derivativesHourlyFallback: "stored-1h",
|
|
340
|
+
derivativesDataModelVersion: 2
|
|
341
|
+
};
|
|
289
342
|
var normalizeForStableJson = (value) => {
|
|
290
343
|
if (Array.isArray(value)) {
|
|
291
344
|
return value.map(normalizeForStableJson);
|
|
@@ -398,6 +451,7 @@ var resolveGateFingerprint = async (projectRoot, strategyName, gitSha) => {
|
|
|
398
451
|
const relativeCandidates = [
|
|
399
452
|
`packages/strategies/src/${strategyName}/adapters/ai.ts`,
|
|
400
453
|
`packages/strategies/src/${strategyName}/guardrails.ts`,
|
|
454
|
+
`packages/strategies/src/${strategyName}/pockets.ts`,
|
|
401
455
|
`packages/strategies/src/${strategyName}/config.ts`,
|
|
402
456
|
"packages/node/src/ai.ts"
|
|
403
457
|
];
|
|
@@ -444,7 +498,8 @@ var buildAiTrainLineage = async ({
|
|
|
444
498
|
...Object.fromEntries(
|
|
445
499
|
RESEARCH_CONTEXT_ENV_KEYS.map((key) => [key, env[key] ?? null])
|
|
446
500
|
),
|
|
447
|
-
...runContext
|
|
501
|
+
...runContext,
|
|
502
|
+
...DERIVATIVES_CONTEXT_DATA_MODEL
|
|
448
503
|
};
|
|
449
504
|
return {
|
|
450
505
|
gitSha,
|
|
@@ -47,6 +47,7 @@ __export(derivativesContextBackfill_exports, {
|
|
|
47
47
|
resolveDerivativesContextLookbackMs: () => resolveDerivativesContextLookbackMs,
|
|
48
48
|
resolveDerivativesContextMissingCoverageFetchFromMs: () => resolveDerivativesContextMissingCoverageFetchFromMs,
|
|
49
49
|
resolveDerivativesContextMissingFetchFromMs: () => resolveDerivativesContextMissingFetchFromMs,
|
|
50
|
+
resolveDerivativesContextRequiredFetchFromMs: () => resolveDerivativesContextRequiredFetchFromMs,
|
|
50
51
|
shouldBackfillDerivativesContextForBacktest: () => shouldBackfillDerivativesContextForBacktest,
|
|
51
52
|
shouldBackfillDerivativesContextForSignals: () => shouldBackfillDerivativesContextForSignals
|
|
52
53
|
});
|
|
@@ -179,6 +180,38 @@ var resolveDerivativesContextMissingFetchFromMs = (params) => {
|
|
|
179
180
|
return null;
|
|
180
181
|
};
|
|
181
182
|
var resolveDerivativesContextFetchFromMs = (params) => resolveDerivativesContextMissingFetchFromMs(params) ?? (params.refreshClosedTail ? params.toMs : null);
|
|
183
|
+
var resolveDerivativesContextRequiredFetchFromMs = (params) => {
|
|
184
|
+
const dataCoverageFromMs = params.dataCoverageKeyExists ? null : params.mode === "signals" ? params.fromMs : resolveDerivativesContextMissingCoverageFetchFromMs({
|
|
185
|
+
ranges: params.dataCoverageRanges,
|
|
186
|
+
fromMs: params.fromMs,
|
|
187
|
+
toMs: params.toMs,
|
|
188
|
+
intervalMs: params.intervalMs
|
|
189
|
+
});
|
|
190
|
+
const edgesFromMs = dataCoverageFromMs == null ? null : resolveDerivativesContextFetchFromMs({
|
|
191
|
+
edges: params.edges,
|
|
192
|
+
fromMs: params.fromMs,
|
|
193
|
+
toMs: params.toMs,
|
|
194
|
+
intervalMs: params.intervalMs,
|
|
195
|
+
refreshClosedTail: params.mode === "signals"
|
|
196
|
+
});
|
|
197
|
+
const dataFetchFromMs = dataCoverageFromMs != null && edgesFromMs != null ? Math.max(dataCoverageFromMs, edgesFromMs) : null;
|
|
198
|
+
const confirmedLiquidationWindow = (0, import_indicators.resolveCoinalyzeConfirmedIntradayCoverage)({
|
|
199
|
+
interval: params.interval,
|
|
200
|
+
fromMs: params.fromMs,
|
|
201
|
+
toMs: params.toMs,
|
|
202
|
+
nowMs: params.nowMs
|
|
203
|
+
});
|
|
204
|
+
const liquidationFetchFromMs = confirmedLiquidationWindow ? resolveDerivativesContextMissingCoverageFetchFromMs({
|
|
205
|
+
ranges: params.liquidationCoverageRanges,
|
|
206
|
+
fromMs: confirmedLiquidationWindow.fromMs,
|
|
207
|
+
toMs: confirmedLiquidationWindow.toMs,
|
|
208
|
+
intervalMs: params.intervalMs
|
|
209
|
+
}) : null;
|
|
210
|
+
const fetchStarts = [dataFetchFromMs, liquidationFetchFromMs].filter(
|
|
211
|
+
(value) => value != null
|
|
212
|
+
);
|
|
213
|
+
return fetchStarts.length ? Math.min(...fetchStarts) : null;
|
|
214
|
+
};
|
|
182
215
|
var countBackfillWindows = (params) => {
|
|
183
216
|
let count = 0;
|
|
184
217
|
let cursor = params.fromMs;
|
|
@@ -517,7 +550,8 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
|
|
|
517
550
|
mode,
|
|
518
551
|
startMs,
|
|
519
552
|
endMs,
|
|
520
|
-
preloadStartMs: params.preloadStartMs
|
|
553
|
+
preloadStartMs: params.preloadStartMs,
|
|
554
|
+
nowMs: Date.now()
|
|
521
555
|
});
|
|
522
556
|
if (safeEndMs <= fromMs) {
|
|
523
557
|
return skippedBackfillResult();
|
|
@@ -551,15 +585,33 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
|
|
|
551
585
|
);
|
|
552
586
|
const coverageKeysByInterval = /* @__PURE__ */ new Map();
|
|
553
587
|
const coverageRangesByInterval = /* @__PURE__ */ new Map();
|
|
588
|
+
const liquidationCoverageRangesByInterval = /* @__PURE__ */ new Map();
|
|
589
|
+
const coverageNowMs = Date.now();
|
|
554
590
|
await Promise.all(
|
|
555
591
|
intervalWindows.map(async (window) => {
|
|
556
|
-
const
|
|
557
|
-
source: "coinalyze",
|
|
558
|
-
symbols,
|
|
592
|
+
const confirmedLiquidationWindow = (0, import_indicators.resolveCoinalyzeConfirmedIntradayCoverage)({
|
|
559
593
|
interval: window.interval,
|
|
560
594
|
fromMs: window.fromMs,
|
|
561
|
-
toMs: window.toMs
|
|
595
|
+
toMs: window.toMs,
|
|
596
|
+
nowMs: coverageNowMs
|
|
562
597
|
});
|
|
598
|
+
const [coverageRows, liquidationCoverageRows] = await Promise.all([
|
|
599
|
+
(0, import_timescale.getDerivativesBackfillCoverage)({
|
|
600
|
+
source: "coinalyze",
|
|
601
|
+
symbols,
|
|
602
|
+
interval: window.interval,
|
|
603
|
+
fromMs: window.fromMs,
|
|
604
|
+
toMs: window.toMs
|
|
605
|
+
}),
|
|
606
|
+
confirmedLiquidationWindow ? (0, import_timescale.getDerivativesMetricCoverage)({
|
|
607
|
+
source: "coinalyze",
|
|
608
|
+
metric: "liquidation",
|
|
609
|
+
symbols,
|
|
610
|
+
interval: window.interval,
|
|
611
|
+
fromMs: confirmedLiquidationWindow.fromMs,
|
|
612
|
+
toMs: confirmedLiquidationWindow.toMs
|
|
613
|
+
}) : Promise.resolve([])
|
|
614
|
+
]);
|
|
563
615
|
coverageKeysByInterval.set(
|
|
564
616
|
window.interval,
|
|
565
617
|
new Set(
|
|
@@ -581,6 +633,17 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
|
|
|
581
633
|
rangesBySymbol.set(symbol, ranges);
|
|
582
634
|
}
|
|
583
635
|
coverageRangesByInterval.set(window.interval, rangesBySymbol);
|
|
636
|
+
const liquidationRangesBySymbol = /* @__PURE__ */ new Map();
|
|
637
|
+
for (const row of liquidationCoverageRows) {
|
|
638
|
+
const symbol = row.symbol.toUpperCase();
|
|
639
|
+
const ranges = liquidationRangesBySymbol.get(symbol) ?? [];
|
|
640
|
+
ranges.push({ fromMs: row.fromMs, toMs: row.toMs });
|
|
641
|
+
liquidationRangesBySymbol.set(symbol, ranges);
|
|
642
|
+
}
|
|
643
|
+
liquidationCoverageRangesByInterval.set(
|
|
644
|
+
window.interval,
|
|
645
|
+
liquidationRangesBySymbol
|
|
646
|
+
);
|
|
584
647
|
})
|
|
585
648
|
);
|
|
586
649
|
const cachedWindows = intervalWindows.reduce(
|
|
@@ -605,7 +668,7 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
|
|
|
605
668
|
(symbol) => backfillWindows.every((backfillWindow) => {
|
|
606
669
|
const normalizedSymbol = symbol.toUpperCase();
|
|
607
670
|
const coverageRanges = coverageRangesByInterval.get(window.interval)?.get(normalizedSymbol) ?? [];
|
|
608
|
-
|
|
671
|
+
const dataCovered = hasDerivativesWindowCoverage({
|
|
609
672
|
edges: edgesBySymbol?.get(normalizedSymbol),
|
|
610
673
|
fromMs: backfillWindow.fromMs,
|
|
611
674
|
toMs: backfillWindow.toMs
|
|
@@ -622,6 +685,19 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
|
|
|
622
685
|
toMs: backfillWindow.toMs
|
|
623
686
|
})
|
|
624
687
|
);
|
|
688
|
+
const confirmedLiquidationWindow = (0, import_indicators.resolveCoinalyzeConfirmedIntradayCoverage)({
|
|
689
|
+
interval: window.interval,
|
|
690
|
+
fromMs: backfillWindow.fromMs,
|
|
691
|
+
toMs: backfillWindow.toMs,
|
|
692
|
+
nowMs: coverageNowMs
|
|
693
|
+
});
|
|
694
|
+
const liquidationCovered = confirmedLiquidationWindow == null || resolveDerivativesContextMissingCoverageFetchFromMs({
|
|
695
|
+
ranges: liquidationCoverageRangesByInterval.get(window.interval)?.get(normalizedSymbol) ?? [],
|
|
696
|
+
fromMs: confirmedLiquidationWindow.fromMs,
|
|
697
|
+
toMs: confirmedLiquidationWindow.toMs,
|
|
698
|
+
intervalMs: window.intervalMs
|
|
699
|
+
}) == null;
|
|
700
|
+
return dataCovered && liquidationCovered;
|
|
625
701
|
})
|
|
626
702
|
);
|
|
627
703
|
});
|
|
@@ -706,6 +782,11 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
|
|
|
706
782
|
coverageKeysByInterval.set(interval, coverageKeys);
|
|
707
783
|
const coverageRangesBySymbol = coverageRangesByInterval.get(interval) ?? /* @__PURE__ */ new Map();
|
|
708
784
|
coverageRangesByInterval.set(interval, coverageRangesBySymbol);
|
|
785
|
+
const liquidationCoverageRangesBySymbol = liquidationCoverageRangesByInterval.get(interval) ?? /* @__PURE__ */ new Map();
|
|
786
|
+
liquidationCoverageRangesByInterval.set(
|
|
787
|
+
interval,
|
|
788
|
+
liquidationCoverageRangesBySymbol
|
|
789
|
+
);
|
|
709
790
|
for (let batchIdx = 0; batchIdx < symbolBatches.length; batchIdx += 1) {
|
|
710
791
|
const batch = symbolBatches[batchIdx];
|
|
711
792
|
let cursor = window.fromMs;
|
|
@@ -718,33 +799,25 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
|
|
|
718
799
|
fromMs: cursor,
|
|
719
800
|
toMs
|
|
720
801
|
});
|
|
721
|
-
if (coverageKeys.has(key)) {
|
|
722
|
-
return null;
|
|
723
|
-
}
|
|
724
802
|
const normalizedSymbol = item.symbol.toUpperCase();
|
|
725
|
-
const
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
refreshClosedTail: mode === "signals"
|
|
741
|
-
});
|
|
742
|
-
if (edgesFromMs == null) {
|
|
743
|
-
return null;
|
|
744
|
-
}
|
|
803
|
+
const requiredFromMs = resolveDerivativesContextRequiredFetchFromMs(
|
|
804
|
+
{
|
|
805
|
+
mode,
|
|
806
|
+
interval,
|
|
807
|
+
intervalMs,
|
|
808
|
+
fromMs: cursor,
|
|
809
|
+
toMs,
|
|
810
|
+
nowMs: coverageNowMs,
|
|
811
|
+
dataCoverageKeyExists: coverageKeys.has(key),
|
|
812
|
+
dataCoverageRanges: coverageRangesBySymbol.get(normalizedSymbol),
|
|
813
|
+
liquidationCoverageRanges: liquidationCoverageRangesBySymbol.get(normalizedSymbol),
|
|
814
|
+
edges: edgesBySymbol.get(normalizedSymbol)
|
|
815
|
+
}
|
|
816
|
+
);
|
|
817
|
+
if (requiredFromMs == null) return null;
|
|
745
818
|
return {
|
|
746
819
|
item,
|
|
747
|
-
fromMs:
|
|
820
|
+
fromMs: requiredFromMs
|
|
748
821
|
};
|
|
749
822
|
}).filter(
|
|
750
823
|
(item) => item != null
|
|
@@ -769,6 +842,7 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
|
|
|
769
842
|
);
|
|
770
843
|
let rows = [];
|
|
771
844
|
let missingClosedSymbols = [];
|
|
845
|
+
let liquidationRowsByMarket = /* @__PURE__ */ new Map();
|
|
772
846
|
for (let attempt = 1; attempt <= maxClosedBarAttempts; attempt += 1) {
|
|
773
847
|
const oiMap = await fetchMetricBatch({
|
|
774
848
|
endpoint: oiPath,
|
|
@@ -788,7 +862,7 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
|
|
|
788
862
|
fromMs: group.fromMs,
|
|
789
863
|
toMs
|
|
790
864
|
});
|
|
791
|
-
|
|
865
|
+
liquidationRowsByMarket = await fetchMetricBatch({
|
|
792
866
|
endpoint: liqPath,
|
|
793
867
|
metric: "liq",
|
|
794
868
|
marketSymbols,
|
|
@@ -803,7 +877,7 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
|
|
|
803
877
|
symbol: item.symbol,
|
|
804
878
|
oiRaw: oiMap.get(marketSymbol) ?? [],
|
|
805
879
|
fundingRaw: fundingMap.get(marketSymbol) ?? [],
|
|
806
|
-
liqRaw:
|
|
880
|
+
liqRaw: liquidationRowsByMarket.get(marketSymbol) ?? []
|
|
807
881
|
});
|
|
808
882
|
return (0, import_indicators.coinalyzePointsToRows)(points, interval, "coinalyze");
|
|
809
883
|
});
|
|
@@ -826,6 +900,42 @@ var backfillDerivativesContext = async (params, enabled, mode) => {
|
|
|
826
900
|
await (0, import_timescale.upsertDerivatives)(rows);
|
|
827
901
|
totalRows += rows.length;
|
|
828
902
|
}
|
|
903
|
+
const confirmedLiquidationWindow = (0, import_indicators.resolveCoinalyzeConfirmedIntradayCoverage)({
|
|
904
|
+
interval,
|
|
905
|
+
fromMs: group.fromMs,
|
|
906
|
+
toMs,
|
|
907
|
+
nowMs: coverageNowMs
|
|
908
|
+
});
|
|
909
|
+
if (confirmedLiquidationWindow) {
|
|
910
|
+
const metricCoverageRows = missingBatch.map((item) => {
|
|
911
|
+
const marketSymbol = item.marketSymbol.toUpperCase();
|
|
912
|
+
const eventRowsCount = (liquidationRowsByMarket.get(marketSymbol) ?? []).filter((point) => {
|
|
913
|
+
const timestamp = (0, import_indicators.toCoinalyzeTimestampMs)(
|
|
914
|
+
point.t ?? point.ts ?? point.time ?? point.timestamp
|
|
915
|
+
);
|
|
916
|
+
return timestamp != null && timestamp >= confirmedLiquidationWindow.fromMs && timestamp <= confirmedLiquidationWindow.toMs;
|
|
917
|
+
}).length;
|
|
918
|
+
return {
|
|
919
|
+
source: "coinalyze",
|
|
920
|
+
metric: "liquidation",
|
|
921
|
+
symbol: item.symbol,
|
|
922
|
+
interval,
|
|
923
|
+
fromMs: confirmedLiquidationWindow.fromMs,
|
|
924
|
+
toMs: confirmedLiquidationWindow.toMs,
|
|
925
|
+
eventRowsCount
|
|
926
|
+
};
|
|
927
|
+
});
|
|
928
|
+
await (0, import_timescale.applyDerivativesMetricCoverage)(metricCoverageRows);
|
|
929
|
+
for (const coverageRow of metricCoverageRows) {
|
|
930
|
+
const symbol = coverageRow.symbol.toUpperCase();
|
|
931
|
+
const ranges = liquidationCoverageRangesBySymbol.get(symbol) ?? [];
|
|
932
|
+
ranges.push({
|
|
933
|
+
fromMs: coverageRow.fromMs,
|
|
934
|
+
toMs: coverageRow.toMs
|
|
935
|
+
});
|
|
936
|
+
liquidationCoverageRangesBySymbol.set(symbol, ranges);
|
|
937
|
+
}
|
|
938
|
+
}
|
|
829
939
|
if (mode === "backtest") {
|
|
830
940
|
const rowsCountBySymbol = /* @__PURE__ */ new Map();
|
|
831
941
|
for (const row of rows) {
|
|
@@ -939,6 +1049,7 @@ var backfillDerivativesContextForSignals = async (params) => backfillDerivatives
|
|
|
939
1049
|
resolveDerivativesContextLookbackMs,
|
|
940
1050
|
resolveDerivativesContextMissingCoverageFetchFromMs,
|
|
941
1051
|
resolveDerivativesContextMissingFetchFromMs,
|
|
1052
|
+
resolveDerivativesContextRequiredFetchFromMs,
|
|
942
1053
|
shouldBackfillDerivativesContextForBacktest,
|
|
943
1054
|
shouldBackfillDerivativesContextForSignals
|
|
944
1055
|
});
|