cswork / libraryOpen practice workspace

Coding questions · EY

Design logical model and consumption

This question evaluates understanding of logical data modeling, ERD-level design with keys and cardinalities, slowly changing dimensions, normalization versus denormalization trade-offs, and strategies for exposing curated datasets and read paths for analytics and regulatory consumers, indicating competency in data modeling and data delivery.

Design a logical data model and consumption layer supporting business and technical services architecture. Provide an ERD‑level description for Accounts, Trades, Positions, Limits, and Customers including keys and cardinalities. Explain normalization vs. denormalization choices, SCD handling for Customers, and how you expose curated datasets for analytics (e.g., star schema vs. data lakehouse tables). Then outline a read path for three consumers: dashboards, regulatory extracts, and ad‑hoc notebooks.

Curated Trade Risk Dataset from Accounts, Trades, Positions, Limits, and SCD Customers

You work in a trading firm that maintains core transaction data in a normalized schema and exposes curated datasets for analytics (dashboards, regulatory extracts, ad-hoc notebooks).

Given the following logical entities:

  • CUSTOMERS are modeled as a Slowly Changing Dimension (Type 2): each customer can have multiple versions over time with effective_from/effective_to dates.
  • ACCOUNTS belong to CUSTOMERS.
  • TRADES are booked against ACCOUNTS.
  • POSITIONS are daily end-of-day quantities per ACCOUNT and instrument.
  • LIMITS define an account-level position-notional limit that applies over a validity period.

Write a single SQL query that produces a curated, denormalized analytics dataset at the trade level with the following columns:

  • trade_id
  • trade_date
  • account_id
  • customer_id (from the owning account)
  • customer_name and risk_segment AS-OF the trade_date (using the SCD2 customers_dim table)
  • instrument
  • trade_quantity (the trade quantity)
  • trade_price (the trade price)
  • position_quantity (end-of-day quantity for that account & instrument on the trade_date)
  • position_notional = ABS(position_quantity * trade_price)
  • limit_value (the POSITION_NOTIONAL limit effective on the trade_date for that account)
  • breach_flag = 'Y' if position_notional > limit_value, otherwise 'N'

Assume:

  • A trade should join to exactly one customer version, position row, and limit row based on date ranges and keys.
  • LIMITS.limit_type = 'POSITION_NOTIONAL' identifies the correct limit.

Return one row per trade in the sample data. Order the result by trade_id.

Table customers_dim

columntypeconstraints
customer_skINTPRIMARY KEY, NOT NULL
customer_idINTNOT NULL
customer_nameVARCHAR(100)NOT NULL
risk_segmentVARCHAR(20)NOT NULL
is_activeCHAR(1)NOT NULL
effective_fromDATENOT NULL
effective_toDATENOT NULL
current_flagCHAR(1)NOT NULL

Sample data:

current_flagcustomer_idcustomer_namecustomer_skeffective_fromeffective_tois_activerisk_segment
N100Alpha Corp12024-01-012024-06-30YLow
Y100Alpha Corp22024-07-019999-12-31YMedium
Y200Beta LLC32024-01-019999-12-31YHigh

Table accounts

columntypeconstraints
account_idINTPRIMARY KEY, NOT NULL
customer_idINTNOT NULL
account_typeVARCHAR(20)NOT NULL
opened_dateDATENOT NULL
closed_dateDATE

Sample data:

account_idaccount_typeclosed_datecustomer_idopened_date
10MARGINNULL1002024-01-10
20CASHNULL2002024-02-01

Table trades

columntypeconstraints
trade_idINTPRIMARY KEY, NOT NULL
account_idINTNOT NULL
trade_dateDATENOT NULL
instrumentVARCHAR(20)NOT NULL
quantityINTNOT NULL
priceDECIMAL(18,2)NOT NULL
sideVARCHAR(4)NOT NULL

Sample data:

account_idinstrumentpricequantitysidetrade_datetrade_id
10AAPL150500BUY2024-06-151000
10AAPL160400BUY2024-08-011001
20GOOG2000300BUY2024-06-201002

Table positions

columntypeconstraints
position_idINTPRIMARY KEY, NOT NULL
account_idINTNOT NULL
instrumentVARCHAR(20)NOT NULL
position_dateDATENOT NULL
quantityINTNOT NULL

Sample data:

account_idinstrumentposition_dateposition_idquantity
10AAPL2024-06-151500
10AAPL2024-08-012900
20GOOG2024-06-203300

Table limits

columntypeconstraints
limit_idINTPRIMARY KEY, NOT NULL
account_idINTNOT NULL
limit_typeVARCHAR(50)NOT NULL
limit_valueDECIMAL(18,2)NOT NULL
currencyVARCHAR(3)NOT NULL
effective_fromDATENOT NULL
effective_toDATENOT NULL

Sample data:

account_idcurrencyeffective_fromeffective_tolimit_idlimit_typelimit_value
10USD2024-01-012024-12-311POSITION_NOTIONAL100000
20USD2024-01-012024-12-312POSITION_NOTIONAL50000

Expected output

One row per trade, enriched with as-of customer attributes (SCD2), end-of-day positions on the trade date, applicable position-notional limit, and a breach flag when position_notional exceeds the limit.

account_idbreach_flagcustomer_idcustomer_nameinstrumentlimit_valueposition_notionalposition_quantityrisk_segmenttrade_datetrade_idtrade_pricetrade_quantity
10N100Alpha CorpAAPL10000075000500Low2024-06-151000150500
10Y100Alpha CorpAAPL100000144000900Medium2024-08-011001160400
20Y200Beta LLCGOOG50000600000300High2024-06-2010022000300

Hints

  1. To get the correct customer version, join customers_dim on customer_id and ensure trade_date falls between effective_from and effective_to.
  2. Join positions and limits on account_id (and instrument/date for positions), then compute position_notional and breach_flag with a CASE expression.