本篇位置 第五部分「从信号到交易」第六篇。第 23 篇讲出场,这一篇讲另外两件会让出场变成空话的事:做空的不对称,和杠杆带来的第二条出场线
用到的数据 BTCUSDT 永续合约 1 分钟最新价与标记价格(各 350 万根)、7,305 次资金费率结算、Binance 分层维持保证金表;864 个永续合约和美股市值前 100 的日线;FINRA 每日卖空成交量 93 天;Nasdaq 空头仓位 40 只;GME 日线
动手 talab.risk 加上杠杆部分:margin_tiermaintenance_marginliquidation_pricemax_leveragereplay_liquidationfunding_cost,新增 7 个测试(test_risk.py 共 23 个)
读完你能 算出任何一个仓位的强平价;说清做空和做多的风险为什么不对称;在选杠杆之前先算出「这个杠杆容得下我的止损吗」

一、先做一个决定

2021 年 2 月 8 日中午 12 点整(UTC),你在 Binance 开了一个 BTCUSDT 永续合约的空单:

  • 进场价 39,495.35(开盘市价)
  • 10 倍杠杆,逐仓
  • 名义价值 10 万美元,保证金 1 万美元

接下来四十分钟风平浪静。然后 12:43 开始,价格突然往上蹿——特斯拉在向 SEC 提交的 10-K 里写明,它买了 15 亿美元的比特币。

那份年报被 EDGAR 接收的时间是 12:27:23,市场用了十五分钟才反应过来:

特斯拉那份 10-K 被 SEC 接收的时间是 2021-02-08 12:27:23+00:00(EDGAR 的记录),价格 12:43 才开始动,中间隔了 15 分钟
                               open      high       low     close   浮亏%    保证金还剩
time                                                                            
2021-02-08 12:40:00+00:00  39303.00  39340.00  39278.81  39340.00 -0.39  10393.0
2021-02-08 12:41:00+00:00  39340.00  39375.00  39294.78  39297.82 -0.50  10500.0
2021-02-08 12:42:00+00:00  39297.82  39394.00  39297.81  39381.74 -0.29  10288.0
2021-02-08 12:43:00+00:00  39381.74  39792.00  39381.74  39792.00  0.75   9249.0
2021-02-08 12:44:00+00:00  39792.00  40680.00  39785.19  40560.80  2.70   7302.0
2021-02-08 12:45:00+00:00  40560.80  41291.60  40542.00  40804.71  3.32   6685.0
2021-02-08 12:46:00+00:00  40804.72  41250.00  40776.00  41110.30  4.09   5911.0
2021-02-08 12:47:00+00:00  41110.30  41291.49  41037.30  41277.49  4.51   5488.0
2021-02-08 12:48:00+00:00  41271.48  41447.57  41232.00  41337.39  4.66   5336.0
2021-02-08 12:49:00+00:00  41337.39  41420.99  40953.28  41116.75  4.11   5895.0
2021-02-08 12:50:00+00:00  41116.75  41861.00  41112.66  41701.50  5.59   4414.0
2021-02-08 12:51:00+00:00  41697.17  42420.11  41643.43  42116.26  6.64   3364.0
2021-02-08 12:52:00+00:00  42119.99  42215.70  41670.43  41934.58  6.18   3824.0
2021-02-08 12:53:00+00:00  41932.15  42190.65  41773.78  42091.59  6.57   3426.0
2021-02-08 12:54:00+00:00  42096.45  42616.60  42091.60  42566.15  7.78   2225.0
2021-02-08 12:55:00+00:00  42566.16  42738.00  42200.59  42631.59  7.94   2059.0

10 倍杠杆做空,浮亏 7.9%

2021-02-08 12:55:00+00:00 收盘 42,631.59:价格朝不利方向走了 7.94%,1 万保证金只剩 2,059 美元

离强平价还有多远?

  • A. 2.1 个百分点左右。10 倍杠杆就是反向走 10% 爆仓,现在走了 7.9%,还剩 2.1
  • B. 2.1 个百分点左右,但要从现在的价格算。保证金还剩 20.6%,20.6% ÷ 10 = 2.06%
  • C. 比这两个都近

先写下你的选择。 第十节揭晓——顺便告诉你,从开仓到结束一共用了多久。


二、打个比方:借邻居的自行车

做空这件事,用自行车讲最清楚。

你看中邻居那辆自行车,觉得它下个月会跌价。于是你把车借来,今天按市价 3,000 块卖掉,打算下个月花 2,000 买一辆一模一样的还给他,中间那 1,000 是你的。

这就是做空的全部:你卖的是借来的东西,你欠的是「一辆车」,不是「3,000 块」。

三件事马上跟着来:

  1. 邻居要收租金。 借车不是白借——美股这边叫借券费,热门的票一年能收到两位数。加密这边有意思:永续合约的资金费率常常是反过来的,别人付钱给你(第七节有数据)。
  2. 邻居随时可以要车。 他儿子明天要骑,你就得今天去买回来还他,不管车价是多少。美股的借券可以被随时召回,这叫 recall。
  3. 车价最多跌到 0,但可以涨到天上去。 车价归零你赚 3,000——这是上限。但如果这车成了绝版收藏品,涨到 30,000,你要花 30,000 买回来还他,亏 27,000——这没有下限

然后是杠杆。你连那 3,000 块都不用自己出:你只在邻居那儿押了 300 块押金,剩下的是平台垫的。

  • 押金就是保证金。
  • 邻居有个底线:押金跌破某个数(维持保证金),他就不等你解释了,直接找人把车买回来——强平
  • 他不会等你说「再给我两天」。你的止损是你自己决定认输的地方,强平是他替你决定的

这一篇剩下的部分,就是把这几句话换成价格、公式和数据。


三、做空的收益,上面有一堵墙

先把最基本的不对称讲清楚。

你做多一笔 1 万美元的仓位:最坏的情况是标的归零,你亏 1 万,亏损有下限;最好的情况没有上限,它可以涨 10 倍、100 倍。

你做空一笔 1 万美元的仓位:最好的情况是标的归零,你赚 1 万,收益有上限;最坏的情况没有上限。

这不是修辞,是能数出来的。拿第 19 篇下载的全市场数据——864 个上过市的 USDT 永续合约,加上美股市值前 100——把每一个「起点 × 持有天数」的做空收益都算一遍:

panel = {}
for folder in sorted(glob.glob("data/universe/um/*")):
    files = sorted(glob.glob(f"{folder}/1d/*.zip"))
    if not files:
        continue
    df = D.load_binance_klines(files)
    panel[folder.split("/")[-1]] = df["close"].where(df["volume"] > 0)   # 第 19 篇:成交量 0 的占位 K 线
crypto = pd.DataFrame(panel).sort_index()
stocks = pd.DataFrame({p.split("/")[-1].split("_")[0]: D.load_nasdaq_daily(p)["close"]
                       for p in sorted(glob.glob("data/universe_us/*_historical.json"))}).sort_index()
print(f"加密:{crypto.shape[1]} 个合约 × {crypto.shape[0]} 天;美股:{stocks.shape[1]} 只 × {stocks.shape[0]} 天")
rows = []
for name, table in [("加密永续", crypto), ("美股市值前 100", stocks)]:
    for days in [20, 60, 250]:
        forward = (table.shift(-days) / table - 1).to_numpy().ravel()
        forward = forward[~np.isnan(forward)]
        short = -forward
        rows.append({"市场": name, "持有天数": days, "样本": len(short),
                     "做空最好的一笔": short.max(), "做空最差的一笔": short.min(),
                     "做空中位数": np.median(short), "做空平均": short.mean(),
                     "亏超过本金的比例": (short < -1).mean(), "赚超过本金的比例": (short > 1).mean()})
print(pd.DataFrame(rows).round(4).to_string(index=False))
       市场  持有天数     样本  做空最好的一笔   做空最差的一笔   做空中位数    做空平均  亏超过本金的比例  赚超过本金的比例
     加密永续    20 567461   0.9999 -109.2815  0.0453 -0.0032    0.0135       0.0
     加密永续    60 534361   0.9999 -207.0099  0.1189 -0.0192    0.0441       0.0
     加密永续   250 404895   0.9997 -208.6792  0.4011 -0.0729    0.0880       0.0
美股市值前 100    20 259109   0.6214   -1.6774 -0.0136 -0.0169    0.0002       0.0
美股市值前 100    60 254642   0.6555   -5.3504 -0.0378 -0.0512    0.0029       0.0
美股市值前 100   250 234093   0.8221  -49.5723 -0.1395 -0.2354    0.0389       0.0

做空的收益分布

读这张表:

  1. 「做空最好的一笔」那一列永远到不了 1。 加密这边最高 0.9999,美股最高 0.82(250 天那一行)——因为价格跌不到负数。这就是图里那堵墙。
  2. 「做空最差的一笔」没有墙。 加密永续持有 60 天,最差的一笔是 −207,也就是亏掉本金的 207 倍;美股持有 250 天最差 −49.6
  3. 亏超过本金的概率不是万分之一。 加密永续持有 250 天,8.8% 的样本做空会亏掉本金一倍以上;美股是 3.9%。「亏掉本金一倍以上」的意思是:你押 1 万,赔 2 万。
  4. 中位数和平均数打架,而且和第 23 篇是同一个道理。 加密永续做空 60 天,中位数 +11.9%,平均 −1.9%:大多数山寨合约确实在跌,但少数几个暴涨的把平均数拖到水下。做空是一个「常赢小钱、偶尔输掉全部」的形状——和趋势跟随正好反过来。

美股那边的中位数是 −14.0%(250 天),平均 −23.5%:做空市值前 100 的股票,连中位数都在亏钱。这不奇怪,这十年它们在涨。

⚠️ 这里说的「亏掉本金的 207 倍」,现实中不会真的发生——你早就被强平了(第六节)。强平的意义就是把这条没有下限的尾巴截断,代价是把决定权交给交易所。


四、下跌真的比上涨快吗

「下跌比上涨快」是句老话。对做空的人来说它很重要:如果下跌真的更快,那么做空就是在赚快钱;如果上涨更快,那么你的亏损会来得比盈利更急。

用第 8 篇的 ZigZag 把每个标的切成一段一段的上涨和下跌,比较每根 K 线平均走多少

rows = []
for name, (df, _), threshold in [("SPY", MARKETS["SPY"], 0.08), ("AAPL", MARKETS["AAPL"], 0.12),
                                 ("BTC", MARKETS["BTC"], 0.20)]:
    close = df["close"]
    points = X.zigzag(close, threshold)
    seg = pd.DataFrame({"起价": points["price"][:-1].to_numpy(), "止价": points["price"][1:].to_numpy()})
    seg["根数"] = [close.index.get_loc(b) - close.index.get_loc(a)
                   for a, b in zip(points["time"][:-1], points["time"][1:])]
    seg["幅度"] = seg["止价"] / seg["起价"] - 1
    seg["每根走多少"] = seg["幅度"].abs() / seg["根数"]
    up, down = seg[seg["幅度"] > 0], seg[seg["幅度"] < 0]
    ret = close.pct_change()
    rows.append({"标的": name, "ZigZag 阈值": threshold, "段数": len(seg),
                 "上涨段中位幅度": up["幅度"].median(), "上涨段中位根数": up["根数"].median(),
                 "上涨每根走%": up["每根走多少"].median() * 100, "下跌段中位幅度": down["幅度"].median(),
                 "下跌段中位根数": down["根数"].median(), "下跌每根走%": down["每根走多少"].median() * 100,
                 "下跌快几倍": down["每根走多少"].median() / up["每根走多少"].median(),
                 "最差单日": ret.min(), "最好单日": ret.max(), "偏度": ret.skew()})
print(pd.DataFrame(rows).round(4).to_string(index=False))
  标的  ZigZag 阈值  段数  上涨段中位幅度  上涨段中位根数  上涨每根走%  下跌段中位幅度  下跌段中位根数  下跌每根走%  下跌快几倍    最差单日   最好单日      偏度
 SPY       0.08  24   0.3290    166.5  0.1765  -0.1494     37.0  0.4922 2.7885 -0.1094 0.1050 -0.3081
AAPL       0.12  34   0.4956     92.0  0.5548  -0.1736     31.0  0.6642 1.1973 -0.1286 0.1533  0.1242
 BTC       0.20  52   0.5280     46.0  1.4822  -0.3227     48.5  0.7381 0.4980 -0.3950 0.2250 -0.2250

三个标的,三个答案:

  • SPY:下跌快 2.79 倍。 上涨段中位数走 32.9%、用 166 根 K 线,每天 0.18%;下跌段走 −14.9%、只用 37 根,每天 0.49%。股指的上涨是磨上去的,下跌是砸下来的。
  • AAPL:快 1.20 倍。 个股上这个效应弱得多。
  • BTC:0.50 倍,反过来。 BTC 的上涨段中位数每天走 1.48%,下跌段只有 0.74%。加密这边猛的是向上那一边。

再看单日的极端值:SPY 最差单日 −10.94%、最好 +10.50%;BTC 最差 −39.50%、最好 +22.50%。单日的极端是向下的,多日的段是向上的(对 BTC 而言)——两句话都对,看的是不同的尺度。

对做空的人,这两条合起来是:

在股指上做空,你的亏损(价格上涨)来得慢,你有时间反应;在 BTC 上做空,你的亏损来得比盈利还快。

第一节那个决策点就是后者的极端版本:55 分钟,7.9%。


五、美股这边:借券、空头仓位和轧空

借券费没有免费数据,但有三件事可以量

美股做空的第一步是借到票。借券费率是券商和你之间的事,没有公开、免费、可复现的历史数据——这一篇不编。可以量的是另外三件事。

第一,空头仓位有多大。 交易所每半个月公布一次每只股票的空头股数。Nasdaq 的接口只覆盖 Nasdaq 上市的股票,所以市值前 100 里只拿到了 40 只:

listing = D.load_nasdaq_screener("data/universe_us/screener.json")
rows = []
for path in sorted(glob.glob("data/short_interest/*_short-interest.json")):
    symbol = path.split("/")[-1].split("_")[0]
    try:
        table = D.load_nasdaq_short_interest(path)
    except TypeError:                                  # 纽交所上市的,这个接口不给
        continue
    if symbol not in listing.index or listing.loc[symbol, "close"] <= 0:
        continue
    shares = listing.loc[symbol, "market_cap"] / listing.loc[symbol, "close"]
    rows.append({"代码": symbol, "空头股数": table["空头股数"].iloc[-1],
                 "占总股本": table["空头股数"].iloc[-1] / shares, "回补天数": table["回补天数"].iloc[-1]})
short_interest = pd.DataFrame(rows).sort_values("占总股本", ascending=False)
print(f"市值前 100 里有 {len(short_interest)} 只拿到了空头仓位记录(这个接口只覆盖 Nasdaq 上市的股票)")
print(short_interest.head(8).round(4).to_string(index=False))
print(f"...\n中位数:占总股本 {short_interest['占总股本'].median():.2%},"
      f"回补天数 {short_interest['回补天数'].median():.2f}")
市值前 100 里有 40 只拿到了空头仓位记录(这个接口只覆盖 Nasdaq 上市的股票)
  代码      空头股数   占总股本   回补天数
MRVL  38175634 0.0435 1.3539
SNDK   5996105 0.0410 1.0000
 STX   8482797 0.0371 2.3626
QCOM  33718025 0.0321 3.3112
INTC 152248752 0.0288 1.6870
PLTR  65131893 0.0271 2.1560
  MU  29705339 0.0263 1.1583
PANW  21411706 0.0262 3.8548
...
中位数:占总股本 1.65%,回补天数 2.61
  代码      空头股数   占总股本   回补天数
TSLA  74230933 0.0188 2.0380
NVDA 298301619 0.0124 2.1442
AAPL 139749097 0.0096 3.5346

美股的空头仓位和卖空占比

中位数只有 1.65%。 大盘股的空头仓位普遍很小——最高的 MRVL 也只有 4.35%。这个数之所以重要,是因为它决定了另一个数:回补天数(空头股数 ÷ 日均成交量)。中位数 2.61 天的意思是,就算所有空头同时想跑,按平时的成交量也就堵两三天。空头仓位小、回补天数短的股票,轧不起来。

第二,每天有多少成交是卖空。 FINRA 每天公布一个文件,里面是每只股票当天的卖空成交量:

files = sorted(glob.glob("data/finra/CNMSshvol*.txt"))
recent = [f for f in files if "CNMSshvol2026" in f]
raw = pd.concat([pd.read_csv(f, sep="|").dropna(subset=["Symbol"]) for f in recent], ignore_index=True)
raw = raw[raw["TotalVolume"] > 0].copy()
raw["卖空占比"] = raw["ShortVolume"] / raw["TotalVolume"]
liquid = raw[raw["TotalVolume"] > 1e6]
print(f"最近 {len(recent)} 个交易日、{len(raw):,} 个「股票 × 日」:卖空占比中位数 {raw['卖空占比'].median():.1%}")
print(f"只看当天成交超过 100 万股的({len(liquid):,} 个):中位数 {liquid['卖空占比'].median():.1%},"
      f"5%–95% 区间 {liquid['卖空占比'].quantile(.05):.1%}{liquid['卖空占比'].quantile(.95):.1%}")
for symbol in ["AAPL", "SPY", "TSLA", "NVDA", "GME"]:
    s = raw[raw["Symbol"] == symbol]["卖空占比"]
    print(f"  {symbol}: 中位数 {s.median():.1%},最低 {s.min():.1%},最高 {s.max():.1%}")
最近 64 个交易日、776,024 个「股票 × 日」:卖空占比中位数 48.9%
只看当天成交超过 100 万股的(75,980 个):中位数 50.2%,5%–95% 区间 24.7%–73.8%
  AAPL: 中位数 47.6%,最低 32.9%,最高 63.3%
  SPY: 中位数 56.6%,最低 39.1%,最高 74.2%
  TSLA: 中位数 48.1%,最低 35.6%,最高 66.3%
  NVDA: 中位数 36.8%,最低 27.7%,最高 48.1%
  GME: 中位数 61.0%,最低 41.3%,最高 79.8%

卖空占成交的一半,是常态。 中位数 50.2%,SPY 甚至有 56.6%。第一次看到这个数的人常常得出「市场极度看空」的结论——那是误读:这里面的大部分是做市商的对冲。你从做市商手里买走 100 股,他手上就少了 100 股,他会同时在别处卖空补平;这笔卖空和「看空」没有关系(第 2 篇:每一笔成交的对手方不一定有观点)。

⚠️ 这个文件只覆盖 FINRA 三个交易报告设施上的成交,不含交易所自己撮合的部分,所以它是这部分成交里的占比,不是全市场的占比。

第三,轧空长什么样。

GME:11 个交易日 +1,642%

2021 年 1 月,游戏驿站(GME)。我们不讲故事,只看价格和那个 1 倍杠杆的空单:

squeeze = [f for f in files if "CNMSshvol2021" in f]
gme_short = pd.concat([pd.read_csv(f, sep="|").dropna(subset=["Symbol"]) for f in squeeze], ignore_index=True)
gme_short = gme_short[gme_short["Symbol"] == "GME"].copy()
gme_short.index = pd.to_datetime(gme_short["Date"].astype(str), format="%Y%m%d")
gme = D.load_nasdaq_daily("data/nasdaq/GME_historical.json")
table = gme.loc["2021-01-12":"2021-02-02", ["open", "high", "low", "close"]].copy()
table["日涨幅"] = table["close"].pct_change()
table["卖空占比"] = gme_short["ShortVolume"] / gme_short["TotalVolume"]
print("⚠️ Nasdaq 的价格按拆股调整过:GME 2022-07-22 一拆四,所以这里的价格是当时的四分之一。")
print(table.round(3).to_string())
start, peak = gme.at[pd.Timestamp("2021-01-12"), "close"], gme.at[pd.Timestamp("2021-01-27"), "close"]
print(f"\n2021-01-12 收盘 {start:.3f}(当时 {start * 4:.2f} 美元)到 2021-01-27 收盘 {peak:.3f}"
      f"(当时 {peak * 4:.2f} 美元):11 个交易日 {peak / start - 1:+.0%}")
print(f"做空的人:收益 {-(peak / start - 1):.0%}。不是亏了 {1 - start / peak:.0%}"
      f"(那是价格从 {peak:.2f} 跌回 {start:.2f} 要走的幅度),是亏掉本金的 {peak / start - 1:.1f} 倍")
for leverage in [1, 2, 3]:
    level = K.liquidation_price(start, leverage, "short")
    after = gme.loc["2021-01-13":]
    hit = after[after["high"] >= level]
    print(f"  {leverage} 倍空单(强平价 {level:.3f}):{hit.index[0].date()} 就到了,"
          f"离开仓 {gme.index.get_loc(hit.index[0]) - gme.index.get_loc(pd.Timestamp('2021-01-12'))} 个交易日")
⚠️ Nasdaq 的价格按拆股调整过:GME 2022-07-22 一拆四,所以这里的价格是当时的四分之一。
              open     high     low   close    日涨幅   卖空占比
date                                                     
2021-01-12   4.990    5.100   4.829   4.988    NaN  0.545
2021-01-13   5.105    9.662   5.008   7.850  0.574  0.532
2021-01-14   9.522   10.765   8.262   9.978  0.271  0.316
2021-01-15   9.622   10.188   8.502   8.875 -0.110  0.323
2021-01-19  10.388   11.380   9.160   9.840  0.109  0.330
2021-01-20   9.342   10.298   9.015   9.780 -0.006  0.345
2021-01-21   9.808   11.188   9.250  10.758  0.100  0.324
2021-01-22  10.648   19.189  10.580  16.253  0.511  0.342
2021-01-25  24.182   39.795  15.282  19.198  0.181  0.379
2021-01-26  22.140   37.500  20.050  36.995  0.927  0.331
2021-01-27  88.708   95.000  62.250  86.878  1.348  0.544
2021-01-28  66.250  120.750  28.062  48.400 -0.443  0.508
2021-01-29  94.928  103.495  62.500  81.250  0.679  0.540
2021-02-01  79.140   80.500  53.000  56.250 -0.308  0.545
2021-02-02  35.190   39.500  18.555  22.500 -0.600  0.550

2021-01-12 收盘 4.987(当时 19.95 美元)到 2021-01-27 收盘 86.877(当时 347.51 美元):11 个交易日 +1642%
做空的人:收益 -1642%。不是亏了 94%(那是价格从 86.88 跌回 4.99 要走的幅度),是亏掉本金的 16.4 倍
  1 倍空单(强平价 9.935):2021-01-14 就到了,离开仓 2 个交易日
  2 倍空单(强平价 7.451):2021-01-13 就到了,离开仓 1 个交易日
  3 倍空单(强平价 6.624):2021-01-13 就到了,离开仓 1 个交易日

GME 2021 年 1 月

  • 2021-01-12 收盘 4.99(当时是 19.95 美元,后来 2022 年一拆四),到 2021-01-27 收盘 86.88(当时 347.51 美元):11 个交易日 +1,642%
  • 做空的人:−1,642%。不是亏了 94%(那是价格从 86.88 跌回 4.99 要走的幅度),是亏掉本金的 16.4 倍
  • 1 倍杠杆的空单,两个交易日就被强平(强平价 9.94,1 月 14 日盘中就到了)。2 倍和 3 倍是第二天。

第三节说「亏掉本金的 207 倍现实中不会发生」,这里就是那个「不会发生」的机制:你在亏到 16 倍之前,早就被强制平仓了。

下面那张卖空占比的图还有一个反直觉的细节:暴涨那几天,GME 的卖空占比反而从 54% 掉到 32%。 轧空的时候空头不是在卖,是在抢着买回来还车——正是这些买单把价格推得更高(第 2 篇的被迫交易者)。


六、永续合约这边:保证金与强平价

交易所的底线:分层维持保证金

回到自行车:邻居的底线是「押金不能低于多少」。交易所的底线叫维持保证金,它不是一个固定比例,而是按仓位大小分层的:

print(brackets.to_string(index=False))
tier = K.margin_tier(100_000, brackets)
print(f"\n名义价值 10 万美元落在第一档:维持保证金率 {tier['维持保证金率']:.2%},速算额 {tier['速算额']},"
      f"最大杠杆 {tier['最大杠杆']}x")
print(f"维持保证金 = 100,000 × {tier['维持保证金率']:.3f}{tier['速算额']} = "
      f"{K.maintenance_margin(100_000, brackets):,.0f} 美元")
        下限         上限  维持保证金率       速算额  最大杠杆
         0     300000  0.0040         0   150
    300000     800000  0.0050       300   100
    800000    3000000  0.0065      1500    75
   3000000   12000000  0.0100     12000    50
  12000000   70000000  0.0200    132000    25
  70000000  100000000  0.0250    482000    20
 100000000  230000000  0.0500   2982000    10
 230000000  480000000  0.1000  14482000     5
 480000000  600000000  0.1250  26482000     4
 600000000  800000000  0.1500  41482000     3
 800000000 1200000000  0.2500 121482000     2
1200000000 1800000000  0.5000 421482000     1

名义价值 10 万美元落在第一档:维持保证金率 0.40%,速算额 0.0,最大杠杆 150.0x
维持保证金 = 100,000 × 0.004 − 0.0 = 400 美元

三件事:

  1. 维持保证金率和你选的杠杆无关,只和名义价值有关。 10 万美元的仓位,不管你用 10 倍还是 100 倍杠杆,维持保证金率都是 0.4%。
  2. 仓位越大,维持保证金率越高,从 0.4% 一路升到 50%。这不是惩罚,是交易所在给自己留平仓的空间:1,200 万美元的仓位在市场上砸出去,冲击比 10 万的大得多(第 20 篇量过价格冲击)。
  3. 速算额是为了让档位之间连续。 名义价值正好 30 万时,按第一档算是 30 万 × 0.4% = 1,200;按第二档算是 30 万 × 0.5% − 300 = 1,200。两边一样,中间不会跳。

强平价的公式

强平价就是「权益正好等于维持保证金」的那个价格。把这句话写成等式:

保证金 + 方向 × 数量 × (价格 − 进场价) = 数量 × 价格 × 维持保证金率 − 速算额

左边是权益(本金加浮动盈亏),右边是底线。两边同除以数量,再用「保证金占名义价值的比例」表示,解出来就是:

强平价 = 进场价 × (方向 − 保证金比例 − 速算额比例) ÷ (方向 − 维持保证金率)

方向是 +1(做多)或 −1(做空)。代进去第一节那个仓位:

强平价 = 39,495.35 × (−1 − 0.1) ÷ (−1 − 0.004) = 43,271.80(比进场价高 9.562%)

所以选项 A 和 B 都错了,而且错的方向一样——都高估了你剩下的空间

天真算法一:10 倍杠杆就是反向 10% 爆仓          43444.88
天真算法二:剩下的保证金比例 ÷ 10,从现在的价格往上量    43509.47
真实强平价                            43271.80
从 42,631.59 算起,三种说法分别是还能涨 1.91%、2.06%、1.50%

为什么天真算法一(反向 10%)会高估?因为它忘了 0.4% 的维持保证金不是你的:你的保证金能承受的亏损是 10% − 0.4%(按名义价值算),不是 10%。

为什么天真算法二(剩余保证金 ÷ 10)也高估?因为做空时,价格涨上去的同时你的名义价值也在变大——你欠的是一辆车,车越贵,你欠得越多,10 倍杠杆这个比例在悄悄变。

仓位越大,强平价越近

       名义价值  维持保证金率  这一档允许的最大杠杆 用的杠杆     维持保证金  空单强平价(进场价 100)   要涨多少
    100,000  0.0040         150  10x     400.0        109.5618 0.0956
    500,000  0.0050         100  10x    2200.0        109.5124 0.0951
  2,000,000  0.0065          75  10x   11500.0        109.3641 0.0936
 10,000,000  0.0100          50  10x   88000.0        109.0297 0.0903
 50,000,000  0.0200          25  10x  868000.0        108.1020 0.0810
150,000,000  0.0500          10  10x 4518000.0        106.6552 0.0666

同样是 10 倍杠杆的空单,10 万美元的仓位要涨 9.56% 才爆,1.5 亿美元的仓位涨 6.66% 就爆。规模本身就是风险

同样的杠杆,空单先爆

  杠杆   多单强平价     要跌    空单强平价     要涨  空单近多少(个百分点)
  1x  0.0000 1.0000 199.2032 0.9920       0.7968
  2x 50.2008 0.4980 149.4024 0.4940       0.3968
  3x 66.9344 0.3307 132.8021 0.3280       0.2635
  5x 80.3213 0.1968 119.5219 0.1952       0.1568
 10x 90.3614 0.0964 109.5618 0.0956       0.0768
 20x 95.3815 0.0462 104.5817 0.0458       0.0368
 25x 96.3855 0.0361 103.5857 0.0359       0.0288
 50x 98.3936 0.0161 101.5936 0.0159       0.0128
100x 99.3976 0.0060 100.5976 0.0060       0.0048
125x 99.5984 0.0040 100.3984 0.0040       0.0032

看第一行:1 倍多单的强平价是 0 ——全额保证金做多,只有标的归零才会被平掉,实际上不会发生。1 倍空单的强平价是 +99.2%——价格翻一倍你就出局了。

这就是第三节那堵墙的另一面:做多的亏损天然被「价格跌不到负数」挡住,做空没有这个保护,所以同样的杠杆,空单的强平价总是更近一点(10 倍时近 0.077 个百分点)。差距不大,但方向是确定的。


七、资金费率:做空的持仓成本,常常是负数

自行车要付租金。永续合约的对应物是资金费率:每 8 小时结算一次,费率为正时多头付给空头,为负时反过来。它的用途是把永续合约的价格拽回现货附近(机制是第 25 篇的主题,这一篇只关心它是多少钱)。

print(funding.describe(percentiles=[.01, .05, .5, .95, .99]).round(6).to_string())
yearly = funding.groupby(funding.index.year)
print("\n按年:")
print(pd.DataFrame({"结算次数": yearly.size(), "平均费率": yearly.mean(),
                    "折成年化(×3×365)": yearly.mean() * 3 * 365,
                    "为正的比例": yearly.apply(lambda s: (s > 0).mean()),
                    "最高一次": yearly.max(), "最低一次": yearly.min()}).round(5).to_string())
cumulative = funding.cumsum()
rows = []
for days in [1, 7, 30, 90, 365]:
    step = days * 3
= (cumulative.shift(-step) - cumulative).dropna()          # 费率为正时多头付、空头收
    rows.append({"持有天数": days, "起点数": len(收), "空头收到的中位数": 收.median(), "平均": 收.mean(),
                 "5% 分位": 收.quantile(.05), "95% 分位": 收.quantile(.95),
                 "空头是收钱的比例": (收 > 0).mean()})
print("\n做空持有 N 天,资金费一共收到多少(占名义价值的比例):")
print(pd.DataFrame(rows).round(4).to_string(index=False))
      结算次数     平均费率  折成年化(×3×365)    为正的比例     最高一次     最低一次
time                                                        
2020  1098  0.00016       0.17193  0.85701  0.00300 -0.00300
2021  1095  0.00028       0.30608  0.92694  0.00249 -0.00090
2022  1095  0.00004       0.04165  0.77900  0.00010 -0.00119
2023  1095  0.00007       0.07866  0.89863  0.00055 -0.00011
2024  1098  0.00011       0.11924  0.91621  0.00088 -0.00011
2025  1095  0.00005       0.05126  0.87123  0.00010 -0.00012
2026   729  0.00002       0.02618  0.71468  0.00010 -0.00015

资金费率

 持有天数  起点数  空头收到的中位数     平均   5% 分位  95% 分位  空头是收钱的比例
    1 7302    0.0002 0.0003 -0.0001  0.0014    0.8783
    7 7284    0.0013 0.0023 -0.0004  0.0093    0.9023
   30 7215    0.0055 0.0097 -0.0008  0.0412    0.9234
   90 7035    0.0163 0.0284  0.0041  0.0996    0.9807
  365 6210    0.0838 0.1204  0.0360  0.3225    1.0000

结论很干脆:

  • 86% 的结算里,费率是正的,也就是多头付钱给空头。
  • 做空持有一年,7,305 次结算里挑任何一个起点,资金费都是收入——6,210 个起点,100% 为正,中位数 +8.4%,平均 +12.0%。2021 年最夸张,折成年化 30.6%
  • 反过来说,做多永续合约要付这笔钱。你拿着一个 10 倍杠杆的多单一年,光资金费就吃掉保证金的 80% 以上。

这是加密和美股最不一样的地方之一:在美股,做空要付租金;在永续合约,通常是做多的人在付租金。原因不难理解——想加杠杆做多的人比想做空的人多,合约价格就挂在现货上方,资金费率把这个溢价收走。

顺便,第一节那个决策点当天:

决策点那一天 16:00 的结算费率是 0.00249,历史第 2 高

那个 16:00 的结算,是 Binance BTCUSDT 历史上第二高的费率。多头在那一天付了不少钱——只不过第一节那个空单没能撑到 16:00。


八、强平价必须在止损价之外

这一节是第 23 篇和这一篇的接口,也是这一篇最该记住的一条。

第 23 篇说:止损是你自己决定认输的地方。 这一篇说:强平是交易所替你决定的地方。

如果强平价比止损价更近,那么你的止损永远不会被触发——在它生效之前,你的仓位已经被交易所平掉了。你以为自己冒的风险是 1 个 R,实际上你冒的是全部保证金。

所以顺序必须是这样的:

先按图定止损距离 → 再算这个距离容得下多大杠杆 → 最后才选杠杆。

反过来(先定杠杆再找止损)就是让交易所替你做交易决定。

def max_leverage(stop_distance: float, side: str = "long", mmr: float = 0.004,
                 cushion: float = 0.0) -> float:
    """止损距离是 `stop_distance`(占进场价的比例)时,强平价还在止损价之外的最大杠杆。

    强平价必须在止损价**之外**:止损是你自己认输的地方,强平是交易所替你认输的地方,
    后者先到的话,你的止损就是一句空话(第 23 篇)。`cushion` 是额外留出的余量,
    比如 0.02 表示「强平价还要再远 2 个百分点」,用来盖住滑点和持仓期间的资金费。
    """
    if side not in ("long", "short"):
        raise ValueError(f"side 只能是 long 或 short,收到 {side!r}")
    d = stop_distance + cushion
    gap = 1 - (1 - d) * (1 - mmr) if side == "long" else (1 + d) * (1 + mmr) - 1
    if gap <= 0:
        return np.inf
    return 1 / gap

用第 15 篇的 3 ATR 止损算一遍:

  标的  3 ATR 止损距离中位数  多单最大安全杠杆  空单最大安全杠杆  空单·留 2 个点余量  止损最宽的 5% 时  那时的空单最大杠杆
 SPY          0.033    27.420    27.226       17.603       0.073     12.928
AAPL          0.062    15.115    15.002       11.529       0.115      8.403
 BTC          0.132     7.389     7.331        6.391       0.297      3.306
  • SPY:3 ATR 止损大约 3.3%,最大安全杠杆 27 倍。
  • AAPL:6.2%,15 倍。
  • **BTC:13.2%,7.3 倍。**留 2 个百分点的余量(盖住滑点和资金费)之后只剩 6.4 倍。而且止损最宽的那 5% 的日子,距离要 29.7%,那时最大杠杆只有 3.3 倍

反过来看这张表更直观:

  杠杆  多单止损最远  空单止损最远
  2x  0.4980  0.4940
  3x  0.3307  0.3280
  5x  0.1968  0.1952
 10x  0.0964  0.0956
 20x  0.0462  0.0458
 25x  0.0361  0.0359
 50x  0.0161  0.0159
100x  0.0060  0.0060

10 倍杠杆的空单,止损最远只能放到 9.56%。 BTC 上一个正常的 3 ATR 止损(13.2%)根本放不进去——也就是说,在 BTC 上用 10 倍杠杆做波段,你的止损必然在强平价之外,它是个摆设。

这不是说 10 倍杠杆不能用,而是说:用 10 倍杠杆,你的止损就只能放 9% 以内,那对应的是日内的止损距离,不是波段的。杠杆和时间尺度是绑在一起的。


九、动手:talab.risk 的杠杆部分

第 23 篇建的 talab.risk 加上第四部分。

查档

def margin_tier(notional: float, brackets: pd.DataFrame) -> pd.Series:
    """名义价值落在哪一档:返回这一档的维持保证金率、速算额和允许的最大杠杆。

    `brackets` 是 `data.load_binance_brackets` 读出来的表。档位是按**名义价值**分的,
    和你选的杠杆无关:仓位越大,交易所要求的维持保证金率越高。
    """
    row = brackets[(brackets["下限"] < notional) & (notional <= brackets["上限"])]
    if row.empty:
        row = brackets.iloc[[0]] if notional <= brackets["上限"].iloc[0] else brackets.iloc[[-1]]
    return row.iloc[0]


def maintenance_margin(notional: float, brackets: pd.DataFrame) -> float:
    """维持保证金 = 名义价值 × 这一档的维持保证金率 − 这一档的速算额。

    速算额是为了让档位之间连续:跨到下一档时,只有超出的部分按新费率算。
    """
    tier = margin_tier(notional, brackets)
    return notional * tier["维持保证金率"] - tier["速算额"]

强平价

def liquidation_price(entry_price: float, leverage: float, side: str = "long",
                      mmr: float = 0.004, amount_ratio: float = 0.0, spent: float = 0.0,
                      notional: float | None = None, brackets: pd.DataFrame | None = None) -> float:
    """逐仓仓位的强平价:价格走到这里,账户权益正好等于维持保证金。

    推导(数量 Q、方向 s = +1 做多 / −1 做空、保证金 WB、名义价值 N = Q × 进场价):

    > WB + s × Q × (P − 进场价) = Q × P × 维持保证金率 − 速算额

    左边是权益,右边是维持保证金。两边同除以 Q,用 wb = WB ÷ N 表示保证金占名义价值的比例、
    用 amount_ratio = 速算额 ÷ N 表示速算额,解出来就是

    > 强平价 = 进场价 × (s − wb − amount_ratio) ÷ (s − 维持保证金率)

    `spent` 是已经从保证金里扣掉的东西(手续费、累计资金费),按占名义价值的比例给。
    给了 `notional` 和 `brackets` 就自动查档,`mmr` 和 `amount_ratio` 由表决定。

    ⚠️ 档位按名义价值分,而名义价值随价格变化。这里用**开仓时**的档位算,
    仓位在一档之内时是精确的。
    ⚠️ 触发强平的是**标记价格**,不是最新成交价(第 22 篇)。
    """
    if side not in ("long", "short"):
        raise ValueError(f"side 只能是 long 或 short,收到 {side!r}")
    if brackets is not None:
        if notional is None:
            raise ValueError("要查档就得给名义价值 notional")
        tier = margin_tier(notional, brackets)
        mmr, amount_ratio = float(tier["维持保证金率"]), float(tier["速算额"]) / notional
    s = 1.0 if side == "long" else -1.0
    wb = 1.0 / leverage - spent                       # 保证金占名义价值的比例
    return max(entry_price * (s - wb - amount_ratio) / (s - mmr), 0.0)

两个刻意的限制写在文档字符串里:档位按开仓时的名义价值取(仓位跨档时要重新取一次),以及触发强平的是标记价格(第 22 篇)。

历史重放和持仓成本

def replay_liquidation(df: pd.DataFrame, leverage: float, side: str = "long", horizon: int = 30,
                       mmr: float = 0.004, prices: pd.DataFrame | None = None) -> pd.Series:
    """历史重放:每一根 K 线的开盘价开一个 `leverage` 倍的仓,`horizon` 根之内有没有被强平。

    `prices` 给标记价格时用它判断强平(第 22 篇),不给就用 `df` 自己的最高最低价。
    最后 `horizon` 根没有完整的观察窗口,返回 NaN。
    """
    judge = df if prices is None else prices.reindex(df.index)
    entry = df["open"].to_numpy(float)
    level = liquidation_price(1.0, leverage, side, mmr) * entry
    if side == "long":
        worst = judge["low"].rolling(horizon, min_periods=horizon).min().shift(-horizon + 1)
        hit = worst.to_numpy() <= level
    else:
        worst = judge["high"].rolling(horizon, min_periods=horizon).max().shift(-horizon + 1)
        hit = worst.to_numpy() >= level
    out = pd.Series(hit, index=df.index, dtype="float64")
    out.iloc[-(horizon - 1):] = np.nan
    return out


def funding_cost(funding: pd.Series, start, end, side: str = "long") -> float:
    """一段持仓期间的资金费,按占名义价值的比例算,**正数表示你付出去**。

    资金费率为正时多头付给空头,为负时反过来。这里把每次结算的费率直接相加
    (没有按名义价值的变化加权),和交易所实际扣的钱会差一点。
    """
    if side not in ("long", "short"):
        raise ValueError(f"side 只能是 long 或 short,收到 {side!r}")
    window = funding.loc[(funding.index > start) & (funding.index <= end)]
    return float(window.sum()) * (1.0 if side == "long" else -1.0)

数据下载

talab.data 这一篇加了四个函数:资金费率、分层表、FINRA 卖空量、Nasdaq 空头仓位。分层表那个特别值得说一句——它是公开接口,不需要账号

def download_binance_brackets(dest: str = "data/binance") -> Path:
    """下载 U 本位合约的分层维持保证金表(第 24 篇),原样保存 JSON。

    这是**今天**的表:档位的名义价值上限、维持保证金率和最大杠杆都会被交易所调整,
    拿它去算几年前的某一笔仓位,算的是「按今天的规则会怎样」。
    """
    path = Path(dest) / "brackets.json"
    path.parent.mkdir(parents=True, exist_ok=True)
    path.write_bytes(_fetch("https://www.binance.com/bapi/futures/v1/friendly/future/common/brackets"))
    return path

⚠️ 下 FINRA 的文件时踩了一个坑:休市那天没有文件,而 FINRA 返回的是 403 而不是 404(2026-06-19 Juneteenth 就是这样),所以两个状态码都要当成「这天没有」:

def download_finra_short_volume(days: list[str], dest: str = "data/finra") -> list[Path]:
    """下载 FINRA 每日卖空成交量文件(第 24 篇),days 是 "YYYY-MM-DD" 的列表。

    ⚠️ 这是 FINRA 三个交易报告设施(TRF)上的成交,不含交易所撮合的部分,
    所以「卖空占比」是这部分成交里的占比,不是全市场的占比。周末和假日没有文件。
    ⚠️ 市场休市那天没有文件,FINRA 对不存在的文件返回 **403 而不是 404**
    (2026-06-19 Juneteenth 就是这样),所以两个状态码都当成「这天没有」。
    """
    folder = Path(dest)
    folder.mkdir(parents=True, exist_ok=True)
    paths = []
    for day in days:
        stamp = day.replace("-", "")
        path = folder / f"CNMSshvol{stamp}.txt"
        if not path.exists():
            try:
                path.write_bytes(_fetch(f"https://cdn.finra.org/equity/regsho/daily/CNMSshvol{stamp}.txt"))
            except urllib.error.HTTPError as e:
                if e.code in (403, 404):            # 周末、假日:没有这个文件
                    continue
                raise
            time.sleep(0.5)                         # 对公开服务器客气一点
        paths.append(path)
    return paths

还有一个数据体检的发现:

⚠️ 标记价格比最新价少 13,015 分钟,整整缺了 9 天:['2021-07-01', '2021-07-24', '2021-07-25', '2021-07-26', '2021-07-27', '2022-07-31', '2022-10-02', '2023-02-24', '2026-06-29']

标记价格的历史文件整整缺了 9 天。第 22 篇做的是分钟级的比较,没碰到;这一篇要在日线上按天重放,就必须先把两边对齐——否则滚动窗口里一旦混进缺失值,样本就悄悄变了(这也是为什么第十一节的三个持有期要用同一批起点)。

测试

新增 7 个,全部手算:

def test_margin_tier_and_maintenance_margin():
    assert K.margin_tier(10_000, BRACKETS)["维持保证金率"] == pytest.approx(0.004)
    assert K.margin_tier(500_000, BRACKETS)["最大杠杆"] == 100
    assert K.maintenance_margin(10_000, BRACKETS) == pytest.approx(40.0)
    assert K.maintenance_margin(500_000, BRACKETS) == pytest.approx(500_000 * 0.005 - 300)
    # 速算额的作用是让档位之间连续:在 300,000 这个边界上,两档算出来一样
    assert K.maintenance_margin(300_000, BRACKETS) == pytest.approx(1200.0)
    assert K.maintenance_margin(300_001, BRACKETS) == pytest.approx(1200.005)
def test_short_is_liquidated_before_long_at_the_same_leverage():
    """同样的杠杆,空单的强平距离比多单近——名义价值朝着不利方向一起变大。"""
    for leverage in [2, 5, 10, 25]:
        long_gap = 1 - K.liquidation_price(100.0, leverage, "long") / 100
        short_gap = K.liquidation_price(100.0, leverage, "short") / 100 - 1
        assert short_gap < long_gap
def test_max_leverage_keeps_the_stop_inside():
    for side, sign in [("long", -1), ("short", 1)]:
        leverage = K.max_leverage(0.10, side)
        stop = 100 * (1 + sign * 0.10)
        level = K.liquidation_price(100.0, leverage, side)
        assert level == pytest.approx(stop)                     # 正好卡在止损价上
        assert abs(K.liquidation_price(100.0, leverage * 1.1, side) - 100) < abs(stop - 100)
    assert K.max_leverage(0.10, "short", cushion=0.05) < K.max_leverage(0.10, "short")
217 passed in 0.84s
185 passed, 32 skipped in 0.94s

十、揭晓

答案是 C:比两个天真算法都近。

  • 天真算法一(反向 10%):还能涨 1.91%
  • 天真算法二(剩余保证金 ÷ 10):还能涨 2.06%
  • 真实:还能涨 1.50%

真实的空间比你以为的少了四分之一。而在 2021 年 2 月 8 日的那个中午,1.5% 是三分钟的事:

最新价先越过强平价:2021-02-08 12:57:00+00:00;标记价格越过:2021-02-08 12:58:00+00:00(强平看的是标记价格,第 22 篇)
被强平之后,最新价回到 12:55 那个水平的时间:2021-02-08 13:10:00+00:00(12 分钟后,收盘 42,606.20)
但第二天 2021-02-09 标记价格最高 48,237.05,10 倍空单无论如何都撑不过去

58 分钟

12:58,标记价格越过 43,271.80,仓位被平掉。 从 12:00 开仓算起,58 分钟

两个细节:

  • 最新成交价 12:57 就越过了,标记价格 12:58 才越过。 强平看的是标记价格(第 22 篇),所以这一分钟是白送的——但也只有一分钟。
  • 12 分钟之后,价格回到了 42,606,比决策点那一刻还低。仓位如果还在,浮亏和 12:55 一样。

这就是杠杆真正的代价:它决定的不是你能赚多少,是你能错多久。 同一个判断,不同的杠杆撑的时间完全不一样:

 杠杆        强平价     要涨           被强平的时刻               撑了多久
 1x 78675.9960 0.9920 2024-11-10 04:53 1370 days 16:53:00
 2x 59006.9970 0.4940 2021-03-13 10:13   32 days 22:13:00
 3x 52450.6640 0.3280 2021-02-17 20:22    9 days 08:22:00
 5x 47205.5976 0.1952 2021-02-09 00:04    0 days 12:04:00
10x 43271.7978 0.0956 2021-02-08 12:58    0 days 00:58:00
20x 41304.8979 0.0458 2021-02-08 12:50    0 days 00:50:00
25x 40911.5179 0.0359 2021-02-08 12:47    0 days 00:47:00
50x 40124.7580 0.0159 2021-02-08 12:44    0 days 00:44:00
  • 50 倍撑了 44 分钟,10 倍撑了 58 分钟,5 倍撑到了第二天凌晨。
  • 3 倍撑了 9 天,2 倍撑了 33 天。
  • 1 倍(全额保证金,没有杠杆)撑了 3 年 9 个月——到 2024 年 11 月 10 日,BTC 涨到 78,676,连全额保证金的空单也被平掉了。

最后这一行才是第三节那堵墙的完整含义:做空的亏损没有下限,所以任何杠杆下的空单,只要拿得够久,都会被平掉。 区别只是多久。

顺带说一句,那 12 分钟的回撤容易让人得出「我只是运气不好」的结论。但第二天 2021-02-09 标记价格最高到了 48,237——10 倍空单无论如何都撑不过第二天。被强平不是运气,是这个仓位本来就撑不住。


十一、爆仓概率:历史重放

一笔交易说明不了问题。把它做成统计:在 BTC 的每一根 K 线开盘都开一个仓,看 N 根之内有没有被强平。触发用标记价格。

hour = B.resample_ohlcv(last, "1h")
mark_hour = B.resample_ohlcv(mark.assign(volume=0.0), "1h")
for label, df, judge, horizons in [("BTC 1 小时线", hour, mark_hour, [24, 24 * 7, 24 * 30]),
                                   ("BTC 日线", um_day, mark_day, [7, 30, 90])]:
    rows = []
    both = df.index.intersection(judge.index)          # 标记价格缺的那些天,两边都去掉
    df, judge = df.loc[both], judge.loc[both]
    keep = df.index[:-(max(horizons))]                 # 所有持有期用同一批起点,才能比
    for leverage in [2, 3, 5, 10, 20, 25, 50, 100]:
        row = {"杠杆": f"{leverage}x"}
        for horizon in horizons:
            for side, word in [("long", "多"), ("short", "空")]:
                hit = K.replay_liquidation(df, leverage, side, horizon=horizon, prices=judge)
                row[f"{horizon} 根内 · {word}"] = hit.reindex(keep).mean()
        rows.append(row)
    print(f"\n--- {label}{len(keep):,} 个起点)---")
    print(pd.DataFrame(rows).round(4).to_string(index=False))
--- BTC 日线(2,336 个起点)---
  杠杆  7 根内 · 多  7 根内 · 空  30 根内 · 多  30 根内 · 空  90 根内 · 多  90 根内 · 空
  2x    0.0030    0.0000     0.0133     0.0385     0.0625     0.2277
  3x    0.0068    0.0051     0.0492     0.1207     0.1665     0.3694
  5x    0.0377    0.0471     0.1824     0.2701     0.3908     0.5124
 10x    0.1742    0.2175     0.4469     0.5659     0.6571     0.7526
 20x    0.4615    0.5021     0.7033     0.7817     0.8014     0.8814
 25x    0.5488    0.5873     0.7530     0.8262     0.8301     0.9088
 50x    0.7740    0.8121     0.8746     0.9191     0.9088     0.9568
100x    0.9097    0.9199     0.9491     0.9645     0.9640     0.9829

爆仓概率与波动拖累

  • 10 倍杠杆持有 30 天:多单 44.7% 被强平,空单 56.6%。 不是「运气不好的时候」,是一半以上。
  • 10 倍持有 90 天:65.7% / 75.3%。
  • 2 倍持有 90 天:6.3% / 22.8%。 空单是多单的三倍多——BTC 这九年在涨,这个差别全是趋势给的。
  • 100 倍持有 7 天:91.0% / 92.0%。 小时线上更直接:100 倍持有 24 小时,75.3% 会被强平。
  • 每一个杠杆、每一个持有期,空单的爆仓率都高于多单。

这些数字有一个前提要说清楚:这是「在随机的时刻开仓,一直不管」的概率。真实的交易有止损、有出场规则,不会放着不动。但它给出了一个有用的下限直觉——如果你的止损在强平价之外(第八节),那么这张表就是你的实际爆仓概率。


十二、L 倍杠杆不是 L 倍收益

最后一件事,和强平无关,但每个用杠杆的人都会撞上。

直觉是:2 倍杠杆 = 2 倍收益。把每天的收益乘以 2 再连乘起来,看看是不是:

rows = []
for name, (df, periods) in MARKETS.items():
    ret = df["close"].pct_change().dropna()
    years = len(ret) / periods
    plain = (df["close"].iloc[-1] / df["close"].iloc[0]) ** (1 / years) - 1
    for leverage in [1, 2, 3, 5]:
        daily = 1 + leverage * ret
        if (daily <= 0).any():
            rows.append({"标的": name, "杠杆": f"{leverage}x", "年化": np.nan,
                         "如果杠杆真能放大收益": plain * leverage,
                         "备注": f"有一天就亏光了({ret.min():.1%} × {leverage})"})
            continue
        rows.append({"标的": name, "杠杆": f"{leverage}x", "年化": daily.prod() ** (1 / years) - 1,
                     "如果杠杆真能放大收益": plain * leverage, "备注": ""})
print(pd.DataFrame(rows).round(4).to_string(index=False))
  标的 杠杆     年化  如果杠杆真能放大收益                  备注
 SPY 1x 0.1346      0.1346                    
 SPY 2x 0.2456      0.2691                    
 SPY 3x 0.3221      0.4037                    
 SPY 5x 0.3368      0.6728                    
AAPL 1x 0.2888      0.2888                    
AAPL 2x 0.5254      0.5775                    
AAPL 3x 0.6555      0.8663                    
AAPL 5x 0.4772      1.4438                    
 BTC 1x 0.3794      0.3794                    
 BTC 2x 0.1578      0.7588                    
 BTC 3x    NaN      1.1382 有一天就亏光了(-39.5% × 3)
 BTC 5x    NaN      1.8970 有一天就亏光了(-39.5% × 5)
  • SPY:1 倍 13.5%,2 倍 24.6%(不是 26.9%),5 倍 33.7%(不是 67.3%)。
  • BTC:1 倍 37.9%,2 倍只有 15.8%——比不加杠杆还差得多。3 倍和 5 倍直接亏光,因为 2020 年 3 月 12 日那天 BTC 跌了 39.5%,乘以 3 就是 −118%。

原因是波动拖累:涨 10% 再跌 10% 不等于回到原点,而是 −1%;杠杆把这个缺口按平方放大。写成公式(用对数收益):

每天再平衡的 L 倍仓位,年化 ≈ L × μ − L² × σ² ÷ 2

前一项随 L 线性增长,后一项随 L 平方增长。两者拉平的地方就是最优杠杆:

对数收益下,每天再平衡的杠杆仓位年化 ≈ L × μ − L² × σ² ÷ 2,最优杠杆是 μ ÷ σ²:
  SPY: μ = 0.126,σ = 0.181 → μ ÷ σ² = 3.87
  AAPL: μ = 0.254,σ = 0.291 → μ ÷ σ² = 3.00
  BTC: μ = 0.322,σ = 0.677 → μ ÷ σ² = 0.70

BTC 的 μ ÷ σ² 是 0.70——最优杠杆小于 1。 一个九年年化 38% 的标的,最适合它的杠杆是不到一倍

(这条 μ ÷ σ² 就是凯利公式的连续版本,第 26 篇会从赌局那一头再推一遍。)


十三、小检查

  1. 你以 100 美元开一个 20 倍杠杆的单,维持保证金率 0.5%。强平价是多少?换成空单呢?
  2. 一个 10 倍杠杆的空单,名义价值从 10 万加到 1.5 亿,其他都不变。强平价会变吗?往哪个方向变?
  3. 你的策略止损距离是 8%,想用 15 倍杠杆。这个组合有什么问题?
  4. 「最近 GME 有 61% 的成交是卖空的,说明大家都在看空。」这句话错在哪里?
  5. BTC 九年年化 38%,为什么 2 倍杠杆的年化只有 15.8%?

十四、常见误用

1. 把杠杆当成仓位。 「我用 10 倍杠杆」说的是保证金占名义价值的比例,不是你冒了多少风险。风险是止损距离 × 名义价值(第 23 篇的一个 R)。同样 10 倍杠杆,止损放 1% 和放 9%,风险差九倍。

2. 先定杠杆,再找止损。 顺序反了。止损距离由图和波动率决定(第 23 篇第四节),杠杆是它的结果。先定杠杆就是让强平价替你决定认输的位置。

3. 以为「反向 10% 才爆仓」。 维持保证金、手续费、已结算的资金费都先从保证金里扣。第十节那个仓位,真实空间比「10% − 浮亏」少了四分之一。

4. 忘了强平看的是标记价格。 第 22 篇量过:极端分钟里两套价格能差 7.5%。你盯着最新成交价算强平,可能提前也可能延后。

5. 以为全仓比逐仓安全。 全仓模式下账户里所有的钱都是这个仓位的保证金,强平价确实更远——代价是爆的时候爆的是整个账户,不是这一笔的保证金。逐仓把亏损关在一个盒子里,全仓把整个账户押上。两者不是「安全 / 危险」,是「先小亏一次 / 可能一次亏完」。

6. 做空时用「跌到 0 能赚多少」估算收益,用同一把尺子估算亏损。 收益那一边有墙(最多 100%),亏损那一边没有。第三节的数据:864 个合约里,持有 250 天亏掉本金一倍以上的占 8.8%。

7. 在永续合约上长期做空,忘了资金费是收入。 86% 的结算是多头付钱。这不改变方向判断,但它意味着做空的持仓成本通常是负的——和美股正好相反。反过来,长期持有杠杆多单,资金费是实打实的成本


十五、小结

  1. 做空是卖借来的东西。 你欠的是标的本身,不是当初卖出的那笔钱。
  2. 收益上面有一堵 100% 的墙,亏损没有底。 864 个永续合约、持有 250 天:8.8% 的样本做空会亏掉本金一倍以上,最差的一笔是 −207 倍。
  3. 「下跌比上涨快」分市场:SPY 上下跌段每天走 0.49%、上涨段 0.18%(2.79 倍);BTC 反过来,上涨段每天 1.48%、下跌段 0.74%。做空 BTC,亏损来得比盈利快。
  4. 美股的空头仓位普遍很小:市值前 100 里中位数占总股本 1.65%、回补天数 2.61 天。轧空需要的是「仓位大 + 回补天数长」,大盘股两条都不满足。
  5. 卖空占成交一半是常态(中位数 50.2%),大部分是做市商对冲,不是看空信号。
  6. 维持保证金是分层的,按名义价值分,和你选的杠杆无关;速算额让档位之间连续。10 万美元的仓位维持保证金率 0.4%,1.5 亿的是 5%。
  7. 强平价 = 进场价 × (方向 − 保证金比例 − 速算额比例) ÷ (方向 − 维持保证金率),触发它的是标记价格。
  8. 同样的杠杆,空单的强平价更近;1 倍多单永远不会被强平,1 倍空单在 +99.2% 就会。
  9. 永续合约的资金费率 86% 的时候是多头付给空头:做空持有一年,6,210 个起点全部是收入,中位数 +8.4%。
  10. 强平价必须在止损价之外。 BTC 的 3 ATR 止损是 13.2%,对应最大杠杆 7.3 倍(留余量后 6.4 倍);10 倍杠杆的止损最远只能放 9.56%。
  11. 爆仓概率对杠杆高度非线性:BTC 日线上,10 倍持有 30 天有 44.7%(多)/ 56.6%(空)被强平;100 倍持有 24 小时是 75.3%。
  12. L 倍杠杆不是 L 倍收益:波动拖累按 L² 增长,BTC 的最优杠杆 μ ÷ σ² 只有 0.70。

练习

  1. 换一个方向:把第十一节的重放改成 SPY 和 AAPL(用日线,不设标记价格)。同样 5 倍杠杆持有 30 天,美股和 BTC 的爆仓率差多少?
  2. 把资金费算进强平价liquidation_pricespent 参数就是为这个准备的。一个 20 倍杠杆的多单,按 2021 年的平均费率持有 30 天,强平价往哪边移多少?
  3. 全仓和逐仓:逐仓的保证金是这一笔的,全仓是整个账户的。假设账户里还有 9 万美元闲钱,第一节那个仓位改成全仓,强平价变成多少?它还会在 12:58 被平掉吗?
  4. 跨档:开一个 2,000 万美元名义价值的空单,价格涨 20% 之后名义价值到了 2,400 万,跨进了下一档。用 margin_tier 重新取一次,强平价比一开始算的近多少?
  5. 轧空的条件:用第 19 篇的筛选器,从 Nasdaq 上市的股票里找出「空头仓位占总股本最高 + 回补天数最长」的二十只,统计它们过去一年的最大单月涨幅,和同样市值的对照组比。
  6. 借券费的替代指标:借券费率没有公开数据,但 FINRA 的卖空占比、空头仓位、回补天数都有。这三个里哪一个和「这只股票难借」的关系最直接?设计一个检验。
  7. 最优杠杆的稳定性:把第十二节的 μ ÷ σ² 按年算一遍(每年一个数),看看它有多稳。你敢按哪一年的数字下注?

小检查答案

  1. 多单:100 × (1 − 0.05) ÷ (1 − 0.005) = 95.48(跌 4.52%);空单:100 × (−1 − 0.05) ÷ (−1 − 0.005) = 104.48(涨 4.48%)。空单近 0.04 个百分点。
  2. 会变,而且变近。1.5 亿落在维持保证金率 5% 的那一档,强平价从 +9.56% 变成 +6.66%。顺便,那一档允许的最大杠杆只有 10 倍——想开更大的仓,杠杆也上不去。
  3. 15 倍杠杆的空单强平价在 +6.24%,而你的止损在 8%。强平先到,止损是摆设:你以为自己冒 1 个 R 的风险,实际上冒的是全部保证金。要么把杠杆降到 11.9 倍以下(max_leverage(0.08, "short") 算出来是 11.86),要么把止损收到 6% 以内——但后者是让交易所替你改交易规则。
  4. 因为大部分卖空是做市商的对冲,不是方向性的看空。全市场中位数就有 50.2%,SPY 是 56.6%——总不能说市场天天在看空 SPY。要看方向性的仓位,得看两周一次的空头仓位和回补天数。
  5. 波动拖累。BTC 九年的 σ 是 0.677,L² × σ² ÷ 2 这一项在 L = 2 时就吃掉 0.46 的年化对数收益,而 L × μ 只加了 0.32。最优杠杆 μ ÷ σ² = 0.70 < 1,所以 2 倍已经在山的另一边了。