avatar of ianzeng123 ianzeng123
关注 私信
2
关注
447
关注者

加密市场RWA多资产动态权重配置:风险平价策略

创建于: 2026-04-08 16:37:16, 更新于: 2026-04-20 18:13:03
comments   0
hits   34

[TOC]

加密市场RWA多资产动态权重配置:风险平价策略

加密市场最近不太好过。BTC从高点跌下来,山寨币更惨,很多人开始观望,甚至离场。但就在这段时间,另一件事悄悄热起来了——RWA,也就是现实世界资产的代币化。黄金、美股、原油,这些传统市场的资产,开始以合约的形式出现在加密交易所里。各大交易所陆续上线了SPY(代表标普500ETF)、黄金XAU、原油CL这些品种,加上本来就有的BTC,一个覆盖美股、黄金、原油、加密货币的多资产交易环境,第一次在链上成型了。

看到这个配置,脑子里冒出来一个念头:传统金融里有一个很经典的思路,叫风险平价。简单说就是——不要把所有鸡蛋放在一个篮子里,而且每个篮子里放多少,是根据这个篮子有多危险来决定的。这个逻辑在传统金融圈子里用了几十年,在这个熊市里,大家都在找不靠单一资产存活的方式,现在加密市场终于有了这些资产的合约,于是就开始动手,把这套逻辑搬过来试试。


先把风险平价说清楚,再往下看

在说具体怎么做之前,得先把风险平价这四个字解释明白,不然后面的逻辑很难跟上。

正常人配置资产,可能会想:我把钱平均分成四份,每个资产各放25%。听起来很均衡,但问题是BTC一天能波动10%,黄金一天可能才动0.5%,同样是25%的仓位,BTC给你带来的风险是黄金的二十倍。表面上看四个资产平均分,实际上组合的命运几乎全由BTC一个人决定,其他三个不过是陪衬。

风险平价的做法是反过来想:不是资金平均分,而是风险平均分。BTC波动大,就少配一点;黄金稳,就多配一点——但这只是第一步。更关键的是,资产之间并非独立的,BTC和美股在某些行情下会一起跌,如果两个资产高度同向,即便各自波动率差异很大,把它们的风险平摊也意义有限。真正的风险平价需要同时考虑每个资产的波动率和它与其他资产的相关性,用协方差矩阵来衡量每个资产对组合整体风险的边际贡献,最终让每个资产的风险贡献大致相等。这套逻辑的好处是,不会因为某一个资产突然暴雷就把整个组合打垮。

加密市场RWA多资产动态权重配置:风险平价策略

先选好资产,分散才有意义

想做风险平价,第一件事是选好放进去的资产。资产选得不对,后面的计算再精细也没用。

资产池选了SPY、XAU、CL、BTC这四个。选这四个是有逻辑的——它们的走势通常不完全同步。经济好的时候股票涨,避险情绪起来的时候黄金涨,通胀预期高的时候原油和黄金都涨,加密市场有时候跟股票同向,有时候又完全脱轨。正是因为它们之间的相关性不高,放在一起才有分散风险的意义。如果四个资产天天一起涨一起跌,分散就是个幻觉,什么方法都救不了你。

资产选好之后,下一个问题是:每个资产应该放多少?这才是风险平价真正需要计算的东西。

加密市场RWA多资产动态权重配置:风险平价策略

怎么算每个资产应该放多少

策略的运算逻辑分两步走。

第一步,把四个资产的K线数据拉下来,按时间戳对齐。策略使用1小时K线(PERIOD_H1)作为数据输入。这一步看起来简单,但实际上RWA合约品种有时候会缺数据,如果某个时间点SPY没有报价,那这个时间点的数据就整行丢掉,只保留四个资产同时存在报价的时间点,再做后续计算。

for (var k = 0; k < keys.length; k++) {
    var row = timeMap[keys[k]], ok = true;
    for (var i = 0; i < LABELS.length; i++) {
        if (row[LABELS[i]] === undefined) { ok = false; break; }
    }
    if (ok) timestamps.push(parseInt(keys[k]));
}

数据对齐之后,把收盘价序列转成对数收益率,也就是 ln(当前价格 / 上一期价格)。用对数收益率而不是简单涨跌幅,是因为它具有时间可加性——多期累加直接等于总对数收益,处理起来更方便;另外在资产价格服从几何布朗运动这个常见假设下,对数收益率服从正态分布,与后续协方差矩阵的计算框架兼容。需要说明的是,加密资产的实际收益率往往有厚尾特征,这是一个需要留意的假设偏差,协方差矩阵在极端行情下可能低估真实风险。

function calcLogReturns(prices) {
    var r = [];
    for (var i = 1; i < prices.length; i++) {
        var prev = prices[i - 1], cur = prices[i];
        if (!prev || !cur || prev <= 0 || cur <= 0) { r.push(0); continue; }
        r.push(Math.log(cur / prev));
    }
    return r;
}

第二步,用这些收益率序列计算协方差矩阵。单纯算历史平均协方差太粗糙,这里用的是EWMA加权——给近期数据更高的权重,让协方差矩阵能更快感知到市场状态的变化。

for (var t = T - 1; t >= 0; t--) {
    var w = Math.pow(lambda, T - 1 - t);
    c  += w * (retMat[i][t] - means[i]) * (retMat[j][t] - means[j]);
    ws += w;
}
cov[i][j] = c / ws;
cov[j][i] = cov[i][j];

lambda 是衰减系数,策略默认使用 EWMA_LAMBDA = 0.94,这个值参考了RiskMetrics针对日度数据的经验设定。需要注意的是,RiskMetrics的0.94是为日度数据设计的,策略使用1小时数据,频率更高,如果希望协方差矩阵对近期变化的敏感程度与日度0.94保持一致,理论上需要换算为更接近1的值。但在实际使用中,0.94在小时级别仍然是可接受的起点,代表近期数据权重更高、历史衰减更快的配置,可以根据实际回测效果进一步调整。lambda 越接近1,历史数据影响越持久,协方差矩阵对近期变化反应越迟钝;lambda 越小,策略对市场状态变化越敏感,但也更容易因短期噪声触发不必要的调仓。

计算完之后会在对角线上加一个正则化项,系数设为对角线均值的0.1%(eps = diagMean * 0.001)。0.001倍的系数是经过测试筛选得出,在数值上足以保证矩阵正定,同时对协方差结构的影响可以忽略不计。另外需要说明一处简化:EWMA协方差的严格做法应使用加权均值计算离差,但代码使用的是整个回望窗口的简单算术均值。在收益率均值接近零的情况下(金融资产短期收益率的常见状态),这个简化对结果的影响可以忽略,但在均值显著偏离零的场景下会引入微小偏差。

有了协方差矩阵,接下来是真正的风险平价求解——找到一组权重,让每个资产对组合风险的贡献尽量相等。在迭代开始之前,策略已经通过等波动率组合协方差完成了每个资产的方向判断,结果存储在 signs[] 数组中(+1 表示做多,-1 表示做空)。迭代过程全程使用这个预判断的固定符号,只调整权重大小,不改变方向——这样做是为了工程稳定性,防止权重在迭代过程中因数值扰动而反复跳变方向。

这个问题没有解析解,用迭代的方式逼近。每一轮计算当前的风险贡献,把贡献太大的资产权重压下去,贡献太小的拉上来,反复调整直到收敛。

for (var iter = 0; iter < 2000; iter++) {
    var trc    = riskContribs(w, scaledCov);
    var pv     = portVol(w, scaledCov);
    var target = pv / n;

    var obj = 0;
    for (var i = 0; i < n; i++) {
        for (var j = i + 1; j < n; j++) {
            var d = Math.abs(trc[i]) - Math.abs(trc[j]);
            obj  += d * d;
        }
    }
    if (obj < 1e-12) break;

    var nw = [], ns = 0;
    for (var i = 0; i < n; i++) {
        var rc   = Math.abs(trc[i]);
        var sign = signs[i];  // 使用预判断的固定符号
        var a    = target / rc;
        var v    = sign * Math.max(Math.abs(w[i]) * Math.pow(a, 0.5), 1e-6);
        nw.push(v);
        ns += Math.abs(v);
    }
    // 绝对值归一化,使各资产权重绝对值之和为1
    for (var i = 0; i < n; i++) w[i] = nw[i] / ns;
}

收敛准则是所有资产风险贡献差异的平方和小于 1e-12,此时认为各资产的风险贡献已足够接近,停止迭代。最大迭代次数限制为2000次,防止在协方差矩阵病态时无限循环。实践中通常在几百次以内收敛。如果达到上限仍未收敛,说明输入数据或协方差矩阵存在问题,此时策略会降级为等权多头,保证系统不会空转。

每个资产的风险贡献定义为它的权重乘以边际风险,结果可以为负,代表这个资产在降低组合整体波动。

function riskContribs(w, cov) {
    var sv  = matVecMul(cov, w);
    var pv  = portVol(w, cov);
    var trc = [];
    for (var i = 0; i < w.length; i++) trc.push(w[i] * sv[i] / pv);
    return trc;
}

权重可以是负数,也就是做空

标准的风险平价策略通常只做多,但合约市场可以做空,所以这个策略做了一个扩展,让权重允许为负数。

判断逻辑是这样的:先算出一个等波动率参考组合——高波动资产权重低,低波动资产权重高——然后看每个资产和这个参考组合的协方差是正是负。协方差为负,说明这个资产与组合整体倾向于反向波动,做空它有助于降低组合整体风险,权重就设为负数,在合约市场里对应开空。

需要说明的是,这是一种工程近似而非标准的多空风险平价理论解——严格的多空风险平价需要基于完整协方差矩阵做约束优化,计算成本更高。这里的近似在多数市场状态下能给出合理的方向判断,但当多个资产同时与参考组合协方差为负时,同时做空多个资产是否仍能降低组合风险,需要具体验证,不能一概而论。

var invVols = [], sumInvVol = 0;
for (var i = 0; i < n; i++) {
    var vol = Math.sqrt(Math.max(scaledCov[i][i], 1e-16));
    invVols.push(1 / vol);
    sumInvVol += 1 / vol;
}
var eqVolWeights = [];
for (var i = 0; i < n; i++) {
    eqVolWeights.push(invVols[i] / sumInvVol);
}

var covWithPortfolio = 0;
for (var j = 0; j < n; j++) {
    covWithPortfolio += eqVolWeights[j] * scaledCov[i][j];
}
var dir = covWithPortfolio < 0 ? -1 : 1;
Log('[方向判断]', LABELS[i],
    '与等波动率组合协方差:', _N(covWithPortfolio, 8),
    '→', dir < 0 ? '🔴 做空(short)' : '🟢 做多(long)');

方向一旦判断好,固定存入 signs[] 数组,在整个迭代求解过程中不再改变。迭代只负责调整权重的大小,不改变方向。方向定好之后,还有一件事要处理:整个组合应该加多少杠杆。


杠杆不是越高越好,而是跟着波动率走

策略内置了一个杠杆调节机制,目标是把组合整体的波动率控制在一个预设水平附近。打个比方:你开车,目标时速60公里。高速路上路况好,你可以多踩一点油门;市区路况复杂,你就收一收。策略的杠杆机制类似——市场整体波动低的时候,适当加杠杆,让组合风险水平达到目标;市场波动高了,自动降杠杆,收缩敞口。

策略使用1小时K线,一年恰好有8760根,因此年化系数直接取 sqrt(8760)。如果改用其他周期,这个系数需要同步调整:15分钟线对应 sqrt(35040),4小时线对应 sqrt(2190),日线对应 sqrt(365)

function calcLeverage(w, cov) {
    var pv = portVol(w, cov) * Math.sqrt(8760);
    if (!isFinite(pv) || pv <= 0) return 1;
    return Math.min(TARGET_VOL / pv, MAX_LEVERAGE);
}

组合年化波动率除以目标波动率,得到应该加的杠杆倍数,同时设一个上限防止极端情况下杠杆失控。这不是在追求最高收益,而是在做风险预算管理——用多少风险换多少回报,是事先想好的,不是随行情漂移的。

杠杆算好之后,组合就可以建仓了。与此同时,策略还会根据当前波动率高低,自动调整主循环的轮询间隔,波动越高刷新越快,波动低了就放慢节奏,减少不必要的计算和交易摩擦。

function getAdaptiveSleep(w, cov) {
    var av = portVol(w, cov) * Math.sqrt(8760);
    if (av > 0.50) return FAST_INTERVAL;    // 15分钟
    else if (av > 0.30) return MID_INTERVAL; // 30分钟
    else return SLOW_INTERVAL;               // 60分钟
}

什么时候调仓

市场每天都在动,权重会不断偏离最初的设计。如果不管它,时间一长组合实际承受的风险就和预期差得很远了。策略设置了两个触发再平衡的条件:一是定时,每隔固定小时数强制重新计算权重;二是漂移超标,当某个资产的实际权重和目标权重偏差超过阈值,或者多空方向发生翻转,立刻触发调仓,不等下次定时。

计算相对漂移时,分母是上一次的权重绝对值。当某资产权重被压得极小(接近零)时,相对漂移会被放大,容易引发频繁再平衡。策略对此做了保护:当 lastWeights[i] 为零时直接跳过漂移检查,避免除零导致的误触发。

if (!lastWeights) {
    needRebal   = true;
    rebalReason = '首次建仓';
} else if ((now - lastTime) > REBALANCE_HOURS * 3600000) {
    needRebal   = true;
    rebalReason = '定时再平衡(' + REBALANCE_HOURS + 'h)';
} else {
    for (var i = 0; i < weights.length; i++) {
        if ((weights[i] >= 0) !== (lastWeights[i] >= 0)) {
            needRebal   = true;
            rebalReason = LABELS[i] + ' 方向翻转(多↔空)';
            break;
        }
        if (lastWeights[i] !== 0 &&
            Math.abs(weights[i] - lastWeights[i]) / Math.abs(lastWeights[i]) > DRIFT_THRESHOLD) {
            needRebal   = true;
            rebalReason = LABELS[i] + ' 权重漂移超阈值';
            break;
        }
    }
}

触发再平衡之后,策略计算每个资产的目标名义金额,正数代表多头,负数代表空头,然后对比当前持仓,决定加仓、减仓还是方向切换。方向切换的时候,先平掉反向仓位,再开新方向,避免同时持有多空两个方向。

if (targetSide === 'long' && shortQty > 0) {
    exchange.CreateOrder(sym, "closesell", -1, shortQty);
}
if (targetSide === 'short' && longQty > 0) {
    exchange.CreateOrder(sym, "closebuy", -1, longQty);
}

if (diffQty > 0) {
    exchange.CreateOrder(sym, targetSide === 'long' ? "buy" : "sell", -1, diffQty);
} else if (diffQty < 0) {
    exchange.CreateOrder(sym, targetSide === 'long' ? "closebuy" : "closesell", -1, Math.abs(diffQty));
}

这样既不会频繁交易产生大量手续费,也不会让组合长时间偏离设计初衷。正常情况下,再平衡机制足够应对大多数市场变化。但市场有时候不正常,所以还需要一道额外的保险。


还有一道紧急熔断

除了正常的再平衡,策略还有一个紧急减仓机制。每一轮主循环都会检查当前所有持仓,对比上一次记录的价格,看每个方向的不利波动有多大。如果多头大跌或者空头大涨超过5%,策略会自动减仓一半,先把损失控制住,不等下一轮再平衡。

if (pos.side === 'long') {
    drop = (last - cur) / last;
} else if (pos.side === 'short') {
    drop = (cur - last) / last;
}

if (drop >= EMERGENCY_DROP) {
    Log('🚨 紧急风险触发! [' + label + '][' + pos.side + '] 不利变动:',
        _N(drop * 100, 2) + '%', '上次:', _N(last, 4), '→ 现价:', _N(cur, 4));
    if (mode === 'live') emergencyReduceLive(sym, label, pos.side, EMERGENCY_REDUCE);
    else                 emergencyReducePaper(sym, label, cur, pos.side, EMERGENCY_REDUCE);
}

实盘模式下调用交易所平仓接口,模拟盘模式下在本地状态里更新保证金和现金,按持仓比例释放对应的保证金,计算当前盈亏,并写入交易记录。

这里有一个设计细节需要说明:紧急减仓触发之后,基准价格不会立即重置,下一轮检查仍以触发前的价格为参考基准。这意味着在资产持续朝不利方向运动的行情中,该机制会在每一轮循环中反复触发,逐步减仓直至仓位耗尽,或等到下一次正常再平衡重置基准价格为止。这个设计是有意为之——在极端单边行情中,持续减仓比减一次就停更能控制损失;但副作用是,如果价格短暂跌破阈值后快速反弹,可能因为过度减仓而错过反弹。正常市场里这个机制几乎不会触发,但有它在,至少不会在黑天鹅事件里一路扛到底。


跑起来是什么样的

策略在平台上运行之后,仪表盘会实时显示每个资产现在是做多还是做空、各自的权重、当前杠杆倍数、组合整体的年化波动率估算值,以及四个资产之间的相关系数矩阵。持仓浮盈每分钟刷新一次,策略核心计算根据市场波动高低自动切换轮询频率。

这个1分钟刷新不是靠多线程实现的,而是在主循环的等待期间嵌套了一个子循环,每隔1分钟唤醒一次拉取最新价格、更新浮盈和净值曲线,累计达到策略轮询间隔之后退出子循环,进入下一轮策略计算。整个策略完全单线程,没有并发风险。

function sleepWithPnlRefresh(totalSleepMs, weights, leverage, covMatrix, corrMatrix) {
    var elapsed = 0;
    while (elapsed < totalSleepMs) {
        var step = Math.min(PNL_INTERVAL, totalSleepMs - elapsed);
        Sleep(step);
        elapsed += step;

        var prices  = getTickers();
        var equity  = calcEquity(prices);
        var initCap = _G('pt_initCapital') || INIT_CAPITAL;
        _chart.add(0, [new Date().getTime(), equity]);
        LogProfit(equity - initCap, '&');
        renderDashboard(weights, leverage, covMatrix, corrMatrix, prices, totalSleepMs, true);
    }
}

另外策略支持模拟盘模式,可以在不动用真实资金的情况下完整跑通逻辑、调试参数,确认行为符合预期之后再评估是否切换实盘。带着这套配置跑了一段时间模拟盘,来看看实际结果。


模拟盘跑出来的结果

策略测试期间暂时整体是盈利的状态。持仓方向:BTC、XAU、SPY均做多,CL原油做空。恰逢这段时间美伊局势出现缓和迹象,地缘风险溢价回落,原油价格出现了比较明显的下跌,做空原油是这一轮的主要盈利来源。这段时间的市场走势对策略比较友好,不代表未来一定如此。事实上,这个策略本身还有不少没想清楚的地方,不说清楚不太诚实。

加密市场RWA多资产动态权重配置:风险平价策略


没想清楚的地方

做这个策略过程中,有几个问题一直没有完全解决。

第一,RWA合约的流动性远不如BTC。SPY、XAU、CL这些品种在链上还很新,滑点和盘口深度是未知数,模拟盘里感知不到这个问题,但实盘里会真实发生。

第二,这个策略依赖各资产之间保持一定的分散性。但在市场极端恐慌的时候,风险资产之间的相关性往往会急剧上升,分散的效果会大打折扣,而这恰恰是你最需要保护的时候。

第三,传统资产有非交易时段,价格几乎不动,但BTC全天候波动。这会导致协方差矩阵低估资产之间的真实联动——美股收盘后BTC还在跑,这段时间的波动没有对应的SPY数据,等传统市场开盘,策略对联动关系的判断可能已经滞后了。

第四,历史数据有限,协方差矩阵在短周期下的稳定性存疑。EWMA衰减系数、目标波动率、再平衡阈值这些参数都是经验值,没有经过严格验证。做空判断使用的是等波动率参考组合协方差符号,这是一种工程近似,当多个资产同时与参考组合协方差为负时,同时做空是否确实降低组合风险,需要逐一验证,不能直接套用。加密资产本身有厚尾特征,实际收益率分布偏离正态,在极端行情下协方差矩阵对风险的估计会偏低,这是使用当前框架必须接受的假设偏差。

第五,实盘环境下方向切换时,平仓指令发出后策略等待固定时长即继续执行后续开仓,没有等待成交确认。在交易所响应较慢或网络延迟较高的情况下,存在旧仓未完全平掉就开新仓的风险。模拟盘不涉及真实成交,感知不到这个问题,切换实盘前需要根据具体交易所的响应速度评估是否需要调整等待时长。

这些问题靠模拟盘跑一跑是发现不了的,需要更细致的建模和理论推导,把每一个假设都认真验证一遍,才能知道这套逻辑在加密市场里到底站不站得住脚。


最后

做这个策略的出发点很简单:加密市场熊市,RWA合约刚好补齐了大类资产的版图,传统金融有几十年积累的配置理论,两件事碰在一起,值得试一试。代码是完整的,逻辑是透明的,参数是可以调的。但这终究只是一次起点,距离真正经得起实盘检验的资产配置策略还差很多。如果你有更好的想法,欢迎一起来完善它。

以上内容仅记录策略探索过程,不构成任何投资建议。合约交易存在较大风险,模拟盘表现不代表实盘结果,涉及真实资金前请充分了解相关风险。

策略源码:RWA大类资产风险平价策略

相关推荐