import json import logging import re import subprocess logger = logging.getLogger(__name__) def analyze_market(indicator_summary: str, account_status: str) -> list[dict]: """Call Claude CLI to analyze market data and return trade signals.""" prompt = f"""你是一位專業的加密貨幣短線交易分析師。 ## 帳戶狀態 {account_status} ## 目前市場指標摘要(5分鐘K線最新值) {indicator_summary} 請根據以下策略分析每個幣種並給出交易建議: ## 交易策略 1. **趨勢確認**: EMA(9) > EMA(21) 為多頭,反之為空頭 2. **進場訊號 (BUY)**: - RSI < 35 且 MACD histogram 由負轉正(超賣反彈) - 價格觸及 Bollinger 下軌且 RSI < 40(均值回歸) - MACD 金叉 + EMA(9) 上穿 EMA(21)(趨勢啟動) 3. **進場訊號 (SELL)**: - RSI > 70 且 MACD histogram 由正轉負(超買回落) - 價格觸及 Bollinger 上軌且 RSI > 65 - MACD 死叉 + EMA(9) 下穿 EMA(21) 4. **過濾條件**: - 成交量需高於 20 期平均(確認動能) - ATR 過高時降低倉位(波動風控) 請以 JSON 格式回傳,每個幣種一個物件: ```json [ {{ "symbol": "tBTCUST", "action": "BUY" | "SELL" | "HOLD", "confidence": 0.0-1.0, "reason": "簡短理由", "suggested_amount_pct": 0.10-0.20 }} ] ``` 只回傳 JSON,不要其他文字。""" try: result = subprocess.run( ["claude", "-p", prompt, "--output-format", "json"], capture_output=True, text=True, timeout=120, ) if result.returncode != 0: logger.error("Claude CLI failed (rc=%d): %s", result.returncode, result.stderr) return [] return _parse_llm_response(result.stdout) except subprocess.TimeoutExpired: logger.error("Claude CLI timed out") return [] except FileNotFoundError: logger.error("Claude CLI not found — is 'claude' installed and in PATH?") return [] except Exception as e: logger.error("LLM analysis error: %s", e) return [] def _parse_llm_response(raw: str) -> list[dict]: """Extract the JSON array from Claude's response.""" # First try: the output-format json wraps response in {"result": "..."} try: wrapper = json.loads(raw) if isinstance(wrapper, dict) and "result" in wrapper: raw = wrapper["result"] except (json.JSONDecodeError, TypeError): pass # Try direct parse if isinstance(raw, list): return raw try: parsed = json.loads(raw) if isinstance(parsed, list): return parsed except (json.JSONDecodeError, TypeError): pass # Try extracting JSON array from markdown code block or mixed text match = re.search(r'\[[\s\S]*?\]', raw) if match: try: return json.loads(match.group()) except json.JSONDecodeError: pass logger.warning("Could not parse LLM response as JSON array") return []