基于Python和Pandas的交易策略回测:实现单一持仓控制
针对问题的解决方案
你的核心问题在于没有跟踪当前持仓状态——代码不知道现在是不是已经持有仓位,所以会在持仓期间不断触发新的买入信号。我们可以通过添加状态变量来解决这个问题,以下是优化后的完整代码:
import pandas as pd # 先把日期转成pandas可识别的datetime格式,避免后续出错 data = { 'date': pd.to_datetime([ '1/3/2000','1/4/2000','1/5/2000','1/6/2000','1/7/2000','1/10/2000','1/11/2000','1/12/2000','1/13/2000','1/14/2000', '1/18/2000','1/19/2000','1/20/2000','1/21/2000','1/24/2000','1/25/2000','1/26/2000','1/27/2000','1/28/2000','1/31/2000', '2/1/2000','2/2/2000','2/3/2000','2/4/2000','2/7/2000','2/8/2000','2/9/2000','2/10/2000','2/11/2000','2/14/2000', '2/15/2000','2/16/2000','2/17/2000','2/18/2000','2/22/2000','2/23/2000','2/24/2000','2/25/2000','2/28/2000','2/29/2000' ]), 'close': [308.3,315.3,314.4,307.5,309.8,313.4,310.7,324.2,332.5,348.8,351.1,348.2,348.7,343.5,343,343.3,342.4,343,334.4,334.6,336,333.8,331.6,332.8,335.9,341.2,338.4,342.1,343.2,339.5,346.9,342,339.6,337.4,335,330.8,331.3,331.1,332.6,335.1] } df = pd.DataFrame(data) # 生成高低点标记(和你原代码逻辑一致) df['prev_close'] = df['close'].shift(1) df['next_close'] = df['close'].shift(-1) df['high_high'] = (df['prev_close'] > df['close']) & (df['next_close'] > df['close']) # 局部高点(卖出基准) df['low_low'] = (df['prev_close'] < df['close']) & (df['next_close'] < df['close']) # 局部低点(买入基准) # 填充基准价格:保留最近的有效基准价 df['comp_price'] = df['close'].where(df['low_low'] == True).ffill() # 买入基准价 df['sell_comp'] = df['close'].where(df['high_high'] == True).ffill() # 卖出基准价 # 新增持仓状态列,初始为False(空仓) df['in_position'] = False df['buy_sig'] = False df['sell_sig'] = False # 逐行遍历,根据持仓状态生成有效信号 for i in range(1, len(df)): prev_state = df.iloc[i-1]['in_position'] current_close = df.iloc[i]['close'] current_buy_price = df.iloc[i]['comp_price'] current_sell_price = df.iloc[i]['sell_comp'] if not prev_state: # 空仓状态:只检查买入信号 if current_close > current_buy_price: df.at[i, 'buy_sig'] = True df.at[i, 'in_position'] = True else: df.at[i, 'in_position'] = False else: # 持仓状态:只检查卖出信号 if current_close < current_sell_price: df.at[i, 'sell_sig'] = True df.at[i, 'in_position'] = False else: df.at[i, 'in_position'] = True # 标记开平仓点 df['open_pos'] = df['buy_sig'] df['close_pos'] = df['sell_sig'] # 填充开仓信息到持仓期间的所有行 df['open_pos_date'] = df['date'].where(df['open_pos']).ffill() df['open_pos_price'] = df['close'].where(df['open_pos']).ffill() # 提取最终的完整交易记录(每个开仓对应唯一平仓) strat_df = df.loc[df['close_pos'], ['open_pos_date','open_pos_price', 'date','close']] strat_df.rename(columns={'date':'close_pos_date', 'close':'close_pos_price'}, inplace=True) strat_df['gain'] = strat_df['close_pos_price'] - strat_df['open_pos_price'] print(strat_df)
关键优化点说明
- 持仓状态跟踪:新增
in_position列,用布尔值记录当前是否持有仓位,这是解决多持仓问题的核心——空仓时才响应买入信号,持仓时只关注卖出信号。 - 逐行逻辑判断:不再依赖向量运算批量生成信号,而是逐行根据前一天的持仓状态决定当天的信号逻辑,从根源上避免了持仓期间触发新买入信号。
- 日期格式修正:将原日期字符串转换为
pd.datetime格式,避免后续日期处理出现异常。 - 交易记录清理:最终生成的
strat_df只会保留完整的开平仓配对,不会出现一个开仓对应多个平仓的情况,完全符合你的需求。
内容的提问来源于stack exchange,提问作者serranzau
相关产品推荐
相关产品推荐

