双向Beta对冲策略


创建日期: 2026-01-07 15:23:50 最后修改: 2026-01-13 09:05:46
复制: 12 点击次数: 87
avatar of ianzeng123 ianzeng123
2
关注
364
关注者
策略源码
/*backtest
start: 2025-10-12 00:00:00
end: 2026-01-07 00:00:00
period: 1m
basePeriod: 1m
exchanges: [{"eid":"Futures_OKX","currency":"ETH_USDT","balance":500000}]
*/


let config = {
    btcSymbol: 'BTC_USDT.swap',
    ethSymbol: 'ETH_USDT.swap',
    triggerThreshold: 0.02,
    takeProfitPct: 0.03,
    stopLossPct: -0.01,
    btcCoinAmount: 0.1,         // BTC下单币数(基准)
    betaLookback: 30,           // Beta计算回溯期(天)
    minBeta: 0.5,               // Beta下限
    maxBeta: 2.0                // Beta上限
}

let currentPosition = null

// 统计数据
let statistics = {
    initialBalance: 0,
    realizedProfit: 0,          // ✅ 已实现盈亏
    takeProfitCount: 0,
    stopLossCount: 0,
    winCount: 0,
    lossCount: 0,
    profitLossRatio: 0
}

// Beta信息
let betaInfo = {
    currentBeta: 1.0,
    correlation: 0,
    lookbackDays: config.betaLookback,
    lastUpdate: null
}

// 合约信息缓存
let contractInfo = {
    btcCtVal: null,
    ethCtVal: null
}

// 获取合约面值
function getContractValue() {
    try {
        let markets = exchange.GetMarkets()
        
        if (!markets) {
            Log("❌ 获取市场信息失败")
            return false
        }
        
        if (markets[config.btcSymbol]) {
            contractInfo.btcCtVal = markets[config.btcSymbol].CtVal
        }
        
        if (markets[config.ethSymbol]) {
            contractInfo.ethCtVal = markets[config.ethSymbol].CtVal
        }
        
        if (!contractInfo.btcCtVal || !contractInfo.ethCtVal) {
            Log("❌ 无法获取合约面值信息")
            return false
        }
        
        Log("📋 合约信息 | BTC CtVal:", contractInfo.btcCtVal, 
            "| ETH CtVal:", contractInfo.ethCtVal)
        
        return true
        
    } catch (e) {
        Log("❌ 获取合约面值异常:", e)
        return false
    }
}

// 更新状态表格
function updateStatusTable(btcTicker, ethTicker) {
    // 计算当前权益
    let currentAccount = _C(exchange.GetAccount)
    let currentEquity = currentAccount.Equity 
    
    // 未实现盈亏
    let unrealizedPnl = 0
    if (currentPosition && btcTicker && ethTicker) {
        let result = checkClose(currentPosition, btcTicker, ethTicker)
        unrealizedPnl = result.pnlUsd
    }
    
    // 总盈亏 = 当前权益 - 初始权益
    let totalProfit = currentEquity - statistics.initialBalance
    let totalProfitRate = totalProfit / statistics.initialBalance

    LogProfit(totalProfit, "&")
    
    let table1 = {
        type: 'table',
        title: '📊 权益统计',
        cols: ['初始金额', '当前权益', '已实现盈亏', '未实现盈亏', '总盈亏', '盈利率', '止盈次数', '止损次数', '胜率'],
        rows: [[
            '$' + _N(statistics.initialBalance, 2),
            '$' + _N(currentEquity, 2),
            '$' + _N(statistics.realizedProfit, 2),
            (unrealizedPnl >= 0 ? '+' : '') + '$' + _N(unrealizedPnl, 2),
            (totalProfit >= 0 ? '+' : '') + '$' + _N(totalProfit, 2),
            _N(totalProfitRate * 100, 2) + '%',
            statistics.takeProfitCount,
            statistics.stopLossCount,
            statistics.profitLossRatio > 0 ? _N(statistics.profitLossRatio, 2) : '-'
        ]]
    }
    
    let table2 = {
        type: 'table',
        title: '🎯 Beta对冲信息',
        cols: ['历史均价比', '当前价格比', '价格比偏离', '收益率Beta', '最终Beta', '相关系数', '回溯天数', '更新时间'],
        rows: [[
            betaInfo.avgPriceRatio ? _N(betaInfo.avgPriceRatio, 2) : '-',
            betaInfo.currentPriceRatio ? _N(betaInfo.currentPriceRatio, 2) : '-',
            betaInfo.avgPriceRatio && betaInfo.currentPriceRatio ? 
                _N((betaInfo.currentPriceRatio - betaInfo.avgPriceRatio) / betaInfo.avgPriceRatio * 100, 2) + '%' : '-',
            betaInfo.returnBeta ? _N(betaInfo.returnBeta, 3) : '-',
            _N(betaInfo.currentBeta, 2),
            _N(betaInfo.correlation, 3),
            betaInfo.lookbackDays + '天',
            betaInfo.lastUpdate ? betaInfo.lastUpdate : '-'
        ]]
    }
    
    let table3 = {
        type: 'table',
        title: '💼 实时持仓信息',
        cols: ['持仓状态', 'BTC仓位', 'BTC盈亏', 'ETH仓位', 'ETH盈亏', '持仓Beta', '总浮盈', '盈亏率'],
        rows: []
    }
    
    if (currentPosition && btcTicker && ethTicker) {
        // 按币数计算盈亏
        let btcPnlUsd, ethPnlUsd
        
        if (currentPosition.type === 'long_btc_short_eth') {
            btcPnlUsd = (btcTicker.Last - currentPosition.btcPrice) * currentPosition.btcCoinAmount
            ethPnlUsd = (currentPosition.ethPrice - ethTicker.Last) * currentPosition.ethCoinAmount
        } else {
            btcPnlUsd = (currentPosition.btcPrice - btcTicker.Last) * currentPosition.btcCoinAmount
            ethPnlUsd = (ethTicker.Last - currentPosition.ethPrice) * currentPosition.ethCoinAmount
        }
        
        let totalPnlUsd = btcPnlUsd + ethPnlUsd
        let totalCost = currentPosition.btcPrice * currentPosition.btcCoinAmount + 
                       currentPosition.ethPrice * currentPosition.ethCoinAmount
        let totalPnlPct = totalPnlUsd / totalCost
        
        let positionType = currentPosition.type === 'long_btc_short_eth' ? 
            '🟢 多BTC空ETH' : '🔴 空BTC多ETH'
        
        let btcPosition = currentPosition.type === 'long_btc_short_eth' ? 
            '多 ' + currentPosition.btcContracts + '张(' + _N(currentPosition.btcCoinAmount, 4) + ' BTC)' : 
            '空 ' + currentPosition.btcContracts + '张(' + _N(currentPosition.btcCoinAmount, 4) + ' BTC)'
        
        let ethPosition = currentPosition.type === 'long_btc_short_eth' ? 
            '空 ' + currentPosition.ethContracts + '张(' + _N(currentPosition.ethCoinAmount, 4) + ' ETH)' : 
            '多 ' + currentPosition.ethContracts + '张(' + _N(currentPosition.ethCoinAmount, 4) + ' ETH)'
        
        table3.rows.push([
            positionType,
            btcPosition + ' @$' + _N(currentPosition.btcPrice, 2),
            (btcPnlUsd >= 0 ? '+' : '') + '$' + _N(btcPnlUsd, 2),
            ethPosition + ' @$' + _N(currentPosition.ethPrice, 2),
            (ethPnlUsd >= 0 ? '+' : '') + '$' + _N(ethPnlUsd, 2),
            _N(currentPosition.beta, 3),
            (totalPnlUsd >= 0 ? '+' : '') + '$' + _N(totalPnlUsd, 2),
            (totalPnlPct >= 0 ? '+' : '') + _N(totalPnlPct * 100, 2) + '%'
        ])
    } else {
        table3.rows.push([
            '⚪ 空仓', '-', '-', '-', '-', '-', '-', '-'
        ])
    }
    
    LogStatus('`' + JSON.stringify([table1, table2, table3]) + '`')
}

// 计算ETH相对BTC的Beta系数
function calculateBeta(btcRecords, ethRecords, lookback) {
    if (btcRecords.length < lookback + 1 || ethRecords.length < lookback + 1) {
        Log("⚠️ K线数据不足,使用当前价格比作为默认Beta")
        
        let btcPrice = btcRecords[btcRecords.length - 1].Close
        let ethPrice = ethRecords[ethRecords.length - 1].Close
        let defaultBeta = btcPrice / ethPrice
        
        betaInfo.currentBeta = defaultBeta
        betaInfo.correlation = 0
        betaInfo.priceRatio = defaultBeta
        betaInfo.returnBeta = 1.0
        betaInfo.lastUpdate = new Date().toLocaleString()
        
        Log("  默认Beta =", _N(defaultBeta, 2), "| 价格比:", _N(btcPrice, 0), "/", _N(ethPrice, 0))
        return defaultBeta
    }
    
    let btcReturns = []
    let ethReturns = []
    let priceRatios = []
    
    // 计算日收益率 + 历史价格比
    for (let i = btcRecords.length - lookback; i < btcRecords.length; i++) {
        let btcRet = (btcRecords[i].Close - btcRecords[i-1].Close) / btcRecords[i-1].Close
        let ethRet = (ethRecords[i].Close - ethRecords[i-1].Close) / ethRecords[i-1].Close
        
        btcReturns.push(btcRet)
        ethReturns.push(ethRet)
        
        let ratio = btcRecords[i].Close / ethRecords[i].Close
        priceRatios.push(ratio)
    }
    
    // 计算历史平均价格比
    let avgPriceRatio = priceRatios.reduce((a, b) => a + b, 0) / priceRatios.length
    
    // 计算价格比的标准差
    let priceRatioVariance = 0
    for (let i = 0; i < priceRatios.length; i++) {
        let diff = priceRatios[i] - avgPriceRatio
        priceRatioVariance += diff * diff
    }
    priceRatioVariance /= (priceRatios.length - 1)
    let priceRatioStd = Math.sqrt(priceRatioVariance)
    let priceRatioCv = priceRatioStd / avgPriceRatio
    
    // 计算收益率的均值
    let btcMean = btcReturns.reduce((a,b) => a+b, 0) / btcReturns.length
    let ethMean = ethReturns.reduce((a,b) => a+b, 0) / ethReturns.length
    
    // 计算协方差和方差
    let covariance = 0
    let btcVariance = 0
    let ethVariance = 0
    
    for (let i = 0; i < btcReturns.length; i++) {
        let btcDiff = btcReturns[i] - btcMean
        let ethDiff = ethReturns[i] - ethMean
        covariance += btcDiff * ethDiff
        btcVariance += btcDiff * btcDiff
        ethVariance += ethDiff * ethDiff
    }
    
    covariance /= (btcReturns.length - 1)
    btcVariance /= (btcReturns.length - 1)
    ethVariance /= (ethReturns.length - 1)
    
    // 收益率Beta
    let returnBeta = covariance / btcVariance
    
    // 计算相关系数
    let correlation = covariance / Math.sqrt(btcVariance * ethVariance)
    
    // 最终Beta = 历史平均价格比 × 收益率Beta
    let finalBeta = avgPriceRatio * returnBeta
    
    // 限制Beta范围
    let minBeta = avgPriceRatio * 0.5
    let maxBeta = avgPriceRatio * 2.0
    finalBeta = Math.max(minBeta, Math.min(maxBeta, finalBeta))
    
    // 获取当前价格比
    let currentBtcPrice = btcRecords[btcRecords.length - 1].Close
    let currentEthPrice = ethRecords[ethRecords.length - 1].Close
    let currentPriceRatio = currentBtcPrice / currentEthPrice
    
    // 更新Beta信息
    betaInfo.currentBeta = finalBeta
    betaInfo.correlation = correlation
    betaInfo.returnBeta = returnBeta
    betaInfo.avgPriceRatio = avgPriceRatio
    betaInfo.currentPriceRatio = currentPriceRatio
    betaInfo.priceRatioStd = priceRatioStd
    betaInfo.priceRatioCv = priceRatioCv
    betaInfo.lastUpdate = new Date().toLocaleString()
    
    return finalBeta
}

// 计算对冲张数
function calculateHedgeAmount(beta) {
    let btcCoinAmount = config.btcCoinAmount
    let ethCoinAmount = btcCoinAmount * beta
    
    let btcContracts = Math.floor(btcCoinAmount / contractInfo.btcCtVal)
    btcContracts = Math.max(1, btcContracts)
    
    let ethContracts = Math.floor(ethCoinAmount / contractInfo.ethCtVal)
    ethContracts = Math.max(1, ethContracts)
    
    let actualBtcCoins = btcContracts * contractInfo.btcCtVal
    let actualEthCoins = ethContracts * contractInfo.ethCtVal
    
    Log("🎯 对冲计算 | Beta:", _N(beta, 3),
        "\n  BTC: ", _N(actualBtcCoins, 4), "币 =", btcContracts, "张 (CtVal:", contractInfo.btcCtVal, ")",
        "\n  ETH: ", _N(actualEthCoins, 4), "币 =", ethContracts, "张 (CtVal:", contractInfo.ethCtVal, ")",
        "\n  实际比例:", _N(actualEthCoins / actualBtcCoins, 3))
    
    return {
        btc: btcContracts,
        eth: ethContracts,
        btcCoins: actualBtcCoins,
        ethCoins: actualEthCoins,
        beta: beta
    }
}

// 检查实际持仓数量
function checkActualPosition(symbol, expectedDirection) {
    try {
        let positions = exchange.GetPositions(symbol)
        if (!positions || positions.length === 0) {
            return {amount: 0, price: 0}
        }
        
        for (let pos of positions) {
            if (pos.Symbol === symbol) {
                if (expectedDirection === "buy" && pos.Type === 0) {
                    // 多仓
                    return {amount: pos.Amount, price: pos.Price}
                } else if (expectedDirection === "sell" && pos.Type === 1) {
                    // 空仓
                    return {amount: pos.Amount, price: pos.Price}
                }
            }
        }
        
        return {amount: 0, price: 0}
    } catch (e) {
        Log("❌ 检查持仓异常:", symbol, e)
        return {amount: 0, price: 0}
    }
}

// 优化的市价下单函数 - 30秒内轮询订单状态
function createMarketOrder(symbol, direction, amount) {
    try {
        let orderId = exchange.CreateOrder(symbol, direction, -1, amount)
        if (!orderId) {
            Log("❌ 下单失败:", symbol, direction)
            return null
        }
        
        Log("📝 订单已提交:", symbol, direction, amount, "张 | ID:", orderId)
        
        // 30秒内轮询订单状态
        let startTime = Date.now()
        let maxWaitTime = 30000 // 30秒
        let orderInfo = null
        let checkCount = 0
        
        while (Date.now() - startTime < maxWaitTime) {
            Sleep(500)
            checkCount++
            orderInfo = exchange.GetOrder(orderId)
            
            if (!orderInfo) {
                Log("⚠️ 获取订单状态失败,继续轮询...")
                continue
            }
            
            if (orderInfo.Status === 1) {
                // 完全成交
                Log("✅", symbol, direction, amount, "张 成交 @", orderInfo.AvgPrice)
                return {
                    id: orderId,
                    avgPrice: orderInfo.AvgPrice,
                    amount: orderInfo.DealAmount,
                    status: orderInfo.Status
                }
            } else if (orderInfo.Status === 2) {
                // 已取消
                Log("⚠️ 订单已取消:", symbol, direction)
                return null
            }
            
            // Status === 0,继续等待
            if (checkCount % 10 === 0) {  // 每5秒打印一次
                Log("⏳ 订单未成交,继续等待... 已等待", _N((Date.now() - startTime)/1000, 1), "秒")
            }
        }
        
        // 30秒超时,取消订单
        Log("⏰ 订单超时30秒未成交,准备取消:", symbol, direction, "订单ID:", orderId)
        let cancelResult = exchange.CancelOrder(orderId)
        
        if (!cancelResult) {
            Log("⚠️ 取消订单失败,可能已成交")
        } else {
            Log("✅ 订单已取消")
        }
        
        // 取消后再次检查订单状态(可能在取消时已成交)
        Sleep(500)
        orderInfo = exchange.GetOrder(orderId)
        
        if (orderInfo && orderInfo.Status === 1) {
            Log("✅ 订单实际已成交:", symbol, direction, orderInfo.DealAmount, "张 @", orderInfo.AvgPrice)
            return {
                id: orderId,
                avgPrice: orderInfo.AvgPrice,
                amount: orderInfo.DealAmount,
                status: orderInfo.Status
            }
        }
        
        // ========== 关键:检查实际持仓 ==========
        Log("🔍 订单状态不明确,检查实际持仓...")
        let posInfo = checkActualPosition(symbol, direction)
        
        if (posInfo.amount > 0) {
            Log("⚠️ 发现实际持仓:", symbol, direction, posInfo.amount, "张 @", posInfo.price)
            return {
                id: orderId,
                avgPrice: posInfo.price,
                amount: posInfo.amount,
                status: 1  // 标记为已成交
            }
        }
        
        Log("❌ 订单最终未成交,且无实际持仓:", symbol, direction)
        return null
        
    } catch (e) {
        Log("❌ 下单异常:", symbol, direction, e)
        return null
    }
}

// 平掉意外持仓
function closeUnexpectedPosition(symbol, direction, amount) {
    try {
        let closeDirection = direction === "buy" ? "closebuy" : "closesell"
        Log("🔄 平掉意外持仓:", symbol, closeDirection, amount, "张")
        
        let closeOrder = createMarketOrder(symbol, closeDirection, amount)
        return closeOrder !== null
    } catch (e) {
        Log("❌ 平仓失败:", symbol, e)
        return false
    }
}

// 做多BTC + 做空ETH(优化版)
function openLongBtcShortEth(btcTicker, ethTicker, btcChange, beta) {
    let amounts = calculateHedgeAmount(beta)
    
    // ========== 第一步:开BTC多仓 ==========
    Log("📍 第1步:开BTC多仓", amounts.btc, "张")
    let btcOrder = createMarketOrder(config.btcSymbol, "buy", amounts.btc)
    
    // 检查BTC实际持仓(无论订单是否成功都要检查)
    let btcPosInfo = checkActualPosition(config.btcSymbol, "buy")
    
    if (btcPosInfo.amount === 0) {
        Log("❌ BTC未开仓成功,终止流程")
        return null
    }
    
    if (btcPosInfo.amount !== amounts.btc) {
        Log("⚠️ BTC仓位数量不匹配!期望:", amounts.btc, "张 | 实际:", btcPosInfo.amount, "张")
        // 如果BTC已经开了仓但数量不对,平掉重来
        Log("🔄 BTC仓位异常,平仓后终止")
        closeUnexpectedPosition(config.btcSymbol, "buy", btcPosInfo.amount)
        return null
    }
    
    let btcAvgPrice = btcOrder ? btcOrder.avgPrice : btcPosInfo.price
    Log("✅ BTC多仓已建立:", btcPosInfo.amount, "张 @", _N(btcAvgPrice, 2))
    
    // ========== 第二步:开ETH空仓,持续尝试直到匹配 ==========
    Log("📍 第2步:开ETH空仓", amounts.eth, "张,持续尝试直到成功")
    let ethOrder = null
    let ethPosInfo = {amount: 0, price: 0}
    let maxRetries = 5
    let retryCount = 0
    
    while (retryCount < maxRetries) {
        // 先检查当前ETH持仓
        ethPosInfo = checkActualPosition(config.ethSymbol, "sell")
        
        if (ethPosInfo.amount === amounts.eth) {
            Log("✅ ETH空仓已完整:", ethPosInfo.amount, "张 @", _N(ethPosInfo.price, 2))
            break
        } else if (ethPosInfo.amount > 0 && ethPosInfo.amount < amounts.eth) {
            // 补仓
            let needAmount = amounts.eth - ethPosInfo.amount
            Log("🔄 补开ETH空仓:", needAmount, "张 (当前:", ethPosInfo.amount, "/ 目标:", amounts.eth, ")")
            ethOrder = createMarketOrder(config.ethSymbol, "sell", needAmount)
        } else if (ethPosInfo.amount === 0) {
            // 首次开仓
            Log("🔄 开ETH空仓:", amounts.eth, "张 (尝试", retryCount + 1, "/", maxRetries, ")")
            ethOrder = createMarketOrder(config.ethSymbol, "sell", amounts.eth)
        } else if (ethPosInfo.amount > amounts.eth) {
            Log("⚠️ ETH仓位超额!期望:", amounts.eth, "张 | 实际:", ethPosInfo.amount, "张")
            // 平掉超额部分
            let excessAmount = ethPosInfo.amount - amounts.eth
            Log("🔄 平掉ETH超额仓位:", excessAmount, "张")
            closeUnexpectedPosition(config.ethSymbol, "sell", excessAmount)
            break
        }
        
        retryCount++
        Sleep(1000)
    }
    
    // ========== 最终检查两腿是否匹配 ==========
    btcPosInfo = checkActualPosition(config.btcSymbol, "buy")
    ethPosInfo = checkActualPosition(config.ethSymbol, "sell")
    
    if (btcPosInfo.amount === 0 || ethPosInfo.amount === 0) {
        Log("❌ 开仓失败,清理残留仓位")
        if (btcPosInfo.amount > 0) {
            Log("🔄 清理BTC多仓:", btcPosInfo.amount, "张")
            closeUnexpectedPosition(config.btcSymbol, "buy", btcPosInfo.amount)
        }
        if (ethPosInfo.amount > 0) {
            Log("🔄 清理ETH空仓:", ethPosInfo.amount, "张")
            closeUnexpectedPosition(config.ethSymbol, "sell", ethPosInfo.amount)
        }
        return null
    }
    
    let ethAvgPrice = ethPosInfo.price
    
    Log("🟢 开仓完成 | BTC涨:", _N(btcChange*100, 2), "%",
        "\n  BTC:", btcPosInfo.amount, "张(", _N(btcPosInfo.amount * contractInfo.btcCtVal, 4), "币) @", _N(btcAvgPrice, 2),
        "\n  ETH:", ethPosInfo.amount, "张(", _N(ethPosInfo.amount * contractInfo.ethCtVal, 4), "币) @", _N(ethAvgPrice, 2))
    
    return {
        type: 'long_btc_short_eth',
        btcPrice: btcAvgPrice,
        ethPrice: ethAvgPrice,
        btcAmount: btcPosInfo.amount,
        ethAmount: ethPosInfo.amount,
        btcContracts: btcPosInfo.amount,
        ethContracts: ethPosInfo.amount,
        btcCoinAmount: btcPosInfo.amount * contractInfo.btcCtVal,
        ethCoinAmount: ethPosInfo.amount * contractInfo.ethCtVal,
        beta: beta,
        openTime: Date.now()
    }
}

// 做空BTC + 做多ETH(优化版)
function openShortBtcLongEth(btcTicker, ethTicker, btcChange, beta) {
    let amounts = calculateHedgeAmount(beta)
    
    // ========== 第一步:开BTC空仓 ==========
    Log("📍 第1步:开BTC空仓", amounts.btc, "张")
    let btcOrder = createMarketOrder(config.btcSymbol, "sell", amounts.btc)
    
    // 检查BTC实际持仓(无论订单是否成功都要检查)
    let btcPosInfo = checkActualPosition(config.btcSymbol, "sell")
    
    if (btcPosInfo.amount === 0) {
        Log("❌ BTC未开仓成功,终止流程")
        return null
    }
    
    if (btcPosInfo.amount !== amounts.btc) {
        Log("⚠️ BTC仓位数量不匹配!期望:", amounts.btc, "张 | 实际:", btcPosInfo.amount, "张")
        // 如果BTC已经开了仓但数量不对,平掉重来
        Log("🔄 BTC仓位异常,平仓后终止")
        closeUnexpectedPosition(config.btcSymbol, "sell", btcPosInfo.amount)
        return null
    }
    
    let btcAvgPrice = btcOrder ? btcOrder.avgPrice : btcPosInfo.price
    Log("✅ BTC空仓已建立:", btcPosInfo.amount, "张 @", _N(btcAvgPrice, 2))
    
    // ========== 第二步:开ETH多仓,持续尝试直到匹配 ==========
    Log("📍 第2步:开ETH多仓", amounts.eth, "张,持续尝试直到成功")
    let ethOrder = null
    let ethPosInfo = {amount: 0, price: 0}
    let maxRetries = 5
    let retryCount = 0
    
    while (retryCount < maxRetries) {
        // 先检查当前ETH持仓
        ethPosInfo = checkActualPosition(config.ethSymbol, "buy")
        
        if (ethPosInfo.amount === amounts.eth) {
            Log("✅ ETH多仓已完整:", ethPosInfo.amount, "张 @", _N(ethPosInfo.price, 2))
            break
        } else if (ethPosInfo.amount > 0 && ethPosInfo.amount < amounts.eth) {
            // 补仓
            let needAmount = amounts.eth - ethPosInfo.amount
            Log("🔄 补开ETH多仓:", needAmount, "张 (当前:", ethPosInfo.amount, "/ 目标:", amounts.eth, ")")
            ethOrder = createMarketOrder(config.ethSymbol, "buy", needAmount)
        } else if (ethPosInfo.amount === 0) {
            // 首次开仓
            Log("🔄 开ETH多仓:", amounts.eth, "张 (尝试", retryCount + 1, "/", maxRetries, ")")
            ethOrder = createMarketOrder(config.ethSymbol, "buy", amounts.eth)
        } else if (ethPosInfo.amount > amounts.eth) {
            Log("⚠️ ETH仓位超额!期望:", amounts.eth, "张 | 实际:", ethPosInfo.amount, "张")
            // 平掉超额部分
            let excessAmount = ethPosInfo.amount - amounts.eth
            Log("🔄 平掉ETH超额仓位:", excessAmount, "张")
            closeUnexpectedPosition(config.ethSymbol, "buy", excessAmount)
            break
        }
        
        retryCount++
        Sleep(1000)
    }
    
    // ========== 最终检查两腿是否匹配 ==========
    btcPosInfo = checkActualPosition(config.btcSymbol, "sell")
    ethPosInfo = checkActualPosition(config.ethSymbol, "buy")
    
    if (btcPosInfo.amount === 0 || ethPosInfo.amount === 0) {
        Log("❌ 开仓失败,清理残留仓位")
        if (btcPosInfo.amount > 0) {
            Log("🔄 清理BTC空仓:", btcPosInfo.amount, "张")
            closeUnexpectedPosition(config.btcSymbol, "sell", btcPosInfo.amount)
        }
        if (ethPosInfo.amount > 0) {
            Log("🔄 清理ETH多仓:", ethPosInfo.amount, "张")
            closeUnexpectedPosition(config.ethSymbol, "buy", ethPosInfo.amount)
        }
        return null
    }
    
    let ethAvgPrice = ethPosInfo.price
    
    Log("🔴 开仓完成 | BTC跌:", _N(btcChange*100, 2), "%",
        "\n  BTC:", btcPosInfo.amount, "张(", _N(btcPosInfo.amount * contractInfo.btcCtVal, 4), "币) @", _N(btcAvgPrice, 2),
        "\n  ETH:", ethPosInfo.amount, "张(", _N(ethPosInfo.amount * contractInfo.ethCtVal, 4), "币) @", _N(ethAvgPrice, 2))
    
    return {
        type: 'short_btc_long_eth',
        btcPrice: btcAvgPrice,
        ethPrice: ethAvgPrice,
        btcAmount: btcPosInfo.amount,
        ethAmount: ethPosInfo.amount,
        btcContracts: btcPosInfo.amount,
        ethContracts: ethPosInfo.amount,
        btcCoinAmount: btcPosInfo.amount * contractInfo.btcCtVal,
        ethCoinAmount: ethPosInfo.amount * contractInfo.ethCtVal,
        beta: beta,
        openTime: Date.now()
    }
}

// 检查平仓条件
function checkClose(pos, btcTicker, ethTicker) {
    let btcPnlUsd, ethPnlUsd
    
    if (pos.type === 'long_btc_short_eth') {
        btcPnlUsd = (btcTicker.Last - pos.btcPrice) * pos.btcCoinAmount
        ethPnlUsd = (pos.ethPrice - ethTicker.Last) * pos.ethCoinAmount
    } else {
        btcPnlUsd = (pos.btcPrice - btcTicker.Last) * pos.btcCoinAmount
        ethPnlUsd = (ethTicker.Last - pos.ethPrice) * pos.ethCoinAmount
    }
    
    let totalPnlUsd = btcPnlUsd + ethPnlUsd
    let totalCost = pos.btcPrice * pos.btcCoinAmount + pos.ethPrice * pos.ethCoinAmount
    let totalPnlPct = totalPnlUsd / totalCost
    
    if (totalPnlPct >= config.takeProfitPct) {
        return {close: true, reason: '✅止盈', pnl: totalPnlPct, pnlUsd: totalPnlUsd}
    }
    if (totalPnlPct <= config.stopLossPct) {
        return {close: true, reason: '🛑止损', pnl: totalPnlPct, pnlUsd: totalPnlUsd}
    }
    
    return {close: false, pnl: totalPnlPct, pnlUsd: totalPnlUsd}
}

// 平仓
function closePosition(pos, btcTicker, ethTicker) {
    try {
        if (pos.type === 'long_btc_short_eth') {
            let btcClose = createMarketOrder(config.btcSymbol, "closebuy", pos.btcAmount)
            let ethClose = createMarketOrder(config.ethSymbol, "closesell", pos.ethAmount)
            
            if (btcClose && ethClose) {
                Log("平仓成交 | BTC:", pos.btcAmount, "张 @", btcClose.avgPrice,
                    "| ETH:", pos.ethAmount, "张 @", ethClose.avgPrice)
                return true
            }
        } else {
            let btcClose = createMarketOrder(config.btcSymbol, "closesell", pos.btcAmount)
            let ethClose = createMarketOrder(config.ethSymbol, "closebuy", pos.ethAmount)
            
            if (btcClose && ethClose) {
                Log("平仓成交 | BTC:", pos.btcAmount, "张 @", btcClose.avgPrice,
                    "| ETH:", pos.ethAmount, "张 @", ethClose.avgPrice)
                return true
            }
        }
        
        return false
    } catch (e) {
        Log("❌ 平仓失败:", e)
        return false
    }
}

function main() {
    Log("策略启动 - BTC-ETH Beta对冲 (币数计算模式 + 优化开仓)")
    
    if (!getContractValue()) {
        Log("❌ 无法获取合约信息,策略退出")
        return
    }
    
    let accountInfo = _C(exchange.GetAccount)
    statistics.initialBalance = accountInfo.Equity
    
    while (true) {
        let btcRecords = exchange.GetRecords(config.btcSymbol, PERIOD_D1)
        let ethRecords = exchange.GetRecords(config.ethSymbol, PERIOD_D1)
        
        if (!btcRecords || btcRecords.length < config.betaLookback + 2 || 
            !ethRecords || ethRecords.length < config.betaLookback + 2) {
            Sleep(60000)
            continue
        }
        
        let beta = calculateBeta(btcRecords, ethRecords, config.betaLookback)
        
        let btcBar = btcRecords[btcRecords.length - 1]
        let ethBar = ethRecords[ethRecords.length - 1]
        
        let btcChange = (btcBar.Close - btcBar.Open) / btcBar.Open
        let ethChange = (ethBar.Close - ethBar.Open) / ethBar.Open
        
        let btcTicker = exchange.GetTicker(config.btcSymbol)
        let ethTicker = exchange.GetTicker(config.ethSymbol)
        
        if (!btcTicker || !ethTicker) {
            Sleep(5000)
            continue
        }
        
        // ✅ 更新胜率
        if (statistics.lossCount > 0) {
            statistics.profitLossRatio = statistics.winCount / (statistics.lossCount + statistics.winCount)
        }

        let positions = _C(exchange.GetPositions)
        
        // 开仓逻辑
        if (!currentPosition && positions.length === 0) {
            if (btcChange > config.triggerThreshold && btcChange > ethChange) {
                Log('BTC涨幅:', _N(btcChange*100, 2), '% | ETH涨幅:', _N(ethChange*100, 2), '%')
                let pos = openLongBtcShortEth(btcTicker, ethTicker, btcChange, beta)
                if (pos) currentPosition = pos
            }
            else if (btcChange < -config.triggerThreshold && btcChange < ethChange) {
                Log('BTC跌幅:', _N(btcChange*100, 2), '% | ETH跌幅:', _N(ethChange*100, 2), '%')
                let pos = openShortBtcLongEth(btcTicker, ethTicker, btcChange, beta)
                if (pos) currentPosition = pos
            }
        }
        
        // ✅ 平仓逻辑 - 累加已实现盈亏
        if (currentPosition && positions.length === 2) {
            let result = checkClose(currentPosition, btcTicker, ethTicker)
            if (result.close) {
                // 先记录平仓盈亏
                let closePnl = result.pnlUsd
                
                let closed = closePosition(currentPosition, btcTicker, ethTicker)
                if (closed) {
                    // ✅ 累加已实现盈亏
                    statistics.realizedProfit += closePnl
                    
                    Log("平仓 |", result.reason, 
                        "| 本次盈亏: $", _N(closePnl, 2),
                        "(", _N(result.pnl*100, 2), "%)",
                        "| 累计已实现: $", _N(statistics.realizedProfit, 2))
                    
                    if (result.reason.includes('止盈')) {
                        statistics.takeProfitCount++
                    }
                    if (result.reason.includes('止损')) {
                        statistics.stopLossCount++
                    }
                    if (closePnl > 0) {
                        statistics.winCount++
                    } else {
                        statistics.lossCount++
                    }
                    
                    currentPosition = null
                }
            }
        }
        
        updateStatusTable(btcTicker, ethTicker)
        
        Sleep(1000)
    }
}