This strategy employs a workflow architecture that places multiple leading AI large language models within a unified competition framework. Through a “survival of the fittest” mechanism, the system automatically identifies the best-performing decision model and routes its signals to live trade execution.
Multi-Model Competition Framework The system runs multiple state-of-the-art AI language models simultaneously, each acting as an independent virtual trader in competition. Every model maintains its own virtual account (starting with 10,000 USDT) with real-time tracking of positions, profit/loss, win rate, and other performance metrics. Models can observe each other’s rankings, creating a competitive environment that incentivizes more decisive and profit-driven trading behavior.
Multi-Timeframe Technical Analysis The system synchronously collects technical indicators across three timeframes — daily, hourly, and 5-minute — including RSI(14), MACD histogram, ATR, and OBV, combined with the latest price for comprehensive market assessment. AI models use this multi-dimensional data to evaluate trends and identify optimal entry timing.
Standardized Decision Output On an hourly cycle, the workflow triggers automatically. Each AI model independently analyzes current market data and its own competitive standing, then outputs a standardized trading instruction: OPEN_LONG, OPEN_SHORT, CLOSE_LONG, CLOSE_SHORT, or NO_ACTION. The system parses outputs uniformly, supporting varied response formats across different models.
Dynamic Champion-Following Mechanism The system continuously monitors the cumulative P&L of all models. Once any model’s virtual profit exceeds a predefined threshold, the system maps that model’s position state to the live account for execution. If a better-performing model emerges over time, the system automatically switches its following target, ensuring it always tracks the strongest current strategy.
Position Synchronization Logic The live account maintains real-time synchronization with the champion model’s virtual positions: when the model opens long, the live account opens long; when the model closes, the live account closes — faithfully replicating the model’s trading behavior.
Visual Monitoring Dashboard The workflow features a built-in real-time status display with four dynamic tables: model real-time status, performance comparison, live account status, and system-wide statistics — providing an intuitive view of the competitive landscape and live trading activity.
Trade Log Recording Each round of decision results is automatically saved as structured logs, enabling retrospective analysis of each model’s historical decisions and equity curves.
Cryptocurrency perpetual swap markets. The strategy runs on an hourly cycle, suited for high-volatility mainstream cryptocurrency trading pairs.
Vedio Link:Ai Contest Model
{"type":"n8n","content":"{\"workflowData\":{\"nodes\":[{\"parameters\":{\"notice\":\"\",\"rule\":{\"interval\":[{\"field\":\"minutes\",\"minutesInterval\":10}]}},\"type\":\"n8n-nodes-base.scheduleTrigger\",\"typeVersion\":1.2,\"position\":[-832,1008],\"id\":\"51bf46b2-aa9d-473b-9ecb-b46c117ccc7d\",\"name\":\"Schedule Trigger\"},{\"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\":[-64,528],\"id\":\"e446f6ce-c557-446b-9046-ade09823003f\",\"name\":\"Claude4.5\",\"credentials\":{\"openAiApi\":{\"id\":\"54d0b567-b3fc-4c6a-b6be-546e0b9cd83f\",\"name\":\"openrouter\"}}},{\"parameters\":{\"model\":{\"__rl\":true,\"value\":\"deepseek/deepseek-chat-v3.1\",\"mode\":\"list\",\"cachedResultName\":\"deepseek/deepseek-chat-v3.1\"}},\"type\":\"n8n-nodes-base.lmOpenAi\",\"typeVersion\":1,\"position\":[-64,128],\"id\":\"7f39c609-676b-40e2-b025-2df3aaf326b1\",\"name\":\"DS3.1\",\"credentials\":{\"openAiApi\":{\"id\":\"54d0b567-b3fc-4c6a-b6be-546e0b9cd83f\",\"name\":\"openrouter\"}}},{\"parameters\":{\"text\":\"=You are a professional cryptocurrency quantitative trading analyst. Please provide trading decision recommendations based on the following multi-timeframe technical indicators, current position information, and competitive ranking.\\n\\n## Competition Status\\nYou are the {{ $json.ranking.currentModel }} AI model, currently ranked {{ $json.ranking.currentRank }} out of {{ $json.ranking.totalModels }} AI models in the trading competition.\\n{{$json.ranking.currentPnl === 0 && $json.ranking.totalTrades === 0 ? 'Competition just started! This is a great opportunity to establish profit foundation! Actively seek quality trading opportunities in the initial phase!' : ''}}\\n\\n### Current Performance\\n- Your Realized PnL: {{ $json.ranking.currentPnl }} USDT {{ $json.ranking.currentPnl === 0 ? '(Initial State)' : '' }}\\n- Your Total Asset Value: {{ $json.ranking.currentTotalValue }} USDT\\n- Total Trades: {{ $json.ranking.totalTrades }} {{ $json.ranking.totalTrades === 0 ? '(No trades yet)' : '' }}\\n- Win Rate: {{ $json.ranking.winRate }}%{{ $json.ranking.totalTrades === 0 ? ' (No data)' : '' }}\\n\\n### Competitor Rankings\\n{{ $json.ranking.rankingText }}\\n{{ $json.ranking.currentPnl === 0 && $json.ranking.totalTrades === 0 ? 'Competition begins!!!' : $json.ranking.gapInfo}}\\n\\n## Current Data Overview\\n- Trading Pair: {{ $json.symbol }}\\n- Analysis Time: {{ new Date($json.timestamp).toLocaleString() }}\\n- Current Position: {{ $json.position && $json.position.length > 0 ? 'Has Position' : 'No Position' }}\\n\\n## Technical Indicators Data (OLDEST-NEWEST)\\n### Daily Timeframe (Trend Analysis)\\n- Price: {{ $json.timeframes.day?.lastPrice || 'N/A' }}\\n- RSI(14): {{ $json.timeframes.day?.RSI || 'N/A' }} \\n- MACD Histogram: {{ $json.timeframes.day?.MACD.histogram || 'N/A' }} \\n- ATR: {{ $json.timeframes.day?.ATR || 'N/A' }} \\n- OBV: {{ $json.timeframes.day?.OBV || 'N/A' }} \\n\\n### Hourly Timeframe (Mid-term Trend)\\n- Price: {{ $json.timeframes.hour?.lastPrice || 'N/A' }}\\n- RSI(14): {{ $json.timeframes.hour?.RSI || 'N/A' }}\\n- MACD Histogram: {{ $json.timeframes.hour?.MACD.histogram || 'N/A' }}\\n- ATR: {{ $json.timeframes.hour?.ATR || 'N/A' }}\\n- OBV: {{ $json.timeframes.hour?.OBV || 'N/A' }}\\n\\n### 5-Minute Timeframe (Entry Timing)\\n- Price: {{ $json.timeframes.min5?.lastPrice || 'N/A' }}\\n- RSI(14): {{ $json.timeframes.min5?.RSI || 'N/A' }}\\n- MACD Histogram: {{ $json.timeframes.min5?.MACD.histogram || 'N/A' }}\\n- ATR: {{ $json.timeframes.min5?.ATR || 'N/A' }}\\n- OBV: {{ $json.timeframes.min5?.OBV || 'N/A' }}\\n\\n### Current Position Info\\n{{JSON.stringify($json.position, null, 2)}}\\n\\n## Analysis Requirements\\nDevelop optimal trading strategy based on technical indicators and your competitive ranking. Your goal is to continuously create more profit, not simply maintain ranking. {{ $json.ranking.totalTrades === 0 ? 'As the competition starts, actively seek high-quality trading opportunities to establish profit foundation.' : 'Continue seeking quality trading opportunities to expand profits, avoid being overly conservative and missing profit chances.' }}\\n\\n### Strategy Recommendations:\\n{{ $json.ranking.totalTrades === 0 ? '- Competition just started, actively find best entry timing to establish profit foundation' : $json.ranking.currentRank > 1 ? '- You are currently behind, actively seek more quality trading opportunities to catch up and create profits' : '- You are performing well, continue active trading to expand profit advantage' }}\\n- Carefully analyze multi-timeframe indicator consistency\\n- Execute trades decisively when technical signals are clear\\n- {{ $json.ranking.totalTrades === 0 ? 'First trade should establish good start' : 'Continue seeking profit opportunities, avoid being overly conservative' }}\\n- Focus on risk-reward ratio, not ranking protection\\n\\n### Executable Actions:\\n- **OPEN_LONG**: Open long position (when no position)\\n- **OPEN_SHORT**: Open short position (when no position) \\n- **CLOSE_LONG**: Close long position (when holding long)\\n- **CLOSE_SHORT**: Close short position (when holding short)\\n- **NO_ACTION**: Wait and watch (when technical signals unclear)\\n\\n### Execution Criteria:\\n- Prioritize profit creation opportunities\\n- {{ $json.ranking.totalTrades === 0 ? 'First trade requires establishing profit foundation' : 'Continue seeking high-quality trading opportunities to expand total profits' }}\\n- Balance risk control with profit pursuit\\n- Only choose NO_ACTION when technical signals clearly conflict\\n\\n## Return Format\\n```json\\n{\\n \\\"action\\\": \\\"OPEN_LONG | OPEN_SHORT | CLOSE_LONG | CLOSE_SHORT | NO_ACTION\\\",\\n \\\"reasoning\\\": \\\"Concise decision rationale combining technical indicators and profit creation goals\\\",\\n \\\"modelname\\\": \\\"{{ $json.ranking.currentModel }}\\\"\\n}\\n```\\nProvide direct judgment.\",\"options\":{}},\"type\":\"@n8n/n8n-nodes-langchain.agent\",\"typeVersion\":1,\"position\":[-160,-96],\"id\":\"ccff925e-e79a-4865-a204-6348cb37ac5f\",\"name\":\"DeepSeek\",\"retryOnFail\":true,\"onError\":\"continueRegularOutput\"},{\"parameters\":{\"text\":\"=You are a professional cryptocurrency quantitative trading analyst. Please provide trading decision recommendations based on the following multi-timeframe technical indicators, current position information, and competitive ranking.\\n\\n## Competition Status\\nYou are the {{ $json.ranking.currentModel }} AI model, currently ranked {{ $json.ranking.currentRank }} out of {{ $json.ranking.totalModels }} AI models in the trading competition.\\n{{$json.ranking.currentPnl === 0 && $json.ranking.totalTrades === 0 ? 'Competition just started! This is a great opportunity to establish profit foundation! Actively seek quality trading opportunities in the initial phase!' : ''}}\\n\\n### Current Performance\\n- Your Realized PnL: {{ $json.ranking.currentPnl }} USDT {{ $json.ranking.currentPnl === 0 ? '(Initial State)' : '' }}\\n- Your Total Asset Value: {{ $json.ranking.currentTotalValue }} USDT\\n- Total Trades: {{ $json.ranking.totalTrades }} {{ $json.ranking.totalTrades === 0 ? '(No trades yet)' : '' }}\\n- Win Rate: {{ $json.ranking.winRate }}%{{ $json.ranking.totalTrades === 0 ? ' (No data)' : '' }}\\n\\n### Competitor Rankings\\n{{ $json.ranking.rankingText }}\\n{{ $json.ranking.currentPnl === 0 && $json.ranking.totalTrades === 0 ? 'Competition begins!!!' : $json.ranking.gapInfo}}\\n\\n## Current Data Overview\\n- Trading Pair: {{ $json.symbol }}\\n- Analysis Time: {{ new Date($json.timestamp).toLocaleString() }}\\n- Current Position: {{ $json.position && $json.position.length > 0 ? 'Has Position' : 'No Position' }}\\n\\n## Technical Indicators Data (OLDEST-NEWEST)\\n### Daily Timeframe (Trend Analysis)\\n- Price: {{ $json.timeframes.day?.lastPrice || 'N/A' }}\\n- RSI(14): {{ $json.timeframes.day?.RSI || 'N/A' }} \\n- MACD Histogram: {{ $json.timeframes.day?.MACD.histogram || 'N/A' }} \\n- ATR: {{ $json.timeframes.day?.ATR || 'N/A' }} \\n- OBV: {{ $json.timeframes.day?.OBV || 'N/A' }} \\n\\n### Hourly Timeframe (Mid-term Trend)\\n- Price: {{ $json.timeframes.hour?.lastPrice || 'N/A' }}\\n- RSI(14): {{ $json.timeframes.hour?.RSI || 'N/A' }}\\n- MACD Histogram: {{ $json.timeframes.hour?.MACD.histogram || 'N/A' }}\\n- ATR: {{ $json.timeframes.hour?.ATR || 'N/A' }}\\n- OBV: {{ $json.timeframes.hour?.OBV || 'N/A' }}\\n\\n### 5-Minute Timeframe (Entry Timing)\\n- Price: {{ $json.timeframes.min5?.lastPrice || 'N/A' }}\\n- RSI(14): {{ $json.timeframes.min5?.RSI || 'N/A' }}\\n- MACD Histogram: {{ $json.timeframes.min5?.MACD.histogram || 'N/A' }}\\n- ATR: {{ $json.timeframes.min5?.ATR || 'N/A' }}\\n- OBV: {{ $json.timeframes.min5?.OBV || 'N/A' }}\\n\\n### Current Position Info\\n{{JSON.stringify($json.position, null, 2)}}\\n\\n## Analysis Requirements\\nDevelop optimal trading strategy based on technical indicators and your competitive ranking. Your goal is to continuously create more profit, not simply maintain ranking. {{ $json.ranking.totalTrades === 0 ? 'As the competition starts, actively seek high-quality trading opportunities to establish profit foundation.' : 'Continue seeking quality trading opportunities to expand profits, avoid being overly conservative and missing profit chances.' }}\\n\\n### Strategy Recommendations:\\n{{ $json.ranking.totalTrades === 0 ? '- Competition just started, actively find best entry timing to establish profit foundation' : $json.ranking.currentRank > 1 ? '- You are currently behind, actively seek more quality trading opportunities to catch up and create profits' : '- You are performing well, continue active trading to expand profit advantage' }}\\n- Carefully analyze multi-timeframe indicator consistency\\n- Execute trades decisively when technical signals are clear\\n- {{ $json.ranking.totalTrades === 0 ? 'First trade should establish good start' : 'Continue seeking profit opportunities, avoid being overly conservative' }}\\n- Focus on risk-reward ratio, not ranking protection\\n\\n### Executable Actions:\\n- **OPEN_LONG**: Open long position (when no position)\\n- **OPEN_SHORT**: Open short position (when no position) \\n- **CLOSE_LONG**: Close long position (when holding long)\\n- **CLOSE_SHORT**: Close short position (when holding short)\\n- **NO_ACTION**: Wait and watch (when technical signals unclear)\\n\\n### Execution Criteria:\\n- Prioritize profit creation opportunities\\n- {{ $json.ranking.totalTrades === 0 ? 'First trade requires establishing profit foundation' : 'Continue seeking high-quality trading opportunities to expand total profits' }}\\n- Balance risk control with profit pursuit\\n- Only choose NO_ACTION when technical signals clearly conflict\\n\\n## Return Format\\n```json\\n{\\n \\\"action\\\": \\\"OPEN_LONG | OPEN_SHORT | CLOSE_LONG | CLOSE_SHORT | NO_ACTION\\\",\\n \\\"reasoning\\\": \\\"Concise decision rationale combining technical indicators and profit creation goals\\\",\\n \\\"modelname\\\": \\\"{{ $json.ranking.currentModel }}\\\"\\n}\\n```\\nProvide direct judgment.\",\"options\":{}},\"type\":\"@n8n/n8n-nodes-langchain.agent\",\"typeVersion\":1,\"position\":[-160,304],\"id\":\"b0a0814f-d64b-4bb5-b1e2-bc05b03f9be3\",\"name\":\"Claude\",\"retryOnFail\":true,\"onError\":\"continueRegularOutput\"},{\"parameters\":{\"text\":\"=You are a professional cryptocurrency quantitative trading analyst. Please provide trading decision recommendations based on the following multi-timeframe technical indicators, current position information, and competitive ranking.\\n\\n## Competition Status\\nYou are the {{ $json.ranking.currentModel }} AI model, currently ranked {{ $json.ranking.currentRank }} out of {{ $json.ranking.totalModels }} AI models in the trading competition.\\n{{$json.ranking.currentPnl === 0 && $json.ranking.totalTrades === 0 ? 'Competition just started! This is a great opportunity to establish profit foundation! Actively seek quality trading opportunities in the initial phase!' : ''}}\\n\\n### Current Performance\\n- Your Realized PnL: {{ $json.ranking.currentPnl }} USDT {{ $json.ranking.currentPnl === 0 ? '(Initial State)' : '' }}\\n- Your Total Asset Value: {{ $json.ranking.currentTotalValue }} USDT\\n- Total Trades: {{ $json.ranking.totalTrades }} {{ $json.ranking.totalTrades === 0 ? '(No trades yet)' : '' }}\\n- Win Rate: {{ $json.ranking.winRate }}%{{ $json.ranking.totalTrades === 0 ? ' (No data)' : '' }}\\n\\n### Competitor Rankings\\n{{ $json.ranking.rankingText }}\\n{{ $json.ranking.currentPnl === 0 && $json.ranking.totalTrades === 0 ? 'Competition begins!!!' : $json.ranking.gapInfo}}\\n\\n## Current Data Overview\\n- Trading Pair: {{ $json.symbol }}\\n- Analysis Time: {{ new Date($json.timestamp).toLocaleString() }}\\n- Current Position: {{ $json.position && $json.position.length > 0 ? 'Has Position' : 'No Position' }}\\n\\n## Technical Indicators Data (OLDEST-NEWEST)\\n### Daily Timeframe (Trend Analysis)\\n- Price: {{ $json.timeframes.day?.lastPrice || 'N/A' }}\\n- RSI(14): {{ $json.timeframes.day?.RSI || 'N/A' }} \\n- MACD Histogram: {{ $json.timeframes.day?.MACD.histogram || 'N/A' }} \\n- ATR: {{ $json.timeframes.day?.ATR || 'N/A' }} \\n- OBV: {{ $json.timeframes.day?.OBV || 'N/A' }} \\n\\n### Hourly Timeframe (Mid-term Trend)\\n- Price: {{ $json.timeframes.hour?.lastPrice || 'N/A' }}\\n- RSI(14): {{ $json.timeframes.hour?.RSI || 'N/A' }}\\n- MACD Histogram: {{ $json.timeframes.hour?.MACD.histogram || 'N/A' }}\\n- ATR: {{ $json.timeframes.hour?.ATR || 'N/A' }}\\n- OBV: {{ $json.timeframes.hour?.OBV || 'N/A' }}\\n\\n### 5-Minute Timeframe (Entry Timing)\\n- Price: {{ $json.timeframes.min5?.lastPrice || 'N/A' }}\\n- RSI(14): {{ $json.timeframes.min5?.RSI || 'N/A' }}\\n- MACD Histogram: {{ $json.timeframes.min5?.MACD.histogram || 'N/A' }}\\n- ATR: {{ $json.timeframes.min5?.ATR || 'N/A' }}\\n- OBV: {{ $json.timeframes.min5?.OBV || 'N/A' }}\\n\\n### Current Position Info\\n{{JSON.stringify($json.position, null, 2)}}\\n\\n## Analysis Requirements\\nDevelop optimal trading strategy based on technical indicators and your competitive ranking. Your goal is to continuously create more profit, not simply maintain ranking. {{ $json.ranking.totalTrades === 0 ? 'As the competition starts, actively seek high-quality trading opportunities to establish profit foundation.' : 'Continue seeking quality trading opportunities to expand profits, avoid being overly conservative and missing profit chances.' }}\\n\\n### Strategy Recommendations:\\n{{ $json.ranking.totalTrades === 0 ? '- Competition just started, actively find best entry timing to establish profit foundation' : $json.ranking.currentRank > 1 ? '- You are currently behind, actively seek more quality trading opportunities to catch up and create profits' : '- You are performing well, continue active trading to expand profit advantage' }}\\n- Carefully analyze multi-timeframe indicator consistency\\n- Execute trades decisively when technical signals are clear\\n- {{ $json.ranking.totalTrades === 0 ? 'First trade should establish good start' : 'Continue seeking profit opportunities, avoid being overly conservative' }}\\n- Focus on risk-reward ratio, not ranking protection\\n\\n### Executable Actions:\\n- **OPEN_LONG**: Open long position (when no position)\\n- **OPEN_SHORT**: Open short position (when no position) \\n- **CLOSE_LONG**: Close long position (when holding long)\\n- **CLOSE_SHORT**: Close short position (when holding short)\\n- **NO_ACTION**: Wait and watch (when technical signals unclear)\\n\\n### Execution Criteria:\\n- Prioritize profit creation opportunities\\n- {{ $json.ranking.totalTrades === 0 ? 'First trade requires establishing profit foundation' : 'Continue seeking high-quality trading opportunities to expand total profits' }}\\n- Balance risk control with profit pursuit\\n- Only choose NO_ACTION when technical signals clearly conflict\\n\\n## Return Format\\n```json\\n{\\n \\\"action\\\": \\\"OPEN_LONG | OPEN_SHORT | CLOSE_LONG | CLOSE_SHORT | NO_ACTION\\\",\\n \\\"reasoning\\\": \\\"Concise decision rationale combining technical indicators and profit creation goals\\\",\\n \\\"modelname\\\": \\\"{{ $json.ranking.currentModel }}\\\"\\n}\\n```\\nProvide direct judgment.\",\"options\":{}},\"type\":\"@n8n/n8n-nodes-langchain.agent\",\"typeVersion\":1,\"position\":[-160,704],\"id\":\"c0b74a78-1494-4495-ac64-3bd4858fc3a3\",\"name\":\"QWEN\",\"retryOnFail\":true,\"onError\":\"continueRegularOutput\"},{\"parameters\":{\"model\":{\"__rl\":true,\"value\":\"qwen/qwen3-max\",\"mode\":\"list\",\"cachedResultName\":\"qwen/qwen3-max\"}},\"type\":\"n8n-nodes-base.lmOpenAi\",\"typeVersion\":1,\"position\":[-64,928],\"id\":\"4df0f0ac-9736-46ff-aeeb-5f1ddbdea9c9\",\"name\":\"OpenAI Model\",\"credentials\":{\"openAiApi\":{\"id\":\"54d0b567-b3fc-4c6a-b6be-546e0b9cd83f\",\"name\":\"openrouter\"}}},{\"parameters\":{\"text\":\"=You are a professional cryptocurrency quantitative trading analyst. Please provide trading decision recommendations based on the following multi-timeframe technical indicators, current position information, and competitive ranking.\\n\\n## Competition Status\\nYou are the {{ $json.ranking.currentModel }} AI model, currently ranked {{ $json.ranking.currentRank }} out of {{ $json.ranking.totalModels }} AI models in the trading competition.\\n{{$json.ranking.currentPnl === 0 && $json.ranking.totalTrades === 0 ? 'Competition just started! This is a great opportunity to establish profit foundation! Actively seek quality trading opportunities in the initial phase!' : ''}}\\n\\n### Current Performance\\n- Your Realized PnL: {{ $json.ranking.currentPnl }} USDT {{ $json.ranking.currentPnl === 0 ? '(Initial State)' : '' }}\\n- Your Total Asset Value: {{ $json.ranking.currentTotalValue }} USDT\\n- Total Trades: {{ $json.ranking.totalTrades }} {{ $json.ranking.totalTrades === 0 ? '(No trades yet)' : '' }}\\n- Win Rate: {{ $json.ranking.winRate }}%{{ $json.ranking.totalTrades === 0 ? ' (No data)' : '' }}\\n\\n### Competitor Rankings\\n{{ $json.ranking.rankingText }}\\n{{ $json.ranking.currentPnl === 0 && $json.ranking.totalTrades === 0 ? 'Competition begins!!!' : $json.ranking.gapInfo}}\\n\\n## Current Data Overview\\n- Trading Pair: {{ $json.symbol }}\\n- Analysis Time: {{ new Date($json.timestamp).toLocaleString() }}\\n- Current Position: {{ $json.position && $json.position.length > 0 ? 'Has Position' : 'No Position' }}\\n\\n## Technical Indicators Data (OLDEST-NEWEST)\\n### Daily Timeframe (Trend Analysis)\\n- Price: {{ $json.timeframes.day?.lastPrice || 'N/A' }}\\n- RSI(14): {{ $json.timeframes.day?.RSI || 'N/A' }} \\n- MACD Histogram: {{ $json.timeframes.day?.MACD.histogram || 'N/A' }} \\n- ATR: {{ $json.timeframes.day?.ATR || 'N/A' }} \\n- OBV: {{ $json.timeframes.day?.OBV || 'N/A' }} \\n\\n### Hourly Timeframe (Mid-term Trend)\\n- Price: {{ $json.timeframes.hour?.lastPrice || 'N/A' }}\\n- RSI(14): {{ $json.timeframes.hour?.RSI || 'N/A' }}\\n- MACD Histogram: {{ $json.timeframes.hour?.MACD.histogram || 'N/A' }}\\n- ATR: {{ $json.timeframes.hour?.ATR || 'N/A' }}\\n- OBV: {{ $json.timeframes.hour?.OBV || 'N/A' }}\\n\\n### 5-Minute Timeframe (Entry Timing)\\n- Price: {{ $json.timeframes.min5?.lastPrice || 'N/A' }}\\n- RSI(14): {{ $json.timeframes.min5?.RSI || 'N/A' }}\\n- MACD Histogram: {{ $json.timeframes.min5?.MACD.histogram || 'N/A' }}\\n- ATR: {{ $json.timeframes.min5?.ATR || 'N/A' }}\\n- OBV: {{ $json.timeframes.min5?.OBV || 'N/A' }}\\n\\n### Current Position Info\\n{{JSON.stringify($json.position, null, 2)}}\\n\\n## Analysis Requirements\\nDevelop optimal trading strategy based on technical indicators and your competitive ranking. Your goal is to continuously create more profit, not simply maintain ranking. {{ $json.ranking.totalTrades === 0 ? 'As the competition starts, actively seek high-quality trading opportunities to establish profit foundation.' : 'Continue seeking quality trading opportunities to expand profits, avoid being overly conservative and missing profit chances.' }}\\n\\n### Strategy Recommendations:\\n{{ $json.ranking.totalTrades === 0 ? '- Competition just started, actively find best entry timing to establish profit foundation' : $json.ranking.currentRank > 1 ? '- You are currently behind, actively seek more quality trading opportunities to catch up and create profits' : '- You are performing well, continue active trading to expand profit advantage' }}\\n- Carefully analyze multi-timeframe indicator consistency\\n- Execute trades decisively when technical signals are clear\\n- {{ $json.ranking.totalTrades === 0 ? 'First trade should establish good start' : 'Continue seeking profit opportunities, avoid being overly conservative' }}\\n- Focus on risk-reward ratio, not ranking protection\\n\\n### Executable Actions:\\n- **OPEN_LONG**: Open long position (when no position)\\n- **OPEN_SHORT**: Open short position (when no position) \\n- **CLOSE_LONG**: Close long position (when holding long)\\n- **CLOSE_SHORT**: Close short position (when holding short)\\n- **NO_ACTION**: Wait and watch (when technical signals unclear)\\n\\n### Execution Criteria:\\n- Prioritize profit creation opportunities\\n- {{ $json.ranking.totalTrades === 0 ? 'First trade requires establishing profit foundation' : 'Continue seeking high-quality trading opportunities to expand total profits' }}\\n- Balance risk control with profit pursuit\\n- Only choose NO_ACTION when technical signals clearly conflict\\n\\n## Return Format\\n```json\\n{\\n \\\"action\\\": \\\"OPEN_LONG | OPEN_SHORT | CLOSE_LONG | CLOSE_SHORT | NO_ACTION\\\",\\n \\\"reasoning\\\": \\\"Concise decision rationale combining technical indicators and profit creation goals\\\",\\n \\\"modelname\\\": \\\"{{ $json.ranking.currentModel }}\\\"\\n}\\n```\\nProvide direct judgment.\",\"options\":{}},\"type\":\"@n8n/n8n-nodes-langchain.agent\",\"typeVersion\":1,\"position\":[-160,1104],\"id\":\"310d7b46-2333-42c5-9c75-753f6f2e2529\",\"name\":\"Grok\",\"retryOnFail\":true,\"onError\":\"continueRegularOutput\"},{\"parameters\":{\"model\":{\"__rl\":true,\"value\":\"x-ai/grok-4\",\"mode\":\"list\",\"cachedResultName\":\"x-ai/grok-4\"}},\"type\":\"n8n-nodes-base.lmOpenAi\",\"typeVersion\":1,\"position\":[-64,1328],\"id\":\"3dabb427-58b9-47b0-ac28-6fb1df083605\",\"name\":\"Grok4\",\"credentials\":{\"openAiApi\":{\"id\":\"54d0b567-b3fc-4c6a-b6be-546e0b9cd83f\",\"name\":\"openrouter\"}}},{\"parameters\":{\"mode\":\"append\",\"numberInputs\":4},\"type\":\"n8n-nodes-base.merge\",\"typeVersion\":3.2,\"position\":[240,944],\"id\":\"ddae2251-7d25-4bee-aacd-f67ab3d8dbf0\",\"name\":\"Merge\"},{\"parameters\":{\"operation\":\"csv\",\"binaryPropertyName\":\"data\",\"options\":{}},\"type\":\"n8n-nodes-base.convertToFile\",\"typeVersion\":1.1,\"position\":[912,1104],\"id\":\"ec0705d4-47ee-4201-8019-3a1b0979b7e3\",\"name\":\"Convert to File\"},{\"parameters\":{\"info\":\"\",\"fileName\":\"tradelog.csv\",\"dataPropertyName\":\"data\",\"options\":{}},\"type\":\"n8n-nodes-base.writeFile\",\"typeVersion\":1,\"position\":[1136,1104],\"id\":\"d67538cf-24e2-47b3-9301-15067aaadcea\",\"name\":\"Write to File\"},{\"parameters\":{\"mode\":\"runOnceForAllItems\",\"language\":\"javaScript\",\"jsCode\":\"// Get AI decision results and clean data\\nconst aiResults = $input.all().map(item => {\\n if (item.json && item.json.output) {\\n return { output: item.json.output };\\n } else if (item.output) {\\n return { output: item.output };\\n }\\n return null;\\n}).filter(item => item !== null);\\n\\nexchange.SetContractType('swap') \\nconst ticker = _C(exchange.GetTicker);\\nconst fixedSize = $vars.size || 0.01; // Fixed trading size\\n\\nif (!ticker || !ticker.Last) {\\n Log(\\\"Failed to get price\\\");\\n return $input.all();\\n}\\n\\nconst currentPrice = ticker.Last;\\nconst timestamp = Date.now();\\n\\n// Get contract value\\nconst markets = _G('markets');\\nconst ctVal = markets ? markets.CtVal : 1;\\n\\n// Get model data - use deep copy to avoid modifying original data\\nconst models = JSON.parse(JSON.stringify($node['Strategy Init'].json));\\n\\n// Process each AI's decision\\naiResults.forEach(item => {\\n Log('Current item:', item)\\n if (!item.output){\\n return;\\n }\\n \\n try {\\n let decision;\\n \\n // Try to extract JSON wrapped in markdown\\n const jsonMatch = item.output.match(/```json\\\\s*([\\\\s\\\\S]*?)\\\\s*```/);\\n \\n if (jsonMatch) {\\n // Claude format: ```json ... ```\\n decision = JSON.parse(jsonMatch[1]);\\n } else {\\n // DS format: direct JSON\\n decision = JSON.parse(item.output);\\n }\\n \\n const modelKey = decision.modelname;\\n\\n if (!models || !models[modelKey]) {\\n Log(`Model not found: ${modelKey}`);\\n return;\\n }\\n \\n const model = models[modelKey];\\n \\n // Record decision\\n model.decisions.push({\\n timestamp,\\n action: decision.action,\\n reasoning: decision.reasoning,\\n price: currentPrice\\n });\\n \\n // ============ Virtual Position Trading Logic ============\\n \\n if (decision.action === 'OPEN_LONG') {\\n // Check if already has position (virtual position)\\n if (model.positions.length === 0) {\\n model.positions.push({\\n side: 'long',\\n size: fixedSize,\\n entryPrice: currentPrice,\\n ctVal: ctVal,\\n timestamp: timestamp\\n });\\n \\n model.account.totalTrades++;\\n \\n model.tradeHistory.push({\\n action: 'OPEN_LONG',\\n price: currentPrice,\\n size: fixedSize,\\n ctVal: ctVal,\\n timestamp: timestamp\\n });\\n \\n Log(`${modelKey} Virtual open long: ${currentPrice}, size: ${fixedSize}`);\\n }\\n }\\n \\n else if (decision.action === 'OPEN_SHORT') {\\n // Check if already has position (virtual position)\\n if (model.positions.length === 0) {\\n model.positions.push({\\n side: 'short',\\n size: fixedSize,\\n entryPrice: currentPrice,\\n ctVal: ctVal,\\n timestamp: timestamp\\n });\\n \\n model.account.totalTrades++;\\n \\n model.tradeHistory.push({\\n action: 'OPEN_SHORT',\\n price: currentPrice,\\n size: fixedSize,\\n ctVal: ctVal,\\n timestamp: timestamp\\n });\\n \\n Log(`${modelKey} Virtual open short: ${currentPrice}, size: ${fixedSize}`);\\n }\\n }\\n \\n else if (decision.action === 'CLOSE_LONG' || decision.action === 'CLOSE_SHORT') {\\n // Close position (virtual position)\\n const targetSide = decision.action === 'CLOSE_LONG' ? 'long' : 'short';\\n const posIndex = model.positions.findIndex(pos => pos.side === targetSide);\\n \\n if (posIndex !== -1) {\\n const position = model.positions[posIndex];\\n \\n // ✅ Fix: Calculate absolute PnL (don't divide by entryPrice)\\n const pnl = position.side === 'long' \\n ? (currentPrice - position.entryPrice) * position.size * position.ctVal\\n : (position.entryPrice - currentPrice) * position.size * position.ctVal;\\n \\n // Opening and closing fees\\n const openFee = position.size * position.ctVal * 0.001;\\n const closeFee = position.size * position.ctVal * 0.001;\\n const totalFee = openFee + closeFee;\\n \\n const netPnl = pnl - totalFee;\\n \\n // Accumulate to total PnL\\n model.account.realizedPnl += netPnl;\\n \\n if (netPnl > 0) model.account.winTrades++;\\n \\n model.tradeHistory.push({\\n action: decision.action,\\n price: currentPrice,\\n size: position.size,\\n ctVal: position.ctVal,\\n entryPrice: position.entryPrice,\\n pnl: netPnl,\\n fee: totalFee,\\n timestamp: timestamp\\n });\\n \\n Log(`${modelKey} Virtual close: ${currentPrice}, PnL: ${netPnl.toFixed(2)}`);\\n \\n // Remove virtual position\\n model.positions.splice(posIndex, 1);\\n }\\n }\\n \\n // ✅ Fix: Calculate unrealized PnL (don't divide by entryPrice)\\n let unrealizedPnl = 0;\\n model.positions.forEach(pos => {\\n if (pos.side === 'long') {\\n unrealizedPnl += (currentPrice - pos.entryPrice) * pos.size * pos.ctVal;\\n } else {\\n unrealizedPnl += (pos.entryPrice - currentPrice) * pos.size * pos.ctVal;\\n }\\n });\\n \\n model.account.totalValue = model.account.initialBalance + model.account.realizedPnl + unrealizedPnl;\\n model.account.balance = model.account.totalValue;\\n \\n } catch (error) {\\n Log(`Error processing decision: ${error.message}`);\\n Log(`Original output: ${item.output}`);\\n }\\n});\\n\\n// Save updated models to _G\\n_G('models', models);\\n\\nreturn models;\",\"notice\":\"\"},\"type\":\"n8n-nodes-base.code\",\"typeVersion\":2,\"position\":[464,1008],\"id\":\"43ca06e2-2047-4005-bdb9-3aaa810881e8\",\"name\":\"Result Calculation\"},{\"parameters\":{\"mode\":\"runOnceForAllItems\",\"language\":\"javaScript\",\"jsCode\":\"// Extract and format all models' trading data\\nconst inputData = $input.all()[0].json;\\n\\n// ✅ Automatically get model list from data\\nconst models = Object.keys(inputData);\\n\\n// Build result array\\nconst results = models.map(model => {\\n const data = inputData[model];\\n \\n // ✅ Safety check\\n if (!data || !data.account) {\\n Log(`Warning: Model ${model} data incomplete, skipping`);\\n return null;\\n }\\n \\n // Get latest decision\\n const latestDecision = data.decisions && data.decisions.length > 0 \\n ? data.decisions[data.decisions.length - 1] \\n : null;\\n \\n // Get current position\\n const currentPosition = data.positions && data.positions.length > 0\\n ? data.positions[0]\\n : null;\\n \\n return {\\n // Model name\\n model: model,\\n \\n // Account info\\n initialBalance: data.account.initialBalance,\\n balance: data.account.balance,\\n totalValue: data.account.totalValue,\\n realizedPnl: data.account.realizedPnl,\\n totalTrades: data.account.totalTrades,\\n winTrades: data.account.winTrades,\\n winRate: data.account.totalTrades > 0 \\n ? ((data.account.winTrades / data.account.totalTrades) * 100).toFixed(2) + '%'\\n : '0%',\\n \\n // Position info\\n hasPosition: currentPosition ? 'YES' : 'NO',\\n positionType: currentPosition?.type || '-',\\n positionSize: currentPosition?.size || 0,\\n entryPrice: currentPosition?.entryPrice || 0,\\n currentPrice: currentPosition?.currentPrice || 0,\\n unrealizedPnl: currentPosition?.unrealizedPnl || 0,\\n \\n // Latest decision\\n latestAction: latestDecision?.action || 'NO_DECISION',\\n latestPrice: latestDecision?.price || 0,\\n latestTimestamp: latestDecision?.timestamp || 0,\\n latestReasoning: latestDecision?.reasoning || '-',\\n \\n // Trade history stats\\n tradeHistoryCount: data.tradeHistory.length,\\n decisionsCount: data.decisions.length,\\n \\n // Add timestamp\\n recordTime: new Date().toISOString()\\n };\\n}).filter(item => item !== null); // ✅ Filter invalid data\\n\\nlet tradelog = _G('tradelog') || [];\\n\\n// Add current records for all models\\ntradelog.push({\\n timestamp: new Date().toISOString(),\\n models: results\\n});\\n\\n_G('tradelog', tradelog);\\n\\n// Return data for next node\\nreturn tradelog;\",\"notice\":\"\"},\"type\":\"n8n-nodes-base.code\",\"typeVersion\":2,\"position\":[688,1008],\"id\":\"afd7d308-7491-4bc0-959b-71f74271452e\",\"name\":\"Trade Log Save\"},{\"parameters\":{\"mode\":\"runOnceForAllItems\",\"language\":\"javaScript\",\"jsCode\":\"// Get all model data\\nconst inputData = $node['Result Calculation'].json;\\n\\n// Get currently active model for real trading (if any)\\nlet activeModel = _G('activeModel') || null;\\nlet highestPnl = _G('highestPnl') || 15; // Initial threshold\\n\\nLog('highestPnl:', highestPnl)\\n\\n// Iterate through all models to find best performing model\\nlet bestModel = null;\\nlet bestPnl = highestPnl;\\n\\nObject.keys(inputData).forEach(modelName => {\\n const model = inputData[modelName];\\n const realizedPnl = model.account.realizedPnl;\\n \\n // Find model with highest PnL\\n if (realizedPnl > bestPnl) {\\n bestPnl = realizedPnl;\\n bestModel = modelName;\\n }\\n});\\n\\n// Decision logic\\nlet shouldTrade = false;\\nlet tradeAction = null;\\nlet modelChanged = false;\\n\\nif (bestModel) {\\n // If no active model and found profitable model\\n if (!activeModel) {\\n activeModel = bestModel;\\n highestPnl = bestPnl;\\n modelChanged = true;\\n Log(`🎯 Start real trading! Selected model: ${activeModel}, PnL: ${bestPnl.toFixed(2)} USDT`);\\n } \\n // If current model is not the best, switch to best model\\n else if (bestModel !== activeModel) {\\n Log(`🔄 Model switch: ${activeModel} (${highestPnl.toFixed(2)}) -> ${bestModel} (${bestPnl.toFixed(2)})`);\\n activeModel = bestModel;\\n highestPnl = bestPnl;\\n modelChanged = true;\\n }\\n \\n // Get paper position status of active model\\n const activeModelData = inputData[activeModel];\\n const paperPositions = activeModelData.positions || [];\\n \\n // Get real position status\\n const realPositions = exchange.GetPosition();\\n const hasRealLong = realPositions && realPositions.some(p => p.Type === PD_LONG && p.Amount > 0);\\n const hasRealShort = realPositions && realPositions.some(p => p.Type === PD_SHORT && p.Amount > 0);\\n \\n // Paper position status\\n const hasPaperLong = paperPositions.some(p => p.side === 'long');\\n const hasPaperShort = paperPositions.some(p => p.side === 'short');\\n \\n // Sync logic: make real position match paper position\\n if (hasPaperLong && !hasRealLong) {\\n // Paper has long, real doesn't have long\\n if (hasRealShort) {\\n // If real has short, close short first\\n shouldTrade = true;\\n tradeAction = {\\n model: activeModel,\\n action: 'CLOSE_SHORT',\\n reason: 'Sync position: close short first',\\n paperPosition: paperPositions[0],\\n modelPnl: activeModelData.account.realizedPnl\\n };\\n } else {\\n // Real has no position, open long\\n shouldTrade = true;\\n tradeAction = {\\n model: activeModel,\\n action: 'OPEN_LONG',\\n reason: 'Sync position: open long',\\n paperPosition: paperPositions[0],\\n modelPnl: activeModelData.account.realizedPnl\\n };\\n }\\n } \\n else if (hasPaperShort && !hasRealShort) {\\n // Paper has short, real doesn't have short\\n if (hasRealLong) {\\n // If real has long, close long first\\n shouldTrade = true;\\n tradeAction = {\\n model: activeModel,\\n action: 'CLOSE_LONG',\\n reason: 'Sync position: close long first',\\n paperPosition: paperPositions[0],\\n modelPnl: activeModelData.account.realizedPnl\\n };\\n } else {\\n // Real has no position, open short\\n shouldTrade = true;\\n tradeAction = {\\n model: activeModel,\\n action: 'OPEN_SHORT',\\n reason: 'Sync position: open short',\\n paperPosition: paperPositions[0],\\n modelPnl: activeModelData.account.realizedPnl\\n };\\n }\\n }\\n else if (!hasPaperLong && !hasPaperShort && (hasRealLong || hasRealShort)) {\\n // Paper has no position but real has position, need to close\\n if (hasRealLong) {\\n shouldTrade = true;\\n tradeAction = {\\n model: activeModel,\\n action: 'CLOSE_LONG',\\n reason: 'Sync position: paper already closed',\\n modelPnl: activeModelData.account.realizedPnl\\n };\\n } else if (hasRealShort) {\\n shouldTrade = true;\\n tradeAction = {\\n model: activeModel,\\n action: 'CLOSE_SHORT',\\n reason: 'Sync position: paper already closed',\\n modelPnl: activeModelData.account.realizedPnl\\n };\\n }\\n }\\n}\\n\\n// Save state\\n_G('activeModel', activeModel);\\n_G('highestPnl', highestPnl);\\n\\n// Execute real trade\\nif (shouldTrade && tradeAction) {\\n const symbol = $vars.coin + \\\"_USDT.swap\\\";\\n const size = $vars.size || 0.01;\\n \\n Log(`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`);\\n Log(`🤖 Real trade execution (sync paper position)`);\\n Log(`Model: ${tradeAction.model}`);\\n Log(`Action: ${tradeAction.action}`);\\n Log(`Reason: ${tradeAction.reason}`);\\n Log(`Size: ${size}`);\\n Log(`Model PnL: ${tradeAction.modelPnl.toFixed(2)} USDT`);\\n if (tradeAction.paperPosition) {\\n Log(`Paper position: ${tradeAction.paperPosition.side}, entry price: ${tradeAction.paperPosition.entryPrice}`);\\n }\\n Log(`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`);\\n \\n try {\\n // Get current position\\n const positions = exchange.GetPosition();\\n const hasLongPosition = positions && positions.some(p => p.Type === PD_LONG && p.Amount > 0);\\n const hasShortPosition = positions && positions.some(p => p.Type === PD_SHORT && p.Amount > 0);\\n \\n // Execute trade\\n if (tradeAction.action === 'OPEN_LONG' && !hasLongPosition && !hasShortPosition) {\\n exchange.SetDirection(\\\"buy\\\");\\n const orderId = exchange.Buy(-1, size);\\n Log(`✅ Open long success, order ID: ${orderId}`);\\n } \\n else if (tradeAction.action === 'OPEN_SHORT' && !hasLongPosition && !hasShortPosition) {\\n exchange.SetDirection(\\\"sell\\\");\\n const orderId = exchange.Sell(-1, size);\\n Log(`✅ Open short success, order ID: ${orderId}`);\\n }\\n else if (tradeAction.action === 'CLOSE_LONG' && hasLongPosition) {\\n const longPos = positions.find(p => p.Type === PD_LONG && p.Amount > 0);\\n exchange.SetDirection(\\\"closebuy\\\");\\n const orderId = exchange.Sell(-1, longPos.Amount);\\n Log(`✅ Close long success, order ID: ${orderId}`);\\n }\\n else if (tradeAction.action === 'CLOSE_SHORT' && hasShortPosition) {\\n const shortPos = positions.find(p => p.Type === PD_SHORT && p.Amount > 0);\\n exchange.SetDirection(\\\"closesell\\\");\\n const orderId = exchange.Buy(-1, shortPos.Amount);\\n Log(`✅ Close short success, order ID: ${orderId}`);\\n }\\n else {\\n Log(`⚠️ Cannot execute trade: position status mismatch`);\\n }\\n } catch (error) {\\n Log(`❌ Trade execution failed: ${error.message}`);\\n }\\n}\\n\\n// Return status info\\nreturn [{\\n json: {\\n activeModel: activeModel,\\n highestPnl: highestPnl,\\n shouldTrade: shouldTrade,\\n modelChanged: modelChanged,\\n tradeAction: tradeAction,\\n allModelsStatus: Object.keys(inputData).map(modelName => {\\n const model = inputData[modelName];\\n return {\\n model: modelName,\\n realizedPnl: model.account.realizedPnl,\\n totalValue: model.account.totalValue,\\n isActive: modelName === activeModel,\\n paperPosition: model.positions.length > 0 ? model.positions[0].side : 'none'\\n };\\n }).sort((a, b) => b.realizedPnl - a.realizedPnl)\\n }\\n}];\",\"notice\":\"\"},\"type\":\"n8n-nodes-base.code\",\"typeVersion\":2,\"position\":[912,912],\"id\":\"0c0712b8-3144-4516-855f-3b30876b7f0f\",\"name\":\"Best Trade\"},{\"parameters\":{\"mode\":\"runOnceForAllItems\",\"language\":\"javaScript\",\"jsCode\":\"api_base = \\\"https://testnet.binancefuture.com\\\"\\nexchange.SetBase(api_base)\\n\\n// Get and split model list\\nconst modelListStr = $vars.modelList || '';\\nconst modelList = modelListStr.split(',').filter(name => name.trim());\\n\\nif (modelList.length === 0) {\\n return {};\\n}\\n\\n// Initialize global container\\nif (!_G('models')) {\\n _G('models', {});\\n _G('tradelog')\\n\\n let allMarkets = null;\\n for (let i = 0; i < 5; i++) {\\n allMarkets = exchange.GetMarkets();\\n if (allMarkets && Object.keys(allMarkets).length > 0) {\\n break;\\n }\\n Sleep(1000);\\n }\\n \\n const symbol = $vars.coin + '_USDT.swap';\\n _G('markets', allMarkets[symbol]);\\n Log(_G('markets'))\\n \\n const initAccount = exchange.GetAccount();\\n _G('initmoney', initAccount.Balance);\\n}\\n\\n// Create trading system for each model\\nmodelList.forEach(modelName => {\\n const key = modelName.trim();\\n const models = _G('models');\\n \\n if (!models[key]) {\\n models[key] = {\\n account: {\\n initialBalance: 10000, // Add initial capital\\n balance: 10000,\\n totalValue: 10000,\\n realizedPnl: 0,\\n totalTrades: 0,\\n winTrades: 0\\n },\\n positions: [],\\n tradeHistory: [],\\n decisions: []\\n };\\n _G('models', models);\\n }\\n});\\n\\nreturn _G('models');\",\"notice\":\"\"},\"type\":\"n8n-nodes-base.code\",\"typeVersion\":2,\"position\":[-608,1008],\"id\":\"33ff8eab-dc47-4794-8667-55e0ea29db25\",\"name\":\"Strategy Init\"},{\"parameters\":{\"mode\":\"runOnceForAllItems\",\"language\":\"javaScript\",\"jsCode\":\"// ========== AI Model Competition Visualization Dashboard ==========\\n\\n/**\\n * Safe formatting helper functions\\n */\\nfunction safeFormat(value, decimals = 2, prefix = '') {\\n if (value === null || value === undefined || isNaN(value)) {\\n return prefix + '0';\\n }\\n return prefix + _N(value, decimals);\\n}\\n\\nfunction safePercentage(numerator, denominator, decimals = 1) {\\n if (!denominator || denominator === 0) return '0%';\\n const result = (numerator / denominator) * 100;\\n return safeFormat(result, decimals) + '%';\\n}\\n\\n/**\\n * Table 1: Model Running Status\\n */\\nfunction createModelStatusTable(allModelsStatus, models) {\\n const statusTable = {\\n type: \\\"table\\\",\\n title: \\\"🤖 AI Model Real-time Status\\\",\\n cols: [\\\"Model\\\", \\\"Active\\\", \\\"Initial\\\", \\\"Current\\\", \\\"Realized\\\", \\\"Profit%\\\", \\\"Paper Pos\\\", \\\"Trades\\\", \\\"Win%\\\", \\\"Latest Decision\\\", \\\"Decision Price\\\", \\\"Reasoning\\\"],\\n rows: []\\n };\\n \\n try {\\n allModelsStatus.forEach(model => {\\n const modelData = models[model.model];\\n if (!modelData) {\\n Log(`Warning: Model data not found ${model.model}`);\\n return;\\n }\\n \\n const latestDecision = modelData.decisions && modelData.decisions.length > 0\\n ? modelData.decisions[modelData.decisions.length - 1]\\n : null;\\n \\n const profitRate = ((modelData.account.realizedPnl || 0) / (modelData.account.initialBalance || 1)) * 100;\\n \\n // Profit rate display\\n let profitRateDisplay = \\\"\\\";\\n if (profitRate > 5) {\\n profitRateDisplay = `🔥 +${safeFormat(profitRate)}%`;\\n } else if (profitRate > 0) {\\n profitRateDisplay = `🟢 +${safeFormat(profitRate)}%`;\\n } else if (profitRate < 0) {\\n profitRateDisplay = `🔴 ${safeFormat(profitRate)}%`;\\n } else {\\n profitRateDisplay = `⚪ 0%`;\\n }\\n \\n // Position display\\n let positionDisplay = \\\"\\\";\\n if (model.paperPosition === 'none') {\\n positionDisplay = \\\"📭 Empty\\\";\\n } else if (model.paperPosition === 'long') {\\n positionDisplay = \\\"📈 Long\\\";\\n } else {\\n positionDisplay = \\\"📉 Short\\\";\\n }\\n \\n // Decision display\\n let actionDisplay = latestDecision?.action || \\\"None\\\";\\n switch(actionDisplay) {\\n case 'OPEN_LONG':\\n actionDisplay = \\\"🟢 Open Long\\\";\\n break;\\n case 'OPEN_SHORT':\\n actionDisplay = \\\"🔴 Open Short\\\";\\n break;\\n case 'CLOSE_LONG':\\n actionDisplay = \\\"⏹️ Close Long\\\";\\n break;\\n case 'CLOSE_SHORT':\\n actionDisplay = \\\"⏹️ Close Short\\\";\\n break;\\n case 'NO_ACTION':\\n actionDisplay = \\\"⏸️ Wait\\\";\\n break;\\n }\\n \\n const realizedPnl = modelData.account.realizedPnl || 0;\\n let pnlDisplay = \\\"\\\";\\n if (realizedPnl > 0) {\\n pnlDisplay = `🟢 ${safeFormat(realizedPnl, 2, \\\"$\\\")}`;\\n } else if (realizedPnl < 0) {\\n pnlDisplay = `🔴 ${safeFormat(realizedPnl, 2, \\\"$\\\")}`;\\n } else {\\n pnlDisplay = `⚪ $0`;\\n }\\n \\n statusTable.rows.push([\\n `${model.isActive ? '⭐' : '💎'} ${model.model}`,\\n model.isActive ? \\\"✅\\\" : \\\"❌\\\",\\n safeFormat(modelData.account.initialBalance, 2, \\\"$\\\"),\\n safeFormat(modelData.account.totalValue, 2, \\\"$\\\"),\\n pnlDisplay,\\n profitRateDisplay,\\n positionDisplay,\\n modelData.account.totalTrades || 0,\\n safePercentage(modelData.account.winTrades || 0, modelData.account.totalTrades || 1),\\n actionDisplay,\\n latestDecision?.price ? safeFormat(latestDecision.price, 2, \\\"$\\\") : '-',\\n latestDecision?.reasoning ? latestDecision.reasoning : '-'\\n ]);\\n });\\n \\n } catch (e) {\\n Log(`❌ Create model status table failed: ${e.message}`);\\n Log(`Error stack: ${e.stack}`);\\n statusTable.rows.push([\\n \\\"❌ Error\\\", \\\"Failed to get data\\\", \\\"-\\\", \\\"-\\\", \\\"-\\\", \\\"-\\\", \\\"-\\\", \\\"-\\\", \\\"-\\\", \\\"-\\\", \\\"-\\\", e.message\\n ]);\\n }\\n \\n return statusTable;\\n}\\n\\n/**\\n * Table 2: Model Performance Comparison\\n */\\nfunction createPerformanceComparisonTable(allModelsStatus, models) {\\n const perfTable = {\\n type: \\\"table\\\",\\n title: \\\"📊 AI Model Performance Comparison (Sorted by PnL)\\\",\\n cols: [\\\"Rank\\\", \\\"Model\\\", \\\"Realized\\\", \\\"Total Assets\\\", \\\"Max Profit\\\", \\\"Max Loss\\\", \\\"Max DD\\\", \\\"Sharpe\\\", \\\"Profit/Loss\\\", \\\"Trades\\\", \\\"Win%\\\", \\\"Avg Win\\\", \\\"Avg Loss\\\"],\\n rows: []\\n };\\n \\n try {\\n const performanceData = allModelsStatus.map((model, index) => {\\n const modelData = models[model.model];\\n if (!modelData) {\\n return null;\\n }\\n \\n const tradeHistory = modelData.tradeHistory || [];\\n \\n // Filter closed trades (records with pnl)\\n const closedTrades = tradeHistory.filter(trade => \\n trade.pnl !== undefined && trade.pnl !== null\\n );\\n \\n // Calculate max profit\\n const maxProfit = closedTrades.length > 0\\n ? Math.max(...closedTrades.map(t => t.pnl || 0))\\n : 0;\\n \\n // Calculate max loss\\n const maxLoss = closedTrades.length > 0\\n ? Math.min(...closedTrades.map(t => t.pnl || 0))\\n : 0;\\n \\n // Calculate max drawdown\\n let maxDrawdown = 0;\\n let peak = modelData.account.initialBalance || 10000;\\n let cumulativePnl = 0;\\n \\n closedTrades.forEach(trade => {\\n cumulativePnl += (trade.pnl || 0);\\n const currentValue = (modelData.account.initialBalance || 10000) + cumulativePnl;\\n \\n if (currentValue > peak) {\\n peak = currentValue;\\n }\\n \\n const drawdown = peak > 0 ? ((peak - currentValue) / peak) * 100 : 0;\\n if (drawdown > maxDrawdown) {\\n maxDrawdown = drawdown;\\n }\\n });\\n \\n // Calculate Sharpe ratio\\n let sharpeRatio = 0;\\n if (closedTrades.length > 1) {\\n const initialBalance = modelData.account.initialBalance || 10000;\\n const returns = closedTrades.map(t => (t.pnl || 0) / initialBalance);\\n const avgReturn = returns.reduce((a, b) => a + b, 0) / returns.length;\\n \\n const variance = returns.reduce((sum, r) => sum + Math.pow(r - avgReturn, 2), 0) / returns.length;\\n const stdDev = Math.sqrt(variance);\\n \\n sharpeRatio = stdDev !== 0 ? (avgReturn / stdDev) : 0;\\n }\\n \\n // Profit/Loss ratio\\n const winTrades = closedTrades.filter(t => (t.pnl || 0) > 0);\\n const lossTrades = closedTrades.filter(t => (t.pnl || 0) < 0);\\n \\n const avgWin = winTrades.length > 0\\n ? winTrades.reduce((sum, t) => sum + (t.pnl || 0), 0) / winTrades.length\\n : 0;\\n \\n const avgLoss = lossTrades.length > 0\\n ? Math.abs(lossTrades.reduce((sum, t) => sum + (t.pnl || 0), 0) / lossTrades.length)\\n : 0;\\n \\n const profitLossRatio = avgLoss !== 0 ? (avgWin / avgLoss) : 0;\\n \\n return {\\n model: model.model,\\n rank: index + 1,\\n realizedPnl: modelData.account.realizedPnl || 0,\\n totalValue: modelData.account.totalValue || 0,\\n maxProfit: maxProfit,\\n maxLoss: maxLoss,\\n maxDrawdown: maxDrawdown,\\n sharpeRatio: sharpeRatio,\\n profitLossRatio: profitLossRatio,\\n totalTrades: closedTrades.length,\\n winRate: (modelData.account.totalTrades || 0) > 0 \\n ? (((modelData.account.winTrades || 0) / modelData.account.totalTrades) * 100)\\n : 0,\\n avgWin: avgWin,\\n avgLoss: avgLoss\\n };\\n }).filter(perf => perf !== null).sort((a, b) => b.realizedPnl - a.realizedPnl);\\n \\n performanceData.forEach((perf, index) => {\\n // Rank display\\n let rankDisplay = \\\"\\\";\\n if (index === 0) {\\n rankDisplay = \\\"🥇\\\";\\n } else if (index === 1) {\\n rankDisplay = \\\"🥈\\\";\\n } else if (index === 2) {\\n rankDisplay = \\\"🥉\\\";\\n } else {\\n rankDisplay = `${index + 1}`;\\n }\\n \\n // Sharpe ratio display\\n let sharpeDisplay = \\\"\\\";\\n if (perf.sharpeRatio > 2) {\\n sharpeDisplay = `🔥 ${safeFormat(perf.sharpeRatio)}`;\\n } else if (perf.sharpeRatio > 1) {\\n sharpeDisplay = `✅ ${safeFormat(perf.sharpeRatio)}`;\\n } else if (perf.sharpeRatio > 0) {\\n sharpeDisplay = `⚡ ${safeFormat(perf.sharpeRatio)}`;\\n } else {\\n sharpeDisplay = `⚠️ ${safeFormat(perf.sharpeRatio)}`;\\n }\\n \\n perfTable.rows.push([\\n rankDisplay,\\n `💎 ${perf.model}`,\\n perf.realizedPnl > 0 \\n ? `🟢 ${safeFormat(perf.realizedPnl, 2, \\\"$\\\")}`\\n : perf.realizedPnl < 0\\n ? `🔴 ${safeFormat(perf.realizedPnl, 2, \\\"$\\\")}`\\n : `⚪ $0`,\\n safeFormat(perf.totalValue, 2, \\\"$\\\"),\\n perf.maxProfit > 0 ? `🟢 ${safeFormat(perf.maxProfit, 2, \\\"$\\\")}` : '$0',\\n perf.maxLoss < 0 ? `🔴 ${safeFormat(perf.maxLoss, 2, \\\"$\\\")}` : '$0',\\n perf.maxDrawdown > 0 ? `⚠️ ${safeFormat(perf.maxDrawdown)}%` : '0%',\\n sharpeDisplay,\\n perf.profitLossRatio > 0 ? safeFormat(perf.profitLossRatio) : '0',\\n perf.totalTrades,\\n `${safeFormat(perf.winRate, 1)}%`,\\n perf.avgWin > 0 ? safeFormat(perf.avgWin, 2, \\\"$\\\") : '$0',\\n perf.avgLoss > 0 ? safeFormat(perf.avgLoss, 2, \\\"$\\\") : '$0'\\n ]);\\n });\\n \\n // Add summary row\\n if (performanceData.length > 0) {\\n const avgPnl = performanceData.reduce((sum, p) => sum + p.realizedPnl, 0) / performanceData.length;\\n const avgSharpe = performanceData.reduce((sum, p) => sum + p.sharpeRatio, 0) / performanceData.length;\\n const totalTrades = performanceData.reduce((sum, p) => sum + p.totalTrades, 0);\\n \\n perfTable.rows.push([\\n \\\"📊\\\",\\n \\\"Average\\\",\\n avgPnl > 0 ? `🟢 ${safeFormat(avgPnl, 2, \\\"$\\\")}` : avgPnl < 0 ? `🔴 ${safeFormat(avgPnl, 2, \\\"$\\\")}` : `⚪ $0`,\\n \\\"-\\\",\\n \\\"-\\\",\\n \\\"-\\\",\\n \\\"-\\\",\\n safeFormat(avgSharpe),\\n \\\"-\\\",\\n totalTrades,\\n \\\"-\\\",\\n \\\"-\\\",\\n \\\"-\\\"\\n ]);\\n }\\n \\n } catch (e) {\\n Log(`❌ Create performance comparison table failed: ${e.message}`);\\n Log(`Error stack: ${e.stack}`);\\n perfTable.rows.push([\\n \\\"❌\\\", \\\"Error\\\", \\\"Failed to get data\\\", \\\"-\\\", \\\"-\\\", \\\"-\\\", \\\"-\\\", \\\"-\\\", \\\"-\\\", \\\"-\\\", \\\"-\\\", \\\"-\\\", e.message\\n ]);\\n }\\n \\n return perfTable;\\n}\\n\\n/**\\n * Table 3: Real Trading Status\\n */\\nfunction createRealTradingTable(inputData, models) {\\n const realTable = {\\n type: \\\"table\\\",\\n title: \\\"💼 Real Trading Status\\\",\\n cols: [\\\"Item\\\", \\\"Value\\\", \\\"Status\\\", \\\"Note\\\"],\\n rows: []\\n };\\n \\n try {\\n const activeModel = inputData.activeModel;\\n const realAccount = exchange.GetAccount();\\n const realPositions = exchange.GetPosition();\\n \\n // Get real position info\\n let realPositionInfo = {\\n type: 'None',\\n size: 0,\\n entryPrice: 0,\\n currentPrice: 0,\\n unrealizedPnl: 0\\n };\\n \\n if (realPositions && realPositions.length > 0) {\\n const pos = realPositions[0];\\n const ticker = exchange.GetTicker();\\n const currentPrice = ticker ? ticker.Last : 0;\\n \\n realPositionInfo = {\\n type: pos.Type === PD_LONG ? 'Long' : 'Short',\\n size: pos.Amount || 0,\\n entryPrice: pos.Price || 0,\\n currentPrice: currentPrice,\\n unrealizedPnl: pos.Profit || 0\\n };\\n }\\n \\n // Calculate real account cumulative profit\\n const initMoney = _G('initmoney') || 10000;\\n const currentBalance = realAccount ? (realAccount.Balance || 0) : 0;\\n const currentEquity = realAccount ? (realAccount.Equity || 0) : 0;\\n const realizedProfit = currentBalance - initMoney;\\n const totalProfit = currentEquity - initMoney;\\n const returnRate = initMoney > 0 ? ((totalProfit / initMoney) * 100) : 0;\\n\\n LogProfit(totalProfit, \\\"&\\\");\\n \\n // Active model info\\n realTable.rows.push([\\n \\\"🤖 Active Model\\\",\\n activeModel || \\\"Not Started\\\",\\n activeModel ? \\\"✅ Running\\\" : \\\"⏸️ Standby\\\",\\n activeModel && models[activeModel] \\n ? `PnL: ${safeFormat(models[activeModel].account.realizedPnl, 2, \\\"$\\\")}` \\n : \\\"Wait for profit > threshold to trigger\\\"\\n ]);\\n \\n // Account info\\n realTable.rows.push([\\n \\\"💰 Initial Capital\\\",\\n safeFormat(initMoney, 2, \\\"$\\\"),\\n \\\"📊\\\",\\n \\\"Real account start amount\\\"\\n ]);\\n \\n realTable.rows.push([\\n \\\"💵 Current Balance\\\",\\n safeFormat(currentBalance, 2, \\\"$\\\"),\\n \\\"📊\\\",\\n \\\"Available funds\\\"\\n ]);\\n \\n realTable.rows.push([\\n \\\"💎 Account Equity\\\",\\n safeFormat(currentEquity, 2, \\\"$\\\"),\\n \\\"📊\\\",\\n \\\"Balance + Position PnL\\\"\\n ]);\\n \\n // PnL info\\n let profitDisplay = \\\"\\\";\\n let profitStatus = \\\"\\\";\\n if (totalProfit > 0) {\\n profitDisplay = `🟢 +${safeFormat(totalProfit, 2, \\\"$\\\")}`;\\n profitStatus = \\\"✅ Profitable\\\";\\n } else if (totalProfit < 0) {\\n profitDisplay = `🔴 ${safeFormat(totalProfit, 2, \\\"$\\\")}`;\\n profitStatus = \\\"⚠️ Loss\\\";\\n } else {\\n profitDisplay = `⚪ $0`;\\n profitStatus = \\\"⚪ Breakeven\\\";\\n }\\n \\n realTable.rows.push([\\n \\\"📈 Total PnL\\\",\\n profitDisplay,\\n profitStatus,\\n `Return: ${returnRate > 0 ? '+' : ''}${safeFormat(returnRate)}%`\\n ]);\\n \\n // Position info\\n realTable.rows.push([\\n \\\"💼 Current Position\\\",\\n realPositionInfo.type === 'None' ? \\\"📭 Empty\\\" : `${realPositionInfo.type === 'Long' ? '📈' : '📉'} ${realPositionInfo.type}`,\\n realPositionInfo.type === 'None' ? \\\"⚪ No Position\\\" : \\\"✅ In Position\\\",\\n realPositionInfo.type === 'None' ? \\\"-\\\" : `Size: ${safeFormat(realPositionInfo.size, 4)}`\\n ]);\\n \\n if (realPositionInfo.type !== 'None') {\\n realTable.rows.push([\\n \\\"💰 Entry Price\\\",\\n safeFormat(realPositionInfo.entryPrice, 4, \\\"$\\\"),\\n \\\"📊\\\",\\n `Current: ${safeFormat(realPositionInfo.currentPrice, 4, \\\"$\\\")}`\\n ]);\\n \\n let positionPnlDisplay = \\\"\\\";\\n let positionPnlStatus = \\\"\\\";\\n if (realPositionInfo.unrealizedPnl > 0) {\\n positionPnlDisplay = `🟢 +${safeFormat(realPositionInfo.unrealizedPnl, 2, \\\"$\\\")}`;\\n positionPnlStatus = \\\"✅ Floating Profit\\\";\\n } else if (realPositionInfo.unrealizedPnl < 0) {\\n positionPnlDisplay = `🔴 ${safeFormat(realPositionInfo.unrealizedPnl, 2, \\\"$\\\")}`;\\n positionPnlStatus = \\\"⚠️ Floating Loss\\\";\\n } else {\\n positionPnlDisplay = `⚪ $0`;\\n positionPnlStatus = \\\"⚪ Breakeven\\\";\\n }\\n \\n realTable.rows.push([\\n \\\"📊 Position PnL\\\",\\n positionPnlDisplay,\\n positionPnlStatus,\\n \\\"Unrealized PnL\\\"\\n ]);\\n }\\n \\n // Sync status\\n realTable.rows.push([\\n \\\"🔄 Sync Status\\\",\\n inputData.shouldTrade ? \\\"Pending Sync\\\" : \\\"Synced\\\",\\n inputData.shouldTrade ? \\\"🔄 Executing\\\" : \\\"✅ Synced\\\",\\n inputData.tradeAction?.reason || \\\"Paper and real match\\\"\\n ]);\\n \\n // Last trade\\n if (inputData.tradeAction) {\\n let actionDisplay = inputData.tradeAction.action;\\n switch(actionDisplay) {\\n case 'OPEN_LONG':\\n actionDisplay = \\\"🟢 Open Long\\\";\\n break;\\n case 'OPEN_SHORT':\\n actionDisplay = \\\"🔴 Open Short\\\";\\n break;\\n case 'CLOSE_LONG':\\n actionDisplay = \\\"⏹️ Close Long\\\";\\n break;\\n case 'CLOSE_SHORT':\\n actionDisplay = \\\"⏹️ Close Short\\\";\\n break;\\n }\\n \\n realTable.rows.push([\\n \\\"📝 Last Trade\\\",\\n actionDisplay,\\n \\\"📊\\\",\\n inputData.tradeAction.reason || \\\"-\\\"\\n ]);\\n }\\n \\n // Model comparison\\n if (activeModel && models[activeModel]) {\\n const modelPnl = models[activeModel].account.realizedPnl || 0;\\n const gap = realizedProfit - modelPnl;\\n \\n realTable.rows.push([\\n \\\"📊 Gap vs Model\\\",\\n gap > 0 ? `🟢 Ahead ${safeFormat(gap, 2, \\\"$\\\")}` : gap < 0 ? `🔴 Behind ${safeFormat(Math.abs(gap), 2, \\\"$\\\")}` : `⚪ Match`,\\n gap > 0 ? \\\"✅ Leading\\\" : gap < 0 ? \\\"⚠️ Catching Up\\\" : \\\"⚪ Synced\\\",\\n `Model Paper: ${safeFormat(modelPnl, 2, \\\"$\\\")}`\\n ]);\\n }\\n \\n } catch (e) {\\n Log(`❌ Create real trading table failed: ${e.message}`);\\n Log(`Error stack: ${e.stack}`);\\n realTable.rows.push([\\n \\\"❌ Error\\\",\\n \\\"Failed to get data\\\",\\n \\\"🚨\\\",\\n e.message\\n ]);\\n }\\n \\n return realTable;\\n}\\n\\n/**\\n * Table 4: System Statistics\\n */\\nfunction createSystemStatsTable(allModelsStatus, models, inputData) {\\n const statsTable = {\\n type: \\\"table\\\",\\n title: \\\"📊 System Overall Statistics\\\",\\n cols: [\\\"Metric\\\", \\\"Value\\\", \\\"Rating\\\", \\\"Note\\\"],\\n rows: []\\n };\\n \\n try {\\n // Total models\\n statsTable.rows.push([\\n \\\"🤖 Competing Models\\\",\\n `${allModelsStatus.length} models`,\\n \\\"📊\\\",\\n \\\"AI Model Competition\\\"\\n ]);\\n \\n // Best model\\n if (allModelsStatus.length > 0) {\\n const bestModel = allModelsStatus[0];\\n statsTable.rows.push([\\n \\\"🥇 Best Model\\\",\\n bestModel.model,\\n bestModel.realizedPnl > 0 ? \\\"✅ Profitable\\\" : \\\"⚠️ Loss\\\",\\n `PnL: ${safeFormat(bestModel.realizedPnl, 2, \\\"$\\\")}`\\n ]);\\n }\\n \\n // Average performance\\n if (allModelsStatus.length > 0) {\\n const avgPnl = allModelsStatus.reduce((sum, m) => sum + (m.realizedPnl || 0), 0) / allModelsStatus.length;\\n statsTable.rows.push([\\n \\\"📊 Average PnL\\\",\\n avgPnl > 0 ? `🟢 ${safeFormat(avgPnl, 2, \\\"$\\\")}` : avgPnl < 0 ? `🔴 ${safeFormat(avgPnl, 2, \\\"$\\\")}` : `⚪ $0`,\\n avgPnl > 0 ? \\\"✅ Positive\\\" : \\\"⚠️ Negative\\\",\\n \\\"All models average\\\"\\n ]);\\n }\\n \\n // Profitable models count\\n const profitableModels = allModelsStatus.filter(m => (m.realizedPnl || 0) > 0).length;\\n statsTable.rows.push([\\n \\\"✅ Profitable Models\\\",\\n `${profitableModels} / ${allModelsStatus.length}`,\\n profitableModels > allModelsStatus.length / 2 ? \\\"✅ Good\\\" : \\\"⚠️ Low\\\",\\n `Ratio: ${safePercentage(profitableModels, allModelsStatus.length)}`\\n ]);\\n \\n // Total trades\\n const totalTrades = allModelsStatus.reduce((sum, m) => {\\n const modelData = models[m.model];\\n return sum + (modelData ? (modelData.account.totalTrades || 0) : 0);\\n }, 0);\\n \\n statsTable.rows.push([\\n \\\"🎲 Total Trades\\\",\\n totalTrades,\\n totalTrades > 50 ? \\\"✅ Sufficient\\\" : totalTrades > 10 ? \\\"✅ Moderate\\\" : \\\"⚠️ Low\\\",\\n `All models combined`\\n ]);\\n \\n // Average win rate\\n let totalWinRate = 0;\\n let validModels = 0;\\n allModelsStatus.forEach(m => {\\n const modelData = models[m.model];\\n if (modelData && (modelData.account.totalTrades || 0) > 0) {\\n totalWinRate += ((modelData.account.winTrades || 0) / modelData.account.totalTrades) * 100;\\n validModels++;\\n }\\n });\\n const avgWinRate = validModels > 0 ? totalWinRate / validModels : 0;\\n \\n statsTable.rows.push([\\n \\\"🎯 Average Win Rate\\\",\\n `${safeFormat(avgWinRate, 1)}%`,\\n avgWinRate > 60 ? \\\"🔥 Excellent\\\" : avgWinRate > 45 ? \\\"✅ Good\\\" : \\\"⚠️ Need Improvement\\\",\\n \\\"Valid trading models average\\\"\\n ]);\\n \\n // Models with position\\n const modelsWithPosition = allModelsStatus.filter(m => m.paperPosition !== 'none').length;\\n statsTable.rows.push([\\n \\\"💼 Models in Position\\\",\\n `${modelsWithPosition} / ${allModelsStatus.length}`,\\n modelsWithPosition > 0 ? \\\"✅ Active\\\" : \\\"📭 Empty\\\",\\n `Ratio: ${safePercentage(modelsWithPosition, allModelsStatus.length)}`\\n ]);\\n \\n // Real trading status\\n const isRealTrading = inputData.activeModel ? true : false;\\n statsTable.rows.push([\\n \\\"🚀 Real Trading Status\\\",\\n isRealTrading ? \\\"Running\\\" : \\\"Not Started\\\",\\n isRealTrading ? \\\"✅ Active\\\" : \\\"⏸️ Standby\\\",\\n isRealTrading ? `Following: ${inputData.activeModel}` : \\\"Wait for profit>500\\\"\\n ]);\\n \\n // Last update time\\n const updateTime = new Date().toLocaleString();\\n statsTable.rows.push([\\n \\\"🕐 Update Time\\\",\\n updateTime,\\n \\\"📊\\\",\\n \\\"Real-time monitoring\\\"\\n ]);\\n \\n } catch (e) {\\n Log(`❌ Create stats table failed: ${e.message}`);\\n Log(`Error stack: ${e.stack}`);\\n statsTable.rows.push([\\n \\\"❌ Error\\\",\\n \\\"Calculation failed\\\",\\n \\\"🚨\\\",\\n e.message\\n ]);\\n }\\n \\n return statsTable;\\n}\\n\\n/**\\n * Main function - Display complete dashboard\\n */\\nfunction main() {\\n try {\\n Log('Visualization node execution started');\\n \\n // Get input data\\n const inputData = $input.all()[0].json;\\n Log('inputData keys:', Object.keys(inputData));\\n \\n const allModelsStatus = inputData.allModelsStatus;\\n if (!allModelsStatus || !Array.isArray(allModelsStatus)) {\\n throw new Error('allModelsStatus data invalid or not an array');\\n }\\n \\n Log(`Model count: ${allModelsStatus.length}`);\\n \\n // Get detailed model data\\n const models = $node['Result Calculation'].json;\\n if (!models) {\\n throw new Error('Unable to get Result Calculation node data');\\n }\\n \\n Log('models keys:', Object.keys(models));\\n \\n // Create 4 tables\\n const table1 = createModelStatusTable(allModelsStatus, models);\\n const table2 = createPerformanceComparisonTable(allModelsStatus, models);\\n const table3 = createRealTradingTable(inputData, models);\\n const table4 = createSystemStatsTable(allModelsStatus, models, inputData);\\n \\n // Combined display\\n const dashboardDisplay = \\n '`' + JSON.stringify(table1) + '`\\\\n\\\\n' +\\n '`' + JSON.stringify(table2) + '`\\\\n\\\\n' +\\n '`' + JSON.stringify(table3) + '`\\\\n\\\\n' +\\n '`' + JSON.stringify(table4) + '`';\\n \\n LogStatus(dashboardDisplay);\\n \\n Log('✅ Visualization display success');\\n \\n // Return complete data\\n return [{\\n json: {\\n ...inputData,\\n tables: {\\n modelStatus: table1,\\n performance: table2,\\n realTrading: table3,\\n systemStats: table4\\n },\\n timestamp: Date.now(),\\n updateTime: new Date().toLocaleString()\\n }\\n }];\\n \\n } catch (e) {\\n Log(`❌ Display dashboard failed: ${e.message}`);\\n Log(`❌ Error stack: ${e.stack}`);\\n \\n // Show simple status on error\\n const errorTable = {\\n type: \\\"table\\\",\\n title: \\\"❌ System Error\\\",\\n cols: [\\\"Error Type\\\", \\\"Error Message\\\", \\\"Time\\\"],\\n rows: [[\\n \\\"Visualization Error\\\",\\n e.message,\\n new Date().toLocaleString()\\n ]]\\n };\\n \\n LogStatus('`' + JSON.stringify(errorTable) + '`');\\n \\n return [{\\n json: {\\n error: e.message,\\n stack: e.stack,\\n timestamp: Date.now()\\n }\\n }];\\n }\\n}\\n\\n// Execute main function\\nreturn main();\",\"notice\":\"\"},\"type\":\"n8n-nodes-base.code\",\"typeVersion\":2,\"position\":[1136,912],\"id\":\"939c1c0f-3c4d-43ab-be31-c2f1ece3b06c\",\"name\":\"Visualization\"},{\"parameters\":{\"mode\":\"runOnceForAllItems\",\"language\":\"javaScript\",\"jsCode\":\"const symbol = $vars.coin + \\\"_USDT.swap\\\"\\nconst timeframes = {\\n day: PERIOD_D1,\\n hour: PERIOD_H1,\\n min5: PERIOD_M5\\n}\\n\\nfunction getIndicatorsForPeriod(symbol, period, periodName) {\\n const records = exchange.GetRecords(symbol, period)\\n \\n if (!records || records.length <= 10) {\\n Log(`Warning: ${periodName} period insufficient data, only ${records ? records.length : 0} records`)\\n return null\\n }\\n \\n const macd = talib.MACD(records)\\n const rsi = talib.RSI(records, 14)\\n const atr = talib.ATR(records, 14)\\n const obv = talib.OBV(records)\\n \\n function getLast10Values(arr) {\\n if (!arr || arr.length === 0) return []\\n return arr.slice(-10)\\n }\\n \\n return {\\n period: periodName,\\n recordsCount: records.length,\\n lastPrice: records[records.length - 1].Close,\\n MACD: {\\n macd: getLast10Values(macd[0]),\\n signal: getLast10Values(macd[1]), \\n histogram: getLast10Values(macd[2])\\n },\\n RSI: getLast10Values(rsi),\\n ATR: getLast10Values(atr),\\n OBV: getLast10Values(obv)\\n }\\n}\\n\\nconst models = _G('models')\\nconst modelPnlData = Object.keys(models).map(modelName => ({\\n name: modelName,\\n realizedPnl: models[modelName].account.realizedPnl,\\n totalValue: models[modelName].account.totalValue\\n}))\\n\\nconst sortedModels = modelPnlData.sort((a, b) => b.realizedPnl - a.realizedPnl)\\n\\nconst rankedModels = []\\nlet currentRank = 1\\nlet previousPnl = null\\n\\nsortedModels.forEach((model, index) => {\\n if (previousPnl !== null && model.realizedPnl < previousPnl) {\\n currentRank = index + 1\\n }\\n \\n rankedModels.push({\\n ...model,\\n rank: currentRank\\n })\\n \\n previousPnl = model.realizedPnl\\n})\\n\\nconst currentModelName = Object.keys(models)[0] // First model\\nconst currentModelRank = rankedModels.find(model => model.name === currentModelName)\\n\\nconst rankingText = rankedModels.map(model => \\n `${model.rank}. ${model.name}: ${model.realizedPnl} USDT${model.name === currentModelName ? ' ← Your Position' : ''}`\\n).join('\\\\n')\\n\\nlet gapInfo = ''\\nif (currentModelRank.rank > 1) {\\n const firstPlace = rankedModels[0]\\n const gap = (firstPlace.realizedPnl - currentModelRank.realizedPnl).toFixed(2)\\n gapInfo = `### Gap from 1st Place\\\\n1st Place PnL: ${firstPlace.realizedPnl} USDT\\\\nYou need to catch up: ${gap} USDT`\\n} else {\\n gapInfo = '🏆 Congratulations! You are leading! Keep the advantage!'\\n}\\n\\nconst result = {\\n symbol: symbol,\\n timestamp: Date.now(),\\n timeframes: {\\n day: getIndicatorsForPeriod(symbol, timeframes.day, 'Daily'),\\n hour: getIndicatorsForPeriod(symbol, timeframes.hour, 'Hourly'),\\n min5: getIndicatorsForPeriod(symbol, timeframes.min5, '5-minute')\\n },\\n // ⚠️ Modified: Use model's own virtual position instead of real position\\n position: models[currentModelName].positions || [],\\n ranking: {\\n currentModel: currentModelName,\\n currentRank: currentModelRank.rank,\\n currentPnl: currentModelRank.realizedPnl,\\n currentTotalValue: currentModelRank.totalValue,\\n totalTrades: models[currentModelName].account.totalTrades,\\n winTrades: models[currentModelName].account.winTrades,\\n winRate: models[currentModelName].account.totalTrades > 0 ? \\n (models[currentModelName].account.winTrades / models[currentModelName].account.totalTrades * 100).toFixed(2) : '0',\\n rankingText: rankingText,\\n gapInfo: gapInfo,\\n totalModels: Object.keys(models).length,\\n allRankings: rankedModels\\n }\\n}\\n\\nreturn result\",\"notice\":\"\"},\"type\":\"n8n-nodes-base.code\",\"typeVersion\":2,\"position\":[-384,16],\"id\":\"910841e7-88cc-4ea0-9b64-c5ff2019967f\",\"name\":\"DS Processing\"},{\"parameters\":{\"mode\":\"runOnceForAllItems\",\"language\":\"javaScript\",\"jsCode\":\"const symbol = $vars.coin + \\\"_USDT.swap\\\"\\nconst timeframes = {\\n day: PERIOD_D1,\\n hour: PERIOD_H1,\\n min5: PERIOD_M5\\n}\\n\\nfunction getIndicatorsForPeriod(symbol, period, periodName) {\\n const records = exchange.GetRecords(symbol, period)\\n \\n if (!records || records.length <= 10) {\\n Log(`Warning: ${periodName} period insufficient data, only ${records ? records.length : 0} records`)\\n return null\\n }\\n \\n const macd = talib.MACD(records)\\n const rsi = talib.RSI(records, 14)\\n const atr = talib.ATR(records, 14)\\n const obv = talib.OBV(records)\\n \\n function getLast10Values(arr) {\\n if (!arr || arr.length === 0) return []\\n return arr.slice(-10)\\n }\\n \\n return {\\n period: periodName,\\n recordsCount: records.length,\\n lastPrice: records[records.length - 1].Close,\\n MACD: {\\n macd: getLast10Values(macd[0]),\\n signal: getLast10Values(macd[1]), \\n histogram: getLast10Values(macd[2])\\n },\\n RSI: getLast10Values(rsi),\\n ATR: getLast10Values(atr),\\n OBV: getLast10Values(obv)\\n }\\n}\\n\\nconst models = _G('models')\\nconst modelPnlData = Object.keys(models).map(modelName => ({\\n name: modelName,\\n realizedPnl: models[modelName].account.realizedPnl,\\n totalValue: models[modelName].account.totalValue\\n}))\\n\\nconst sortedModels = modelPnlData.sort((a, b) => b.realizedPnl - a.realizedPnl)\\n\\nconst rankedModels = []\\nlet currentRank = 1\\nlet previousPnl = null\\n\\nsortedModels.forEach((model, index) => {\\n if (previousPnl !== null && model.realizedPnl < previousPnl) {\\n currentRank = index + 1\\n }\\n \\n rankedModels.push({\\n ...model,\\n rank: currentRank\\n })\\n \\n previousPnl = model.realizedPnl\\n})\\n\\nconst currentModelName = Object.keys(models)[1] // Second model\\nconst currentModelRank = rankedModels.find(model => model.name === currentModelName)\\n\\nconst rankingText = rankedModels.map(model => \\n `${model.rank}. ${model.name}: ${model.realizedPnl} USDT${model.name === currentModelName ? ' ← Your Position' : ''}`\\n).join('\\\\n')\\n\\nlet gapInfo = ''\\nif (currentModelRank.rank > 1) {\\n const firstPlace = rankedModels[0]\\n const gap = (firstPlace.realizedPnl - currentModelRank.realizedPnl).toFixed(2)\\n gapInfo = `### Gap from 1st Place\\\\n1st Place PnL: ${firstPlace.realizedPnl} USDT\\\\nYou need to catch up: ${gap} USDT`\\n} else {\\n gapInfo = '🏆 Congratulations! You are leading! Keep the advantage!'\\n}\\n\\nconst result = {\\n symbol: symbol,\\n timestamp: Date.now(),\\n timeframes: {\\n day: getIndicatorsForPeriod(symbol, timeframes.day, 'Daily'),\\n hour: getIndicatorsForPeriod(symbol, timeframes.hour, 'Hourly'),\\n min5: getIndicatorsForPeriod(symbol, timeframes.min5, '5-minute')\\n },\\n // ⚠️ Modified: Use model's own virtual position instead of real position\\n position: models[currentModelName].positions || [],\\n ranking: {\\n currentModel: currentModelName,\\n currentRank: currentModelRank.rank,\\n currentPnl: currentModelRank.realizedPnl,\\n currentTotalValue: currentModelRank.totalValue,\\n totalTrades: models[currentModelName].account.totalTrades,\\n winTrades: models[currentModelName].account.winTrades,\\n winRate: models[currentModelName].account.totalTrades > 0 ? \\n (models[currentModelName].account.winTrades / models[currentModelName].account.totalTrades * 100).toFixed(2) : '0',\\n rankingText: rankingText,\\n gapInfo: gapInfo,\\n totalModels: Object.keys(models).length,\\n allRankings: rankedModels\\n }\\n}\\n\\nreturn result\",\"notice\":\"\"},\"type\":\"n8n-nodes-base.code\",\"typeVersion\":2,\"position\":[-384,416],\"id\":\"4854a515-d721-4d32-b7f9-fd0b3706c36a\",\"name\":\"Claude Processing\"},{\"parameters\":{\"mode\":\"runOnceForAllItems\",\"language\":\"javaScript\",\"jsCode\":\"const symbol = $vars.coin + \\\"_USDT.swap\\\"\\nconst timeframes = {\\n day: PERIOD_D1,\\n hour: PERIOD_H1,\\n min5: PERIOD_M5\\n}\\n\\nfunction getIndicatorsForPeriod(symbol, period, periodName) {\\n const records = exchange.GetRecords(symbol, period)\\n \\n if (!records || records.length <= 10) {\\n Log(`Warning: ${periodName} period insufficient data, only ${records ? records.length : 0} records`)\\n return null\\n }\\n \\n const macd = talib.MACD(records)\\n const rsi = talib.RSI(records, 14)\\n const atr = talib.ATR(records, 14)\\n const obv = talib.OBV(records)\\n \\n function getLast10Values(arr) {\\n if (!arr || arr.length === 0) return []\\n return arr.slice(-10)\\n }\\n \\n return {\\n period: periodName,\\n recordsCount: records.length,\\n lastPrice: records[records.length - 1].Close,\\n MACD: {\\n macd: getLast10Values(macd[0]),\\n signal: getLast10Values(macd[1]), \\n histogram: getLast10Values(macd[2])\\n },\\n RSI: getLast10Values(rsi),\\n ATR: getLast10Values(atr),\\n OBV: getLast10Values(obv)\\n }\\n}\\n\\nconst models = _G('models')\\nconst modelPnlData = Object.keys(models).map(modelName => ({\\n name: modelName,\\n realizedPnl: models[modelName].account.realizedPnl,\\n totalValue: models[modelName].account.totalValue\\n}))\\n\\nconst sortedModels = modelPnlData.sort((a, b) => b.realizedPnl - a.realizedPnl)\\n\\nconst rankedModels = []\\nlet currentRank = 1\\nlet previousPnl = null\\n\\nsortedModels.forEach((model, index) => {\\n if (previousPnl !== null && model.realizedPnl < previousPnl) {\\n currentRank = index + 1\\n }\\n \\n rankedModels.push({\\n ...model,\\n rank: currentRank\\n })\\n \\n previousPnl = model.realizedPnl\\n})\\n\\nconst currentModelName = Object.keys(models)[2] // Third model\\nconst currentModelRank = rankedModels.find(model => model.name === currentModelName)\\n\\nconst rankingText = rankedModels.map(model => \\n `${model.rank}. ${model.name}: ${model.realizedPnl} USDT${model.name === currentModelName ? ' ← Your Position' : ''}`\\n).join('\\\\n')\\n\\nlet gapInfo = ''\\nif (currentModelRank.rank > 1) {\\n const firstPlace = rankedModels[0]\\n const gap = (firstPlace.realizedPnl - currentModelRank.realizedPnl).toFixed(2)\\n gapInfo = `### Gap from 1st Place\\\\n1st Place PnL: ${firstPlace.realizedPnl} USDT\\\\nYou need to catch up: ${gap} USDT`\\n} else {\\n gapInfo = '🏆 Congratulations! You are leading! Keep the advantage!'\\n}\\n\\nconst result = {\\n symbol: symbol,\\n timestamp: Date.now(),\\n timeframes: {\\n day: getIndicatorsForPeriod(symbol, timeframes.day, 'Daily'),\\n hour: getIndicatorsForPeriod(symbol, timeframes.hour, 'Hourly'),\\n min5: getIndicatorsForPeriod(symbol, timeframes.min5, '5-minute')\\n },\\n // ⚠️ Modified: Use model's own virtual position instead of real position\\n position: models[currentModelName].positions || [],\\n ranking: {\\n currentModel: currentModelName,\\n currentRank: currentModelRank.rank,\\n currentPnl: currentModelRank.realizedPnl,\\n currentTotalValue: currentModelRank.totalValue,\\n totalTrades: models[currentModelName].account.totalTrades,\\n winTrades: models[currentModelName].account.winTrades,\\n winRate: models[currentModelName].account.totalTrades > 0 ? \\n (models[currentModelName].account.winTrades / models[currentModelName].account.totalTrades * 100).toFixed(2) : '0',\\n rankingText: rankingText,\\n gapInfo: gapInfo,\\n totalModels: Object.keys(models).length,\\n allRankings: rankedModels\\n }\\n}\\n\\nreturn result\",\"notice\":\"\"},\"type\":\"n8n-nodes-base.code\",\"typeVersion\":2,\"position\":[-384,816],\"id\":\"56aa5a7b-479c-47df-b3c7-d47ccdedae29\",\"name\":\"QWEN Processing\"},{\"parameters\":{\"mode\":\"runOnceForAllItems\",\"language\":\"javaScript\",\"jsCode\":\"const symbol = $vars.coin + \\\"_USDT.swap\\\"\\nconst timeframes = {\\n day: PERIOD_D1,\\n hour: PERIOD_H1,\\n min5: PERIOD_M5\\n}\\n\\nfunction getIndicatorsForPeriod(symbol, period, periodName) {\\n const records = exchange.GetRecords(symbol, period)\\n \\n if (!records || records.length <= 10) {\\n Log(`Warning: ${periodName} period insufficient data, only ${records ? records.length : 0} records`)\\n return null\\n }\\n \\n const macd = talib.MACD(records)\\n const rsi = talib.RSI(records, 14)\\n const atr = talib.ATR(records, 14)\\n const obv = talib.OBV(records)\\n \\n function getLast10Values(arr) {\\n if (!arr || arr.length === 0) return []\\n return arr.slice(-10)\\n }\\n \\n return {\\n period: periodName,\\n recordsCount: records.length,\\n lastPrice: records[records.length - 1].Close,\\n MACD: {\\n macd: getLast10Values(macd[0]),\\n signal: getLast10Values(macd[1]), \\n histogram: getLast10Values(macd[2])\\n },\\n RSI: getLast10Values(rsi),\\n ATR: getLast10Values(atr),\\n OBV: getLast10Values(obv)\\n }\\n}\\n\\nconst models = _G('models')\\nconst modelPnlData = Object.keys(models).map(modelName => ({\\n name: modelName,\\n realizedPnl: models[modelName].account.realizedPnl,\\n totalValue: models[modelName].account.totalValue\\n}))\\n\\nconst sortedModels = modelPnlData.sort((a, b) => b.realizedPnl - a.realizedPnl)\\n\\nconst rankedModels = []\\nlet currentRank = 1\\nlet previousPnl = null\\n\\nsortedModels.forEach((model, index) => {\\n if (previousPnl !== null && model.realizedPnl < previousPnl) {\\n currentRank = index + 1\\n }\\n \\n rankedModels.push({\\n ...model,\\n rank: currentRank\\n })\\n \\n previousPnl = model.realizedPnl\\n})\\n\\nconst currentModelName = Object.keys(models)[3] // Fourth model\\nconst currentModelRank = rankedModels.find(model => model.name === currentModelName)\\n\\nconst rankingText = rankedModels.map(model => \\n `${model.rank}. ${model.name}: ${model.realizedPnl} USDT${model.name === currentModelName ? ' ← Your Position' : ''}`\\n).join('\\\\n')\\n\\nlet gapInfo = ''\\nif (currentModelRank.rank > 1) {\\n const firstPlace = rankedModels[0]\\n const gap = (firstPlace.realizedPnl - currentModelRank.realizedPnl).toFixed(2)\\n gapInfo = `### Gap from 1st Place\\\\n1st Place PnL: ${firstPlace.realizedPnl} USDT\\\\nYou need to catch up: ${gap} USDT`\\n} else {\\n gapInfo = '🏆 Congratulations! You are leading! Keep the advantage!'\\n}\\n\\nconst result = {\\n symbol: symbol,\\n timestamp: Date.now(),\\n timeframes: {\\n day: getIndicatorsForPeriod(symbol, timeframes.day, 'Daily'),\\n hour: getIndicatorsForPeriod(symbol, timeframes.hour, 'Hourly'),\\n min5: getIndicatorsForPeriod(symbol, timeframes.min5, '5-minute')\\n },\\n // ⚠️ Modified: Use model's own virtual position instead of real position\\n position: models[currentModelName].positions || [],\\n ranking: {\\n currentModel: currentModelName,\\n currentRank: currentModelRank.rank,\\n currentPnl: currentModelRank.realizedPnl,\\n currentTotalValue: currentModelRank.totalValue,\\n totalTrades: models[currentModelName].account.totalTrades,\\n winTrades: models[currentModelName].account.winTrades,\\n winRate: models[currentModelName].account.totalTrades > 0 ? \\n (models[currentModelName].account.winTrades / models[currentModelName].account.totalTrades * 100).toFixed(2) : '0',\\n rankingText: rankingText,\\n gapInfo: gapInfo,\\n totalModels: Object.keys(models).length,\\n allRankings: rankedModels\\n }\\n}\\n\\nreturn result\",\"notice\":\"\"},\"type\":\"n8n-nodes-base.code\",\"typeVersion\":2,\"position\":[-384,1216],\"id\":\"cd92d6e1-97bf-4e48-9c0f-73d2f7ea0c03\",\"name\":\"Grok Processing\"}],\"pinData\":{},\"connections\":{\"Schedule Trigger\":{\"main\":[[{\"node\":\"Strategy Init\",\"type\":\"main\",\"index\":0}]]},\"Claude4.5\":{\"ai_languageModel\":[[{\"node\":\"Claude\",\"type\":\"ai_languageModel\",\"index\":0}]]},\"DS3.1\":{\"ai_languageModel\":[[{\"node\":\"DeepSeek\",\"type\":\"ai_languageModel\",\"index\":0}]]},\"DeepSeek\":{\"main\":[[{\"node\":\"Merge\",\"type\":\"main\",\"index\":0}]]},\"Claude\":{\"main\":[[{\"node\":\"Merge\",\"type\":\"main\",\"index\":1}]]},\"QWEN\":{\"main\":[[{\"node\":\"Merge\",\"type\":\"main\",\"index\":2}]]},\"OpenAI Model\":{\"ai_languageModel\":[[{\"node\":\"QWEN\",\"type\":\"ai_languageModel\",\"index\":0}]]},\"Grok\":{\"main\":[[{\"node\":\"Merge\",\"type\":\"main\",\"index\":3}]]},\"Grok4\":{\"ai_languageModel\":[[{\"node\":\"Grok\",\"type\":\"ai_languageModel\",\"index\":0}]]},\"Merge\":{\"main\":[[{\"node\":\"Result Calculation\",\"type\":\"main\",\"index\":0}]]},\"Convert to File\":{\"main\":[[{\"node\":\"Write to File\",\"type\":\"main\",\"index\":0}]]},\"Result Calculation\":{\"main\":[[{\"node\":\"Trade Log Save\",\"type\":\"main\",\"index\":0}]]},\"Trade Log Save\":{\"main\":[[{\"node\":\"Convert to File\",\"type\":\"main\",\"index\":0},{\"node\":\"Best Trade\",\"type\":\"main\",\"index\":0}]]},\"Best Trade\":{\"main\":[[{\"node\":\"Visualization\",\"type\":\"main\",\"index\":0}]]},\"Strategy Init\":{\"main\":[[{\"node\":\"DS Processing\",\"type\":\"main\",\"index\":0},{\"node\":\"Claude Processing\",\"type\":\"main\",\"index\":0},{\"node\":\"QWEN Processing\",\"type\":\"main\",\"index\":0},{\"node\":\"Grok Processing\",\"type\":\"main\",\"index\":0}]]},\"DS Processing\":{\"main\":[[{\"node\":\"DeepSeek\",\"type\":\"main\",\"index\":0}]]},\"Claude Processing\":{\"main\":[[{\"node\":\"Claude\",\"type\":\"main\",\"index\":0}]]},\"QWEN Processing\":{\"main\":[[{\"node\":\"QWEN\",\"type\":\"main\",\"index\":0}]]},\"Grok Processing\":{\"main\":[[{\"node\":\"Grok\",\"type\":\"main\",\"index\":0}]]}},\"active\":false,\"settings\":{\"timezone\":\"Asia/Shanghai\",\"executionOrder\":\"v1\"},\"tags\":[],\"meta\":{\"templateCredsSetupCompleted\":true},\"credentials\":{},\"id\":\"6c8dfda8-4657-42fa-b4dc-8f723e88aaa7\",\"plugins\":{},\"mcpClients\":{}},\"startNodes\":[],\"triggerToStartFrom\":{\"name\":\"Schedule Trigger\"}}"}