Write a property operations analysis report for The Edgewater in Philadelphia. Show me all the trends
mkdir -p /vercel/sandbox/work && cat > /vercel/sandbox/work/edgewater_trends.py << 'EOF'
import json
# Monthly time series (most recent first), from mf_rent_ts
rows = [
("2026-08","occ",0.944055944056,"askavg",2887.541667,"ipavg",2633.928571,"vac",16,"leases",13,"dom",78,"tradepct",-0.110827),
("2026-07","occ",0.979020979021,"askavg",2866.034483,"ipavg",2631.879310,"vac",6,"leases",11,"dom",44,"tradepct",0.127613),
("2026-06","occ",0.968531468531,"askavg",2790.875000,"ipavg",2626.630952,"vac",9,"leases",11,"dom",22,"tradepct",0.027396),
("2026-05","occ",0.986013986014,"askavg",2665.071429,"ipavg",2619.537037,"vac",4,"leases",14,"dom",12,"tradepct",-0.031403),
("2026-04","occ",1.000000000000,"askavg",2311.000000,"ipavg",2614.282051,"vac",0,"leases",17,"dom",42,"tradepct",-0.011052),
("2026-03","occ",0.972027972028,"askavg",2279.095238,"ipavg",2621.082759,"vac",8,"leases",16,"dom",181,"tradepct",-0.073181),
("2026-02","occ",0.926573426573,"askavg",2259.466667,"ipavg",2647.162791,"vac",21,"leases",6,"dom",17,"tradepct",0.034061),
("2026-01","occ",0.940559440559,"askavg",2310.387097,"ipavg",2659.817460,"vac",17,"leases",7,"dom",150,"tradepct",-0.004180),
("2025-12","occ",0.940559440559,"askavg",2254.823529,"ipavg",2670.387097,"vac",17,"leases",7,"dom",39,"tradepct",-0.064008),
("2025-11","occ",0.940559440559,"askavg",2170.648649,"ipavg",2690.295082,"vac",17,"leases",4,"dom",7,"tradepct",0.029516),
("2025-10","occ",0.947552447552,"askavg",2306.000000,"ipavg",2671.277311,"vac",15,"leases",2,"dom",66.5,"tradepct",0.015767),
("2025-09","occ",0.958041958042,"askavg",2400.281250,"ipavg",2681.537190,"vac",12,"leases",7,"dom",21,"tradepct",0.037803),
("2025-08","occ",0.979020979021,"askavg",2578.482759,"ipavg",2621.870968,"vac",6,"leases",9,"dom",52,"tradepct",-0.096552),
("2025-07","occ",0.965034965035,"askavg",2434.440000,"ipavg",2658.500000,"vac",10,"leases",13,"dom",71,"tradepct",0.058686),
("2025-06","occ",0.965034965035,"askavg",2567.363636,"ipavg",2670.163462,"vac",10,"leases",11,"dom",41.5,"tradepct",0.085081),
("2025-05","occ",0.979020979021,"askavg",2542.290323,"ipavg",2708.063830,"vac",6,"leases",15,"dom",47,"tradepct",None),
("2025-04","occ",0.975524475524,"askavg",2683.687500,"ipavg",2706.734177,"vac",7,"leases",7,"dom",110,"tradepct",None),
("2025-03","occ",0.968531468531,"askavg",2624.391304,"ipavg",2727.916667,"vac",9,"leases",7,"dom",76,"tradepct",-0.014540),
("2025-02","occ",0.961538461538,"askavg",2470.352941,"ipavg",2700.439394,"vac",11,"leases",6,"dom",24.5,"tradepct",-0.064185),
("2025-01","occ",0.968531468531,"askavg",2582.230769,"ipavg",2714.360656,"vac",9,"leases",12,"dom",156,"tradepct",None),
("2024-12","occ",0.954545454545,"askavg",2365.125000,"ipavg",2713.081633,"vac",13,"leases",7,"dom",143,"tradepct",None),
("2024-11","occ",0.954545454545,"askavg",2534.100000,"ipavg",2714.071429,"vac",13,"leases",3,"dom",77,"tradepct",None),
("2024-10","occ",0.951048951049,"askavg",2614.875000,"ipavg",2667.717949,"vac",14,"leases",4,"dom",50,"tradepct",None),
("2024-09","occ",0.951048951049,"askavg",2508.666667,"ipavg",2685.857143,"vac",14,"leases",11,"dom",55,"tradepct",None),
]
# parse into dict list
data = []
for r in rows:
d = {}
for i in range(0,len(r),2):
pass
data.append(r)
# occupancy series
occ = [(r[0], r[2]) for r in rows]
print("Occupancy series (oldest->newest):")
for m,v in reversed(occ):
print(m, round(v*100,1))
print()
occ_now = occ[0][1]
occ_3mo = occ[2][1]
occ_6mo = occ[5][1]
occ_12mo = occ[11][1]
print("Occ now:", round(occ_now*100,2), "3mo ago:", round(occ_3mo*100,2), "6mo ago:", round(occ_6mo*100,2), "12mo ago:", round(occ_12mo*100,2))
print("Occ chg t3 (pp):", round((occ_now-occ_3mo)*100,2))
print("Occ chg t6 (pp):", round((occ_now-occ_6mo)*100,2))
print("Occ chg t12 (pp):", round((occ_now-occ_12mo)*100,2))
# asking rent series
ask = [(r[0], r[4]) for r in rows]
ask_now = ask[0][1]; ask_12mo = ask[11][1]
print("\nAsking rent now:", round(ask_now,0), "12mo ago:", round(ask_12mo,0), "chg%:", round((ask_now/ask_12mo-1)*100,1))
ip = [(r[0], r[6]) for r in rows]
ip_now = ip[0][1]; ip_12mo = ip[11][1]
print("In-place rent now:", round(ip_now,0), "12mo ago:", round(ip_12mo,0), "chg%:", round((ip_now/ip_12mo-1)*100,2))
# spread ask vs in-place
spread_now = ask_now-ip_now
spread_pct_now = spread_now/ip_now*100
print("\nAsking vs in-place spread now: $%.0f (%.1f%%)" % (spread_now, spread_pct_now))
ask_12 = ask[11][1]; ip_12=ip[11][1]
spread_12 = ask_12-ip_12
print("Asking vs in-place spread 12mo ago: $%.0f (%.1f%%)" % (spread_12, spread_12/ip_12*100))
# tradeout trend - last 6 months avg vs prior 6 months avg (exclude None)
trades = [r[10] for r in rows if r[10] is not None]
recent6 = trades[:6]
prior6 = trades[6:12]
print("\nTradeout % last 6 months avg:", round(sum(recent6)/len(recent6)*100,2))
print("Tradeout % prior 6 months (7-12mo ago) avg:", round(sum(prior6)/len(prior6)*100,2) if prior6 else "n/a")
print("Months with negative tradeout (renewals renegotiated down) in trailing 12:", sum(1 for t in trades[:12] if t<0), "of", len(trades[:12]))
# vacant units trend
vac = [r[8] for r in rows]
print("\nVacant units now:", vac[0], "3mo ago:", vac[2], "6mo ago:", vac[5], "12mo ago:", vac[11])
# leasing velocity / DOM
dom = [r[12] for r in rows]
print("Days on market now:", dom[0], "3mo ago:", dom[2], "trailing 12mo avg DOM:", round(sum(dom[:12])/12,1))
EOF
python3 /vercel/sandbox/work/edgewater_trends.pycd /vercel/sandbox/work && python3 - << 'EOF'
rows = [
("2026-08",0.944055944056,2887.541667,2633.928571,16,13,78,-0.110827),
("2026-07",0.979020979021,2866.034483,2631.879310,6,11,44,0.127613),
("2026-06",0.968531468531,2790.875000,2626.630952,9,11,22,0.027396),
("2026-05",0.986013986014,2665.071429,2619.537037,4,14,12,-0.031403),
("2026-04",1.000000000000,2311.000000,2614.282051,0,17,42,-0.011052),
("2026-03",0.972027972028,2279.095238,2621.082759,8,16,181,-0.073181),
("2026-02",0.926573426573,2259.466667,2647.162791,21,6,17,0.034061),
("2026-01",0.940559440559,2310.387097,2659.817460,17,7,150,-0.004180),
("2025-12",0.940559440559,2254.823529,2670.387097,17,7,39,-0.064008),
("2025-11",0.940559440559,2170.648649,2690.295082,17,4,7,0.029516),
("2025-10",0.947552447552,2306.000000,2671.277311,15,2,66.5,0.015767),
("2025-09",0.958041958042,2400.281250,2681.537190,12,7,21,0.037803),
("2025-08",0.979020979021,2578.482759,2621.870968,6,9,52,-0.096552),
("2025-07",0.965034965035,2434.440000,2658.500000,10,13,71,0.058686),
("2025-06",0.965034965035,2567.363636,2670.163462,10,11,41.5,0.085081),
]
tradepct = [r[7] for r in rows]
recent6 = tradepct[:6]
prior6 = tradepct[6:12]
print("Last 6mo avg tradeout %:", round(sum(recent6)/len(recent6)*100,2))
print("Prior 6mo (7-12mo ago) avg tradeout %:", round(sum(prior6)/len(prior6)*100,2))
neg12 = sum(1 for t in tradepct[:12] if t<0)
print("Months negative tradeout trailing 12:", neg12, "of 12")
# vacancy / days on market
vac = [r[4] for r in rows]
dom = [r[6] for r in rows]
print("Vacant units: now",vac[0],"3mo ago",vac[2],"6mo ago",vac[5],"12mo ago",vac[11])
print("DOM: now",dom[0],"3mo ago",dom[2],"trailing12avg",round(sum(dom[:12])/12,1))
EOFcd /vercel/sandbox/work && python3 - << 'EOF'
# Financials
egi = 10492307.74
gpr = 9113510.82
opex = 3356859.16
noi = 7135448.59
noi_pct = 0.6946
opex_pct = 0.3054
vac_loss = 449426.36
other_income = 1828223.27
net_rent = 8664084.47
payroll_pct = 0.0670
rm_pct = 0.0372
insurance_pct = 0.0208
mgmt_pct = 0.0250
ga_pct = 0.0316
adv_pct = 0.0111
util_pct = 0.0187
tax_pct = 0.094 # this seems to be pct of egi per mf_tax_and_assessment... wait check units
capex_pct = 0.0066
print("EGI: $%.0f" % egi)
print("Gross potential rent: $%.0f" % gpr)
print("Vacancy loss % of GPR:", round(vac_loss/gpr*100,1),"%")
print("Other income % of net rent:", round(other_income/net_rent*100,1),"%")
print("Total OpEx: $%.0f (%.1f%% of EGI)" % (opex, opex_pct*100))
print("NOI: $%.0f (%.1f%% margin)" % (noi, noi_pct*100))
print()
lines = {
"Payroll & benefits": payroll_pct,
"R&M": rm_pct,
"Insurance": insurance_pct,
"Management fees": mgmt_pct,
"G&A": ga_pct,
"Advertising & marketing": adv_pct,
"Utilities": util_pct,
"CapEx": capex_pct,
}
tot = sum(lines.values())
print("Sum of itemized % (excl tax):", round(tot*100,2), "% -> $ amount:", round(tot*egi,0))
print("Tax amt pct of EGI from P&L:", tax_pct*100, "%")
tax_amt = 1137942
print("Tax $ amt:", tax_amt, "as % of EGI:", round(tax_amt/egi*100,2))
# unit economics
units=286
print("\nPer unit annualized:")
print("NOI/unit: $%.0f" % (noi/units))
print("OpEx/unit: $%.0f" % (opex/units))
print("Tax/unit: $%.0f" % (tax_amt/units))
print("Payroll/unit: $%.0f" % (payroll_pct*egi/units))
print("R&M/unit: $%.0f" % (rm_pct*egi/units))
print("Insurance/unit: $%.0f" % (insurance_pct*egi/units))
# mgmt fee rate check vs GPR
mgmt_amt = 262307.69
print("\nManagement fee as % of GPR:", round(mgmt_amt/gpr*100,2), "% (286 units -> norm band 3-4%)")
# tax burden context: assessed value vs sale price/unit
assess_total = 84009700
sale_price = 115973616
sale_ppu = 405502.15
assess_ppu = 293740.21
print("\nAssessed value: $%.0f vs 2019 sale price: $%.0f (assessed = %.0f%% of last sale)" % (assess_total, sale_price, assess_total/sale_price*100))
print("Assessed $/unit: $%.0f vs sale $/unit: $%.0f" % (assess_ppu, sale_ppu))
# submarket comparison
sub_asking_avg = 2659.03
sub_ip_avg = 2724.81
sub_occ = 0.9662
sub_retention = 0.731
sub_tradeout_pct = 0.0802
sub_ip_t12 = -0.0146
sub_ask_t12 = -0.0131
edge_asking_avg = 3059.62
edge_ip_avg = 2637.38
edge_occ = 0.9371
edge_retention = 0.6888
print("\n--- vs submarket (Center City - west) ---")
print("Asking rent: subject $%.0f vs submarket $%.0f (+%.1f%%)" % (edge_asking_avg, sub_asking_avg, (edge_asking_avg/sub_asking_avg-1)*100))
print("In-place rent: subject $%.0f vs submarket $%.0f (%.1f%%)" % (edge_ip_avg, sub_ip_avg, (edge_ip_avg/sub_ip_avg-1)*100))
print("Occupancy: subject %.1f%% vs submarket %.1f%% (%.1f pp)" % (edge_occ*100, sub_occ*100, (edge_occ-sub_occ)*100))
print("Retention: subject %.1f%% vs submarket %.1f%% (%.1f pp)" % (edge_retention*100, sub_retention*100, (edge_retention-sub_retention)*100))
EOFThe Edgewater is a stable, well-capitalized Center City high-rise where the story is a widening gap between advertised rent momentum and what's actually happening on lease renewals — and a leasing pace that's cooled noticeably in the last quarter.
Rent growth is a two-speed story. Asking rent has surged 20.3% over the trailing 12 months (to $2,888) , while in-place rent — what tenants actually pay — is essentially flat, down 1.8% to $2,637 . That flipped the asking/in-place spread from -10.5% a year ago to +9.6% today , meaning the property is quoting well above its existing rent roll. But new-lease tradeout tells the real story: it's negative in 6 of the last 12 months, and the trailing-6-month average tradeout is -1.2%, down from +0.8% in the prior six months . Renewals and re-leases are landing below the prior tenant's rent more often than not — the asking-rent rally is largely a marketing quote that isn't converting into achieved rent growth.
Leasing has decelerated sharply. Days-on-market for signed leases jumped to 78 in August, versus 22 just three months earlier and a trailing 12-month average of 57 . Vacant units nearly doubled from 9 to 16 over the same window , and physical occupancy has round-tripped from a brief 100% in April to 94.4% today — down 2.5 points over three months and 2.8 points over six . Retention sits at 68.9%, four points below the Center City-west submarket's 73.1% , with 89 unit turnovers projected annually on 286 units — roughly a 31% churn rate.
Cost structure is lean, and the tax line deserves a closer look. Total operating expenses run 30.5% of EGI against a 69.5% NOI margin — efficient by any peer standard, with payroll ($2,458/unit), R&M ($1,365/unit), and insurance ($763/unit) all modest for a 12-story, amenitized high-rise . Property tax, though, is $3,979/unit and 9–11% of EGI — and the assessed value ($293,740/unit) is still only 72% of the 2019 sale price ($405,502/unit) . That gap is a future expense risk, not an opportunity: reassessment catching up to the 2019 basis would push taxes higher, not lower.
The tenant base is strong but showing a soft signal worth tracking. Average FICO is 783, up 8.3 points year-over-year, with net worth in the 95th–96th percentile versus the MSA and nation — this is a high-quality renter pool. But household income growth has slowed to -1.1% over six months (below-average versus both MSA and national peers) , and personal-finance credit inquiries are up 97% year-over-year — often an early tell of households shopping for new credit lines before stress shows up in delinquency data, which remains low today (1.0% past-due rate) .
Bottom line: the fundamentals (cost structure, tenant credit) are sound, but the leasing engine is losing steam — rising vacancy, stretched days-on-market, and negative tradeout all point the same direction. Before trusting the 20% asking-rent story, the manager needs to explain why concessions or renewal pricing are eating into what's actually being signed; that's the single question this analysis raises, and the leasing/tradeout detail by unit type is the natural next pull.
This is a read-only snapshot of a RealAI analysis.