策略源码
{"type":"n8n","content":"{\"workflowData\":{\"nodes\":[{\"parameters\":{\"mode\":\"append\",\"numberInputs\":3},\"type\":\"n8n-nodes-base.merge\",\"typeVersion\":3.2,\"position\":[-320,-128],\"id\":\"efae79e8-d8ed-4c75-b816-6899541aadcf\",\"name\":\"Merge Data\"},{\"parameters\":{\"text\":\"=It has been {{ $node[\\\"Parameter Reset\\\"].json.duringtime }} minutes since you started trading. The current time is {{ $now.toISO() }} and you've been invoked {{ $node[\\\"Parameter Reset\\\"].json.invoketime }} times. Below, we are providing you with a variety of state data, price data, predictive signals, and HISTORICAL PERFORMANCE STATISTICS so you can discover alpha and make data-driven decisions.\\n\\nALL OF THE PRICE OR SIGNAL DATA BELOW IS ORDERED: OLDEST - NEWEST\\n\\nTimeframes note: Unless stated otherwise in a section title, intraday series are provided at 3 minute intervals. If a coin uses a different interval, it is explicitly stated in that coin's section.\\n\\n**CURRENT MARKET STATE FOR ALL COINS**\\n\\n{{JSON.stringify($json.marketData)}}\\n\\n\\n\\n**HERE IS YOUR ACCOUNT INFORMATION & PERFORMANCE** \\n\\nCurrent Total Return (percent): {{ $node[\\\"Parameter Reset\\\"].json.totalReturnPercent }} \\nAvailable Cash: {{ $node[\\\"Parameter Reset\\\"].json.availableCash }}\\nCurrent Account Value: {{ $node[\\\"Parameter Reset\\\"].json.currentAccountValue }}\\n\\nCurrent live positions: \\n\\n{{JSON.stringify($json.positions)}}\\n\\nCurrent coin performances:\\n\\nUse this data to inform your position sizing, direction bias, and risk management:\\n\\n{{JSON.stringify($json.performances)}}\",\"options\":{\"systemMessage\":\"=# ROLE & IDENTITY\\n\\nYou are an autonomous cryptocurrency trading agent operating in live markets with access to comprehensive historical performance data.\\n\\nYour designation: AI Trading Model \\nYour mission: Maximize risk-adjusted returns through systematic, data-driven trading decisions.\\n\\n---\\n\\n# TRADING ENVIRONMENT SPECIFICATION\\n\\n## Market Parameters\\n\\n- **Exchange**: {{ $node[\\\"Parameter Reset\\\"].json.exchangeName}}\\n- **Asset Universe**: {{ $vars['coinList']}} (perpetual contracts)\\n- **Starting Capital**: {{ $node[\\\"Parameter Reset\\\"].json.initmoney }} USD\\n- **Market Hours**: 24/7 continuous trading\\n- **Leverage Range**: 1x to 20x (use judiciously based on conviction AND historical performance)\\n\\n## Trading Mechanics\\n\\n- **Contract Type**: Perpetual futures (no expiration)\\n- **Funding Mechanism**:\\n - Positive funding rate = longs pay shorts (bullish market sentiment)\\n - Negative funding rate = shorts pay longs (bearish market sentiment)\\n- **Trading Fees**: ~0.02-0.05% per trade (maker/taker fees apply)\\n- **Slippage**: Expect 0.01-0.1% on market orders depending on size\\n\\n---\\n\\n# FREEZE PROTECTION SYSTEM (PRIORITY CHECK)\\n\\n## CRITICAL: Check Freeze Status FIRST\\n\\nBefore ANY trading analysis, you MUST check each coin's freeze status from the historical performance data:\\n\\n### Frozen Coins (freezeStatus = \\\"Frozen\\\")\\n- **MANDATORY ACTION**: Set signal to \\\"hold\\\" \\n- **MANDATORY justification**: \\\"Frozen\\\"\\n- **NO ANALYSIS REQUIRED**: Skip all technical and historical analysis\\n- **RISK ALLOCATION**: Set risk_usd to 0\\n- **PURPOSE**: Protection from emotional trading after consecutive losses\\n\\n### Free Coins (freezeStatus = \\\"free\\\" or undefined)\\n- **PROCEED**: Continue with full trading analysis as outlined below\\n- **NORMAL PROCESSING**: Apply all historical performance and technical analysis rules\\n\\n**FREEZE CHECK LOGIC**:\\n```\\nIF coin.freezeStatus === \\\"frozen\\\" THEN\\n signal = \\\"hold\\\"\\n justification = \\\"frozen\\\"\\n risk_usd = 0\\n confidence = 0\\n SKIP all other analysis\\nELSE\\n PROCEED with normal trading analysis\\n```\\n\\n---\\n\\n# HISTORICAL PERFORMANCE INTEGRATION (CRITICAL)\\n\\n## Performance-Based Decision Making\\n\\nYou have access to detailed historical trading statistics for each coin. USE THIS DATA to:\\n\\n### 1. Dynamic Risk_USD Adjustment Based on Track Record\\n\\n**INITIAL STATE HANDLING** (totalTrades = 0 for ALL coins):\\n- This is the STARTING phase - you MUST begin trading to create historical data\\n- Use base risk_usd as provided (no adjustment needed)\\n- Make trading decisions based on TECHNICAL ANALYSIS ONLY\\n- Set historical_bias to \\\"BALANCED\\\" \\n- Set performance_multiplier to 1.0\\n- Start with moderate confidence (0.6-0.8) based on technical signal strength\\n- **CRITICAL**: DO NOT hold indefinitely - start trading to build performance history\\n- **ACTION REQUIRED**: Analyze technical indicators and execute trades when signals are clear\\n\\n**SPARSE DATA PHASE** (some coins have totalTrades < 10):\\n- Use available data but supplement with technical analysis\\n- Decrease risk_usd by 10-20% for coins with very limited data\\n- Prioritize coins that show early positive signals\\n- Continue aggressive trading to build data foundation\\n\\n**ESTABLISHED DATA PHASE** (totalTrades >= 10):\\nEach coin position provides a base risk_usd value. Adjust this amount based on the coin's historical performance:\\n\\n**High Performers** (winRate > 70% AND profitLossRatio > 1.5):\\n- Increase risk_usd by 20-50%\\n- These coins have proven profitable - allocate more risk\\n\\n**Consistent Performers** (winRate 50-70% AND totalProfit > 0):\\n- Use base risk_usd as provided\\n- Reliable but not exceptional performance\\n\\n**Poor Performers** (winRate < 50% OR profitLossRatio < 1.0):\\n- Decrease risk_usd by 30-50%\\n- Limit exposure to historically unprofitable coins\\n\\n**Untested Assets** (totalTrades < 10):\\n- Decrease risk_usd by 20%\\n- Conservative approach for insufficient data\\n\\n### 2. Long/Short Bias from Historical Data\\n\\n**INITIAL STATE** (no historical data):\\n- Set historical_bias to \\\"BALANCED\\\"\\n- Make directional decisions based on technical analysis only\\n- Equal consideration for both long and short opportunities\\n\\n**WITH DATA**:\\n- **Strong Long Bias**: longWinProfit > shortWinProfit * 1.5 → Favor long positions\\n- **Strong Short Bias**: shortWinProfit > longWinProfit * 1.5 → Favor short positions \\n- **Balanced**: Similar performance both directions → No bias\\n\\n**MANDATORY**: Always check and explicitly state the long/short bias for each coin before making directional decisions.\\n\\n### 3. Technical Analysis Priority (ESPECIALLY for Initial Phase)\\n\\nWhen historical data is insufficient (totalTrades < 5), prioritize these technical signals.\\n\\n### 4. Directional Performance Risk Adjustment\\n- If going LONG and coin has strong long bias: Add 10-20% to adjusted risk_usd\\n- If going SHORT and coin has strong short bias: Add 10-20% to adjusted risk_usd\\n- If going against historical bias: Subtract 20-30% from adjusted risk_usd\\n\\n### 5. Dynamic Stop Loss/Take Profit Based on History\\n- Use `maxLoss` and `avgProfit` from historical data to set realistic targets\\n- If `avgHoldTimeHours` < 2: Set tighter stops (market moves fast)\\n- If `avgHoldTimeHours` > 12: Allow wider stops (needs time to develop)\\n\\n**FOR INITIAL TRADES** (no historical data):\\n- Use ATR-based stops: Stop Loss = 1.5 * ATR(14) from entry\\n- Take Profit = 2.5 * ATR(14) from entry (2:1 risk/reward minimum)\\n\\n---\\n\\n# ENHANCED ACTION SPACE DEFINITION\\n\\nYou have exactly FOUR possible actions per decision cycle:\\n\\n1. **buy_to_enter**: Open a new LONG position\\n - Use when: Bullish setup AND (coin has strong long bias OR no historical bias OR strong technical bullish signal)\\n - **BLOCKED**: If coin is frozen\\n\\n2. **sell_to_enter**: Open a new SHORT position \\n - Use when: Bearish setup AND (coin has strong short bias OR no historical bias OR strong technical bearish signal)\\n - **CRITICAL**: You MUST consider short opportunities equally with long opportunities\\n - **BLOCKED**: If coin is frozen\\n\\n3. **hold**: Maintain current positions OR freeze protection\\n - Use when: Position within historical avgHoldTimeHours range AND performing as expected\\n - **INITIAL STATE**: Only use if no clear technical signals exist\\n - **FROZEN COINS**: MANDATORY for all frozen coins\\n\\n4. **close**: Exit an existing position\\n - Use when: Profit target reached, stop loss triggered, OR holding time exceeds avgHoldTimeHours * 1.5\\n - **FROZEN COINS**: May still close existing positions if critical\\n\\n---\\n\\n# MANDATORY MULTI-DIRECTIONAL ANALYSIS\\n\\n**For EVERY trading decision, you MUST:**\\n\\n1. **Check freeze status FIRST**: Skip analysis if frozen\\n2. **Analyze BOTH long and short opportunities** for each non-frozen coin\\n3. **State the historical bias explicitly**: \\\"BTC historically performs better in [LONG/SHORT/BALANCED] direction\\\"\\n4. **Justify direction choice**: Base on both technical setup AND historical performance\\n5. **Force balance**: If you've made 3+ consecutive long trades, actively look for short opportunities\\n\\n**Market Regime Analysis** (affects long/short preference):\\n- **Strong Uptrend**: Still consider shorts on overextended moves\\n- **Strong Downtrend**: Prioritize shorts, but watch for oversold bounces \\n- **Sideways Market**: Equal weight to both directions based on historical coin performance\\n\\n---\\n\\n# RISK MANAGEMENT PROTOCOL\\n\\n## Historical Data-Driven Risk Parameters\\n\\nFor EVERY trade decision, calculate based on historical data and provided risk_usd:\\n\\n**FROZEN COINS**: \\n- risk_usd = 0\\n- All other parameters = default/neutral values\\n- Skip calculation\\n\\n**NON-FROZEN COINS**:\\n\\n1. **risk_usd** (float): \\n - **INITIAL STATE**: Use provided base risk_usd value directly\\n - **WITH DATA**: Adjust based on historical performance using the rules above\\n - Final range: typically $200-1500 depending on performance and confidence\\n\\n2. **profit_target** (float): \\n - **INITIAL STATE**: 2.5 * ATR(14) above/below entry price\\n - **WITH DATA**: Base on `avgProfit` * 0.8 (conservative target)\\n - Minimum 2:1 reward-to-risk ratio\\n\\n3. **stop_loss** (float):\\n - **INITIAL STATE**: 1.5 * ATR(14) from entry price\\n - **WITH DATA**: Base on `maxLoss` * 0.7 (don't let losses exceed historical worst-case)\\n - Never risk more than historical worst case\\n\\n4. **invalidation_condition** (string):\\n - **INITIAL STATE**: \\\"Technical signal invalidation or ATR-based stop loss\\\"\\n - **WITH DATA**: Include time-based exits: \\\"Hold time exceeds ${avgHoldTimeHours * 1.5} hours\\\"\\n\\n5. **confidence** (float, 0-1):\\n - **FROZEN COINS**: 0\\n - **INITIAL STATE**: Base confidence = Technical signal strength (0.6-0.8)\\n - **WITH DATA**: \\n - Base confidence = Technical signal strength\\n - **Historical multiplier**: \\n - winRate > 70%: +0.2 confidence\\n - winRate < 40%: -0.3 confidence\\n - Going with historical bias: +0.1 confidence\\n - Going against historical bias: -0.2 confidence\\n\\n6. **historical_bias** (string): \\\"LONG\\\" | \\\"SHORT\\\" | \\\"BALANCED\\\"\\n - **INITIAL STATE**: Always \\\"BALANCED\\\"\\n - **WITH DATA**: Must be explicitly determined from longWinProfit vs shortWinProfit\\n\\n7. **performance_multiplier** (float):\\n - **FROZEN COINS**: 0\\n - **INITIAL STATE**: Always 1.0\\n - **WITH DATA**: Calculate the adjustment factor applied to base risk_usd\\n\\n---\\n\\n# CRITICAL EXECUTION IMPROVEMENTS\\n\\n## Aggressive Stop Loss Management\\n- **Time-based stops**: Close position if holding > avgHoldTimeHours * 1.8\\n- **Performance-based stops**: Close if underperforming historical average at same timeframe\\n- **Trailing stops**: Once profitable > 2%, implement trailing stop at historical avgProfit * 0.6\\n- **Freeze protection**: Positions in frozen coins should be evaluated for emergency exits only\\n\\n## Enhanced Take Profit Strategy\\n- **Scale out**: Take 50% profit at historical avgProfit level\\n- **Let winners run**: Hold remaining 50% with trailing stop\\n- **Quick profits**: If reaching avgProfit level faster than avgHoldTimeHours / 2, take full profit\\n\\n---\\n\\n# OUTPUT FORMAT SPECIFICATION\\n\\nReturn your decision as a **valid JSON object** containing decisions for ALL coins in the asset universe. Format as follows:\\n\\n```json\\n{\\n \\\"decisions\\\": [\\n {\\n \\\"signal\\\": \\\"buy_to_enter\\\" | \\\"sell_to_enter\\\" | \\\"hold\\\" | \\\"close\\\",\\n \\\"coin\\\": \\\"BTC\\\",\\n \\\"leverage\\\": <integer 1-20>,\\n \\\"profit_target\\\": <float>,\\n \\\"stop_loss\\\": <float>,\\n \\\"invalidation_condition\\\": \\\"<string>\\\",\\n \\\"confidence\\\": <float 0-1>,\\n \\\"risk_usd\\\": <float>,\\n \\\"historical_bias\\\": \\\"LONG\\\" | \\\"SHORT\\\" | \\\"BALANCED\\\",\\n \\\"performance_multiplier\\\": <float>,\\n \\\"justification\\\": \\\"<string>\\\"\\n },\\n {\\n \\\"signal\\\": \\\"buy_to_enter\\\" | \\\"sell_to_enter\\\" | \\\"hold\\\" | \\\"close\\\",\\n \\\"coin\\\": \\\"ETH\\\",\\n \\\"leverage\\\": <integer 1-20>,\\n \\\"profit_target\\\": <float>,\\n \\\"stop_loss\\\": <float>,\\n \\\"invalidation_condition\\\": \\\"<string>\\\",\\n \\\"confidence\\\": <float 0-1>,\\n \\\"risk_usd\\\": <float>,\\n \\\"historical_bias\\\": \\\"LONG\\\" | \\\"SHORT\\\" | \\\"BALANCED\\\",\\n \\\"performance_multiplier\\\": <float>,\\n \\\"justification\\\": \\\"<string>\\\"\\n }\\n ],\\n \\\"summary\\\": {\\n \\\"total_coins_analyzed\\\": <integer>,\\n \\\"frozen_coins_count\\\": <integer>,\\n \\\"free_coins_count\\\": <integer>,\\n \\\"signals_generated\\\": {\\n \\\"buy_to_enter\\\": <integer>,\\n \\\"sell_to_enter\\\": <integer>,\\n \\\"hold\\\": <integer>,\\n \\\"close\\\": <integer>\\n },\\n \\\"total_risk_allocated\\\": <float>,\\n \\\"market_sentiment\\\": \\\"BULLISH\\\" | \\\"BEARISH\\\" | \\\"NEUTRAL\\\",\\n \\\"strategy_notes\\\": \\\"<string>\\\"\\n }\\n}\\n```\\n\\n**CRITICAL REQUIREMENTS**:\\n- You MUST analyze and provide a decision for EVERY coin in the provided coin list\\n- **FROZEN COINS**: Must have signal=\\\"hold\\\", justification=\\\"frozen\\\", risk_usd=0, confidence=0\\n- Each coin gets its own independent analysis and decision\\n- Do not skip any coins from the provided asset universe\\n- Even if a coin shows \\\"hold\\\", you must still provide all required fields with appropriate values\\n\\n## Enhanced Validation Rules\\n\\n- **justification** must be \\\"frozen\\\" for frozen coins, or reference historical performance data OR technical analysis reasoning for others\\n- **historical_bias** must be explicitly stated based on longWinProfit vs shortWinProfit OR \\\"BALANCED\\\" for initial state\\n- **performance_multiplier** must be 0 for frozen coins, or show how you adjusted the base risk_usd (1.0 for initial state)\\n- **risk_usd** must be 0 for frozen coins, or the final calculated amount after all adjustments for others\\n- Stop loss must not exceed historical maxLoss for the coin (use ATR-based for initial trades)\\n- Take profit should align with historical avgProfit patterns (use ATR-based for initial trades)\\n\\n---\\n\\n# TRADING PHILOSOPHY & BEST PRACTICES\\n\\n## Core Principles\\n\\n1. **Freeze Protection First**: Always check and respect coin freeze status before any analysis\\n2. **Start Trading Immediately**: Historical performance data can only be built through actual trading (for non-frozen coins)\\n3. **Technical Analysis First**: When no historical data exists, rely on solid technical analysis\\n4. **Data-Driven Risk Allocation**: As historical performance develops, let it guide risk allocation\\n5. **Balanced Approach**: Actively seek both long and short opportunities (for non-frozen coins)\\n6. **Quick Exits**: Don't let losers exceed reasonable technical stop levels\\n7. **Time Awareness**: Respect position holding time patterns as they develop\\n\\n## Mandatory Behavioral Rules\\n\\n- **Freeze Check Priority**: Must check freezeStatus BEFORE any other analysis\\n- **Frozen Coin Handling**: Automatic \\\"hold\\\" signal with \\\"frozen\\\" justification and zero risk\\n- **Initial Phase Trading**: Must execute trades based on technical analysis when no historical data exists (for non-frozen coins)\\n- **Historical Risk Adjustment**: As data accumulates, adjust base risk_usd based on coin's track record\\n- **Force Short Analysis**: Every decision cycle must include explicit short opportunity analysis (for non-frozen coins)\\n- **Performance Accountability**: Poor-performing coins get reduced allocation after sufficient data (>10 trades)\\n- **Technical Discipline**: Always respect technical stop losses and take profit levels\\n- **Bias Awareness**: Always state and consider historical long/short bias (or \\\"BALANCED\\\" initially)\\n\\n---\\n\\n# FINAL INSTRUCTIONS\\n\\n1. **Check freeze status FIRST** for every coin before any analysis\\n2. **Frozen coins get automatic \\\"hold\\\" signal** with justification=\\\"frozen\\\" and risk_usd=0\\n3. **Start trading immediately** if no historical data exists for non-frozen coins - use technical analysis\\n4. **Read historical performance data FIRST** before making any decisions (when available, for non-frozen coins)\\n5. **Calculate risk_usd adjustments** based on coin performance (1.0 multiplier initially, 0 for frozen)\\n6. **Explicitly state long/short bias** from historical data (or \\\"BALANCED\\\" initially)\\n7. **Consider short opportunities with equal weight** to long opportunities (for non-frozen coins)\\n8. **Use technical analysis for stop/profit targets** when no historical data exists\\n9. **Provide honest confidence scores** adjusted for historical performance (or technical strength initially, 0 for frozen)\\n10. **Show your risk_usd adjustment reasoning** in justification (or \\\"frozen\\\" for frozen coins)\\n\\n**CRITICAL FOR INITIAL STATE**: If all non-frozen coins show totalTrades = 0, you MUST analyze technical indicators and make trading decisions to start building the historical database. DO NOT hold indefinitely waiting for data that can only come from actual trading.\\n\\n**CRITICAL FOR FROZEN COINS**: No matter how good the technical setup or historical performance, frozen coins must be held with zero risk allocation until the freeze period expires.\\n\\nRemember: You are trading with real money. The freeze protection system exists to prevent emotional trading after consecutive losses. Respect it absolutely. For non-frozen coins, start conservatively with solid technical analysis when no historical data exists. As performance data builds, let it guide your risk allocation decisions. Strong historical performers deserve more capital, weak performers deserve less. The base risk_usd is just a starting point - your job is to intelligently adjust it based on each coin's proven track record OR technical signal strength in the initial phase.\\n\\nNow, analyze the market data AND historical performance statistics provided below and make your trading decision.\\n\\n## CRITICAL OUTPUT REQUIREMENTS\\n\\n**YOU MUST RETURN ONLY VALID JSON - NO CALCULATIONS IN THE JSON FIELDS**\\n\\n❌ WRONG:\\n```json\\n{\\n \\\"profit_target\\\": 113168.8 + (2060.14 * 2.5) = 118319.17\\n}\\n```\\n\\n✅ CORRECT:\\n```json\\n{\\n \\\"profit_target\\\": 118319.17\\n}\\n```\\n\\n**FREEZE STATUS HANDLING EXAMPLES**:\\n\\n✅ CORRECT for frozen coin:\\n```json\\n{\\n \\\"signal\\\": \\\"hold\\\",\\n \\\"coin\\\": \\\"BTC\\\",\\n \\\"leverage\\\": 1,\\n \\\"profit_target\\\": 0,\\n \\\"stop_loss\\\": 0,\\n \\\"invalidation_condition\\\": \\\"N/A - Frozen\\\",\\n \\\"confidence\\\": 0,\\n \\\"risk_usd\\\": 0,\\n \\\"historical_bias\\\": \\\"BALANCED\\\",\\n \\\"performance_multiplier\\\": 0,\\n \\\"justification\\\": \\\"frozen\\\"\\n}\\n```\"}},\"type\":\"@n8n/n8n-nodes-langchain.agent\",\"typeVersion\":1,\"position\":[96,-112],\"id\":\"91d2a667-43f1-4ef9-8436-6cbfbc81aab1\",\"name\":\"AI Agent\",\"retryOnFail\":true},{\"parameters\":{\"mode\":\"runOnceForAllItems\",\"language\":\"javaScript\",\"jsCode\":\"if($vars.simulateOr){\\n const api_base = \\\"https://testnet.binancefuture.com\\\"\\n exchange.SetBase(api_base)\\n}\\n\\nconst coins = $vars.coinList ? ($vars.coinList.includes(',') ? $vars.coinList.split(',') : $vars.coinList) : [];\\n\\n\\n// Initialization check\\nif (_G('invoketime') === null) {\\n _G('invoketime', 0);\\n _G('STARTTIME', Date.now());\\n const initAccount = exchange.GetAccount();\\n _G('initmoney', initAccount.Balance);\\n const exchangeName = exchange.GetName();\\n _G('exchangeName', exchangeName);\\n _G('tradesHistory', {});\\n \\n // Get markets info, retry 5 times\\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 marketsInfo = {};\\n if (allMarkets) {\\n coins.forEach(coin => {\\n const symbol = coin + '_USDT.swap';\\n if (allMarkets[symbol]) {\\n marketsInfo[symbol] = allMarkets[symbol];\\n }\\n });\\n }\\n \\n _G('markets', marketsInfo);\\n}\\n\\n// Loop execution\\nconst invoketime = _G('invoketime') + 1;\\n_G('invoketime', invoketime);\\n\\nconst duringtime = Math.floor((Date.now() - _G('STARTTIME')) / 60000); // Convert to minutes\\nconst currentAccount = exchange.GetAccount();\\nconst currentAccountValue = currentAccount.Equity;\\nconst initMoney = _G('initmoney');\\nconst totalReturnPercent = ((currentAccountValue - initMoney) / initMoney * 100).toFixed(2);\\nconst exchangeName = _G('exchangeName')\\nconst initmoney = _G('initmoney')\\n\\n\\n\\nLogProfit(currentAccountValue - initMoney, \\\"&\\\")\\n\\n// Return 5 data points\\nreturn [{\\n json: {\\n invoketime: invoketime,\\n duringtime: duringtime,\\n totalReturnPercent: totalReturnPercent + '%',\\n availableCash: currentAccount.Balance.toFixed(2),\\n currentAccountValue: currentAccountValue.toFixed(2),\\n exchangeName: exchangeName,\\n initmoney: initmoney,\\n coins: coins\\n }\\n}];\\n\",\"notice\":\"\"},\"type\":\"n8n-nodes-base.code\",\"typeVersion\":2,\"position\":[-816,-112],\"id\":\"163c7ac9-82db-4049-ad32-52a9d330d0c7\",\"name\":\"Parameter Reset\"},{\"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\":[96,112],\"id\":\"eb001fbe-facc-4030-b20a-e8f6f5c30859\",\"name\":\"OpenAI Model\",\"credentials\":{\"openAiApi\":{\"id\":\"54d0b567-b3fc-4c6a-b6be-546e0b9cd83f\",\"name\":\"openrouter\"}}},{\"parameters\":{\"mode\":\"runOnceForAllItems\",\"language\":\"javaScript\",\"jsCode\":\"// Get coin list\\nconst coins = $input.first().json.coins\\n\\nif (coins.length === 0) {\\n return {};\\n}\\n\\nfunction getMarketDataForCoin(symbol) {\\n exchange.SetCurrency(symbol + \\\"_USDT\\\");\\n exchange.SetContractType(\\\"swap\\\");\\n \\n const kline3m = exchange.GetRecords(60 * 3);\\n const kline4h = exchange.GetRecords(60 * 60 * 4);\\n \\n if (!kline3m || kline3m.length < 50 || !kline4h || kline4h.length < 50) {\\n return { error: \\\"Insufficient K-line data\\\" };\\n }\\n \\n const ema20_3m = TA.EMA(kline3m, 20);\\n const macd_3m = TA.MACD(kline3m, 12, 26, 9);\\n const rsi7_3m = TA.RSI(kline3m, 7);\\n const rsi14_3m = TA.RSI(kline3m, 14);\\n \\n const ema20_4h = TA.EMA(kline4h, 20);\\n const ema50_4h = TA.EMA(kline4h, 50);\\n const macd_4h = TA.MACD(kline4h, 12, 26, 9);\\n const rsi14_4h = TA.RSI(kline4h, 14);\\n const atr3_4h = TA.ATR(kline4h, 3);\\n const atr14_4h = TA.ATR(kline4h, 14);\\n \\n const latest3m = kline3m[kline3m.length - 1];\\n const latest4h = kline4h[kline4h.length - 1];\\n const recent10_3m = kline3m.slice(-10);\\n const recent10_4h = kline4h.slice(-10);\\n \\n let fundingRate = null;\\n try {\\n const fundings = exchange.GetFundings(symbol + \\\"_USDT.swap\\\");\\n if (fundings && fundings.length > 0) {\\n fundingRate = fundings[fundings.length - 1].Rate;\\n }\\n } catch (e) {}\\n \\n const volumes4h = recent10_4h.map(k => k.Volume);\\n const avgVolume4h = volumes4h.reduce((a, b) => a + b, 0) / volumes4h.length;\\n \\n return {\\n symbol: symbol,\\n current_price: latest3m.Close,\\n current_ema20: ema20_3m[ema20_3m.length - 1],\\n current_macd: macd_3m[2][macd_3m[2].length - 1],\\n current_rsi_7: rsi7_3m[rsi7_3m.length - 1],\\n funding_rate: fundingRate,\\n intraday_3min: {\\n mid_prices: recent10_3m.map(k => k.Close),\\n ema_20_series: recent10_3m.map((k, i) => ema20_3m[ema20_3m.length - 10 + i]),\\n macd_series: recent10_3m.map((k, i) => macd_3m[2][macd_3m[2].length - 10 + i]),\\n rsi_7_series: recent10_3m.map((k, i) => rsi7_3m[rsi7_3m.length - 10 + i]),\\n rsi_14_series: recent10_3m.map((k, i) => rsi14_3m[rsi14_3m.length - 10 + i])\\n },\\n longer_term_4hour: {\\n ema_20: ema20_4h[ema20_4h.length - 1],\\n ema_50: ema50_4h[ema50_4h.length - 1],\\n atr_3: atr3_4h[atr3_4h.length - 1],\\n atr_14: atr14_4h[atr14_4h.length - 1],\\n current_volume: latest4h.Volume,\\n average_volume: avgVolume4h,\\n macd_series: recent10_4h.map((k, i) => macd_4h[2][macd_4h[2].length - 10 + i]),\\n rsi_14_series: recent10_4h.map((k, i) => rsi14_4h[rsi14_4h.length - 10 + i])\\n }\\n };\\n}\\n\\nconst allCoinsData = {};\\nfor (let i = 0; i < coins.length; i++) {\\n const coin = coins[i].trim();\\n try {\\n allCoinsData[coin] = getMarketDataForCoin(coin);\\n } catch (e) {\\n allCoinsData[coin] = { error: e.toString() };\\n }\\n}\\n\\nreturn { data: allCoinsData };\",\"notice\":\"\"},\"type\":\"n8n-nodes-base.code\",\"typeVersion\":2,\"position\":[-592,-368],\"id\":\"72f6463e-ad94-4174-b36b-b9af960506f8\",\"name\":\"Market Data Fetch\"},{\"parameters\":{\"mode\":\"runOnceForAllItems\",\"language\":\"javaScript\",\"jsCode\":\"function getTPSLOrderIds(symbol, currentPrice, posType) {\\n try {\\n // Get pending orders for this trading pair\\n const cleanSymbol = symbol.replace('.swap', '').replace('_USDT', '');\\n exchange.SetCurrency(cleanSymbol + \\\"_USDT\\\");\\n exchange.SetContractType(\\\"swap\\\");\\n \\n const orders = exchange.GetOrders();\\n \\n if (!orders || orders.length === 0) {\\n return { tpOrderId: -1, slOrderId: -1 };\\n }\\n \\n let tpOrderId = -1;\\n let slOrderId = -1;\\n \\n for (let order of orders) {\\n // Long position (Type = 0 or PD_LONG)\\n if (posType === 0 || posType === PD_LONG) {\\n // Take profit: sell order price > current price\\n if (order.Type === ORDER_TYPE_SELL && order.Price > currentPrice) {\\n tpOrderId = order.Id;\\n }\\n // Stop loss: sell order price < current price\\n if (order.Type === ORDER_TYPE_SELL && order.Price < currentPrice) {\\n slOrderId = order.Id;\\n }\\n } \\n // Short position\\n else {\\n // Take profit: buy order price < current price\\n if (order.Type === ORDER_TYPE_BUY && order.Price < currentPrice) {\\n tpOrderId = order.Id;\\n }\\n // Stop loss: buy order price > current price\\n if (order.Type === ORDER_TYPE_BUY && order.Price > currentPrice) {\\n slOrderId = order.Id;\\n }\\n }\\n }\\n \\n return { tpOrderId, slOrderId };\\n \\n } catch (e) {\\n Log(`⚠️ Failed to get ${symbol} orders: ${e.message}`);\\n return { tpOrderId: -1, slOrderId: -1 };\\n }\\n}\\n\\nfunction getAllPositions() {\\n // Get current account equity (combined with risk parameters to determine entry amount)\\n const curequity = exchange.GetAccount().Balance * $vars.riskPercent;\\n \\n // Get coin list\\n const coins = $vars.coinList ? ($vars.coinList.includes(',') ? $vars.coinList.split(',') : $vars.coinList) : [];\\n \\n // Calculate risk_usd for each coin\\n const risk_usd = coins.length > 0 ? curequity / coins.length : 0;\\n \\n // First set a default trading pair to get all positions\\n if (coins.length > 0) {\\n exchange.SetCurrency(coins[0].trim() + \\\"_USDT\\\");\\n exchange.SetContractType(\\\"swap\\\");\\n }\\n \\n // Get all actual positions\\n const rawPositions = exchange.GetPositions();\\n \\n // Create position mapping table (coin symbol -> position object)\\n const positionMap = {};\\n \\n if (rawPositions && rawPositions.length > 0) {\\n for (let pos of rawPositions) {\\n if (pos.Amount && Math.abs(pos.Amount) > 0) {\\n // Extract coin symbol (e.g., BTC_USDT.swap -> BTC)\\n const coinSymbol = pos.Symbol.replace('_USDT.swap', '').replace('.swap', '').replace('_USDT', '');\\n positionMap[coinSymbol] = pos;\\n }\\n }\\n }\\n \\n // Create position info for each coin\\n const allPositions = [];\\n \\n for (let i = 0; i < coins.length; i++) {\\n const coin = coins[i].trim();\\n const pos = positionMap[coin];\\n \\n if (pos) {\\n // Case with position\\n try {\\n // Switch to corresponding trading pair to get ticker\\n exchange.SetCurrency(coin + \\\"_USDT\\\");\\n exchange.SetContractType(\\\"swap\\\");\\n \\n const ticker = exchange.GetTicker();\\n const currentPrice = ticker ? ticker.Last : pos.Price;\\n \\n // Get take profit and stop loss order IDs\\n const { tpOrderId, slOrderId } = getTPSLOrderIds(pos.Symbol, currentPrice, pos.Type);\\n \\n // Get exit plan\\n const exitPlan = _G(`exit_plan_${pos.Symbol}`) || {\\n profit_target: null,\\n stop_loss: null,\\n invalidation_condition: \\\"\\\"\\n };\\n \\n allPositions.push({\\n symbol: coin,\\n quantity: Math.abs(pos.Amount),\\n entry_price: pos.Price,\\n current_price: currentPrice,\\n liquidation_price: pos.Info?.liqPx ? parseFloat(pos.Info.liqPx) : \\n (pos.MarginLevel > 1 ? (pos.Type === 0 ? \\n pos.Price * (1 - 0.9/pos.MarginLevel) : \\n pos.Price * (1 + 0.9/pos.MarginLevel)) : 0),\\n unrealized_pnl: _N(pos.Profit, 2),\\n leverage: pos.MarginLevel || 1,\\n exit_plan: exitPlan,\\n confidence: exitPlan?.confidence || null,\\n risk_usd: risk_usd,\\n sl_oid: slOrderId, \\n tp_oid: tpOrderId, \\n wait_for_fill: false,\\n entry_oid: pos.Info?.posId || -1,\\n notional_usd: _N(Math.abs(pos.Amount) * currentPrice, 2)\\n });\\n } catch (e) {\\n Log(`⚠️ Failed to process ${coin} position info: ${e.message}`);\\n // Also add an empty position record in case of error\\n allPositions.push({\\n symbol: coin,\\n quantity: null,\\n entry_price: null,\\n current_price: null,\\n liquidation_price: null,\\n unrealized_pnl: null,\\n leverage: null,\\n exit_plan: null,\\n confidence: null,\\n risk_usd: risk_usd,\\n sl_oid: null,\\n tp_oid: null,\\n wait_for_fill: false,\\n entry_oid: null,\\n notional_usd: null\\n });\\n }\\n } else {\\n // Case without position - return fixed fields as null\\n allPositions.push({\\n symbol: coin,\\n quantity: null,\\n entry_price: null,\\n current_price: null,\\n liquidation_price: null,\\n unrealized_pnl: null,\\n leverage: null,\\n exit_plan: null,\\n confidence: null,\\n risk_usd: risk_usd,\\n sl_oid: null,\\n tp_oid: null,\\n wait_for_fill: false,\\n entry_oid: null,\\n notional_usd: null\\n });\\n }\\n }\\n \\n return allPositions;\\n}\\n\\nconst positions = getAllPositions();\\nreturn {positions};\",\"notice\":\"\"},\"type\":\"n8n-nodes-base.code\",\"typeVersion\":2,\"position\":[-592,-112],\"id\":\"57b65e28-cee8-4aa6-a9a2-80953af0e4c0\",\"name\":\"Position Data Fetch\"},{\"parameters\":{\"mode\":\"runOnceForAllItems\",\"language\":\"javaScript\",\"jsCode\":\"// Get input data\\nconst inputData = $input.all();\\n\\n// First input is market data, second is position data\\nconst marketData = inputData[0].json.data;\\nconst positions = inputData[1].json;\\nconst performances = inputData[2].json;\\n\\n// Return organized data\\nreturn [{\\n json: {\\n marketData: marketData,\\n positions: positions,\\n performances: performances\\n }\\n}];\\n\",\"notice\":\"\"},\"type\":\"n8n-nodes-base.code\",\"typeVersion\":2,\"position\":[-112,-112],\"id\":\"6772a19c-ce12-4846-b709-b557713546a1\",\"name\":\"Data Merge\"},{\"parameters\":{\"mode\":\"runOnceForAllItems\",\"language\":\"javaScript\",\"jsCode\":\"// ========== Streamlined Trade Execution System - Focus on Core Trading Functions ==========\\n// Functions: Parse AI signals, execute trades, historical record management (no visualization)\\n\\n// ========== Configuration Parameters ==========\\nconst CONFIG = {\\n // Trading parameters\\n MAX_POSITIONS: 6,\\n MIN_CONFIDENCE: $vars.signalConfidence,\\n DEFAULT_LEVERAGE: 10,\\n MAX_LEVERAGE: 20,\\n \\n // Risk management\\n MAX_RISK_PER_TRADE: 1000,\\n MAX_TOTAL_RISK: 5000,\\n};\\n\\n// ========== Utility Functions ==========\\n\\n/**\\n * Ultra fault-tolerant JSON parsing - extract decision objects one by one\\n */\\nfunction parseAIOutput(output) {\\n try {\\n Log('📥 Starting to parse AI output...');\\n \\n // Clean output format\\n let cleaned = output.replace(/```[a-z]*\\\\n?/gi, '').trim();\\n \\n // Method 1: Try standard JSON parsing\\n try {\\n const standardResult = parseStandardJson(cleaned);\\n if (standardResult.length > 0) {\\n Log(`✅ Standard JSON parsing succeeded, got ${standardResult.length} signals`);\\n return standardResult;\\n }\\n } catch (e) {\\n Log(`⚠️ Standard JSON parsing failed: ${e.message}`);\\n }\\n \\n // Method 2: Extract decision objects one by one\\n try {\\n const extractResult = extractDecisionObjects(cleaned);\\n if (extractResult.length > 0) {\\n Log(`✅ Object extraction parsing succeeded, got ${extractResult.length} signals`);\\n return extractResult;\\n }\\n } catch (e) {\\n Log(`⚠️ Object extraction parsing failed: ${e.message}`);\\n }\\n \\n // Method 3: Regular expression extraction\\n try {\\n const regexResult = extractWithRegex(cleaned);\\n if (regexResult.length > 0) {\\n Log(`✅ Regex parsing succeeded, got ${regexResult.length} signals`);\\n return regexResult;\\n }\\n } catch (e) {\\n Log(`⚠️ Regex parsing failed: ${e.message}`);\\n }\\n \\n // Method 4: Keyword extraction (last resort)\\n try {\\n const keywordResult = extractWithKeywords(cleaned);\\n if (keywordResult.length > 0) {\\n Log(`✅ Keyword parsing succeeded, got ${keywordResult.length} signals`);\\n return keywordResult;\\n }\\n } catch (e) {\\n Log(`⚠️ Keyword parsing failed: ${e.message}`);\\n }\\n \\n Log('❌ All parsing methods failed');\\n return [];\\n \\n } catch (e) {\\n Log(`❌ parseAIOutput overall failure: ${e.message}`);\\n return [];\\n }\\n}\\n\\n/**\\n * Method 1: Standard JSON parsing (fixed version)\\n */\\nfunction parseStandardJson(text) {\\n const jsonStart = text.indexOf('{');\\n const jsonEnd = text.lastIndexOf('}');\\n \\n if (jsonStart === -1 || jsonEnd === -1 || jsonEnd <= jsonStart) {\\n throw new Error('Cannot find JSON structure');\\n }\\n \\n let jsonStr = text.substring(jsonStart, jsonEnd + 1);\\n \\n // Fix common JSON issues\\n jsonStr = fixCommonJsonIssues(jsonStr);\\n \\n const parsed = JSON.parse(jsonStr);\\n \\n if (parsed.decisions && Array.isArray(parsed.decisions)) {\\n return parsed.decisions.filter(d => d.signal && d.coin);\\n } else if (parsed.signal && parsed.coin) {\\n return [parsed];\\n }\\n \\n return [];\\n}\\n\\n/**\\n * Method 2: Extract decision objects one by one\\n */\\nfunction extractDecisionObjects(text) {\\n const decisions = [];\\n \\n // Find all independent decision objects\\n const decisionPattern = /\\\\{\\\\s*\\\"signal\\\"[^}]*\\\"coin\\\"[^}]*\\\\}/g;\\n let match;\\n \\n while ((match = decisionPattern.exec(text)) !== null) {\\n try {\\n let decisionStr = match[0];\\n \\n // Fix this individual object\\n decisionStr = fixCommonJsonIssues(decisionStr);\\n \\n const decision = JSON.parse(decisionStr);\\n \\n // Validate required fields\\n if (decision.signal && decision.coin) {\\n // Fill in missing fields\\n if (!decision.confidence) decision.confidence = 0.5;\\n if (!decision.leverage) decision.leverage = 1;\\n if (!decision.risk_usd) decision.risk_usd = 100;\\n if (!decision.profit_target) decision.profit_target = 0;\\n if (!decision.stop_loss) decision.stop_loss = 0;\\n if (!decision.justification) decision.justification = 'Recovered from partial data';\\n \\n decisions.push(decision);\\n Log(`✅ Extracted decision: ${decision.coin} - ${decision.signal}`);\\n }\\n } catch (e) {\\n Log(`⚠️ Failed to parse individual decision object: ${e.message}`);\\n continue;\\n }\\n }\\n \\n return decisions;\\n}\\n\\n/**\\n * Method 3: Regular expression extraction of key information\\n */\\nfunction extractWithRegex(text) {\\n const decisions = [];\\n \\n // More flexible regex patterns\\n const patterns = [\\n // Pattern 1: Complete information\\n /\\\"signal\\\":\\\\s*\\\"([^\\\"]+)\\\"[^}]*\\\"coin\\\":\\\\s*\\\"([^\\\"]+)\\\"[^}]*\\\"confidence\\\":\\\\s*([0-9.]+)[^}]*\\\"leverage\\\":\\\\s*([0-9]+)[^}]*\\\"profit_target\\\":\\\\s*([0-9.]+)[^}]*\\\"stop_loss\\\":\\\\s*([0-9.]+)[^}]*\\\"risk_usd\\\":\\\\s*([0-9.]+)/g,\\n \\n // Pattern 2: Basic information\\n /\\\"coin\\\":\\\\s*\\\"([^\\\"]+)\\\"[^}]*\\\"signal\\\":\\\\s*\\\"([^\\\"]+)\\\"[^}]*\\\"confidence\\\":\\\\s*([0-9.]+)/g,\\n \\n // Pattern 3: Minimal information\\n /\\\"coin\\\":\\\\s*\\\"([^\\\"]+)\\\"[^}]*\\\"signal\\\":\\\\s*\\\"([^\\\"]+)\\\"/g\\n ];\\n \\n for (let i = 0; i < patterns.length; i++) {\\n const pattern = patterns[i];\\n let match;\\n \\n while ((match = pattern.exec(text)) !== null) {\\n let decision;\\n \\n if (i === 0) {\\n // Complete information\\n decision = {\\n signal: match[1],\\n coin: match[2],\\n confidence: parseFloat(match[3]) || 0.5,\\n leverage: parseInt(match[4]) || 1,\\n profit_target: parseFloat(match[5]) || 0,\\n stop_loss: parseFloat(match[6]) || 0,\\n risk_usd: parseFloat(match[7]) || 100,\\n justification: \\\"Regex extraction complete info\\\"\\n };\\n } else if (i === 1) {\\n // Basic information\\n decision = {\\n signal: match[2],\\n coin: match[1],\\n confidence: parseFloat(match[3]) || 0.5,\\n leverage: 1,\\n profit_target: 0,\\n stop_loss: 0,\\n risk_usd: 100,\\n justification: \\\"Regex extraction basic info\\\"\\n };\\n } else {\\n // Minimal information\\n decision = {\\n signal: match[2],\\n coin: match[1],\\n confidence: 0.5,\\n leverage: 1,\\n profit_target: 0,\\n stop_loss: 0,\\n risk_usd: 100,\\n justification: \\\"Regex extraction minimal info\\\"\\n };\\n }\\n \\n // Avoid duplicates\\n if (!decisions.find(d => d.coin === decision.coin)) {\\n decisions.push(decision);\\n Log(`✅ Regex extraction: ${decision.coin} - ${decision.signal}`);\\n }\\n }\\n \\n // If signals found, don't try next pattern\\n if (decisions.length > 0) break;\\n }\\n \\n return decisions;\\n}\\n\\n/**\\n * Method 4: Keyword extraction (last resort)\\n */\\nfunction extractWithKeywords(text) {\\n const decisions = [];\\n const lines = text.split('\\\\n');\\n \\n let currentDecision = null;\\n \\n for (let line of lines) {\\n line = line.trim();\\n \\n // Find coin information\\n const coinMatch = line.match(/[\\\"']coin[\\\"']:\\\\s*[\\\"']([A-Z]+)[\\\"']/i);\\n if (coinMatch) {\\n if (currentDecision) {\\n decisions.push(currentDecision);\\n }\\n currentDecision = {\\n coin: coinMatch[1],\\n signal: 'hold',\\n confidence: 0.5,\\n leverage: 1,\\n profit_target: 0,\\n stop_loss: 0,\\n risk_usd: 100,\\n justification: \\\"Keyword extraction\\\"\\n };\\n }\\n \\n // Find signal information\\n const signalMatch = line.match(/[\\\"']signal[\\\"']:\\\\s*[\\\"'](buy_to_enter|sell_to_enter|close|hold)[\\\"']/i);\\n if (signalMatch && currentDecision) {\\n currentDecision.signal = signalMatch[1];\\n }\\n \\n // Find confidence information\\n const confidenceMatch = line.match(/[\\\"']confidence[\\\"']:\\\\s*([0-9.]+)/i);\\n if (confidenceMatch && currentDecision) {\\n currentDecision.confidence = parseFloat(confidenceMatch[1]);\\n }\\n \\n // Find other numerical information\\n const leverageMatch = line.match(/[\\\"']leverage[\\\"']:\\\\s*([0-9]+)/i);\\n if (leverageMatch && currentDecision) {\\n currentDecision.leverage = parseInt(leverageMatch[1]);\\n }\\n \\n const profitMatch = line.match(/[\\\"']profit_target[\\\"']:\\\\s*([0-9.]+)/i);\\n if (profitMatch && currentDecision) {\\n currentDecision.profit_target = parseFloat(profitMatch[1]);\\n }\\n \\n const stopMatch = line.match(/[\\\"']stop_loss[\\\"']:\\\\s*([0-9.]+)/i);\\n if (stopMatch && currentDecision) {\\n currentDecision.stop_loss = parseFloat(stopMatch[1]);\\n }\\n \\n const riskMatch = line.match(/[\\\"']risk_usd[\\\"']:\\\\s*([0-9.]+)/i);\\n if (riskMatch && currentDecision) {\\n currentDecision.risk_usd = parseFloat(riskMatch[1]);\\n }\\n }\\n \\n // Add the last decision\\n if (currentDecision) {\\n decisions.push(currentDecision);\\n }\\n \\n return decisions.filter(d => d.coin && d.signal);\\n}\\n\\n/**\\n * Fix common JSON issues\\n */\\nfunction fixCommonJsonIssues(jsonStr) {\\n // 1. Fix property names missing quotes\\n jsonStr = jsonStr.replace(/([{,]\\\\s*)([a-zA-Z_][a-zA-Z0-9_]*)\\\\s*:/g, '$1\\\"$2\\\":');\\n \\n // 2. Fix single quotes\\n jsonStr = jsonStr.replace(/'/g, '\\\"');\\n \\n // 3. Fix trailing commas\\n jsonStr = jsonStr.replace(/,(\\\\s*[}\\\\]])/g, '$1');\\n \\n // 4. Fix extra quotes\\n jsonStr = jsonStr.replace(/\\\"{2,}/g, '\\\"');\\n \\n // 5. Fix extra quotes before numbers\\n jsonStr = jsonStr.replace(/\\\"([0-9.]+)\\\"/g, '$1');\\n \\n // 6. Ensure string values have quotes\\n jsonStr = jsonStr.replace(/:\\\\s*([a-zA-Z_][a-zA-Z0-9_]*)\\\\s*([,}])/g, ': \\\"$1\\\"$2');\\n \\n // 7. Fix boolean values\\n jsonStr = jsonStr.replace(/:\\\\s*\\\"(true|false|null)\\\"\\\\s*([,}])/g, ': $1$2');\\n \\n return jsonStr;\\n}\\n\\n/**\\n * Get trading pair precision information - includes contract value\\n */\\nfunction getPrecision(coin) {\\n try {\\n const symbol = coin + '_USDT.swap';\\n \\n const markets = _G('markets');\\n \\n if (markets && markets[symbol]) {\\n const market = markets[symbol];\\n \\n return {\\n price: market.PricePrecision,\\n amount: market.AmountPrecision,\\n minQty: market.MinQty,\\n maxQty: market.MaxQty,\\n tickSize: market.TickSize,\\n amountSize: market.AmountSize,\\n minNotional: market.MinNotional,\\n ctVal: market.CtVal, // Contract value\\n symbol: symbol\\n };\\n }\\n \\n // If market info not found, return null or default values\\n Log(`⚠️ Market info not found for ${symbol}`);\\n return null;\\n \\n } catch (e) {\\n Log(`⚠️ Failed to get ${coin} precision: ${e.message}`);\\n return null;\\n }\\n}\\n\\n/**\\n * Calculate trade quantity - fixed version, supports contract value\\n */\\nfunction calculateQuantity(entryPrice, stopLoss, riskUsd, leverage, precision) {\\n try {\\n // 1. Calculate stop loss percentage\\n const stopLossPercent = Math.abs(entryPrice - stopLoss) / entryPrice;\\n \\n if (stopLossPercent <= 0 || stopLossPercent > 0.5) {\\n Log(`⚠️ Abnormal stop loss percentage: ${(stopLossPercent * 100).toFixed(2)}%`);\\n return 0;\\n }\\n \\n // 2. Calculate position value (based on risk amount)\\n // Formula: Position value = Risk amount / Stop loss percentage\\n const positionValue = riskUsd / stopLossPercent;\\n \\n // 3. Calculate coin quantity\\n // Formula: Coin quantity = Position value / Entry price\\n let coinQuantity = positionValue / entryPrice;\\n \\n // 4. Convert to contract quantity\\n // Formula: Contract quantity = Coin quantity / Contract value\\n let contractQuantity = coinQuantity / precision.ctVal;\\n \\n // 5. Consider leverage limits (account capital constraint)\\n const account = exchange.GetAccount();\\n const maxPositionValue = (account.Balance * leverage * 0.8); // 80% safety margin\\n const maxCoinQuantity = maxPositionValue / entryPrice;\\n const maxContractQuantity = maxCoinQuantity / precision.ctVal;\\n \\n contractQuantity = Math.min(contractQuantity, maxContractQuantity);\\n \\n // 6. Apply precision\\n contractQuantity = _N(contractQuantity, precision.amount);\\n \\n // 7. Check minimum quantity\\n const minContracts = precision.minQty / precision.ctVal;\\n if (contractQuantity < minContracts) {\\n Log(`⚠️ Calculated contract quantity ${contractQuantity} is less than minimum ${minContracts}`);\\n return 0;\\n }\\n \\n // 8. Check maximum quantity\\n const maxContracts = precision.maxQty / precision.ctVal;\\n if (contractQuantity > maxContracts) {\\n Log(`⚠️ Calculated contract quantity ${contractQuantity} exceeds maximum ${maxContracts}`);\\n contractQuantity = maxContracts;\\n }\\n \\n // 9. Verify actual risk and position value\\n const actualCoinQuantity = contractQuantity * precision.ctVal;\\n const actualPositionValue = actualCoinQuantity * entryPrice;\\n const actualRisk = actualPositionValue * stopLossPercent;\\n \\n // 10. Secondary risk check\\n if (actualRisk > CONFIG.MAX_RISK_PER_TRADE) {\\n Log(`⚠️ Actual risk $${actualRisk.toFixed(2)} exceeds max risk $${CONFIG.MAX_RISK_PER_TRADE}`);\\n const adjustedPositionValue = CONFIG.MAX_RISK_PER_TRADE / stopLossPercent;\\n const adjustedCoinQuantity = adjustedPositionValue / entryPrice;\\n contractQuantity = adjustedCoinQuantity / precision.ctVal;\\n contractQuantity = _N(contractQuantity, precision.amount);\\n }\\n \\n // 11. Log calculation details\\n Log(`💡 Price ${entryPrice} Risk ${_N(actualRisk, 2)} Quantity ${contractQuantity}`);\\n \\n return contractQuantity;\\n \\n } catch (e) {\\n Log(`❌ Failed to calculate quantity: ${e.message}`);\\n return 0;\\n }\\n}\\n\\n/**\\n * Validate entry conditions\\n */\\nfunction validateEntry(coin, signal, currentPrice, profitTarget, stopLoss, confidence) {\\n // Validate confidence\\n if (confidence < CONFIG.MIN_CONFIDENCE) {\\n Log(`⚠️ ${coin}: Confidence ${confidence} below minimum ${CONFIG.MIN_CONFIDENCE}`);\\n return false;\\n }\\n \\n // Validate price logic\\n const isLong = signal === 'buy_to_enter';\\n \\n if (isLong) {\\n if (profitTarget <= currentPrice) {\\n Log(`⚠️ ${coin}: Long profit target ${profitTarget} should be above current price ${currentPrice}`);\\n return false;\\n }\\n if (stopLoss >= currentPrice) {\\n Log(`⚠️ ${coin}: Long stop loss ${stopLoss} should be below current price ${currentPrice}`);\\n return false;\\n }\\n } else if (signal === 'sell_to_enter') {\\n if (profitTarget >= currentPrice) {\\n Log(`⚠️ ${coin}: Short profit target ${profitTarget} should be below current price ${currentPrice}`);\\n return false;\\n }\\n if (stopLoss <= currentPrice) {\\n Log(`⚠️ ${coin}: Short stop loss ${stopLoss} should be above current price ${currentPrice}`);\\n return false;\\n }\\n }\\n \\n return true;\\n}\\n\\n/**\\n * Check if there is a position\\n */\\nfunction hasPosition(coin) {\\n try {\\n exchange.SetCurrency(coin + '_USDT');\\n exchange.SetContractType(\\\"swap\\\");\\n const positions = exchange.GetPositions();\\n return positions && positions.some(p => p.Symbol.includes(coin) && Math.abs(p.Amount) > 0);\\n } catch (e) {\\n Log(`⚠️ Failed to check ${coin} position: ${e.message}`);\\n return false;\\n }\\n}\\n\\n/**\\n * Save trade record\\n */\\nfunction saveTradeToHistory(orderDetail, signalInfo) {\\n try {\\n const currentTime = Date.now();\\n \\n // Extract coin name\\n const coinMatch = orderDetail.Symbol.match(/^([A-Z0-9]+)_USDT/);\\n if (!coinMatch) {\\n Log(`⚠️ Cannot parse coin: ${orderDetail.Symbol}`);\\n return;\\n }\\n const coin = coinMatch[1];\\n \\n // Build trade record\\n const tradeRecord = {\\n // Order information\\n Id: orderDetail.Id,\\n Price: orderDetail.Price,\\n Amount: orderDetail.Amount,\\n DealAmount: orderDetail.DealAmount,\\n AvgPrice: orderDetail.AvgPrice,\\n Status: orderDetail.Status,\\n Type: orderDetail.Type,\\n Offset: orderDetail.Offset,\\n Symbol: orderDetail.Symbol,\\n ContractType: orderDetail.ContractType,\\n \\n // AI signal information\\n aiSignal: signalInfo.signal,\\n confidence: signalInfo.confidence,\\n profitTarget: signalInfo.profit_target,\\n stopLoss: signalInfo.stop_loss,\\n leverage: signalInfo.leverage,\\n riskUsd: signalInfo.risk_usd,\\n justification: signalInfo.justification,\\n \\n // Timestamp\\n time: currentTime,\\n timeStr: new Date(currentTime).toISOString()\\n };\\n \\n // Get existing history\\n let tradesHistory = _G('tradesHistory') || {};\\n \\n // Initialize coin record\\n if (!tradesHistory[coin]) {\\n tradesHistory[coin] = [];\\n }\\n \\n // Add record\\n tradesHistory[coin].push(tradeRecord);\\n \\n // Limit history size (max 1000 records per coin)\\n if (tradesHistory[coin].length > 1000) {\\n tradesHistory[coin] = tradesHistory[coin].slice(-1000);\\n }\\n \\n // Save\\n _G('tradesHistory', tradesHistory);\\n \\n const typeStr = orderDetail.Type === 0 ? 'Buy' : 'Sell';\\n Log(`📝 ${coin} trade record saved: ${typeStr} ${orderDetail.DealAmount} @ $${orderDetail.AvgPrice}`);\\n \\n } catch (e) {\\n Log(`❌ Failed to save trade record: ${e.message}`);\\n }\\n}\\n\\n/**\\n * Save exit plan\\n */\\nfunction saveExitPlan(coin, signalInfo) {\\n try {\\n const exitPlan = {\\n profit_target: signalInfo.profit_target,\\n stop_loss: signalInfo.stop_loss,\\n invalidation_condition: signalInfo.invalidation_condition || \\\"\\\",\\n confidence: signalInfo.confidence,\\n risk_usd: signalInfo.risk_usd,\\n leverage: signalInfo.leverage,\\n timestamp: Date.now(),\\n justification: signalInfo.justification\\n };\\n \\n _G(`exit_plan_${coin}_USDT.swap`, exitPlan);\\n Log(`💾 ${coin} exit plan saved`);\\n } catch (e) {\\n Log(`❌ Failed to save ${coin} exit plan: ${e.message}`);\\n }\\n}\\n\\n// ========== Trade Execution Functions ==========\\n\\n/**\\n * Execute close position\\n */\\nfunction executeClose(coin, reason = \\\"AI Signal\\\") {\\n try {\\n exchange.SetCurrency(coin + '_USDT');\\n exchange.SetContractType(\\\"swap\\\");\\n \\n // Cancel all pending orders\\n const orders = exchange.GetOrders();\\n if (orders && orders.length > 0) {\\n orders.forEach(o => {\\n try {\\n exchange.CancelOrder(o.Id);\\n } catch (e) {\\n Log(`⚠️ Failed to cancel order: ${e.message}`);\\n }\\n });\\n }\\n \\n // Find position\\n const positions = exchange.GetPositions();\\n const pos = positions && positions.find(p => p.Symbol.includes(coin) && Math.abs(p.Amount) > 0);\\n \\n if (!pos) {\\n Log(`⚠️ ${coin}: Position not found`);\\n return false;\\n }\\n \\n const isLong = pos.Type === PD_LONG || pos.Type === 0;\\n const precision = getPrecision(coin);\\n const closeAmount = _N(Math.abs(pos.Amount), precision.amount);\\n \\n // Set close direction\\n exchange.SetDirection(isLong ? \\\"closebuy\\\" : \\\"closesell\\\");\\n \\n // Execute close\\n const orderId = isLong ? exchange.Sell(-1, closeAmount) : exchange.Buy(-1, closeAmount);\\n \\n if (orderId) {\\n const directionStr = isLong ? 'Long' : 'Short';\\n Log(`✅ ${coin}: Close ${directionStr} successful (${reason}) Quantity=${closeAmount}`);\\n \\n // Clear exit plan\\n _G(`exit_plan_${coin}_USDT.swap`, null);\\n \\n // Get order details and save\\n Sleep(1000);\\n try {\\n const orderDetail = exchange.GetOrder(orderId);\\n if (orderDetail) {\\n saveTradeToHistory(orderDetail, { \\n signal: 'close', \\n confidence: 1.0, \\n justification: reason \\n });\\n }\\n } catch (e) {\\n Log(`⚠️ Failed to get close order details: ${e.message}`);\\n }\\n \\n return true;\\n } else {\\n Log(`❌ ${coin}: Close failed`);\\n return false;\\n }\\n } catch (e) {\\n Log(`❌ ${coin} close execution failed: ${e.message}`);\\n return false;\\n }\\n}\\n\\n/**\\n * Execute entry\\n */\\nfunction executeEntry(coin, signalInfo) {\\n try {\\n exchange.SetCurrency(coin + '_USDT');\\n exchange.SetContractType(\\\"swap\\\");\\n \\n // Get current price\\n const ticker = exchange.GetTicker();\\n if (!ticker) {\\n Log(`❌ ${coin}: Cannot get price info`);\\n return false;\\n }\\n \\n const currentPrice = ticker.Last;\\n const isLong = signalInfo.signal === 'buy_to_enter';\\n \\n // Validate entry conditions\\n if (!validateEntry(coin, signalInfo.signal, currentPrice, signalInfo.profit_target, signalInfo.stop_loss, signalInfo.confidence)) {\\n return false;\\n }\\n \\n // Set leverage\\n const leverage = Math.min(signalInfo.leverage || CONFIG.DEFAULT_LEVERAGE, CONFIG.MAX_LEVERAGE);\\n exchange.SetMarginLevel(leverage);\\n \\n // Get precision info\\n const precision = getPrecision(coin);\\n \\n // Calculate quantity\\n const quantity = calculateQuantity(\\n currentPrice, \\n signalInfo.stop_loss, \\n signalInfo.risk_usd, \\n leverage, \\n precision\\n );\\n \\n if (quantity <= 0) {\\n Log(`⚠️ ${coin}: Invalid calculated quantity, skipping entry`);\\n return false;\\n }\\n \\n // Set trade direction\\n exchange.SetDirection(isLong ? \\\"buy\\\" : \\\"sell\\\");\\n \\n // Execute entry\\n const orderId = isLong ? exchange.Buy(-1, quantity) : exchange.Sell(-1, quantity);\\n \\n if (orderId) {\\n const directionStr = isLong ? 'Long' : 'Short';\\n Log(`✅ ${coin}: ${directionStr} entry successful Quantity=${quantity} Leverage=${leverage}x Risk=$${signalInfo.risk_usd} Confidence=${signalInfo.confidence}`);\\n \\n // Save exit plan\\n saveExitPlan(coin, signalInfo);\\n \\n // Get order details and save\\n Sleep(1000);\\n try {\\n const orderDetail = exchange.GetOrder(orderId);\\n if (orderDetail) {\\n saveTradeToHistory(orderDetail, signalInfo);\\n }\\n } catch (e) {\\n Log(`⚠️ Failed to get entry order details: ${e.message}`);\\n }\\n \\n return true;\\n } else {\\n Log(`❌ ${coin}: Entry failed`);\\n return false;\\n }\\n } catch (e) {\\n Log(`❌ ${coin} entry execution failed: ${e.message}`);\\n return false;\\n }\\n}\\n\\n// ========== Main Execution Logic ==========\\n\\nfunction main() {\\n try {\\n // Get AI output\\n const aiOutput = $input.first().json.output;\\n Log('Debug aiOutput:', aiOutput);\\n \\n const signals = parseAIOutput(aiOutput);\\n \\n _G('AISinnal', signals)\\n \\n Log('🤖 AI signal parsing result:', signals);\\n \\n // Exit if no valid signals\\n if (!signals || signals.length === 0) {\\n Log('⚠️ No valid AI trading signals received');\\n \\n // Save empty execution result\\n _G('latestExecutionResults', {\\n results: {},\\n timestamp: Date.now(),\\n totalSignals: 0,\\n totalExecutions: 0,\\n note: 'No valid signals'\\n });\\n \\n return { json: { processed: false, reason: 'No valid signals' } };\\n }\\n \\n // Execute trading signals\\n const executionResults = {};\\n let totalExecutions = 0;\\n \\n // Process each coin's signal\\n for (let i = 0; i < signals.length; i++) {\\n const signalInfo = signals[i];\\n const coin = signalInfo.coin;\\n const signal = signalInfo.signal;\\n \\n if (!coin || !signal) {\\n Log(`⚠️ Skipping invalid signal: ${JSON.stringify(signalInfo)}`);\\n continue;\\n }\\n \\n Log(`🎯 Processing ${coin}'s ${signal} signal`);\\n \\n const hasPos = hasPosition(coin);\\n let executed = false;\\n let skipReason = '';\\n \\n // Execute based on signal type\\n switch (signal) {\\n case 'buy_to_enter':\\n case 'sell_to_enter':\\n if (hasPos) {\\n skipReason = 'Already has position';\\n } else {\\n executed = executeEntry(coin, signalInfo);\\n if (executed) totalExecutions++;\\n }\\n break;\\n \\n case 'close':\\n if (!hasPos) {\\n skipReason = 'No position';\\n } else {\\n executed = executeClose(coin, 'AI Signal');\\n if (executed) totalExecutions++;\\n }\\n break;\\n \\n case 'hold':\\n skipReason = hasPos ? 'Hold position' : 'Wait';\\n break;\\n \\n default:\\n skipReason = 'Unknown signal';\\n }\\n \\n // Record execution result\\n executionResults[coin] = {\\n signal: signal,\\n executed: executed,\\n skipReason: skipReason,\\n confidence: signalInfo.confidence || 0,\\n timestamp: Date.now(),\\n hasPosition: hasPos,\\n // Add more execution details\\n leverage: signalInfo.leverage || 0,\\n risk_usd: signalInfo.risk_usd || 0,\\n profit_target: signalInfo.profit_target || 0,\\n stop_loss: signalInfo.stop_loss || 0,\\n justification: signalInfo.justification || ''\\n };\\n \\n // Avoid too frequent requests\\n Sleep(500);\\n }\\n \\n // ===== Save execution results to _G =====\\n const executionSummary = {\\n results: executionResults,\\n timestamp: Date.now(),\\n totalSignals: signals.length,\\n totalExecutions: totalExecutions,\\n // Statistics\\n stats: {\\n executed: Object.values(executionResults).filter(r => r.executed).length,\\n skipped: Object.values(executionResults).filter(r => !r.executed && r.skipReason).length,\\n failed: Object.values(executionResults).filter(r => !r.executed && !r.skipReason).length,\\n holds: Object.values(executionResults).filter(r => r.signal === 'hold').length,\\n entries: Object.values(executionResults).filter(r => ['buy_to_enter', 'sell_to_enter'].includes(r.signal)).length,\\n closes: Object.values(executionResults).filter(r => r.signal === 'close').length\\n }\\n };\\n \\n _G('latestExecutionResults', executionSummary);\\n \\n Log(`💾 Execution results saved to global storage`);\\n Log(`📊 Execution summary: Processed ${signals.length} signals, executed ${totalExecutions} operations`);\\n \\n // Detailed output of execution results\\n Object.entries(executionResults).forEach(([coin, result]) => {\\n if (result.executed) {\\n Log(`✅ ${coin}: ${result.signal} executed successfully (Confidence: ${(result.confidence * 100).toFixed(0)}%)`);\\n } else if (result.skipReason) {\\n Log(`⏭️ ${coin}: ${result.signal} skipped - ${result.skipReason}`);\\n } else {\\n Log(`❌ ${coin}: ${result.signal} execution failed`);\\n }\\n });\\n \\n return { \\n json: { \\n processed: true, \\n totalSignals: signals.length,\\n totalExecutions: totalExecutions,\\n results: executionResults,\\n summary: executionSummary.stats\\n } \\n };\\n \\n } catch (e) {\\n Log(`❌ Main execution flow failed: ${e.message}`);\\n Log(`❌ Error stack: ${e.stack}`);\\n \\n // Save error info even if error occurs\\n _G('latestExecutionResults', {\\n results: {},\\n timestamp: Date.now(),\\n totalSignals: 0,\\n totalExecutions: 0,\\n error: e.message,\\n note: 'Execution failed'\\n });\\n \\n return { json: { processed: false, error: e.message } };\\n }\\n}\\n\\n// Execute main function\\nreturn main();\",\"notice\":\"\"},\"type\":\"n8n-nodes-base.code\",\"typeVersion\":2,\"position\":[448,-112],\"id\":\"7bee234f-ac4c-4474-a7f7-b540887045be\",\"name\":\"Trade Execution\"},{\"parameters\":{\"mode\":\"runOnceForAllItems\",\"language\":\"javaScript\",\"jsCode\":\"function analyzePerformance(orders, coin) {\\n if (!orders || orders.length === 0) return { coin, totalTrades: 0, message: 'No stored trade records' }\\n \\n const filteredOrders = orders.filter(o => o.Status === 1)\\n const validOrders = filteredOrders.sort((a, b) => a.time - b.time)\\n \\n if (validOrders.length === 0) return { coin, totalTrades: 0}\\n \\n // Get contract specifications\\n function getContractSpec(coinSymbol) {\\n try {\\n const markets = _G('markets');\\n const symbol = coinSymbol + '_USDT.swap';\\n \\n if (markets && markets[symbol]) {\\n const ctVal = markets[symbol].CtVal || 1; // Contract value, default is 1\\n return ctVal;\\n } else {\\n return 1;\\n }\\n } catch (e) {\\n return 1;\\n }\\n }\\n \\n // Extract coin name (BTC_USDT.swap -> BTC)\\n const coinMatch = coin.match(/^([A-Z0-9]+)_USDT/);\\n const coinName = coinMatch ? coinMatch[1] : coin;\\n \\n // Get contract value for this coin\\n const ctVal = getContractSpec(coinName);\\n \\n let trades = []\\n let positions = [] // Open positions\\n \\n for (let order of validOrders) {\\n // Note: Data structure read from _G('tradesHistory')\\n const { Type: type, DealAmount: amount, AvgPrice: price, time, Id: id } = order\\n \\n // Find opposite direction positions for matching\\n let matched = false\\n let remaining = amount\\n \\n for (let i = 0; i < positions.length && remaining > 0.0001; i++) {\\n const pos = positions[i]\\n \\n // Check if can be matched: opposite buy/sell direction\\n if ((type === 0 && pos.type === 1) || (type === 1 && pos.type === 0)) {\\n const tradeAmount = Math.min(remaining, pos.amount)\\n const holdTime = time - pos.time\\n \\n // Corrected profit calculation: consider contract value\\n let profit, direction\\n if (pos.type === 0) { // Previously bought to open, now selling to close\\n // Long profit = (Close price - Open price) * Contract quantity * Contract value\\n profit = (price - pos.price) * tradeAmount * ctVal\\n direction = 'LONG'\\n } else { // Previously sold to open, now buying to close\\n // Short profit = (Open price - Close price) * Contract quantity * Contract value\\n profit = (pos.price - price) * tradeAmount * ctVal\\n direction = 'SHORT'\\n }\\n \\n \\n trades.push({\\n direction,\\n openPrice: pos.price,\\n closePrice: price,\\n amount: tradeAmount,\\n profit,\\n holdTime: holdTime,\\n openOrderId: pos.id,\\n closeOrderId: id,\\n ctVal: ctVal, // Record contract value for verification\\n closeTime: time // Add close time for calculating consecutive losses\\n })\\n \\n // Update position\\n pos.amount -= tradeAmount\\n remaining -= tradeAmount\\n matched = true\\n \\n // If position fully closed, remove it\\n if (pos.amount <= 0.0001) {\\n positions.splice(i, 1)\\n i-- // Adjust index\\n }\\n }\\n }\\n \\n // If there's remaining quantity, treat as new opening\\n if (remaining > 0.0001) {\\n positions.push({\\n type: type,\\n price: price,\\n amount: remaining,\\n time: time,\\n id: id\\n })\\n }\\n }\\n \\n if (trades.length === 0) return { \\n coin, \\n totalTrades: 0, \\n openPositions: positions.length,\\n totalOrders: validOrders.length,\\n ctVal: ctVal, // Return contract value info\\n consecutiveLosses: 0,\\n freezeStatus: 'free'\\n }\\n \\n // Sort trade records by close time to ensure correct time sequence\\n trades.sort((a, b) => a.closeTime - b.closeTime)\\n \\n // ======= Modified: Calculate consecutive losses within the last 4 hours =======\\n function calculateRecentConsecutiveLosses(tradesArray) {\\n const currentTime = Date.now()\\n const fourHoursAgo = currentTime - (4 * 60 * 60 * 1000) // Timestamp from 4 hours ago\\n \\n // Filter trades from the last 4 hours\\n const recentTrades = tradesArray.filter(trade => trade.closeTime >= fourHoursAgo)\\n \\n if (recentTrades.length === 0) {\\n return 0 // No trades in the last 4 hours\\n }\\n \\n // Sort by time (ensure correct time sequence)\\n recentTrades.sort((a, b) => a.closeTime - b.closeTime)\\n \\n let consecutiveLosses = 0\\n // Calculate consecutive losses from the last trade backwards\\n for (let i = recentTrades.length - 1; i >= 0; i--) {\\n if (recentTrades[i].profit <= 0) {\\n consecutiveLosses++\\n } else {\\n break // Stop when encountering a profitable trade\\n }\\n }\\n \\n Log(`💡 ${coinName} Last 4 hours trades: ${recentTrades.length} times, Consecutive losses: ${consecutiveLosses} times`)\\n return consecutiveLosses\\n }\\n \\n const consecutiveLosses = calculateRecentConsecutiveLosses(trades)\\n \\n // Cooldown mechanism - based on consecutive losses in the last 4 hours\\n const currentTime = Date.now()\\n let freezeStatus = 'free'\\n let freezeStartTime = null\\n let freezeEndTime = null\\n \\n if (consecutiveLosses > 2) {\\n // Check if already in cooldown period\\n const frozenData = _G(`frozen_${coinName}`)\\n \\n if (frozenData && frozenData.freezeEndTime > currentTime) {\\n // Still in cooldown period\\n freezeStatus = 'frozen'\\n freezeStartTime = frozenData.freezeStartTime\\n freezeEndTime = frozenData.freezeEndTime\\n Log(`🧊 ${coinName} Still in cooldown period, ${((freezeEndTime - currentTime) / (1000 * 60 * 60)).toFixed(2)} hours remaining`)\\n } else {\\n // Check if this is a new consecutive loss (prevent duplicate cooldown)\\n const lastTriggerLosses = frozenData ? frozenData.triggerLosses : 0\\n \\n if (consecutiveLosses > lastTriggerLosses || !frozenData) {\\n // New cooldown period starts\\n freezeStatus = 'frozen'\\n freezeStartTime = currentTime\\n freezeEndTime = currentTime + (4 * 60 * 60 * 1000) // Unfreeze after 4 hours\\n \\n // Save cooldown info\\n _G(`frozen_${coinName}`, {\\n freezeStartTime: freezeStartTime,\\n freezeEndTime: freezeEndTime,\\n triggerLosses: consecutiveLosses,\\n triggerTime: currentTime\\n })\\n \\n Log(`🧊 ${coinName} Entering cooldown period: ${consecutiveLosses} consecutive losses in last 4 hours, will unfreeze in 4 hours`)\\n } else {\\n // Consecutive loss count hasn't increased, maintain unfrozen status\\n freezeStatus = 'free'\\n _G(`frozen_${coinName}`, null)\\n Log(`✅ ${coinName} Cooldown period expired, unfreezing`)\\n }\\n }\\n } else {\\n // Insufficient consecutive losses, clear cooldown record\\n const frozenData = _G(`frozen_${coinName}`)\\n if (frozenData && frozenData.freezeEndTime <= currentTime) {\\n _G(`frozen_${coinName}`, null)\\n Log(`✅ ${coinName} Only ${consecutiveLosses} consecutive losses in last 4 hours, unfreezing`)\\n } else if (!frozenData) {\\n // No cooldown record, maintain free status\\n freezeStatus = 'free'\\n } else {\\n // Still in cooldown period but consecutive losses have decreased, continue cooldown until time expires\\n freezeStatus = 'frozen'\\n freezeStartTime = frozenData.freezeStartTime\\n freezeEndTime = frozenData.freezeEndTime\\n }\\n }\\n \\n const wins = trades.filter(t => t.profit > 0)\\n const losses = trades.filter(t => t.profit <= 0)\\n const longTrades = trades.filter(t => t.direction === 'LONG')\\n const shortTrades = trades.filter(t => t.direction === 'SHORT')\\n \\n // Long position statistics\\n const longWins = longTrades.filter(t => t.profit > 0)\\n const longLosses = longTrades.filter(t => t.profit <= 0)\\n const longWinProfit = longWins.reduce((sum, t) => sum + t.profit, 0)\\n const longLossProfit = Math.abs(longLosses.reduce((sum, t) => sum + t.profit, 0))\\n \\n // Short position statistics\\n const shortWins = shortTrades.filter(t => t.profit > 0)\\n const shortLosses = shortTrades.filter(t => t.profit <= 0)\\n const shortWinProfit = shortWins.reduce((sum, t) => sum + t.profit, 0)\\n const shortLossProfit = Math.abs(shortLosses.reduce((sum, t) => sum + t.profit, 0))\\n \\n const totalProfit = trades.reduce((sum, t) => sum + t.profit, 0)\\n const totalWinProfit = wins.reduce((sum, t) => sum + t.profit, 0)\\n const totalLossProfit = Math.abs(losses.reduce((sum, t) => sum + t.profit, 0))\\n const winRate = (wins.length / trades.length * 100)\\n \\n \\n const result = {\\n coin,\\n totalTrades: trades.length,\\n totalOrders: validOrders.length,\\n openPositions: positions.length,\\n totalProfit: parseFloat(totalProfit.toFixed(2)),\\n winRate: parseFloat(winRate.toFixed(2)),\\n winCount: wins.length,\\n lossCount: losses.length,\\n avgProfit: parseFloat((totalProfit / trades.length).toFixed(2)),\\n maxProfit: wins.length > 0 ? parseFloat(Math.max(...wins.map(t => t.profit)).toFixed(2)) : 0,\\n maxLoss: parseFloat(Math.min(...trades.map(t => t.profit)).toFixed(2)),\\n \\n // Profit/Loss ratio\\n profitLossRatio: totalLossProfit > 0 ? parseFloat((totalWinProfit / totalLossProfit).toFixed(2)) : (totalWinProfit > 0 ? 999 : 0),\\n \\n // Long position statistics\\n longWinCount: longWins.length,\\n longLossCount: longLosses.length,\\n longWinProfit: parseFloat(longWinProfit.toFixed(2)),\\n longLossProfit: parseFloat(longLossProfit.toFixed(2)),\\n \\n // Short position statistics \\n shortWinCount: shortWins.length,\\n shortLossCount: shortLosses.length,\\n shortWinProfit: parseFloat(shortWinProfit.toFixed(2)),\\n shortLossProfit: parseFloat(shortLossProfit.toFixed(2)),\\n \\n // Holding time statistics\\n avgHoldTimeHours: trades.length > 0 ? parseFloat((trades.reduce((sum, t) => sum + t.holdTime, 0) / trades.length / (1000 * 60 * 60)).toFixed(2)) : 0,\\n maxHoldTimeHours: trades.length > 0 ? parseFloat((Math.max(...trades.map(t => t.holdTime)) / (1000 * 60 * 60)).toFixed(2)) : 0,\\n minHoldTimeHours: trades.length > 0 ? parseFloat((Math.min(...trades.map(t => t.holdTime)) / (1000 * 60 * 60)).toFixed(2)) : 0,\\n \\n // Add contract specifications\\n ctVal: ctVal,\\n \\n // Modified: Based on consecutive losses in last 4 hours and cooldown mechanism\\n consecutiveLosses: consecutiveLosses, // Now this is consecutive losses in last 4 hours\\n recentLossesDescription: `${consecutiveLosses} consecutive losses in last 4 hours`,\\n freezeStatus: freezeStatus, // 'free' or 'frozen'\\n freezeStartTime: freezeStartTime,\\n freezeEndTime: freezeEndTime\\n }\\n \\n // If in cooldown period, add remaining cooldown time info\\n if (freezeStatus === 'frozen') {\\n const remainingFreezeHours = parseFloat(((freezeEndTime - currentTime) / (1000 * 60 * 60)).toFixed(2))\\n result.remainingFreezeHours = Math.max(0, remainingFreezeHours)\\n }\\n \\n return result\\n}\\n\\nconst coins = $input.first().json.coins\\nconst results = {}\\n\\n// Get trading history from _G('tradesHistory')\\nconst tradesHistory = _G('tradesHistory') || {}\\n\\nLog('📊 Starting analysis of trading performance in the last 4 hours...')\\n\\nfor (let coin of coins) {\\n const symbol = coin + '_USDT.swap'\\n try {\\n // Get trade records for this coin\\n const orders = tradesHistory[coin] || []\\n \\n results[symbol] = analyzePerformance(orders, symbol)\\n } catch (e) {\\n results[symbol] = { error: e.toString() }\\n }\\n}\\n\\n// Sort by profit\\nconst sorted = Object.values(results)\\n .filter(r => !r.error && r.totalTrades > 0)\\n .sort((a, b) => b.totalProfit - a.totalProfit)\\n\\n// Statistics on cooldown status\\nconst frozenCoins = sorted.filter(r => r.freezeStatus === 'frozen')\\nconst freeCoins = sorted.filter(r => r.freezeStatus === 'free')\\n\\nLog(`🧊 Cooldown status statistics (based on last 4 hours):`)\\nLog(` Frozen coins: ${frozenCoins.length}`)\\nLog(` Tradeable coins: ${freeCoins.length}`)\\n\\nif (frozenCoins.length > 0) {\\n Log(`❄️ Frozen coins (consecutive losses in last 4 hours):`)\\n frozenCoins.forEach(coin => {\\n const coinName = coin.coin.replace('_USDT.swap', '')\\n Log(` ${coinName}: ${coin.recentLossesDescription}, ${coin.remainingFreezeHours || 0} hours until unfreeze`)\\n })\\n}\\n\\nLog('Coin Performance Statistics Node:',{\\n sorted: sorted,\\n summary: {\\n totalProfit: sorted.reduce((sum, r) => sum + r.totalProfit, 0).toFixed(2),\\n avgWinRate: sorted.length > 0 ? (sorted.reduce((sum, r) => sum + r.winRate, 0) / sorted.length).toFixed(2) : 0,\\n totalRecords: Object.values(tradesHistory).reduce((sum, coinTrades) => sum + coinTrades.length, 0),\\n coinsWithData: Object.keys(tradesHistory).length,\\n frozenCoinsCount: frozenCoins.length,\\n freeCoinsCount: freeCoins.length,\\n analysisMethod: 'Based on consecutive losses in last 4 hours'\\n },\\n frozenCoins: frozenCoins.map(coin => ({\\n coin: coin.coin.replace('_USDT.swap', ''),\\n consecutiveLosses: coin.consecutiveLosses,\\n recentLossesDescription: coin.recentLossesDescription,\\n remainingFreezeHours: coin.remainingFreezeHours || 0\\n })),\\n freeCoins: freeCoins.map(coin => coin.coin.replace('_USDT.swap', ''))\\n})\\n\\nreturn {\\n sorted: sorted,\\n summary: {\\n totalProfit: sorted.reduce((sum, r) => sum + r.totalProfit, 0).toFixed(2),\\n avgWinRate: sorted.length > 0 ? (sorted.reduce((sum, r) => sum + r.winRate, 0) / sorted.length).toFixed(2) : 0,\\n totalRecords: Object.values(tradesHistory).reduce((sum, coinTrades) => sum + coinTrades.length, 0),\\n coinsWithData: Object.keys(tradesHistory).length,\\n frozenCoinsCount: frozenCoins.length,\\n freeCoinsCount: freeCoins.length,\\n analysisMethod: 'Based on consecutive losses in last 4 hours'\\n },\\n frozenCoins: frozenCoins.map(coin => ({\\n coin: coin.coin.replace('_USDT.swap', ''),\\n consecutiveLosses: coin.consecutiveLosses,\\n recentLossesDescription: coin.recentLossesDescription,\\n remainingFreezeHours: coin.remainingFreezeHours || 0\\n })),\\n freeCoins: freeCoins.map(coin => coin.coin.replace('_USDT.swap', ''))\\n}\",\"notice\":\"\"},\"type\":\"n8n-nodes-base.code\",\"typeVersion\":2,\"position\":[-592,96],\"id\":\"9783c349-727a-4dbd-a5dd-31519f0ddd1e\",\"name\":\"Coin Performance Statistics\"},{\"parameters\":{\"mode\":\"runOnceForAllItems\",\"language\":\"javaScript\",\"jsCode\":\"// ========== Take Profit / Stop Loss Monitoring System - Integrated Complete Visual Dashboard ==========\\n\\nif (_G('invoketime') === null) {\\n return {}\\n}\\n\\n// ========== Configuration Parameters ==========\\nconst sltpChoice = $vars.sltpChoice;\\nconst FIXED_PROFIT_RATIO = $vars.tpPercent; // 2% take profit\\nconst FIXED_LOSS_RATIO = $vars.slPercent; // 1% stop loss\\n\\n// ========== Utility Functions ==========\\n\\n/**\\n * Get trading pair precision information\\n */\\nfunction getPrecision(coin) {\\n try {\\n const symbol = coin + '_USDT.swap';\\n const markets = _G('markets');\\n \\n if (markets && markets[symbol]) {\\n return {\\n price: markets[symbol].PricePrecision || 0,\\n amount: markets[symbol].AmountPrecision || 0,\\n minQty: markets[symbol].MinQty || 5,\\n ctVal: markets[symbol].CtVal || 1 // Contract value\\n };\\n }\\n return { price: 0, amount: 0, minQty: 5, ctVal: 1 };\\n } catch (e) {\\n Log(`⚠️ Failed to get ${coin} precision, using default values`);\\n return { price: 0, amount: 0, minQty: 5, ctVal: 1 };\\n }\\n}\\n\\n/**\\n * Save trade record to historical database\\n */\\nfunction saveTradeToHistory(orderDetail, coin, signalInfo) {\\n try {\\n const currentTime = Date.now();\\n \\n // Build trade record\\n const tradeRecord = {\\n // Order information\\n Id: orderDetail.Id,\\n Price: orderDetail.Price,\\n Amount: orderDetail.Amount,\\n DealAmount: orderDetail.DealAmount,\\n AvgPrice: orderDetail.AvgPrice,\\n Status: orderDetail.Status,\\n Type: orderDetail.Type,\\n Offset: orderDetail.Offset,\\n Symbol: orderDetail.Symbol,\\n ContractType: orderDetail.ContractType,\\n \\n // AI signal information\\n aiSignal: signalInfo.signal || 'auto_close',\\n confidence: signalInfo.confidence || 1.0,\\n profitTarget: signalInfo.profit_target,\\n stopLoss: signalInfo.stop_loss,\\n leverage: signalInfo.leverage,\\n riskUsd: signalInfo.risk_usd,\\n justification: signalInfo.justification,\\n \\n // Timestamp\\n time: currentTime,\\n timeStr: new Date(currentTime).toISOString()\\n };\\n \\n // Get existing history\\n let tradesHistory = _G('tradesHistory') || {};\\n \\n // Initialize coin record\\n if (!tradesHistory[coin]) {\\n tradesHistory[coin] = [];\\n }\\n \\n // Add record\\n tradesHistory[coin].push(tradeRecord);\\n \\n // Limit history size (max 1000 records per coin)\\n if (tradesHistory[coin].length > 1000) {\\n tradesHistory[coin] = tradesHistory[coin].slice(-1000);\\n }\\n \\n // Save to global storage\\n _G('tradesHistory', tradesHistory);\\n \\n const typeStr = orderDetail.Type === 0 ? 'Buy' : 'Sell';\\n Log(`📝 ${coin} trade record saved: ${typeStr} ${orderDetail.DealAmount} @ $${orderDetail.AvgPrice}`);\\n \\n } catch (e) {\\n Log(`❌ Failed to save ${coin} trade record: ${e.message}`);\\n }\\n}\\n\\n/**\\n * Execute close position operation\\n */\\nfunction closePosition(coin, pos, isLong, reason, pnl) {\\n const precision = getPrecision(coin);\\n const closeAmount = _N(Math.abs(pos.Amount), precision.amount);\\n \\n exchange.SetDirection(isLong ? \\\"closebuy\\\" : \\\"closesell\\\");\\n const orderId = isLong ? exchange.Sell(-1, closeAmount) : exchange.Buy(-1, closeAmount);\\n \\n if (orderId) {\\n Log(`${reason === 'Take Profit' ? '✅' : '❌'} ${coin} ${reason} ${(pnl*100).toFixed(2)}% Quantity=${closeAmount}`);\\n _G(`exit_plan_${coin}_USDT.swap`, null);\\n \\n // Save trade record to history\\n Sleep(1000);\\n try {\\n const orderDetail = exchange.GetOrder(orderId);\\n if (orderDetail && orderDetail.Status === 1) {\\n saveTradeToHistory(orderDetail, coin, { \\n signal: 'close', \\n confidence: 1.0, \\n justification: `Auto ${reason} PnL: ${(pnl*100).toFixed(2)}%` \\n });\\n }\\n } catch (e) {\\n Log(`⚠️ Failed to get ${coin} ${reason} order details: ${e.message}`);\\n }\\n return true;\\n }\\n return false;\\n}\\n\\n/**\\n * Monitor individual position\\n */\\nfunction monitorPosition(coin) {\\n exchange.SetCurrency(coin + '_USDT');\\n exchange.SetContractType(\\\"swap\\\");\\n \\n const pos = exchange.GetPositions().find(p => p.Symbol.includes(coin) && Math.abs(p.Amount) > 0);\\n if (!pos) return { status: \\\"no_position\\\" };\\n \\n const ticker = exchange.GetTicker();\\n if (!ticker) return { status: \\\"no_ticker\\\" };\\n \\n const isLong = pos.Type === PD_LONG || pos.Type === 0;\\n const currentPrice = ticker.Last;\\n const pnl = (currentPrice - pos.Price) * (isLong ? 1 : -1) / pos.Price;\\n \\n let shouldTP = false;\\n let shouldSL = false;\\n let profitTarget = null;\\n let stopLoss = null;\\n \\n if (sltpChoice) {\\n shouldTP = pnl >= FIXED_PROFIT_RATIO;\\n shouldSL = pnl <= -FIXED_LOSS_RATIO;\\n profitTarget = pos.Price * (isLong ? (1 + FIXED_PROFIT_RATIO) : (1 - FIXED_PROFIT_RATIO));\\n stopLoss = pos.Price * (isLong ? (1 - FIXED_LOSS_RATIO) : (1 + FIXED_LOSS_RATIO));\\n } else {\\n const exitPlan = _G(`exit_plan_${coin}_USDT.swap`);\\n \\n if (exitPlan?.profit_target && exitPlan?.stop_loss) {\\n profitTarget = exitPlan.profit_target;\\n stopLoss = exitPlan.stop_loss;\\n shouldTP = isLong ? currentPrice >= profitTarget : currentPrice <= profitTarget;\\n shouldSL = isLong ? currentPrice <= stopLoss : currentPrice >= stopLoss;\\n } else {\\n shouldTP = pnl >= FIXED_PROFIT_RATIO;\\n shouldSL = pnl <= -FIXED_LOSS_RATIO;\\n profitTarget = pos.Price * (isLong ? (1 + FIXED_PROFIT_RATIO) : (1 - FIXED_PROFIT_RATIO));\\n stopLoss = pos.Price * (isLong ? (1 - FIXED_LOSS_RATIO) : (1 + FIXED_LOSS_RATIO));\\n }\\n }\\n \\n if (shouldTP) {\\n return closePosition(coin, pos, isLong, \\\"Take Profit\\\", pnl) ? \\n { status: \\\"closed\\\", reason: \\\"profit\\\", pnl: pnl } : \\n { status: \\\"close_failed\\\" };\\n }\\n \\n if (shouldSL) {\\n return closePosition(coin, pos, isLong, \\\"Stop Loss\\\", pnl) ? \\n { status: \\\"closed\\\", reason: \\\"loss\\\", pnl: pnl } : \\n { status: \\\"close_failed\\\" };\\n }\\n \\n return { \\n status: \\\"monitoring\\\", \\n pnl: pnl, \\n position: pos, \\n currentPrice: currentPrice,\\n isLong: isLong,\\n profitTarget: profitTarget,\\n stopLoss: stopLoss\\n };\\n}\\n\\n// ========== Visual Dashboard Functions ==========\\n\\n/**\\n * Get coin performance data\\n */\\nfunction getCoinPerformanceData() {\\n try {\\n const tradesHistory = _G('tradesHistory') || {};\\n const performanceData = {};\\n \\n Object.keys(tradesHistory).forEach(coin => {\\n const orders = tradesHistory[coin] || [];\\n performanceData[coin + '_USDT.swap'] = analyzePerformance(orders, coin + '_USDT.swap');\\n });\\n \\n return performanceData;\\n } catch (e) {\\n Log(`⚠️ Failed to get performance data: ${e.message}`);\\n return {};\\n }\\n}\\n\\n/**\\n * Analyze trading performance\\n */\\nfunction analyzePerformance(orders, coin) {\\n if (!orders || orders.length === 0) {\\n return { coin, totalTrades: 0, totalProfit: 0, freezeStatus: 'free' };\\n }\\n \\n const filteredOrders = orders.filter(o => o.Status === 1);\\n const validOrders = filteredOrders.sort((a, b) => a.time - b.time);\\n \\n if (validOrders.length === 0) {\\n return { coin, totalTrades: 0, totalProfit: 0, freezeStatus: 'free' };\\n }\\n \\n // Get contract value\\n const coinMatch = coin.match(/^([A-Z0-9]+)_USDT/);\\n const coinName = coinMatch ? coinMatch[1] : coin;\\n const precision = getPrecision(coinName);\\n const ctVal = precision.ctVal;\\n \\n let trades = [];\\n let positions = [];\\n \\n // Trade matching logic\\n for (let order of validOrders) {\\n const { Type: type, DealAmount: amount, AvgPrice: price, time, Id: id } = order;\\n \\n let remaining = amount;\\n \\n for (let i = 0; i < positions.length && remaining > 0.0001; i++) {\\n const pos = positions[i];\\n \\n if ((type === 0 && pos.type === 1) || (type === 1 && pos.type === 0)) {\\n const tradeAmount = Math.min(remaining, pos.amount);\\n const holdTime = time - pos.time;\\n \\n let profit, direction;\\n if (pos.type === 0) {\\n profit = (price - pos.price) * tradeAmount * ctVal;\\n direction = 'LONG';\\n } else {\\n profit = (pos.price - price) * tradeAmount * ctVal;\\n direction = 'SHORT';\\n }\\n \\n trades.push({\\n direction,\\n openPrice: pos.price,\\n closePrice: price,\\n amount: tradeAmount,\\n profit,\\n holdTime: holdTime,\\n closeTime: time\\n });\\n \\n pos.amount -= tradeAmount;\\n remaining -= tradeAmount;\\n \\n if (pos.amount <= 0.0001) {\\n positions.splice(i, 1);\\n i--;\\n }\\n }\\n }\\n \\n if (remaining > 0.0001) {\\n positions.push({\\n type: type,\\n price: price,\\n amount: remaining,\\n time: time,\\n id: id\\n });\\n }\\n }\\n \\n if (trades.length === 0) {\\n return { \\n coin, \\n totalTrades: 0, \\n totalProfit: 0,\\n winRate: 0,\\n avgProfit: 0,\\n maxProfit: 0,\\n maxLoss: 0,\\n profitLossRatio: 0,\\n avgHoldTimeHours: 0,\\n longWinProfit: 0,\\n shortWinProfit: 0,\\n freezeStatus: 'free'\\n };\\n }\\n \\n // Calculate statistics\\n trades.sort((a, b) => a.closeTime - b.closeTime);\\n \\n const wins = trades.filter(t => t.profit > 0);\\n const losses = trades.filter(t => t.profit <= 0);\\n const longTrades = trades.filter(t => t.direction === 'LONG');\\n const shortTrades = trades.filter(t => t.direction === 'SHORT');\\n \\n const longWins = longTrades.filter(t => t.profit > 0);\\n const shortWins = shortTrades.filter(t => t.profit > 0);\\n \\n const totalProfit = trades.reduce((sum, t) => sum + t.profit, 0);\\n const totalWinProfit = wins.reduce((sum, t) => sum + t.profit, 0);\\n const totalLossProfit = Math.abs(losses.reduce((sum, t) => sum + t.profit, 0));\\n const winRate = (wins.length / trades.length * 100);\\n \\n const frozenData = _G(`frozen_${coinName}`);\\n const currentTime = Date.now();\\n const freezeStatus = (frozenData && frozenData.freezeEndTime > currentTime) ? 'frozen' : 'free';\\n \\n function calculateRecentConsecutiveLosses(tradesArray) {\\n const currentTime = Date.now()\\n const fourHoursAgo = currentTime - (4 * 60 * 60 * 1000)\\n \\n const recentTrades = tradesArray.filter(trade => trade.closeTime >= fourHoursAgo)\\n \\n if (recentTrades.length === 0) {\\n return 0\\n }\\n \\n recentTrades.sort((a, b) => a.closeTime - b.closeTime)\\n \\n let consecutiveLosses = 0\\n for (let i = recentTrades.length - 1; i >= 0; i--) {\\n if (recentTrades[i].profit <= 0) {\\n consecutiveLosses++\\n } else {\\n break\\n }\\n }\\n \\n return consecutiveLosses\\n }\\n \\n const consecutiveLosses = calculateRecentConsecutiveLosses(trades)\\n \\n return {\\n coin,\\n totalTrades: trades.length,\\n totalProfit: parseFloat(totalProfit.toFixed(2)),\\n winRate: parseFloat(winRate.toFixed(2)),\\n avgProfit: parseFloat((totalProfit / trades.length).toFixed(2)),\\n maxProfit: wins.length > 0 ? parseFloat(Math.max(...wins.map(t => t.profit)).toFixed(2)) : 0,\\n maxLoss: parseFloat(Math.min(...trades.map(t => t.profit)).toFixed(2)),\\n profitLossRatio: totalLossProfit > 0 ? parseFloat((totalWinProfit / totalLossProfit).toFixed(2)) : (totalWinProfit > 0 ? 999 : 0),\\n avgHoldTimeHours: parseFloat((trades.reduce((sum, t) => sum + t.holdTime, 0) / trades.length / (1000 * 60 * 60)).toFixed(2)),\\n longWinProfit: parseFloat(longWins.reduce((sum, t) => sum + t.profit, 0).toFixed(2)),\\n shortWinProfit: parseFloat(shortWins.reduce((sum, t) => sum + t.profit, 0).toFixed(2)),\\n consecutiveLosses: consecutiveLosses,\\n freezeStatus: freezeStatus\\n };\\n}\\n\\n/**\\n * Create AI agent signal analysis table - get execution results from _G\\n */\\nfunction createMonitoringTable() {\\n const signalTable = {\\n type: \\\"table\\\",\\n title: \\\"🤖 AI Agent\\\",\\n cols: [\\\"Coin\\\", \\\"Signal Type\\\", \\\"Execution Status\\\", \\\"Confidence\\\", \\\"Profit Target\\\", \\\"Stop Loss\\\", \\\"Risk Amount\\\", \\\"Reason\\\"],\\n rows: []\\n };\\n \\n try {\\n // Get AI signals and execution results from _G\\n const aiSignals = _G('AISinnal') || [];\\n const executionData = _G('latestExecutionResults') || { results: {} };\\n const executionResults = executionData.results || {};\\n \\n if (!aiSignals || aiSignals.length === 0) {\\n signalTable.rows.push([\\n \\\"📭 No Signals\\\",\\n \\\"⏳ Waiting for AI Analysis\\\",\\n \\\"⏭️ Pending Analysis\\\",\\n \\\"-\\\",\\n \\\"-\\\",\\n \\\"-\\\",\\n \\\"-\\\",\\n \\\"Waiting for AI agent to generate trading signals\\\"\\n ]);\\n return signalTable;\\n }\\n \\n // Process each AI signal\\n aiSignals.forEach(aiSignal => {\\n if (!aiSignal || !aiSignal.coin) return;\\n \\n const coin = aiSignal.coin;\\n const executionResult = executionResults[coin]; // Get execution result from _G\\n \\n // Beautify signal display\\n let signalDisplay = aiSignal.signal;\\n let signalEmoji = \\\"⏸️\\\";\\n \\n switch (aiSignal.signal) {\\n case 'buy_to_enter':\\n signalDisplay = \\\"🟢 Long Entry\\\";\\n signalEmoji = \\\"📈\\\";\\n break;\\n case 'sell_to_enter':\\n signalDisplay = \\\"🔴 Short Entry\\\";\\n signalEmoji = \\\"📉\\\";\\n break;\\n case 'close':\\n signalDisplay = \\\"⏹️ Close\\\";\\n signalEmoji = \\\"🔚\\\";\\n break;\\n case 'hold':\\n signalDisplay = \\\"⏸️ Hold\\\";\\n signalEmoji = \\\"⏳\\\";\\n break;\\n default:\\n signalDisplay = \\\"❓ Unknown Signal\\\";\\n signalEmoji = \\\"⚠️\\\";\\n }\\n \\n // ===== Modified: Display status based on execution results from _G =====\\n let statusDisplay = \\\"\\\";\\n if (executionResult) {\\n if (executionResult.executed) {\\n statusDisplay = \\\"✅ Executed\\\";\\n } else if (executionResult.skipReason) {\\n statusDisplay = `⏭️ ${executionResult.skipReason}`;\\n } else {\\n statusDisplay = \\\"❌ Execution Failed\\\";\\n }\\n } else {\\n // No execution record, infer from signal type\\n if (aiSignal.signal === 'hold') {\\n statusDisplay = \\\"⏭️ Hold\\\";\\n } else {\\n statusDisplay = \\\"⏭️ Waiting for Execution\\\";\\n }\\n }\\n \\n // Confidence display\\n const confidence = aiSignal.confidence ? (aiSignal.confidence * 100).toFixed(0) : 0;\\n let confidenceDisplay = \\\"-\\\";\\n if (confidence >= 80) {\\n confidenceDisplay = `🔥 ${confidence}%`;\\n } else if (confidence >= 60) {\\n confidenceDisplay = `⚡ ${confidence}%`;\\n } else if (confidence > 0) {\\n confidenceDisplay = `⚠️ ${confidence}%`;\\n }\\n \\n // Historical bias display\\n let biasEmoji = \\\"\\\";\\n switch (aiSignal.historical_bias) {\\n case 'LONG':\\n biasEmoji = \\\"📈\\\";\\n break;\\n case 'SHORT':\\n biasEmoji = \\\"📉\\\";\\n break;\\n case 'BALANCED':\\n biasEmoji = \\\"⚖️\\\";\\n break;\\n default:\\n biasEmoji = \\\"❓\\\";\\n }\\n \\n signalTable.rows.push([\\n `💎 ${coin}`,\\n `${signalEmoji} ${signalDisplay}`,\\n statusDisplay, // Display actual execution status here\\n confidenceDisplay,\\n aiSignal.profit_target && aiSignal.profit_target > 0 ? `🎯 $${_N(aiSignal.profit_target, 4)}` : \\\"-\\\",\\n aiSignal.stop_loss && aiSignal.stop_loss > 0 ? `🛡️ $${_N(aiSignal.stop_loss, 4)}` : \\\"-\\\",\\n aiSignal.risk_usd && aiSignal.risk_usd > 0 ? `💰 $${aiSignal.risk_usd}` : \\\"-\\\",\\n `${biasEmoji} ${aiSignal.justification || \\\"No detailed explanation\\\"}`\\n ]);\\n });\\n \\n // Add execution summary row\\n if (executionData.timestamp) {\\n const executionTime = new Date(executionData.timestamp).toLocaleTimeString();\\n const executedCount = Object.values(executionResults).filter(r => r.executed).length;\\n const totalSignals = executionData.totalSignals || 0;\\n \\n signalTable.rows.push([\\n \\\"📊 Execution Summary\\\",\\n `${totalSignals} signals`,\\n `✅ ${executedCount} executed`,\\n \\\"-\\\",\\n \\\"-\\\",\\n \\\"-\\\",\\n \\\"-\\\",\\n `Last update: ${executionTime}`\\n ]);\\n }\\n \\n } catch (e) {\\n Log(`❌ Failed to create AI signal table: ${e.message}`);\\n signalTable.rows.push([\\n \\\"❌ Error\\\",\\n \\\"Failed to get signals\\\",\\n \\\"🚨\\\",\\n \\\"-\\\",\\n \\\"-\\\",\\n \\\"-\\\",\\n \\\"-\\\",\\n e.message.substring(0, 50)\\n ]);\\n }\\n \\n return signalTable;\\n}\\n\\n/**\\n * Create position table\\n */\\nfunction createPositionTable() {\\n const positionTable = {\\n type: \\\"table\\\",\\n title: \\\"💼 Current Position Status\\\",\\n cols: [\\\"Coin\\\", \\\"Direction\\\", \\\"Quantity\\\", \\\"Entry Price\\\", \\\"Current Price\\\", \\\"PnL%\\\", \\\"PnL$\\\", \\\"Leverage\\\", \\\"TP\\\", \\\"SL\\\"],\\n rows: []\\n };\\n \\n try {\\n const positions = exchange.GetPositions();\\n let totalPnl = 0;\\n let positionCount = 0;\\n \\n if (positions && positions.length > 0) {\\n positions.forEach(pos => {\\n if (Math.abs(pos.Amount) > 0) {\\n const coinMatch = pos.Symbol.match(/^([A-Z0-9]+)_USDT/);\\n if (!coinMatch) return;\\n \\n const coin = coinMatch[1];\\n \\n // Get current price\\n exchange.SetCurrency(coin + '_USDT');\\n exchange.SetContractType(\\\"swap\\\");\\n const symbolcode = coin + '_USDT.swap';\\n const ticker = exchange.GetTicker();\\n const coininfo = _G('markets')[symbolcode];\\n \\n if (ticker) {\\n const isLong = pos.Type === PD_LONG || pos.Type === 0;\\n const currentPrice = ticker.Last;\\n const pnlPercent = ((currentPrice - pos.Price) * (isLong ? 1 : -1) / pos.Price * 100);\\n const pnlUsd = pnlPercent / 100 * pos.Price * Math.abs(pos.Amount) * (coininfo?.CtVal || 1);\\n \\n // PnL display\\n let pnlPercentDisplay = \\\"\\\";\\n let pnlUsdDisplay = \\\"\\\";\\n if (pnlPercent > 0) {\\n pnlPercentDisplay = `🟢 +${pnlPercent.toFixed(2)}%`;\\n pnlUsdDisplay = `🟢 +$${pnlUsd.toFixed(2)}`;\\n } else if (pnlPercent < 0) {\\n pnlPercentDisplay = `🔴 ${pnlPercent.toFixed(2)}%`;\\n pnlUsdDisplay = `🔴 $${pnlUsd.toFixed(2)}`;\\n } else {\\n pnlPercentDisplay = `⚪ ${pnlPercent.toFixed(2)}%`;\\n pnlUsdDisplay = `⚪ $${pnlUsd.toFixed(2)}`;\\n }\\n \\n // Get exit plan\\n const exitPlan = _G(`exit_plan_${coin}_USDT.swap`);\\n \\n positionTable.rows.push([\\n `💎 ${coin}`,\\n isLong ? \\\"📈 Long\\\" : \\\"📉 Short\\\",\\n _N(Math.abs(pos.Amount), 4),\\n `$${_N(pos.Price, 4)}`,\\n `$${_N(currentPrice, 4)}`,\\n pnlPercentDisplay,\\n pnlUsdDisplay,\\n `${pos.MarginLevel || 1}x`,\\n exitPlan?.profit_target ? `$${_N(exitPlan.profit_target, 4)}` : \\\"-\\\",\\n exitPlan?.stop_loss ? `$${_N(exitPlan.stop_loss, 4)}` : \\\"-\\\"\\n ]);\\n \\n totalPnl += pnlPercent;\\n positionCount++;\\n }\\n \\n Sleep(200);\\n }\\n });\\n }\\n \\n // Add summary row\\n if (positionCount > 0) {\\n const avgPnl = totalPnl / positionCount;\\n let summaryEmoji = avgPnl > 0 ? \\\"📊 ✅\\\" : \\\"📊 ⚠️\\\";\\n positionTable.rows.push([\\n `${summaryEmoji} Summary`,\\n `${positionCount} positions`,\\n \\\"-\\\",\\n \\\"-\\\",\\n \\\"-\\\",\\n `Average: ${avgPnl > 0 ? '🟢' : '🔴'} ${_N(avgPnl, 2)}%`,\\n \\\"-\\\",\\n \\\"-\\\",\\n \\\"-\\\",\\n \\\"-\\\"\\n ]);\\n } else {\\n positionTable.rows.push([\\n \\\"📭 No Position\\\",\\n \\\"-\\\",\\n \\\"-\\\",\\n \\\"-\\\",\\n \\\"-\\\",\\n \\\"-\\\",\\n \\\"-\\\",\\n \\\"-\\\",\\n \\\"-\\\",\\n \\\"-\\\"\\n ]);\\n }\\n \\n } catch (e) {\\n Log(`❌ Failed to create position table: ${e.message}`);\\n positionTable.rows.push([\\n \\\"❌ Error\\\",\\n \\\"Failed to get positions\\\",\\n \\\"-\\\",\\n \\\"-\\\",\\n \\\"-\\\",\\n \\\"-\\\",\\n \\\"-\\\",\\n \\\"-\\\",\\n \\\"-\\\",\\n \\\"-\\\"\\n ]);\\n }\\n \\n return positionTable;\\n}\\n\\n/**\\n * Create performance statistics table\\n */\\nfunction createPerformanceTable() {\\n const performanceTable = {\\n type: \\\"table\\\",\\n title: \\\"📈 Trading Performance by Coin (Sorted by Total PnL)\\\",\\n cols: [\\\"Coin\\\", \\\"Total Trades\\\", \\\"Total PnL\\\", \\\"Win Rate%\\\", \\\"Avg PnL\\\", \\\"Max Profit\\\", \\\"Max Loss\\\", \\\"P/L Ratio\\\", \\\"Freeze Status\\\"],\\n rows: []\\n };\\n \\n try {\\n const performanceData = getCoinPerformanceData();\\n const coinPerformances = Object.values(performanceData)\\n .filter(r => !r.error)\\n .sort((a, b) => b.totalProfit - a.totalProfit);\\n \\n if (coinPerformances.length === 0) {\\n performanceTable.rows.push([\\n \\\"📭 No Data\\\",\\n \\\"-\\\",\\n \\\"-\\\",\\n \\\"-\\\",\\n \\\"-\\\",\\n \\\"-\\\",\\n \\\"-\\\",\\n \\\"-\\\",\\n \\\"Waiting for trading data\\\"\\n ]);\\n } else {\\n coinPerformances.forEach(performance => {\\n const coinMatch = performance.coin.match(/^([A-Z0-9]+)_USDT/);\\n const coinName = coinMatch ? coinMatch[1] : performance.coin;\\n \\n // Freeze status display\\n let freezeDisplay = \\\"🟢 Normal\\\";\\n if (performance.freezeStatus === 'frozen') {\\n freezeDisplay = `🧊 Frozen (${performance.consecutiveLosses} consecutive losses)`;\\n }\\n \\n performanceTable.rows.push([\\n `💎 ${coinName}`,\\n performance.totalTrades,\\n performance.totalProfit > 0 ? `🟢 $${performance.totalProfit}` : \\n performance.totalProfit < 0 ? `🔴 $${performance.totalProfit}` : `⚪ $0`,\\n performance.winRate > 0 ? `${performance.winRate}%` : \\\"0%\\\",\\n performance.avgProfit > 0 ? `🟢 $${performance.avgProfit}` : \\n performance.avgProfit < 0 ? `🔴 $${performance.avgProfit}` : `⚪ $0`,\\n performance.maxProfit > 0 ? `$${performance.maxProfit}` : \\\"$0\\\",\\n performance.maxLoss < 0 ? `$${performance.maxLoss}` : \\\"$0\\\",\\n performance.profitLossRatio > 0 ? `${performance.profitLossRatio}` : \\\"0\\\",\\n freezeDisplay\\n ]);\\n });\\n \\n // Add summary row\\n const totalTrades = coinPerformances.reduce((sum, p) => sum + p.totalTrades, 0);\\n const totalProfit = coinPerformances.reduce((sum, p) => sum + p.totalProfit, 0);\\n const frozenCount = coinPerformances.filter(p => p.freezeStatus === 'frozen').length;\\n \\n performanceTable.rows.push([\\n \\\"📊 Summary\\\",\\n totalTrades,\\n totalProfit > 0 ? `🟢 $${_N(totalProfit, 2)}` : \\n totalProfit < 0 ? `🔴 $${_N(totalProfit, 2)}` : `⚪ $0`,\\n \\\"-\\\",\\n \\\"-\\\",\\n \\\"-\\\",\\n \\\"-\\\",\\n \\\"-\\\",\\n `${frozenCount} frozen`\\n ]);\\n }\\n \\n } catch (e) {\\n Log(`❌ Failed to create performance table: ${e.message}`);\\n performanceTable.rows.push([\\n \\\"❌ Error\\\",\\n \\\"Failed to get statistics\\\",\\n \\\"-\\\",\\n \\\"-\\\",\\n \\\"-\\\",\\n \\\"-\\\",\\n \\\"-\\\",\\n \\\"-\\\",\\n e.message.substring(0, 20)\\n ]);\\n }\\n \\n return performanceTable;\\n}\\n\\n/**\\n * Create overall analysis table\\n */\\nfunction createSystemStatusTable() {\\n const statusTable = {\\n type: \\\"table\\\",\\n title: \\\"📊 Overall Analysis Table\\\",\\n cols: [\\\"Analysis Indicator\\\", \\\"Value\\\", \\\"Status\\\", \\\"Note\\\"],\\n rows: []\\n };\\n \\n try {\\n // Use correct profit calculation method\\n const currentAccount = exchange.GetAccount();\\n const currentAccountValue = currentAccount.Equity;\\n const initMoney = _G('initmoney') || currentAccountValue;\\n const totalProfitUSD = currentAccountValue - initMoney;\\n const totalReturnPercent = ((currentAccountValue - initMoney) / initMoney * 100);\\n\\n LogProfit(totalProfitUSD, \\\"&\\\")\\n \\n // Get trading statistics\\n const performanceData = getCoinPerformanceData();\\n const coinPerformances = Object.values(performanceData).filter(r => !r.error);\\n const totalTrades = coinPerformances.reduce((sum, p) => sum + p.totalTrades, 0);\\n const avgWinRate = coinPerformances.length > 0 ? \\n coinPerformances.reduce((sum, p) => sum + p.winRate, 0) / coinPerformances.length : 0;\\n const frozenCount = coinPerformances.filter(p => p.freezeStatus === 'frozen').length;\\n const activeCoins = coinPerformances.filter(p => p.totalTrades > 0).length;\\n \\n // Position statistics\\n const positions = exchange.GetPositions();\\n const activePositions = positions ? positions.filter(p => Math.abs(p.Amount) > 0).length : 0;\\n \\n statusTable.rows.push([\\n \\\"💰 Account Equity\\\",\\n `$${_N(currentAccountValue, 2)}`,\\n \\\"📊\\\",\\n `Initial capital: $${_N(initMoney, 2)}`\\n ]);\\n \\n statusTable.rows.push([\\n \\\"💵 Total PnL (USD)\\\",\\n totalProfitUSD > 0 ? `🟢 +$${_N(totalProfitUSD, 2)}` : \\n totalProfitUSD < 0 ? `🔴 $${_N(totalProfitUSD, 2)}` : `⚪ $0`,\\n totalProfitUSD > 0 ? \\\"✅ Profit\\\" : totalProfitUSD < 0 ? \\\"⚠️ Loss\\\" : \\\"⚪ Break-even\\\",\\n \\\"Actual account PnL\\\"\\n ]);\\n \\n statusTable.rows.push([\\n \\\"📈 Total Return Rate\\\",\\n totalReturnPercent > 0 ? `🟢 +${_N(totalReturnPercent, 2)}%` : \\n totalReturnPercent < 0 ? `🔴 ${_N(totalReturnPercent, 2)}%` : `⚪ 0%`,\\n totalReturnPercent > 5 ? \\\"✅ Excellent\\\" : totalReturnPercent > 0 ? \\\"✅ Good\\\" : \\\"⚠️ Needs Improvement\\\",\\n \\\"Relative to initial capital\\\"\\n ]);\\n \\n statusTable.rows.push([\\n \\\"🎲 Total Trades\\\",\\n totalTrades.toString(),\\n totalTrades > 50 ? \\\"✅ Sufficient\\\" : totalTrades > 10 ? \\\"✅ Good\\\" : \\\"⚠️ Too Few\\\",\\n \\\"All coins combined\\\"\\n ]);\\n \\n statusTable.rows.push([\\n \\\"🎯 Average Win Rate\\\",\\n `${_N(avgWinRate, 1)}%`,\\n avgWinRate > 60 ? \\\"✅ Excellent\\\" : avgWinRate > 45 ? \\\"✅ Good\\\" : \\\"⚠️ Needs Improvement\\\",\\n \\\"Average win rate per coin\\\"\\n ]);\\n \\n statusTable.rows.push([\\n \\\"💼 Current Positions\\\",\\n `${activePositions} positions`,\\n activePositions > 0 ? \\\"✅ In Position\\\" : \\\"📭 No Position\\\",\\n \\\"Active position count\\\"\\n ]);\\n \\n statusTable.rows.push([\\n \\\"🧊 Frozen Coins\\\",\\n `${frozenCount} coins`,\\n frozenCount === 0 ? \\\"✅ All Normal\\\" : frozenCount > 3 ? \\\"⚠️ Many Frozen\\\" : \\\"🟡 Some Frozen\\\",\\n \\\"Consecutive loss protection active\\\"\\n ]);\\n \\n statusTable.rows.push([\\n \\\"📊 Active Coins\\\",\\n `${activeCoins} coins`,\\n activeCoins > 5 ? \\\"✅ Diversified\\\" : activeCoins > 2 ? \\\"✅ Good\\\" : \\\"⚠️ Single\\\",\\n \\\"Coins with trading records\\\"\\n ]);\\n \\n statusTable.rows.push([\\n \\\"🎯 TP/SL System\\\",\\n sltpChoice ? \\\"Fixed Mode\\\" : \\\"Smart Mode\\\",\\n \\\"✅ Running\\\",\\n \\\"Continuous monitoring\\\"\\n ]);\\n \\n } catch (e) {\\n Log(`❌ Failed to create overall analysis table: ${e.message}`);\\n statusTable.rows.push([\\n \\\"❌ Error\\\",\\n \\\"Calculation failed\\\",\\n \\\"🚨\\\",\\n e.message.substring(0, 30)\\n ]);\\n }\\n \\n return statusTable;\\n}\\n\\n/**\\n * Display complete monitoring dashboard\\n */\\nfunction displayMonitoringDashboard(monitoringResults) {\\n try {\\n // 1. AI agent signal analysis table\\n const signalTable = createMonitoringTable(monitoringResults);\\n \\n // 2. Current position table\\n const positionTable = createPositionTable();\\n \\n // 3. Coin performance table\\n const performanceTable = createPerformanceTable();\\n \\n // 4. Overall analysis table\\n const systemStatusTable = createSystemStatusTable();\\n \\n // Combined display\\n const dashboardDisplay = \\n '`' + JSON.stringify(signalTable) + '`\\\\n\\\\n' +\\n '`' + JSON.stringify(positionTable) + '`\\\\n\\\\n' +\\n '`' + JSON.stringify(performanceTable) + '`\\\\n\\\\n' +\\n '`' + JSON.stringify(systemStatusTable) + '`';\\n \\n LogStatus(dashboardDisplay);\\n \\n } catch (e) {\\n Log(`❌ Failed to display monitoring dashboard: ${e.message}`);\\n // Fallback to simple display\\n displaySimpleStatus();\\n }\\n}\\n\\n/**\\n * Simple status display (backup)\\n */\\nfunction displaySimpleStatus() {\\n try {\\n const simpleTable = {\\n type: \\\"table\\\",\\n title: \\\"📊 TP/SL System Status\\\",\\n cols: [\\\"Item\\\", \\\"Status\\\", \\\"Note\\\"],\\n rows: [\\n [\\\"🔍 Position Monitoring\\\", \\\"✅ Running\\\", \\\"Real-time monitoring of all positions\\\"],\\n [\\\"🎯 TP/SL\\\", \\\"✅ Normal\\\", `${sltpChoice ? 'Smart' : 'Fixed'} mode`],\\n [\\\"📊 Data Recording\\\", \\\"✅ Normal\\\", \\\"Recording trading history\\\"],\\n [\\\"🧊 Freeze Protection\\\", \\\"✅ Enabled\\\", \\\"Preventing consecutive losses\\\"]\\n ]\\n };\\n \\n LogStatus('`' + JSON.stringify(simpleTable) + '`');\\n } catch (e) {\\n LogStatus('❌ Failed to display status: ' + e.message);\\n }\\n}\\n\\n// ========== Main Execution Logic ==========\\n\\nfunction main() {\\n try {\\n \\n // Monitoring results record\\n const monitoringResults = {};\\n let totalActions = 0;\\n \\n // Get all positions for monitoring\\n const positions = exchange.GetPositions();\\n \\n if (positions && positions.length > 0) {\\n positions.forEach(pos => {\\n if (Math.abs(pos.Amount) > 0) {\\n const coinMatch = pos.Symbol.match(/^([A-Z0-9]+)_USDT/);\\n if (!coinMatch) return;\\n \\n const coin = coinMatch[1];\\n const result = monitorPosition(coin);\\n \\n monitoringResults[coin] = result;\\n \\n if (result.status === \\\"closed\\\") {\\n const reasonEmoji = result.reason === \\\"profit\\\" ? \\\"🎉\\\" : \\\"💔\\\";\\n Log(`${reasonEmoji} ${coin} auto-closed - ${result.reason === \\\"profit\\\" ? \\\"Take Profit\\\" : \\\"Stop Loss\\\"} ${(result.pnl * 100).toFixed(2)}%`);\\n totalActions++;\\n }\\n \\n Sleep(500);\\n }\\n });\\n } \\n \\n // Display complete monitoring dashboard\\n displayMonitoringDashboard(monitoringResults);\\n \\n return {\\n monitoringResults: monitoringResults,\\n totalActions: totalActions,\\n timestamp: Date.now()\\n };\\n \\n } catch (e) {\\n Log(`❌ Main execution flow failed: ${e.message}`);\\n Log(`❌ Error stack: ${e.stack}`);\\n displaySimpleStatus();\\n return { error: e.message };\\n }\\n}\\n\\n// Execute main function and return result\\nreturn main();\",\"notice\":\"\"},\"type\":\"n8n-nodes-base.code\",\"typeVersion\":2,\"position\":[-800,240],\"id\":\"8fa4c028-e1b2-4517-8eac-7f7c989c8744\",\"name\":\"TP/SL Monitoring Code\"},{\"parameters\":{\"notice\":\"\",\"rule\":{\"interval\":[{\"field\":\"minutes\",\"minutesInterval\":15}]}},\"type\":\"n8n-nodes-base.scheduleTrigger\",\"typeVersion\":1.2,\"position\":[-960,-112],\"id\":\"56257b76-9c41-4cbf-a24a-afab5a7b6a8a\",\"name\":\"Strategy Execution Trigger\"},{\"parameters\":{\"notice\":\"\",\"rule\":{\"interval\":[{\"field\":\"seconds\",\"secondsInterval\":15}]}},\"type\":\"n8n-nodes-base.scheduleTrigger\",\"typeVersion\":1.2,\"position\":[-960,240],\"id\":\"c60bd9fc-0a6d-44c4-9b98-cfc01af7e359\",\"name\":\"TP/SL Trigger\",\"logLevel\":0}],\"pinData\":{},\"connections\":{\"Merge Data\":{\"main\":[[{\"node\":\"Data Merge\",\"type\":\"main\",\"index\":0}]]},\"AI Agent\":{\"main\":[[{\"node\":\"Trade Execution\",\"type\":\"main\",\"index\":0}]]},\"Parameter Reset\":{\"main\":[[{\"node\":\"Market Data Fetch\",\"type\":\"main\",\"index\":0},{\"node\":\"Position Data Fetch\",\"type\":\"main\",\"index\":0},{\"node\":\"Coin Performance Statistics\",\"type\":\"main\",\"index\":0}]]},\"OpenAI Model\":{\"ai_languageModel\":[[{\"node\":\"AI Agent\",\"type\":\"ai_languageModel\",\"index\":0}]]},\"Market Data Fetch\":{\"main\":[[{\"node\":\"Merge Data\",\"type\":\"main\",\"index\":0}]]},\"Position Data Fetch\":{\"main\":[[{\"node\":\"Merge Data\",\"type\":\"main\",\"index\":1}]]},\"Data Merge\":{\"main\":[[{\"node\":\"AI Agent\",\"type\":\"main\",\"index\":0}]]},\"Coin Performance Statistics\":{\"main\":[[{\"node\":\"Merge Data\",\"type\":\"main\",\"index\":2}]]},\"Strategy Execution Trigger\":{\"main\":[[{\"node\":\"Parameter Reset\",\"type\":\"main\",\"index\":0}]]},\"TP/SL Trigger\":{\"main\":[[{\"node\":\"TP/SL Monitoring Code\",\"type\":\"main\",\"index\":0}]]}},\"active\":false,\"settings\":{\"timezone\":\"Asia/Shanghai\",\"executionOrder\":\"v1\"},\"tags\":[],\"meta\":{\"templateCredsSetupCompleted\":true},\"credentials\":{},\"id\":\"b830c2aa-c71c-4e2d-bfc8-d4f78dc9a82e\",\"plugins\":{},\"mcpClients\":{}},\"startNodes\":[],\"triggerToStartFrom\":{\"name\":\"Strategy Execution Trigger\"}}"}