#!/usr/bin/env python3
"""technical_analysis.py — 技术面分析模块 v2

基于多日价格数据计算支撑位/压力位：
1. 缓存每日 HLC 到 price_history.json
2. 使用 5 日最高/最低计算枢轴点
3. 结合振幅自动调整区间宽度

使用方式：
    from technical_analysis import full_analysis
    result = full_analysis("603259")  # 自动识别A股/港股
"""

import json
from datetime import datetime, date
from mo_data import get_price

HISTORY_PATH = "/home/hmo/web-dashboard/data/price_history.json"
HISTORY_DAYS = 60   # 使用最近 N 天的 HLC 数据


def _load_history():
    """读取价格历史缓存"""
    try:
        return json.load(open(HISTORY_PATH))
    except (FileNotFoundError, json.JSONDecodeError):
        return {}


def _save_history(h):
    json.dump(h, open(HISTORY_PATH, "w"), ensure_ascii=False, indent=2)


def _market_prefix(code):
    """根据代码确定腾讯API前缀"""
    if code.startswith("sh") or code.startswith("sz") or code.startswith("hk"):
        code = code[2:] if code[2:].isdigit() else code
    raw = str(code).split("_")[0]
    if len(raw) == 5 and raw.isdigit():
        return "hk"
    if raw.startswith("6") or raw.startswith("5"):
        return "sh"
    return "sz"


def get_quote(code):
    """获取行情数据。使用 mo_data.get_price 统一入口，缓存+格式转换"""
    import time
    _cache = get_quote.__dict__.get("_cache", {})
    now = time.time()
    cached = _cache.get(code)
    if cached and (now - cached["ts"]) < 60:
        return cached["data"]

    price, change_pct = get_price(code)
    if price is None:
        return {"code": code, "error": "价格获取失败"}

    raw = str(code).split("_")[0]
    prefix = _market_prefix(code)
    today_str = date.today().isoformat()

    q = {
        "code": raw,
        "market": prefix,
        "name": code,
        "price": price,
        "close_yest": None,
        "open": None,
        "high": None,
        "low": None,
        "volume": None,
        "amount": None,
        "change": None,
        "change_pct": change_pct or 0,
        "amplitude": None,
        "turnover_rate": None,
        "pe": None,
        "pb": None,
        "limit_up": None,
        "limit_down": None,
        "avg_price": None,
        "inner_vol": None,
        "outer_vol": None,
        "timestamp": "",
        "_date": today_str,
    }

    # 写入价格历史缓存（每日一次，只存价格）
    history = _load_history()
    if raw not in history:
        history[raw] = []
    days = history[raw]
    if days and len(days) > 0 and days[-1].get("date") == today_str:
        days[-1]["close"] = price
    else:
        days.append({"date": today_str, "close": price})
    history[raw] = days[-HISTORY_DAYS:]
    _save_history(history)

    # 写入60秒缓存
    get_quote.__dict__["_cache"] = {**get_quote.__dict__.get("_cache", {}), code: {"ts": now, "data": q}}

    return q


def calc_support_resistance(q):
    """计算技术支撑位和压力位 — 多日枢轴点算法

    使用多个数据源确定有效区间：
    1. 当日波幅（H-L）
    2. 最近 N 日的最高/最低（从 price_history.json 读取）
    3. 价格基数的百分比（对大市值低波动股票有效）
    """
    h = q.get("high")
    l = q.get("low")
    c = q.get("price")
    yc = q.get("close_yest")
    amplitude = q.get("amplitude")  # 当日振幅%
    code = q.get("code", "")

    if not all([h, l, c]):
        return {"error": "数据不足"}

    # 多日最高/最低（从历史缓存读取）
    history = _load_history()
    hist_days = history.get(code, [])
    multi_high = max(d["high"] for d in hist_days) if hist_days else h
    multi_low = min(d["low"] for d in hist_days) if hist_days else l

    # 有效区间 = max(当日波幅, 多日波幅, 价格×5%)
    daily_range = h - l
    multi_range = multi_high - multi_low
    min_range = c * 0.05  # 5%价格基数

    effective_range = max(daily_range, multi_range, min_range)

    # 如果股价接近多日高点（>80%分位），说明在上升趋势中，扩大区间
    trend_position = (c - multi_low) / (multi_high - multi_low) if multi_high > multi_low else 0.5
    if trend_position > 0.8:
        # 高位运行，扩大有效区间到价格的8%确保合理空间
        effective_range = max(effective_range, c * 0.08)
    elif trend_position < 0.2:
        # 低位运行，同样扩大
        effective_range = max(effective_range, c * 0.08)

    # 如果振幅数据可用且振幅较小(<3%)，进一步扩大区间确保有效性
    if amplitude and amplitude > 0 and amplitude < 3:
        # 低波动股票用 振幅×3 作为最小范围
        amp_based = c * amplitude / 100 * 3
        effective_range = max(effective_range, amp_based)

    # 枢轴点 (Pivot Point)
    pp = (h + l + c) / 3

    # 支撑位
    s1 = 2 * pp - h           # 弱支撑
    s2 = pp - effective_range  # 强支撑

    # 压力位
    r1 = 2 * pp - l           # 弱压力
    r2 = pp + effective_range  # 强压力

    # 参考昨收调整
    if yc:
        if yc < s1:
            s1 = yc
        if yc > r1:
            r1 = yc

    # A股涨停/跌停价作为极端边界
    limit_up = q.get("limit_up")
    limit_down = q.get("limit_down")
    market = q.get("market", "hk")
    if market != "hk" and limit_up and limit_down:
        # 注意：当现价逼近涨停/跌停时，limit不再是有效边界
        # 用有效区间判断：如果自然计算的r2/s2在合理范围内不截断
        natural_r2 = r2
        natural_s2 = s2
        # 涨停限制只对距离现价超过2%的强压位生效
        if limit_up < r2 and (limit_up - c) / c < 0.02:
            # 涨停价离现价<2%，说明可能封板，不截断
            pass  # 使用自然计算的r2
        elif limit_up < r2:
            r2 = limit_up
        if limit_down > s2 and (c - limit_down) / c < 0.02:
            pass  # 接近跌停，不截断
        elif limit_down > s2:
            s2 = limit_down

    return {
        "strong_support": round(s2, 2),
        "weak_support": round(s1, 2),
        "pivot": round(pp, 2),
        "weak_resist": round(r1, 2),
        "strong_resist": round(r2, 2),
        "today_high": h,
        "today_low": l,
        "multi_high": multi_high,
        "multi_low": multi_low,
        "effective_range": round(effective_range, 2),
    }


def analyze_candlestick(q):
    """判断K线形态"""
    o = q.get("open")
    c = q.get("price")
    h = q.get("high")
    l = q.get("low")
    yc = q.get("close_yest")

    if not all([o, c, h, l]):
        return {"pattern": "unknown", "sentiment": "neutral"}

    if c >= o:
        body = c - o
        upper = h - c
        lower = o - l
        is_green = True
    else:
        body = o - c
        upper = h - o
        lower = c - l
        is_green = False

    total_range = h - l
    if total_range == 0:
        return {"pattern": "平盘", "sentiment": "neutral"}

    body_pct = body / total_range * 100
    upper_pct = upper / total_range * 100
    lower_pct = lower / total_range * 100

    if body_pct < 5:
        if upper_pct > 60:
            pattern = "倒T线/射击之星"
            sentiment = "bearish"
        elif lower_pct > 60:
            pattern = "锤子线/T字线"
            sentiment = "bullish"
        else:
            pattern = "十字星"
            sentiment = "neutral"
    elif body_pct < 30:
        if upper_pct > 40 and lower_pct > 40:
            pattern = "长影星线"
            sentiment = "neutral"
        elif upper_pct > 40:
            pattern = "倒T线/射击之星"
            sentiment = "bearish" if is_green else "bearish"
        elif lower_pct > 40:
            pattern = "锤子线/T字线"
            sentiment = "bullish" if is_green else "bullish"
        else:
            pattern = "小阳线" if is_green else "小阴线"
            sentiment = "bullish" if is_green else "bearish"
    else:
        if upper_pct > 30:
            pattern = "带上影阳线" if is_green else "带上影阴线"
            sentiment = "neutral" if is_green else "bearish"
        elif lower_pct > 30:
            pattern = "带下影阳线" if is_green else "带下影阴线"
            sentiment = "bullish" if is_green else "neutral"
        else:
            pattern = "光头光脚阳线" if is_green else "光头光脚阴线"
            sentiment = "bullish" if is_green else "bearish"

    gap_up = ""
    gap_down = ""
    if yc:
        if o > yc * 1.01:
            gap_up = "跳空高开"
            if not is_green:
                sentiment = "neutral"
        elif o < yc * 0.99:
            gap_down = "跳空低开"
            if is_green:
                sentiment = "neutral"

    return {
        "pattern": pattern,
        "sentiment": sentiment,
        "body_pct": round(body_pct, 1),
        "upper_shadow_pct": round(upper_pct, 1),
        "lower_shadow_pct": round(lower_pct, 1),
        "is_green": is_green,
        "gap": gap_up or gap_down or "无跳空",
    }


def analyze_volume(q):
    """量价分析"""
    outer = q.get("outer_vol")
    inner = q.get("inner_vol")
    turnover = q.get("turnover_rate")

    result = {}
    if outer and inner and (outer + inner) > 0:
        ratio = outer / (outer + inner)
        result["buy_sell_ratio"] = round(ratio, 2)
        if ratio > 0.55:
            result["volume_signal"] = "主动买盘占优"
        elif ratio < 0.45:
            result["volume_signal"] = "主动卖盘占优"
        else:
            result["volume_signal"] = "买卖均衡"
    else:
        result["volume_signal"] = "数据不足"

    if turnover:
        result["turnover_rate"] = turnover

    return result


def analyze_volume_trend(code):
    """量价趋势分析：对比历史N日平均成交量，检测量价背离模式

    从 price_history.json 读取历史数据，比较今日量价关系。
    """
    result = {}
    try:
        history = _load_history()
        days = history.get(code, [])
        if len(days) < 3:
            result["trend"] = "数据不足"
            return result

        today = days[-1]
        prev = days[-2] if len(days) >= 2 else None
        today_vol = today.get("volume", 0)
        today_close = today.get("close", 0)

        if not today_vol or not today_close:
            result["trend"] = "数据不足"
            return result

        # 计算N日均量
        vols_5 = [d.get("volume", 0) for d in days[-6:-1] if d.get("volume")]
        vols_20 = [d.get("volume", 0) for d in days[-21:-1] if d.get("volume")]
        avg_5 = sum(vols_5) / len(vols_5) if vols_5 else 0
        avg_20 = sum(vols_20) / len(vols_20) if vols_20 else 0

        vol_ratio_vs_5 = today_vol / avg_5 if avg_5 > 0 else 0
        vol_ratio_vs_20 = today_vol / avg_20 if avg_20 > 0 else 0

        result["avg_volume_5d"] = round(avg_5, 0)
        result["avg_volume_20d"] = round(avg_20, 0)
        result["today_volume"] = int(today_vol)
        result["volume_ratio_vs_5d"] = round(vol_ratio_vs_5, 2)
        result["volume_ratio_vs_20d"] = round(vol_ratio_vs_20, 2)

        # 最近3日的收盘价和成交量趋势
        if len(days) >= 3:
            recent_close = [d.get("close", 0) for d in days[-4:-1]]
            recent_vol = [d.get("volume", 0) for d in days[-4:-1]]
            if all(recent_close) and all(recent_vol):
                price_up = today_close > recent_close[-1]
                vol_up = today_vol > recent_vol[-1]

                # 量价模式判定
                if vol_ratio_vs_5 >= 1.8:
                    # 明显放量
                    if price_up:
                        result["trend"] = "放量上攻"
                        result["action"] = "buy_conformation"
                    else:
                        # 价格下跌但大幅放量 = 恐慌？还是承接收筹？
                        # 看今日K线：如果是阳线（低开高走）= 承接
                        # 简单判断：如果close > open = 有承接
                        result["trend"] = "放量下跌"
                        result["action"] = "watch"
                elif vol_ratio_vs_5 <= 0.6:
                    # 明显缩量
                    if price_up:
                        result["trend"] = "缩量上涨"
                        result["action"] = "divergence"
                    else:
                        result["trend"] = "缩量回调"
                        result["action"] = "healthy_pullback"
                elif vol_ratio_vs_5 >= 1.3:
                    # 温和放量
                    if price_up:
                        result["trend"] = "温和放量上涨"
                        result["action"] = "bullish"
                    else:
                        result["trend"] = "温和放量下跌"
                        result["action"] = "bearish"
                else:
                    # 正常量
                    if price_up:
                        result["trend"] = "正常量上涨"
                        result["action"] = "neutral_bullish"
                    else:
                        result["trend"] = "正常量下跌"
                        result["action"] = "neutral_bearish"

        # 量价背离检测：价格走高但成交量逐日递减
        if len(days) >= 5:
            close_5 = [d.get("close", 0) for d in days[-5:]]
            vol_5 = [d.get("volume", 0) for d in days[-5:]]
            if all(close_5) and all(vol_5):
                close_trend = close_5[-1] - close_5[0]
                vol_trend = vol_5[-1] - vol_5[0]
                # 价格涨但量跌 = 顶背离
                if close_trend > 0 and vol_trend < 0 and abs(vol_trend) > sum(vol_5) * 0.3:
                    result["divergence"] = "顶背离(价涨量缩)"
                # 价格跌但量涨 = 底背离
                elif close_trend < 0 and vol_trend > 0 and abs(vol_trend) > sum(vol_5) * 0.3:
                    result["divergence"] = "底背离(价跌量增)"

    except Exception as e:
        result["trend_error"] = str(e)

    return result


def analyze_volume_deep(code):
    """深度量价分析：从日K线分析量价配合/背离/建仓/出货

    使用 mtf_cache 表的日K线数据做历史量价分析。
    """
    import sqlite3
    from pathlib import Path

    DATA_DIR = Path(__file__).parent.parent / "data"
    try:
        conn = sqlite3.connect(str(DATA_DIR / "mofin.db"))
        row = conn.execute("SELECT cache_json FROM mtf_cache WHERE code=?", (code,)).fetchone()
        conn.close()
        if not row:
            return {"volume_signal": "数据不足"}
        data = json.loads(row[0])
    except Exception:
        return {"volume_signal": "数据不足"}

    daily = data.get("daily", [])
    if len(daily) < 5:
        return {"volume_signal": "数据不足"}

    closes = [d["close"] for d in daily]
    volume = [d["volume"] for d in daily]
    n = len(daily)

    # 基准：最近20日均量（不足20日则用全部）
    lookback = min(20, n - 1)
    avg_vol_20d = sum(volume[-lookback-1:-1]) / lookback if lookback > 0 else volume[-1]

    # 最近N日的量比
    recent = min(5, n)
    recent_vol_ratios = []
    for i in range(recent):
        vol = volume[-i-1] if i+1 <= n else volume[0]
        recent_vol_ratios.append(round(vol / avg_vol_20d, 2) if avg_vol_20d > 0 else 1)

    today_ratio = recent_vol_ratios[0] if recent_vol_ratios else 1
    recent_max_ratio = max(recent_vol_ratios) if recent_vol_ratios else 1

    # 量价配合度
    signals = []
    patterns = {}

    # 1. 放量检测（量比 > 2x）
    if today_ratio > 2.0:
        signals.append(f"量比{today_ratio:.1f}倍放量")
        patterns["volume_surge"] = True
        # 放量方向
        if len(closes) >= 2 and closes[-1] > closes[-2]:
            patterns["surge_direction"] = "放量上涨"
            if today_ratio > 2.5 and closes[-1] > closes[-2] * 1.03:
                signals[-1] += "↑主力买入"
            else:
                signals[-1] += "↑"
        elif len(closes) >= 2 and closes[-1] < closes[-2]:
            patterns["surge_direction"] = "放量下跌"
            if today_ratio > 2.5 and closes[-1] < closes[-2] * 0.97:
                signals[-1] += "↓主力出货"
            else:
                signals[-1] += "↓"
        else:
            patterns["surge_direction"] = "放量平盘"
    elif today_ratio < 0.5:
        signals.append(f"量比{today_ratio:.1f}倍缩量")
        patterns["volume_shrink"] = True
    else:
        signals.append(f"量比{today_ratio:.1f}倍正常")
        patterns["volume_normal"] = True

    # 2. 量价趋势分析（近5日 vs 前5日）
    if len(daily) >= 10:
        recent5_vol = sum(volume[-5:]) / 5
        prev5_vol = sum(volume[-10:-5]) / 5
        vol_trend = "增" if recent5_vol > prev5_vol * 1.3 else ("减" if recent5_vol < prev5_vol * 0.7 else "稳")
        recent5_price = closes[-5:]
        price_trend = "涨" if recent5_price[-1] > recent5_price[0] else ("跌" if recent5_price[-1] < recent5_price[0] * 0.95 else "平")

        if vol_trend == "增" and price_trend == "涨":
            patterns["accumulation"] = True  # 量价齐升=建仓
            signals.append(f"近5日{vol_trend}量{price_trend}价=建仓型")
        elif vol_trend == "增" and price_trend == "跌":
            patterns["distribution"] = True  # 放量下跌=出货
            signals.append(f"近5日{vol_trend}量{price_trend}价=⚠️出货型")
        elif vol_trend == "减" and price_trend == "涨":
            patterns["divergence"] = True  # 量缩价涨=背离
            signals.append(f"近5日{vol_trend}量{price_trend}价=⬆量价背离")
        elif vol_trend == "减" and price_trend == "跌":
            patterns["washout"] = True  # 缩量下跌=洗盘末端
            signals.append(f"近5日{vol_trend}量{price_trend}价=洗盘特征")
        else:
            signals.append(f"近5日{vol_trend}量{price_trend}价")
    else:
        vol_trend = price_trend = "?"

    # 3. 寻找历史放量区间（主力活动痕迹）
    surge_days = []
    for i in range(max(0, n - 60), n):
        vol_ratio = volume[i] / avg_vol_20d if avg_vol_20d > 0 else 0
        if vol_ratio > 2.0:
            surge_days.append({
                "date": daily[i].get("date", ""),
                "ratio": round(vol_ratio, 1),
                "close": closes[i],
                "direction": "涨" if (i > 0 and closes[i] > closes[i-1]) else "跌"
            })

    # 汇总描述
    vol_level = "放量" if today_ratio > 2.0 else ("缩量" if today_ratio < 0.5 else "正常")
    price_vol = f"{vol_level}"
    if patterns.get("accumulation"):
        price_vol = f"量价齐升(建仓特征) | {signals[-1]}"
    elif patterns.get("distribution"):
        price_vol = f"放量下跌⚠️ | {signals[-1]}"
    elif patterns.get("washout"):
        price_vol = f"缩量回踩(洗盘末端) | {signals[-1]}"
    elif patterns.get("divergence"):
        price_vol = f"量价背离 | {signals[-1]}"

    return {
        "volume_signal": " ; ".join(signals) if signals else "正常",
        "volume_ratio": today_ratio,
        "avg_volume_20d": int(avg_vol_20d),
        "recent_ratios": recent_vol_ratios,
        "surge_count_60d": len(surge_days),
        "price_vol_description": price_vol,
        "patterns": patterns,
        "surge_days": surge_days[-5:] if surge_days else [],
    }


def full_analysis(code):
    """完整技术分析（带30秒缓存，避免分钟级波动）"""
    import time
    _cache = full_analysis.__dict__.get("_cache", {})
    now = time.time()
    cached = _cache.get(code)
    if cached and (now - cached["ts"]) < 30:
        return cached["data"]

    q = get_quote(code)
    if not q or "error" in q:
        return q

    sr = calc_support_resistance(q)
    candle = analyze_candlestick(q)
    vol = analyze_volume(q)
    # 深度量价分析（使用日K线历史数据）
    vol_deep = {}
    try:
        vol_deep = analyze_volume_deep(code)
    except Exception:
        pass  # graceful degradation

    # 多周期+均线分析（整合 multi_timeframe）
    mtf = {}
    try:
        from multi_timeframe import full_multi_tf_analysis as _mtf
        mtf_raw = _mtf(code)
        if mtf_raw and 'daily' in mtf_raw:
            d = mtf_raw['daily']
            mtf = {
                'mas': d.get('mas', {}),
                'multi_tf_sr': d.get('support_resistance', {}),
                'trend': d.get('trend', {}),
            }
            # 周线弱压/弱撑作为中周期参考
            if 'weekly' in mtf_raw:
                w = mtf_raw['weekly']
                ws = w.get('support_resistance', {})
                wt = w.get('trend', {})
                wm = w.get('mas', {})
                mtf['weekly'] = {
                    'weak_resist': ws.get('weak_resist'),
                    'weak_support': ws.get('weak_support'),
                    'strong_resist': ws.get('strong_resist'),
                    'strong_support': ws.get('strong_support'),
                    'trend': wt.get('direction', ''),
                    'ma5': wm.get('ma5'),
                    'ma10': wm.get('ma10'),
                }
            # 月线作为长期参考
            if 'monthly' in mtf_raw:
                m = mtf_raw['monthly']
                ms = m.get('support_resistance', {})
                mt = m.get('trend', {})
                mm = m.get('mas', {})
                mtf['monthly'] = {
                    'weak_resist': ms.get('weak_resist'),
                    'weak_support': ms.get('weak_support'),
                    'strong_resist': ms.get('strong_resist'),
                    'strong_support': ms.get('strong_support'),
                    'trend': mt.get('direction', ''),
                    'ma5': mm.get('ma5'),
                }
    except Exception:
        pass  # non-critical, graceful degradation

    result = {
        "quote": {
            "name": q.get("name", code),
            "price": q["price"],
            "change_pct": q.get("change_pct", 0),
            "open": q.get("open", 0),
            "high": q.get("high", 0),
            "low": q.get("low", 0),
            "close_yest": q.get("close_yest", 0),
            "volume": q.get("volume", 0),
            "amplitude": q.get("amplitude", 0),
        },
        "support_resistance": sr,
        "candlestick": candle,
        "volume": vol,
        "volume_deep": vol_deep,
        "multi_tf": mtf,
        "analyzed_at": datetime.now().strftime("%H:%M"),
    }

    # 写入缓存
    _cache[code] = {"ts": now, "data": result}
    full_analysis.__dict__["_cache"] = _cache
    return result


if __name__ == "__main__":
    import sys
    codes = sys.argv[1:] or ["603259", "002594", "00700"]
    for c in codes:
        r = full_analysis(c)
        print(json.dumps(r, ensure_ascii=False, indent=2))
        print()
