A sponsor is requesting acquisition financing at 70% LTV for The Modern Apartments in San Diego. Stress test the deal: what happens to DSCR if rent growth comes in at the bear-case forecast and exit cap rates widen 50 bps?
cat skills/forecasting/references/request.md | head -150
cat > /tmp/rent_history.json << 'EOF'
[
{"period_start_date":"2024-09-01","value":2864.952843},
{"period_start_date":"2024-10-01","value":2865.972254},
{"period_start_date":"2024-11-01","value":2863.159043},
{"period_start_date":"2024-12-01","value":2861.026584},
{"period_start_date":"2025-01-01","value":2857.268342},
{"period_start_date":"2025-02-01","value":2863.148358},
{"period_start_date":"2025-03-01","value":2865.532390},
{"period_start_date":"2025-04-01","value":2865.482531},
{"period_start_date":"2025-05-01","value":2866.155698},
{"period_start_date":"2025-06-01","value":2868.855569},
{"period_start_date":"2025-07-01","value":2872.012853},
{"period_start_date":"2025-08-01","value":2870.872415},
{"period_start_date":"2025-09-01","value":2877.035058},
{"period_start_date":"2025-10-01","value":2870.814240},
{"period_start_date":"2025-11-01","value":2867.102616},
{"period_start_date":"2025-12-01","value":2866.072514},
{"period_start_date":"2026-01-01","value":2869.285334},
{"period_start_date":"2026-02-01","value":2869.508619},
{"period_start_date":"2026-03-01","value":2870.882704},
{"period_start_date":"2026-04-01","value":2874.346933},
{"period_start_date":"2026-05-01","value":2877.189242},
{"period_start_date":"2026-06-01","value":2882.825459},
{"period_start_date":"2026-07-01","value":2891.514593},
{"period_start_date":"2026-08-01","value":2897.170591}
]
EOF
python3 -c "
import json
data = json.load(open('/tmp/rent_history.json'))
print(len(data), data[0]['value'], data[-1]['value'], sum(d['value'] for d in data))
"
python3 << 'EOF' > /tmp/rent_payload.json
import json
data = json.load(open('/tmp/rent_history.json'))
history = [{"period": d["period_start_date"], "value": d["value"], "source": "datamart"} for d in data]
payload = {
"metric": {"name": "mf_rent_ts.in_place_rent_latest_avg for San Diego, CA MSA", "units": "$", "family": "rent_or_occupancy"},
"subject": {"entity_type": "market", "entity_id": "41740", "label": "San Diego, CA MSA - multifamily in-place rent"},
"horizon": {"years": 5, "intervals": "annual"},
"as_of": "2026-09-11",
"requested_lookback_years": 2.0,
"caller_disclosures": {
"peer_omitted_reason": "not gathered",
"lookback_note": "full available monthly history (24 months, Sep 2024-Aug 2026); no earlier months populated in datamart"
},
"history": history,
"history_check": {"count": len(history), "sum": round(sum(d["value"] for d in history),6), "first_value": history[0]["value"], "last_value": history[-1]["value"]},
"context_signals": {
"supply_pipeline": {},
},
"scenarios": ["base","upside","downside"]
}
print(json.dumps(payload))
EOF
cat /tmp/rent_payload.json | python3 -m json.tool | head -5
python3 << 'EOF' > /tmp/rent_payload.json
import json
data = json.load(open('/tmp/rent_history.json'))
history = [{"period": d["period_start_date"], "value": d["value"], "source": "datamart"} for d in data]
payload = {
"metric": {"name": "mf_rent_ts.in_place_rent_latest_avg for San Diego, CA MSA", "units": "$", "family": "rent_or_occupancy"},
"subject": {"entity_type": "market", "entity_id": "41740", "label": "San Diego, CA MSA - multifamily in-place rent"},
"horizon": {"years": 5, "intervals": "annual"},
"as_of": "2026-09-11",
"requested_lookback_years": 2.0,
"caller_disclosures": {
"peer_omitted_reason": "not gathered",
"lookback_note": "full available monthly history (24 months, Sep 2024-Aug 2026); no earlier months populated in datamart",
"signals_omitted": [
{"signal": "supply_pipeline", "reason": "not gathered"},
{"signal": "migration", "reason": "not gathered"},
{"signal": "employment", "reason": "not gathered"}
]
},
"history": history,
"history_check": {"count": len(history), "sum": round(sum(d["value"] for d in history),6), "first_value": history[0]["value"], "last_value": history[-1]["value"]},
"scenarios": ["base","upside","downside"]
}
print(json.dumps(payload))
EOF
python skills/forecasting/scripts/forecast.py --file /tmp/rent_payload.json > /tmp/rent_forecast.json
cat /tmp/rent_forecast.json | python3 -m json.tool
cat > /tmp/cap_history.json << 'EOF'
[
{"quarter":"3Q20","value":0.0425},
{"quarter":"4Q20","value":0.0425},
{"quarter":"1Q21","value":0.0425},
{"quarter":"2Q21","value":0.0384},
{"quarter":"3Q21","value":0.0373},
{"quarter":"4Q21","value":0.0367},
{"quarter":"1Q22","value":0.0378},
{"quarter":"2Q22","value":0.0413},
{"quarter":"3Q22","value":0.0423},
{"quarter":"4Q22","value":0.0488},
{"quarter":"1Q23","value":0.0503},
{"quarter":"2Q23","value":0.0503},
{"quarter":"3Q23","value":0.0564},
{"quarter":"4Q23","value":0.0554},
{"quarter":"1Q24","value":0.0555},
{"quarter":"2Q24","value":0.0514},
{"quarter":"3Q24","value":0.0499},
{"quarter":"4Q24","value":0.0508},
{"quarter":"1Q25","value":0.0509},
{"quarter":"2Q25","value":0.0508},
{"quarter":"3Q25","value":0.0509},
{"quarter":"4Q25","value":0.0509},
{"quarter":"1Q26","value":0.0509},
{"quarter":"2Q26","value":0.0493}
]
EOF
python3 << 'EOF' > /tmp/cap_payload.json
import json
data = json.load(open('/tmp/cap_history.json'))
qmap = {"1":"01","2":"04","3":"07","4":"10"}
def qtr_to_date(q):
qn, yy = q[0], q[1:]
year = 2000+int(yy)
return f"{year}-{qmap[qn]}-01"
history = [{"period": qtr_to_date(d["quarter"]), "value": d["value"], "source":"datamart"} for d in data]
payload = {
"metric": {"name": "caprate_ts.multifamily for San Diego, CA MSA", "units": "%", "family": "capital_markets"},
"subject": {"entity_type": "market", "entity_id": "41740", "label": "San Diego, CA MSA - multifamily cap rate"},
"horizon": {"years": 5, "intervals": "annual"},
"as_of": "2026-09-11",
"caller_disclosures": {
"peer_omitted_reason": "no comparable peer at grain (caprate_ts is market-grain only; national multifamily average available but represents a different price/cycle tier)",
"rate_instrument": "ten_year_treasury",
"lookback_note": "full available quarterly history, 3Q20-2Q26 (24 quarters)"
},
"history": history,
"history_check": {"count": len(history), "sum": round(sum(d["value"] for d in history),6), "first_value": history[0]["value"], "last_value": history[-1]["value"]},
"context_signals": {
"rate_environment": {"current_rate": 4.77, "direction": "stable", "note": "10Y Treasury 4.77% as of 2026-09-04"}
}
}
print(json.dumps(payload))
EOF
python skills/forecasting/scripts/forecast.py --file /tmp/cap_payload.json > /tmp/cap_forecast.json
cat /tmp/cap_forecast.json | python3 -m json.tool
python3 << 'EOF' > /tmp/cap_payload.json
import json
data = json.load(open('/tmp/cap_history.json'))
qmap = {"1":"01","2":"04","3":"07","4":"10"}
def qtr_to_date(q):
qn, yy = q.split("Q")
year = 2000+int(yy)
return f"{year}-{qmap[qn]}-01"
history = [{"period": qtr_to_date(d["quarter"]), "value": d["value"], "source":"datamart"} for d in data]
payload = {
"metric": {"name": "caprate_ts.multifamily for San Diego, CA MSA", "units": "%", "family": "capital_markets"},
"subject": {"entity_type": "market", "entity_id": "41740", "label": "San Diego, CA MSA - multifamily cap rate"},
"horizon": {"years": 5, "intervals": "annual"},
"as_of": "2026-09-11",
"caller_disclosures": {
"peer_omitted_reason": "no comparable peer at grain (caprate_ts is market-grain only; national multifamily average available but represents a different price/cycle tier)",
"rate_instrument": "ten_year_treasury",
"lookback_note": "full available quarterly history, 3Q20-2Q26 (24 quarters)"
},
"history": history,
"history_check": {"count": len(history), "sum": round(sum(d["value"] for d in history),6), "first_value": history[0]["value"], "last_value": history[-1]["value"]},
"context_signals": {
"rate_environment": {"current_rate": 4.77, "direction": "stable", "note": "10Y Treasury 4.77% as of 2026-09-04"}
}
}
print(json.dumps(payload))
EOF
python skills/forecasting/scripts/forecast.py --file /tmp/cap_payload.json > /tmp/cap_forecast.json
cat /tmp/cap_forecast.json | python3 -m json.tool
cat agents/01a087be-a40c-70fb-88a9-40a61b0703c2/RealAI_Credit_Underwriting_Template_manifest_v2.md | head -200
sed -n '200,420p' agents/01a087be-a40c-70fb-88a9-40a61b0703c2/RealAI_Credit_Underwriting_Template_manifest_v2.md
cp agents/01a087be-a40c-70fb-88a9-40a61b0703c2/RealAI_Credit_Underwriting_Template_v3_clean.xlsx /vercel/sandbox/Modern_Apts_SanDiego_Underwriting.xlsx
python3 -c "
import openpyxl
wb = openpyxl.load_workbook('Modern_Apts_SanDiego_Underwriting.xlsx')
print(wb.sheetnames)
ws = wb['Assumptions']
for row in range(1,60):
for col in ['B','C','E','F']:
cell = ws[f'{col}{row}']
if cell.value is not None:
print(col,row,repr(cell.value))
"
python3 -c "
import openpyxl
wb = openpyxl.load_workbook('Modern_Apts_SanDiego_Underwriting.xlsx')
for sheetname in ['Assumptions','Prepayment','Defeasance']:
ws = wb[sheetname]
print('---', sheetname)
for dv in ws.data_validations.dataValidation:
print(dv.sqref, dv.formula1)
"
python3 -c "
import openpyxl
wb = openpyxl.load_workbook('Modern_Apts_SanDiego_Underwriting.xlsx')
ws = wb['Pro Forma']
for row in range(1,55):
for col in ['B','C','D','E','F','G','H','I','J']:
cell = ws[f'{col}{row}']
if cell.value is not None:
print(col,row,repr(cell.value))
"
python3 << 'EOF'
# Key computations for underwriting assumptions
unit_count = 369
egi = 13070570.87
gpr = 12485572.08
noi = 9000616.35
vacancy_loss = 482471.36
other_income = 1067470.15
total_opex = 4069954.51
mgmt_fee_pct = 0.0250
capex = 128677.91
tax_pct = 0.1086
vacancy_rate_pct_of_gpr = vacancy_loss/gpr
other_income_pct_gpr = other_income/gpr
opex_ratio_pct_of_egi = total_opex/egi
reserves_per_unit = capex/unit_count
market_cap_rate = 0.0493
lender_value = noi/market_cap_rate
requested_ltv = 0.70
requested_proceeds = lender_value*requested_ltv
print("vacancy_rate_pct_of_gpr", round(vacancy_rate_pct_of_gpr,4))
print("other_income_pct_gpr", round(other_income_pct_gpr,4))
print("opex_ratio_pct_of_egi", round(opex_ratio_pct_of_egi,4))
print("reserves_per_unit", round(reserves_per_unit,2))
print("lender_value", round(lender_value,0))
print("lender_value_per_unit", round(lender_value/unit_count,0))
print("requested_proceeds", round(requested_proceeds,0))
print("requested_proceeds_per_unit", round(requested_proceeds/unit_count,0))
# bear-case rent growth CAGR from forecast downside_case
start = 2897.170591
y5 = 2722.76
cagr = (y5/start)**(1/5)-1
print("bear_case_noi_growth_cagr", round(cagr,4))
# base case CAGR for reference
y5_base = 3115.02
cagr_base = (y5_base/start)**(1/5)-1
print("base_case_growth_cagr", round(cagr_base,4))
EOF
python3 -c "
import openpyxl
wb = openpyxl.load_workbook('Modern_Apts_SanDiego_Underwriting.xlsx')
ws = wb['Assumptions']
for row in range(46,59):
for col in ['B','C','E','F']:
cell = ws[f'{col}{row}']
print(col,row,repr(cell.value))
print('---F thresholds---')
for row in [55,56,57,58]:
print('F',row, ws[f'F{row}'].value)
print('--- prepayment sheet defaults ---')
ws2 = wb['Prepayment']
for row in range(4,80):
c = ws2[f'C{row}']
if c.value is not None:
print('C',row, repr(c.value))
"
python3 -c "
import openpyxl
wb = openpyxl.load_workbook('Modern_Apts_SanDiego_Underwriting.xlsx')
for sheetname in ['Loan Sizing','Recovery','Stress & Break-Even','Refi Analysis']:
ws = wb[sheetname]
print('===',sheetname)
for row in ws.iter_rows():
for cell in row:
if cell.value is not None:
print(cell.coordinate, repr(cell.value))
" | head -260
python3 -c "
import openpyxl
wb = openpyxl.load_workbook('Modern_Apts_SanDiego_Underwriting.xlsx')
ws = wb['Refi Analysis']
for row in ws.iter_rows():
for cell in row:
if cell.value is not None:
print(cell.coordinate, repr(cell.value))
"
cat > /tmp/populate.py << 'PYEOF'
import openpyxl
from datetime import date
wb = openpyxl.load_workbook('Modern_Apts_SanDiego_Underwriting.xlsx')
ws = wb['Assumptions']
pf = wb['Prepayment']
de = wb['Defeasance']
# --- Collateral ---
ws['C5'] = "The Modern Apartments"
ws['C6'] = "3201 Annrae St"
ws['C7'] = "San Diego, CA 92123"
ws['C8'] = "Multifamily"
ws['C9'] = 369
ws['C10'] = 367155
ws['C11'] = "Units"
ws['C13'] = 1959
ws['C14'] = 0.9675
ws['C15'] = "Market Averages"
# --- Loan request ---
ws['F5'] = "Origination"
ws['F6'] = "Acquisition-Term"
ws['F7'] = date(2026, 9, 11)
ws['F8'] = 127797798 # 70% LTV x lender value (NOI / current 4.93% market cap)
ws['F9'] = 0.0606 # Freddie Mac CME Fixed Rate, 10yr, <=80% LTV tier, as of 2026-09-02
ws['F10'] = 10
ws['F11'] = 0
ws['F12'] = 30
ws['F14'] = 0.01
ws['F15'] = "Non-Recourse"
# --- Credit box (default) ---
ws['F18'] = 0.65
ws['F19'] = 1.25
ws['F20'] = 1.20
ws['F21'] = 0.095
ws['F22'] = 0.10
ws['F23'] = "default"
ws['F24'] = 50
ws['F25'] = 50
# --- Sponsor & guarantee ---
ws['C18'] = "Not disclosed"
ws['C19'] = "Partial / Springing"
ws['C20'] = "No"
# --- Growth & exit: THE STRESS SCENARIO ---
ws['C28'] = -0.0123 # bear-case in-place rent/NOI CAGR (forecast engine downside_case, San Diego MF)
ws['C29'] = 0 # cap rate held flat through the hold; the 50bp widening is applied discretely at maturity via F25
ws['C30'] = 0.02
ws['C31'] = "Moderate"
# --- Takeout terms at maturity ---
ws['C34'] = 0.0606
ws['C35'] = 1.25
ws['C36'] = 0.65
ws['C37'] = 0.095
ws['C38'] = 30
ws['C41'] = "Moderate"
# --- Bridge/recovery non-applicable inputs still read by Recovery sheet ---
ws['C53'] = 0.05 # Foreclosure & legal % of value
ws['C54'] = 6 # Workout carry months
# --- Rollover (non-MF): not applicable ---
ws['F47'] = "No"
# --- Flag thresholds (defaults) ---
ws['F55'] = 0.90
ws['F56'] = 0.30
ws['F57'] = 0.10
ws['F58'] = 0.03
# --- Market benchmarks ---
ws['F29'] = 0.0386 # market/submarket vacancy (property benchmark vacancy loss / GPR)
ws['F30'] = 0.05 # asset-class vacancy norm, MF
ws['F32'] = 0.3114 # benchmark OpEx ratio - submarket (property benchmark total opex/EGI, incl. taxes)
ws['F33'] = 0.42 # asset-class OpEx norm, MF
ws['F35'] = "Yes"
ws['F36'] = 0.0493 # market cap rate, San Diego MF, latest quarter (2Q26)
ws['F38'] = 3256.04 # market asking rent/unit/month, San Diego MSA (Aug 2026)
ws['F39'] = 0.0 # concessions not separately broken out in this dataset (folded into vacancy loss)
ws['F40'] = 0.0 # bad debt not separately broken out (folded into vacancy loss)
ws['F41'] = 0.0855 # other income % of GPR (property benchmark)
ws['F44'] = "Modest" # supply pipeline vs term - AI estimate, San Diego is land/entitlement constrained
wb.save('Modern_Apts_SanDiego_Underwriting.xlsx')
print("Assumptions written")
PYEOF
python3 /tmp/populate.py
Now the Pro Forma rates and the Prepayment/Defeasance inputs.
cat > /tmp/populate2.py << 'PYEOF'
import openpyxl
from datetime import date
wb = openpyxl.load_workbook('Modern_Apts_SanDiego_Underwriting.xlsx')
pfsheet = wb['Pro Forma']
prep = wb['Prepayment']
de = wb['Defeasance']
# Historical columns: no T12 provided -> zero (never text)
for col in ['C','D','E']:
for row in [5,6,7,8,9,13,14,15,16,17,18,19,20,21,22]:
pfsheet[f'{col}{row}'] = 0
# Rate assumptions (always rate-driven on lender's own base)
pfsheet['C39'] = 0.0250 # management fee % of EGI (property benchmark)
pfsheet['C40'] = 348.72 # replacement reserves $/unit/yr (property benchmark capex/unit)
# --- Prepayment ---
prep['C4'] = 60 # assumed 5-yr hold/exit (not specified by sponsor; disclosed assumption)
prep['C5'] = 0.01
prep['C6'] = 0.02
prep['C7'] = 50
prep['C8'] = 24 # lockout, months
prep['C9'] = 3 # open period before maturity, months
prep['C10'] = "Yield Maintenance"
# Treasury curve, anchored to platform 10Y (4.77%), web curve shape shifted -6bps to tie out
prep['D27'] = 0.0395 # 6mo
prep['D28'] = 0.0411 # 1yr
prep['D29'] = 0.0437 # 2yr
prep['D30'] = 0.0443 # 3yr
prep['D31'] = 0.0455 # 5yr
prep['D32'] = 0.0465 # 7yr
prep['D33'] = 0.0477 # 10yr - platform value (national_metrics_daily.ten_year_treasury_pct)
prep['D34'] = 0.0522 # 20yr
prep['D35'] = 0.0522 # 30yr
prep['C36'] = date(2026, 9, 9)
# --- Defeasance ---
de['C11'] = 25000
de['C12'] = 40000
de['C13'] = 10000
de['C14'] = 15000
de['C15'] = 10000
de['C16'] = "Open Window"
wb.save('Modern_Apts_SanDiego_Underwriting.xlsx')
print("Pro Forma / Prepayment / Defeasance written")
PYEOF
python3 /tmp/populate2.py
Now recalc and run the integrity gate.
cd /vercel/sandbox && python skills/xlsx/scripts/workbook_integrity_scan.py Modern_Apts_SanDiego_Underwriting.xlsx 2>&1 | tail -80
cd /vercel/sandbox && python skills/xlsx/scripts/recalc.py Modern_Apts_SanDiego_Underwriting.xlsx 2>&1 | tail -40
cd /vercel/sandbox && cp agents/01a087be-a40c-70fb-88a9-40a61b0703c2/RealAI_Credit_Underwriting_Template_v3_clean.xlsx /tmp/orig_check.xlsx python skills/xlsx/scripts/recalc.py /tmp/orig_check.xlsx 2>&1 | tail -20
cd /vercel/sandbox && python3 -c "
import openpyxl
wb = openpyxl.load_workbook('Modern_Apts_SanDiego_Underwriting.xlsx', data_only=True)
pf = wb['Pro Forma']
ls = wb['Loan Sizing']
ra = wb['Refi Analysis']
sb = wb['Stress & Break-Even']
rec = wb['Recovery']
asum = wb['Assumptions']
print('Lender-case NOI G28:', pf['G28'].value)
print('Lender value C44:', pf['C44'].value)
print('Lender value/unit C45:', pf['C45'].value)
print()
print('Requested proceeds F8:', asum['F8'].value)
print('DSCR at request C17:', ls['C17'].value)
print('Debt yield at request C18:', ls['C18'].value)
print('LTV at request C19:', ls['C19'].value)
print('Supportable proceeds C29:', ls['C29'].value)
print('Binding constraint C30:', ls['C30'].value)
print('Surplus/(gap) C35:', ls['C35'].value)
print('Counter-offer C36:', ls['C36'].value)
print()
print('--- Refi Analysis annual DSCR (row 11), years 1-10 ---')
for col in ['E','F','G','H','I','J','K','L','M','N']:
yr = ra[f'{col}4'].value
dscr = ra[f'{col}11'].value
noi = ra[f'{col}8'].value
print(col, 'yearidx',yr, 'NOI', noi, 'DSCR', dscr)
print()
print('Maturity month C19:', ra['C19'].value)
print('Balance at maturity C20:', ra['C20'].value)
print('NOI at maturity C21:', ra['C21'].value)
print('Stressed takeout rate C22:', ra['C22'].value)
print('Stressed cap at maturity C23:', ra['C23'].value)
print('Stressed value at maturity C24:', ra['C24'].value)
print('Stressed refi proceeds C28:', ra['C28'].value)
print('Binding takeout constraint C29:', ra['C29'].value)
print('Refi gap C30:', ra['C30'].value)
print('Refi gap % C31:', ra['C31'].value)
print('Refi coverage x C32:', ra['C32'].value)
print('Maturity test result C33:', ra['C33'].value)
print()
print('--- Stress & Break-even grid ---')
print('DSCR floor applied C12:', sb['C12'].value)
for r in range(6,12):
rate = sb[f'B{r}'].value
row = [sb[f'{c}{r}'].value for c in ['C','D','E','F','G']]
print('rate',rate, row)
print('NOI breakeven C18:', sb['C18'].value)
print('NOI cushion C20/C21:', sb['C20'].value, sb['C21'].value)
print('Breakeven occ C23:', sb['C23'].value)
print('Breakeven rent C24:', sb['C24'].value)
print()
print('Recovery E14 (-20%):', rec['E14'].value)
print('Recovery E13:', rec['E13'].value)
print('Recovery E16 loss severity:', rec['E16'].value)
"
python3 -c "
import numpy_financial as npf
" 2>/dev/null || pip -q install numpy-financial 2>/dev/null
python3 << 'EOF'
import numpy_financial as npf
loan = 89124733.233792
rate = 0.0606
n = 360
pmt_monthly = -npf.pmt(rate/12, n, loan)
annual_ds = pmt_monthly*12
print('annual debt service at counter-offer', annual_ds)
noi_y1 = 8362707.40642655
noi_y10 = 7481219.30579932
noi_y0 = 8466849.65721024
print('DSCR at counter-offer, year0 (unstressed):', noi_y0/annual_ds)
print('DSCR at counter-offer, year1 bear case:', noi_y1/annual_ds)
print('DSCR at counter-offer, year10 bear case:', noi_y10/annual_ds)
EOF
cd /vercel/sandbox && python3 -c "
import openpyxl
wb = openpyxl.load_workbook('Modern_Apts_SanDiego_Underwriting.xlsx', data_only=True)
ls = wb['Loan Sizing']
pf = wb['Pro Forma']
ra = wb['Refi Analysis']
rec = wb['Recovery']
print('C39 DSCR at counter-offer (unstressed):', ls['C39'].value)
print('C40 debt yield at counter-offer:', ls['C40'].value)
print('C41 LTV at counter-offer:', ls['C41'].value)
print('Recovery E14 (-20% value):', rec['E14'].value)
print('Recovery E16 (-20% loss severity):', rec['E16'].value)
print('Recovery C21 value decline at threshold recovery of par:', rec['C21'].value)
"
Now saving and presenting the workbook.
Setup: 70% LTV ($127.8M) acquisition-term loan on The Modern Apartments (369 units, San Diego), Freddie Mac CME 10yr fixed at 6.06%/30yr am, non-recourse, sized against benchmark operations since no T12 was provided — full detail and every input is in the attached workbook.
This deal doesn't clear before you stress anything — and the stress makes it worse
The 70% LTV request implies $127.8M of proceeds against a lender-case NOI of $8.47M — that alone prices to a 0.91x DSCR , already below breakeven. The debt yield test caps supportable proceeds at $89.1M (49% LTV) , a $38.7M gap to what's being asked for. The market cap rate compressed to 4.93% while the lender's own vacancy/opex floors (5% MF norm, 42% expense ratio) cut harder into revenue than the raw benchmarks suggest — that gap exists before either stress is applied.
Layer in the two stresses:
What a lender actually sizes to. At the $89.1M supportable proceeds (49% LTV), Year-0 DSCR is a healthy 1.31x — but run the same bear-case rent path against that fixed debt service and coverage still crosses below the 1.25x floor around Year 4 and drifts to roughly 1.16x by Year 10 . Even the right-sized loan has thin room against a sustained rent downturn; it just survives it instead of failing it.
Recommendation: Decline as requested; Approve with Structure at $89.1M (49% LTV). The binding constraint is the debt yield test, not leverage optics — cutting proceeds to $89.1M is what buys the cushion to survive the bear case. Sponsor financials weren't provided, which caps any verdict at "Approve with Structure" regardless . At -20% value decline in a default scenario, recovery on the requested $127.8M is 84% of par — value would need to fall roughly 26% before recovery breaks below par .
What would change this: a sponsor T12 running materially above the market-benchmark NOI used here (no document was provided), or a purchase price meaningfully below the $182.6M this request implies at the current market cap rate.
This is a read-only snapshot of a RealAI analysis.