bifitnex-trading/llm_analyzer.py
kroutony 1abfdefecd Add advanced indicators: MTF analysis, ADX, StochRSI, OBV, CMF, pivot points
- config.py: Add HTF_TIMEFRAME (1h) and HTF_CANDLE_LIMIT (50)
- data_fetcher.py: Fetch both 5m and 1h candles per symbol
- indicators.py: Add ADX, StochRSI, OBV+slope, CMF to 5m indicators;
  new functions for HTF indicators, pivot points, and their summaries
- main.py: Wire up HTF data flow (1h indicators + pivots → LLM summary)
- llm_analyzer.py: Rewrite prompt with MTF filter (1h trend alignment),
  require 2+ confirmations for BUY, confidence scoring guide

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 03:26:59 +00:00

119 lines
3.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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}
## 目前市場指標摘要
{indicator_summary}
請根據以下策略分析每個幣種並給出交易建議:
## 交易策略
### 最高優先:多時間框架過濾(現貨,只做多)
- 1小時趨勢為多頭EMA9 > EMA21才考慮 BUY
- 1小時為空頭時只持有或平倉SELL不開新倉
- 1小時 ADX < 20盤整避免進場
- 1小時 ADX > 25 且方向一致 = 高信心交易
### 進場訊號 (BUY) — 需至少2個確認
1. **超賣反彈**: StochRSI K < 0.2 且 K 上穿 D + RSI < 40
2. **均值回歸**: 價格觸及 BB 下軌 + RSI < 40 + CMF > 0有買壓
3. **趨勢啟動**: MACD 金叉 + EMA9 上穿 EMA21 + ADX > 20有趨勢
4. **支撐反彈**: 價格接近樞紐支撐位S1/S2+ OBV 流入 + RSI < 45
### 出場訊號 (SELL)
1. **超買回落**: StochRSI K > 0.8 且 K 下穿 D + RSI > 65
2. **阻力拒絕**: 價格接近樞紐阻力位R1/R2+ OBV 流出
3. **趨勢反轉**: MACD 死叉 + EMA9 下穿 EMA21 + CMF < 0
### 過濾條件(必須全部滿足)
- 成交量需高於 20 期平均(確認動能)
- 5分鐘 ADX > 15排除極度盤整
- OBV 方向需與交易方向一致(量價確認)
- ATR 過高時降低 suggested_amount_pct波動風控
### 信心分數指引
- 0.8+: 多時間框架對齊 + 3個以上確認指標
- 0.6-0.8: 多時間框架對齊 + 2個確認
- 0.4-0.6: 單一時間框架訊號,降低倉位
- < 0.4: 不建議交易,回傳 HOLD
請以 JSON 格式回傳,每個幣種一個物件:
```json
[
{{
"symbol": "tBTCUST",
"action": "BUY" | "SELL" | "HOLD",
"confidence": 0.0-1.0,
"reason": "簡短理由(含觸發的指標)",
"suggested_amount_pct": 0.05-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 []