From 2c1689089d2465b2d5fe6f896f52d7916fcd34a1 Mon Sep 17 00:00:00 2001 From: aszerW Date: Sat, 16 May 2026 00:34:12 +0800 Subject: [PATCH] =?UTF-8?q?revert(execution):=20=E6=81=A2=E5=A4=8D?= =?UTF-8?q?=E5=8A=A8=E6=80=81=E6=9D=83=E9=87=8D=E4=BB=93=E4=BD=8D=E5=88=86?= =?UTF-8?q?=E9=85=8D=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 恢复原逻辑: 按实际持仓数量等权分配 - 选出2只时每只权重50%,选出1只时权重100% - 收益计算恢复为 np.mean(returns) - 交易成本恢复为 swapped/len(old) - 固定仓位逻辑记录在 docs/experiments/仓位分配逻辑修改分析.md --- framework/execution/__init__.py | 34 ++++++++------------------------- 1 file changed, 8 insertions(+), 26 deletions(-) diff --git a/framework/execution/__init__.py b/framework/execution/__init__.py index 47958ba..671c131 100644 --- a/framework/execution/__init__.py +++ b/framework/execution/__init__.py @@ -228,29 +228,18 @@ class BacktestExecutor(Executor): result['策略日收益率'] = result.apply(calc_return, axis=1) else: - # 多标的策略(固定仓位分配) - # 核心逻辑:按select_num固定分配仓位,缺失标的用现金替代 - # 例如:select_num=3,选出2只标的 → 权重=1/3+1/3,现金权重=1/3(收益为0) + # 多标的策略(等权组合) + # 按实际持仓数量等权分配:选出2只时每只50%,选出1只时100% def calc_multi_return(row): codes = [c for c in row[signal_col].split(',') if c] if not codes: - # 空仓:全部现金,收益为0 return 0.0 - - # 固定仓位权重:每只标的权重 = 1 / select_num - unit_weight = 1.0 / self.select_num - - # 计算实际持仓收益(缺失标的用现金替代,收益为0) - total_return = 0.0 + returns = [] for c in codes: ret = data.loc[row.name, f'日收益率_{c}'] if f'日收益率_{c}' in data.columns else None if ret is not None and pd.notna(ret): - total_return += ret * unit_weight - # 如果数据缺失,视为现金(收益为0,不累加) - - # 缺失标的的仓位自动变成现金(收益为0) - # 总收益 = sum(实际持仓收益) + 0 * (缺失仓位) - return total_return + returns.append(ret) + return np.mean(returns) if returns else 0.0 result['策略日收益率'] = result.apply(calc_multi_return, axis=1) @@ -268,9 +257,7 @@ class BacktestExecutor(Executor): changed = (signals[signal_col] != prev_signal) & prev_signal.notna() result.loc[changed, '策略日收益率'] -= self.trade_cost else: - # 多标的策略:按固定仓位比例扣除成本 - # 核心逻辑:每只标的权重固定为1/select_num - # 换手率 = (调出数量 + 调入数量) / select_num + # 多标的策略:按换手率比例扣除成本 turnover_list = [] for curr, prev in zip(signals[signal_col], prev_signal): if pd.isna(prev) or curr == prev: @@ -278,13 +265,8 @@ class BacktestExecutor(Executor): else: old = set(prev.split(',')) new = set(curr.split(',')) - # 调出的标的数量(这些仓位需要卖出) - exit_count = len(old - new) - # 调入的标的数量(这些仓位需要买入) - enter_count = len(new - old) - # 换手率 = (卖出 + 买入) / select_num - # 每次调仓涉及的仓位比例 - turnover = (exit_count + enter_count) / self.select_num + swapped = len(old - new) + turnover = swapped / len(old) if old else 0.0 turnover_list.append(turnover) result['换手率'] = turnover_list