CDISC Standards (SDTM/ADaM) for RWE Submissions
CDISC's Study Data Tabulation Model (SDTM) and Analysis Data Model (ADaM) are the electronic submission formats the FDA requires for study data packages; applying them to real-world data forces explicit modeling decisions — mapping claims dispensings to EX-domain exposures, translating diagnosis codes to MedDRA for adverse-event domains, constructing ADTTE parameters for estimand-aligned time-to-event analyses — and documenting every derivation in define.xml and a Reviewer's Guide so an independent analyst can verify the traceability chain from raw source record to headline estimate.
In plain language
When a real-world evidence study is submitted to the FDA for regulatory review, the data cannot arrive as a raw spreadsheet — they must follow two specific data structures called SDTM (which organizes what happened to each patient, like drug fills and diagnoses) and ADaM (which organizes the analysis-ready numbers, like time on treatment). Converting claims or electronic health record data into these structures is not automatic: every choice about how to map a pharmacy dispensing to an exposure record, or how to translate a billing diagnosis code into a standard adverse-event term, is a documented decision the FDA reviewer can audit. Think of it as building a paper trail so that any number in the final statistical table can be traced back, step by step, to the original patient data — and the CDISC standards define exactly what that paper trail must contain.
The standards trio: what CDISC requires for an FDA electronic submission
The Clinical Data Interchange Standards Consortium (CDISC) defines a layered set of electronic submission standards that the FDA has required for new drug applications since 2016 and that increasingly governs real-world evidence (RWE) submissions. Three layers are essential.
SDTM — Study Data Tabulation Model. SDTM organizes subject-level data into domain datasets: one dataset per clinical domain, structured so an FDA reviewer can navigate directly to the raw observations without further transformation. Each domain has a fixed set of required and expected variables defined in the SDTM Implementation Guide (SDTMIG). The most relevant domains for RWE studies are: DM (Demographics — one row per subject, required in every submission); EX (Exposure — one row per administered or dispensed dose, the primary link from claims pharmacy records to the submission package); AE (Adverse Events — one row per adverse event, with the term coded to MedDRA); CM (Concomitant Medications — other drugs the subject was taking); and LB (Laboratory results). SDTM is not an analytic format; it is a standardized tabulation of what happened to whom, when, and at what dose.
ADaM — Analysis Data Model. ADaM sits above SDTM and is the layer that actually feeds statistical analyses and tables, listings, and figures (TLFs). The core datasets are: ADSL (the subject-level dataset — one row per subject, containing all baseline flags, treatment arm assignments, and stratification variables); ADTTE (time-to-event dataset — one row per subject per parameter, with PARAMCD, PARAM, AVAL, CNSR, ADT, and STARTDT as the critical variables); ADAE (adverse event analysis dataset); and ADLB (lab analysis dataset). Every ADaM derived variable is required to trace back to its SDTM source variable, the derivation algorithm, and the governing analysis plan section.
Define.xml and Reviewer's Guides. Define.xml is the machine-readable metadata catalog — it documents every dataset, every variable, every controlled-terminology code, and every derivation algorithm. The Analysis Data Reviewer's Guide (ADRG) and Study Data Reviewer's Guide (SDRG) are human-readable companions that explain the analysis choices, dataset relationships, and how to navigate the submission. Together these three artifacts — define.xml, ADRG, SDRG — are what allow an FDA statistician to re-derive the headline estimate without asking the sponsor a single question.
The RWD-to-SDTM mapping problem: decisions masquerading as lookups
The single most consequential insight for RWE practitioners is that RWD-to-SDTM mapping is not a lookup process — it is a sequence of documented modeling decisions that the define.xml and ADRG must expose and justify.
Claims dispensing → EX (exposure) domain. An administrative pharmacy claim carries a fill_date, an NDC code, and a days_supply. Mapping to EX requires deciding: Does fill_date equal exposure start (EXSTDTC)? Most RWE protocols treat dispensing date as exposure start because administration date is unobservable in claims — but this is not true for IV therapies or inpatient administration, where the service date differs. What dose (EXDOSE) and unit (EXDOSU) are assigned? For fixed-dose products the NDC determines the dose; for weight-based or titrated drugs this becomes a complex derivation. Does a 90-day mail-order fill represent one exposure row or 90 one-day rows? The SDTMIG provides guidance, but the sponsor chooses. Every choice becomes a row in define.xml.
Enrollment periods → DS (Disposition) domain. An insurance enrollment span does not map cleanly to the DS domain, which tracks subject disposition events (entered study, completed, discontinued). A claims enrollment period is an eligibility flag, not a protocol-defined disposition event. The sponsor must decide how to represent enrollment gaps, plan-type changes, and coverage termination in a domain designed for randomized trial milestones.
Diagnosis codes → MedDRA for the AE domain. ICD-10 codes in claims were assigned for billing, not for adverse event reporting. Mapping a billing code (ICD-10-CM) to a MedDRA preferred term requires a code-to-concept crosswalk plus a decision about which SNOMED or WHO-ART hierarchy level to use. A single ICD-10 code can map to multiple MedDRA preferred terms depending on context; the sponsor must document which mapping was used and why, and the mapping is a protocol-level decision that must appear in the analysis plan before any data are coded.
ADaM for the target-trial world: ADSL and ADTTE parameterization
For RWE studies using a target-trial emulation framework, ADaM's ADTTE dataset is the primary vehicle for time-to-event estimands. The PARAMCD variable distinguishes parameters within the same dataset: a single ADTTE dataset can contain TTDISC (time to discontinuation), TTEVENT (time to primary event), and TTDEATH (time to death) as separate PARAMCD strata. This structure maps naturally to the ICH E9(R1) summary-measure attribute: AVAL is the continuous time in days, ADT is the event or censoring date, and CNSR (0 = event, 1 = censored) implements the intercurrent-event strategy. An ADTTE row with CNSR = 0 and AVAL = 87 means the event occurred exactly 87 days after the reference date (STARTDT = ADT - 87 days); the estimand-to-ADaM traceability matrix must document which intercurrent events are censored (producing CNSR = 1) and which are treated as events (CNSR = 0).
Traceability as the regulatory currency
The FDA Study Data Technical Conformance Guide states that every ADaM derived variable must be traceable to its source, either through a direct source variable reference in define.xml or through an explicit derivation algorithm. For RWE submissions, this traceability runs three levels deep: ADaM variable → SDTM source variable → raw data field (e.g., claim line on the institutional file or the pharmacy claims table). The complete chain — fill_date (claims) → EXSTDTC (SDTM EX) → STARTDT (ADaM ADTTE) → AVAL derivation (ADTTE) — is what a regulatory reviewer can follow, step by step, using define.xml and the ADRG. If any link in the chain is undocumented, the submission receives a deficiency letter. This is why the CDISC standards are the regulatory currency for RWE: they impose a data structure that forces traceability into the submission artifact rather than leaving it to the sponsor's narrative.
OMOP vs CDISC: research CDM vs submission standard
OMOP (the OHDSI Common Data Model) and CDISC serve different purposes and should not be confused. OMOP is a research CDM: its goal is to standardize data structure and vocabulary across many sites so that one analysis script runs everywhere. CDISC is a submission standard: its goal is to provide a regulatory reviewer with a navigable, auditable package for a single study. OMOP is optimized for portability and network analytics; CDISC is optimized for per-submission transparency. Mappings from OMOP standard concepts (RxNorm, SNOMED) to CDISC controlled terminology (drug dictionaries, MedDRA) exist and are partially automated, but the mapping is lossy — concept granularity differs, route/form distinctions differ, and OMOP's OBSERVATION_PERIOD does not translate directly to any SDTM domain. A study can be executed on an OMOP CDM and then post-processed into CDISC for submission, but the OMOP-to-CDISC conversion layer requires the same mapping decisions as a raw-claims-to-CDISC conversion, just with a more standardized input vocabulary.
Pros, cons, and trade-offs
Pros of full CDISC SDTM/ADaM compliance for RWE submissions: The primary benefit is regulatory credibility — an FDA reviewer can navigate the submission without sponsor assistance, run independent QC against define.xml, and verify the traceability chain from any headline estimate back to the raw dispensing or diagnosis record. CDISC-compliant submissions experience fewer deficiency letters and shorter review timelines than non-conformant ones. A secondary benefit is internal auditability: the discipline of building define.xml before delivering ADaM datasets forces documentation of every modeling decision early, when changing it is cheap.
Cons and costs: CDISC compliance is expensive for RWE. Most commercial claims and EHR databases are not structured as SDTM, and the conversion layer requires specialized programming (SAS is the dominant language for CDISC production work), vocabulary crosswalks (NDC to WHO drug dictionary, ICD-10 to MedDRA), and a structured define.xml generation pipeline. The effort can reach 20–40% of total study programming cost for a complex submission. Waivers from full compliance exist (the FDA can grant them for specific domains where mapping is not feasible), but waiver requests must be submitted early and are not guaranteed.
Trade-off with OMOP-only pipelines: Using OMOP for the analytic layer and converting to CDISC only for submission is the most practical approach for most RWE submissions, but the OMOP-to-CDISC conversion is not a push-button step — it requires the same mapping decisions as a raw-to-CDISC conversion, and the conversion layer must itself be auditable.
When to use
CDISC SDTM/ADaM compliance is required or strongly indicated when: (1) the study will be submitted to FDA as part of a regulatory action — new drug application, supplemental application, post-marketing requirement, or externally controlled trial; (2) the study is a confirmatory RWE study under the FDA RWE Action Plan or a post-market safety commitment; (3) the study will be reviewed by EMA or another regulatory body whose guidelines cite CDISC standards (EMA expects CDISC alignment for ICH E9(R1) submissions); (4) the study involves an electronic patient data submission to a regulatory authority, even if the primary data are observational. Plan for CDISC compliance at protocol conception — defining the EX-domain mapping rules and the ADTTE PARAMCD structure before data extraction is far cheaper than retro-fitting after the analysis.
When NOT to use
Do not apply full CDISC SDTM/ADaM production pipelines to internal exploratory or hypothesis-generating analyses where the regulatory reviewer is not the audience. The overhead is unjustifiable for a feasibility study, a retrospective chart review used only for internal planning, or a real-world data analysis whose deliverable is a journal manuscript rather than a regulatory submission. It also becomes actively misleading when CDISC-like formatting is applied to a study whose underlying modeling decisions are undocumented — a define.xml that lists variable labels but does not describe derivation algorithms gives a reviewer the appearance of compliance while hiding the critical decisions. Partial compliance with empty derivation fields is worse than no compliance, because it creates false confidence.
Interpreting the output
The worked example produces an ADaM ADTTE record with AVAL = 87. This single field is the characteristic artifact of the traceability chain.
(1) Formal interpretation. AVAL = 87 is the time-to-discontinuation in days for patient STUDY-001-1001, measured from STARTDT = 2023-01-01 (the date of the first qualifying apixaban dispensing, sourced from EXSTDTC in the EX domain, which was itself derived from the claims fill_date via a documented mapping rule) to ADT = 2023-03-29 (the first day after the last supply window closed with no qualifying refill within the grace period). CNSR = 0 means the event (discontinuation) was observed; this patient contributes an uncensored observation to any ADTTE-based time-to-event analysis. PARAMCD = TTDISC identifies the parameter; the corresponding ADSL record anchors the subject identifier, treatment arm, and baseline characteristics. The value 87 is reproduced exactly from the date arithmetic: from Jan 1 to Feb 1 = 31 days, Feb 1 to Mar 1 = 28 days, Mar 1 to Mar 29 = 28 days, total = 31 + 28 + 28 = 87 days. No approximation or rounding occurs in any link of the chain.
(2) Practical interpretation. AVAL = 87 tells a regulatory reviewer that this patient remained on apixaban for approximately three months before stopping. Crucially, an independent analyst can verify this value without contacting the sponsor: define.xml documents that AVAL is derived as ADT minus STARTDT; STARTDT traces to EX.EXSTDTC; EXSTDTC traces to the pharmacy claims fill_date; and ADT traces to the last covered day plus one, with the 30-day grace-period rule documented in the ADRG. If the 30-day grace rule had been 60 days, the patient might not have been classified as discontinuing at all (a refill arriving within 60 days would extend the treatment episode). The value 87 is only interpretable — and only verifiable — if every one of these derivation choices is visible in define.xml and the ADRG. That auditability is the entire regulatory purpose of CDISC compliance for RWE.
Worked example
Scenario
A pharmacoepidemiology team is preparing an FDA regulatory submission for a post-marketing safety study of apixaban in atrial fibrillation. The analytic dataset is built from commercial claims, and the team must produce CDISC-conformant SDTM and ADaM datasets. They trace one patient's first qualifying dispensing — a 87-day supply of apixaban 5 mg dispensed on January 1, 2023 — through the three-layer CDISC chain to produce an ADaM ADTTE record for the time-to-discontinuation parameter. The patient does not refill within the 30-day grace period after the supply ends, so discontinuation is observed. The team must document every modeling decision in define.xml and the ADRG.
Dataset
Three-layer traceability chain for patient STUDY-001-1001: source claims row → SDTM EX domain record → ADaM ADTTE record. Each row shows one field, its value, and the derivation rule or modeling decision that produced it. The Derivation Rule column is what define.xml and the ADRG must document.
| Layer | Field_Name | Value | Derivation_Rule_or_Modeling_Decision |
|---|---|---|---|
| Source (pharmacy claim) | fill_date | 2023-01-01 | Date of first qualifying apixaban dispensing; raw field from the pharmacy claims table |
| Source (pharmacy claim) | ndc | 00310-0892-10 | NDC for apixaban 5 mg tablet (Bristol-Myers Squibb); maps to EXTRT and EXDOSE via sponsor formulary crosswalk |
| Source (pharmacy claim) | days_supply | 87 | Quantity dispensed; 87 days of supply covers January 1 through March 28 |
| SDTM EX domain | EXSTDTC | 2023-01-01 | {'Modeling decision': 'fill_date = exposure start date; inpatient or IV administration would require a different rule'} |
| SDTM EX domain | EXTRT | APIXABAN | {'Modeling decision': 'NDC translated to generic drug name via sponsor formulary crosswalk (not an automated CDISC lookup)'} |
| SDTM EX domain | EXDOSE / EXDOSU | 5 / mg | Dose from NDC product label for the 5 mg tablet form; EXDOSU code from CDISC controlled terminology CT |
| ADaM ADTTE | PARAMCD / PARAM | TTDISC / Time to Discontinuation (days) | Pre-specified analysis parameter; PARAMCD and PARAM values frozen in the SAP before any data extraction |
| ADaM ADTTE | STARTDT | 2023-01-01 | Sourced from EX.EXSTDTC; traceability documented in define.xml STARTDT derivation algorithm |
| ADaM ADTTE | ADT | 2023-03-29 | Last covered day (March 28 = fill_date + 86 days) + 1; no qualifying refill within the 30-day grace period |
| ADaM ADTTE | AVAL | 87 | ADT minus STARTDT = March 29 minus January 1; derivation algorithm documented in define.xml |
| ADaM ADTTE | CNSR | 0 = event observed (discontinuation); 1 = censored; strategy documented in the ADRG intercurrent-event section |
Steps
The source pharmacy claim for patient 1001 records a fill on 2023-01-01 for NDC 00310-0892-10 (apixaban 5 mg, 87 days supply). This is the raw evidence layer; nothing has been transformed yet. The 87-day supply provides coverage from January 1 through March 28 (the last covered day is fill_date + days_supply - 1 = 87 - 1 = 86 days after January 1). Verification of 86 days from January 1 to March 28: January contributes 30 days (Jan 2 through Jan 31), February contributes 28 days, March 1 through March 28 contributes 28 days; 30 + 28 + 28 = 86 calendar days from January 1 to March 28, confirming the last covered day.
The SDTM EX domain record is built from the claims row. Modeling decision 1: fill_date (2023-01-01) becomes EXSTDTC (2023-01-01) because the sponsor protocol designates dispensing date as the exposure start; this would differ for IV or inpatient drugs. Modeling decision 2: the NDC is translated to EXTRT = APIXABAN via the sponsor's formulary crosswalk, and EXDOSE = 5 and EXDOSU = mg are drawn from the product label for that NDC. These are not automatic mappings; define.xml must describe the crosswalk source and version in the EXTRT variable-level metadata.
No qualifying apixaban refill arrives within 30 days after March 28 (the last covered day). The ADaM discontinuation date ADT is therefore set to March 29, 2023 (the first day the patient is not covered and is past the grace period). The STARTDT in ADTTE is sourced from EXSTDTC and equals 2023-01-01. AVAL is computed as ADT minus STARTDT in days.
Date arithmetic for AVAL: from January 1 to March 29, count the days by month. January 1 to February 1 = 31 days (31 days in January). February 1 to March 1 = 28 days (2023 is not a leap year). March 1 to March 29 = 28 days. Total: 31 + 28 + 28 = 87 days. CNSR = 0 because discontinuation was observed (not censored). PARAMCD = TTDISC as pre-specified in the SAP.
Result
AVAL = 31 + 28 + 28 = 87 days (Jan 1 to Feb 1 = 31; Feb 1 to Mar 1 = 28; Mar 1 to Mar 29 = 28). CNSR = 0 (event: discontinuation observed after 87-day supply with no refill in grace period). Traceability chain: claims fill_date 2023-01-01 -> SDTM EXSTDTC 2023-01-01 -> ADaM ADTTE STARTDT 2023-01-01 -> AVAL = 87 days. Every link documented in define.xml and the ADRG.
Runnable example
sas implementation
SDTM EX domain construction from pharmacy claims and ADaM ADTTE derivation in SAS. Demonstrates the modeling decisions at each layer: fill_date -> EXSTDTC (via the ISO 8601 format CDISC requires), NDC -> EXTRT via a formulary crosswalk dataset, and AVAL...
/* ── Macro parameters: set before running ── */
%let grace_days = 30; /* grace period after last covered day */
%let study_id = STUDY-001; /* submission study identifier prefix */
/* ── Step 1: Identify first qualifying apixaban fill from pharmacy claims ── */
/* Source table: work.pharmacy_claims */
/* Columns: person_id, fill_date (date9.), ndc, drug_name, dose_mg, days_supply */
proc sort data=work.pharmacy_claims (where=(study_drug_flag=1))
out=work.px_sorted;
by person_id fill_date;
run;
data work.first_fill;
set work.px_sorted; by person_id;
if first.person_id; /* keep only the first qualifying fill per patient */
run;
/* ── Step 2: SDTM EX domain ── */
/* Modeling decisions documented here must appear in define.xml EX variable metadata. */
data work.ex;
set work.first_fill;
length DOMAIN $2 USUBJID $20 EXTRT $40 EXDOSU $10 EXSTDTC $10;
DOMAIN = 'EX';
/* Modeling decision: concatenate study_id + person_id for USUBJID */
USUBJID = catx('-', "&study_id", strip(put(person_id, best.)));
/* Modeling decision: fill_date = exposure start date (dispensing = initiation) */
EXSTDTC = put(fill_date, is8601da.); /* ISO 8601: YYYY-MM-DD, required by CDISC */
/* Modeling decision: drug_name from formulary crosswalk (not raw NDC) = EXTRT */
EXTRT = upcase(strip(drug_name));
EXDOSE = dose_mg; /* dose from NDC product label */
EXDOSU = 'mg'; /* CDISC controlled terminology unit code */
keep DOMAIN USUBJID EXSTDTC EXTRT EXDOSE EXDOSU person_id fill_date days_supply;
run;
proc print data=work.ex noobs;
var USUBJID EXSTDTC EXTRT EXDOSE EXDOSU;
title "SDTM EX domain -- verify EXSTDTC = fill_date in ISO 8601";
run;
/* ── Step 3: ADaM ADTTE -- time to discontinuation ── */
/* Modeling decisions: PARAMCD name, AVAL derivation, CNSR coding, grace period. */
/* All documented in define.xml AVAL derivation algorithm and ADRG section 4. */
data work.adtte;
set work.ex;
length PARAMCD $8 PARAM $40;
PARAMCD = 'TTDISC';
PARAM = 'Time to Discontinuation (days)';
STARTDT = fill_date; /* traceability: STARTDT <- EXSTDTC */
/* Last covered day = fill_date + days_supply - 1 (0-indexed supply) */
last_cov = fill_date + days_supply - 1;
/* Discontinuation date = day after last covered day (no refill in grace window) */
ADT = last_cov + 1; /* for confirmed discontinuation */
/* AVAL = ADT - STARTDT (integer days; equals days_supply for single-fill case) */
AVAL = ADT - STARTDT; /* e.g., 2023-03-29 - 2023-01-01 = 87 days */
CNSR = 0; /* 0 = event (discontinuation observed) */
format STARTDT ADT date9.;
keep USUBJID PARAMCD PARAM STARTDT ADT AVAL CNSR;
run;
proc print data=work.adtte noobs;
var USUBJID PARAMCD AVAL CNSR STARTDT ADT;
title "ADaM ADTTE -- AVAL = ADT - STARTDT (days); CNSR = 0 for observed event";
run;
/* ── Step 4: QC check -- verify AVAL matches expected value ── */
data _null_;
set work.adtte;
if AVAL ne days_supply then
put 'WARNING: AVAL ' AVAL ' does not equal days_supply for USUBJID=' USUBJID;
else
put 'QC PASS: AVAL = ' AVAL '(days) for USUBJID=' USUBJID;
run;
/* Expected output: QC PASS: AVAL = 87 (days) for USUBJID=STUDY-001-1001 */
/* define.xml entry for AVAL: "ADT minus STARTDT in days; STARTDT sourced from */
/* EX.EXSTDTC; ADT = last covered day (fill_date + days_supply - 1) + 1." */python implementation
SDTM EX domain construction and ADaM ADTTE derivation using pandas. Mirrors the SAS implementation: fill_date -> EXSTDTC in ISO 8601, NDC -> EXTRT via formulary crosswalk, AVAL = ADT - STARTDT in days. The assert at the end verifies the arithmetic gate:...
import pandas as pd
# ── Source data: pharmacy claims for study-eligible patients ──
# In production, this comes from the analytic claims extract.
pharmacy_claims = pd.DataFrame({
"person_id": [1001],
"fill_date": [pd.Timestamp("2023-01-01")],
"ndc": ["00310-0892-10"],
"drug_name": ["apixaban"], # from formulary crosswalk (modeling decision)
"dose_mg": [5],
"days_supply": [87],
"study_drug_flag": [1],
})
STUDY_ID = "STUDY-001"
GRACE_DAYS = 30 # days after last covered day before discontinuation is declared
# ── Step 1: SDTM EX domain ──
# Modeling decisions encoded here must appear in define.xml variable-level metadata.
px = pharmacy_claims[pharmacy_claims["study_drug_flag"] == 1].copy()
ex = pd.DataFrame()
ex["DOMAIN"] = "EX"
# Modeling decision: USUBJID = study_id + person_id (pattern documented in define.xml DM domain)
ex["USUBJID"] = STUDY_ID + "-" + px["person_id"].astype(str).values
# Modeling decision: fill_date is the exposure start date (dispensing = initiation)
ex["EXSTDTC"] = px["fill_date"].dt.strftime("%Y-%m-%d").values # ISO 8601 (CDISC required)
# Modeling decision: generic drug name from crosswalk, upper-cased per SDTM convention
ex["EXTRT"] = px["drug_name"].str.upper().values
ex["EXDOSE"] = px["dose_mg"].values # from NDC product label
ex["EXDOSU"] = "mg" # CDISC controlled terminology unit
# Carry forward for ADaM derivation (not in the SDTM export but needed downstream)
ex["_fill_date"] = px["fill_date"].values
ex["_days_supply"] = px["days_supply"].values
print("SDTM EX domain:")
print(ex[["USUBJID", "EXSTDTC", "EXTRT", "EXDOSE", "EXDOSU"]].to_string(index=False))
# ── Step 2: ADaM ADTTE -- time to discontinuation ──
adtte = ex.copy()
adtte["PARAMCD"] = "TTDISC"
adtte["PARAM"] = "Time to Discontinuation (days)"
adtte["STARTDT"] = adtte["_fill_date"] # STARTDT <- EXSTDTC (traceability)
# Last covered day = fill_date + days_supply - 1 (supply is 0-indexed from fill_date)
last_covered = adtte["_fill_date"] + pd.to_timedelta(adtte["_days_supply"] - 1, unit="D")
# Discontinuation date = day after last covered day (no qualifying refill in grace window)
adtte["ADT"] = last_covered + pd.Timedelta(days=1) # 2023-01-01 + 87 days = 2023-03-29
# AVAL = ADT - STARTDT in days (integer; equals days_supply for a single-fill discontinuation)
adtte["AVAL"] = (adtte["ADT"] - adtte["STARTDT"]).dt.days # 87
adtte["CNSR"] = 0 # 0 = event (discontinuation observed); 1 = censored
print("\nADaM ADTTE:")
print(adtte[["USUBJID", "PARAMCD", "AVAL", "CNSR",
"STARTDT", "ADT"]].to_string(index=False))
# ── Arithmetic verification gate (mirrors the catalog's worked-example check) ──
# AVAL = ADT - STARTDT = 2023-03-29 - 2023-01-01
# Month breakdown: Jan 1 to Feb 1 = 31 days, Feb 1 to Mar 1 = 28 days,
# Mar 1 to Mar 29 = 28 days; total = 31 + 28 + 28 = 87 days
assert adtte["AVAL"].iloc[0] == 87, (
f"AVAL gate failed: expected 87, got {adtte['AVAL'].iloc[0]}"
)
print(f"\nAVAL = {adtte['AVAL'].iloc[0]} days (verified: 31 + 28 + 28 = 87)")
# define.xml entry for AVAL: "Derived as ADT minus STARTDT in days.
# ADT = fill_date + days_supply (first day no longer covered under supply window);
# STARTDT sourced from EX.EXSTDTC = claims fill_date."