Multi-asset design#

The schema is designed for FX, crypto, bonds and equities. v1 implements and seeds only one simulated FX pair (EURUSD).

Principle: a new asset class is added by adding rows and, at most, one subtype table. It never requires changing ``trades``, ``prices_eod``, ``pnl_eod`` or the risk tables. Those tables are deliberately asset-class-neutral.

How it works#

  1. ``instruments`` is a neutral core. Every instrument, whatever its class, has a unique symbol, an asset_class, a quote_ccy (the currency its price, trades and P&L are denominated in) and a price_multiplier. See Reference database (reference.db).

  2. Class-specific attributes live in a one-to-one subtype table keyed by instrument_id (foreign key to instruments, same database). FX’s base_ccy, pip_size and lot_size are in fx_instruments. This avoids a wide table full of nullable class-specific columns and lets each subtype carry its own NOT NULL and CHECK rules.

  3. ``price_multiplier`` absorbs differences in quoting convention. P&L per unit of quantity is price difference * price_multiplier, and notional is quantity * price * price_multiplier. It is 1.0 for FX, crypto and equities, and 0.01 for bonds quoted in percent of par with quantity as face amount. It is positive by CHECK.

  4. ``symbol`` is the system’s unique internal instrument code, not a vendor ticker. It is the cross-database key (trades, prices, P&L and risk all carry it), so it must be unique across asset classes and immutable. Vendor tickers, ISIN and FIGI belong in a separate identifiers table (below), because tickers collide across venues.

  5. P&L carries its currency (``pnl_eod.ccy``), so positions in different currencies are never silently summed.

Mapping of the four asset classes#

FX

Crypto

Bond

Equity

asset_class

FX

CRYPTO

BOND

EQUITY

Quantity means

base-currency units

base-asset units (fractional)

face amount

shares

Price means

quote currency per base unit

quote asset per base unit

clean price, percent of par

currency per share

price_multiplier

1.0

1.0

0.01

1.0

quote_ccy

quote currency

quote asset (may be a stablecoin)

bond currency

listing currency

Subtype table

fx_instruments (built): base_ccy, pip_size, lot_size

crypto_instruments (designed): base_asset, quantity_decimals, price_decimals, venue

bond_instruments (designed): issuer, coupon_rate, coupon_frequency, day_count, issue_date, maturity_date, face_value

equity_instruments (designed): exchange, country, lot_size

Needs beyond the core

forwards and value-date points (later)

trading fees; 24/7 market, so an explicit EOD cut-off (e.g. 00:00 UTC)

coupon accrual and payments; clean vs dirty price (accrued interest)

dividends and splits (corporate actions); adjusted price history

(built) means implemented and tested. (designed) means specified here and added by migration when the first instrument of that class is introduced, so no empty tables exist before they are needed.

Recipe: adding an asset class#

  1. Add the value to AssetClass in domain/enums.py. The CHECK on instruments.asset_class is generated from the enum; ship a migration to update it.

  2. Create the subtype table (foreign key to instruments.instrument_id).

  3. Insert the instrument rows with the right quote_ccy and price_multiplier.

  4. Load prices with an explicit data_origin (see Market database (market.db)).

  5. Book trades as usual and run EOD. P&L and risk need no changes unless the class needs behaviour from the “Needs beyond the core” row.

  6. If it does, add that behaviour in domain/ (pure and unit-tested) and any new table in the database that owns the data (next section).

This is exercised by tests (tests/test_multi_asset.py): a bond-style instrument and a EUR-quoted equity are created with core rows only and run through booking, P&L and risk.

What is not generic yet#

These are real gaps, listed so they are not mistaken for supported features.

Gap

Owning database

Planned table or change

Fees and commissions on trades

trade

trade_fees(trade_id, ccy, amount, fee_type)

Coupons, dividends, other cash flows

trade

cashflows(id, symbol, book, ccy, amount, kind, pay_date)

Splits, mergers, symbol changes

market

corporate_actions and adjusted-price handling

Vendor identifiers (ISIN, FIGI, ticker, venue)

reference

instrument_identifiers(instrument_id, scheme, value), unique (scheme, value)

Currency conversion for P&L aggregation and USD notional and VaR

market

FX rates (e.g. via FX instruments’ own prices) and a reporting-currency setting

Trading calendars and EOD cut-offs per instrument

reference

trading_calendars

Accrued interest (dirty price) for bonds

domain

day-count-aware accrual in domain/

Until currency conversion exists, risk (USD notional, VaR, limits) refuses non-USD instruments with an explicit error rather than producing a wrong number. P&L works for any currency but is reported per currency.