风险情景树的期望 PnL
Expected PnL on a Risk-Officer Scenario Tree
开始编码风险官给出一棵刻画下季度 PnL 走向的通用情景树。根节点为 {"prob": 1.0, "children": [...]};每个非叶节点都带一个 prob(条件概率——已经到达父节点的前提下到达本节点的概率,不是全局/联合概率)以及一个 children 列表,列表里所有 prob 之和为 1.0;每个叶节点带一个 prob 和一个 pnl(一个有限 float——该情景下实际兑现的 PnL)。请用如下递推求出根节点的期望 PnL:
并返回单个 float。根节点自己的 prob(1.0)只是惯例标记——答案就是 E[\text{root}] 本身。请实现 solution(tree) -> float,用一次递归 DFS;输入树形就是上述递归 dict。
校验合约。 每个非叶节点必须满足 abs(sum(child.prob for child in children) - 1.0) <= 1e-9,否则输入非法,必须 raise ValueError。空 children 列表也是非法(一个没有任何延续的非叶节点没有意义——如果它没有续推,就该写成带 pnl 的叶子),同样 raise ValueError。两个校验都要在求和 child.prob * E[child] 之前完成。递推用的是条件的 child.prob,不是从根节点累乘出来的联合概率。
例
solution({
"prob": 1.0,
"children": [
{"prob": 0.6, "pnl": 100.0},
{"prob": 0.4, "pnl": -50.0}
]
})返回 40.0:根节点下挂两个叶子,60% 概率拿到 +100、40% 概率拿到 -50,所以 E = 0.6 * 100 + 0.4 * (-50) = 60 - 20 = 40.0。把条件概率换成均匀的 1/k = 0.5,结果会变成 0.5 * 100 + 0.5 * (-50) = 25.0,差出来的 15.0 远超 1e-9 的比较器容差。
实践背景
情景树期望是 quant 桌每季度风险会上的标配「资本影响估算」工具。风险官写下一棵这样的树:「rates 上 50bps 概率 0.2、不动概率 0.6、下行概率 0.2;然后在每种利率情形下,credit-spread 走宽 / 收窄按各自子概率分裂;再然后股票端在每个分支里出最终冲击」——桌上要的就是一个数:整棵树上的期望 PnL。生产中的树通常层数不深(3–6 层),但每层都很宽;条件概率递推让数学保持干净——每个分支贡献 prob * E[child],与到达它的路径无关。同样的 backsolve 几乎不用改就能套到信用迁移矩阵(Markov 转移树)、实物期权分析里的嵌套决策树,以及多项情景 VaR 的期望 payoff 阶段。
约束条件
- 入参 `tree` 是一个嵌套 `dict`,结构如下:根节点是 `{"prob": 1.0, "children": [...]}`;每个非叶节点有 `prob`(float)和 `children`(非空节点列表);每个叶节点有 `prob`(float)和 `pnl`(有限 float)。
- 每个非叶节点都要满足 `abs(sum(child.prob for child in children) - 1.0) <= 1e-9`,否则 `raise ValueError`。空的 `children` 列表也是非法输入,同样 `raise ValueError`。
- 所有 `pnl` 都是有限 float;树深不超过 50,节点总数不超过 1000;输出是 `solution(tree)` 返回的单个 Python `float`。
- 实现必须是 `O(N)` 时间(`N` 为节点总数),单次递归 DFS;**不要**枚举所有根到叶路径再乘联合概率。
- 返回:单个 float,即根节点的期望 PnL;浮点容差 rel_tol `1e-9`、abs_tol `1e-12`。
样例
Case 1 · trivial single-leaf tree (one outcome)
输入: [{"prob":1,"children":[{"prob":1,"pnl":12500}]}]
期望: 12500
退化情形:只有一个分支,全概率落在唯一叶子,因此 E = pnl = 12500。
Case 2 · two-branch root: up vs down
输入: [{"prob":1,"children":[{"prob":0.6,"pnl":100},{"prob":0.4,"pnl":-50}]}]
期望: 40
标准两分支:0.6 * 100 + 0.4 * (-50) = 40。
Case 3 · three-branch flat root: rates up/flat/down
输入: [{"prob":1,"children":[{"prob":0.2,"pnl":250000},{"prob":0.6,"pnl":0},{"prob":0.2,"pnl":-300000}]}]
期望: -10000
最近提交
还没有提交记录。
编码区
实现 solution(...)。本地运行当前支持 Python 可见样例;服务端提交会运行可见样例和隐藏测试。
默认展示公开样例。点击「运行样例」后会在这里显示实际输出;点击「提交评测」会进入隐藏测试。
Case 1 · trivial single-leaf tree (one outcome)
输入: [{"prob":1,"children":[{"prob":1,"pnl":12500}]}]
期望: 12500
退化情形:只有一个分支,全概率落在唯一叶子,因此 E = pnl = 12500。
Case 2 · two-branch root: up vs down
输入: [{"prob":1,"children":[{"prob":0.6,"pnl":100},{"prob":0.4,"pnl":-50}]}]
期望: 40
标准两分支:0.6 * 100 + 0.4 * (-50) = 40。
Case 3 · three-branch flat root: rates up/flat/down
输入: [{"prob":1,"children":[{"prob":0.2,"pnl":250000},{"prob":0.6,"pnl":0},{"prob":0.2,"pnl":-300000}]}]
期望: -10000