使用backtesting.py回测单CSV交易数据:交易仅执行5笔的问题
问题:Backtest.py回测仅触发5笔交易,无法执行更多重叠交易
- 需求:使用包含买卖日期、收盘价的单个CSV文件,替代价格数据、买入信号、卖出信号三个独立文件,基于backtesting.py开展回测
- 现状:回测仅执行5笔交易,无法触发更多重叠交易
- 已尝试操作:设置
hedging=True、exclusive_orders=False以允许重叠交易,配置日期校验规避节假日,问题仍未解决
核心问题分析
- 重复买入日期被覆盖:用
dict(zip(data['Buy Dates'], data['Sell Dates']))创建买卖映射时,CSV中重复的买入日期(如2015-09-10、2017-02-15)会被字典自动去重,导致后续对应的卖出日期丢失 - 买入信号被去重:
self.buy_dates = set(data['Buy Dates'])将重复买入日期转为唯一值,直接忽略了重复的买入信号 - 卖出信号匹配混乱:仅判断当前日期是否在卖出日期集合中,无法区分对应哪笔持仓,会错误关闭所有持仓而非指定配对的持仓
修复后的完整代码
import pandas as pd from backtesting import Backtest as OriginalBacktest, Strategy from io import StringIO from pandas.tseries.holiday import USFederalHolidayCalendar # 包含买卖日期和收盘价的CSV数据 csv_data = """ Buy Dates,Sell Dates,Buy Close,Sell Close 2010-01-15,2010-03-20,120.45,130.23 2011-02-25,2011-05-10,125.67,135.89 2012-08-21,2013-07-22,130.54,140.67 2014-05-23,2014-08-28,135.78,145.34 2015-09-10,2016-01-12,140.23,150.56 2017-02-15,2017-06-18,145.45,155.78 2013-04-18,2014-08-28,135.78,145.34 2015-09-10,2016-01-12,140.23,150.56 2017-02-15,2017-06-18,145.45,155.78 """ # 读取CSV数据 data = pd.read_csv(StringIO(csv_data), parse_dates=['Buy Dates', 'Sell Dates']) # 调整日期规避节假日和周末 cal = USFederalHolidayCalendar() holidays = cal.holidays(start=data['Buy Dates'].min(), end=data['Sell Dates'].max()) def adjust_date(date): while date in holidays or date.weekday() > 4: date += pd.Timedelta(days=1) return date data['Buy Dates'] = data['Buy Dates'].apply(adjust_date) data['Sell Dates'] = data['Sell Dates'].apply(adjust_date) # 构建回测用的股票数据 date_range = pd.date_range(start=data['Buy Dates'].min(), end=data['Sell Dates'].max(), freq='B') stock_data = pd.DataFrame(index=date_range, columns=['Open', 'High', 'Low', 'Close', 'Volume']) # 填充收盘价:匹配最近的买入收盘价,再做前后填充 def get_close_price(date): matching = data[data['Buy Dates'] <= date] if not matching.empty: return matching['Buy Close'].iloc[-1] return data['Buy Close'].iloc[0] stock_data['Close'] = stock_data.index.map(get_close_price) stock_data.ffill(inplace=True) stock_data.bfill(inplace=True) stock_data['Open'] = stock_data['Close'] stock_data['High'] = stock_data['Close'] stock_data['Low'] = stock_data['Close'] stock_data['Volume'] = 1000 # 设定固定成交量 # 定义策略:保留所有买卖配对,精准触发每笔交易 class LinkedTrade(Strategy): def init(self): # 保存所有买卖日期配对(保留重复项) self.buy_sell_pairs = list(zip(data['Buy Dates'], data['Sell Dates'])) # 用于跟踪已触发的买入和对应的卖出日期 self.active_positions = [] def next(self): current_date = self.data.index[-1] # 检查是否有需要触发的买入信号 for buy_date, sell_date in self.buy_sell_pairs: if buy_date == current_date: # 执行买入,并记录该笔持仓对应的卖出日期 position = self.buy() self.active_positions.append((position, sell_date)) # 检查是否有需要平仓的持仓 positions_to_remove = [] for idx, (position, sell_date) in enumerate(self.active_positions): if sell_date == current_date and position.is_long: position.close() positions_to_remove.append(idx) # 移除已平仓的持仓记录 for idx in reversed(positions_to_remove): del self.active_positions[idx] # 自定义Backtest类,传递hedging和exclusive_orders参数 class Backtest(OriginalBacktest): def __init__(self, data, strategy, *, cash=10000, commission=0.0, margin=1.0, trade_on_close=False, hedging=True, exclusive_orders=False): self.hedging = hedging self.exclusive_orders = exclusive_orders super().__init__(data, strategy, cash=cash, commission=commission, margin=margin, trade_on_close=trade_on_close) # 初始化回测 bt = Backtest(stock_data, LinkedTrade, cash=10_000, commission=.002, hedging=True, exclusive_orders=False) # 运行回测 stats = bt.run() print(stats) # 绘制回测结果 bt.plot() # 保存交易记录 trades = stats['_trades'] trades.to_csv('linked_trades.csv')
关键修复说明
- 保留所有交易配对:将买卖日期存储为列表而非字典/集合,完整保留CSV中的所有交易信号,包括重复的买入日期
- 跟踪每笔持仓的卖出日期:买入时记录该笔持仓对应的卖出日期,存入
active_positions列表,实现精准配对 - 精准平仓逻辑:遍历活跃持仓,仅在对应卖出日期到达时平仓该笔持仓,避免误操作其他持仓
- 修复价格填充逻辑:调整收盘价映射逻辑,确保价格序列更贴合实际交易场景
内容的提问来源于stack exchange,提问作者MMsmithH
相关产品推荐
相关产品推荐

