"""
v6.0 实盘脚本 — 1H突破策略 (10x杠杆, 只做多, 真实Taker)
=========================================================
运行方式：systemd 服务 或 手动 python3 v6_live_bot.py
"""
import pandas as pd
import numpy as np
import requests
import time
import hmac
import base64
import json
import os
from datetime import datetime, timedelta

# ==================== 配置区 ====================
API_KEY = os.getenv("OKX_API_KEY", "")
SECRET_KEY = os.getenv("OKX_SECRET_KEY", "")
PASSPHRASE = os.getenv("OKX_PASSPHRASE", "")

INST_ID = "BTC-USDT"          # 现货交易对
SWAP_ID = "BTC-USDT-SWAP"     # 永续合约
LEVERAGE = 10                 # 杠杆倍数
POSITION_SIZE_USDT = 5.0      # 单笔仓位 (5U)
COOLDOWN_BARS = 2             # 冷却期 (根K线)

# 策略参数
TAKER_RATIO_THRESH = 0.55     # Taker买入占比阈值
VOL_RATIO_THRESH = 1.5        # 成交量放大倍数
SL_ATR_MULT = 1.5             # 止损ATR倍数
TP_ATR_MULT = 3.0             # 止盈ATR倍数
MAX_HOLD_BARS = 12            # 最大持仓 (根1H K线 = 12小时)

# 数据文件
DATA_DIR = "/root/quant_pipeline/data"
SIGNAL_LOG = f"{DATA_DIR}/v6_signals.csv"
TRADE_LOG = f"{DATA_DIR}/v6_trades.csv"
# ==================================================

BASE_URL = "https://www.okx.com"

def okx_sign(method, path, body=""):
    """OKX API 签名"""
    timestamp = datetime.utcnow().isoformat("T", "milliseconds") + "Z"
    prehash = timestamp + method.upper() + path + (body if body else "")
    mac = hmac.new(SECRET_KEY.encode(), prehash.encode(), digestmod="sha256")
    sign = base64.b64encode(mac.digest()).decode()
    return {
        "OK-ACCESS-KEY": API_KEY,
        "OK-ACCESS-SIGN": sign,
        "OK-ACCESS-TIMESTAMP": timestamp,
        "OK-ACCESS-PASSPHRASE": PASSPHRASE,
        "Content-Type": "application/json",
    }

def okx_get(path, params=None):
    """OKX GET 请求"""
    headers = okx_sign("GET", path)
    resp = requests.get(BASE_URL + path, headers=headers, params=params, timeout=10)
    return resp.json()

def okx_post(path, body):
    """OKX POST 请求"""
    body_str = json.dumps(body)
    headers = okx_sign("POST", path, body_str)
    resp = requests.post(BASE_URL + path, headers=headers, data=body_str, timeout=10)
    return resp.json()

# ==================== 数据获取 ====================

def load_1h_data():
    """加载本地1小时K线数据 (从15分钟重采样)"""
    df_15m = pd.read_parquet(f"{DATA_DIR}/btc_15m_binance_full.parquet")
    df_15m['time'] = df_15m.index
    df_1h = df_15m.resample('1h').agg({
        'o': 'first', 'h': 'max', 'l': 'min', 'c': 'last',
        'v': 'sum', 'taker_buy_v': 'sum',
        'taker_buy_quote': 'sum', 'quote_volume': 'sum',
    }).dropna()
    df_1h.rename(columns={'o':'open','h':'high','l':'low','c':'close','v':'volume'}, inplace=True)
    df_1h['taker_ratio'] = df_1h['taker_buy_v'] / (df_1h['volume'] + 1e-9)
    return df_1h

def fetch_okx_1h_klines(inst_id="BTC-USDT", limit=100):
    """从OKX API获取1小时K线 (用于验证/更新)"""
    path = f"/api/v5/market/candles"
    params = {"instId": inst_id, "bar": "1H", "limit": limit}
    data = okx_get(path, params)
    if data.get('code') == '0' and data.get('data'):
        df = pd.DataFrame(data['data'], columns=[
            'ts', 'open', 'high', 'low', 'close', 'volume',
            'quote_vol', 'count', 'taker_buy_vol', 'taker_buy_quote', 'ignore'
        ])
        for col in ['open', 'high', 'low', 'close', 'volume', 'taker_buy_vol']:
            df[col] = df[col].astype(float)
        df['ts'] = pd.to_datetime(df['ts'].astype(int), unit='ms')
        df.set_index('ts', inplace=True)
        df.sort_index(inplace=True)
        df['taker_ratio'] = df['taker_buy_vol'] / (df['volume'] + 1e-9)
        return df
    return None

# ==================== 策略信号 ====================

def calc_indicators(df):
    """计算策略所需指标"""
    df['high_20'] = df['high'].rolling(20).max().shift(1)
    df['vol_ma20'] = df['volume'].rolling(20).mean().shift(1)
    tr = np.maximum(df['high'] - df['low'],
           np.maximum(np.abs(df['high'] - df['close'].shift(1)),
                      np.abs(df['low'] - df['close'].shift(1))))
    df['atr14'] = tr.rolling(14).mean()
    df['ema21'] = df['close'].ewm(span=21).mean()
    df['ema50'] = df['close'].ewm(span=50).mean()
    return df

def check_signal(df, idx):
    """检查指定索引是否产生入场信号"""
    if idx < 50 or idx >= len(df) - 2:
        return None
    
    row = df.iloc[idx]
    
    # 趋势过滤
    if pd.isna(row['ema21']) or pd.isna(row['ema50']) or row['ema21'] < row['ema50']:
        return None
    
    # 突破前20根高点
    if pd.isna(row['high_20']) or row['close'] <= row['high_20']:
        return None
    
    # Taker > 阈值
    if pd.isna(row['taker_ratio']) or row['taker_ratio'] <= TAKER_RATIO_THRESH:
        return None
    
    # 成交量放大
    if pd.isna(row['vol_ma20']) or row['volume'] <= row['vol_ma20'] * VOL_RATIO_THRESH:
        return None
    
    # 阳线
    if row['close'] <= row['open']:
        return None
    
    return {
        'time': df.index[idx],
        'entry': row['close'],
        'atr': row['atr14'],
        'taker_ratio': row['taker_ratio'],
        'vol_ratio': row['volume'] / (row['vol_ma20'] + 1e-9),
    }

# ==================== 交易执行 ====================

def get_btc_price():
    """获取当前BTC价格"""
    try:
        ticker = okx_get("/api/v5/market/ticker", {"instId": INST_ID})
        if ticker.get('code') == '0' and ticker.get('data'):
            return float(ticker['data'][0]['last'])
    except:
        pass
    return None

def get_spot_balance(ccy="USDT"):
    """获取现货余额"""
    try:
        result = okx_get("/api/v5/account/balance", {"ccy": ccy})
        if result.get('code') == '0' and result.get('data'):
            return float(result['data'][0][ccy]['availBal'])
    except:
        pass
    return 0.0

def get_futures_position():
    """获取合约当前持仓"""
    try:
        result = okx_get("/api/v5/account/positions", {"instId": SWAP_ID})
        if result.get('code') == '0' and result.get('data'):
            for pos in result['data']:
                if float(pos.get('pos', 0)) != 0:
                    return pos
    except:
        pass
    return None

def place_spot_order(side, size_btc):
    """现货下单"""
    body = {
        "instId": INST_ID,
        "tdMode": "cash",  # 现货模式
        "side": side,      # "buy" or "sell"
        "ordType": "market",
        "sz": str(size_btc),
    }
    result = okx_post("/api/v5/trade/order", body)
    return result

def place_futures_order(side, size_btc):
    """合约下单 (1x杠杆对冲)"""
    # 先设置杠杆
    try:
        okx_post("/api/v5/trade/set-leverage", {
            "instId": SWAP_ID,
            "lever": str(LEVERAGE),
            "mgnMode": "isolated",  # 逐仓模式
        })
    except:
        pass
    
    body = {
        "instId": SWAP_ID,
        "tdMode": "isolated",
        "side": side,          # "buy" or "sell"
        "ordType": "market",
        "sz": str(size_btc),
    }
    result = okx_post("/api/v5/trade/order", body)
    return result

def close_futures_position():
    """平掉所有合约仓位"""
    pos = get_futures_position()
    if pos and float(pos.get('pos', 0)) != 0:
        side = "sell" if float(pos['pos']) > 0 else "buy"
        okx_post("/api/v5/trade/close-position", {
            "instId": SWAP_ID,
            "mgnMode": "isolated",
            "posSide": "net",  # 净持仓模式
        })
        return True
    return False

# ==================== 日志 ====================

def log_signal(signal):
    """记录信号到CSV"""
    df = pd.DataFrame([signal])
    if os.path.exists(SIGNAL_LOG):
        df.to_csv(SIGNAL_LOG, mode='a', header=False, index=False)
    else:
        df.to_csv(SIGNAL_LOG, index=False)

def log_trade(trade):
    """记录交易到CSV"""
    df = pd.DataFrame([trade])
    if os.path.exists(TRADE_LOG):
        df.to_csv(TRADE_LOG, mode='a', header=False, index=False)
    else:
        df.to_csv(TRADE_LOG, index=False)

# ==================== 主循环 ====================

def run_once():
    """执行一次检查"""
    print(f"\n⏰ [{datetime.now()}] 检查信号...")
    
    # 加载数据
    df = load_1h_data()
    df = calc_indicators(df)
    
    # 检查最新两根K线 (避免未收盘的K线)
    for idx in range(-2, 0):
        signal = check_signal(df, len(df) + idx)
        if signal:
            print(f"🎯 信号触发! 时间: {signal['time']}")
            print(f"   入场价: {signal['entry']:.0f}")
            print(f"   Taker: {signal['taker_ratio']:.2f}")
            print(f"   成交量比: {signal['vol_ratio']:.2f}")
            print(f"   ATR: {signal['atr']:.1f}")
            
            # 记录信号
            log_signal(signal)
            
            # 执行交易
            execute_trade(signal)
            return
    
    print("📭 无信号")

def execute_trade(signal):
    """执行套利交易"""
    # 1. 检查当前持仓
    pos = get_futures_position()
    if pos and float(pos.get('pos', 0)) != 0:
        print("⚠️ 已有持仓，先平仓")
        close_futures_position()
        time.sleep(1)
    
    # 2. 获取当前价格
    price = get_btc_price()
    if not price:
        print("❌ 获取价格失败")
        return
    
    # 3. 计算仓位
    size_btc = POSITION_SIZE_USDT / price
    
    # 4. 现货买入 (做多)
    print(f"🛒 买入现货 {size_btc:.5f} BTC")
    spot_result = place_spot_order("buy", size_btc)
    print(f"   现货结果: {spot_result.get('sMsg', spot_result.get('msg', 'N/A'))}")
    
    # 5. 合约做空 (对冲)
    print(f"📉 做空合约 {size_btc:.5f} BTC (1x杠杆)")
    futures_result = place_futures_order("sell", size_btc)
    print(f"   合约结果: {futures_result.get('sMsg', futures_result.get('msg', 'N/A'))}")
    
    # 6. 记录交易
    trade = {
        'time': datetime.now(),
        'signal_time': signal['time'],
        'entry': signal['entry'],
        'taker': signal['taker_ratio'],
        'vol_ratio': signal['vol_ratio'],
        'atr': signal['atr'],
        'size_btc': size_btc,
        'side': 'LONG',
    }
    log_trade(trade)
    print(f"✅ 交易完成! 仓位: {size_btc:.5f} BTC")

def close_all():
    """平掉所有仓位"""
    print("🛑 平仓中...")
    closed = close_futures_position()
    if closed:
        print("✅ 合约已平仓")
    else:
        print("📭 无合约仓位")
    
    # 现货卖出
    price = get_btc_price()
    if price:
        size = POSITION_SIZE_USDT / price
        result = place_spot_order("sell", size)
        print(f"✅ 现货已卖出: {result.get('sMsg', 'N/A')}")

# ==================== 入口 ====================

if __name__ == "__main__":
    import sys
    
    if len(sys.argv) > 1:
        if sys.argv[1] == "close":
            close_all()
            sys.exit(0)
        elif sys.argv[1] == "once":
            run_once()
            sys.exit(0)
    
    # 默认：持续运行
    print("🤖 v6.0 1H突破策略 实盘机器人启动")
    print(f"   杠杆: {LEVERAGE}x")
    print(f"   仓位: {POSITION_SIZE_USDT}U")
    print(f"   冷却: {COOLDOWN_BARS}根K线")
    print("")
    
    while True:
        try:
            run_once()
        except Exception as e:
            print(f"❌ 错误: {e}")
        
        # 每小时检查一次 (与1H K线对齐)
        print(f"💤 下次检查: {(datetime.now() + timedelta(hours=1)).strftime('%H:%M')}")
        time.sleep(3600)
