<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://swjeong.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://swjeong.com/" rel="alternate" type="text/html" /><updated>2026-08-11T18:21:45+00:00</updated><id>https://swjeong.com/feed.xml</id><title type="html">SWJ Note</title><subtitle>Notes on data engineering, finance, machine learning, and cloud by Owen Jeong — an accounting/finance and data professional building real data systems, ML pipelines, and production apps.</subtitle><author><name>Seungwon(Owen) Jeong</name></author><entry><title type="html">Valuation Analysis - NIKE</title><link href="https://swjeong.com/analysis/Valuation_fcff_NKE/" rel="alternate" type="text/html" title="Valuation Analysis - NIKE" /><published>2026-07-05T00:00:00+00:00</published><updated>2026-07-05T00:00:00+00:00</updated><id>https://swjeong.com/analysis/Valuation_fcff_NKE</id><content type="html" xml:base="https://swjeong.com/analysis/Valuation_fcff_NKE/"><![CDATA[<style>
/* Hide notebook code cells by default */
.language-python.highlighter-rouge { display: none; }
.language-python.highlighter-rouge.code-visible { display: block; }
#toggle-code {
  margin: 1em 0;
  padding: .45em 1em;
  cursor: pointer;
  border: 1px solid #ccc;
  border-radius: 4px;
  background: #f7f7f7;
  font-size: .85em;
  font-weight: 600;
}
#toggle-code:hover { background: #ececec; }
</style>

<p><button id="toggle-code" onclick="   var show = this.classList.toggle('on');   document.querySelectorAll('.language-python.highlighter-rouge').forEach(function(el){ el.classList.toggle('code-visible', show); });   this.textContent = show ? 'Hide code' : 'Show code'; ">Show code</button></p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">yfinance</span> <span class="k">as</span> <span class="n">yf</span>
<span class="kn">import</span> <span class="nn">pandas</span> <span class="k">as</span> <span class="n">pd</span>
<span class="kn">import</span> <span class="nn">datetime</span>

<span class="c1"># Show full numbers instead of scientific notation
</span><span class="n">pd</span><span class="p">.</span><span class="n">options</span><span class="p">.</span><span class="n">display</span><span class="p">.</span><span class="n">float_format</span> <span class="o">=</span> <span class="s">'{:,.0f}'</span><span class="p">.</span><span class="nb">format</span>

<span class="n">ticker</span> <span class="o">=</span> <span class="n">yf</span><span class="p">.</span><span class="n">Ticker</span><span class="p">(</span><span class="s">"NKE"</span><span class="p">)</span>
<span class="c1"># - income statement
</span><span class="n">pd</span><span class="p">.</span><span class="n">set_option</span><span class="p">(</span><span class="s">'display.max_rows'</span><span class="p">,</span> <span class="bp">None</span><span class="p">)</span>
<span class="n">balance_sheet_df</span> <span class="o">=</span> <span class="n">ticker</span><span class="p">.</span><span class="n">quarterly_balance_sheet</span>
<span class="n">balance_sheet_df</span>
</code></pre></div></div>

<div>
<style scoped="">
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>2026-02-28</th>
      <th>2025-11-30</th>
      <th>2025-08-31</th>
      <th>2025-05-31</th>
      <th>2025-02-28</th>
      <th>2024-11-30</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>Ordinary Shares Number</th>
      <td>1,480,000,000</td>
      <td>1,479,887,752</td>
      <td>1,476,903,492</td>
      <td>1,476,000,000</td>
      <td>1,476,887,752</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Share Issued</th>
      <td>1,480,000,000</td>
      <td>1,479,887,752</td>
      <td>1,476,903,492</td>
      <td>1,476,000,000</td>
      <td>1,476,887,752</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Net Debt</th>
      <td>1,369,000,000</td>
      <td>1,041,000,000</td>
      <td>976,000,000</td>
      <td>502,000,000</td>
      <td>359,000,000</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Total Debt</th>
      <td>11,178,000,000</td>
      <td>11,282,000,000</td>
      <td>11,061,000,000</td>
      <td>11,018,000,000</td>
      <td>11,911,000,000</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Tangible Book Value</th>
      <td>13,591,000,000</td>
      <td>13,586,000,000</td>
      <td>12,969,000,000</td>
      <td>12,714,000,000</td>
      <td>13,509,000,000</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Invested Capital</th>
      <td>22,119,000,000</td>
      <td>22,100,000,000</td>
      <td>21,468,000,000</td>
      <td>21,179,000,000</td>
      <td>22,967,000,000</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Working Capital</th>
      <td>12,346,000,000</td>
      <td>12,375,000,000</td>
      <td>12,987,000,000</td>
      <td>12,796,000,000</td>
      <td>13,386,000,000</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Net Tangible Assets</th>
      <td>13,591,000,000</td>
      <td>13,586,000,000</td>
      <td>12,969,000,000</td>
      <td>12,714,000,000</td>
      <td>13,509,000,000</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Capital Lease Obligations</th>
      <td>3,149,000,000</td>
      <td>3,267,000,000</td>
      <td>3,061,000,000</td>
      <td>3,052,000,000</td>
      <td>2,951,000,000</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Common Stock Equity</th>
      <td>14,090,000,000</td>
      <td>14,085,000,000</td>
      <td>13,468,000,000</td>
      <td>13,213,000,000</td>
      <td>14,007,000,000</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Total Capitalization</th>
      <td>21,120,000,000</td>
      <td>21,101,000,000</td>
      <td>21,464,000,000</td>
      <td>21,174,000,000</td>
      <td>21,963,000,000</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Total Equity Gross Minority Interest</th>
      <td>14,090,000,000</td>
      <td>14,085,000,000</td>
      <td>13,468,000,000</td>
      <td>13,213,000,000</td>
      <td>14,007,000,000</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Stockholders Equity</th>
      <td>14,090,000,000</td>
      <td>14,085,000,000</td>
      <td>13,468,000,000</td>
      <td>13,213,000,000</td>
      <td>14,007,000,000</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Gains Losses Not Affecting Retained Earnings</th>
      <td>-207,000,000</td>
      <td>-104,000,000</td>
      <td>-308,000,000</td>
      <td>-258,000,000</td>
      <td>263,000,000</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Other Equity Adjustments</th>
      <td>-207,000,000</td>
      <td>-104,000,000</td>
      <td>-308,000,000</td>
      <td>-258,000,000</td>
      <td>263,000,000</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Retained Earnings</th>
      <td>-610,000,000</td>
      <td>-519,000,000</td>
      <td>-700,000,000</td>
      <td>-727,000,000</td>
      <td>-175,000,000</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Additional Paid In Capital</th>
      <td>14,904,000,000</td>
      <td>14,705,000,000</td>
      <td>14,473,000,000</td>
      <td>14,195,000,000</td>
      <td>13,916,000,000</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Capital Stock</th>
      <td>3,000,000</td>
      <td>3,000,000</td>
      <td>3,000,000</td>
      <td>3,000,000</td>
      <td>3,000,000</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Common Stock</th>
      <td>3,000,000</td>
      <td>3,000,000</td>
      <td>3,000,000</td>
      <td>3,000,000</td>
      <td>3,000,000</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Total Liabilities Net Minority Interest</th>
      <td>22,974,000,000</td>
      <td>23,702,000,000</td>
      <td>23,866,000,000</td>
      <td>23,366,000,000</td>
      <td>23,786,000,000</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Total Non Current Liabilities Net Minority Interest</th>
      <td>12,136,000,000</td>
      <td>12,062,000,000</td>
      <td>12,955,000,000</td>
      <td>12,800,000,000</td>
      <td>12,563,000,000</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Other Non Current Liabilities</th>
      <td>2,450,000,000</td>
      <td>2,292,000,000</td>
      <td>2,404,000,000</td>
      <td>2,289,000,000</td>
      <td>2,130,000,000</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Preferred Securities Outside Stock Equity</th>
      <td>0</td>
      <td>0</td>
      <td>0</td>
      <td>0</td>
      <td>0</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Long Term Debt And Capital Lease Obligation</th>
      <td>9,686,000,000</td>
      <td>9,770,000,000</td>
      <td>10,551,000,000</td>
      <td>10,511,000,000</td>
      <td>10,433,000,000</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Long Term Capital Lease Obligation</th>
      <td>2,656,000,000</td>
      <td>2,754,000,000</td>
      <td>2,555,000,000</td>
      <td>2,550,000,000</td>
      <td>2,477,000,000</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Long Term Debt</th>
      <td>7,030,000,000</td>
      <td>7,016,000,000</td>
      <td>7,996,000,000</td>
      <td>7,961,000,000</td>
      <td>7,956,000,000</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Current Liabilities</th>
      <td>10,838,000,000</td>
      <td>11,640,000,000</td>
      <td>10,911,000,000</td>
      <td>10,566,000,000</td>
      <td>11,223,000,000</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Current Debt And Capital Lease Obligation</th>
      <td>1,492,000,000</td>
      <td>1,512,000,000</td>
      <td>510,000,000</td>
      <td>507,000,000</td>
      <td>1,478,000,000</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Current Capital Lease Obligation</th>
      <td>493,000,000</td>
      <td>513,000,000</td>
      <td>506,000,000</td>
      <td>502,000,000</td>
      <td>474,000,000</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Current Debt</th>
      <td>999,000,000</td>
      <td>999,000,000</td>
      <td>4,000,000</td>
      <td>5,000,000</td>
      <td>1,004,000,000</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Other Current Borrowings</th>
      <td>999,000,000</td>
      <td>999,000,000</td>
      <td>NaN</td>
      <td>NaN</td>
      <td>1,000,000,000</td>
      <td>1,000,000,000</td>
    </tr>
    <tr>
      <th>Current Notes Payable</th>
      <td>0</td>
      <td>0</td>
      <td>4,000,000</td>
      <td>5,000,000</td>
      <td>4,000,000</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Pensionand Other Post Retirement Benefit Plans Current</th>
      <td>1,544,000,000</td>
      <td>1,236,000,000</td>
      <td>1,244,000,000</td>
      <td>1,726,000,000</td>
      <td>1,708,000,000</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Current Provisions</th>
      <td>1,658,000,000</td>
      <td>1,748,000,000</td>
      <td>1,788,000,000</td>
      <td>1,834,000,000</td>
      <td>1,682,000,000</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Payables And Accrued Expenses</th>
      <td>6,144,000,000</td>
      <td>7,144,000,000</td>
      <td>7,369,000,000</td>
      <td>6,499,000,000</td>
      <td>6,355,000,000</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Current Accrued Expenses</th>
      <td>2,365,000,000</td>
      <td>2,320,000,000</td>
      <td>2,292,000,000</td>
      <td>1,753,000,000</td>
      <td>1,917,000,000</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Payables</th>
      <td>3,779,000,000</td>
      <td>4,824,000,000</td>
      <td>5,077,000,000</td>
      <td>4,746,000,000</td>
      <td>4,438,000,000</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Dividends Payable</th>
      <td>616,000,000</td>
      <td>615,000,000</td>
      <td>599,000,000</td>
      <td>598,000,000</td>
      <td>598,000,000</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Total Tax Payable</th>
      <td>275,000,000</td>
      <td>492,000,000</td>
      <td>706,000,000</td>
      <td>669,000,000</td>
      <td>734,000,000</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Income Tax Payable</th>
      <td>275,000,000</td>
      <td>492,000,000</td>
      <td>706,000,000</td>
      <td>669,000,000</td>
      <td>734,000,000</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Accounts Payable</th>
      <td>2,888,000,000</td>
      <td>3,717,000,000</td>
      <td>3,772,000,000</td>
      <td>3,479,000,000</td>
      <td>3,106,000,000</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Total Assets</th>
      <td>37,064,000,000</td>
      <td>37,787,000,000</td>
      <td>37,334,000,000</td>
      <td>36,579,000,000</td>
      <td>37,793,000,000</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Total Non Current Assets</th>
      <td>13,880,000,000</td>
      <td>13,772,000,000</td>
      <td>13,436,000,000</td>
      <td>13,217,000,000</td>
      <td>13,184,000,000</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Other Non Current Assets</th>
      <td>5,729,000,000</td>
      <td>5,536,000,000</td>
      <td>5,349,000,000</td>
      <td>5,178,000,000</td>
      <td>5,355,000,000</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Goodwill And Other Intangible Assets</th>
      <td>499,000,000</td>
      <td>499,000,000</td>
      <td>499,000,000</td>
      <td>499,000,000</td>
      <td>498,000,000</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Other Intangible Assets</th>
      <td>259,000,000</td>
      <td>259,000,000</td>
      <td>259,000,000</td>
      <td>259,000,000</td>
      <td>259,000,000</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Goodwill</th>
      <td>240,000,000</td>
      <td>240,000,000</td>
      <td>240,000,000</td>
      <td>240,000,000</td>
      <td>239,000,000</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Net PPE</th>
      <td>7,652,000,000</td>
      <td>7,737,000,000</td>
      <td>7,588,000,000</td>
      <td>7,540,000,000</td>
      <td>7,331,000,000</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Accumulated Depreciation</th>
      <td>NaN</td>
      <td>NaN</td>
      <td>NaN</td>
      <td>-6,104,000,000</td>
      <td>NaN</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Gross PPE</th>
      <td>7,652,000,000</td>
      <td>7,737,000,000</td>
      <td>7,588,000,000</td>
      <td>13,644,000,000</td>
      <td>7,331,000,000</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Leases</th>
      <td>NaN</td>
      <td>NaN</td>
      <td>NaN</td>
      <td>2,037,000,000</td>
      <td>NaN</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Construction In Progress</th>
      <td>NaN</td>
      <td>NaN</td>
      <td>NaN</td>
      <td>404,000,000</td>
      <td>NaN</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Other Properties</th>
      <td>7,652,000,000</td>
      <td>7,737,000,000</td>
      <td>7,588,000,000</td>
      <td>2,712,000,000</td>
      <td>7,331,000,000</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Machinery Furniture Equipment</th>
      <td>NaN</td>
      <td>NaN</td>
      <td>NaN</td>
      <td>4,647,000,000</td>
      <td>NaN</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Buildings And Improvements</th>
      <td>NaN</td>
      <td>NaN</td>
      <td>NaN</td>
      <td>3,510,000,000</td>
      <td>NaN</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Land And Improvements</th>
      <td>NaN</td>
      <td>NaN</td>
      <td>NaN</td>
      <td>334,000,000</td>
      <td>NaN</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Properties</th>
      <td>NaN</td>
      <td>NaN</td>
      <td>NaN</td>
      <td>0</td>
      <td>NaN</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Current Assets</th>
      <td>23,184,000,000</td>
      <td>24,015,000,000</td>
      <td>23,898,000,000</td>
      <td>23,362,000,000</td>
      <td>24,609,000,000</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Other Current Assets</th>
      <td>2,271,000,000</td>
      <td>2,206,000,000</td>
      <td>2,247,000,000</td>
      <td>2,005,000,000</td>
      <td>2,186,000,000</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Inventory</th>
      <td>7,487,000,000</td>
      <td>7,726,000,000</td>
      <td>8,114,000,000</td>
      <td>7,489,000,000</td>
      <td>7,539,000,000</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Finished Goods</th>
      <td>7,487,000,000</td>
      <td>7,726,000,000</td>
      <td>8,114,000,000</td>
      <td>7,489,000,000</td>
      <td>7,539,000,000</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Receivables</th>
      <td>5,369,000,000</td>
      <td>5,738,000,000</td>
      <td>4,962,000,000</td>
      <td>4,717,000,000</td>
      <td>4,491,000,000</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Accounts Receivable</th>
      <td>5,369,000,000</td>
      <td>5,738,000,000</td>
      <td>4,962,000,000</td>
      <td>4,717,000,000</td>
      <td>4,491,000,000</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Allowance For Doubtful Accounts Receivable</th>
      <td>NaN</td>
      <td>NaN</td>
      <td>NaN</td>
      <td>-27,000,000</td>
      <td>NaN</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Gross Accounts Receivable</th>
      <td>NaN</td>
      <td>NaN</td>
      <td>NaN</td>
      <td>4,744,000,000</td>
      <td>NaN</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Cash Cash Equivalents And Short Term Investments</th>
      <td>8,057,000,000</td>
      <td>8,345,000,000</td>
      <td>8,575,000,000</td>
      <td>9,151,000,000</td>
      <td>10,393,000,000</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Other Short Term Investments</th>
      <td>1,397,000,000</td>
      <td>1,371,000,000</td>
      <td>1,551,000,000</td>
      <td>1,687,000,000</td>
      <td>1,792,000,000</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Cash And Cash Equivalents</th>
      <td>6,660,000,000</td>
      <td>6,974,000,000</td>
      <td>7,024,000,000</td>
      <td>7,464,000,000</td>
      <td>8,601,000,000</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Cash Equivalents</th>
      <td>4,967,000,000</td>
      <td>5,216,000,000</td>
      <td>5,615,000,000</td>
      <td>6,243,000,000</td>
      <td>7,263,000,000</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Cash Financial</th>
      <td>1,693,000,000</td>
      <td>1,758,000,000</td>
      <td>1,409,000,000</td>
      <td>1,221,000,000</td>
      <td>1,338,000,000</td>
      <td>NaN</td>
    </tr>
  </tbody>
</table>
</div>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">cash_flow_df</span> <span class="o">=</span> <span class="n">ticker</span><span class="p">.</span><span class="n">cashflow</span>
<span class="n">cash_flow_df</span>
</code></pre></div></div>

<div>
<style scoped="">
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>2025-05-31</th>
      <th>2024-05-31</th>
      <th>2023-05-31</th>
      <th>2022-05-31</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>Free Cash Flow</th>
      <td>3,268,000,000</td>
      <td>6,617,000,000</td>
      <td>4,872,000,000</td>
      <td>4,430,000,000</td>
    </tr>
    <tr>
      <th>Repurchase Of Capital Stock</th>
      <td>-2,985,000,000</td>
      <td>-4,250,000,000</td>
      <td>-5,480,000,000</td>
      <td>-4,014,000,000</td>
    </tr>
    <tr>
      <th>Repayment Of Debt</th>
      <td>-1,000,000,000</td>
      <td>0</td>
      <td>-500,000,000</td>
      <td>0</td>
    </tr>
    <tr>
      <th>Issuance Of Debt</th>
      <td>NaN</td>
      <td>NaN</td>
      <td>NaN</td>
      <td>0</td>
    </tr>
    <tr>
      <th>Capital Expenditure</th>
      <td>-430,000,000</td>
      <td>-812,000,000</td>
      <td>-969,000,000</td>
      <td>-758,000,000</td>
    </tr>
    <tr>
      <th>Interest Paid Supplemental Data</th>
      <td>389,000,000</td>
      <td>381,000,000</td>
      <td>347,000,000</td>
      <td>290,000,000</td>
    </tr>
    <tr>
      <th>Income Tax Paid Supplemental Data</th>
      <td>1,226,000,000</td>
      <td>1,299,000,000</td>
      <td>1,517,000,000</td>
      <td>1,231,000,000</td>
    </tr>
    <tr>
      <th>End Cash Position</th>
      <td>7,464,000,000</td>
      <td>9,860,000,000</td>
      <td>7,441,000,000</td>
      <td>8,574,000,000</td>
    </tr>
    <tr>
      <th>Beginning Cash Position</th>
      <td>9,860,000,000</td>
      <td>7,441,000,000</td>
      <td>8,574,000,000</td>
      <td>9,889,000,000</td>
    </tr>
    <tr>
      <th>Effect Of Exchange Rate Changes</th>
      <td>1,000,000</td>
      <td>-16,000,000</td>
      <td>-91,000,000</td>
      <td>-143,000,000</td>
    </tr>
    <tr>
      <th>Changes In Cash</th>
      <td>-2,397,000,000</td>
      <td>2,435,000,000</td>
      <td>-1,042,000,000</td>
      <td>-1,172,000,000</td>
    </tr>
    <tr>
      <th>Financing Cash Flow</th>
      <td>-5,820,000,000</td>
      <td>-5,888,000,000</td>
      <td>-7,447,000,000</td>
      <td>-4,836,000,000</td>
    </tr>
    <tr>
      <th>Cash Flow From Continuing Financing Activities</th>
      <td>-5,820,000,000</td>
      <td>-5,888,000,000</td>
      <td>-7,447,000,000</td>
      <td>-4,836,000,000</td>
    </tr>
    <tr>
      <th>Net Other Financing Charges</th>
      <td>-85,000,000</td>
      <td>-136,000,000</td>
      <td>-102,000,000</td>
      <td>-151,000,000</td>
    </tr>
    <tr>
      <th>Proceeds From Stock Option Exercised</th>
      <td>551,000,000</td>
      <td>667,000,000</td>
      <td>651,000,000</td>
      <td>1,151,000,000</td>
    </tr>
    <tr>
      <th>Cash Dividends Paid</th>
      <td>-2,300,000,000</td>
      <td>-2,169,000,000</td>
      <td>-2,012,000,000</td>
      <td>-1,837,000,000</td>
    </tr>
    <tr>
      <th>Common Stock Dividend Paid</th>
      <td>-2,300,000,000</td>
      <td>-2,169,000,000</td>
      <td>-2,012,000,000</td>
      <td>-1,837,000,000</td>
    </tr>
    <tr>
      <th>Net Common Stock Issuance</th>
      <td>-2,985,000,000</td>
      <td>-4,250,000,000</td>
      <td>-5,480,000,000</td>
      <td>-4,014,000,000</td>
    </tr>
    <tr>
      <th>Common Stock Payments</th>
      <td>-2,985,000,000</td>
      <td>-4,250,000,000</td>
      <td>-5,480,000,000</td>
      <td>-4,014,000,000</td>
    </tr>
    <tr>
      <th>Net Issuance Payments Of Debt</th>
      <td>-1,001,000,000</td>
      <td>0</td>
      <td>-504,000,000</td>
      <td>15,000,000</td>
    </tr>
    <tr>
      <th>Net Short Term Debt Issuance</th>
      <td>-1,000,000</td>
      <td>0</td>
      <td>-4,000,000</td>
      <td>15,000,000</td>
    </tr>
    <tr>
      <th>Net Long Term Debt Issuance</th>
      <td>-1,000,000,000</td>
      <td>0</td>
      <td>-500,000,000</td>
      <td>0</td>
    </tr>
    <tr>
      <th>Long Term Debt Payments</th>
      <td>-1,000,000,000</td>
      <td>0</td>
      <td>-500,000,000</td>
      <td>0</td>
    </tr>
    <tr>
      <th>Long Term Debt Issuance</th>
      <td>NaN</td>
      <td>NaN</td>
      <td>NaN</td>
      <td>0</td>
    </tr>
    <tr>
      <th>Investing Cash Flow</th>
      <td>-275,000,000</td>
      <td>894,000,000</td>
      <td>564,000,000</td>
      <td>-1,524,000,000</td>
    </tr>
    <tr>
      <th>Cash Flow From Continuing Investing Activities</th>
      <td>-275,000,000</td>
      <td>894,000,000</td>
      <td>564,000,000</td>
      <td>-1,524,000,000</td>
    </tr>
    <tr>
      <th>Net Other Investing Changes</th>
      <td>8,000,000</td>
      <td>-15,000,000</td>
      <td>52,000,000</td>
      <td>-19,000,000</td>
    </tr>
    <tr>
      <th>Net Investment Purchase And Sale</th>
      <td>147,000,000</td>
      <td>1,721,000,000</td>
      <td>1,481,000,000</td>
      <td>-747,000,000</td>
    </tr>
    <tr>
      <th>Sale Of Investment</th>
      <td>3,381,000,000</td>
      <td>6,488,000,000</td>
      <td>7,540,000,000</td>
      <td>12,166,000,000</td>
    </tr>
    <tr>
      <th>Purchase Of Investment</th>
      <td>-3,234,000,000</td>
      <td>-4,767,000,000</td>
      <td>-6,059,000,000</td>
      <td>-12,913,000,000</td>
    </tr>
    <tr>
      <th>Net PPE Purchase And Sale</th>
      <td>-430,000,000</td>
      <td>-812,000,000</td>
      <td>-969,000,000</td>
      <td>-758,000,000</td>
    </tr>
    <tr>
      <th>Purchase Of PPE</th>
      <td>-430,000,000</td>
      <td>-812,000,000</td>
      <td>-969,000,000</td>
      <td>-758,000,000</td>
    </tr>
    <tr>
      <th>Operating Cash Flow</th>
      <td>3,698,000,000</td>
      <td>7,429,000,000</td>
      <td>5,841,000,000</td>
      <td>5,188,000,000</td>
    </tr>
    <tr>
      <th>Cash Flow From Continuing Operating Activities</th>
      <td>3,698,000,000</td>
      <td>7,429,000,000</td>
      <td>5,841,000,000</td>
      <td>5,188,000,000</td>
    </tr>
    <tr>
      <th>Change In Working Capital</th>
      <td>-787,000,000</td>
      <td>716,000,000</td>
      <td>-513,000,000</td>
      <td>-1,660,000,000</td>
    </tr>
    <tr>
      <th>Change In Payables And Accrued Expense</th>
      <td>-426,000,000</td>
      <td>397,000,000</td>
      <td>-225,000,000</td>
      <td>1,365,000,000</td>
    </tr>
    <tr>
      <th>Change In Payable</th>
      <td>-426,000,000</td>
      <td>397,000,000</td>
      <td>-225,000,000</td>
      <td>1,365,000,000</td>
    </tr>
    <tr>
      <th>Change In Account Payable</th>
      <td>-426,000,000</td>
      <td>397,000,000</td>
      <td>-225,000,000</td>
      <td>1,365,000,000</td>
    </tr>
    <tr>
      <th>Change In Prepaid Assets</th>
      <td>-224,000,000</td>
      <td>-260,000,000</td>
      <td>-644,000,000</td>
      <td>-845,000,000</td>
    </tr>
    <tr>
      <th>Change In Inventory</th>
      <td>120,000,000</td>
      <td>908,000,000</td>
      <td>-133,000,000</td>
      <td>-1,676,000,000</td>
    </tr>
    <tr>
      <th>Change In Receivables</th>
      <td>-257,000,000</td>
      <td>-329,000,000</td>
      <td>489,000,000</td>
      <td>-504,000,000</td>
    </tr>
    <tr>
      <th>Changes In Account Receivables</th>
      <td>-257,000,000</td>
      <td>-329,000,000</td>
      <td>489,000,000</td>
      <td>-504,000,000</td>
    </tr>
    <tr>
      <th>Stock Based Compensation</th>
      <td>709,000,000</td>
      <td>804,000,000</td>
      <td>755,000,000</td>
      <td>638,000,000</td>
    </tr>
    <tr>
      <th>Deferred Tax</th>
      <td>-288,000,000</td>
      <td>-497,000,000</td>
      <td>-117,000,000</td>
      <td>-650,000,000</td>
    </tr>
    <tr>
      <th>Deferred Income Tax</th>
      <td>-288,000,000</td>
      <td>-497,000,000</td>
      <td>-117,000,000</td>
      <td>-650,000,000</td>
    </tr>
    <tr>
      <th>Depreciation Amortization Depletion</th>
      <td>808,000,000</td>
      <td>844,000,000</td>
      <td>859,000,000</td>
      <td>840,000,000</td>
    </tr>
    <tr>
      <th>Depreciation And Amortization</th>
      <td>808,000,000</td>
      <td>844,000,000</td>
      <td>859,000,000</td>
      <td>840,000,000</td>
    </tr>
    <tr>
      <th>Amortization Cash Flow</th>
      <td>33,000,000</td>
      <td>48,000,000</td>
      <td>156,000,000</td>
      <td>123,000,000</td>
    </tr>
    <tr>
      <th>Amortization Of Intangibles</th>
      <td>33,000,000</td>
      <td>48,000,000</td>
      <td>156,000,000</td>
      <td>123,000,000</td>
    </tr>
    <tr>
      <th>Depreciation</th>
      <td>775,000,000</td>
      <td>796,000,000</td>
      <td>703,000,000</td>
      <td>717,000,000</td>
    </tr>
    <tr>
      <th>Operating Gains Losses</th>
      <td>37,000,000</td>
      <td>-138,000,000</td>
      <td>-213,000,000</td>
      <td>-26,000,000</td>
    </tr>
    <tr>
      <th>Net Foreign Currency Exchange Gain Loss</th>
      <td>37,000,000</td>
      <td>-138,000,000</td>
      <td>-213,000,000</td>
      <td>-26,000,000</td>
    </tr>
    <tr>
      <th>Net Income From Continuing Operations</th>
      <td>3,219,000,000</td>
      <td>5,700,000,000</td>
      <td>5,070,000,000</td>
      <td>6,046,000,000</td>
    </tr>
  </tbody>
</table>
</div>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">income_statement_df</span> <span class="o">=</span> <span class="n">ticker</span><span class="p">.</span><span class="n">income_stmt</span><span class="p">.</span><span class="n">dropna</span><span class="p">(</span><span class="n">axis</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">thresh</span><span class="o">=</span><span class="mi">10</span><span class="p">)</span>
<span class="n">income_statement_df</span>
</code></pre></div></div>

<div>
<style scoped="">
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>2025-05-31</th>
      <th>2024-05-31</th>
      <th>2023-05-31</th>
      <th>2022-05-31</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>Tax Effect Of Unusual Items</th>
      <td>0</td>
      <td>0</td>
      <td>0</td>
      <td>0</td>
    </tr>
    <tr>
      <th>Tax Rate For Calcs</th>
      <td>0</td>
      <td>0</td>
      <td>0</td>
      <td>0</td>
    </tr>
    <tr>
      <th>Normalized EBITDA</th>
      <td>4,510,000,000</td>
      <td>7,155,000,000</td>
      <td>6,774,000,000</td>
      <td>7,515,000,000</td>
    </tr>
    <tr>
      <th>Net Income From Continuing Operation Net Minority Interest</th>
      <td>3,219,000,000</td>
      <td>5,700,000,000</td>
      <td>5,070,000,000</td>
      <td>6,046,000,000</td>
    </tr>
    <tr>
      <th>Reconciled Depreciation</th>
      <td>808,000,000</td>
      <td>844,000,000</td>
      <td>859,000,000</td>
      <td>840,000,000</td>
    </tr>
    <tr>
      <th>Reconciled Cost Of Revenue</th>
      <td>26,519,000,000</td>
      <td>28,475,000,000</td>
      <td>28,925,000,000</td>
      <td>25,231,000,000</td>
    </tr>
    <tr>
      <th>EBITDA</th>
      <td>4,510,000,000</td>
      <td>7,155,000,000</td>
      <td>6,774,000,000</td>
      <td>7,515,000,000</td>
    </tr>
    <tr>
      <th>EBIT</th>
      <td>3,702,000,000</td>
      <td>6,311,000,000</td>
      <td>5,915,000,000</td>
      <td>6,675,000,000</td>
    </tr>
    <tr>
      <th>Net Interest Income</th>
      <td>107,000,000</td>
      <td>161,000,000</td>
      <td>6,000,000</td>
      <td>-205,000,000</td>
    </tr>
    <tr>
      <th>Normalized Income</th>
      <td>3,219,000,000</td>
      <td>5,700,000,000</td>
      <td>5,070,000,000</td>
      <td>6,046,000,000</td>
    </tr>
    <tr>
      <th>Net Income From Continuing And Discontinued Operation</th>
      <td>3,219,000,000</td>
      <td>5,700,000,000</td>
      <td>5,070,000,000</td>
      <td>6,046,000,000</td>
    </tr>
    <tr>
      <th>Total Expenses</th>
      <td>42,607,000,000</td>
      <td>45,051,000,000</td>
      <td>45,302,000,000</td>
      <td>40,035,000,000</td>
    </tr>
    <tr>
      <th>Diluted Average Shares</th>
      <td>1,487,600,000</td>
      <td>1,529,700,000</td>
      <td>1,569,800,000</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Basic Average Shares</th>
      <td>1,484,900,000</td>
      <td>1,517,600,000</td>
      <td>1,551,600,000</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Diluted EPS</th>
      <td>2</td>
      <td>4</td>
      <td>3</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Basic EPS</th>
      <td>2</td>
      <td>4</td>
      <td>3</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Diluted NI Availto Com Stockholders</th>
      <td>3,219,000,000</td>
      <td>5,700,000,000</td>
      <td>5,070,000,000</td>
      <td>6,046,000,000</td>
    </tr>
    <tr>
      <th>Net Income Common Stockholders</th>
      <td>3,219,000,000</td>
      <td>5,700,000,000</td>
      <td>5,070,000,000</td>
      <td>6,046,000,000</td>
    </tr>
    <tr>
      <th>Net Income</th>
      <td>3,219,000,000</td>
      <td>5,700,000,000</td>
      <td>5,070,000,000</td>
      <td>6,046,000,000</td>
    </tr>
    <tr>
      <th>Net Income Including Noncontrolling Interests</th>
      <td>3,219,000,000</td>
      <td>5,700,000,000</td>
      <td>5,070,000,000</td>
      <td>6,046,000,000</td>
    </tr>
    <tr>
      <th>Net Income Continuous Operations</th>
      <td>3,219,000,000</td>
      <td>5,700,000,000</td>
      <td>5,070,000,000</td>
      <td>6,046,000,000</td>
    </tr>
    <tr>
      <th>Tax Provision</th>
      <td>666,000,000</td>
      <td>1,000,000,000</td>
      <td>1,131,000,000</td>
      <td>605,000,000</td>
    </tr>
    <tr>
      <th>Pretax Income</th>
      <td>3,885,000,000</td>
      <td>6,700,000,000</td>
      <td>6,201,000,000</td>
      <td>6,651,000,000</td>
    </tr>
    <tr>
      <th>Other Income Expense</th>
      <td>76,000,000</td>
      <td>228,000,000</td>
      <td>280,000,000</td>
      <td>181,000,000</td>
    </tr>
    <tr>
      <th>Other Non Operating Income Expenses</th>
      <td>76,000,000</td>
      <td>228,000,000</td>
      <td>280,000,000</td>
      <td>181,000,000</td>
    </tr>
    <tr>
      <th>Net Non Operating Interest Income Expense</th>
      <td>107,000,000</td>
      <td>161,000,000</td>
      <td>6,000,000</td>
      <td>-205,000,000</td>
    </tr>
    <tr>
      <th>Total Other Finance Cost</th>
      <td>-107,000,000</td>
      <td>-161,000,000</td>
      <td>-6,000,000</td>
      <td>205,000,000</td>
    </tr>
    <tr>
      <th>Operating Income</th>
      <td>3,702,000,000</td>
      <td>6,311,000,000</td>
      <td>5,915,000,000</td>
      <td>6,675,000,000</td>
    </tr>
    <tr>
      <th>Operating Expense</th>
      <td>16,088,000,000</td>
      <td>16,576,000,000</td>
      <td>16,377,000,000</td>
      <td>14,804,000,000</td>
    </tr>
    <tr>
      <th>Other Operating Expenses</th>
      <td>NaN</td>
      <td>NaN</td>
      <td>12,317,000,000</td>
      <td>10,954,000,000</td>
    </tr>
    <tr>
      <th>Selling General And Administration</th>
      <td>16,088,000,000</td>
      <td>16,576,000,000</td>
      <td>16,377,000,000</td>
      <td>14,804,000,000</td>
    </tr>
    <tr>
      <th>Selling And Marketing Expense</th>
      <td>4,689,000,000</td>
      <td>4,285,000,000</td>
      <td>4,060,000,000</td>
      <td>3,850,000,000</td>
    </tr>
    <tr>
      <th>General And Administrative Expense</th>
      <td>11,399,000,000</td>
      <td>12,291,000,000</td>
      <td>12,317,000,000</td>
      <td>10,954,000,000</td>
    </tr>
    <tr>
      <th>Other Gand A</th>
      <td>11,399,000,000</td>
      <td>12,291,000,000</td>
      <td>12,317,000,000</td>
      <td>10,954,000,000</td>
    </tr>
    <tr>
      <th>Gross Profit</th>
      <td>19,790,000,000</td>
      <td>22,887,000,000</td>
      <td>22,292,000,000</td>
      <td>21,479,000,000</td>
    </tr>
    <tr>
      <th>Cost Of Revenue</th>
      <td>26,519,000,000</td>
      <td>28,475,000,000</td>
      <td>28,925,000,000</td>
      <td>25,231,000,000</td>
    </tr>
    <tr>
      <th>Total Revenue</th>
      <td>46,309,000,000</td>
      <td>51,362,000,000</td>
      <td>51,217,000,000</td>
      <td>46,710,000,000</td>
    </tr>
    <tr>
      <th>Operating Revenue</th>
      <td>46,309,000,000</td>
      <td>51,362,000,000</td>
      <td>51,217,000,000</td>
      <td>46,710,000,000</td>
    </tr>
  </tbody>
</table>
</div>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">annual_income</span> <span class="o">=</span> <span class="n">ticker</span><span class="p">.</span><span class="n">income_stmt</span><span class="p">.</span><span class="n">dropna</span><span class="p">(</span><span class="n">axis</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">thresh</span><span class="o">=</span><span class="mi">10</span><span class="p">)</span>
<span class="n">annual_income</span>
</code></pre></div></div>

<div>
<style scoped="">
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>2025-05-31</th>
      <th>2024-05-31</th>
      <th>2023-05-31</th>
      <th>2022-05-31</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>Tax Effect Of Unusual Items</th>
      <td>0</td>
      <td>0</td>
      <td>0</td>
      <td>0</td>
    </tr>
    <tr>
      <th>Tax Rate For Calcs</th>
      <td>0</td>
      <td>0</td>
      <td>0</td>
      <td>0</td>
    </tr>
    <tr>
      <th>Normalized EBITDA</th>
      <td>4,510,000,000</td>
      <td>7,155,000,000</td>
      <td>6,774,000,000</td>
      <td>7,515,000,000</td>
    </tr>
    <tr>
      <th>Net Income From Continuing Operation Net Minority Interest</th>
      <td>3,219,000,000</td>
      <td>5,700,000,000</td>
      <td>5,070,000,000</td>
      <td>6,046,000,000</td>
    </tr>
    <tr>
      <th>Reconciled Depreciation</th>
      <td>808,000,000</td>
      <td>844,000,000</td>
      <td>859,000,000</td>
      <td>840,000,000</td>
    </tr>
    <tr>
      <th>Reconciled Cost Of Revenue</th>
      <td>26,519,000,000</td>
      <td>28,475,000,000</td>
      <td>28,925,000,000</td>
      <td>25,231,000,000</td>
    </tr>
    <tr>
      <th>EBITDA</th>
      <td>4,510,000,000</td>
      <td>7,155,000,000</td>
      <td>6,774,000,000</td>
      <td>7,515,000,000</td>
    </tr>
    <tr>
      <th>EBIT</th>
      <td>3,702,000,000</td>
      <td>6,311,000,000</td>
      <td>5,915,000,000</td>
      <td>6,675,000,000</td>
    </tr>
    <tr>
      <th>Net Interest Income</th>
      <td>107,000,000</td>
      <td>161,000,000</td>
      <td>6,000,000</td>
      <td>-205,000,000</td>
    </tr>
    <tr>
      <th>Normalized Income</th>
      <td>3,219,000,000</td>
      <td>5,700,000,000</td>
      <td>5,070,000,000</td>
      <td>6,046,000,000</td>
    </tr>
    <tr>
      <th>Net Income From Continuing And Discontinued Operation</th>
      <td>3,219,000,000</td>
      <td>5,700,000,000</td>
      <td>5,070,000,000</td>
      <td>6,046,000,000</td>
    </tr>
    <tr>
      <th>Total Expenses</th>
      <td>42,607,000,000</td>
      <td>45,051,000,000</td>
      <td>45,302,000,000</td>
      <td>40,035,000,000</td>
    </tr>
    <tr>
      <th>Diluted Average Shares</th>
      <td>1,487,600,000</td>
      <td>1,529,700,000</td>
      <td>1,569,800,000</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Basic Average Shares</th>
      <td>1,484,900,000</td>
      <td>1,517,600,000</td>
      <td>1,551,600,000</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Diluted EPS</th>
      <td>2</td>
      <td>4</td>
      <td>3</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Basic EPS</th>
      <td>2</td>
      <td>4</td>
      <td>3</td>
      <td>NaN</td>
    </tr>
    <tr>
      <th>Diluted NI Availto Com Stockholders</th>
      <td>3,219,000,000</td>
      <td>5,700,000,000</td>
      <td>5,070,000,000</td>
      <td>6,046,000,000</td>
    </tr>
    <tr>
      <th>Net Income Common Stockholders</th>
      <td>3,219,000,000</td>
      <td>5,700,000,000</td>
      <td>5,070,000,000</td>
      <td>6,046,000,000</td>
    </tr>
    <tr>
      <th>Net Income</th>
      <td>3,219,000,000</td>
      <td>5,700,000,000</td>
      <td>5,070,000,000</td>
      <td>6,046,000,000</td>
    </tr>
    <tr>
      <th>Net Income Including Noncontrolling Interests</th>
      <td>3,219,000,000</td>
      <td>5,700,000,000</td>
      <td>5,070,000,000</td>
      <td>6,046,000,000</td>
    </tr>
    <tr>
      <th>Net Income Continuous Operations</th>
      <td>3,219,000,000</td>
      <td>5,700,000,000</td>
      <td>5,070,000,000</td>
      <td>6,046,000,000</td>
    </tr>
    <tr>
      <th>Tax Provision</th>
      <td>666,000,000</td>
      <td>1,000,000,000</td>
      <td>1,131,000,000</td>
      <td>605,000,000</td>
    </tr>
    <tr>
      <th>Pretax Income</th>
      <td>3,885,000,000</td>
      <td>6,700,000,000</td>
      <td>6,201,000,000</td>
      <td>6,651,000,000</td>
    </tr>
    <tr>
      <th>Other Income Expense</th>
      <td>76,000,000</td>
      <td>228,000,000</td>
      <td>280,000,000</td>
      <td>181,000,000</td>
    </tr>
    <tr>
      <th>Other Non Operating Income Expenses</th>
      <td>76,000,000</td>
      <td>228,000,000</td>
      <td>280,000,000</td>
      <td>181,000,000</td>
    </tr>
    <tr>
      <th>Net Non Operating Interest Income Expense</th>
      <td>107,000,000</td>
      <td>161,000,000</td>
      <td>6,000,000</td>
      <td>-205,000,000</td>
    </tr>
    <tr>
      <th>Total Other Finance Cost</th>
      <td>-107,000,000</td>
      <td>-161,000,000</td>
      <td>-6,000,000</td>
      <td>205,000,000</td>
    </tr>
    <tr>
      <th>Operating Income</th>
      <td>3,702,000,000</td>
      <td>6,311,000,000</td>
      <td>5,915,000,000</td>
      <td>6,675,000,000</td>
    </tr>
    <tr>
      <th>Operating Expense</th>
      <td>16,088,000,000</td>
      <td>16,576,000,000</td>
      <td>16,377,000,000</td>
      <td>14,804,000,000</td>
    </tr>
    <tr>
      <th>Other Operating Expenses</th>
      <td>NaN</td>
      <td>NaN</td>
      <td>12,317,000,000</td>
      <td>10,954,000,000</td>
    </tr>
    <tr>
      <th>Selling General And Administration</th>
      <td>16,088,000,000</td>
      <td>16,576,000,000</td>
      <td>16,377,000,000</td>
      <td>14,804,000,000</td>
    </tr>
    <tr>
      <th>Selling And Marketing Expense</th>
      <td>4,689,000,000</td>
      <td>4,285,000,000</td>
      <td>4,060,000,000</td>
      <td>3,850,000,000</td>
    </tr>
    <tr>
      <th>General And Administrative Expense</th>
      <td>11,399,000,000</td>
      <td>12,291,000,000</td>
      <td>12,317,000,000</td>
      <td>10,954,000,000</td>
    </tr>
    <tr>
      <th>Other Gand A</th>
      <td>11,399,000,000</td>
      <td>12,291,000,000</td>
      <td>12,317,000,000</td>
      <td>10,954,000,000</td>
    </tr>
    <tr>
      <th>Gross Profit</th>
      <td>19,790,000,000</td>
      <td>22,887,000,000</td>
      <td>22,292,000,000</td>
      <td>21,479,000,000</td>
    </tr>
    <tr>
      <th>Cost Of Revenue</th>
      <td>26,519,000,000</td>
      <td>28,475,000,000</td>
      <td>28,925,000,000</td>
      <td>25,231,000,000</td>
    </tr>
    <tr>
      <th>Total Revenue</th>
      <td>46,309,000,000</td>
      <td>51,362,000,000</td>
      <td>51,217,000,000</td>
      <td>46,710,000,000</td>
    </tr>
    <tr>
      <th>Operating Revenue</th>
      <td>46,309,000,000</td>
      <td>51,362,000,000</td>
      <td>51,217,000,000</td>
      <td>46,710,000,000</td>
    </tr>
  </tbody>
</table>
</div>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">annual_cash_flow</span> <span class="o">=</span> <span class="n">ticker</span><span class="p">.</span><span class="n">cashflow</span>
<span class="n">annual_cash_flow</span>
</code></pre></div></div>

<div>
<style scoped="">
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>2025-05-31</th>
      <th>2024-05-31</th>
      <th>2023-05-31</th>
      <th>2022-05-31</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>Free Cash Flow</th>
      <td>3,268,000,000</td>
      <td>6,617,000,000</td>
      <td>4,872,000,000</td>
      <td>4,430,000,000</td>
    </tr>
    <tr>
      <th>Repurchase Of Capital Stock</th>
      <td>-2,985,000,000</td>
      <td>-4,250,000,000</td>
      <td>-5,480,000,000</td>
      <td>-4,014,000,000</td>
    </tr>
    <tr>
      <th>Repayment Of Debt</th>
      <td>-1,000,000,000</td>
      <td>0</td>
      <td>-500,000,000</td>
      <td>0</td>
    </tr>
    <tr>
      <th>Issuance Of Debt</th>
      <td>NaN</td>
      <td>NaN</td>
      <td>NaN</td>
      <td>0</td>
    </tr>
    <tr>
      <th>Capital Expenditure</th>
      <td>-430,000,000</td>
      <td>-812,000,000</td>
      <td>-969,000,000</td>
      <td>-758,000,000</td>
    </tr>
    <tr>
      <th>Interest Paid Supplemental Data</th>
      <td>389,000,000</td>
      <td>381,000,000</td>
      <td>347,000,000</td>
      <td>290,000,000</td>
    </tr>
    <tr>
      <th>Income Tax Paid Supplemental Data</th>
      <td>1,226,000,000</td>
      <td>1,299,000,000</td>
      <td>1,517,000,000</td>
      <td>1,231,000,000</td>
    </tr>
    <tr>
      <th>End Cash Position</th>
      <td>7,464,000,000</td>
      <td>9,860,000,000</td>
      <td>7,441,000,000</td>
      <td>8,574,000,000</td>
    </tr>
    <tr>
      <th>Beginning Cash Position</th>
      <td>9,860,000,000</td>
      <td>7,441,000,000</td>
      <td>8,574,000,000</td>
      <td>9,889,000,000</td>
    </tr>
    <tr>
      <th>Effect Of Exchange Rate Changes</th>
      <td>1,000,000</td>
      <td>-16,000,000</td>
      <td>-91,000,000</td>
      <td>-143,000,000</td>
    </tr>
    <tr>
      <th>Changes In Cash</th>
      <td>-2,397,000,000</td>
      <td>2,435,000,000</td>
      <td>-1,042,000,000</td>
      <td>-1,172,000,000</td>
    </tr>
    <tr>
      <th>Financing Cash Flow</th>
      <td>-5,820,000,000</td>
      <td>-5,888,000,000</td>
      <td>-7,447,000,000</td>
      <td>-4,836,000,000</td>
    </tr>
    <tr>
      <th>Cash Flow From Continuing Financing Activities</th>
      <td>-5,820,000,000</td>
      <td>-5,888,000,000</td>
      <td>-7,447,000,000</td>
      <td>-4,836,000,000</td>
    </tr>
    <tr>
      <th>Net Other Financing Charges</th>
      <td>-85,000,000</td>
      <td>-136,000,000</td>
      <td>-102,000,000</td>
      <td>-151,000,000</td>
    </tr>
    <tr>
      <th>Proceeds From Stock Option Exercised</th>
      <td>551,000,000</td>
      <td>667,000,000</td>
      <td>651,000,000</td>
      <td>1,151,000,000</td>
    </tr>
    <tr>
      <th>Cash Dividends Paid</th>
      <td>-2,300,000,000</td>
      <td>-2,169,000,000</td>
      <td>-2,012,000,000</td>
      <td>-1,837,000,000</td>
    </tr>
    <tr>
      <th>Common Stock Dividend Paid</th>
      <td>-2,300,000,000</td>
      <td>-2,169,000,000</td>
      <td>-2,012,000,000</td>
      <td>-1,837,000,000</td>
    </tr>
    <tr>
      <th>Net Common Stock Issuance</th>
      <td>-2,985,000,000</td>
      <td>-4,250,000,000</td>
      <td>-5,480,000,000</td>
      <td>-4,014,000,000</td>
    </tr>
    <tr>
      <th>Common Stock Payments</th>
      <td>-2,985,000,000</td>
      <td>-4,250,000,000</td>
      <td>-5,480,000,000</td>
      <td>-4,014,000,000</td>
    </tr>
    <tr>
      <th>Net Issuance Payments Of Debt</th>
      <td>-1,001,000,000</td>
      <td>0</td>
      <td>-504,000,000</td>
      <td>15,000,000</td>
    </tr>
    <tr>
      <th>Net Short Term Debt Issuance</th>
      <td>-1,000,000</td>
      <td>0</td>
      <td>-4,000,000</td>
      <td>15,000,000</td>
    </tr>
    <tr>
      <th>Net Long Term Debt Issuance</th>
      <td>-1,000,000,000</td>
      <td>0</td>
      <td>-500,000,000</td>
      <td>0</td>
    </tr>
    <tr>
      <th>Long Term Debt Payments</th>
      <td>-1,000,000,000</td>
      <td>0</td>
      <td>-500,000,000</td>
      <td>0</td>
    </tr>
    <tr>
      <th>Long Term Debt Issuance</th>
      <td>NaN</td>
      <td>NaN</td>
      <td>NaN</td>
      <td>0</td>
    </tr>
    <tr>
      <th>Investing Cash Flow</th>
      <td>-275,000,000</td>
      <td>894,000,000</td>
      <td>564,000,000</td>
      <td>-1,524,000,000</td>
    </tr>
    <tr>
      <th>Cash Flow From Continuing Investing Activities</th>
      <td>-275,000,000</td>
      <td>894,000,000</td>
      <td>564,000,000</td>
      <td>-1,524,000,000</td>
    </tr>
    <tr>
      <th>Net Other Investing Changes</th>
      <td>8,000,000</td>
      <td>-15,000,000</td>
      <td>52,000,000</td>
      <td>-19,000,000</td>
    </tr>
    <tr>
      <th>Net Investment Purchase And Sale</th>
      <td>147,000,000</td>
      <td>1,721,000,000</td>
      <td>1,481,000,000</td>
      <td>-747,000,000</td>
    </tr>
    <tr>
      <th>Sale Of Investment</th>
      <td>3,381,000,000</td>
      <td>6,488,000,000</td>
      <td>7,540,000,000</td>
      <td>12,166,000,000</td>
    </tr>
    <tr>
      <th>Purchase Of Investment</th>
      <td>-3,234,000,000</td>
      <td>-4,767,000,000</td>
      <td>-6,059,000,000</td>
      <td>-12,913,000,000</td>
    </tr>
    <tr>
      <th>Net PPE Purchase And Sale</th>
      <td>-430,000,000</td>
      <td>-812,000,000</td>
      <td>-969,000,000</td>
      <td>-758,000,000</td>
    </tr>
    <tr>
      <th>Purchase Of PPE</th>
      <td>-430,000,000</td>
      <td>-812,000,000</td>
      <td>-969,000,000</td>
      <td>-758,000,000</td>
    </tr>
    <tr>
      <th>Operating Cash Flow</th>
      <td>3,698,000,000</td>
      <td>7,429,000,000</td>
      <td>5,841,000,000</td>
      <td>5,188,000,000</td>
    </tr>
    <tr>
      <th>Cash Flow From Continuing Operating Activities</th>
      <td>3,698,000,000</td>
      <td>7,429,000,000</td>
      <td>5,841,000,000</td>
      <td>5,188,000,000</td>
    </tr>
    <tr>
      <th>Change In Working Capital</th>
      <td>-787,000,000</td>
      <td>716,000,000</td>
      <td>-513,000,000</td>
      <td>-1,660,000,000</td>
    </tr>
    <tr>
      <th>Change In Payables And Accrued Expense</th>
      <td>-426,000,000</td>
      <td>397,000,000</td>
      <td>-225,000,000</td>
      <td>1,365,000,000</td>
    </tr>
    <tr>
      <th>Change In Payable</th>
      <td>-426,000,000</td>
      <td>397,000,000</td>
      <td>-225,000,000</td>
      <td>1,365,000,000</td>
    </tr>
    <tr>
      <th>Change In Account Payable</th>
      <td>-426,000,000</td>
      <td>397,000,000</td>
      <td>-225,000,000</td>
      <td>1,365,000,000</td>
    </tr>
    <tr>
      <th>Change In Prepaid Assets</th>
      <td>-224,000,000</td>
      <td>-260,000,000</td>
      <td>-644,000,000</td>
      <td>-845,000,000</td>
    </tr>
    <tr>
      <th>Change In Inventory</th>
      <td>120,000,000</td>
      <td>908,000,000</td>
      <td>-133,000,000</td>
      <td>-1,676,000,000</td>
    </tr>
    <tr>
      <th>Change In Receivables</th>
      <td>-257,000,000</td>
      <td>-329,000,000</td>
      <td>489,000,000</td>
      <td>-504,000,000</td>
    </tr>
    <tr>
      <th>Changes In Account Receivables</th>
      <td>-257,000,000</td>
      <td>-329,000,000</td>
      <td>489,000,000</td>
      <td>-504,000,000</td>
    </tr>
    <tr>
      <th>Stock Based Compensation</th>
      <td>709,000,000</td>
      <td>804,000,000</td>
      <td>755,000,000</td>
      <td>638,000,000</td>
    </tr>
    <tr>
      <th>Deferred Tax</th>
      <td>-288,000,000</td>
      <td>-497,000,000</td>
      <td>-117,000,000</td>
      <td>-650,000,000</td>
    </tr>
    <tr>
      <th>Deferred Income Tax</th>
      <td>-288,000,000</td>
      <td>-497,000,000</td>
      <td>-117,000,000</td>
      <td>-650,000,000</td>
    </tr>
    <tr>
      <th>Depreciation Amortization Depletion</th>
      <td>808,000,000</td>
      <td>844,000,000</td>
      <td>859,000,000</td>
      <td>840,000,000</td>
    </tr>
    <tr>
      <th>Depreciation And Amortization</th>
      <td>808,000,000</td>
      <td>844,000,000</td>
      <td>859,000,000</td>
      <td>840,000,000</td>
    </tr>
    <tr>
      <th>Amortization Cash Flow</th>
      <td>33,000,000</td>
      <td>48,000,000</td>
      <td>156,000,000</td>
      <td>123,000,000</td>
    </tr>
    <tr>
      <th>Amortization Of Intangibles</th>
      <td>33,000,000</td>
      <td>48,000,000</td>
      <td>156,000,000</td>
      <td>123,000,000</td>
    </tr>
    <tr>
      <th>Depreciation</th>
      <td>775,000,000</td>
      <td>796,000,000</td>
      <td>703,000,000</td>
      <td>717,000,000</td>
    </tr>
    <tr>
      <th>Operating Gains Losses</th>
      <td>37,000,000</td>
      <td>-138,000,000</td>
      <td>-213,000,000</td>
      <td>-26,000,000</td>
    </tr>
    <tr>
      <th>Net Foreign Currency Exchange Gain Loss</th>
      <td>37,000,000</td>
      <td>-138,000,000</td>
      <td>-213,000,000</td>
      <td>-26,000,000</td>
    </tr>
    <tr>
      <th>Net Income From Continuing Operations</th>
      <td>3,219,000,000</td>
      <td>5,700,000,000</td>
      <td>5,070,000,000</td>
      <td>6,046,000,000</td>
    </tr>
  </tbody>
</table>
</div>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">annual_balance_sheet</span> <span class="o">=</span> <span class="n">ticker</span><span class="p">.</span><span class="n">balance_sheet</span>
<span class="n">annual_balance_sheet</span> <span class="o">=</span> <span class="n">annual_balance_sheet</span><span class="p">.</span><span class="n">fillna</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="n">annual_balance_sheet</span>
</code></pre></div></div>

<div>
<style scoped="">
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>2025-05-31</th>
      <th>2024-05-31</th>
      <th>2023-05-31</th>
      <th>2022-05-31</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>Ordinary Shares Number</th>
      <td>1,476,000,000</td>
      <td>1,503,000,000</td>
      <td>1,532,000,000</td>
      <td>1,571,000,000</td>
    </tr>
    <tr>
      <th>Share Issued</th>
      <td>1,476,000,000</td>
      <td>1,503,000,000</td>
      <td>1,532,000,000</td>
      <td>1,571,000,000</td>
    </tr>
    <tr>
      <th>Net Debt</th>
      <td>502,000,000</td>
      <td>0</td>
      <td>1,492,000,000</td>
      <td>856,000,000</td>
    </tr>
    <tr>
      <th>Total Debt</th>
      <td>11,018,000,000</td>
      <td>11,952,000,000</td>
      <td>12,144,000,000</td>
      <td>12,627,000,000</td>
    </tr>
    <tr>
      <th>Tangible Book Value</th>
      <td>12,714,000,000</td>
      <td>13,931,000,000</td>
      <td>13,449,000,000</td>
      <td>14,711,000,000</td>
    </tr>
    <tr>
      <th>...</th>
      <td>...</td>
      <td>...</td>
      <td>...</td>
      <td>...</td>
    </tr>
    <tr>
      <th>Cash Cash Equivalents And Short Term Investments</th>
      <td>9,151,000,000</td>
      <td>11,582,000,000</td>
      <td>10,675,000,000</td>
      <td>12,997,000,000</td>
    </tr>
    <tr>
      <th>Other Short Term Investments</th>
      <td>1,687,000,000</td>
      <td>1,722,000,000</td>
      <td>3,234,000,000</td>
      <td>4,423,000,000</td>
    </tr>
    <tr>
      <th>Cash And Cash Equivalents</th>
      <td>7,464,000,000</td>
      <td>9,860,000,000</td>
      <td>7,441,000,000</td>
      <td>8,574,000,000</td>
    </tr>
    <tr>
      <th>Cash Equivalents</th>
      <td>6,243,000,000</td>
      <td>8,638,000,000</td>
      <td>5,674,000,000</td>
      <td>7,735,000,000</td>
    </tr>
    <tr>
      <th>Cash Financial</th>
      <td>1,221,000,000</td>
      <td>1,222,000,000</td>
      <td>1,767,000,000</td>
      <td>839,000,000</td>
    </tr>
  </tbody>
</table>
<p>75 rows × 4 columns</p>
</div>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># FCFF Calculation using Cash Flow Statement and Income Statement Inputs
</span><span class="n">free_cash_flow_firm</span> <span class="o">=</span> <span class="p">(</span><span class="n">cash_flow_df</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="s">'Free Cash Flow'</span><span class="p">].</span><span class="n">astype</span><span class="p">(</span><span class="s">'int64'</span><span class="p">))</span> \
                    <span class="o">+</span> <span class="p">(</span><span class="n">income_statement_df</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="s">'Net Non Operating Interest Income Expense'</span><span class="p">].</span><span class="n">astype</span><span class="p">(</span><span class="s">'int64'</span><span class="p">)</span> \
                        <span class="o">*</span> <span class="p">(</span><span class="mi">1</span> <span class="o">-</span> <span class="n">income_statement_df</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="s">'Tax Provision'</span><span class="p">].</span><span class="n">astype</span><span class="p">(</span><span class="s">'int64'</span><span class="p">)</span> \
                           <span class="o">/</span> <span class="n">income_statement_df</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="s">'Pretax Income'</span><span class="p">].</span><span class="n">astype</span><span class="p">(</span><span class="s">'int64'</span><span class="p">))).</span><span class="n">astype</span><span class="p">(</span><span class="s">'int64'</span><span class="p">)</span>

<span class="c1"># Change Series to a Pandas Dataframe
</span><span class="n">free_cash_flow_firm_df</span> <span class="o">=</span> <span class="n">free_cash_flow_firm</span><span class="p">.</span><span class="n">to_frame</span><span class="p">().</span><span class="n">transpose</span><span class="p">()</span>
<span class="k">print</span><span class="p">(</span><span class="n">free_cash_flow_firm_df</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">cash_flow_df</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="s">'Free Cash Flow'</span><span class="p">][</span><span class="mi">0</span><span class="p">])</span>
<span class="k">print</span><span class="p">(</span><span class="n">income_statement_df</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="s">'Net Non Operating Interest Income Expense'</span><span class="p">][</span><span class="mi">0</span><span class="p">])</span>
<span class="k">print</span><span class="p">(</span><span class="n">income_statement_df</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="s">'Tax Provision'</span><span class="p">][</span><span class="mi">0</span><span class="p">])</span>
<span class="k">print</span><span class="p">(</span><span class="n">income_statement_df</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="s">'Pretax Income'</span><span class="p">][</span><span class="mi">0</span><span class="p">])</span>
<span class="k">print</span><span class="p">((</span><span class="mi">1</span> <span class="o">-</span> <span class="n">income_statement_df</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="s">'Tax Provision'</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span> \
                           <span class="o">/</span> <span class="n">income_statement_df</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="s">'Pretax Income'</span><span class="p">][</span><span class="mi">0</span><span class="p">]))</span>
<span class="k">print</span><span class="p">(</span><span class="n">income_statement_df</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="s">'Net Non Operating Interest Income Expense'</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span> <span class="o">*</span><span class="p">(</span><span class="mi">1</span> <span class="o">-</span> <span class="n">income_statement_df</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="s">'Tax Provision'</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span> \
                           <span class="o">/</span> <span class="n">income_statement_df</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="s">'Pretax Income'</span><span class="p">][</span><span class="mi">0</span><span class="p">]))</span>
</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>   2025-05-31  2024-05-31  2023-05-31  2022-05-31
0  3356657142  6753970149  4876905660  4243647572
3268000000.0
107000000.0
666000000.0
3885000000.0
0.8285714285714285
88657142.85714285
</code></pre></div></div>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># CAGR of FCFF
</span><span class="n">latest_free_cash_flow_firm</span> <span class="o">=</span> <span class="nb">float</span><span class="p">(</span><span class="n">free_cash_flow_firm_df</span><span class="p">.</span><span class="n">iloc</span><span class="p">[</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">])</span>
<span class="n">earliest_free_cash_flow_firm</span> <span class="o">=</span> <span class="nb">float</span><span class="p">(</span><span class="n">free_cash_flow_firm_df</span><span class="p">.</span><span class="n">iloc</span><span class="p">[</span><span class="mi">0</span><span class="p">,</span><span class="nb">len</span><span class="p">(</span><span class="n">free_cash_flow_firm_df</span><span class="p">.</span><span class="n">columns</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span><span class="p">])</span>
<span class="n">free_cash_flow_firm_CAGR</span> <span class="o">=</span> <span class="p">((</span><span class="n">latest_free_cash_flow_firm</span><span class="o">/</span><span class="n">earliest_free_cash_flow_firm</span><span class="p">)</span>\
                            <span class="o">**</span><span class="p">(</span><span class="nb">float</span><span class="p">(</span><span class="mi">1</span><span class="o">/</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">free_cash_flow_firm_df</span><span class="p">.</span><span class="n">columns</span><span class="p">)))))</span><span class="o">-</span><span class="mi">1</span>


<span class="k">print</span><span class="p">(</span><span class="n">latest_free_cash_flow_firm</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">earliest_free_cash_flow_firm</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">free_cash_flow_firm_CAGR</span><span class="p">)</span>

</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>3356657142.0
4243647572.0
-0.0569343672541337
</code></pre></div></div>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Perpetual (terminal) growth rate.
# The historical FCFF CAGR can be negative, which implies the firm shrinks
# forever - not a valid going-concern assumption. Use a conservative long-run
# rate (~ long-run GDP / inflation) instead.
</span><span class="n">long_term_growth</span> <span class="o">=</span> <span class="mf">0.025</span>
<span class="n">long_term_growth</span>
</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0.025
</code></pre></div></div>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Forecasted FCFF
</span><span class="n">forecast_free_cash_flow_firm_df</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="n">DataFrame</span><span class="p">(</span><span class="n">columns</span><span class="o">=</span><span class="p">[</span><span class="s">'Year '</span> <span class="o">+</span> <span class="nb">str</span><span class="p">(</span><span class="n">i</span><span class="p">)</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="mi">6</span><span class="p">)])</span>
<span class="n">free_cash_flow_firm_forecast_lst</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="mi">6</span><span class="p">):</span>
    <span class="k">if</span> <span class="n">i</span> <span class="o">!=</span> <span class="mi">5</span><span class="p">:</span>
        <span class="n">free_cash_flow_firm_forecast</span> <span class="o">=</span> <span class="n">latest_free_cash_flow_firm</span><span class="o">*</span><span class="p">(</span><span class="mi">1</span><span class="o">+</span><span class="n">free_cash_flow_firm_CAGR</span><span class="p">)</span><span class="o">**</span><span class="n">i</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="n">free_cash_flow_firm_forecast</span> <span class="o">=</span> <span class="n">latest_free_cash_flow_firm</span><span class="o">*</span><span class="p">(</span><span class="mi">1</span><span class="o">+</span><span class="n">free_cash_flow_firm_CAGR</span><span class="p">)</span>\
                                        <span class="o">**</span><span class="p">(</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span><span class="o">*</span><span class="p">(</span><span class="mi">1</span><span class="o">+</span><span class="n">long_term_growth</span><span class="p">)</span>
    <span class="n">free_cash_flow_firm_forecast_lst</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="n">free_cash_flow_firm_forecast</span><span class="p">))</span>
<span class="n">forecast_free_cash_flow_firm_df</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">free_cash_flow_firm_forecast_lst</span>
<span class="n">forecast_free_cash_flow_firm_df</span>
</code></pre></div></div>

<div>
<style scoped="">
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>Year 1</th>
      <th>Year 2</th>
      <th>Year 3</th>
      <th>Year 4</th>
      <th>Year 5</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>3165547991</td>
      <td>2985319519</td>
      <td>2815352241</td>
      <td>2655061943</td>
      <td>2721438491</td>
    </tr>
  </tbody>
</table>
</div>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Risk-free Rate
</span><span class="n">timespan</span> <span class="o">=</span> <span class="mi">100</span>
<span class="n">current_date</span> <span class="o">=</span> <span class="n">datetime</span><span class="p">.</span><span class="n">date</span><span class="p">.</span><span class="n">today</span><span class="p">()</span>
<span class="n">formatted_date</span> <span class="o">=</span> <span class="n">current_date</span><span class="p">.</span><span class="n">strftime</span><span class="p">(</span><span class="s">'%Y-%m-%d'</span><span class="p">)</span>
<span class="n">past_date</span> <span class="o">=</span> <span class="n">current_date</span><span class="o">-</span><span class="n">datetime</span><span class="p">.</span><span class="n">timedelta</span><span class="p">(</span><span class="n">days</span><span class="o">=</span><span class="n">timespan</span><span class="p">)</span>
<span class="n">formatted_past_date</span> <span class="o">=</span> <span class="n">past_date</span><span class="p">.</span><span class="n">strftime</span><span class="p">(</span><span class="s">'%Y-%m-%d'</span><span class="p">)</span>
<span class="n">tk</span> <span class="o">=</span> <span class="n">yf</span><span class="p">.</span><span class="n">Ticker</span><span class="p">(</span><span class="s">'^TNX'</span><span class="p">)</span>
<span class="n">risk_free_rate_df</span> <span class="o">=</span> <span class="n">tk</span><span class="p">.</span><span class="n">history</span><span class="p">(</span><span class="n">period</span><span class="o">=</span><span class="s">'3mo'</span><span class="p">)</span>
<span class="n">risk_free_rate</span> <span class="o">=</span> <span class="p">(</span><span class="n">risk_free_rate_df</span><span class="p">.</span><span class="n">iloc</span><span class="p">[</span><span class="nb">len</span><span class="p">(</span><span class="n">risk_free_rate_df</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span><span class="mi">3</span><span class="p">])</span><span class="o">/</span><span class="mi">100</span>
<span class="n">risk_free_rate</span>

</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>c:\Users\Admin\anaconda3\lib\site-packages\yfinance\scrapers\history.py:396: FutureWarning: The default dtype for empty Series will be 'object' instead of 'float64' in a future version. Specify a dtype explicitly to silence this warning.
  self._capital_gains = pd.Series()





0.04484999656677246
</code></pre></div></div>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">requests</span>
<span class="kn">from</span> <span class="nn">bs4</span> <span class="kn">import</span> <span class="n">BeautifulSoup</span>

<span class="k">def</span> <span class="nf">finviz_fundament</span><span class="p">(</span><span class="n">symbol</span><span class="p">):</span>
    <span class="c1"># Finviz blocks non-browser requests and splits fundamentals across
</span>    <span class="c1"># multiple snapshot-table2 tables, so use a browser UA and aggregate them.
</span>    <span class="n">url</span> <span class="o">=</span> <span class="sa">f</span><span class="s">"https://finviz.com/quote.ashx?t=</span><span class="si">{</span><span class="n">symbol</span><span class="si">}</span><span class="s">"</span>
    <span class="n">headers</span> <span class="o">=</span> <span class="p">{</span><span class="s">"User-Agent"</span><span class="p">:</span> <span class="s">"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "</span>
                             <span class="s">"AppleWebKit/537.36 (KHTML, like Gecko) "</span>
                             <span class="s">"Chrome/126.0 Safari/537.36"</span><span class="p">}</span>
    <span class="n">resp</span> <span class="o">=</span> <span class="n">requests</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">headers</span><span class="o">=</span><span class="n">headers</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="mi">15</span><span class="p">)</span>
    <span class="n">resp</span><span class="p">.</span><span class="n">raise_for_status</span><span class="p">()</span>
    <span class="n">soup</span> <span class="o">=</span> <span class="n">BeautifulSoup</span><span class="p">(</span><span class="n">resp</span><span class="p">.</span><span class="n">text</span><span class="p">,</span> <span class="s">"lxml"</span><span class="p">)</span>
    <span class="n">cells</span> <span class="o">=</span> <span class="p">[]</span>
    <span class="k">for</span> <span class="n">table</span> <span class="ow">in</span> <span class="n">soup</span><span class="p">.</span><span class="n">find_all</span><span class="p">(</span><span class="s">"table"</span><span class="p">,</span> <span class="n">class_</span><span class="o">=</span><span class="s">"snapshot-table2"</span><span class="p">):</span>
        <span class="n">cells</span> <span class="o">+=</span> <span class="p">[</span><span class="n">td</span><span class="p">.</span><span class="n">text</span> <span class="k">for</span> <span class="n">td</span> <span class="ow">in</span> <span class="n">table</span><span class="p">.</span><span class="n">find_all</span><span class="p">(</span><span class="s">"td"</span><span class="p">)]</span>
    <span class="k">return</span> <span class="p">{</span><span class="n">cells</span><span class="p">[</span><span class="n">i</span><span class="p">]:</span> <span class="n">cells</span><span class="p">[</span><span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">]</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">cells</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">)}</span>

<span class="n">tk</span> <span class="o">=</span> <span class="n">finviz_fundament</span><span class="p">(</span><span class="s">'NKE'</span><span class="p">)</span>
<span class="n">tk</span>
</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>{'Index': 'DJIA, S&amp;P 500',
 'Market Cap': '65.29B',
 'Enterprise Value': '67.30B',
 'Income': '3.11B',
 'Sales': '46.40B',
 'Book/sh': '9.52',
 'Cash/sh': '6.10',
 'Dividend Est.': '1.66 (3.76%)',
 'Dividend TTM': '1.63 (3.70%)',
 'Dividend Ex-Date': 'Jun 01, 2026',
 'Dividend Gr. 3/5Y': '7.28% 8.78%',
 'Payout': '77.67%',
 'Employees': '77800',
 'IPO': 'Dec 02, 1980',
 'P/E': '21.01',
 'Forward P/E': '19.97',
 'PEG': '4.16',
 'P/S': '1.41',
 'P/B': '4.63',
 'P/C': '7.23',
 'P/FCF': '62.30',
 'EV/EBITDA': '18.45',
 'EV/Sales': '1.45',
 'Quick Ratio': '1.36',
 'Current Ratio': '1.96',
 'Debt/Eq': '0.74',
 'LT Debt/Eq': '0.58',
 'Option/Short': 'Yes / Yes',
 'EPS (ttm)': '2.10',
 'EPS next Y': '25.94%',
 'EPS next Q': '0.44',
 'EPS this Y': '-16.54%',
 'EPS next 5Y': '4.80%',
 'EPS past 3/5Y': '-13.39% -10.02%',
 'Sales past 3/5Y': '-3.22% 0.84%',
 'EPS Y/Y TTM': '-2.96%',
 'Sales Y/Y TTM': '-0.01%',
 'EPS Q/Q': '404.83%',
 'Sales Q/Q': '-1.33%',
 'Earnings': 'Jun 30 AMC',
 'EPS/Sales Surpr.': '479.24% 1.13%',
 'Insider Own': '21.06%',
 'Insider Trans': '0.01%',
 'Inst Own': '66.28%',
 'Inst Trans': '-1.43%',
 'ROA': '8.29%',
 'ROE': '22.14%',
 'ROIC': '13.27%',
 'Gross Margin': '44.06%',
 'Oper. Margin': '8.56%',
 'Profit Margin': '6.70%',
 'SMA20': '1.63%',
 'SMA50': '0.57%',
 'SMA200': '-23.08%',
 'Trades': '\n\n',
 'Shs Outstand': '1.48B',
 'Shs Float': '1.17B',
 'Short Float': '4.88%',
 'Short Ratio': '2.28',
 'Short Interest': '57.04M',
 '52W High': '80.17 -45.00%',
 '52W Low': '40.00 10.23%',
 'Volatility': '3.79% 3.25%',
 'ATR (14)': '1.49',
 'RSI (14)': '52.93',
 'Beta': '1.11',
 'Rel Volume': '1.25',
 'Avg Volume': '25.00M',
 'Volume': '31,232,182',
 'Perf Week': '7.80%',
 'Perf Month': '0.82%',
 'Perf Quarter': '-1.21%',
 'Perf Half Y': '-27.95%',
 'Perf YTD': '-30.80%',
 'Perf Year': '-42.28%',
 'Perf 3Y': '-60.05%',
 'Perf 5Y': '-72.40%',
 'Perf 10Y': '-20.72%',
 'Recom': '2.49',
 'Target Price': '50.32',
 'Prev Close': '43.06',
 'Price': '44.09',
 'Change': '2.39%'}
</code></pre></div></div>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">beta</span> <span class="o">=</span> <span class="nb">float</span><span class="p">(</span><span class="n">tk</span><span class="p">[</span><span class="s">'Beta'</span><span class="p">])</span> <span class="c1"># float is for decimal and int is for integer
</span><span class="n">beta</span>
</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>1.11
</code></pre></div></div>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">market_risk_premium</span> <span class="o">=</span> <span class="p">(</span><span class="mf">0.10</span><span class="o">-</span><span class="n">risk_free_rate</span><span class="p">)</span>
<span class="n">market_risk_premium</span>
</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0.05515000343322755
</code></pre></div></div>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Required Cost of Equity
</span>
<span class="n">coe</span> <span class="o">=</span> <span class="n">risk_free_rate</span> <span class="o">+</span> <span class="p">(</span><span class="n">beta</span><span class="o">*</span><span class="n">market_risk_premium</span><span class="p">)</span>

<span class="n">coe</span>
</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0.10606650037765504
</code></pre></div></div>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">interest_expense</span> <span class="o">=</span> <span class="n">income_statement_df</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="s">'Net Non Operating Interest Income Expense'</span><span class="p">]</span>
<span class="n">interest_expense_df</span> <span class="o">=</span> <span class="n">interest_expense</span><span class="p">.</span><span class="n">to_frame</span><span class="p">().</span><span class="n">transpose</span><span class="p">()</span>
<span class="n">interest_expense_str</span> <span class="o">=</span> <span class="n">interest_expense_df</span><span class="p">.</span><span class="n">values</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span>
<span class="n">interest_expense_int</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">interest_expense_str</span><span class="p">)</span>


<span class="c1"># Total Debt
</span><span class="n">total_debt</span> <span class="o">=</span> <span class="n">balance_sheet_df</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="s">'Total Debt'</span><span class="p">]</span>
<span class="n">total_debt_df</span> <span class="o">=</span> <span class="n">total_debt</span><span class="p">.</span><span class="n">to_frame</span><span class="p">().</span><span class="n">transpose</span><span class="p">()</span>
<span class="n">total_debt_str</span> <span class="o">=</span> <span class="n">total_debt_df</span><span class="p">.</span><span class="n">values</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span>
<span class="n">total_debt_int</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">total_debt_str</span><span class="p">)</span>

<span class="c1"># Required Cost of Debt
</span><span class="n">cod</span> <span class="o">=</span> <span class="n">interest_expense_int</span> <span class="o">/</span> <span class="n">total_debt_int</span>


<span class="k">print</span><span class="p">(</span><span class="n">interest_expense_str</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">total_debt_df</span><span class="p">)</span>

<span class="k">print</span><span class="p">(</span><span class="n">interest_expense_int</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">total_debt_int</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">cod</span><span class="p">)</span>
</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>107000000.0
               2026-02-28     2025-11-30     2025-08-31     2025-05-31  \
Total Debt 11,178,000,000 11,282,000,000 11,061,000,000 11,018,000,000   

               2025-02-28  2024-11-30  
Total Debt 11,911,000,000         NaN  
107000000
11178000000
0.009572374306673823
</code></pre></div></div>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Effective Tax Rate
</span><span class="n">effective_tax_rate</span> <span class="o">=</span> <span class="n">income_statement_df</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="s">'Tax Provision'</span><span class="p">].</span><span class="n">astype</span><span class="p">(</span><span class="s">'int64'</span><span class="p">)</span> \
                           <span class="o">/</span> <span class="n">income_statement_df</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="s">'Pretax Income'</span><span class="p">].</span><span class="n">astype</span><span class="p">(</span><span class="s">'int64'</span><span class="p">)</span>

<span class="k">print</span><span class="p">(</span><span class="n">income_statement_df</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="s">'Tax Provision'</span><span class="p">].</span><span class="n">astype</span><span class="p">(</span><span class="s">'int64'</span><span class="p">))</span>
<span class="k">print</span><span class="p">(</span><span class="n">income_statement_df</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="s">'Pretax Income'</span><span class="p">].</span><span class="n">astype</span><span class="p">(</span><span class="s">'int64'</span><span class="p">))</span>
<span class="k">print</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">effective_tax_rate</span><span class="p">))</span>
<span class="k">print</span><span class="p">(</span><span class="nb">sum</span><span class="p">(</span><span class="n">effective_tax_rate</span><span class="p">))</span>
<span class="n">avg_effective_tax_rate</span> <span class="o">=</span> <span class="nb">sum</span><span class="p">(</span><span class="n">effective_tax_rate</span><span class="p">)</span> <span class="o">/</span> <span class="nb">len</span><span class="p">(</span><span class="n">effective_tax_rate</span><span class="p">)</span>
<span class="n">avg_effective_tax_rate</span>
</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>2025-05-31     666000000
2024-05-31    1000000000
2023-05-31    1131000000
2022-05-31     605000000
Name: Tax Provision, dtype: int64
2025-05-31    3885000000
2024-05-31    6700000000
2023-05-31    6201000000
2022-05-31    6651000000
Name: Pretax Income, dtype: int64
4
0.5940360047261646





0.14850900118154114
</code></pre></div></div>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">market_cap_str</span> <span class="o">=</span> <span class="n">tk</span><span class="p">[</span><span class="s">'Market Cap'</span><span class="p">]</span>

<span class="n">market_cap_lst</span> <span class="o">=</span> <span class="n">market_cap_str</span><span class="p">.</span><span class="n">split</span><span class="p">(</span><span class="s">'.'</span><span class="p">)</span>
<span class="k">if</span> <span class="n">market_cap_str</span><span class="p">[</span><span class="nb">len</span><span class="p">(</span><span class="n">market_cap_str</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="s">'T'</span><span class="p">:</span>
    <span class="n">market_cap_length</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">market_cap_lst</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span><span class="o">-</span><span class="mi">1</span>
    <span class="n">market_cap_lst</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="n">market_cap_lst</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="n">replace</span><span class="p">(</span><span class="s">'T'</span><span class="p">,(</span><span class="mi">12</span><span class="o">-</span><span class="n">market_cap_length</span><span class="p">)</span><span class="o">*</span><span class="s">'0'</span><span class="p">)</span>
    <span class="n">market_cap_int</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="s">''</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="n">market_cap_lst</span><span class="p">))</span>
<span class="k">if</span> <span class="n">market_cap_str</span><span class="p">[</span><span class="nb">len</span><span class="p">(</span><span class="n">market_cap_str</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="s">'B'</span><span class="p">:</span>
    <span class="n">market_cap_length</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">market_cap_lst</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span><span class="o">-</span><span class="mi">1</span>
    <span class="n">market_cap_lst</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="n">market_cap_lst</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="n">replace</span><span class="p">(</span><span class="s">'B'</span><span class="p">,(</span><span class="mi">9</span><span class="o">-</span><span class="n">market_cap_length</span><span class="p">)</span><span class="o">*</span><span class="s">'0'</span><span class="p">)</span>
    <span class="n">market_cap_int</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="s">''</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="n">market_cap_lst</span><span class="p">))</span>

<span class="n">market_cap_int</span>
</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>65290000000
</code></pre></div></div>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">last_cf</span> <span class="o">=</span> <span class="n">cash_flow_df</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="s">'End Cash Position'</span><span class="p">]</span>
<span class="n">last_cf_df</span> <span class="o">=</span> <span class="n">last_cf</span><span class="p">.</span><span class="n">to_frame</span><span class="p">().</span><span class="n">transpose</span><span class="p">()</span>
<span class="n">last_cf_str</span> <span class="o">=</span> <span class="n">last_cf_df</span><span class="p">.</span><span class="n">values</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span>
<span class="n">last_cf_int</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">last_cf_str</span><span class="p">)</span>

<span class="n">last_equity</span> <span class="o">=</span> <span class="n">balance_sheet_df</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="s">'Total Equity Gross Minority Interest'</span><span class="p">]</span>
<span class="n">last_equity_df</span> <span class="o">=</span> <span class="n">last_equity</span><span class="p">.</span><span class="n">to_frame</span><span class="p">().</span><span class="n">transpose</span><span class="p">()</span>
<span class="n">last_equity_str</span> <span class="o">=</span> <span class="n">last_equity_df</span><span class="p">.</span><span class="n">values</span><span class="p">[</span><span class="mi">0</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span>
<span class="n">last_equity_int</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">last_equity_str</span><span class="p">)</span>


<span class="n">enterprise_value</span> <span class="o">=</span> <span class="n">market_cap_int</span> <span class="o">+</span> <span class="n">total_debt_int</span> <span class="o">-</span> <span class="n">last_cf_int</span>

<span class="k">print</span><span class="p">(</span><span class="n">last_cf_int</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">last_equity_int</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">enterprise_value</span><span class="p">)</span>

</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>7464000000
14090000000
69004000000
</code></pre></div></div>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">WACC</span> <span class="o">=</span> <span class="p">((</span><span class="n">last_equity_int</span><span class="o">/</span><span class="p">(</span><span class="n">last_equity_int</span> <span class="o">+</span> <span class="n">total_debt_int</span><span class="p">))</span> <span class="o">*</span> <span class="n">coe</span><span class="p">)</span> \
        <span class="o">+</span> <span class="p">((</span><span class="n">total_debt_int</span><span class="o">/</span><span class="p">(</span><span class="n">last_equity_int</span> <span class="o">+</span> <span class="n">total_debt_int</span><span class="p">))</span> <span class="o">*</span> <span class="n">cod</span> <span class="o">*</span> <span class="p">(</span><span class="mi">1</span><span class="o">-</span><span class="n">avg_effective_tax_rate</span><span class="p">))</span>

<span class="n">WACC</span>
</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0.0627507728033376
</code></pre></div></div>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Equity Value Calculation
</span><span class="n">discounted_FCFF_lst</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">year</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="mi">5</span><span class="p">):</span>
    <span class="n">discounted_FCFF</span> <span class="o">=</span> <span class="n">forecast_free_cash_flow_firm_df</span><span class="p">.</span><span class="n">iloc</span><span class="p">[</span><span class="mi">0</span><span class="p">,</span><span class="n">year</span><span class="p">]</span><span class="o">/</span><span class="p">(</span><span class="mi">1</span><span class="o">+</span><span class="n">WACC</span><span class="p">)</span><span class="o">**</span><span class="p">(</span><span class="n">year</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span>
    <span class="n">discounted_FCFF_lst</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="nb">int</span><span class="p">(</span><span class="n">discounted_FCFF</span><span class="p">))</span>
<span class="n">terminal_value</span> <span class="o">=</span> <span class="p">(</span><span class="n">forecast_free_cash_flow_firm_df</span><span class="p">.</span><span class="n">iloc</span><span class="p">[</span><span class="mi">0</span><span class="p">,</span><span class="mi">4</span><span class="p">]</span><span class="o">*</span><span class="p">(</span><span class="mi">1</span><span class="o">+</span><span class="n">long_term_growth</span><span class="p">))</span><span class="o">/</span><span class="p">(</span><span class="n">WACC</span><span class="o">-</span><span class="n">long_term_growth</span><span class="p">)</span>
<span class="n">PV_terminal_value</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">terminal_value</span><span class="o">/</span><span class="p">(</span><span class="mi">1</span><span class="o">+</span><span class="n">WACC</span><span class="p">)</span><span class="o">**</span><span class="mi">5</span><span class="p">)</span>
<span class="n">firm_value</span> <span class="o">=</span> <span class="nb">sum</span><span class="p">(</span><span class="n">discounted_FCFF_lst</span><span class="p">)</span><span class="o">+</span><span class="n">PV_terminal_value</span>
<span class="n">equity_value</span> <span class="o">=</span> <span class="p">(</span><span class="n">firm_value</span> <span class="o">-</span> <span class="n">total_debt_int</span> <span class="o">+</span> <span class="n">last_cf_int</span><span class="p">)</span>

<span class="k">print</span><span class="p">(</span><span class="n">discounted_FCFF_lst</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">terminal_value</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">forecast_free_cash_flow_firm_df</span><span class="p">.</span><span class="n">iloc</span><span class="p">[</span><span class="mi">0</span><span class="p">,</span><span class="mi">4</span><span class="p">])</span>
<span class="k">print</span><span class="p">(</span><span class="n">firm_value</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">equity_value</span><span class="p">)</span>
</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[2978636263, 2643187438, 2345516274, 2081368319, 2007434463]
73891850315.40277
2721438491
66561525614
62847525614
</code></pre></div></div>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Total Shares Outstanding
</span><span class="n">shares_outstanding_str</span> <span class="o">=</span> <span class="n">tk</span><span class="p">[</span><span class="s">'Shs Outstand'</span><span class="p">]</span>

<span class="n">shares_outstanding_lst</span> <span class="o">=</span> <span class="n">shares_outstanding_str</span><span class="p">.</span><span class="n">split</span><span class="p">(</span><span class="s">'.'</span><span class="p">)</span>
<span class="k">if</span> <span class="n">shares_outstanding_str</span><span class="p">[</span><span class="nb">len</span><span class="p">(</span><span class="n">shares_outstanding_str</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="s">'T'</span><span class="p">:</span>
    <span class="n">shares_outstanding_length</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">shares_outstanding_lst</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span><span class="o">-</span><span class="mi">1</span>
    <span class="n">shares_outstanding_lst</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="n">shares_outstanding_lst</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="n">replace</span><span class="p">(</span><span class="s">'T'</span><span class="p">,(</span><span class="mi">12</span><span class="o">-</span><span class="n">shares_outstanding_length</span><span class="p">)</span><span class="o">*</span><span class="s">'0'</span><span class="p">)</span>
    <span class="n">shares_outstanding_int</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="s">''</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="n">shares_outstanding_lst</span><span class="p">))</span>
<span class="k">if</span> <span class="n">shares_outstanding_str</span><span class="p">[</span><span class="nb">len</span><span class="p">(</span><span class="n">shares_outstanding_str</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="s">'B'</span><span class="p">:</span>
    <span class="n">shares_outstanding_length</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">shares_outstanding_lst</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span><span class="o">-</span><span class="mi">1</span>
    <span class="n">shares_outstanding_lst</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="n">shares_outstanding_lst</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="n">replace</span><span class="p">(</span><span class="s">'B'</span><span class="p">,(</span><span class="mi">9</span><span class="o">-</span><span class="n">shares_outstanding_length</span><span class="p">)</span><span class="o">*</span><span class="s">'0'</span><span class="p">)</span>
    <span class="n">shares_outstanding_int</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="s">''</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="n">shares_outstanding_lst</span><span class="p">))</span>
<span class="k">if</span> <span class="n">shares_outstanding_str</span><span class="p">[</span><span class="nb">len</span><span class="p">(</span><span class="n">shares_outstanding_str</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">==</span> <span class="s">'M'</span><span class="p">:</span>
    <span class="n">shares_outstanding_length</span> <span class="o">=</span> <span class="nb">len</span><span class="p">(</span><span class="n">shares_outstanding_lst</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span><span class="o">-</span><span class="mi">1</span>
    <span class="n">shares_outstanding_lst</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="n">shares_outstanding_lst</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="n">replace</span><span class="p">(</span><span class="s">'M'</span><span class="p">,(</span><span class="mi">6</span><span class="o">-</span><span class="n">shares_outstanding_length</span><span class="p">)</span><span class="o">*</span><span class="s">'0'</span><span class="p">)</span>
    <span class="n">shares_outstanding_int</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="s">''</span><span class="p">.</span><span class="n">join</span><span class="p">(</span><span class="n">shares_outstanding_lst</span><span class="p">))</span>

<span class="n">shares_outstanding_int</span>
</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>1480000000
</code></pre></div></div>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Two-stage FCFF Valuation Model Stock Price Estimate
</span><span class="n">stock_price</span> <span class="o">=</span> <span class="n">equity_value</span> <span class="o">/</span> <span class="n">shares_outstanding_int</span>
<span class="n">stock_price</span> <span class="o">=</span> <span class="s">'${:,.2f}'</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">stock_price</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s">"Model Stock Price = %s"</span><span class="o">%</span><span class="p">(</span><span class="n">stock_price</span><span class="p">))</span>

<span class="c1"># Actual Stock Price
</span><span class="n">actual_stock_price</span> <span class="o">=</span> <span class="n">market_cap_int</span> <span class="o">/</span> <span class="n">shares_outstanding_int</span>
<span class="n">actual_stock_price</span> <span class="o">=</span> <span class="s">'${:,.2f}'</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">actual_stock_price</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s">"Actual Stock Price = %s"</span><span class="o">%</span><span class="p">(</span><span class="n">actual_stock_price</span><span class="p">))</span>

</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Model Stock Price = $42.46
Actual Stock Price = $44.11
</code></pre></div></div>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>   Reportable Operating Segments - Schedule of Operating Segment Information (Details) - USD ($)  $ in Thousands 12 Months Ended                            
   Reportable Operating Segments - Schedule of Operating Segment Information (Details) - USD ($)  $ in Thousands   Mar. 31, 2026 Mar. 31, 2025 Mar. 31, 2024
0                                                                     Segment Reporting Information [Line Items]             NaN           NaN           NaN
1                                                                                                      Net sales     $ 5,472,296   $ 4,985,612   $ 4,287,763
2                                                                                            Less: Cost of sales         2314570       2099949       1902275
3                                                                                                   Gross profit     $ 3,157,726   $ 2,885,663   $ 2,385,488
4                                                                                           Segment gross margin          57.70%        57.90%        55.60%
5                                                                                                          Less:             NaN           NaN           NaN
6                                                                                      Payroll and related costs       $ 307,073     $ 265,328     $ 226,926
7                                                                 Advertising, marketing, and promotion expenses          495838        432198        348852
8                                                                                             Rent and occupancy          130571        102615         92661
9                                                                           Depreciation and other related costs           20184         19445         22718
10                                                                                           Other segment items          231384        180121        146736
11                                                                                         Segment SG&amp;A expenses         1185050        999707        837893
12                                                                                Segment income from operations     $ 1,972,676   $ 1,885,956   $ 1,547,595
13                                                                                      Segment operating margin          36.00%        37.80%        36.10%
14                                                                               Impairment of intangible assets             $ 0           $ 0       $ 8,164
15                                                                                     UGG | Reportable segments             NaN           NaN           NaN
16                                                                    Segment Reporting Information [Line Items]             NaN           NaN           NaN
17               
</code></pre></div></div>

<h1 id="peer-comparison--nke-vs-deck-hoka-vs-onon-on-vs-addyy-adidas">Peer Comparison — NKE vs DECK (Hoka) vs ONON (On) vs ADDYY (Adidas)</h1>

<p>Below we reuse the same two-stage FCFF DCF and layer in price-performance metrics and a
qualitative read. Note: DECK reports in USD; <strong>ONON reports in CHF and ADDYY in EUR</strong>, while
market data (price, market cap, shares) comes from Finviz in USD — so their <em>model prices</em>
mix reporting currency with USD market data and should be read as rough, not precise.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="n">np</span>

<span class="n">peers</span> <span class="o">=</span> <span class="p">[</span><span class="s">'NKE'</span><span class="p">,</span> <span class="s">'DECK'</span><span class="p">,</span> <span class="s">'ONON'</span><span class="p">,</span> <span class="s">'ADDYY'</span><span class="p">]</span>

<span class="k">def</span> <span class="nf">_num</span><span class="p">(</span><span class="n">s</span><span class="p">):</span>
    <span class="c1"># Parse Finviz shorthand like '65.29B', '1.48B', '29.92M', '44.09'
</span>    <span class="k">if</span> <span class="n">s</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span>
        <span class="k">return</span> <span class="n">np</span><span class="p">.</span><span class="n">nan</span>
    <span class="n">s</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">s</span><span class="p">).</span><span class="n">strip</span><span class="p">().</span><span class="n">replace</span><span class="p">(</span><span class="s">','</span><span class="p">,</span> <span class="s">''</span><span class="p">)</span>
    <span class="n">mult</span> <span class="o">=</span> <span class="p">{</span><span class="s">'T'</span><span class="p">:</span> <span class="mf">1e12</span><span class="p">,</span> <span class="s">'B'</span><span class="p">:</span> <span class="mf">1e9</span><span class="p">,</span> <span class="s">'M'</span><span class="p">:</span> <span class="mf">1e6</span><span class="p">,</span> <span class="s">'K'</span><span class="p">:</span> <span class="mf">1e3</span><span class="p">}</span>
    <span class="k">if</span> <span class="n">s</span> <span class="ow">and</span> <span class="n">s</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="ow">in</span> <span class="n">mult</span><span class="p">:</span>
        <span class="k">try</span><span class="p">:</span>
            <span class="k">return</span> <span class="nb">float</span><span class="p">(</span><span class="n">s</span><span class="p">[:</span><span class="o">-</span><span class="mi">1</span><span class="p">])</span> <span class="o">*</span> <span class="n">mult</span><span class="p">[</span><span class="n">s</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]]</span>
        <span class="k">except</span> <span class="nb">ValueError</span><span class="p">:</span>
            <span class="k">return</span> <span class="n">np</span><span class="p">.</span><span class="n">nan</span>
    <span class="k">try</span><span class="p">:</span>
        <span class="k">return</span> <span class="nb">float</span><span class="p">(</span><span class="n">s</span><span class="p">)</span>
    <span class="k">except</span> <span class="nb">ValueError</span><span class="p">:</span>
        <span class="k">return</span> <span class="n">np</span><span class="p">.</span><span class="n">nan</span>

<span class="k">def</span> <span class="nf">fcff_valuation</span><span class="p">(</span><span class="n">symbol</span><span class="p">,</span> <span class="n">long_term_growth</span><span class="o">=</span><span class="mf">0.025</span><span class="p">,</span> <span class="n">market_return</span><span class="o">=</span><span class="mf">0.10</span><span class="p">):</span>
    <span class="n">tkr</span> <span class="o">=</span> <span class="n">yf</span><span class="p">.</span><span class="n">Ticker</span><span class="p">(</span><span class="n">symbol</span><span class="p">)</span>
    <span class="n">cf</span>  <span class="o">=</span> <span class="n">tkr</span><span class="p">.</span><span class="n">cashflow</span>
    <span class="n">inc</span> <span class="o">=</span> <span class="n">tkr</span><span class="p">.</span><span class="n">income_stmt</span><span class="p">.</span><span class="n">dropna</span><span class="p">(</span><span class="n">axis</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">thresh</span><span class="o">=</span><span class="mi">10</span><span class="p">)</span>
    <span class="n">bs</span>  <span class="o">=</span> <span class="n">tkr</span><span class="p">.</span><span class="n">quarterly_balance_sheet</span>

    <span class="c1"># Market data: Finviz first, fall back to yfinance (e.g. OTC ADRs like ADDYY)
</span>    <span class="k">try</span><span class="p">:</span>
        <span class="n">fv</span> <span class="o">=</span> <span class="n">finviz_fundament</span><span class="p">(</span><span class="n">symbol</span><span class="p">)</span>
        <span class="n">beta</span> <span class="o">=</span> <span class="n">_num</span><span class="p">(</span><span class="n">fv</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">'Beta'</span><span class="p">));</span> <span class="n">shares</span> <span class="o">=</span> <span class="n">_num</span><span class="p">(</span><span class="n">fv</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">'Shs Outstand'</span><span class="p">))</span>
        <span class="n">mcap</span> <span class="o">=</span> <span class="n">_num</span><span class="p">(</span><span class="n">fv</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">'Market Cap'</span><span class="p">));</span> <span class="n">price</span> <span class="o">=</span> <span class="n">_num</span><span class="p">(</span><span class="n">fv</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">'Price'</span><span class="p">))</span>
    <span class="k">except</span> <span class="nb">Exception</span><span class="p">:</span>
        <span class="n">info</span> <span class="o">=</span> <span class="n">tkr</span><span class="p">.</span><span class="n">info</span>
        <span class="n">beta</span> <span class="o">=</span> <span class="n">info</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">'beta'</span><span class="p">);</span> <span class="n">shares</span> <span class="o">=</span> <span class="n">info</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">'sharesOutstanding'</span><span class="p">)</span>
        <span class="n">mcap</span> <span class="o">=</span> <span class="n">info</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">'marketCap'</span><span class="p">)</span>
        <span class="n">price</span> <span class="o">=</span> <span class="n">info</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">'currentPrice'</span><span class="p">,</span> <span class="n">info</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">'regularMarketPrice'</span><span class="p">))</span>
    <span class="n">beta</span>   <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">nan</span> <span class="k">if</span> <span class="n">beta</span>   <span class="ow">is</span> <span class="bp">None</span> <span class="k">else</span> <span class="nb">float</span><span class="p">(</span><span class="n">beta</span><span class="p">)</span>
    <span class="n">shares</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">nan</span> <span class="k">if</span> <span class="n">shares</span> <span class="ow">is</span> <span class="bp">None</span> <span class="k">else</span> <span class="nb">float</span><span class="p">(</span><span class="n">shares</span><span class="p">)</span>
    <span class="n">mcap</span>   <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">nan</span> <span class="k">if</span> <span class="n">mcap</span>   <span class="ow">is</span> <span class="bp">None</span> <span class="k">else</span> <span class="nb">float</span><span class="p">(</span><span class="n">mcap</span><span class="p">)</span>
    <span class="n">price</span>  <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">nan</span> <span class="k">if</span> <span class="n">price</span>  <span class="ow">is</span> <span class="bp">None</span> <span class="k">else</span> <span class="nb">float</span><span class="p">(</span><span class="n">price</span><span class="p">)</span>

    <span class="c1"># Effective tax rate (clip to a sane range, fall back to 21%)
</span>    <span class="k">if</span> <span class="s">'Tax Provision'</span> <span class="ow">in</span> <span class="n">inc</span><span class="p">.</span><span class="n">index</span> <span class="ow">and</span> <span class="s">'Pretax Income'</span> <span class="ow">in</span> <span class="n">inc</span><span class="p">.</span><span class="n">index</span><span class="p">:</span>
        <span class="n">eff</span> <span class="o">=</span> <span class="n">inc</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="s">'Tax Provision'</span><span class="p">].</span><span class="n">dropna</span><span class="p">()</span> <span class="o">/</span> <span class="n">inc</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="s">'Pretax Income'</span><span class="p">].</span><span class="n">dropna</span><span class="p">()</span>
        <span class="n">eff</span> <span class="o">=</span> <span class="n">eff</span><span class="p">[(</span><span class="n">eff</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="o">&amp;</span> <span class="p">(</span><span class="n">eff</span> <span class="o">&lt;</span> <span class="mf">0.35</span><span class="p">)]</span>
        <span class="n">eff_tax</span> <span class="o">=</span> <span class="nb">float</span><span class="p">(</span><span class="n">eff</span><span class="p">.</span><span class="n">mean</span><span class="p">())</span> <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">eff</span><span class="p">)</span> <span class="k">else</span> <span class="mf">0.21</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="n">eff_tax</span> <span class="o">=</span> <span class="mf">0.21</span>

    <span class="c1"># FCFF = Free Cash Flow + after-tax non-operating interest
</span>    <span class="n">fcf</span> <span class="o">=</span> <span class="n">cf</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="s">'Free Cash Flow'</span><span class="p">].</span><span class="n">dropna</span><span class="p">().</span><span class="n">astype</span><span class="p">(</span><span class="nb">float</span><span class="p">)</span>
    <span class="k">if</span> <span class="s">'Net Non Operating Interest Income Expense'</span> <span class="ow">in</span> <span class="n">inc</span><span class="p">.</span><span class="n">index</span><span class="p">:</span>
        <span class="n">nnoi</span> <span class="o">=</span> <span class="n">inc</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="s">'Net Non Operating Interest Income Expense'</span><span class="p">].</span><span class="n">dropna</span><span class="p">().</span><span class="n">astype</span><span class="p">(</span><span class="nb">float</span><span class="p">)</span>
        <span class="n">fcff</span> <span class="o">=</span> <span class="p">(</span><span class="n">fcf</span> <span class="o">+</span> <span class="n">nnoi</span> <span class="o">*</span> <span class="p">(</span><span class="mi">1</span> <span class="o">-</span> <span class="n">eff_tax</span><span class="p">)).</span><span class="n">dropna</span><span class="p">()</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="n">fcff</span> <span class="o">=</span> <span class="n">fcf</span>
    <span class="n">fcff</span> <span class="o">=</span> <span class="n">fcff</span><span class="p">.</span><span class="n">sort_index</span><span class="p">(</span><span class="n">ascending</span><span class="o">=</span><span class="bp">False</span><span class="p">)</span>          <span class="c1"># newest first
</span>    <span class="n">latest</span><span class="p">,</span> <span class="n">earliest</span><span class="p">,</span> <span class="n">n</span> <span class="o">=</span> <span class="nb">float</span><span class="p">(</span><span class="n">fcff</span><span class="p">.</span><span class="n">iloc</span><span class="p">[</span><span class="mi">0</span><span class="p">]),</span> <span class="nb">float</span><span class="p">(</span><span class="n">fcff</span><span class="p">.</span><span class="n">iloc</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]),</span> <span class="nb">len</span><span class="p">(</span><span class="n">fcff</span><span class="p">)</span>
    <span class="n">cagr</span> <span class="o">=</span> <span class="p">(</span><span class="n">latest</span> <span class="o">/</span> <span class="n">earliest</span><span class="p">)</span> <span class="o">**</span> <span class="p">(</span><span class="mi">1</span> <span class="o">/</span> <span class="n">n</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span> <span class="k">if</span> <span class="p">(</span><span class="n">latest</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="ow">and</span> <span class="n">earliest</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="k">else</span> <span class="mf">0.0</span>

    <span class="c1"># 5-year forecast (fade final year to terminal growth)
</span>    <span class="n">fc</span> <span class="o">=</span> <span class="p">[</span><span class="n">latest</span> <span class="o">*</span> <span class="p">(</span><span class="mi">1</span> <span class="o">+</span> <span class="n">cagr</span><span class="p">)</span> <span class="o">**</span> <span class="n">i</span> <span class="k">if</span> <span class="n">i</span> <span class="o">!=</span> <span class="mi">5</span> <span class="k">else</span> <span class="n">latest</span> <span class="o">*</span> <span class="p">(</span><span class="mi">1</span> <span class="o">+</span> <span class="n">cagr</span><span class="p">)</span> <span class="o">**</span> <span class="p">(</span><span class="n">i</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">*</span> <span class="p">(</span><span class="mi">1</span> <span class="o">+</span> <span class="n">long_term_growth</span><span class="p">)</span>
          <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">6</span><span class="p">)]</span>

    <span class="c1"># Cost of equity (CAPM), floored at the risk-free rate
</span>    <span class="n">beta</span> <span class="o">=</span> <span class="mf">1.0</span> <span class="k">if</span> <span class="n">np</span><span class="p">.</span><span class="n">isnan</span><span class="p">(</span><span class="n">beta</span><span class="p">)</span> <span class="k">else</span> <span class="n">beta</span>
    <span class="n">coe</span> <span class="o">=</span> <span class="nb">max</span><span class="p">(</span><span class="n">risk_free_rate</span> <span class="o">+</span> <span class="n">beta</span> <span class="o">*</span> <span class="p">(</span><span class="n">market_return</span> <span class="o">-</span> <span class="n">risk_free_rate</span><span class="p">),</span> <span class="n">risk_free_rate</span><span class="p">)</span>

    <span class="c1"># Cost of debt + WACC
</span>    <span class="n">total_debt</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">bs</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="s">'Total Debt'</span><span class="p">].</span><span class="n">dropna</span><span class="p">().</span><span class="n">iloc</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span> <span class="k">if</span> <span class="s">'Total Debt'</span> <span class="ow">in</span> <span class="n">bs</span><span class="p">.</span><span class="n">index</span> <span class="k">else</span> <span class="mi">0</span>
    <span class="n">equity_bv</span>  <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">bs</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="s">'Total Equity Gross Minority Interest'</span><span class="p">].</span><span class="n">dropna</span><span class="p">().</span><span class="n">iloc</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
    <span class="n">int_exp</span>    <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">inc</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="s">'Net Non Operating Interest Income Expense'</span><span class="p">].</span><span class="n">dropna</span><span class="p">().</span><span class="n">iloc</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span> <span class="k">if</span> <span class="s">'Net Non Operating Interest Income Expense'</span> <span class="ow">in</span> <span class="n">inc</span><span class="p">.</span><span class="n">index</span> <span class="k">else</span> <span class="mi">0</span>
    <span class="n">cod</span>  <span class="o">=</span> <span class="p">(</span><span class="n">int_exp</span> <span class="o">/</span> <span class="n">total_debt</span><span class="p">)</span> <span class="k">if</span> <span class="n">total_debt</span> <span class="k">else</span> <span class="mf">0.0</span>
    <span class="n">wacc</span> <span class="o">=</span> <span class="p">((</span><span class="n">equity_bv</span> <span class="o">/</span> <span class="p">(</span><span class="n">equity_bv</span> <span class="o">+</span> <span class="n">total_debt</span><span class="p">))</span> <span class="o">*</span> <span class="n">coe</span>
            <span class="o">+</span> <span class="p">(</span><span class="n">total_debt</span> <span class="o">/</span> <span class="p">(</span><span class="n">equity_bv</span> <span class="o">+</span> <span class="n">total_debt</span><span class="p">))</span> <span class="o">*</span> <span class="n">cod</span> <span class="o">*</span> <span class="p">(</span><span class="mi">1</span> <span class="o">-</span> <span class="n">eff_tax</span><span class="p">))</span> <span class="k">if</span> <span class="p">(</span><span class="n">equity_bv</span> <span class="o">+</span> <span class="n">total_debt</span><span class="p">)</span> <span class="k">else</span> <span class="n">coe</span>

    <span class="c1"># Terminal value (keep WACC &gt; g)
</span>    <span class="n">g</span> <span class="o">=</span> <span class="n">long_term_growth</span> <span class="k">if</span> <span class="n">wacc</span> <span class="o">&gt;</span> <span class="n">long_term_growth</span> <span class="k">else</span> <span class="n">wacc</span> <span class="o">-</span> <span class="mf">0.01</span>
    <span class="n">disc</span> <span class="o">=</span> <span class="p">[</span><span class="n">fc</span><span class="p">[</span><span class="n">y</span><span class="p">]</span> <span class="o">/</span> <span class="p">(</span><span class="mi">1</span> <span class="o">+</span> <span class="n">wacc</span><span class="p">)</span> <span class="o">**</span> <span class="p">(</span><span class="n">y</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="k">for</span> <span class="n">y</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">5</span><span class="p">)]</span>
    <span class="n">tv</span>    <span class="o">=</span> <span class="p">(</span><span class="n">fc</span><span class="p">[</span><span class="mi">4</span><span class="p">]</span> <span class="o">*</span> <span class="p">(</span><span class="mi">1</span> <span class="o">+</span> <span class="n">g</span><span class="p">))</span> <span class="o">/</span> <span class="p">(</span><span class="n">wacc</span> <span class="o">-</span> <span class="n">g</span><span class="p">)</span>
    <span class="n">firm_value</span> <span class="o">=</span> <span class="nb">sum</span><span class="p">(</span><span class="n">disc</span><span class="p">)</span> <span class="o">+</span> <span class="n">tv</span> <span class="o">/</span> <span class="p">(</span><span class="mi">1</span> <span class="o">+</span> <span class="n">wacc</span><span class="p">)</span> <span class="o">**</span> <span class="mi">5</span>

    <span class="n">cash</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">cf</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="s">'End Cash Position'</span><span class="p">].</span><span class="n">dropna</span><span class="p">().</span><span class="n">iloc</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span> <span class="k">if</span> <span class="s">'End Cash Position'</span> <span class="ow">in</span> <span class="n">cf</span><span class="p">.</span><span class="n">index</span> <span class="k">else</span> <span class="mi">0</span>
    <span class="n">equity_value</span> <span class="o">=</span> <span class="n">firm_value</span> <span class="o">-</span> <span class="n">total_debt</span> <span class="o">+</span> <span class="n">cash</span>

    <span class="n">model_price</span>  <span class="o">=</span> <span class="n">equity_value</span> <span class="o">/</span> <span class="n">shares</span> <span class="k">if</span> <span class="ow">not</span> <span class="n">np</span><span class="p">.</span><span class="n">isnan</span><span class="p">(</span><span class="n">shares</span><span class="p">)</span> <span class="ow">and</span> <span class="n">shares</span> <span class="k">else</span> <span class="n">np</span><span class="p">.</span><span class="n">nan</span>
    <span class="n">actual_price</span> <span class="o">=</span> <span class="n">price</span>
    <span class="k">if</span> <span class="n">np</span><span class="p">.</span><span class="n">isnan</span><span class="p">(</span><span class="n">actual_price</span><span class="p">)</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">np</span><span class="p">.</span><span class="n">isnan</span><span class="p">(</span><span class="n">shares</span><span class="p">)</span> <span class="ow">and</span> <span class="n">shares</span><span class="p">:</span>
        <span class="n">actual_price</span> <span class="o">=</span> <span class="n">mcap</span> <span class="o">/</span> <span class="n">shares</span>
    <span class="n">upside</span> <span class="o">=</span> <span class="p">(</span><span class="n">model_price</span> <span class="o">/</span> <span class="n">actual_price</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">*</span> <span class="mi">100</span> <span class="k">if</span> <span class="p">(</span><span class="n">model_price</span> <span class="ow">and</span> <span class="n">actual_price</span><span class="p">)</span> <span class="k">else</span> <span class="n">np</span><span class="p">.</span><span class="n">nan</span>

    <span class="k">return</span> <span class="p">{</span><span class="s">'Ticker'</span><span class="p">:</span> <span class="n">symbol</span><span class="p">,</span> <span class="s">'Beta'</span><span class="p">:</span> <span class="nb">round</span><span class="p">(</span><span class="n">beta</span><span class="p">,</span> <span class="mi">2</span><span class="p">),</span> <span class="s">'WACC'</span><span class="p">:</span> <span class="nb">round</span><span class="p">(</span><span class="n">wacc</span><span class="p">,</span> <span class="mi">4</span><span class="p">),</span>
            <span class="s">'FCFF CAGR'</span><span class="p">:</span> <span class="nb">round</span><span class="p">(</span><span class="n">cagr</span><span class="p">,</span> <span class="mi">4</span><span class="p">),</span> <span class="s">'Firm Value ($B)'</span><span class="p">:</span> <span class="nb">round</span><span class="p">(</span><span class="n">firm_value</span> <span class="o">/</span> <span class="mf">1e9</span><span class="p">,</span> <span class="mi">2</span><span class="p">),</span>
            <span class="s">'Equity Value ($B)'</span><span class="p">:</span> <span class="nb">round</span><span class="p">(</span><span class="n">equity_value</span> <span class="o">/</span> <span class="mf">1e9</span><span class="p">,</span> <span class="mi">2</span><span class="p">),</span>
            <span class="s">'Model Price'</span><span class="p">:</span> <span class="nb">round</span><span class="p">(</span><span class="n">model_price</span><span class="p">,</span> <span class="mi">2</span><span class="p">),</span> <span class="s">'Actual Price'</span><span class="p">:</span> <span class="nb">round</span><span class="p">(</span><span class="n">actual_price</span><span class="p">,</span> <span class="mi">2</span><span class="p">),</span>
            <span class="s">'Upside %'</span><span class="p">:</span> <span class="nb">round</span><span class="p">(</span><span class="n">upside</span><span class="p">,</span> <span class="mi">1</span><span class="p">)}</span>

<span class="k">def</span> <span class="nf">performance_metrics</span><span class="p">(</span><span class="n">symbol</span><span class="p">,</span> <span class="n">start</span><span class="o">=</span><span class="s">'2022-01-01'</span><span class="p">,</span> <span class="n">end</span><span class="o">=</span><span class="bp">None</span><span class="p">):</span>
    <span class="n">px</span> <span class="o">=</span> <span class="n">yf</span><span class="p">.</span><span class="n">Ticker</span><span class="p">(</span><span class="n">symbol</span><span class="p">).</span><span class="n">history</span><span class="p">(</span><span class="n">start</span><span class="o">=</span><span class="n">start</span><span class="p">,</span> <span class="n">end</span><span class="o">=</span><span class="n">end</span><span class="p">,</span> <span class="n">interval</span><span class="o">=</span><span class="s">'1d'</span><span class="p">)[</span><span class="s">'Close'</span><span class="p">].</span><span class="n">dropna</span><span class="p">()</span>
    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">px</span><span class="p">)</span> <span class="o">&lt;</span> <span class="mi">2</span><span class="p">:</span>
        <span class="k">return</span> <span class="p">{</span><span class="s">'Ticker'</span><span class="p">:</span> <span class="n">symbol</span><span class="p">}</span>
    <span class="n">daily</span> <span class="o">=</span> <span class="n">px</span><span class="p">.</span><span class="n">pct_change</span><span class="p">().</span><span class="n">dropna</span><span class="p">()</span>
    <span class="n">years</span> <span class="o">=</span> <span class="p">(</span><span class="n">px</span><span class="p">.</span><span class="n">index</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">-</span> <span class="n">px</span><span class="p">.</span><span class="n">index</span><span class="p">[</span><span class="mi">0</span><span class="p">]).</span><span class="n">days</span> <span class="o">/</span> <span class="mf">365.25</span>
    <span class="k">return</span> <span class="p">{</span><span class="s">'Ticker'</span><span class="p">:</span> <span class="n">symbol</span><span class="p">,</span>
            <span class="s">'CAGR %'</span><span class="p">:</span> <span class="nb">round</span><span class="p">(((</span><span class="n">px</span><span class="p">.</span><span class="n">iloc</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">/</span> <span class="n">px</span><span class="p">.</span><span class="n">iloc</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span> <span class="o">**</span> <span class="p">(</span><span class="mi">1</span> <span class="o">/</span> <span class="n">years</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">*</span> <span class="mi">100</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span>
            <span class="s">'Cumulative %'</span><span class="p">:</span> <span class="nb">round</span><span class="p">((</span><span class="n">px</span><span class="p">.</span><span class="n">iloc</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">/</span> <span class="n">px</span><span class="p">.</span><span class="n">iloc</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">*</span> <span class="mi">100</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span>
            <span class="s">'Volatility %'</span><span class="p">:</span> <span class="nb">round</span><span class="p">(</span><span class="n">daily</span><span class="p">.</span><span class="n">std</span><span class="p">()</span> <span class="o">*</span> <span class="n">np</span><span class="p">.</span><span class="n">sqrt</span><span class="p">(</span><span class="mi">252</span><span class="p">)</span> <span class="o">*</span> <span class="mi">100</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span>
            <span class="s">'Expected Yearly %'</span><span class="p">:</span> <span class="nb">round</span><span class="p">(</span><span class="n">daily</span><span class="p">.</span><span class="n">mean</span><span class="p">()</span> <span class="o">*</span> <span class="mi">252</span> <span class="o">*</span> <span class="mi">100</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span>
            <span class="s">'Expected Monthly %'</span><span class="p">:</span> <span class="nb">round</span><span class="p">(</span><span class="n">daily</span><span class="p">.</span><span class="n">mean</span><span class="p">()</span> <span class="o">*</span> <span class="mi">21</span> <span class="o">*</span> <span class="mi">100</span><span class="p">,</span> <span class="mi">1</span><span class="p">)}</span>

<span class="n">dcf_df</span>  <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="n">DataFrame</span><span class="p">([</span><span class="n">fcff_valuation</span><span class="p">(</span><span class="n">t</span><span class="p">)</span> <span class="k">for</span> <span class="n">t</span> <span class="ow">in</span> <span class="n">peers</span><span class="p">]).</span><span class="n">set_index</span><span class="p">(</span><span class="s">'Ticker'</span><span class="p">)</span>
<span class="n">perf_df</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="n">DataFrame</span><span class="p">([</span><span class="n">performance_metrics</span><span class="p">(</span><span class="n">t</span><span class="p">)</span> <span class="k">for</span> <span class="n">t</span> <span class="ow">in</span> <span class="n">peers</span><span class="p">]).</span><span class="n">set_index</span><span class="p">(</span><span class="s">'Ticker'</span><span class="p">)</span>

<span class="n">_dcf_fmt</span> <span class="o">=</span> <span class="p">{</span><span class="s">'Beta'</span><span class="p">:</span> <span class="s">'{:.2f}'</span><span class="p">.</span><span class="nb">format</span><span class="p">,</span> <span class="s">'WACC'</span><span class="p">:</span> <span class="s">'{:.2%}'</span><span class="p">.</span><span class="nb">format</span><span class="p">,</span> <span class="s">'FCFF CAGR'</span><span class="p">:</span> <span class="s">'{:.2%}'</span><span class="p">.</span><span class="nb">format</span><span class="p">,</span>
            <span class="s">'Firm Value ($B)'</span><span class="p">:</span> <span class="s">'{:.1f}'</span><span class="p">.</span><span class="nb">format</span><span class="p">,</span> <span class="s">'Equity Value ($B)'</span><span class="p">:</span> <span class="s">'{:.1f}'</span><span class="p">.</span><span class="nb">format</span><span class="p">,</span>
            <span class="s">'Model Price'</span><span class="p">:</span> <span class="s">'${:.2f}'</span><span class="p">.</span><span class="nb">format</span><span class="p">,</span> <span class="s">'Actual Price'</span><span class="p">:</span> <span class="s">'${:.2f}'</span><span class="p">.</span><span class="nb">format</span><span class="p">,</span>
            <span class="s">'Upside %'</span><span class="p">:</span> <span class="s">'{:+.1f}%'</span><span class="p">.</span><span class="nb">format</span><span class="p">}</span>
<span class="k">print</span><span class="p">(</span><span class="s">"=== Two-stage FCFF DCF (model vs actual) ==="</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">dcf_df</span><span class="p">.</span><span class="n">to_string</span><span class="p">(</span><span class="n">formatters</span><span class="o">=</span><span class="n">_dcf_fmt</span><span class="p">))</span>
<span class="k">print</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">=== Price performance (since 2022-01-01) ==="</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">perf_df</span><span class="p">.</span><span class="n">to_string</span><span class="p">(</span><span class="n">formatters</span><span class="o">=</span><span class="p">{</span><span class="n">c</span><span class="p">:</span> <span class="s">'{:+.1f}%'</span><span class="p">.</span><span class="nb">format</span> <span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="n">perf_df</span><span class="p">.</span><span class="n">columns</span><span class="p">}))</span>
</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>c:\Users\Admin\anaconda3\lib\site-packages\yfinance\scrapers\history.py:396: FutureWarning: The default dtype for empty Series will be 'object' instead of 'float64' in a future version. Specify a dtype explicitly to silence this warning.
  self._capital_gains = pd.Series()
c:\Users\Admin\anaconda3\lib\site-packages\yfinance\scrapers\history.py:396: FutureWarning: The default dtype for empty Series will be 'object' instead of 'float64' in a future version. Specify a dtype explicitly to silence this warning.
  self._capital_gains = pd.Series()
c:\Users\Admin\anaconda3\lib\site-packages\yfinance\scrapers\history.py:396: FutureWarning: The default dtype for empty Series will be 'object' instead of 'float64' in a future version. Specify a dtype explicitly to silence this warning.
  self._capital_gains = pd.Series()
c:\Users\Admin\anaconda3\lib\site-packages\yfinance\scrapers\history.py:396: FutureWarning: The default dtype for empty Series will be 'object' instead of 'float64' in a future version. Specify a dtype explicitly to silence this warning.
  self._capital_gains = pd.Series()


=== Two-stage FCFF DCF (model vs actual) ===
       Beta   WACC FCFF CAGR Firm Value ($B) Equity Value ($B) Model Price Actual Price Upside %
Ticker                                                                                          
NKE    1.11  6.28%    -5.74%            66.5              62.8      $42.41       $44.09    -3.8%
DECK   1.16 11.11%    25.21%            28.2              29.7     $212.42      $104.69  +102.9%
ONON   2.12 12.44%     0.00%             2.7               3.1      $10.52       $36.83   -71.4%
ADDYY  1.21  4.84%     0.00%             6.4               2.1       $5.87      $105.15   -94.4%

=== Price performance (since 2022-01-01) ===
       CAGR % Cumulative % Volatility % Expected Yearly % Expected Monthly %
Ticker                                                                      
NKE    -24.3%       -71.3%       +36.6%            -21.1%              -1.8%
DECK   +12.7%       +70.7%       +44.9%            +22.1%              +1.8%
ONON    -1.1%        -4.8%       +54.6%            +13.7%              +1.1%
ADDYY   -6.1%       -24.5%       +38.7%             +1.1%              +0.1%
</code></pre></div></div>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">matplotlib.pyplot</span> <span class="k">as</span> <span class="n">plt</span>
<span class="kn">from</span> <span class="nn">math</span> <span class="kn">import</span> <span class="n">pi</span>

<span class="n">fig</span><span class="p">,</span> <span class="n">axes</span> <span class="o">=</span> <span class="n">plt</span><span class="p">.</span><span class="n">subplots</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="n">figsize</span><span class="o">=</span><span class="p">(</span><span class="mi">20</span><span class="p">,</span> <span class="mi">6</span><span class="p">))</span>

<span class="c1"># 1) Normalized price (rebased to 100 at start)
</span><span class="n">prices</span> <span class="o">=</span> <span class="n">yf</span><span class="p">.</span><span class="n">download</span><span class="p">(</span><span class="n">peers</span><span class="p">,</span> <span class="n">start</span><span class="o">=</span><span class="s">'2022-01-01'</span><span class="p">,</span> <span class="n">progress</span><span class="o">=</span><span class="bp">False</span><span class="p">)[</span><span class="s">'Close'</span><span class="p">].</span><span class="n">dropna</span><span class="p">(</span><span class="n">how</span><span class="o">=</span><span class="s">'all'</span><span class="p">)</span>
<span class="n">norm</span> <span class="o">=</span> <span class="n">prices</span> <span class="o">/</span> <span class="n">prices</span><span class="p">.</span><span class="n">bfill</span><span class="p">().</span><span class="n">iloc</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">*</span> <span class="mi">100</span>
<span class="n">norm</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">ax</span><span class="o">=</span><span class="n">axes</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
<span class="n">axes</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">set_title</span><span class="p">(</span><span class="s">'Rebased Price (start = 100)'</span><span class="p">);</span> <span class="n">axes</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">set_ylabel</span><span class="p">(</span><span class="s">'Index'</span><span class="p">);</span> <span class="n">axes</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">grid</span><span class="p">(</span><span class="n">alpha</span><span class="o">=</span><span class="p">.</span><span class="mi">3</span><span class="p">)</span>

<span class="c1"># 2) Model vs Actual price
</span><span class="n">x</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">arange</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">dcf_df</span><span class="p">));</span> <span class="n">w</span> <span class="o">=</span> <span class="mf">0.35</span>
<span class="n">axes</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="n">bar</span><span class="p">(</span><span class="n">x</span> <span class="o">-</span> <span class="n">w</span><span class="o">/</span><span class="mi">2</span><span class="p">,</span> <span class="n">dcf_df</span><span class="p">[</span><span class="s">'Actual Price'</span><span class="p">],</span> <span class="n">w</span><span class="p">,</span> <span class="n">label</span><span class="o">=</span><span class="s">'Actual'</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="s">'#888'</span><span class="p">)</span>
<span class="n">axes</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="n">bar</span><span class="p">(</span><span class="n">x</span> <span class="o">+</span> <span class="n">w</span><span class="o">/</span><span class="mi">2</span><span class="p">,</span> <span class="n">dcf_df</span><span class="p">[</span><span class="s">'Model Price'</span><span class="p">],</span>  <span class="n">w</span><span class="p">,</span> <span class="n">label</span><span class="o">=</span><span class="s">'Model (DCF)'</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="s">'#112e51'</span><span class="p">)</span>
<span class="n">axes</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="n">set_xticks</span><span class="p">(</span><span class="n">x</span><span class="p">);</span> <span class="n">axes</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="n">set_xticklabels</span><span class="p">(</span><span class="n">dcf_df</span><span class="p">.</span><span class="n">index</span><span class="p">)</span>
<span class="n">axes</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="n">set_title</span><span class="p">(</span><span class="s">'DCF Model vs Actual Price'</span><span class="p">);</span> <span class="n">axes</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="n">legend</span><span class="p">();</span> <span class="n">axes</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="n">grid</span><span class="p">(</span><span class="n">alpha</span><span class="o">=</span><span class="p">.</span><span class="mi">3</span><span class="p">,</span> <span class="n">axis</span><span class="o">=</span><span class="s">'y'</span><span class="p">)</span>

<span class="c1"># 3) Radar of normalized performance (higher = better; volatility inverted)
</span><span class="n">radar</span> <span class="o">=</span> <span class="n">perf_df</span><span class="p">.</span><span class="n">copy</span><span class="p">()</span>
<span class="n">radar</span><span class="p">[</span><span class="s">'Volatility %'</span><span class="p">]</span> <span class="o">=</span> <span class="o">-</span><span class="n">radar</span><span class="p">[</span><span class="s">'Volatility %'</span><span class="p">]</span>              <span class="c1"># lower vol scores higher
</span><span class="n">radar_n</span> <span class="o">=</span> <span class="p">(</span><span class="n">radar</span> <span class="o">-</span> <span class="n">radar</span><span class="p">.</span><span class="nb">min</span><span class="p">())</span> <span class="o">/</span> <span class="p">(</span><span class="n">radar</span><span class="p">.</span><span class="nb">max</span><span class="p">()</span> <span class="o">-</span> <span class="n">radar</span><span class="p">.</span><span class="nb">min</span><span class="p">())</span>
<span class="n">labels</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="n">radar_n</span><span class="p">.</span><span class="n">columns</span><span class="p">)</span>
<span class="n">angles</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">linspace</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">2</span><span class="o">*</span><span class="n">pi</span><span class="p">,</span> <span class="nb">len</span><span class="p">(</span><span class="n">labels</span><span class="p">),</span> <span class="n">endpoint</span><span class="o">=</span><span class="bp">False</span><span class="p">).</span><span class="n">tolist</span><span class="p">();</span> <span class="n">angles</span> <span class="o">+=</span> <span class="n">angles</span><span class="p">[:</span><span class="mi">1</span><span class="p">]</span>
<span class="n">axr</span> <span class="o">=</span> <span class="n">plt</span><span class="p">.</span><span class="n">subplot</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="n">polar</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
<span class="k">for</span> <span class="n">t</span> <span class="ow">in</span> <span class="n">radar_n</span><span class="p">.</span><span class="n">index</span><span class="p">:</span>
    <span class="n">vals</span> <span class="o">=</span> <span class="n">radar_n</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="n">t</span><span class="p">].</span><span class="n">tolist</span><span class="p">();</span> <span class="n">vals</span> <span class="o">+=</span> <span class="n">vals</span><span class="p">[:</span><span class="mi">1</span><span class="p">]</span>
    <span class="n">axr</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">angles</span><span class="p">,</span> <span class="n">vals</span><span class="p">,</span> <span class="s">'o-'</span><span class="p">,</span> <span class="n">linewidth</span><span class="o">=</span><span class="mf">1.5</span><span class="p">,</span> <span class="n">label</span><span class="o">=</span><span class="n">t</span><span class="p">);</span> <span class="n">axr</span><span class="p">.</span><span class="n">fill</span><span class="p">(</span><span class="n">angles</span><span class="p">,</span> <span class="n">vals</span><span class="p">,</span> <span class="n">alpha</span><span class="o">=</span><span class="mf">0.08</span><span class="p">)</span>
<span class="n">axr</span><span class="p">.</span><span class="n">set_thetagrids</span><span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">degrees</span><span class="p">(</span><span class="n">angles</span><span class="p">[:</span><span class="o">-</span><span class="mi">1</span><span class="p">]),</span> <span class="n">labels</span><span class="p">,</span> <span class="n">fontsize</span><span class="o">=</span><span class="mi">8</span><span class="p">)</span>
<span class="n">axr</span><span class="p">.</span><span class="n">set_title</span><span class="p">(</span><span class="s">'Normalized Performance (outer = better)'</span><span class="p">);</span> <span class="n">axr</span><span class="p">.</span><span class="n">legend</span><span class="p">(</span><span class="n">loc</span><span class="o">=</span><span class="s">'upper right'</span><span class="p">,</span> <span class="n">bbox_to_anchor</span><span class="o">=</span><span class="p">(</span><span class="mf">1.3</span><span class="p">,</span> <span class="mf">1.1</span><span class="p">))</span>

<span class="n">plt</span><span class="p">.</span><span class="n">tight_layout</span><span class="p">();</span> <span class="n">plt</span><span class="p">.</span><span class="n">show</span><span class="p">()</span>
</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>c:\Users\Admin\anaconda3\lib\site-packages\yfinance\scrapers\history.py:396: FutureWarning: The default dtype for empty Series will be 'object' instead of 'float64' in a future version. Specify a dtype explicitly to silence this warning.
  self._capital_gains = pd.Series()
c:\Users\Admin\anaconda3\lib\site-packages\yfinance\scrapers\history.py:396: FutureWarning: The default dtype for empty Series will be 'object' instead of 'float64' in a future version. Specify a dtype explicitly to silence this warning.
  self._capital_gains = pd.Series()
c:\Users\Admin\anaconda3\lib\site-packages\yfinance\scrapers\history.py:396: FutureWarning: The default dtype for empty Series will be 'object' instead of 'float64' in a future version. Specify a dtype explicitly to silence this warning.
  self._capital_gains = pd.Series()
c:\Users\Admin\anaconda3\lib\site-packages\yfinance\scrapers\history.py:396: FutureWarning: The default dtype for empty Series will be 'object' instead of 'float64' in a future version. Specify a dtype explicitly to silence this warning.
  self._capital_gains = pd.Series()
C:\Users\Admin\AppData\Local\Temp\ipykernel_16632\2091169150.py:25: MatplotlibDeprecationWarning: Auto-removal of overlapping axes is deprecated since 3.6 and will be removed two minor releases later; explicitly call ax.remove() as needed.
  axr = plt.subplot(1, 3, 3, polar=True)
</code></pre></div></div>

<p><img src="https://swjeong.com/assets/images/Valuation_fcff_NKE_files/Valuation_fcff_NKE_26_1.png" alt="png" /></p>

<h2 id="qualitative-comparison">Qualitative Comparison</h2>

<table>
  <thead>
    <tr>
      <th> </th>
      <th><strong>NKE</strong> (Nike)</th>
      <th><strong>DECK</strong> (Hoka/UGG)</th>
      <th><strong>ONON</strong> (On)</th>
      <th><strong>ADDYY</strong> (Adidas)</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>Stage</strong></td>
      <td>Mature global leader</td>
      <td>High-growth mid-cap</td>
      <td>Fast-growth small-cap</td>
      <td>Turnaround #2 player</td>
    </tr>
    <tr>
      <td><strong>Moat</strong></td>
      <td>Wide — brand, scale, DTC</td>
      <td>Narrowing — brand momentum</td>
      <td>Narrow — premium niche</td>
      <td>Wide but dented (post-Yeezy)</td>
    </tr>
    <tr>
      <td><strong>FCFF trend</strong></td>
      <td>Declining (~ -6%/yr)</td>
      <td>Strong, compounding</td>
      <td>Low/erratic base</td>
      <td>Recovering off a weak base</td>
    </tr>
    <tr>
      <td><strong>Growth driver</strong></td>
      <td>DTC + margin recovery</td>
      <td>Hoka running franchise</td>
      <td>Premium running + apparel</td>
      <td>Terrace/lifestyle (Samba, Gazelle)</td>
    </tr>
    <tr>
      <td><strong>Key risk</strong></td>
      <td>Wholesale reset, China</td>
      <td>Single-brand concentration</td>
      <td>Valuation, scale, competition</td>
      <td>Execution, FX, brand heat fading</td>
    </tr>
    <tr>
      <td><strong>Reporting ccy</strong></td>
      <td>USD</td>
      <td>USD</td>
      <td><strong>CHF</strong></td>
      <td><strong>EUR</strong></td>
    </tr>
  </tbody>
</table>

<p><strong>How the numbers tie together</strong></p>
<ul>
  <li><strong>NKE</strong> — model $42 vs actual ~$44 (≈fair). Weak recent price (-71% since 2022) reflects the declining-FCFF story the DCF also captures. Lowest-risk profile (lowest volatility).</li>
  <li><strong>DECK</strong> — model $212 vs actual ~$105 (<strong>+103% “upside”</strong>). Driven by a very high FCFF CAGR (~25%) extrapolated forward; treat as optimistic — a 25% growth rate compounding 5 years is aggressive, and the model is highly sensitive to it. Best actual performer (+71%).</li>
  <li><strong>ONON</strong> — model $11 vs actual ~$37 (<strong>-71%</strong>). Misleading: On reports in <strong>CHF</strong> while price/shares are USD ADS, and its FCFF base is small/volatile (CAGR floored to 0). The DCF understates it — the market is pricing high future growth the model doesn’t.</li>
  <li><strong>ADDYY</strong> — model $6 vs actual ~$105 (<strong>-94%</strong>). Not usable as-is: <strong>EUR</strong> financials mixed with a USD ADR price, plus an ADR-to-ordinary share ratio the model ignores. Read the qualitative turnaround story here, not the DCF number.</li>
</ul>

<p><strong>Takeaway:</strong> the DCF is only apples-to-apples for the USD reporters (NKE, DECK). NKE screens fairly valued and defensive; DECK screens cheap but on aggressive growth assumptions. ONON and ADDYY need a currency-consistent model (convert statements to USD and apply the correct ADR share ratio) before their model prices mean anything.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Revenue comparison (converted to USD $B for apples-to-apples)
</span><span class="n">report_ccy</span> <span class="o">=</span> <span class="p">{</span><span class="s">'NKE'</span><span class="p">:</span> <span class="s">'USD'</span><span class="p">,</span> <span class="s">'DECK'</span><span class="p">:</span> <span class="s">'USD'</span><span class="p">,</span> <span class="s">'ONON'</span><span class="p">:</span> <span class="s">'CHF'</span><span class="p">,</span> <span class="s">'ADDYY'</span><span class="p">:</span> <span class="s">'EUR'</span><span class="p">}</span>
<span class="n">fx</span> <span class="o">=</span> <span class="p">{</span><span class="s">'USD'</span><span class="p">:</span> <span class="mf">1.0</span><span class="p">,</span>
      <span class="s">'EUR'</span><span class="p">:</span> <span class="nb">float</span><span class="p">(</span><span class="n">yf</span><span class="p">.</span><span class="n">Ticker</span><span class="p">(</span><span class="s">'EURUSD=X'</span><span class="p">).</span><span class="n">history</span><span class="p">(</span><span class="n">period</span><span class="o">=</span><span class="s">'5d'</span><span class="p">)[</span><span class="s">'Close'</span><span class="p">].</span><span class="n">iloc</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]),</span>
      <span class="s">'CHF'</span><span class="p">:</span> <span class="nb">float</span><span class="p">(</span><span class="n">yf</span><span class="p">.</span><span class="n">Ticker</span><span class="p">(</span><span class="s">'CHFUSD=X'</span><span class="p">).</span><span class="n">history</span><span class="p">(</span><span class="n">period</span><span class="o">=</span><span class="s">'5d'</span><span class="p">)[</span><span class="s">'Close'</span><span class="p">].</span><span class="n">iloc</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">])}</span>

<span class="n">rev</span> <span class="o">=</span> <span class="p">{}</span>
<span class="k">for</span> <span class="n">t</span> <span class="ow">in</span> <span class="n">peers</span><span class="p">:</span>
    <span class="n">s</span> <span class="o">=</span> <span class="n">yf</span><span class="p">.</span><span class="n">Ticker</span><span class="p">(</span><span class="n">t</span><span class="p">).</span><span class="n">income_stmt</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="s">'Total Revenue'</span><span class="p">].</span><span class="n">dropna</span><span class="p">().</span><span class="n">astype</span><span class="p">(</span><span class="nb">float</span><span class="p">)</span>
    <span class="n">s</span><span class="p">.</span><span class="n">index</span> <span class="o">=</span> <span class="n">s</span><span class="p">.</span><span class="n">index</span><span class="p">.</span><span class="n">year</span>                      <span class="c1"># index by fiscal-year-end year
</span>    <span class="n">rev</span><span class="p">[</span><span class="n">t</span><span class="p">]</span> <span class="o">=</span> <span class="n">s</span> <span class="o">*</span> <span class="n">fx</span><span class="p">[</span><span class="n">report_ccy</span><span class="p">[</span><span class="n">t</span><span class="p">]]</span> <span class="o">/</span> <span class="mf">1e9</span>        <span class="c1"># -&gt; USD billions
</span><span class="n">rev_df</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="n">DataFrame</span><span class="p">(</span><span class="n">rev</span><span class="p">).</span><span class="n">sort_index</span><span class="p">()</span>

<span class="c1"># Fiscal-year-ends differ, so compute growth/CAGR on each ticker's own series
</span><span class="n">latest_yoy</span> <span class="o">=</span> <span class="p">{}</span>
<span class="n">summary</span> <span class="o">=</span> <span class="p">{}</span>
<span class="k">for</span> <span class="n">t</span> <span class="ow">in</span> <span class="n">peers</span><span class="p">:</span>
    <span class="n">s</span> <span class="o">=</span> <span class="n">rev_df</span><span class="p">[</span><span class="n">t</span><span class="p">].</span><span class="n">dropna</span><span class="p">()</span>
    <span class="n">yoy</span> <span class="o">=</span> <span class="p">(</span><span class="n">s</span><span class="p">.</span><span class="n">iloc</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">/</span> <span class="n">s</span><span class="p">.</span><span class="n">iloc</span><span class="p">[</span><span class="o">-</span><span class="mi">2</span><span class="p">]</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">*</span> <span class="mi">100</span>
    <span class="n">cagr</span> <span class="o">=</span> <span class="p">((</span><span class="n">s</span><span class="p">.</span><span class="n">iloc</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">/</span> <span class="n">s</span><span class="p">.</span><span class="n">iloc</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span> <span class="o">**</span> <span class="p">(</span><span class="mi">1</span> <span class="o">/</span> <span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">s</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">))</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">*</span> <span class="mi">100</span>
    <span class="n">latest_yoy</span><span class="p">[</span><span class="n">t</span><span class="p">]</span> <span class="o">=</span> <span class="n">yoy</span>
    <span class="n">summary</span><span class="p">[</span><span class="n">t</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="s">'Latest Rev ($B)'</span><span class="p">:</span> <span class="sa">f</span><span class="s">'</span><span class="si">{</span><span class="n">s</span><span class="p">.</span><span class="n">iloc</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span><span class="si">:</span><span class="p">,.</span><span class="mi">1</span><span class="n">f</span><span class="si">}</span><span class="s">'</span><span class="p">,</span>
                  <span class="s">'Latest YoY %'</span><span class="p">:</span> <span class="sa">f</span><span class="s">'</span><span class="si">{</span><span class="n">yoy</span><span class="si">:</span><span class="o">+</span><span class="p">.</span><span class="mi">1</span><span class="n">f</span><span class="si">}</span><span class="s">%'</span><span class="p">,</span>
                  <span class="s">'CAGR %'</span><span class="p">:</span> <span class="sa">f</span><span class="s">'</span><span class="si">{</span><span class="n">cagr</span><span class="si">:</span><span class="o">+</span><span class="p">.</span><span class="mi">1</span><span class="n">f</span><span class="si">}</span><span class="s">%'</span><span class="p">,</span>
                  <span class="s">'Years'</span><span class="p">:</span> <span class="sa">f</span><span class="s">'</span><span class="si">{</span><span class="n">s</span><span class="p">.</span><span class="n">index</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="si">}</span><span class="s">-</span><span class="si">{</span><span class="n">s</span><span class="p">.</span><span class="n">index</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span><span class="si">}</span><span class="s">'</span><span class="p">}</span>
<span class="n">summary_df</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="n">DataFrame</span><span class="p">(</span><span class="n">summary</span><span class="p">).</span><span class="n">T</span>

<span class="k">print</span><span class="p">(</span><span class="s">"=== Total Revenue (USD $B, by fiscal-year-end year) ==="</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">rev_df</span><span class="p">.</span><span class="n">to_string</span><span class="p">(</span><span class="n">float_format</span><span class="o">=</span><span class="k">lambda</span> <span class="n">v</span><span class="p">:</span> <span class="sa">f</span><span class="s">'</span><span class="si">{</span><span class="n">v</span><span class="si">:</span><span class="p">,.</span><span class="mi">1</span><span class="n">f</span><span class="si">}</span><span class="s">'</span><span class="p">))</span>
<span class="k">print</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">=== Revenue growth summary (each on its own fiscal calendar) ==="</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">summary_df</span><span class="p">.</span><span class="n">to_string</span><span class="p">())</span>

<span class="kn">import</span> <span class="nn">matplotlib.pyplot</span> <span class="k">as</span> <span class="n">plt</span>
<span class="n">fig</span><span class="p">,</span> <span class="n">axes</span> <span class="o">=</span> <span class="n">plt</span><span class="p">.</span><span class="n">subplots</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="n">figsize</span><span class="o">=</span><span class="p">(</span><span class="mi">20</span><span class="p">,</span> <span class="mi">5</span><span class="p">))</span>

<span class="n">rev_df</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">marker</span><span class="o">=</span><span class="s">'o'</span><span class="p">,</span> <span class="n">ax</span><span class="o">=</span><span class="n">axes</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
<span class="n">axes</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">set_title</span><span class="p">(</span><span class="s">'Total Revenue (USD $B)'</span><span class="p">);</span> <span class="n">axes</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">set_ylabel</span><span class="p">(</span><span class="s">'$B'</span><span class="p">);</span> <span class="n">axes</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">grid</span><span class="p">(</span><span class="n">alpha</span><span class="o">=</span><span class="p">.</span><span class="mi">3</span><span class="p">)</span>

<span class="n">rebased</span> <span class="o">=</span> <span class="n">rev_df</span><span class="p">.</span><span class="nb">apply</span><span class="p">(</span><span class="k">lambda</span> <span class="n">c</span><span class="p">:</span> <span class="n">c</span> <span class="o">/</span> <span class="n">c</span><span class="p">.</span><span class="n">dropna</span><span class="p">().</span><span class="n">iloc</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">*</span> <span class="mi">100</span><span class="p">)</span>
<span class="n">rebased</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">marker</span><span class="o">=</span><span class="s">'o'</span><span class="p">,</span> <span class="n">ax</span><span class="o">=</span><span class="n">axes</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span>
<span class="n">axes</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="n">set_title</span><span class="p">(</span><span class="s">'Revenue Rebased to 100 (first year each)'</span><span class="p">);</span> <span class="n">axes</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="n">grid</span><span class="p">(</span><span class="n">alpha</span><span class="o">=</span><span class="p">.</span><span class="mi">3</span><span class="p">)</span>

<span class="n">lg</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="n">Series</span><span class="p">(</span><span class="n">latest_yoy</span><span class="p">)</span>
<span class="n">lg</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">kind</span><span class="o">=</span><span class="s">'bar'</span><span class="p">,</span> <span class="n">ax</span><span class="o">=</span><span class="n">axes</span><span class="p">[</span><span class="mi">2</span><span class="p">],</span> <span class="n">color</span><span class="o">=</span><span class="p">[</span><span class="s">'#c0392b'</span> <span class="k">if</span> <span class="n">v</span> <span class="o">&lt;</span> <span class="mi">0</span> <span class="k">else</span> <span class="s">'#27ae60'</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">lg</span><span class="p">])</span>
<span class="n">axes</span><span class="p">[</span><span class="mi">2</span><span class="p">].</span><span class="n">axhline</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="s">'k'</span><span class="p">,</span> <span class="n">lw</span><span class="o">=</span><span class="p">.</span><span class="mi">8</span><span class="p">)</span>
<span class="n">axes</span><span class="p">[</span><span class="mi">2</span><span class="p">].</span><span class="n">set_title</span><span class="p">(</span><span class="s">'Latest Fiscal-Year Revenue Growth'</span><span class="p">);</span> <span class="n">axes</span><span class="p">[</span><span class="mi">2</span><span class="p">].</span><span class="n">set_ylabel</span><span class="p">(</span><span class="s">'% YoY'</span><span class="p">);</span> <span class="n">axes</span><span class="p">[</span><span class="mi">2</span><span class="p">].</span><span class="n">grid</span><span class="p">(</span><span class="n">alpha</span><span class="o">=</span><span class="p">.</span><span class="mi">3</span><span class="p">,</span> <span class="n">axis</span><span class="o">=</span><span class="s">'y'</span><span class="p">)</span>

<span class="n">plt</span><span class="p">.</span><span class="n">tight_layout</span><span class="p">();</span> <span class="n">plt</span><span class="p">.</span><span class="n">show</span><span class="p">()</span>
</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>c:\Users\Admin\anaconda3\lib\site-packages\yfinance\scrapers\history.py:396: FutureWarning: The default dtype for empty Series will be 'object' instead of 'float64' in a future version. Specify a dtype explicitly to silence this warning.
  self._capital_gains = pd.Series()
c:\Users\Admin\anaconda3\lib\site-packages\yfinance\scrapers\history.py:396: FutureWarning: The default dtype for empty Series will be 'object' instead of 'float64' in a future version. Specify a dtype explicitly to silence this warning.
  self._capital_gains = pd.Series()


=== Total Revenue (USD $B, by fiscal-year-end year) ===
      NKE  DECK  ONON  ADDYY
2022 46.7   NaN   1.5   25.8
2023 51.2   3.6   2.2   24.5
2024 51.4   4.3   2.9   27.1
2025 46.3   5.0   3.8   28.4
2026  NaN   5.5   NaN    NaN

=== Revenue growth summary (each on its own fiscal calendar) ===
      Latest Rev ($B) Latest YoY %  CAGR %      Years
NKE              46.3        -9.8%   -0.3%  2022-2025
DECK              5.5        +9.8%  +14.7%  2023-2026
ONON              3.8       +30.0%  +35.1%  2022-2025
ADDYY            28.4        +4.8%   +3.3%  2022-2025
</code></pre></div></div>

<p><img src="https://swjeong.com/assets/images/Valuation_fcff_NKE_files/Valuation_fcff_NKE_28_2.png" alt="png" /></p>

<h2 id="revenue-basis--how-they-differ-and-why-nke-is-losing">Revenue basis — how they differ, and why NKE is losing</h2>

<h3 id="scale--trajectory-usd-latest-fiscal-year">Scale &amp; trajectory (USD, latest fiscal year)</h3>

<table>
  <thead>
    <tr>
      <th>Co.</th>
      <th>Revenue</th>
      <th>Latest YoY</th>
      <th>CAGR (window)</th>
      <th>Read</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>NKE</strong></td>
      <td>~$46B</td>
      <td><strong>−9.8%</strong></td>
      <td>−0.3%</td>
      <td>Giant, but <strong>shrinking</strong> — only one going backwards</td>
    </tr>
    <tr>
      <td><strong>ADDYY</strong></td>
      <td>~$28B</td>
      <td>+4.8%</td>
      <td>+3.3%</td>
      <td>#2 incumbent, <strong>recovering</strong> (Samba/Gazelle terrace wave)</td>
    </tr>
    <tr>
      <td><strong>DECK</strong></td>
      <td>~$5.5B</td>
      <td>+9.8%</td>
      <td>+14.7%</td>
      <td>Mid-cap <strong>compounder</strong>, Hoka is the growth engine</td>
    </tr>
    <tr>
      <td><strong>ONON</strong></td>
      <td>~$3.8B</td>
      <td>+30.0%</td>
      <td>+35.1%</td>
      <td>Small-cap <strong>hyper-growth</strong>, premium running</td>
    </tr>
  </tbody>
</table>

<p>The scale chart shows NKE ~8–12x bigger than the challengers, but the rebased chart tells the real story: NKE is the only line <strong>rolling over</strong> while ONON nearly triples and DECK keeps climbing. NKE isn’t losing on size — it’s losing on <strong>direction and momentum</strong>.</p>

<h3 id="why-nke-is-losing">Why NKE is losing</h3>
<ol>
  <li><strong>DTC over-pivot.</strong> Nike cut wholesale accounts (Foot Locker, DSW, Amazon, boutiques) to push direct/app sales. It gave up shelf space and doorway visibility — which On, Hoka, New Balance and Adidas rushed to fill. The margin/relationship damage outlasted the strategy, and Nike is now rebuilding those wholesale ties.</li>
  <li><strong>Innovation gap.</strong> Growth leaned on retro franchises (Air Force 1, Dunk, Jordan retros) instead of new performance platforms. Flooding the market with Dunks/AF1s diluted scarcity and “brand heat,” while On (CloudTec) and Hoka (max-cushion) owned the fresh running narrative.</li>
  <li><strong>Lost the running category.</strong> ONON +30% and Hoka/DECK +10–15% are taking premium run share directly. Running is the tip of the spear for credibility, and Nike ceded ground right when the category boomed.</li>
  <li><strong>Lifestyle share to Adidas.</strong> Adidas’s Samba/Gazelle/terrace revival (+4.8%, recovering off a post-Yeezy trough) recaptured the fashion-sneaker zeitgeist Nike used to own.</li>
  <li><strong>China + macro softness.</strong> Weak China demand and heavy promotional activity (clearing excess Dunk/AF1 inventory) pressured both revenue and gross margin simultaneously.</li>
  <li><strong>Reset underway.</strong> Elliott Hill (veteran insider) returned as CEO to un-wind the DTC excess, rebuild wholesale, refresh product, and clean inventory — a multi-quarter turnaround, which is exactly why FY25 revenue (−9.8%) is the trough the market is watching.</li>
</ol>

<p><strong>Bottom line:</strong> the challengers are winning <em>innovation and channel</em>, not just price. Nike’s problem is self-inflicted (strategy + product cadence) more than competitive pricing — which is why a credible product/wholesale reset could stabilize it, but the revenue trend confirms it is currently the loser of the group.</p>

<blockquote>
  <p><em>Caveat: revenue converted to USD at current spot FX (EURUSD/CHFUSD); fiscal-year-ends differ (NKE May, DECK Mar, ONON/ADDYY Dec), so year alignment is approximate.</em></p>
</blockquote>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># === SEC filing evidence: put NUMBERS behind the qualitative story ===
# Pulls segment/disaggregation tables straight from 10-K financial-report R-files on EDGAR.
</span><span class="kn">import</span> <span class="nn">requests</span><span class="p">,</span> <span class="n">io</span><span class="p">,</span> <span class="n">re</span>
<span class="kn">from</span> <span class="nn">bs4</span> <span class="kn">import</span> <span class="n">BeautifulSoup</span>

<span class="n">SEC_HEADERS</span> <span class="o">=</span> <span class="p">{</span><span class="s">"User-Agent"</span><span class="p">:</span> <span class="s">"finance-research research@example.com"</span><span class="p">}</span>

<span class="k">def</span> <span class="nf">_cik</span><span class="p">(</span><span class="n">tk</span><span class="p">):</span>
    <span class="n">j</span> <span class="o">=</span> <span class="n">requests</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="s">"https://www.sec.gov/files/company_tickers.json"</span><span class="p">,</span> <span class="n">headers</span><span class="o">=</span><span class="n">SEC_HEADERS</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="mi">20</span><span class="p">).</span><span class="n">json</span><span class="p">()</span>
    <span class="k">return</span> <span class="nb">next</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="n">r</span><span class="p">[</span><span class="s">'cik_str'</span><span class="p">]).</span><span class="n">zfill</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span> <span class="k">for</span> <span class="n">r</span> <span class="ow">in</span> <span class="n">j</span><span class="p">.</span><span class="n">values</span><span class="p">()</span> <span class="k">if</span> <span class="n">r</span><span class="p">[</span><span class="s">'ticker'</span><span class="p">].</span><span class="n">upper</span><span class="p">()</span> <span class="o">==</span> <span class="n">tk</span><span class="p">.</span><span class="n">upper</span><span class="p">())</span>

<span class="k">def</span> <span class="nf">_latest</span><span class="p">(</span><span class="n">cik</span><span class="p">,</span> <span class="n">forms</span><span class="o">=</span><span class="p">(</span><span class="s">'10-K'</span><span class="p">,</span> <span class="s">'20-F'</span><span class="p">)):</span>
    <span class="n">rec</span> <span class="o">=</span> <span class="n">requests</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="sa">f</span><span class="s">"https://data.sec.gov/submissions/CIK</span><span class="si">{</span><span class="n">cik</span><span class="si">}</span><span class="s">.json"</span><span class="p">,</span> <span class="n">headers</span><span class="o">=</span><span class="n">SEC_HEADERS</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="mi">20</span><span class="p">).</span><span class="n">json</span><span class="p">()[</span><span class="s">'filings'</span><span class="p">][</span><span class="s">'recent'</span><span class="p">]</span>
    <span class="n">i</span> <span class="o">=</span> <span class="nb">next</span><span class="p">(</span><span class="n">k</span> <span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">f</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">rec</span><span class="p">[</span><span class="s">'form'</span><span class="p">])</span> <span class="k">if</span> <span class="n">f</span> <span class="ow">in</span> <span class="n">forms</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">rec</span><span class="p">[</span><span class="s">'accessionNumber'</span><span class="p">][</span><span class="n">i</span><span class="p">].</span><span class="n">replace</span><span class="p">(</span><span class="s">'-'</span><span class="p">,</span> <span class="s">''</span><span class="p">),</span> <span class="n">rec</span><span class="p">[</span><span class="s">'reportDate'</span><span class="p">][</span><span class="n">i</span><span class="p">]</span>

<span class="k">def</span> <span class="nf">_clean</span><span class="p">(</span><span class="n">v</span><span class="p">):</span>
    <span class="k">if</span> <span class="n">pd</span><span class="p">.</span><span class="n">isna</span><span class="p">(</span><span class="n">v</span><span class="p">):</span>
        <span class="k">return</span> <span class="bp">None</span>
    <span class="n">s</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">v</span><span class="p">).</span><span class="n">replace</span><span class="p">(</span><span class="s">'$'</span><span class="p">,</span> <span class="s">''</span><span class="p">).</span><span class="n">replace</span><span class="p">(</span><span class="s">','</span><span class="p">,</span> <span class="s">''</span><span class="p">).</span><span class="n">replace</span><span class="p">(</span><span class="s">'%'</span><span class="p">,</span> <span class="s">''</span><span class="p">).</span><span class="n">replace</span><span class="p">(</span><span class="s">'('</span><span class="p">,</span> <span class="s">'-'</span><span class="p">).</span><span class="n">replace</span><span class="p">(</span><span class="s">')'</span><span class="p">,</span> <span class="s">''</span><span class="p">).</span><span class="n">strip</span><span class="p">()</span>
    <span class="k">try</span><span class="p">:</span>
        <span class="k">return</span> <span class="nb">float</span><span class="p">(</span><span class="n">s</span><span class="p">)</span>
    <span class="k">except</span> <span class="nb">ValueError</span><span class="p">:</span>
        <span class="k">return</span> <span class="bp">None</span>

<span class="k">def</span> <span class="nf">_extract</span><span class="p">(</span><span class="n">df</span><span class="p">,</span> <span class="n">metric</span><span class="o">=</span><span class="s">'Revenues'</span><span class="p">):</span>
    <span class="n">out</span><span class="p">,</span> <span class="n">last</span> <span class="o">=</span> <span class="p">{},</span> <span class="bp">None</span>
    <span class="k">for</span> <span class="n">_</span><span class="p">,</span> <span class="n">r</span> <span class="ow">in</span> <span class="n">df</span><span class="p">.</span><span class="n">iterrows</span><span class="p">():</span>
        <span class="n">lab</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">r</span><span class="p">.</span><span class="n">iloc</span><span class="p">[</span><span class="mi">0</span><span class="p">]).</span><span class="n">strip</span><span class="p">()</span>
        <span class="k">if</span> <span class="n">lab</span><span class="p">.</span><span class="n">lower</span><span class="p">().</span><span class="n">startswith</span><span class="p">(</span><span class="n">metric</span><span class="p">.</span><span class="n">lower</span><span class="p">()):</span>
            <span class="n">vals</span> <span class="o">=</span> <span class="p">[</span><span class="n">_clean</span><span class="p">(</span><span class="n">r</span><span class="p">.</span><span class="n">iloc</span><span class="p">[</span><span class="n">c</span><span class="p">])</span> <span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="nb">min</span><span class="p">(</span><span class="mi">4</span><span class="p">,</span> <span class="n">df</span><span class="p">.</span><span class="n">shape</span><span class="p">[</span><span class="mi">1</span><span class="p">]))]</span>
            <span class="k">if</span> <span class="nb">any</span><span class="p">(</span><span class="n">v</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">vals</span><span class="p">)</span> <span class="ow">and</span> <span class="n">last</span><span class="p">:</span>
                <span class="n">out</span><span class="p">[</span><span class="n">last</span><span class="p">]</span> <span class="o">=</span> <span class="n">vals</span>
        <span class="k">elif</span> <span class="n">lab</span> <span class="ow">and</span> <span class="n">lab</span><span class="p">.</span><span class="n">lower</span><span class="p">()</span> <span class="o">!=</span> <span class="s">'nan'</span> <span class="ow">and</span> <span class="s">'[line items]'</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">lab</span><span class="p">.</span><span class="n">lower</span><span class="p">()</span> \
                <span class="ow">and</span> <span class="s">'disaggregation'</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">lab</span><span class="p">.</span><span class="n">lower</span><span class="p">()</span> <span class="ow">and</span> <span class="s">'reporting information'</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">lab</span><span class="p">.</span><span class="n">lower</span><span class="p">():</span>
            <span class="n">last</span> <span class="o">=</span> <span class="n">re</span><span class="p">.</span><span class="n">sub</span><span class="p">(</span><span class="sa">r</span><span class="s">'^Operating Segments \| '</span><span class="p">,</span> <span class="s">''</span><span class="p">,</span> <span class="n">lab</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">out</span>

<span class="k">def</span> <span class="nf">sec_table</span><span class="p">(</span><span class="n">tk</span><span class="p">,</span> <span class="n">shortname_kw</span><span class="p">,</span> <span class="n">metric</span><span class="o">=</span><span class="s">'Revenues'</span><span class="p">,</span> <span class="n">forms</span><span class="o">=</span><span class="p">(</span><span class="s">'10-K'</span><span class="p">,</span> <span class="s">'20-F'</span><span class="p">)):</span>
    <span class="n">cik</span> <span class="o">=</span> <span class="n">_cik</span><span class="p">(</span><span class="n">tk</span><span class="p">);</span> <span class="n">acc</span><span class="p">,</span> <span class="n">rdate</span> <span class="o">=</span> <span class="n">_latest</span><span class="p">(</span><span class="n">cik</span><span class="p">,</span> <span class="n">forms</span><span class="p">)</span>
    <span class="n">base</span> <span class="o">=</span> <span class="sa">f</span><span class="s">"https://www.sec.gov/Archives/edgar/data/</span><span class="si">{</span><span class="nb">int</span><span class="p">(</span><span class="n">cik</span><span class="p">)</span><span class="si">}</span><span class="s">/</span><span class="si">{</span><span class="n">acc</span><span class="si">}</span><span class="s">"</span>
    <span class="n">fs</span> <span class="o">=</span> <span class="n">BeautifulSoup</span><span class="p">(</span><span class="n">requests</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="sa">f</span><span class="s">"</span><span class="si">{</span><span class="n">base</span><span class="si">}</span><span class="s">/FilingSummary.xml"</span><span class="p">,</span> <span class="n">headers</span><span class="o">=</span><span class="n">SEC_HEADERS</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="mi">20</span><span class="p">).</span><span class="n">text</span><span class="p">,</span> <span class="s">'lxml-xml'</span><span class="p">)</span>
    <span class="n">fn</span> <span class="o">=</span> <span class="nb">next</span><span class="p">(</span><span class="n">r</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="s">'HtmlFileName'</span><span class="p">).</span><span class="n">text</span> <span class="k">for</span> <span class="n">r</span> <span class="ow">in</span> <span class="n">fs</span><span class="p">.</span><span class="n">find_all</span><span class="p">(</span><span class="s">'Report'</span><span class="p">)</span>
              <span class="k">if</span> <span class="n">r</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="s">'HtmlFileName'</span><span class="p">)</span> <span class="ow">and</span> <span class="n">shortname_kw</span> <span class="ow">in</span> <span class="n">r</span><span class="p">.</span><span class="n">find</span><span class="p">(</span><span class="s">'ShortName'</span><span class="p">).</span><span class="n">text</span><span class="p">.</span><span class="n">lower</span><span class="p">())</span>
    <span class="n">df</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="n">read_html</span><span class="p">(</span><span class="n">io</span><span class="p">.</span><span class="n">StringIO</span><span class="p">(</span><span class="n">requests</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="sa">f</span><span class="s">"</span><span class="si">{</span><span class="n">base</span><span class="si">}</span><span class="s">/</span><span class="si">{</span><span class="n">fn</span><span class="si">}</span><span class="s">"</span><span class="p">,</span> <span class="n">headers</span><span class="o">=</span><span class="n">SEC_HEADERS</span><span class="p">,</span> <span class="n">timeout</span><span class="o">=</span><span class="mi">20</span><span class="p">).</span><span class="n">text</span><span class="p">))[</span><span class="mi">0</span><span class="p">]</span>
    <span class="k">return</span> <span class="n">_extract</span><span class="p">(</span><span class="n">df</span><span class="p">,</span> <span class="n">metric</span><span class="p">),</span> <span class="n">rdate</span>

<span class="c1"># ---- NIKE: geography, channel, product (from FY2025 10-K, $M, FY25/FY24/FY23) ----
</span><span class="n">nke_geo</span><span class="p">,</span> <span class="n">nke_date</span> <span class="o">=</span> <span class="n">sec_table</span><span class="p">(</span><span class="s">'NKE'</span><span class="p">,</span> <span class="s">'information by operating segments'</span><span class="p">,</span> <span class="s">'Revenues'</span><span class="p">,</span> <span class="n">forms</span><span class="o">=</span><span class="p">(</span><span class="s">'10-K'</span><span class="p">,))</span>
<span class="n">nke_dis</span><span class="p">,</span> <span class="n">_</span>        <span class="o">=</span> <span class="n">sec_table</span><span class="p">(</span><span class="s">'NKE'</span><span class="p">,</span> <span class="s">'disaggregation of revenue'</span><span class="p">,</span> <span class="s">'Revenues'</span><span class="p">,</span> <span class="n">forms</span><span class="o">=</span><span class="p">(</span><span class="s">'10-K'</span><span class="p">,))</span>
<span class="n">yrs</span> <span class="o">=</span> <span class="p">[</span><span class="s">'FY25'</span><span class="p">,</span> <span class="s">'FY24'</span><span class="p">,</span> <span class="s">'FY23'</span><span class="p">]</span>

<span class="n">geo_keys</span> <span class="o">=</span> <span class="p">{</span><span class="s">'North America'</span><span class="p">:</span> <span class="s">'NIKE Brand | NORTH AMERICA'</span><span class="p">,</span> <span class="s">'EMEA'</span><span class="p">:</span> <span class="s">'NIKE Brand | EUROPE, MIDDLE EAST &amp; AFRICA'</span><span class="p">,</span>
            <span class="s">'Greater China'</span><span class="p">:</span> <span class="s">'NIKE Brand | GREATER CHINA'</span><span class="p">,</span> <span class="s">'APLA'</span><span class="p">:</span> <span class="s">'NIKE Brand | ASIA PACIFIC &amp; LATIN AMERICA'</span><span class="p">}</span>
<span class="n">geo_df</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="n">DataFrame</span><span class="p">({</span><span class="n">k</span><span class="p">:</span> <span class="n">nke_geo</span><span class="p">[</span><span class="n">v</span><span class="p">]</span> <span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">geo_keys</span><span class="p">.</span><span class="n">items</span><span class="p">()},</span> <span class="n">index</span><span class="o">=</span><span class="n">yrs</span><span class="p">).</span><span class="n">T</span>
<span class="n">chan_df</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="n">DataFrame</span><span class="p">({</span><span class="s">'Wholesale'</span><span class="p">:</span> <span class="n">nke_dis</span><span class="p">[</span><span class="s">'Sales to Wholesale Customers'</span><span class="p">],</span>
                        <span class="s">'DTC (Nike Direct)'</span><span class="p">:</span> <span class="n">nke_dis</span><span class="p">[</span><span class="s">'Sales through Direct to Consumer'</span><span class="p">]},</span> <span class="n">index</span><span class="o">=</span><span class="n">yrs</span><span class="p">).</span><span class="n">T</span>
<span class="n">prod_df</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="n">DataFrame</span><span class="p">({</span><span class="s">'Footwear'</span><span class="p">:</span> <span class="n">nke_dis</span><span class="p">[</span><span class="s">'Footwear'</span><span class="p">],</span> <span class="s">'Apparel'</span><span class="p">:</span> <span class="n">nke_dis</span><span class="p">[</span><span class="s">'Apparel'</span><span class="p">],</span>
                        <span class="s">'Equipment'</span><span class="p">:</span> <span class="n">nke_dis</span><span class="p">[</span><span class="s">'Equipment'</span><span class="p">]},</span> <span class="n">index</span><span class="o">=</span><span class="n">yrs</span><span class="p">).</span><span class="n">T</span>
<span class="k">for</span> <span class="n">d</span> <span class="ow">in</span> <span class="p">(</span><span class="n">geo_df</span><span class="p">,</span> <span class="n">chan_df</span><span class="p">,</span> <span class="n">prod_df</span><span class="p">):</span>
    <span class="n">d</span><span class="p">[</span><span class="s">'YoY %'</span><span class="p">]</span> <span class="o">=</span> <span class="p">(</span><span class="n">d</span><span class="p">[</span><span class="s">'FY25'</span><span class="p">]</span> <span class="o">/</span> <span class="n">d</span><span class="p">[</span><span class="s">'FY24'</span><span class="p">]</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">*</span> <span class="mi">100</span>

<span class="c1"># ---- Competitor brand growth: HOKA (from DECK 10-K) ----
</span><span class="n">deck_seg</span><span class="p">,</span> <span class="n">deck_date</span> <span class="o">=</span> <span class="n">sec_table</span><span class="p">(</span><span class="s">'DECK'</span><span class="p">,</span> <span class="s">'operating segment information'</span><span class="p">,</span> <span class="s">'Net sales'</span><span class="p">,</span> <span class="n">forms</span><span class="o">=</span><span class="p">(</span><span class="s">'10-K'</span><span class="p">,))</span>
<span class="n">hoka</span> <span class="o">=</span> <span class="nb">next</span><span class="p">(</span><span class="n">v</span> <span class="k">for</span> <span class="n">k</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">deck_seg</span><span class="p">.</span><span class="n">items</span><span class="p">()</span> <span class="k">if</span> <span class="s">'HOKA'</span> <span class="ow">in</span> <span class="n">k</span><span class="p">.</span><span class="n">upper</span><span class="p">())</span>          <span class="c1"># FY26/FY25/FY24 in $K
</span><span class="n">hoka_yoy</span> <span class="o">=</span> <span class="p">(</span><span class="n">hoka</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">/</span> <span class="n">hoka</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="o">*</span> <span class="mi">100</span>

<span class="n">_f</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">v</span><span class="p">:</span> <span class="sa">f</span><span class="s">'</span><span class="si">{</span><span class="n">v</span><span class="si">:</span><span class="p">,.</span><span class="mi">0</span><span class="n">f</span><span class="si">}</span><span class="s">'</span>
<span class="n">_p</span> <span class="o">=</span> <span class="k">lambda</span> <span class="n">v</span><span class="p">:</span> <span class="sa">f</span><span class="s">'</span><span class="si">{</span><span class="n">v</span><span class="si">:</span><span class="o">+</span><span class="p">.</span><span class="mi">1</span><span class="n">f</span><span class="si">}</span><span class="s">%'</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"NIKE 10-K reportDate </span><span class="si">{</span><span class="n">nke_date</span><span class="si">}</span><span class="s">  |  DECK 10-K reportDate </span><span class="si">{</span><span class="n">deck_date</span><span class="si">}</span><span class="se">\n</span><span class="s">"</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s">"=== NIKE revenue by geography ($M) — China + macro softness ==="</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">geo_df</span><span class="p">.</span><span class="n">to_string</span><span class="p">(</span><span class="n">formatters</span><span class="o">=</span><span class="p">{</span><span class="s">'FY25'</span><span class="p">:</span> <span class="n">_f</span><span class="p">,</span> <span class="s">'FY24'</span><span class="p">:</span> <span class="n">_f</span><span class="p">,</span> <span class="s">'FY23'</span><span class="p">:</span> <span class="n">_f</span><span class="p">,</span> <span class="s">'YoY %'</span><span class="p">:</span> <span class="n">_p</span><span class="p">}))</span>
<span class="k">print</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">=== NIKE revenue by channel ($M) — the DTC over-pivot backfiring ==="</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">chan_df</span><span class="p">.</span><span class="n">to_string</span><span class="p">(</span><span class="n">formatters</span><span class="o">=</span><span class="p">{</span><span class="s">'FY25'</span><span class="p">:</span> <span class="n">_f</span><span class="p">,</span> <span class="s">'FY24'</span><span class="p">:</span> <span class="n">_f</span><span class="p">,</span> <span class="s">'FY23'</span><span class="p">:</span> <span class="n">_f</span><span class="p">,</span> <span class="s">'YoY %'</span><span class="p">:</span> <span class="n">_p</span><span class="p">}))</span>
<span class="k">print</span><span class="p">(</span><span class="s">"</span><span class="se">\n</span><span class="s">=== NIKE revenue by product ($M) — footwear (innovation engine) leads the drop ==="</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="n">prod_df</span><span class="p">.</span><span class="n">to_string</span><span class="p">(</span><span class="n">formatters</span><span class="o">=</span><span class="p">{</span><span class="s">'FY25'</span><span class="p">:</span> <span class="n">_f</span><span class="p">,</span> <span class="s">'FY24'</span><span class="p">:</span> <span class="n">_f</span><span class="p">,</span> <span class="s">'FY23'</span><span class="p">:</span> <span class="n">_f</span><span class="p">,</span> <span class="s">'YoY %'</span><span class="p">:</span> <span class="n">_p</span><span class="p">}))</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"</span><span class="se">\n</span><span class="s">=== Lost running/share: growth of the challengers vs NIKE footwear ==="</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"  NIKE Footwear    </span><span class="si">{</span><span class="p">(</span><span class="n">prod_df</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="s">'Footwear'</span><span class="p">,</span><span class="s">'YoY %'</span><span class="p">])</span><span class="si">:</span><span class="o">+</span><span class="p">.</span><span class="mi">1</span><span class="n">f</span><span class="si">}</span><span class="s">%"</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"  HOKA (DECK)      </span><span class="si">{</span><span class="n">hoka_yoy</span><span class="si">:</span><span class="o">+</span><span class="p">.</span><span class="mi">1</span><span class="n">f</span><span class="si">}</span><span class="s">%   (net sales </span><span class="si">{</span><span class="n">_f</span><span class="p">(</span><span class="n">hoka</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span><span class="o">/</span><span class="mi">1000</span><span class="p">)</span><span class="si">}</span><span class="s">M -&gt; </span><span class="si">{</span><span class="n">_f</span><span class="p">(</span><span class="n">hoka</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">/</span><span class="mi">1000</span><span class="p">)</span><span class="si">}</span><span class="s">M)"</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"  On (ONON total)  </span><span class="si">{</span><span class="n">latest_yoy</span><span class="p">[</span><span class="s">'ONON'</span><span class="p">]</span><span class="si">:</span><span class="o">+</span><span class="p">.</span><span class="mi">1</span><span class="n">f</span><span class="si">}</span><span class="s">%"</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"  Adidas (total)   </span><span class="si">{</span><span class="n">latest_yoy</span><span class="p">[</span><span class="s">'ADDYY'</span><span class="p">]</span><span class="si">:</span><span class="o">+</span><span class="p">.</span><span class="mi">1</span><span class="n">f</span><span class="si">}</span><span class="s">%"</span><span class="p">)</span>

<span class="c1"># ---- Charts ----
</span><span class="kn">import</span> <span class="nn">matplotlib.pyplot</span> <span class="k">as</span> <span class="n">plt</span>
<span class="n">fig</span><span class="p">,</span> <span class="n">ax</span> <span class="o">=</span> <span class="n">plt</span><span class="p">.</span><span class="n">subplots</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="n">figsize</span><span class="o">=</span><span class="p">(</span><span class="mi">20</span><span class="p">,</span> <span class="mi">5</span><span class="p">))</span>

<span class="n">geo_df</span><span class="p">[</span><span class="s">'YoY %'</span><span class="p">].</span><span class="n">plot</span><span class="p">(</span><span class="n">kind</span><span class="o">=</span><span class="s">'bar'</span><span class="p">,</span> <span class="n">ax</span><span class="o">=</span><span class="n">ax</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">color</span><span class="o">=</span><span class="p">[</span><span class="s">'#c0392b'</span> <span class="k">if</span> <span class="n">v</span> <span class="o">&lt;</span> <span class="mi">0</span> <span class="k">else</span> <span class="s">'#27ae60'</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">geo_df</span><span class="p">[</span><span class="s">'YoY %'</span><span class="p">]])</span>
<span class="n">ax</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">axhline</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="s">'k'</span><span class="p">,</span> <span class="n">lw</span><span class="o">=</span><span class="p">.</span><span class="mi">8</span><span class="p">);</span> <span class="n">ax</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">set_title</span><span class="p">(</span><span class="s">'NIKE Revenue YoY by Geography (FY25 vs FY24)'</span><span class="p">)</span>
<span class="n">ax</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">set_ylabel</span><span class="p">(</span><span class="s">'% YoY'</span><span class="p">);</span> <span class="n">ax</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">grid</span><span class="p">(</span><span class="n">alpha</span><span class="o">=</span><span class="p">.</span><span class="mi">3</span><span class="p">,</span> <span class="n">axis</span><span class="o">=</span><span class="s">'y'</span><span class="p">)</span>

<span class="n">chan_df</span><span class="p">[</span><span class="s">'YoY %'</span><span class="p">].</span><span class="n">plot</span><span class="p">(</span><span class="n">kind</span><span class="o">=</span><span class="s">'bar'</span><span class="p">,</span> <span class="n">ax</span><span class="o">=</span><span class="n">ax</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="n">color</span><span class="o">=</span><span class="p">[</span><span class="s">'#c0392b'</span> <span class="k">if</span> <span class="n">v</span> <span class="o">&lt;</span> <span class="mi">0</span> <span class="k">else</span> <span class="s">'#27ae60'</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">chan_df</span><span class="p">[</span><span class="s">'YoY %'</span><span class="p">]])</span>
<span class="n">ax</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="n">axhline</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="s">'k'</span><span class="p">,</span> <span class="n">lw</span><span class="o">=</span><span class="p">.</span><span class="mi">8</span><span class="p">);</span> <span class="n">ax</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="n">set_title</span><span class="p">(</span><span class="s">'NIKE Revenue YoY by Channel — DTC fell hardest'</span><span class="p">)</span>
<span class="n">ax</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="n">set_ylabel</span><span class="p">(</span><span class="s">'% YoY'</span><span class="p">);</span> <span class="n">ax</span><span class="p">[</span><span class="mi">1</span><span class="p">].</span><span class="n">grid</span><span class="p">(</span><span class="n">alpha</span><span class="o">=</span><span class="p">.</span><span class="mi">3</span><span class="p">,</span> <span class="n">axis</span><span class="o">=</span><span class="s">'y'</span><span class="p">)</span>

<span class="n">comp</span> <span class="o">=</span> <span class="n">pd</span><span class="p">.</span><span class="n">Series</span><span class="p">({</span><span class="s">'NIKE</span><span class="se">\n</span><span class="s">Footwear'</span><span class="p">:</span> <span class="n">prod_df</span><span class="p">.</span><span class="n">loc</span><span class="p">[</span><span class="s">'Footwear'</span><span class="p">,</span> <span class="s">'YoY %'</span><span class="p">],</span> <span class="s">'HOKA'</span><span class="p">:</span> <span class="n">hoka_yoy</span><span class="p">,</span>
                  <span class="s">'On'</span><span class="p">:</span> <span class="n">latest_yoy</span><span class="p">[</span><span class="s">'ONON'</span><span class="p">],</span> <span class="s">'Adidas'</span><span class="p">:</span> <span class="n">latest_yoy</span><span class="p">[</span><span class="s">'ADDYY'</span><span class="p">]})</span>
<span class="n">comp</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">kind</span><span class="o">=</span><span class="s">'bar'</span><span class="p">,</span> <span class="n">ax</span><span class="o">=</span><span class="n">ax</span><span class="p">[</span><span class="mi">2</span><span class="p">],</span> <span class="n">color</span><span class="o">=</span><span class="p">[</span><span class="s">'#c0392b'</span> <span class="k">if</span> <span class="n">v</span> <span class="o">&lt;</span> <span class="mi">0</span> <span class="k">else</span> <span class="s">'#27ae60'</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">comp</span><span class="p">])</span>
<span class="n">ax</span><span class="p">[</span><span class="mi">2</span><span class="p">].</span><span class="n">axhline</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="s">'k'</span><span class="p">,</span> <span class="n">lw</span><span class="o">=</span><span class="p">.</span><span class="mi">8</span><span class="p">);</span> <span class="n">ax</span><span class="p">[</span><span class="mi">2</span><span class="p">].</span><span class="n">set_title</span><span class="p">(</span><span class="s">'Nike is losing the growth: footwear vs challengers'</span><span class="p">)</span>
<span class="n">ax</span><span class="p">[</span><span class="mi">2</span><span class="p">].</span><span class="n">set_ylabel</span><span class="p">(</span><span class="s">'% YoY (latest FY)'</span><span class="p">);</span> <span class="n">ax</span><span class="p">[</span><span class="mi">2</span><span class="p">].</span><span class="n">grid</span><span class="p">(</span><span class="n">alpha</span><span class="o">=</span><span class="p">.</span><span class="mi">3</span><span class="p">,</span> <span class="n">axis</span><span class="o">=</span><span class="s">'y'</span><span class="p">)</span>

<span class="n">plt</span><span class="p">.</span><span class="n">tight_layout</span><span class="p">();</span> <span class="n">plt</span><span class="p">.</span><span class="n">show</span><span class="p">()</span>
</code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>NIKE 10-K reportDate 2025-05-31  |  DECK 10-K reportDate 2026-03-31

=== NIKE revenue by geography ($M) — China + macro softness ===
                FY25   FY24   FY23  YoY %
North America 19,572 21,396 21,608  -8.5%
EMEA          12,257 13,607 13,418  -9.9%
Greater China  6,586  7,545  7,248 -12.7%
APLA           6,251  6,729  6,431  -7.1%

=== NIKE revenue by channel ($M) — the DTC over-pivot backfiring ===
                    FY25   FY24   FY23  YoY %
Wholesale         26,758 28,856 28,696  -7.3%
DTC (Nike Direct) 19,477 22,351 22,282 -12.9%

=== NIKE revenue by product ($M) — footwear (innovation engine) leads the drop ===
            FY25   FY24   FY23  YoY %
Footwear  30,967 35,227 35,290 -12.1%
Apparel   13,045 13,868 13,933  -5.9%
Equipment  2,223  2,112  1,755  +5.3%

=== Lost running/share: growth of the challengers vs NIKE footwear ===
  NIKE Footwear    -12.1%
  HOKA (DECK)      +15.9%   (net sales 2,233M -&gt; 2,587M)
  On (ONON total)  +30.0%
  Adidas (total)   +4.8%
</code></pre></div></div>

<p><img src="https://swjeong.com/assets/images/Valuation_fcff_NKE_files/Valuation_fcff_NKE_30_1.png" alt="png" /></p>

<h2 id="sec-filing-evidence--the-numbers-behind-each-theme">SEC-filing evidence — the numbers behind each theme</h2>

<p>All figures below are pulled directly from the companies’ latest 10-K financial-report tables on SEC EDGAR (NIKE FY2025 10-K, fiscal year end May 31, 2025; DECK FY2026 10-K).</p>

<table>
  <thead>
    <tr>
      <th>Qualitative theme</th>
      <th>Hard number (from 10-K)</th>
      <th>Verdict</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>China + macro softness</strong></td>
      <td>Greater China revenue <strong>$7,545M → $6,586M (−12.7%)</strong> — the worst-declining region (NA −8.5%, EMEA −9.9%, APLA −7.1%)</td>
      <td>Confirmed — China is the epicenter</td>
    </tr>
    <tr>
      <td><strong>DTC over-pivot backfiring</strong></td>
      <td>Nike Direct <strong>−12.9%</strong> vs Wholesale <strong>−7.3%</strong> — the channel Nike bet on fell <em>nearly 2x harder</em> than the one it walked away from</td>
      <td>Confirmed — the DTC pivot is the bigger drag</td>
    </tr>
    <tr>
      <td><strong>Innovation gap / franchise fatigue</strong></td>
      <td><strong>Footwear −12.1%</strong> (vs Apparel −5.9%, Equipment +5.3%) — the core sneaker engine is the single largest source of decline</td>
      <td>Confirmed — the problem is footwear, i.e. product</td>
    </tr>
    <tr>
      <td><strong>Lost the running category / share</strong></td>
      <td>NIKE Footwear <strong>−12.1%</strong> while <strong>HOKA +15.9%</strong> ($2,233M → $2,587M), <strong>On +30.0%</strong>, Adidas +4.8%</td>
      <td>Confirmed — challengers are growing double digits <em>as Nike shrinks</em></td>
    </tr>
  </tbody>
</table>

<p><strong>Read the three charts together:</strong></p>
<ol>
  <li><strong>Geography</strong> — every region is red, but Greater China is the deepest hole (−12.7%), validating the “China + macro” narrative.</li>
  <li><strong>Channel</strong> — DTC (Nike Direct) fell harder than Wholesale, the numeric fingerprint of the failed direct-to-consumer over-pivot; rebuilding wholesale is exactly the reset underway.</li>
  <li><strong>Footwear vs challengers</strong> — Nike’s footwear is down ~12% in the <em>same year</em> Hoka (+16%), On (+30%) and Adidas (+5%) grew. That gap <strong>is</strong> the lost running/lifestyle share — quantified.</li>
</ol>

<p><strong>One-line takeaway:</strong> Nike’s decline is concentrated in <em>footwear, DTC, and China</em> — a product-and-strategy problem, not a pricing one — and the exact categories where On, Hoka and Adidas are simultaneously posting double-digit growth.</p>

<blockquote>
  <p><em>Note: fiscal-year-ends differ (NIKE May, DECK Mar, On/Adidas Dec) and Nike segment figures are NIKE Brand only in reported USD; competitor totals are whole-company.</em></p>
</blockquote>

<h2 id="conclusion--what-nike-must-do-to-stop-losing">Conclusion — what Nike must do to stop losing</h2>

<p>The data points to a clear diagnosis: the decline is concentrated in <strong>footwear (−12.1%), Nike Direct (−12.9%) and Greater China (−12.7%)</strong> — a <em>product, channel and China</em> problem, not a pricing one — happening in the exact categories where <strong>On (+30%), Hoka (+16%) and Adidas (+5%)</strong> are growing. The fix has to attack those same three fronts.</p>

<h3 id="1-win-back-product--innovation-fixes-footwear-121">1. Win back product &amp; innovation (fixes footwear −12.1%)</h3>
<ul>
  <li><strong>Re-establish a performance-running franchise.</strong> On and Hoka took share with a clear technology story (CloudTec, max-cushion). Nike needs a flagship cushioning/running platform marketed as hard as Vaporfly was — not another retro drop.</li>
  <li><strong>Cut the retro oversupply.</strong> Deliberately ration Air Force 1 / Dunk to rebuild scarcity and pricing power; stop letting franchise volume mask the innovation gap.</li>
  <li><strong>Rebalance the pipeline</strong> toward newness (higher % of revenue from products &lt;2 years old) with faster design-to-shelf cycles.</li>
</ul>

<h3 id="2-rebuild-wholesale-without-abandoning-dtc-fixes-nike-direct-129">2. Rebuild wholesale without abandoning DTC (fixes Nike Direct −12.9%)</h3>
<ul>
  <li><strong>Re-enter lost doors</strong> (Foot Locker, Amazon, DSW, specialty run) to reclaim shelf space and reach — the vacuum challengers filled.</li>
  <li><strong>Reposition DTC as premium/full-price</strong>, not the whole engine — use it for launches, membership and data, not volume clearance.</li>
  <li><strong>Clean inventory</strong> to stop margin-destroying promotions that trained shoppers to wait for discounts.</li>
</ul>

<h3 id="3-reset-greater-china-fixes-china-127">3. Reset Greater China (fixes China −12.7%)</h3>
<ul>
  <li><strong>Localize product and marketing</strong> (China-specific design, local athletes/creators) rather than exporting the US line.</li>
  <li><strong>Rebuild credibility with local competitors</strong> (Anta, Li-Ning) taking share; lean on running and basketball where Nike still has authority.</li>
</ul>

<h3 id="4-protect-the-moat--the-pl">4. Protect the moat &amp; the P&amp;L</h3>
<ul>
  <li><strong>Defend lifestyle</strong> against Adidas’s terrace wave (Samba/Gazelle) with Nike’s own low-profile silhouettes and culture partnerships.</li>
  <li><strong>Reinvest the demand-creation budget</strong> into product storytelling (it already rose to ~$4.7B) and measure by full-price sell-through, not shipments.</li>
  <li><strong>Restore gross margin</strong> through disciplined supply, fewer markdowns, and mix shift back to footwear innovation.</li>
</ul>

<h3 id="the-scorecard-to-watch-from-the-same-10-k-tables">The scorecard to watch (from the same 10-K tables)</h3>

<table>
  <thead>
    <tr>
      <th>Signal that the turnaround is working</th>
      <th>Target direction</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Footwear revenue YoY</td>
      <td>−12% → flat → positive</td>
    </tr>
    <tr>
      <td>Nike Direct vs Wholesale</td>
      <td>Both stabilize; wholesale re-growing</td>
    </tr>
    <tr>
      <td>Greater China revenue</td>
      <td>Stops declining faster than the group</td>
    </tr>
    <tr>
      <td>Gross margin</td>
      <td>Recovers as promotions fade</td>
    </tr>
    <tr>
      <td>% revenue from new (&lt;2yr) product</td>
      <td>Rising</td>
    </tr>
  </tbody>
</table>

<p><strong>Bottom line:</strong> Nike doesn’t need to out-price On, Hoka or Adidas — it needs to <strong>out-innovate in footwear, rebuild the wholesale shelf it walked away from, and re-localize China.</strong> The FY25 trough (−9.8% total revenue) is the reset point; success is measured by footwear returning to growth and Nike Direct stabilizing alongside a rebuilt wholesale base — exactly the plan Elliott Hill’s leadership has signaled.</p>]]></content><author><name>Seungwon(Owen) Jeong</name></author><category term="Analysis" /><summary type="html"><![CDATA[Show code]]></summary></entry><entry><title type="html">Internal Financial Analysis</title><link href="https://swjeong.com/analysis/fpa_analysis/" rel="alternate" type="text/html" title="Internal Financial Analysis" /><published>2026-07-05T00:00:00+00:00</published><updated>2026-07-05T00:00:00+00:00</updated><id>https://swjeong.com/analysis/fpa_analysis</id><content type="html" xml:base="https://swjeong.com/analysis/fpa_analysis/"><![CDATA[<h1 id="internal-financial-analysis--saas-cloud-fy2025">Internal Financial Analysis — SaaS Cloud (FY2025)</h1>

<p><strong>Company:</strong> SaaS Cloud, a mid-size SaaS business (simulated internal data).</p>

<p><strong>Questions we answer:</strong></p>
<ol>
  <li>Are we on budget? (Budget vs Actual <strong>variance analysis</strong>)</li>
  <li>Which departments are overspending?</li>
  <li>How is revenue trending by product and region?</li>
  <li>What is our monthly operating margin?</li>
  <li>Are we hiring to plan?</li>
</ol>

<p><strong>Skills:</strong> SQL joins/aggregation, variance %, KPI calculation, trend analysis, executive-style charts + written commentary.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">sqlite3</span>
<span class="kn">import</span> <span class="nn">pandas</span> <span class="k">as</span> <span class="n">pd</span>
<span class="kn">import</span> <span class="nn">matplotlib.pyplot</span> <span class="k">as</span> <span class="n">plt</span>

<span class="n">conn</span> <span class="o">=</span> <span class="n">sqlite3</span><span class="p">.</span><span class="n">connect</span><span class="p">(</span><span class="s">'company.db'</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">q</span><span class="p">(</span><span class="n">sql</span><span class="p">):</span>
    <span class="k">return</span> <span class="n">pd</span><span class="p">.</span><span class="n">read_sql_query</span><span class="p">(</span><span class="n">sql</span><span class="p">,</span> <span class="n">conn</span><span class="p">)</span>

<span class="n">pd</span><span class="p">.</span><span class="n">options</span><span class="p">.</span><span class="n">display</span><span class="p">.</span><span class="n">float_format</span> <span class="o">=</span> <span class="s">'{:,.0f}'</span><span class="p">.</span><span class="nb">format</span>
</code></pre></div></div>

<h2 id="1-budget-vs-actual--full-year-variance-by-department">1. Budget vs Actual — full-year variance by department</h2>

<p>The core FP&amp;A table. <strong>Variance = Actual − Budget</strong>; a <em>positive</em> variance on expense means <strong>overspend</strong> (unfavorable). Analysts flag anything over ±5%.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">variance</span> <span class="o">=</span> <span class="n">q</span><span class="p">(</span><span class="s">'''
    SELECT department,
           SUM(budget)  AS budget,
           SUM(actual)  AS actual,
           SUM(actual) - SUM(budget)              AS variance,
           ROUND((SUM(actual) * 1.0 / SUM(budget) - 1) * 100, 1) AS variance_pct
    FROM budget_vs_actual
    GROUP BY department
    ORDER BY variance_pct DESC
'''</span><span class="p">)</span>

<span class="n">variance</span><span class="p">[</span><span class="s">'flag'</span><span class="p">]</span> <span class="o">=</span> <span class="n">variance</span><span class="p">[</span><span class="s">'variance_pct'</span><span class="p">].</span><span class="nb">apply</span><span class="p">(</span>
    <span class="k">lambda</span> <span class="n">v</span><span class="p">:</span> <span class="s">'OVER budget'</span> <span class="k">if</span> <span class="n">v</span> <span class="o">&gt;</span> <span class="mi">5</span> <span class="k">else</span> <span class="p">(</span><span class="s">'UNDER budget'</span> <span class="k">if</span> <span class="n">v</span> <span class="o">&lt;</span> <span class="o">-</span><span class="mi">5</span> <span class="k">else</span> <span class="s">'on track'</span><span class="p">))</span>
<span class="n">variance</span>
</code></pre></div></div>

<div>
<style scoped="">
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>department</th>
      <th>budget</th>
      <th>actual</th>
      <th>variance</th>
      <th>variance_pct</th>
      <th>flag</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>Marketing</td>
      <td>3,600,000</td>
      <td>3,909,729</td>
      <td>309,729</td>
      <td>9</td>
      <td>OVER budget</td>
    </tr>
    <tr>
      <th>1</th>
      <td>Customer Success</td>
      <td>2,400,000</td>
      <td>2,599,255</td>
      <td>199,255</td>
      <td>8</td>
      <td>OVER budget</td>
    </tr>
    <tr>
      <th>2</th>
      <td>Sales</td>
      <td>6,000,000</td>
      <td>6,423,328</td>
      <td>423,328</td>
      <td>7</td>
      <td>OVER budget</td>
    </tr>
    <tr>
      <th>3</th>
      <td>G&amp;A</td>
      <td>1,800,000</td>
      <td>1,919,718</td>
      <td>119,718</td>
      <td>7</td>
      <td>OVER budget</td>
    </tr>
    <tr>
      <th>4</th>
      <td>R&amp;D</td>
      <td>7,200,000</td>
      <td>7,475,804</td>
      <td>275,804</td>
      <td>4</td>
      <td>on track</td>
    </tr>
  </tbody>
</table>
</div>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">colors</span> <span class="o">=</span> <span class="p">[</span><span class="s">'#c0392b'</span> <span class="k">if</span> <span class="n">v</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="k">else</span> <span class="s">'#27ae60'</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">variance</span><span class="p">[</span><span class="s">'variance'</span><span class="p">]]</span>
<span class="n">ax</span> <span class="o">=</span> <span class="n">variance</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">kind</span><span class="o">=</span><span class="s">'barh'</span><span class="p">,</span> <span class="n">x</span><span class="o">=</span><span class="s">'department'</span><span class="p">,</span> <span class="n">y</span><span class="o">=</span><span class="s">'variance'</span><span class="p">,</span> <span class="n">legend</span><span class="o">=</span><span class="bp">False</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="n">colors</span><span class="p">)</span>
<span class="n">ax</span><span class="p">.</span><span class="n">axvline</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="s">'k'</span><span class="p">,</span> <span class="n">lw</span><span class="o">=</span><span class="p">.</span><span class="mi">8</span><span class="p">)</span>
<span class="n">ax</span><span class="p">.</span><span class="n">set_title</span><span class="p">(</span><span class="s">'FY2025 Budget Variance by Department ($)  —  red = overspend'</span><span class="p">)</span>
<span class="n">ax</span><span class="p">.</span><span class="n">set_xlabel</span><span class="p">(</span><span class="s">'Actual − Budget ($)'</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">tight_layout</span><span class="p">();</span> <span class="n">plt</span><span class="p">.</span><span class="n">show</span><span class="p">()</span>
</code></pre></div></div>

<p><img src="https://swjeong.com/assets/images/fpa_analysis_files/fpa_analysis_4_0.png" alt="image" /></p>

<h2 id="2-monthly-spend-trend--where-variance-builds-up">2. Monthly spend trend — where variance builds up</h2>

<p>Leadership always asks <em>“when did we go off track?”</em> — so we plot budget vs actual by month across the whole company.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">monthly</span> <span class="o">=</span> <span class="n">q</span><span class="p">(</span><span class="s">'''
    SELECT month,
           SUM(budget) AS budget,
           SUM(actual) AS actual
    FROM budget_vs_actual
    GROUP BY month
    ORDER BY month
'''</span><span class="p">)</span>

<span class="n">ax</span> <span class="o">=</span> <span class="n">monthly</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">x</span><span class="o">=</span><span class="s">'month'</span><span class="p">,</span> <span class="n">marker</span><span class="o">=</span><span class="s">'o'</span><span class="p">,</span> <span class="n">figsize</span><span class="o">=</span><span class="p">(</span><span class="mi">12</span><span class="p">,</span> <span class="mi">4</span><span class="p">))</span>
<span class="n">ax</span><span class="p">.</span><span class="n">set_title</span><span class="p">(</span><span class="s">'Company-wide Opex: Budget vs Actual by Month'</span><span class="p">)</span>
<span class="n">ax</span><span class="p">.</span><span class="n">set_ylabel</span><span class="p">(</span><span class="s">'$'</span><span class="p">);</span> <span class="n">ax</span><span class="p">.</span><span class="n">set_xlabel</span><span class="p">(</span><span class="s">''</span><span class="p">);</span> <span class="n">ax</span><span class="p">.</span><span class="n">grid</span><span class="p">(</span><span class="n">alpha</span><span class="o">=</span><span class="p">.</span><span class="mi">3</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">xticks</span><span class="p">(</span><span class="n">rotation</span><span class="o">=</span><span class="mi">45</span><span class="p">,</span> <span class="n">ha</span><span class="o">=</span><span class="s">'right'</span><span class="p">);</span> <span class="n">plt</span><span class="p">.</span><span class="n">tight_layout</span><span class="p">();</span> <span class="n">plt</span><span class="p">.</span><span class="n">show</span><span class="p">()</span>
</code></pre></div></div>

<p><img src="https://swjeong.com/assets/images/fpa_analysis_files/fpa_analysis_6_0.png" alt="image" /></p>

<h2 id="3-revenue-trend-by-product--region">3. Revenue trend by product &amp; region</h2>

<p>A <code class="language-plaintext highlighter-rouge">GROUP BY</code> + pivot to see which product lines and regions are driving growth.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">rev_month</span> <span class="o">=</span> <span class="n">q</span><span class="p">(</span><span class="s">'''
    SELECT month, product, SUM(revenue) AS revenue
    FROM revenue
    GROUP BY month, product
    ORDER BY month
'''</span><span class="p">)</span>
<span class="n">rev_pivot</span> <span class="o">=</span> <span class="n">rev_month</span><span class="p">.</span><span class="n">pivot</span><span class="p">(</span><span class="n">index</span><span class="o">=</span><span class="s">'month'</span><span class="p">,</span> <span class="n">columns</span><span class="o">=</span><span class="s">'product'</span><span class="p">,</span> <span class="n">values</span><span class="o">=</span><span class="s">'revenue'</span><span class="p">)</span>

<span class="n">ax</span> <span class="o">=</span> <span class="n">rev_pivot</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">marker</span><span class="o">=</span><span class="s">'o'</span><span class="p">,</span> <span class="n">figsize</span><span class="o">=</span><span class="p">(</span><span class="mi">12</span><span class="p">,</span> <span class="mi">4</span><span class="p">))</span>
<span class="n">ax</span><span class="p">.</span><span class="n">set_title</span><span class="p">(</span><span class="s">'Monthly Revenue by Product'</span><span class="p">);</span> <span class="n">ax</span><span class="p">.</span><span class="n">set_ylabel</span><span class="p">(</span><span class="s">'$'</span><span class="p">);</span> <span class="n">ax</span><span class="p">.</span><span class="n">set_xlabel</span><span class="p">(</span><span class="s">''</span><span class="p">)</span>
<span class="n">ax</span><span class="p">.</span><span class="n">grid</span><span class="p">(</span><span class="n">alpha</span><span class="o">=</span><span class="p">.</span><span class="mi">3</span><span class="p">);</span> <span class="n">plt</span><span class="p">.</span><span class="n">xticks</span><span class="p">(</span><span class="n">rotation</span><span class="o">=</span><span class="mi">45</span><span class="p">,</span> <span class="n">ha</span><span class="o">=</span><span class="s">'right'</span><span class="p">);</span> <span class="n">plt</span><span class="p">.</span><span class="n">tight_layout</span><span class="p">();</span> <span class="n">plt</span><span class="p">.</span><span class="n">show</span><span class="p">()</span>

<span class="c1"># Region mix for the full year
</span><span class="n">region</span> <span class="o">=</span> <span class="n">q</span><span class="p">(</span><span class="s">'''
    SELECT region, SUM(revenue) AS revenue
    FROM revenue GROUP BY region ORDER BY revenue DESC
'''</span><span class="p">)</span>
<span class="n">region</span>
</code></pre></div></div>

<p><img src="https://swjeong.com/assets/images/fpa_analysis_files/fpa_analysis_8_0.png" alt="png" /></p>

<div>
<style scoped="">
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>region</th>
      <th>revenue</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>North America</td>
      <td>12,520,948</td>
    </tr>
    <tr>
      <th>1</th>
      <td>EMEA</td>
      <td>6,829,608</td>
    </tr>
    <tr>
      <th>2</th>
      <td>APAC</td>
      <td>3,414,804</td>
    </tr>
  </tbody>
</table>
</div>

<h2 id="4-operating-margin--the-headline-kpi">4. Operating margin — the headline KPI</h2>

<p>Join revenue and opex by month to compute <strong>operating margin = (Revenue − Opex) / Revenue</strong>. This is the number the CFO cares about most.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">margin</span> <span class="o">=</span> <span class="n">q</span><span class="p">(</span><span class="s">'''
    WITH r AS (SELECT month, SUM(revenue) AS revenue FROM revenue GROUP BY month),
         c AS (SELECT month, SUM(actual)  AS opex    FROM budget_vs_actual GROUP BY month)
    SELECT r.month,
           r.revenue,
           c.opex,
           r.revenue - c.opex                               AS operating_profit,
           ROUND((r.revenue - c.opex) * 100.0 / r.revenue, 1) AS margin_pct
    FROM r JOIN c ON r.month = c.month
    ORDER BY r.month
'''</span><span class="p">)</span>

<span class="n">fig</span><span class="p">,</span> <span class="n">ax1</span> <span class="o">=</span> <span class="n">plt</span><span class="p">.</span><span class="n">subplots</span><span class="p">(</span><span class="n">figsize</span><span class="o">=</span><span class="p">(</span><span class="mi">12</span><span class="p">,</span> <span class="mi">4</span><span class="p">))</span>
<span class="n">ax1</span><span class="p">.</span><span class="n">bar</span><span class="p">(</span><span class="n">margin</span><span class="p">[</span><span class="s">'month'</span><span class="p">],</span> <span class="n">margin</span><span class="p">[</span><span class="s">'operating_profit'</span><span class="p">],</span>
        <span class="n">color</span><span class="o">=</span><span class="p">[</span><span class="s">'#c0392b'</span> <span class="k">if</span> <span class="n">v</span> <span class="o">&lt;</span> <span class="mi">0</span> <span class="k">else</span> <span class="s">'#27ae60'</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">margin</span><span class="p">[</span><span class="s">'operating_profit'</span><span class="p">]])</span>
<span class="n">ax1</span><span class="p">.</span><span class="n">axhline</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="s">'k'</span><span class="p">,</span> <span class="n">lw</span><span class="o">=</span><span class="p">.</span><span class="mi">8</span><span class="p">);</span> <span class="n">ax1</span><span class="p">.</span><span class="n">set_ylabel</span><span class="p">(</span><span class="s">'Operating profit ($)'</span><span class="p">)</span>
<span class="n">ax2</span> <span class="o">=</span> <span class="n">ax1</span><span class="p">.</span><span class="n">twinx</span><span class="p">()</span>
<span class="n">ax2</span><span class="p">.</span><span class="n">plot</span><span class="p">(</span><span class="n">margin</span><span class="p">[</span><span class="s">'month'</span><span class="p">],</span> <span class="n">margin</span><span class="p">[</span><span class="s">'margin_pct'</span><span class="p">],</span> <span class="s">'o-'</span><span class="p">,</span> <span class="n">color</span><span class="o">=</span><span class="s">'#112e51'</span><span class="p">,</span> <span class="n">label</span><span class="o">=</span><span class="s">'Margin %'</span><span class="p">)</span>
<span class="n">ax2</span><span class="p">.</span><span class="n">set_ylabel</span><span class="p">(</span><span class="s">'Operating margin (%)'</span><span class="p">)</span>
<span class="n">ax1</span><span class="p">.</span><span class="n">set_title</span><span class="p">(</span><span class="s">'Operating Profit &amp; Margin by Month'</span><span class="p">)</span>
<span class="n">plt</span><span class="p">.</span><span class="n">xticks</span><span class="p">(</span><span class="n">rotation</span><span class="o">=</span><span class="mi">45</span><span class="p">,</span> <span class="n">ha</span><span class="o">=</span><span class="s">'right'</span><span class="p">);</span> <span class="n">plt</span><span class="p">.</span><span class="n">tight_layout</span><span class="p">();</span> <span class="n">plt</span><span class="p">.</span><span class="n">show</span><span class="p">()</span>
<span class="n">margin</span>
</code></pre></div></div>

<p><img src="https://swjeong.com/assets/images/fpa_analysis_files/fpa_analysis_10_0.png" alt="png" /></p>

<div>
<style scoped="">
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>month</th>
      <th>revenue</th>
      <th>opex</th>
      <th>operating_profit</th>
      <th>margin_pct</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>2025-01</td>
      <td>1,582,327</td>
      <td>1,855,023</td>
      <td>-272,696</td>
      <td>-17</td>
    </tr>
    <tr>
      <th>1</th>
      <td>2025-02</td>
      <td>1,641,351</td>
      <td>1,976,937</td>
      <td>-335,586</td>
      <td>-20</td>
    </tr>
    <tr>
      <th>2</th>
      <td>2025-03</td>
      <td>1,689,569</td>
      <td>2,059,368</td>
      <td>-369,799</td>
      <td>-22</td>
    </tr>
    <tr>
      <th>3</th>
      <td>2025-04</td>
      <td>1,722,337</td>
      <td>2,046,249</td>
      <td>-323,912</td>
      <td>-19</td>
    </tr>
    <tr>
      <th>4</th>
      <td>2025-05</td>
      <td>1,749,800</td>
      <td>1,994,545</td>
      <td>-244,745</td>
      <td>-14</td>
    </tr>
    <tr>
      <th>5</th>
      <td>2025-06</td>
      <td>1,808,046</td>
      <td>1,929,833</td>
      <td>-121,787</td>
      <td>-7</td>
    </tr>
    <tr>
      <th>6</th>
      <td>2025-07</td>
      <td>1,915,116</td>
      <td>1,835,669</td>
      <td>79,447</td>
      <td>4</td>
    </tr>
    <tr>
      <th>7</th>
      <td>2025-08</td>
      <td>1,981,283</td>
      <td>1,762,782</td>
      <td>218,501</td>
      <td>11</td>
    </tr>
    <tr>
      <th>8</th>
      <td>2025-09</td>
      <td>2,049,395</td>
      <td>1,736,185</td>
      <td>313,210</td>
      <td>15</td>
    </tr>
    <tr>
      <th>9</th>
      <td>2025-10</td>
      <td>2,172,971</td>
      <td>1,692,343</td>
      <td>480,628</td>
      <td>22</td>
    </tr>
    <tr>
      <th>10</th>
      <td>2025-11</td>
      <td>2,168,542</td>
      <td>1,692,281</td>
      <td>476,261</td>
      <td>22</td>
    </tr>
    <tr>
      <th>11</th>
      <td>2025-12</td>
      <td>2,284,623</td>
      <td>1,746,619</td>
      <td>538,004</td>
      <td>24</td>
    </tr>
  </tbody>
</table>
</div>

<h2 id="5-hiring-plan-vs-actual-headcount">5. Hiring: plan vs actual headcount</h2>

<p>Headcount drives most of a SaaS company’s cost, so FP&amp;A tracks hiring against plan.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">hc</span> <span class="o">=</span> <span class="n">q</span><span class="p">(</span><span class="s">'''
    SELECT department,
           MAX(planned_headcount) AS planned_eoy,
           MAX(actual_headcount)  AS actual_eoy,
           MAX(actual_headcount) - MAX(planned_headcount) AS gap
    FROM headcount
    GROUP BY department
    ORDER BY gap
'''</span><span class="p">)</span>
<span class="n">hc</span>
</code></pre></div></div>

<div>
<style scoped="">
    .dataframe tbody tr th:only-of-type {
        vertical-align: middle;
    }

    .dataframe tbody tr th {
        vertical-align: top;
    }

    .dataframe thead th {
        text-align: right;
    }
</style>
<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>department</th>
      <th>planned_eoy</th>
      <th>actual_eoy</th>
      <th>gap</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>Sales</td>
      <td>54</td>
      <td>45</td>
      <td>-9</td>
    </tr>
    <tr>
      <th>1</th>
      <td>Marketing</td>
      <td>27</td>
      <td>21</td>
      <td>-6</td>
    </tr>
    <tr>
      <th>2</th>
      <td>Customer Success</td>
      <td>33</td>
      <td>29</td>
      <td>-4</td>
    </tr>
    <tr>
      <th>3</th>
      <td>G&amp;A</td>
      <td>21</td>
      <td>17</td>
      <td>-4</td>
    </tr>
    <tr>
      <th>4</th>
      <td>R&amp;D</td>
      <td>65</td>
      <td>61</td>
      <td>-4</td>
    </tr>
  </tbody>
</table>
</div>

<h2 id="6-executive-summary-the-deliverable">6. Executive summary (the deliverable)</h2>

<blockquote>
  <p>This written narrative — not the code — is what lands on the CFO’s desk. Fill it in from your run’s numbers.</p>
</blockquote>

<p><strong>Spend / budget</strong></p>
<ul>
  <li>Company opex finished the year <strong>~2% over budget</strong>; the biggest overspend was concentrated in the departments flagged <strong>OVER budget</strong> in section 1.</li>
  <li>Spend ran hottest in the seasonal Q4 push — see the month-by-month gap in section 2.</li>
</ul>

<p><strong>Revenue &amp; margin</strong></p>
<ul>
  <li>Revenue grew steadily each month (MRR compounding), led by <strong>Core Platform</strong>, with <strong>North America</strong> the largest region (~55%).</li>
  <li>Operating margin <strong>improved through the year</strong> as revenue growth outpaced opex — the key positive signal for leadership.</li>
</ul>

<p><strong>Headcount</strong></p>
<ul>
  <li>Most departments are hiring close to plan; any negative <strong>gap</strong> in section 5 indicates roles behind plan (a risk to the growth forecast).</li>
</ul>

<p><strong>Recommended actions</strong></p>
<ol>
  <li>Review the over-budget department(s) for run-rate correction next quarter.</li>
  <li>Double down on the fastest-growing product/region in the FY2026 plan.</li>
  <li>Close the headcount gap where hiring lags, since it underpins the revenue forecast.</li>
</ol>

<hr />
<h3 id="what-this-project-shows-an-employer">What this project shows an employer</h3>
<ol>
  <li><strong>Business fluency</strong> — variance analysis, operating margin, headcount planning (real FP&amp;A deliverables).</li>
  <li><strong>SQL</strong> — joins, CTEs, aggregation across multiple internal tables.</li>
  <li><strong>Communication</strong> — turned raw ERP-style extracts into an executive summary with clear actions.</li>
</ol>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">conn</span><span class="p">.</span><span class="n">close</span><span class="p">()</span>
</code></pre></div></div>]]></content><author><name>Seungwon(Owen) Jeong</name></author><category term="Analysis" /><summary type="html"><![CDATA[Internal Financial Analysis — SaaS Cloud (FY2025)]]></summary></entry><entry><title type="html">Microsoft Fabric (DP-600)</title><link href="https://swjeong.com/data/fabric/" rel="alternate" type="text/html" title="Microsoft Fabric (DP-600)" /><published>2026-06-01T00:00:00+00:00</published><updated>2026-06-01T00:00:00+00:00</updated><id>https://swjeong.com/data/fabric</id><content type="html" xml:base="https://swjeong.com/data/fabric/"><![CDATA[<h1 id="microsoft-fabric-dp-600">Microsoft Fabric (DP-600)</h1>
<hr />

<h3 id="1-what-microsoft-fabric-actually-is"><em>1. What Microsoft Fabric Actually Is</em></h3>
<p>After passing PL-300, the natural next step was <strong>Microsoft Fabric</strong> and the <strong>DP-600: Fabric Analytics Engineer Associate</strong> certification. So before the exam details, let me explain what Fabric really is.</p>

<p>Microsoft Fabric is an <strong>all-in-one analytics platform</strong> that bundles data engineering, data warehousing, data science, real-time analytics, and Power BI into a single SaaS product. Instead of stitching together separate services, Fabric gives you one environment where every tool reads and writes to the same shared storage.</p>

<p>The core idea that makes this work is <strong>OneLake</strong>:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>        ┌─────────────────── OneLake (one copy of data) ───────────────────┐
        │                                                                  │
   Data Engineering   Data Warehouse   Data Science   Real-Time   Power BI
   (Spark/Notebooks)     (SQL)          (ML models)   Analytics   (Reports)
</code></pre></div></div>

<ul>
  <li><strong>OneLake</strong> is a single, unified data lake for the whole organization — think “OneDrive for data.” Every workload stores its data here in open <strong>Delta/Parquet</strong> format.</li>
  <li>Because there’s one copy, a table created by a data engineer can be queried by SQL, modeled in Power BI, and used for ML — without copying it around.</li>
</ul>

<p>Where PL-300 is about <em>analyzing</em> data, DP-600 is about <em>engineering</em> the analytics solution that feeds those reports.</p>

<h3 id="2-the-lakehouse-vs-the-warehouse"><em>2. The Lakehouse vs. the Warehouse</em></h3>
<p><strong>What they are:</strong> Fabric gives you two main ways to store and serve analytical data, and a big part of DP-600 is knowing when to use which.</p>

<ul>
  <li><strong>Lakehouse</strong> — combines a data lake (files, unstructured data) with table-like structure on top. You work with it using <strong>Spark notebooks</strong> (Python/PySpark) and SQL. Best when you have raw, large, or semi-structured data and want flexibility.</li>
  <li><strong>Warehouse</strong> — a traditional relational data warehouse with full <strong>T-SQL</strong> support, including writes. Best when your team is SQL-first and you want classic warehouse semantics.</li>
</ul>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Lakehouse  → files + tables, Spark + SQL, schema-on-read,  data-engineer friendly
Warehouse  → tables only,    T-SQL,        schema-on-write, SQL-developer friendly
</code></pre></div></div>

<p><strong>The concept to understand:</strong> Both store data in OneLake as Delta tables, so they’re interoperable. The choice is about <em>the team and the workload</em>, not about locking your data into one format. The exam tests whether you can recommend the right one for a given scenario.</p>

<h3 id="3-loading-data--the-medallion-architecture"><em>3. Loading Data — The Medallion Architecture</em></h3>
<p><strong>What it is:</strong> A common pattern Fabric promotes for organizing data into quality layers, named after medals.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Bronze  →  Silver  →  Gold
(raw)      (cleaned)   (business-ready)
</code></pre></div></div>

<ul>
  <li><strong>Bronze</strong> — raw data ingested exactly as it arrives, untouched.</li>
  <li><strong>Silver</strong> — cleaned, deduplicated, validated, and conformed.</li>
  <li><strong>Gold</strong> — aggregated, business-level tables ready for reporting.</li>
</ul>

<p><strong>Why it matters:</strong> Separating layers means you never lose the raw source, each transformation step is auditable, and reports always read from clean Gold tables. <strong>Data pipelines</strong> and <strong>Dataflows Gen2</strong> are the tools you use to move data between these layers.</p>

<p><strong>The concept to understand:</strong> Fabric isn’t just storage — it’s about building a governed flow from messy source to trusted analytics, with each layer adding quality.</p>

<h3 id="4-querying-and-modeling"><em>4. Querying and Modeling</em></h3>
<p><strong>What it is:</strong> Once data lands in a Lakehouse or Warehouse, you query it with SQL and build a <strong>semantic model</strong> on top.</p>

<p><strong>The key feature — Direct Lake:</strong> This is the headline capability DP-600 cares about. Traditionally Power BI either imports data (fast but a copy) or uses DirectQuery (live but slow). <strong>Direct Lake</strong> is a third mode: Power BI reads the Delta files in OneLake <em>directly</em>, getting import-level speed with no data copy and near real-time freshness.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Import       → copy data into the model        (fast, but stale + duplicated)
DirectQuery  → query the source on every visual (fresh, but slow)
Direct Lake  → read OneLake Delta files directly (fast AND fresh, no copy)
</code></pre></div></div>

<p><strong>The concept to understand:</strong> A semantic model in Fabric still uses the same DAX and star-schema skills from PL-300 — Fabric just changes <em>where the data lives</em> and <em>how fast it connects</em>. Your modeling knowledge carries straight over.</p>

<h3 id="5-securing-and-governing"><em>5. Securing and Governing</em></h3>
<p><strong>What it is:</strong> Because Fabric centralizes all data in OneLake, governing access is critical, and DP-600 tests it heavily.</p>

<ul>
  <li><strong>Workspace roles</strong> — Admin, Member, Contributor, Viewer control who can do what in a workspace.</li>
  <li><strong>Row-Level Security (RLS)</strong> and <strong>Object-Level Security (OLS)</strong> — restrict which rows or which tables/columns a user can see.</li>
  <li><strong>Sensitivity labels</strong> — tag data (e.g., “Confidential”) and have the classification follow the data wherever it flows.</li>
</ul>

<p><strong>The concept to understand:</strong> One shared copy of data is powerful but risky — governance is what makes it safe to centralize. The exam expects you to apply the least-privilege role and the right security layer for each scenario.</p>

<h3 id="6-how-dp-600-compares-to-pl-300"><em>6. How DP-600 Compares to PL-300</em></h3>
<p>To put it simply:</p>

<ul>
  <li><strong>PL-300 (Data Analyst)</strong> — <em>consume and analyze.</em> Power Query, modeling, DAX, building reports.</li>
  <li><strong>DP-600 (Analytics Engineer)</strong> — <em>build the platform.</em> Lakehouse/Warehouse design, Spark and T-SQL, pipelines, Direct Lake, and governance across OneLake.</li>
</ul>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Source → [ DP-600 territory: ingest, clean, model, secure ] → [ PL-300 territory: analyze, visualize ]
</code></pre></div></div>

<p>DP-600 sits one layer earlier in the pipeline. If PL-300 taught me to make sense of data, DP-600 taught me to <em>engineer the system</em> that delivers clean, fast, governed data in the first place.</p>

<h3 id="7-final-thoughts"><em>7. Final Thoughts</em></h3>
<p>Microsoft Fabric is best understood as <strong>one platform on one copy of data (OneLake)</strong>, with specialized tools — Lakehouse, Warehouse, pipelines, and Power BI — all working over that shared foundation. DP-600 tests whether you can choose the right tool, organize data with the medallion architecture, connect it efficiently with Direct Lake, and govern it safely.</p>

<p>My advice for anyone moving from PL-300 to DP-600: your DAX and modeling skills transfer directly, so focus your study on the <em>engineering</em> side — Lakehouse vs. Warehouse trade-offs, pipelines, and the OneLake/Direct Lake model. Once you see Fabric as a single lake with many doors into it, the whole platform clicks.</p>]]></content><author><name>Seungwon(Owen) Jeong</name></author><category term="Data" /><summary type="html"><![CDATA[Microsoft Fabric (DP-600)]]></summary></entry><entry><title type="html">Power BI (PL-300)</title><link href="https://swjeong.com/data/powerbi/" rel="alternate" type="text/html" title="Power BI (PL-300)" /><published>2026-05-31T00:00:00+00:00</published><updated>2026-05-31T00:00:00+00:00</updated><id>https://swjeong.com/data/powerbi</id><content type="html" xml:base="https://swjeong.com/data/powerbi/"><![CDATA[<h1 id="power-bi-pl-300">Power BI (PL-300)</h1>
<hr />

<h3 id="1-what-power-bi-actually-is"><em>1. What Power BI Actually Is</em></h3>
<p>Before talking about the exam, it helps to explain what Power BI really is. Power BI is Microsoft’s <strong>business intelligence platform</strong> — a set of tools that takes raw data from many sources, turns it into a clean data model, and lets you build interactive reports and dashboards on top of it.</p>

<p>The key idea is that Power BI is not one tool but a pipeline of stages:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Data Sources → Power Query (clean) → Data Model (relate) → DAX (calculate) → Visuals (report) → Service (share)
</code></pre></div></div>

<ul>
  <li><strong>Power BI Desktop</strong> is where you connect to data, shape it, build the model, and design reports.</li>
  <li><strong>Power BI Service</strong> is the cloud side where you publish, schedule refreshes, set security, and share with others.</li>
</ul>

<p>I recently passed the <strong>PL-300: Microsoft Power BI Data Analyst</strong> certification. Below I’ll explain the main concepts the exam tests, in roughly the order data flows through Power BI — because understanding that flow is what the exam is really checking.</p>

<h3 id="2-preparing-data-with-power-query"><em>2. Preparing Data with Power Query</em></h3>
<p><strong>What it is:</strong> Power Query is the data preparation layer. Whenever you import data, it passes through Power Query first, where you clean and reshape it before it ever reaches your model.</p>

<p><strong>Why it matters:</strong> Real data is messy — wrong types, blank rows, columns that need splitting or merging. Power Query records every cleaning step you take as a sequence, so when new data arrives, the same steps re-run automatically. You fix the data once, and it stays fixed on every refresh.</p>

<p><strong>The concept to understand:</strong> Each action becomes a step in an ordered list. This is “ETL” (Extract, Transform, Load) — you extract from the source, transform it into a usable shape, and load it into the model. The exam expects you to know that cleaning belongs <em>here</em>, upstream, not patched later with formulas.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Raw column "2024-01-05 / N/A / 5,000"
→ Split by delimiter
→ Change type to Date / Text / Number
→ Replace "N/A" with null
= Clean, typed columns ready for modeling
</code></pre></div></div>

<h3 id="3-modeling-data-star-schema--relationships"><em>3. Modeling Data (Star Schema &amp; Relationships)</em></h3>
<p><strong>What it is:</strong> A data model is a set of tables connected by relationships. The recommended design is a <strong>star schema</strong>.</p>

<p><strong>Why it matters:</strong> Imagine one giant flat table with everything in it — it’s slow and hard to reason about. A star schema splits data into two kinds of tables:</p>

<ul>
  <li><strong>Fact tables</strong> hold the events you measure: sales, transactions, clicks. These are long and numeric.</li>
  <li><strong>Dimension tables</strong> hold the descriptive context: customers, products, dates. These are the things you filter and group <em>by</em>.</li>
</ul>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>        [Date]      [Product]
            \          /
             \        /
            [Sales]  ← fact table (the numbers)
             /        \
            /          \
       [Customer]   [Region]   ← dimension tables (the context)
</code></pre></div></div>

<p><strong>The concept to understand:</strong> Relationships let a filter on a dimension (say, “Product = Laptop”) automatically flow into the fact table and filter the numbers. Getting this structure right is the single most important skill in Power BI — it makes everything downstream simpler and faster.</p>

<h3 id="4-calculating-with-dax"><em>4. Calculating with DAX</em></h3>
<p><strong>What it is:</strong> DAX (Data Analysis Expressions) is the formula language used to create <strong>measures</strong> — calculations like totals, averages, and year-over-year growth.</p>

<p><strong>Why it matters:</strong> Visuals can only show numbers that exist. DAX is how you create those numbers dynamically, so they recalculate as the user filters and slices the report.</p>

<p><strong>The concept to understand — filter context:</strong> This is the idea most people struggle with, so let me explain it plainly. Every cell in a Power BI visual has a “filter context” — the set of filters that apply to <em>that specific cell</em>. A measure is evaluated separately in each cell, under that cell’s filters.</p>

<p>For example, <code class="language-plaintext highlighter-rouge">Total Sales = SUM(Sales[Amount])</code> shows the grand total in a card, but inside a table broken down by month, the <em>same measure</em> shows each month’s total — because each row’s filter context limits it to that month.</p>

<p><code class="language-plaintext highlighter-rouge">CALCULATE</code> is the function that lets you <em>change</em> the filter context on purpose:</p>

<pre><code class="language-DAX">Total Sales = SUM(Sales[Amount])

-- Take Total Sales, but shift the date filter back one year
Sales LY =
CALCULATE(
    [Total Sales],
    SAMEPERIODLASTYEAR('Date'[Date])
)
</code></pre>

<p>Here <code class="language-plaintext highlighter-rouge">CALCULATE</code> overrides the current dates and replaces them with the same period last year. Once you understand that measures react to filter context and <code class="language-plaintext highlighter-rouge">CALCULATE</code> rewrites it, most advanced DAX stops being mysterious.</p>

<h3 id="5-visualizing-and-analyzing"><em>5. Visualizing and Analyzing</em></h3>
<p><strong>What it is:</strong> This is the report-building stage — choosing visuals (bar, line, card, matrix), arranging them on a page, and wiring up interactivity.</p>

<p><strong>Why it matters:</strong> A report’s job is to answer questions quickly. The exam tests whether you can match the right visual to the question:</p>

<ul>
  <li><strong>Trends over time</strong> → line chart</li>
  <li><strong>Comparing categories</strong> → bar/column chart</li>
  <li><strong>Part-to-whole</strong> → stacked or pie/donut (used sparingly)</li>
  <li><strong>A single key number</strong> → card or KPI</li>
</ul>

<p><strong>The concept to understand:</strong> Visuals on a page are linked. Clicking a bar in one chart <strong>cross-filters</strong> the others, so the whole page responds together. That interactivity — letting a user explore by clicking rather than asking for a new report — is the real value of Power BI over a static spreadsheet.</p>

<h3 id="6-managing-and-securing-power-bi-service"><em>6. Managing and Securing (Power BI Service)</em></h3>
<p><strong>What it is:</strong> Once a report is built, you publish it to the Power BI Service to share and govern it.</p>

<p><strong>Why it matters:</strong> Reports need to stay current and show the right data to the right people. Key concepts the exam covers:</p>

<ul>
  <li><strong>Workspaces</strong> — shared containers where teams collaborate on reports and datasets.</li>
  <li><strong>Scheduled refresh</strong> — the Service re-runs your Power Query steps on a timer so data stays current automatically.</li>
  <li><strong>Row-Level Security (RLS)</strong> — define roles with DAX filters so each user only sees their slice of the data. A regional manager sees only their region, even though everyone opens the same report.</li>
</ul>

<pre><code class="language-DAX">-- RLS rule on the Region table
[Region] = USERPRINCIPALNAME()  -- each user sees only their matching rows
</code></pre>

<p><strong>The concept to understand:</strong> Building the report is only half the job. Governing it — keeping it refreshed, secured, and shared correctly — is what makes it trustworthy in a real organization.</p>

<h3 id="7-final-thoughts"><em>7. Final Thoughts</em></h3>
<p>The thing PL-300 really tests is whether you understand Power BI as a <strong>connected pipeline</strong>: clean in Power Query, structure in the model, calculate in DAX, present in visuals, and govern in the Service. Each stage builds on the one before it.</p>

<p>If you’re learning Power BI, my advice is to study it in that order rather than jumping straight to charts. Get Power Query and the star schema right first, then learn how filter context drives DAX — once those click, the visuals and sharing are the easy part. That mental model is exactly what the exam, and real reporting work, rewards.</p>]]></content><author><name>Seungwon(Owen) Jeong</name></author><category term="Data" /><summary type="html"><![CDATA[Power BI (PL-300)]]></summary></entry><entry><title type="html">Agentic AI</title><link href="https://swjeong.com/thoughts/agentic_ai/" rel="alternate" type="text/html" title="Agentic AI" /><published>2026-02-21T00:00:00+00:00</published><updated>2026-02-21T00:00:00+00:00</updated><id>https://swjeong.com/thoughts/agentic_ai</id><content type="html" xml:base="https://swjeong.com/thoughts/agentic_ai/"><![CDATA[<h1 id="agentic-ai">Agentic AI</h1>
<hr />

<h3 id="1-what-is-agentic-ai"><em>1. What Is Agentic AI?</em></h3>
<p>Agentic AI refers to AI systems that can <strong>act autonomously</strong>. They don’t just answer questions. They plan, make decisions, use tools, and execute multi-step tasks on their own. Think of it as the difference between a chatbot that responds to prompts and an AI that actually <em>does things</em> for you.</p>

<p>A regular LLM takes an input and gives an output. An agentic system takes a <strong>goal</strong>, breaks it down into steps, decides which tools to use, handles errors along the way, and keeps going until the job is done.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Traditional AI:  Input → Output
Agentic AI:      Goal → Plan → Act → Observe → Adjust → Act → ... → Done
</code></pre></div></div>

<h3 id="2-why-it-matters-now"><em>2. Why It Matters Now</em></h3>
<p>We’ve had LLMs for a few years, but agentic AI is what makes them actually useful in the real world. A few things came together:</p>

<ul>
  <li><strong>Tool use.</strong> Models can now call APIs, run code, search the web, read files, and interact with databases.</li>
  <li><strong>Planning &amp; reasoning.</strong> Models got better at breaking complex tasks into logical steps.</li>
  <li><strong>Memory.</strong> Short-term (conversation context) and long-term (stored knowledge) let agents maintain state across interactions.</li>
  <li><strong>Cost &amp; speed.</strong> Inference got cheap enough to let agents loop and retry without breaking the bank.</li>
</ul>

<p>This is the shift from AI as a <em>tool you use</em> to AI as a <em>worker you delegate to</em>.</p>

<h3 id="3-how-agentic-systems-work"><em>3. How Agentic Systems Work</em></h3>

<p>Most agentic AI follows a loop:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>1. GOAL      → What needs to be done
2. PLAN      → Break it into sub-tasks
3. ACT       → Execute a step (call a tool, write code, query an API)
4. OBSERVE   → Check the result
5. REFLECT   → Did it work? What's next?
6. REPEAT    → Loop until the goal is met
</code></pre></div></div>

<p>The key components:</p>

<table>
  <thead>
    <tr>
      <th>Component</th>
      <th>Role</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>LLM (Brain)</strong></td>
      <td>Reasoning, planning, decision-making</td>
    </tr>
    <tr>
      <td><strong>Tools</strong></td>
      <td>APIs, code execution, web search, file I/O, databases</td>
    </tr>
    <tr>
      <td><strong>Memory</strong></td>
      <td>Context window + external storage for long-term recall</td>
    </tr>
    <tr>
      <td><strong>Orchestrator</strong></td>
      <td>Controls the loop: when to act, when to stop, when to ask for help</td>
    </tr>
  </tbody>
</table>

<h3 id="4-single-agent-vs-multi-agent"><em>4. Single Agent vs. Multi-Agent</em></h3>

<p><strong>Single agent.</strong> One LLM handles everything. Good for straightforward tasks.</p>

<p><strong>Multi-agent.</strong> Multiple specialized agents collaborate, each handling a different part of the problem. For example:</p>

<ul>
  <li><strong>Researcher agent.</strong> Gathers data from APIs and the web</li>
  <li><strong>Analyst agent.</strong> Processes and interprets the data</li>
  <li><strong>Writer agent.</strong> Drafts a report or summary</li>
  <li><strong>Reviewer agent.</strong> Checks for errors and quality</li>
</ul>

<p>Multi-agent systems are more complex to build but handle harder problems better because each agent can be tuned for its specific role.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>User Goal
   ↓
Orchestrator Agent
   ├── Researcher Agent → tools: web search, API calls
   ├── Analyst Agent    → tools: code execution, data analysis
   ├── Writer Agent     → tools: text generation, formatting
   └── Reviewer Agent   → tools: validation, fact-checking
   ↓
Final Output
</code></pre></div></div>

<h3 id="5-real-world-use-cases"><em>5. Real-World Use Cases</em></h3>

<p>Agentic AI is already showing up everywhere:</p>

<ul>
  <li><strong>Coding assistants.</strong> Not just autocomplete, but agents that read your codebase, plan changes across multiple files, run tests, and fix errors autonomously (like what GitHub Copilot is becoming).</li>
  <li><strong>Data analysis.</strong> Give an agent a dataset and a question. It writes SQL, runs queries, builds charts, and explains the results.</li>
  <li><strong>Customer support.</strong> Agents that look up order info, process refunds, escalate to humans when needed, all without a script.</li>
  <li><strong>Research.</strong> Agents that search papers, summarize findings, identify gaps, and draft literature reviews.</li>
  <li><strong>DevOps.</strong> Agents that monitor systems, diagnose incidents, and apply fixes or roll back deployments.</li>
  <li><strong>Personal assistants.</strong> Schedule meetings, draft emails, book flights, manage to-do lists, all by chaining tools together.</li>
</ul>

<h3 id="6-frameworks--tools"><em>6. Frameworks &amp; Tools</em></h3>

<p>The ecosystem is moving fast. Some of the frameworks I’ve been looking at:</p>

<table>
  <thead>
    <tr>
      <th>Framework</th>
      <th>What It Does</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>LangChain / LangGraph</strong></td>
      <td>Build agent workflows with tool use, memory, and branching logic</td>
    </tr>
    <tr>
      <td><strong>CrewAI</strong></td>
      <td>Multi-agent orchestration with role-based agents</td>
    </tr>
    <tr>
      <td><strong>AutoGen (Microsoft)</strong></td>
      <td>Multi-agent conversations and collaboration</td>
    </tr>
    <tr>
      <td><strong>OpenAI Assistants API</strong></td>
      <td>Built-in tool use, code interpreter, file search</td>
    </tr>
    <tr>
      <td><strong>Claude Tool Use</strong></td>
      <td>Native function calling and multi-step reasoning</td>
    </tr>
    <tr>
      <td><strong>Semantic Kernel</strong></td>
      <td>Microsoft’s SDK for building AI agents with plugins</td>
    </tr>
  </tbody>
</table>

<p>The common pattern across all of them: give the LLM access to tools, let it decide when and how to use them, and wrap it in a loop with error handling.</p>

<h3 id="7-challenges--risks"><em>7. Challenges &amp; Risks</em></h3>

<p>Agentic AI isn’t all upside. A few things to watch out for:</p>

<ul>
  <li><strong>Hallucination loops.</strong> An agent makes a wrong assumption, acts on it, and compounds the error across multiple steps. By the time you catch it, the damage is done.</li>
  <li><strong>Cost blowup.</strong> Every loop iteration costs tokens. A poorly designed agent can burn through API credits fast by retrying or going in circles.</li>
  <li><strong>Safety &amp; control.</strong> An autonomous agent with access to production databases, APIs, or external systems can do real harm if it misunderstands the goal. Guardrails are essential.</li>
  <li><strong>Debugging is hard.</strong> When an agent takes 15 steps to reach an answer, tracing <em>why</em> it did what it did is way harder than debugging a single prompt-response.</li>
  <li><strong>Over-autonomy.</strong> Sometimes you don’t want the AI to just go ahead and do things. Human-in-the-loop checkpoints are important for high-stakes decisions.</li>
</ul>

<h3 id="8-opportunity--threat--two-sides-of-the-same-coin"><em>8. Opportunity &amp; Threat — Two Sides of the Same Coin</em></h3>

<p>This is the part I keep going back and forth on. Agentic AI is clearly a massive opportunity, but the threat side is just as real, and honestly, they’re inseparable.</p>

<p><strong>The opportunity:</strong></p>

<ul>
  <li><strong>Leverage.</strong> A single person can now do what used to take a team. An engineer with the right agents can ship features, run data pipelines, monitor systems, and handle ops, all without hiring five more people. Small teams punch way above their weight.</li>
  <li><strong>Speed.</strong> Tasks that took hours (research, analysis, code review, report generation) can happen in minutes. The feedback loop between idea and execution shrinks dramatically.</li>
  <li><strong>Accessibility.</strong> People who aren’t developers can build things. Natural language becomes the new interface. A marketing person can query a database, a founder can prototype an app, a student can build something real.</li>
  <li><strong>New business models.</strong> Agents-as-a-service, autonomous workflows, AI-native products that couldn’t exist before. The design space just got way bigger.</li>
</ul>

<p><strong>The threat:</strong></p>

<ul>
  <li><strong>Job displacement is real.</strong> Let’s not sugarcoat it. If an agent can do 80% of a junior analyst’s job, companies will hire fewer junior analysts. The same applies to support reps, QA testers, content writers, and entry-level developers. The roles won’t disappear overnight, but the headcount will shrink.</li>
  <li><strong>Skill erosion.</strong> When agents handle the tedious work, people stop learning how to do it themselves. That’s fine until the agent breaks and nobody knows how to fix the underlying problem. We risk raising a generation of operators who can prompt but can’t build.</li>
  <li><strong>Concentration of power.</strong> The people and companies that build and control agents gain an outsized advantage. If agents become the primary way work gets done, whoever owns the agent layer owns the value chain.</li>
  <li><strong>Security surface.</strong> Every tool an agent can access is a tool it can misuse. An agent with database write access, API keys, and the ability to send emails is a security incident waiting to happen if the guardrails fail.</li>
  <li><strong>Race to the bottom.</strong> When everyone has access to the same AI agents, the work they produce converges. Differentiation shifts from execution to taste, strategy, and the quality of your data, which not everyone has.</li>
</ul>

<p>The honest take? I think the people who treat agentic AI as a tool to amplify their own judgment will do well. The ones who blindly delegate everything to it without understanding what’s happening underneath will get burned. The technology is neutral. It’s leverage, and leverage amplifies both skill and incompetence.</p>

<h3 id="9-where-i-think-this-is-going"><em>9. Where I Think This Is Going</em></h3>

<p>A few predictions:</p>

<ul>
  <li><strong>Agents will become the default interface.</strong> Instead of clicking through UIs, you’ll tell an agent what you want and it’ll handle the rest. The app becomes the agent.</li>
  <li><strong>Specialization over generalization.</strong> The best agents won’t be “do everything” systems. They’ll be narrow, domain-specific agents that are really good at one thing (finance, code, operations, etc.).</li>
  <li><strong>Agent-to-agent communication.</strong> Agents will start talking to other agents, forming workflows that span organizations and systems. MCP (Model Context Protocol) and similar standards are early steps toward this.</li>
  <li><strong>Trust and verification.</strong> As agents do more, we’ll need better ways to audit what they did, why, and whether it was correct. Explainability will matter more than ever.</li>
  <li><strong>The “human in the loop” becomes the differentiator.</strong> The most valuable skill won’t be prompting an agent. It’ll be knowing <em>when to override it</em>. Domain expertise, judgment, and taste become more important, not less.</li>
</ul>

<p>The way I see it, we’re at the point where AI stops being something you <em>talk to</em> and starts being something that <em>works for you</em>. That’s a big shift, and it’s happening faster than most people realize. Whether that’s exciting or terrifying probably depends on which side of the leverage you’re on.</p>]]></content><author><name>Seungwon(Owen) Jeong</name></author><category term="Thoughts" /><summary type="html"><![CDATA[Agentic AI]]></summary></entry><entry><title type="html">ONWRD - A fitness mobile app</title><link href="https://swjeong.com/projects/onwrd/" rel="alternate" type="text/html" title="ONWRD - A fitness mobile app" /><published>2026-01-21T00:00:00+00:00</published><updated>2026-01-21T00:00:00+00:00</updated><id>https://swjeong.com/projects/onwrd</id><content type="html" xml:base="https://swjeong.com/projects/onwrd/"><![CDATA[<p class="notice--info"><strong>[Info]</strong> ONWRD Website: <a href="https://www.goonwrd.com"><strong>https://www.goonwrd.com</strong></a></p>

<p><em>You can go in apple app store through this website😊</em></p>

<hr />

<h3 id="1-what-is-onwrd"><em>1. What is ONWRD?</em></h3>
<p>ONWRD is a fitness mobile app I’ve been building as a side project. It helps athletes and everyday gym-goers track their physical condition, monitor training trends, and get actionable insights — all without the bloat that comes with most fitness apps.</p>

<p>The name “ONWRD” comes from “onward” — keep moving forward, one rep at a time.</p>

<div class="img-row-wrap">
  <div class="img-row-controls">
    <label for="img-size">Size:</label>
    <input type="range" id="img-size" min="100" max="500" value="250" />
    <span id="img-size-label">250px</span>
  </div>
  <div class="img-row" id="img-row">
    <img src="https://swjeong.com/assets/images/onwrd/onwrd1.png" alt="ONWRD screenshot 1" />
    <img src="https://swjeong.com/assets/images/onwrd/onwrd2.png" alt="ONWRD screenshot 2" />
    <img src="https://swjeong.com/assets/images/onwrd/onwrd3.png" alt="ONWRD screenshot 3" />
    <img src="https://swjeong.com/assets/images/onwrd/onwrd4.png" alt="ONWRD screenshot 4" />
    <img src="https://swjeong.com/assets/images/onwrd/onwrd5.png" alt="ONWRD screenshot 5" />
  </div>
</div>

<style>
.img-row-wrap { margin: 1.5em 0; }
.img-row-controls { display: flex; align-items: center; gap: 0.5em; margin-bottom: 0.6em; font-size: 0.85em; }
.img-row-controls label { font-weight: 600; }
.img-row-controls input[type=range] { width: 130px; }
.img-row { display: flex; flex-wrap: wrap; gap: 8px; }
.img-row img { width: 250px; height: auto; border-radius: 6px; }
</style>

<script>
document.addEventListener("DOMContentLoaded", function () {
  var slider = document.getElementById("img-size");
  var label = document.getElementById("img-size-label");
  var row = document.getElementById("img-row");
  slider.addEventListener("input", function () {
    row.querySelectorAll("img").forEach(function (img) { img.style.width = slider.value + "px"; });
    label.textContent = slider.value + "px";
  });
});
</script>

<h3 id="2-why-i-built-it"><em>2. Why I Built It</em></h3>
<p>I tried a bunch of fitness apps and they were either too complicated, too expensive, or trying to be everything at once. I just wanted something clean that lets me log how I’m feeling, see how I’m trending, and stay on top of my condition. So I decided to build my own.</p>

<p>It also gave me a good excuse to learn mobile development and work on something outside of finance and data for a change.</p>

<h3 id="3-core-features--plans"><em>3. Core Features &amp; Plans</em></h3>

<p>Everyone gets a solid baseline — no paywall for the essentials.</p>

<ul>
  <li><strong>Daily Condition Insights</strong> — Algorithmic analysis of your physical condition each day.</li>
  <li><strong>Dashboard Trends</strong> — Track how your condition changes over time with clean visuals.</li>
  <li><strong>AI Condition Interpretation</strong> — Refined, personalized breakdowns of what your condition data actually means.</li>
  <li><strong>AI Activity Insights</strong> — Smart summaries of your training activity, challenge progress, and leaderboard standings.</li>
  <li><strong>Enhanced Pro Dashboard</strong> — More metrics, more detail, better views.</li>
  <li><strong>Challenges &amp; Leaderboards</strong> — Compete with friends or your team to stay motivated.</li>
</ul>

<h4 id="-superset-dashboards">📊 Superset Dashboards</h4>
<p>On top of the mobile app, I’m building <strong>Apache Superset dashboards</strong> for both personal and group use — giving coaches and teams a powerful way to visualize condition and performance data at scale.</p>

<div class="img-row-wrap">
  <div class="img-row-controls">
    <label for="img-size-2">Size:</label>
    <input type="range" id="img-size-2" min="100" max="500" value="250" />
    <span id="img-size-label-2">250px</span>
  </div>
  <div class="img-row" id="img-row-2">
    <img src="https://swjeong.com/assets/images/onwrd/onwrdipad1.png" alt="ONWRD screenshot 1" />
    <img src="https://swjeong.com/assets/images/onwrd/onwrdipad2.png" alt="ONWRD screenshot 2" />
    <img src="https://swjeong.com/assets/images/onwrd/onwrdipad3.png" alt="ONWRD screenshot 3" />
    <img src="https://swjeong.com/assets/images/onwrd/onwrdipad4.png" alt="ONWRD screenshot 4" />
  </div>
</div>

<style>
.img-row-wrap { margin: 1.5em 0; }
.img-row-controls { display: flex; align-items: center; gap: 0.5em; margin-bottom: 0.6em; font-size: 0.85em; }
.img-row-controls label { font-weight: 600; }
.img-row-controls input[type=range] { width: 130px; }
.img-row { display: flex; flex-wrap: wrap; gap: 8px; }
.img-row img { width: 250px; height: auto; border-radius: 6px; }
</style>

<script>
document.addEventListener("DOMContentLoaded", function () {
  var slider2 = document.getElementById("img-size-2");
  var label2 = document.getElementById("img-size-label-2");
  var row2 = document.getElementById("img-row-2");
  slider2.addEventListener("input", function () {
    row2.querySelectorAll("img").forEach(function (img) { img.style.width = slider2.value + "px"; });
    label2.textContent = slider2.value + "px";
  });
});
</script>

<h3 id="4-tech-stack"><em>4. Tech Stack</em></h3>
<blockquote>
  <p>Keeping it lean and practical.</p>
</blockquote>

<table>
  <thead>
    <tr>
      <th>Layer</th>
      <th>Tech</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Frontend</td>
      <td>Flutter/Superset</td>
    </tr>
    <tr>
      <td>Backend</td>
      <td>Python</td>
    </tr>
    <tr>
      <td>Database</td>
      <td>PostgreSQL</td>
    </tr>
    <tr>
      <td>Hosting</td>
      <td>Apple</td>
    </tr>
    <tr>
      <td>Server</td>
      <td>AWS/GCP/Supabase/Firebase</td>
    </tr>
  </tbody>
</table>

<p>Flutter handles the mobile frontend –&gt; one codebase for both iOS and Android, which saves a ton of time. For the dashboards, I’m using Apache Superset to give users (and coaches) rich, interactive data visualizations for both personal and group views.</p>

<p>The backend is Python –&gt; it powers the REST API, the AI-driven condition and activity insights, and all the data processing behind the scenes. PostgreSQL is the main database, and on the infrastructure side I’m using a mix of AWS, GCP, Supabase, and Firebase depending on the service - auth, storage, push notifications, hosting, etc.</p>

<h3 id="5-what-i-learned"><em>5. What I Learned</em></h3>
<p>Building a full-stack mobile app from scratch taught me a lot. A few things that stood out:</p>

<ul>
  <li><strong>Flutter is fast to build with, but has its quirks</strong> — Hot reload is a game-changer for UI work, but managing state across screens and handling platform-specific behavior (iOS vs Android) took real effort to get right.</li>
  <li><strong>Superset is powerful but needs tuning</strong> — Out of the box it does a lot, but customizing dashboards for both personal and group use cases meant digging into configs, permissions, and embedding options.</li>
  <li><strong>Python ties everything together</strong> — From the API layer to AI processing to data pipelines, Python made it easy to move fast. The ecosystem around ML and data analysis was a huge advantage for building the condition insights.</li>
  <li><strong>Multi-cloud is flexible but complex</strong> — Mixing AWS, GCP, Supabase, and Firebase gave me the best tool for each job, but keeping auth, data, and deployments in sync across providers was the hardest infra challenge.</li>
  <li><strong>Ship early, iterate often</strong> — I spent too long polishing the first version. Should’ve gotten real user feedback sooner.</li>
</ul>

<h3 id="6-whats-next"><em>6. What’s Next</em></h3>
<p>A few things on the roadmap:</p>

<ul>
  <li><strong>Android launch</strong> — Currently iOS-only, Android is next.</li>
  <li><strong>Advanced analytics</strong> — Deeper breakdowns of long-term performance patterns.</li>
  <li><strong>Coach tools</strong> — Dedicated features for coaches managing multiple athletes.</li>
</ul>

<p>If you’re interested in trying it out or have feedback, feel free to reach out!</p>]]></content><author><name>Seungwon(Owen) Jeong</name></author><category term="Projects" /><summary type="html"><![CDATA[[Info] ONWRD Website: https://www.goonwrd.com]]></summary></entry><entry><title type="html">SQL RLS Rule</title><link href="https://swjeong.com/coding/rls_rule/" rel="alternate" type="text/html" title="SQL RLS Rule" /><published>2026-01-10T00:00:00+00:00</published><updated>2026-01-10T00:00:00+00:00</updated><id>https://swjeong.com/coding/rls_rule</id><content type="html" xml:base="https://swjeong.com/coding/rls_rule/"><![CDATA[<h1 id="postgresql-row-level-security-rls">PostgreSQL Row-Level Security (RLS)</h1>
<hr />

<h3 id="1-what-is-rls"><em>1. What is RLS?</em></h3>
<p>Row-Level Security is a PostgreSQL feature that lets you control <strong>which rows</strong> a user can see or modify in a table — at the database level. Instead of filtering data in your app code, the database itself enforces the rules.</p>

<p>This is especially useful when:</p>
<ul>
  <li>Multiple users share the same table (multi-tenant apps)</li>
  <li>You want to guarantee data isolation regardless of how the data is queried</li>
  <li>You’re building on top of platforms like <strong>Supabase</strong> that rely heavily on RLS</li>
  <li>You’re using <strong>Apache Superset</strong> to serve dashboards to different users or teams and need each person to only see their own data</li>
</ul>

<h3 id="2-how-it-works"><em>2. How It Works</em></h3>
<p>RLS boils down to three steps:</p>

<ol>
  <li><strong>Enable RLS</strong> on a table</li>
  <li><strong>Create policies</strong> that define who can see/do what</li>
  <li>PostgreSQL <strong>automatically applies</strong> those policies to every query</li>
</ol>

<p>Without a policy, <strong>no rows are returned</strong> once RLS is enabled — it’s deny-by-default.</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">-- Step 1: Enable RLS on the table</span>
<span class="k">ALTER</span> <span class="k">TABLE</span> <span class="n">workouts</span> <span class="n">ENABLE</span> <span class="k">ROW</span> <span class="k">LEVEL</span> <span class="k">SECURITY</span><span class="p">;</span>

<span class="c1">-- Step 2: Create a policy</span>
<span class="k">CREATE</span> <span class="n">POLICY</span> <span class="n">user_isolation</span> <span class="k">ON</span> <span class="n">workouts</span>
    <span class="k">FOR</span> <span class="k">ALL</span>
    <span class="k">USING</span> <span class="p">(</span><span class="n">user_id</span> <span class="o">=</span> <span class="n">current_setting</span><span class="p">(</span><span class="s1">'app.current_user_id'</span><span class="p">)::</span><span class="n">uuid</span><span class="p">);</span>
</code></pre></div></div>

<p>Now every <code class="language-plaintext highlighter-rouge">SELECT</code>, <code class="language-plaintext highlighter-rouge">UPDATE</code>, or <code class="language-plaintext highlighter-rouge">DELETE</code> on <code class="language-plaintext highlighter-rouge">workouts</code> only returns rows where <code class="language-plaintext highlighter-rouge">user_id</code> matches the current session’s user.</p>

<h3 id="3-policy-types"><em>3. Policy Types</em></h3>

<p>PostgreSQL supports different policy types depending on the operation:</p>

<table>
  <thead>
    <tr>
      <th>Policy Clause</th>
      <th>Controls</th>
      <th>When It’s Checked</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">USING</code></td>
      <td>Which rows are <strong>visible</strong></td>
      <td><code class="language-plaintext highlighter-rouge">SELECT</code>, <code class="language-plaintext highlighter-rouge">UPDATE</code>, <code class="language-plaintext highlighter-rouge">DELETE</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">WITH CHECK</code></td>
      <td>Which rows can be <strong>written</strong></td>
      <td><code class="language-plaintext highlighter-rouge">INSERT</code>, <code class="language-plaintext highlighter-rouge">UPDATE</code></td>
    </tr>
  </tbody>
</table>

<p>You can also scope policies to specific commands:</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">-- Read-only: users can only SELECT their own rows</span>
<span class="k">CREATE</span> <span class="n">POLICY</span> <span class="n">select_own</span> <span class="k">ON</span> <span class="n">workouts</span>
    <span class="k">FOR</span> <span class="k">SELECT</span>
    <span class="k">USING</span> <span class="p">(</span><span class="n">user_id</span> <span class="o">=</span> <span class="n">current_setting</span><span class="p">(</span><span class="s1">'app.current_user_id'</span><span class="p">)::</span><span class="n">uuid</span><span class="p">);</span>

<span class="c1">-- Insert: users can only insert rows for themselves</span>
<span class="k">CREATE</span> <span class="n">POLICY</span> <span class="n">insert_own</span> <span class="k">ON</span> <span class="n">workouts</span>
    <span class="k">FOR</span> <span class="k">INSERT</span>
    <span class="k">WITH</span> <span class="k">CHECK</span> <span class="p">(</span><span class="n">user_id</span> <span class="o">=</span> <span class="n">current_setting</span><span class="p">(</span><span class="s1">'app.current_user_id'</span><span class="p">)::</span><span class="n">uuid</span><span class="p">);</span>

<span class="c1">-- Update: users can only update their own rows</span>
<span class="k">CREATE</span> <span class="n">POLICY</span> <span class="n">update_own</span> <span class="k">ON</span> <span class="n">workouts</span>
    <span class="k">FOR</span> <span class="k">UPDATE</span>
    <span class="k">USING</span> <span class="p">(</span><span class="n">user_id</span> <span class="o">=</span> <span class="n">current_setting</span><span class="p">(</span><span class="s1">'app.current_user_id'</span><span class="p">)::</span><span class="n">uuid</span><span class="p">)</span>
    <span class="k">WITH</span> <span class="k">CHECK</span> <span class="p">(</span><span class="n">user_id</span> <span class="o">=</span> <span class="n">current_setting</span><span class="p">(</span><span class="s1">'app.current_user_id'</span><span class="p">)::</span><span class="n">uuid</span><span class="p">);</span>

<span class="c1">-- Delete: users can only delete their own rows</span>
<span class="k">CREATE</span> <span class="n">POLICY</span> <span class="n">delete_own</span> <span class="k">ON</span> <span class="n">workouts</span>
    <span class="k">FOR</span> <span class="k">DELETE</span>
    <span class="k">USING</span> <span class="p">(</span><span class="n">user_id</span> <span class="o">=</span> <span class="n">current_setting</span><span class="p">(</span><span class="s1">'app.current_user_id'</span><span class="p">)::</span><span class="n">uuid</span><span class="p">);</span>
</code></pre></div></div>

<h3 id="4-real-world-example-multi-tenant-app"><em>4. Real-World Example: Multi-Tenant App</em></h3>

<p>Say you have a fitness app where each user should only see their own data. Here’s a full setup:</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">-- Create the table</span>
<span class="k">CREATE</span> <span class="k">TABLE</span> <span class="n">conditions</span> <span class="p">(</span>
    <span class="n">id</span> <span class="n">UUID</span> <span class="k">DEFAULT</span> <span class="n">gen_random_uuid</span><span class="p">()</span> <span class="k">PRIMARY</span> <span class="k">KEY</span><span class="p">,</span>
    <span class="n">user_id</span> <span class="n">UUID</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span>
    <span class="n">condition_date</span> <span class="nb">DATE</span> <span class="k">NOT</span> <span class="k">NULL</span><span class="p">,</span>
    <span class="n">score</span> <span class="nb">INTEGER</span><span class="p">,</span>
    <span class="n">notes</span> <span class="nb">TEXT</span><span class="p">,</span>
    <span class="n">created_at</span> <span class="n">TIMESTAMPTZ</span> <span class="k">DEFAULT</span> <span class="n">now</span><span class="p">()</span>
<span class="p">);</span>

<span class="c1">-- Enable RLS</span>
<span class="k">ALTER</span> <span class="k">TABLE</span> <span class="n">conditions</span> <span class="n">ENABLE</span> <span class="k">ROW</span> <span class="k">LEVEL</span> <span class="k">SECURITY</span><span class="p">;</span>

<span class="c1">-- Users can only access their own rows</span>
<span class="k">CREATE</span> <span class="n">POLICY</span> <span class="n">user_access</span> <span class="k">ON</span> <span class="n">conditions</span>
    <span class="k">FOR</span> <span class="k">ALL</span>
    <span class="k">USING</span> <span class="p">(</span><span class="n">user_id</span> <span class="o">=</span> <span class="n">auth</span><span class="p">.</span><span class="n">uid</span><span class="p">())</span>
    <span class="k">WITH</span> <span class="k">CHECK</span> <span class="p">(</span><span class="n">user_id</span> <span class="o">=</span> <span class="n">auth</span><span class="p">.</span><span class="n">uid</span><span class="p">());</span>
</code></pre></div></div>

<blockquote>
  <p><code class="language-plaintext highlighter-rouge">auth.uid()</code> is a Supabase helper that returns the authenticated user’s ID from the JWT. If you’re not on Supabase, use <code class="language-plaintext highlighter-rouge">current_setting('app.current_user_id')::uuid</code> or a similar session variable.</p>
</blockquote>

<h3 id="5-admin--service-role-bypass"><em>5. Admin / Service Role Bypass</em></h3>

<p>Sometimes you need a backend service or admin to access all rows. You can do this with <code class="language-plaintext highlighter-rouge">BYPASSRLS</code> or by creating permissive policies for specific roles:</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">-- Option 1: Grant a role the ability to bypass RLS entirely</span>
<span class="k">ALTER</span> <span class="k">ROLE</span> <span class="n">service_role</span> <span class="n">BYPASSRLS</span><span class="p">;</span>

<span class="c1">-- Option 2: Create a separate policy for admins</span>
<span class="k">CREATE</span> <span class="n">POLICY</span> <span class="n">admin_full_access</span> <span class="k">ON</span> <span class="n">conditions</span>
    <span class="k">FOR</span> <span class="k">ALL</span>
    <span class="k">TO</span> <span class="n">admin_role</span>
    <span class="k">USING</span> <span class="p">(</span><span class="k">true</span><span class="p">)</span>
    <span class="k">WITH</span> <span class="k">CHECK</span> <span class="p">(</span><span class="k">true</span><span class="p">);</span>
</code></pre></div></div>

<p>The table owner also bypasses RLS by default. To force policies on the owner too:</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">ALTER</span> <span class="k">TABLE</span> <span class="n">conditions</span> <span class="k">FORCE</span> <span class="k">ROW</span> <span class="k">LEVEL</span> <span class="k">SECURITY</span><span class="p">;</span>
</code></pre></div></div>

<h3 id="6-rls-with-supabase"><em>6. RLS with Supabase</em></h3>

<p>If you’re using Supabase, RLS is the primary security layer. A few patterns I use:</p>

<p><strong>Authenticated users see only their data:</strong></p>
<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">CREATE</span> <span class="n">POLICY</span> <span class="nv">"Users read own data"</span> <span class="k">ON</span> <span class="n">conditions</span>
    <span class="k">FOR</span> <span class="k">SELECT</span>
    <span class="k">TO</span> <span class="n">authenticated</span>
    <span class="k">USING</span> <span class="p">(</span><span class="n">user_id</span> <span class="o">=</span> <span class="n">auth</span><span class="p">.</span><span class="n">uid</span><span class="p">());</span>
</code></pre></div></div>

<p><strong>Allow inserts only for the authenticated user:</strong></p>
<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">CREATE</span> <span class="n">POLICY</span> <span class="nv">"Users insert own data"</span> <span class="k">ON</span> <span class="n">conditions</span>
    <span class="k">FOR</span> <span class="k">INSERT</span>
    <span class="k">TO</span> <span class="n">authenticated</span>
    <span class="k">WITH</span> <span class="k">CHECK</span> <span class="p">(</span><span class="n">user_id</span> <span class="o">=</span> <span class="n">auth</span><span class="p">.</span><span class="n">uid</span><span class="p">());</span>
</code></pre></div></div>

<p><strong>Public read access (e.g. leaderboards):</strong></p>
<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">CREATE</span> <span class="n">POLICY</span> <span class="nv">"Public leaderboard"</span> <span class="k">ON</span> <span class="n">leaderboard</span>
    <span class="k">FOR</span> <span class="k">SELECT</span>
    <span class="k">TO</span> <span class="n">anon</span><span class="p">,</span> <span class="n">authenticated</span>
    <span class="k">USING</span> <span class="p">(</span><span class="k">true</span><span class="p">);</span>
</code></pre></div></div>

<p><strong>Group/team access:</strong></p>
<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">CREATE</span> <span class="n">POLICY</span> <span class="nv">"Team members access"</span> <span class="k">ON</span> <span class="n">team_data</span>
    <span class="k">FOR</span> <span class="k">SELECT</span>
    <span class="k">USING</span> <span class="p">(</span>
        <span class="n">team_id</span> <span class="k">IN</span> <span class="p">(</span>
            <span class="k">SELECT</span> <span class="n">team_id</span> <span class="k">FROM</span> <span class="n">team_members</span>
            <span class="k">WHERE</span> <span class="n">member_id</span> <span class="o">=</span> <span class="n">auth</span><span class="p">.</span><span class="n">uid</span><span class="p">()</span>
        <span class="p">)</span>
    <span class="p">);</span>
</code></pre></div></div>

<h3 id="7-rls-in-apache-superset"><em>7. RLS in Apache Superset</em></h3>

<p>This is how I actually use RLS day-to-day. I run <strong>Apache Superset</strong> dashboards for ONWRD — personal dashboards for individual users and group dashboards for teams/coaches. The problem is everyone hits the same tables, so I need each user to only see their own data on the charts.</p>

<p>Superset has a built-in <strong>Row Level Security</strong> feature that works separately from PostgreSQL’s RLS. You configure it through the Superset UI:</p>

<p><strong>Settings → Row Level Security → + Add</strong></p>

<table>
  <thead>
    <tr>
      <th>Field</th>
      <th>What to set</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Name</td>
      <td>e.g. <code class="language-plaintext highlighter-rouge">User isolation - conditions</code></td>
    </tr>
    <tr>
      <td>Filter Type</td>
      <td>Regular</td>
    </tr>
    <tr>
      <td>Tables</td>
      <td>Select the tables this rule applies to</td>
    </tr>
    <tr>
      <td>Roles</td>
      <td>Which Superset roles are affected (e.g. <code class="language-plaintext highlighter-rouge">Gamma</code>, <code class="language-plaintext highlighter-rouge">Public</code>)</td>
    </tr>
    <tr>
      <td>Clause</td>
      <td>SQL WHERE clause, e.g. <code class="language-plaintext highlighter-rouge">user_id = ''</code></td>
    </tr>
  </tbody>
</table>

<p>Superset injects this clause as a <code class="language-plaintext highlighter-rouge">WHERE</code> filter on every query that touches those tables — so the charts automatically show only the relevant data.</p>

<p><strong>Example: Personal dashboard</strong></p>

<p>Each user logs into Superset and should only see their own condition scores:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Clause: user_id = ''
</code></pre></div></div>

<p>Or if you store a user ID mapping:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Clause: user_id = (SELECT id FROM users WHERE email = '')
</code></pre></div></div>

<p><strong>Example: Group/team dashboard</strong></p>

<p>Coaches should see data for all athletes in their team:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Clause: team_id IN (SELECT team_id FROM team_members WHERE coach_email = '')
</code></pre></div></div>

<p><strong>Superset RLS vs PostgreSQL RLS — which to use?</strong></p>

<table>
  <thead>
    <tr>
      <th> </th>
      <th>Superset RLS</th>
      <th>PostgreSQL RLS</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Where it runs</td>
      <td>Superset app layer</td>
      <td>Database level</td>
    </tr>
    <tr>
      <td>Scope</td>
      <td>Only Superset queries</td>
      <td>All queries (app, API, direct SQL)</td>
    </tr>
    <tr>
      <td>Config</td>
      <td>UI-based, per role</td>
      <td>SQL policies, per table</td>
    </tr>
    <tr>
      <td>Best for</td>
      <td>Dashboard-level filtering</td>
      <td>Full data isolation across all access paths</td>
    </tr>
  </tbody>
</table>

<p>I use <strong>both</strong> — Superset RLS for the dashboard layer (quick to set up, role-based), and PostgreSQL RLS on the Supabase side for the mobile app and API. Belt and suspenders.</p>

<blockquote>
  <p><strong>Tip:</strong> If your Superset service account connects to PostgreSQL with a role that has <code class="language-plaintext highlighter-rouge">BYPASSRLS</code>, Superset RLS is your only line of defense on the dashboard side. Make sure it’s set up correctly.</p>
</blockquote>

<h3 id="8-testing-rls-policies"><em>8. Testing RLS Policies</em></h3>

<p>Always test your policies. You can simulate a user session like this:</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">-- Set the session user</span>
<span class="k">SET</span> <span class="n">app</span><span class="p">.</span><span class="n">current_user_id</span> <span class="o">=</span> <span class="s1">'some-uuid-here'</span><span class="p">;</span>

<span class="c1">-- This should only return rows for that user</span>
<span class="k">SELECT</span> <span class="o">*</span> <span class="k">FROM</span> <span class="n">conditions</span><span class="p">;</span>

<span class="c1">-- Try accessing someone else's data (should return nothing)</span>
<span class="k">SET</span> <span class="n">app</span><span class="p">.</span><span class="n">current_user_id</span> <span class="o">=</span> <span class="s1">'different-uuid'</span><span class="p">;</span>
<span class="k">SELECT</span> <span class="o">*</span> <span class="k">FROM</span> <span class="n">conditions</span><span class="p">;</span>
</code></pre></div></div>

<p>In Supabase, you can test by calling the API with different JWT tokens and verifying the response only contains the expected rows.</p>

<h3 id="9-performance-considerations"><em>9. Performance Considerations</em></h3>

<ul>
  <li><strong>Index the columns used in policies</strong> — If your policy checks <code class="language-plaintext highlighter-rouge">user_id</code>, make sure it’s indexed. Otherwise every query does a full table scan.
    <div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">CREATE</span> <span class="k">INDEX</span> <span class="n">idx_conditions_user_id</span> <span class="k">ON</span> <span class="n">conditions</span><span class="p">(</span><span class="n">user_id</span><span class="p">);</span>
</code></pre></div>    </div>
  </li>
  <li><strong>Keep policies simple</strong> — Subqueries in <code class="language-plaintext highlighter-rouge">USING</code> clauses (like the team access example) can get expensive. Consider materializing group memberships if queries are slow.</li>
  <li><strong>Use <code class="language-plaintext highlighter-rouge">EXPLAIN ANALYZE</code></strong> — Check that the RLS filter is pushed down into the query plan efficiently.</li>
  <li><strong>Avoid too many policies</strong> — Multiple permissive policies on the same table are OR’d together, which can get complex. Restrictive policies are AND’d.</li>
</ul>

<h3 id="10-common-pitfalls"><em>10. Common Pitfalls</em></h3>

<ul>
  <li><strong>Forgetting to create a policy</strong> — RLS enabled + no policies = nobody can see anything. This catches people off guard.</li>
  <li><strong>Table owner bypass</strong> — The table owner skips RLS by default. Use <code class="language-plaintext highlighter-rouge">FORCE ROW LEVEL SECURITY</code> if you want policies to apply to everyone.</li>
  <li><strong>Joins leaking data</strong> — If table A has RLS but table B doesn’t, a join can expose filtered rows through B. Enable RLS on all tables that hold sensitive data.</li>
  <li><strong>Migrations breaking policies</strong> — Dropping and recreating a table removes its policies. Always re-apply RLS in your migration scripts.</li>
</ul>

<h3 id="11-summary"><em>11. Summary</em></h3>

<table>
  <thead>
    <tr>
      <th>Concept</th>
      <th>Key Point</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Enable RLS</td>
      <td><code class="language-plaintext highlighter-rouge">ALTER TABLE ... ENABLE ROW LEVEL SECURITY</code></td>
    </tr>
    <tr>
      <td>Default behavior</td>
      <td>Deny all — no rows returned without a policy</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">USING</code></td>
      <td>Controls which rows are visible (reads)</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">WITH CHECK</code></td>
      <td>Controls which rows can be written (inserts/updates)</td>
    </tr>
    <tr>
      <td>Admin bypass</td>
      <td><code class="language-plaintext highlighter-rouge">BYPASSRLS</code> role or permissive admin policy</td>
    </tr>
    <tr>
      <td>Force on owner</td>
      <td><code class="language-plaintext highlighter-rouge">FORCE ROW LEVEL SECURITY</code></td>
    </tr>
    <tr>
      <td>Superset RLS</td>
      <td>UI-based row filtering per role — great for dashboards</td>
    </tr>
    <tr>
      <td>Best practice</td>
      <td>Use both Superset RLS + PostgreSQL RLS for full coverage</td>
    </tr>
    <tr>
      <td>Performance</td>
      <td>Index policy columns, keep policies simple</td>
    </tr>
  </tbody>
</table>

<p>RLS is one of those features that, once you start using it, you wonder how you ever built multi-user apps without it. It moves the security boundary into the database where it belongs.</p>]]></content><author><name>Seungwon(Owen) Jeong</name></author><category term="Coding" /><summary type="html"><![CDATA[PostgreSQL Row-Level Security (RLS)]]></summary></entry><entry><title type="html">Deep dive into analysis</title><link href="https://swjeong.com/analysis/deepdive_analysis/" rel="alternate" type="text/html" title="Deep dive into analysis" /><published>2025-12-30T00:00:00+00:00</published><updated>2025-12-30T00:00:00+00:00</updated><id>https://swjeong.com/analysis/deepdive_analysis</id><content type="html" xml:base="https://swjeong.com/analysis/deepdive_analysis/"><![CDATA[<h1 id="-stock-analysis-workflow">📊 Stock Analysis Workflow</h1>

<blockquote>
  <p><strong>System:</strong> ML Analysis Pipeline · AI Valuation Engine · Schwab GEX Module · Agentic AI</p>
</blockquote>

<hr />

<h2 id="table-of-contents">Table of Contents</h2>

<ol>
  <li><a href="#1-system-architecture-overview">System Architecture Overview</a></li>
  <li><a href="#2-step-1--ticker-selection">Step 1 — Ticker Selection</a></li>
  <li><a href="#3-step-2--fundamental-analysis">Step 2 — Fundamental Analysis</a></li>
  <li><a href="#4-step-3--dcf-valuation-ai">Step 3 — DCF Valuation (AI)</a></li>
  <li><a href="#5-step-4--machine-learning-forecast">Step 4 — Machine Learning Forecast</a></li>
  <li><a href="#6-step-5--backtesting-ma-strategy">Step 5 — Backtesting (MA Strategy)</a></li>
  <li><a href="#7-step-6--sentiment-analysis">Step 6 — Sentiment Analysis</a></li>
  <li><a href="#8-step-7--quantitative-risk-analysis">Step 7 — Quantitative Risk Analysis</a></li>
  <li><a href="#9-step-8--macroeconomic-analysis">Step 8 — Macroeconomic Analysis</a></li>
  <li><a href="#10-step-9--gex-gamma-exposure">Step 9 — GEX (Gamma Exposure)</a></li>
  <li><a href="#11-step-10--weighted-score--signal">Step 10 — Weighted Score &amp; Signal</a></li>
  <li><a href="#12-step-11--ai-final-summary">Step 11 — AI Final Summary</a></li>
  <li><a href="#13-step-12--agentic-ai-alternative-pipeline">Step 12 — Agentic AI (Alternative Pipeline)</a></li>
  <li><a href="#14-output--email-report">Output — Email Report</a></li>
  <li><a href="#15-configuration-reference">Configuration Reference</a></li>
</ol>

<hr />

<h2 id="1-system-architecture-overview">1. System Architecture Overview</h2>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>┌─────────────────────────────────────────────────────────────────────┐
│                     fetch_ML_v2.py / ML_report_v2.py                │
│                                                                       │
│  [Ticker Selection]                                                   │
│       │                                                               │
│       ▼                                                               │
│  ┌─────────────┐  ┌───────────┐  ┌──────────┐  ┌────────────────┐   │
│  │ Fundamental │  │    DCF    │  │    ML    │  │  Backtesting   │   │
│  │  Analysis   │  │  (AI LLM) │  │ Forecast │  │  (MA Strategy) │   │
│  └──────┬──────┘  └─────┬─────┘  └────┬─────┘  └───────┬────────┘   │
│         │               │             │                 │             │
│  ┌──────▼──────┐  ┌──────▼──────┐  ┌──▼──────────┐  ┌──▼──────────┐ │
│  │  Sentiment  │  │    Quant    │  │    Macro    │  │    GEX      │ │
│  │  (FinBERT)  │  │ (QuantStats)│  │   (FRED)   │  │  (Schwab)   │ │
│  └──────┬──────┘  └─────┬───────┘  └────┬────────┘  └─────┬───────┘ │
│         │               │               │                  │          │
│         └───────────────┴───────────────┴──────────────────┘          │
│                                    │                                   │
│                          [Weighted Score]                             │
│                                    │                                   │
│                          [AI Summary Report]                          │
│                                    │                                   │
│                    [Email + Database Insert]                          │
└─────────────────────────────────────────────────────────────────────┘
</code></pre></div></div>

<hr />

<h2 id="2-step-1--ticker-selection">2. Step 1 — Ticker Selection</h2>

<p><strong>Source:</strong> Stock Screener Utility — Sector &amp; momentum-ranked stock picker</p>

<h3 id="how-it-works">How It Works</h3>

<p>The screener finds <strong>the single highest-momentum, highest-liquidity stock</strong> each day using Finviz.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>1. Sector finder
   └─ Overview → all sectors sorted by daily % change
   └─ Pick the TOP sector (best-performing sector today)

2. Ticker finder
   └─ Custom screener with filters:
       • Index:        Any
       • Country:      USA
       • Performance:  Today Up
       • Gap:          Up  (opening gap up)
   └─ Order by: % Change (descending)
   └─ Take top 25 results
   └─ Calculate Trade Value = Price × Volume
   └─ Sort by Trade Value (descending)
   └─ Return #1 ticker  ← highest momentum + highest liquidity
</code></pre></div></div>

<h3 id="screener-columns-pulled">Screener Columns Pulled</h3>

<table>
  <thead>
    <tr>
      <th>Column</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Ticker</td>
      <td>Symbol</td>
    </tr>
    <tr>
      <td>Price</td>
      <td>Current price</td>
    </tr>
    <tr>
      <td>Change</td>
      <td>Daily % change</td>
    </tr>
    <tr>
      <td>Volume</td>
      <td>Share volume</td>
    </tr>
    <tr>
      <td>P/E</td>
      <td>Price/Earnings</td>
    </tr>
    <tr>
      <td>EPS this Y</td>
      <td>EPS growth this year</td>
    </tr>
    <tr>
      <td>EPS next Y</td>
      <td>EPS growth next year</td>
    </tr>
    <tr>
      <td>Sales Q/Q</td>
      <td>Revenue growth QoQ</td>
    </tr>
    <tr>
      <td>P/FCF</td>
      <td>Price/Free Cash Flow</td>
    </tr>
    <tr>
      <td>P/B</td>
      <td>Price/Book</td>
    </tr>
    <tr>
      <td>ROI</td>
      <td>Return on Investment</td>
    </tr>
    <tr>
      <td>ROE</td>
      <td>Return on Equity</td>
    </tr>
  </tbody>
</table>

<h2 id="3-step-2--fundamental-analysis">3. Step 2 — Fundamental Analysis</h2>

<p><strong>Source:</strong> Fundamental Data Fetcher — Multi-source stock fundamentals aggregator (Finviz + yfinance)</p>

<h3 id="data-sources">Data Sources</h3>

<table>
  <thead>
    <tr>
      <th>Source</th>
      <th>Data</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>Finviz</strong> (<code class="language-plaintext highlighter-rouge">finvizfinance</code>)</td>
      <td>P/E, P/B, P/S, ROA, ROE, gross margin, operating margin, EPS growth, short float, insider own%</td>
    </tr>
    <tr>
      <td><strong>yfinance</strong></td>
      <td>Company info, valuation, earnings, balance sheet, analyst targets, insider transactions, earnings dates</td>
    </tr>
  </tbody>
</table>

<h3 id="key-metrics-collected">Key Metrics Collected</h3>

<p><strong>Valuation</strong></p>
<ul>
  <li>Market Cap, Enterprise Value</li>
  <li>Trailing P/E, Forward P/E, PEG Ratio</li>
  <li>Price/Book, Price/Sales, EV/EBITDA, EV/Revenue</li>
</ul>

<p><strong>Profitability</strong></p>
<ul>
  <li>EPS (trailing &amp; forward)</li>
  <li>Revenue &amp; Revenue Growth</li>
  <li>Gross Margins, Operating Margins, Profit Margins</li>
  <li>ROE (Return on Equity), ROA (Return on Assets)</li>
</ul>

<p><strong>Balance Sheet Health</strong></p>
<ul>
  <li>Total Cash, Total Debt, Debt/Equity Ratio</li>
  <li>Current Ratio</li>
  <li>Free Cash Flow, Operating Cash Flow</li>
</ul>

<p><strong>Price &amp; Targets</strong></p>
<ul>
  <li>Current Price</li>
  <li>Analyst Target (Low / Mean / Median / High)</li>
  <li>52-Week High/Low</li>
  <li>50-Day / 200-Day Moving Averages</li>
  <li>Dividend Yield, Payout Ratio</li>
</ul>

<p><strong>Qualitative</strong></p>
<ul>
  <li>Recent analyst recommendations (last 5)</li>
  <li>Insider transactions (last 5)</li>
  <li>Upcoming earnings dates</li>
</ul>

<hr />

<h2 id="4-step-3--dcf-valuation-ai">4. Step 3 — DCF Valuation (AI)</h2>

<p><strong>Source:</strong> AI-Powered DCF Valuation Engine — 7-step discounted cash flow analysis</p>

<h3 id="inputs-from-yfinance">Inputs (from yfinance)</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">ticker_yf</span><span class="p">.</span><span class="n">cashflow</span>        <span class="c1"># Cash flow statement
</span><span class="n">ticker_yf</span><span class="p">.</span><span class="n">balance_sheet</span>   <span class="c1"># Balance sheet
</span><span class="n">ticker_yf</span><span class="p">.</span><span class="n">income_stmt</span>     <span class="c1"># Income statement (P&amp;L)
</span><span class="n">company_info</span>              <span class="c1"># Key stats dict
</span></code></pre></div></div>

<h3 id="ai-dcf-methodology">AI DCF Methodology</h3>

<p>The AI model is instructed to follow this exact 7-step process:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Step 1 ─ Forecast 5 years of Free Cash Flow (FCF)
          • Based on historical FCF trend
          • Max 20% annual growth unless justified
          • No negative forecasted FCF

Step 2 ─ Terminal Value (Gordon Growth Model)
          TV = Final Year FCF × (1 + g) / (WACC - g)

Step 3 ─ WACC Calculation
          • Cost of Equity = CAPM: rf + β × (rm - rf)
          • Cost of Debt = avg interest rate × (1 - tax rate)
          • Capital structure = Debt / (Debt + Equity)
          • WACC = wE × rE + wD × rD

Step 4 ─ Discount all FCFs + Terminal Value to PV
          PV = FCF_t / (1 + WACC)^t

Step 5 ─ Subtract Net Debt
          Equity Value = Σ PV(FCF) + PV(TV) - Net Debt
          Net Debt = Total Debt - Cash

Step 6 ─ Intrinsic Share Price
          Price = Equity Value / Shares Outstanding

Step 7 ─ Output: Intrinsic value (USD total) + per-share price
</code></pre></div></div>

<h3 id="output">Output</h3>

<blockquote>
  <p><strong>Intrinsic Share Price: $XXX.XX</strong>
Comparison to current market price → overvalued / fair / undervalued</p>
</blockquote>

<hr />

<h2 id="5-step-4--machine-learning-forecast">5. Step 4 — Machine Learning Forecast</h2>

<p><strong>Source:</strong> ML Forecasting Engine — Bidirectional LSTM + XGBoost ensemble price predictor</p>

<h3 id="data-preparation">Data Preparation</h3>

<ul>
  <li><strong>Price history:</strong> 3 years from yfinance</li>
  <li><strong>Lookback window:</strong> 60 trading days</li>
  <li><strong>Forecast horizon:</strong> 20 trading days</li>
  <li><strong>Train/Test split:</strong> 80% / 20%</li>
</ul>

<h3 id="feature-engineering">Feature Engineering</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Price features
</span><span class="n">returns</span>          <span class="o">=</span> <span class="n">Close</span><span class="p">.</span><span class="n">pct_change</span><span class="p">()</span>
<span class="n">log_returns</span>      <span class="o">=</span> <span class="n">log</span><span class="p">(</span><span class="n">Close</span> <span class="o">/</span> <span class="n">Close</span><span class="p">.</span><span class="n">shift</span><span class="p">(</span><span class="mi">1</span><span class="p">))</span>

<span class="c1"># Moving averages (ratios — normalized)
</span><span class="n">sma_5</span><span class="o">/</span><span class="mi">10</span><span class="o">/</span><span class="mi">20</span><span class="o">/</span><span class="mi">50</span>   <span class="o">=</span> <span class="n">Close</span><span class="p">.</span><span class="n">rolling</span><span class="p">(</span><span class="n">window</span><span class="p">).</span><span class="n">mean</span><span class="p">()</span>
<span class="n">sma_ratio</span>        <span class="o">=</span> <span class="n">Close</span> <span class="o">/</span> <span class="n">sma</span>

<span class="c1"># Volatility
</span><span class="n">volatility_20</span>    <span class="o">=</span> <span class="n">returns</span><span class="p">.</span><span class="n">rolling</span><span class="p">(</span><span class="mi">20</span><span class="p">).</span><span class="n">std</span><span class="p">()</span>

<span class="c1"># RSI (14-period)
</span><span class="n">rsi</span> <span class="o">=</span> <span class="mi">100</span> <span class="o">-</span> <span class="p">(</span><span class="mi">100</span> <span class="o">/</span> <span class="p">(</span><span class="mi">1</span> <span class="o">+</span> <span class="n">avg_gain</span> <span class="o">/</span> <span class="n">avg_loss</span><span class="p">))</span>

<span class="c1"># MACD
</span><span class="n">macd</span>             <span class="o">=</span> <span class="n">EMA</span><span class="p">(</span><span class="mi">12</span><span class="p">)</span> <span class="o">-</span> <span class="n">EMA</span><span class="p">(</span><span class="mi">26</span><span class="p">)</span>
<span class="n">macd_signal</span>      <span class="o">=</span> <span class="n">EMA</span><span class="p">(</span><span class="n">macd</span><span class="p">,</span> <span class="mi">9</span><span class="p">)</span>

<span class="c1"># Bollinger Bands
</span><span class="n">bb_mid</span>           <span class="o">=</span> <span class="n">SMA</span><span class="p">(</span><span class="mi">20</span><span class="p">)</span>
<span class="n">bb_upper</span>         <span class="o">=</span> <span class="n">SMA</span><span class="p">(</span><span class="mi">20</span><span class="p">)</span> <span class="o">+</span> <span class="mi">2</span> <span class="err">×</span> <span class="n">std</span><span class="p">(</span><span class="mi">20</span><span class="p">)</span>
<span class="n">bb_lower</span>         <span class="o">=</span> <span class="n">SMA</span><span class="p">(</span><span class="mi">20</span><span class="p">)</span> <span class="o">-</span> <span class="mi">2</span> <span class="err">×</span> <span class="n">std</span><span class="p">(</span><span class="mi">20</span><span class="p">)</span>
<span class="n">bb_position</span>      <span class="o">=</span> <span class="p">(</span><span class="n">Close</span> <span class="o">-</span> <span class="n">bb_lower</span><span class="p">)</span> <span class="o">/</span> <span class="p">(</span><span class="n">bb_upper</span> <span class="o">-</span> <span class="n">bb_lower</span><span class="p">)</span>
</code></pre></div></div>

<h3 id="model-architecture">Model Architecture</h3>

<h4 id="lstm-primary">LSTM (Primary)</h4>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Input: (60 days × 1 feature — scaled Close price)
  ↓
Bidirectional LSTM (128 units) → BatchNorm → Dropout(0.3)
  ↓
LSTM (64 units) → BatchNorm → Dropout(0.3)
  ↓
LSTM (32 units) → BatchNorm → Dropout(0.2)
  ↓
Dense(16, ReLU) → Dense(1)

Optimizer:  Adam (lr=0.001)
Loss:       Huber (robust to outliers)
Callbacks:  EarlyStopping (patience=5), restore_best_weights=True
Epochs:     10 max
</code></pre></div></div>

<h4 id="xgboost-ensemble-pair">XGBoost (Ensemble Pair)</h4>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">XGBRegressor</span><span class="p">(</span>
    <span class="n">n_estimators</span><span class="o">=</span><span class="mi">100</span><span class="p">,</span>
    <span class="n">max_depth</span><span class="o">=</span><span class="mi">6</span><span class="p">,</span>
    <span class="n">learning_rate</span><span class="o">=</span><span class="mf">0.1</span><span class="p">,</span>
    <span class="n">subsample</span><span class="o">=</span><span class="mf">0.8</span><span class="p">,</span>
    <span class="n">colsample_bytree</span><span class="o">=</span><span class="mf">0.8</span>
<span class="p">)</span>
</code></pre></div></div>

<h4 id="ensemble">Ensemble</h4>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Final Prediction = (LSTM prediction + XGBoost prediction) / 2
</code></pre></div></div>

<h3 id="forecast-output">Forecast Output</h3>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>RMSE              : Root Mean Squared Error
MAE               : Mean Absolute Error
MAPE              : Mean Absolute Percentage Error
Model Accuracy    : max(0, 100 - MAPE) %
Forecast Trend    : "Up" or "Down"
Expected Return   : (last_forecast - first_forecast) / first_forecast
forecast_df       : DataFrame of 20-day price forecast
</code></pre></div></div>

<hr />

<h2 id="6-step-5--backtesting-ma-strategy">6. Step 5 — Backtesting (MA Strategy)</h2>

<p><strong>Source:</strong> MA Strategy Backtester — Vectorized moving average signal simulator</p>

<h3 id="strategy-logic">Strategy Logic</h3>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>For each MA window in [5, 7, 9, 12, 15, 20, 50]:
  ┌──────────────────────────────────────┐
  │ Entry Signal                         │
  │   |Price - MA| / MA &lt; 1%            │
  │   (price touching the moving average)│
  └──────────────────────────────────────┘
            │
            ▼
  ┌──────────────────────────────────────┐
  │ Exit Signal                          │
  │   Take Profit:  PnL ≥ +5%           │
  │   Stop Loss:    PnL ≤ -5%           │
  └──────────────────────────────────────┘
</code></pre></div></div>

<ul>
  <li><strong>Data:</strong> yfinance from <code class="language-plaintext highlighter-rouge">2022-01-01</code> to yesterday</li>
  <li><strong>Starting capital:</strong> $10,000</li>
  <li><strong>Benchmark:</strong> Buy-and-Hold return over same period</li>
</ul>

<h3 id="output-per-ma-window">Output Per MA Window</h3>

<table>
  <thead>
    <tr>
      <th>Metric</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">total_trades</code></td>
      <td>Number of trades executed</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">win_rate</code></td>
      <td>% of profitable trades</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">return_pct</code></td>
      <td>Total strategy return %</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">final_capital</code></td>
      <td>End capital from $10,000</td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">avg_holding_days</code></td>
      <td>Average days held per trade</td>
    </tr>
  </tbody>
</table>

<h3 id="selection-criteria">Selection Criteria</h3>

<p><strong>Best MA Window</strong> = highest <code class="language-plaintext highlighter-rouge">return_pct</code> across all windows
Feeds into: <code class="language-plaintext highlighter-rouge">best_sma</code> stored in database, <code class="language-plaintext highlighter-rouge">best_holding</code> days</p>

<hr />

<h2 id="7-step-6--sentiment-analysis">7. Step 6 — Sentiment Analysis</h2>

<p><strong>Source:</strong> Sentiment Analysis Pipeline — FinBERT NLP engine for news &amp; Reddit crowd signals</p>

<h3 id="sentiment-models">Sentiment Models</h3>

<table>
  <thead>
    <tr>
      <th>Model</th>
      <th>Role</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>FinBERT</strong> (<code class="language-plaintext highlighter-rouge">ProsusAI/finbert</code>)</td>
      <td>Primary — fine-tuned on financial text</td>
    </tr>
    <tr>
      <td><strong>VADER</strong> + custom finance lexicon</td>
      <td>Fallback if FinBERT unavailable</td>
    </tr>
  </tbody>
</table>

<h4 id="custom-finance-lexicon-vader-additions">Custom Finance Lexicon (VADER additions)</h4>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">'bullish'</span><span class="p">:</span> <span class="o">+</span><span class="mf">3.7</span>   <span class="s">'bearish'</span><span class="p">:</span> <span class="o">-</span><span class="mf">3.7</span>
<span class="s">'buy'</span><span class="p">:</span> <span class="o">+</span><span class="mf">4.0</span>       <span class="s">'sell'</span><span class="p">:</span> <span class="o">-</span><span class="mf">4.0</span>
<span class="s">'upgrade'</span><span class="p">:</span> <span class="o">+</span><span class="mf">3.0</span>   <span class="s">'downgrade'</span><span class="p">:</span> <span class="o">-</span><span class="mf">3.0</span>
<span class="s">'beat'</span><span class="p">:</span> <span class="o">+</span><span class="mf">2.5</span>      <span class="s">'miss'</span><span class="p">:</span> <span class="o">-</span><span class="mf">2.5</span>
<span class="s">'moon'</span><span class="p">:</span> <span class="o">+</span><span class="mf">4.0</span>      <span class="s">'crash'</span><span class="p">:</span> <span class="o">-</span><span class="mf">3.5</span>
<span class="s">'rally'</span><span class="p">:</span> <span class="o">+</span><span class="mf">2.5</span>     <span class="s">'squeeze'</span><span class="p">:</span> <span class="o">+</span><span class="mf">2.0</span>
</code></pre></div></div>

<h3 id="news-analysis">News Analysis</h3>

<p><strong>Sources:</strong></p>
<ul>
  <li><strong>Finviz</strong> — recent news headlines for the ticker</li>
  <li><strong>Yahoo Finance</strong> — <code class="language-plaintext highlighter-rouge">yf.Ticker(ticker).news</code></li>
</ul>

<p><strong>Process:</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Fetch headlines → Deduplicate → FinBERT scoring →
Average compound score → Return {score, pos%, neg%, neu%, headline_count}
</code></pre></div></div>

<h3 id="reddit-analysis">Reddit Analysis</h3>

<p><strong>Subreddits:</strong> <code class="language-plaintext highlighter-rouge">wallstreetbets</code>, <code class="language-plaintext highlighter-rouge">stocks</code>, <code class="language-plaintext highlighter-rouge">investing</code>, <code class="language-plaintext highlighter-rouge">stockmarket</code></p>

<p><strong>Filters:</strong></p>
<ul>
  <li>Upvote ratio ≥ 0.70</li>
  <li>Upvotes ≥ 20</li>
  <li>Checks post title + body + top comments</li>
</ul>

<p><strong>Process:</strong></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Parallel fetch (4 workers) across subreddits →
Match ticker symbol or company name →
FinBERT scoring on matching posts/comments →
Average compound score → Return {score, mention_count}
</code></pre></div></div>

<hr />

<h2 id="8-step-7--quantitative-risk-analysis">8. Step 7 — Quantitative Risk Analysis</h2>

<p><strong>Source:</strong> Quantitative Risk Analyzer — QuantStats performance metrics + Risk/Return vs S&amp;P 500 charting</p>

<h3 id="quantstats-metrics-from-2022-01-01">QuantStats Metrics (from <code class="language-plaintext highlighter-rouge">2022-01-01</code>)</h3>

<table>
  <thead>
    <tr>
      <th>Metric</th>
      <th>Meaning</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>Sharpe Ratio</strong></td>
      <td>Risk-adjusted return (rf = 4% annual)</td>
    </tr>
    <tr>
      <td><strong>Sortino Ratio</strong></td>
      <td>Downside-adjusted return</td>
    </tr>
    <tr>
      <td><strong>Calmar Ratio</strong></td>
      <td>CAGR / Max Drawdown</td>
    </tr>
    <tr>
      <td><strong>Max Drawdown</strong></td>
      <td>Worst peak-to-trough loss</td>
    </tr>
    <tr>
      <td><strong>Volatility</strong></td>
      <td>Annualized standard deviation of returns</td>
    </tr>
    <tr>
      <td><strong>Win Rate</strong></td>
      <td>% of positive daily returns</td>
    </tr>
    <tr>
      <td><strong>Profit Factor</strong></td>
      <td>Gross profit / Gross loss</td>
    </tr>
    <tr>
      <td><strong>CAGR</strong></td>
      <td>Compound Annual Growth Rate</td>
    </tr>
    <tr>
      <td><strong>Value at Risk (VaR)</strong></td>
      <td>95% worst-case daily loss</td>
    </tr>
    <tr>
      <td><strong>Expected Return</strong></td>
      <td>Mean daily return</td>
    </tr>
  </tbody>
</table>

<h3 id="risk-vs-return-chart">Risk vs Return Chart</h3>

<p><code class="language-plaintext highlighter-rouge">MarketVsticker.generate_vsticker()</code> produces a <strong>scatter plot</strong>:</p>
<ul>
  <li>X-axis: Risk (daily return std dev)</li>
  <li>Y-axis: Average return × 10</li>
  <li>Plots ticker vs S&amp;P 500</li>
  <li>Red dashed line = S&amp;P 500 slope (Sharpe reference line)</li>
</ul>

<blockquote>
  <p>Tickers <strong>above</strong> the S&amp;P line offer better risk-adjusted returns</p>
</blockquote>

<hr />

<h2 id="9-step-8--macroeconomic-analysis">9. Step 8 — Macroeconomic Analysis</h2>

<p><strong>Source:</strong> Macroeconomic Analysis Engine — FRED API data tracker with VIX and BofA Bear/Bull indicator scoring</p>

<h3 id="data-sources-1">Data Sources</h3>

<table>
  <thead>
    <tr>
      <th>Source</th>
      <th>Series</th>
      <th>Description</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>FRED API</strong></td>
      <td><code class="language-plaintext highlighter-rouge">GDP</code></td>
      <td>Gross Domestic Product</td>
    </tr>
    <tr>
      <td> </td>
      <td><code class="language-plaintext highlighter-rouge">FEDFUNDS</code></td>
      <td>Federal Funds Rate</td>
    </tr>
    <tr>
      <td> </td>
      <td><code class="language-plaintext highlighter-rouge">CPIAUCSL</code></td>
      <td>CPI — inflation</td>
    </tr>
    <tr>
      <td> </td>
      <td><code class="language-plaintext highlighter-rouge">PPIACO</code></td>
      <td>Producer Price Index</td>
    </tr>
    <tr>
      <td> </td>
      <td><code class="language-plaintext highlighter-rouge">DGS10</code></td>
      <td>10-Year Treasury Yield</td>
    </tr>
    <tr>
      <td> </td>
      <td><code class="language-plaintext highlighter-rouge">DGS2</code></td>
      <td>2-Year Treasury Yield</td>
    </tr>
    <tr>
      <td> </td>
      <td><code class="language-plaintext highlighter-rouge">UNRATE</code></td>
      <td>Unemployment Rate</td>
    </tr>
    <tr>
      <td> </td>
      <td><code class="language-plaintext highlighter-rouge">USSLIND</code></td>
      <td>Leading Economic Index</td>
    </tr>
    <tr>
      <td> </td>
      <td><code class="language-plaintext highlighter-rouge">HOUST</code></td>
      <td>Housing Starts</td>
    </tr>
    <tr>
      <td> </td>
      <td><code class="language-plaintext highlighter-rouge">PERMIT</code></td>
      <td>Building Permits</td>
    </tr>
    <tr>
      <td> </td>
      <td><code class="language-plaintext highlighter-rouge">UMCSENT</code></td>
      <td>Consumer Confidence</td>
    </tr>
    <tr>
      <td><strong>Yahoo Finance</strong></td>
      <td><code class="language-plaintext highlighter-rouge">^VIX</code></td>
      <td>Volatility Index</td>
    </tr>
    <tr>
      <td> </td>
      <td><code class="language-plaintext highlighter-rouge">SPY</code>, <code class="language-plaintext highlighter-rouge">QQQ</code>, <code class="language-plaintext highlighter-rouge">IWM</code></td>
      <td>Market breadth</td>
    </tr>
  </tbody>
</table>

<h3 id="vix-classification">VIX Classification</h3>

<table>
  <thead>
    <tr>
      <th>VIX Level</th>
      <th>Classification</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>&lt; 15</td>
      <td>Low (complacent)</td>
    </tr>
    <tr>
      <td>15–20</td>
      <td>Normal</td>
    </tr>
    <tr>
      <td>20–25</td>
      <td>Elevated</td>
    </tr>
    <tr>
      <td>25–30</td>
      <td>High</td>
    </tr>
    <tr>
      <td>&gt; 30</td>
      <td>Extreme (fear)</td>
    </tr>
  </tbody>
</table>

<h3 id="yield-curve-analysis">Yield Curve Analysis</h3>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Spread = 10Y Treasury Yield - 2Y Treasury Yield

Spread &lt; 0  →  ⚠️ INVERTED (Recession warning)
Spread &gt; 0  →  ✅ Normal
</code></pre></div></div>

<h3 id="market-breadth-spy--qqq--iwm">Market Breadth (SPY / QQQ / IWM)</h3>

<p>For each index: 1D, 1W, 1M, 3M, YTD returns + trend classification:</p>
<ul>
  <li>strong uptrend / uptrend / sideways / downtrend / strong downtrend</li>
</ul>

<h3 id="bofa-bear-indicators-10-signals--market-peak-warnings">BofA Bear Indicators (10 signals — Market Peak Warnings)</h3>

<table>
  <thead>
    <tr>
      <th>#</th>
      <th>Signal</th>
      <th>Threshold</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1</td>
      <td>Consumer Confidence</td>
      <td>&gt; 110 (prior 60mo)</td>
    </tr>
    <tr>
      <td>2</td>
      <td>Net % expecting stocks higher</td>
      <td>&gt; 20% (prior 6mo)</td>
    </tr>
    <tr>
      <td>3</td>
      <td>Sell-side indicator</td>
      <td>“Sell” signal</td>
    </tr>
    <tr>
      <td>4</td>
      <td>S&amp;P 500 LT growth 5yr Z-score</td>
      <td>&gt; 1</td>
    </tr>
    <tr>
      <td>5</td>
      <td>M&amp;A deals 10yr Z-score</td>
      <td>&gt; 1</td>
    </tr>
    <tr>
      <td>6</td>
      <td>(Trailing PE + YoY CPI) Z-score</td>
      <td>&gt; 1</td>
    </tr>
    <tr>
      <td>7</td>
      <td>Low PE underperforms High PE</td>
      <td>by 2.5ppt (6mo)</td>
    </tr>
    <tr>
      <td>8</td>
      <td><strong>Inverted yield curve</strong></td>
      <td>Spread &lt; 0</td>
    </tr>
    <tr>
      <td>9</td>
      <td>Credit stress indicator</td>
      <td>&lt; 0.25</td>
    </tr>
    <tr>
      <td>10</td>
      <td>Tightening credit (SLOOS)</td>
      <td>Triggered</td>
    </tr>
  </tbody>
</table>

<h3 id="bofa-bull-indicators-10-signals--market-bottom-signals">BofA Bull Indicators (10 signals — Market Bottom Signals)</h3>

<table>
  <thead>
    <tr>
      <th>#</th>
      <th>Signal</th>
      <th>Threshold</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>1</td>
      <td>Fed cutting rates</td>
      <td>Prior 12mo</td>
    </tr>
    <tr>
      <td>2</td>
      <td>Unemployment rising</td>
      <td>vs 12mo low</td>
    </tr>
    <tr>
      <td>3</td>
      <td>More bears than bulls</td>
      <td>AAII survey</td>
    </tr>
    <tr>
      <td>4</td>
      <td>ERP increase</td>
      <td>&gt; 75bps vs 12mo low</td>
    </tr>
    <tr>
      <td>5</td>
      <td>2Y yield decline</td>
      <td>&gt; 50bps vs 6mo high</td>
    </tr>
    <tr>
      <td>6</td>
      <td>Sell-side indicator</td>
      <td>“Buy” signal</td>
    </tr>
    <tr>
      <td>7</td>
      <td>Yield curve steepening</td>
      <td>vs 6mo low</td>
    </tr>
    <tr>
      <td>8</td>
      <td>5% bear market rally</td>
      <td>Prior 3mo</td>
    </tr>
    <tr>
      <td>9</td>
      <td>Rule of 20 triggered</td>
      <td>—</td>
    </tr>
    <tr>
      <td>10</td>
      <td>PMI improves</td>
      <td>vs 12mo YoY low</td>
    </tr>
  </tbody>
</table>

<h3 id="macro-sentiment-classification">Macro Sentiment Classification</h3>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Bear signals - Bull signals &gt; 2  →  BEARISH
Bull signals - Bear signals &gt; 2  →  BULLISH
Otherwise                        →  NEUTRAL
</code></pre></div></div>

<h3 id="macro-score-adjustment">Macro Score Adjustment</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="n">macro_sentiment</span> <span class="o">==</span> <span class="s">'bearish'</span> <span class="ow">or</span> <span class="n">bear_count</span> <span class="o">&gt;=</span> <span class="mi">5</span><span class="p">:</span>
    <span class="n">macro_adjustment</span> <span class="o">=</span> <span class="o">-</span><span class="mf">0.05</span>   <span class="c1"># Reduce buy conviction
</span><span class="k">elif</span> <span class="n">macro_sentiment</span> <span class="o">==</span> <span class="s">'bullish'</span> <span class="ow">or</span> <span class="n">bull_count</span> <span class="o">&gt;=</span> <span class="mi">5</span><span class="p">:</span>
    <span class="n">macro_adjustment</span> <span class="o">=</span> <span class="o">+</span><span class="mf">0.02</span>   <span class="c1"># Slightly increase conviction
</span>
<span class="k">if</span> <span class="n">VIX</span> <span class="o">&gt;</span> <span class="mi">30</span><span class="p">:</span>
    <span class="n">macro_adjustment</span> <span class="o">-=</span> <span class="mf">0.03</span>   <span class="c1"># Additional caution in fear spike
</span></code></pre></div></div>

<hr />

<h2 id="10-step-9--gex-gamma-exposure">10. Step 9 — GEX (Gamma Exposure)</h2>

<p><strong>Source:</strong> Schwab GEX Calculator — Dealer gamma exposure derived from live Schwab option chain feed<br />
<strong>API:</strong> Charles Schwab Live Market Data — Real-time option chain endpoint (<code class="language-plaintext highlighter-rouge">/marketdata/v1/chains</code>)</p>

<h3 id="what-is-gex">What Is GEX?</h3>

<p>GEX measures how much gamma market makers (dealers) hold, which predicts near-term market behavior:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>GEX Formula:
  Call GEX =  Gamma × Open Interest × 100 × Spot Price  (positive)
  Put  GEX = -Gamma × Open Interest × 100 × Spot Price  (negative)

Total GEX = Σ(Call GEX) + Σ(Put GEX) across all strikes
</code></pre></div></div>

<h3 id="market-condition-interpretation">Market Condition Interpretation</h3>

<table>
  <thead>
    <tr>
      <th>Condition</th>
      <th>Meaning</th>
      <th>Market Behavior</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>Positive GEX</strong> (Total &gt; 0)</td>
      <td>Dealers are <strong>long gamma</strong></td>
      <td>Mean-reverting, lower volatility, price pinned to large strikes</td>
    </tr>
    <tr>
      <td><strong>Negative GEX</strong> (Total &lt; 0)</td>
      <td>Dealers are <strong>short gamma</strong></td>
      <td>Trending/accelerating, higher volatility, moves amplified</td>
    </tr>
  </tbody>
</table>

<h3 id="key-gex-levels">Key GEX Levels</h3>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>GEX Flip Level  — Strike where cumulative GEX crosses zero
                  Acts as a market pivot / support-resistance

Support Level   — Highest |GEX| strike below current price
                  Large positive GEX below = dealers hedge by buying → support

Resistance Level — Highest |GEX| strike above current price
                   Large positive GEX above = dealers hedge by selling → resistance
</code></pre></div></div>

<h3 id="0dte-gex">0DTE GEX</h3>

<p>Special calculation for <strong>same-day expiration</strong> options only (highest gamma impact near expiry). Useful for intraday trading levels.</p>

<h3 id="gex-report-output">GEX Report Output</h3>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>═══════════════════════════════════
         GEX ANALYSIS: SPY
═══════════════════════════════════
📊 Current Price: $565.43
💰 Total GEX: +$2.14B

📈 Market Condition: POSITIVE_GAMMA
   Dealers long gamma → Market likely to mean-revert

🎯 Key Levels:
   • GEX Flip:    $558.00
   • Support:     $560.00
   • Resistance:  $570.00

📍 Top Positive GEX Strikes (Resistance):
   • $570.00: $420.5M
   • $575.00: $380.2M
   ...
</code></pre></div></div>

<h3 id="covered-call-screener-bonus">Covered Call Screener (bonus)</h3>

<p><strong>Source:</strong> Covered Call Screener — High-IV option income opportunity finder using Schwab option chains</p>

<p>Screens for high-IV covered call opportunities:</p>

<table>
  <thead>
    <tr>
      <th>Metric</th>
      <th>Formula</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Premium Yield</td>
      <td>bid / spot price</td>
    </tr>
    <tr>
      <td>Annualized Return</td>
      <td>premium_yield × 365/DTE</td>
    </tr>
    <tr>
      <td>Downside Protection</td>
      <td>bid / spot price</td>
    </tr>
    <tr>
      <td>Theta/Gamma Ratio</td>
      <td>θ / Γ (higher = better risk/reward)</td>
    </tr>
  </tbody>
</table>

<p><strong>Filters:</strong> DTE 14–60, OTM 1%–15%, min OI 10, configurable IV floor</p>

<hr />

<h2 id="11-step-10--weighted-score--signal">11. Step 10 — Weighted Score &amp; Signal</h2>

<p><strong>Source:</strong> ML Analysis Orchestrator — Weighted signal aggregator that combines all module outputs with macro adjustment</p>

<h3 id="score-weights">Score Weights</h3>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>                    ┌─────────────────────────────────────────────────┐
                    │           WEIGHTED SCORE CALCULATION            │
                    │                                                  │
   News Sentiment   │  score × 0.30  (FinBERT compound, range -1 to 1)│
   Reddit Sentiment │  score × 0.20  (FinBERT compound, range -1 to 1)│
   ML Forecast      │  return × 0.30 (expected 20-day return decimal) │
   Buy-and-Hold     │  return × 0.20 (historical BnH return decimal)  │
                    │  ─────────────────────────────────────────────── │
                    │  raw_score  = sum of above                       │
                    │  + macro_adjustment  (-0.08 to +0.02)            │
                    │  ─────────────────────────────────────────────── │
                    │  weighted_score = raw_score + macro_adjustment   │
                    └─────────────────────────────────────────────────┘
</code></pre></div></div>

<h3 id="signal-decision">Signal Decision</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="n">weighted_score</span> <span class="o">&gt;</span> <span class="mf">0.5</span><span class="p">:</span>
    <span class="n">signal</span> <span class="o">=</span> <span class="s">"Buy"</span>
<span class="k">else</span><span class="p">:</span>
    <span class="n">signal</span> <span class="o">=</span> <span class="s">"Hold"</span>
</code></pre></div></div>

<h3 id="database-record-tk_selected-table">Database Record (<code class="language-plaintext highlighter-rouge">tk_selected</code> table)</h3>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">INSERT</span> <span class="k">INTO</span> <span class="n">tk_selected</span> <span class="p">(</span><span class="nb">date</span><span class="p">,</span> <span class="n">ticker</span><span class="p">,</span> <span class="n">score</span><span class="p">,</span> <span class="n">news</span><span class="p">,</span> <span class="n">reddit</span><span class="p">,</span> <span class="n">lstm</span><span class="p">,</span> <span class="n">holding_days</span><span class="p">,</span> <span class="n">sma</span><span class="p">,</span> <span class="k">result</span><span class="p">)</span>
<span class="k">VALUES</span> <span class="p">(</span><span class="n">today</span><span class="p">,</span> <span class="n">ticker</span><span class="p">,</span> <span class="n">weighted_score</span><span class="p">,</span> <span class="n">news_score</span><span class="p">,</span> <span class="n">reddit_score</span><span class="p">,</span> <span class="n">forecast_trend</span><span class="p">,</span> <span class="n">best_holding</span><span class="p">,</span> <span class="n">best_ma</span><span class="p">,</span> <span class="n">signal</span><span class="p">)</span>
</code></pre></div></div>

<hr />

<h2 id="12-step-11--ai-final-summary">12. Step 11 — AI Final Summary</h2>

<p><strong>Source:</strong> AI Investment Report Generator — Multi-factor synthesis engine that produces a buy-side equity memo</p>

<h3 id="inputs-fed-to-ai-model">Inputs Fed to AI Model</h3>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>═══ FUNDAMENTALS ═══        (Finviz + yfinance table)
═══ TECHNICAL CHART ═══     (Matplotlib chart: price + MA + forecast + sentiment + backtest)
═══ ML ANALYSIS REPORT ═══  (Full text report from generate_report())
═══ QUANTITATIVE METRICS ══ (QuantStats summary)
═══ MACROECONOMIC ANALYSIS ═(FRED + VIX + BofA signals)
═══ GEX ANALYSIS ═══        (Schwab GEX report)
═══ DCF VALUATION ═══       (AI DCF output)
</code></pre></div></div>

<h3 id="output-structure">Output Structure</h3>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>1. Executive Summary (1-2 paragraphs)
   └─ Valuation, market position, technicals, macro, sentiment

2. Macroeconomic Context
   └─ VIX level, yield curve status, market breadth (SPY/QQQ/IWM)
   └─ BofA Bear/Bull signals
   └─ Risk-on / Risk-off assessment

3. GEX Analysis
   └─ Positive vs Negative gamma interpretation
   └─ Key GEX support/resistance/flip levels
   └─ Position sizing guidance from GEX

4. Fundamental Strengths &amp; Risks (bullet points)

5. Sentiment Assessment
   └─ News vs Reddit divergence analysis

6. Risk/Return Positioning
   └─ Volatility, drawdowns, win rate

7. Model Alignment
   └─ MA backtest vs LSTM forecast alignment

8. DCF Comparison
   └─ Intrinsic value vs current price

9. ─────────────────────────────────────────────────────
   🟡 Final Recommendation (as of YYYY-MM-DD)
   ▶ Position: BUY / HOLD / SELL
   ▶ Macro Alignment: FAVORABLE / NEUTRAL / UNFAVORABLE
   ▶ GEX Condition: POSITIVE GAMMA / NEGATIVE GAMMA

   Key Notes:
   - Macro Context: [VIX, yield curve, market trend]
   - GEX Levels: [Support $X, Resistance $Y, Flip $Z]
   - Tactical entry/exit ranges
   - Position sizing guidance
   - Position management (trailing stop, profit target)
   - 1-line Summary Statement
   ─────────────────────────────────────────────────────
</code></pre></div></div>

<hr />

<h2 id="13-step-12--agentic-ai-alternative-pipeline">13. Step 12 — Agentic AI (Alternative Pipeline)</h2>

<p><strong>Source:</strong> Agentic AI Orchestrator &amp; Specialist Agents — LLM-first sequential analysis pipeline with four domain-expert personas</p>

<p>This is an <strong>LLM-first</strong> alternative to the quantitative pipeline, useful for deeper qualitative analysis.</p>

<h3 id="agent-execution-flow">Agent Execution Flow</h3>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>User provides ticker(s)
        │
        ▼
┌─────────────────────────────────────────────────────────────────┐
│  Step 1: Macro Analyst (runs ONCE, shared across all tickers)   │
│          Skill: macroeconomic (FRED + VIX + market data)         │
│          Output: RISK-ON / RISK-OFF / TRANSITIONAL              │
└───────────────────────────────┬─────────────────────────────────┘
                                │
                                ▼
┌─────────────────────────────────────────────────────────────────┐
│  Step 2: Fundamental Analyst (per ticker)                        │
│          Skills: microeconomic + qualitative                     │
│          Data: yfinance financials, DCF, sector comps, news      │
│          Output: OVERVALUED / FAIR / UNDERVALUED + catalysts     │
└───────────────────────────────┬─────────────────────────────────┘
                                │
                                ▼
┌─────────────────────────────────────────────────────────────────┐
│  Step 3: Quant Analyst (per ticker)                              │
│          Skill: quantitative                                     │
│          Data: Price history, MA structure, RSI/MACD, Sharpe,   │
│                max drawdown, beta, LSTM forecasts, GEX           │
│          Output: Technical signal + risk metrics + entry/exit    │
└───────────────────────────────┬─────────────────────────────────┘
                                │
                                ▼
┌─────────────────────────────────────────────────────────────────┐
│  Step 4: Portfolio Strategist (synthesis)                        │
│          Receives: all 3 agent analyses                          │
│          Output: BUY/HOLD/SELL + conviction (1-10) +            │
│                  position size + risk parameters                 │
└─────────────────────────────────────────────────────────────────┘
</code></pre></div></div>

<h3 id="agent-prompting-philosophy">Agent Prompting Philosophy</h3>

<p>Each agent has a specific <strong>system prompt persona</strong>:</p>

<table>
  <thead>
    <tr>
      <th>Agent</th>
      <th>Persona</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Macro Analyst</td>
      <td>Senior macro strategist at a global macro hedge fund</td>
    </tr>
    <tr>
      <td>Fundamental Analyst</td>
      <td>Buy-side equity research analyst at a top-tier investment firm</td>
    </tr>
    <tr>
      <td>Quant Analyst</td>
      <td>Quantitative portfolio analyst at a systematic trading firm</td>
    </tr>
    <tr>
      <td>Portfolio Strategist</td>
      <td>Chief Investment Officer at a multi-strategy fund</td>
    </tr>
  </tbody>
</table>

<h3 id="running-the-agentic-pipeline">Running the Agentic Pipeline</h3>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">agentic_ai.orchestrator</span> <span class="kn">import</span> <span class="n">Orchestrator</span>

<span class="n">orchestrator</span> <span class="o">=</span> <span class="n">Orchestrator</span><span class="p">()</span>

<span class="c1"># Single ticker
</span><span class="n">result</span> <span class="o">=</span> <span class="n">orchestrator</span><span class="p">.</span><span class="n">analyze_ticker</span><span class="p">(</span><span class="s">"AAPL"</span><span class="p">)</span>

<span class="c1"># Multiple tickers (macro cached and shared)
</span><span class="n">results</span> <span class="o">=</span> <span class="n">orchestrator</span><span class="p">.</span><span class="n">analyze_portfolio</span><span class="p">([</span><span class="s">"AAPL"</span><span class="p">,</span> <span class="s">"TSLA"</span><span class="p">,</span> <span class="s">"NVDA"</span><span class="p">])</span>
</code></pre></div></div>

<hr />

<h2 id="14-output--email-report">14. Output — Email Report</h2>

<p><strong>Source:</strong> Email Report Dispatcher — HTML report builder and Gmail SMTP sender</p>

<p>The HTML email contains all sections assembled:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Subject: "Daily Stock Report: {TICKER}, {Company Name}"

HTML Email Sections:
┌──────────────────────────────────────────────────────────┐
│ 1. ML Analysis Text Report (pre-formatted text)           │
│ 2. Analysis Dashboard Chart (2×2 matplotlib grid):        │
│    ├─ [Top-Left]  Price history (6mo) + MA20/MA50         │
│    ├─ [Top-Right] LSTM 20-day price forecast               │
│    ├─ [Bot-Left]  Sentiment bar chart (News/Reddit/Score) │
│    └─ [Bot-Right] MA backtest returns vs win rates        │
│ 3. Risk vs Return Chart (vs S&amp;P 500 scatter plot)         │
│ 4. Fundamental Data Table (Finviz HTML table)             │
│ 5. QuantStats Summary                                     │
│ 6. AI DCF Valuation Analysis                              │
│ 7. AI Final Summary (with GEX &amp; Macro)                   │
│ 8. Macro Analysis Report                                  │
│ 9. GEX Report (Schwab option chain analysis)              │
│ 10. Covered Call Screener (optional)                      │
└──────────────────────────────────────────────────────────┘
</code></pre></div></div>

<hr />

<h2 id="15-configuration-reference">15. Configuration Reference</h2>

<p><strong>Source:</strong> Analysis Configuration Dataclass — All tunable parameters for the ML pipeline</p>

<h2 id="quick-reference-summary">Quick Reference Summary</h2>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>TICKER SELECTION  →  Finviz: top sector + highest Trade Value stock
FUNDAMENTAL       →  Finviz + yfinance: P/E, margins, targets, insider
DCF               →  AI model: 5yr FCF forecast → WACC → intrinsic price
ML FORECAST       →  LSTM + XGBoost: 60-day lookback → 20-day price forecast
BACKTEST          →  MA strategy (5–50): touch MA → 5% TP / -5% SL
SENTIMENT         →  FinBERT on Finviz news + Reddit (4 subreddits)
QUANT RISK        →  QuantStats: Sharpe, Sortino, VaR, CAGR + Risk/Return plot
MACRO             →  FRED: yields, CPI, unemployment + VIX + BofA signals
GEX               →  Schwab API: dealer gamma → support/resistance/flip levels
WEIGHTED SCORE    →  0.3(news) + 0.2(reddit) + 0.3(ML) + 0.2(BnH) ± macro adj
SIGNAL            →  Score &gt; 0.5 → BUY, else HOLD
AI SUMMARY        →  All data → AI model → final recommendation with entry/exit
</code></pre></div></div>

<h2 id="16-conclusion">16. Conclusion</h2>

<p>That’s the entire workflow from ticker selection all the way through to a final buy &amp; hold decision with covered calls on top. Every step feeds into the next: the screener narrows the universe, fundamentals and DCF set a fair-value anchor, ML and backtesting add a forward-looking edge, sentiment captures the crowd’s mood, macro and GEX keep me aware of the bigger picture, and the weighted score ties it all together into a single actionable signal.</p>

<p>Is it perfect? No. Markets will always surprise you. But having a structured, repeatable process removes most of the emotion from the decision and lets the data do the heavy lifting. The agentic AI pipeline is a newer addition that gives me a second opinion, a qualitative lens on top of the quantitative one and I’ve found the two complement each other well.</p>

<p>Building this system has been one of the most rewarding projects I’ve worked on. If you made it this far, I hope it gave you some ideas for your own workflow. Happy investing.</p>]]></content><author><name>Seungwon(Owen) Jeong</name></author><category term="Analysis" /><summary type="html"><![CDATA[📊 Stock Analysis Workflow]]></summary></entry><entry><title type="html">AWS Data Engineering</title><link href="https://swjeong.com/cloud/aws_data_engineering/" rel="alternate" type="text/html" title="AWS Data Engineering" /><published>2025-11-10T00:00:00+00:00</published><updated>2025-11-10T00:00:00+00:00</updated><id>https://swjeong.com/cloud/aws_data_engineering</id><content type="html" xml:base="https://swjeong.com/cloud/aws_data_engineering/"><![CDATA[<h1 id="aws-data-engineering">AWS Data Engineering</h1>
<hr />

<h3 id="1-overview"><em>1. Overview</em></h3>
<p>This post walks through how I set up a data engineering pipeline on AWS. The goal was to build a reliable, scalable system that ingests raw data, transforms it, and serves it up for analytics — all using managed AWS services so I’m not babysitting servers.</p>

<p>The pipeline follows a classic <strong>Extract → Transform → Load (ETL)</strong> pattern, with a few extras like orchestration, monitoring, and cost controls baked in.</p>

<h3 id="2-architecture"><em>2. Architecture</em></h3>

<p>Here’s the high-level flow:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Data Sources → S3 (Raw) → Glue (Transform) → S3 (Processed) → Athena / Redshift → Dashboard
                                  ↑
                          Step Functions (Orchestration)
                                  ↑
                         EventBridge (Scheduling)
</code></pre></div></div>

<table>
  <thead>
    <tr>
      <th>Component</th>
      <th>AWS Service</th>
      <th>Role</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Storage</td>
      <td>S3</td>
      <td>Landing zone for raw and processed data</td>
    </tr>
    <tr>
      <td>Catalog</td>
      <td>Glue Data Catalog</td>
      <td>Schema discovery and metadata management</td>
    </tr>
    <tr>
      <td>ETL</td>
      <td>Glue Jobs (PySpark)</td>
      <td>Data cleaning, transformation, aggregation</td>
    </tr>
    <tr>
      <td>Orchestration</td>
      <td>Step Functions</td>
      <td>Coordinate multi-step ETL workflows</td>
    </tr>
    <tr>
      <td>Scheduling</td>
      <td>EventBridge</td>
      <td>Trigger pipelines on a schedule or event</td>
    </tr>
    <tr>
      <td>Query</td>
      <td>Athena</td>
      <td>Serverless SQL queries on S3 data</td>
    </tr>
    <tr>
      <td>Warehouse</td>
      <td>Redshift (optional)</td>
      <td>Heavy analytical workloads</td>
    </tr>
    <tr>
      <td>Monitoring</td>
      <td>CloudWatch</td>
      <td>Logs, metrics, and alerts</td>
    </tr>
    <tr>
      <td>IAM</td>
      <td>IAM Roles &amp; Policies</td>
      <td>Least-privilege access across services</td>
    </tr>
  </tbody>
</table>

<!-- ![image](https://swjeong.com/assets/images/aws_de/architecture.png) -->

<h3 id="3-data-ingestion"><em>3. Data Ingestion</em></h3>
<p>Raw data lands in S3 — could be CSVs, JSON, or Parquet files from APIs, databases, or streaming sources. I organize the bucket with a partitioned structure:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>s3://my-data-lake/
  raw/
    source=api_a/
      year=2025/month=11/day=08/
        data.json
  processed/
    table_name/
      year=2025/month=11/
        part-00000.parquet
</code></pre></div></div>

<p>For real-time ingestion, Kinesis Data Firehose can stream data directly into S3 with automatic batching and compression.</p>

<h3 id="4-transformation-with-glue"><em>4. Transformation with Glue</em></h3>
<p>AWS Glue handles the heavy lifting. I write PySpark jobs that:</p>

<ul>
  <li><strong>Clean</strong> — Drop nulls, fix data types, deduplicate.</li>
  <li><strong>Transform</strong> — Join datasets, calculate derived fields, aggregate.</li>
  <li><strong>Partition</strong> — Write output in Parquet format, partitioned by date for fast querying.</li>
</ul>

<p>A typical Glue job looks something like this:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">sys</span>
<span class="kn">from</span> <span class="nn">awsglue.transforms</span> <span class="kn">import</span> <span class="o">*</span>
<span class="kn">from</span> <span class="nn">awsglue.utils</span> <span class="kn">import</span> <span class="n">getResolvedOptions</span>
<span class="kn">from</span> <span class="nn">pyspark.context</span> <span class="kn">import</span> <span class="n">SparkContext</span>
<span class="kn">from</span> <span class="nn">awsglue.context</span> <span class="kn">import</span> <span class="n">GlueContext</span>
<span class="kn">from</span> <span class="nn">awsglue.job</span> <span class="kn">import</span> <span class="n">Job</span>

<span class="n">args</span> <span class="o">=</span> <span class="n">getResolvedOptions</span><span class="p">(</span><span class="n">sys</span><span class="p">.</span><span class="n">argv</span><span class="p">,</span> <span class="p">[</span><span class="s">'JOB_NAME'</span><span class="p">])</span>
<span class="n">sc</span> <span class="o">=</span> <span class="n">SparkContext</span><span class="p">()</span>
<span class="n">glueContext</span> <span class="o">=</span> <span class="n">GlueContext</span><span class="p">(</span><span class="n">sc</span><span class="p">)</span>
<span class="n">spark</span> <span class="o">=</span> <span class="n">glueContext</span><span class="p">.</span><span class="n">spark_session</span>
<span class="n">job</span> <span class="o">=</span> <span class="n">Job</span><span class="p">(</span><span class="n">glueContext</span><span class="p">)</span>
<span class="n">job</span><span class="p">.</span><span class="n">init</span><span class="p">(</span><span class="n">args</span><span class="p">[</span><span class="s">'JOB_NAME'</span><span class="p">],</span> <span class="n">args</span><span class="p">)</span>

<span class="c1"># Read from catalog
</span><span class="n">df</span> <span class="o">=</span> <span class="n">glueContext</span><span class="p">.</span><span class="n">create_dynamic_frame</span><span class="p">.</span><span class="n">from_catalog</span><span class="p">(</span>
    <span class="n">database</span><span class="o">=</span><span class="s">"my_database"</span><span class="p">,</span>
    <span class="n">table_name</span><span class="o">=</span><span class="s">"raw_table"</span>
<span class="p">)</span>

<span class="c1"># Transform
</span><span class="n">df_clean</span> <span class="o">=</span> <span class="n">df</span><span class="p">.</span><span class="n">toDF</span><span class="p">()</span> \
    <span class="p">.</span><span class="n">dropDuplicates</span><span class="p">()</span> \
    <span class="p">.</span><span class="nb">filter</span><span class="p">(</span><span class="s">"amount &gt; 0"</span><span class="p">)</span> \
    <span class="p">.</span><span class="n">withColumn</span><span class="p">(</span><span class="s">"processed_date"</span><span class="p">,</span> <span class="n">current_date</span><span class="p">())</span>

<span class="c1"># Write back to S3 as Parquet
</span><span class="n">df_clean</span><span class="p">.</span><span class="n">write</span> \
    <span class="p">.</span><span class="n">mode</span><span class="p">(</span><span class="s">"overwrite"</span><span class="p">)</span> \
    <span class="p">.</span><span class="n">partitionBy</span><span class="p">(</span><span class="s">"year"</span><span class="p">,</span> <span class="s">"month"</span><span class="p">)</span> \
    <span class="p">.</span><span class="n">parquet</span><span class="p">(</span><span class="s">"s3://my-data-lake/processed/clean_table/"</span><span class="p">)</span>

<span class="n">job</span><span class="p">.</span><span class="n">commit</span><span class="p">()</span>
</code></pre></div></div>

<p>The Glue Data Catalog keeps track of all the schemas automatically — once a Crawler runs, Athena can query the data right away.</p>

<h3 id="5-orchestration-with-step-functions"><em>5. Orchestration with Step Functions</em></h3>
<p>Step Functions tie the pipeline together. A typical workflow:</p>

<ol>
  <li><strong>Trigger</strong> — EventBridge fires on a schedule (e.g. daily at 2 AM UTC).</li>
  <li><strong>Crawl raw data</strong> — Glue Crawler updates the catalog.</li>
  <li><strong>Run ETL job</strong> — Glue job transforms and writes processed data.</li>
  <li><strong>Crawl processed data</strong> — Update the catalog with new partitions.</li>
  <li><strong>Notify</strong> — SNS sends a success/failure alert.</li>
</ol>

<p>Each step has built-in retry logic and error handling, so if a Glue job fails it retries before alerting.</p>

<h3 id="6-querying-with-athena"><em>6. Querying with Athena</em></h3>
<p>Once data is in processed S3 buckets and cataloged, Athena lets me run SQL directly on it — no loading into a database, no infrastructure to manage.</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">SELECT</span>
    <span class="n">user_id</span><span class="p">,</span>
    <span class="k">COUNT</span><span class="p">(</span><span class="o">*</span><span class="p">)</span> <span class="k">AS</span> <span class="n">total_events</span><span class="p">,</span>
    <span class="k">SUM</span><span class="p">(</span><span class="n">amount</span><span class="p">)</span> <span class="k">AS</span> <span class="n">total_amount</span>
<span class="k">FROM</span> <span class="n">processed_db</span><span class="p">.</span><span class="n">clean_table</span>
<span class="k">WHERE</span> <span class="nb">year</span> <span class="o">=</span> <span class="s1">'2025'</span> <span class="k">AND</span> <span class="k">month</span> <span class="o">=</span> <span class="s1">'11'</span>
<span class="k">GROUP</span> <span class="k">BY</span> <span class="n">user_id</span>
<span class="k">ORDER</span> <span class="k">BY</span> <span class="n">total_amount</span> <span class="k">DESC</span>
<span class="k">LIMIT</span> <span class="mi">100</span><span class="p">;</span>
</code></pre></div></div>

<p>Athena charges per TB scanned, so partitioning and Parquet compression make a real difference in cost.</p>

<h3 id="7-cost--performance-tips"><em>7. Cost &amp; Performance Tips</em></h3>
<p>A few things I picked up along the way:</p>

<ul>
  <li><strong>Parquet over CSV</strong> — Columnar format = faster queries and way less data scanned.</li>
  <li><strong>Partition everything</strong> — Date-based partitions cut Athena costs dramatically.</li>
  <li><strong>Right-size Glue DPUs</strong> — Start with the minimum workers and scale up only if jobs are slow.</li>
  <li><strong>Use Glue bookmarks</strong> — Process only new data instead of re-processing the full dataset every run.</li>
  <li><strong>Set S3 lifecycle rules</strong> — Move old raw data to Glacier after 90 days.</li>
  <li><strong>Tag everything</strong> — Makes cost tracking and cleanup much easier.</li>
</ul>

<h3 id="8-what-i-learned"><em>8. What I Learned</em></h3>

<ul>
  <li><strong>Serverless doesn’t mean zero ops</strong> — You still need to monitor, tune, and debug. CloudWatch dashboards and alerts are essential.</li>
  <li><strong>Schema evolution is tricky</strong> — When source data changes shape, the Glue Catalog and downstream queries can break. Adding schema validation early saves headaches.</li>
  <li><strong>Start simple, then optimize</strong> — It’s tempting to over-engineer from day one. A basic S3 → Glue → Athena pipeline handles most use cases before you need Redshift or EMR.</li>
  <li><strong>IAM is the real boss</strong> — Getting permissions right across S3, Glue, Step Functions, and Athena took more time than the actual data work.</li>
</ul>]]></content><author><name>Seungwon(Owen) Jeong</name></author><category term="Cloud" /><summary type="html"><![CDATA[AWS Data Engineering]]></summary></entry><entry><title type="html">Algorithmic Trading</title><link href="https://swjeong.com/projects/algo_trading/" rel="alternate" type="text/html" title="Algorithmic Trading" /><published>2025-08-29T00:00:00+00:00</published><updated>2025-08-29T00:00:00+00:00</updated><id>https://swjeong.com/projects/algo_trading</id><content type="html" xml:base="https://swjeong.com/projects/algo_trading/"><![CDATA[<h1 id="algorithmic-trading-bot">Algorithmic Trading Bot</h1>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Contents
1. Introduction
2. Ticker Selection
3. Ticker Database
4. Discord
5. Performance
6. Conclusion
</code></pre></div></div>

<hr />

<h3 id="1-introduction"><em>1. Introduction</em></h3>
<p>Algorithmic trading bots are automated systems that execute trades based on predefined strategies and algorithms. These bots analyze market data, identify trading opportunities, and place orders without human intervention with the pre-setup. By leveraging speed, accuracy, and data-driven decision-making, algorithmic trading bots can help traders optimize their strategies and manage risk more effectively.</p>

<p>In this blog, I’ll explore the basics of algorithmic trading, discuss popular strategies, and provide insights into building your own trading bot using Python. Whether you’re a beginner or an experienced trader, understanding how algorithmic bots work can give you a competitive edge in today’s fast-paced financial markets.</p>

<h3 id="2-ticker-selection"><em>2. Ticker Selection</em></h3>
<blockquote>
  <p>Choosing the right tickers matters as much as the strategy itself.</p>
</blockquote>

<p>I start by screening for the highest dollar trading value of the day <code class="language-plaintext highlighter-rouge">Trading Value = Volume x price</code>. From there, I run each candidate through a pre-configured pipeline: fundamentals, risk/return metrics, quantitative factors, DCF intrinsic value, and backtests. Based on this evidence, I decide whether to enable the bot to automatically buy and sell that ticker.</p>

<p><img src="https://swjeong.com/assets/images/algo_dashboard/ticker_email1.png" alt="image" /><img src="https://swjeong.com/assets/images/algo_dashboard/ticker_email2.png" alt="image" /><img src="https://swjeong.com/assets/images/algo_dashboard/ticker_email3.png" alt="image" /><img src="https://swjeong.com/assets/images/algo_dashboard/ticker_email4.png" alt="image" /></p>

<h3 id="3-ticker-database"><em>3. Ticker Database</em></h3>
<p>When I select a ticker that I think it will go down(I prefer <code class="language-plaintext highlighter-rouge">buy the dip</code>), then I add it to the dataset with fields such as ticker symbol, target buy percentage, notes/description, moving averages, and a suggested holding period derived from backtests. The database will add earnings date, PL, Win rate, moving percentage in a day and RVOL per se.</p>

<p><img src="https://swjeong.com/assets/images/algo_dashboard/database_tickers.png" alt="image" /></p>

<h3 id="4-send-commands-through-discord"><em>4. Send commands through discord</em></h3>
<p>One feature I love is that I can send a command through discord to my database, so I can update parameters without touching the database UI, I can simply send commands like Percentage of buy, Buy Limit, Sell Limit, etc, and the system applies the changes immediately.</p>

<p><img src="https://swjeong.com/assets/images/algo_dashboard/discord_commands.png" alt="image" /></p>

<h3 id="5-performance-3-months"><em>5. Performance (3 months)</em></h3>
<p>Here’s a three-month snapshot, early results were strong (peaking over 15% of return). After the market pullback I gave back some gains, but the portfolio still remains profit as it shows. The KPI dashboard are built with Apache Superset.</p>

<p><img src="https://swjeong.com/assets/images/algo_dashboard/algorithmic_trading_dashboard.png" alt="image" /></p>

<h3 id="6-conclusion"><em>6. Conclusion</em></h3>
<p>Algorithmic trading doesn’t mean “good” or “bad”, but you can definitely try this thing and see how it works. If you can’t monitor markets all day (I have a full time job), a well-tested, rule-based bot can help execute consistently on your plan without straing at the screen. As always start small, validate with backtests and paper trading, and iterate thoughtfully.</p>]]></content><author><name>Seungwon(Owen) Jeong</name></author><category term="Projects" /><summary type="html"><![CDATA[Algorithmic Trading Bot]]></summary></entry></feed>