/** * TradeSync —— 交易记录本地同步(R-009 / 迭代 07,2026-09-01) * * 职责:把 QMT Bridge 当日交易数据(委托 + 成交)定时同步落 SQLite(trade_orders + trade_fills)。 * - 启动预热:插件启动即同步一次(今日数据入库); * - 定时同步:60s 轮询拉当日 orders+trades → UPSERT(幂等,状态覆盖更新); * - 失败容忍:单次同步失败只记日志,不中断定时循环。 * * 数据来源:QmtBridgeRestDataSource.getOrders() / getTrades()(仅当日,QMT 无历史接口)。 * 存储:DataStore.upsertTradeOrders / upsertTradeFills(委托 SqliteStore,UPSERT 幂等)。 */ const SYNC_INTERVAL_MS = 60 * 1000; // 60s export class TradeSync { /** * @param {object} opts * @param {object} opts.runtime { dataSource, storage } —— dataSource 拉当日数据,storage 落库 * @param {object} [opts.logger] */ constructor({ runtime, logger } = {}) { this.runtime = runtime; this.logger = logger; this.timer = null; this.mounted = false; this.syncing = false; } /** 启动:立即同步一次(预热)+ 定时同步 */ start() { if (this.mounted) return; this.mounted = true; this.syncNow().catch((e) => { this.logger?.warn?.('[one-divine-lot] TradeSync 启动预热失败: ' + (e?.message ?? e)); }); this.timer = setInterval(() => { this.syncNow().catch((e) => { this.logger?.debug?.('[one-divine-lot] TradeSync 定时同步失败: ' + (e?.message ?? e)); }); }, SYNC_INTERVAL_MS); this.logger?.info?.('[one-divine-lot] TradeSync 启动(60s 定时同步交易记录)'); } /** 停止(插件释放时) */ stop() { this.mounted = false; if (this.timer) { clearInterval(this.timer); this.timer = null; } } /** * 同步一次:拉当日 orders+trades → UPSERT 落库(幂等) * @returns {Promise<{orders: number, fills: number} | null>} 写入条数;数据源不可用返回 null */ async syncNow() { if (!this.mounted || this.syncing) return null; const { dataSource, storage } = this.runtime; if (!dataSource || !storage) return null; this.syncing = true; try { const [orders, fills] = await Promise.all([ dataSource.getOrders({}).catch((e) => { this.logger?.debug?.('[one-divine-lot] TradeSync 拉委托失败: ' + (e?.message ?? e)); return null; }), dataSource.getTrades({}).catch((e) => { this.logger?.debug?.('[one-divine-lot] TradeSync 拉成交失败: ' + (e?.message ?? e)); return null; }), ]); const nOrders = Array.isArray(orders) ? await storage.upsertTradeOrders(orders) : 0; const nFills = Array.isArray(fills) ? await storage.upsertTradeFills(fills) : 0; if (nOrders > 0 || nFills > 0) { this.logger?.debug?.('[one-divine-lot] TradeSync 同步完成: 委托 ' + nOrders + ' / 成交 ' + nFills); } return { orders: nOrders, fills: nFills }; } finally { this.syncing = false; } } /** 手动触发同步(api 层可调用;返回最新同步结果) */ async sync() { return this.syncNow(); } }