backtrader学习系列-基础知识:凯利公式

凯利公式(Kelly formula),也称凯利方程式,是一个用以使特定赌局中,拥有正期望值之重复行为长期增长率最大化的公式。这个公式由约翰·拉里·凯利于1956年在《贝尔系统技术期刊》中发表,可用以计算出每次游戏中应投注的资金比例。凯利公式是一种优化投资策略的公式,它可以帮助投资者在长期投资中最大化其收益。

以投硬币为例,假设获胜200%收益,失败100%亏损,那应该下注多少呢?其期望值为2 * 0.5 +(-1) * 0.5 = 0.5

import pandas as pd
## 1.1 每次100%投入多久会血本无归? 
def cal_zero_cost_times(total_nums):
    count = 0
    win_times = 0
    ## 所有实验次数
    while count < total_nums:
        win_or_loss = np.random.uniform(0, 1)
        win_times = 0
        # 一直扔,直到赔光
        while win_or_loss >= 0.5:
            win_times = win_times + 1
            win_or_loss = np.random.uniform(0, 1)
        count = count + win_times
        # 返回平均计算,归0 的次数
    return count/total_nums
df = pd.DataFrame({})
test_numbsers = [1,10,1000,10000,100000,1000000]
df.insert(0, 'TestNumbers', test_numbsers)
df.insert(1, 'TestResults', test_numbsers) 
df['TestResults'] = df['TestResults'].apply(cal_zero_cost_times)
df
image.png

从超过1000次的平均来看,基本一笔就亏空了,这反向说明数据随机性。这种赌徒式押注,从长期看注定一无所有。

## 1.2 每次投入不同比例呢? 
def cal_diff_percent_profit(test_percent, loop_times = 100,  test_cases = 10000,):
    count = 0
    ## 所有实验次数
    total_value = 0.0
    while count < test_cases:
        init_value = 1
        loop_number = 0 
        # test_number次测试
        while loop_number < loop_times:
            is_win = np.random.uniform(-1, 1)
            if is_win> 0:
                # 正收益
                init_value = init_value + init_value * test_percent
            else:
                # 亏了
                init_value = init_value - init_value *test_percent
            loop_number = loop_number + 1
            
        total_value = total_value + init_value
        count = count + 1
    return round(total_value/test_cases,2)
df = pd.DataFrame({})
df.insert(0, 'TestNumbers', np.arange(0.1,1,0.1))
df.insert(1, 'TestResults', np.arange(0.1,1,0.1)) 
df['TestResults'] = df['TestResults'].apply(cal_diff_percent_profit)
df
image.png

从结果上看,这个盈亏比少下仓为妙。

## 1.3 凯利公式怎么说? 
# 赔率是2,因为获胜将获得200%的收益,即赢得的金额是投注金额的两倍
def kelly_criterion(p_win, odds = 2.0):  
    """  
    使用凯利公式计算最佳投注比例。  
  
    参数:  
        p_win (float): 获胜的概率。  
        odds (float): 赔率,即赢得的金额与投注金额的比例。  
  
    返回:  
        float: 最佳投注比例。  
    """  
    # 失败的概率  
    p_lose = 1.0 - p_win  
  
    # 根据凯利公式计算最佳投注比例  
    return (p_win * odds - p_lose) / odds  
  
df = pd.DataFrame({})
df.insert(0, 'WinRate', np.arange(0.1,1,0.1))
df.insert(1, 'KellyBet', np.arange(0.1,1,0.1)) 
# 使用凯利公式计算最佳投注比例, 赔率从0.1 到0.9 都计算
df['KellyBet'] = df['KellyBet'].apply(kelly_criterion)
df
image.png
# 只有胜率大于40的时候,凯利公式才给出了正值;在前面的例子中,胜率为0.5,赔率为2, 凯利公式推荐0.25,虽然
print("凯利公式计算结果收益:",cal_diff_percent_profit(0.25, 100,10000))
凯利公式计算结果收益: 1.05

在股票中使用凯利公式的例子

# 2.1 假设有一个股票,估计它有60%的概率上涨5%,有40%的概率下跌2%。使用这些信息来计算凯利公式中的最优投资比例。
import math  
import akshare as ak

win_rate = 0.6  # 胜率,即60%的交易是盈利的  
win_return = 0.05  # 盈利交易的平均收益率  
loss_return = 0.02  # 亏损交易的平均亏损率  
reward_to_risk = abs(win_return / loss_return) 
# 计算最佳比率 
optimal_bet_fraction = kelly_criterion(win_prob, reward_to_risk)  
# 输出最优投资比例  
print(f"最优投资比例为:{optimal_bet_fraction:.2%}")  
最优投资比例为:34.90%
# 2.2 使用akshare 获取一个股票,并根据历史数据计算收益和概率,计算最佳投资比例
### 股票
##### 使用akshare 获取上证指数历史行情
df = ak.stock_zh_index_daily_em(symbol="sh000001", start_date="20200101", end_date="20210101")
#print(df)
df['Return'] = df['close'].pct_change()  
# 定义股票上涨和下跌的概率及收益率  

up_days = df[df['Return'] > 0]  
down_days = df[df['Return'] < 0]  
  
# 计算上涨和下跌的概率  
win_prob = len(up_days) / len(df)  
loss_prob = len(down_days) / len(df)  
# 计算收益
win_return = up_days['Return'].mean()  
loss_return = down_days['Return'].mean()  

# 盈亏比
reward_to_risk = abs(win_return / loss_return)  
# 计算最佳比率 
optimal_bet_fraction = kelly_criterion(win_prob, reward_to_risk)  
  
# 输出最优投资比例  
print(f"最优投资比例为:{optimal_bet_fraction:.2%}")  
  
# 假设你有10000元的资本,计算实际应该投入的金额  
capital = 100  
actual_bet_amount = capital * optimal_bet_fraction  
  
print(f"如果你有{capital}元的资本,你应该投入{actual_bet_amount:.2f}元进行这次交易。")
最优投资比例为:6.06%
如果你有100元的资本,你应该投入6.06元进行这次交易。

使用凯利公式计算杠杆倍数与预期收益

假设有一组由 N 个算法交易策略组成的策略集,要确定如何为每个策略应用最优杠杆以最大化增长率(同时最小化回撤),以及如何在每个策略之间分配资本。如果用向量 f 表示每个策略 i 之间的分配,其中 f 的长度为 N,即 f = (f1, ..., fN),那么每个策略 fi 的凯利公式最优分配由以下公式给出:

fi = μi/σi²

其中,μi 是策略 i 的平均超额收益,σi 是策略 i 超额收益的标准差。这个公式基本上描述了应该为每个策略应用的最优杠杆。

凯利准则根据每个投资的预期回报和风险来确定资本的最优分配。应用步骤如下:

  1. 计算超额收益: 对于每个策略,通过从实际回报中减去无风险利率来计算超额收益。

  2. 估计均值和方差: 基于历史数据或其他合适的方法,为每个策略估计超额收益的均值和方差。

  3. 应用公式: 使用公式 fi = μi/σi² 来计算每个策略的最优分配。

  4. 规范化分配: 确保所有分配的总和等于 1(或 100%),通过对 fi 值进行规范化来实现。这一步是必要的,以确保所有资本都分配给了各个策略。

  5. 监控和调整: 由于市场条件会发生变化,因此需要监控策略的性能,并相应地调整分配。这可能涉及重新估计均值和方差,或定期重新平衡分配。

凯利公式提供了理论上的最优分配。

另外, 凯利公式提供了最优的杠杆和策略分配,但它并不直接告诉投资组合的长期复合增长率是多少。为了计算这个期望增长率(通常表示为 (g)),可以使用如下公式进行计算:

g = r +S^2/2
r:无风险利率,通常与某个国债或政府债券的利率相关,因为这些通常被认为是无风险的。
S:策略的年化夏普比率(Sharpe Ratio)。

其背后的理念是,投资组合的长期增长率不仅与无风险利率有关,还与其风险调整后的表现(即夏普比率)有关。夏普比率越高,投资组合相对于其风险的表现越好,因此预期的长期增长率也越高。

# 假设股票a 年化平均收益为 8%,标准差为 12%, 无风险利率 为3%,
stock_return = 0.08
risk_free_return = 0.03
annual_standard_deviation = 0.12
ui = stock_return - risk_free_return
sharp_ratio = ui / annual_standard_deviation
## sharp ratio
print(f"sharp_ratio:{sharp_ratio:.2f}")
optimal_kelly_leverage = ui/annual_standard_deviation/annual_standard_deviation
print(f"最优杠杆为:{optimal_kelly_leverage:.2f}")
expect_return = risk_free_return + sharp_ratio* sharp_ratio /2 
print(f"预期收益为:{expect_return:.2f}")
sharp_ratio:0.42
最优杠杆为:3.47
预期收益为:0.12

计算出来最优秀杠杆为1.39, 即你有10000本金的话,在不考虑交易成本等因素的情况下,做多可以借入(3.47-1)*10000 = 24700 元进行交易,且收益率也不是股票的历史年华收益,而是12%。

凯利公式要求持续调整资本配置以保持其有效性。在实际交易的离散环境中,这是不可能实现的,这里的标准“经验法则”是每天更新一次凯利配置。此外,应定期使用最新周期数据的平均值和标准偏差重新计算凯利准则本身。对于每天大约交易一次的策略,可以使用3-6个月的每日回报。

风险管理

估算算法交易策略或策略组合的损失风险对于长期资本增长极为重要。有一种特别的技术被称为风险价值(Value at Risk,VaR)。VaR在给定置信度下,提供了对特定时间段内投资组合可能发生的损失规模的估计。

# 使用方差-协方差方法来计算投资组合的VaR

import numpy as np  
import pandas as pd  
from scipy.stats import norm  
  
# 假设我们有两个资产的历史日收益率数据  
astock = ak.stock_zh_a_hist(symbol='603023', period="daily", start_date="20220101", end_date='20231231', adjust="hfq")
astock.index = astock['日期']
bstock = ak.stock_zh_a_hist(symbol='601916', period="daily", start_date="20220101", end_date='20231231', adjust="hfq")
bstock.index = bstock['日期']
#print(bstock)
returns = pd.DataFrame({})
returns['Asset1'] = astock['收盘'].pct_change()
returns['Asset2'] = astock['收盘'].pct_change()
  
# 计算资产的均值  
mean_returns = returns.mean()  
  
# 计算资产的协方差矩阵  
cov_matrix = returns.cov()  
  
# 定义投资组合的权重(这里假设是等权重)  
portfolio_weights = np.array([0.5, 0.5])  
  
# 计算投资组合的预期收益率  
expected_portfolio_return = np.sum(mean_returns * portfolio_weights)  
  
# 计算投资组合的方差  
portfolio_variance = np.dot(portfolio_weights.T, np.dot(cov_matrix, portfolio_weights))  
  
# 计算投资组合的标准差  
portfolio_std_dev = np.sqrt(portfolio_variance)  
  
# 定义置信水平  
confidence_levels = [0.90, 0.95, 0.99]  
  
# 计算不同置信水平下的VaR  
for confidence_level in confidence_levels:  
    z_score = norm.ppf(confidence_level)  # 正态分布的z分数  
    VaR = portfolio_std_dev * z_score  
    print(f"VaR at {confidence_level * 100}% confidence level: {VaR:.4f}")  
    
# 输出投资组合的预期收益  
print(f"Expected Portfolio Return: {expected_portfolio_return:.4f}")  
  
# 输出投资组合的标准差  
print(f"Portfolio Standard Deviation: {portfolio_std_dev:.4f}")
VaR at 90.0% confidence level: 0.0360
VaR at 95.0% confidence level: 0.0462
VaR at 99.0% confidence level: 0.0653
Expected Portfolio Return: 0.0006
Portfolio Standard Deviation: 0.0281

Ref:

  1. https://zhuanlan.zhihu.com/p/491841440
  2. Successful Algorithmic Trading
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容