Compare Brush Creek Meadows rent, leasing and occupancy activity against its core rent comps and the wider submarket. Where do we have room to grow, and where should we focus improvements?
I'll start by locating the property and checking for relevant agent methodology.
mkdir -p /vercel/sandbox/work && cd /vercel/sandbox/work && cat > comp_select.py <<'EOF'
import math, json
subj = dict(name="Brush Creek Meadows", lat=36.36111706495294, lon=-82.31966078281403, units=83, yb=2008, style="LOW_RISE", sqft=869, restr="unrestricted")
cands = [
("Universal at Mountainview",36.34249180555352,-82.41263687610626,324,2025,"GARDEN",905,"unrestricted"),
("Colony Brooke Condominiums",36.27850502729425,-82.48472392559052,97,None,"TOWNHOUSE",1440,"unrestricted"),
("Universal at Gray Station",36.41607552766809,-82.47332990169527,226,2023,"GARDEN",891,"unrestricted"),
("The Villas at Boone Ridge",36.38418406248103,-82.42635905742645,251,2014,"TOWNHOUSE",921,"unrestricted"),
("Knob Creek Apartments",36.34429961442955,-82.41144597530366,285,2008,"MID_RISE",998,"unrestricted"),
("Smoketree Townhomes",36.37616962194452,-82.38298237323761,20,1981,"TOWNHOUSE",1100,"unrestricted"),
("The Villas at Towne Acres",36.34972840547571,-82.39145815372467,60,2012,"TOWNHOUSE",781,"unrestricted"),
("Martin Square",36.411344110965814,-82.46587336063386,120,2024,"TOWNHOUSE",1455,"unrestricted"),
("Rockingham Townhomes",36.414176523685555,-82.42005050182344,14,2016,"TOWNHOUSE",640,"unrestricted"),
("Park Place Apartments",36.320363581180665,-82.32902705669403,64,2001,"LOW_RISE",1264,"unrestricted"),
("The Hub at Mountcastle",36.34293168783198,-82.37073004245758,63,2023,"MID_RISE",600,"unrestricted"),
("Stone Crest Townhomes",36.344836056232545,-82.39849627017976,88,2010,"TOWNHOUSE",903,"unrestricted"),
("The Reserve at Johnson City",36.34918123483667,-82.40837752819061,248,2013,"LOW_RISE",1024,"unrestricted"),
("Rosemont",36.32125407457361,-82.38774597644806,131,1983,"TOWNHOUSE",836,"unrestricted"),
("North Creek Duplex",36.41361862421045,-82.42365539073946,2,1980,"LOW_RISE",675,"unrestricted"),
("University Edge",36.31102412939081,-82.36144959926605,192,2011,"GARDEN",371,"STUDENT"),
("Universal at Mockingbird Lane",36.33577555418023,-82.37948477268219,262,2021,"LOW_RISE",948,"unrestricted"),
("Vintage Johnson City Apartments",36.34924560785302,-82.4063390493393,288,2026,"MID_RISE",1182,"unrestricted"),
("Summer Chase Apartments",36.348628699779596,-82.36391723155975,96,1983,"LOW_RISE",909,"unrestricted"),
("Miller Crest",36.318856179714295,-82.38467752933502,101,1973,"LOW_RISE",898,"unrestricted"),
("Gardner Village",36.41144067049035,-82.46810495853424,66,2023,"TOWNHOUSE",1435,"unrestricted"),
("Blue Ridge Apartments",36.30103021860131,-82.32638776302338,80,1999,"GARDEN",1034,"unrestricted"),
("Suncrest Village",36.400357782840814,-82.48843610286713,97,2007,"GARDEN",832,"unrestricted"),
]
def hav(a,b,c,d):
R=3958.8; p=math.radians
dlat=p(c-a); dlon=p(d-b)
x=math.sin(dlat/2)**2+math.cos(p(a))*math.cos(p(c))*math.sin(dlon/2)**2
return 2*R*math.asin(math.sqrt(x))
rows=[]
for n,la,lo,u,yb,st,sf,r in cands:
rows.append(dict(name=n,dist=round(hav(subj['lat'],subj['lon'],la,lo),2),units=u,yb=yb,style=st,sqft=sf,restr=r))
rows.sort(key=lambda r:r['dist'])
for r in rows: print(f"{r['dist']:>5} mi {r['name'][:32]:<34} {str(r['units']):>4}u yb={r['yb']} {r['style']:<10} {r['sqft']}sf {r['restr']}")
EOF
python3 comp_select.pycd /vercel/sandbox/work && cat > rent_position_data.py <<'PY'
# Subject rent roll: (unit, beds, sqft, status, in_place, asking, dom)
RR = [
("9-902",2,1024,"OCCUPIED",1350,None,None),("9-903",2,1024,"OCCUPIED",1350,None,None),
("14-1403",1,576,"OCCUPIED",1000,None,None),("15-1502",1,576,"OCCUPIED",1025,None,3),
("7-702",2,1024,"OCCUPIED",1350,None,None),("8-804",2,1024,"VACANT",None,1299,None),
("4-403",1,512,"OCCUPIED",1096,None,28),("16-1603",1,576,"OCCUPIED",1000,None,None),
("9-905",2,1024,"OCCUPIED",1350,None,None),("14-1404",1,576,"OCCUPIED",1000,None,None),
("7-707",2,1024,"OCCUPIED",1350,None,None),("16-1604",1,576,"OCCUPIED",1000,None,None),
("7-703",2,1024,"OCCUPIED",1350,None,None),("13-1304",1,576,"OCCUPIED",1000,None,None),
("11-1105",2,1024,"OCCUPIED",1350,None,None),("16-1605",1,576,"OCCUPIED",1025,None,75),
("13-1303",1,576,"OCCUPIED",1000,None,None),("12-1203",1,576,"OCCUPIED",1000,None,30),
("11-1101",2,1024,"OCCUPIED",1350,None,None),("1-101",3,1280,"OCCUPIED",1944,None,None),
("10-1001",2,1024,"OCCUPIED",1350,None,None),("4-405",1,512,"OCCUPIED",1000,None,None),
("13-1302",1,576,"OCCUPIED",1000,None,None),("12-1205",1,576,"OCCUPIED",1000,None,None),
("8-803",2,1024,"OCCUPIED",1350,None,None),("4-404",1,512,"OCCUPIED",1000,None,None),
("1-103",3,1280,"OCCUPIED",1550,None,None),("15-1503",1,576,"OCCUPIED",1000,None,None),
("10-1005",2,1024,"OCCUPIED",1350,None,26),("17-1701",2,1024,"OCCUPIED",1350,None,None),
("6-602",2,1024,"OCCUPIED",1350,None,None),("11-1103",2,1024,"OCCUPIED",1350,None,None),
("15-1505",1,576,"OCCUPIED",1000,None,None),("4-407",1,512,"OCCUPIED",1096,None,23),
("15-1501",1,576,"OCCUPIED",1000,None,None),("4-402",1,512,"OCCUPIED",1000,None,None),
("10-1003",2,1024,"OCCUPIED",1350,None,None),("5-502",2,1024,"OCCUPIED",1275,None,31),
("4-401",1,512,"OCCUPIED",1096,None,None),("5-506",2,1024,"OCCUPIED",1275,None,46),
("8-805",2,1024,"OCCUPIED",1350,None,None),("16-1602",1,576,"OCCUPIED",1000,None,None),
("17-1702",2,1024,"OCCUPIED",1350,None,None),("6-601",2,1024,"OCCUPIED",1350,None,None),
("13-1305",1,576,"OCCUPIED",1000,None,None),("5-503",2,1024,"OCCUPIED",1350,None,None),
("10-1004",2,1024,"OCCUPIED",1350,None,None),("6-604",2,1024,"OCCUPIED",1350,None,None),
("7-705",2,1024,"OCCUPIED",1275,None,85),("11-1102",2,1024,"OCCUPIED",1350,None,None),
("9-904",2,1024,"OCCUPIED",1350,None,None),("7-706",2,1024,"OCCUPIED",1350,None,None),
("8-802",2,1024,"OCCUPIED",1350,None,None),("6-605",2,1024,"OCCUPIED",1350,None,None),
("16-1601",1,576,"OCCUPIED",1000,None,None),("8-801",2,1024,"OCCUPIED",1350,None,None),
("13-1301",1,576,"OCCUPIED",1149,None,4),("3-304",3,1280,"OCCUPIED",1550,None,None),
("3-302",3,1280,"VACANT",None,1699,None),("3-301",3,1280,"OCCUPIED",1550,None,None),
("12-1201",1,576,"OCCUPIED",1025,None,31),("12-1204",1,576,"OCCUPIED",1000,None,24),
("14-1405",1,576,"OCCUPIED",1000,None,None),("9-901",2,1024,"OCCUPIED",1275,None,72),
("10-1002",2,1024,"OCCUPIED",1350,None,75),("15-1504",1,576,"OCCUPIED",1000,None,None),
("14-1402",1,576,"OCCUPIED",1000,None,None),("6-606",2,1024,"OCCUPIED",1350,None,43),
("3-303",3,1280,"OCCUPIED",1550,None,None),("7-704",2,1024,"OCCUPIED",1350,None,102),
("11-1104",2,1024,"OCCUPIED",1350,None,None),("6-603",2,1024,"OCCUPIED",1350,None,None),
("5-507",2,1024,"OCCUPIED",1350,None,None),("12-1202",1,576,"OCCUPIED",1000,None,None),
("14-1401",1,576,"OCCUPIED",1000,None,None),("5-501",2,1024,"OCCUPIED",1350,None,None),
("5-504",2,1024,"OCCUPIED",1275,None,41),("7-701",2,1024,"OCCUPIED",1350,None,None),
("5-505",2,1024,"OCCUPIED",1350,None,None),("1-104",3,1280,"OCCUPIED",1699,None,78),
("1-102",3,1280,"OCCUPIED",1699,None,26),("4-406",1,512,"OCCUPIED",1000,None,None),
]
# Core comp set: name, dist_mi, units, yr_built, yr_reno, style, avg_sqft, occ_latest, occ_t12chg,
# retention, dom, in1, in2, in3, ask1, ask2, ask3, sample_conf
COMPS = [
("Summer Chase Apartments",2.61,96,1983,2004,"LOW_RISE",909,0.9063,-0.0938,0.8750,20,None,1321.91,None,None,1350.64,1675.00,"ACCEPTABLE"),
("Park Place Apartments",2.86,64,2001,None,"LOW_RISE",1264,0.8594,-0.0625,0.7500,63,None,1070.81,1405.63,None,1350.00,1499.00,"EXCELLENT"),
("The Hub at Mountcastle",3.11,63,2023,None,"MID_RISE",600,0.9206,-0.0317,0.6667,93,1123.90,1382.57,None,1291.00,1474.00,None,"EXCELLENT"),
("Universal at Mockingbird Lane",3.76,262,2021,None,"LOW_RISE",948,0.9847,0.0076,0.6565,17,1095.70,1255.85,1594.31,1304.00,1472.33,1884.00,"EXCELLENT"),
("Blue Ridge Apartments",4.17,80,1999,None,"GARDEN",1034,0.9500,0.0000,0.6875,40,None,1266.67,1447.95,None,1337.50,1518.75,"EXCELLENT"),
("Stone Crest Townhomes",4.53,88,2010,None,"TOWNHOUSE",903,1.0000,0.0114,0.8068,2,917.97,1108.46,1485.25,None,1459.00,None,"EXCELLENT"),
("Miller Crest",4.65,101,1973,2017,"LOW_RISE",898,0.9802,0.0000,0.5743,46,1219.00,1412.48,None,1199.00,1365.00,None,"EXCELLENT"),
("Rosemont",4.68,131,1983,None,"TOWNHOUSE",836,0.9771,-0.0076,0.6870,71,1159.43,1468.60,1692.33,1149.00,1449.00,1649.00,"EXCELLENT"),
("The Reserve at Johnson City",5.01,248,2013,None,"LOW_RISE",1024,0.9435,-0.0202,0.6048,93,1405.07,1555.40,1921.74,1520.67,1698.93,1945.67,"EXCELLENT"),
("Knob Creek Apartments",5.24,285,2008,None,"MID_RISE",998,0.9439,-0.0526,0.4386,57,1408.33,1621.29,1967.22,1608.70,1929.00,1995.00,"EXCELLENT"),
]
SUBJECT = dict(name="Brush Creek Meadows", units=83, yr_built=2008, style="LOW_RISE", avg_sqft=869,
occ_latest=0.9639, occ_30d=0.9729, occ_12mo_ago=1.0000, occ_t12chg=-0.0361,
retention=0.7590, proj_turnovers=20, dom=65, leases_30d=1,
tradeout_amt=-41.67, tradeout_pct=-0.0287, rent_to_income=0.2387,
ip_avg=1241.73, ask_avg=1499.00, ip1=1011.27, ip2=1340.85, ip3=1648.86,
ask1=1199.00, ask2=1599.00, ask3=1699.00, sample_conf="EXCELLENT", sample_cov=0.9759)
SUBMARKET = dict(name="East Tennessee State University (Johnson City)",
occ_latest=0.9289, occ_12mo_ago=0.9719, occ_t12chg=-0.0430,
retention=0.6870, dom=66, tradeout_amt=126.66, tradeout_pct=0.1242,
ip_avg=1247.83, ask_avg=1532.84, ip1=1165.60, ip2=1368.79, ip3=1407.08,
ask1=1525.47, ask2=1618.44, ask3=1562.31,
ip_t12=-0.0120, ask_t12=0.0758, sample_size=3008)
PNL = dict(noi_pct_egi=0.5613, opex_pct_egi=0.4387, sample=15, geo="Washington County, TN")
PY
python3 -c "
from rent_position_data import *
import statistics as st
print('rows', len(RR))
from collections import Counter
print(Counter(r[1] for r in RR))
for b in (1,2,3):
occ=[r[4] for r in RR if r[1]==b and r[4] is not None]
print(b,'n',len(occ),'avg',round(st.mean(occ),2),'min',min(occ),'max',max(occ),'median',st.median(occ))
print('vac', [r[0] for r in RR if r[3]=='VACANT'])
"cd /vercel/sandbox/work && cat > comp_position.py <<'PY'
"""Comp-set market rent benchmarks and Brush Creek Meadows rent-gap position by bedroom."""
import statistics as st
from rent_position_data import RR, COMPS, SUBJECT, SUBMARKET, PNL
def med(vals):
v = sorted(x for x in vals if x is not None)
return round(st.median(v), 2), len(v)
print("=== Core comp set (10), in-place rent by bedroom ===")
for i, lbl in ((11, "1BR"), (12, "2BR"), (13, "3BR")):
m, n = med([c[i] for c in COMPS])
print(f" {lbl} in-place median ${m:,.0f} (n={n} comps)")
print("=== Core comp set, asking rent by bedroom ===")
for i, lbl in ((14, "1BR"), (15, "2BR"), (16, "3BR")):
m, n = med([c[i] for c in COMPS])
print(f" {lbl} asking median ${m:,.0f} (n={n} comps)")
print("\n=== Comp-set operating benchmarks (median of 10) ===")
for i, lbl, f in ((7,"occupancy","{:.1%}"),(9,"retention","{:.1%}"),(10,"days on market","{:.0f}")):
m,n = med([c[i] for c in COMPS])
print(f" {lbl}: "+f.format(m)+f" (n={n})")
print("\n=== Subject unit mix (from unit-level rent roll, 82 of 83 units tracked) ===")
mix = {b: sum(1 for r in RR if r[1]==b) for b in (1,2,3)}
print(" ", mix, " total", sum(mix.values()))
print("\n=== Gap to comp-set in-place median, by bedroom ===")
rows=[]
for b, lbl, ip in ((1,"1BR",SUBJECT['ip1']),(2,"2BR",SUBJECT['ip2']),(3,"3BR",SUBJECT['ip3'])):
cm,_ = med([c[{1:11,2:12,3:13}[b]] for c in COMPS])
gap = cm - ip
units = mix[b]
rows.append((lbl, units, ip, cm, gap, gap*12*units))
print(f" {lbl}: {units}u in-place ${ip:,.2f} comp median ${cm:,.2f} gap ${gap:+,.2f}/mo ({gap/cm:+.1%}) annualized ${gap*12*units:+,.0f}")
tot = sum(r[5] for r in rows)
pos = sum(r[5] for r in rows if r[5]>0)
print(f" NET annualized gap ${tot:+,.0f} POSITIVE-only ${pos:+,.0f}")
print(f" NOI impact at 90% flow-through, positive-only: ${pos*0.90:+,.0f}")
print(f" (county NOI margin {PNL['noi_pct_egi']:.1%} of EGI, n={PNL['sample']})")
print("\n=== Leasing / occupancy vs benchmarks ===")
print(f" occupancy subject {SUBJECT['occ_latest']:.1%} (30d {SUBJECT['occ_30d']:.1%}) | comp median {med([c[7] for c in COMPS])[0]:.1%} | submarket {SUBMARKET['occ_latest']:.1%}")
print(f" retention subject {SUBJECT['retention']:.1%} | comp median {med([c[9] for c in COMPS])[0]:.1%} | submarket {SUBMARKET['retention']:.1%}")
print(f" DOM subject {SUBJECT['dom']}d | comp median {med([c[10] for c in COMPS])[0]:.0f}d | submarket {SUBMARKET['dom']}d")
print(f" tradeout subject {SUBJECT['tradeout_pct']:+.1%} (${SUBJECT['tradeout_amt']:+,.2f}) | submarket {SUBMARKET['tradeout_pct']:+.1%} (${SUBMARKET['tradeout_amt']:+,.2f})")
print(f" tradeout spread subject vs submarket: {(SUBJECT['tradeout_pct']-SUBMARKET['tradeout_pct'])*100:.1f} pts")
print("\n=== Subject DOM detail from rent roll (units with an observed listing episode) ===")
doms = sorted(r[6] for r in RR if r[6] is not None)
print(" n=",len(doms)," median",st.median(doms)," mean",round(st.mean(doms),1)," max",max(doms))
d1=[r[6] for r in RR if r[1]==1 and r[6]]; d2=[r[6] for r in RR if r[1]==2 and r[6]]; d3=[r[6] for r in RR if r[1]==3 and r[6]]
print(" 1BR median",st.median(d1),"n",len(d1),"| 2BR median",st.median(d2),"n",len(d2),"| 3BR median",st.median(d3),"n",len(d3))
print("\n=== 2BR internal price ladder (the softening signal) ===")
from collections import Counter
print(" 2BR in-place distribution:", dict(Counter(r[4] for r in RR if r[1]==2 and r[4])))
print(" units re-leased at $1,275 (down from $1,350 street):", sum(1 for r in RR if r[1]==2 and r[4]==1275))
PY
python3 comp_position.pycd /vercel/sandbox/work && cat > build_workbook.py <<'PY'
import openpyxl
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
from openpyxl.utils import get_column_letter
from rent_position_data import RR, COMPS, SUBJECT, SUBMARKET, PNL
from collections import Counter
BLUE=Font(name="Arial",size=10,color="0000FF"); BLK=Font(name="Arial",size=10,color="000000")
GRN=Font(name="Arial",size=10,color="008000")
HDR=Font(name="Arial",size=10,bold=True,color="FFFFFF"); TTL=Font(name="Arial",size=12,bold=True)
SEC=Font(name="Arial",size=10,bold=True); BOLDBLK=Font(name="Arial",size=10,bold=True,color="000000")
FILLH=PatternFill("solid",fgColor="1F3864"); FILLS=PatternFill("solid",fgColor="D9E2F3")
TOPB=Border(top=Side(style="thin"))
USD='$#,##0;($#,##0);"-"'; USD2='$#,##0.00;($#,##0.00);"-"'; PCT='0.0%;(0.0%);"-"'; NUM='#,##0;(#,##0);"-"'
wb=openpyxl.Workbook()
mix=Counter(r[1] for r in RR)
sqft={1:576,2:1024,3:1280}
# ---------------- Comp Set ----------------
cs=wb.active; cs.title="Comp Set"
cs["A1"]="Brush Creek Meadows - Core Rent Comp Set"; cs["A1"].font=TTL
cs["A2"]="Market-rate, unrestricted multifamily within 5.3 miles of 2900 Watauga Rd; RealAI Rent Index as of 2026-09-12"
cs["A2"].font=Font(name="Arial",size=9,italic=True)
heads=["Property","Dist (mi)","Units","Yr Built","Yr Renov","Building Style","Avg SqFt","Occupancy",
"Occ 12-Mo Chg (pts)","Retention","Days on Market","1BR In-Place","2BR In-Place","3BR In-Place",
"1BR Asking","2BR Asking","3BR Asking","Sample Confidence"]
for j,h in enumerate(heads,1):
c=cs.cell(row=4,column=j,value=h); c.font=HDR; c.fill=FILLH
c.alignment=Alignment(wrap_text=True,vertical="center",horizontal="center")
cs.row_dimensions[4].height=32
r=5
for c_ in COMPS:
vals=[c_[0],c_[1],c_[2],str(c_[3]),(str(c_[4]) if c_[4] else None),c_[5],c_[6],c_[7],c_[8],c_[9],c_[10],
c_[11],c_[12],c_[13],c_[14],c_[15],c_[16],c_[17]]
for j,v in enumerate(vals,1):
cell=cs.cell(row=r,column=j,value=v); cell.font=BLUE
r+=1
LAST=r-1
# subject row
srow=r+1
cs.cell(row=srow,column=1,value="SUBJECT - Brush Creek Meadows").font=BOLDBLK
sv=[0.0,SUBJECT['units'],"2008",None,SUBJECT['style'],SUBJECT['avg_sqft'],SUBJECT['occ_latest'],
SUBJECT['occ_t12chg'],SUBJECT['retention'],SUBJECT['dom'],SUBJECT['ip1'],SUBJECT['ip2'],SUBJECT['ip3'],
SUBJECT['ask1'],SUBJECT['ask2'],SUBJECT['ask3'],SUBJECT['sample_conf']]
for j,v in enumerate(sv,2):
cell=cs.cell(row=srow,column=j,value=v); cell.font=Font(name="Arial",size=10,bold=True,color="0000FF")
# median row
mrow=srow+1
cs.cell(row=mrow,column=1,value="Comp-set median (10 comps)").font=BOLDBLK
cs.cell(row=mrow,column=1).border=TOPB
for j in list(range(2,4))+list(range(7,18)):
L=get_column_letter(j)
cell=cs.cell(row=mrow,column=j,value=f"=MEDIAN({L}5:{L}{LAST})"); cell.font=BOLDBLK; cell.border=TOPB
for j in (1,4,5,6,18):
cs.cell(row=mrow,column=j).border=TOPB
fmts={2:'0.00',3:NUM,7:NUM,8:PCT,9:PCT,10:PCT,11:NUM}
for j,f in fmts.items():
for rr in range(5,mrow+1): cs.cell(row=rr,column=j).number_format=f
for j in range(12,18):
for rr in range(5,mrow+1): cs.cell(row=rr,column=j).number_format=USD
widths=[32,9,8,9,10,16,9,11,12,11,13,12,12,12,12,12,12,12]
for j,w in enumerate(widths,1): cs.column_dimensions[get_column_letter(j)].width=w
cs.freeze_panes="B5"
# ---------------- Inputs ----------------
ip=wb.create_sheet("Inputs")
ip["A1"]="Inputs & Assumptions"; ip["A1"].font=TTL
ip.column_dimensions["A"].width=46; ip.column_dimensions["B"].width=14
ip.column_dimensions["C"].width=64
def sec(row,txt):
c=ip.cell(row=row,column=1,value=txt); c.font=SEC; c.fill=FILLS
ip.cell(row=row,column=2).fill=FILLS; ip.cell(row=row,column=3).fill=FILLS
def line(row,label,value,fmt=None,font=BLUE,note=None,formula=False):
ip.cell(row=row,column=1,value=label).font=BLK
c=ip.cell(row=row,column=2,value=value); c.font=font
if fmt: c.number_format=fmt
if note: ip.cell(row=row,column=3,value=note).font=Font(name="Arial",size=9,italic=True)
ip["C3"]="Source / basis"; ip["C3"].font=SEC
sec(3,"Subject property - physical & unit mix")
line(4,"Total units (property record)",SUBJECT['units'],NUM,note="RealAI property attributes")
line(5,"Units tracked in unit-level rent roll",82,NUM,note="82 of 83 units carry a unit-level record (97.6% sample coverage)")
line(6,"1BR units",mix[1],NUM,note="Unit-level rent roll, Aug-2026 snapshot")
line(7,"2BR units",mix[2],NUM,note="Unit-level rent roll, Aug-2026 snapshot")
line(8,"3BR units",mix[3],NUM,note="Unit-level rent roll, Aug-2026 snapshot")
ip["A9"]="Units in rent-roll mix (check)"; ip["A9"].font=BLK
ip["B9"]="=SUM(B6:B8)"; ip["B9"].font=BLK; ip["B9"].number_format=NUM
line(10,"Year built",2008,None,note="RealAI property attributes")
line(11,"Average unit size (sq ft)",SUBJECT['avg_sqft'],NUM,note="RealAI property attributes")
line(12,"1BR floorplan size (sq ft)",sqft[1],NUM,note="Modal size from unit-level rent roll (512-576 sf)")
line(13,"2BR floorplan size (sq ft)",sqft[2],NUM,note="Unit-level rent roll")
line(14,"3BR floorplan size (sq ft)",sqft[3],NUM,note="Unit-level rent roll")
sec(16,"Subject - in-place & asking rent by bedroom")
line(17,"1BR in-place rent (avg)",SUBJECT['ip1'],USD2,note="RealAI Rent Index, 30 leased 1BR units")
line(18,"2BR in-place rent (avg)",SUBJECT['ip2'],USD2,note="RealAI Rent Index, 41 leased 2BR units")
line(19,"3BR in-place rent (avg)",SUBJECT['ip3'],USD2,note="RealAI Rent Index, 7 leased 3BR units")
line(20,"1BR asking rent",SUBJECT['ask1'],USD,note="Current published ask")
line(21,"2BR asking rent",SUBJECT['ask2'],USD,note="Current published ask")
line(22,"3BR asking rent",SUBJECT['ask3'],USD,note="Current published ask")
sec(24,"Subject - leasing & occupancy")
line(25,"Physical occupancy (latest)",SUBJECT['occ_latest'],PCT,note="RealAI Rent Index as of 2026-09-12")
line(26,"Physical occupancy (30-day avg)",SUBJECT['occ_30d'],PCT)
line(27,"Physical occupancy (12 months ago)",SUBJECT['occ_12mo_ago'],PCT)
line(28,"Trailing-12-mo retention rate",SUBJECT['retention'],PCT,note="Share of tenants renewing, TTM 2025-09 to 2026-09")
line(29,"Projected annual unit turnovers",SUBJECT['proj_turnovers'],NUM,note="Observed turnovers, trailing 12 months")
line(30,"Median days on market (leases signed, TTM)",SUBJECT['dom'],NUM)
line(31,"New-lease tradeout ($/mo)",SUBJECT['tradeout_amt'],USD2,note="New lease rent less prior tenant rent on same unit")
line(32,"New-lease tradeout (%)",SUBJECT['tradeout_pct'],PCT)
line(33,"Rent-to-income ratio",SUBJECT['rent_to_income'],PCT,note="Below 30% = cost-affordable; headroom for increases")
sec(35,"Comp-set benchmarks (median of 10 core comps)")
for i,(lbl,col) in enumerate([("1BR in-place rent","L"),("2BR in-place rent","M"),("3BR in-place rent","N"),
("1BR asking rent","O"),("2BR asking rent","P"),("3BR asking rent","Q")]):
rw=36+i
ip.cell(row=rw,column=1,value=lbl).font=BLK
c=ip.cell(row=rw,column=2,value=f"='Comp Set'!{col}{mrow}"); c.font=GRN; c.number_format=USD2
ip.cell(row=rw,column=3,value="Median across core comp set - see Comp Set tab").font=Font(name="Arial",size=9,italic=True)
for i,(lbl,col,fmt) in enumerate([("Occupancy","H",PCT),("Retention","J",PCT),("Days on market","K",NUM)]):
rw=42+i
ip.cell(row=rw,column=1,value=lbl).font=BLK
c=ip.cell(row=rw,column=2,value=f"='Comp Set'!{col}{mrow}"); c.font=GRN; c.number_format=fmt
ip.cell(row=rw,column=3,value="Median across core comp set").font=Font(name="Arial",size=9,italic=True)
sec(46,"Submarket benchmarks - ETSU / Johnson City")
line(47,"1BR in-place rent (avg)",SUBMARKET['ip1'],USD2,note="Submarket aggregate; includes by-the-bed student product")
line(48,"2BR in-place rent (avg)",SUBMARKET['ip2'],USD2)
line(49,"3BR in-place rent (avg)",SUBMARKET['ip3'],USD2,note="Depressed by by-the-bed student 3BR pricing")
line(50,"Physical occupancy (latest)",SUBMARKET['occ_latest'],PCT,note="3,008-unit in-place sample")
line(51,"Physical occupancy (12 months ago)",SUBMARKET['occ_12mo_ago'],PCT)
line(52,"Retention rate",SUBMARKET['retention'],PCT)
line(53,"Median days on market",SUBMARKET['dom'],NUM)
line(54,"New-lease tradeout ($/mo)",SUBMARKET['tradeout_amt'],USD2)
line(55,"New-lease tradeout (%)",SUBMARKET['tradeout_pct'],PCT)
line(56,"Asking rent, 12-month change",SUBMARKET['ask_t12'],PCT)
line(57,"In-place rent, 12-month change",SUBMARKET['ip_t12'],PCT)
sec(59,"Assumptions - JUDGMENT INPUTS (change these)")
line(60,"Achievable-rent adjustment vs comp median",0.0,PCT,note="JUDGMENT: haircut/premium applied to comp median to set achievable rent. 0% = comp median is achievable as-is.")
line(61,"Share of gap captured in next 12 months",0.30,PCT,note="JUDGMENT: 20 projected turnovers on 83 units = 24% natural roll; 30% assumes turnover capture plus modest renewal increases.")
line(62,"Incremental NOI flow-through on rent gains",0.90,PCT,note="JUDGMENT: only management fee (4.3% of EGI) and marginal variable costs scale with rent; cross-check vs county 43.9% total OpEx ratio below.")
line(63,"County total OpEx (% of EGI) - reference only",PNL['opex_pct_egi'],PCT,note=f"RealAI Ops Benchmarks, {PNL['geo']}, n={PNL['sample']}. Reference for full-P&L margin, not for incremental rent.")
line(64,"County NOI margin (% of EGI) - reference only",PNL['noi_pct_egi'],PCT,note=f"RealAI Ops Benchmarks, {PNL['geo']}, n={PNL['sample']}")
for rw in (60,61,62):
ip.cell(row=rw,column=2).fill=PatternFill("solid",fgColor="FFFF00")
# ---------------- Position vs Comps ----------------
pv=wb.create_sheet("Position vs Comps")
pv["A1"]="Rent Position by Bedroom - Subject vs Core Comps vs Submarket"; pv["A1"].font=TTL
h=["Unit Type","Units","Floorplan SqFt","Subject In-Place Rent","Subject Asking Rent",
"Comp-Set In-Place Median","Comp-Set Asking Median","Submarket In-Place Avg",
"Gap to Comp Median ($/mo)","Gap to Comp Median (%)","Annualized Gap ($)","Subject In-Place $/SqFt","Comp Median $/SqFt"]
for j,t in enumerate(h,1):
c=pv.cell(row=3,column=j,value=t); c.font=HDR; c.fill=FILLH
c.alignment=Alignment(wrap_text=True,vertical="center",horizontal="center")
pv.row_dimensions[3].height=44
lbls=["1BR","2BR","3BR"]
for i,lbl in enumerate(lbls):
rw=4+i
pv.cell(row=rw,column=1,value=lbl).font=BLK
pv.cell(row=rw,column=2,value=f"=Inputs!$B${6+i}").font=GRN
pv.cell(row=rw,column=3,value=f"=Inputs!$B${12+i}").font=GRN
pv.cell(row=rw,column=4,value=f"=Inputs!$B${17+i}").font=GRN
pv.cell(row=rw,column=5,value=f"=Inputs!$B${20+i}").font=GRN
pv.cell(row=rw,column=6,value=f"=Inputs!$B${36+i}").font=GRN
pv.cell(row=rw,column=7,value=f"=Inputs!$B${39+i}").font=GRN
pv.cell(row=rw,column=8,value=f"=Inputs!$B${47+i}").font=GRN
pv.cell(row=rw,column=9,value=f"=F{rw}-D{rw}").font=BLK
pv.cell(row=rw,column=10,value=f"=I{rw}/F{rw}").font=BLK
pv.cell(row=rw,column=11,value=f"=I{rw}*12*B{rw}").font=BLK
pv.cell(row=rw,column=12,value=f"=D{rw}/C{rw}").font=BLK
pv.cell(row=rw,column=13,value=f"=F{rw}/C{rw}").font=BLK
tr=7
pv.cell(row=tr,column=1,value="Total / weighted").font=BOLDBLK
pv.cell(row=tr,column=2,value="=SUM(B4:B6)").font=BOLDBLK
pv.cell(row=tr,column=4,value="=SUMPRODUCT($B$4:$B$6,D4:D6)/$B$7").font=BOLDBLK
pv.cell(row=tr,column=6,value="=SUMPRODUCT($B$4:$B$6,F4:F6)/$B$7").font=BOLDBLK
pv.cell(row=tr,column=8,value="=SUMPRODUCT($B$4:$B$6,H4:H6)/$B$7").font=BOLDBLK
pv.cell(row=tr,column=9,value="=F7-D7").font=BOLDBLK
pv.cell(row=tr,column=10,value="=I7/F7").font=BOLDBLK
pv.cell(row=tr,column=11,value="=SUM(K4:K6)").font=BOLDBLK
for j in range(1,14): pv.cell(row=tr,column=j).border=TOPB
for rw in range(4,8):
for j in (2,3): pv.cell(row=rw,column=j).number_format=NUM
for j in (4,5,6,7,8,9): pv.cell(row=rw,column=j).number_format=USD2
pv.cell(row=rw,column=10).number_format=PCT
pv.cell(row=rw,column=11).number_format=USD
for j in (12,13): pv.cell(row=rw,column=j).number_format='$0.00'
pv["A10"]="Leasing & Occupancy Position"; pv["A10"].font=TTL
for j,t in enumerate(["Metric","Subject","Comp-Set Median","Submarket","Subject vs Comp Set","Read"],1):
c=pv.cell(row=11,column=j,value=t); c.font=HDR; c.fill=FILLH
c.alignment=Alignment(wrap_text=True,vertical="center",horizontal="center")
ops=[("Physical occupancy (latest)","Inputs!$B$25","Inputs!$B$42","Inputs!$B$50",PCT,"pts","Outperforming - subject is the tighter asset"),
("Physical occupancy (12 months ago)","Inputs!$B$27",None,"Inputs!$B$51",PCT,"pts","Subject gave back ground from full occupancy"),
("Trailing-12-mo retention","Inputs!$B$28","Inputs!$B$43","Inputs!$B$52",PCT,"pts","Outperforming - residents are staying"),
("Median days on market","Inputs!$B$30","Inputs!$B$44","Inputs!$B$53",NUM,"days","Lagging the comp set - units sit longer"),
("New-lease tradeout (%)","Inputs!$B$32",None,"Inputs!$B$55",PCT,"pts","Core problem - new leases signing BELOW prior rent")]
for i,(lbl,s,c_,m,fmt,unit,read) in enumerate(ops):
rw=12+i
pv.cell(row=rw,column=1,value=lbl).font=BLK
pv.cell(row=rw,column=2,value=f"={s}").font=GRN; pv.cell(row=rw,column=2).number_format=fmt
if c_:
pv.cell(row=rw,column=3,value=f"={c_}").font=GRN; pv.cell(row=rw,column=3).number_format=fmt
pv.cell(row=rw,column=4,value=f"={m}").font=GRN; pv.cell(row=rw,column=4).number_format=fmt
if c_:
pv.cell(row=rw,column=5,value=f"=B{rw}-C{rw}").font=BLK
pv.cell(row=rw,column=5).number_format=('0.0"pts";(0.0"pts")' if unit=="pts" else '0" days";(0" days")')
else:
pv.cell(row=rw,column=5,value=f"=B{rw}-D{rw}").font=BLK
pv.cell(row=rw,column=5).number_format='0.0"pts";(0.0"pts")'
pv.cell(row=rw,column=6,value=read).font=BLK
pv["E11"].alignment=Alignment(wrap_text=True,vertical="center",horizontal="center")
pv["A18"]="Rows 13 and 17 compare against the submarket (no comp-set median is published for prior-year occupancy or tradeout)."
pv["A18"].font=Font(name="Arial",size=9,italic=True)
for col,w in zip("ABCDEFGHIJKLM",[34,10,13,14,14,16,16,15,15,13,15,13,13]): pv.column_dimensions[col].width=w
pv.column_dimensions["F"].width=48
pv.freeze_panes="B4"
# ---------------- Upside & Sensitivity ----------------
up=wb.create_sheet("Upside & Sensitivity")
up["A1"]="Mark-to-Market Upside and Sensitivity"; up["A1"].font=TTL
up["A3"]="Achievable rent build"; up["A3"].font=SEC; up["A3"].fill=FILLS
for j in range(2,8): up.cell(row=3,column=j).fill=FILLS
for j,t in enumerate(["Unit Type","Units","In-Place Rent","Comp-Set Median","Achievable Rent (adj.)",
"Monthly Gap per Unit","Annualized Gap ($)"],1):
c=up.cell(row=4,column=j,value=t); c.font=HDR; c.fill=FILLH
c.alignment=Alignment(wrap_text=True,vertical="center",horizontal="center")
up.row_dimensions[4].height=32
for i,lbl in enumerate(lbls):
rw=5+i
up.cell(row=rw,column=1,value=lbl).font=BLK
up.cell(row=rw,column=2,value=f"='Position vs Comps'!B{4+i}").font=GRN
up.cell(row=rw,column=3,value=f"='Position vs Comps'!D{4+i}").font=GRN
up.cell(row=rw,column=4,value=f"='Position vs Comps'!F{4+i}").font=GRN
up.cell(row=rw,column=5,value=f"=D{rw}*(1+Inputs!$B$60)").font=BLK
up.cell(row=rw,column=6,value=f"=MAX(0,E{rw}-C{rw})").font=BLK
up.cell(row=rw,column=7,value=f"=F{rw}*12*B{rw}").font=BLK
up.cell(row=rw,column=2).number_format=NUM
for j in (3,4,5,6): up.cell(row=rw,column=j).number_format=USD2
up.cell(row=rw,column=7).number_format=USD
up.cell(row=8,column=1,value="Total gross upside at full capture").font=BOLDBLK
up.cell(row=8,column=2,value="=SUM(B5:B7)").font=BOLDBLK; up.cell(row=8,column=2).number_format=NUM
up.cell(row=8,column=7,value="=SUM(G5:G7)").font=BOLDBLK; up.cell(row=8,column=7).number_format=USD
for j in range(1,8): up.cell(row=8,column=j).border=TOPB
up.cell(row=9,column=1,value="Note: gap floored at zero per unit type - 3BR in-place sits above the comp median and is a retention risk, not upside.").font=Font(name="Arial",size=9,italic=True)
up["A11"]="Capture and NOI impact"; up["A11"].font=SEC; up["A11"].fill=FILLS
for j in range(2,8): up.cell(row=11,column=j).fill=FILLS
build=[("Gross upside at full capture (annual)","=G8",USD),
("Share of gap captured in next 12 months","=Inputs!$B$61",PCT),
("Year-1 revenue captured","=B12*B13",USD),
("Incremental NOI flow-through","=Inputs!$B$62",PCT),
("Year-1 NOI impact","=B14*B15",USD),
("Year-1 NOI impact per unit","=B16/Inputs!$B$4",USD),
("Projected annual unit turnovers","=Inputs!$B$29",NUM),
("Natural annual roll (% of units)","=B18/Inputs!$B$4",PCT)]
for i,(lbl,f,fmt) in enumerate(build):
rw=12+i
up.cell(row=rw,column=1,value=lbl).font=BLK
c=up.cell(row=rw,column=2,value=f); c.font=(GRN if "Inputs!" in f and "*" not in f else BLK); c.number_format=fmt
for rw in (16,):
up.cell(row=rw,column=1).font=BOLDBLK; up.cell(row=rw,column=2).font=BOLDBLK
up.cell(row=rw,column=1).border=TOPB; up.cell(row=rw,column=2).border=TOPB
up["A22"]="Sensitivity: Year-1 NOI impact ($) - achievable rent vs comp median (rows) x share of gap captured in 12 months (columns)"
up["A22"].font=SEC
up["A23"]="Achievable vs comp median"; up["A23"].font=SEC
up["A23"].alignment=Alignment(wrap_text=True,vertical="center")
caps=[0.15,0.25,0.30,0.40,0.50]
for j,cp in enumerate(caps,2):
c=up.cell(row=23,column=j,value=cp); c.font=BLUE; c.number_format=PCT
c.alignment=Alignment(horizontal="center")
adjs=[-0.08,-0.04,0.0,0.04,0.08]
for i,a in enumerate(adjs):
rw=24+i
c=up.cell(row=rw,column=1,value=a); c.font=BLUE; c.number_format='+0.0%;-0.0%;"0.0%"'
for j,cp in enumerate(caps,2):
L=get_column_letter(j)
f=(f"=SUMPRODUCT($B$5:$B$7,(($D$5:$D$7*(1+$A{rw})-$C$5:$C$7)>0)*"
f"($D$5:$D$7*(1+$A{rw})-$C$5:$C$7))*12*{L}$23*Inputs!$B$62")
cell=up.cell(row=rw,column=j,value=f); cell.font=BLK; cell.number_format=USD
up.cell(row=30,column=1,value="Base case is the 0.0% row at the 30% column. Rows hold the 3BR gap floored at zero, so a negative-adjustment row reduces 1BR upside rather than crediting the over-market 3BR.").font=Font(name="Arial",size=9,italic=True)
up.column_dimensions["A"].width=42
for col in "BCDEFG": up.column_dimensions[col].width=17
wb.save("Brush_Creek_Meadows_Rent_Position.xlsx")
print("saved")
PY
python3 build_workbook.py && python3 skills/xlsx/scripts/workbook_integrity_scan.py Brush_Creek_Meadows_Rent_Position.xlsx 2>&1 | tail -60cd /vercel/sandbox && python3 skills/xlsx/scripts/workbook_integrity_scan.py work/Brush_Creek_Meadows_Rent_Position.xlsx 2>&1 | tail -80
cd /vercel/sandbox/work && python3 - <<'PY'
import re
s=open("build_workbook.py").read()
s=s.replace('''for i,(lbl,col) in enumerate([("1BR in-place rent","L"),("2BR in-place rent","M"),("3BR in-place rent","N"),
("1BR asking rent","O"),("2BR asking rent","P"),("3BR asking rent","Q")]):''',
'''for i,(lbl,col) in enumerate([("1BR in-place rent - comp-set median (10 comps)","L"),("2BR in-place rent - comp-set median (10 comps)","M"),("3BR in-place rent - comp-set median (10 comps)","N"),
("1BR asking rent - comp-set median (10 comps)","O"),("2BR asking rent - comp-set median (10 comps)","P"),("3BR asking rent - comp-set median (10 comps)","Q")]):''')
s=s.replace('''for i,(lbl,col,fmt) in enumerate([("Occupancy","H",PCT),("Retention","J",PCT),("Days on market","K",NUM)]):''',
'''for i,(lbl,col,fmt) in enumerate([("Occupancy - comp-set median (10 comps)","H",PCT),("Retention - comp-set median (10 comps)","J",PCT),("Days on market - comp-set median (10 comps)","K",NUM)]):''')
s=s.replace('line(10,"Year built",2008,None,','line(10,"Year built","2008",None,')
s=s.replace('Font(name="Arial",size=9,italic=True)','Font(name="Arial",size=10,italic=True)')
s=s.replace('c.alignment=Alignment(horizontal="center")\nadjs','c.alignment=Alignment(horizontal="right")\nadjs')
s=s.replace('pv.freeze_panes="B4"\n','')
s=s.replace('pv.cell(row=tr,column=2,value="=SUM(B4:B6)")','pv.cell(row=tr,column=2,value="=SUM($B$4:$B$6)")')
s=s.replace('pv.cell(row=tr,column=11,value="=SUM(K4:K6)")','pv.cell(row=tr,column=11,value="=SUM($K$4:$K$6)")')
s=s.replace('up.cell(row=8,column=2,value="=SUM(B5:B7)")','up.cell(row=8,column=2,value="=SUM($B$5:$B$7)")')
s=s.replace('up.cell(row=8,column=7,value="=SUM(G5:G7)")','up.cell(row=8,column=7,value="=SUM($G$5:$G$7)")')
open("build_workbook.py","w").write(s)
PY
python3 build_workbook.py && cd /vercel/sandbox && python3 skills/xlsx/scripts/workbook_integrity_scan.py work/Brush_Creek_Meadows_Rent_Position.xlsx 2>&1 | tail -40cd /vercel/sandbox && python3 skills/xlsx/scripts/recalc.py work/Brush_Creek_Meadows_Rent_Position.xlsx 2>&1 | tail -20
cd /vercel/sandbox/work && python3 - <<'PY'
import openpyxl
wb=openpyxl.load_workbook("Brush_Creek_Meadows_Rent_Position.xlsx",data_only=True)
pv=wb["Position vs Comps"]; up=wb["Upside & Sensitivity"]; ip=wb["Inputs"]
print("--- Position vs Comps rows 3-7 ---")
for r in range(3,8):
print([pv.cell(row=r,column=c).value for c in range(1,14)])
print("--- Leasing 11-17 ---")
for r in range(11,18):
print([pv.cell(row=r,column=c).value for c in range(1,6)])
print("--- Upside 4-20 ---")
for r in range(4,21):
print([up.cell(row=r,column=c).value for c in range(1,8)])
print("--- Sensitivity 23-29 ---")
for r in range(23,30):
print([up.cell(row=r,column=c).value for c in range(1,7)])
PYThe room to grow is all in the one-bedrooms — and you're funding it by cutting two-bedroom rents in a market that's raising them.
Brush Creek Meadows is the healthier asset on every occupancy and retention measure: 96.4% occupied against a 94.7% comp-set median and 92.9% for the ETSU/Johnson City submarket , with 75.9% trailing-12-month retention versus 67.7% across the comps . That is not a property with a demand problem. It's a property with a pricing problem, and the two have been confused.
The tell is tradeout. New leases at Brush Creek are signing 2.9% below the prior tenant's rent while the submarket is trading new leases up 12.4% — a 15.3-point spread . Unit-level, that's five 2BR units re-leased at $1,275 against the $1,350 street rate the other 36 occupied 2BRs pay . You are discounting to fill units in a market where competitors are pushing.
| Unit Type | Units | Subject In-Place | Comp-Set In-Place Median | Gap $/mo | Gap % | Annualized Gap |
|---|---|---|---|---|---|---|
| 1BR | 32 | $1,011 | $1,1591 | +$148 | +12.8% | +$56,893 |
| 2BR | 42 | $1,341 | $1,352 | +$11 | +0.8% | +$5,741 |
| 3BR | 8 | $1,649 | $1,594 | -$552 | -3.4% | -$5,237 |
| Total/Weighted | 82 | $1,2423 | $1,301 | +$58 | +4.5% | +$57,397 |
Where the growth is: the 1BRs, and almost nowhere else. Thirty-two one-bedrooms sit at $1,011 against a $1,159 comp median — a $148/month, 12.8% gap worth $56,893 annualized . That's 91% of the property's entire mark-to-market opportunity. And these units are the easiest sell you have: 1BR median days-on-market at the property runs 26 days versus 46 for the 2BRs , and the one 1BR that was actually pushed re-leased at +10.7% in four days. Tenant capacity supports it — rent-to-income is 23.9% , well inside affordable, so a $100–150 increase doesn't break the resident base. Gross upside at full capture is $62,634; at 30% capture in year one (against a 24% natural roll) that's roughly $16,900 of NOI .
Where to focus improvements: the 2BRs, and it's a velocity problem, not a rent problem. Forty-two units — half the property — are already at market ($11/month gap) and sitting nearly twice as long on market as the 1BRs. There is no rent upside here; the upside is not giving away $75/month to lease them. Two-bedroom asking rent of $1,599 is 10% above the $1,454 comp median , which is why they sit, and why they then clear at a discount. Reset the 2BR ask to roughly $1,450–1,475 and hold the floor — a credible ask that leases in three weeks beats an aspirational ask that leases in seven at $1,275.
Leave the 3BRs alone. Eight units at $1,649 are 3.4% above the comp median ; one is already vacant at a $1,699 ask. Hold rent flat at renewal here. Any push is a move-out you can't afford at 1,280 sq ft.
What would change this read: the downside case is that the comp median isn't achievable at Brush Creek's east-side location — every core comp sits 2.6–5.3 miles west, closer to ETSU and the Med-Tech corridor. If achievable rent is 8% below the comp median, year-one NOI lift falls to about $5,700; if the 1BR gap is real and capture reaches 50%, it's roughly $28,200 . Watch the next three 1BR turns: if they clear at $1,125–1,150 inside 30 days, the gap is real and you should be pushing renewals too. Also watch supply — Vintage Johnson City (288 units, 2026) and Universal at Mountainview (324 units, 2025) are both in lease-up in the submarket, which is what's holding submarket occupancy at 92.9% and will cap the 2BR ask.
The base case to underwrite is about $17,000 of year-one NOI, almost entirely from repricing 32 one-bedrooms at turn — not from the 2BRs, and not from a broad rent push. The workbook lets you move the achievable-rent and capture assumptions directly.
This is a read-only snapshot of a RealAI analysis.