← 返回编程题库
coding-scenario-tree-expected-pnl中等免费版500ms未尝试

风险情景树的期望 PnL

Expected PnL on a Risk-Officer Scenario Tree

开始编码

风险官给出一棵刻画下季度 PnL 走向的通用情景树。根节点为 {"prob": 1.0, "children": [...]};每个非叶节点都带一个 prob条件概率——已经到达父节点的前提下到达本节点的概率,不是全局/联合概率)以及一个 children 列表,列表里所有 prob 之和为 1.0;每个叶节点带一个 prob 和一个 pnl(一个有限 float——该情景下实际兑现的 PnL)。请用如下递推求出根节点的期望 PnL

E[node]={node.pnl当 node 是叶子cchildrenc.probE[c]否则 E[\text{node}] = \begin{cases} \text{node.pnl} & \text{当 node 是叶子} \\ \sum_{c \in \text{children}} c.\text{prob} \cdot E[c] & \text{否则} \end{cases}

并返回单个 float。根节点自己的 prob1.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 可见样例;服务端提交会运行可见样例和隐藏测试。

加载编辑器...
计时0:00

默认展示公开样例。点击「运行样例」后会在这里显示实际输出;点击「提交评测」会进入隐藏测试。

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