ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Beancount 复权限制性股票单元(RSU)记账实战:用双商品追踪 Vesting 全生命周期

Beancount 复权限制性股票单元(RSU)记账实战:用双商品追踪 Vesting 全生命周期 Beancount 复权限制性股票单元RSU记账实战用双商品追踪 Vesting 全生命周期【免费下载链接】beancountBeancount: Double-Entry Accounting from Text Files.项目地址: https://gitcode.com/GitHub_Trending/be/beancount导读本文基于 Beancount 仓库中的 examples/vesting/docs.md 与配套的完整账本示例 vesting.beancount讲解如何用纯文本复式记账追踪限制性股票单元Restricted Stock UnitsRSU从授予Grant到归属Vesting、再到转股与现金结算的完整生命周期。核心思路是用HOOL真实股票与HOOL.UNVEST未归属追踪商品两种不同商品分离已归属的流动资产与未归属的潜在价值。读完本文你将掌握双商品方案的账户结构设计、授予/归属/转股/现金结算四步交易流的具体分录写法以及balance断言、price指令等 Beancount 核心语法的实战用法。为什么用两种商品未归属与已归属分离记账在大多数券商账户里RSU 的授予与归属过程并不在经纪账户余额中体现——你只有在归属日才能真正看到股票。如果直接在经纪账户里记录授予会导致账面资产虚高且无法区分已经到手、可以卖出的股票与尚未归属、只是承诺中的股票。该示例文档给出的解法非常简洁把同一种标的物拆成两个不同的 commodity。HOOL真实的普通股流动资产可交易、可卖出。HOOL.UNVEST仅用于追踪的未归属份额商品代表未来潜在价值不代表任何真实资产。通过这两个商品的分离你的未归属投资组合价值可以与实际流动资产分开核算。归属时未归属份额减少、真实份额增加价值转移在账面上清晰可见。这是一种利用 Beancount 商品机制commodity建模时间锁定资产的通用思路同样适用于股票期权Stock Options、限制性股票RSU/RS等任何需要分阶段释放权益的场景。商品需要显式声明以便附加元数据1990-12-02 commodity HOOL name: Hooli Common shares. 2013-01-28 commodity HOOL.UNVEST name: Unvested award of Hooli Common shares.从源码看commodity指令在 beancount/core/data.py 中被定义为Commodity数据结构注释明确指出商品通常不需要声明但声明后可以为商品名附加元数据如这里的name也可以在商品尚未被任何分录使用时就预先定义好。账户结构五类账户各司其职示例文档设计了五类账户每个账户承担一个明确的语义角色账户持有的商品语义角色Assets:US:Hooli:Unvested:...HOOL.UNVEST未归属份额的存放地代表未来潜在价值每个授予批次award单独开一个子账户Income:US:Hooli:AwardsHOOL.UNVEST未归属授予grant的来源Income:US:Hooli:RSUUSD归属时刻实现的收入以法币计价如 USDAssets:US:Hooli:RSURefundUSD清算账户clearing account用于平衡归属交易、税款与最终转股之间的现金流差异和舍入误差Expenses:Hooli:VestedHOOL.UNVEST专属费用账户用于在归属时平衡HOOL.UNVEST的移除这一设计的关键洞察在于Assets:US:Hooli:RSURefund是一个暂存池。归属事件产生的收入与税款之间必然存在差额含税代扣、零股折算、舍入误差如果每次都直接落到支票账户账目会非常零碎且难以对账先汇聚到清算账户待转股与代扣清算完毕后一次性结转账目就非常干净。示例账本中的实际开户语句如下见 vesting.beancount2013-01-28 open Income:US:Hooli:Awards HOOL.UNVEST 2014-01-28 open Expenses:Hooli:Vested HOOL.UNVEST 2013-04-04 open Income:US:Hooli:RSU 2015-01-01 open Expenses:Taxes:TY2015:US:StateNY 2015-01-01 open Expenses:Taxes:TY2015:US:Federal 2015-01-01 open Expenses:Taxes:TY2015:US:SocSec 2015-01-01 open Expenses:Taxes:TY2015:US:SDI 2015-01-01 open Expenses:Taxes:TY2015:US:Medicare 2015-01-01 open Expenses:Taxes:TY2015:US:CityNYC ; Rounding errors accumulate here and are paid 2-3 months after vesting 2013-01-28 open Assets:US:Hooli:RSURefund 2013-04-04 open Assets:US:Schwab:HOOL 2001-01-01 open Assets:US:BofA:Checking注意open指令后的货币参数open Assets:US:Schwab:HOOL表示该账户只允许HOOL一种货币流入。从 beancount/core/data.py 中Open数据结构的定义看currencies参数可以为None不限制货币种类也可以显式列出允许的货币列表——示例中每个账户都严格限定了货币这正是为了保证不同商品不会在同一个账户里混在一起。另外税款账户按纳税年度TY2015细分分别覆盖纽约州StateNY、联邦Federal、社会保障SocSec、州残障保险SDI、医疗保险Medicare与纽约市CityNYC。这种按税务年度和税种开设子账户的做法使年终报税时的数据汇总一目了然。四步交易流从授予到现金结算示例文档将整个生命周期归纳为四个步骤每一步对应一笔或多笔交易。下面结合账本原文逐一展开。第一步授予Grant授予日你获得一笔奖励。此时只是承诺没有真实股票到账因此以HOOL.UNVEST计价从Income:Awards转入Assets:Unvested。2014-04-02 * Award S0012345 Income:US:Hooli:Awards -1680 HOOL.UNVEST Assets:US:Hooli:Unvested:S0012345 1680 HOOL.UNVEST 2014-04-02 open Assets:US:Hooli:Unvested:S0012345 2014-07-02 * Award C123456 Assets:US:Hooli:Unvested:C123456 720 HOOL.UNVEST Income:US:Hooli:Awards -720 HOOL.UNVEST 2014-07-02 open Assets:US:Hooli:Unvested:C123456值得注意的细节每个授予批次一个子账户Assets:US:Hooli:Unvested:S0012345、Assets:US:Hooli:Unvested:C123456这样不同批次的归属进度可以分别追踪也便于后续用balance断言逐批校验。授予日与开户日分开示例中open语句写在授予交易之后、与授予交易日同日期。Beancount 要求账户在使用前已open但允许在同一天开户并发生交易实际使用时应把open放在交易之前同一文件内按日期排序即可。由于HOOL.UNVEST不是真实资产授予并不产生任何税费或现金流因此这笔交易没有 USD 分录。第二步归属事件——收入与税款Vesting Event: Income Taxes归属日份额真正兑现公司按归属日市价折算为美元收入并代扣各类税款。这里的关键是以 USD 确认收入Income:RSU记录联邦、州、医保等税款费用收入减税款的净额贷记到清算账户Assets:RSURefund。账本中归属事件分录原文以批次 S0012345 为例vesting.beancount2015-05-27 * Vesting Event - S0012345 - HOOL #award-S0012345 ^392f97dd62d0 doc: 2015-02-13.hooli.38745783.pdf Income:US:Hooli:RSU -4597.95 USD Assets:US:Hooli:RSURefund 2458.97 USD Expenses:Taxes:TY2015:US:Medicare 66.68 USD Expenses:Taxes:TY2015:US:Federal 1149.48 USD Expenses:Taxes:TY2015:US:CityNYC 195.42 USD Expenses:Taxes:TY2015:US:SDI 0.00 USD Expenses:Taxes:TY2015:US:StateNY 442.32 USD Expenses:Taxes:TY2015:US:SocSec 285.08 USD验算一下收入 4597.95 2458.97 66.68 1149.48 195.42 0.00 442.32 285.08借贷完全平衡。第二个批次 C123456 的分录结构完全相同vesting.beancount收入 1970.55 USD净计入清算账户 1053.84 USD。分录的实用技巧交易标签#award-S0012345是标签tag^392f97dd62d0是链接link。同一归属事件的三笔交易收入税款、转股、现金结算共享同一个 link可以一键拉取该事件的完整链路#award-*标签则用于区分不同授予批次。Beancount 的标签与链接机制在审计和报表筛选中非常有用。文档附件doc:字段将归属日收到的 PDF 对账单如2015-02-13.hooli.38745783.pdf挂到交易上实现凭证与分录一一对应。收入计入归属日Income:US:Hooli:RSU在归属日才被确认为应税收入这与税务口径一致。第三步转股Conversion归属后未归属追踪份额变成真实股票。交易分两步走减少Assets:Unvested中的HOOL.UNVEST同时按归属价/成本基础cost basis向Assets:Brokerage增加真实HOOL份额并用Assets:RSURefund与Expenses:Vested做平衡。账本中的转股分录原文vesting.beancount2015-05-25 * Conversion into shares ^392f97dd62d0 Assets:US:Schwab:HOOL 18 HOOL {131.3700 USD} Assets:US:Hooli:RSURefund Assets:US:Hooli:Unvested:S0012345 -35 HOOL.UNVEST Expenses:Hooli:Vested 35 HOOL.UNVEST 2015-05-25 * Conversion into shares ^392f97dd62d0 Assets:US:Schwab:HOOL 8 HOOL {131.3700 USD} Assets:US:Hooli:RSURefund Assets:US:Hooli:Unvested:C123456 -15 HOOL.UNVEST Expenses:Hooli:Vested 15 HOOL.UNVEST几个值得展开的细节成本基础语法18 HOOL {131.3700 USD}中的{131.3700 USD}是每个份额的成本价cost basis这也是日后卖出时计算资本利得的依据。示例文档 examples/tutorial/invest-with-cost.output 对带成本价的投资记账有更系统的演示。未归属份额的移除平衡减少的HOOL.UNVEST-35由Expenses:Hooli:Vested35平衡。这个费用并非真实现金支出而是为保持复式记账平衡、清除未归属追踪商品所设的专用平衡账户——这正是Expenses:Hooli:Vested存在的意义。转股与归属并非同日注意归属事件日期是 2015-05-27而转股日期是 2015-05-25两者通过 link^392f97dd62d0关联。公司实际打款与账务处理日期的差异完全可以用这种先记账、后对齐的方式处理。转股后清算账户中多出的现金如零股折算、超额代扣留在RSURefund中等待第四步结转。第四步现金结算Cash Payout归属后 2-3 个月公司通过工资单payroll将清算账户中剩余的现金零股折算差额或税款超额代扣的返还打入你的支票账户2015-06-13 * HOOLI INC PAYROLL ^392f97dd62d0 doc: 2015-02-13.hooli.38745783.pdf Assets:US:Hooli:RSURefund -94.31 USD Assets:US:Hooli:RSURefund -2.88 USD Assets:US:BofA:Checking 97.19 USD 2015-06-14 balance Assets:US:Hooli:RSURefund 0 USD这里两个RSURefund分录分别是两个批次的尾差94.31 2.88 97.19全部转入Assets:US:BofA:Checking。随后紧跟着一条balance断言确认清算账户归零——这是整套流程闭环的关键收尾动作。用 balance 断言守护账目闭环示例中的三条断言账本用三条balance指令作为检查点分别验证清算账户归零和两个未归属子账户的剩余份额2015-06-14 balance Assets:US:Hooli:RSURefund 0 USD 2015-06-04 balance Assets:US:Hooli:Unvested:S0012345 1645 HOOL.UNVEST 2015-06-04 balance Assets:US:Hooli:Unvested:C123456 705 HOOL.UNVEST验算未归属余额S0012345 授予 1680、归属扣除 35剩余 1645 ✓C123456 授予 720、归属扣除 15剩余 705 ✓。归属与转股是两笔独立交易未归属余额在归属或转股后即相应减少断言日期06-04在转股05-25与现金结算06-13之间恰好覆盖了未归属已扣减、尾差尚未结算的中间状态。balance 指令的底层原理balance指令在 beancount/core/data.py 中被定义为Balance数据结构注释称它本质上是一个断言assertion对应真实世界中账单上的最终余额Statement Balance行目的是确保你的记账正确无误。它在加载时由内置插件执行。在 beancount/loader.py 中beancount.ops.balance被注册进PLUGINS_POST加载后期执行的插件列表与beancount.ops.pad一起在用户自定义插件全部执行完毕后自动运行。核心实现在 beancount/ops/balance.py遍历所有Balance指令收集被断言的账户并连同其全部子账户一起纳入实时计算这使你可以对父账户做汇总断言逐条处理交易仅对涉及被断言账户的分录累加库存到达断言日期时用realization.compute_balance计算账户含子账户的当前余额与期望值比较通过get_balance_tolerancebeancount/ops/balance.py自动推断容差默认按期望金额的小数位数生成容差并乘以tolerance_multiplier默认 0.5见 beancount/parser/options.py的 2 倍因为用户手工编写的断言往往存在更多舍入误差也可以为单条断言显式指定~容差若差异超出容差则生成BalanceError并在返回的条目上设置diff_amount字段同时替换该指令为失败标记条目。如何验证你的账本安装 Beancount 后pip install beancount在项目根目录执行bean-check examples/vesting/vesting.beancountbean-check对应源码 beancount/scripts/check.py由 pyproject.toml 中的[project.scripts]段注册为命令行入口。它会加载账本、自动运行余额校验以及HARDCORE_VALIDATIONS级别的强校验无错误时退出码为 0否则打印错误并以非零退出码退出。由于示例文件中的balance断言与分录完全吻合bean-check会干净通过你还可以用--json参数获得机器可读的错误输出方便集成到 CI。定价与估值price 指令与价格图归属后如果你想对仍未归属的份额做估值例如计算净资产时把未归属价值也折算成 USD可以像示例一样为HOOL.UNVEST声明价格2015-06-02 price HOOL.UNVEST 132.4300 USD.UNVEST这里出现了一个值得注意的细节价格目标货币写成了USD.UNVEST而不是普通 USD。price指令在 beancount/core/data.py 中被定义为Price数据结构声明某商品以另一货币计价的兑换率例如1 份 HOOL.UNVEST 值 132.43 USD。源码注释特别提醒Beancount 不存储日内时间精度同一天对同一货币对不要有多条价格指令。从实现看加载器会把所有Price指令汇集为价格图price mapbeancount/core/prices.py 的build_price_map按(currency, cost-currency)分组、按日期排序去重并自动补全反向汇率对get_pricebeancount/core/prices.py与get_latest_pricebeancount/core/prices.py提供按日期查询与查询最新两种取值方式。这意味着只要你持续维护价格指令就能在任意时点把未归属份额折算成美元估值。如果你只想要一个看得见的未归属估值、不关心精确的已实现损益也可以在报表层而非账本层用价格图折算避免在账本里引入不必要的价格噪音。卖出示例与扩展思路账本末尾附了一段被注释掉的卖出示例vesting.beancount展示了归属股票最终变现的完整写法;; 2013-04-04 open Assets:US:Schwab:Cash ;; 2013-04-04 open Income:US:Schwab:Gains ;; ;; 2015-09-10 * Selling shares ;; Assets:US:Schwab:HOOL -26 HOOL {131.3700 USD} 138.23 USD ;; Assets:US:Schwab:Cash 3593.98 USD ;; Income:US:Schwab:Gains ;; ;; 2015-09-11 balance Assets:US:Schwab:HOOL 0 HOOL卖出分录使用{成本价} 卖出价语法26 股按成本 131.3700 USD 记价、以 138.23 USD 卖出实现现金 3593.98 USD差额资本利得自动进入Income:US:Schwab:Gains最后再用balance断言确认经纪账户持股清零。基于这个示例你可以按需扩展更多批次每个授予批次一个Assets:US:Hooli:Unvested:id子账户 一个#award-id标签天然支持多批次并行。多币种/多标的把HOOL换成任何股票代码即可双商品模式与具体标的名无关。回购/卖回sell-to-cover若公司用卖出部分股票来抵扣税款可在归属交易中直接追加卖股代扣的分录再通过RSURefund清算差额。结合 bean-query 与报表HOOL.UNVEST作为独立商品出现在各账户报表中可用bean-query按#award-*标签或Assets:US:Hooli:Unvested:*账户前缀查询各批次的归属进度用bean-report生成资产负债表时未归属价值也会独立成行。小结examples/vesting/用一份不到 130 行的账本演示了 Beancount 处理 RSU 全生命周期的完整方案。其精髓可以概括为三点双商品分离用HOOL.UNVEST追踪承诺中的份额用HOOL记录到手可卖的份额使未归属价值与流动资产互不混淆清算账户闭环Assets:US:Hooli:RSURefund汇聚归属、税款、转股与尾差之间的所有现金流差异最终通过工资单一次性结算并用balance断言归零断言守护正确性balance指令作为自动检查点配合bean-check在每次加载账本时自动校验任何漏记、错记都会在第一时间暴露。这套模式不仅是 RSU 的记账模板更是用 Beancount 建模分阶段释放权益类资产的通用方法论只要把锁定资产抽象为一个独立的追踪商品再围绕清算账户设计好现金流闭环就能把复杂的股权激励账务纳入到纯文本复式记账的严密框架中。【免费下载链接】beancountBeancount: Double-Entry Accounting from Text Files.项目地址: https://gitcode.com/GitHub_Trending/be/beancount创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表