@tradejs/cli 1.0.5 → 1.0.8
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 +4 -1
- package/dist/cli.js +13791 -7652
- package/dist/lib/aiTrainMetrics.js +173 -0
- package/dist/lib/candlesProviderMigration.js +266 -0
- package/dist/lib/cliArgs.js +40 -0
- package/dist/lib/derivativesContextBackfill.js +487 -0
- package/dist/lib/researchAgent.js +195 -0
- package/dist/lib/runtimeParity.js +234 -0
- package/dist/lib/telegramReports.js +91 -0
- package/dist/lib/timeWindow.js +101 -0
- package/dist/scripts/agentRun.js +895 -0
- package/dist/scripts/aiExport.js +192 -0
- package/dist/scripts/aiExportSelect.js +135 -0
- package/dist/scripts/aiTrain.js +2321 -0
- package/dist/scripts/backtest.js +998 -111
- package/dist/scripts/candlesMigrateProvider.js +5302 -0
- package/dist/scripts/continuity.js +1 -1
- package/dist/scripts/derivativesIngest.js +5 -0
- package/dist/scripts/derivativesIngestCoinalyzeAll.js +21 -12
- package/dist/scripts/migration.js +1 -1
- package/dist/scripts/mlExport.js +112 -13
- package/dist/scripts/mlExportSelect.js +43 -9
- package/dist/scripts/mlTrainLatestSelect.js +4 -3
- package/dist/scripts/researchAuto.js +845 -0
- package/dist/scripts/resolveExportStrategy.js +133 -0
- package/dist/scripts/results.js +46 -22
- package/dist/scripts/runtimeParity.js +1651 -0
- package/dist/scripts/selectStrategy.js +4 -3
- package/dist/scripts/signals.js +799 -62
- package/dist/scripts/signalsSummary.js +655 -0
- package/dist/scripts/spawnSiblingScript.js +71 -0
- package/dist/scripts/user-add.js +0 -9
- package/dist/workers/testerWorker.js +13 -2
- package/package.json +18 -12
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/lib/aiTrainMetrics.ts
|
|
21
|
+
var aiTrainMetrics_exports = {};
|
|
22
|
+
__export(aiTrainMetrics_exports, {
|
|
23
|
+
summarizeAiTrainEvaluations: () => summarizeAiTrainEvaluations,
|
|
24
|
+
summarizeAiTrainEvaluationsByDirection: () => summarizeAiTrainEvaluationsByDirection
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(aiTrainMetrics_exports);
|
|
27
|
+
var divideOrNull = (num, denom) => {
|
|
28
|
+
if (denom <= 0) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
return num / denom;
|
|
32
|
+
};
|
|
33
|
+
var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
34
|
+
var DAYS_PER_WEEK = 7;
|
|
35
|
+
var DAYS_PER_MONTH = 30.4375;
|
|
36
|
+
var getEvaluationPeriodDays = (evaluations) => {
|
|
37
|
+
const timestamps = evaluations.map(
|
|
38
|
+
(evaluation) => typeof evaluation.timestamp === "number" && Number.isFinite(evaluation.timestamp) ? evaluation.timestamp : null
|
|
39
|
+
).filter((timestamp) => timestamp != null);
|
|
40
|
+
if (!timestamps.length) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
const minTimestamp = Math.min(...timestamps);
|
|
44
|
+
const maxTimestamp = Math.max(...timestamps);
|
|
45
|
+
return Math.max((maxTimestamp - minTimestamp) / DAY_MS, 1);
|
|
46
|
+
};
|
|
47
|
+
var qualitySortKey = (quality) => quality == null ? Number.POSITIVE_INFINITY : quality;
|
|
48
|
+
var summarizeAiTrainEvaluations = (evaluations) => {
|
|
49
|
+
let approved = 0;
|
|
50
|
+
let rejected = 0;
|
|
51
|
+
let correct = 0;
|
|
52
|
+
let incorrect = 0;
|
|
53
|
+
let profitable = 0;
|
|
54
|
+
let unprofitable = 0;
|
|
55
|
+
let flat = 0;
|
|
56
|
+
let truePositive = 0;
|
|
57
|
+
let falsePositive = 0;
|
|
58
|
+
let trueNegative = 0;
|
|
59
|
+
let falseNegative = 0;
|
|
60
|
+
let totalProfitAll = 0;
|
|
61
|
+
let totalProfitApproved = 0;
|
|
62
|
+
const bucketMap = /* @__PURE__ */ new Map();
|
|
63
|
+
for (const evaluation of evaluations) {
|
|
64
|
+
const { profit, profitableTrade, aiApproved, quality } = evaluation;
|
|
65
|
+
totalProfitAll += profit;
|
|
66
|
+
if (profitableTrade) {
|
|
67
|
+
profitable += 1;
|
|
68
|
+
} else if (profit < 0) {
|
|
69
|
+
unprofitable += 1;
|
|
70
|
+
} else {
|
|
71
|
+
flat += 1;
|
|
72
|
+
}
|
|
73
|
+
if (aiApproved) {
|
|
74
|
+
approved += 1;
|
|
75
|
+
totalProfitApproved += profit;
|
|
76
|
+
} else {
|
|
77
|
+
rejected += 1;
|
|
78
|
+
}
|
|
79
|
+
const isCorrect = aiApproved === profitableTrade;
|
|
80
|
+
if (isCorrect) {
|
|
81
|
+
correct += 1;
|
|
82
|
+
} else {
|
|
83
|
+
incorrect += 1;
|
|
84
|
+
}
|
|
85
|
+
if (aiApproved && profitableTrade) {
|
|
86
|
+
truePositive += 1;
|
|
87
|
+
} else if (aiApproved && !profitableTrade) {
|
|
88
|
+
falsePositive += 1;
|
|
89
|
+
} else if (!aiApproved && profitableTrade) {
|
|
90
|
+
falseNegative += 1;
|
|
91
|
+
} else {
|
|
92
|
+
trueNegative += 1;
|
|
93
|
+
}
|
|
94
|
+
const bucket = bucketMap.get(quality) ?? {
|
|
95
|
+
quality,
|
|
96
|
+
count: 0,
|
|
97
|
+
approved: 0,
|
|
98
|
+
profitable: 0,
|
|
99
|
+
totalProfit: 0
|
|
100
|
+
};
|
|
101
|
+
bucket.count += 1;
|
|
102
|
+
bucket.totalProfit += profit;
|
|
103
|
+
if (aiApproved) {
|
|
104
|
+
bucket.approved += 1;
|
|
105
|
+
}
|
|
106
|
+
if (profitableTrade) {
|
|
107
|
+
bucket.profitable += 1;
|
|
108
|
+
}
|
|
109
|
+
bucketMap.set(quality, bucket);
|
|
110
|
+
}
|
|
111
|
+
const avgProfitAll = divideOrNull(totalProfitAll, evaluations.length);
|
|
112
|
+
const avgProfitApproved = divideOrNull(totalProfitApproved, approved);
|
|
113
|
+
const periodDays = getEvaluationPeriodDays(evaluations);
|
|
114
|
+
const avgProfitApprovedPerDay = periodDays == null ? null : divideOrNull(totalProfitApproved, periodDays);
|
|
115
|
+
const avgApprovedTradesPerDay = periodDays == null ? null : divideOrNull(approved, periodDays);
|
|
116
|
+
const avgProfitApprovedPerMonth = avgProfitApprovedPerDay == null ? null : avgProfitApprovedPerDay * DAYS_PER_MONTH;
|
|
117
|
+
const avgApprovedTradesPerWeek = avgApprovedTradesPerDay == null ? null : avgApprovedTradesPerDay * DAYS_PER_WEEK;
|
|
118
|
+
const expectancyDelta = avgProfitAll == null || avgProfitApproved == null ? null : avgProfitApproved - avgProfitAll;
|
|
119
|
+
return {
|
|
120
|
+
approved,
|
|
121
|
+
rejected,
|
|
122
|
+
correct,
|
|
123
|
+
incorrect,
|
|
124
|
+
truePositive,
|
|
125
|
+
falsePositive,
|
|
126
|
+
trueNegative,
|
|
127
|
+
falseNegative,
|
|
128
|
+
profitable,
|
|
129
|
+
unprofitable,
|
|
130
|
+
flat,
|
|
131
|
+
precisionApproved: divideOrNull(truePositive, approved),
|
|
132
|
+
recallWinners: divideOrNull(truePositive, profitable),
|
|
133
|
+
avgProfitAll,
|
|
134
|
+
avgProfitApproved,
|
|
135
|
+
avgProfitApprovedPerDay,
|
|
136
|
+
avgProfitApprovedPerMonth,
|
|
137
|
+
avgApprovedTradesPerDay,
|
|
138
|
+
avgApprovedTradesPerWeek,
|
|
139
|
+
expectancyDelta,
|
|
140
|
+
qualityBuckets: [...bucketMap.values()].sort(
|
|
141
|
+
(a, b) => qualitySortKey(a.quality) - qualitySortKey(b.quality)
|
|
142
|
+
)
|
|
143
|
+
};
|
|
144
|
+
};
|
|
145
|
+
var getDirectionSortKey = (direction) => {
|
|
146
|
+
if (direction === "LONG") {
|
|
147
|
+
return 0;
|
|
148
|
+
}
|
|
149
|
+
if (direction === "SHORT") {
|
|
150
|
+
return 1;
|
|
151
|
+
}
|
|
152
|
+
return 2;
|
|
153
|
+
};
|
|
154
|
+
var summarizeAiTrainEvaluationsByDirection = (evaluations) => {
|
|
155
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
156
|
+
for (const evaluation of evaluations) {
|
|
157
|
+
const direction = typeof evaluation.direction === "string" && evaluation.direction.trim() ? evaluation.direction : "UNKNOWN";
|
|
158
|
+
const bucket = grouped.get(direction) ?? [];
|
|
159
|
+
bucket.push(evaluation);
|
|
160
|
+
grouped.set(direction, bucket);
|
|
161
|
+
}
|
|
162
|
+
return [...grouped.entries()].sort(
|
|
163
|
+
([leftDirection], [rightDirection]) => getDirectionSortKey(leftDirection) - getDirectionSortKey(rightDirection) || leftDirection.localeCompare(rightDirection)
|
|
164
|
+
).map(([direction, directionEvaluations]) => ({
|
|
165
|
+
direction,
|
|
166
|
+
summary: summarizeAiTrainEvaluations(directionEvaluations)
|
|
167
|
+
}));
|
|
168
|
+
};
|
|
169
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
170
|
+
0 && (module.exports = {
|
|
171
|
+
summarizeAiTrainEvaluations,
|
|
172
|
+
summarizeAiTrainEvaluationsByDirection
|
|
173
|
+
});
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/lib/candlesProviderMigration.ts
|
|
21
|
+
var candlesProviderMigration_exports = {};
|
|
22
|
+
__export(candlesProviderMigration_exports, {
|
|
23
|
+
runCandlesProviderMigration: () => runCandlesProviderMigration
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(candlesProviderMigration_exports);
|
|
26
|
+
var LOCK_KEY = "tradejs:candles-provider-migration";
|
|
27
|
+
var assertIdentifier = (value, label) => {
|
|
28
|
+
const normalized = String(value || "").trim();
|
|
29
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(normalized)) {
|
|
30
|
+
throw new Error(`Invalid ${label}: ${value}`);
|
|
31
|
+
}
|
|
32
|
+
return normalized;
|
|
33
|
+
};
|
|
34
|
+
var quoteIdentifier = (value) => `"${value.replace(/"/g, '""')}"`;
|
|
35
|
+
var quoteQualifiedName = (schema, table) => `${quoteIdentifier(schema)}.${quoteIdentifier(table)}`;
|
|
36
|
+
var toQualifiedRegclass = (schema, table) => `${quoteIdentifier(schema)}.${quoteIdentifier(table)}`;
|
|
37
|
+
var getProviderColumnExists = async (db, schema, table) => {
|
|
38
|
+
const res = await db.query(
|
|
39
|
+
`
|
|
40
|
+
SELECT EXISTS (
|
|
41
|
+
SELECT 1
|
|
42
|
+
FROM information_schema.columns
|
|
43
|
+
WHERE table_schema = $1
|
|
44
|
+
AND table_name = $2
|
|
45
|
+
AND column_name = 'provider'
|
|
46
|
+
) AS exists
|
|
47
|
+
`,
|
|
48
|
+
[schema, table]
|
|
49
|
+
);
|
|
50
|
+
return Boolean(res.rows[0]?.exists);
|
|
51
|
+
};
|
|
52
|
+
var getPrimaryKeyColumns = async (db, schema, table) => {
|
|
53
|
+
const res = await db.query(
|
|
54
|
+
`
|
|
55
|
+
SELECT a.attname AS column_name
|
|
56
|
+
FROM pg_index i
|
|
57
|
+
JOIN pg_class t
|
|
58
|
+
ON t.oid = i.indrelid
|
|
59
|
+
JOIN pg_namespace n
|
|
60
|
+
ON n.oid = t.relnamespace
|
|
61
|
+
JOIN unnest(i.indkey) WITH ORDINALITY AS keys(attnum, ord)
|
|
62
|
+
ON true
|
|
63
|
+
JOIN pg_attribute a
|
|
64
|
+
ON a.attrelid = t.oid
|
|
65
|
+
AND a.attnum = keys.attnum
|
|
66
|
+
WHERE i.indisprimary
|
|
67
|
+
AND n.nspname = $1
|
|
68
|
+
AND t.relname = $2
|
|
69
|
+
ORDER BY keys.ord ASC
|
|
70
|
+
`,
|
|
71
|
+
[schema, table]
|
|
72
|
+
);
|
|
73
|
+
return res.rows.map((row) => String(row.column_name));
|
|
74
|
+
};
|
|
75
|
+
var getCompressionJobs = async (db, schema, table) => {
|
|
76
|
+
const res = await db.query(
|
|
77
|
+
`
|
|
78
|
+
SELECT job_id, scheduled
|
|
79
|
+
FROM timescaledb_information.jobs
|
|
80
|
+
WHERE proc_name = 'policy_compression'
|
|
81
|
+
AND hypertable_schema = $1
|
|
82
|
+
AND hypertable_name = $2
|
|
83
|
+
ORDER BY job_id ASC
|
|
84
|
+
`,
|
|
85
|
+
[schema, table]
|
|
86
|
+
);
|
|
87
|
+
return res.rows.map((row) => ({
|
|
88
|
+
jobId: Number(row.job_id),
|
|
89
|
+
scheduled: Boolean(row.scheduled)
|
|
90
|
+
}));
|
|
91
|
+
};
|
|
92
|
+
var getCompressedChunks = async (db, schema, table) => {
|
|
93
|
+
const res = await db.query(
|
|
94
|
+
`
|
|
95
|
+
SELECT chunk::text AS chunk_name
|
|
96
|
+
FROM timescaledb_information.chunk_compression_settings
|
|
97
|
+
WHERE hypertable = $1::regclass
|
|
98
|
+
ORDER BY chunk::text ASC
|
|
99
|
+
`,
|
|
100
|
+
[toQualifiedRegclass(schema, table)]
|
|
101
|
+
);
|
|
102
|
+
return res.rows.map((row) => String(row.chunk_name));
|
|
103
|
+
};
|
|
104
|
+
var runCandlesProviderMigration = async (db, options = {}) => {
|
|
105
|
+
const schema = assertIdentifier(options.schema || "public", "schema");
|
|
106
|
+
const table = assertIdentifier(options.table || "candles", "table");
|
|
107
|
+
const dryRun = Boolean(options.dryRun);
|
|
108
|
+
const recompress = options.recompress !== false;
|
|
109
|
+
const keepPolicyPaused = Boolean(options.keepPolicyPaused);
|
|
110
|
+
const quotedTable = quoteQualifiedName(schema, table);
|
|
111
|
+
const onProgress = typeof options.onProgress === "function" ? options.onProgress : void 0;
|
|
112
|
+
const progress = (message) => {
|
|
113
|
+
onProgress?.(message);
|
|
114
|
+
};
|
|
115
|
+
const summary = {
|
|
116
|
+
schema,
|
|
117
|
+
table,
|
|
118
|
+
dryRun,
|
|
119
|
+
providerColumnExistsBefore: false,
|
|
120
|
+
providerBackfilledRows: 0,
|
|
121
|
+
primaryKeyColumnsBefore: [],
|
|
122
|
+
primaryKeyChanged: false,
|
|
123
|
+
compressionJobIds: [],
|
|
124
|
+
pausedJobIds: [],
|
|
125
|
+
resumedJobIds: [],
|
|
126
|
+
compressedChunksBefore: [],
|
|
127
|
+
decompressedChunks: [],
|
|
128
|
+
recompressedChunks: [],
|
|
129
|
+
indexEnsured: false,
|
|
130
|
+
compressionSettingsUpdated: false
|
|
131
|
+
};
|
|
132
|
+
const jobsToResume = /* @__PURE__ */ new Set();
|
|
133
|
+
progress(`Acquire migration lock for ${schema}.${table}`);
|
|
134
|
+
await db.query("SELECT pg_advisory_lock(hashtext($1))", [LOCK_KEY]);
|
|
135
|
+
try {
|
|
136
|
+
progress("Inspect current candles schema");
|
|
137
|
+
summary.providerColumnExistsBefore = await getProviderColumnExists(
|
|
138
|
+
db,
|
|
139
|
+
schema,
|
|
140
|
+
table
|
|
141
|
+
);
|
|
142
|
+
summary.primaryKeyColumnsBefore = await getPrimaryKeyColumns(
|
|
143
|
+
db,
|
|
144
|
+
schema,
|
|
145
|
+
table
|
|
146
|
+
);
|
|
147
|
+
const compressionJobs = await getCompressionJobs(db, schema, table);
|
|
148
|
+
summary.compressionJobIds = compressionJobs.map((job) => job.jobId);
|
|
149
|
+
progress(
|
|
150
|
+
`Found ${summary.compressionJobIds.length} compression job(s) for ${schema}.${table}`
|
|
151
|
+
);
|
|
152
|
+
summary.compressedChunksBefore = await getCompressedChunks(
|
|
153
|
+
db,
|
|
154
|
+
schema,
|
|
155
|
+
table
|
|
156
|
+
);
|
|
157
|
+
progress(
|
|
158
|
+
`Found ${summary.compressedChunksBefore.length} compressed chunk(s) to process`
|
|
159
|
+
);
|
|
160
|
+
if (dryRun) {
|
|
161
|
+
progress("Dry run finished");
|
|
162
|
+
return summary;
|
|
163
|
+
}
|
|
164
|
+
for (const [index, job] of compressionJobs.entries()) {
|
|
165
|
+
if (!job.scheduled) continue;
|
|
166
|
+
progress(
|
|
167
|
+
`Pause compression job ${job.jobId} (${index + 1}/${compressionJobs.length})`
|
|
168
|
+
);
|
|
169
|
+
await db.query("SELECT alter_job($1, scheduled => false)", [job.jobId]);
|
|
170
|
+
summary.pausedJobIds.push(job.jobId);
|
|
171
|
+
if (!keepPolicyPaused) {
|
|
172
|
+
jobsToResume.add(job.jobId);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
for (const [index, chunkName] of summary.compressedChunksBefore.entries()) {
|
|
176
|
+
progress(
|
|
177
|
+
`Decompress chunk ${index + 1}/${summary.compressedChunksBefore.length}: ${chunkName}`
|
|
178
|
+
);
|
|
179
|
+
await db.query("SELECT decompress_chunk($1::regclass, true)", [
|
|
180
|
+
chunkName
|
|
181
|
+
]);
|
|
182
|
+
summary.decompressedChunks.push(chunkName);
|
|
183
|
+
}
|
|
184
|
+
progress("Ensure provider column exists");
|
|
185
|
+
await db.query(
|
|
186
|
+
`ALTER TABLE ${quotedTable} ADD COLUMN IF NOT EXISTS provider text`
|
|
187
|
+
);
|
|
188
|
+
progress("Backfill provider='bybit' for existing rows");
|
|
189
|
+
const updateResult = await db.query(
|
|
190
|
+
`UPDATE ${quotedTable} SET provider = 'bybit' WHERE provider IS NULL`
|
|
191
|
+
);
|
|
192
|
+
summary.providerBackfilledRows = Number(updateResult.rowCount ?? 0);
|
|
193
|
+
progress("Set provider column default and NOT NULL");
|
|
194
|
+
await db.query(
|
|
195
|
+
`ALTER TABLE ${quotedTable}
|
|
196
|
+
ALTER COLUMN provider SET DEFAULT 'bybit',
|
|
197
|
+
ALTER COLUMN provider SET NOT NULL`
|
|
198
|
+
);
|
|
199
|
+
const expectedPrimaryKey = ["provider", "symbol", "interval", "ts"];
|
|
200
|
+
if (summary.primaryKeyColumnsBefore.join(",") !== expectedPrimaryKey.join(",")) {
|
|
201
|
+
progress("Replace primary key with provider-aware key");
|
|
202
|
+
await db.query(
|
|
203
|
+
`ALTER TABLE ${quotedTable} DROP CONSTRAINT IF EXISTS candles_pkey`
|
|
204
|
+
);
|
|
205
|
+
await db.query(
|
|
206
|
+
`ALTER TABLE ${quotedTable}
|
|
207
|
+
ADD CONSTRAINT candles_pkey
|
|
208
|
+
PRIMARY KEY (provider, symbol, interval, ts)`
|
|
209
|
+
);
|
|
210
|
+
summary.primaryKeyChanged = true;
|
|
211
|
+
}
|
|
212
|
+
progress("Recreate provider-aware candle index");
|
|
213
|
+
await db.query("DROP INDEX IF EXISTS candles_symbol_interval_ts_idx");
|
|
214
|
+
await db.query(
|
|
215
|
+
`CREATE INDEX IF NOT EXISTS candles_provider_symbol_interval_ts_idx
|
|
216
|
+
ON ${quotedTable} (provider, symbol, interval, ts DESC)`
|
|
217
|
+
);
|
|
218
|
+
summary.indexEnsured = true;
|
|
219
|
+
progress("Update Timescale compression settings");
|
|
220
|
+
await db.query(
|
|
221
|
+
`ALTER TABLE ${quotedTable} SET (
|
|
222
|
+
timescaledb.compress,
|
|
223
|
+
timescaledb.compress_segmentby = 'provider, symbol, interval'
|
|
224
|
+
)`
|
|
225
|
+
);
|
|
226
|
+
summary.compressionSettingsUpdated = true;
|
|
227
|
+
if (recompress) {
|
|
228
|
+
for (const [index, chunkName] of summary.decompressedChunks.entries()) {
|
|
229
|
+
progress(
|
|
230
|
+
`Recompress chunk ${index + 1}/${summary.decompressedChunks.length}: ${chunkName}`
|
|
231
|
+
);
|
|
232
|
+
await db.query("SELECT compress_chunk($1::regclass, true)", [
|
|
233
|
+
chunkName
|
|
234
|
+
]);
|
|
235
|
+
summary.recompressedChunks.push(chunkName);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
if (!keepPolicyPaused) {
|
|
239
|
+
const jobIds = [...jobsToResume];
|
|
240
|
+
for (const [index, jobId] of jobIds.entries()) {
|
|
241
|
+
progress(
|
|
242
|
+
`Resume compression job ${jobId} (${index + 1}/${jobIds.length})`
|
|
243
|
+
);
|
|
244
|
+
await db.query("SELECT alter_job($1, scheduled => true)", [jobId]);
|
|
245
|
+
summary.resumedJobIds.push(jobId);
|
|
246
|
+
}
|
|
247
|
+
jobsToResume.clear();
|
|
248
|
+
}
|
|
249
|
+
progress("Migration finished");
|
|
250
|
+
return summary;
|
|
251
|
+
} finally {
|
|
252
|
+
for (const jobId of jobsToResume) {
|
|
253
|
+
try {
|
|
254
|
+
progress(`Best-effort resume of compression job ${jobId}`);
|
|
255
|
+
await db.query("SELECT alter_job($1, scheduled => true)", [jobId]);
|
|
256
|
+
} catch {
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
progress(`Release migration lock for ${schema}.${table}`);
|
|
260
|
+
await db.query("SELECT pg_advisory_unlock(hashtext($1))", [LOCK_KEY]);
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
264
|
+
0 && (module.exports = {
|
|
265
|
+
runCandlesProviderMigration
|
|
266
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/lib/cliArgs.ts
|
|
21
|
+
var cliArgs_exports = {};
|
|
22
|
+
__export(cliArgs_exports, {
|
|
23
|
+
normalizeCliArgv: () => normalizeCliArgv
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(cliArgs_exports);
|
|
26
|
+
var normalizeCliArgv = (argv, legacyFlagMap) => argv.map((arg) => {
|
|
27
|
+
const [flag, ...rest] = String(arg).split("=");
|
|
28
|
+
const normalizedFlag = legacyFlagMap[flag];
|
|
29
|
+
if (!normalizedFlag) {
|
|
30
|
+
return arg;
|
|
31
|
+
}
|
|
32
|
+
if (!rest.length) {
|
|
33
|
+
return normalizedFlag;
|
|
34
|
+
}
|
|
35
|
+
return `${normalizedFlag}=${rest.join("=")}`;
|
|
36
|
+
});
|
|
37
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
38
|
+
0 && (module.exports = {
|
|
39
|
+
normalizeCliArgv
|
|
40
|
+
});
|