如何用 Vibe-Trading 的 cashflow_performance 为基金现金流算出 XIRR/MOIC/DPI 报告?
【免费下载链接】Vibe-Trading"Vibe-Trading: Your Personal Trading Agent"项目地址: https://gitcode.com/GitHub_Trending/vi/Vibe-Trading
你要完成的任务是:手里有一只基金(或客户账户)的日期化现金流——若干期估值(NAV)、期间申购/赎回(capital call、distribution),需要算出 LP 报告口径的 XIRR(资金加权年化回报)、MOIC、DPI,并顺带拿到 TWR(时间加权)与 Modified Dietz 作为交叉对照。Vibe-Trading 的cashflow_performance工具专门处理这条路径:它接收估值序列加不规则现金流,输出只读计算信封;配套的src/quantlib/fundmath模块提供 DPI/RVPI/TVPI/MOIC 的基金倍数计算。两者都标注为 research-only,不产生任何订单(cashflow_analytics_tool.py 源文件开头明确声明 "No order path exists here")。
前提:已按 README.md 安装:
pip install vibe-trading-ai该工具在 CLI、Web UI、REST API 和 MCP 各入口均可用,无需额外 API key(SKILL.md 工具表中cashflow_performance一行的依赖列为 None)。
准备数据:估值序列 + 现金流,符号约定最重要
cashflow_performance要求两类输入:
valuations(必填):[{date, value}]数组,最早在前,至少 2 条;两条即得单期结果,中间加估值点可让 TWR 精确到分段。上限 4000 条。- 现金流二选一:
flows(内联数组,每条{date, amount, kind},上限 4000 条)或flows_path(指向.csv/.tsv/.txt文件)。两者互斥,同时给出会直接报not both错误。
三条来自 cashflow.py 模块文档的硬约定:
- 持仓人视角符号:
amount > 0表示现金流入(distribution、dividend),amount < 0表示现金流出(申购、capital call)。符号错误会被拒绝而不是静默接受,例如 contribution 传正数会返回含negative (cash out)的错误信封。 - 币种必填且不可混用:内联现金流要么每条自带
currency,要么传顶层currency;"currency is never defaulted"——缺失时返回no currency错误。 - 估值不是现金:
nav/residual_value/valuation三种 kind 记录的是估值标记,不计入现金流合计。
外部/内部流的默认分类(cashflow_analytics_tool.py 工具描述):外部流为 contributions、capital calls、subscriptions、transfers、distributions、redemptions、withdrawals;dividends、coupons、interest、fees 属于内部流,视为已包含在估值序列里。文件里出现不在两张名单里的自定义 kind(比如wire_in)会报neither external nor internal,此时用external_kinds/internal_kinds参数显式归类即可。
读取文件时的辅助参数:flows_columns(把标准字段映射到文件列名,如{"date": "Payment Date", "amount": "Net"})、flows_date_format(非 ISO-8601 日期列的 strptime 格式)、flows_default_kind(kind 列缺失时的兜底)、flows_invert_sign(对文件导出把申购记为正数的场景整体取反)。flow_timing取"end"(默认,流水在当日估值后到账)或"start"(下一区间前到账),只影响 TWR。
调用 cashflow_performance:一条主路径
通过 agent 会话发起(CLI 示例,README Quick Example 同款模式):
vibe-trading run -p "用 cashflow_performance 计算一个基金账户:估值 2024-01-01:100、2024-07-01:1010、2024-12-31:909;现金流 2024-07-01 申购 -900 USD(kind=contribution)、2024-09-01 分红 +40 USD(kind=dividend)。报告 XIRR、TWR 和 Modified Dietz,并给出分段收益"(上面的数值取自仓库测试夹具 test_cashflow_analytics_tool.py,仅作示例,替换成你自己的账户数据即可。)
MCP / API 入口则直接按工具 schema 传参,核心调用等价于:
{ "valuations": [ {"date": "2024-01-01", "value": 100.0}, {"date": "2024-07-01", "value": 1010.0}, {"date": "2024-12-31", "value": 909.0} ], "flows": [ {"date": "2024-07-01", "amount": -900.0, "kind": "contribution", "currency": "USD"}, {"date": "2024-09-01", "amount": 40.0, "kind": "dividend", "currency": "USD"} ] }长历史不要内联,改用文件路径。仓库测试中用的 CSV 形态(文档示例,client_flows.csv):
date,amount,kind,currency 2024-07-01,-900.00,contribution,USD 2024-09-01,40.00,dividend,USD此时调用里放flows_path指向该文件即可;dividend行会被识别为内部流,不计入net_external_flow。
读结果信封:哪里是 XIRR
成功时返回status: "ok"的 JSON 信封,报告口径在summary:
time_weighted_return/time_weighted_annualized——TWR,衡量管理人能力,不受流水时点影响;sub_periods给出每个分段的起止与return;modified_dietz_return——Modified Dietz,资金加权的日权重近似(源文件注释强调它是 money-weighted approximation,不是"更便宜的 TWR");money_weighted_return_annualized/money_weighted_return_period——这就是工具描述中的money-weighted XIRR(客户实际体验),money_weighted对象里还有years和solver_iterations。
用上面测试夹具的数值,文档示例结果是:sub_periods分段收益[0.10, -0.10],time_weighted_return ≈ -0.01,net_external_flow = 900.0,Modified Dietz 与 money-weighted 年化均低于 -10%(客户在低点大额申购,资金回报远差于管理人单位资本回报)。这些是仓库测试断言的示例值,你的账户会不同。
两个边界行为:
- 无解不报错:单向现金流(例如只投不赎、期末全损)不存在 IRR,此时
money_weighted_return_annualized为null,notes中记一条 "one-directional" 说明,但 TWR 和 Dietz 照常返回,status仍是ok。 - 输入错误是显式信封:
valuations少于 2 条(at least two)、flow_timing取值非法、同时传flows和flows_path、币种缺失、符号违反约定、文件不存在——全部返回status: "error"加可读消息,从不静默返回空结果。 - 信封末尾固定带
limitations:流水是持仓人视角(contribution 为负)、分红/票息/费用假定已在估值内、收益是未扣除估值外费用的 gross 口径、不含税和货币折算。写报告时照抄这一段作为口径声明即可。
MOIC / DPI / TVPI:同一现金流走 fundmath 路径
倍数计算在 fundmath.py(src/quantlib/fundmath),README 对这条路径的定位是 "src/entitiesingests irregular dated cash flows (NAVs, capital calls, coupons) andcashflow_performancereports XIRR / MOIC / DPI / TVPI / TWR / Modified Dietz / MWR over them"。倍数公式与类型分类在该模块文档中明确给出:
- DPI= distributed / paid_in;RVPI= 最新 NAV 标记 / paid_in;TVPI= DPI + RVPI(实现上保证恒等);MOIC= (distributed + 最新 NAV) / 分母,分默认为 paid-in,此时等于 TVPI,传
invested_capital可换分母; - 默认计入投入的 kind(
CONTRIBUTION_KINDS):contribution、capital_call、subscription;默认计入返还的 kind(DISTRIBUTION_KINDS):distribution、dividend、redemption、proceeds;管理费在承诺外收取的基金应传contribution_kinds=(*CONTRIBUTION_KINDS, "fee")。
注意一个真实差异:cashflow_performance把dividend视为内部流(已在估值里),而fundmath默认把dividend计入 distributions。同一份文件在两条路径下口径不同,出报告前必须选定一边,不要混用结果。
在装有该仓库代码/包的 Python 环境里(CLI 的 bash 路径可import,quantlib_call工具文档也说明 bash+import 是数学层的既有入口,见 quantlib_tool.py 模块注释),最小调用是:
from src.entities.cashflow import CashFlow, CashFlowSeries from src.quantlib.fundmath import fund_multiples, xirr, xirr_all series = CashFlowSeries(( CashFlow(date="2024-01-01", amount=-100.0, kind="contribution", currency="USD"), CashFlow(date="2024-07-01", amount=-900.0, kind="contribution", currency="USD"), CashFlow(date="2024-09-01", amount=40.0, kind="dividend", currency="USD"), CashFlow(date="2024-12-31", amount=909.0, kind="nav", currency="USD"), )) m = fund_multiples(series) print(m.dpi, m.rvpi, m.tvpi, m.moic) print(xirr(series)) # 默认取最小根 print(xirr_all(series)) # 先确认是否存在多个根(示例数值取自仓库测试夹具,替换为你的数据。)
按文档公式手工核对(这是公式推导,不是程序输出):paid_in = 1000,distributed = 40,最新 NAV = 909,于是 DPI = 0.04、RVPI = 0.909、TVPI = 0.949、MOIC(paid-in 分母)= 0.949,且 TVPI = DPI + RVPI 恒等。xirr的日期分母默认 365(与 ExcelXIRR一致);它先做带区间的网格扫描,多根时返回最小根,怀疑存在多个 IRR(可回拨的分红、救援融资)时先用xirr_all查看全部根再决定是否可信。fund_multiples会在未出资(paid_in ≤ 0)、符号违反约定、最新 NAV 为负时抛错而不是给出 0 或 inf。
报错现象与适用边界
排查时按信封消息定位:
pass either flows or flows_path, not both——两个现金流来源只保留一个;no currency——给内联流水补currency或顶层币种;negative (cash out)——contribution 传了正数,改回持仓人视角负值,或文件侧用flows_invert_sign;neither external nor internal——自定义 kind 用external_kinds/internal_kinds归类;money-weighted return not reported(notes 里)——单向现金流无 IRR,属账户属性而非故障,另外两个指标仍可用。
适用边界:这是纯研究面,无下单入口;返回的是 gross 口径,估值外费用、税、货币折算都不在计算内;quantlib_call走模块白名单且拒绝export_*写文件入口,导出报告文件要用write_file。
下一步
同一fundmath模块还提供 PME(ks_pme/pme_plus)、欧式/美式 waterfall 与 GP clawback,以及xirr之外的npv逐利率折现;需要相对基准比较或分配瀑布拆分时,从quantlib_call的list/describe(module="fundmath")取签名再调用即可。
【免费下载链接】Vibe-Trading"Vibe-Trading: Your Personal Trading Agent"项目地址: https://gitcode.com/GitHub_Trending/vi/Vibe-Trading
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考