| 本篇位置 | 第五部分「从信号到交易」最后一篇。第 23 篇定了「亏多少算看错」,第 24 篇量了杠杆怎么把你赶出场,这一篇填最后一格:这一笔买多少 |
| 用到的数据 | SPY、AAPL、BTC 日线(主线策略的 106 笔交易)、11 只行业 ETF、704 个还在交易的 USDT 永续合约日线 |
| 动手 | 新模块 talab.size:position_size、size_by_atr、simulate、kelly、optimal_f、recovery、underwater、vol_target、effective_bets、combined_risk 等 15 个函数,附 18 个测试 |
| 读完你能 | 把「一笔最多亏账户的百分之几」翻译成金额;说清凯利公式给的那个数为什么没人敢用;算出一篮子东西的有效独立仓位数 |
一、先做一个决定
2020 年 10 月 10 日,主线策略(第 21 篇的 v3)在 BTC 上发出买入信号:50 日均线在 200 日均线上方,当天收盘创了 20 日新高。按规则,你在下一根开盘以 11,050.64 买入,3 倍 ATR 的吊灯止损落在 10,126.43——从进场价往下 8.36%。
你的账户是 10 万美元。
现在只剩一个问题没答:买多少。

四个候选摆在这里:
| 买法 | 买入金额 | 占账户 | 数量 | 被止损时亏 | 亏掉账户的 |
|---|---|---|---|---|---|
| A 满仓 | 100,000 | 100% | 9.0492 BTC | 8,363 | 8.36% |
| B 固定金额 1 万 | 10,000 | 10% | 0.9049 BTC | 836 | 0.84% |
| C 固定风险 1% | 11,957 | 11.96% | 1.0820 BTC | 1,000 | 1.00% |
| D 固定风险 10% | 100,000 | 100% | 9.0492 BTC | 8,363 | 8.36% |
买入日 2020-10-10,开盘价买入 11,050.64,初始止损 10,126.43
一个 R = 924.21,止损距离 = 8.3634%;账户 100,000 美元
买法 买入金额 占账户 数量(BTC) 被止损时亏 亏掉账户的
A 满仓 100000.0000 1.0000 9.0492 -8363.3859 -0.0836
B 固定金额 1 万 10000.0000 0.1000 0.9049 -836.3386 -0.0084
C 固定风险 1% 11956.8799 0.1196 1.0820 -1000.0000 -0.0100
D 固定风险 10% 100000.0000 1.0000 9.0492 -8363.3859 -0.0836
注意 A 和 D:这一笔上它们一模一样。10% 的风险预算除以 8.36% 的止损距离等于 1.196 倍账户,不借钱就封顶在满仓,实际风险退回 8.36%。
选哪个?先别翻到后面——这一篇会先把四种方法在同一串交易上都跑一遍,最后再回来说这一笔的结局。
二、打个比方:配电箱
把账户想成家里的配电箱。
- 每条支路上的保险丝=止损。第 23 篇已经把它装好了:这台电器出事,最多烧掉这一条线。
- 电器的功率=仓位。一台 2,000 瓦的空调和一盏 5 瓦的灯,接在同一条线上,跳闸的概率完全不是一回事。
- 总闸的额定电流=你的风险预算。所有支路加起来不能超过它。
- 跳闸=大回撤。合闸容易,把所有设备重新启动起来慢得多——这就是第七节的回撤数学。
- 几个插座其实接在同一根干线上=相关性。你以为有五条独立支路,跳的时候一起跳。
这篇要讲的全部内容,就是这一句话:保险丝的额定电流再准,你给一条 16 安的线接上 40 安的电器,跳的就不是支路而是总闸。止损和仓位必须一起定,只定一个等于没定。
三、把「买多少」变成一条公式
三种最常见的写法
固定金额:每笔都买 1 万美元。简单,但它不复利——账户从 10 万涨到 100 万,你还在买 1 万。
固定比例:每笔都买账户的 10%。会复利了,但它没有看止损距离:同样买 10%,止损放 3% 的那笔亏 0.3%,止损放 20% 的那笔亏 2%,差了将近七倍。
固定风险:先定「这一笔最多亏账户的百分之几」,再倒推该买多少。
仓位金额 = 账户 × 每笔风险比例 ÷ 止损距离
推导只有一步:被止损时亏掉的钱 = 仓位金额 × 止损距离。你要这个数等于「账户 × 风险比例」,
仓位金额 × 止损距离 = 账户 × 每笔风险比例
两边除以止损距离,就得到上面那条式子。整条公式里没有任何对行情的判断——它只是把你已经做过的两个决定(账户能亏多少、止损放在哪)算成一个数。
一个逐步算例
账户 10 万美元,某只股票现价 200 美元,止损放在 194 美元(距离 3%),这一笔最多亏账户的 1%:
- 这笔允许亏多少钱:100,000 × 1% = 1,000 美元
- 每一美元的仓位会亏多少:止损距离 3%,所以每买 1 美元,被止损时亏 0.03 美元
- 该买多少钱:1,000 ÷ 0.03 = 33,333 美元
- 换成股数:33,333 ÷ 200 = 166.7 股
仓位金额 33333.333333
占账户 0.333333
止损时亏 1000.0
实际风险 0.01
有没有封顶 False
数量 166.666667
换成决策点那一笔(止损距离 8.36%):
同一笔,止损距离换成 8.36%(2020-10-10 那笔):
仓位金额 11956.879879
占账户 0.119569
止损时亏 1000.0
实际风险 0.01
有没有封顶 False
数量 1.082008
⚠️ 止损距离小到一定程度,这条公式会要求你借钱。止损只有 0.5% 时,1% 的风险对应两倍账户的仓位;不借钱就得封顶,这时你实际冒的风险比你定的小,函数会明确告诉你这件事:
止损距离只有 0.5% 时,1% 的风险要借钱才够——不借钱就封顶,实际风险变成 0.5%:
仓位金额 100000.0
占账户 1.0
止损时亏 500.0
实际风险 0.005
有没有封顶 True
「按 ATR 调整仓位」是同一条公式
交易书里常见另一种写法:仓位数量 = 账户 × 风险比例 ÷ (k × ATR)。它听上去像另一条规则——「波动大就少买」——其实只是把止损距离换了个写法:止损放在进场价下方 k 倍 ATR 时,止损距离就等于 k × ATR ÷ 价格,代进去就是同一条式子。
按 ATR 的等价写法(进场前一天的 14 日 ATR = 308.07,3 倍 = 924.21):
仓位金额 11956.879879
占账户 0.119569
止损时亏 1000.0
实际风险 0.01
有没有封顶 False
数量 1.082008
同一笔交易,1.082008 个 BTC,两种写法一个字都不差。「波动大就少买」不是另加的一条规则,是这条公式的自动结果:ATR 翻倍,止损距离翻倍,仓位减半。
四、满仓时,「一笔能亏多少」不是你定的
这一节是整篇最容易被跳过、也最关键的一段。
主线策略 v3 的仓位那一格填的是「满仓」。满仓意味着仓位金额 = 账户,代进公式反过来看:
这一笔亏掉账户的比例 = 止损距离
也就是说,满仓时「一笔最多亏多少」这个数完全由市场决定:止损距离是 3 倍 ATR 算出来的,ATR 是行情给的。你没有投票权。
那这个数波动有多大?
标的 交易数 止损距离中位数 最小 最大 最大÷最小 1% 风险对应的仓位
SPY 39 0.0362 0.0195 0.0608 3.1215 0.2759
AAPL 37 0.0659 0.0357 0.1431 4.0066 0.1518
BTC 30 0.1082 0.0639 0.2623 4.1061 0.0924
⚠️ 满仓时,「这一笔最多亏账户的百分之几」= 止损距离,是市场替你定的;BTC 上它在 6.4% 和 26.2% 之间跳
- SPY:2.0% 到 6.1%,差 3.1 倍
- AAPL:3.6% 到 14.3%,差 4.0 倍
- BTC:6.4% 到 26.2%,差 4.1 倍
BTC 上最狠的那一笔,满仓被止损要亏掉账户的四分之一;最温和的那一笔只亏 6.4%。这两笔交易在信号上没有任何区别,都是「金叉 + 收盘创 20 日新高」,区别只在于进场那几天的 ATR。
反过来,这张表最后一列说的是另一件事:同样冒 1% 的风险,在 SPY 上要买账户的 27.6%,在 AAPL 上买 15.2%,在 BTC 上只买 9.2%。三个市场的仓位差了三倍,而你定的风险是同一个数。这就是固定风险仓位真正的用处——它让不同标的、不同时期的交易可比。
五、同一串交易,四种仓位方法
现在把主线策略在三个标的上的全部交易(SPY 39 笔、AAPL 37 笔、BTC 30 笔)拿出来,信号一个字不改,只换仓位方法,看资金曲线长什么样。

标的 仓位方法 期末账户 最差一笔 最好一笔 平均仓位 逐笔最大回撤
SPY 满仓 133610.0348 -0.0571 0.1675 1.0000 -0.0917
SPY 固定金额 1 万 103318.9809 -0.0057 0.0165 0.0984 -0.0091
SPY 固定比例 10% 103329.0869 -0.0057 0.0168 0.1000 -0.0094
SPY 固定风险 1% 111216.9560 -0.0122 0.0531 0.2930 -0.0267
SPY 固定风险 10% 133610.0348 -0.0571 0.1675 1.0000 -0.0917
AAPL 满仓 275164.8627 -0.0814 0.4535 1.0000 -0.2109
AAPL 固定金额 1 万 112213.5818 -0.0072 0.0447 0.0928 -0.0201
AAPL 固定比例 10% 112718.1224 -0.0081 0.0454 0.1000 -0.0226
AAPL 固定风险 1% 117905.1620 -0.0123 0.0637 0.1590 -0.0368
AAPL 固定风险 10% 254619.8934 -0.0814 0.4535 0.9919 -0.2109
BTC 满仓 462901.1788 -0.2384 2.0814 1.0000 -0.3520
BTC 固定金额 1 万 128839.0722 -0.0238 0.2015 0.0840 -0.0288
BTC 固定比例 10% 130344.4234 -0.0238 0.2081 0.1000 -0.0364
BTC 固定风险 1% 136771.9941 -0.0120 0.2489 0.0914 -0.0355
BTC 固定风险 10% 581817.7139 -0.1202 2.0814 0.8072 -0.2454
四件事值得看:
一、固定金额和固定比例几乎一样。 SPY 上期末账户 103,319 对 103,329,AAPL 上 112,214 对 112,718。十年下来复利没拉开差距,因为这条策略的总收益本来就不大,账户没怎么变。在收益更高的序列上差距会显出来——BTC 上是 128,839 对 130,344。
二、固定风险 1% 在这里是一个很小的仓位。 BTC 上平均只买账户的 9.1%,十年把 10 万变成 13.7 万。这不是「固定风险不好」,而是 1% 这个数,对「一次只有一笔仓」的策略来说太小了——第六节专门讲这件事。
三、右边那张图才是重点。 它画的是「进场那一刻就知道的数」:这一笔被止损时会亏掉账户的百分之几。满仓那条黑线在 6.4% 和 26.2% 之间乱跳,固定风险 1% 是一条贴着 1% 的直线,固定风险 10% 是一条 10% 的直线(只在止损距离本来就小于 10% 时才低于 10%,那是封顶)。
仓位方法唯一确定决定的东西,就是这条线的形状。 年化、回撤、期末账户都是它的后果。
四、BTC 上「固定风险 10%」比满仓赚得多。 期末 581,818 对 462,901,最差一笔 −12.0% 对 −23.8%,逐笔最大回撤 −24.5% 对 −35.2%。更小的风险换来了更高的收益,这不是错觉,下一节会说清它为什么会这样。
六、风险预算该定多大
「每笔风险 1%」是交易书里最常见的数字。它从哪来的?
它默认的场景是:你同时持有很多笔仓位。1% × 几笔并行 × 几笔连亏,加起来才是一个像样的回撤。但主线策略是一次只有一笔仓的策略,1% 在它身上意味着什么?
把风险预算从 0.25% 一路调到 20%,看年化和回撤怎么变:
--- SPY ---
风险预算 年化 最大回撤 年化波动 平均仓位 封顶的比例
每笔风险 0.25% 0.0027 -0.0089 0.0053 0.0749 0.0000
每笔风险 0.50% 0.0053 -0.0177 0.0105 0.1497 0.0000
每笔风险 1.00% 0.0105 -0.0353 0.0210 0.2994 0.0000
每笔风险 2.00% 0.0204 -0.0702 0.0419 0.5965 0.0968
每笔风险 5.00% 0.0285 -0.1228 0.0738 0.9879 0.9040
每笔风险 10.00% 0.0297 -0.1235 0.0753 1.0000 1.0000
每笔风险 20.00% 0.0297 -0.1235 0.0753 1.0000 1.0000
满仓 0.0297 -0.1235 0.0753 1.0000 1.0000
--- AAPL ---
风险预算 年化 最大回撤 年化波动 平均仓位 封顶的比例
每笔风险 0.25% 0.0040 -0.0124 0.0059 0.0393 0.0000
每笔风险 0.50% 0.0080 -0.0247 0.0118 0.0786 0.0000
每笔风险 1.00% 0.0158 -0.0490 0.0236 0.1571 0.0000
每笔风险 2.00% 0.0313 -0.0964 0.0472 0.3142 0.0000
每笔风险 5.00% 0.0743 -0.2300 0.1149 0.7651 0.1596
每笔风险 10.00% 0.0982 -0.2785 0.1504 0.9803 0.9345
每笔风险 20.00% 0.1075 -0.2785 0.1542 1.0000 1.0000
满仓 0.1075 -0.2785 0.1542 1.0000 1.0000
--- BTC ---
风险预算 年化 最大回撤 年化波动 平均仓位 封顶的比例
每笔风险 0.25% 0.0064 -0.0118 0.0073 0.0246 0.0000
每笔风险 0.50% 0.0129 -0.0236 0.0145 0.0491 0.0000
每笔风险 1.00% 0.0257 -0.0467 0.0290 0.0982 0.0000
每笔风险 2.00% 0.0512 -0.0919 0.0580 0.1964 0.0000
每笔风险 5.00% 0.1259 -0.2182 0.1451 0.4911 0.0000
每笔风险 10.00% 0.2153 -0.3231 0.2543 0.8690 0.5851
每笔风险 20.00% 0.1901 -0.4365 0.2941 0.9912 0.9565
满仓 0.1855 -0.4769 0.3007 1.0000 1.0000

三条规律,每一条都有用:
一、封顶之前,年化几乎和风险预算成正比。 BTC 上 0.25% 对应年化 0.64%,0.5% 对应 1.29%,1% 对应 2.57%,2% 对应 5.12%——每翻一倍,年化也翻一倍,回撤也翻一倍。这段区间里,「每笔风险 X%」不是一条风险规则,它就是一个杠杆倍数:你只是决定了用账户的百分之几去跑这条策略,剩下的放着不动。
所以「一次只有一笔仓的策略 + 每笔风险 1%」这个组合,翻译过来是「我拿账户的十分之一来做这条策略」。这话本身没错,但你要知道自己说的是这个,而不是「我的风险控制得很好」。
二、封顶之后,形状变了。 SPY 的止损距离最大才 6.1%,所以风险预算超过 6.1% 之后每一笔都封顶在满仓,曲线彻底压平——10%、20%、满仓三行一模一样。AAPL 从 10% 起基本封顶。
三、BTC 上出现了一个驼峰。 10% 时年化 21.53%、回撤 −32.31%;再往上加到 20%,年化掉到 19.01% 而回撤扩到 −43.65%;满仓是 18.55% 和 −47.69%。
年化在某个下注比例上见顶,再加就掉头向下。
这不是 BTC 的特性,是复利的算术。第 24 篇量过同一件事的另一面:每天再平衡的 L 倍仓位,年化大约是 L·μ − L²σ²/2,L 太大时那个平方项会把收益吃光,BTC 的最优杠杆 μ÷σ² 只有 0.70。这里的驼峰是同一条抛物线——只不过这一次,收缩仓位的那只手不是固定杠杆,是止损距离:ATR 大的时候(也就是 σ 大的时候)仓位自动缩小,正好缩在该缩的地方。
那么风险预算到底该定多少?这条曲线本身不能回答——它只告诉你年化和回撤是捆在一起卖的。能回答的是你自己:你打算让一笔交易亏掉账户的百分之几。 定下这个数,剩下的都是算术。第十五节会用这个办法给主线策略定一个数。
七、回撤的数学:为什么少亏比多赚值钱
上一节把「年化」和「回撤」并排放,好像它们地位相当。其实不是。
亏掉的钱和赚回来的钱不对称:
回本需要的涨幅 = 回撤 ÷ (1 − 回撤)
回撤 要涨回来
0.050 0.0526
0.100 0.1111
0.200 0.2500
0.300 0.4286
0.500 1.0000
0.700 2.3333
0.832 4.9524

亏 10% 涨 11.1% 就回来了,差别不大。亏 50% 要翻倍。亏 83%(BTC 2018 年那次)要涨 488%。曲线在 40% 之后开始陡峭,在 70% 之后几乎垂直。
这条式子解释了一件常被说反的事:降低回撤不是「用收益换安全」,在复利上它本身就是收益。同样是 2 倍的波动,涨一倍再跌一半回到原点,跌一半再涨一倍也回到原点——但如果跌的那一半深到 83%,你需要的上涨就不是「一倍」而是「五倍」。
深度只是一半,另一半是时长:
标的 在水下的时间 水下段数 最长一段(天) 最长那段最深 跌超 20% 的段数 这些段恢复天数中位数
SPY 0.8607 175 745 -0.2536 3 220.0
AAPL 0.8957 114 514 -0.3091 5 297.0
BTC 0.9679 50 1073 -0.8319 10 152.0
BTC 最长的一段:2017-12-17 → 2020-11-24,1073 天,最深 -83.2%,要涨 495% 才回本
三个数字值得记住:
- 在水下的时间:SPY 86.1%、AAPL 89.6%、BTC 96.8%。 买入持有的日子里,绝大多数时候你的账户都低于历史最高点。「一直创新高」是资金曲线图给人的错觉,那条线只有百分之几的日子在最高点上。
- BTC 那一段:2017-12-17 到 2020-11-24,1,073 天。 将近三年,账户最深的时候只剩本金的 16.8%。
- SPY 最长一段 745 天(2022-01-04 到 2024-01-19),最深 −25.4%。跌超 20% 的三段,恢复用了 220、180、745 天。
回到配电箱:跳闸本身是一瞬间的事,难的是之后——冰箱要重新制冷、电脑要重启、你得检查是哪条线出的问题。总闸跳得越狠,恢复得越慢,而且慢得不成比例。
八、凯利公式:它说的数你不敢用
上一节留了个问题:下注比例该定多大。有一条公式号称能回答它。
公式
一次赌局,赢的概率是 p,赢的时候赚 b 份、输的时候亏 1 份。凯利公式说,每次押上本金的这个比例,长期增长最快:
f* = 胜率 − 败率 ÷ 盈亏比
抛硬币赔率 2 比 1、胜率 60%:f* = 0.6 − 0.4 ÷ 2 = 0.4,押四成。胜率 50%、赔率 1 比 1:f* = 0,没有优势就不该下注。
它最大化的不是期望收益。期望收益永远是「押得越多越好」——押满就是期望最大。凯利最大化的是对数收益的期望,也就是长期的复利增长率。这个区别不是数学洁癖:押满的那个策略,只要有一次输光,后面赚多少都没用。
放到交易上,公式里的「押上本金的比例」对应的是这一笔冒的风险占账户的比例,也就是第三节那个 f,不是仓位金额。
它给出的数字
把主线策略的 R 倍数(第 23 篇的记分方式)代进去:
标的 笔数 胜率 平均盈利 平均亏损 盈亏比 凯利公式 f* 网格最优 f*
SPY 39 0.4359 1.4296 0.6039 2.3672 0.1976 0.21
AAPL 37 0.5135 1.5714 0.7170 2.1917 0.2916 0.29
BTC 30 0.4333 3.5814 0.7194 4.9786 0.3195 0.27
⚠️ 这是「每笔冒账户 27%–32% 的风险」,不是「仓位 27%」——BTC 上它对应的名义仓位早就顶到满仓了
两点公式(0.198 / 0.292 / 0.320)和在网格上直接搜出来的最优值(0.21 / 0.29 / 0.27)对得很近。这说明这三串交易的形状确实像一次赌局——虽然第 23 篇量过它们的右尾很长(BTC 上两成的交易贡献一半的 R)。
然后看看这个数是多少:BTC 上每一笔冒账户 32% 的风险。按 10.8% 的中位止损距离算,那是三倍账户的仓位。

左边那条曲线的形状值得盯一会儿。它在 f* 处见顶,然后掉下来,在大约 2f* 处穿过零——下注两倍凯利,长期增长率为零。AAPL 上双倍凯利跑 37 笔之后期末是本金的 0.9655 倍(对数增长 −0.0009,理论值 0),路上最深回撤 −97.84%。
你估不准那个 f*
凯利公式里的胜率和盈亏比是真值。你只有估计值,而且是从 30 来笔交易估出来的。
把同样的 30 笔重抽 2,000 次(有放回),每次重新算最优下注比例:
--- SPY(39 笔)全样本 f* = 0.210
重抽 2000 次:中位数 0.215,90% 区间 [0.005, 0.495],有 10.5% 的重抽说「别下注」
前半段 f* = 0.215,后半段 f* = 0.210;拿前半段的 f* 去跑后半段,每笔对数增长 +0.0255,后半段自己的最优是 +0.0255
下注比例 f 期末是本金的几倍 每笔对数增长 最差一笔 逐笔最大回撤
全凯利 0.2100 2.8213 0.0266 -0.2563 -0.4773
半凯利 0.1050 2.2164 0.0204 -0.1282 -0.2608
四分之一凯利 0.0525 1.6167 0.0123 -0.0641 -0.1358
双倍凯利 0.4200 1.2124 0.0049 -0.5126 -0.7791
每笔 1% 0.0100 1.1122 0.0027 -0.0122 -0.0267
每笔 10% 0.1000 2.1641 0.0198 -0.1221 -0.2494
--- AAPL(37 笔)全样本 f* = 0.290
重抽 2000 次:中位数 0.290,90% 区间 [0.040, 0.530],有 3.2% 的重抽说「别下注」
前半段 f* = 0.405,后半段 f* = 0.110;拿前半段的 f* 去跑后半段,每笔对数增长 -0.0345,后半段自己的最优是 +0.0060
下注比例 f 期末是本金的几倍 每笔对数增长 最差一笔 逐笔最大回撤
全凯利 0.2900 8.4947 0.0578 -0.3562 -0.7438
半凯利 0.1450 5.2310 0.0447 -0.1781 -0.4533
四分之一凯利 0.0725 2.7334 0.0272 -0.0891 -0.2482
双倍凯利 0.5800 0.9655 -0.0009 -0.7125 -0.9784
每笔 1% 0.0100 1.1791 0.0045 -0.0123 -0.0368
每笔 10% 0.1000 3.6268 0.0348 -0.1228 -0.3310
--- BTC(30 笔)全样本 f* = 0.270
重抽 2000 次:中位数 0.265,90% 区间 [0.010, 0.540],有 5.1% 的重抽说「别下注」
前半段 f* = 0.345,后半段 f* = 0.190;拿前半段的 f* 去跑后半段,每笔对数增长 +0.0155,后半段自己的最优是 +0.0331
下注比例 f 期末是本金的几倍 每笔对数增长 最差一笔 逐笔最大回撤
全凯利 0.2700 14.9578 0.0902 -0.3246 -0.6800
半凯利 0.1350 9.1329 0.0737 -0.1623 -0.4079
四分之一凯利 0.0675 4.3802 0.0492 -0.0812 -0.2228
双倍凯利 0.5400 2.8137 0.0345 -0.6493 -0.9467
每笔 1% 0.0100 1.3677 0.0104 -0.0120 -0.0355
每笔 10% 0.1000 6.6011 0.0629 -0.1202 -0.3164
BTC 的 90% 区间是 [0.010, 0.540]——五十多倍的范围,而且有 5.1% 的重抽直接说「别下注」。SPY 更夸张:区间 [0.005, 0.495],10.5% 的重抽说别下注。
样本外更直接。把每串交易从中间切开:
- AAPL:前半段 f* = 0.405,后半段 f* = 0.110。拿前半段那个 0.405 去跑后半段,每笔对数增长 −0.0345——亏的,而后半段自己的最优是 +0.0060。
- BTC:前半段 0.345,后半段 0.190。用前半段的数跑后半段,增长率 +0.0155,只有后半段最优(+0.0331)的不到一半。
- SPY:0.215 对 0.210,这次碰巧对上了。
这不是「凯利公式错了」。公式没错,错的是把估计值当真值代进去。而且这个错误是不对称的:f 比真值小一点,你只损失一点增长率(曲线在顶点附近很平);f 比真值大一点,你掉进曲线右半边,那里陡得多。
所以大家用半凯利
看三张小表里「半凯利」那一行:
| 标的 | 全凯利增长率 | 半凯利增长率 | 保留 | 全凯利回撤 | 半凯利回撤 |
|---|---|---|---|---|---|
| SPY | 0.0266 | 0.0204 | 77% | −47.73% | −26.08% |
| AAPL | 0.0578 | 0.0447 | 77% | −74.38% | −45.33% |
| BTC | 0.0902 | 0.0737 | 82% | −68.00% | −40.79% |
下注减半,增长率只掉两成,回撤砍掉四成多。 这就是「分数凯利」的全部理由:曲线顶点附近很平,你在左边站着几乎不损失什么,而右边的悬崖很陡。四分之一凯利更保守,增长率还剩一半左右,回撤只有全凯利的三分之一。
⚠️ 但请注意上面那些回撤数字——半凯利在 AAPL 上的逐笔回撤仍然是 −45%。凯利公式从头到尾没有「你受得了多深的回撤」这个输入,它只关心长期增长最快。回撤能不能扛得住,是你自己要加的第二个约束。
回到配电箱:全凯利就是把总闸的电流开到刚好等于额定值。理论上效率最高,实际上你根本不知道额定值是多少——铭牌上的数是你从三十次用电记录里估出来的。
九、波动率目标仓位
固定风险盯的是「一笔亏多少」。还有一种做法盯的是另一个数:账户每天晃多少。
仓位 = 目标波动率 ÷ 最近实际波动率
想让账户的年化波动稳定在 15%,而最近 20 天算出来的波动是 30%,那就只买半仓;波动降到 10%,就买 1.5 倍。它能成立的前提只有一条:
波动率可以预测,收益率不行。
先确认前提:波动率本身好不好预测(前 20 天的波动 vs 后 20 天的波动,秩相关)
SPY:0.561(同样算法下,收益的前后秩相关只有 -0.119)
AAPL:0.484(同样算法下,收益的前后秩相关只有 -0.017)
BTC:0.496(同样算法下,收益的前后秩相关只有 0.095)
前 20 天的波动和后 20 天的波动,秩相关 0.48 到 0.56;同样算法下收益的前后秩相关只有 −0.12 到 +0.10。这就是第 15 篇讲的波动聚集性——它是整个技术分析里少数几个「用历史估一个数、拿到未来还能用」的地方。
实测,带对照组
直接比「波动率目标」和「买入持有」不公平:它们的平均仓位不一样,波动也不一样。所以每一行后面都跟一个对照组:把买入持有按一个固定倍数放大或缩小,让它的实现波动和波动率目标那行完全相等,再比收益。
--- SPY ---
方法 年化 实现波动 最大回撤 平均仓位 仓位换手/年 夏普(无风险 0)
买入持有(1 倍) 0.1346 0.1801 -0.3410 1.0000 0.0000 0.7470
波动率目标 15%,20 天窗口 0.1532 0.1690 -0.2213 1.2885 13.6847 0.9063
对照:恒定 0.94 倍买入持有 0.1268 0.1690 -0.3226 0.9381 0.0000 0.7504
波动率目标 15%,60 天窗口 0.1267 0.1633 -0.2463 1.1532 4.4492 0.7762
对照:恒定 0.91 倍买入持有 0.1228 0.1633 -0.3130 0.9063 0.0000 0.7520
波动率目标 30%,20 天窗口 0.2756 0.3039 -0.3671 2.2500 12.9815 0.9070
对照:恒定 1.69 倍买入持有 0.2140 0.3039 -0.5230 1.6871 0.0000 0.7043
--- AAPL ---
方法 年化 实现波动 最大回撤 平均仓位 仓位换手/年 夏普(无风险 0)
买入持有(1 倍) 0.2888 0.2912 -0.3852 1.0000 0.0000 0.9915
波动率目标 15%,20 天窗口 0.2085 0.1724 -0.1990 0.6755 7.6313 1.2097
对照:恒定 0.59 倍买入持有 0.1740 0.1724 -0.2453 0.5919 0.0000 1.0093
波动率目标 15%,60 天窗口 0.1757 0.1615 -0.2100 0.6044 2.2913 1.0882
对照:恒定 0.55 倍买入持有 0.1631 0.1615 -0.2313 0.5544 0.0000 1.0104
波动率目标 30%,20 天窗口 0.4115 0.3421 -0.3654 1.3470 14.7991 1.2029
对照:恒定 1.17 倍买入持有 0.3354 0.3421 -0.4383 1.1745 0.0000 0.9806
--- BTC ---
方法 年化 实现波动 最大回撤 平均仓位 仓位换手/年 夏普(无风险 0)
买入持有(1 倍) 0.3794 0.6702 -0.8319 1.0000 0.0000 0.5661
波动率目标 15%,20 天窗口 0.1342 0.1838 -0.3043 0.3040 5.3849 0.7303
对照:恒定 0.27 倍买入持有 0.1430 0.1838 -0.3386 0.2742 0.0000 0.7778
波动率目标 15%,60 天窗口 0.1205 0.1619 -0.2793 0.2696 1.4759 0.7443
对照:恒定 0.24 倍买入持有 0.1269 0.1619 -0.3031 0.2415 0.0000 0.7839
波动率目标 30%,20 天窗口 0.2438 0.3676 -0.5316 0.6079 10.7698 0.6633
对照:恒定 0.55 倍买入持有 0.2626 0.3676 -0.5865 0.5484 0.0000 0.7145
结果分成清楚的两半:
| 标的 | 波动率目标 15%(20 天)夏普 | 同波动的恒定杠杆对照 | 差 |
|---|---|---|---|
| SPY | 0.906 | 0.750 | +0.156 |
| AAPL | 1.210 | 1.009 | +0.201 |
| BTC | 0.730 | 0.778 | −0.048 |
两只股票上,波动率目标在同样的实现波动下多赚 2.6 到 3.5 个百分点的年化;BTC 上反而少赚 0.9 个百分点。
回撤则是一边倒:九组(3 个标的 × 3 种目标)里波动率目标的回撤全部比对照组浅,BTC 上 −30.4% 对 −33.9%,SPY 上 −22.1% 对 −32.3%。削掉尾巴是它确定能做到的事,多赚不是。
为什么 BTC 上不行
按前 20 天的波动分五组,看之后一天的收益:
SPY:按前 20 天的波动分五组,之后一天的收益
count 折成年化 年化波动 收益÷波动
组
最低 20% 499 0.0448 0.0929 0.4818
2 498 0.1461 0.1084 1.3482
3 498 0.2360 0.1264 1.8660
4 498 0.1153 0.1771 0.6506
最高 20% 498 0.1817 0.3092 0.5877
最高波动组的收益是最低组的 4.06 倍,「收益÷波动」是 1.22 倍
AAPL:按前 20 天的波动分五组,之后一天的收益
count 折成年化 年化波动 收益÷波动
组
最低 20% 499 0.3209 0.2016 1.5917
2 498 0.3657 0.2275 1.6073
3 498 0.2341 0.2449 0.9556
4 498 0.3710 0.2923 1.2690
最高 20% 499 0.1947 0.4352 0.4474
最高波动组的收益是最低组的 0.61 倍,「收益÷波动」是 0.28 倍
BTC:按前 20 天的波动分五组,之后一天的收益
count 折成年化 年化波动 收益÷波动
组
最低 20% 657 0.3719 0.4318 0.8613
2 656 0.0568 0.5307 0.1071
3 656 0.3008 0.6694 0.4493
4 656 1.1284 0.6781 1.6640
最高 20% 656 0.8536 0.9314 0.9165
最高波动组的收益是最低组的 2.30 倍,「收益÷波动」是 1.06 倍
- AAPL:最高波动组之后的年化 19.5%,最低组 32.1%。高波动期赚得少,缩仓正好缩在该缩的地方——所以波动率目标在它身上效果最好(夏普 +0.20)。
- BTC:最高的两组年化 112.8% 和 85.4%,最低组 37.2%。BTC 的高波动期恰恰是涨出来的——牛市加速的时候波动最大。缩仓缩在赚钱的时候,当然吃亏。
- SPY:五组不单调(最低组反而最差),所以这条解释在 SPY 上并不完整。它的好处更多来自「把波动拉平」这件事本身,而不是避开了差的时段。
BTC 这一条和第 24 篇量过的事是同一件:用 ZigZag 把行情分段,「下跌比上涨快」只在股指上成立(SPY 下跌段每根走 0.49% 对上涨 0.18%,2.79 倍),而 BTC 是 0.50 倍——上涨更猛。波动率目标仓位的全部好处都建立在「波动大的时候是坏时候」上,BTC 上这个前提不成立。
⚠️ 还有一笔账这一节没算:20 天窗口的版本每年要把仓位翻动 5 到 15 次(表里的「仓位换手/年」:SPY 13.7、AAPL 7.6、BTC 5.4),每一次都是真实的买卖。成本留到第 28 篇算,但你现在就该知道,那张夏普表上的 +0.15 不是白拿的。
十、几笔交易其实是几笔
到这里为止,全篇讨论的都是一笔。真实账户里同时有好几笔。
风险不能相加
五笔交易,每笔都按「最多亏账户 1%」定的仓位。合起来这个账户一天冒多少风险?
不是 5%(那是它们同时全亏的情况),也不是 1%。正确的算法是:
合计风险 = 单笔风险 × sqrt(n + n(n−1)ρ)
其中 ρ 是两两之间的相关系数。
同时持有几笔 两两相关 0.0 两两相关 0.3 两两相关 0.6 两两相关 0.9 两两相关 1.0
1 0.0100 0.0100 0.0100 0.0100 0.01
2 0.0141 0.0161 0.0179 0.0195 0.02
3 0.0173 0.0219 0.0257 0.0290 0.03
5 0.0224 0.0332 0.0412 0.0480 0.05
10 0.0316 0.0608 0.0800 0.0954 0.10
每笔都只冒 1% 的风险,五笔同时持有、两两相关 0.6 时,账户一天冒的风险是 4.12%,不是 5%,也不是 1%
反过来:想让「同时五笔」合起来只冒 2% 的风险,两两相关 0.6 时每笔只能冒 0.485%,相关 0 时可以冒 0.894%
- ρ = 0(完全独立):五笔合计 2.24%,也就是教科书里的「开根号相加」
- ρ = 1(完全一样):五笔合计 5%,五笔其实是一笔,只是放大了五倍
- ρ = 0.6(真实世界常见的水平):4.12%——离 5% 比离 2.24% 近得多
最后这一行是重点。多数人以为分散会把风险除以 √5,实际上在相关 0.6 的世界里只除以 1.21。
反过来用更有意思:想让「同时五笔」合起来只冒 2% 的风险,两两相关 0.6 时每笔只能冒 0.485%;完全独立时可以冒 0.894%。 同一个总风险预算,能分给每一笔的额度差了将近一倍——差的那一截全是相关性吃掉的。
有效独立仓位数
把这个想法推到底,就有了一个直接能算的数:
N_eff = (Σw)² ÷ (wᵀ C w)
全都完全相关时它等于 1(买十个等于买一个),互不相关且等权时等于 n。它回答的是「我到底分散了没有」,而不是「我持有几个代码」。
加密成交额前 10(2022-12-30 选的):['BTCUSDT', 'ETHUSDT', 'DOGEUSDT', 'BNBUSDT', 'XRPUSDT', 'LTCUSDT', 'LINKUSDT', 'SOLUSDT', 'MASKUSDT', 'APEUSDT']
11 个行业 ETF:2070 天,两两相关 中位数 0.635(0.372 到 0.871),有效独立仓位数 1.53 / 11,第一主成分解释 65.9%
10 个成交额最大的币:1338 天,两两相关 中位数 0.575(0.410 到 0.813),有效独立仓位数 1.59 / 10,第一主成分解释 63.5%

11 个行业 ETF,有效独立仓位数 1.53。 能源、科技、公用事业、房地产——看上去是十一个不同的行业,两两相关的中位数 0.635,第一主成分解释了 65.9% 的方差。你以为买了十一样东西,账户体验到的接近 1.5 样。
10 个成交额最大的币,有效独立仓位数 1.59。 名单是 2022 年底按成交额定下来的(BTC、ETH、DOGE、BNB、XRP、LTC、LINK、SOL、MASK、APE),相关性在之后的三年半里量。中位相关 0.575,第一主成分 63.5%。
回到配电箱:这就是「五个插座其实接在同一根干线上」。你在墙上看到五个面板,分线盒里它们并成一根。跳闸的时候,它们一起跳。
十一、相关性真的会在下跌时上升吗
「平时分散得好好的,一到危机全都一起跌」——这句话几乎是共识。数据支持吗?
先看真实数据。11 个行业 ETF 的平均两两相关:
真实数据(2070 天):全样本 0.6185
SPY 跌超 2% 的 80 天:0.6946
SPY 涨超 2% 的 61 天:0.6397
|SPY| 最大的 10%(207 天):0.8455
- 全部日子:0.619
- SPY 跌超 2% 的 80 天:0.695
- SPY 涨超 2% 的 61 天:0.640
- SPY 当天涨跌幅绝对值最大的 10%(207 天):0.846
看起来结论很清楚:大波动的日子相关性从 0.62 飙到 0.85。
但这里有个陷阱。 「只挑市场波动最大的那些天再算相关」这个动作本身就会抬高相关系数,哪怕真实的相关矩阵一动不动。理由是:按「市场(也就是各成分的平均)走得远」来筛选,筛出来的必然是成分们步调一致的那些天。
要拆穿它,就造一个相关矩阵恒定不变的模拟世界:用真实的相关矩阵生成随机收益,然后对模拟数据执行完全一样的筛选,看筛出来的相关系数是多少。
对照(正态,相关矩阵不变):全样本 0.6196,最差的 80 天 0.1361,最好的 61 天 0.1254,|涨跌| 最大的 10% 0.8764
对照(t(4)(厚尾),相关矩阵不变):全样本 0.6202,最差的 80 天 0.3357,最好的 61 天 0.3410,|涨跌| 最大的 10% 0.7892
结果把这句共识劈成了两半:
| 挑法 | 真实数据 | 对照(正态) | 对照(t 分布,厚尾) | 结论 |
|---|---|---|---|---|
| 全部日子 | 0.619 | 0.620 | 0.620 | 基准对齐 ✓ |
| |涨跌| 最大的 10% | 0.846 | 0.876 | 0.789 | 全是挑出来的 ❌ |
| 跌超 2% | 0.695 | 0.136 | 0.336 | 真的 ✅ |
| 涨超 2% | 0.640 | 0.125 | 0.341 | 也真,但低一截 |
第一行结论:「大波动的日子相关性高」这件事,在一个相关性根本没变的模拟世界里同样出现,而且幅度更大(正态对照 0.876 > 真实 0.846)。这条观察里没有任何信息,它是筛选方式的产物。
第二行结论:「下跌的日子相关性高」是真的,而且很强。按单侧筛选(只挑最差的 80 天)在模拟世界里会把相关系数压低到 0.136(厚尾对照 0.336),因为你沿着一个方向切了一刀;而真实数据里它反而升到 0.695。真实和对照之间差了 0.36 到 0.56,这不是筛选能解释的。
而且它是不对称的:跌超 2% 的 0.695 比涨超 2% 的 0.640 高 0.055,而两个对照组的上下两侧几乎一样(0.136 对 0.125,0.336 对 0.341)。下跌那一侧是真的更抱团。
对仓位管理的含义很直接:你按平时的相关系数算出来的「合计风险」,在最需要它准的那些天是偏小的。上一节那张表里,如果平时的 ρ 是 0.6、下跌时变成 0.7,五笔各 1% 的合计风险就从 4.12% 变成 4.43%——不是天塌下来,但你该知道它往哪边偏。
⚠️ 这个对照组也有限制:模拟用的是正态和 t 分布,它们的尾部形状和真实收益不完全一样。所以上面两行的方向是可靠的(一个是纯筛选效应、一个不是),具体数值不必当真。
十二、三条策略放进同一个账户
前面都是抽象的 ρ。现在用真东西:主线策略在 SPY、AAPL、BTC 上各跑一条,三条腿放进同一个账户。
2017-09-01 到 2026-08-31,3287 个日历日(美股开市 2259 天)
三条策略日收益的相关(美股开市日):
SPY AAPL BTC
SPY 1.000 0.395 0.142
AAPL 0.395 1.000 0.075
BTC 0.142 0.075 1.000
同时持有几条: {0: 1506, 1: 773, 2: 774, 3: 234} ;三条同时只占 7.1% 的日子
怎么分配 年化 最大回撤 年化波动 夏普(无风险 0)
只做 SPY 0.0349 -0.1235 0.0786 0.4438
只做 AAPL 0.1003 -0.2785 0.1587 0.6319
只做 BTC 0.1855 -0.4769 0.3007 0.6169
三条各 1/3 0.1187 -0.1702 0.1266 0.9376
三条各满仓(总敞口 300%) 0.3340 -0.4494 0.3797 0.8795
三条等权的有效独立仓位数:2.13 / 3
先看两个分散来源:
一、收益相关低。 SPY 和 AAPL 的日收益相关 0.395(都是美股,意料之中),SPY 和 BTC 只有 0.142,AAPL 和 BTC 0.075。有效独立仓位数 2.13 / 3——比行业 ETF 的 1.53 / 11 好太多了。
二、时间上就错开了。 三条腿同时持仓的日子只占 7.1%(234 天);45.8% 的日子一笔都没有,只持一条和同时持两条各占 23.5%。趋势跟随策略本来就有一半时间空仓,三条叠在一起自然错峰——这是一种不花钱的分散。
⚠️ 这一节的年化和前面几节对不上(比如 AAPL 是 10.03% 而不是 10.75%),因为美股和加密的日历不一样:三条腿合在一个账户里只能按日历日记账,周末美股不动但 BTC 照走,所以这里一年按 365 天折算,前面几节的美股按 252 天。同一条策略换个折算方式数字就会变,这是记账口径,不是业绩差别。
然后看结果:
| 怎么分配 | 年化 | 最大回撤 | 年化波动 | 夏普 |
|---|---|---|---|---|
| 只做 SPY | 3.49% | −12.35% | 7.86% | 0.444 |
| 只做 AAPL | 10.03% | −27.85% | 15.87% | 0.632 |
| 只做 BTC | 18.55% | −47.69% | 30.07% | 0.617 |
| 三条各 1/3 | 11.87% | −17.02% | 12.66% | 0.938 |
| 三条各满仓(总敞口 300%) | 33.40% | −44.94% | 37.97% | 0.880 |
等权组合的夏普 0.938,比三条腿里最好的那条(0.632)高一半。 它的年化 11.87% 落在三条之间,但回撤 −17.02% 比三条里最小的那条(SPY 的 −12.35%)只差一点点,而 BTC 单独跑是 −47.69%。
这是整篇里唯一一个「免费的午餐」——不需要任何预测能力,只要把不相关的东西放在一起。它也说明了第六节那个疑问的答案:「每笔风险 1%」这种小数字,是给「同时有很多笔」的账户用的。三条腿各满仓那一行就是另一个极端:总敞口 300%,年化翻到 33.4%,但回撤回到 −44.94%,夏普掉到 0.880。
几条策略之间怎么分钱
等权是最笨的办法。能不能用历史数据分得更聪明一点?
每天用过去 365 天估参数,第二天按估出来的权重分配——严格样本外:
每天用过去 365 天估参数、第二天用,样本外 2922 天 (2018-09-01 到 2026-08-31)
资金怎么分 年化 最大回撤 年化波动 夏普(无风险 0) 平均权重
等权 0.1400 -0.1702 0.1317 1.0632 {'SPY': 0.333, 'AAPL': 0.333, 'BTC': 0.333}
波动率倒数 0.1066 -0.1108 0.0955 1.1162 {'SPY': 0.548, 'AAPL': 0.295, 'BTC': 0.158}
按历史夏普 0.1361 -0.3438 0.2020 0.6736 {'SPY': 0.237, 'AAPL': 0.394, 'BTC': 0.369}
按历史年化 0.1317 -0.3898 0.2436 0.5408 {'SPY': 0.144, 'AAPL': 0.37, 'BTC': 0.486}
| 资金怎么分 | 年化 | 最大回撤 | 年化波动 | 夏普 | 平均权重(SPY / AAPL / BTC) |
|---|---|---|---|---|---|
| 等权 | 14.00% | −17.02% | 13.17% | 1.063 | 0.33 / 0.33 / 0.33 |
| 波动率倒数 | 10.66% | −11.08% | 9.55% | 1.116 | 0.55 / 0.30 / 0.16 |
| 按历史夏普 | 13.61% | −34.38% | 20.20% | 0.674 | 0.24 / 0.39 / 0.37 |
| 按历史年化 | 13.17% | −38.98% | 24.36% | 0.541 | 0.14 / 0.37 / 0.49 |
规律很干脆:越是用历史收益来分配资金,样本外越差。
- 只用波动率(波动大的少给):夏普 1.116,回撤 −11.08%,两项都最好
- 等权(什么都不估):夏普 1.063,紧随其后
- 用历史夏普:夏普掉到 0.674
- 用历史年化:夏普 0.541,回撤 −38.98%,比等权深一倍多
原因和第八节的凯利是同一个:波动率估得准(秩相关 0.5),收益率估不准(秩相关 0.0 上下)。把估不准的数拿去做除法或者排序,得到的权重就是在追过去一年涨得最猛的那条腿——「按历史年化」平均给了 BTC 49% 的权重,而它正好在 2022 年最惨。
⚠️ 一个诚实的限制:这里只有三条腿,而且它们的波动率差得很远(7.9% / 15.9% / 30.1%)。波动率倒数赢在把 BTC 压到 16%,这一半是「风险平价」的道理,另一半只是「少买波动最大的那个」。三条腿的样本分不出这两者。
十三、动手:talab.size
新模块 talab.size,15 个函数分四部分。
"""talab.size:仓位。第 26 篇。
第 23 篇把「亏多少算看错」变成了一条止损线,第 24 篇量了杠杆怎么把你赶出场。
夹在它们中间还有一个问题没答:**这一笔,买多少**。
把账户想成家里的配电箱。止损是每条支路上的保险丝:这台电器出事,最多烧掉这一条。
仓位是「这台电器要占多少安培」。两件事必须一起定——保险丝的额定电流再准,
你给一条 16 安的线接上 40 安的电器,跳的就不是支路而是总闸。
模块分四部分:
1. **一笔买多少**:`position_size`、`size_by_atr`、`simulate`——
把「一笔最多亏账户的百分之几」翻译成金额和数量。
2. **上限在哪**:`kelly`、`growth_rate`、`optimal_f`、`recovery`、`underwater`——
下注比例再往上加,增长率会掉头向下;回撤要爬回来,需要的涨幅比跌幅大。
3. **按波动调**:`vol_target`——盯住「账户的波动」而不是「持仓的金额」。
4. **组合**:`portfolio_vol`、`effective_bets`、`inverse_vol_weights`、`combined_risk`——
五条支路接在同一根干线上时,它们其实是一条。
所有函数都不含成本和滑点(成本是第 28 篇)。
"""
from __future__ import annotations
import numpy as np
import pandas as pd
METHODS = ("full", "amount", "fraction", "risk")
一笔买多少
def stop_distance(entry_price: float, stop_price: float) -> float:
"""止损距离,按**占进场价的比例**算。
这是仓位公式里唯一和行情有关的输入:它由第 23 篇的四个出场问题决定,
不是你随便填的数。入场条件一改(比如要求「收盘创 20 日新高」),它也跟着变。
"""
if not 0 < stop_price < entry_price:
raise ValueError(f"止损价要在 0 和进场价之间(进场 {entry_price},止损 {stop_price})")
return (entry_price - stop_price) / entry_price
def position_size(equity: float, distance: float, risk_fraction: float = 0.01,
price: float | None = None, max_fraction: float = 1.0) -> pd.Series:
"""一笔最多亏账户的 `risk_fraction`,止损距离 `distance`,该买多少。
> 仓位金额 = 账户 × 每笔风险比例 ÷ 止损距离
推导只有一步:仓位金额 × 止损距离 = 这笔被止损时亏的钱,让它等于「账户 × 风险比例」。
`max_fraction` 是名义敞口的上限,默认 1.0=不借钱;算出来超过它就封顶,
这时**实际风险会小于你定的风险比例**,返回的「实际风险」会告诉你小了多少。
"""
if not 0 < risk_fraction < 1:
raise ValueError("每笔风险比例要在 0 和 1 之间")
if distance <= 0:
raise ValueError("止损距离要大于 0,没有止损就算不出仓位")
want = equity * risk_fraction / distance
amount = min(want, equity * max_fraction)
out = {"仓位金额": amount, "占账户": amount / equity,
"止损时亏": amount * distance, "实际风险": amount * distance / equity,
"有没有封顶": want > amount}
if price is not None:
out["数量"] = amount / price
return pd.Series(out)
position_size 返回的不是一个数而是一张小表,因为封顶这件事必须说出来:算出来要 2 倍账户、你只能买 1 倍时,你实际冒的风险是 0.5% 而不是 1%,静悄悄地把它改小比报错更危险。
def size_by_atr(equity: float, atr: float, price: float, risk_fraction: float = 0.01,
k: float = 3.0, max_fraction: float = 1.0) -> pd.Series:
"""「按 ATR 调整仓位」的写法——它和 `position_size` 是同一个公式。
止损放在进场价下方 k 倍 ATR 时,止损距离就是 `k × ATR ÷ 价格`,代进去得到
> 仓位数量 = 账户 × 每笔风险比例 ÷ (k × ATR)
「波动大就少买」不是另一条规则,是这条公式的自动结果:ATR 翻倍,仓位减半。
"""
return position_size(equity, k * atr / price, risk_fraction, price, max_fraction)
size_by_atr 只有一行:它把「k 倍 ATR 的止损」翻译成止损距离,然后原样调用 position_size。两个名字,一条公式。
跑一遍资金曲线
def simulate(trades: pd.DataFrame, method: str = "risk", equity: float = 100_000.0,
amount: float = 10_000.0, fraction: float = 0.10, risk: float = 0.01,
max_fraction: float = 1.0) -> pd.DataFrame:
"""同一串交易,换一种仓位方法,资金曲线长什么样。
`trades` 要有「买入价」「卖出价」「初始止损」三列(第 23 篇 `risk.run` 的输出就是)。
四种方法:
- `full`:满仓,每笔都把账户全押上
- `amount`:固定金额,每笔都买 `amount` 美元(不随账户变,所以**不复利**)
- `fraction`:固定比例,每笔都买账户的 `fraction`
- `risk`:固定风险,每笔都让「被止损时亏掉的钱」等于账户的 `risk`
⚠️ 这里假设同一时刻只有一笔仓(主线策略就是这样),所以一笔结束才开下一笔。
多笔并行的组合风险要用 `combined_risk` 和 `effective_bets` 另算。
"""
if method not in METHODS:
raise ValueError(f"method 只能是 {METHODS} 之一,收到 {method!r}")
rows, account = [], float(equity)
for _, t in trades.iterrows():
distance = stop_distance(t["买入价"], t["初始止损"])
if method == "full":
notional = account
elif method == "amount":
notional = min(amount, account)
elif method == "fraction":
notional = account * fraction
else:
notional = min(account * risk / distance, account * max_fraction)
profit = notional * (t["卖出价"] / t["买入价"] - 1)
account += profit
rows.append({"买入日": t["买入日"], "卖出日": t["卖出日"], "止损距离": distance,
"仓位金额": notional, "占账户": notional / (account - profit),
"盈亏": profit, "盈亏占账户": profit / (account - profit), "账户": account})
return pd.DataFrame(rows)
回撤和凯利
def recovery(loss) -> float | np.ndarray:
"""亏掉 `loss` 之后,要涨多少才回本:`loss ÷ (1 − loss)`。
这条式子是整篇的地基。亏 10% 要涨 11.1%,亏 50% 要涨 100%,亏 83% 要涨 488%——
**亏损和收益不是对称的**,所以「少亏一点」比「多赚一点」在复利上更值钱。
"""
loss = np.asarray(loss, dtype=float)
if np.any((loss < 0) | (loss >= 1)):
raise ValueError("loss 要写成 0 到 1 之间的正数(亏 20% 写 0.2)")
out = loss / (1 - loss)
return float(out) if out.ndim == 0 else out
def drawdown(equity: pd.Series) -> pd.Series:
"""每个时点距离历史最高点还差多少(负数)。"""
return equity / equity.cummax() - 1
def underwater(equity: pd.Series) -> pd.DataFrame:
"""把资金曲线拆成一段段「水下」的日子:从跌破前高到重新创新高。
最大回撤只说最深有多深,这张表还说**最长有多长**——
对着账户等三年和亏三成,难受程度完全不是一回事。
"""
below = drawdown(equity) < -1e-12
rows, start = [], None
for time, under in below.items():
if under and start is None:
start = time
elif not under and start is not None:
rows.append((start, time, float(drawdown(equity).loc[start:time].min())))
start = None
if start is not None: # 最后一段还没爬回来
rows.append((start, equity.index[-1], float(drawdown(equity).loc[start:].min())))
out = pd.DataFrame(rows, columns=["开始", "结束", "最深"])
out["天数"] = (out["结束"] - out["开始"]).dt.days
out["要涨回来"] = recovery(-out["最深"].to_numpy())
return out
def kelly(win_rate: float, payoff: float) -> float:
"""凯利公式的两点版本:赢的概率 `win_rate`,赢时赚 `payoff` 份、输时亏 1 份。
> f* = 胜率 − 败率 ÷ 盈亏比
f* 是**每次下注冒的风险占账户的比例**(在交易里就是「一笔最多亏账户的百分之几」)。
它最大化的是对数收益的期望,也就是长期的复利增长率——不是期望收益,
期望收益永远是「押得越多越好」。⚠️ 公式里的胜率和盈亏比是**真值**,
而你只有估计值;`optimal_f` 上的 bootstrap 会告诉你这个估计有多晃。
"""
if not 0 <= win_rate <= 1:
raise ValueError("胜率要在 0 和 1 之间")
if payoff <= 0:
raise ValueError("盈亏比要大于 0")
return win_rate - (1 - win_rate) / payoff
def growth_rate(r, f: float) -> float:
"""按比例 `f` 下注一串 R 倍数时,每笔的对数增长率 `mean(log(1 + f·R))`。
账户是乘起来的,所以要最大化的是对数的平均数,不是收益的平均数。
下注比例大到某一笔会把账户打到 0 以下时,返回 −inf:那不是「亏得多」,是出局。
"""
x = 1 + f * np.asarray(r, dtype=float)
return -np.inf if np.any(x <= 0) else float(np.mean(np.log(x)))
def optimal_f(r, grid=None) -> float:
"""在网格上直接找让 `growth_rate` 最大的下注比例,不假设只有两种结果。
和 `kelly` 的两点公式对照着看:两者接近,说明那串交易的形状确实像一次赌局;
差得远,说明分布的尾巴在起作用(第 23 篇:两成的交易贡献一半的 R)。
"""
grid = np.arange(0.005, 1.0, 0.005) if grid is None else np.asarray(grid, dtype=float)
values = [growth_rate(r, f) for f in grid]
return float(grid[int(np.argmax(values))])
growth_rate 里那个 -np.inf 是刻意的:下注比例大到某一笔会把账户打到 0 以下时,那不是「亏得多」,是出局,后面所有交易都不存在了。用平均收益算的话这种情况会被平均掉,用对数算就会正确地变成负无穷。
按波动调仓
def vol_target(returns: pd.Series, target: float, lookback: int = 20,
periods_per_year: int = 252, cap: float = 3.0) -> pd.Series:
"""波动率目标仓位:仓位 = 目标波动 ÷ 最近实际波动。
固定风险盯的是「一笔亏多少」,波动率目标盯的是「账户每天晃多少」。
波动率比收益好估得多(第 15 篇:它有聚集性,昨天高今天大概率还高),
所以这是少数几个「用历史估一个数、拿到未来还能用」的地方。
⚠️ 仓位用 `shift(1)`:今天的仓位只能用昨天收盘前算得出来的波动。
`cap` 是杠杆上限,默认 3 倍;波动特别低的时候公式会要求很大的杠杆。
"""
realized = returns.rolling(lookback).std() * np.sqrt(periods_per_year)
return (target / realized).shift(1).clip(upper=cap).fillna(0.0).rename("仓位")
shift(1) 那一行是全篇最容易写错的一行:今天的仓位只能用昨天收盘前算得出来的波动。少写这一个 shift,回测会凭空好看很多,而且看不出破绽——这是第 28 篇要专门讲的前视偏差。测试里专门验了这件事:改掉第 100 天的收益,第 100 天的仓位必须不变,第 101 天才允许变。
组合
def portfolio_vol(weights, vols, corr) -> float:
"""组合的波动:`sqrt(wᵀ Σ w)`,其中 `Σ` 由波动和相关矩阵拼出来。
这条式子说的就是那句话:**风险不能相加,只能这样合起来**。
"""
w, s = np.asarray(weights, dtype=float), np.asarray(vols, dtype=float)
cov = np.asarray(corr, dtype=float) * np.outer(s, s)
return float(np.sqrt(w @ cov @ w))
def combined_risk(n: int, per_trade: float, corr: float) -> float:
"""`n` 笔同样大小、两两相关都是 `corr` 的仓位,合起来的风险。
> 合计 = 单笔 × sqrt(n + n(n−1)ρ)
ρ=0 时是 `单笔 × sqrt(n)`(教科书上的「风险开根号相加」),
ρ=1 时是 `单笔 × n`(五笔其实是一笔,只是放大了五倍)。
真实世界在两者之间,而且离 1 比多数人以为的近。
"""
if not -1 / max(n - 1, 1) <= corr <= 1:
raise ValueError(f"{n} 笔仓位的两两相关不可能是 {corr}(相关矩阵会不正定)")
return per_trade * np.sqrt(n + n * (n - 1) * corr)
def effective_bets(weights, corr) -> float:
"""有效独立仓位数:这一篮子东西,其实相当于几个互不相干的东西。
> N_eff = (Σw)² ÷ (wᵀ C w)
全都完全相关时等于 1(买十个等于买一个),互不相关且等权时等于 n。
它回答的是「我到底分散了没有」,而不是「我持有几个代码」。
"""
w = np.asarray(weights, dtype=float)
return float(w.sum() ** 2 / (w @ np.asarray(corr, dtype=float) @ w))
def inverse_vol_weights(vols) -> np.ndarray:
"""按波动率倒数分配资金:波动大的少给,让每份贡献的风险大致相等。
它只用到波动率,不用到收益率——**波动率估得准,收益率估不准**,
这就是这个看起来过分简单的做法在样本外往往赢过「按历史收益分配」的原因。
"""
s = np.asarray(vols, dtype=float)
if np.any(s <= 0):
raise ValueError("波动率要大于 0")
return (1 / s) / (1 / s).sum()
测试
18 个测试,每一个的期望值都是手算的:
def test_position_size_loses_exactly_the_risk_budget():
"""账户 10 万、止损距离 3%、一笔最多亏 1%:买 33,333 美元,止损时正好亏 1,000。"""
plan = Z.position_size(100_000, 0.03, 0.01, price=200.0)
assert plan["仓位金额"] == pytest.approx(100_000 * 0.01 / 0.03)
assert plan["止损时亏"] == pytest.approx(1_000.0)
assert plan["数量"] == pytest.approx(plan["仓位金额"] / 200.0)
assert not plan["有没有封顶"]
def test_simulate_fixed_risk_loses_the_same_percentage_every_time():
"""止损距离差三倍的两笔亏损交易,按固定风险下注时亏掉的账户比例一样。"""
table = Z.simulate(trades([(100.0, 97.0, 97.0), (100.0, 91.0, 91.0)]), "risk", risk=0.01)
assert list(table["盈亏占账户"].round(10)) == [-0.01, -0.01]
assert table["占账户"].iloc[0] == pytest.approx(1 / 3)
assert table["占账户"].iloc[1] == pytest.approx(1 / 9)
def test_simulate_full_lets_the_market_pick_the_loss():
"""同样两笔,满仓时亏掉的是止损距离本身——3% 和 9%,你没有选择权。"""
table = Z.simulate(trades([(100.0, 97.0, 97.0), (100.0, 91.0, 91.0)]), "full")
assert list(table["盈亏占账户"].round(10)) == [-0.03, -0.09]
上面这两个测试合起来就是第四节那句话:同样两笔交易,固定风险时亏的都是 1%,满仓时亏的是 3% 和 9%——那个数是市场给的。
def test_optimal_f_agrees_with_kelly_on_a_two_point_bet():
"""只有两种结果时,网格搜出来的最优下注比例要和公式对得上。"""
r = np.array([2.0] * 60 + [-1.0] * 40) # 胜率 60%,赢 2 份输 1 份
assert Z.optimal_f(r) == pytest.approx(Z.kelly(0.6, 2.0), abs=0.005)
assert Z.growth_rate(r, Z.optimal_f(r)) > Z.growth_rate(r, 0.9)
def test_growth_rate_is_minus_infinity_when_a_bet_wipes_you_out():
assert Z.growth_rate([0.5, -1.0], 1.0) == -np.inf # 亏满 1 倍=账户归零
assert np.isfinite(Z.growth_rate([0.5, -1.0], 0.5))
def test_portfolio_risk_does_not_add_up():
"""五笔各 1%:完全独立时合计 2.24%,两两相关 0.6 时 4.12%,完全一样时 5%。"""
assert Z.combined_risk(5, 0.01, 0.0) == pytest.approx(0.01 * np.sqrt(5))
assert Z.combined_risk(5, 0.01, 0.6) == pytest.approx(0.0412, abs=1e-4)
assert Z.combined_risk(5, 0.01, 1.0) == pytest.approx(0.05)
with pytest.raises(ValueError):
Z.combined_risk(5, 0.01, -0.5)
corr = np.full((5, 5), 0.6) + np.eye(5) * 0.4
assert Z.portfolio_vol(np.ones(5) * 0.01, np.ones(5), corr) == pytest.approx(Z.combined_risk(5, 0.01, 0.6))
最后一个测试和第 21 篇的 rules.run 对账——同一笔交易,两个模块算出来的仓位必须是同一个数:
def test_size_agrees_with_the_mainline_strategy_sizing():
"""和第 21 篇的 `rules.run(sizing="risk")` 对账:同一笔交易算出的仓位必须一样。"""
rng = np.random.default_rng(26)
close = pd.Series(100 * np.exp(np.cumsum(rng.standard_normal(600) * 0.02)),
index=pd.date_range("2020-01-01", periods=600, freq="D"))
df = pd.DataFrame({"open": close, "high": close * 1.01, "low": close * 0.99, "close": close})
entry = pd.Series(False, index=df.index)
entry.iloc[299] = True
rule = R.Rule(entry=entry, exit=pd.Series(False, index=df.index), stop="percent",
stop_percent=0.08, sizing="risk", risk_per_trade=0.01)
_, held, table = R.run(df, rule)
assert held.iloc[300] == pytest.approx(Z.position_size(1.0, 0.08, 0.01)["占账户"])
assert table["仓位"].iloc[0] == pytest.approx(0.125)
245 passed in 0.97s
没装 TA-Lib 时:
213 passed, 32 skipped in 1.30s
十四、揭晓
回到 2020 年 10 月 10 日那一笔。
它在 2021-01-12 被吊灯止损打掉,卖出价 34,051.24,+24.89R。 三个月,价格从 11,050 涨到 34,051,涨了 208%。
那一笔的结局:2021-01-12 以 34,051.24 止损出场,24.89R
买法 这一笔赚 账户变成 涨了
A 满仓 208138.1712 308138.1712 2.0814
B 固定金额 1 万 20813.8171 120813.8171 0.2081
C 固定风险 1% 24886.8311 124886.8311 0.2489
D 固定风险 10% 208138.1712 308138.1712 2.0814

| 买法 | 这一笔赚 | 账户变成 | 涨了 |
|---|---|---|---|
| A 满仓 | 208,138 | 308,138 | +208% |
| B 固定金额 1 万 | 20,814 | 120,814 | +20.8% |
| C 固定风险 1% | 24,887 | 124,887 | +24.9% |
| D 固定风险 10% | 208,138 | 308,138 | +208% |
满仓赢了,赢了整整一个数量级。所以答案是满仓?
不是。因为这个问题问错了——「买多少」的对错,从来不能用一笔交易来判断。同一套买法要在这条策略的每一笔上都用,所以要看的是全部 30 笔:
同样四种买法,跑完 BTC 上全部 30 笔交易:
仓位方法 期末账户 最差一笔 最好一笔 平均仓位 逐笔最大回撤
满仓 462901.1788 -0.2384 2.0814 1.0000 -0.3520
固定金额 1 万 128839.0722 -0.0238 0.2015 0.0840 -0.0288
固定比例 10% 130344.4234 -0.0238 0.2081 0.1000 -0.0364
固定风险 1% 136771.9941 -0.0120 0.2489 0.0914 -0.0355
固定风险 10% 581817.7139 -0.1202 2.0814 0.8072 -0.2454
| 仓位方法 | 期末账户 | 最差一笔 | 平均仓位 | 逐笔最大回撤 |
|---|---|---|---|---|
| 满仓 | 462,901 | −23.84% | 100% | −35.20% |
| 固定金额 1 万 | 128,839 | −2.38% | 8.4% | −2.88% |
| 固定风险 1% | 136,772 | −1.20% | 9.1% | −3.55% |
| 固定风险 10% | 581,818 | −12.02% | 80.7% | −24.54% |
在同一条策略、同一批信号上,D 比 A 多赚 11.9 万美元,最差一笔只有 A 的一半,最大回撤小三分之一。
为什么?看第五节右边那张图就明白了:D 和 A 在 30 笔里有 13 笔完全一样(止损距离小于 10%,封顶在满仓),区别全在另外 17 笔——那些止损距离 12%、18%、26% 的交易,A 照样满仓,D 按比例缩到 83%、56%、38%。
而止损距离大意味着什么?意味着进场那几天的 ATR 大,也就是市场正在剧烈波动。D 做的事翻译成一句话就是:
市场晃得厉害的时候少买一点。
这句话不需要任何预测能力,也不依赖「高波动之后会跌」——第九节刚量过,BTC 上高波动之后其实涨得更凶。D 赢的不是方向,是把每一笔的亏损上限压成同一个数之后,复利少挨了几次重击(第七节:亏 24% 要涨 31% 才回来,亏 12% 只要涨 14%)。
⚠️ 一句必要的提醒:这 30 笔交易里,这一笔贡献了绝大部分收益。换一段历史、换一个标的,A 和 D 的排序可能反过来——AAPL 上 D 就比 A 少赚(254,620 对 275,165)。D 确定给你的不是更高的收益,是那张右图上的那条直线。
十五、主线策略 v4
第 21 篇的六个格子,最后一格一直填的是「满仓」。现在填上它。
选什么数? 不是挑年化最高的那个——第六节那条曲线上年化和回撤是捆着卖的,挑最高点就是在 30 笔交易上做曲线拟合(第 20、21 篇两次拒绝过这种做法)。要回答的是一个你自己的问题:
这个账户只跑这一条策略,一次只有一笔仓,不借钱。我能接受一笔交易亏掉账户的百分之几?
定 10%。理由是机制,不是回测数字:
- 一次只有一笔仓,所以不需要给并行的仓位留额度(第十节:五笔并行时同样的总预算要除以 4 左右)
- 不借钱,所以风险预算超过 10% 在 SPY 和 AAPL 上几乎没有效果——它们的止损距离本来就小于 10%,怎么定都是满仓
- 它只在该起作用的地方起作用:止损距离大于 10% 才缩仓,也就是只在波动特别大的时候
标的 版本 交易数 年化 最大回撤 最差一笔 平均仓位 缩过仓的交易
SPY v3:满仓 39 0.0297 -0.1235 -0.0571 1.0000 0.0000
SPY v4:一笔最多亏账户 10% 39 0.0297 -0.1235 -0.0571 1.0000 0.0000
AAPL v3:满仓 37 0.1075 -0.2785 -0.0814 1.0000 0.0000
AAPL v4:一笔最多亏账户 10% 37 0.0982 -0.2785 -0.0814 0.9803 0.0270
BTC v3:满仓 30 0.1855 -0.4769 -0.2384 1.0000 0.0000
BTC v4:一笔最多亏账户 10% 30 0.2153 -0.3231 -0.1202 0.8690 0.5667
| 标的 | 版本 | 年化 | 最大回撤 | 最差一笔 | 平均仓位 | 缩过仓的交易 |
|---|---|---|---|---|---|---|
| SPY | v3 满仓 | 2.97% | −12.35% | −5.71% | 100% | 0% |
| SPY | v4 | 2.97% | −12.35% | −5.71% | 100% | 0% |
| AAPL | v3 满仓 | 10.75% | −27.85% | −8.14% | 100% | 0% |
| AAPL | v4 | 9.82% | −27.85% | −8.14% | 98.0% | 2.7% |
| BTC | v3 满仓 | 18.55% | −47.69% | −23.84% | 100% | 0% |
| BTC | v4 | 21.53% | −32.31% | −12.02% | 86.9% | 56.7% |
- SPY 上一个字没变:39 笔交易的止损距离全部小于 10%,规则从不触发。
- AAPL 上只动了一笔(37 笔里的 2.7%,那笔止损距离 14.3%),年化因此掉了 0.93 个百分点。这是诚实的代价:规则在它不需要的地方也会偶尔咬你一口。
- BTC 上 56.7% 的交易缩了仓,年化从 18.55% 升到 21.53%,回撤从 −47.69% 收到 −32.31%,最差一笔从 −23.84% 收到 −12.02%。
为什么接受 AAPL 上这 0.93 个百分点的损失? 因为换来的是三个标的用同一条规则、每一笔的亏损上限都是你定的数。如果为了 AAPL 上那 0.93 个点去把阈值调成 15%,那就是在用回测数字选规则——而 15% 这个数只在这 37 笔上成立。
v4 的六要素(第 21 篇的格子,最后一格终于填上了):
一、环境过滤 不过滤
二、入场条件 给定的信号序列
三、入场方式 下一根开盘价
四、初始止损 3.0 倍 ATR 吊灯(close 触发)
五、出场规则 给定的出场信号
六、仓位 每笔风险 10%
六个格子终于全是你填的了。 第 21 篇写过一句话:六要素是咬合的,不能一格一格单独优化。这一篇是那句话的另一面——最后一格填上之前,前面五格里有一格(「一笔亏多少」)其实一直是市场在填。
这一篇没有采纳的东西
- 波动率目标仓位:第九节量过,它在 BTC 上跑输同波动的恒定杠杆,而主线策略的大部分收益来自 BTC。何况主线一半时间空仓,「账户波动率」这个量在空仓期间是 0,目标波动这个概念对它本来就别扭。
- 凯利公式给的 27%–32%:第八节量过,那个数重抽一遍能跳到 [0.01, 0.54]。
- 三条腿合成一个组合:第十二节的结果很好(夏普 0.938),但那是另一条策略,不是给 v4 加一格。它属于第 30 篇之后的话题。
十六、小检查
- 账户 5 万美元,某只股票现价 80 美元,止损放在 76 美元,这一笔最多亏账户的 1.5%。该买多少股?
- 某条策略一次只有一笔仓、满仓运行,历史上止损距离的中位数是 5%。有人说它「每笔风险 1%」。对吗?
- 凯利公式算出 f* = 0.3。为什么几乎没有人真按 0.3 下注?说两个理由。
- 十个标的等权持有,两两相关都是 0.8。有效独立仓位数是多少?
- 「危机的时候所有东西一起跌」——用这一篇的方法,你会怎么检验这句话?
十七、常见误用
把「仓位比例」当成「风险」。 「我只买了三成仓,很保守」——三成仓配一个 30% 的止损距离,这一笔冒的是账户的 9%。风险是仓位乘以止损距离,只说一个数等于没说。
给不同波动的标的用同一个仓位。 SPY 和 BTC 都买 10%,意思是你在 BTC 上冒的风险是 SPY 上的三倍(第四节:同样 1% 的风险,SPY 买 27.6%、BTC 买 9.2%)。这种账户的收益几乎全部来自波动最大的那个标的,你以为自己在做资产配置,实际上在单押。
把凯利公式的输出直接当仓位。 两个层面的错:一是那个 f 是风险比例不是仓位金额(第八节:BTC 的 0.32 对应三倍账户的仓位);二是它用估计值算出来的,90% 区间能覆盖五十倍的范围。
用「每笔 1%」安慰自己,却同时开了八笔高度相关的仓。 八笔两两相关 0.8 的仓位,各冒 1%,合起来是 1% × sqrt(8 + 56×0.8) = 7.3%,接近 8% 这个上限。真正独立的话应该是 2.8%。
回撤发生后加仓摊平。 第七节那条曲线的意思正好相反:回撤越深,回本需要的涨幅越不成比例。在亏损的仓位上加仓,是在回撤最深、判断最不可靠的时候把风险预算翻倍。
看到「危机时相关性上升」就重估整个组合。 第十一节量过:按「大波动的日子」筛出来的相关性上升是筛选造成的,在一个相关性恒定的模拟世界里同样出现。真正存在的是「下跌那一侧更抱团」,幅度是 0.055,不是 0.23。
忘记波动率目标仓位的换手成本。 20 天窗口每年翻动仓位 5 到 15 次。第 28 篇会把这笔钱算出来。
十八、小结
- 仓位和止损必须一起定。 被止损时亏掉的钱 = 仓位金额 × 止损距离。只定一个,另一个就由市场替你定。
- 固定风险的公式只有一行:仓位金额 = 账户 × 每笔风险比例 ÷ 止损距离。「按 ATR 调仓」是它的等价写法,不是另一条规则。
- 满仓的真实含义是「一笔能亏多少由 ATR 决定」:主线策略在 BTC 上,这个数在 6.4% 和 26.2% 之间跳,差 4.1 倍。
- 一次只有一笔仓的策略 + 每笔风险 1% = 只用账户的十分之一。 封顶之前年化和风险预算成正比,那段区间里「风险预算」就是杠杆倍数。
- 年化在某个下注比例上见顶然后掉头向下(BTC 上顶点在每笔 10%,年化 21.5%、回撤 −32.3%,满仓是 18.6% 和 −47.7%)。这是复利的算术,第 24 篇的
μ ÷ σ²是同一条抛物线。 - 回撤和收益不对称:亏 50% 要翻倍,亏 83% 要涨 488%。而且你绝大多数日子都在水下(SPY 86.1%、AAPL 89.6%、BTC 96.8%),BTC 最长一段 1,073 天。
- 凯利公式的 f* 你估不准:30 笔交易重抽 2,000 次,BTC 的 90% 区间是 [0.010, 0.540],5.1% 的重抽说「别下注」;AAPL 用前半段的 f* 跑后半段,增长率是负的。半凯利保留八成增长、砍掉四成回撤。
- 波动率目标仓位靠的是「波动率可预测、收益率不可预测」(秩相关 0.5 对 0.0)。在同样实现波动的对照下,它在 SPY 和 AAPL 上把夏普抬高 0.16–0.20,在 BTC 上反而低 0.05——因为 BTC 的高波动期是涨出来的。
- 风险不能相加:五笔各 1%、两两相关 0.6,合计 4.12%(独立时 2.24%,完全相关时 5%)。
- 有效独立仓位数才是分散的度量:11 个行业 ETF 只有 1.53 个,10 个大币只有 1.59 个,而主线策略的三个标的有 2.13 个。
- 「危机时相关性上升」一半是假的:按「涨跌绝对值最大」筛出来的上升,在相关性恒定的模拟世界里更大(0.876 对真实的 0.846);按「下跌」筛出来的上升是真的(0.695 对对照的 0.136–0.336)。
- 分配资金时越用历史收益越差:样本外夏普 波动率倒数 1.116 > 等权 1.063 > 按历史夏普 0.674 > 按历史年化 0.541。
- 主线策略 v4 = v3 + 一笔最多亏账户 10%。 SPY 上一个字没变,AAPL 上动了一笔(年化 −0.93 个百分点),BTC 上 56.7% 的交易缩仓,年化 18.55% → 21.53%、回撤 −47.69% → −32.31%、最差一笔 −23.84% → −12.02%。
练习
- 把
simulate的method="fraction"和method="risk"在 AAPL 的 37 笔交易上跑一遍,找出两者仓位差最大的那一笔,看看那天的 ATR 发生了什么。 - 第六节的风险预算扫描只跑了三个标的。用第 19 篇下载的 704 个永续合约,挑 10 个成交额大的,各自跑一遍扫描,看看「年化的顶点」落在哪个风险预算上,以及它和该标的的波动率有没有关系。
- 用
optimal_f对 BTC 的 30 笔交易做一个滚动估计:每次用前 15 笔算 f*,用在第 16 笔上,一直滚到最后。把这条资金曲线和「固定 10%」比一比。 - 第十一节的模拟对照用的是正态和 t 分布。换一个保留真实边际分布的对照:把 11 个行业 ETF 的整行(同一天的 11 个收益)打乱顺序重排,这样相关矩阵完全不变、边际分布也完全不变,再执行同样的筛选。结论会变吗?
- 把
vol_target加进主线策略:不是替换固定风险,而是叠在上面(仓位取两者的较小值)。年化和回撤怎么变?在三个标的上分别看。 effective_bets假设等权。改写一个版本接受任意权重,算一下「波动率倒数」权重下三条腿的有效独立仓位数,和等权比。
小检查答案
- 止损距离 = (80 − 76) ÷ 80 = 5%;允许亏 50,000 × 1.5% = 750 美元;仓位金额 = 750 ÷ 5% = 15,000 美元;股数 = 15,000 ÷ 80 = 187.5 股(取 187 股)。仓位占账户 30%。
- 不对。 满仓时每笔亏掉账户的比例就是止损距离本身,中位数 5% 意味着典型的一笔亏 5%,而不是 1%;而且这个数会随 ATR 在一个很宽的区间里跳。要真的做到「每笔风险 1%」,仓位得是 1% ÷ 5% = 20% 的账户。
- 任选两条:(a)f* 是用估计出来的胜率和盈亏比算的,重抽一遍能跳到 [0.01, 0.54];(b)曲线在顶点左边很平、右边很陡,估高了的代价远大于估低了;(c)全凯利的回撤在这三个标的上是 −47% 到 −74%,凯利公式里根本没有「回撤」这个输入;(d)它最大化的是长期增长率,而你不一定有「长期」。
- N_eff = 1 ÷ (0.1×0.1×(10 + 90×0.8)) 的倒数形式,直接算:wᵀCw = (1/10)² × (10 + 10×9×0.8) = 0.01 × 82 = 0.82,N_eff = 1 ÷ 0.82 = 1.22。十个标的,相当于 1.22 个。
- 造一个相关矩阵恒定不变的对照世界(用真实相关矩阵生成随机收益,或者把整行打乱),对它执行和真实数据完全一样的筛选,比较两边筛出来的相关系数。真实值明显高于对照,这句话才成立;两边差不多,说明看到的上升是筛选方式的产物。第十一节的结论是:按「涨跌幅绝对值最大」筛不成立,按「下跌」筛成立。