[原创] 牛来上了币安合约,恐高也恐低,于是我去做了一个不赌方向的策略

8月30日11点30分,币安上线了「牛来」的USDT本位永续,合约代号就是中文的「牛来USDT」。这个币来自那部两个人做了五年、因为画面粗糙反而出圈的国产动画,加上「牛来」谐音「牛市来了」,梗就这么成了。上线第一天24小时振幅57.4%,单日涨28.7%,按分钟算的已实现波动率95.7个基点——我拉了全市场612个永续做横向对比,它在下影线的深度和频率上排第一。

问题也在这儿。这种波动下追多,你不知道自己是不是在山顶接盘;做空,一根针就能把你打爆。恐高恐低,两边都不敢下手,但四亿美金的日成交额摆在那里,又实在不想干看着。

在这类币上,我先试了几种最直观的做法——挂低价单接针、双向网格、资金费结算前后套利,都没走通。事后看它们的失败原因是同一个:全都在赌「跌下去会弹回来」。接针赌深针回归、网格赌价格在区间里来回、资金费狙击赌结算后的价格回摆。但数据给出的答案很一致:牛来的深针回归率只有0.31,浅回调回归率1.65却因为盘口队列根本接不到;网格在139天样本里横盘和上涨窗口几乎必赚、大跌窗口胜率只有8%。

高波动的meme永续是动量型资产,不是均值回归型资产。 想明白这一点,剩下的方向就只有一个:不要跟它对着干,顺着它走。

强者恒强,以及怎么定义「强」

顺势策略最朴素的形态是买涨得最多的、卖跌得最多的。但只看涨幅有个明显缺陷:涨幅是结果,不是原因。一个币涨了20%,可能是有持续的新资金在推,也可能是几笔空头平仓砸出来的短暂拉升,两者的后续走势完全不同,而K线上看起来一模一样。

所以我加了第二个维度:资金费率

永续合约的资金费本质上是多空双方的持仓成本平衡机制。费率被推高,说明有大量的人愿意持续付钱去持有多头头寸。这跟"价格涨了"是两件事——价格可以被一笔大单推上去,但资金费率反映的是持仓结构,是真金白银的持有意愿。

价格动量告诉你「它涨了」,资金费率告诉你「有人愿意为这个方向付成本」。 两个维度合成一个复合动量分,做多分数最高的一批、做空最低的一批,两边名义金额相等。整个板块一起涨或一起跌,多空两腿抵消,收益只来自"最强的那批"和"最弱的那批"之间的相对表现——这正好绕开了最开始那个困境:你不需要判断牛来会涨还是会跌,只需要判断它比其他妖币强还是弱。

两个因子该各占多少权重

最初我写的是固定系数,价格动量给1.0、资金费给0.6。跑起来之后自己就觉得不对——这两个数字是我拍脑袋定的,没有任何依据。凭什么是0.6不是0.4?

更麻烦的是,就算我试出一组当下最优的系数,它也未必稳定。我在测试阶段观察到一个现象:同一个资金费因子,票池从44个合约换成35个、时间窗口从41天换成31天,它的表现方向就会翻转。一个会随市场状态改变符号的因子,用固定权重去装它,等于把过去某段行情的特征焊死在策略里。

所以改成让数据自己决定。方法是滚动截面回归:每一期用当时的全部候选合约做一次横截面OLS,把下一期的实际收益对两个因子的z-score回归。

// 单期横截面 OLS: fwd = a + b1*z(动量) + b2*z(资金费)
function crossSectionOLS(zm, zf, fwd) {
    var n = fwd.length; if (n < 10) return null;
    var A = [[0,0,0],[0,0,0],[0,0,0]], b = [0,0,0];
    for (var i = 0; i < n; i++) {
        var x = [1, zm[i], zf[i]];
        for (var r = 0; r < 3; r++) {
            b[r] += x[r] * fwd[i];
            for (var c = 0; c < 3; c++) A[r][c] += x[r] * x[c];
        }
    }
    return solve3(A, b);
}

把过去120期(4小时一期,约20天)逐期跑一遍,得到两个系数各自的时间序列,再对时间取均值——这就是Fama-MacBeth的做法。均值给你权重,标准差给你显著性。

关键在于怎么用这个显著性。系数均值再大,如果它在时间序列上跳来跳去,说明这个因子在不同时期的作用完全不同,不该给它高权重。所以按t统计量收缩:

function shrink(r) {
    var k = Math.min(1, Math.abs(r.t) / T_SHRINK);   // |t|>=2 全额采用, 否则按比例压缩
    return r.m * k;
}
var w1 = shrink(r1), w2 = shrink(r2);
var scale = Math.abs(w1) + Math.abs(w2);
w1 = w1 / scale;  w2 = w2 / scale;                    // 归一化到 |w1|+|w2|=1

这样做有三个好处。因子不显著会自动被压权重,不需要我判断哪个该占多少;符号由数据决定,如果某段时间资金费的正确用法是反向,回归会给出负系数,策略跟着改,而不是死扛着我当初写下的正号;权重归一化之后总敞口不受影响,只改变两个因子的相对话语权。

每12小时重新拟合一次。面板上直接显示两个因子的β和t值,你能看到当前权重是怎么来的——如果某天资金费的t值掉到0.3,你会看到它的权重自动缩到接近下限,而不是蒙在鼓里。

一个必须修的坑:资金费率的时间口径

这是我在实现时踩到的,而且影响足够大,值得单独说。

币安不同合约的资金费结算周期是不一样的。查fundingInfo接口,全市场768个有记录的合约里,440个是4小时结算、324个是8小时、还有4个是1小时。牛来正是4小时结算。

问题在于:一个4小时结算、费率5个基点的合约,和一个8小时结算、费率5个基点的合约,标称数字一样,真实持仓成本差一倍。前者每天收6次,后者每天收3次。

如果直接拿原始费率做横截面排序,等于在比较不同量纲的东西——会系统性地把4小时合约排低、8小时合约排高,而票池里4小时合约占了一多半。选出来的多空腿是歪的。

修正很简单,但不能不做:

// 拉取各合约结算周期
var fi = api('fundingInfo');
for (var i = 0; i < fi.length; i++)
    _ivh[fi[i].symbol] = parseInt(fi[i].fundingIntervalHours || 8);

//  统一到 8 小时口径后再进入排序
c.fund8 = c.fr * (8.0 / ivh(c.bin));

风险均衡:让每个币贡献相同的风险

妖币票池里各个标的的波动率差异极大,同一批候选里4小时波动率可以从15个基点到200个基点。如果等权重分配名义金额,波动最大的那两三个币会贡献组合80%以上的风险,其余的形同虚设。这时候你名义上持有10个币,实际上是在赌其中2个。

所以组内按波动率反比配权:

function riskParity(g) {
    var iv = [], z = 0;
    for (var i = 0; i < g.length; i++) { var x = 1 / Math.max(5, g[i].sigma); iv.push(x); z += x; }
    var w = []; for (var j = 0; j < g.length; j++) w.push(iv[j] / z);
    return w;
}

这一步是我试过的所有改进里唯一一个在全样本上三项指标同时改善的——中位数从36.5个基点提到68.0、胜率从53%提到57%、单次最差从-2021收窄到-1422。它不是参数拟合,是原理性的:风险均摊之后,组合的表现不再由某一两个币的运气决定

组合层面再加一道目标波动率缩放。先估计当前候选组合的波动水平,如果高于目标就按比例缩小总敞口,低于目标则维持上限:

var pv = 0;
for (var a = 0; a < L.length; a++) pv += wl[a] * L[a].sigma;
for (var b = 0; b < Sh.length; b++) pv += ws[b] * Sh[b].sigma;
pv = pv / 2 * 0.6 / 1e4 * 100;                        // 多空对冲后的残差波动估计
var scale = pv > 0 ? Math.min(1, TARGET_VOL_PCT / pv) : 1;
var gross = GROSS_MAX_USDT * scale;

这样做的意义是收益分布可预期。妖币行情有时极度平静、有时全线暴动,固定名义敞口意味着你的实际风险随行情漂移好几倍。目标波动率把这个漂移拉平,代价是行情最躁动的时候你的仓位会自动缩小——而那恰恰是最该缩小的时候。

统一账户:多空腿浮亏互抵

这是这类策略在实盘层面最容易被忽略、但对生存率影响最大的一点。

同样是持有10个多头、10个空头,逐仓保证金下每条腿独立计算维持保证金,任何一条腿单独打到爆仓线就被强平——哪怕你的组合整体是赚钱的。妖币日内20%的振幅很常见,逐仓模式下这几乎是必然会发生的事。

统一账户(全仓保证金)模式下,多空两腿的浮盈浮亏在账户层面互相抵消。板块整体上涨时,多头腿的浮盈直接顶上空头腿的浮亏,同等名义敞口下的维持保证金率远高于逐仓。对一个市场中性的多空组合来说,这不是可选项,是必需项。

策略启动时会提示这一点,杠杆也建议压在3倍以内——杠杆是用来提高资金效率的,不是用来放大信号的,信号本身的强度不会因为杠杆变高。

轮转频率:不是越快越好

复合分是实时变化的,理论上可以每5分钟重排一次。我做了实测,把5分钟到24小时共九档周期在同一批数据上跑了一遍,结论很明确:

轮转周期 日化收益(bp) 换手腿数/期
5分钟 -579 11 / 20
1小时 -41.6 16.5 / 20
2小时 +34.5 17.0 / 20
4小时 +103.8 17.7 / 20
12小时 +118.0 19.3 / 20

1小时以内必亏。 换手率在所有周期上都高得惊人——20条腿里每期要换掉16到19条,说明排名本身在短周期上极不稳定。频率越高,你付的手续费越多,而抓到的信号增量越少。

12小时和24小时的数字更高,但样本只有83期和41期,而且中位数和均值差距巨大(24小时原始信号中位-22.62、均值+151.45),说明收益靠少数几次大赢撑着。4小时不一样:中位+12.23、均值+17.30、胜率53%、样本249期,中位和均值同向且接近——典型交易就在赚钱,而不是靠尾部拉起来

而且4小时有个结构性理由:票池里将近一半的合约是4小时结算,轮转周期和资金费结算周期对齐。所以默认参数定在4小时。

执行层:不允许残缺仓位存在

策略要同时持有20条腿。回测里这是一行代码,假设全部按中价成交;实盘不是。

最初我写了个"失败腿计数",某条腿开不出来就记一笔,超过阈值放弃这轮。写完自己看着别扭,后来想明白哪里不对:失败腿这个东西根本不该被统计,它该被消除。

这是个市场中性策略,全部安全性来自多空两腿相互抵消。如果计划10多10空、实际成交了9个多头7个空头,你手上就不是中性组合,而是一个净多两条腿的方向性赌注——而你还以为自己是中性的,风险模型在那一刻就失效了。更麻烦的是这种失衡不会自愈,下一轮轮转之前你会带着这个错误敞口跑满4小时。

所以第一层在规划阶段:不可交易的腿直接剔除,剔除时对侧同步裁剪,保证两侧腿数和名义严格相等;某一侧凑不齐就整体缩减腿数重来,从10对减到9对、8对,直到两边都合格。

第二层在执行阶段,循环到完全匹配为止:

function strictOpen(targets) {
    S.execState = 'OPENING'; _targets = targets; saveState();
    while (n++ < MAX_REPAIR_LOOP) {
        if (!cancelAll(list)) { Sleep(ORDER_SETTLE_MS); continue; }   // 先撤单并确认无残留
        var pq = queryPos(true);
        if (!pq.ok) { Sleep(ORDER_SETTLE_MS); continue; }             // 核仓失败 → 绝不下单
        if (exactMatch(pq.positions, targets)) {
            var v = queryPos(true);                                    // 二次确认
            if (v.ok && exactMatch(v.positions, targets)) {
                S.execState = 'HOLDING'; saveState(); return true;
            }
            continue;
        }
        submitDiff(pq.positions, targets, LIMIT_FIRST && n <= 2);       // 按差额补单
    }
    strictClose('开仓未收敛');
    return false;
}

// 数量比较必须按精度转成整数, 浮点直接比永远不会相等
function qtyU(f, q) { return Math.round(Math.abs(q) * Math.pow(10, spec(f).ap)); }

这里面有条不能省的规则:查不到真实持仓就一律禁止下单。接口异常、返回格式不对、限流被拒,任何一种情况下都不操作。在不知道自己有什么仓位的时候下单,是所有交易事故里最经典的一类。

最后是净敞口守卫:每次核仓都算多头总名义和空头总名义的偏差,超过8%立即全部平仓重来。这也决定了执行上的一个取舍——挂限价单能省手续费但可能挂不上,挂不上就意味着腿不齐,所以前两轮修复用限价争取maker,第三轮开始转市价强制成交。对中性策略来说,敞口失衡比多付手续费危险得多,这个优先级不能反。

牛来只是个由头。它上线那天的振幅、波动率、下影线深度都排在全市场前列,是妖币这个类别里最新的一个样本,但策略从头到尾不依赖它——这类策略的对象不是某个币,是整个横截面。今天最强的可能是牛来,下周可能是另一个刚上线的梗,再下周是某个老币突然被资金重新盯上。你不需要知道下一个是谁,只需要每四小时问一次"现在谁最强、谁最弱",然后让仓位跟着答案走。

这也是它和"追热点"的区别。追热点是押注某个标的,赢面取决于你选得准不准;横截面轮转是押注"强弱分化会持续"这个统计性质,单个标的选错了,会被另外九条腿摊薄掉。妖币板块波动越大,这个分化就越明显——别的策略最怕的高波动,恰好是这类策略的燃料

免责声明:信息仅供参考,不构成投资及交易建议。投资者据此操作,风险自担。
如果觉得文章对你有用,请随意赞赏收藏
相关推荐
相关下载
登录后评论
Copyright © 2019 宽客在线