@tradejs/cli 1.0.0

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.
@@ -0,0 +1,153 @@
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 __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") {
10
+ for (let key of __getOwnPropNames(from))
11
+ if (!__hasOwnProp.call(to, key) && key !== except)
12
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
13
+ }
14
+ return to;
15
+ };
16
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
17
+ // If the importer is in node compatibility mode or this is not an ESM
18
+ // file that has been converted to a CommonJS file using a Babel-
19
+ // compatible transform (i.e. "__esModule" has not been set), then set
20
+ // "default" to the CommonJS "module.exports" for node compatibility.
21
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
22
+ mod
23
+ ));
24
+
25
+ // src/scripts/bot.ts
26
+ var import_config = require("dotenv/config");
27
+
28
+ // src/lib/runBot.ts
29
+ var import_lodash = __toESM(require("lodash"));
30
+ var import_connectors = require("@tradejs/node/connectors");
31
+ var import_json = require("@tradejs/core/json");
32
+ var import_strategies = require("@tradejs/node/strategies");
33
+ var import_async = require("@tradejs/core/async");
34
+ var import_constants = require("@tradejs/core/constants");
35
+ var import_time = require("@tradejs/core/time");
36
+ var import_logger = require("@tradejs/infra/logger");
37
+ var import_redis = require("@tradejs/infra/redis");
38
+ var runBot = async () => {
39
+ const botResults = [];
40
+ const preloadStart = (0, import_time.getTimestamp)(import_constants.BOT_PRELOAD_DAYS);
41
+ const end = (0, import_time.getTimestamp)();
42
+ const keys = await (0, import_redis.getKeys)(import_redis.redisKeys.botsPrefix());
43
+ const botKeys = keys.filter((key) => key.endsWith(":bots"));
44
+ await (0, import_async.delay)(5e3);
45
+ import_logger.logger.log("info", "files count: %s", botKeys.length);
46
+ for await (const key of botKeys) {
47
+ const userName = key.split(":")[1];
48
+ import_logger.logger.log("info", "user: %s", userName);
49
+ const botConfig = await (0, import_redis.getData)(import_redis.redisKeys.bots(userName));
50
+ if (import_lodash.default.isEmpty(botConfig)) {
51
+ import_logger.logger.log("error", "botConfig is empty: %s", userName);
52
+ continue;
53
+ }
54
+ import_logger.logger.log("info", "bots count: %s", botConfig.length);
55
+ for await (const bot of botConfig) {
56
+ const { symbol, strategyName, strategyConfig, connectorName, disabled } = bot;
57
+ import_logger.logger.log("info", "bot %s", symbol);
58
+ if (disabled) {
59
+ import_logger.logger.log("error", "bot %s disabled", symbol);
60
+ continue;
61
+ }
62
+ try {
63
+ const strategyCreator = await (0, import_strategies.getStrategyCreator)(strategyName);
64
+ if (!strategyCreator) {
65
+ throw new Error(`Unknown strategy: ${strategyName}`);
66
+ }
67
+ const connectorCreator = await (0, import_connectors.getConnectorCreatorByName)(connectorName);
68
+ if (!connectorCreator) {
69
+ throw new Error(`Unknown connector: ${connectorName}`);
70
+ }
71
+ const connector = await connectorCreator({
72
+ userName
73
+ });
74
+ const binanceConnectorCreator = await (0, import_connectors.getConnectorCreatorByName)(
75
+ import_connectors.BUILTIN_CONNECTOR_NAMES.Binance
76
+ );
77
+ const coinbaseConnectorCreator = await (0, import_connectors.getConnectorCreatorByName)(
78
+ import_connectors.BUILTIN_CONNECTOR_NAMES.Coinbase
79
+ );
80
+ if (!binanceConnectorCreator || !coinbaseConnectorCreator) {
81
+ throw new Error("Binance/Coinbase connectors are required");
82
+ }
83
+ const [binanceConnector, coinbaseConnector] = await Promise.all([
84
+ binanceConnectorCreator({
85
+ userName
86
+ }),
87
+ coinbaseConnectorCreator({
88
+ userName
89
+ })
90
+ ]);
91
+ const interval = "15";
92
+ const data = await connector.kline({
93
+ symbol,
94
+ start: preloadStart,
95
+ end,
96
+ interval
97
+ });
98
+ const [btcData, btcBinanceData, btcCoinbaseData] = await Promise.all([
99
+ connector.kline({
100
+ symbol: "BTCUSDT",
101
+ start: preloadStart,
102
+ end,
103
+ interval
104
+ }),
105
+ binanceConnector.kline({
106
+ symbol: "BTCUSDT",
107
+ start: preloadStart,
108
+ end,
109
+ interval
110
+ }),
111
+ coinbaseConnector.kline({
112
+ symbol: "BTCUSDT",
113
+ start: preloadStart,
114
+ end,
115
+ interval
116
+ })
117
+ ]);
118
+ const candle = data.pop();
119
+ const btcCandle = btcData.pop();
120
+ const strategy = await strategyCreator({
121
+ userName,
122
+ config: strategyConfig,
123
+ symbol,
124
+ data,
125
+ btcData,
126
+ btcBinanceData,
127
+ btcCoinbaseData,
128
+ connector
129
+ });
130
+ import_logger.logger.log("info", "strategy created");
131
+ if (!candle || !btcCandle) {
132
+ throw new Error("Candle is empty");
133
+ }
134
+ const status = await strategy(candle, btcCandle);
135
+ botResults.push({
136
+ symbol,
137
+ status: JSON.stringify(status)
138
+ });
139
+ } catch (err) {
140
+ import_logger.logger.log("error", "bot %s error: %s", bot.symbol, (0, import_json.toJson)(err, false));
141
+ }
142
+ }
143
+ }
144
+ import_logger.logger.log("info", "botResults: %s", (0, import_json.toJson)(botResults, true));
145
+ return botResults;
146
+ };
147
+
148
+ // src/scripts/bot.ts
149
+ var run = async () => {
150
+ await runBot();
151
+ process.exit();
152
+ };
153
+ run();
@@ -0,0 +1,40 @@
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 __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") {
10
+ for (let key of __getOwnPropNames(from))
11
+ if (!__hasOwnProp.call(to, key) && key !== except)
12
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
13
+ }
14
+ return to;
15
+ };
16
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
17
+ // If the importer is in node compatibility mode or this is not an ESM
18
+ // file that has been converted to a CommonJS file using a Babel-
19
+ // compatible transform (i.e. "__esModule" has not been set), then set
20
+ // "default" to the CommonJS "module.exports" for node compatibility.
21
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
22
+ mod
23
+ ));
24
+
25
+ // src/scripts/cleanDir.ts
26
+ var import_args = __toESM(require("args"));
27
+ var import_cli = require("@tradejs/node/cli");
28
+ import_args.default.option("dir", "Directory clean up", "cache");
29
+ var flags = import_args.default.parse(process.argv);
30
+ var getDir = () => {
31
+ if (flags.dir) {
32
+ return `data/${flags.dir}`;
33
+ }
34
+ return "data/cache";
35
+ };
36
+ var run = async () => {
37
+ await (0, import_cli.cleanFiles)(getDir());
38
+ process.exit();
39
+ };
40
+ run();
@@ -0,0 +1,40 @@
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 __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") {
10
+ for (let key of __getOwnPropNames(from))
11
+ if (!__hasOwnProp.call(to, key) && key !== except)
12
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
13
+ }
14
+ return to;
15
+ };
16
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
17
+ // If the importer is in node compatibility mode or this is not an ESM
18
+ // file that has been converted to a CommonJS file using a Babel-
19
+ // compatible transform (i.e. "__esModule" has not been set), then set
20
+ // "default" to the CommonJS "module.exports" for node compatibility.
21
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
22
+ mod
23
+ ));
24
+
25
+ // src/scripts/cleanRedis.ts
26
+ var import_args = __toESM(require("args"));
27
+ var import_cli = require("@tradejs/node/cli");
28
+ import_args.default.option("area", "Area clean up", "cache");
29
+ var flags = import_args.default.parse(process.argv);
30
+ var getArea = () => {
31
+ if (flags.area) {
32
+ return flags.area;
33
+ }
34
+ return "cache";
35
+ };
36
+ var run = async () => {
37
+ await (0, import_cli.cleanRedis)(getArea());
38
+ process.exit();
39
+ };
40
+ run();
@@ -0,0 +1,81 @@
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 __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") {
10
+ for (let key of __getOwnPropNames(from))
11
+ if (!__hasOwnProp.call(to, key) && key !== except)
12
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
13
+ }
14
+ return to;
15
+ };
16
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
17
+ // If the importer is in node compatibility mode or this is not an ESM
18
+ // file that has been converted to a CommonJS file using a Babel-
19
+ // compatible transform (i.e. "__esModule" has not been set), then set
20
+ // "default" to the CommonJS "module.exports" for node compatibility.
21
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
22
+ mod
23
+ ));
24
+
25
+ // src/scripts/cleanTests.ts
26
+ var import_args = __toESM(require("args"));
27
+ var import_chalk = __toESM(require("chalk"));
28
+ var import_cli = require("@tradejs/node/cli");
29
+ var import_redis = require("@tradejs/infra/redis");
30
+ import_args.default.option(["U", "user"], "Clean tests for user", "");
31
+ import_args.default.option(["C", "cache"], "Clean only cache keys", false);
32
+ var flags = import_args.default.parse(process.argv);
33
+ var extractUsers = (keys) => {
34
+ const users = /* @__PURE__ */ new Set();
35
+ for (const key of keys) {
36
+ const parts = key.split(":");
37
+ if (parts.length < 2) {
38
+ continue;
39
+ }
40
+ if (parts[0] !== "users" || parts[1] !== "index") {
41
+ continue;
42
+ }
43
+ const userName = (parts[2] ?? "").trim();
44
+ if (userName) {
45
+ users.add(userName);
46
+ }
47
+ }
48
+ return Array.from(users).sort();
49
+ };
50
+ var getUsersToClean = async () => {
51
+ const userName = String(flags.user || "").trim();
52
+ if (userName) {
53
+ return [userName];
54
+ }
55
+ const keys = await (0, import_redis.getKeys)(import_redis.redisKeys.users());
56
+ return extractUsers(keys);
57
+ };
58
+ var cleanUserTests = async (userName) => {
59
+ const testsPrefix = `users:${userName}:tests:`;
60
+ const testsCachePrefix = `users:${userName}:cache:tests:`;
61
+ if (flags.cache) {
62
+ console.log(import_chalk.default.yellow(`clean user cache: ${userName}`));
63
+ await (0, import_cli.cleanRedis)(testsCachePrefix);
64
+ return;
65
+ }
66
+ console.log(import_chalk.default.yellow(`clean user tests: ${userName}`));
67
+ await (0, import_cli.cleanRedis)(testsPrefix);
68
+ await (0, import_cli.cleanRedis)(testsCachePrefix);
69
+ };
70
+ var run = async () => {
71
+ const users = await getUsersToClean();
72
+ if (users.length === 0) {
73
+ console.log(import_chalk.default.yellow("No users found to clean tests."));
74
+ process.exit();
75
+ }
76
+ for await (const userName of users) {
77
+ await cleanUserTests(userName);
78
+ }
79
+ process.exit();
80
+ };
81
+ run();
@@ -0,0 +1,183 @@
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 __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") {
10
+ for (let key of __getOwnPropNames(from))
11
+ if (!__hasOwnProp.call(to, key) && key !== except)
12
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
13
+ }
14
+ return to;
15
+ };
16
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
17
+ // If the importer is in node compatibility mode or this is not an ESM
18
+ // file that has been converted to a CommonJS file using a Babel-
19
+ // compatible transform (i.e. "__esModule" has not been set), then set
20
+ // "default" to the CommonJS "module.exports" for node compatibility.
21
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
22
+ mod
23
+ ));
24
+
25
+ // src/scripts/continuity.ts
26
+ var import_args = __toESM(require("args"));
27
+ var import_progress = __toESM(require("progress"));
28
+ var import_chalk = __toESM(require("chalk"));
29
+ var import_connectors = require("@tradejs/node/connectors");
30
+ var import_cli = require("@tradejs/node/cli");
31
+ var import_constants = require("@tradejs/core/constants");
32
+ var import_time = require("@tradejs/core/time");
33
+ var import_logger = require("@tradejs/infra/logger");
34
+ var import_timescale = require("@tradejs/infra/timescale");
35
+ import_args.default.option(["t", "tickers"], "Selected tickers");
36
+ import_args.default.option(["f", "timeframe"], "Timeframe", 15);
37
+ import_args.default.option(
38
+ ["p", "provider"],
39
+ "Data provider: all|bybit|binance|coinbase or comma list",
40
+ "all"
41
+ );
42
+ import_args.default.option(["U", "user"], "Use user confg", "root");
43
+ var flags = import_args.default.parse(process.argv);
44
+ var interval = Number(flags.timeframe);
45
+ var intervalKey = flags.timeframe.toString();
46
+ var parseProviders = async (value) => {
47
+ const allProviders = await (0, import_connectors.getAvailableConnectorProviders)();
48
+ const raw = String(value || "").trim().toLowerCase();
49
+ if (!raw || raw === "all") {
50
+ return allProviders;
51
+ }
52
+ const selected = raw.split(",").map((item) => item.trim()).filter(Boolean);
53
+ const uniqueSelected = [...new Set(selected)];
54
+ const invalid = uniqueSelected.filter((item) => !allProviders.includes(item));
55
+ if (invalid.length) {
56
+ import_logger.logger.error(
57
+ "Unknown provider(s): %s. Supported: %s",
58
+ invalid.join(", "),
59
+ allProviders.join(", ")
60
+ );
61
+ process.exit(1);
62
+ }
63
+ return uniqueSelected;
64
+ };
65
+ var findGapInData = (data, expectedMs) => {
66
+ for (let i = 1; i < data.length; i++) {
67
+ const prev = data[i - 1];
68
+ const current = data[i];
69
+ const diff = current.timestamp - prev.timestamp;
70
+ if (diff !== expectedMs) {
71
+ return {
72
+ prevTs: prev.timestamp,
73
+ ts: current.timestamp,
74
+ diffSeconds: Math.floor(diff / 1e3)
75
+ };
76
+ }
77
+ }
78
+ return null;
79
+ };
80
+ var continuity = async () => {
81
+ if (!Number.isFinite(interval) || interval <= 0) {
82
+ import_logger.logger.error("Invalid timeframe: %s", flags.timeframe);
83
+ process.exit(1);
84
+ }
85
+ await (0, import_timescale.waitForDbReady)();
86
+ const reloadStart = (0, import_time.getTimestamp)(import_constants.PRELOAD_DAYS);
87
+ const reloadEnd = (0, import_time.getTimestamp)();
88
+ const providerIds = await parseProviders(flags.provider);
89
+ const providers = await Promise.all(
90
+ providerIds.map(async (providerId) => {
91
+ const connectorName = await (0, import_connectors.getConnectorNameByProvider)(providerId);
92
+ if (!connectorName) {
93
+ import_logger.logger.warn(
94
+ 'Skip provider "%s": connector mapping is missing',
95
+ providerId
96
+ );
97
+ return null;
98
+ }
99
+ const creator = await (0, import_connectors.getConnectorCreatorByName)(connectorName);
100
+ if (!creator) {
101
+ import_logger.logger.warn(
102
+ 'Skip provider "%s": connector "%s" is not registered',
103
+ providerId,
104
+ connectorName
105
+ );
106
+ return null;
107
+ }
108
+ return {
109
+ id: providerId,
110
+ name: connectorName,
111
+ create: creator
112
+ };
113
+ })
114
+ );
115
+ const activeProviders = providers.filter(Boolean);
116
+ if (!activeProviders.length) {
117
+ import_logger.logger.error("No connector providers available");
118
+ process.exit(1);
119
+ }
120
+ for await (const provider of activeProviders) {
121
+ const connector = await provider.create({
122
+ userName: flags.user
123
+ });
124
+ const tickers = await (0, import_cli.getTickers)(connector, flags.tickers);
125
+ const bar = new import_progress.default(
126
+ ":current/:total [:bar][:percent] broken::broken fixed::fixed :eta(s) :symbol",
127
+ {
128
+ total: tickers.length,
129
+ width: 30
130
+ }
131
+ );
132
+ let broken = 0;
133
+ let fixed = 0;
134
+ const expectedMs = interval * 60 * 1e3;
135
+ import_logger.logger.info(import_chalk.default.yellow(`continuity ${provider.name}: ${tickers.length}`));
136
+ for await (const symbol of tickers) {
137
+ const data = await connector.kline({
138
+ symbol,
139
+ interval: intervalKey,
140
+ start: reloadStart,
141
+ end: reloadEnd,
142
+ silent: true
143
+ });
144
+ let gap = findGapInData(data, expectedMs);
145
+ if (gap) {
146
+ broken++;
147
+ import_logger.logger.warn(
148
+ "[%s] gap %s %s: %s -> %s (%ss)",
149
+ provider.id,
150
+ symbol,
151
+ interval,
152
+ (0, import_time.formatUnix)(gap.prevTs),
153
+ (0, import_time.formatUnix)(gap.ts),
154
+ gap.diffSeconds
155
+ );
156
+ await (0, import_timescale.deleteCandles)(symbol, interval);
157
+ const reloaded = await connector.kline({
158
+ symbol,
159
+ interval: intervalKey,
160
+ start: reloadStart,
161
+ end: reloadEnd,
162
+ silent: true
163
+ });
164
+ gap = findGapInData(reloaded, expectedMs);
165
+ if (!gap) {
166
+ fixed++;
167
+ }
168
+ }
169
+ bar.tick(1, {
170
+ broken: import_chalk.default.yellow(broken),
171
+ fixed: import_chalk.default.cyan(fixed),
172
+ symbol: import_chalk.default.gray(symbol)
173
+ });
174
+ }
175
+ import_logger.logger.info(
176
+ import_chalk.default.yellow(
177
+ `[${provider.id}] broken: ${broken}/${tickers.length}, fixed: ${fixed}`
178
+ )
179
+ );
180
+ }
181
+ process.exit();
182
+ };
183
+ continuity();
@@ -0,0 +1,107 @@
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 __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") {
10
+ for (let key of __getOwnPropNames(from))
11
+ if (!__hasOwnProp.call(to, key) && key !== except)
12
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
13
+ }
14
+ return to;
15
+ };
16
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
17
+ // If the importer is in node compatibility mode or this is not an ESM
18
+ // file that has been converted to a CommonJS file using a Babel-
19
+ // compatible transform (i.e. "__esModule" has not been set), then set
20
+ // "default" to the CommonJS "module.exports" for node compatibility.
21
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
22
+ mod
23
+ ));
24
+
25
+ // src/scripts/derivativesIngest.ts
26
+ var import_config = require("dotenv/config");
27
+ var import_args = __toESM(require("args"));
28
+ var import_chalk = __toESM(require("chalk"));
29
+ var import_indicators = require("@tradejs/core/indicators");
30
+ var import_timescale = require("@tradejs/infra/timescale");
31
+ var import_connectors = require("@tradejs/connectors");
32
+ import_args.default.example(
33
+ "yarn ts-node ./src/scripts/derivativesIngest --provider coinalyze --symbols BTCUSDT,ETHUSDT --intervals 15m,1h --days 120",
34
+ "Ingest market features (derivatives/spread) into Timescale by provider"
35
+ );
36
+ import_args.default.option(["s", "symbols"], "Comma-separated symbols", "BTCUSDT,ETHUSDT");
37
+ import_args.default.option(["t", "intervals"], "Comma-separated intervals: 15m,1h", "15m,1h");
38
+ import_args.default.option(["d", "days"], "Lookback in days", 120);
39
+ import_args.default.option(
40
+ ["p", "provider"],
41
+ "Provider name: coinalyze | binance_coinbase_spread",
42
+ "coinalyze"
43
+ );
44
+ import_args.default.option(["b", "batchDays"], "Request chunk size in days", 7);
45
+ var flags = import_args.default.parse(process.argv);
46
+ var asInt = (value, fallback) => {
47
+ const parsed = Number.parseInt(String(value ?? ""), 10);
48
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
49
+ };
50
+ var run = async () => {
51
+ const providerName = String(flags.provider || "coinalyze").trim().toLowerCase();
52
+ const provider = import_connectors.marketDataProviders[providerName];
53
+ if (!provider) {
54
+ throw new Error(`Unsupported provider: ${providerName}`);
55
+ }
56
+ const symbols = (0, import_indicators.normalizeCoinalyzeSymbols)(flags.symbols);
57
+ const intervals = (0, import_indicators.normalizeDerivativesIntervals)(
58
+ flags.intervals
59
+ );
60
+ const days = asInt(flags.days, 120);
61
+ const batchDays = asInt(flags.batchDays, 7);
62
+ if (!symbols.length) throw new Error("No symbols provided");
63
+ if (!intervals.length) throw new Error("No intervals provided");
64
+ await (0, import_timescale.waitForDbReady)();
65
+ const now = Date.now();
66
+ const fromMs = now - days * 24 * 60 * 60 * 1e3;
67
+ let totalDerivativesRows = 0;
68
+ let totalSpreadRows = 0;
69
+ for (const symbol of symbols) {
70
+ for (const interval of intervals) {
71
+ let cursor = fromMs;
72
+ while (cursor < now) {
73
+ const toMs = Math.min(now, cursor + batchDays * 24 * 60 * 60 * 1e3);
74
+ process.stdout.write(
75
+ `\r${import_chalk.default.cyan(providerName)} ${import_chalk.default.yellow(symbol)} ${interval} ${new Date(cursor).toISOString()} .. ${new Date(toMs).toISOString()} `
76
+ );
77
+ const window = await provider.fetchWindow({
78
+ symbol,
79
+ interval,
80
+ fromMs: cursor,
81
+ toMs
82
+ });
83
+ const derivativesRows = window.derivativesRows ?? [];
84
+ if (derivativesRows.length) {
85
+ await (0, import_timescale.upsertDerivatives)(derivativesRows);
86
+ totalDerivativesRows += derivativesRows.length;
87
+ }
88
+ const spreadRows = window.spreadRows ?? [];
89
+ if (spreadRows.length) {
90
+ await (0, import_timescale.upsertSpreadRows)(spreadRows);
91
+ totalSpreadRows += spreadRows.length;
92
+ }
93
+ cursor = toMs + 1;
94
+ }
95
+ process.stdout.write("\n");
96
+ }
97
+ }
98
+ console.log(
99
+ import_chalk.default.green(
100
+ `Done. provider=${providerName} derivatives_rows=${totalDerivativesRows} spread_rows=${totalSpreadRows}`
101
+ )
102
+ );
103
+ };
104
+ run().catch((error) => {
105
+ console.error(import_chalk.default.red(`derivativesIngest failed: ${error}`));
106
+ process.exit(1);
107
+ });