#!/usr/bin/env python3
"""
OKX Auto Position Advisor
根据余额自动推荐开仓数量+止盈止损位

Usage:
    python3 okx_position_advisor.py --symbol ETH --side short --leverage 10
    python3 okx_position_advisor.py --symbol BTC --side long --leverage 5
    python3 okx_position_advisor.py --symbol ETH --side short  # 默认10x
"""

import re
import os
import sys
import json
import argparse
import ccxt
import math

# Import cost performance module
sys.path.insert(0, os.path.dirname(__file__))
from cost_performance import calc_cost_performance, calc_min_contracts_for_profit
from config_loader import get as cfg


def load_credentials():
    """Load OKX credentials from ~/.bashrc"""
    creds = {}
    with open(os.path.expanduser("~/.bashrc")) as f:
        for line in f:
            m = re.match(r'export\s+(OKX_\w+)=(.*)', line.strip())
            if m:
                val = m.group(2).strip()
                if val.startswith('"') and val.endswith('"'):
                    val = val[1:-1]
                elif val.startswith("'") and val.endswith("'"):
                    val = val[1:-1]
                creds[m.group(1)] = val
    return creds


def create_exchange(creds):
    """Create ccxt OKX exchange instance with proxy"""
    return ccxt.okx({
        'apiKey': creds['OKX_API_KEY'],
        'secret': creds['OKX_SECRET'],
        'password': creds['OKX_PASSPHRASE'],
        'proxies': {
            'http': 'http://127.0.0.1:7890',
            'https': 'http://127.0.0.1:7890',
        },
        'options': {'defaultType': 'swap'},
    })


def get_account_info(exchange):
    """Get account balance and positions"""
    balance = exchange.fetch_balance()
    usdt_free = float(balance.get('USDT', {}).get('free', 0))
    usdt_total = float(balance.get('USDT', {}).get('total', 0))

    positions = exchange.fetch_positions()
    active = []
    for p in positions:
        if float(p.get('contracts', 0)) > 0:
            active.append({
                'symbol': p['symbol'],
                'side': p['side'],
                'contracts': float(p['contracts']),
                'entry': float(p['entryPrice']) if p.get('entryPrice') else 0,
                'pnl': float(p.get('unrealizedPnl', 0)),
                'liq': float(p.get('liquidationPrice', 0)) if p.get('liquidationPrice') else 0,
            })

    return {
        'usdt_free': usdt_free,
        'usdt_total': usdt_total,
        'positions': active,
    }


def get_instrument(exchange, inst_id):
    """Get contract specifications"""
    inst = exchange.public_get_public_instruments({
        'instType': 'SWAP',
        'instId': inst_id,
    })
    spec = inst['data'][0]
    return {
        'ct_val': float(spec['ctVal']),       # contract value in base currency
        'min_sz': float(spec['minSz']),        # minimum order size
        'lot_sz': float(spec['lotSz']),        # order step size
        'ct_mult': float(spec.get('ctMult', 1)),
        'inst_id': inst_id,
    }


def calc_atr(exchange, symbol, timeframe='4h', periods=30):
    """Calculate Average True Range"""
    try:
        ohlcv = exchange.fetch_ohlcv(symbol, timeframe, limit=periods)
        if len(ohlcv) < 5:
            return None

        true_ranges = []
        for i in range(1, len(ohlcv)):
            high = ohlcv[i][2]
            low = ohlcv[i][3]
            prev_close = ohlcv[i - 1][4]
            tr = max(high - low, abs(high - prev_close), abs(low - prev_close))
            true_ranges.append(tr)

        return sum(true_ranges) / len(true_ranges)
    except Exception:
        return None


def calc_multi_atr(exchange, symbol):
    """多周期ATR融合: 1H×0.5 + 4H×0.3 + 1D×0.2 × 1.5
    
    比单用4H ATR更灵敏——1H应对短期波动，4H做主心骨，1D兜底。
    """
    try:
        atr_1h = calc_atr(exchange, symbol, '1h', 24)
        atr_4h = calc_atr(exchange, symbol, '4h', 30)
        atr_1d = calc_atr(exchange, symbol, '1d', 14)
        values = [v for v in [atr_1h, atr_4h, atr_1d] if v is not None]
        if not values:
            return None, None, None, None
        if atr_1h is not None and atr_4h is not None and atr_1d is not None:
            fused = (atr_1h * cfg('atr','weight_1h',0.5) + atr_4h * cfg('atr','weight_4h',0.3) + atr_1d * cfg('atr','weight_1d',0.2)) * cfg('atr','multiplier',1.5)
        elif atr_4h is not None:
            fused = atr_4h * cfg('atr','multiplier',1.5)
        else:
            fused = sum(values) / len(values) * cfg('atr','multiplier',1.5)
        return fused, atr_1h, atr_4h, atr_1d
    except Exception:
        return None, None, None, None


def estimate_trend_strength(exchange, symbol):
    """通过EMA12-EMA26斜率估算趋势强度
    
    Returns: ('strong_up'|'strong_down'|'ranging'|'weak_trend', slope_pct)
    """
    try:
        ohlcv = exchange.fetch_ohlcv(symbol, '4h', limit=30)
        closes = [c[4] for c in ohlcv[-26:]]
        if len(closes) < 14:
            return 'weak_trend', 0
        ema12 = sum(closes[-12:]) / 12
        ema26 = sum(closes) / 26
        slope = (ema12 - ema26) / ema26 * 100
        if slope > 0.5:     return 'strong_up', round(slope, 2)
        if slope < -0.5:    return 'strong_down', round(slope, 2)
        if abs(slope) < 0.1: return 'ranging', round(slope, 2)
        return 'weak_trend', round(slope, 2)
    except Exception:
        return 'weak_trend', 0


def _rr_by_trend():
    return cfg('rr_by_trend', 'strong_up', 3.0), cfg('rr_by_trend', 'strong_down', 3.0), cfg('rr_by_trend', 'weak_trend', 2.0), cfg('rr_by_trend', 'ranging', 1.5)

RR_BY_TREND = {
    'strong_up': cfg('rr_by_trend', 'strong_up', 3.0),
    'strong_down': cfg('rr_by_trend', 'strong_down', 3.0),
    'weak_trend': cfg('rr_by_trend', 'weak_trend', 2.0),
    'ranging': cfg('rr_by_trend', 'ranging', 1.5),
}

TREND_LABEL = {
    'strong_up': '强上升趋势',
    'strong_down': '强下降趋势',
    'weak_trend': '弱趋势',
    'ranging': '震荡',
}


def recommend_position(symbol, side, leverage, exchange, acct_info):
    """Calculate recommended position size, TP, SL"""

    # Get current price
    ticker = exchange.fetch_ticker(symbol)
    price = ticker['last']

    # Get instrument specs
    inst_id = symbol.replace('/', '-').replace(':USDT', '-SWAP').replace(':USD', '-SWAP')
    # Handle common formats: ETH/USDT:USDT -> ETH-USDT-SWAP
    parts = symbol.split('/')
    base = parts[0]
    inst_id = f"{base}-USDT-SWAP"

    spec = get_instrument(exchange, inst_id)
    ct_val = spec['ct_val']
    min_sz = spec['min_sz']
    lot_sz = spec['lot_sz']

    # Cap leverage for safety
    max_lev = cfg('position_sizing', 'max_leverage', 20)
    if leverage > max_lev:
        leverage = max_lev
    if leverage < 1:
        leverage = 1

    # Position sizing: use 45% of available balance
    avail_margin = acct_info['usdt_free'] * cfg('position_sizing', 'balance_utilization', 0.45)
    margin_per_contract = ct_val * price / leverage

    if margin_per_contract <= 0:
        return {'error': 'Invalid margin calculation'}

    raw_contracts = avail_margin / margin_per_contract
    # Round down to lot_sz
    contracts = int(raw_contracts / lot_sz) * lot_sz
    contracts = max(contracts, min_sz)

    if contracts < min_sz:
        return {
            'error': f'余额不足: 需要至少 {margin_per_contract * min_sz:.2f} USDT, 可用 {acct_info["usdt_free"]:.2f} USDT'
        }

    # Calculate A+E+D multi-timeframe ATR fusion (方案A)
    fused_atr, atr_1h, atr_4h, atr_1d = calc_multi_atr(exchange, symbol)

    if fused_atr and fused_atr > 0:
        sl_distance = fused_atr  # fused_atr already includes ×1.5 multiplier
    else:
        # Fallback: fixed percentage
        sl_distance = price * cfg('atr', 'fallback_sl_pct', 0.03)

    # Adaptive R:R based on trend strength (方案D)
    trend, slope = estimate_trend_strength(exchange, symbol)
    rr_target = RR_BY_TREND.get(trend, 2.0)
    tp_distance = sl_distance * rr_target

    # Calculate TP/SL prices
    if side == 'sell':  # Short
        tp_price = price - tp_distance
        sl_price = price + sl_distance
    else:  # Long
        tp_price = price + tp_distance
        sl_price = price - sl_distance

    # Calculate liquidation price estimate
    if side == 'sell':
        liq_price = price * (1 + 1 / leverage * cfg('safety', 'liq_estimate_factor', 0.9))  # ~90% of theoretical max
    else:
        liq_price = price * (1 - 1 / leverage * cfg('safety', 'liq_estimate_factor', 0.9))

    # Safety check: SL must be inside liquidation (20% buffer)
    if side == 'sell':
        # Short: SL is above entry, liq is further above
        # max_sl = entry + (liq - entry) * 0.8
        max_sl = price + (liq_price - price) * cfg('safety', 'liq_buffer', 0.8)
        if sl_price > max_sl:
            sl_price = max_sl
            tp_price = price - (sl_price - price) * 2  # Maintain R:R
    else:
        # Long: SL is below entry, liq is further below
        # min_sl = entry - (entry - liq) * 0.8
        min_sl = price - (price - liq_price) * cfg('safety', 'liq_buffer', 0.8)
        if sl_price < min_sl:
            sl_price = min_sl
            tp_price = price + (price - sl_price) * 2

    # Calculate percentages
    tp_pct = abs(tp_price - price) / price * 100
    sl_pct = abs(sl_price - price) / price * 100
    liq_pct = abs(liq_price - price) / price * 100

    # Risk/reward ratio
    rr = tp_pct / sl_pct if sl_pct > 0 else 0

    # Total margin used
    total_margin = contracts * margin_per_contract
    margin_pct = total_margin / acct_info['usdt_free'] * 100

    # Estimated P&L
    tp_pnl = contracts * ct_val * abs(tp_price - price)
    sl_pnl = contracts * ct_val * abs(sl_price - price)

    # Cost-performance check (性价比检查)
    fee_rate = cfg('cost_performance', 'fee_rate', 0.0005)
    cost_check = calc_cost_performance(
        entry_price=price,
        sl_price=sl_price,
        tp_price=tp_price,
        contracts=contracts,
        ct_val=ct_val,
        leverage=leverage,
        fee_rate=fee_rate
    )
    
    # If profit < 5 USDT, adjust contracts to meet minimum
    min_profit = cfg('position_sizing', 'min_profit_usdt', 10)
    if cost_check['profit_amount'] < min_profit:
        tp_distance = abs(tp_price - price)
        min_contracts = calc_min_contracts_for_profit(tp_distance, ct_val, min_profit=min_profit)
        # Round up to lot_sz
        min_contracts = math.ceil(min_contracts / lot_sz) * lot_sz
        
        if min_contracts * margin_per_contract <= acct_info['usdt_free']:
            contracts = min_contracts
            # Recalculate P&L
            tp_pnl = contracts * ct_val * abs(tp_price - price)
            sl_pnl = contracts * ct_val * abs(sl_price - price)
            total_margin = contracts * margin_per_contract
            margin_pct = total_margin / acct_info['usdt_free'] * 100
            
            # Recalculate cost check
            cost_check = calc_cost_performance(
                entry_price=price,
                sl_price=sl_price,
                tp_price=tp_price,
                contracts=contracts,
                ct_val=ct_val,
                leverage=leverage,
                fee_rate=fee_rate
            )

    return {
        'symbol': f"{base}/USDT",
        'side': side,
        'side_cn': '做空' if side == 'sell' else '做多',
        'leverage': leverage,
        'price': price,
        'contracts': contracts,
        'base_amount': contracts * ct_val,
        'margin': round(total_margin, 2),
        'margin_pct': round(margin_pct, 1),
        'tp_price': round(tp_price, 2),
        'tp_pct': round(tp_pct, 2),
        'tp_pnl': round(tp_pnl, 2),
        'sl_price': round(sl_price, 2),
        'sl_pct': round(sl_pct, 2),
        'sl_pnl': round(sl_pnl, 2),
        'rr': round(rr, 1),
        'liq_price': round(liq_price, 2),
        'liq_pct': round(liq_pct, 1),
        'atr_fused': round(fused_atr, 2) if fused_atr else None,
        'atr_1h': round(atr_1h, 2) if atr_1h else None,
        'atr_4h': round(atr_4h, 2) if atr_4h else None,
        'atr_1d': round(atr_1d, 2) if atr_1d else None,
        'trend': trend,
        'trend_label': TREND_LABEL.get(trend, ''),
        'slope': slope,
        'inst_id': inst_id,
        'ct_val': ct_val,
        'min_sz': min_sz,
        'acct_free': round(acct_info['usdt_free'], 2),
        'cost_check': cost_check,
        'auto_execute': cost_check['auto_execute'],
    }


def format_recommendation(rec):
    """Format recommendation as readable text"""
    if 'error' in rec:
        return f"❌ {rec['error']}"

    cost_check = rec.get('cost_check', {})
    rating = cost_check.get('rating', 'unknown')
    rating_emoji = cost_check.get('rating_emoji', '')
    rating_text = cost_check.get('rating_text', '')
    auto_execute = rec.get('auto_execute', False)

    # 根据性价比等级选择模板
    if rating == 'high':
        # 性价比高 - 自动开仓后推送
        lines = [
            f"✅ **{rec['symbol']} {rec['side_cn']}** 自动开仓",
            f"",
            f"📊 方向: {rec['side_cn']} | 杠杆: **{rec['leverage']}x**",
            f"📍 入场: **{rec['price']}**",
            f"🛑 止损: **{rec['sl_price']}** (-{rec['sl_pct']}%)",
            f"🎯 止盈: **{rec['tp_price']}** (+{rec['tp_pct']}%)",
            f"📐 盈亏比: **{rec['rr']}:1** ✅",
            f"",
            f"📦 张数: **{rec['contracts']}张** ({rec['base_amount']}个)",
            f"💰 保证金: {rec['margin']} USDT ({rec['margin_pct']}%)",
            f"",
            f"⚖️ 盈利: {cost_check['profit_amount']} USDT | 手续费: {cost_check['fee_cost']} USDT ({cost_check['fee_pct']}%)",
        ]
    elif rating == 'medium':
        # 性价比一般 - 等确认
        lines = [
            f"⚠️ **{rec['symbol']} {rec['side_cn']}** 性价比一般",
            f"",
            f"📊 方向: {rec['side_cn']} | 杠杆: **{rec['leverage']}x**",
            f"📍 入场: **{rec['price']}**",
            f"🛑 止损: **{rec['sl_price']}** (-{rec['sl_pct']}%)",
            f"🎯 止盈: **{rec['tp_price']}** (+{rec['tp_pct']}%)",
            f"📐 盈亏比: **{rec['rr']}:1** ⚠️",
            f"",
            f"📦 张数: **{rec['contracts']}张** ({rec['base_amount']}个)",
            f"💰 保证金: {rec['margin']} USDT ({rec['margin_pct']}%)",
            f"",
            f"⚠️ {cost_check.get('reason', '')}",
            f"",
            f"回复 **Y** 仍要开仓 / **N** 取消",
        ]
    else:
        # 性价比低 - 不建议
        lines = [
            f"❌ **{rec['symbol']} {rec['side_cn']}** 性价比低，不建议",
            f"",
            f"📊 方向: {rec['side_cn']} | 杠杆: **{rec['leverage']}x**",
            f"📍 入场: **{rec['price']}**",
            f"🛑 止损: **{rec['sl_price']}** (-{rec['sl_pct']}%)",
            f"🎯 止盈: **{rec['tp_price']}** (+{rec['tp_pct']}%)",
            f"📐 盈亏比: **{rec['rr']}:1** ❌",
            f"",
            f"❌ {cost_check.get('reason', '')}",
            f"",
            f"💡 建议：观望或等更好入场点",
        ]

    # 添加ATR和趋势信息
    if rec.get('atr_fused'):
        lines.append(f"📊 多周期ATR: 融合${rec['atr_fused']} (1H=${rec.get('atr_1h','?')} 4H=${rec.get('atr_4h','?')} 1D=${rec.get('atr_1d','?')})")
    if rec.get('trend_label'):
        lines.append(f"🧭 趋势: {rec['trend_label']} (斜率{rec.get('slope','?')}%)")

    return '\n'.join(lines)


def execute_order(exchange, rec):
    """Execute the order after user confirmation"""
    symbol = f"{rec['symbol'].split('/')[0]}/USDT:USDT"
    inst_id = rec['inst_id']
    side = rec['side']
    contracts = rec['contracts']
    leverage = rec['leverage']

    results = {'steps': []}

    # 1. Set leverage
    try:
        exchange.set_leverage(leverage, symbol)
        results['steps'].append({'step': 'leverage', 'status': 'ok'})
    except Exception as e:
        results['steps'].append({'step': 'leverage', 'status': 'warn', 'msg': str(e)})

    # 2. Place market order
    try:
        if side == 'sell':
            order = exchange.create_market_sell_order(symbol, contracts, params={'tdMode': 'cross'})
        else:
            order = exchange.create_market_buy_order(symbol, contracts, params={'tdMode': 'cross'})
        results['order'] = {
            'id': order['id'],
            'status': order['status'],
            'side': side,
            'amount': contracts,
        }
        results['steps'].append({'step': 'order', 'status': 'ok', 'order_id': order['id']})
    except Exception as e:
        results['steps'].append({'step': 'order', 'status': 'error', 'msg': str(e)})
        return results

    # 3. Wait for position update
    import time
    time.sleep(2)

    # 4. Cancel existing algo orders for this instrument (避免多开止盈止损单)
    cancelled = 0
    for otype in ['oco', 'conditional']:
        try:
            resp = exchange.private_get_trade_orders_algo_pending({
                'ordType': otype,
                'instId': inst_id,
            })
            for algo in resp.get('data', []):
                try:
                    exchange.private_post_trade_cancel_algos([{
                        'algoId': algo['algoId'],
                        'instId': inst_id,
                    }])
                    cancelled += 1
                except Exception:
                    pass
        except Exception:
            pass
    if cancelled > 0:
        results['steps'].append({'step': 'cancel_old_algos', 'status': 'ok', 'cancelled': cancelled})
        time.sleep(0.5)  # wait for cancellation to propagate

    # 5. Set TP/SL via OCO algo order
    try:
        # For OCO: tpOrdPx=-1 and slOrdPx=-1 means market order on trigger
        if side == 'sell':
            # Short: TP trigger below, SL trigger above
            algo_params = {
                'instId': inst_id,
                'tdMode': 'cross',
                'side': 'buy',       # buy to close short
                'posSide': 'net',
                'ordType': 'oco',
                'sz': str(contracts),
                'tpTriggerPx': str(rec['tp_price']),
                'tpOrdPx': '-1',
                'tpTriggerPxType': 'last',
                'slTriggerPx': str(rec['sl_price']),
                'slOrdPx': '-1',
                'slTriggerPxType': 'last',
                'reduceOnly': 'true',
            }
        else:
            # Long: TP trigger above, SL trigger below
            algo_params = {
                'instId': inst_id,
                'tdMode': 'cross',
                'side': 'sell',      # sell to close long
                'posSide': 'net',
                'ordType': 'oco',
                'sz': str(contracts),
                'tpTriggerPx': str(rec['tp_price']),
                'tpOrdPx': '-1',
                'tpTriggerPxType': 'last',
                'slTriggerPx': str(rec['sl_price']),
                'slOrdPx': '-1',
                'slTriggerPxType': 'last',
                'reduceOnly': 'true',
            }

        resp = exchange.private_post_trade_order_algo(algo_params)
        if resp.get('data') and resp['data'][0].get('algoId'):
            algo_id = resp['data'][0]['algoId']
            results['algo'] = {'id': algo_id, 'tp': rec['tp_price'], 'sl': rec['sl_price']}
            results['steps'].append({'step': 'tp_sl', 'status': 'ok', 'algo_id': algo_id})
        else:
            results['steps'].append({'step': 'tp_sl', 'status': 'warn', 'msg': str(resp)})
    except Exception as e:
        results['steps'].append({'step': 'tp_sl', 'status': 'error', 'msg': str(e)})

    # 5. Verify position
    try:
        positions = exchange.fetch_positions([symbol])
        for p in positions:
            if float(p.get('contracts', 0)) > 0:
                results['position'] = {
                    'side': p['side'],
                    'contracts': float(p['contracts']),
                    'entry': float(p['entryPrice']) if p.get('entryPrice') else 0,
                    'liq': float(p.get('liquidationPrice', 0)) if p.get('liquidationPrice') else 0,
                    'pnl': float(p.get('unrealizedPnl', 0)),
                }
    except Exception:
        pass

    return results


def format_execution_result(results):
    """Format execution result for user"""
    lines = []
    for step in results.get('steps', []):
        if step['step'] == 'leverage':
            if step['status'] == 'ok':
                lines.append("✅ 杠杆设置成功")
            else:
                lines.append(f"⚠️ 杠杆: {step.get('msg', '')}")
        elif step['step'] == 'order':
            if step['status'] == 'ok':
                lines.append(f"✅ 下单成功 (ID: {step['order_id']})")
            else:
                lines.append(f"❌ 下单失败: {step.get('msg', '')}")
                return '\n'.join(lines)
        elif step['step'] == 'cancel_old_algos':
            lines.append(f"🧹 已清理 {step['cancelled']} 个旧止盈止损单")
        elif step['step'] == 'tp_sl':
            if step['status'] == 'ok':
                lines.append(f"✅ 止盈止损设置成功 (ID: {step['algo_id']})")
            else:
                lines.append(f"⚠️ 止盈止损: {step.get('msg', '')}")

    pos = results.get('position')
    if pos:
        lines.extend([
            "",
            "📊 **持仓确认:**",
            f"• 方向: {pos['side']}",
            f"• 数量: {pos['contracts']}张",
            f"• 入场价: **{pos['entry']}**",
            f"• 清算价: {pos['liq']}",
        ])
        algo = results.get('algo')
        if algo:
            lines.extend([
                f"• 🎯 止盈: {algo['tp']}",
                f"• 🛑 止损: {algo['sl']}",
            ])

    return '\n'.join(lines)


def close_position(exchange, symbol, inst_id):
    """Close all positions for a symbol and cancel algo orders"""
    results = {'steps': []}

    # 1. Get current position
    positions = exchange.fetch_positions([symbol])
    pos = None
    for p in positions:
        if float(p.get('contracts', 0)) > 0:
            pos = p
            break

    if not pos:
        results['steps'].append({'step': 'check', 'status': 'none', 'msg': '没有持仓'})
        return results

    contracts = float(pos['contracts'])
    side = pos['side']
    entry = float(pos['entryPrice'])
    pnl = float(pos.get('unrealizedPnl', 0))

    # 2. Cancel all algo orders
    for otype in ['oco', 'conditional']:
        try:
            resp = exchange.private_get_trade_orders_algo_pending({
                'ordType': otype,
                'instId': inst_id,
            })
            for algo in resp.get('data', []):
                try:
                    exchange.private_post_trade_cancel_algos([{
                        'algoId': algo['algoId'],
                        'instId': inst_id,
                    }])
                except Exception:
                    pass
        except Exception:
            pass
    results['steps'].append({'step': 'cancel_algos', 'status': 'ok'})

    # 3. Close position with market order
    try:
        if side == 'short':
            order = exchange.create_market_buy_order(symbol, contracts, params={
                'tdMode': 'cross',
                'reduceOnly': True,
            })
        else:
            order = exchange.create_market_sell_order(symbol, contracts, params={
                'tdMode': 'cross',
                'reduceOnly': True,
            })
        results['steps'].append({'step': 'close', 'status': 'ok', 'order_id': order['id']})
    except Exception as e:
        results['steps'].append({'step': 'close', 'status': 'error', 'msg': str(e)})
        return results

    # 4. Wait and verify
    import time
    time.sleep(2)

    # 5. Get close price from trades
    try:
        fills = exchange.fetch_my_trades(symbol, limit=1)
        close_price = float(fills[0]['price']) if fills else 0
    except Exception:
        close_price = 0

    results['closed'] = {
        'symbol': symbol.split('/')[0] + '/USDT',
        'side': side,
        'contracts': contracts,
        'entry': entry,
        'close_price': close_price,
        'pnl': pnl,
    }

    return results


def format_close_result(results):
    """Format close position result"""
    lines = []
    for step in results.get('steps', []):
        if step['step'] == 'none':
            return f"ℹ️ {step['msg']}"
        elif step['step'] == 'close':
            if step['status'] == 'ok':
                lines.append("✅ 平仓成功")
            else:
                lines.append(f"❌ 平仓失败: {step.get('msg', '')}")
                return '\n'.join(lines)

    c = results.get('closed')
    if c:
        pnl_emoji = "🟢" if c['pnl'] >= 0 else "🔴"
        lines.extend([
            f"",
            f"📊 **{c['symbol']} 平仓确认:**",
            f"• 方向: {c['side']}",
            f"• 数量: {c['contracts']}张",
            f"• 入场价: {c['entry']}",
            f"• 平仓价: **{c['close_price']}**",
            f"• {pnl_emoji} 盈亏: **{c['pnl']:.2f} USDT**",
            f"• 已取消止盈止损",
        ])

    return '\n'.join(lines)


def main():
    parser = argparse.ArgumentParser(description='OKX Position Advisor')
    parser.add_argument('--symbol', required=True, help='Base currency: ETH, BTC, SOL...')
    parser.add_argument('--side', choices=['long', 'short', 'buy', 'sell'],
                       help='Position direction (required for open, optional for close)')
    parser.add_argument('--leverage', type=int, default=10, help='Leverage (default: 10)')
    parser.add_argument('--execute', action='store_true', help='Execute order (requires prior --json output)')
    parser.add_argument('--rec-json', type=str, help='Recommendation JSON to execute')
    parser.add_argument('--close', action='store_true', help='Close position for symbol')
    parser.add_argument('--close-all', action='store_true', help='Close all positions')
    parser.add_argument('--json', action='store_true', help='Output as JSON')
    args = parser.parse_args()

    # Normalize side (only needed for open)
    if args.side:
        side = 'sell' if args.side in ('short', 'sell') else 'buy'
    else:
        side = None

    # Load credentials and create exchange
    creds = load_credentials()
    exchange = create_exchange(creds)

    # Build symbol
    symbol = f"{args.symbol.upper()}/USDT:USDT"
    inst_id = f"{args.symbol.upper()}-USDT-SWAP"

    # Close mode
    if args.close:
        results = close_position(exchange, symbol, inst_id)
        print(format_close_result(results))
        return

    if args.close_all:
        positions = exchange.fetch_positions()
        active = [p for p in positions if float(p.get('contracts', 0)) > 0]
        if not active:
            print("ℹ️ 没有持仓")
            return
        for p in active:
            sym = p['symbol']
            iid = sym.split('/')[0].replace(':USDT', '') + '-USDT-SWAP'
            results = close_position(exchange, sym, iid)
            print(format_close_result(results))
            print()
        return

    # Open mode requires --side
    if not side:
        print("❌ 开仓需要指定 --side (long/short/buy/sell)")
        return

    # Get account info
    acct_info = get_account_info(exchange)

    # Calculate recommendation
    rec = recommend_position(symbol, side, args.leverage, exchange, acct_info)

    # Execute mode: run the order
    if args.execute and args.rec_json:
        rec = json.loads(args.rec_json)
        results = execute_order(exchange, rec)
        # Output JSON for trade_signal_handler to parse
        if args.json:
            print(json.dumps(results, ensure_ascii=False))
        else:
            print(format_execution_result(results))
        return

    # Auto-execute mode: if cost-performance is high, execute directly
    if rec.get('auto_execute') and not args.json:
        print(f"✅ 性价比高，自动开仓...")
        results = execute_order(exchange, rec)
        print(format_execution_result(results))
        return

    if args.json:
        print(json.dumps(rec, indent=2, ensure_ascii=False))
    else:
        print(format_recommendation(rec))


if __name__ == '__main__':
    main()
