polycopy 0.1.7 → 0.1.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/dist/index.js CHANGED
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
- const init = require("./init-Bs-N_AZi.js");
2
+ const processLock = require("./process-lock-BbFF27h8.js");
3
3
  const grammy = require("grammy");
4
+ const log4js = require("log4js");
5
+ const path = require("path");
4
6
  const wallet = require("@ethersproject/wallet");
5
7
  const clobClient = require("@polymarket/clob-client");
6
8
  const builderSigningSdk = require("@polymarket/builder-signing-sdk");
@@ -11,7 +13,6 @@ const builderRelayerClient = require("@polymarket/builder-relayer-client");
11
13
  const setPromiseInterval = require("set-promise-interval");
12
14
  const child_process = require("child_process");
13
15
  const fs = require("fs");
14
- const paths = require("./paths-CEjGES8j.js");
15
16
  const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
16
17
  function _interopNamespace(e) {
17
18
  if (e && e.__esModule) return e;
@@ -30,8 +31,114 @@ function _interopNamespace(e) {
30
31
  n.default = e;
31
32
  return Object.freeze(n);
32
33
  }
34
+ const log4js__default = /* @__PURE__ */ _interopDefault(log4js);
35
+ const path__namespace = /* @__PURE__ */ _interopNamespace(path);
33
36
  const setPromiseInterval__default = /* @__PURE__ */ _interopDefault(setPromiseInterval);
34
37
  const fs__namespace = /* @__PURE__ */ _interopNamespace(fs);
38
+ const LOG_FILE = path__namespace.join(processLock.LOGS_DIR, "polycopy.log");
39
+ path__namespace.join(processLock.LOGS_DIR, "polycopy");
40
+ log4js__default.default.configure({
41
+ appenders: {
42
+ file: {
43
+ type: "dateFile",
44
+ filename: LOG_FILE,
45
+ pattern: "yyyy-MM-dd",
46
+ alwaysIncludePattern: true,
47
+ compress: false,
48
+ keepFileExt: true,
49
+ maxLogSize: 2 * 1024 * 1024,
50
+ backups: 10,
51
+ layout: { type: "pattern", pattern: "[%d{yyyy-MM-dd hh:mm:ss}] %m" }
52
+ },
53
+ stdout: {
54
+ type: "stdout",
55
+ layout: { type: "pattern", pattern: "[%d{yyyy-MM-dd hh:mm:ss}] %m" }
56
+ }
57
+ },
58
+ categories: {
59
+ default: { appenders: ["stdout", "file"], level: "info" }
60
+ }
61
+ });
62
+ const log4jsLogger = log4js__default.default.getLogger("polycopy");
63
+ const formatMessage = (message, args) => args.length > 0 ? `${message} ${args.join(" ")}` : message;
64
+ class Logger {
65
+ initialized = false;
66
+ /**
67
+ * 初始化日志模块(输出日志路径信息)
68
+ */
69
+ init() {
70
+ if (this.initialized) return;
71
+ this.initialized = true;
72
+ console.log(`
73
+ 📁 日志目录: ${processLock.LOGS_DIR}`);
74
+ }
75
+ /**
76
+ * 输出信息日志(前后自动添加空行)
77
+ */
78
+ info(message, ...args) {
79
+ log4jsLogger.info(formatMessage(message, args));
80
+ }
81
+ /**
82
+ * 输出成功日志(前后自动添加空行)
83
+ */
84
+ success(message, ...args) {
85
+ log4jsLogger.info(`✅ ${formatMessage(message, args)}`);
86
+ }
87
+ /**
88
+ * 输出警告日志(前后自动添加空行)
89
+ */
90
+ warning(message, ...args) {
91
+ log4jsLogger.warn(`⚠️ ${formatMessage(message, args)}`);
92
+ }
93
+ /**
94
+ * 输出错误日志(前后自动添加空行)
95
+ */
96
+ error(message, ...args) {
97
+ log4jsLogger.error(`❌ ${formatMessage(message, args)}`);
98
+ }
99
+ /**
100
+ * 输出章节标题(前后自动添加空行)
101
+ */
102
+ section(title) {
103
+ log4jsLogger.info(`=== ${title} ===`);
104
+ }
105
+ /**
106
+ * 输出列表项(不添加空行,用于连续输出)
107
+ */
108
+ item(message, indent = 0) {
109
+ const indentStr = " ".repeat(indent);
110
+ const formatted = `${indentStr}${message}`;
111
+ log4jsLogger.info(formatted);
112
+ }
113
+ /**
114
+ * 输出普通文本(不添加空行,用于连续输出)
115
+ */
116
+ text(message, ...args) {
117
+ log4jsLogger.info(formatMessage(message, args));
118
+ }
119
+ /**
120
+ * 输出空行
121
+ */
122
+ blank() {
123
+ console.log("");
124
+ }
125
+ /**
126
+ * 输出带前缀的信息(不添加空行)
127
+ * 用于在同一组日志中输出多行信息
128
+ */
129
+ line(prefix, message, ...args) {
130
+ const formatted = args.length > 0 ? ` ${prefix} ${message} ${args.join(" ")}` : ` ${prefix} ${message}`;
131
+ log4jsLogger.info(formatted);
132
+ }
133
+ /**
134
+ * 刷新日志输出(用于进程退出前)
135
+ */
136
+ async flush(timeoutMs = 1e3) {
137
+ await log4js__default.default.shutdown();
138
+ await new Promise((resolve) => setTimeout(resolve, timeoutMs));
139
+ }
140
+ }
141
+ const logger = new Logger();
35
142
  function parseSignalMetadata(messageText) {
36
143
  const match = messageText.match(/🤖\s*({.*})/s);
37
144
  if (!match) {
@@ -41,7 +148,7 @@ function parseSignalMetadata(messageText) {
41
148
  const jsonStr = match[1];
42
149
  const metadata = JSON.parse(jsonStr);
43
150
  if (!metadata.eventId || !metadata.assetId || !metadata.outcome || !metadata.endTime) {
44
- init.logger.error("解析失败: 缺少必需字段", {
151
+ logger.error("解析失败: 缺少必需字段", {
45
152
  hasEventId: !!metadata.eventId,
46
153
  hasAssetId: !!metadata.assetId,
47
154
  hasOutcome: !!metadata.outcome,
@@ -50,14 +157,14 @@ function parseSignalMetadata(messageText) {
50
157
  return null;
51
158
  }
52
159
  if (metadata.outcome !== "Up" && metadata.outcome !== "Down") {
53
- init.logger.error("解析失败: outcome 值无效", {
160
+ logger.error("解析失败: outcome 值无效", {
54
161
  outcome: metadata.outcome,
55
162
  expected: "Up 或 Down"
56
163
  });
57
164
  return null;
58
165
  }
59
166
  if (typeof metadata.endTime !== "number" || metadata.endTime <= 0) {
60
- init.logger.error("解析失败: endTime 值无效", {
167
+ logger.error("解析失败: endTime 值无效", {
61
168
  endTime: metadata.endTime,
62
169
  expected: "毫秒级时间戳"
63
170
  });
@@ -65,9 +172,9 @@ function parseSignalMetadata(messageText) {
65
172
  }
66
173
  return metadata;
67
174
  } catch (error) {
68
- init.logger.error("解析信号元数据失败:", error);
175
+ logger.error("解析信号元数据失败:", error);
69
176
  if (error instanceof Error) {
70
- init.logger.line("", `错误详情: ${error.message}`);
177
+ logger.line("", `错误详情: ${error.message}`);
71
178
  }
72
179
  return null;
73
180
  }
@@ -97,12 +204,12 @@ class TelegramListener {
97
204
  await this.handleMyChatMemberUpdate(ctx);
98
205
  });
99
206
  this.bot.catch((err) => {
100
- init.logger.error("Telegram Bot 错误:", err);
207
+ logger.error("Telegram Bot 错误:", err);
101
208
  if (err.error) {
102
- init.logger.line("", `错误详情: ${err.error}`);
209
+ logger.line("", `错误详情: ${err.error}`);
103
210
  }
104
211
  if (err.ctx) {
105
- init.logger.line("", `上下文信息:`, {
212
+ logger.line("", `上下文信息:`, {
106
213
  chatId: err.ctx.chat?.id,
107
214
  messageId: err.ctx.message?.message_id
108
215
  });
@@ -169,28 +276,28 @@ class TelegramListener {
169
276
  reason = "需要管理员权限才能监听";
170
277
  }
171
278
  }
172
- init.logger.info(`📢 频道状态更新: ${chatTitle}`);
173
- init.logger.line("", action);
174
- init.logger.line(
279
+ logger.info(`📢 频道状态更新: ${chatTitle}`);
280
+ logger.line("", action);
281
+ logger.line(
175
282
  "",
176
283
  `当前状态: ${canListen ? "✅ 可监听预测消息" : "❌ 无法监听预测消息"}`
177
284
  );
178
285
  if (reason) {
179
- init.logger.line("", `原因: ${reason}`);
286
+ logger.line("", `原因: ${reason}`);
180
287
  }
181
288
  }
182
289
  if (isAdmin && !this.targetChatIds.has(chatId)) {
183
290
  this.targetChatIds.add(chatId);
184
291
  await this.saveChatIds();
185
- init.logger.line("📡", "已自动开始监听此频道");
292
+ logger.line("📡", "已自动开始监听此频道");
186
293
  }
187
294
  if ((isRemoved || !isAdmin) && this.targetChatIds.has(chatId)) {
188
295
  this.targetChatIds.delete(chatId);
189
296
  await this.saveChatIds();
190
297
  if (isRemoved) {
191
- init.logger.line("⏹️", "已停止监听此频道(Bot 已被移除)");
298
+ logger.line("⏹️", "已停止监听此频道(Bot 已被移除)");
192
299
  } else {
193
- init.logger.line("⏹️", "已停止监听此频道(Bot 不再是管理员)");
300
+ logger.line("⏹️", "已停止监听此频道(Bot 不再是管理员)");
194
301
  }
195
302
  }
196
303
  }
@@ -205,7 +312,7 @@ class TelegramListener {
205
312
  const chatIds = Array.from(this.targetChatIds);
206
313
  await this.onChatIdsChangedCallback(chatIds);
207
314
  } catch (error) {
208
- init.logger.error("保存频道 ID 列表时出错:", error);
315
+ logger.error("保存频道 ID 列表时出错:", error);
209
316
  }
210
317
  }
211
318
  /**
@@ -234,9 +341,9 @@ class TelegramListener {
234
341
  }
235
342
  if (isChannel && !this.targetChatIds.has(chatId)) {
236
343
  this.targetChatIds.add(chatId);
237
- init.logger.info(`➕ 自动添加频道到监听列表: ${chatTitle} (ID: ${chatId})`);
344
+ logger.info(`➕ 自动添加频道到监听列表: ${chatTitle} (ID: ${chatId})`);
238
345
  await this.saveChatIds();
239
- init.logger.line("💾", "已保存到配置文件");
346
+ logger.line("💾", "已保存到配置文件");
240
347
  }
241
348
  if (!isChannel) {
242
349
  return;
@@ -250,13 +357,13 @@ class TelegramListener {
250
357
  const emojiIndex = messageText.indexOf("🤖");
251
358
  const jsonStart = emojiIndex + 1;
252
359
  const jsonEnd = Math.min(jsonStart + 200, messageText.length);
253
- init.logger.warning(`解析预测消息失败:`);
254
- init.logger.line("", `频道 ID: ${chatId}`);
255
- init.logger.line(
360
+ logger.warning(`解析预测消息失败:`);
361
+ logger.line("", `频道 ID: ${chatId}`);
362
+ logger.line(
256
363
  "",
257
364
  `消息片段: ${messageText.substring(emojiIndex, jsonEnd)}${messageText.length > jsonEnd ? "..." : ""}`
258
365
  );
259
- init.logger.line("", "提示: 请检查消息格式是否正确");
366
+ logger.line("", "提示: 请检查消息格式是否正确");
260
367
  }
261
368
  return;
262
369
  }
@@ -271,7 +378,7 @@ class TelegramListener {
271
378
  try {
272
379
  await this.onSignalCallback(parsedSignal);
273
380
  } catch (error) {
274
- init.logger.error("处理信号时出错:", error);
381
+ logger.error("处理信号时出错:", error);
275
382
  }
276
383
  }
277
384
  }
@@ -299,15 +406,15 @@ class TelegramListener {
299
406
  */
300
407
  async start(onReady) {
301
408
  try {
302
- init.logger.info("正在启动 Bot...");
409
+ logger.info("正在启动 Bot...");
303
410
  const botInfo = await this.bot.api.getMe();
304
411
  const botId = botInfo.id;
305
- init.logger.info(`Bot 信息: @${botInfo.username} (ID: ${botId})`);
412
+ logger.info(`Bot 信息: @${botInfo.username} (ID: ${botId})`);
306
413
  await this.checkAndDisplayChannels(botId);
307
414
  if (onReady) {
308
415
  await onReady();
309
416
  }
310
- init.logger.info("开始监听预测消息...");
417
+ logger.info("开始监听预测消息...");
311
418
  await this.bot.start({
312
419
  drop_pending_updates: true,
313
420
  allowed_updates: [
@@ -317,7 +424,7 @@ class TelegramListener {
317
424
  ]
318
425
  });
319
426
  } catch (error) {
320
- init.logger.error("启动 Bot 失败:", error);
427
+ logger.error("启动 Bot 失败:", error);
321
428
  throw error;
322
429
  }
323
430
  }
@@ -328,14 +435,14 @@ class TelegramListener {
328
435
  async checkAndDisplayChannels(botId) {
329
436
  const chatIds = Array.from(this.targetChatIds);
330
437
  if (chatIds.length === 0) {
331
- init.logger.info(`📋 当前没有保存的频道配置`);
332
- init.logger.line(
438
+ logger.info(`📋 当前没有保存的频道配置`);
439
+ logger.line(
333
440
  "",
334
441
  "提示: 当 Bot 被添加为频道管理员时,会自动添加到监听列表"
335
442
  );
336
443
  return;
337
444
  }
338
- init.logger.info(`📋 检查 Bot 所在的频道:`);
445
+ logger.info(`📋 检查 Bot 所在的频道:`);
339
446
  const validChannels = [];
340
447
  const invalidChannels = [];
341
448
  for (const chatId of chatIds) {
@@ -367,43 +474,43 @@ class TelegramListener {
367
474
  const adminChannels = validChannels.filter((c) => c.isAdmin);
368
475
  const nonAdminChannels = validChannels.filter((c) => !c.isAdmin);
369
476
  if (adminChannels.length > 0) {
370
- init.logger.info(`📡 可监听频道 (${adminChannels.length} 个):`);
477
+ logger.info(`📡 可监听频道 (${adminChannels.length} 个):`);
371
478
  adminChannels.forEach((channel, index) => {
372
- init.logger.item(
479
+ logger.item(
373
480
  `${index + 1}. ${channel.title} (ID: ${channel.id}) 👑 管理员`,
374
481
  1
375
482
  );
376
483
  });
377
484
  }
378
485
  if (nonAdminChannels.length > 0) {
379
- init.logger.warning(`无法监听频道 (${nonAdminChannels.length} 个):`);
486
+ logger.warning(`无法监听频道 (${nonAdminChannels.length} 个):`);
380
487
  nonAdminChannels.forEach((channel, index) => {
381
- init.logger.item(`${index + 1}. ${channel.title}`, 1);
382
- init.logger.item(`ID: ${channel.id}`, 2);
383
- init.logger.item(`状态: 🚫 非管理员(无法接收预测消息)`, 2);
384
- init.logger.item(`原因: 需要管理员权限才能监听`, 2);
488
+ logger.item(`${index + 1}. ${channel.title}`, 1);
489
+ logger.item(`ID: ${channel.id}`, 2);
490
+ logger.item(`状态: 🚫 非管理员(无法接收预测消息)`, 2);
491
+ logger.item(`原因: 需要管理员权限才能监听`, 2);
385
492
  });
386
493
  nonAdminChannels.forEach((channel) => {
387
494
  this.targetChatIds.delete(channel.id);
388
495
  });
389
496
  if (nonAdminChannels.length > 0) {
390
497
  await this.saveChatIds();
391
- init.logger.line("💾", "已清理无法监听的频道并保存配置");
498
+ logger.line("💾", "已清理无法监听的频道并保存配置");
392
499
  }
393
500
  }
394
501
  }
395
502
  if (invalidChannels.length > 0) {
396
- init.logger.error(`无效频道 (${invalidChannels.length} 个):`);
503
+ logger.error(`无效频道 (${invalidChannels.length} 个):`);
397
504
  invalidChannels.forEach((channel, index) => {
398
- init.logger.item(`${index + 1}. ID: ${channel.id}`, 1);
399
- init.logger.item(`原因: ${channel.reason}`, 2);
505
+ logger.item(`${index + 1}. ID: ${channel.id}`, 1);
506
+ logger.item(`原因: ${channel.reason}`, 2);
400
507
  });
401
508
  invalidChannels.forEach((channel) => {
402
509
  this.targetChatIds.delete(channel.id);
403
510
  });
404
511
  if (invalidChannels.length > 0) {
405
512
  await this.saveChatIds();
406
- init.logger.line("💾", "已清理无效频道并保存配置");
513
+ logger.line("💾", "已清理无效频道并保存配置");
407
514
  }
408
515
  }
409
516
  }
@@ -439,13 +546,13 @@ class TelegramService {
439
546
  this.bot = new grammy.Bot(botToken);
440
547
  this.listener = new TelegramListener(this.bot, targetChatIds);
441
548
  if (!adminChatId) {
442
- init.logger.warning("未配置管理员 Chat ID,通知功能已禁用");
549
+ logger.warning("未配置管理员 Chat ID,通知功能已禁用");
443
550
  this.notifyEnabled = false;
444
551
  return;
445
552
  }
446
553
  this.adminChatId = adminChatId;
447
554
  this.notifyEnabled = true;
448
- init.logger.info("管理员通知已启用");
555
+ logger.info("管理员通知已启用");
449
556
  }
450
557
  /**
451
558
  * 设置频道 ID 列表变更回调
@@ -511,7 +618,7 @@ ${details}` : message;
511
618
  });
512
619
  } catch (error) {
513
620
  const errorMsg = error instanceof Error ? error.message : String(error);
514
- init.logger.error(`发送通知失败: ${errorMsg}`);
621
+ logger.error(`发送通知失败: ${errorMsg}`);
515
622
  }
516
623
  }
517
624
  }
@@ -528,11 +635,11 @@ class ClobClientWrapper {
528
635
  return;
529
636
  }
530
637
  const wallet$1 = new wallet.Wallet(config.privateKey);
531
- init.logger.info(`钱包地址: ${wallet$1.address}`);
638
+ logger.info(`钱包地址: ${wallet$1.address}`);
532
639
  const tempClient = new clobClient.ClobClient(CLOB_HOST, clobClient.Chain.POLYGON, wallet$1);
533
640
  this.patchClient(tempClient);
534
641
  const creds = await tempClient.deriveApiKey();
535
- init.logger.success("API 凭证已获取");
642
+ logger.success("API 凭证已获取");
536
643
  let builderConfig;
537
644
  if (config.builderCreds) {
538
645
  builderConfig = new builderSigningSdk.BuilderConfig({
@@ -556,7 +663,7 @@ class ClobClientWrapper {
556
663
  );
557
664
  this.patchClient(this.client);
558
665
  this.initialized = true;
559
- init.logger.success("ClobClient 初始化完成");
666
+ logger.success("ClobClient 初始化完成");
560
667
  }
561
668
  /**
562
669
  * 魔法操作:clob-client 请求出错不抛异常,只返回 { error: ... }
@@ -632,13 +739,13 @@ class ClobClientWrapper {
632
739
  clobClient.OrderType.GTC
633
740
  );
634
741
  if (response.success) {
635
- init.logger.success(`买单已提交: ${response.orderID}`);
742
+ logger.success(`买单已提交: ${response.orderID}`);
636
743
  return {
637
744
  success: true,
638
745
  orderId: response.orderID
639
746
  };
640
747
  } else {
641
- init.logger.error(`买单提交失败: ${response.errorMsg}`);
748
+ logger.error(`买单提交失败: ${response.errorMsg}`);
642
749
  return {
643
750
  success: false,
644
751
  orderId: "",
@@ -647,7 +754,7 @@ class ClobClientWrapper {
647
754
  }
648
755
  } catch (error) {
649
756
  const errorMsg = error instanceof Error ? error.message : String(error);
650
- init.logger.error(`买单提交失败: ${errorMsg}`);
757
+ logger.error(`买单提交失败: ${errorMsg}`);
651
758
  return {
652
759
  success: false,
653
760
  orderId: "",
@@ -709,11 +816,11 @@ class ClobClientWrapper {
709
816
  const client = this.ensureInitialized();
710
817
  try {
711
818
  await client.cancelOrder({ orderID: orderId });
712
- init.logger.info(`订单已取消: ${orderId}`);
819
+ logger.info(`订单已取消: ${orderId}`);
713
820
  return true;
714
821
  } catch (error) {
715
822
  const errorMsg = error instanceof Error ? error.message : String(error);
716
- init.logger.error(`取消订单失败: ${errorMsg}`);
823
+ logger.error(`取消订单失败: ${errorMsg}`);
717
824
  return false;
718
825
  }
719
826
  }
@@ -759,7 +866,7 @@ class RelayClientWrapper {
759
866
  builderRelayerClient.RelayerTxType.SAFE
760
867
  );
761
868
  this.initialized = true;
762
- init.logger.success("RelayClient 初始化完成");
869
+ logger.success("RelayClient 初始化完成");
763
870
  }
764
871
  /**
765
872
  * 检查是否已初始化
@@ -838,7 +945,7 @@ class AssetFilter {
838
945
  this.traded.set(endTime, /* @__PURE__ */ new Set());
839
946
  }
840
947
  this.traded.get(endTime).add(assetId);
841
- init.logger.info(`已标记 assetId: ${assetId.slice(0, 10)}...`);
948
+ logger.info(`已标记 assetId: ${assetId.slice(0, 10)}...`);
842
949
  }
843
950
  /**
844
951
  * 清理过期记录
@@ -852,7 +959,7 @@ class AssetFilter {
852
959
  }
853
960
  }
854
961
  if (cleanedCount > 0) {
855
- init.logger.info(`已清理 ${cleanedCount} 个过期周期的交易记录`);
962
+ logger.info(`已清理 ${cleanedCount} 个过期周期的交易记录`);
856
963
  }
857
964
  }
858
965
  }
@@ -872,12 +979,12 @@ class BalanceCache {
872
979
  }
873
980
  if (this.cache.has(endTime)) {
874
981
  const cached = this.cache.get(endTime);
875
- init.logger.info(`使用缓存余额: ${cached} USDC`);
982
+ logger.info(`使用缓存余额: ${cached} USDC`);
876
983
  return cached;
877
984
  }
878
985
  const balance = await fetchFn();
879
986
  this.cache.set(endTime, balance);
880
- init.logger.info(`获取当前余额: ${balance} USDC`);
987
+ logger.info(`获取当前余额: ${balance} USDC`);
881
988
  return balance;
882
989
  }
883
990
  /**
@@ -886,7 +993,7 @@ class BalanceCache {
886
993
  invalidate() {
887
994
  if (this.cache.size > 0) {
888
995
  this.cache.clear();
889
- init.logger.info("余额缓存已清除");
996
+ logger.info("余额缓存已清除");
890
997
  }
891
998
  }
892
999
  /**
@@ -901,7 +1008,7 @@ class BalanceCache {
901
1008
  }
902
1009
  }
903
1010
  if (cleanedCount > 0) {
904
- init.logger.info(`已清理 ${cleanedCount} 个过期周期的余额缓存`);
1011
+ logger.info(`已清理 ${cleanedCount} 个过期周期的余额缓存`);
905
1012
  }
906
1013
  }
907
1014
  }
@@ -918,13 +1025,13 @@ class OrderWatcher {
918
1025
  async watch(orderId, endTime, getOrderFn) {
919
1026
  const timeoutAt = endTime + TIMEOUT_BUFFER;
920
1027
  const shortId = orderId.substring(0, 10) + "...";
921
- init.logger.info(
1028
+ logger.info(
922
1029
  `开始监控订单 ${shortId},超时: ${new Date(timeoutAt).toLocaleString()}`
923
1030
  );
924
1031
  while (true) {
925
1032
  const now = Date.now();
926
1033
  if (now >= timeoutAt) {
927
- init.logger.warning(`订单超时: ${shortId}`);
1034
+ logger.warning(`订单超时: ${shortId}`);
928
1035
  return { status: "TIMEOUT" };
929
1036
  }
930
1037
  try {
@@ -932,7 +1039,7 @@ class OrderWatcher {
932
1039
  const status = order.status.toUpperCase();
933
1040
  if (TERMINAL_STATUSES.includes(status)) {
934
1041
  const filledSize = parseFloat(order.size_matched || "0");
935
- init.logger.info(`订单 ${shortId} 终态: ${status},成交: ${filledSize}`);
1042
+ logger.info(`订单 ${shortId} 终态: ${status},成交: ${filledSize}`);
936
1043
  return {
937
1044
  status,
938
1045
  filledSize,
@@ -941,7 +1048,7 @@ class OrderWatcher {
941
1048
  }
942
1049
  } catch (error) {
943
1050
  const errorMsg = error instanceof Error ? error.message : String(error);
944
- init.logger.warning(`查询订单失败: ${errorMsg},继续重试...`);
1051
+ logger.warning(`查询订单失败: ${errorMsg},继续重试...`);
945
1052
  }
946
1053
  await this.delay(POLL_INTERVAL);
947
1054
  }
@@ -986,7 +1093,7 @@ class Redeemer {
986
1093
  this.funderAddress = funderAddress;
987
1094
  this.onRedeemSuccess = onRedeemSuccess || null;
988
1095
  this.state = "running";
989
- init.logger.success("自动 redeem 已启动");
1096
+ logger.success("自动 redeem 已启动");
990
1097
  this.startInterval();
991
1098
  }
992
1099
  /**
@@ -1034,7 +1141,7 @@ class Redeemer {
1034
1141
  this.delayTimeoutId = void 0;
1035
1142
  }
1036
1143
  this.state = "stopped";
1037
- init.logger.info("自动 redeem 已停止");
1144
+ logger.info("自动 redeem 已停止");
1038
1145
  }
1039
1146
  /**
1040
1147
  * 获取可 Redeem 的仓位
@@ -1085,7 +1192,7 @@ class Redeemer {
1085
1192
  const errorMsg = error instanceof Error ? error.message : String(error);
1086
1193
  if (errorMsg.includes("429") || errorMsg.includes("Too Many Requests")) {
1087
1194
  const resetSeconds = this.parseResetSeconds(errorMsg);
1088
- init.logger.warning(`获取仓位限流,${resetSeconds} 秒后重试`);
1195
+ logger.warning(`获取仓位限流,${resetSeconds} 秒后重试`);
1089
1196
  telegramService.warning(
1090
1197
  "API 限流",
1091
1198
  `获取仓位限流,${resetSeconds} 秒后重试`
@@ -1093,7 +1200,7 @@ class Redeemer {
1093
1200
  this.pauseAndRestart((resetSeconds + 60) * 1e3);
1094
1201
  return;
1095
1202
  }
1096
- init.logger.error(`获取仓位失败: ${errorMsg}`);
1203
+ logger.error(`获取仓位失败: ${errorMsg}`);
1097
1204
  return;
1098
1205
  }
1099
1206
  const pendingRedeemablePositions = positions.filter((position) => {
@@ -1121,7 +1228,7 @@ class Redeemer {
1121
1228
  if (record.success) {
1122
1229
  record.matchedCount++;
1123
1230
  if (record.matchedCount >= MAX_MATCH_TIMES) {
1124
- init.logger.error(
1231
+ logger.error(
1125
1232
  `Redeem 失败: 执行成功但仍存在 ${record.matchedCount} 次 (conditionId: ${position.conditionId.slice(0, 10)}...)`
1126
1233
  );
1127
1234
  telegramService.error(
@@ -1135,18 +1242,18 @@ conditionId: ${position.conditionId}
1135
1242
  try {
1136
1243
  const txHash = await this.redeemFn?.(position.conditionId);
1137
1244
  record.success = true;
1138
- init.logger.info(`Redeem 已执行: ${txHash}`);
1245
+ logger.info(`Redeem 已执行: ${txHash}`);
1139
1246
  } catch (error) {
1140
1247
  const errorMsg = error instanceof Error ? error.message : String(error);
1141
1248
  if (errorMsg.includes("429") || errorMsg.includes("Too Many Requests")) {
1142
1249
  const resetSeconds = this.parseResetSeconds(errorMsg);
1143
- init.logger.warning(`Redeem 限流,${resetSeconds} 秒后重试`);
1250
+ logger.warning(`Redeem 限流,${resetSeconds} 秒后重试`);
1144
1251
  telegramService.warning("Redeem 限流", `${resetSeconds} 秒后重试`);
1145
1252
  this.pauseAndRestart((resetSeconds + 60) * 1e3);
1146
1253
  return;
1147
1254
  }
1148
1255
  record.failedCount++;
1149
- init.logger.error(
1256
+ logger.error(
1150
1257
  `Redeem 异常 (第 ${record.failedCount} 次): ${errorMsg}`
1151
1258
  );
1152
1259
  if (record.failedCount >= MAX_MATCH_TIMES) {
@@ -1177,7 +1284,7 @@ conditionId: ${position.conditionId}
1177
1284
  telegramService.success(successTitle, successLines.join("\n"));
1178
1285
  this.onRedeemSuccess?.();
1179
1286
  }
1180
- init.logger.info(`redeem 状态: ${this.records.size} 个待确认`);
1287
+ logger.info(`redeem 状态: ${this.records.size} 个待确认`);
1181
1288
  }
1182
1289
  /**
1183
1290
  * 统一格式化仓位信息
@@ -1192,15 +1299,12 @@ conditionId: ${position.conditionId}
1192
1299
  * 统一输出 Redeem 仓位列表
1193
1300
  */
1194
1301
  logPositions(title, items) {
1195
- init.logger.info(title);
1302
+ logger.info(title);
1196
1303
  const lines = [];
1197
1304
  for (const item of items) {
1198
- const line = this.formatPositionLine(
1199
- item.position,
1200
- item.matchedCount
1201
- );
1305
+ const line = this.formatPositionLine(item.position, item.matchedCount);
1202
1306
  lines.push(line);
1203
- init.logger.line("", `- ${line}`);
1307
+ logger.line("", `- ${line}`);
1204
1308
  }
1205
1309
  return lines;
1206
1310
  }
@@ -1240,7 +1344,7 @@ class Trader {
1240
1344
  }
1241
1345
  this.config = config;
1242
1346
  if (!config.polymarket?.privateKey) {
1243
- init.logger.warning("未配置 PolyMarket 私钥,交易功能已禁用");
1347
+ logger.warning("未配置 PolyMarket 私钥,交易功能已禁用");
1244
1348
  return;
1245
1349
  }
1246
1350
  await this.client.init({
@@ -1254,10 +1358,10 @@ class Trader {
1254
1358
  builderCreds: config.polymarket.builderCreds
1255
1359
  });
1256
1360
  } else {
1257
- init.logger.warning("未配置 builderCreds,自动 Redeem 功能已禁用");
1361
+ logger.warning("未配置 builderCreds,自动 Redeem 功能已禁用");
1258
1362
  }
1259
1363
  this.initialized = true;
1260
- init.logger.success("交易模块初始化完成");
1364
+ logger.success("交易模块初始化完成");
1261
1365
  }
1262
1366
  /**
1263
1367
  * 启动 Redeemer(在 Telegram listener 就绪后调用)
@@ -1283,19 +1387,19 @@ class Trader {
1283
1387
  */
1284
1388
  async executeSignal(signal) {
1285
1389
  if (!this.initialized || !this.config) {
1286
- init.logger.warning("交易模块未初始化,跳过信号");
1390
+ logger.warning("交易模块未初始化,跳过信号");
1287
1391
  return;
1288
1392
  }
1289
1393
  const { metadata } = signal;
1290
1394
  const { assetId, endTime, outcome } = metadata;
1291
- init.logger.section(`处理信号: ${outcome} (${metadata.eventId})`);
1395
+ logger.section(`处理信号: ${outcome} (${metadata.eventId})`);
1292
1396
  if (!this.assetFilter.canTrade(assetId, endTime)) {
1293
- init.logger.warning(`assetId 已交易,跳过: ${assetId.slice(0, 10)}...`);
1397
+ logger.warning(`assetId 已交易,跳过: ${assetId.slice(0, 10)}...`);
1294
1398
  return;
1295
1399
  }
1296
1400
  const tradingConfig = this.config.trading;
1297
1401
  const amountMode = tradingConfig?.amountMode || "fixed";
1298
- const amountValue = tradingConfig?.amountValue || 1;
1402
+ const amountValue = amountMode === "percentage" ? tradingConfig?.amountPercentageValue ?? 0.1 : tradingConfig?.amountFixedValue ?? 5;
1299
1403
  const buyPrice = tradingConfig?.buyPrice || 0.98;
1300
1404
  const sellPrice = tradingConfig?.sellPrice || 1;
1301
1405
  let balance;
@@ -1306,12 +1410,12 @@ class Trader {
1306
1410
  );
1307
1411
  } catch (error) {
1308
1412
  const errorMsg = error instanceof Error ? error.message : String(error);
1309
- init.logger.error(`获取余额失败: ${errorMsg}`);
1413
+ logger.error(`获取余额失败: ${errorMsg}`);
1310
1414
  return;
1311
1415
  }
1312
1416
  if (amountMode === "fixed" && balance < amountValue) {
1313
1417
  const msg = `余额不足: ${balance} USDC < ${amountValue} USDC`;
1314
- init.logger.warning(msg);
1418
+ logger.warning(msg);
1315
1419
  telegramService.warning(
1316
1420
  "余额不足",
1317
1421
  `当前余额: ${balance} USDC
@@ -1320,18 +1424,18 @@ class Trader {
1320
1424
  return;
1321
1425
  }
1322
1426
  if (amountMode === "percentage" && balance <= 0) {
1323
- init.logger.warning(`余额为零: ${balance} USDC`);
1427
+ logger.warning(`余额为零: ${balance} USDC`);
1324
1428
  telegramService.warning("余额为零", `当前余额: ${balance} USDC`);
1325
1429
  return;
1326
1430
  }
1327
1431
  const orderAmount = amountMode === "fixed" ? amountValue : balance * amountValue;
1328
1432
  const size = orderAmount / buyPrice;
1329
- init.logger.info(`下单金额: ${orderAmount.toFixed(2)} USDC`);
1330
- init.logger.info(`下单价格: ${buyPrice}`);
1331
- init.logger.info(`下单数量: ${size.toFixed(4)}`);
1433
+ logger.info(`下单金额: ${orderAmount.toFixed(2)} USDC`);
1434
+ logger.info(`下单价格: ${buyPrice}`);
1435
+ logger.info(`下单数量: ${size.toFixed(4)}`);
1332
1436
  const buyResult = await this.client.createBuyOrder(assetId, buyPrice, size);
1333
1437
  if (!buyResult.success) {
1334
- init.logger.error(`买单失败: ${buyResult.errorMsg}`);
1438
+ logger.error(`买单失败: ${buyResult.errorMsg}`);
1335
1439
  telegramService.error(
1336
1440
  "买单失败",
1337
1441
  `事件: ${metadata.eventId}
@@ -1341,7 +1445,7 @@ class Trader {
1341
1445
  }
1342
1446
  this.assetFilter.markTraded(assetId, endTime);
1343
1447
  if (sellPrice === 1) {
1344
- init.logger.info("sellPrice = 1,成交后将自动 redeem");
1448
+ logger.info("sellPrice = 1,成交后将自动 redeem");
1345
1449
  return;
1346
1450
  }
1347
1451
  this.watchAndHandle(
@@ -1371,7 +1475,7 @@ class Trader {
1371
1475
  );
1372
1476
  } catch (error) {
1373
1477
  const errorMsg = error instanceof Error ? error.message : String(error);
1374
- init.logger.error(`订单监控异常: ${errorMsg}`);
1478
+ logger.error(`订单监控异常: ${errorMsg}`);
1375
1479
  telegramService.error(
1376
1480
  "订单监控异常",
1377
1481
  `事件: ${eventId}
@@ -1388,22 +1492,22 @@ class Trader {
1388
1492
  case "MATCHED":
1389
1493
  case "CONFIRMED":
1390
1494
  const filledSize = result.filledSize || 0;
1391
- init.logger.success(`买单成交: ${filledSize.toFixed(4)} 份`);
1495
+ logger.success(`买单成交: ${filledSize.toFixed(4)} 份`);
1392
1496
  if (sellPrice < 1) {
1393
1497
  this.waitAndSell(assetId, filledSize, sellPrice, endTime);
1394
1498
  } else {
1395
- init.logger.info("sellPrice = 1,等待 Redeem");
1499
+ logger.info("sellPrice = 1,等待 Redeem");
1396
1500
  }
1397
1501
  break;
1398
1502
  case "CANCELLED":
1399
- init.logger.info("订单已取消(市场可能已结束)");
1503
+ logger.info("订单已取消(市场可能已结束)");
1400
1504
  break;
1401
1505
  case "FAILED":
1402
- init.logger.error("订单执行失败");
1506
+ logger.error("订单执行失败");
1403
1507
  telegramService.error("订单执行失败", `订单 ID: ${orderId}`);
1404
1508
  break;
1405
1509
  case "TIMEOUT":
1406
- init.logger.warning("订单超时,尝试取消");
1510
+ logger.warning("订单超时,尝试取消");
1407
1511
  await this.client.cancelOrder(orderId);
1408
1512
  break;
1409
1513
  }
@@ -1414,18 +1518,18 @@ class Trader {
1414
1518
  async waitAndSell(assetId, expectedSize, sellPrice, endTime) {
1415
1519
  const shortId = assetId.substring(0, 10) + "...";
1416
1520
  const threshold = expectedSize * POSITION_MATCH_THRESHOLD;
1417
- init.logger.info(`等待仓位同步: ${shortId},预期 >= ${threshold.toFixed(4)}`);
1521
+ logger.info(`等待仓位同步: ${shortId},预期 >= ${threshold.toFixed(4)}`);
1418
1522
  while (true) {
1419
1523
  const now = Date.now();
1420
1524
  if (now >= endTime) {
1421
- init.logger.warning(`仓位同步超时: ${shortId},等待 Redeem`);
1525
+ logger.warning(`仓位同步超时: ${shortId},等待 Redeem`);
1422
1526
  this.notifyRedeemFallback(assetId, expectedSize);
1423
1527
  return;
1424
1528
  }
1425
1529
  try {
1426
1530
  const positionSize = await this.client.getPositionSize(assetId);
1427
1531
  if (positionSize >= threshold) {
1428
- init.logger.info(
1532
+ logger.info(
1429
1533
  `仓位已同步: ${shortId},数量: ${positionSize.toFixed(4)}`
1430
1534
  );
1431
1535
  await this.executeSellOrder(assetId, sellPrice, positionSize);
@@ -1433,7 +1537,7 @@ class Trader {
1433
1537
  }
1434
1538
  } catch (error) {
1435
1539
  const errorMsg = error instanceof Error ? error.message : String(error);
1436
- init.logger.warning(`获取仓位失败: ${shortId},${errorMsg}`);
1540
+ logger.warning(`获取仓位失败: ${shortId},${errorMsg}`);
1437
1541
  }
1438
1542
  await this.delay(POSITION_POLL_INTERVAL);
1439
1543
  }
@@ -1444,7 +1548,7 @@ class Trader {
1444
1548
  notifyRedeemFallback(assetId, expectedSize) {
1445
1549
  if (!this.relayClient.isInitialized()) {
1446
1550
  const msg = "仓位同步超时且 Redeem 不可用";
1447
- init.logger.warning(msg);
1551
+ logger.warning(msg);
1448
1552
  telegramService.warning(
1449
1553
  msg,
1450
1554
  `Asset: ${assetId.substring(0, 20)}...
@@ -1463,12 +1567,12 @@ class Trader {
1463
1567
  * 执行卖单
1464
1568
  */
1465
1569
  async executeSellOrder(assetId, price, size) {
1466
- init.logger.info(`创建卖单: ${size.toFixed(4)} 份 @ ${price}`);
1570
+ logger.info(`创建卖单: ${size.toFixed(4)} 份 @ ${price}`);
1467
1571
  const sellResult = await this.client.createSellOrder(assetId, price, size);
1468
1572
  if (sellResult.success) {
1469
- init.logger.success(`卖单已提交: ${sellResult.orderId}`);
1573
+ logger.success(`卖单已提交: ${sellResult.orderId}`);
1470
1574
  } else {
1471
- init.logger.error(`卖单提交失败: ${sellResult.errorMsg}`);
1575
+ logger.error(`卖单提交失败: ${sellResult.errorMsg}`);
1472
1576
  telegramService.error("卖单提交失败", `错误: ${sellResult.errorMsg}`);
1473
1577
  }
1474
1578
  }
@@ -1486,13 +1590,13 @@ class Trader {
1486
1590
  */
1487
1591
  async shutdown() {
1488
1592
  this.redeemer.stop();
1489
- init.logger.info("交易模块已停止");
1593
+ logger.info("交易模块已停止");
1490
1594
  }
1491
1595
  }
1492
1596
  const trader = new Trader();
1493
1597
  function enterDaemonMode() {
1494
- if (!fs__namespace.existsSync(paths.PID_DIR)) {
1495
- fs__namespace.mkdirSync(paths.PID_DIR, { recursive: true });
1598
+ if (!fs__namespace.existsSync(processLock.PID_DIR)) {
1599
+ fs__namespace.mkdirSync(processLock.PID_DIR, { recursive: true });
1496
1600
  }
1497
1601
  const child = child_process.spawn(process.execPath, [__filename], {
1498
1602
  detached: true,
@@ -1504,46 +1608,77 @@ function enterDaemonMode() {
1504
1608
  }
1505
1609
  });
1506
1610
  if (child.pid) {
1507
- fs__namespace.writeFileSync(paths.PID_FILE, child.pid.toString());
1508
- init.logger.blank();
1509
- init.logger.success(`已进入后台模式 (PID: ${child.pid})`);
1510
- init.logger.line("", `日志目录: ${paths.LOGS_DIR}`);
1511
- init.logger.line("", `使用 'polycopy stop' 停止`);
1512
- init.logger.line("", `使用 'polycopy log' 查看日志`);
1611
+ fs__namespace.writeFileSync(processLock.PID_FILE, child.pid.toString());
1612
+ logger.blank();
1613
+ logger.success(`已进入后台模式 (PID: ${child.pid})`);
1614
+ logger.line("", `日志目录: ${processLock.LOGS_DIR}`);
1615
+ logger.line("", `使用 'polycopy stop' 停止`);
1616
+ logger.line("", `使用 'polycopy log' 查看日志`);
1513
1617
  child.unref();
1514
1618
  process.exit(0);
1515
1619
  } else {
1516
- init.logger.error("进入后台模式失败");
1620
+ logger.error("进入后台模式失败");
1517
1621
  process.exit(1);
1518
1622
  }
1519
1623
  }
1520
1624
  async function main() {
1521
- init.logger.init();
1522
- init.logger.section("PolyMarket 跟单机器人");
1523
- const config = await init.ensureConfig();
1625
+ logger.init();
1626
+ if (process.env.POLYCOPY_DAEMON_CHILD !== "1") {
1627
+ const runningPid = processLock.resolveRunningPid();
1628
+ if (runningPid) {
1629
+ console.log(`⚠️ 程序正在运行中 (PID: ${runningPid})`);
1630
+ console.log(` 请先停止程序后再启动`);
1631
+ process.exit(1);
1632
+ }
1633
+ processLock.savePid(process.pid);
1634
+ }
1635
+ const exitOnFatal = async () => {
1636
+ await logger.flush();
1637
+ if (process.env.POLYCOPY_DAEMON_CHILD !== "1") {
1638
+ processLock.removePid();
1639
+ }
1640
+ process.exit(1);
1641
+ };
1642
+ process.on("uncaughtException", async (error) => {
1643
+ const message = error instanceof Error ? error.stack || error.message : String(error);
1644
+ logger.error(`捕获未处理异常:
1645
+ ${message}`);
1646
+ await exitOnFatal();
1647
+ });
1648
+ process.on("unhandledRejection", async (reason) => {
1649
+ const message = reason instanceof Error ? reason.stack || reason.message : String(reason);
1650
+ logger.error(`捕获未处理 Promise 拒绝:
1651
+ ${message}`);
1652
+ await exitOnFatal();
1653
+ });
1654
+ process.on("beforeExit", async () => {
1655
+ await logger.flush();
1656
+ });
1657
+ logger.section("PolyMarket 跟单机器人");
1658
+ const config = await processLock.ensureConfig();
1524
1659
  telegramService.init(
1525
1660
  config.telegram.botToken,
1526
1661
  config.telegram.adminChatId,
1527
1662
  config.telegram.targetChatIds || []
1528
1663
  );
1529
- const autoTradeEnabled = init.canAutoTrade(config);
1664
+ const autoTradeEnabled = processLock.canAutoTrade(config);
1530
1665
  if (!autoTradeEnabled) {
1531
- init.logger.warning("自动跟单功能未启用");
1666
+ logger.warning("自动跟单功能未启用");
1532
1667
  if (!config.polymarket?.privateKey) {
1533
- init.logger.line("", "- PolyMarket 配置不完整(缺少私钥)");
1668
+ logger.line("", "- PolyMarket 配置不完整(缺少私钥)");
1534
1669
  }
1535
- if (!config.trading?.amountMode || !config.trading?.amountValue || !config.trading?.buyPrice || !config.trading?.sellPrice) {
1536
- init.logger.line("", "- 交易配置不完整");
1670
+ if (!config.trading?.amountMode || !config.trading?.buyPrice || !config.trading?.sellPrice || (config.trading.amountMode === "fixed" ? !config.trading.amountFixedValue : !config.trading.amountPercentageValue)) {
1671
+ logger.line("", "- 交易配置不完整");
1537
1672
  }
1538
- init.logger.line("", "当前不会跟单任何预测消息");
1673
+ logger.line("", "当前不会跟单任何预测消息");
1539
1674
  } else {
1540
1675
  try {
1541
1676
  await trader.init(config);
1542
1677
  if (config.trading?.sellPrice === 1 && !config.polymarket?.builderCreds) {
1543
- init.logger.error(
1678
+ logger.error(
1544
1679
  "sellPrice = 1 但未配置 builderCreds,Redeem 功能无法使用,程序已退出"
1545
1680
  );
1546
- init.logger.line(
1681
+ logger.line(
1547
1682
  "",
1548
1683
  "请配置 Builder API 凭证,或将卖价设置为 < 1 使用限价卖出模式"
1549
1684
  );
@@ -1551,29 +1686,29 @@ async function main() {
1551
1686
  }
1552
1687
  } catch (error) {
1553
1688
  const errorMsg = error instanceof Error ? error.message : String(error);
1554
- init.logger.error(`交易模块初始化失败: ${errorMsg}`);
1555
- init.logger.line("", "当前不会跟单任何预测消息");
1689
+ logger.error(`交易模块初始化失败: ${errorMsg}`);
1690
+ logger.line("", "当前不会跟单任何预测消息");
1556
1691
  }
1557
1692
  }
1558
1693
  telegramService.onChatIdsChanged((chatIds) => {
1559
- const telegram = init.configLocal.getItem("telegram");
1694
+ const telegram = processLock.configLocal.getItem("telegram");
1560
1695
  if (!telegram) {
1561
- init.logger.error("警告: 无法保存频道 ID,telegram 配置不存在");
1696
+ logger.error("警告: 无法保存频道 ID,telegram 配置不存在");
1562
1697
  return;
1563
1698
  }
1564
1699
  telegram.targetChatIds = chatIds.length > 0 ? chatIds : void 0;
1565
- init.configLocal.setItem("telegram", telegram);
1566
- init.logger.line("💾", `已保存频道 ID 列表: ${chatIds.length} 个频道`);
1700
+ processLock.configLocal.setItem("telegram", telegram);
1701
+ logger.line("💾", `已保存频道 ID 列表: ${chatIds.length} 个频道`);
1567
1702
  });
1568
1703
  telegramService.onSignal(async (signal) => {
1569
- init.logger.info(`📨 收到预测信号:`);
1570
- init.logger.line("", `事件: ${signal.metadata.eventId}`);
1571
- init.logger.line(
1704
+ logger.info(`📨 收到预测信号:`);
1705
+ logger.line("", `事件: ${signal.metadata.eventId}`);
1706
+ logger.line(
1572
1707
  "",
1573
1708
  `方向: ${signal.metadata.outcome === "Up" ? "上涨 📈" : "下跌 📉"}`
1574
1709
  );
1575
- init.logger.line("", `Asset: ${signal.metadata.assetId}`);
1576
- init.logger.line(
1710
+ logger.line("", `Asset: ${signal.metadata.assetId}`);
1711
+ logger.line(
1577
1712
  "",
1578
1713
  `结束时间: ${new Date(signal.metadata.endTime).toLocaleString()}`
1579
1714
  );
@@ -1582,7 +1717,7 @@ async function main() {
1582
1717
  await trader.executeSignal(signal);
1583
1718
  } catch (error) {
1584
1719
  const errorMsg = error instanceof Error ? error.message : String(error);
1585
- init.logger.error(`交易执行失败: ${errorMsg}`);
1720
+ logger.error(`交易执行失败: ${errorMsg}`);
1586
1721
  }
1587
1722
  }
1588
1723
  });
@@ -1596,10 +1731,13 @@ async function main() {
1596
1731
  isShuttingDown = true;
1597
1732
  process.removeAllListeners("SIGINT");
1598
1733
  process.removeAllListeners("SIGTERM");
1599
- init.logger.blank();
1600
- init.logger.info("正在退出...");
1734
+ logger.blank();
1735
+ logger.info("正在退出...");
1601
1736
  await Promise.all([trader.shutdown(), telegramService.stop()]);
1602
- await init.logger.flush();
1737
+ await logger.flush();
1738
+ if (process.env.POLYCOPY_DAEMON_CHILD !== "1") {
1739
+ processLock.removePid();
1740
+ }
1603
1741
  process.exit(0);
1604
1742
  };
1605
1743
  process.prependListener("SIGINT", () => {
@@ -1614,7 +1752,13 @@ async function main() {
1614
1752
  }
1615
1753
  });
1616
1754
  }
1617
- main().catch((error) => {
1618
- init.logger.error("程序运行出错:", error);
1755
+ main().catch(async (error) => {
1756
+ const message = error instanceof Error ? error.stack || error.message : String(error);
1757
+ logger.error(`程序运行出错:
1758
+ ${message}`);
1759
+ await logger.flush();
1760
+ if (process.env.POLYCOPY_DAEMON_CHILD !== "1") {
1761
+ processLock.removePid();
1762
+ }
1619
1763
  process.exit(1);
1620
1764
  });