diff --git a/src/quant_engine/execution.py b/src/quant_engine/execution.py index a69e77e..5d38ec3 100644 --- a/src/quant_engine/execution.py +++ b/src/quant_engine/execution.py @@ -422,6 +422,50 @@ class ExecutionSimulationResult: ] return pd.DataFrame(rows, columns=columns) + @property + def ledger_frame(self) -> pd.DataFrame: + """返回稳定的日频 Ledger 投影,不附加运行元数据或写数据库。""" + columns = [ + "trade_date", + "portfolio_value", + "nav", + "pnl", + "pnl_pct", + "position_value", + "cash", + "turnover", + ] + previous_value = self.initial_cash + rows: list[dict[str, float | str]] = [] + daily_returns = self.daily_returns + for index, (position, daily) in enumerate( + zip(self.positions, self.daily_executions, strict=True) + ): + daily_turnover = sum( + execution.executed_value + for execution in daily.executions + if execution.quantity > 0 + ) + turnover_rate = daily_turnover / daily.nav_before if daily.nav_before > 0 else 0.0 + rows.append( + { + "trade_date": position.date, + "portfolio_value": position.portfolio_value, + "nav": ( + position.portfolio_value / self.initial_cash + if self.initial_cash != 0 + else 0.0 + ), + "pnl": position.portfolio_value - previous_value, + "pnl_pct": float(daily_returns.iloc[index]), + "position_value": position.portfolio_value - position.cash, + "cash": position.cash, + "turnover": turnover_rate, + } + ) + previous_value = position.portfolio_value + return pd.DataFrame(rows, columns=columns) + @property def total_costs(self) -> float: """汇总实际成交产生的成本。"""