谈谈凯利公式
一、关于凯利公式
百度上一堆介绍。这里就简要说明一下各个参数
https://zhuanlan.zhihu.com/p/23805353
式中f为你该用资产多少比例下注
b为盈亏比
p为胜率
q为亏损概率,即q=1-p
二、从筛子游戏讲起
比如骰子出现1,2,3,4,5都是不中奖,出现6中奖。那么中奖概率是1/6。然后规定按一下方式赔偿: 必须放1个筹码进去,赢了的话可以得到6个筹码,输了的话就得到0个筹码。其实这个赔率是5:1。
%2Fb%3D%5B5*(1%2F6)-5%2F6%5D%2F5%3D0#card=math&code=f%3D%28b%2Ap-q%29%2Fb%3D%5B5%2A%281%2F6%29-5%2F6%5D%2F5%3D0)
如果稍微提升一下赔率,然后规定按一下方式赔偿: 赢了的话可以得到6个筹码(加上本身1个,总共7个),输了的话就得到付出1个筹码
%2Fb%3D%5B6*(1%2F6)-5%2F6%5D%2F6%3D1%2F36#card=math&code=f%3D%28b%2Ap-q%29%2Fb%3D%5B6%2A%281%2F6%29-5%2F6%5D%2F6%3D1%2F36)
理论上不会输,但难度很大,因为这个概率和赔率下,每次投注要很小。
如果没有出老千的前提下,骰子的概率是可以根据数学知识计算的。但是股票是不可能由数学原理推导出概率公式的。虽然没法用数学的推导方式,但可以用更简单粗暴有效广泛的方式,比如掷骰子的例子,出现1的概率推导的结论是1/6,但还可以通过做无数次掷骰子活动。甚至,假设由于制造水平导致骰子质量不均匀,导致某个点数概率不是1/6,也只能从实验n次的方式得到。
三、股市中的涨涨跌跌
回到股票中来,假设经过研究发现有红色星期一的规律。星期一股票上涨和下跌比是8:2。而且平均涨幅和跌幅分别是2%和0.5%。那么对应凯利公式的胜率,败率是0.8和0.2,赔率是2% / 0.5%=4
对应的凯利公式
%2F4%3D0.75#card=math&code=f%3D%284%2A0.8-0.2%29%2F4%3D0.75)
如果这种情况,你要投入3/4的总筹码。需要理解的是,此时3/4的筹码不等于3/4的本金。这里的总筹码的概念,指的是当发生投注失败时,一次性输光的全部资产。
简单的打个比方,比如你全部资产1万块,然后和某人赌抛硬币,规定赔率是一比一。那么总筹码就是一万块。但如果规定,每次只能用1000。比如输了,只需要付1000,赢了,也只能得到1000。因为上述股票的涨跌幅才2%和0.5%, 所以假设每次赢的话,也需要50次才能赢回本。
所以,总筹码相当于50倍的资产(杠杆)。当然,这里只是在数学上进行说明,概念上的区别。并不是鼓励进行融资来实现投资的最大化。因为融资一方面有利息,这是其一。另一方面是更致命的,就是你的融资能力是跟你的净资产成比例的,而不是固定不的。比如你有10万的时候能够融资10万,但万一投资失败了一次剩下5万,那么你的融资能力也只能5万了。这个时候你就没法运用凯利公式进行。所以,我们完全可以根据凯利公式算出来的比例,来进行本金仓位的控制。这样虽然不能达到凯利公式的最快速收益的目标,但却增加了安全边际。
四、计算模拟
下面编写代码来演示,不同几率和赔率下翻盘的平均次数。
package com.godofstockgod;
import java.util.Random;
import java.util.Scanner;
/**
* 通过数值模拟计算,演示不同几率和赔率下翻盘的平均次数
*/
public class DemoKeli {
private static Boolean randomIsWin(double winRate)
{
Random rand=new Random();
double value=rand.nextDouble();
return value>winRate?false:true;
}
public static int keli(double me,double you,double winRate,double payRate) throws Exception
{
double p=winRate;
double q=1-winRate;
double b=payRate;
double f=(b*p-q)/b;
if(f<0)
{
throw new Exception("输入的胜率和赔率不符合凯利投注规则");
}
int times=0;
while(you>0)
{
double input=me*f;
if(randomIsWin(winRate))
{
me+=input;
you-=input;
}else
{
me-=input;
you+=input;
}
times++;
if(me<=0)
{
throw new Exception("Unbelieveral");
}
}
return times;
}
public static void demoKeli(double winRate,double payRate,int count) throws Exception
{
int n=count;
double sum=0;
for(int i=0;i<n;i++)
{
sum+=keli(1,1,winRate,payRate);
}
String msg=String.format("胜率:%.3f,赔率:%.3f,平均翻盘次数:%.3f", winRate,payRate,sum/n);
System.out.println(msg);
}
public static void main(String[] args) throws Exception
{
Scanner scanner=new Scanner(System.in);
while (true){
try {
System.out.println("请输入胜率:");
double winRate = scanner.nextDouble();
System.out.println("请输入赔率:");
double payRate = scanner.nextDouble();
System.out.println("请输入样本次数:");
int count = scanner.nextInt();
demoKeli(winRate, payRate, count);
}catch (Exception e){
System.out.println(e.getMessage());
}
System.out.println("还要玩吗?(Y,任意输入退出)");
String str=scanner.next();
if(!str.toUpperCase().equals("Y")){
break;
}
}
}
}
运行后效果如下:
请输入胜率:
0.9
请输入赔率:
2
请输入样本次数:
10000
胜率:0.900,赔率:2.000,平均翻盘次数:3.341