Oracle3
Oracle3: 예측시장 거래 엔진·MCP 서버
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
Structured Finance Cashflow Engine
저장소 README에서 가져옴
README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
graam-flows is the open execution layer behind Graam.
It provides a deterministic, auditable engine for running structured credit deals — from collateral projections through waterfall execution to bond analytics.
The goal is to move structured credit away from encrypted CDI files and fragmented spreadsheet models toward a shared, executable representation of a deal.
Read more: From CDI Files to Executable DealsREADME 전문 보기 ↗graam-flows is the open execution layer behind Graam.
It provides a deterministic, auditable engine for running structured credit deals — from collateral projections through waterfall execution to bond analytics.
The goal is to move structured credit away from encrypted CDI files and fragmented spreadsheet models toward a shared, executable representation of a deal.
Read more: From CDI Files to Executable Deals
Structured credit workflows today rely heavily on spreadsheets and fragmented models. Deals are often re-built independently by each party, introducing inconsistencies and operational risk.
graam-flows provides a single, executable representation of a deal that can be:
This is a step toward making structured credit programmable.
graam-flows is the execution layer. Graam builds on top of it with:
This repository focuses on the deterministic modeling layer.
graph LR
Input["User Query / Prospectus"] --> Agent["Graam Agent"]
Agent -->|Deal DSL| D
subgraph graam-flows
D["Collateral Engine"] -->|pool cashflows| E["Waterfall Engine"]
E -->|tranche cashflows| F["Pricing Engine"]
end
F --> Output["Price / Yield / Spread / WAL"]
graam-flows has three engines:
The engines can be used together or independently. Generate collateral projections, run them through a waterfall, then price the output tranches — or use any engine in isolation.
The engine supports the deal structures found in Auto ABS, RMBS, and credit risk transfer (CRT) securitizations: sequential and pro-rata payment priorities, shifting interest, enhancement caps, trigger-dependent structure switching, reserve accounts, OC turbo mechanisms, and more.
dotnet build
dotnet test
dotnet run --project src/GraamFlows.Api
The API starts on http://localhost:5200 with Swagger UI at /swagger.
docker build -t graam-flows .
docker run -p 5200:5200 graam-flows
Both commands take the deal model as a positional argument:
# project a deal and export the cashflows to Excel
dotnet run --project src/GraamFlows.Cli -- run path/to/deal.json
# validate the deal's WAL against its published decrement table
dotnet run --project src/GraamFlows.Cli -- wal-tests path/to/deal.json --verbose
wal-tests strikes the grid entirely from the deal model — nothing is taken from the clock, so
two runs of the same file on different days give the same answer. Three fields decide the basis:
| Field | Meaning |
|---|---|
firstPaymentDate (deal level) | First distribution date, when the tranches carry only a payDay. Per-tranche firstPayDate wins; closingDate is the last resort. A deal that states none of the three is rejected rather than anchored to today. |
walScenarios.assumptions.prepaymentType | CPR (% of current balance — the default, and what RMBS decrement tables are struck in) or ABS (% of original balance, the auto-ABS convention). |
walScenarios.assumptions.callTriggers | Names of the triggers the "to redemption date" column is struck to; the run stops at whichever fires first. Omitted, a dated DATE_TERMINATION redemption is used when the deal declares one, else the COLLATERAL_VALUE clean-up. |
POST /api/calccollateral projects loan pool cashflows from asset data and assumptions.
{
"projectionDate": "2024-01-25",
"assets": [
{
"assetName": "Pool",
"interestRateType": "FRM",
"originalDate": "2023-06-01",
"originalBalance": 100000000,
"originalInterestRate": 6.5,
"currentInterestRate": 6.5,
"originalAmortizationTerm": 72,
"currentBalance": 95000000,
"serviceFee": 1.0
}
],
"assumptions": {
"cpr": 6.0,
"cdr": 1.0,
"severity": 40.0,
"prepaymentType": "CPR"
}
}
Assumptions support constant scalars, per-period vectors (cprVector: [6.0, 7.0, 8.0, ...]), or ramp strings (cprVectorStr: "1.0R12,6.0"). Prepayment convention can be CPR (% of current balance, standard for RMBS) or ABS (% of original balance, standard for Auto ABS).
POST /api/pricing computes bond analytics from a cashflow stream.
{
"cashflows": [
{ "date": "2024-02-25", "interest": 333333, "principal": 1500000, "balance": 98500000 },
{ "date": "2024-03-25", "interest": 328333, "principal": 1500000, "balance": 97000000 }
],
"params": {
"inputType": "price",
"inputValue": 99.5,
"settleDate": "2024-01-25",
"balance": 100000000,
"dayCount": "30/360",
"compounding": "SemiAnnual"
},
"rates": [[0.5, 5.0], [1.0, 4.8], [2.0, 4.6], [5.0, 4.5], [10.0, 4.4]]
}
Input types: price (solve for yield/spread), yield (solve for price), spread (solve for price from Z-spread). Returns price, yield, Z-spread, discount margin, modified duration, WAL, and accrued interest. The rates array provides a term structure as [term_years, rate_percent] pairs for spread and duration calculations.
POST /api/waterfall distributes collateral cashflows across tranches and returns tranche-level cashflows.
{
"projectionDate": "2024-01-25",
"collateralCashflows": [
{
"cashflowDate": "2024-02-25",
"groupNum": "1",
"beginBalance": 100000000,
"scheduledPrincipal": 1000000,
"unscheduledPrincipal": 500000,
"interest": 666667,
"netInterest": 585417,
"serviceFee": 81250,
"defaultedPrincipal": 0,
"recoveryPrincipal": 0
}
],
"deal": {
"dealName": "EXAMPLE-2024-1",
"waterfallType": "ComposableStructure",
"interestTreatment": "Collateral",
"closingDate": "2024-01-25",
"tranches": [
{
"trancheName": "A",
"originalBalance": 80000000,
"couponType": "Fixed",
"fixedCoupon": 5.0,
"firstPayDate": "2024-02-25",
"legalMaturityDate": "2030-01-25",
"dayCount": "30/360",
"cashflowType": "PI",
"trancheType": "Offered",
"payDay": 25,
"payFrequency": 12
},
{
"trancheName": "B",
"originalBalance": 20000000,
"couponType": "Fixed",
"fixedCoupon": 6.0,
"firstPayDate": "2024-02-25",
"legalMaturityDate": "2030-01-25",
"dayCount": "30/360",
"cashflowType": "PI",
"trancheType": "Offered",
"payDay": 25,
"payFrequency": 12
}
],
"unifiedWaterfall": {
"executionOrder": [
"INTEREST",
"PRINCIPAL_SCHEDULED",
"PRINCIPAL_UNSCHEDULED",
"WRITEDOWN"
],
"steps": [
{
"type": "INTEREST",
"structure": { "type": "SEQ", "tranches": ["A", "B"] }
},
{
"type": "PRINCIPAL",
"source": "scheduled",
"default": { "type": "SEQ", "tranches": ["A", "B"] }
},
{
"type": "PRINCIPAL",
"source": "unscheduled",
"default": { "type": "SEQ", "tranches": ["A", "B"] }
},
{
"type": "WRITEDOWN",
"structure": { "type": "SEQ", "tranches": ["B", "A"] }
}
]
}
}
}
Required fields to note:
collateralCashflows[].groupNum must match the group emitted by the unified waterfall ("1" by default). The default on PeriodCashflowDto is "0", which will cause every PayRule to be skipped.collateralCashflows[].netInterest drives tranche interest distribution; interest alone is not used. When chaining from /api/calccollateral, both fields are populated automatically.deal.closingDate seeds FirstSettleDate on each tranche and anchors first-period accrual. Without it, accrual days collapse to zero for interestTreatment: "Collateral".tranche.payFrequency is payments per year (12 = monthly, 4 = quarterly, 2 = semi-annual, 1 = annual), not months between payments. It must match the cadence of collateralCashflows; /api/calccollateral emits monthly cashflows, so tranches should use 12. The default is 12.The API accepts both camelCase and snake_case JSON keys.
Deals can be defined using the unified waterfall format (recommended) or the lower-level PayRules DSL.
The unified waterfall is a steps-based format where each step defines how a specific cashflow type is distributed. Step types:
| Step | Description |
|---|---|
INTEREST | Interest distribution to tranches |
PRINCIPAL | Principal distribution (source: scheduled, unscheduled, recovery) |
WRITEDOWN | Loss allocation (reverse seniority) |
EXPENSE | Deal expense payments |
RESERVE_DEPOSIT | Reserve account deposits |
EXCESS_TURBO | OC turbo paydown from excess interest |
EXCESS_RELEASE | Remaining excess to certificateholders |
SUPPLEMENTAL_REDUCTION | Credit-support-capped subordinate reduction |
CAP_CARRYOVER | WAC cap shortfall payback |
Each step uses a payable structure tree to define distribution order:
| Structure | DSL | Description |
|---|---|---|
| Sequential | SEQ(A, B, C) | Pay A fully, then B, then C |
| Pro rata | PRORATA(A, B) | Split proportionally by balance |
| Shifting interest | SHIFTI(0.7, seniors, subs) | Split by percentage (70/30) |
| Enhancement cap | CSCAP(0.055, seniors, subs) | Redirect excess credit support to subs |
| Fixed amount | FIXED(var, primary, overflow) | Fixed dollar amount to primary, rest to overflow |
| Forced paydown | FORCE_PAYDOWN(forced, support) | Force one tranche to zero first |
| Proforma | PROFORMA(A, 0.6, B, 0.4) | Fixed percentage shares |
Structures can be nested: SEQ(PRORATA('A-1','A-2','A-3'), SINGLE('B'), SINGLE('C')).
Principal structures can switch based on trigger pass/fail state:
{
"type": "PRINCIPAL",
"source": "scheduled",
"rules": [
{
"when": { "pass": ["CE_Test"] },
"structure": { "type": "SHIFTI", "shiftVariable": "ShiftPct", ... }
},
{
"structure": { "type": "SEQ", "tranches": ["A", "B", "C"] }
}
]
}
Rules are evaluated in order; the first match wins. A rule with no when is the unconditional fallback.
The executionOrder array controls which steps run and in what sequence. The default order is:
EXPENSE -> INTEREST -> PRINCIPAL_SCHEDULED -> PRINCIPAL_UNSCHEDULED ->
PRINCIPAL_RECOVERY -> RESERVE -> WRITEDOWN -> EXCESS_TURBO -> EXCESS_RELEASE
The waterfallOrder field controls how interest and principal interact:
standard (default): all interest paid, then all principalinterestFirst: per seniority level, pay interest then principalprincipalFirst: per seniority level, pay principal then interestsrc/
GraamFlows.Api/ REST API (ASP.NET Core)
GraamFlows.Cli/ Command-line interface
GraamFlows.Core/ Waterfall engine, rules engine, triggers
GraamFlows.Domain/ Domain models (Deal, Tranche, PayRule, etc.)
GraamFlows.Objects/ Interfaces, enums, data contracts
GraamFlows.Util/ Pricing analytics, calendar, day counters, term structure, solvers
tests/
GraamFlows.Tests/ Unit and integration tests (xUnit)
Core/AssetCashflowEngine/Amortizer.cs) - High-performance collateral cashflow generator. Uses a struct-of-arrays asset layout (AssetDataArrays) and tight, allocation-free inner loops for cache locality — execution itself is single-threaded. Projects scheduled principal, prepayments, defaults, recoveries, and delinquencies for fixed-rate, ARM, step-rate, and IO loans.Core/Waterfall/Structures/ComposableStructure.cs) - The waterfall execution engine. Runs step-based waterfall periods, tracking available funds through each step.Core/Waterfall/Structures/PayableStructures/) - Composable payment distribution trees (Sequential, Prorata, ShiftingInterest, EnhancementCap, etc.).Core/RulesEngine/) - Compiles PayRule DSL formulas into executable C# at runtime using Roslyn. Supports trigger conditions, variable lookups, balance queries, and structure-building functions.Api/Transformers/UnifiedWaterfallBuilder.cs) - Transforms the steps-based JSON format into PayRule DSL for the engine.Util/Finance/Cashflow.cs) - Bond analytics calculator. Computes price, yield, Z-spread, discount margin, modified duration, WAL, and accrued interest from any cashflow stream against a term structure.Util/Solvers1D/) - Numerical root-finding (Brent, Newton-Raphson, bisection, etc.) used by the pricing engine to solve for yield and spread.The engine has been used to model:
Business Source License 1.1 — converts to Apache 2.0 on April 3, 2029. See LICENSE.