本策略专注于币安永续合约新币上线打新机会。通过自动监听币安官方公告,提前发现即将上线的新合约,在上线前持续采集多交易所数据、新闻舆情与链上基本面,由 AI 大模型进行带历史记忆的多轮渐进分析,在币安合约实际开盘的第一时间自动执行入场,并全程托管止盈止损。
核心优势在于:AI 不会每次独立判断,而是像真实分析师一样积累记忆,随着数据增多持续修正判断,开盘日给出最终精确的入场执行指令。
① 公告监听 + 队列维护(每15分钟) 自动拉取币安官方公告,识别”Binance Futures Will Launch … Perpetual Contract”类型的新合约公告,解析上线日期与币种名称,加入追踪队列。队列自动维护状态流转:预追踪 → 开盘日 → 交易中 → 已完成,过期条目自动清理。
② 多维数据采集 每次分析前对队列中的币种进行全量数据采集,涵盖四个维度:
③ AI 带记忆的渐进分析(核心) 使用大模型进行分析,每次分析均注入完整历史记录,实现真正的趋势记忆:
④ 上线检测 + 入场执行(每1分钟) 快速循环检测追踪队列中的币种是否已在币安开盘(GetMarkets检测),一旦上线立即读取最终 AI 策略并执行入场。支持两种入场模式:
入场前自动执行多重安全检查:置信度门槛过滤、高风险拦截、观望状态跳过、重复持仓检测,超过2小时未入场自动放弃。
⑤ 止盈止损全程托管 支持双重退出机制,无需人工值守:
| 参数 | 说明 | 默认值 |
|---|---|---|
| initmoney | 初始资金(用于收益统计) | 自动读取账户余额 |
| positionAmount | 每笔开仓金额(USDT) | 500 |
| maxLeverage | 最大允许杠杆倍数 | 10 |
| minConfidence | 最低入场置信度阈值 | 60% |
| cmcApiKey | CoinMarketCap API Key | 必填 |
| braveKey | Brave Search API Key | 选填(无则跳过新闻) |
策略运行后实时展示四张可交互表格:
视频链接:从平台公告到标的上线打新,AI替你跑全程
{"type":"n8n","content":"{\"workflowData\":{\"nodes\":[{\"parameters\":{\"notice\":\"\",\"rule\":{\"interval\":[{\"field\":\"minutes\",\"minutesInterval\":15}]}},\"type\":\"n8n-nodes-base.scheduleTrigger\",\"typeVersion\":1.2,\"position\":[-16,-16],\"id\":\"9473656e-d40d-435c-9ee6-141b6bcbe12b\",\"name\":\"慢触发器(15min)\"},{\"parameters\":{\"notice\":\"\",\"rule\":{\"interval\":[{\"field\":\"minutes\",\"minutesInterval\":1}]}},\"type\":\"n8n-nodes-base.scheduleTrigger\",\"typeVersion\":1.2,\"position\":[-32,416],\"id\":\"9986289c-da1f-443a-bb44-89aa1f66c774\",\"name\":\"快触发器(1min)\",\"logLevel\":0},{\"parameters\":{\"mode\":\"runOnceForAllItems\",\"language\":\"javaScript\",\"jsCode\":\"/*\\n * 初始化全局状态\\n * FMZ参数:\\n * initmoney: 初始资金\\n * positionAmount: 单币种最大持仓金额(U) 默认500\\n * cmcApiKey: CoinMarketCap API Key\\n * braveKey: Brave Search API Key\\n * maxLeverage: 最大杠杆倍数 默认10\\n * minConfidence: 最低入场置信度 默认60\\n */\\n\\nif (_G('nl_initialized') === null) {\\n _G('nl_initialized', true);\\n _G('nl_trackingList', JSON.stringify([]));\\n _G('nl_STARTTIME', Date.now());\\n const initAccount = exchange.GetAccount();\\n _G('nl_initmoney', $vars.initmoney || initAccount.Balance);\\n _G('nl_invoketime', 0);\\n Log('初始化完成,初始资金:', _G('nl_initmoney'));\\n}\\n\\nconst invoketime = (_G('nl_invoketime') || 0) + 1;\\n_G('nl_invoketime', invoketime);\\n\\nconst account = exchange.GetAccount();\\nconst initMoney = _G('nl_initmoney');\\nconst equity = account.Equity;\\nconst totalReturn = ((equity - initMoney) / initMoney * 100).toFixed(2);\\nconst trackingList = JSON.parse(_G('nl_trackingList') || '[]');\\n\\nLogProfit(equity - initMoney, '&');\\n\\nLog('策略状态 | 运行次数:', invoketime,\\n '| 权益:', equity.toFixed(2),\\n '| 收益率:', totalReturn + '%',\\n '| 追踪币种:', trackingList.length);\\n\\nreturn [{\\n json: {\\n invoketime,\\n equity: equity.toFixed(2),\\n availableCash: account.Balance.toFixed(2),\\n initMoney,\\n totalReturn: totalReturn + '%',\\n trackingList\\n }\\n}];\\n\",\"notice\":\"\"},\"type\":\"n8n-nodes-base.code\",\"typeVersion\":2,\"position\":[144,-16],\"id\":\"54fc3357-5e1f-40d5-a637-746552fa3f30\",\"name\":\"初始化状态\"},{\"parameters\":{\"mode\":\"runOnceForAllItems\",\"language\":\"javaScript\",\"jsCode\":\"/*\\n * 检测币安公告,发现新上线币种,维护追踪队列\\n */\\n\\nconst input = $input.first().json;\\n\\n// 1. 拉取币安新上线合约公告\\nlet catalogs = [];\\ntry {\\n const raw = HttpQuery(\\n 'https://www.binance.com/bapi/composite/v1/public/cms/article/list/query?type=1&pageNo=1&pageSize=10',\\n { method: 'GET', headers: { 'User-Agent': 'Mozilla/5.0', 'clienttype': 'web' } }\\n );\\n const data = JSON.parse(raw);\\n catalogs = data.data.catalogs || [];\\n} catch(e) {\\n Log('拉取币安公告失败:', e.message);\\n}\\n\\n// 2. 读取当前追踪队列\\nlet trackingList = JSON.parse(_G('nl_trackingList') || '[]');\\nconst existingSymbols = trackingList.map(t => t.symbol);\\n\\n// 3. 解析公告,找新币\\nfor (const catalog of catalogs) {\\n if (catalog.catalogId !== 48) continue;\\n const articles = catalog.articles || [];\\n\\n for (const a of articles) {\\n const title = a.title || '';\\n if (title.indexOf('Binance Futures Will Launch') === -1) continue;\\n if (title.indexOf('Perpetual Contract') === -1) continue;\\n\\n const dateMatch = title.match(/(\\\\d{4}-\\\\d{2}-\\\\d{2})/);\\n if (!dateMatch) continue;\\n const launchDate = dateMatch[1];\\n\\n // 过滤已过期(超过3天前上线的跳过)\\n const launchTime = new Date(launchDate).getTime();\\n if (Date.now() - launchTime > 3 * 86400000) continue;\\n\\n const symbolMatch = title.match(/([\\\\w]+USDT)(?=\\\\s+(?:USDⓈ|Perpetual))/);\\n if (!symbolMatch) continue;\\n const symbol = symbolMatch[1];\\n\\n if (existingSymbols.indexOf(symbol) !== -1) continue;\\n\\n const now = new Date();\\n const launch = new Date(launchDate);\\n const hoursToLaunch = Math.round((launch - now) / 3600000);\\n const isLaunchDay = now.toDateString() === launch.toDateString();\\n\\n trackingList.push({\\n symbol,\\n launchDate,\\n status: isLaunchDay ? 'LAUNCH_DAY' : 'PRE_LISTING',\\n discoveredAt: now.toISOString(),\\n hoursToLaunch: isLaunchDay ? '今日发行' : hoursToLaunch,\\n analysisCount: 0,\\n lastAnalyzedAt: null\\n });\\n existingSymbols.push(symbol);\\n Log('发现新币:', symbol, '| 预计上线:', launchDate, '| 距今:', isLaunchDay ? '今日发行' : hoursToLaunch + '小时');\\n }\\n break;\\n}\\n\\n// 4. 更新各币种状态,清理过期项\\nconst now = new Date();\\ntrackingList = trackingList\\n .map(item => {\\n if (item.status === 'TRADING' || item.status === 'TRADING_WAIT' || item.status === 'DONE') return item;\\n const launch = new Date(item.launchDate);\\n const hoursToLaunch = Math.round((launch - now) / 3600000);\\n const isLaunchDay = now.toDateString() === launch.toDateString();\\n return {\\n ...item,\\n hoursToLaunch: isLaunchDay ? '今日发行' : hoursToLaunch,\\n status: isLaunchDay ? 'LAUNCH_DAY' : (hoursToLaunch > 0 ? 'PRE_LISTING' : item.status)\\n };\\n })\\n .filter(item => {\\n if (item.status === 'DONE') return false;\\n const launch = new Date(item.launchDate);\\n return (Date.now() - launch.getTime()) < 3 * 86400000;\\n });\\n\\n_G('nl_trackingList', JSON.stringify(trackingList));\\n\\n// 5. 筛选需要本次分析的币种\\nconst toAnalyze = trackingList.filter(item => {\\n if (item.status !== 'PRE_LISTING' && item.status !== 'LAUNCH_DAY') return false;\\n if (item.status === 'LAUNCH_DAY') return true;\\n if (!item.lastAnalyzedAt) return true;\\n const msSince = Date.now() - new Date(item.lastAnalyzedAt).getTime();\\n return msSince >= 55 * 60 * 1000;\\n});\\n\\nif (toAnalyze.length === 0) {\\n Log('本次无需分析(未到采集间隔)');\\n return [];\\n}\\n\\nLog('本次需要分析', toAnalyze.length, '个币种:',\\n toAnalyze.map(t => t.symbol + '(' + t.status + ')').join(', '));\\n\\nreturn toAnalyze.map(item => ({ json: item }));\",\"notice\":\"\"},\"type\":\"n8n-nodes-base.code\",\"typeVersion\":2,\"position\":[320,-16],\"id\":\"c9b49012-e01b-4597-9fba-4990aa021ff2\",\"name\":\"检测公告+维护队列\"},{\"parameters\":{\"splitInBatchesNotice\":\"\",\"batchSize\":1,\"options\":{}},\"type\":\"n8n-nodes-base.splitInBatches\",\"typeVersion\":3,\"position\":[496,-16],\"id\":\"807b74b5-a1b6-4106-864d-889bbca2072b\",\"name\":\"逐个处理币种\"},{\"parameters\":{\"mode\":\"runOnceForAllItems\",\"language\":\"javaScript\",\"jsCode\":\"/*\\n * 采集当前币种的所有数据:CMC + Brave + 四个交易所\\n */\\n\\nconst item = $input.first().json;\\nLog(item)\\nconst symbol = item.symbol;\\nconst coin = symbol.replace(/USDT$/i, '');\\nLog(coin)\\n\\nLog('========== 采集:', symbol, '阶段:', item.status, '第', (item.analysisCount + 1), '次 ==========');\\n\\n// ==================== CMC ====================\\nfunction fetchCMC(coin) {\\n const key = $vars.cmcApiKey || '';\\n if (!key) { Log('未配置 cmcApiKey'); return null; }\\n try {\\n const raw = HttpQuery(\\n 'https://pro-api.coinmarketcap.com/v1/cryptocurrency/quotes/latest?symbol=' + coin + '&convert=USD',\\n { method: 'GET', headers: { 'X-CMC_PRO_API_KEY': key, 'Accept': 'application/json' } }\\n );\\n const data = JSON.parse(raw);\\n const c = data.data && data.data[coin];\\n if (!c) return null;\\n const q = c.quote.USD;\\n const supply = c.max_supply || c.total_supply;\\n return {\\n name: c.name, rank: c.cmc_rank,\\n circulatingSupply: c.circulating_supply,\\n circulationRatio: supply\\n ? parseFloat((c.circulating_supply / supply * 100).toFixed(2))\\n : null,\\n spotPrice: q.price,\\n change1h: q.percent_change_1h,\\n change24h: q.percent_change_24h,\\n change7d: q.percent_change_7d,\\n volume24h: q.volume_24h,\\n marketCap: q.market_cap,\\n fullyDilutedMarketCap: q.fully_diluted_market_cap\\n };\\n } catch(e) { Log('CMC 失败:', e.message); return null; }\\n}\\n\\n// ==================== Brave ====================\\nfunction fetchBraveNews(coin) {\\n const key = $vars.braveKey || '';\\n if (!key) { Log('未配置 braveKey'); return []; }\\n const queries = [\\n '\\\"' + coin + '\\\" token funding team',\\n '\\\"' + coin + '\\\" vesting unlock schedule',\\n '\\\"' + coin + '\\\" binance futures listing'\\n ];\\n const allNews = [];\\n const seen = {};\\n for (const q of queries) {\\n try {\\n const raw = HttpQuery(\\n 'https://api.search.brave.com/res/v1/news/search?q=' + encodeURIComponent(q) + '&count=5&freshness=pw',\\n { method: 'GET', headers: { 'X-Subscription-Token': key, 'Accept': 'application/json' } }\\n );\\n const data = JSON.parse(raw);\\n if (data.results) {\\n for (const nr of data.results) {\\n const hit = (nr.title || '').toUpperCase().indexOf(coin.toUpperCase()) !== -1 ||\\n (nr.description || '').toUpperCase().indexOf(coin.toUpperCase()) !== -1;\\n if (!hit) continue;\\n const k = nr.url || nr.title;\\n if (!seen[k]) {\\n seen[k] = true;\\n allNews.push({ title: nr.title || '', desc: nr.description || '', url: nr.url || '', age: nr.age || '' });\\n }\\n }\\n }\\n } catch(e) { Log('Brave失败:', e.message); }\\n Sleep(1200);\\n }\\n return allNews;\\n}\\n\\n// ==================== Bybit ====================\\nfunction fetchBybit(coin) {\\n try {\\n const raw = HttpQuery('https://api.bybit.com/v5/market/tickers?category=linear&symbol=' + coin + 'USDT',\\n { method: 'GET', headers: { 'Accept': 'application/json' } });\\n const parsed = JSON.parse(raw);\\n const t = parsed.result && parsed.result.list && parsed.result.list[0];\\n if (!t) return null;\\n let lastFundingRate = null;\\n try {\\n const frRaw = HttpQuery(\\n 'https://api.bybit.com/v5/market/funding/history?category=linear&symbol=' + coin + 'USDT&limit=1',\\n { method: 'GET', headers: { 'Accept': 'application/json' } });\\n const frParsed = JSON.parse(frRaw);\\n const frData = frParsed.result && frParsed.result.list && frParsed.result.list[0];\\n lastFundingRate = frData ? parseFloat(frData.fundingRate) : null;\\n } catch(e) {}\\n return {\\n price: parseFloat(t.lastPrice),\\n markPrice: parseFloat(t.markPrice),\\n indexPrice: parseFloat(t.indexPrice),\\n bid1: parseFloat(t.bid1Price),\\n bid1Size: parseFloat(t.bid1Size),\\n ask1: parseFloat(t.ask1Price),\\n ask1Size: parseFloat(t.ask1Size),\\n spread: parseFloat((parseFloat(t.ask1Price) - parseFloat(t.bid1Price)).toFixed(8)),\\n change24h: parseFloat(t.price24hPcnt) * 100,\\n high24h: parseFloat(t.highPrice24h),\\n low24h: parseFloat(t.lowPrice24h),\\n prevPrice24h: parseFloat(t.prevPrice24h),\\n volume24h: parseFloat(t.volume24h),\\n turnover24h: parseFloat(t.turnover24h),\\n openInterest: parseFloat(t.openInterest),\\n openInterestValue: parseFloat(t.openInterestValue),\\n fundingRate: parseFloat(t.fundingRate),\\n nextFundingTime: t.nextFundingTime,\\n lastFundingRate\\n };\\n } catch(e) { Log('Bybit失败:', e.message); return null; }\\n}\\n\\n// ==================== OKX ====================\\nfunction fetchOKX(coin) {\\n try {\\n const instId = coin + '-USDT-SWAP';\\n const tkRaw = HttpQuery('https://www.okx.com/api/v5/market/ticker?instId=' + instId,\\n { method: 'GET', headers: { 'Accept': 'application/json' } });\\n const tk = JSON.parse(tkRaw).data && JSON.parse(tkRaw).data[0];\\n if (!tk) return null;\\n Sleep(150);\\n let fundingRate = null, nextFundingRate = null, fundingTime = null, nextFundingTime = null;\\n try {\\n const frRaw = HttpQuery('https://www.okx.com/api/v5/public/funding-rate?instId=' + instId,\\n { method: 'GET', headers: { 'Accept': 'application/json' } });\\n const fr = JSON.parse(frRaw).data && JSON.parse(frRaw).data[0];\\n if (fr) { fundingRate = parseFloat(fr.fundingRate); nextFundingRate = parseFloat(fr.nextFundingRate); fundingTime = fr.fundingTime; nextFundingTime = fr.nextFundingTime; }\\n } catch(e) {}\\n return {\\n price: parseFloat(tk.last),\\n bid1: parseFloat(tk.bidPx), bid1Size: parseFloat(tk.bidSz),\\n ask1: parseFloat(tk.askPx), ask1Size: parseFloat(tk.askSz),\\n spread: parseFloat((parseFloat(tk.askPx) - parseFloat(tk.bidPx)).toFixed(8)),\\n change24h: parseFloat(tk.changeUtc0),\\n high24h: parseFloat(tk.high24h), low24h: parseFloat(tk.low24h), open24h: parseFloat(tk.open24h),\\n volume24h: parseFloat(tk.vol24h), volCcy24h: parseFloat(tk.volCcy24h),\\n openInterest: parseFloat(tk.openInterest || 0),\\n fundingRate, nextFundingRate, fundingTime, nextFundingTime\\n };\\n } catch(e) { Log('OKX失败:', e.message); return null; }\\n}\\n\\n// ==================== Gate ====================\\nfunction fetchGate(coin) {\\n const result = {};\\n try {\\n const sd = JSON.parse(HttpQuery('https://api.gateio.ws/api/v4/spot/tickers?currency_pair=' + coin + '_USDT',\\n { method: 'GET', headers: { 'Accept': 'application/json' } }));\\n const s = sd && sd[0];\\n if (s) result.spot = {\\n price: parseFloat(s.last), bid1: parseFloat(s.highest_bid), ask1: parseFloat(s.lowest_ask),\\n spread: parseFloat((parseFloat(s.lowest_ask) - parseFloat(s.highest_bid)).toFixed(8)),\\n change24h: parseFloat(s.change_percentage),\\n high24h: parseFloat(s.high_24h), low24h: parseFloat(s.low_24h),\\n volume24h: parseFloat(s.base_volume), turnover24h: parseFloat(s.quote_volume)\\n };\\n } catch(e) {}\\n Sleep(200);\\n try {\\n const fc = JSON.parse(HttpQuery('https://api.gateio.ws/api/v4/futures/usdt/contracts/' + coin + '_USDT',\\n { method: 'GET', headers: { 'Accept': 'application/json' } }));\\n if (fc && fc.name) result.futures = {\\n markPrice: parseFloat(fc.mark_price), indexPrice: parseFloat(fc.index_price),\\n lastPrice: parseFloat(fc.last_price), fundingRate: parseFloat(fc.funding_rate),\\n fundingInterval: fc.funding_interval,\\n openInterest: parseFloat(fc.open_interest),\\n change24h: parseFloat(fc.change_percentage),\\n high24h: parseFloat(fc.high_24h), low24h: parseFloat(fc.low_24h),\\n volume24h: parseFloat(fc.volume_24h), turnover24h: parseFloat(fc.volume_24h_quote),\\n leverageMax: fc.leverage_max\\n };\\n } catch(e) {}\\n return (result.spot || result.futures) ? result : null;\\n}\\n\\n// ==================== HTX ====================\\nfunction fetchHTX(coin) {\\n const result = {};\\n const sym = coin.toLowerCase();\\n try {\\n const sd = JSON.parse(HttpQuery('https://api.huobi.pro/market/detail/merged?symbol=' + sym + 'usdt',\\n { method: 'GET', headers: { 'Accept': 'application/json' } }));\\n if (sd.status === 'ok' && sd.tick) {\\n const tk = sd.tick;\\n result.spot = {\\n price: parseFloat(tk.close),\\n bid1: parseFloat(tk.bid[0]), bid1Size: parseFloat(tk.bid[1]),\\n ask1: parseFloat(tk.ask[0]), ask1Size: parseFloat(tk.ask[1]),\\n spread: parseFloat((tk.ask[0] - tk.bid[0]).toFixed(8)),\\n open24h: parseFloat(tk.open), high24h: parseFloat(tk.high), low24h: parseFloat(tk.low),\\n change24h: parseFloat(((tk.close - tk.open) / tk.open * 100).toFixed(2)),\\n volume24h: parseFloat(tk.amount), turnover24h: parseFloat(tk.vol)\\n };\\n }\\n } catch(e) {}\\n Sleep(200);\\n try {\\n const fd = JSON.parse(HttpQuery('https://api.hbdm.com/linear-swap-ex/market/detail/merged?contract_code=' + coin + '-USDT',\\n { method: 'GET', headers: { 'Accept': 'application/json' } }));\\n if (fd.status === 'ok' && fd.tick) {\\n const ft = fd.tick;\\n let fundingRate = null, nextFundingRate = null, markPrice = null, indexPrice = null, fundingTime = null;\\n try {\\n const frRaw = JSON.parse(HttpQuery('https://api.hbdm.com/linear-swap-api/v1/swap_funding_rate?contract_code=' + coin + '-USDT',\\n { method: 'GET', headers: { 'Accept': 'application/json' } }));\\n const frData = frRaw.data && frRaw.data[0];\\n if (frData) {\\n fundingRate = parseFloat(frData.funding_rate);\\n nextFundingRate = parseFloat(frData.estimated_rate);\\n markPrice = parseFloat(frData.mark_price);\\n indexPrice = parseFloat(frData.index_price);\\n fundingTime = frData.funding_time;\\n }\\n } catch(e) {}\\n result.futures = {\\n price: parseFloat(ft.close), markPrice, indexPrice,\\n bid1: parseFloat(ft.bid[0]), bid1Size: parseFloat(ft.bid[1]),\\n ask1: parseFloat(ft.ask[0]), ask1Size: parseFloat(ft.ask[1]),\\n spread: parseFloat((ft.ask[0] - ft.bid[0]).toFixed(8)),\\n open24h: parseFloat(ft.open), high24h: parseFloat(ft.high), low24h: parseFloat(ft.low),\\n change24h: parseFloat(((ft.close - ft.open) / ft.open * 100).toFixed(2)),\\n volume24h: parseFloat(ft.amount), turnover24h: parseFloat(ft.vol),\\n fundingRate, nextFundingRate, fundingTime\\n };\\n }\\n } catch(e) {}\\n return (result.spot || result.futures) ? result : null;\\n}\\n\\n// ==================== 执行采集 ====================\\nconst cmc = fetchCMC(coin); Sleep(300);\\nconst news = fetchBraveNews(coin); Sleep(300);\\nconst bybit = fetchBybit(coin); Sleep(200);\\nconst okx = fetchOKX(coin); Sleep(200);\\nconst gate = fetchGate(coin); Sleep(200);\\nconst htx = fetchHTX(coin);\\n\\nconst mulexchanges = { bybit, okx, gate, htx };\\n\\nLog('采集完成 | CMC:', cmc ? 'OK' : '无',\\n '| 新闻:', news.length + '条',\\n '| Bybit:', bybit ? bybit.price : '未上线',\\n '| OKX:', okx ? okx.price : '未上线',\\n '| Gate:', gate ? (gate.spot ? gate.spot.price : gate.futures ? gate.futures.lastPrice : '无') : '未上线',\\n '| HTX:', htx ? (htx.spot ? htx.spot.price : htx.futures ? htx.futures.price : '无') : '未上线'\\n);\\n\\nreturn [{\\n json: {\\n ...item,\\n currentData: { collectedAt: new Date().toISOString(), cmc, news, mulexchanges }\\n }\\n}];\",\"notice\":\"\"},\"type\":\"n8n-nodes-base.code\",\"typeVersion\":2,\"position\":[688,-144],\"id\":\"986ddac8-0c78-4db5-86dc-aea8a00a0641\",\"name\":\"采集数据\"},{\"parameters\":{\"mode\":\"runOnceForAllItems\",\"language\":\"javaScript\",\"jsCode\":\"/*\\n * 读取历史分析记录,构建带历史记忆的 AI Prompt\\n */\\n\\nconst item = $input.first().json;\\nconst symbol = item.symbol;\\nconst coin = symbol.replace(/USDT$/i, '');\\nconst currentData = item.currentData;\\nconst isLaunchDay = item.status === 'LAUNCH_DAY';\\n\\nconst historyKey = 'nl_history_' + symbol;\\nconst history = JSON.parse(_G(historyKey) || '[]');\\nconst orderNum = history.length + 1;\\n\\n// ===== 构建历史摘要 =====\\nfunction buildHistorySection(history) {\\n if (history.length === 0) return '(这是对该币种的第一次分析,暂无历史记录)';\\n return history.map(h => {\\n const ex = (h.currentData && h.currentData.mulexchanges) || {};\\n const bybit = ex.bybit;\\n const okx = ex.okx;\\n const ai = h.aiConclusion;\\n const lines = [\\n '### 第' + h.order + '次分析',\\n '- 时间: ' + h.timestamp + ' | 距上线' + h.hoursToLaunch + ' | 阶段:' + h.phase + ' | 频率:' + h.interval,\\n '- Bybit: 价格=' + (bybit ? bybit.price : '未上线') + ' / 资金费率=' + (bybit ? bybit.fundingRate : '-') + ' / 持仓量=' + (bybit ? bybit.openInterestValue + 'U' : '-'),\\n '- OKX: 价格=' + (okx ? okx.price : '未上线') + ' / 资金费率=' + (okx ? okx.fundingRate : '-'),\\n '- CMC: 排名=' + ((h.currentData && h.currentData.cmc && h.currentData.cmc.rank) || '-') + ' / 流通比=' + ((h.currentData && h.currentData.cmc && h.currentData.cmc.circulationRatio !== null ? h.currentData.cmc.circulationRatio + '%' : '数据缺失')),\\n '- 结论: ' + ai.direction + ', 置信度' + ai.confidence + '%, 风险' + ai.riskLevel,\\n '- 趋势: ' + (ai.trendConsistency || '初始') + (ai.reversalType && ai.reversalType !== 'null' ? '(' + ai.reversalType + ')' : ''),\\n '- 入场时机: ' + (ai.entryTiming || '-'),\\n '- 关键变化: ' + (ai.keyChanges || '-'),\\n '- 综合判断: ' + ai.summary\\n ];\\n return lines.join('\\\\n');\\n }).join('\\\\n\\\\n');\\n}\\n\\n// ===== 格式化交易所数据 =====\\nfunction fmtEx(name, data) {\\n if (!data) return '### ' + name + '\\\\n未上线/无数据';\\n return [\\n '### ' + name,\\n '- 价格/标记/指数: ' + (data.price || data.lastPrice || '-') + ' / ' + (data.markPrice || '-') + ' / ' + (data.indexPrice || '-'),\\n '- 买一/卖一/价差: ' + (data.bid1 || '-') + ' / ' + (data.ask1 || '-') + ' / ' + (data.spread || '-'),\\n '- 24h涨跌: ' + (data.change24h !== undefined ? data.change24h + '%' : '-') + ' | 最高: ' + (data.high24h || '-') + ' | 最低: ' + (data.low24h || '-'),\\n '- 24h成交额: ' + (data.turnover24h || data.volCcy24h || '-') + 'U | 持仓量: ' + (data.openInterest || data.openInterestValue || '-') + 'U',\\n '- 资金费率(当期/预测): ' + (data.fundingRate !== undefined ? data.fundingRate : '-') + ' / ' + (data.nextFundingRate !== undefined ? data.nextFundingRate : '-')\\n ].join('\\\\n');\\n}\\n\\nconst ex = currentData.mulexchanges;\\nconst cmc = currentData.cmc;\\n\\nconst gateStr = ex.gate ? [\\n '### Gate',\\n ex.gate.spot ? '现货: 价格=' + ex.gate.spot.price + ' / 24h涨跌=' + ex.gate.spot.change24h + '%' : '现货: 未上线',\\n ex.gate.futures ? '合约: 价格=' + ex.gate.futures.lastPrice + ' / 资金费率=' + ex.gate.futures.fundingRate + ' / 持仓量=' + ex.gate.futures.openInterest : '合约: 未上线'\\n].join('\\\\n') : '### Gate\\\\n未上线/无数据';\\n\\nconst htxStr = ex.htx ? [\\n '### HTX',\\n ex.htx.spot ? '现货: 价格=' + ex.htx.spot.price + ' / 24h涨跌=' + ex.htx.spot.change24h + '%' : '现货: 未上线',\\n ex.htx.futures ? '合约: 价格=' + ex.htx.futures.price + ' / 资金费率=' + ex.htx.futures.fundingRate : '合约: 未上线'\\n].join('\\\\n') : '### HTX\\\\n未上线/无数据';\\n\\nconst newsStr = currentData.news.slice(0, 6).map((n, i) =>\\n (i + 1) + '. [' + (n.age || '') + '] ' + n.title + '\\\\n ' + n.desc\\n).join('\\\\n');\\n\\nconst cmcStr = cmc ? [\\n '- 名称: ' + cmc.name + ' / CMC排名: ' + cmc.rank,\\n '- 现货价: ' + cmc.spotPrice + ' USD',\\n '- 涨跌: 1h=' + cmc.change1h + '% / 24h=' + cmc.change24h + '% / 7d=' + cmc.change7d + '%',\\n '- 流通比: ' + (cmc.circulationRatio !== null ? cmc.circulationRatio + '% (仅供参考)' : '数据缺失,忽略此项'),\\n '- 市值: ' + cmc.marketCap + ' USD / 完全稀释市值: ' + cmc.fullyDilutedMarketCap + ' USD',\\n '- 24h成交量: ' + cmc.volume24h + ' USD'\\n].join('\\\\n') : '无CMC数据';\\n\\nconst launchDayNote = isLaunchDay\\n ? '[今天是上线日,这次分析将作为最终入场依据,请给出精确的入场价格区间和明确的止盈止损位。]'\\n : '[当前为上线前预判阶段,重点识别方向趋势和风险因素,价格区间可适当宽泛。]';\\n\\nconst promptLines = [\\n '你是负责跟踪 ' + coin + ' 币安永续合约打新的专职量化分析师。',\\n '这是你对该币种的第 ' + orderNum + ' 次分析。',\\n '',\\n '## 历史分析记录',\\n buildHistorySection(history),\\n '',\\n '---',\\n '',\\n '## 当前数据(第' + orderNum + '次 | ' + (isLaunchDay ? '今日发行' : '距上线约' + item.hoursToLaunch + '小时') + ' | 阶段:' + item.status + ' | 频率:' + (isLaunchDay ? '每15分钟' : '每小时') + ')',\\n '',\\n '### CMC基本面',\\n cmcStr,\\n '',\\n fmtEx('Bybit合约', ex.bybit),\\n '',\\n fmtEx('OKX合约', ex.okx),\\n '',\\n gateStr,\\n '',\\n htxStr,\\n '',\\n '### 近期新闻(共' + currentData.news.length + '条)',\\n newsStr || '无相关新闻',\\n '',\\n '---',\\n '',\\n '## 分析指引',\\n '',\\n '历史判断原则:',\\n '1. 历史方向一致 → 置信度应随次数递增,标记趋势为强化或维持',\\n '2. 出现方向反转 → 必须明确说明是[真实信号](有新的基本面/数据支撑)还是[短期噪音](开盘前情绪波动)',\\n '3. 若历史判断摇摆不定,置信度应保守,不可盲目给高分',\\n '4. keyChanges字段必须与上次数据做具体对比',\\n '',\\n '数据缺失处理原则:',\\n '1. 某交易所无数据属正常现象,不作为负面信号',\\n '2. 流通比、持仓量等字段缺失时忽略该项,不纳入风险判断',\\n '3. 风险点只列出有实际数据支撑的问题,不列推测性风险',\\n '',\\n '价格基准:',\\n '- 若多交易所均有合约价格,以Bybit标记价为基准估算币安开盘价区间',\\n '- 若仅有现货,以现货价为参考,开盘价通常有5~20%溢价',\\n '- 若所有交易所均无数据,基于CMC价格估算,区间要宽泛',\\n '',\\n '入场时机说明:',\\n '- immediate = 开盘立即市价入场',\\n '- drawdown_N = 等开盘后回调N%再入场(N为数字,如drawdown_5代表等回调5%)',\\n '- 做多时回调 = 价格下跌N%触发;做空时回调 = 价格上涨N%触发',\\n '- 新币开盘波动极大,若判断趋势明确建议immediate,若开盘溢价过高可选drawdown',\\n '',\\n launchDayNote,\\n '',\\n '请严格输出以下JSON,不输出任何其他内容:',\\n '{',\\n ' \\\"order\\\": ' + orderNum + ',',\\n ' \\\"direction\\\": \\\"做多|做空|观望\\\",',\\n ' \\\"confidence\\\": 0到100的整数,',\\n ' \\\"trendConsistency\\\": \\\"初始|强化|维持|弱化|反转\\\",',\\n ' \\\"reversalType\\\": \\\"真实信号|短期噪音|null\\\",',\\n ' \\\"entryTiming\\\": \\\"immediate|drawdown_N\\\",',\\n ' \\\"priceRange\\\": { \\\"low\\\": 数字, \\\"high\\\": 数字 },',\\n ' \\\"leverage\\\": 建议杠杆1到20的整数,',\\n ' \\\"stopLoss\\\": 止损百分比的数字,',\\n ' \\\"takeProfit\\\": 止盈百分比的数字,',\\n ' \\\"riskLevel\\\": \\\"高|中|低\\\",',\\n ' \\\"riskPoints\\\": [\\\"风险点1\\\", \\\"风险点2\\\"],',\\n ' \\\"keyChanges\\\": \\\"与上次分析相比最重要的数据变化\\\",',\\n ' \\\"summary\\\": \\\"100字以内综合判断,需引用历史趋势和当前最关键信号\\\"',\\n '}'\\n];\\n\\nconst prompt = promptLines.join('\\\\n');\\n\\nreturn [{\\n json: {\\n ...item,\\n currentData,\\n history,\\n orderNum,\\n prompt\\n }\\n}];\",\"notice\":\"\"},\"type\":\"n8n-nodes-base.code\",\"typeVersion\":2,\"position\":[864,-144],\"id\":\"83802ad8-d19d-42ff-85ac-e2786d85285a\",\"name\":\"读历史+构建Prompt\"},{\"parameters\":{\"text\":\"={{ $json.prompt }}\",\"options\":{\"systemMessage\":\"你是一名专业的加密货币衍生品量化交易员,专注于新币上线打新策略分析。\\n\\n核心原则:\\n1. 始终输出纯JSON,不包含任何解释文字、markdown格式或代码块标记\\n2. 利用历史分析记录建立趋势记忆,不允许每次独立判断\\n3. 方向反转时必须提供明确的基本面或数据支撑理由\\n4. 高风险情形(资金费率异常、无流动性、明确的基本面风险)需在riskPoints列出;流通比等数据缺失时忽略该项,不作为高风险依据\\n5. 置信度要真实反映信息质量,信息不足时不超过60\\n6. 开盘当天输出必须包含可执行的精确价格区间和明确的止盈止损位\\n\\n严格禁止:\\n- 输出JSON以外的任何文字\\n- 使用```json```等代码块包裹\\n- 在信息不足时仍给出高置信度判断\"}},\"type\":\"@n8n/n8n-nodes-langchain.agent\",\"typeVersion\":1,\"position\":[1024,-144],\"id\":\"5e2f6157-7fb8-4b2d-88de-e9d00a7cda39\",\"name\":\"AI分析智能体\",\"retryOnFail\":true},{\"parameters\":{\"model\":{\"__rl\":true,\"value\":\"x-ai/grok-4.1-fast\",\"mode\":\"list\",\"cachedResultName\":\"x-ai/grok-4.1-fast\"}},\"type\":\"n8n-nodes-base.lmOpenAi\",\"typeVersion\":1,\"position\":[1024,32],\"id\":\"26a9693b-88a7-46ba-906c-949ae333498f\",\"name\":\"OpenAI 模型\",\"credentials\":{\"openAiApi\":{\"id\":\"54d0b567-b3fc-4c6a-b6be-546e0b9cd83f\",\"name\":\"openrouter\"}}},{\"parameters\":{\"mode\":\"runOnceForAllItems\",\"language\":\"javaScript\",\"jsCode\":\"/*\\n * 解析AI返回结果,保存历史记录,更新最终策略\\n */\\n\\nconst item = $node[\\\"读历史+构建Prompt\\\"].json; // 取原始数据\\nconst symbol = item.symbol;\\nconst aiOutput = $input.first().json.output || $input.first().json.text || '';\\n\\nLog(item)\\nLog(symbol)\\nLog(aiOutput)\\n\\nfunction parseAIResult(output) {\\n try {\\n let cleaned = output.replace(/```[a-z]*\\\\n?/gi, '').trim();\\n const match = cleaned.match(/\\\\{[\\\\s\\\\S]*\\\\}/);\\n if (!match) throw new Error('未找到JSON内容');\\n return JSON.parse(match[0]);\\n } catch(e) {\\n try {\\n let s = output.replace(/```[a-z]*\\\\n?/gi, '').trim();\\n const open = (s.match(/\\\\{/g) || []).length;\\n const close = (s.match(/\\\\}/g) || []).length;\\n for (let i = 0; i < open - close; i++) s += '}';\\n const m = s.match(/\\\\{[\\\\s\\\\S]*\\\\}/);\\n if (m) return JSON.parse(m[0]);\\n } catch(e2) {}\\n Log('AI JSON解析失败:', e.message, '原始输出前300字:', aiOutput.substring(0, 300));\\n return null;\\n }\\n}\\n\\nconst aiResult = parseAIResult(aiOutput);\\n\\nif (!aiResult) {\\n Log(symbol, 'AI分析失败,跳过保存');\\n return [{ json: { ...item, aiResult: null, saved: false } }];\\n}\\n\\nconst record = {\\n order: item.orderNum,\\n timestamp: new Date().toISOString(),\\n phase: item.status,\\n interval: item.status === 'LAUNCH_DAY' ? '每15分钟' : '每小时',\\n hoursToLaunch: item.hoursToLaunch,\\n currentData: item.currentData,\\n aiConclusion: aiResult\\n};\\n\\nconst historyKey = 'nl_history_' + symbol;\\nconst history = JSON.parse(_G(historyKey) || '[]');\\nhistory.push(record);\\n_G(historyKey, JSON.stringify(history));\\n\\n_G('nl_strategy_' + symbol, JSON.stringify({\\n symbol,\\n updatedAt: new Date().toISOString(),\\n phase: item.status,\\n hoursToLaunch: item.hoursToLaunch,\\n historyCount: history.length,\\n aiResult\\n}));\\n\\nlet trackingList = JSON.parse(_G('nl_trackingList') || '[]');\\ntrackingList = trackingList.map(t =>\\n t.symbol === symbol\\n ? { ...t, analysisCount: history.length, lastAnalyzedAt: new Date().toISOString() }\\n : t\\n);\\n_G('nl_trackingList', JSON.stringify(trackingList));\\n\\nLog(symbol, '第' + record.order + '次分析保存完成',\\n '| 方向:', aiResult.direction,\\n '| 置信度:', aiResult.confidence + '%',\\n '| 趋势:', aiResult.trendConsistency,\\n '| 风险:', aiResult.riskLevel\\n);\\nLog(symbol, '判断:', aiResult.summary);\\nif (aiResult.riskPoints && aiResult.riskPoints.length > 0) {\\n Log(symbol, '风险点:', aiResult.riskPoints.join(' | '));\\n}\\n\\nreturn [{ json: { ...item, aiResult, historyCount: history.length, saved: true } }];\\n\",\"notice\":\"\"},\"type\":\"n8n-nodes-base.code\",\"typeVersion\":2,\"position\":[1360,0],\"id\":\"0a2e51dc-30b1-4bb7-8484-164f9e5f61f1\",\"name\":\"保存历史+更新策略\"},{\"parameters\":{\"mode\":\"runOnceForAllItems\",\"language\":\"javaScript\",\"jsCode\":\"/*\\n * 快触发器主逻辑(每1分钟):\\n * 1. 检查等待回调入场的币种\\n * 2. 遍历追踪队列中 PRE_LISTING / LAUNCH_DAY 状态的币种\\n * 3. GetMarkets() 检测是否已在币安上线\\n * 4. 检测到上线 → 读取最终策略 → 安全检查 → 执行入场\\n */\\n\\nconst CONFIG = {\\n POSITION_AMOUNT: $vars.positionAmount || 500,\\n MAX_LEVERAGE: $vars.maxLeverage || 10,\\n MIN_CONFIDENCE: $vars.minConfidence || 60,\\n RETRY: 3,\\n RETRY_INTERVAL: 500\\n};\\n\\nfunction updateStatus(symbol, status) {\\n let list = JSON.parse(_G('nl_trackingList') || '[]');\\n list = list.map(t => t.symbol === symbol ? { ...t, status } : t);\\n _G('nl_trackingList', JSON.stringify(list));\\n}\\n\\nfunction getMarketInfo(coin) {\\n const sym = coin + '_USDT.swap';\\n for (let i = 0; i < CONFIG.RETRY; i++) {\\n try {\\n const markets = exchange.GetMarkets();\\n if (markets && markets[sym]) return markets[sym];\\n } catch(e) {}\\n Sleep(CONFIG.RETRY_INTERVAL);\\n }\\n return null;\\n}\\n\\nfunction calcContractAmount(amountUSD, price, market) {\\n if (!market || !market.CtVal || price <= 0) return 0;\\n let qty = amountUSD / price / market.CtVal;\\n qty = _N(qty, market.AmountPrecision || 0);\\n if (market.MinQty && qty < market.MinQty) return 0;\\n if (market.MaxQty && qty > market.MaxQty) qty = market.MaxQty;\\n return qty;\\n}\\n\\nfunction hasPosition(coin) {\\n try {\\n exchange.SetCurrency(coin + '_USDT');\\n exchange.SetContractType('swap');\\n const positions = exchange.GetPositions();\\n const sym = coin + '_USDT.swap';\\n return positions && positions.some(p => p.Symbol === sym && Math.abs(p.Amount) > 0);\\n } catch(e) { return false; }\\n}\\n\\nfunction executeEntry(symbol, ai) {\\n const coin = symbol.replace(/USDT$/i, '');\\n try {\\n exchange.SetCurrency(coin + '_USDT');\\n exchange.SetContractType('swap');\\n\\n const leverage = Math.min(ai.leverage || 5, CONFIG.MAX_LEVERAGE);\\n exchange.SetMarginLevel(leverage);\\n\\n const market = getMarketInfo(coin);\\n if (!market) return { success: false, reason: '无法获取市场信息' };\\n\\n exchange.SetCurrency(coin + '_USDT');\\n exchange.SetContractType('swap');\\n const ticker = exchange.GetTicker();\\n if (!ticker) return { success: false, reason: '无法获取价格' };\\n const price = ticker.Last;\\n if (price <= 0) return { success: false, reason: '价格异常' };\\n\\n const account = exchange.GetAccount();\\n const allocAmount = Math.min(CONFIG.POSITION_AMOUNT, account.Balance * 0.3);\\n\\n const qty = calcContractAmount(allocAmount, price, market);\\n if (qty <= 0) return { success: false, reason: '合约数量计算无效' };\\n\\n let orderId;\\n if (ai.direction === '做多') {\\n exchange.SetDirection('buy');\\n orderId = exchange.Buy(-1, qty);\\n } else if (ai.direction === '做空') {\\n exchange.SetDirection('sell');\\n orderId = exchange.Sell(-1, qty);\\n } else {\\n return { success: false, reason: '方向为观望,不入场' };\\n }\\n\\n if (!orderId) return { success: false, reason: '下单失败' };\\n\\n _G(coin + '_USDT.swap_maxprofit', 0);\\n _G(coin + '_USDT.swap_maxStopLoss', ai.stopLoss || 10);\\n _G(coin + '_nl_entry', JSON.stringify({\\n symbol, coin,\\n direction: ai.direction,\\n entryPrice: price,\\n leverage,\\n stopLoss: ai.stopLoss,\\n takeProfit: ai.takeProfit,\\n priceRange: ai.priceRange,\\n entryTime: new Date().toISOString(),\\n orderId\\n }));\\n\\n Log('入场成功:', symbol,\\n '| 方向:', ai.direction,\\n '| 价格:', price,\\n '| 杠杆:', leverage + 'x',\\n '| 数量:', qty + '张',\\n '| 金额:', allocAmount + 'U',\\n '| 止损:', ai.stopLoss + '%',\\n '| 止盈:', ai.takeProfit + '%'\\n );\\n return { success: true, price, direction: ai.direction, qty, leverage };\\n } catch(e) {\\n Log('入场异常:', symbol, e.message);\\n return { success: false, reason: e.message };\\n }\\n}\\n\\n// ==================== 检查等待回调入场 ====================\\n\\nfunction checkPendingEntries(results) {\\n const trackingList = JSON.parse(_G('nl_trackingList') || '[]');\\n const waiting = trackingList.filter(t => t.status === 'TRADING_WAIT');\\n\\n for (const item of waiting) {\\n const coin = item.symbol.replace(/USDT$/i, '');\\n const waitRaw = _G(coin + '_nl_waitEntry');\\n\\n // [BUG1修复] waitEntry 丢失时直接清理,防止状态永久挂起\\n if (!waitRaw) {\\n Log(item.symbol, 'waitEntry 数据丢失,强制清理 TRADING_WAIT');\\n _G(coin + '_nl_listedAt', null);\\n updateStatus(item.symbol, 'DONE');\\n results.push({ symbol: item.symbol, action: 'skip', reason: 'waitEntry丢失' });\\n continue;\\n }\\n\\n const wait = JSON.parse(waitRaw);\\n\\n // [BUG1修复] TRADING_WAIT 独立超时检查(2小时),主循环的超时对此状态无效\\n const waitStartTime = wait.waitStartTime || Date.now();\\n if (!wait.waitStartTime) {\\n // 首次执行时补写 waitStartTime(兼容旧数据)\\n wait.waitStartTime = waitStartTime;\\n _G(coin + '_nl_waitEntry', JSON.stringify(wait));\\n }\\n const hoursWaiting = (Date.now() - waitStartTime) / 3600000;\\n if (hoursWaiting > 2) {\\n Log(item.symbol, '回调等待超过2小时,放弃入场 | 等待时长:',\\n _N(hoursWaiting * 60, 1) + '分钟',\\n '| 开盘价:', wait.openPrice,\\n '| 目标价:', wait.targetPrice\\n );\\n _G(coin + '_nl_waitEntry', null);\\n _G(coin + '_nl_listedAt', null);\\n updateStatus(item.symbol, 'DONE');\\n results.push({ symbol: item.symbol, action: 'skip', reason: '回调等待超时' });\\n continue;\\n }\\n\\n try {\\n exchange.SetCurrency(coin + '_USDT');\\n exchange.SetContractType('swap');\\n const ticker = exchange.GetTicker();\\n if (!ticker) continue;\\n const curPrice = ticker.Last;\\n\\n const triggered = wait.ai.direction === '做多'\\n ? curPrice <= wait.targetPrice\\n : curPrice >= wait.targetPrice;\\n\\n if (triggered) {\\n Log(item.symbol, '回调到位 | 当前价:', curPrice, '| 目标价:', wait.targetPrice);\\n const execResult = executeEntry(item.symbol, wait.ai);\\n if (execResult.success) {\\n _G(coin + '_nl_waitEntry', null);\\n updateStatus(item.symbol, 'TRADING');\\n results.push({ symbol: item.symbol, action: 'entered_after_drawdown', ...execResult });\\n }\\n } else {\\n Log(item.symbol, '等待回调中 | 当前价:', curPrice,\\n '| 目标价:', wait.targetPrice,\\n '| 还差:', _N(Math.abs(curPrice - wait.targetPrice) / wait.openPrice * 100, 2) + '%',\\n '| 已等:', _N(hoursWaiting * 60, 1) + '分钟 / 上限120分钟'\\n );\\n }\\n } catch(e) {\\n Log(item.symbol, '检查等待入场异常:', e.message);\\n }\\n Sleep(200);\\n }\\n}\\n\\n// ==================== 主逻辑 ====================\\n\\nconst trackingList = JSON.parse(_G('nl_trackingList') || '[]');\\nconst results = [];\\n\\ncheckPendingEntries(results);\\n\\nif (trackingList.length === 0) {\\n return [{ json: { action: 'no_tracking' } }];\\n}\\n\\nlet allMarkets = {};\\ntry { allMarkets = exchange.GetMarkets() || {}; } catch(e) { Log('GetMarkets失败:', e.message); }\\n\\nconst toCheck = trackingList.filter(t => t.status === 'PRE_LISTING' || t.status === 'LAUNCH_DAY');\\n\\nfor (const item of toCheck) {\\n const symbol = item.symbol;\\n const coin = symbol.replace(/USDT$/i, '');\\n const marketKey = coin + '_USDT.swap';\\n const listedAtKey = coin + '_nl_listedAt';\\n\\n if (!allMarkets[marketKey]) continue;\\n\\n // 第一次检测到上线,记录实际上线时间\\n if (!_G(listedAtKey)) {\\n _G(listedAtKey, Date.now());\\n Log(symbol, '首次检测到上线,记录时间:', new Date().toISOString());\\n }\\n\\n // 超过2小时未成功入场,视为异常跳过\\n const listedAt = _G(listedAtKey);\\n const hoursSinceListed = (Date.now() - listedAt) / 3600000;\\n if (hoursSinceListed > 2) {\\n Log(symbol, '自上线已超过2小时仍未入场,视为异常,跳过');\\n _G(listedAtKey, null);\\n updateStatus(symbol, 'DONE');\\n results.push({ symbol, action: 'skip', reason: '超时未入场' });\\n continue;\\n }\\n\\n Log('在币安检测到上线:', symbol,\\n '| 已上线:', _N(hoursSinceListed * 60, 1) + '分钟'\\n );\\n\\n const strategyRaw = _G('nl_strategy_' + symbol);\\n if (!strategyRaw) {\\n Log(symbol, '无策略数据,等待下次分析');\\n continue;\\n }\\n\\n const strategy = JSON.parse(strategyRaw);\\n const ai = strategy.aiResult;\\n\\n if (!ai) {\\n Log(symbol, 'AI策略数据异常,跳过');\\n _G(listedAtKey, null);\\n updateStatus(symbol, 'DONE');\\n results.push({ symbol, action: 'skip', reason: 'AI数据异常' });\\n continue;\\n }\\n\\n if (ai.direction === '观望') {\\n Log(symbol, 'AI建议观望,不入场');\\n _G(listedAtKey, null);\\n updateStatus(symbol, 'DONE');\\n results.push({ symbol, action: 'skip', reason: '观望' });\\n continue;\\n }\\n\\n if (ai.riskLevel === '高') {\\n Log(symbol, '高风险,不入场 |', (ai.riskPoints || []).join('、'));\\n _G(listedAtKey, null);\\n updateStatus(symbol, 'DONE');\\n results.push({ symbol, action: 'skip', reason: '高风险' });\\n continue;\\n }\\n\\n if (ai.confidence < CONFIG.MIN_CONFIDENCE) {\\n Log(symbol, '置信度过低(' + ai.confidence + '% < ' + CONFIG.MIN_CONFIDENCE + '%),不入场');\\n _G(listedAtKey, null);\\n updateStatus(symbol, 'DONE');\\n results.push({ symbol, action: 'skip', reason: '置信度不足' });\\n continue;\\n }\\n\\n if (hasPosition(coin)) {\\n Log(symbol, '已有持仓,标记为TRADING');\\n _G(listedAtKey, null);\\n updateStatus(symbol, 'TRADING');\\n results.push({ symbol, action: 'already_trading' });\\n continue;\\n }\\n\\n const entryTiming = ai.entryTiming || 'immediate';\\n\\n if (entryTiming === 'immediate') {\\n const execResult = executeEntry(symbol, ai);\\n if (execResult.success) {\\n _G(listedAtKey, null);\\n updateStatus(symbol, 'TRADING');\\n results.push({ symbol, action: 'entered', ...execResult });\\n } else {\\n // [BUG2修复] 记录失败次数,超过3次放弃,避免无限重试\\n const retryKey = coin + '_nl_retryCount';\\n const retryCount = (_G(retryKey) || 0) + 1;\\n _G(retryKey, retryCount);\\n Log(symbol, '入场失败(' + retryCount + '/3):', execResult.reason);\\n if (retryCount >= 3) {\\n Log(symbol, '连续失败3次,放弃入场');\\n _G(retryKey, null);\\n _G(listedAtKey, null);\\n updateStatus(symbol, 'DONE');\\n results.push({ symbol, action: 'skip', reason: '重试超限:' + execResult.reason });\\n } else {\\n results.push({ symbol, action: 'failed', retryCount, reason: execResult.reason });\\n }\\n }\\n }\\n else if (entryTiming.startsWith('drawdown_')) {\\n const pct = parseFloat(entryTiming.split('_')[1]) || 5;\\n exchange.SetCurrency(coin + '_USDT');\\n exchange.SetContractType('swap');\\n const ticker = exchange.GetTicker();\\n const openPrice = ticker ? ticker.Last : 0;\\n if (openPrice > 0) {\\n const targetPrice = ai.direction === '做多'\\n ? openPrice * (1 - pct / 100)\\n : openPrice * (1 + pct / 100);\\n _G(coin + '_nl_waitEntry', JSON.stringify({\\n type: 'drawdown',\\n waitStartTime: Date.now(), // [BUG1修复] 记录开始等待时间,供超时检查使用\\n openPrice,\\n targetPrice,\\n drawdownPct: pct,\\n ai\\n }));\\n updateStatus(symbol, 'TRADING_WAIT');\\n Log(symbol, '等待回调', pct + '%后入场',\\n '| 开盘价:', openPrice,\\n '| 目标价:', targetPrice\\n );\\n results.push({ symbol, action: 'waiting_drawdown', openPrice, pct, targetPrice });\\n }\\n }\\n\\n Sleep(300);\\n}\\n\\nreturn [{ json: { checked: toCheck.length, results, timestamp: new Date().toISOString() } }];\",\"notice\":\"\"},\"type\":\"n8n-nodes-base.code\",\"typeVersion\":2,\"position\":[144,416],\"id\":\"c1b3aed2-a972-4f09-9a1f-d6bf1c4b8be9\",\"name\":\"上线检测+入场执行\"},{\"parameters\":{\"mode\":\"runOnceForAllItems\",\"language\":\"javaScript\",\"jsCode\":\"/*\\n * 可视化仪表板 + 止盈止损监控\\n * 1. 账户概览\\n * 2. 追踪队列状态\\n * 3. AI分析摘要\\n * 4. 实时持仓监控(含止盈止损自动执行)\\n */\\n\\nconst TP_SL = {\\n DEFAULT_SL: 10,\\n DEFAULT_TP: 25,\\n TRAILING_TRIGGER: 30,\\n TRAILING_DRAWDOWN: 8\\n};\\n\\n// ==================== 工具 ====================\\n\\nfunction getStartTime() {\\n let t = _G('nl_STARTTIME');\\n if (!t) { t = Date.now(); _G('nl_STARTTIME', t); }\\n return t;\\n}\\n\\nfunction formatRuntime(ts) {\\n const d = Date.now() - ts;\\n const days = Math.floor(d / 86400000);\\n const h = Math.floor((d % 86400000) / 3600000);\\n const m = Math.floor((d % 3600000) / 60000);\\n if (days > 0) return days + '天' + h + '时' + m + '分';\\n if (h > 0) return h + '时' + m + '分';\\n return m + '分';\\n}\\n\\nfunction fmtPnl(v) {\\n if (v > 0) return '🟢 +$' + _N(v, 2);\\n if (v < 0) return '🔴 $' + _N(v, 2);\\n return '⚪ $0';\\n}\\n\\nfunction fmtPct(v) {\\n if (v > 0) return '🟢 +' + _N(v, 2) + '%';\\n if (v < 0) return '🔴 ' + _N(v, 2) + '%';\\n return '⚪ 0%';\\n}\\n\\n// ==================== 命令处理 ====================\\n\\nfunction handleCommand() {\\n const cmd = GetCommand();\\n if (!cmd) return;\\n const parts = cmd.split(':');\\n const action = parts[0];\\n\\n if (action === '重置日志') {\\n LogReset();\\n _G('nl_initmoney', null);\\n Log('日志和初始资金已重置');\\n }\\n else if (action === '清除币种') {\\n const symbol = parts[1];\\n if (symbol) {\\n let list = JSON.parse(_G('nl_trackingList') || '[]');\\n list = list.filter(t => t.symbol !== symbol);\\n _G('nl_trackingList', JSON.stringify(list));\\n _G('nl_history_' + symbol, null);\\n _G('nl_strategy_' + symbol, null);\\n Log('已清除币种:', symbol);\\n }\\n }\\n else if (action === '手动平仓') {\\n const coin = parts[1];\\n const posType = parseInt(parts[2]);\\n const confirmed = parts.length >= 4;\\n if (confirmed && coin) {\\n try {\\n exchange.SetCurrency(coin + '_USDT');\\n exchange.SetContractType('swap');\\n const positions = exchange.GetPositions();\\n let targetPos = null;\\n for (const pos of positions) {\\n if (pos.Symbol.indexOf(coin) !== -1 && pos.Type === posType) { targetPos = pos; break; }\\n }\\n if (targetPos && Math.abs(targetPos.Amount) > 0) {\\n const isLong = posType === PD_LONG || posType === 0;\\n if (isLong) { exchange.SetDirection('closebuy'); exchange.Sell(-1, Math.abs(targetPos.Amount)); }\\n else { exchange.SetDirection('closesell'); exchange.Buy(-1, Math.abs(targetPos.Amount)); }\\n Log(coin, '手动平仓成功');\\n _G(coin + '_USDT.swap_maxprofit', null);\\n _G(coin + '_USDT.swap_maxStopLoss', null);\\n _G(coin + '_USDT.swap_tpDrawdown', null);\\n _G(coin + '_nl_entry', null);\\n let list = JSON.parse(_G('nl_trackingList') || '[]');\\n list = list.map(t => t.symbol === coin + 'USDT' ? { ...t, status: 'DONE' } : t);\\n _G('nl_trackingList', JSON.stringify(list));\\n } else { Log(coin, '无持仓'); }\\n } catch(e) { Log(coin, '平仓失败:', e.message); }\\n }\\n }\\n else if (action === '修改止损') {\\n const coin = parts[1];\\n const jsonStr = parts.slice(2).join(':');\\n try {\\n const params = JSON.parse(jsonStr);\\n const sl = parseFloat(params['止损%'] || 0);\\n const tp = parseFloat(params['止盈回撤%'] || 0);\\n if (sl > 0) { _G(coin + '_USDT.swap_maxStopLoss', sl); Log(coin, '止损更新:', sl + '%'); }\\n if (tp > 0) { _G(coin + '_USDT.swap_tpDrawdown', tp); Log(coin, '移动止盈回撤:', tp + '%'); }\\n } catch(e) { Log('修改止损失败:', e.message); }\\n }\\n}\\n\\n// ==================== 止盈止损监控 ====================\\n\\nfunction monitorPositions(positions, tickers) {\\n if (!positions || positions.length === 0) return;\\n for (const pos of positions) {\\n if (Math.abs(pos.Amount) === 0) continue;\\n const coinMatch = pos.Symbol.match(/^(.+)_USDT/);\\n if (!coinMatch) continue;\\n const coin = coinMatch[1];\\n const ticker = tickers[coin + '_USDT.swap'];\\n if (!ticker) continue;\\n\\n const isLong = pos.Type === PD_LONG || pos.Type === 0;\\n const curPrice = ticker.Last;\\n const entPrice = pos.Price;\\n const amount = Math.abs(pos.Amount);\\n const pnlPct = ((curPrice - entPrice) * (isLong ? 1 : -1) / entPrice * 100);\\n\\n const maxProfitKey = coin + '_USDT.swap_maxprofit';\\n const slKey = coin + '_USDT.swap_maxStopLoss';\\n const tpDrawKey = coin + '_USDT.swap_tpDrawdown';\\n\\n let maxProfit = _G(maxProfitKey);\\n if (maxProfit === null) { maxProfit = pnlPct / 100; _G(maxProfitKey, maxProfit); }\\n else if (pnlPct / 100 > maxProfit) { maxProfit = pnlPct / 100; _G(maxProfitKey, maxProfit); }\\n const maxPnlPct = maxProfit * 100;\\n\\n let tpDrawdown = _G(tpDrawKey) || 0;\\n if (tpDrawdown === 0 && maxPnlPct >= TP_SL.TRAILING_TRIGGER) {\\n tpDrawdown = TP_SL.TRAILING_DRAWDOWN;\\n _G(tpDrawKey, tpDrawdown);\\n Log(coin, '浮盈达' + _N(maxPnlPct, 2) + '%,自动启用移动止盈,回撤' + tpDrawdown + '%触发');\\n }\\n\\n const maxSL = _G(slKey) || TP_SL.DEFAULT_SL;\\n const drawdown = maxPnlPct - pnlPct;\\n\\n let closeReason = null;\\n if (tpDrawdown > 0 && maxPnlPct > 0 && drawdown >= tpDrawdown) {\\n closeReason = '移动止盈(回撤' + _N(drawdown, 2) + '%>=' + tpDrawdown + '%)';\\n }\\n if (!closeReason && maxSL > 0 && pnlPct <= -maxSL) {\\n closeReason = '止损(亏损' + _N(Math.abs(pnlPct), 2) + '%>=' + maxSL + '%)';\\n }\\n const entryRaw = _G(coin + '_nl_entry');\\n if (!closeReason && entryRaw) {\\n const entry = JSON.parse(entryRaw);\\n if (entry.takeProfit && pnlPct >= entry.takeProfit) {\\n closeReason = '固定止盈(' + _N(pnlPct, 2) + '%>=' + entry.takeProfit + '%)';\\n }\\n }\\n\\n if (closeReason) {\\n try {\\n exchange.SetCurrency(coin + '_USDT');\\n exchange.SetContractType('swap');\\n if (isLong) { exchange.SetDirection('closebuy'); exchange.Sell(-1, amount); }\\n else { exchange.SetDirection('closesell'); exchange.Buy(-1, amount); }\\n Log(coin, '触发', closeReason, '自动平仓 | 浮盈:', _N(pnlPct, 2) + '%');\\n _G(maxProfitKey, null); _G(slKey, null); _G(tpDrawKey, null); _G(coin + '_nl_entry', null);\\n let list = JSON.parse(_G('nl_trackingList') || '[]');\\n list = list.map(t => t.symbol === coin + 'USDT' ? { ...t, status: 'DONE' } : t);\\n _G('nl_trackingList', JSON.stringify(list));\\n } catch(e) { Log(coin, '自动平仓失败:', e.message); }\\n }\\n }\\n}\\n\\n// ==================== 表格:账户概览 ====================\\n\\nfunction createAccountTable(account) {\\n const table = {\\n type: 'table', title: '账户概览',\\n cols: ['运行时间', '初始权益', '当前权益', '总收益', '收益率', '操作'],\\n rows: []\\n };\\n const initMoney = _G('nl_initmoney') || account.Equity;\\n const profit = account.Equity - initMoney;\\n const pct = ((profit / initMoney) * 100);\\n LogProfit(profit, '&');\\n table.rows.push([\\n formatRuntime(getStartTime()),\\n '$' + _N(initMoney, 2),\\n '$' + _N(account.Equity, 2),\\n fmtPnl(profit),\\n fmtPct(pct),\\n [{ type: 'button', cmd: '重置日志', name: '重置日志' }]\\n ]);\\n return table;\\n}\\n\\n// ==================== 表格:追踪队列 ====================\\n\\nfunction createTrackingTable() {\\n const table = {\\n type: 'table', title: '追踪队列',\\n cols: ['币种', '状态', '上线日期', '距今(h)', '分析次数', 'AI方向', '置信度', '风险', '操作'],\\n rows: []\\n };\\n const trackingList = JSON.parse(_G('nl_trackingList') || '[]');\\n if (trackingList.length === 0) {\\n table.rows.push(['暂无追踪币种', '-', '-', '-', '-', '-', '-', '-', '-']);\\n return table;\\n }\\n for (const item of trackingList) {\\n const statusMap = {\\n 'PRE_LISTING': '预追踪',\\n 'LAUNCH_DAY': '开盘日',\\n 'TRADING_WAIT': '等待回调', // ← 新增这行\\n 'TRADING': '交易中',\\n 'DONE': '已完成'\\n };\\n const statusDisplay = statusMap[item.status] || item.status;\\n const stratRaw = _G('nl_strategy_' + item.symbol);\\n let direction = '-', confidence = '-', riskLevel = '-';\\n if (stratRaw) {\\n const ai = JSON.parse(stratRaw).aiResult;\\n if (ai) { direction = ai.direction; confidence = ai.confidence + '%'; riskLevel = ai.riskLevel; }\\n }\\n table.rows.push([\\n item.symbol, statusDisplay, item.launchDate,\\n item.hoursToLaunch !== undefined ? item.hoursToLaunch : '-',\\n item.analysisCount || 0,\\n direction, confidence, riskLevel,\\n [{ type: 'button', cmd: '清除币种:' + item.symbol, name: '清除' }]\\n ]);\\n }\\n return table;\\n}\\n\\n// ==================== 表格:AI分析摘要 ====================\\n\\nfunction createAITable() {\\n const table = {\\n type: 'table', title: 'AI分析摘要(最新一次)',\\n cols: ['币种', '更新时间', '阶段', '距上线', '方向', '置信度', '趋势', '入场时机', '杠杆', '止损', '止盈', '风险', '综合判断'],\\n rows: []\\n };\\n const trackingList = JSON.parse(_G('nl_trackingList') || '[]');\\n const active = trackingList.filter(t => t.analysisCount > 0);\\n if (active.length === 0) {\\n table.rows.push(['暂无', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '等待AI分析']);\\n return table;\\n }\\n for (const item of active) {\\n const stratRaw = _G('nl_strategy_' + item.symbol);\\n if (!stratRaw) continue;\\n const strat = JSON.parse(stratRaw);\\n const ai = strat.aiResult;\\n if (!ai) continue;\\n const updatedAt = strat.updatedAt ? strat.updatedAt.replace('T', ' ').substring(0, 16) : '-';\\n table.rows.push([\\n item.symbol, updatedAt, strat.phase || '-',\\n strat.hoursToLaunch !== undefined ? (typeof strat.hoursToLaunch === 'number' ? strat.hoursToLaunch + 'h' : strat.hoursToLaunch) : '-',\\n ai.direction, ai.confidence + '%', ai.trendConsistency || '-',\\n ai.entryTiming || '-',\\n ai.leverage ? ai.leverage + 'x' : '-',\\n ai.stopLoss ? ai.stopLoss + '%' : '-',\\n ai.takeProfit ? ai.takeProfit + '%' : '-',\\n ai.riskLevel || '-',\\n ai.summary || '-'\\n ]);\\n }\\n return table;\\n}\\n\\n// ==================== 表格:实时持仓 ====================\\n\\nfunction createPositionTable(positions, tickers) {\\n const table = {\\n type: 'table', title: '实时持仓监控',\\n cols: ['币种', '方向', '入场价', '现价', '杠杆', '头寸(U)', '浮盈', '浮盈%', '最高浮盈%', '移动止盈', '止损', '状态', '操作'],\\n rows: []\\n };\\n let hasPos = false;\\n for (const pos of (positions || [])) {\\n if (Math.abs(pos.Amount) === 0) continue;\\n const cm = pos.Symbol.match(/^(.+)_USDT/);\\n if (!cm) continue;\\n const coin = cm[1];\\n const ticker = tickers[coin + '_USDT.swap'];\\n if (!ticker) continue;\\n hasPos = true;\\n\\n const isLong = pos.Type === PD_LONG || pos.Type === 0;\\n const curPrice = ticker.Last;\\n const entPrice = pos.Price;\\n const amount = Math.abs(pos.Amount);\\n const notional = amount * curPrice;\\n const pnlUsd = pos.Profit || ((curPrice - entPrice) * (isLong ? 1 : -1) * amount);\\n const pnlPct = ((curPrice - entPrice) * (isLong ? 1 : -1) / entPrice * 100);\\n const maxPnlPct = (_G(coin + '_USDT.swap_maxprofit') || 0) * 100;\\n const tpDrawdown = _G(coin + '_USDT.swap_tpDrawdown') || 0;\\n const maxSL = _G(coin + '_USDT.swap_maxStopLoss') || TP_SL.DEFAULT_SL;\\n const entryRaw = _G(coin + '_nl_entry');\\n const entryInfo = entryRaw ? JSON.parse(entryRaw) : null;\\n const tpFixed = entryInfo ? entryInfo.takeProfit : TP_SL.DEFAULT_TP;\\n const leverage = pos.MarginLevel || (entryInfo ? entryInfo.leverage : '-');\\n\\n let status = '持仓中';\\n if (tpDrawdown > 0 && maxPnlPct > 0) {\\n const remain = tpDrawdown - (maxPnlPct - pnlPct);\\n status = remain <= 1 ? '即将止盈(剩' + _N(remain, 1) + '%)' : '移动止盈' + tpDrawdown + '%';\\n }\\n if (pnlPct <= -(maxSL - 2)) status = '接近止损位';\\n\\n table.rows.push([\\n coin, isLong ? '多' : '空',\\n '$' + _N(entPrice, 6), '$' + _N(curPrice, 6),\\n leverage + 'x', '$' + _N(notional, 2),\\n fmtPnl(pnlUsd), fmtPct(pnlPct),\\n maxPnlPct > 0 ? '+' + _N(maxPnlPct, 2) + '%' : _N(maxPnlPct, 2) + '%',\\n tpDrawdown > 0 ? '回撤' + tpDrawdown + '%' : '未启用',\\n '-' + maxSL + '% / +' + tpFixed + '%',\\n status,\\n [\\n {\\n type: 'button', name: '平仓',\\n cmd: '手动平仓:' + coin + ':' + pos.Type + ':confirm',\\n input: { type: 'string', defValue: '确认平仓 ' + coin }\\n },\\n {\\n type: 'button', name: '修改止损',\\n cmd: '修改止损:' + coin,\\n group: [\\n { name: '止损%', type: 'number', defValue: maxSL },\\n { name: '止盈回撤%', type: 'number', defValue: tpDrawdown || TP_SL.TRAILING_DRAWDOWN }\\n ]\\n }\\n ]\\n ]);\\n }\\n if (!hasPos) {\\n table.rows.push(['当前无持仓', '-', '-', '-', '-', '-', '-', '-', '-', '-', '-', '等待入场信号', '-']);\\n }\\n return table;\\n}\\n\\n// ==================== 主执行 ====================\\n\\nhandleCommand();\\n\\nconst account = exchange.GetAccount();\\nconst positions = exchange.GetPositions() || [];\\nconst tickers = {};\\ntry {\\n const allTickers = exchange.GetTickers() || [];\\n for (const t of allTickers) tickers[t.Symbol] = t;\\n} catch(e) {}\\n\\nmonitorPositions(positions, tickers);\\n\\nconst t1 = createAccountTable(account);\\nconst t2 = createTrackingTable();\\nconst t3 = createAITable();\\nconst t4 = createPositionTable(positions, tickers);\\n\\nLogStatus(\\n '`' + JSON.stringify(t1) + '`\\\\n\\\\n' +\\n '`' + JSON.stringify(t2) + '`\\\\n\\\\n' +\\n '`' + JSON.stringify(t3) + '`\\\\n\\\\n' +\\n '`' + JSON.stringify(t4) + '`'\\n);\\n\\nreturn [{ json: { success: true, timestamp: new Date().toISOString() } }];\\n\",\"notice\":\"\"},\"type\":\"n8n-nodes-base.code\",\"typeVersion\":2,\"position\":[320,416],\"id\":\"d7d2bb2d-9726-4c52-94c4-9c6f0408a6e1\",\"name\":\"可视化仪表板+止盈止损\"}],\"pinData\":{},\"connections\":{\"慢触发器(15min)\":{\"main\":[[{\"node\":\"初始化状态\",\"type\":\"main\",\"index\":0}]]},\"初始化状态\":{\"main\":[[{\"node\":\"检测公告+维护队列\",\"type\":\"main\",\"index\":0}]]},\"检测公告+维护队列\":{\"main\":[[{\"node\":\"逐个处理币种\",\"type\":\"main\",\"index\":0}]]},\"逐个处理币种\":{\"main\":[[],[{\"node\":\"采集数据\",\"type\":\"main\",\"index\":0}]]},\"采集数据\":{\"main\":[[{\"node\":\"读历史+构建Prompt\",\"type\":\"main\",\"index\":0}]]},\"读历史+构建Prompt\":{\"main\":[[{\"node\":\"AI分析智能体\",\"type\":\"main\",\"index\":0}]]},\"AI分析智能体\":{\"main\":[[{\"node\":\"保存历史+更新策略\",\"type\":\"main\",\"index\":0}]]},\"OpenAI 模型\":{\"ai_languageModel\":[[{\"node\":\"AI分析智能体\",\"type\":\"ai_languageModel\",\"index\":0}]]},\"快触发器(1min)\":{\"main\":[[{\"node\":\"上线检测+入场执行\",\"type\":\"main\",\"index\":0}]]},\"上线检测+入场执行\":{\"main\":[[{\"node\":\"可视化仪表板+止盈止损\",\"type\":\"main\",\"index\":0}]]},\"保存历史+更新策略\":{\"main\":[[{\"node\":\"逐个处理币种\",\"type\":\"main\",\"index\":0}]]}},\"active\":false,\"settings\":{\"timezone\":\"Asia/Shanghai\",\"executionOrder\":\"v1\"},\"tags\":[],\"credentials\":{},\"id\":\"645b98a6-729a-4e32-8848-45def8709777\",\"plugins\":{},\"mcpClients\":{}},\"startNodes\":[],\"triggerToStartFrom\":{\"name\":\"慢触发器(15min)\"}}"}