Source code · Python

update_2026_10_17.py

256 lines · Part of AMD equity research · All code · Read only; the files run as published

"""AMD research update, data updated through October 17, 2026 (research valuation cutoff). Every derived figure on the report page is calculated here from reported inputs,so growth rates, margins and LTM figures cannot drift from their sources. Source tags used below:  10-Q    AMD Form 10-Q, quarter ended June 27, 2026 (read in full for this update)  10-K    AMD FY2025 Form 10-K (figures supplied; filing not accessible from this          environment, so not re-read here)  REL     AMD earnings releases (Aug 4, 2026 and FY2025; not accessible here,          figures corroborated only through search summaries)  CIQ     S&P Capital IQ Estimates as reproduced by ChatIQ, stated estimate dates          Oct 14-15, 2026; provisional pending the underlying consensus tables  KOYFIN  Koyfin consensus captured Oct 7 and Oct 14, 2026 (the earlier vintage) The October 14 DCF in report_model.py is not refreshed by this file. It onlymeasures how the corrections found in this update would move that illustration."""import jsonimport sysfrom pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parent))import report_model as rm  # noqa: E402 CUTOFF = "2026-10-17" # ---------------------------------------------------------------- reported, $M# 10-K (supplied). Segment revenue sums to total in every year (checked below).FY = {    "FY2023": dict(revenue=22680, dc=6496, client=4651, gaming=6212, embedded=5321, gp=10460, op=401),    "FY2024": dict(revenue=25785, dc=12579, client=7054, gaming=2595, embedded=3557, gp=12725, op=1900),    "FY2025": dict(revenue=34639, dc=16635, client=10640, gaming=3910, embedded=3454, gp=17152, op=3694),}# 10-Q: three and six month columns, so Q1 = six months less Q2 for both years.Q2_26 = dict(revenue=11536, dc=6718, client=3062, gaming=779, embedded=977, gp=6203, op=1990,             dc_op=2103, cg_op=582, emb_op=386, other_op=-1081, sbc=503)Q2_25 = dict(revenue=7685, dc=3240, client=2499, gaming=1122, embedded=824, gp=3059, op=-134,             dc_op=-155, cg_op=767, emb_op=275, other_op=-1021, sbc=369)H1_26 = dict(revenue=21789, dc=12493, client=5947, gaming=1499, embedded=1850, gp=11619, op=3466,             dc_op=3702, cg_op=1157, emb_op=724, other_op=-2117, ocf=5321, capex=1197, sbc=990)H1_25 = dict(revenue=15123, dc=6914, client=4793, gaming=1769, embedded=1647, gp=6795, op=672,             dc_op=777, cg_op=1263, emb_op=603, other_op=-1971, ocf=2401, capex=494, sbc=733)REVENUE_KEYS = ("revenue", "dc", "client", "gaming", "embedded", "gp", "op")Q1_26 = {k: H1_26[k] - Q2_26[k] for k in REVENUE_KEYS + ("dc_op", "cg_op", "emb_op", "other_op")}Q1_25 = {k: H1_25[k] - Q2_25[k] for k in REVENUE_KEYS + ("dc_op", "cg_op", "emb_op", "other_op")} # Figures supplied with the update brief for Q1 2026; they must match the 10-Q derivation.SUPPLIED_Q1_26 = dict(revenue=10253, dc=5775, client=2885, gaming=720, embedded=873, gp=5416, op=1476) FY2025_FCF = 5519  # REL/10-K, company defined: operating cash flow less purchases of property and equipment# FY2025 release inputs (supplied; release not inspected). Continuing operations, the# same basis as the H1 figures in the 10-Q. Must reconcile to FY2025_FCF.FY2025_OCF = 6493FY2025_CAPEX = 974NONGAAP_Q2 = dict(gp=6488, gm_reported="56%")  # REL, suppliedLEGAL_CONTINGENCY_Q2 = 17  # REL reconciliation line "loss contingency on legal matters" (supplied; release not inspected)COGS_AMORTIZATION_Q2 = 260  # 10-Q, amortization of acquisition-related intangibles in cost of salesCOGS_SBC_Q2 = 8  # 10-Q, stock-based compensation in cost of sales BALANCE = dict(  # 10-Q, June 27, 2026    cash=5086, st_investments=8025, debt_current=875, debt_long=2351, debt_principal_text="$3.3 billion",    lease_long_term=1050, lease_long_term_dec25=625,    purchase_commitments=30300, commitments_rest_of_2026=17400, lease_guarantees=4100,    leases_not_commenced=4500, post_quarter_leases=9500, post_quarter_investments=5000,    revolver=5000, buyback_remaining=9200, inventories=8468, inventories_dec25=7920,)SHARES = dict(outstanding_jul29=1632.475042, basic_q2=1632, diluted_q2=1659, award_dilution_q2=27, model=rm.SHARES_OUT)WARRANTS = dict(per_holder=160, holders=["OpenAI", "Meta"], exercise_price=0.01, vested_jun27=0,                openai_expiry="2030-10-05", meta_expiry="2031-02-23")CHATIQ_DEBT = 4276  # vendor figure to reconcile, not adopted # Management guidance (REL Aug 4, 2026): quarterly, kept apart from annual consensus.GUIDANCE_Q3 = dict(revenue=13000, band=300, nongaap_gm=0.56, rejected=dict(revenue=12500, nongaap_gm=0.57)) # ---------------------------------------------------------------- CIQ (provisional)CIQ_YEARS = ["2026", "2027", "2028", "2029", "2030"]CIQ = dict(    label=("S&P Capital IQ Estimates, as reproduced by ChatIQ; stated estimate dates October 14-15, 2026. "           "Provisional pending verification against underlying consensus tables."),    revenue=[50948, 89054, 123765, 161896, 224944],    adj_ebit=[14243, 30008, 43659, 65027, 97800],    norm_eps=[7.59, 15.76, 22.82, 31.48, 44.31],    fcf=[7355, 16403, 30342, 42584, 61058],    contributors=dict(revenue=[48, 52, 37, 8, 9], adj_ebit=[33, 34, 21, 3, 4], norm_eps=[49, 50, 33, 6, 7],                      fcf=[19, 19, 14, 5, 5]),    revenue_range=[[48339, 50948, 51004, 52660], [61257, 89054, 89018, 118112], [93892, 123765, 124179, 191354],                   [127079, 161896, 163806, 186966], [185740, 224944, 223234, 274376]],    eps_range=[[7.00, 7.59, 7.62, 8.20], [9.60, 15.76, 15.79, 20.25], [15.60, 22.82, 23.30, 34.62],               [24.32, 31.48, 32.26, 39.47], [33.33, 44.31, 42.68, 53.59]],    revisions=dict(revenue=[0.031, 0.141, 0.166, 0.316, 0.253], adj_ebit=[0.022, 0.168, 0.192, 0.549, 0.351],                   norm_eps=[0.023, 0.171, 0.209, 0.453, 0.330], fcf=[-0.088, 0.020, 0.153, 0.248, 0.385]),    excluded_target=dict(value=627.65, date="2026-10-18", reason="published after the October 17 cutoff"),)  def r(x, d=6):    return round(x, d)  def check(cond, msg):    if not cond:        raise SystemExit(f"reconciliation failed: {msg}")  def main():    # -------- reconciliation checks    for name, p in {**FY, "Q1_26": Q1_26, "Q2_26": Q2_26, "Q1_25": Q1_25, "Q2_25": Q2_25}.items():        check(p["dc"] + p["client"] + p["gaming"] + p["embedded"] == p["revenue"], f"{name} segments sum")    for k, v in SUPPLIED_Q1_26.items():        check(Q1_26[k] == v, f"Q1 2026 {k}: 10-Q derivation {Q1_26[k]} vs supplied {v}")    for p in (Q2_26, Q2_25, H1_26, H1_25):        check(p["dc_op"] + p["cg_op"] + p["emb_op"] + p["other_op"] == p["op"], "segment operating income sum")     periods = {"FY2023": FY["FY2023"], "FY2024": FY["FY2024"], "FY2025": FY["FY2025"], "Q1 2026": Q1_26, "Q2 2026": Q2_26}    prior = {"FY2024": FY["FY2023"], "FY2025": FY["FY2024"], "Q1 2026": Q1_25, "Q2 2026": Q2_25}    history = []    for label, p in periods.items():        cg = p["client"] + p["gaming"]        row = dict(period=label, revenue=p["revenue"], dc=p["dc"], client=p["client"], gaming=p["gaming"],                   client_gaming=cg, embedded=p["embedded"], gp=p["gp"], op=p["op"],                   gm=r(p["gp"] / p["revenue"]), op_margin=r(p["op"] / p["revenue"]),                   dc_share=r(p["dc"] / p["revenue"]),                   source="10-K (supplied)" if label.startswith("FY") else "10-Q")        if label in prior:            q = prior[label]            row["growth"] = dict(total=r(p["revenue"] / q["revenue"] - 1), dc=r(p["dc"] / q["dc"] - 1),                                 client_gaming=r(cg / (q["client"] + q["gaming"]) - 1),                                 client=r(p["client"] / q["client"] - 1), gaming=r(p["gaming"] / q["gaming"] - 1),                                 embedded=r(p["embedded"] / q["embedded"] - 1))        history.append(row)     # Supplied growth table, checked to one decimal.    supplied_growth = {"FY2024": (0.137, 0.936, -0.112, -0.332), "FY2025": (0.343, 0.322, 0.508, -0.029),                       "Q1 2026": (0.378, 0.572, 0.226, 0.061), "Q2 2026": (0.501, 1.073, 0.061, 0.186)}    for row in history:        if row["period"] in supplied_growth:            g = row["growth"]            mine = (g["total"], g["dc"], g["client_gaming"], g["embedded"])            for a, b in zip(mine, supplied_growth[row["period"]]):                check(abs(a - b) < 0.0005, f"growth {row['period']}: {a} vs {b}")     segment_op = []    for label, p in (("Q1 2025", Q1_25), ("Q2 2025", Q2_25), ("Q1 2026", Q1_26), ("Q2 2026", Q2_26)):        cg = p["client"] + p["gaming"]        segment_op.append(dict(period=label, dc_op=p["dc_op"], dc_margin=r(p["dc_op"] / p["dc"]),                               cg_op=p["cg_op"], cg_margin=r(p["cg_op"] / cg), emb_op=p["emb_op"],                               emb_margin=r(p["emb_op"] / p["embedded"]), other_op=p["other_op"]))     fy25 = FY["FY2025"]    h1_fcf_26 = H1_26["ocf"] - H1_26["capex"]    h1_fcf_25 = H1_25["ocf"] - H1_25["capex"]    check(h1_fcf_26 == 4124, "H1 2026 FCF")    ltm = dict(        revenue=fy25["revenue"] + H1_26["revenue"] - H1_25["revenue"],        dc=fy25["dc"] + H1_26["dc"] - H1_25["dc"],        client=fy25["client"] + H1_26["client"] - H1_25["client"],        gaming=fy25["gaming"] + H1_26["gaming"] - H1_25["gaming"],        embedded=fy25["embedded"] + H1_26["embedded"] - H1_25["embedded"],        gp=fy25["gp"] + H1_26["gp"] - H1_25["gp"],        op=fy25["op"] + H1_26["op"] - H1_25["op"],        fcf=FY2025_FCF + h1_fcf_26 - h1_fcf_25,        ocf=FY2025_OCF + H1_26["ocf"] - H1_25["ocf"],        capex=FY2025_CAPEX + H1_26["capex"] - H1_25["capex"],        rejected_dc=24718,    )    ltm["client_gaming"] = ltm["client"] + ltm["gaming"]    ltm["gm"] = r(ltm["gp"] / ltm["revenue"])    ltm["op_margin"] = r(ltm["op"] / ltm["revenue"])    ltm["fcf_margin"] = r(ltm["fcf"] / ltm["revenue"])    for k, v in dict(revenue=41305, dc=22214, gp=21976, fcf=7736).items():        check(ltm[k] == v, f"LTM {k}: {ltm[k]} vs {v}")     nongaap = dict(gp=NONGAAP_Q2["gp"], gm=r(NONGAAP_Q2["gp"] / Q2_26["revenue"]), gaap_gp=Q2_26["gp"],                   gaap_gm=r(Q2_26["gp"] / Q2_26["revenue"]), cogs_amortization=COGS_AMORTIZATION_Q2,                   cogs_sbc=COGS_SBC_Q2, gp_ex_amortization=Q2_26["gp"] + COGS_AMORTIZATION_Q2,                   identified=Q2_26["gp"] + COGS_AMORTIZATION_Q2 + COGS_SBC_Q2)    nongaap["unidentified"] = nongaap["gp"] - nongaap["identified"]    nongaap["legal_contingency"] = LEGAL_CONTINGENCY_Q2    check(nongaap["unidentified"] == LEGAL_CONTINGENCY_Q2, "non-GAAP bridge closes with the legal contingency line")    check(FY2025_OCF - FY2025_CAPEX == FY2025_FCF, "FY2025 FCF = OCF - capex")     debt = BALANCE["debt_current"] + BALANCE["debt_long"]    cash_inv = BALANCE["cash"] + BALANCE["st_investments"]    capital = dict(BALANCE, debt_carrying=debt, cash_and_investments=cash_inv, net_cash=cash_inv - debt,                   net_cash_after_lt_leases=cash_inv - debt - BALANCE["lease_long_term"],                   chatiq_debt=CHATIQ_DEBT, chatiq_gap=CHATIQ_DEBT - debt)    check(capital["net_cash"] == 9885, "net cash")    check(ltm["ocf"] - ltm["capex"] == ltm["fcf"], "LTM FCF bridge")    check(capital["chatiq_gap"] == BALANCE["lease_long_term"], "ChatIQ debt reconciliation lead")     cash_flow = dict(fy2025_fcf=FY2025_FCF, fy2025_ocf=FY2025_OCF, fy2025_capex=FY2025_CAPEX,                     fy2025_revenue=fy25["revenue"],                     h1_26=dict(revenue=H1_26["revenue"], ocf=H1_26["ocf"], capex=H1_26["capex"], fcf=h1_fcf_26, sbc=H1_26["sbc"],                                owner_fcf=h1_fcf_26 - H1_26["sbc"]),                     h1_25=dict(revenue=H1_25["revenue"], ocf=H1_25["ocf"], capex=H1_25["capex"], fcf=h1_fcf_25, sbc=H1_25["sbc"],                                owner_fcf=h1_fcf_25 - H1_25["sbc"]))    check(cash_flow["h1_26"]["owner_fcf"] == 3134, "H1 owner FCF")     # -------- CIQ derived ratios (ratios of means; samples differ by metric and year)    rev = CIQ["revenue"]    ciq = dict(CIQ, years=CIQ_YEARS,               growth=[r(rev[0] / fy25["revenue"] - 1)] + [r(rev[i] / rev[i - 1] - 1) for i in range(1, 5)],               ebit_margin=[r(e / v) for e, v in zip(CIQ["adj_ebit"], rev)],               fcf_margin=[r(f / v) for f, v in zip(CIQ["fcf"], rev)],               revenue_high_low=[r(x[3] / x[0], 2) for x in CIQ["revenue_range"]],               eps_high_low=[r(x[3] / x[0], 2) for x in CIQ["eps_range"]],               revenue_vs_ltm_2030=r(rev[4] / ltm["revenue"]))    koyfin = dict(revenue=[rm.STREET["revenue"][y] for y in CIQ_YEARS],                  eps_adj=[rm.STREET["eps_adj"][y] for y in CIQ_YEARS])    ciq["vs_koyfin_revenue"] = [r(a / b - 1) for a, b in zip(rev, koyfin["revenue"])]     guidance = dict(GUIDANCE_Q3, low=GUIDANCE_Q3["revenue"] - GUIDANCE_Q3["band"],                    high=GUIDANCE_Q3["revenue"] + GUIDANCE_Q3["band"],                    sequential=r(GUIDANCE_Q3["revenue"] / Q2_26["revenue"] - 1),                    q2_nongaap_gm=nongaap["gm"])     # -------- what the verified corrections would do to the October 14 illustration    base = rm.value(rm.BASE)    saved_bal, saved_shares = rm.BAL, rm.SHARES_OUT    rm.BAL = dict(saved_bal, debt_long=saved_bal["debt_long"] + BALANCE["lease_long_term"])    with_lease = rm.value(rm.BASE)    rm.BAL = saved_bal    rm.SHARES_OUT = SHARES["diluted_q2"]    with_diluted = rm.value(rm.BASE)    rm.SHARES_OUT = saved_shares    no_warrants = rm.value(dict(rm.BASE, warrant_vest=0.0))    illustration_impacts = dict(        base=round(base["per_share"], 1),        lease=round(with_lease["per_share"] - base["per_share"], 1),        diluted_shares=round(with_diluted["per_share"] - base["per_share"], 1),        warrants_off=round(no_warrants["per_share"] - base["per_share"], 1),        rebase_days=3,        rebase_pct=round((1 + rm.BASE["wacc"]) ** (3 / 365) - 1, 4),    )     potential = WARRANTS["per_holder"] * len(WARRANTS["holders"])    dilution = dict(potential_shares=potential, existing=SHARES["outstanding_jul29"],                    of_existing=r(potential / SHARES["outstanding_jul29"]),                    of_enlarged=r(potential / (SHARES["outstanding_jul29"] + potential)))    out = dict(cutoff=CUTOFF, dilution=dilution, history=history, segment_op=segment_op, ltm=ltm, nongaap=nongaap, capital=capital,               shares=SHARES, warrants=WARRANTS, cash_flow=cash_flow, guidance=guidance, ciq=ciq,               koyfin=koyfin, illustration_impacts=illustration_impacts)    target = Path(__file__).resolve().parents[2] / "app" / "lib" / "amdUpdate.json"    target.write_text(json.dumps(out, indent=1) + "\n")    print("reconciliation checks passed")    print("LTM:", {k: ltm[k] for k in ("revenue", "dc", "gp", "gm", "op", "fcf")})    print("non-GAAP Q2:", nongaap)    print("illustration impacts:", illustration_impacts)    print("CIQ growth:", ciq["growth"], "EBIT margin:", ciq["ebit_margin"], "FCF margin:", ciq["fcf_margin"])    print("CIQ vs Koyfin revenue:", ciq["vs_koyfin_revenue"])    print("wrote", target)  if __name__ == "__main__":    main()