Free & Open · MariaDB 10.11 · Version 6

Portfolio Management System (PMS)
database schema

A complete MariaDB schema that sits between a portfolio manager and the OACT trade-execution platform. It handles order intake, execution routing, transaction settlement, daily NAV valuation, and fee management — end to end, fully auditable.

37 Tables
6 Reporting views
32 Stored procedures
Fully idempotent
SYSTEM OVERVIEW OPERATOR Fund Admin CSV INBOUND File intake MASTERDATA Discovery ORDERS DRAFT → EXPORTED CSV export OACT Executes trades confirm TRANSACTIONS Import & normalize NAV SNAPSHOT Daily valuation CHARGES Fee engine INTEGRATION AUDIT — integration_run + integration_error
Business Documentation
Overview

What does it do?

portfolio_core_v6 manages the complete lifecycle of portfolio orders — from an admin uploading a CSV, through trade execution at OACT, to back-office settlement and daily NAV reporting. Everything is tracked, auditable, and safe to re-run.

📥

Receive order requests

Operators submit semicolon-delimited CSV files. The system registers each file, validates it, and loads every order line into a staging table — idempotently.

→ See flow
🔍

Discover & promote masterdata

New securities, channels, and order types found in the CSV are automatically surfaced as candidates and promoted to live reference tables.

→ Masterdata
📤

Export orders to OACT

Approved orders are batched and written as a CSV file consumed by the OACT platform, which executes the actual trades.

→ Order lifecycle
📊

Daily NAV valuation

Each day the system aggregates transactions, market prices, and FX rates to compute each portfolio's total value (NAV) in its base currency.

→ Snapshot detail
How it works

End-to-end flow

From CSV drop to NAV report — everything happens inside the database via stored procedures.

① Inbound file intake
Operator CSV
Semicolon-delimited file dropped on the server filesystem at /var/OACT/…/orders/
File
sp_register_
inbound_file
inbound_file
The file is registered with operator, path, and status. Duplicate submissions are silently skipped.
Table
sp_process_
inbound_file
Staging table
Each row of the CSV is bulk-loaded via LOAD DATA INFILE into inbound_order_request_stage. One row per order line.
Staging
Idempotency: Files are identified by (source_system, request_id). Re-submitting the same file is safe — the system recognises and skips duplicates at every step.
② Masterdata discovery & promotion
Staged rows
Each inbound file triggers a discovery run over its staged order lines.
Input
sp_discover_
masterdata
Candidate tables
New channels, securities, order types, and measurement codes found in the CSV are written to candidate tables with an exists_in_masterdata flag.
Discovery
sp_upsert_
masterdata
Live config tables
Truly new items are inserted into security, config_channel, security_channel_map, etc. Existing records are updated non-destructively.
Promoted
Why this matters: Operators never have to pre-register new instruments. The first time a new security appears in a CSV, portfolio-core adds it automatically.
③ Order lifecycle

Each order line from staging becomes a container_order row, passing through a clear status progression before reaching OACT.

DRAFT
→ approve
APPROVED
→ batch
BATCHED
→ export CSV
EXPORTED
↺ reset if needed
APPROVED
container_order
One row per trade instruction. Carries security, amount, BUY/SELL direction, and a UUID external_id used to match OACT's confirmation back.
Core table
batch &
export
order_batch
Groups approved orders for one operator. Stores the generated CSV file path and export timestamp.
Export container
SELECT
INTO OUTFILE
OACT order CSV
Written to /var/OACT/…/orders/. Picked up by the OACT platform for trade execution.
File → OACT
Full audit trail: Every status transition is recorded in order_lifecycle_event. A BEFORE UPDATE trigger auto-stamps approved_utc when an order is approved.
④ Transaction import (OACT confirmation)
OACT confirmation CSV
After executing trades, OACT returns a semicolon-delimited file at /var/OACT/…/transactions/. Contains 29 columns including prices, units, fees, and taxes.
File from OACT
register +
LOAD DATA
Staging
All 29 raw OACT columns are preserved as-is in oact_transaction_import_stage. No data is discarded.
Staging
sp_process_
oact_txns
container_transaction
Normalized trade event (BUY/SELL/FEE/TAX). Joined back to the original order via external_id. Broken into components: money, fees, taxes, rounding.
Core table
⑤ Daily NAV snapshot
Inputs
Transactions (units + cash), market prices from security_price_history, FX rates from fx_rate_history.
Daily data
sp_snapshot_
container_day
3 snapshot tables
Security positions (units × price), cash balances (balance × FX), and total valuation (securities + cash = NAV) — all upserted per container per day.
Upserted daily
6 reporting views
Current positions, cash balances, market values, NAV time-series, and order overview — ready to query directly.
Views
Snapshot is safe to re-run: All three tables use UPSERT (INSERT … ON DUPLICATE KEY UPDATE), so running the snapshot twice for the same date is harmless.
Core concepts

Key entities

Three business objects sit at the heart of the system.

📦 Container

A container is an investment portfolio or account, denominated in a base currency. It holds securities (positions) and cash, on behalf of one or more holders. Most of the system revolves around containers — orders, transactions, snapshots, and charges are all linked to a container.

👤 Holder

A holder is a person or legal entity. Holders are attached to containers via container_holder_role — a temporal table (with valid_from / valid_to) that tracks ownership over time. One container can have multiple holders with different roles.

📈 Security

A security is a tradable instrument. Each security has a unique code, a base currency, and can carry additional identifiers (ISIN, Bloomberg, etc.). The link between a security and an execution channel is stored in security_channel_map — this is how the system knows which OACT item code to use when exporting an order.

🔧 Operator

An operator (e.g. fundagg) is a registered administrator or aggregator authorised to submit files. The operator code ties inbound files, order batches, and transaction imports together and controls file-system path routing.

Fee engine

Charges & fees

The system can automatically apply recurring fees, taxes, bonuses, and kickbacks to containers on a schedule.

container_charge_scheme
Defines the rule: charge type (FEE/TAX/BONUS/KICKBACK), calculation method (FIXED or % of NAV), rate, and the validity window (valid_from / valid_to).
Config
sp_apply_container
_charges (scheduled)
container_charge_event
A realized posting on a specific date and amount. Idempotent — a scheme that already has an event for today is skipped.
Event
container_transaction
Each charge event also writes a container_transaction row, so fees flow through the same settlement and NAV pipeline as trades.
Feeds NAV
Audit

Full traceability

Every automated operation is wrapped in an audit record. Nothing runs silently.

integration_run

One row per orchestration call (ingest, export, snapshot). Records run_type_code, start time, finish time, status (STARTED / SUCCESS / ERROR), and a human-readable message.

→ IT detail

integration_error

Exceptions captured by EXIT HANDLER blocks inside each orchestration procedure. Linked to the integration_run that failed. Carries the entity name, key, and full error message.

→ IT detail

order_lifecycle_event

Append-only log for every status change on every order. Events: CREATED, APPROVED, PREPARED_FOR_EXPORT, EXPORTED, BATCH_RESET. Indexed by (order_id, event_utc) for per-order timelines.

→ IT detail

Trigger

trg_container_order_approved_utc fires BEFORE UPDATE on container_order. When the status transitions to APPROVED and approved_utc is NULL, it auto-stamps the timestamp — no application logic required.

IT Reference
IT Reference

Technical reference

MariaDB 10.11 · Definer: oact_admin@localhost · All text stored as utf8mb4. This section lists every stored procedure, view, and key table grouped by purpose.

Stored procedures

All orchestration procedures wrap their work in an integration_run / integration_error audit record and use EXIT HANDLER for error capture. All ingest procedures are idempotent.

ProcedureWhat it doesBusiness step
sp_register_inbound_file Registers an inbound file (any type) into inbound_file. Idempotent via (source_system, request_id) or (file_type_code, file_path). Returns the file ID as an OUT parameter. ① Intake
sp_process_inbound_file Reads an inbound_file record and dispatches it — for order-request files, creates the inbound_order_request_file metadata record and bulk-loads rows into staging via LOAD DATA INFILE. ① Intake
sp_discover_masterdata_candidates Scans staged rows for a given file, writes candidate records to five candidate tables, and sets exists_in_masterdata flags. ② Masterdata
sp_upsert_masterdata_from_candidates Promotes candidates to live config/security tables. Inserts new records, updates existing ones. Marks the masterdata_candidate_run as PROMOTED. ② Masterdata
sp_create_orders Creates one or more orders from a JSON array payload. Validates all reference data, resolves or creates the container_order_set, writes a CREATED lifecycle event. Idempotent on (source_system, request_id, request_item_no). ③ Orders
sp_create_order Single-order convenience wrapper around sp_create_orders. Accepts individual fields instead of a JSON array. ③ Orders
sp_create_orders_from_stage Reads all rows from inbound_order_request_stage for a given file and calls sp_create_orders for each. Resolves security via security_channel_map.oact_item. ③ Orders
sp_approve_container_orders Transitions DRAFT orders to APPROVED for one container (or all if NULL). Writes APPROVED lifecycle events. The trigger stamps approved_utc automatically. ③ Orders
sp_prepare_order_batch Creates an order_batch record for an operator, links all APPROVED orders to it, and transitions them to BATCHED. ③ Orders
sp_write_oact_order_file Writes the OACT-format CSV export for a batch using SELECT … INTO OUTFILE. Marks the batch EXPORTED and stamps exported_utc on all included orders. ③ Orders
sp_reset_order_batch Rolls back a batch: unlinks orders (sets batch_id = NULL), resets status to APPROVED, writes BATCH_RESET events. Allows re-export after an error. ③ Orders
sp_register_oact_transaction_file Registers an OACT transaction confirmation CSV into inbound_file and oact_transaction_import_file. ④ Transactions
sp_load_oact_transaction_stage Bulk-loads the raw OACT confirmation CSV into oact_transaction_import_stage (all 29 columns preserved). ④ Transactions
sp_process_oact_transactions Normalises staged rows into container_transaction + container_transaction_component. Joins via external_id to resolve container and cash account. Inserts component rows for money, fees, taxes, costs, and rounding. ④ Transactions
sp_snapshot_container_day The daily NAV engine. Upserts security positions, cash balances, and total valuation for all active containers on a given date. Safe to re-run. ⑤ NAV
sp_apply_container_charges Applies FIXED_AMOUNT charge events to every active scheme whose validity window covers the process date. Skips schemes already processed for that date. Charges
Functions
FunctionReturnsWhat it does
fn_get_latest_fx_rate DECIMAL(28,12) Most recent FX rate from fx_rate_history for a currency pair on or before a given date. Returns NULL if not found. Used by the snapshot engine.
fn_get_container_id BIGINT Resolves a container_code string to its internal container_id. Raises SQLSTATE 45000 if not found — prevents silent NULL propagation.
fn_get_security_id BIGINT Resolves a security_code string to its internal security_id. Same error-raising pattern as above.
Reporting views

All views are read-only surfaces over the snapshot and transaction tables. MariaDB does not support COMMENT= on views — descriptions live here and in the SQL script header.

ViewWhat it showsBusiness section
vw_container_total_value Most recent NAV per container: securities value + cash value + total, all in base currency. ⑤ NAV
vw_container_nav_timeseries Full history of daily NAV per container. Ready for time-series charting. ⑤ NAV
vw_container_security_market_value Latest security positions per container: units, market price, and market value. ⑤ NAV
vw_container_current_cash_positions Running cash balance per container and cash account (SUM of all transaction cash amounts). ④ Transactions
vw_container_current_security_positions Running unit balance per container and security (SUM of all transaction units). ④ Transactions
vw_orders Wide order view joining container, security, channel. Adds a draft_order_count window aggregate for pending-order dashboards. ③ Orders
Key tables

The 37 tables fall into a few clear groups. Reference/config tables are seeded once and rarely change.

Core domain

container · holder · security
container_cash_account
container_holder_role
security_channel_map
security_identifier

Orders & batches

container_order
container_order_set
order_batch
order_lifecycle_event

Transactions

container_transaction
container_transaction_component
oact_transaction_import_file
oact_transaction_import_stage

Snapshot / NAV

container_security_position_daily
container_cash_balance_daily
container_valuation_daily
security_price_history
fx_rate_history

Charges

container_charge_scheme
container_charge_event

Intake & audit

inbound_file
inbound_order_request_file
inbound_order_request_stage
integration_run
integration_error

Reference / config tables (seeded once): config_currency · config_channel · config_measurement · config_aggregatable_type · config_order_status · config_batch_status · config_charge_type · config_charge_calc_method · config_transaction_event_type · config_txn_component_type · config_role_type · operator_ref · masterdata_candidate_run + 4 candidate detail tables