""" Synthetic statement line items for alternatives, structured notes and insurance. Each row is the kind of free-text description that arrives from GP capital account statements, administrator notices, custodian activity files and carrier statements, labeled with a standardized transaction type. To measure generalization honestly, the test split uses fund sponsors, fund names and phrasings that never appear in training. Run: python data/generate.py """ import csv import random from pathlib import Path random.seed(42) OUT = Path(__file__).parent LABELS = [ "capital_call", "distribution_income", "distribution_return_of_capital", "distribution_capital_gain", "recallable_distribution", "management_fee", "incentive_fee", "fund_expense", "subscription", "redemption", "insurance_premium", "policy_charge", "policy_loan", "withdrawal_surrender", "index_credit", "note_coupon", "note_call_maturity", "valuation_update", ] SPONSORS_TRAIN = ["Summit Ridge", "Harborview", "Granite", "Meridian", "Wasatch", "Keystone", "Northbeam", "Cascade", "Bonneville", "Timberline", "Redwood", "Ironbridge", "Silverlake Trail", "Oakmont", "Bluewater", "Highline", "Stonegate", "Pinecrest", "Copperleaf", "Westfield"] SPONSORS_TEST = ["Lakeshore", "Aspen Hollow", "Driftwood", "Kingsley", "Marrow Creek", "Halcyon", "Tidewater"] STRATS = ["Growth Fund", "Buyout Partners", "Direct Lending Fund", "Senior Credit Fund", "Multifamily Fund", "Infrastructure Fund", "Opportunity Zone Fund", "Co-Invest", "Secondaries Fund", "Venture Partners", "Real Estate Income Trust", "Private Credit Interval Fund", "Multi-Strategy Ltd", "Energy Transition Fund"] ROMAN = ["I", "II", "III", "IV", "V", "VI", "VII", "2019", "2021", "2023"] SUFFIX = ["", " LP", " L.P.", ", L.P.", " Fund LP", " Feeder LP", " (Offshore) Ltd", " Onshore LP"] CARRIERS_TRAIN = ["Pinnacle Life", "Evergreen Mutual", "Atlas Annuity", "Sterling Life & Annuity", "Harbor Mutual"] CARRIERS_TEST = ["Northstar Life", "Crestline Annuity"] PRODUCTS = ["PPLI Policy", "PPVA Contract", "Fixed Indexed Annuity", "RILA Contract", "VUL Policy"] ISSUERS_TRAIN = ["Global Bank AG", "Northern Securities", "Atlantic Capital Markets", "Continental Bank"] ISSUERS_TEST = ["Pacific Rim Finance"] UNDERLYING = ["S&P 500", "Russell 2000", "Nasdaq-100", "EURO STOXX 50", "SPX/RTY worst-of", "MSCI EAFE"] MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] def fund(test): s = random.choice(SPONSORS_TEST if test else SPONSORS_TRAIN) name = f"{s} {random.choice(STRATS)} {random.choice(ROMAN)}{random.choice(SUFFIX)}" return random.choice([name, name.upper(), name.replace("Fund", "Fd").replace("Partners", "Ptnrs")]) def policy(test): c = random.choice(CARRIERS_TEST if test else CARRIERS_TRAIN) return f"{c} {random.choice(PRODUCTS)} #{random.randint(10000, 99999)}" def note(test): i = random.choice(ISSUERS_TEST if test else ISSUERS_TRAIN) kind = random.choice(["Autocallable", "Buffered", "Barrier", "Contingent Income", "Market-Linked CD"]) return f"{i} {kind} Note on {random.choice(UNDERLYING)} due {random.randint(2026, 2032)} CUSIP {random.randint(10000, 99999)}X{random.randint(10, 99)}" def amt(): v = random.choice([random.uniform(500, 9999), random.uniform(10000, 250000), random.uniform(250000, 2500000)]) return random.choice([f"${v:,.2f}", f"{v:,.2f}", f"USD {v:,.2f}", f"{v:.2f}", ""]) def dt(): m, d, y = random.randint(1, 12), random.randint(1, 28), random.choice([2024, 2025, 2026]) return random.choice([f"{m:02d}/{d:02d}/{y}", f"{y}-{m:02d}-{d:02d}", f"{MONTHS[m-1]} {d}, {y}", f"Q{(m-1)//3+1} {y}", ""]) def q(): return random.choice([f"Q{random.randint(1, 4)} {random.choice([2024, 2025, 2026])}", f"{random.choice(MONTHS)} {random.choice([2025, 2026])}", "period end", ""]) # Templates: (train_phrasings, test_only_phrasings). {f}=fund, {p}=policy, {n}=note, {a}=amount, {d}=date, {q}=period, {k}=call number T = { "capital_call": ( ["Capital Call #{k} - {f} {a}", "CAP CALL {f} DUE {d}", "Contribution - {f} - Call Notice {k}", "{f}: capital contribution {a}", "Drawdown notice #{k} {f}", "INVESTOR CONTRIBUTION {f} {a}", "Capital call for investments - {f}", "CONTRIB {f} CALL {k}", "{f} - Call {k} (investments & fees) {a}", "Funding request {f} due {d}"], ["Draw-down #{k} {f} payable {d}", "Paid-in capital {f} {a}", "Commitment drawn - {f}"]), "distribution_income": ( ["Distribution - Income - {f} {a}", "DIST INC {f}", "Income distribution {q} {f}", "{f} interest income distribution {a}", "Dividend distribution - {f}", "INCOME DISTR {f} {d}", "Net investment income distribution {f}", "{f} - distribution (ordinary income)"], ["Yield distribution {f} {a}", "Current income paid - {f}", "{f} quarterly income payout"]), "distribution_return_of_capital": ( ["Distribution - Return of Capital - {f} {a}", "DIST ROC {f}", "Return of capital {f} {d}", "{f} ROC distribution {a}", "RETURN OF CAPITAL DISTR {f}", "Capital returned - {f} - realization", "{f} distribution (return of contributed capital)"], ["Cost basis returned {f} {a}", "Non-taxable return of capital - {f}", "Capital repayment distribution {f}"]), "distribution_capital_gain": ( ["Distribution - Realized Gain - {f} {a}", "DIST LTCG {f}", "Capital gain distribution {f} {d}", "{f} gain on sale distribution", "Realized gain distr - {f} - exit proceeds", "LONG TERM CAP GAIN DIST {f} {a}", "{f} distribution of realized gains"], ["Exit gain paid {f} {a}", "Profit distribution on realization - {f}", "{f} - proceeds in excess of cost"]), "recallable_distribution": ( ["Recallable distribution - {f} {a}", "DIST RECALLABLE {f}", "{f} recallable return of capital {d}", "Temporary distribution (subject to recall) {f}", "RECALL DIST {f} {a}", "{f} - distribution, recallable per LPA"], ["Distribution subject to recycling {f}", "Reinvestable distribution {f} {a}"]), "management_fee": ( ["Management fee {q} - {f} {a}", "MGMT FEE {f}", "{f} management fee (outside commitment) {a}", "Mgmt fee {q} {f}", "Advisory fee - {f} - {q}", "MANAGEMENT FEES {f} {d}", "{f} - management fee offset applied"], ["Quarterly sponsor fee {f} {a}", "Base fee {q} - {f}", "GP fee {f} {q}"]), "incentive_fee": ( ["Carried interest - {f} {a}", "INCENTIVE ALLOCATION {f}", "{f} performance fee {q}", "Carry paid to GP - {f}", "INCENT FEE {f} {a}", "Incentive fee crystallization {f} {d}", "{f} - general partner carried interest"], ["Promote paid {f} {a}", "Performance allocation to manager - {f}"]), "fund_expense": ( ["Fund expenses {q} - {f} {a}", "ORG EXPENSES {f}", "{f} partnership expenses {a}", "Admin & audit expense - {f}", "Organizational costs {f} {d}", "FUND EXP {f} {q}", "{f} - legal and fund administration expenses"], ["Operating expense allocation {f} {a}", "Partnership costs - {f} {q}", "Audit/tax prep charge {f}"]), "subscription": ( ["Subscription - {f} {a}", "SUB {f} {d}", "Initial subscription {f}", "{f} additional subscription {a}", "Purchase - {f} - subscription accepted", "SUBSCR {f}", "{f} subscription settled {d}"], ["Buy-in to {f} {a}", "Shares issued - {f} {d}", "Initial investment {f}"]), "redemption": ( ["Redemption - {f} {a}", "REDEMPTION {f} {d}", "{f} partial redemption {a}", "Tender offer proceeds - {f}", "REDEEM {f} {q}", "{f} full redemption paid", "Repurchase proceeds {f} {a}"], ["Withdrawal of shares {f} {a}", "Liquidity window payout - {f}", "Share repurchase {f} {q}"]), "insurance_premium": ( ["Premium payment - {p} {a}", "PREM PMT {p}", "{p} annual premium {d}", "Planned premium {p} {a}", "Initial premium - {p}", "PREMIUM {p} {q}", "{p} - additional premium deposit {a}", "Purchase payment {p}"], ["Contract deposit {p} {a}", "Policy funding payment - {p}", "Single premium paid {p}"]), "policy_charge": ( ["Cost of insurance charge - {p} {a}", "COI {p} {q}", "{p} M&E charge {a}", "Policy admin fee {p}", "MONTHLY DEDUCTION {p}", "{p} - premium load / DAC tax", "Rider charge {p} {a}", "Mortality & expense fee {p} {q}"], ["Insurance cost deduction {p} {a}", "Contract maintenance fee {p}", "Monthly policy charges - {p}"]), "policy_loan": ( ["Policy loan - {p} {a}", "LOAN ADVANCE {p}", "{p} loan disbursement {d}", "Loan repayment - {p} {a}", "POLICY LOAN INT {p}", "{p} - loan interest charged {q}"], ["Borrowing against cash value {p} {a}", "Loan proceeds paid {p}"]), "withdrawal_surrender": ( ["Partial withdrawal - {p} {a}", "SURRENDER {p}", "{p} full surrender {d}", "Free withdrawal {p} {a}", "PARTIAL SURR {p}", "{p} - surrender charge applied", "Required minimum distribution - {p} {a}", "Systematic withdrawal {p}"], ["Cash-out of contract {p} {a}", "Contract termination proceeds {p}", "Annuity payout {p} {q}"]), "index_credit": ( ["Index credit - {p} {a}", "INDEX CREDIT {p} {d}", "{p} annual point-to-point credit {a}", "Interest credited - {p}", "Cap rate credit {p} {q}", "{p} - segment maturity credit", "Buffer segment credit {p} {a}"], ["Crediting strategy gain {p} {a}", "Indexed interest posted {p}", "Segment term credit {p} {q}"]), "note_coupon": ( ["Coupon payment - {n} {a}", "CPN {n}", "Contingent coupon paid {n} {d}", "Interest payment {n}", "COUPON {n} {a}", "{n} - periodic coupon"], ["Income observation paid {n} {a}", "Fixed coupon credited {n}"]), "note_call_maturity": ( ["Autocall redemption - {n} {a}", "CALLED {n} {d}", "Maturity proceeds - {n}", "{n} called at par plus coupon", "MATURITY {n} {a}", "Early redemption (autocall) {n}", "Principal repayment at maturity {n}"], ["Note terminated early {n} {a}", "Final settlement {n} {d}"]), "valuation_update": ( ["NAV statement {q} - {f} {a}", "Capital account balance {q} {f}", "{f} quarterly valuation {a}", "Market value update {p} {d}", "ENDING CAPITAL {f} {q}", "Contract value as of {d} {p}", "{f} - estimated NAV {q}", "Accumulation value {p} {a}"], ["Partner's capital at {q} - {f}", "Statement value {p} {a}", "Reported fair value {f} {q}"]), } def noise(s): r = random.random() if r < 0.15: s = s.upper() elif r < 0.22: s = s.lower() if random.random() < 0.08: # small typo i = random.randrange(len(s)) s = s[:i] + s[i + 1:] if random.random() < 0.15: s = random.choice(["WIRE IN ", "WIRE OUT ", "ACH ", "JNL ", "ACTIVITY: ", "REF#" + str(random.randint(100000, 999999)) + " "]) + s return " ".join(s.split()) def render(tpl, test): return noise(tpl.format(f=fund(test), p=policy(test), n=note(test), a=amt(), d=dt(), q=q(), k=random.randint(1, 14))) def build(n_train=700, n_test=150): train, test = [], [] for label in LABELS: tr_t, te_t = T[label] for _ in range(n_train): train.append({"text": render(random.choice(tr_t), False), "label": label}) for _ in range(n_test): # Test: unseen sponsors/carriers/issuers always; held-out phrasings half the time. unseen = random.random() < 0.5 tpl = random.choice(te_t) if unseen else random.choice(tr_t) test.append({"text": render(tpl, True), "label": label, "condition": "unseen_phrasing" if unseen else "unseen_entities"}) # Reviewer feedback: 10 items per label in the phrasings the model has not seen, labeled by # a person during exception review. Uses training-side entities, so test entities stay unseen. feedback = [{"text": render(random.choice(T[label][1]), False), "label": label} for label in LABELS for _ in range(10)] random.shuffle(train) random.shuffle(test) random.shuffle(feedback) return train, test, feedback def write(name, rows): with open(OUT / name, "w", newline="") as f: w = csv.DictWriter(f, fieldnames=list(rows[0].keys())) w.writeheader() w.writerows(rows) if __name__ == "__main__": tr, te, fb = build() write("train.csv", tr) write("test.csv", te) write("review_feedback.csv", fb) print(f"{len(tr)} train, {len(te)} test, {len(fb)} review feedback, {len(LABELS)} labels")