如何用Pandas Dataframe计算策略总盈亏(PnL)?持仓不重复累计
策略总盈亏(PnL)计算问题解决
问题背景
现有一份Pandas DataFrame,close为收盘价,entry_prices为开仓时的收盘价;平仓后entry_prices变为NaN,直至再次开仓。需计算策略总盈亏(PnL),要求:
- 持仓期间不重复累计盈亏(仅显示当前持仓浮盈+历史平仓总盈亏)
- 回测时假设任何时刻仅持有1手仓位
原始DataFrame
import pandas as pd import numpy as np df = pd.DataFrame({ 'close': [98, 101, 106, 107, 110, 111, 120, 123, 124, 123, 122, 123, 120, 121, 125], 'entry_prices': [np.nan, 101.0, 101.0, 101.0, 101.0, 101.0, np.nan, np.nan, np.nan, 123.0, 123.0, 123.0, 123.0, np.nan, np.nan] })
用户尝试的错误代码
df['pnl'] = np.where(df['entry_prices'].shift() == df['entry_prices'], df['close'] - df['entry_prices'], 0) # Initialize strategy_pnl column df['strategy_pnl'] = np.nan # Calculate the cumulative sum of total P&L regardless of trading positions cumulative_pnl = 0 for idx, row in df.iterrows(): if not np.isnan(row['entry_prices']): pnl = row['close'] - row['entry_prices'] cumulative_pnl += pnl df.at[idx, 'strategy_pnl'] = cumulative_pnl
这段代码的问题是持仓期间会重复累加每日浮盈,导致总盈亏计算混乱。
期望输出结果
close entry_prices Total_PNL 0 98 NaN 0 1 101 101.0 0 2 106 101.0 5 3 107 101.0 6 4 110 101.0 9 5 111 101.0 10 6 120 NaN 10 7 123 NaN 10 8 124 NaN 10 9 123 123.0 10 10 122 123.0 9 11 123 123.0 9 12 120 123.0 6 13 121 NaN 6 14 125 NaN 6
正确解决方案
核心思路:识别每笔交易的开平仓区间,仅在平仓时记录该笔交易的固定盈亏,持仓时显示当前浮盈加历史平仓累计盈亏。
# 1. 标记交易区间:对非NaN的entry_prices分组,NaN设为0 df['trade_group'] = df['entry_prices'].notna().cumsum() df['trade_group'] = np.where(df['entry_prices'].isna(), 0, df['trade_group']) # 2. 计算每笔交易的平仓盈亏(仅在平仓时刻记录该笔最终盈亏) df['close_pnl'] = np.where( # 平仓条件:当前为NaN,前一行处于持仓状态 df['entry_prices'].isna() & df['entry_prices'].shift().notna(), # 用平仓前最后收盘价减开仓价 df['close'].shift() - df['entry_prices'].shift(), 0 ) # 3. 计算历史平仓总盈亏的累计值 df['cum_close_pnl'] = df['close_pnl'].cumsum() # 4. 计算当前持仓浮盈:持仓时用当前收盘价减开仓价,否则为0 df['holding_pnl'] = np.where(df['entry_prices'].notna(), df['close'] - df['entry_prices'], 0) # 5. 总盈亏 = 历史平仓累计盈亏 + 当前持仓浮盈 df['Total_PNL'] = df['cum_close_pnl'] + df['holding_pnl'] # 处理初始值,填充为0并转为整数 df['Total_PNL'] = df['Total_PNL'].fillna(0).astype(int) # 可选:删除中间辅助列 df = df.drop(['trade_group', 'close_pnl', 'cum_close_pnl', 'holding_pnl'], axis=1) print(df)
运行后即可得到符合要求的结果。
内容的提问来源于stack exchange,提问作者Ron
相关产品推荐
相关产品推荐

