Target specs¶
A template's output_schema decides which tracker it targets. BXP ships
templates for two: Wealthfolio and brycht.app. Neither publishes a
machine-readable spec, so the existing built-in templates are the
de-facto contract.
Wealthfolio¶
The output .csvx is consumed by Wealthfolio. The conventions below are
enforced by the existing built-in templates and are the canonical
reference for new templates.
Sign conventions¶
All three numeric variables are always positive; direction (buy vs sell,
deposit vs withdrawal) is encoded in $action, not in the sign of the
amount.
| Variable | Convention |
|---|---|
$amount |
Always positive — wrap raw source values in ABS() if the source reports signed values. |
$quantity |
Always positive — ABS() if needed. |
$fee |
Always positive (a cost). ABS() if needed. |
Activity-type vocabulary¶
$action is set inside row_rules. Eight values cover the everyday cash
and trade events:
| Action | When |
|---|---|
'BUY' |
Buy / acquisition |
'SELL' |
Sell / disposal |
'DEPOSIT' |
Cash deposit into the account |
'WITHDRAWAL' |
Cash withdrawal |
'DIVIDEND' |
Dividend received |
'TAX' |
Tax withheld |
'INTEREST' |
Interest paid (e.g. on cash balance) |
'FEE' |
Fee charged (e.g. monthly account fee, ADR fee) |
Three more handle portfolio bookkeeping events; the built-in templates emit these as well:
| Action | When |
|---|---|
'TRANSFER_IN' |
Stock moved into the account from elsewhere (zero-cost arrival) |
'TRANSFER_OUT' |
Stock moved out of the account to elsewhere |
'SPLIT' |
Stock split — $amount carries the split ratio (e.g. 2 for 2-for-1) |
Wealthfolio also accepts three more activity types that none of the built-in templates currently emit, but which a new template may need:
| Action | When |
|---|---|
'CREDIT' |
Capital added without a trade — sign-up bonus, cashback, rebate, refund |
'ADJUSTMENT' |
Catch-all bookkeeping correction (e.g. option expiry) — cash and/or holdings move as needed |
'UNKNOWN' |
Fallback for an event you can't classify; flagged for re-review in Wealthfolio |
For currency conversions use 'TRANSFER_IN' / 'TRANSFER_OUT' (or a
'WITHDRAWAL' + 'DEPOSIT' pair): Wealthfolio's older CONVERSION_IN /
CONVERSION_OUT types were removed and migrated to TRANSFER_IN /
TRANSFER_OUT, so do not emit them.
If your source emits an event that doesn't fit any of these, prefer
'INTEREST' for income-like cash, 'FEE' for cost-like cash, and skip
the row (rows: []) if you can't classify it cleanly.
Non-trade row patterns¶
Cash events (DEPOSIT, WITHDRAWAL, INTEREST, FEE, and DIVIDEND on a balance without a ticker) don't have a meaningful symbol or unit price. The existing templates demonstrate two valid patterns — pick the one that matches your source, do not invent a third:
- Centralised in
input_schema(Anycoin, Revolut X, XTB cash) —IF([type] = 'cash', '$CASH-XXX', REMAP([Symbol], 'mymap'))style branching at variable definition time.row_rulesthen only sets$action. Compact when most cash events take the same shape and the input has a single column that distinguishes cash from stock rows. - Per-rule overrides (Trading 212) —
input_schemadefines defaults that work for the trade rows, then individualrow_rules[].rows[]entries clear or override$variablesper event type (e.g.$quantity: "",$unitprice: ""for a deposit; three differentrowsfor a currency conversion). Verbose but flexible when different cash events need different shapes or when one input row must produce multiple output rows.
Output columns¶
| Required | Optional |
|---|---|
date, symbol, quantity, activityType, unitPrice, currency, fee, amount |
account, fxRate, subtype, instrumentType, comment |
$date should be YYYY-MM-DD hh:mm:ss. Sources that report date-only
(no time) result in ... 00:00:00 — that's accepted. Output is RFC
4180–compliant with protection against spreadsheet formula injection.
What the formula guard touches
A cell is prefixed with a single apostrophe — which the tracker then reads as part of the value — only when it would otherwise open a formula in Excel / LibreOffice / Sheets:
- it starts with
=, or with a tab or CR hiding one, or - it starts with
+,-or@and carries formula machinery — a function call (@SUM(1,1)) or a DDE link (+cmd|'/c calc'!A1).
Everything else is written through untouched: signed numbers
(-12.34), phone numbers (+420 555 0101), amounts (-$1,259.59)
and names or titles that merely begin with one of those characters
(-ISM FURNITURE, LLC, @midnight with …) cannot execute, so they
keep their exact value.
The guard does not depend on quoting: a spreadsheet strips CSV quotes
before it parses the cell, so "=1+2" evaluates just like =1+2 and
both get the apostrophe.
The default Wealthfolio column mapping:
| Column | Value | Notes |
|---|---|---|
date |
$date |
YYYY-MM-DD hh:mm:ss |
symbol |
$ticker |
Yahoo Finance ticker |
quantity |
$quantity |
Number of units |
activityType |
$action |
BUY, SELL, DEPOSIT, DIVIDEND, … |
unitPrice |
$unitprice |
Price per unit |
currency |
$currency |
ISO currency code |
fee |
$fee |
Blank if not reported |
amount |
$amount |
Total value |
account |
$account |
Optional |
fxRate |
$fxRate |
Optional |
subtype |
$subtype |
Optional |
instrumentType |
$instrumentType |
Optional |
comment |
$comment |
Optional |
output_schema is free-form — the column set is whatever you declare, so a
template may add columns beyond this list. trading212_to_wealthfolio does
exactly that, emitting an extra isin column after symbol.
brycht.app¶
The shipping *_to_brychtapp templates target a different column set than
Wealthfolio. The tracker imports by header name, so the column order varies
between templates (and isin is present only where the source reports one);
the fields are:
| Column | Holds |
|---|---|
date |
Trade date (YYYY-MM-DD) |
type |
Activity type — $type, set in row_rules (see below) |
ticker |
Instrument symbol |
isin |
ISIN, when the source provides one (optional) |
quantity |
Share count — unsigned; type carries the direction |
price |
Price per share — unsigned |
currency |
Price currency (e.g. GBp for pence) |
fees |
Fee amount (may be empty) |
notes |
Free-text comment (may be empty) |
Activity type lives in $type (not Wealthfolio's $action), and the
shipping templates only emit 'BUY' and 'SELL' — brycht.app is a
buy-side tracker, so cash events (deposits, dividends, interest) are not
mapped. Quantity and price are unsigned; type encodes buy-vs-sell.
Templates default to combined_output: true so the tracker imports a
single merged file per template.
brycht.app does not publish a separate machine-readable spec — treat the
existing brycht.app entries in bxp-cli.examples.json as the canonical
reference: each carries inline JSON5 comments documenting what each
$variable represents and how $type maps to the source's own
vocabulary.
When authoring a new *_to_brychtapp template, pattern-match against
trading212_to_brychtapp (simple stock broker) or
xtb2_cash_to_brychtapp (xlsx-sourced, paired cash/closed shape) rather
than against the Wealthfolio templates above — the output_schema shape
and $action vocabulary differ.