Skip to content

Crossfield / Documentation

Portfolio exposure and execution price

A runnable trading example: define gross and net exposure, calculate a weighted execution price, join issuer limits, and compare model revisions.

A portfolio team and a risk team can work from the same position records and still report different exposure numbers. A shared model makes the choice of measure, valuation date, and included books explicit.

This example uses independently authored, synthetic cash-equity data. The positions have already been valued in USD. We are modeling reporting definitions, not pricing instruments or calculating total portfolio risk.

Understand the positions

Each row is one position at a valuation date. Atlas and Boreal are fictional issuer identifiers. The Atlas positions hold the same common share class, valued at the same USD closing price on each date.

Date Book Issuer Signed market value (USD) Current scope?
2024-01-31 Book A Atlas +12,000,000 Yes
2024-01-31 Book B Atlas −8,000,000 Yes
2024-01-31 Book A Boreal +3,000,000 Yes
2024-01-31 Expansion Atlas +5,000,000 No: proposed additional book
2024-01-30 Book A Atlas +300,000,000 No: older valuation date

For Atlas, gross exposure is the sum of absolute position values: $20 million. Net exposure is the signed sum: $4 million. Neither is a substitute for the other.

Illustrative cash-equity positions The same positions. Two valid measures.

Both books hold the same issuer’s common shares, valued at the same USD closing price.

Book ALong position
+$12m
Book BShort position
−$8m
Gross exposure $20m |+$12m| + |−$8m|

Add the absolute position values. Both books contribute to the total.

Net exposure $4m +$12m − $8m

Add the signed position values. The long and short positions offset.

Both bars use a $20m scale. All amounts are USD.
Gross and net answer different questions. Net exposure describes the signed balance of these positions; it is not a measure of total risk.

Load the example

Download finance.zip. It contains the data setup, complete models, two revision packages, queries, independently specified answers, and captured explanation. No API account or Python package is required.

Use the matching native engine and extension. Request evaluation access if you need the development build; the example download contains data and models only. The verified shared-engine CLI is named haybarn; use its actual path:

unzip finance.zip
cd finance
/path/to/matched/haybarn -unsigned -init /dev/null :memory: \
-cmd "LOAD '/absolute/path/to/crossfield.duckdb_extension';" < session.sql

For an interactive session, omit < session.sql, then run:

.bail on
.read setup.sql
.read run.sql
.read explain.sql

The setup creates a warehouse attachment with positions, limits, and fills. It attaches four semantic catalogs: portfolio, proposed, risk, and trading. Explicit bindings connect the models’ logical source to the physical warehouse. Run setup once per fresh connection.

Select named measures

SQL · Gross and net exposure by issuer
SELECT issuer, gross_exposure, net_exposure
FROM SEMANTIC portfolio.exposure
GROUP BY issuer
ORDER BY issuer;
issuer gross_exposure net_exposure
Atlas 20,000,000 4,000,000
Boreal 3,000,000 3,000,000
SQL · Portfolio totals
SELECT gross_exposure, net_exposure
FROM SEMANTIC portfolio.exposure;

The totals are $23 million gross and $7 million net. The older position and Expansion book are excluded by the current model, even though the query has no WHERE clause.

SQL · The same measures by trading book
SELECT book, gross_exposure, net_exposure
FROM SEMANTIC portfolio.exposure
GROUP BY book
ORDER BY book;

Book A: $15m gross, $15m net. Book B: $8m gross, −$8m net.

The Ossie document defines the position fields and metric expressions. The Crossfield manifest declares approved dimensions, units, binding identity, and this population:

as_of = DATE '2024-01-31' AND book IN ('Book A', 'Book B')

That fragment is a model-authored population expression, not an additional query the user must remember. This fixture intentionally fixes the reporting date. A production model with selectable dates needs an explicit as-of interface and temporal aggregation rules; positions should not simply be summed across dates.

Calculate the price actually paid

The fills table contains two active fills for buy order ORD-101: 100 shares at $100 and 900 shares at $102. A third, voided fill at $999 tests the model’s active-fill filter.

Illustrative buy order · USD More shares give a fill more weight.

Two fills for our own buy order, in the same security and currency.

Fill A 100 shares at $100
Fill B 900 shares at $102
Total shares1,000
Total cost$101,800
Ignores the share quantities Unweighted fill-price mean $101.00 Each fill gets half the weight. ($100 + $102) ÷ 2
Weights each fill by its shares Quantity-weighted execution price $101.80 Fill B gets nine times the weight. $101,800 ÷ 1,000 shares
Total cost comes from both fills (100 × $100) + (900 × $102) = $101,800
For our executions, average price is total cost divided by total shares. This measures our own fills, not market VWAP.
SQL · Approved execution-price calculation
SELECT order_id, filled_shares, execution_cost, execution_price
FROM SEMANTIC trading.executions
GROUP BY order_id;
order_id filled_shares execution_cost execution_price
ORD-101 1,000 101,800 101.80

The execution model defines execution_price as SUM(shares * price) / NULLIF(SUM(shares), 0). Crossfield recomputes this ratio from the selected fill population. It does not average precomputed order prices. The metric is marked non-additive; zero total shares yields NULL. Prices are USD per share, excluding fees, for our own order—not a market-wide VWAP benchmark.

Compare exposure with limits

The risk model contains one Atlas limit of $22 million at the same reporting date. Both models explicitly approve semantic joins and identify their issuer fields as the same shared dimension. This is stronger than merely naming both columns issuer.

SET crossfield_mode = 'semantic';
SQL · Independently calculated exposure and limits
SELECT p.issuer, p.gross_exposure, l.gross_limit
FROM SEMANTIC portfolio.exposure AS p
LEFT JOIN SEMANTIC risk.limits AS l
  ON p.issuer = l.issuer
GROUP BY p.issuer
ORDER BY p.issuer;
issuer gross_exposure gross_limit
Atlas 20,000,000 22,000,000
Boreal 3,000,000 NULL

The left join preserves Boreal. A missing limit remains NULL. This query reports exposure alongside a limit; it does not block a trade or establish that a portfolio is within every applicable risk limit.

Cross-model queries use semantic or off mode. Strict mode keeps each business query within one attachment. See query composition for full grouping and compatibility rules.

Review a model revision

The proposed revision includes the Expansion book’s $5 million position. It uses the same source records and exposure formulas as the current revision. The two packages have the same package identity and revisions 1 and 2; separate attachment names keep both definitions available.

SQL · Measure the effect of a reporting scope change
WITH current AS (
  SELECT gross_exposure FROM SEMANTIC portfolio.exposure
), candidate AS (
  SELECT gross_exposure FROM SEMANTIC proposed.exposure
)
SELECT current.gross_exposure AS current_exposure,
       candidate.gross_exposure AS proposed_exposure,
       candidate.gross_exposure - current.gross_exposure AS change
FROM current CROSS JOIN candidate;
current_exposure proposed_exposure change
23,000,000 28,000,000 5,000,000

The $5 million difference comes from model scope, not a new trade or changed price. The outer subtraction is an analyst’s reconciliation. It is not a newly approved semantic metric. A model revision preserves a definition; it does not freeze the underlying data.

The model versions guide explains how to keep earlier definitions available and select a reviewed version for future reporting.

Inspect the generated SQL

SQL · Inspect the reporting rules
EXPLAIN (SEMANTIC)
SELECT issuer, gross_exposure, net_exposure
FROM SEMANTIC portfolio.exposure
GROUP BY issuer
ORDER BY issuer;

The captured native SQL contains the January 31/book filter, both exposure formulas, and grouping by issuer. Download the full explanation. EXPLAIN (SEMANTIC) does not execute the report or call providers. Execute the original semantic query to retain its validation.

Verification and scope

The numerical expectations are specified directly from the source rows in expected-results.txt, independently of the compiler. The focused native regression runs with optimization enabled and disabled. The complete downloadable CLI session also runs in both modes. Read the exact verification record.

No live connector, customer account, pricing feed, FX model, derivatives netting, or trading-limit enforcement is included. The retail walkthrough remains a separate example of relationships between facts at different grains.

NextRetail orders and items