Grid WorkFlow Test Version


创建日期: 2025-10-20 11:47:09 最后修改: 2025-10-23 13:55:15
复制: 0 点击次数: 74
avatar of ianzeng123 ianzeng123
2
关注
329
关注者
策略源码
{"type":"n8n","content":"{\"workflowData\":{\"nodes\":[{\"parameters\":{\"mode\":\"rules\",\"rules\":{\"values\":[{\"conditions\":{\"options\":{\"caseSensitive\":true,\"leftValue\":\"\",\"typeValidation\":\"strict\",\"version\":2},\"conditions\":[{\"leftValue\":\"={{ $json.aiTrigger.shouldTrigger }}\",\"rightValue\":\"false\",\"operator\":{\"type\":\"boolean\",\"operation\":\"false\",\"singleValue\":true},\"id\":\"0029cdc5-fc22-425f-82aa-bea90f32f866\"}],\"combinator\":\"and\"},\"renameOutput\":false},{\"conditions\":{\"options\":{\"caseSensitive\":true,\"leftValue\":\"\",\"typeValidation\":\"strict\",\"version\":2},\"conditions\":[{\"id\":\"64d4c904-c8d4-439b-a890-335ae87605ac\",\"leftValue\":\"={{ $json.aiTrigger.shouldTrigger }}\",\"rightValue\":\"\",\"operator\":{\"type\":\"boolean\",\"operation\":\"true\",\"singleValue\":true}}],\"combinator\":\"and\"},\"renameOutput\":false}]},\"looseTypeValidation\":false,\"options\":{}},\"type\":\"n8n-nodes-base.switch\",\"typeVersion\":3.2,\"position\":[-1552,288],\"id\":\"62a42868-2807-4950-bfb6-ea738343c846\",\"name\":\"Branch\"},{\"parameters\":{\"mode\":\"runOnceForAllItems\",\"language\":\"javaScript\",\"jsCode\":\"let grid = _G('grid');\\nlet initPrice = _G('initPrice');\\nlet initEquity = _G('initEquity');\\n\\n// ========== Read configuration parameters from n8n variables ==========\\nlet maxPositions = $vars.maxPositions;      // Maximum grid levels\\nlet stepPercent = $vars.stepPercent;        // Grid step size\\nlet volatilityThreshold = 0.02; // Volatility threshold (default 2%)\\nlet volatilityPeriod = 20; // Volatility calculation period (default 20 candles)\\n\\n// ========== Volatility check function ==========\\nfunction checkVolatility() {\\n  // Get historical candlestick data\\n  let records = exchange.GetRecords();\\n  if (!records || records.length < volatilityPeriod) {\\n    Log('Insufficient candlestick data, unable to calculate volatility');\\n    return { isHigh: false, value: 0 };\\n  }\\n  \\n  // Calculate price volatility for the last N candles\\n  let prices = [];\\n  for (let i = records.length - volatilityPeriod; i < records.length; i++) {\\n    prices.push(records[i].Close);\\n  }\\n  \\n  // Calculate average price\\n  let avgPrice = prices.reduce((a, b) => a + b, 0) / prices.length;\\n  \\n  // Calculate standard deviation\\n  let squareDiffs = prices.map(price => Math.pow(price - avgPrice, 2));\\n  let avgSquareDiff = squareDiffs.reduce((a, b) => a + b, 0) / squareDiffs.length;\\n  let stdDev = Math.sqrt(avgSquareDiff);\\n  \\n  // Calculate volatility (standard deviation/average price)\\n  let volatility = stdDev / avgPrice;\\n  \\n  Log('Current volatility:', (volatility * 100).toFixed(2) + '%', \\n      'Threshold:', (volatilityThreshold * 100).toFixed(2) + '%');\\n  \\n  return {\\n    isHigh: volatility > volatilityThreshold,\\n    value: volatility\\n  };\\n}\\n\\n// ========== Check volatility before initialization ==========\\nif (!grid || Object.keys(grid).length === 0) {\\n  \\n  // Check volatility\\n  let volatilityCheck = checkVolatility();\\n  \\n  if (volatilityCheck.isHigh) {\\n    Log('⚠️ Current market volatility is too high:', (volatilityCheck.value * 100).toFixed(2) + '%');\\n    Log('Waiting for market stabilization before initializing grid...');\\n    return { \\n      status: 'waiting',\\n      reason: 'high_volatility',\\n      volatility: volatilityCheck.value\\n    };\\n  }\\n  \\n  Log('✓ Volatility check passed, starting grid initialization');\\n  \\n  // ========== Get initial equity ==========\\n  if (!initEquity) {\\n    let equity = exchange.GetAccount();\\n    if (equity) {\\n      initEquity = equity.Equity;\\n      _G('initEquity', initEquity);\\n      Log('Using current market equity as initial equity:', initEquity);\\n    } else {\\n      Log('Failed to get market account');\\n      return null;\\n    }\\n  }\\n\\n  // ========== Get initial price ==========\\n  if (!initPrice) {\\n    let ticker = exchange.GetTicker();\\n    if (ticker) {\\n      initPrice = ticker.Last;\\n      _G('initPrice', initPrice);\\n      Log('Using current market price as initial price:', initPrice);\\n    } else {\\n      Log('Failed to get market price');\\n      return null;\\n    }\\n  }\\n\\n  // ========== Initialize grid ==========\\n  grid = {\\n    // ========== Configuration parameters ==========\\n    stepPercent: stepPercent,        // Grid step size\\n    maxPositions: maxPositions,      // Maximum grid levels\\n    \\n    // ========== Grid data ==========\\n    longOpenPrices: [],    // Target long position open price array\\n    longClosePrices: [],   // Target long position close price array\\n    longPositions: [],     // Long position status array\\n    shortOpenPrices: [],   // Target short position open price array\\n    shortClosePrices: [],  // Target short position close price array\\n    shortPositions: []     // Short position status array\\n  };\\n  \\n  // Initialize long position grid (open long when price drops)\\n  for (let i = 1; i <= maxPositions; i++) {\\n    grid.longOpenPrices.push(initPrice * (1 - stepPercent * i));\\n    grid.longClosePrices.push(initPrice * (1 - stepPercent * (i - 1)));\\n    grid.longPositions.push({\\n      isOpen: false,\\n      openTime: null,\\n      openPrice: null\\n    });\\n  }\\n  \\n  // Initialize short position grid (open short when price rises)\\n  for (let i = 1; i <= maxPositions; i++) {\\n    grid.shortOpenPrices.push(initPrice * (1 + stepPercent * i));\\n    grid.shortClosePrices.push(initPrice * (1 + stepPercent * (i - 1)));\\n    grid.shortPositions.push({\\n      isOpen: false,\\n      openTime: null,\\n      openPrice: null\\n    });\\n  }\\n  \\n  _G('grid', grid);\\n  Log('========== Grid initialization complete ==========');\\n  Log('Initial price:', initPrice);\\n  Log('Initial equity:', initEquity);\\n  Log('Grid step size:', (stepPercent * 100) + '%');\\n  Log('Maximum levels:', maxPositions);\\n  Log('Current volatility:', (volatilityCheck.value * 100).toFixed(2) + '%');\\n  Log('Long grid range:', grid.longOpenPrices[0].toFixed(2), '-', grid.longOpenPrices[maxPositions-1].toFixed(2));\\n  Log('Short grid range:', grid.shortOpenPrices[0].toFixed(2), '-', grid.shortOpenPrices[maxPositions-1].toFixed(2));\\n  Log('===================================');\\n} \\n\\nreturn {};\",\"notice\":\"\"},\"type\":\"n8n-nodes-base.code\",\"typeVersion\":2,\"position\":[-2224,288],\"id\":\"f9368077-bb7e-43af-855a-309a7e25f43d\",\"name\":\"Parameter Initialization\"},{\"parameters\":{},\"type\":\"n8n-nodes-base.noOp\",\"typeVersion\":1,\"position\":[-1328,192],\"id\":\"3d841754-993f-4075-be23-1336e8fa5afd\",\"name\":\"No Operation\"},{\"parameters\":{\"endpointUrl\":\"https://mcp.alphavantage.co/mcp?apikey='PL7O6NWMVMLEQD0Y'\",\"authentication\":\"none\",\"tool\":\"NEWS_SENTIMENT\",\"tickers\":\"=CRYPTO:{{$vars.contract}}\"},\"type\":\"@n8n/n8n-nodes-langchain.mcpClient\",\"typeVersion\":1.1,\"position\":[-1328,384],\"id\":\"07a3c5dd-5379-40b6-b10a-694d4052d944\",\"name\":\"Sentiment News Fetch\"},{\"parameters\":{\"mode\":\"runOnceForAllItems\",\"language\":\"javaScript\",\"jsCode\":\"// n8n correct syntax\\nconst inputData = $input.all();\\nconst sentimentData = inputData[0].json;\\n\\n// Get data from specific node\\nconst positionNode = $node[\\\"Trigger Evaluation\\\"].json;\\n\\n// Return data\\nreturn {\\n  timestamp: new Date().toISOString(),\\n  \\n  // Keep original news data unchanged\\n  sentimentData: sentimentData,\\n  \\n  // Only organize position data\\n  positions: positionNode\\n};\",\"notice\":\"\"},\"type\":\"n8n-nodes-base.code\",\"typeVersion\":2,\"position\":[-1104,384],\"id\":\"84b10ce1-4d6d-4569-8f4a-89b4e2af5e9b\",\"name\":\"Result Organization\"},{\"parameters\":{\"inputText\":\"=## Strategy Background\\nYou are analyzing a bidirectional grid trading strategy. This strategy sets up both long and short grids based on the initial price (initPrice):\\n- **Long Grid**: Opens long positions incrementally when price drops, closes for profit when price rebounds (step size {{$vars.stepPercent}}%)\\n- **Short Grid**: Opens short positions incrementally when price rises, closes for profit when price drops (step size {{$vars.stepPercent}}%)\\n- **Maximum Positions**: {{$vars.maxPositions}} levels for both long and short, total of {{2 * $vars.maxPositions}} positions\\n\\n## Current Trigger Conditions\\nThe system has detected one of the following abnormal situations:\\n1. Position count reaches {{$vars.maxPositions}} (full position status)\\n2. Longest holding time exceeds 24 hours (position trapped)\\n3. When holding short positions, price breaks through grid upper limit (price continues to rise)\\n4. When holding long positions, price breaks through grid lower limit (price continues to fall)\\n\\n## Your Analysis Task\\nPlease make a comprehensive judgment based on the following data:\\n\\n### Data 1: Position Status (positions){{JSON.stringify($json.positions)}}\\n- **longPositions**: Long position list, including open price, holding duration, floating P&L\\n- **shortPositions**: Short position list, including open price, holding duration, floating P&L\\n- **summary**: Summary data (total positions, average holding duration, total floating P&L)\\n- **gridInfo**: Grid range (initPrice, current price, grid upper and lower limits)\\n\\n### Data 2: Market Sentiment (sentimentData){{JSON.stringify($json.sentimentData)}}\\n- From Alpha Vantage cryptocurrency news sentiment analysis\\n- Includes latest market news, sentiment scores, topic heat, etc.\\n\\n## Judgment Criteria\\n**Situations REQUIRING grid price adjustment**:\\n- Clear and sustained market trend (news sentiment extremely bullish/bearish)\\n- Current price far from initial grid range (breakthrough or breakdown exceeding 3%)\\n- Positions severely trapped and market sentiment does not support reversal\\n- News shows significant fundamental changes (regulation, tech upgrades, major events)\\n\\n**Situations NOT requiring adjustment**:\\n- Price fluctuates normally within grid range\\n- News sentiment neutral or contradictory\\n- Short-term volatility, lacking trend confirmation\\n- Floating losses within acceptable range\\n\\n**Note**:\\n- Must return clear \\\"Yes\\\" or \\\"No\\\"\\n- Reasoning should be concise, specific, and actionable\\n- Exercise caution, avoid frequent grid adjustments\",\"options\":{\"categories\":\"Yes, No\",\"systemPromptTemplate\":\"You are highly intelligent and accurate sentiment analyzer. Analyze the sentiment of the provided text. Categorize it into one of the following: {categories}. Use the provided formatting instructions. Only output the JSON.\\nPlease respond with a raw JSON object only, not inside any code block, Markdown, or text explanation.\\nYour response must be only valid JSON (no backticks, no language labels, no extra text).\"}},\"type\":\"n8n-nodes-base.sentimentAnalysis\",\"typeVersion\":1.1,\"position\":[-880,384],\"id\":\"1e049938-d06d-40b6-b123-26ad06525642\",\"name\":\"AI Parameter Analysis\"},{\"parameters\":{\"mode\":\"runOnceForAllItems\",\"language\":\"javaScript\",\"jsCode\":\"Log('Close all positions, reset all parameters #ff0000')\\n\\nlet positions = exchange.GetPosition();\\n\\nif (positions[0].Type === 0) {\\n    // Close long position - market sell\\n    const orderId = exchange.CreateOrder(positions[0].Symbol, 'closebuy', -1, positions[0].Amount);\\n    Log(`✓ Successfully closed long position, Order ID: ${orderId}`);\\n} else if (positions[0].Type === 1) {\\n    // Close short position - market buy\\n    const orderId = exchange.CreateOrder(positions[0].Symbol, 'closesell', -1, positions[0].Amount);\\n    Log(`✓ Successfully closed short position, Order ID: ${orderId}`);\\n}\\n\\n_G('grid', null);\\n_G('initPrice', null);\\n_G('lastAItime', Date.now());\\n\\nreturn {};\",\"notice\":\"\"},\"type\":\"n8n-nodes-base.code\",\"typeVersion\":2,\"position\":[-480,288],\"id\":\"73738726-e0f0-4cfa-8722-61f33bcf5578\",\"name\":\"Reset Strategy\"},{\"parameters\":{\"mode\":\"runOnceForAllItems\",\"language\":\"javaScript\",\"jsCode\":\"Log('AI analysis does not support adjusting original price')\\n\\n_G('lastAItime', Date.now())\\n\\nreturn {};\",\"notice\":\"\"},\"type\":\"n8n-nodes-base.code\",\"typeVersion\":2,\"position\":[-480,480],\"id\":\"b9837b90-613f-48ad-ae0b-3cff3bfe936a\",\"name\":\"AI Cooldown\"},{\"parameters\":{\"mode\":\"runOnceForAllItems\",\"language\":\"javaScript\",\"jsCode\":\"// ========== Position Statistics Node ==========\\nvar grid = _G('grid');\\nvar ticker = exchange.GetTicker();\\nvar curaccount = exchange.GetAccount();\\nvar initPrice = _G('initPrice');\\nvar initEquity = _G('initEquity');\\n\\nif (!ticker || !grid || !initPrice || !curaccount || !initEquity) {\\n    return {};\\n}\\n\\nlet curProfit = curaccount.Equity - initEquity;\\nLogProfit(curProfit, \\\"&\\\");\\n\\nvar currentPrice = ticker.Last;\\nvar now = Date.now();\\nvar maxPositions = grid.maxPositions || 5;\\n\\n// Count open positions and total floating P&L\\nvar openCount = 0;\\nvar lastOpenPosition = null;\\nvar totalProfit = 0;\\nvar longCount = 0;\\nvar shortCount = 0;\\n\\n// Count long positions\\nfor (var i = 0; i < grid.longPositions.length; i++) {\\n    if (grid.longPositions[i].isOpen) {\\n        openCount++;\\n        longCount++;\\n        lastOpenPosition = grid.longPositions[i];\\n        var posProfit = ((currentPrice - grid.longPositions[i].openPrice) / grid.longPositions[i].openPrice) * 100;\\n        totalProfit += posProfit;\\n    }\\n}\\n\\n// Count short positions\\nfor (var i = 0; i < grid.shortPositions.length; i++) {\\n    if (grid.shortPositions[i].isOpen) {\\n        openCount++;\\n        shortCount++;\\n        lastOpenPosition = grid.shortPositions[i];\\n        var posProfit = ((grid.shortPositions[i].openPrice - currentPrice) / grid.shortPositions[i].openPrice) * 100;\\n        totalProfit += posProfit;\\n    }\\n}\\n\\n// Build position table\\nvar table = {\\n    type: \\\"table\\\",\\n    title: \\\"Bidirectional Grid Positions\\\",\\n    cols: [\\\"Init Price\\\", \\\"Current Price\\\", \\\"Grid Step\\\", \\\"Long Count\\\", \\\"Short Count\\\", \\\"Total Positions\\\", \\\"Init Equity\\\", \\\"Current Equity\\\", \\\"Cumulative P&L\\\", \\\"Floating P&L%\\\"],\\n    rows: [[\\n        _N(initPrice, 2),\\n        _N(currentPrice, 2),\\n        _N(grid.stepPercent * 100, 2) + '%',\\n        longCount,\\n        shortCount,\\n        openCount + '/' + maxPositions,\\n        _N(initEquity, 2),\\n        _N(curaccount.Equity, 2),\\n        _N(curProfit, 2),\\n        _N(totalProfit, 2) + '%'\\n    ]]\\n};\\n\\nLogStatus(\\\"`\\\" + JSON.stringify(table) + \\\"`\\\");\\n\\n// Don't trigger AI if not at maximum positions\\nif (openCount < maxPositions) {\\n    return { aiTrigger: { shouldTrigger: false } };\\n}\\n\\n// Check AI cooldown period (10 minutes)\\nvar lastAItime = _G('lastAItime');\\nif (lastAItime && (now - lastAItime) < 600000) {\\n    Log('AI cooling down, remaining', ((600000 - (now - lastAItime)) / 60000).toFixed(1), 'minutes');\\n    return { aiTrigger: { shouldTrigger: false } };\\n}\\n\\n// Calculate conditions when at maximum positions\\nvar holdHours = (now - lastOpenPosition.openTime) / 3600000;\\nvar priceDeviation = Math.abs(currentPrice / lastOpenPosition.openPrice - 1);\\n\\n// Price deviation >3% OR holding >24 hours\\nvar shouldTriggerAI = priceDeviation > 0.03 || holdHours >= 24;\\n\\nif (shouldTriggerAI) {\\n    Log('Trigger AI analysis. Deviation:', (priceDeviation * 100).toFixed(2) + '% Duration:', holdHours.toFixed(1), 'hours');\\n}\\n\\nreturn {\\n    aiTrigger: {\\n        shouldTrigger: shouldTriggerAI\\n    }\\n};\",\"notice\":\"\"},\"type\":\"n8n-nodes-base.code\",\"typeVersion\":2,\"position\":[-1776,288],\"id\":\"dcbe63f2-c5bd-4bcb-bd8c-9db30709bf46\",\"name\":\"Trigger Evaluation\"},{\"parameters\":{\"notice\":\"\",\"exchange\":0,\"symbol\":{\"__rl\":true,\"mode\":\"list\",\"value\":\"\"},\"period\":60,\"limit\":500,\"rule\":{\"interval\":[{\"field\":\"seconds\",\"secondsInterval\":60}]}},\"type\":\"n8n-nodes-base.klineCloseTrigger\",\"typeVersion\":1,\"position\":[-2448,288],\"id\":\"2e355c06-05b1-409e-b9d4-dec8fc382d47\",\"name\":\"Kline Trigger\"},{\"parameters\":{\"mode\":\"runOnceForAllItems\",\"language\":\"javaScript\",\"jsCode\":\"var lotSize = $vars.lotSize || 0.001; // Lot size per trade\\n\\nvar grid = _G('grid');\\nvar initPrice = _G('initPrice');\\n\\nif (!initPrice || !grid) {\\n    return {};\\n}\\n\\nfunction checkLongOpen(price) {\\n    for (var i = 0; i < grid.longOpenPrices.length; i++) {\\n        // Price below target open price AND no position at this level\\n        if (price <= grid.longOpenPrices[i] && !grid.longPositions[i].isOpen) {\\n            Log('Open Long')\\n            exchange.SetDirection('buy');\\n            var orderId = exchange.Buy(-1, lotSize);\\n            if (orderId) {\\n                // Record open position info\\n                grid.longPositions[i] = {\\n                    isOpen: true,\\n                    openTime: Date.now(),\\n                    openPrice: price\\n                };\\n                _G('grid', grid);\\n                Log('Open Long Level', i + 1, 'Open Price:', price, 'Target Close Price:', grid.longClosePrices[i]);\\n            }\\n        }\\n    }\\n}\\n\\nfunction checkLongClose(price) {\\n    for (var i = 0; i < grid.longClosePrices.length; i++) {\\n        // Has position AND price reaches target close price\\n        if (grid.longPositions[i].isOpen && price >= grid.longClosePrices[i]) {\\n            Log('Close Long')\\n            exchange.SetDirection('closebuy');\\n            var orderId = exchange.Sell(-1, lotSize);\\n            if (orderId) {\\n                var profit = ((price - grid.longPositions[i].openPrice) / grid.longPositions[i].openPrice * 100).toFixed(2);\\n                Log('Close Long Level', i + 1, 'Open Price:', grid.longPositions[i].openPrice, 'Close Price:', price, 'Profit:', profit + '%');\\n                \\n                // Clear position info\\n                grid.longPositions[i] = {\\n                    isOpen: false,\\n                    openTime: null,\\n                    openPrice: null\\n                };\\n                _G('grid', grid);\\n            }\\n        }\\n    }\\n}\\n\\nfunction checkShortOpen(price) {\\n    for (var i = 0; i < grid.shortOpenPrices.length; i++) {\\n        // Price above target open price AND no position at this level\\n        if (price >= grid.shortOpenPrices[i] && !grid.shortPositions[i].isOpen) {\\n            Log('Open Short')\\n            exchange.SetDirection('sell');\\n            var orderId = exchange.Sell(-1, lotSize);\\n            if (orderId) {\\n                // Record open position info\\n                grid.shortPositions[i] = {\\n                    isOpen: true,\\n                    openTime: Date.now(),\\n                    openPrice: price\\n                };\\n                _G('grid', grid);\\n                Log('Open Short Level', i + 1, 'Open Price:', price, 'Target Close Price:', grid.shortClosePrices[i]);\\n            }\\n        }\\n    }\\n}\\n\\nfunction checkShortClose(price) {\\n    for (var i = 0; i < grid.shortClosePrices.length; i++) {\\n        // Has position AND price reaches target close price\\n        if (grid.shortPositions[i].isOpen && price <= grid.shortClosePrices[i]) {\\n            Log('Close Short')          \\n            exchange.SetDirection('closesell');\\n            var orderId = exchange.Buy(-1, lotSize);\\n            if (orderId) {\\n                var profit = ((grid.shortPositions[i].openPrice - price) / grid.shortPositions[i].openPrice * 100).toFixed(2);\\n                Log('Close Short Level', i + 1, 'Open Price:', grid.shortPositions[i].openPrice, 'Close Price:', price, 'Profit:', profit + '%');\\n                \\n                // Clear position info\\n                grid.shortPositions[i] = {\\n                    isOpen: false,\\n                    openTime: null,\\n                    openPrice: null\\n                };\\n                _G('grid', grid);\\n            }\\n        }\\n    }\\n}\\n\\n// Get current price\\nvar ticker = exchange.GetTicker();\\nif (!ticker) {\\n    Log('Failed to get ticker');\\n    return {};\\n}\\n\\nvar price = ticker.Last;\\n\\n// Check long position open/close\\ncheckLongOpen(price);\\ncheckLongClose(price);\\n\\n// Check short position open/close\\ncheckShortOpen(price);\\ncheckShortClose(price);\\n\\nreturn {};\",\"notice\":\"\"},\"type\":\"n8n-nodes-base.code\",\"typeVersion\":2,\"position\":[-2000,288],\"id\":\"941facc6-379a-4c51-afb5-ef76f3ac43c1\",\"name\":\"Grid Strategy Code\"},{\"parameters\":{\"model\":{\"__rl\":true,\"value\":\"anthropic/claude-sonnet-4.5\",\"mode\":\"list\",\"cachedResultName\":\"anthropic/claude-sonnet-4.5\"}},\"type\":\"n8n-nodes-base.lmOpenAi\",\"typeVersion\":1,\"position\":[-784,608],\"id\":\"1e60139c-1904-4811-b109-88494066fae4\",\"name\":\"OpenAI Model\",\"credentials\":{\"openAiApi\":{\"id\":\"54d0b567-b3fc-4c6a-b6be-546e0b9cd83f\",\"name\":\"openrouter\"}}}],\"pinData\":{},\"connections\":{\"Branch\":{\"main\":[[{\"node\":\"No Operation\",\"type\":\"main\",\"index\":0}],[{\"node\":\"Sentiment News Fetch\",\"type\":\"main\",\"index\":0}]]},\"Parameter Initialization\":{\"main\":[[{\"node\":\"Grid Strategy Code\",\"type\":\"main\",\"index\":0}]]},\"Sentiment News Fetch\":{\"main\":[[{\"node\":\"Result Organization\",\"type\":\"main\",\"index\":0}]]},\"Result Organization\":{\"main\":[[{\"node\":\"AI Parameter Analysis\",\"type\":\"main\",\"index\":0}]]},\"AI Parameter Analysis\":{\"main\":[[{\"node\":\"Reset Strategy\",\"type\":\"main\",\"index\":0}],[{\"node\":\"AI Cooldown\",\"type\":\"main\",\"index\":0}]]},\"Trigger Evaluation\":{\"main\":[[{\"node\":\"Branch\",\"type\":\"main\",\"index\":0}]]},\"Kline Trigger\":{\"main\":[[{\"node\":\"Parameter Initialization\",\"type\":\"main\",\"index\":0}]]},\"Grid Strategy Code\":{\"main\":[[{\"node\":\"Trigger Evaluation\",\"type\":\"main\",\"index\":0}]]},\"OpenAI Model\":{\"ai_languageModel\":[[{\"node\":\"AI Parameter Analysis\",\"type\":\"ai_languageModel\",\"index\":0}]]}},\"active\":false,\"settings\":{\"timezone\":\"Asia/Shanghai\",\"executionOrder\":\"v1\"},\"tags\":[],\"meta\":{\"templateCredsSetupCompleted\":true},\"credentials\":{},\"id\":\"0af0ed8a-5755-402d-b1cd-01c8a7a3fb55\",\"plugins\":{},\"mcpClients\":{\"NEWS_SENTIMENT\":{\"name\":\"NEWS_SENTIMENT\",\"description\":\"Returns live and historical market news & sentiment data from premier news outlets worldwide.\",\"properties\":[{\"displayName\":\"tickers\",\"name\":\"tickers\",\"type\":\"string\",\"description\":\"Stock/crypto/forex symbols to filter articles. Example: \\\"IBM\\\" or \\\"COIN,CRYPTO:BTC,FOREX:USD\\\".\",\"displayOptions\":{\"show\":{\"tool\":[\"NEWS_SENTIMENT\"]}},\"placeholder\":\"Enter tickers\"},{\"displayName\":\"topics\",\"name\":\"topics\",\"type\":\"string\",\"description\":\"News topics to filter by. Example: \\\"technology\\\" or \\\"technology,ipo\\\".\",\"displayOptions\":{\"show\":{\"tool\":[\"NEWS_SENTIMENT\"]}},\"placeholder\":\"Enter topics\"},{\"displayName\":\"time_from\",\"name\":\"time_from\",\"type\":\"string\",\"description\":\"Start time range in YYYYMMDDTHHMM format. Example: \\\"20220410T0130\\\".\",\"displayOptions\":{\"show\":{\"tool\":[\"NEWS_SENTIMENT\"]}},\"placeholder\":\"Enter time_from\"},{\"displayName\":\"time_to\",\"name\":\"time_to\",\"type\":\"string\",\"description\":\"End time range in YYYYMMDDTHHMM format. Defaults to current time if time_from specified.\",\"displayOptions\":{\"show\":{\"tool\":[\"NEWS_SENTIMENT\"]}},\"placeholder\":\"Enter time_to\"},{\"displayName\":\"sort\",\"name\":\"sort\",\"type\":\"string\",\"description\":\"Sort order - \\\"LATEST\\\" (default), \\\"EARLIEST\\\", or \\\"RELEVANCE\\\".\",\"displayOptions\":{\"show\":{\"tool\":[\"NEWS_SENTIMENT\"]}},\"placeholder\":\"Enter sort\"},{\"displayName\":\"limit\",\"name\":\"limit\",\"type\":\"number\",\"description\":\"Number of results to return. Default 50, max 1000.\",\"displayOptions\":{\"show\":{\"tool\":[\"NEWS_SENTIMENT\"]}}}]}}},\"startNodes\":[],\"triggerToStartFrom\":{\"name\":\"Kline Trigger\"}}"}