#!/usr/bin/env python3
"""
Multi-Session Market Analyzer
分析亚洲、伦敦、纽约三大交易时段的市场状态
输出：每个时段的偏向、波动性、流动性质量
用于增强策略决策（方向过滤 + 置信度调整）
"""
import json, math, time, pathlib, urllib.request
from datetime import datetime, timezone

# ============ 时区配置 ============
# 黄金主要交易时段（UTC）
SESSIONS = {
    "ASIA": {
        "name": "亚洲时段",
        "start_hour": 22,
        "end_hour": 8,
        "timezone": "UTC+8",
        "typical_range_pct": 0.40,  # 占日均波幅约40%
        "trend_bias": 0,   # -1=bearish, 0=neutral, 1=bullish (历史统计)
    },
    "LONDON": {
        "name": "伦敦时段",
        "start_hour": 8,   # 08:00 UTC
        "end_hour": 17,    # 17:00 UTC
        "timezone": "UTC+8",
        "typical_range_pct": 0.35,
        "trend_bias": 0,
    },
    "NEW_YORK": {
        "name": "纽约时段",
        "start_hour": 13,  # 13:00 UTC
        "end_hour": 22,    # 22:00 UTC
        "timezone": "UTC+8",
        "typical_range_pct": 0.50,  # 重叠时段波动最大
        "trend_bias": 0,
    },
    "ASIA_LONDON_OVERLAP": {
        "name": "亚欧重叠",
        "start_hour": 8,
        "end_hour": 13,
        "timezone": "UTC+8",
        "typical_range_pct": 0.55,
        "trend_bias": 0,
    },
    "LONDON_NY_OVERLAP": {
        "name": "欧美重叠",
        "start_hour": 13,
        "end_hour": 17,
        "timezone": "UTC+8",
        "typical_range_pct": 0.70,  # 最重要时段，趋势最强
        "trend_bias": 0,
    },
}

def get_live_bid_ask():
    """获取实时报价"""
    try:
        url = "https://forex-data-feed.swissquote.com/public-quotes/bboquotes/instrument/XAU/USD"
        req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"})
        with urllib.request.urlopen(req, timeout=5) as r:
            data = json.loads(r.read())
        profiles = data[0].get("spreadProfilePrices", [])
        if profiles:
            return float(profiles[0]["bid"]), float(profiles[0]["ask"])
        return float(data[0]["bids"][0]["price"]), float(data[0]["asks"][0]["price"])
    except Exception:
        return None, None

def get_ea_bars(count=200, interval="15m"):
    """通过 MT5 EA Bridge 获取 XAUUSD 历史K线（15分钟）"""
    try:
        import sys, pathlib, json, time
        sys.path.insert(0, str(pathlib.Path(__file__).parent.parent))
        from bridge.strategy import _read_bars_from_ea
        bars = _read_bars_from_ea(count=count)
        return bars
    except Exception:
        return []

def _ema(data, period):
    if len(data) < period:
        return [None] * len(data)
    k = 2.0 / (period + 1)
    ema = [None] * (period - 1)
    ema.append(sum(data[:period]) / period)
    for v in data[period:]:
        ema.append(v * k + ema[-1] * (1 - k))
    return ema

def _atr(bars, period=14):
    trs = []
    for i, b in enumerate(bars):
        if i == 0:
            trs.append(b["high"] - b["low"])
        else:
            hl = b["high"] - b["low"]
            hc = abs(b["high"] - bars[i-1]["close"])
            lc = abs(b["low"] - bars[i-1]["close"])
            trs.append(max(hl, hc, lc))
    ema_trs = _ema(trs, period)
    return [v if v is not None else 0 for v in ema_trs]

def _rsi(closes, period=14):
    gains, losses = [], []
    for i in range(1, len(closes)):
        delta = closes[i] - closes[i - 1]
        gains.append(max(delta, 0))
        losses.append(max(-delta, 0))
    if len(gains) < period:
        return [None] * len(closes)
    result = [None] * (period + 1)
    avg_gain = sum(gains[:period]) / period
    avg_loss = sum(losses[:period]) / period
    rs = avg_gain / avg_loss if avg_loss != 0 else 100
    result.append(100 - 100 / (1 + rs))
    for i in range(period, len(gains)):
        avg_gain = (avg_gain * (period - 1) + gains[i]) / period
        avg_loss = (avg_loss * (period - 1) + losses[i]) / period
        rs = avg_gain / avg_loss if avg_loss != 0 else 100
        result.append(100 - 100 / (1 + rs))
    return result


def _adx(bars, period=14):
    if len(bars) < period + 15:
        return 25.0, "UNCERTAIN"

    plus_dm, minus_dm, true_range = [], [], []
    for i in range(1, len(bars)):
        b = bars[i]
        pb = bars[i - 1]
        hl = b["high"] - pb["high"]
        lh = pb["low"] - b["low"]
        plus_dm.append(max(hl, 0) if hl > lh else 0)
        minus_dm.append(max(lh, 0) if lh > hl else 0)
        tr = max(b["high"] - b["low"], abs(b["high"] - pb["close"]), abs(b["low"] - pb["close"]))
        true_range.append(tr)

    if len(true_range) < period:
        return 25.0, "UNCERTAIN"

    atr_vals = _ema(true_range, period)
    ema_plus_dm = _ema(plus_dm, period)
    ema_minus_dm = _ema(minus_dm, period)
    plus_di, minus_di = [], []

    for i in range(len(plus_dm)):
        atr_v = atr_vals[i + 1] if (i + 1) < len(atr_vals) and atr_vals[i + 1] is not None else None
        if atr_v and atr_v != 0 and (i + 1) < len(ema_plus_dm) and ema_plus_dm[i + 1] is not None:
            plus_di.append(100 * ema_plus_dm[i + 1] / atr_v)
            minus_di.append(
                100 * ema_minus_dm[i + 1] / atr_v if (i + 1) < len(ema_minus_dm) and ema_minus_dm[i + 1] is not None else 0
            )
        else:
            plus_di.append(0)
            minus_di.append(0)

    dx = []
    for i in range(len(plus_di)):
        di_sum = plus_di[i] + minus_di[i]
        dx.append(0 if di_sum == 0 else 100 * abs(plus_di[i] - minus_di[i]) / di_sum)

    adx_vals = _ema(dx, period)
    adx = adx_vals[-1] if adx_vals and adx_vals[-1] is not None else 25.0
    if adx >= 25:
        label = "TRENDING"
    elif adx >= 18:
        label = "RANGING"
    else:
        label = "UNCERTAIN"
    return adx, label


def _classify_asia_profile(session_key, direction, ema_bias, atr_pct, score, adx_value):
    if session_key not in ("ASIA", "ASIA_LONDON_OVERLAP"):
        return {
            "mode": "NORMAL",
            "block_new_positions": False,
            "confidence_penalty": 0.0,
            "risk_multiplier": 1.0,
            "entry_style": "MARKET",
        }

    overlap = session_key == "ASIA_LONDON_OVERLAP"
    low_atr_cutoff = 0.10 if not overlap else 0.12
    trend_atr_cutoff = 0.14 if not overlap else 0.15
    weak_points = 0
    if direction == "NEUTRAL":
        weak_points += 1
    if ema_bias == "NEUTRAL":
        weak_points += 1
    if adx_value < (16 if not overlap else 18):
        weak_points += 1
    if atr_pct < low_atr_cutoff:
        weak_points += 1
    if abs(score) < 2:
        weak_points += 1
    noise = weak_points >= (2 if not overlap else 3)
    trend = (
        direction != "NEUTRAL"
        and ema_bias == direction
        and adx_value >= (22 if not overlap else 24)
        and atr_pct >= trend_atr_cutoff
        and abs(score) >= 3
    )

    if trend:
        return {
            "mode": "ASIA_TREND",
            "block_new_positions": False,
            "confidence_penalty": -0.01,
            "risk_multiplier": 0.8 if not overlap else 0.9,
            "entry_style": "LIMIT",
        }
    if noise:
        return {
            "mode": "ASIA_NOISE",
            "block_new_positions": False,
            "confidence_penalty": -0.08 if not overlap else -0.05,
            "risk_multiplier": 0.35 if not overlap else 0.45,
            "entry_style": "LIMIT",
        }
    return {
        "mode": "ASIA_DEFENSIVE",
        "block_new_positions": False,
        "confidence_penalty": -0.05 if not overlap else -0.03,
        "risk_multiplier": 0.55 if not overlap else 0.7,
        "entry_style": "LIMIT",
    }

def _hour_in_session(hour, session_key):
    cfg = SESSIONS[session_key]
    start_h = cfg["start_hour"]
    end_h = cfg["end_hour"]
    if start_h > end_h:
        return hour >= start_h or hour < end_h
    return start_h <= hour < end_h

def get_current_session():
    """判断当前处于哪个交易时段"""
    now_utc = datetime.now(timezone.utc)
    h = now_utc.hour

    if 13 <= h < 17:
        return "LONDON_NY_OVERLAP"
    elif 8 <= h < 13:
        return "ASIA_LONDON_OVERLAP"
    elif 17 <= h < 22:
        return "NEW_YORK"
    elif _hour_in_session(h, "ASIA"):
        return "ASIA"
    return "LONDON"

def filter_session_bars(bars, session_key):
    """
    过滤出属于特定时段的K线（UTC小时范围）
    """
    filtered = []
    for b in bars:
        dt = datetime.fromtimestamp(b["time"], tz=timezone.utc)
        h = dt.hour
        if _hour_in_session(h, session_key):
            filtered.append(b)
    return filtered

def analyze_session(all_bars, session_key):
    """
    分析特定时段的K线，返回该时段的状态。
    """
    cfg = SESSIONS[session_key]

    # 过滤该时段的K线
    bars = filter_session_bars(all_bars, session_key)

    if len(bars) < 10:
        # 数据不足，用全量数据估算，注意排除正在形成的当前K线 (-101:-1)
        bars = all_bars[-101:-1] if len(all_bars) >= 101 else all_bars[:-1]
        
    if len(bars) < 10:
        return None

    closes = [b["close"] for b in bars]
    highs  = [b["high"]  for b in bars]
    lows   = [b["low"]   for b in bars]

    # 计算 EMA 方向（趋势）
    ema9  = _ema(closes, 9)
    ema21 = _ema(closes, 21)
    ema50 = _ema(closes, 50)

    n = len(closes) - 1
    def v(arr): return arr[n] if n < len(arr) and arr[n] is not None else 0

    # ATR（波动性）
    atr_vals = _atr(bars, 14)
    atr_now = v(atr_vals)

    # RSI
    rsi_vals = _rsi(closes, 14)
    rsi_now = v(rsi_vals)
    adx_now, adx_label = _adx(bars, 14)

    # 计算日内范围（平均）
    daily_ranges = []
    for i in range(len(bars)):
        if i > 0:
            daily_ranges.append(bars[i]["high"] - bars[i]["low"])

    avg_range = sum(daily_ranges) / max(len(daily_ranges), 1) if daily_ranges else 0

    # 当前价格在 EMA50 的相对位置
    ema50v = v(ema50)
    ema9v  = v(ema9)
    ema21v = v(ema21)
    current_price = closes[-1] if closes else 0

    # 时段偏向判断
    # 1. EMA 排列
    if ema9v > ema21v > ema50v:
        ema_bias = "BULLISH"
        ema_score = 1
    elif ema9v < ema21v < ema50v:
        ema_bias = "BEARISH"
        ema_score = -1
    else:
        ema_bias = "NEUTRAL"
        ema_score = 0

    # 2. RSI 极端区域
    if rsi_now > 65:
        rsi_zone = "OVERBOUGHT"
    elif rsi_now < 35:
        rsi_zone = "OVERSOLD"
    else:
        rsi_zone = "NEUTRAL"

    # 3. 波动性评估（ATR vs 历史）
    atr_pct = (atr_now / current_price) * 100 if current_price else 0

    # 综合评分（-10 到 +10）
    score = ema_score * 3
    if rsi_zone == "OVERSOLD":
        score += 1
    elif rsi_zone == "OVERBOUGHT":
        score -= 1
    if atr_pct > 0.35:
        score += 1  # 高波动 = 更多机会
    elif atr_pct < 0.15:
        score -= 1  # 低波动 = 谨慎

    # 方向偏向
    if score >= 3:
        direction = "BULLISH"
        confidence_adj = 0.05   # 提高做多置信度
    elif score <= -3:
        direction = "BEARISH"
        confidence_adj = 0.05   # 提高做空置信度
    else:
        direction = "NEUTRAL"
        confidence_adj = -0.02  # 降低置信度

    session_profile = _classify_asia_profile(session_key, direction, ema_bias, atr_pct, score, adx_now)
    confidence_adj += session_profile["confidence_penalty"]

    return {
        "session": session_key,
        "name": cfg["name"],
        "direction": direction,
        "ema_bias": ema_bias,
        "rsi_zone": rsi_zone,
        "rsi": round(rsi_now, 1),
        "ema9": round(ema9v, 3),
        "ema21": round(ema21v, 3),
        "ema50": round(ema50v, 3),
        "atr": round(atr_now, 3),
        "atr_pct": round(atr_pct, 3),
        "adx": round(adx_now, 2),
        "trend_strength": adx_label,
        "avg_range": round(avg_range, 3),
        "confidence_adj": confidence_adj,  # 对基础置信度的调整
        "score": score,
        "current_price": round(current_price, 3),
        "bar_count": len(bars),
        "session_profile": session_profile,
    }

def get_session_bias(session_key):
    """返回时段的固定偏向（历史统计）"""
    biases = {
        "ASIA":              {"bias": "NEUTRAL",  "note": "低波动，观望为主"},
        "LONDON":            {"bias": "BULLISH",   "note": "伦敦开盘推动方向"},
        "NEW_YORK":          {"bias": "BULLISH",   "note": "美国数据前后波动大"},
        "ASIA_LONDON_OVERLAP": {"bias": "NEUTRAL", "note": "亚欧交接，震荡"},
        "LONDON_NY_OVERLAP": {"bias": "BULLISH",  "note": "最重要时段，趋势最强"},
    }
    return biases.get(session_key, {"bias": "NEUTRAL", "note": ""})

def analyze_all_sessions():
    """
    主分析函数：加载数据 → 分析各时段 → 综合输出
    """
    bid, ask = get_live_bid_ask()
    if bid is None:
        return {"error": "无法获取实时报价", "sessions": {}}

    # 获取历史数据（5天5分钟数据）
    bars = get_ea_bars(count=200)

    current_session_key = get_current_session()

    results = {}
    all_scores = []
    all_directions = []

    for session_key in SESSIONS:
        analysis = analyze_session(bars, session_key)
        if analysis:
            # 叠加历史偏向
            hist = get_session_bias(session_key)
            analysis["hist_bias"] = hist["bias"]
            analysis["hist_note"] = hist["note"]

            # 当前活跃时段标记
            analysis["is_active"] = (session_key == current_session_key)

            results[session_key] = analysis
            all_scores.append(analysis["score"])
            all_directions.append(analysis["direction"])

    # 综合判断
    avg_score = sum(all_scores) / max(len(all_scores), 1)
    bullish_count = all_directions.count("BULLISH")
    bearish_count = all_directions.count("BEARISH")

    if bullish_count >= 3:
        overall_direction = "BULLISH"
    elif bearish_count >= 3:
        overall_direction = "BEARISH"
    elif bullish_count > bearish_count:
        overall_direction = "SLIGHTLY_BULLISH"
    elif bearish_count > bullish_count:
        overall_direction = "SLIGHTLY_BEARISH"
    else:
        overall_direction = "NEUTRAL"

    # 当前时段加权（更重要）
    current_analysis = results.get(current_session_key, {})
    current_direction = current_analysis.get("direction", "NEUTRAL")
    current_conf_adj  = current_analysis.get("confidence_adj", 0)
    current_session_profile = current_analysis.get("session_profile", {
        "mode": "NORMAL",
        "block_new_positions": False,
        "confidence_penalty": 0.0,
        "risk_multiplier": 1.0,
        "entry_style": "MARKET",
    })

    return {
        "timestamp": time.strftime("%Y-%m-%dT%H:%M:%SZ"),
        "bid": bid, "ask": ask,
        "spread": round((ask - bid) * 100, 1),
        "current_session": current_session_key,
        "current_session_name": SESSIONS[current_session_key]["name"],
        "overall_direction": overall_direction,
        "overall_score": round(avg_score, 2),
        "session_count": {
            "bullish": bullish_count,
            "neutral": all_directions.count("NEUTRAL"),
            "bearish": bearish_count,
        },
        "confidence_adj_for_signal": round(current_conf_adj, 3),
        "current_session_profile": current_session_profile,
        "current_analysis": current_analysis,
        "all_sessions": results,
        "bars_used": len(bars),
    }

def get_strategy_recommendation(analysis_result):
    """
    根据多时段分析，生成策略建议
    """
    if "error" in analysis_result:
        return {"action": "HOLD", "reason": analysis_result["error"]}

    current = analysis_result.get("current_analysis", {})
    overall = analysis_result["overall_direction"]
    current_dir = current.get("direction", "NEUTRAL")
    rsi = current.get("rsi", 50)
    ema_bias = current.get("ema_bias", "NEUTRAL")
    score = analysis_result.get("overall_score", 0)
    session_profile = current.get("session_profile", analysis_result.get("current_session_profile", {
        "mode": "NORMAL",
        "block_new_positions": False,
        "confidence_penalty": 0.0,
        "risk_multiplier": 1.0,
        "entry_style": "MARKET",
    }))
    session_mode = session_profile.get("mode", "NORMAL")
    risk_multiplier = session_profile.get("risk_multiplier", 1.0)
    entry_style = session_profile.get("entry_style", "MARKET")

    # 方向过滤
    # 如果当前时段和总体方向一致 → 增强置信度
    # 如果矛盾 → 降低置信度或反向

    if overall == "BULLISH" and current_dir == "BULLISH":
        recommendation = "PREFER_LONG"
        reason = "多时段共振看涨"
        conf_bonus = 0.05
    elif overall == "BEARISH" and current_dir == "BEARISH":
        recommendation = "PREFER_SHORT"
        reason = "多时段共振看跌"
        conf_bonus = 0.05
    elif overall == "NEUTRAL" or current_dir == "NEUTRAL":
        recommendation = "CAUTIOUS"
        reason = "方向不明，缩小仓位"
        conf_bonus = -0.05
    elif overall == "SLIGHTLY_BULLISH":
        recommendation = "LONG_WITH_CAUTION"
        reason = "轻微看涨，控制仓位"
        conf_bonus = 0
    elif overall == "SLIGHTLY_BEARISH":
        recommendation = "SHORT_WITH_CAUTION"
        reason = "轻微看跌，控制仓位"
        conf_bonus = 0
    else:
        recommendation = "HOLD"
        reason = "方向冲突，观望"
        conf_bonus = -0.10

    # RSI 极端值二次过滤
    if rsi > 70 and recommendation in ("PREFER_LONG", "LONG_WITH_CAUTION"):
        recommendation = "AVOID_LONG"
        reason = f"RSI超买({rsi})，不追多"
        conf_bonus = -0.10
    elif rsi < 30 and recommendation in ("PREFER_SHORT", "SHORT_WITH_CAUTION"):
        recommendation = "AVOID_SHORT"
        reason = f"RSI超卖({rsi})，不追空"
        conf_bonus = -0.10

    if session_profile.get("block_new_positions"):
        recommendation = "AVOID"
        reason = f"{session_mode}：亚盘噪音主导，禁止新开仓"
        conf_bonus = min(conf_bonus, -0.12)
    elif session_mode == "ASIA_DEFENSIVE":
        if recommendation == "PREFER_LONG":
            recommendation = "LONG_WITH_CAUTION"
        elif recommendation == "PREFER_SHORT":
            recommendation = "SHORT_WITH_CAUTION"
        elif recommendation not in ("HOLD", "AVOID_LONG", "AVOID_SHORT"):
            recommendation = "CAUTIOUS"
        reason = f"{session_mode}：波动不足但未失真，只允许轻仓防守"
        conf_bonus = min(conf_bonus, -0.04)
    elif session_mode == "ASIA_TREND" and current_dir in ("BULLISH", "BEARISH"):
        if recommendation in ("CAUTIOUS", "HOLD"):
            recommendation = "LONG_WITH_CAUTION" if current_dir == "BULLISH" else "SHORT_WITH_CAUTION"
            reason = f"{session_mode}：亚盘顺向结构成立，可轻仓限价跟随"
        conf_bonus = max(conf_bonus, 0.01)

    return {
        "recommendation": recommendation,
        "reason": reason,
        "conf_bonus": conf_bonus,
        "overall_direction": overall,
        "current_direction": current_dir,
        "score": score,
        "rsi": rsi,
        "ema_bias": ema_bias,
        "session_mode": session_mode,
        "session_profile": session_profile,
        "risk_multiplier": risk_multiplier,
        "entry_style": entry_style,
    }

# ============ 测试入口 ============
if __name__ == "__main__":
    print("=== 多时段市场分析 ===")
    result = analyze_all_sessions()

    rec = get_strategy_recommendation(result)

    print(f"\n当前时段: {result.get('current_session_name','?')} ({result.get('current_session','?')})")
    print(f"总体方向: {result.get('overall_direction','?')} (score={result.get('overall_score','?')})")
    print(f"各时段偏向: 多头={result.get('session_count',{}).get('bullish',0)} 空头={result.get('session_count',{}).get('bearish',0)} 中性={result.get('session_count',{}).get('neutral',0)}")

    print(f"\n策略建议: {rec['recommendation']}")
    print(f"原因: {rec['reason']}")
    print(f"置信度调整: {rec['conf_bonus']:+.2f}")
    print(f"RSI: {rec['rsi']} | EMA偏向: {rec['ema_bias']}")

    print("\n--- 各时段详情 ---")
    for key, s in result.get("all_sessions", {}).items():
        marker = " ◀" if s.get("is_active") else ""
        print(f"  [{key}] {s['name']}{marker}: {s['direction']} EMA={s['ema_bias']} RSI={s['rsi']} score={s['score']}")
