仓位管理大杀器:凯利公式你用对了吗
今天我们介绍一个神奇的人,和他神奇的公式。目前网络上传播较广的一篇讲凯利公式前世今生的文章,个人认为深度不够,而且思路是有问题的。大家不要看多了走偏路。最近也花了一些力气做梳理,把这篇分享出来,尽量做到讲对,讲透,也是提醒各位,低头挖矿,不要搞错了挖矿的初衷。
一、故事背景
市场不好的时候,我们总是会犹豫,满仓?半仓?还是空仓?有没有一个科学的办法给出一个标准答案呢?下面来看看仓位管理神器——凯利公式。
投资(ji)就是一场赌博。像索普、香农等很多投资大师早期都对研究赌博业的秘密情有独钟,科学家们总是希望从理论上找到赌博游戏的必胜策略,使得一场游戏中赢的概率远远高过输的概率。
凯利就是这样一个大师。他是香农(信息论创始人)在贝尔实验室的同事,这个来自德克萨斯州、爱摆弄手枪、喜欢一根接一根抽烟的狂野硬汉,干过很多恶作剧,特别喜欢将填满塑料的子弹射进他客厅的墙壁里来戏弄家里暂住的客人。他的研究领域从量子物理学到电视信号解码,发明了能够准确模拟人类声音的电脑设备。最广为人知的研究贡献,便是将香农的信息论运用到了赛马赌博中。
凯利用这样一个精巧简洁的公式,将信息论与赌博之间的本质联系揭露出来,告诉我们在有限了解的信息下,如何下注能使得资本增值的速度最大化。
二、赌博怎么用凯利公式?
最早的凯利公式是运用在赌博游戏中的,我们先看看赌博情形下凯利公式的特殊形式:
f:下注比例
**Pwin:**赌赢的概率
Ploss:赌输的概率(=1-Pwin)
b:赔率,押 1 赔 b(这个赌球的朋友们是不陌生啦)
**特殊形式凯利公式的证明过程(此处感谢
的建议)**
我们可以做一个简单的证明。资金曲线 asset 对 f 求导,就可以得到特殊形式下的凯利公式。
In[0]:
from sympy import *
f,b,pwin = symbols('f b pwin')
#资金曲线asset
asset = pwin*log(1 + b*f) + (1 - pwin)*log(1-f)
#资金曲线增长最大,即asset对f求导=0时f的值
solve(diff(asset,f),f)
Out[0]:
[(b*pwin + pwin - 1)/b]
举个简单的例子
假想一个游戏。赢的概率是 60%,输的概率 40%。入场费随意交。如果赢了获得 2 倍的入场费金额(1 赔 1),输则输掉入场费。小米有 100 元做本金,请问小米每次给多少入场费,理论上 4 次游戏后几何期望收益能最大?
然后我拿凯利公式算了一下,最佳的策略是每次投剩余本金的 20%。
f = (1×0.6-0.4)/1 = 0.2。
基于上述的例子,做个简单的蒙特卡洛模拟实验(进行 200 次游戏):
In[1]:
from pandas import DataFrame
base = 100
pwin = 0.6
ploss = 1-pwin
b = 1
c = 1
rnd_position = 0.25
rnd_position2 = 0.15
kelly_position = (pwin*b - ploss)/b
stopline = 1
print 'kelly position is %s'%kelly_position
port_A = DataFrame()
port_B = DataFrame()
port_C = DataFrame()
port_D = DataFrame()
#重复模拟次数
for i in range(1000):
port1 = [base]
port2 = [base]
port3 = [base]
port4 = [base]
#游戏进行次数
for step in range(200):
rnd = random.random()
if rnd < pwin:
next_step = b
else:
next_step = -c
if port1[-1] > base*(1-stopline):
port1.append(port1[-1]*(1+next_step))
else:
port1.append(port1[-1])
if port2[-1] > base*(1-stopline):
port2.append(port2[-1]*(1+next_step*kelly_position))
else:
port2.append(port2[-1])
if port3[-1] > base*(1-stopline):
port3.append(port3[-1]*(1+next_step*rnd_position))
else:
port3.append(port3[-1])
if port4[-1] > base*(1-stopline):
port4.append(port4[-1]*(1+next_step*rnd_position2))
else:
port4.append(port4[-1])
port_A[i] = port1
port_B[i] = port2
port_C[i] = port3
port_D[i] = port4
plt.figure(figsize = (8,5))
plt.plot(exp(log(port_A.T).sum()/1000),label = 'port1:full')
plt.plot(exp(log(port_B.T).sum()/1000),'*',label = 'port2:kelly')
plt.plot(exp(log(port_C.T).sum()/1000),label = 'port3:0.25')
plt.plot(exp(log(port_D.T).sum()/1000),label = 'port4:0.15')
plt.legend(loc = 0)
print '\n不同组合的几何期望收益'
print 'full position %s'%exp(log(port_A.T).sum()/1000).iloc[-1]
print 'kelly position %s'%exp(log(port_B.T).sum()/1000).iloc[-1]
print 'position = 0.25 %s'%exp(log(port_C.T).sum()/1000).iloc[-1]
print 'position = 0.15 %s'%exp(log(port_D.T).sum()/1000).iloc[-1]
Out[1]:
观察四种操作方式:满仓下注、按凯利公式下注 20%、按 25% 下注、按 15% 下注
图为进行 200 次游戏之后几何期望资金曲线的情况
凯利无疑是增长最快的!
从另一个角度,我们来理解一下
不同赔率下,赢的概率多大我们会选择入场参与游戏?
还是上面的游戏,如果赢的概率 40%,输的概率 60%,那么,期望净收益就是(1×0.4-0.6)< 0;从概率的角度说,一个期望净收益为负的游戏是不值得参与的,求得的 f 小于 0,也就是不下注。
下面看一下不同赔率下一个游戏赌赢的概率为多少才值得参加?
同样,用实验来观察:
In[2]:
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
b = np.linspace(20,1,20)
pwin = np.linspace(0,1,20)
b,pwin = np.meshgrid(b,pwin)
kelly = (b*pwin-(1-pwin))/b*(b*pwin-(1-pwin)>=0)
zero = 0
fig = plt.figure(figsize = (10,6))
ax = fig.gca(projection = '3d')
surf = ax.plot_surface(b,pwin,kelly,rstride=1, cstride=1,cmap = plt.cm.coolwarm)
ax.plot_surface((1-pwin)/pwin,pwin,0)
ax.set_xlabel('b 赔率')
ax.set_ylabel('pwin 赢概率')
ax.set_zlabel('kelly 下注比例')
fig.colorbar(surf,shrink = 0.6,aspect = 10)
Out[2]:
上图说明两个问题:
- 如果一次赌博赔率越大,在赢的概率较小的情况下,凯利公式就开始提示要下注啦。(如图赔率为 20 时,只需要 10% 的赢率就可以入场玩啦)
- 同一赔率下,凯利公式只有在稳赢(赢概率 = 100%)时才会支持押下全部本金,否则都是本金的一定比例。也即永远不会输完所有的钱。
三、炒股怎么用凯利公式?
凯利的论文给出的押注策略,神奇之处就在于,当你总是遵循这一准则进行操作,你就能预测接下来发生的事情,你也能清楚的知道你的财富增长速度是在控制住风险情况下最优的结果。
来看看凯利运用他的财富公式,专门成立的 hedge fund 的 performance~20 年 15 倍,就是辣么厉害。
那么问题来了,我们量化炒股如何引入这么神奇的仓位管理神器?
因为股市的涨跌我们不会一次性赔光本金,所以引入损失率对凯利公式做微调,即更一般性的凯利公式:
f:仓位比例
Pwin:赌赢的概率—股市上涨概率
Ploss:赌输的概率—股市下跌概率
b:赢钱率(资产从 1 增加到 1+b)
c:损失率(资产从 1 减少到 1-c)
**一般性凯利公式的证明过程(再次感谢
的提议,在此加上证明过程)
**
一次投资过程,压上总资本(A)的一定比例:fA。有 pwin 的概率赢,赢了财富为 A(1+fb),ploss 的概率会输,输了资本变为 A*(1-fc)。N 次投资后,总资本函数为:
凯利公式要做的是使得总资本曲线的几何收益最大,也就是 log(An)/N 最大。即 log(AN)/N 对 f 求导 = 0 时 f 的值就是凯利公式了~
仔细想一个问题(挖矿秘籍)
赌博和买股票,赢概率 Pwin 和输概率 Ploss 究竟是什么?
赌博的时候,Pwin 和 Ploss 是根据游戏规则算出的概率而定。比如投硬币(Pwin=Ploss=0.5),或者转轮盘,扑克等更为复杂的游戏。
而买股票的过程,是 n 次离散赌博的过程。当你找到一个有效信号之后进行一次操作(比如有效信号是价格突破 5 日均线,财务数据好,成交量放大,或者各种金叉死叉等等。。)
假设我们找到了一个有效信号,信号发生时,股价为 S。我们提前给定价格 S(1+b)和 S(1-c)作为信号发生后止盈和止损的边界,这个时候的 Pwin 和 Ploss 应该是基于历史这个信号的收益情况做数据统计分析胜率(赢概率)和败率(输概率)来给出,也即价格触碰到 S(1+b)止盈的概率是 Pwin,触碰到 S(1-c)止损的概率是 Ploss。
关于信号这部分的可视化的理解,可参见好多凯利公式的文章,但好像没有说透彻的……。这篇是与 JoinQuant 的小编讨论后发布在 JQ 社区的一篇文章。
又举一个简单例子
有效信号:当前价突破 5 日均线
统计样本:100 只相似股票,过去三年有效信号发生了 1000 次
统计包含:假定上涨 20% 止盈,下跌 20% 止损。止盈赢钱的次数 57 次,止损输钱的次数 43 次
对应公式的参数:Pwin=0.57,Ploss=0.43,b=0.20,c=0.20
此时 f=Pwin/c – Ploss/b = 0.57/0.20 – 0.43/0.20= 70%
按照我们对某一个有效信号做历史统计定出来的 Pwin,Ploss,b 和 c 来进行模拟的投资组合看看看效果:
In[3]:
from pandas import DataFrame
base = 100
pwin = 0.57
ploss = 1-pwin
b = 0.2
c = 0.2
# stopline后文会介绍
stopline = 1
rnd_position = 0.6
rnd_position2 = 0.9
kelly_position = (pwin/c - ploss/b)*stopline
print 'kelly position is %s'%kelly_position
port_A = DataFrame()
port_B = DataFrame()
port_C = DataFrame()
port_D = DataFrame()
#重复模拟次数
for i in range(1000):
port1 = [base]
port2 = [base]
port3 = [base]
port4 = [base]
#投资次数步长
for step in range(500):
rnd = random.random()
if rnd < pwin:
next_step = b
else:
next_step = -c
if port1[-1] > base*(1-stopline):
port1.append(port1[-1]*(1+next_step))
else:
port1.append(port1[-1])
if port2[-1] > base*(1-stopline):
port2.append(port2[-1]*(1+next_step*kelly_position))
else:
port2.append(port2[-1])
if port3[-1] > base*(1-stopline):
port3.append(port3[-1]*(1+next_step*rnd_position))
else:
port3.append(port3[-1])
if port4[-1] > base*(1-stopline):
port4.append(port4[-1]*(1+next_step*rnd_position2))
else:
port4.append(port4[-1])
port_A[i] = port1
port_B[i] = port2
port_C[i] = port3
port_D[i] = port4
plt.figure(figsize = (8,5))
plt.plot(exp(log(port_A.T).sum()/1000),label = 'port1:full')
plt.plot(exp(log(port_B.T).sum()/1000),label = 'port2:kelly')
plt.plot(exp(log(port_C.T).sum()/1000),label = 'port3:position = 0.6')
plt.plot(exp(log(port_D.T).sum()/1000),label = 'port3:position = 0.9')
plt.legend(loc = 0)
print '\n不同组合的几何期望收益'
print 'full position %s'%exp(log(port_A.T).sum()/1000).iloc[-1]
print 'kelly position %s'%exp(log(port_B.T).sum()/1000).iloc[-1]
print 'position = 0.6 %s'%exp(log(port_C.T).sum()/1000).iloc[-1]
print 'position = 0.9 %s'%exp(log(port_D.T).sum()/1000).iloc[-1]
Out[3]:
同样四种仓位操作后的资金曲线来进行比较:
- 凯利公式下资金曲线增长是最快的。
- 高于或低于凯利公式的比例资金曲线增长都不是最快的。
再思考一个问题:关于杠杆
再多举一个例子:f>1
研究不能止步于此啊。很多知道凯利公式的朋友都有的疑问,是凯利计算出来的仓位容易过大。比如一不小心就提示几倍,几十倍了。这是什么情况呢?
比如我们找到一个信号:Pwin=0.7,Ploss=0.3,b=0.20,c=0.20
来看看实验结果(代码同上,只修改上述参数)
Out[4]:
f>1 了,甚至等于 4 了,什么意思!!凯利公式在告诉你这个信号太好了,值得你做 4 倍的杠杆来操作~!什么,你说我多加一点好不好,看上图,加到 6 倍显然就挫了。。。
(想知道凯利公式为何能做到几何期望收益最大化,证明过程戳 wiki 百科自行科普)
再思考一个止损的问题,stopline 不为 1
上面杠杆的问题引出其实说明了 A 股版凯利公式暗含的一个假设:
资金可以随意无摩擦地加杠杆操作,无借贷成本。
什么意思。一个好的信号,凯利会告诉你在已知风险(Pwin,Ploss,b 和 c)的情况下,最优的杠杆是多少。你可以毫不犹豫的就按这个杠杆去操作,最大化自己的资金曲线。
那么真实情况下呢,我们可能不加杠杆,而且也不能承受全部本金损失掉的风险。也就是,我们 stopline 会小于 1,甚至只到 30% 或者 20%。那这时候凯利公式怎么用?
这里不做实验,仅抛个引子。感兴趣的朋友可以继续深入研究,也欢迎与我探讨。
A. 静态止损:即亏损本金的固定数额后撤出投资
kelly_position = (pwin/c - ploss/b)×stopline
stopline 应为此时剩余可承受的损失 / 本金 base。
举例:base=100 元,输 20 元止损。initial_stopline = 0.2。假设损失率 c = 0.04
第一次,赔了 4 块,base=96,stopline = (20-4)/96 = 0.16
第 n 次,输输赢赢后,base = 250,stopline = (150+20)/250 = 0.68
B. 动态止损:即亏损剩余资金的一定比例后撤出投资
比如动态 20% 止损。base =100 元,赚到 200 元后,如亏损到 160 元即止损。
这个具体实验就留给各位自己研究啦~~
Bonus:小结一下
好啦,少年。真正赚钱的是找有效的因子 or 信号,使得 Pwin 尽可能大,b 尽可能大。无论是以往大家关注的一些技术指标(各种金叉、死叉)还是量价指标(放量、突破等等)又或者是财务指标,都可以作为一个信号。
统计历史信号出现时的一个表现,得到这个信号产生的收益的分布。只要这个信号的收益分布正偏一些些,就是纯纯的 Alpha 啊。当然这个因子 or 信号的挖掘,就是作为矿工孜孜不倦追求的终极目标了。当你找到这样一个神奇的信号,配上凯利公式会让你的财富增加更快~
好啦,说完啦。挖信号去了~
致谢
本文研究工具为 IPython Notebook 2.7。API 来自聚宽(JoinQuant),在此表示感谢。
更多扩展可见:好多凯利公式的文章,但好像没有说透彻的……
关于凯利和他的财富公式,有一本书专门介绍这个。财富公式:玩转拉斯维加斯和华尔街的故事. PDF,密码:i8fi。
最后:附上凯利的论文,
A New Interpretation of Information Rate
资源共享,是人类进步的推动力
『分享文章 + 关注公众号』
回复**『大数据书籍』or『量化书籍』**领取
更多内容:神秘的宽客们 - 知乎专栏