#!/usr/bin/env python3 """Build Excel spreadsheet from ARI's real estate cost seg model""" import sys sys.path.insert(0, '/home/wdjones/.openclaw/workspace-ari/data/real-estate-model') from detailed_calculations import * import openpyxl from openpyxl.styles import Font, PatternFill, Alignment, Border, Side, numbers from openpyxl.utils import get_column_letter # Check if openpyxl available try: import openpyxl except ImportError: import subprocess subprocess.run([sys.executable, '-m', 'pip', 'install', 'openpyxl'], check=True) import openpyxl results = run_portfolio_model() wb = openpyxl.Workbook() # Styles header_font = Font(bold=True, color="FFFFFF", size=11) header_fill = PatternFill(start_color="1a1a2e", end_color="1a1a2e", fill_type="solid") red_font = Font(color="FF4444", bold=True) green_font = Font(color="44FF44", bold=True) money_fmt = '#,##0' money_neg_fmt = '#,##0;[Red]-#,##0' pct_fmt = '0.0%' thin_border = Border( left=Side(style='thin'), right=Side(style='thin'), top=Side(style='thin'), bottom=Side(style='thin') ) section_fill = PatternFill(start_color="2d2d44", end_color="2d2d44", fill_type="solid") section_font = Font(bold=True, color="00BFFF", size=11) warn_fill = PatternFill(start_color="4a1a1a", end_color="4a1a1a", fill_type="solid") warn_font = Font(bold=True, color="FF6666", size=12) def style_header(ws, row, max_col): for col in range(1, max_col + 1): cell = ws.cell(row=row, column=col) cell.font = header_font cell.fill = header_fill cell.alignment = Alignment(horizontal='center', wrap_text=True) cell.border = thin_border def style_row(ws, row, max_col): for col in range(1, max_col + 1): cell = ws.cell(row=row, column=col) cell.border = thin_border cell.alignment = Alignment(horizontal='right') if col == 1: cell.alignment = Alignment(horizontal='left') def auto_width(ws): for col in ws.columns: max_len = 0 col_letter = get_column_letter(col[0].column) for cell in col: if cell.value: max_len = max(max_len, len(str(cell.value))) ws.column_dimensions[col_letter].width = min(max_len + 4, 22) # ============ SHEET 1: Portfolio Overview ============ ws1 = wb.active ws1.title = "Portfolio Overview" ws1.sheet_properties.tabColor = "1a1a2e" # Title ws1.merge_cells('A1:K1') ws1['A1'] = "REAL ESTATE COST SEGREGATION MODEL — 5-YEAR PORTFOLIO" ws1['A1'].font = Font(bold=True, color="00BFFF", size=14) # Warning banner ws1.merge_cells('A3:K3') ws1['A3'] = "⚠️ VERDICT: NO-GO — Negative cash flow all 5 years. $0 tax benefit at >$150K AGI. $337K passive losses stranded." ws1['A3'].font = warn_font ws1['A3'].fill = warn_fill # Headers row = 5 headers = ['Year', 'Properties', 'Gross Rent', 'Effective Rent', 'NOI', 'Cash Flow', 'Depreciation', 'Taxable Income', 'Cash Invested', 'Cumulative Invested', 'Cumulative Cash Flow'] for col, h in enumerate(headers, 1): ws1.cell(row=row, column=col, value=h) style_header(ws1, row, len(headers)) cumulative_invested = 0 cumulative_cf = 0 for i, r in enumerate(results): row = 6 + i cumulative_invested += r['total_cash_invested'] cumulative_cf += r['total_cash_flow'] values = [ f"Year {r['year']}", r['properties_owned'], r['total_gross_rent'], r['total_effective_rent'], r['total_noi'], r['total_cash_flow'], r['total_depreciation'], r['total_taxable_income'], r['total_cash_invested'], cumulative_invested, cumulative_cf ] for col, v in enumerate(values, 1): cell = ws1.cell(row=row, column=col, value=v) if col >= 3: cell.number_format = money_neg_fmt if col == 6 and isinstance(v, (int, float)) and v < 0: cell.font = red_font if col == 8 and isinstance(v, (int, float)) and v < 0: cell.font = red_font style_row(ws1, row, len(headers)) # Totals row row = 11 ws1.cell(row=row, column=1, value="5-YEAR TOTALS").font = Font(bold=True) ws1.cell(row=row, column=3, value=sum(r['total_gross_rent'] for r in results)).number_format = money_fmt ws1.cell(row=row, column=5, value=sum(r['total_noi'] for r in results)).number_format = money_fmt ws1.cell(row=row, column=6, value=sum(r['total_cash_flow'] for r in results)) ws1.cell(row=row, column=6).number_format = money_neg_fmt ws1.cell(row=row, column=6).font = red_font ws1.cell(row=row, column=7, value=sum(r['total_depreciation'] for r in results)).number_format = money_fmt ws1.cell(row=row, column=9, value=395000).number_format = money_fmt style_row(ws1, row, len(headers)) auto_width(ws1) # ============ SHEET 2: Tax Impact by AGI ============ ws2 = wb.create_sheet("Tax Impact by AGI") ws2.sheet_properties.tabColor = "4a1a1a" ws2.merge_cells('A1:H1') ws2['A1'] = "PASSIVE LOSS & TAX SAVINGS BY AGI BRACKET" ws2['A1'].font = Font(bold=True, color="00BFFF", size=14) # Critical note ws2.merge_cells('A3:H4') ws2['A3'] = ("CRITICAL: At AGI >$150K (D J + partners likely scenario), passive losses CANNOT offset W-2 income. " "Losses carry forward indefinitely but only offset future passive rental income or are released upon sale. " "This means $0 annual tax benefit from depreciation — the entire cost seg strategy provides NO cash flow benefit.") ws2['A3'].font = Font(color="FF6666", size=10, italic=True) ws2['A3'].alignment = Alignment(wrap_text=True) row = 6 headers = ['Year', 'Passive Loss Generated', 'Tax Savings\n(AGI <$100K)', 'Losses Carried\n(AGI <$100K)', 'Tax Savings\n(AGI $100-150K)', 'Losses Carried\n(AGI $100-150K)', 'Tax Savings\n(AGI >$150K)', 'Losses Carried\n(AGI >$150K)'] for col, h in enumerate(headers, 1): ws2.cell(row=row, column=col, value=h) style_header(ws2, row, len(headers)) for i, r in enumerate(results): row = 7 + i values = [ f"Year {r['year']}", abs(r['total_taxable_income']), r['tax_savings_scenario_a'], r['passive_losses_scenario_a'], r['tax_savings_scenario_b'], r['passive_losses_scenario_b'], r['tax_savings_scenario_c'], r['passive_losses_scenario_c'] ] for col, v in enumerate(values, 1): cell = ws2.cell(row=row, column=col, value=v) if col >= 2: cell.number_format = money_fmt # Highlight the >$150K columns if col in (7, 8) and isinstance(v, (int, float)): cell.font = red_font style_row(ws2, row, len(headers)) # Summary row = 13 ws2.cell(row=row, column=1, value="5-YEAR TOTALS").font = Font(bold=True) ws2.cell(row=row, column=3, value=30000).number_format = money_fmt ws2.cell(row=row, column=5, value=15000).number_format = money_fmt ws2.cell(row=row, column=7, value=0).font = red_font ws2.cell(row=row, column=7).number_format = money_fmt ws2.cell(row=row, column=8, value=337877).font = red_font ws2.cell(row=row, column=8).number_format = money_fmt auto_width(ws2) # ============ SHEET 3: S&P 500 Comparison ============ ws3 = wb.create_sheet("vs S&P 500") ws3.sheet_properties.tabColor = "1a4a1a" ws3.merge_cells('A1:F1') ws3['A1'] = "SAME CAPITAL IN S&P 500 vs REAL ESTATE PORTFOLIO" ws3['A1'].font = Font(bold=True, color="00BFFF", size=14) row = 3 headers = ['Year', 'Capital Deployed', 'S&P 500 Value\n(9% avg)', 'RE Portfolio Equity', 'RE Cash Flow\n(Cumulative)', 'S&P 500 Advantage'] for col, h in enumerate(headers, 1): ws3.cell(row=row, column=col, value=h) style_header(ws3, row, len(headers)) sp500_values = [] sp500_total = 0 cum_cf = 0 for i, r in enumerate(results): row = 4 + i year = r['year'] # Each year invest $79K, compounds at 9% sp500_total = (sp500_total * 1.09) + r['total_cash_invested'] sp500_values.append(sp500_total) # RE equity = appreciation + principal paydown (rough) props = r['properties_owned'] re_equity = props * (DOWN_PAYMENT + (PURCHASE_PRICE * ((1.03 ** year) - 1))) # appreciation gain # Add principal paydown estimate (~$3K/yr per property in early years) re_equity += props * 3000 * ((year + 1) / 2) cum_cf += r['total_cash_flow'] advantage = sp500_total - (re_equity + cum_cf) values = [f"Year {year}", r['total_cash_invested'], sp500_total, re_equity, cum_cf, advantage] for col, v in enumerate(values, 1): cell = ws3.cell(row=row, column=col, value=v) if col >= 2: cell.number_format = money_neg_fmt if col == 6 and isinstance(v, (int, float)) and v > 0: cell.font = green_font style_row(ws3, row, len(headers)) auto_width(ws3) # ============ SHEET 4: Property Assumptions ============ ws4 = wb.create_sheet("Assumptions") ws4.sheet_properties.tabColor = "444444" ws4.merge_cells('A1:C1') ws4['A1'] = "MODEL ASSUMPTIONS" ws4['A1'].font = Font(bold=True, color="00BFFF", size=14) assumptions = [ ("Purchase", "", ""), ("Purchase Price", "$345,000", ""), ("Down Payment", "20%", "$69,000"), ("Closing Costs", "$6,000", ""), ("Cost Seg Study", "$4,000", "per property"), ("Total Cash Per Property", "$79,000", ""), ("", "", ""), ("Financing", "", ""), ("Loan Amount", "$276,000", ""), ("Interest Rate", "6.25%", "30-year fixed"), ("Monthly P&I", "$1,699.38", ""), ("Monthly PITI", "$2,016.05", ""), ("", "", ""), ("Income", "", ""), ("Monthly Rent", "$2,000", "0.58% rent-to-price ratio"), ("Annual Increase", "4%", "Aggressive — 2-3% more realistic"), ("Vacancy", "5%", ""), ("Management", "0%", "Self-managed"), ("", "", ""), ("Expenses", "", ""), ("Property Tax", "$2,400/yr", ""), ("Insurance", "$1,400/yr", ""), ("Maintenance", "$2,000/yr", "LOW — should be 1% of value = $3,450"), ("Appreciation", "3%/yr", ""), ("", "", ""), ("Cost Segregation", "", ""), ("5-Year Property", "15%", "$41,250 of $275K basis"), ("15-Year Property", "12%", "$33,000 of $275K basis"), ("27.5-Year Property", "73%", "$200,750 of $275K basis"), ("Bonus Depreciation (2026)", "40%", "Declining 20%/yr from 100% in 2022"), ("", "", ""), ("Tax Rules", "", ""), ("AGI >$150K", "$0 passive deduction", "Losses carry forward ONLY"), ("AGI $100-150K", "Partial $25K deduction", "Phased out"), ("AGI <$100K", "Full $25K deduction", "Against W-2 income"), ] for i, (label, val, note) in enumerate(assumptions): row = 3 + i ws4.cell(row=row, column=1, value=label) ws4.cell(row=row, column=2, value=val) ws4.cell(row=row, column=3, value=note).font = Font(italic=True, color="888888") if val == "" and note == "" and label: ws4.cell(row=row, column=1).font = Font(bold=True, color="00BFFF") auto_width(ws4) # Save outpath = "/home/wdjones/.openclaw/workspace/data/real-estate-cost-seg-model.xlsx" wb.save(outpath) print(f"Saved to {outpath}")