基于资金流入流出计算账户收益率的Python实现问询
计算排除资金流动影响的账户业绩收益率
问题描述
现有两个DataFrame:
- 账户余额DataFrame:包含
Date(估值日)和Balance(账户总余额)两列 - 资金流动DataFrame:包含
Date和Movement(当日资金存入/取出金额,存入为正,取出为负)两列
需要计算每日账户业绩收益率,要求排除当日资金流动对余额的影响,具体规则:
- 当日业绩 = (当日余额 - 调整后的期初余额) / 调整后的期初余额
- 调整后的期初余额规则:
- 对于第n天(n>1),若第n-1天有资金流动,调整后的期初余额 = 第n-1天余额 + 第n-1天资金流动
- 若第n-1天无资金流动,调整后的期初余额 = 第n-1天余额
示例数据
账户余额DataFrame
| Date | Balance |
|---|---|
| 05/06/2023 | 100 |
| 06/06/2023 | 110 |
| 07/06/2023 | 90 |
| 08/06/2023 | 120 |
资金流动DataFrame
| Date | Movement |
|---|---|
| 07/06/2023 | 20 |
期望结果
| Date | Performance |
|---|---|
| 05/06/2023 | N/A |
| 06/06/2023 | 10% |
| 07/06/2023 | -18.2% |
| 08/06/2023 | 9.1% |
计算逻辑说明:
- 06/06/2023:
(110-100)/100 = 10% - 07/06/2023:无前期资金流动,
(90-110)/110 ≈ -18.2% - 08/06/2023:前一天(07/06)有资金流入20,调整后期初余额=90+20=110,
(120-110)/110≈9.1%
Pythonic解决方案
使用pandas可以高效实现需求,步骤如下:
1. 导入库并准备数据
import pandas as pd # 账户余额数据 balance_df = pd.DataFrame({ 'Date': ['05/06/2023', '06/06/2023', '07/06/2023', '08/06/2023'], 'Balance': [100, 110, 90, 120] }) # 资金流动数据 movement_df = pd.DataFrame({ 'Date': ['07/06/2023'], 'Movement': [20] }) # 将Date列转为日期类型(确保排序正确) balance_df['Date'] = pd.to_datetime(balance_df['Date'], format='%d/%m/%Y') movement_df['Date'] = pd.to_datetime(movement_df['Date'], format='%d/%m/%Y')
2. 合并两个DataFrame
将资金流动数据合并到余额数据中,无流动的日期填充0:
# 按Date合并,无Movement的日期填充0 merged_df = pd.merge(balance_df, movement_df, on='Date', how='left').fillna(0)
3. 计算调整后的期初余额
通过shift()获取前一天的余额和资金流动,计算调整后的期初余额:
# 获取前一天的余额和资金流动 merged_df['prev_balance'] = merged_df['Balance'].shift(1) merged_df['prev_movement'] = merged_df['Movement'].shift(1) # 计算调整后的期初余额:前一天余额 + 前一天资金流动 merged_df['adjusted_start_balance'] = merged_df['prev_balance'] + merged_df['prev_movement']
4. 计算业绩收益率
基于调整后的期初余额计算收益率,并格式化百分比:
# 计算收益率,首日为N/A merged_df['Performance'] = merged_df.apply( lambda row: (row['Balance'] - row['adjusted_start_balance']) / row['adjusted_start_balance'] if pd.notna(row['adjusted_start_balance']) else 'N/A', axis=1 ) # 格式化百分比(保留1位小数) merged_df['Performance'] = merged_df['Performance'].apply( lambda x: f"{x*100:.1f}%" if isinstance(x, float) else x )
5. 整理最终结果
提取需要的列并按日期排序:
final_df = merged_df[['Date', 'Performance']].sort_values('Date') # 转换日期格式为原格式 final_df['Date'] = final_df['Date'].dt.strftime('%d/%m/%Y') print(final_df)
运行后输出结果:
Date Performance 0 05/06/2023 N/A 1 06/06/2023 10.0% 2 07/06/2023 -18.2% 3 08/06/2023 9.1%
关键说明
- 使用
shift()高效获取前一天的数据,避免循环,符合Pythonic风格 - 通过合并填充确保所有日期都有资金流动数据(无流动为0)
- 利用
apply()和lambda函数简洁实现收益率计算和格式化
内容的提问来源于stack exchange,提问作者Hotone
相关产品推荐
相关产品推荐

