跳到正文

研究指南

股票组合与模拟研究

账户持续加钱怎样评价股票组合:同时复算时间加权与资金加权收益

账户余额上涨,不代表投资赚钱:投资者持续入金时,资产可能一边增加、一边亏损。解释结果需要从同一账本走两条计算链:在外部资金流之间计算并连乘子期回报,得到时间加权收益TWR;再用投资者带日期现金流求资金加权收益MWR。二者问题不同,但必须共享估值边界、期间和币种。

GIPS官方手册介绍子期几何链接,以及用内部收益率反映资金流大小和时点的方法。以下是原创个人账户算例,不是GIPS合规声明。它属于外部重算流程;只读聚合或模拟账户的存在,不能证明CoTrading已经提供TWR或MWR。

确定账户边界并取得输入

选择单一币种股票组合,把其中现金与证券都放在估值边界内。明确结果是否扣除了约定费用和税款。取得期初、期末总值、可得的每日估值,以及每笔外部投入和取出资金的生效时点。保留股数、报价时间及必要应收款,确保每次总值都有重算依据。

股票买入是内部现金换证券,不是外部投入;卖出则相反。股息留在账户现金中,仍然位于组合内部;把现金实际分配到账户外时,才形成外部取款。每个经济事件只能计入一次,否则同一笔股息可能既进入估值收益,又被当成虚构的入金调整。

成本口径也要一致。本文假设观察到的账户价值已经扣除账内约定成本,没有另行支付的账户外费用。真实账户若存在外部支付成本,应说明绩效边界怎样处理,不能自动把所有现金变动都归为投资者资本。费用或收入信息缺失时,“净收益”的含义也相应受限。

让外部资金流时点可见

每个外部资金流时点记录流入前估值、资金金额及流入后估值。没有其他同时事件时,应满足流后价值等于流前价值加入金。同一天有多个事件,就保留先后顺序,或者明示采用日初、日末政策。单看收盘余额,无法识别当天入金是否参与了当日市场波动。

如果资金在盘中到达,但只有日末报价,就没有精确流量时点的TWR输入。需要补充合适估值,或在明确政策下使用标注为近似的方法,不能把资金时间加权近似说成精确子期切分。若某子期起始资产为零,百分比收益没有定义,应从重新注资后建立新段,而不是除以零。

主例使用虚构账本,两个区间都恰好三百六十五天:

日期与事件流前价值外部入金流后价值
2023-01-01:开始0100100
2024-01-01:先估值再入金11090200
2024-12-31:期末估值1800180

期末日期有意设为十二月三十一日。虽然二〇二四年是闰年,但第二段结束在当年最后一天,因此两段经过的时间均为三百六十五天。这样数字易于核验,代码仍然按真实日期差计算。MWR把期末价值当作假设清算收款,只是方程边界,不表示真的卖出了股票。

计算时间加权收益链

第一段从一百到入金前一百一十,回报百分之十。第二段从入金后二百到期末一百八十,回报负百分之十。几何链接为(110/100) × (180/200) − 1 = −1%。直接把正百分之十与负百分之十相加得到零,是错误的累计方法。

九十元入金进入第二段期初基数,没有成为盈利。账户余额从一百到一百八十,未调资金流的增幅为百分之八十;累计投入为一百九十,投资损益则为负十。这三项说的是不同数量。金额桥为期初一百,加后续投入九十,加损益负十,等于期末一百八十。

外部流水更多时,对每个边界重复流前、流后勾稽,再连乘所有子期增长因子。不要把不同日期的账户价值相加。取款减少下一段的起始资本,而不是上一段的投资回报。如果估值和资金流对不上,应停下来核对分类和时点,再给TWR百分比。

建立带日期的资金加权方程

采用投资者视角符号:付入账户为负,实际取出为正,期末组合价值作为正的终值。三笔现金流为期初负一百,经过三百六十五天负九十,再经过三百六十五天正一百八十。采用实际经过天数除以三百六十五的年化口径,求解:

-100 - 90/(1+r) + 180/(1+r)^2 = 0
r > -1

令x=1+r,得到100x² + 90x − 180 = 0。取正的增长因子,年化MWR为负百分之三点四九〇二八三。完整七百三十天对应的复合回报为(1+r)^2−1,即负百分之六点八五八七四五。前面的TWR负百分之一是整个期间累计值,比较时应使用这里的全期等效值,不能悄悄把累计值与年化值并列成同一口径。

本例MWR更低,是因为第二段亏损时参与的资金更多。这不是两种算法必须算成同一个数字,也不意味着其中一个出错。一个账户路径还不足以证明投资者一贯擅长或不擅长择时入金;此处只解释该账本的资金权重差异。

实现带明确状态的有限求解器

带日期净现值函数如下:

def xnpv(rate, flows):
    if rate <= -1:
        raise ValueError("rate must exceed -1")
    origin = flows[0][0]
    return sum(amount / (1 + rate) ** ((day-origin).days / 365)
               for day, amount in flows)

完整脚本使用datetime官方文档说明的日期差,并使用math模块做算术核验。求解器只接受先负投入、后非负回收的常规模式,在负零点九九九九到十之间检查包围区间,再二分一百八十次。最终检查方程残差,不凭显示出来的百分比判断成功。

真实流水调用前要先勾稽。这个日期版求解器按日期排序并净额归并同一自然日的资金流;日内时刻会影响判断时,需要另用支持时间戳的扩展。空账本返回invalid_empty,全零或归并后完全抵消返回undefined_zero_flows,有效日期不足两个返回undefined_zero_period。同日负一百和正一百的净现值对所有允许收益率均为零,不能据此得到唯一年化率,这些状态必须在二分前排除。没有包围到根时返回not_bracketed,它表示当前实现尚未找到包围区间,不是数学上已经证明无解。这是为本题设计的有限求解器,不是覆盖所有不规则现金流的通用IRR库。

无解与多解必须分别处理

第一种反例只有负一百和负十两笔现金流。在收益率大于负百分之百的域内,每个折现项都为负,因此不存在有限的零点。脚本返回no_root_same_sign。普通账户报告遇到这种情况,常见检查方向是终值收款是否漏记、账本是否不完整;不能伪造零收益,也不能悄悄删除该账户。

另一种机制是同样年度间隔的负一百、正二百三十、负一百三十二。这是先收款再承担后续付款义务的抽象序列,不是主例那种正终值的普通多头账户。方程化为100x²−230x+132=0,增长因子可以为一点一或一点二,因此百分之十与百分之二十两个年化收益率都能使净现值为零。

常规求解器面对这种符号模式返回nonconventional_needs_root_analysis,脚本只对这个特定二次式用解析法确认两根,不根据数值初值碰到哪个答案就选哪个。粗网格扫描可能漏掉相切根,或者扫描范围之外的根,所以“只找到一个”也不能普遍证明唯一。保留全部已经建立的根,并说明单一MWR解释存在歧义。

实际复现并验收报告

运行python docs/growth/research/round-5/examples/R633.py,实际输出包含TWR负百分之一、年化MWR负百分之三点四九〇二八三、全期MWR等效值负百分之六点八五八七四五,以及百分之十和百分之二十两个根、同号无解状态和零基数TWR拒绝。断言核验几何链接、解析基准、方程残差及失败状态。

验收时需要外部流水账可勾稽、估值时点明确、币种分开、年化标签可见。把现金流方程和求解状态放在结果旁边。交易、分类、时间戳或估值修订后重算,并保留旧版本。这些合成检查只证明文中算术与处理规则,没有核实真实账户数据、历史表现,也没有验证产品端到端流程。

在百分比旁保留解释表

报告应把余额变化、投资损益、累计TWR、年化MWR和全期MWR等效值分别列行,并在每行附同一个账户、币种、起止日期。主例的余额增加八十、投资损益负十,百分比则回答不同分母和时点的问题。若事后发现九十元入金的一部分,其实是估值边界内两个现金子账户之间的转账,就应修正分类并重算两条链。不能只修改显示出来的收益率,否则金额桥和现金流方程将彼此矛盾。多币种账户要先分账,另做报告币转换时还需独立的汇率时点和外部流折算政策;持仓页面能显示换汇估值,并不证明回测和绩效求解已经支持混合币种。交付原始币种结果可以让读者先验证基础账,再判断新增换算的影响。

下载完整示例。使用Python 3.12或更新版本,在下载目录运行python R633.py;仅使用标准库与合成输入。

常见问题

买股票是否属于投资者负现金流?

现金和股票同在组合边界内时不属于。买入是内部交换,投资者入金才跨越边界。

求解器失败是否表示没有赚亏?

不是。未包围根、输入不完整、数学无解和多解应分别标记,不能统一替换成方便展示的零。

相关阅读