Documentation
¶
Overview ¶
Package model is the common language between sources and destinations: every platform's history is normalized into Activities, every platform's holdings into Balances. Amounts are exact decimals, never floats.
It also holds what is computed across sources: transfers between your own accounts (PairTransfers), cost basis by replaying the history with average cost per source, transfers carrying their cost (Books, Costs), and the balance check (Reconcile, Adjustments). Activities from Fundarr's API decode into Activity; see pkg/api.
Index ¶
- Constants
- func ActivityID(sourceID, nativeID string) string
- func CanPair(out, in Activity) bool
- func CashLike(a Asset) bool
- func Costs(acts []Activity, pairs map[string]string, value Valuer) map[string]decimal.Decimal
- func Holdings(acts []Activity) map[string]Amount
- func PeggedTo(a Asset) string
- func Transferable(a Activity) bool
- type Activity
- type Amount
- type Asset
- type AssetKind
- type Balance
- type Book
- type Gap
- type Pair
- type Side
- type Type
- type Valuer
Constants ¶
const ( AdjustPrefix = "adjust:" // written by Fundarr's balance check ManualPrefix = "manual:" // entered by the user (a reclassified adjustment) )
NativeID prefixes of activities that do not come from the platform.
Variables ¶
This section is empty.
Functions ¶
func ActivityID ¶
ActivityID derives the stable id of an activity from where it came from.
func Costs ¶
Costs replays every source's history together and returns, for each activity that brings a non-cash asset in, what that asset cost, in the Valuer's currency:
- bought with cash: the cash paid;
- swapped for another asset, or moved from another of your accounts (a paired transfer): the cost carried by what left, at its average cost;
- received for nothing (rewards, airdrops, deposits from outside, adjustments): its market value that day.
Activities whose cost is unknown (no price) are left out. pairs maps a deposit's id to the withdrawal it came from.
func Holdings ¶
Holdings replays activities and returns the quantity of each asset they add up to. Comparing it with what the platform reports (Reconcile) is how we know a source understood the whole history.
func Transferable ¶
Transferable reports whether an activity can be one side of a transfer between your own accounts. Fiat moves through banks Fundarr does not see.
Types ¶
type Activity ¶
type Activity struct {
ID string `json:"id"` // stable: hash of source and native id
SourceID string `json:"sourceId"` // the configured source it came from
NativeID string `json:"nativeId"` // the platform's own id (or a stable composite)
Account string `json:"account"` // sub-account within the platform, "" for the main one
Time time.Time `json:"time"`
Type Type `json:"type"`
Side Side `json:"side,omitempty"` // trades (buy, sell) and adjustments (in, out)
Base Amount `json:"base"` // the asset bought/sold/moved/received
Quote *Amount `json:"quote,omitempty"` // trades: what was paid or received in exchange
Fee *Amount `json:"fee,omitempty"` // fee charged with it, in any asset
Network string `json:"network,omitempty"` // deposits/withdrawals: chain (BTC, ETH, TRX...)
TxID string `json:"txId,omitempty"` // on-chain hash or bank reference
Address string `json:"address,omitempty"`
Note string `json:"note,omitempty"` // human description from the platform
}
Activity is one movement on one platform, as faithful to the source as possible; destinations translate it into their own vocabulary.
func Adjustments ¶
Adjustments returns the corrections that make acts add up to the reported balances (gaps from Reconcile on the same acts).
Where history shows an asset leaving before it ever arrived (a purchase the platform no longer reports), the missing quantity is added just before the balance would go negative, so it is valued at that day's price and replayed in order. Whatever else is missing or extra is corrected at now. Ids depend only on the content: the same gap gives the same adjustment on every run.
func ReplayOrder ¶
ReplayOrder sorts activities in time order, a deposit after the withdrawal it came from even when the receiving platform's clock says otherwise. pairs maps a deposit's id to its withdrawal.
type Amount ¶
Amount is a quantity of an asset. Qty is always positive; the activity type and side say which way it moves.
type Asset ¶
type Asset struct {
Kind AssetKind `json:"kind"`
Symbol string `json:"symbol"`
ISIN string `json:"isin,omitempty"`
Name string `json:"name,omitempty"`
}
Asset identifies what is held. Symbol is the platform's ticker, upper-cased (BTC, EUR, IWDA); ISIN is set for securities when the platform gives it.
func CryptoOrFiat ¶
CryptoOrFiat builds the asset for a currency code as exchanges report them.
type Book ¶
type Book struct {
Costs map[string]decimal.Decimal
// Taken: by activity id, the cost of what a trade gave or an outflow
// sent (cash at its market value); Fees: of a fee paid apart.
Taken, Fees map[string]decimal.Decimal
// contains filtered or unexported fields
}
Book is the replay behind Costs, with what left the pools along the way and what they hold at the end.
type Gap ¶
type Gap struct {
Asset Asset `json:"asset"`
Replayed decimal.Decimal `json:"replayed"`
Reported decimal.Decimal `json:"reported"`
Diff decimal.Decimal `json:"diff"` // Reported - Replayed
}
Gap is a difference between replayed history and reported balance.
type Pair ¶
type Pair struct {
OutID string `json:"outId"`
InID string `json:"inId"`
// By says how the two were matched: "txid" (same transaction hash),
// "amount" (same asset and amount, a few days apart) or "user".
By string `json:"by"`
}
Pair links a withdrawal on one source to the deposit it became on another (Binance → Ledger, OKX → Binance): the coins stayed yours, so destinations move them between accounts and carry their cost over.
func PairTransfers ¶
PairTransfers matches withdrawals with deposits across sources. forced pairs (chosen by the user) come first; refused ones ("out\x00in") are never made. Each activity is in at most one pair. Hashes win over amounts; among amount matches the closest in time wins.
type Type ¶
type Type string
Type is what happened.
const ( // Trade: Side of Base against Quote (buy BTC with USDT, sell IWDA for EUR, // a "convert", a card purchase of crypto). Trade Type = "trade" // Deposit / Withdrawal: Base enters or leaves the platform (crypto // transfer, bank transfer, card top-up). TxID and Network help matching // the other side when it is another platform or wallet you own. Deposit Type = "deposit" Withdrawal Type = "withdrawal" // Reward: Base received for nothing in return (staking, savings/earn // interest paid in crypto, airdrop, referral, distribution). Reward Type = "reward" // Dividend: cash Base paid by the security in Quote.Asset (Quote.Qty 0). Dividend Type = "dividend" // Interest: cash interest on a fiat balance. Interest Type = "interest" // Fee: a charge not tied to another activity (custody, account fees). Fee Type = "fee" // Tax: withholding or other tax taken by the platform. Tax Type = "tax" // Adjustment: a correction Fundarr makes when the history a platform // gives does not add up to what it holds. Side In adds Base, Out removes // it. NativeID starts with AdjustPrefix. Adjustment Type = "adjustment" )