107 lines
2.5 KiB
JavaScript
107 lines
2.5 KiB
JavaScript
import { parseArgs } from "node:util";
|
|
import { prefetchRates } from "./src/api.js";
|
|
import { Bot } from "./src/bot.js";
|
|
import logger from "./src/logger.js";
|
|
import { initDB, insertIntoDB, closePool } from "./src/db.js";
|
|
import { startServer } from "./src/server.js";
|
|
|
|
process.on("uncaughtException", (err) => {
|
|
logger.fatal(err, "Uncaught exception. Application crashing...");
|
|
process.exit(1);
|
|
});
|
|
|
|
process.on("unhandledRejection", (reason) => {
|
|
logger.fatal(reason, "Unhandled promise Rejection");
|
|
process.exit(1);
|
|
});
|
|
|
|
const options = {
|
|
pairs: {
|
|
type: "string",
|
|
short: "p",
|
|
default: process.env.PAIRS || "BTC-USD",
|
|
},
|
|
interval: {
|
|
type: "string",
|
|
short: "i",
|
|
default: process.env.INTERVAL || "5000",
|
|
},
|
|
threshold: {
|
|
type: "string",
|
|
short: "t",
|
|
default: process.env.THRESHOLD || "0.01",
|
|
},
|
|
};
|
|
|
|
const { values } = parseArgs({
|
|
options,
|
|
strict: true,
|
|
allowPositionals: true,
|
|
args: process.argv.slice(2),
|
|
});
|
|
|
|
const pairs = values.pairs.split(",").map((p) => p.trim());
|
|
const interval = parseInt(values.interval, 10);
|
|
const threshold = parseFloat(values.threshold);
|
|
|
|
if (isNaN(interval) || interval < 100) {
|
|
logger.error("Interval must be a number larger than 100");
|
|
process.exit(1);
|
|
}
|
|
if (pairs.length === 0 || pairs.some((p) => !p.match(/^[A-Z]+-?[A-Z]+$/))) {
|
|
logger.error("Invalid pair format (expected BTC-USD or CNYUSD)");
|
|
process.exit(1);
|
|
}
|
|
if (threshold <= 0 || threshold > 100) {
|
|
logger.error("Threshold must be between 0 and 100");
|
|
process.exit(1);
|
|
}
|
|
|
|
async function main() {
|
|
logger.info("Bot starting...");
|
|
logger.info(
|
|
`[WATCHING] ${pairs.join(", ")} | Every ${interval}ms | Threshold ${threshold}%`,
|
|
);
|
|
|
|
await initDB();
|
|
startServer();
|
|
|
|
try {
|
|
await prefetchRates(pairs);
|
|
} catch (err) {
|
|
logger.error(err, "Critical failure during cache population");
|
|
process.exit(1);
|
|
}
|
|
|
|
const handleAlert = async (alertData) => {
|
|
await insertIntoDB(alertData);
|
|
};
|
|
|
|
const bots = pairs.map((pair) => {
|
|
return new Bot(
|
|
{
|
|
pair,
|
|
interval,
|
|
threshold,
|
|
},
|
|
handleAlert,
|
|
);
|
|
});
|
|
bots.forEach((b) => b.start());
|
|
|
|
const shutdown = async () => {
|
|
logger.info("Shutting down...");
|
|
bots.forEach((b) => b.stop());
|
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
await closePool();
|
|
process.exit(0);
|
|
};
|
|
|
|
process.on("SIGINT", shutdown);
|
|
process.on("SIGTERM", shutdown);
|
|
}
|
|
|
|
main().catch((err) => {
|
|
logger.fatal(err, "Fatal error in main loop");
|
|
process.exit(1);
|
|
});
|