zfin/docs/reference/config/transaction-log-srf.md

5.1 KiB

transaction_log.srf reference

transaction_log.srf is an optional sibling of portfolio.srf that declares real-world transactions which change how zfin interprets the portfolio diff. It holds exactly one kind of record: transfers -- money or securities you moved between accounts you own.

Why it exists

zfin contributions infers "new money" by diffing your portfolio over time. A plain diff can't tell an external contribution apart from an internal transfer, so moving (say) $50k from one account to another would otherwise be double-counted: the receiving account's new lots look like contributions while the sending account's removed lots are ignored. Declaring the transfer here cancels that out.

Missing file -> the matcher is a no-op; nothing changes.

File format

One record per destination. A record pins the money to exactly one landing spot: either a specific lot (SYMBOL@open_date) or the literal token cash. A sweep that lands in several lots becomes several records sharing the same (date, from, to) but differing in dest_lot.

#!srfv1
# Simple cash transfer between two accounts
transfer::2026-05-02,type::cash,amount:num:5000,from::Sample IRA,to::Sample Brokerage,dest_lot::cash

# Transfer that was invested into a single stock lot on arrival
transfer::2026-05-02,type::cash,amount:num:7000,from::Sample IRA,to::Sample Brokerage,dest_lot::VTI@2026-05-03

# Sweep into a basket plus a cash residual (two records, same date/from/to)
transfer::2026-05-02,type::cash,amount:num:145300,from::Sample IRA,to::Sample Brokerage,dest_lot::VTI@2026-05-03
transfer::2026-05-02,type::cash,amount:num:4700,from::Sample IRA,to::Sample Brokerage,dest_lot::cash

Fields

Field Type Required Description
transfer date Yes Transfer date (YYYY-MM-DD); the record key.
type string Yes cash (money moved) or in_kind (securities moved). See below.
amount num Yes Dollar amount transferred to this destination.
from string Yes Source account name (matches an account:: in your portfolio).
to string Yes Destination account name.
dest_lot string Yes Where it landed: cash, or SYMBOL@YYYY-MM-DD for a specific lot.

type::cash vs type::in_kind

Both are matched by the contributions classifier; they differ in what moved and therefore in how the record is verified.

type::cash -- dollars left one account and arrived at another, possibly getting invested on arrival. The amount is load-bearing: it's matched against the destination's value, and the three outcomes are

  • within $1 -- fully a transfer; contributes $0 to attribution.
  • destination worth more than amount -- a partial transfer. Only amount is cancelled out; the excess is still counted as new money. That's the "I moved $5k in and also added $2k of my own" case.
  • amount exceeds the destination's value by more than $1 -- rejected, and reported under Flagged for review. You can't have moved more into a lot than the lot is worth, so the record is presumed wrong rather than trusted.

type::in_kind -- the securities themselves moved (an ACAT transfer, an in-kind rollover); no cash changed hands. Requires dest_lot::SYMBOL@YYYY-MM-DD -- dest_lot::cash is rejected, since nothing can land as cash in a transfer where no cash moved.

#!srfv1
# 300 shares of VTI moved from one account to the other, no cash involved
transfer::2026-05-02,type::in_kind,amount:num:87000,from::Sample IRA,to::Sample Brokerage,dest_lot::VTI@2024-01-15

Here amount is informational only. The moved value comes from the destination lot itself, and verification is by share count: the shares that left from must match the shares that arrived at to, within 1% or 0.01 shares (whichever is larger). A mismatch is reported under Flagged for review rather than silently absorbing what might be a real contribution.

A missing source side is not an error for either type -- the sending account may be untracked (an external rollover origin, a 401k you don't model). Only the destination is required.

Because no cash funded an in-kind move, there is no partial outcome: a destination is either fully a transfer or not one at all.

Scope and limits

  • Only transfer:: records. Buys, sells, and dividends stay inferred from the portfolio diff.
  • Forward-looking only -- there is no historical reconstruction.
  • Account names are matched byte-exactly, so a renamed account breaks records that reference the old name.

See also


Documentation home