← 返回研究论文
RESEARCH PAPER 03ACCOUNTING & SETTLEMENT

从成交到账:
可核对的策略账本与结算路径

以业务事件连接资金、份额、损益与费用,让净值计算和退出支付逐项可核对。

原创技术研究2023.10.07研究设计与方法

摘要

策略账本需要回答三件事:资金经历了什么、当前净值如何形成、退出金额怎样到账。本文为 LUMENOVA 提出一套事件驱动的账本设计,把成交、费用、估值与结算拆成可独立核对的记录,再通过同一组分录生成账户视图。重点在于保持资金、份额和损益之间的计算关系,让一次余额变化能够追溯到具体业务事件。

01 / 一笔成交,为什么会出现多个数字

订单金额代表交易意图,成交金额代表实际交换,账户权益还包含未卖出资产的估值。把这三者混在一张余额表中,容易把买入支出当作亏损,把提现当作收益下降,也难以解释费用何时扣除。研究设计应从业务事实出发:订单不直接改变持仓;每笔成交改变现金和资产数量;价格更新改变估值;资金进出改变资本与份额。

因此,账本需要同时保留“发生时间”和“记账时间”。前者确定业务归属,后者说明系统何时获知;一个账户内再用连续序号固定处理顺序。展示层可以即时刷新,但每个账户快照都应带上已处理的事件序号和估值时点,让两份报表具有可比较的边界。

02 / 事件保存事实,分录形成账户

Martin Fowler 的 Event Sourcing 讨论了以事件序列保存状态变化,并据此重建历史状态。本文借用这一思路,将原始成交凭据、业务事件和记账分录分层保存:凭据说明来源,事件说明含义,分录描述各科目的增减。账户余额则是分录的计算结果。

事件公共字段包括账户、策略、来源、来源事件编号、发生时间、记账时间与规则版本。一个订单产生多次成交时,必须按成交编号分别入账。数量与金额使用固定精度,保留资产单位;不能把不同币种的原始余额直接相加。

事件字段与其计算职责
事件关键字段影响
资金流入资产、实收金额、来源凭据增加现金与投入资本
份额变动份额数量、确认净值、关联资金事件记录申购或赎回权益
成交方向、数量、价格、成交编号改变现金、持仓及已实现损益
交易费用费用资产、金额、关联成交单独归集成本,避免重复计入
估值价格、来源、估值时点更新未实现损益,不改变数量
结算赎回份额、定价时点、应付金额形成应付款,关联后续支付凭据

本设计把交易费即时费用化,持仓成本只记录成交本金。现金、持仓成本、损益和费用由配套分录连接;每个资产单位分别检查借贷平衡。估值差额进入独立视图,保留价格依据,不回写原始成交价格。

每个结算周期还应生成一份轻量快照,列出现金、资产数量、选用价格、累计费用、应付款和有效份额,并记录覆盖到的事件序号。核对人员据此使用同一批事实复算,避免用稍后的市场价格去解释较早的净值。跨币种费用需同时保留原币金额和换算价格;精度舍入产生的尾差单列,结算金额与内部高精度计算各有明确位置。

03 / 同一事实只记一次,纠正留下路径

成交推送和历史补拉可能返回同一条记录。幂等键采用“来源+账户+来源事件编号”,由数据库唯一约束确定是否已处理;写入事件、追加分录和更新账户视图应处于同一事务。重复接收返回已有结果,不能再次增加持仓。

post(event):
  key = (event.source, event.account, event.source_id)
  with transaction():
    stored = append_event_if_absent(key, event)
    if not stored.is_new:
      return stored.result
    entries = derive_entries(event, event.rule_version)
    assert balanced_by_asset(entries)
    append_entries(entries)
    update_account_view(entries, event.sequence)
  return recorded_result(key)

若费用从 0.40 更正为 0.45,不覆盖原费用记录。追加一条冲回 0.40 的纠正分录,再记入 0.45,两条都关联原事件及更正原因;账户净影响是再减少 0.05。重建历史时只重算账本和视图,不重新下单或付款。规则版本使同一事件始终按当时确认的口径解释。

04 / 从 1,000 USDT 到可核对的净值

下面是一组教学算例:初始投入 1,000 USDT,按单位净值 1 生成 1,000 份;以每枚 100 USDT 买入 4 枚示例资产,随后分别卖出 2 枚。所有成交按金额收取 0.1% 交易费,费用均以 USDT 支付。本例只核算交易过程,产品收益提取与提现收费另按产品规则处理。

同一账户、同一计价币种下的状态变化
事件现金持仓估值账户净资产单位净值
初始入金1,00001,0001.000000
买入 4 × 100,费用 0.400599.6004 × 100 = 400999.6000.999600
估值价格升至 110599.6004 × 110 = 4401,039.6001.039600
卖出 2 × 110,费用 0.220819.3802 × 110 = 2201,039.3801.039380
卖出 2 × 108,费用 0.2161,035.16401,035.1641.035164

第一次卖出后,已实现毛利为 20,剩余持仓浮盈为 20,累计交易费为 0.620,因此净收益为 39.380。第二次卖出后,累计毛利为 36,累计交易费为 0.836,净收益为 35.164。现金也可独立复算:1,000 − 400 − 0.400 + 220 − 0.220 + 216 − 0.216 = 1,035.164。

这一口径下,账户净资产等于现金加持仓估值减应付款;单位净值等于账户净资产除以有效份额。交易损益不改变份额数,申购和赎回才改变份额,因此资金进出不会被误读成策略表现。

05 / 结算与到账分步核对

若在全部卖出后赎回 500 份,并以 1.035164 定价,教学模型中的应付金额为 517.582 USDT。请求阶段先锁定份额;确认结算后注销这 500 份并建立应付款,直到支付确认才同时减少现金与应付款。

赎回 500 份时的三次核对
状态现金资产应付款有效份额净资产
请求锁定1,035.16401,0001,035.164
结算确认1,035.164517.582500517.582
到账确认517.5820500517.582

结算确认后,现金资产仍含待支付资金,其中 517.582 被划为支付专用,剩余可用现金为 517.582。到账后,剩余 500 份的单位净值仍为 1.035164;已付 517.582 加剩余净资产 517.582,等于结算前的 1,035.164,资金关系闭合。

对于链上支付,Ethereum 官方交易回执文档提供执行状态、区块位置及日志等字段,待处理交易尚无回执。核对时,应将退出请求关联至支付记录,再检查执行状态、目标资产、收款地址和实际转移数量;一次执行成功本身不能替代金额核对。满足该链的确认规则后,再形成到账事件。

06 / 把核对边界做清楚

核对流程可以按成交、账户、支付三个层次展开:先比较来源成交与费用记录,再重算现金、持仓和净值,最后逐项匹配应付款与到账凭据。差异应定位到事件和科目,避免直接修改最终余额来消除表面偏差。

事件账本增加了存储和规则维护成本,却能减少结算解释中的歧义。这里建议只对资金、成交、费用和份额等关键事实采用事件记录;日常查询使用增量视图与周期快照。历史重建保留原估值价格,新的分析口径另建视图。这样可以在查询速度和历史可解释性之间建立明确边界,也为后续多市场接入保留一致的账户语言。

参考文献

  1. Martin Fowler — Event Sourcing,2005。
  2. Ethereum — JSON-RPC API: eth_getTransactionReceipt。