[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M71KBHXM1DHMV805519V8NF6":3,"skill-same:01M71KBHXM1DHMV805519V8NF6":71},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":23,"publishedAt":36,"linkOnly":37,"markdownContent":38,"markdownBody":39,"frontMatter":40,"refName":41,"commitSha":42,"skillDirectory":43,"forksCount":44,"repositoryHtmlUrl":45,"repositoryHomepageUrl":46,"ownerHtmlUrl":47,"ownerBlogUrl":48,"ownerDescription":49,"updatedAt":50,"install":51,"resources":64},"01M71KBHXM1DHMV805519V8NF6","stock-analyzer",null,"Analyze stocks with fundamental analysis, technical indicators, and PDF reports.","clawdsolana\u002FOpenClawd","clawdsolana","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F279036008?v=4","01M71JTVF9FNGKE2SVJRPTQM31","OpenClawd","01M71JTVGCKWBSS8ZYC1HYWVA5",2,"MIT","https:\u002F\u002Fgithub.com\u002Fclawdsolana\u002FOpenClawd\u002Fblob\u002Fmain\u002Fsecondary_skills\u002Fstock-analyzer\u002FSKILL.md",[18],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000002","文档处理","SCENARIO","使用场景",[24,25,26,27,28,29,30,31,32,33,34,35],"stock","stock analyzer","analyzer","reports","with","indicators","PDF","technical indicators","technical","analysis","stocks","analyze","2026-11-10 10:12:30",false,"---\nname: stock-analyzer\ndescription: Analyze stocks with fundamental analysis, technical indicators, and PDF reports.\n---\n\n# Stock & Investment Analyzer\n\n## OpenClawd Operator Adaptation\n\nRun this skill as part of the OpenClawd operator deck. Preserve the skill-specific workflow below, but frame outputs for Solana-native agents when relevant: prefer OpenClawd language, note whether the work can support autonomous agent operations, and keep financial, legal, tax, hiring, medical, or other regulated outputs informational unless the skill already requires a stricter disclaimer. Use the Llobster Legend persona only as light operator framing; do not let branding override accuracy, safety, or the user's stated domain.\n\n\nAnalyze stocks, companies, and investment opportunities using financial market data. Provide company profiles, technical analysis, fundamental analysis, and portfolio insights.\n\nPrimary deliverable: A professional PDF research report. The Excel model and interactive web app are optional extras — only build them if the user explicitly requests them.\n\nWhen to Use\n\nUser wants to analyze a specific stock or company\n\nUser asks about financial metrics, earnings, or valuations\n\nUser wants to compare investment options\n\nUser needs portfolio analysis or allocation advice\n\nUser asks about market trends or sector performance\n\nWhen NOT to Use\n\nTax-specific questions (use tax-reviewer skill)\n\nPersonal budgeting (use budget-planner skill)\n\nInsurance coverage (use insurance-optimizer skill)\n\nIMPORTANT: Disclaimer & Compliance\n\nAll output from this skill is strictly informational. It does not constitute investment advice, a recommendation or solicitation to buy or sell any security, or a substitute for professional financial counsel.\n\nEvery report, PDF, Excel model, or web dashboard produced by this skill must:\n\nState clearly on the cover page (or first visible section): \"This report is for informational purposes only and does not constitute investment advice. It is not a recommendation to buy, sell, or hold any security.\"\n\nInclude a full disclaimer on the final page (see \"Limitations & Disclaimer\" section below for the required text).\n\nNever use imperative language that implies a directive — say \"may outperform\" or \"could be worth investigating,\" not \"you should buy\" or \"add this to your portfolio.\"\n\nAlways recommend consulting a licensed financial advisor before making any investment decision.\n\nThis applies to all outputs regardless of whether an investor profile has been collected.\n\nOptional Investor Profile\n\nBefore starting any analysis, offer the user the option to share their investment profile. This step is entirely optional — if the user declines or wants to skip it, proceed immediately with a general-purpose analysis using the default assumptions below.\n\nHow to offer it: At the start of the analysis, ask something like: \"I can tailor this analysis to your investment style if you'd like to answer a few quick questions. Otherwise, I'll provide a general market perspective. Which do you prefer?\"\n\nProfiling Dimensions (ask all 5 if the user opts in)\n\nDimension\n\nQuestion\n\nOptions\n\nRisk tolerance\n\nHow would you describe your comfort with investment risk?\n\nConservative (preserve capital, minimize losses) \u002F Moderate (balanced growth with some downside protection) \u002F Aggressive (maximize returns, comfortable with volatility)\n\nTime horizon\n\nHow long do you plan to hold these investments?\n\nShort-term (under 1 year) \u002F Medium-term (1-5 years) \u002F Long-term (5+ years)\n\nCurrent allocation\n\nRoughly, what is your current portfolio split?\n\nApproximate % in equities, bonds\u002Ffixed income, cash, alternatives (real estate, commodities, crypto)\n\nIncome needs\n\nDo you need your investments to generate regular income?\n\nYes, dividend\u002Fincome-focused \u002F No, purely growth-focused \u002F Mixed\n\nExperience level\n\nHow would you describe your investing experience?\n\nBeginner (started recently, still learning) \u002F Intermediate (a few years, understands basic concepts) \u002F Advanced (experienced, comfortable with complex strategies)\n\nDefault Assumptions (when user skips profiling)\n\nWhen the user declines profiling, use these neutral defaults and do not assume anything about their personal situation:\n\nRisk tolerance: Moderate\n\nTime horizon: Medium-term (1-5 years)\n\nAllocation: Not assumed — present analysis without allocation recommendations\n\nIncome needs: Not assumed — cover both growth and income angles\n\nExperience level: Intermediate — use clear language but don't oversimplify\n\nHow Profiling Shapes the Report\n\nWhen profile data is available, adjust the report in these ways:\n\nCover page: Add an \"Investor Profile\" summary box showing the user's stated risk tolerance, time horizon, and income preference\n\nStock\u002Fsector picks: Add a suitability tag to each recommendation (e.g., \"Suitable for: moderate risk, 3-5yr horizon\" or \"Caution: high volatility — may not suit conservative investors\")\n\nAllocation section: Add a \"Suggested Allocation\" section at the end of the report, tailored to their risk\u002Ftimeline. Frame it as illustrative, not prescriptive (e.g., \"A moderate-risk investor with a 3-5 year horizon might consider an allocation along these lines...\")\n\nLanguage calibration: For beginners, use simpler explanations and fewer technical terms. For advanced investors, include more granular data and nuanced analysis.\n\nPicks that don't fit: Still include all relevant picks, but flag any that conflict with the user's stated profile (e.g., \"Note: This is a high-volatility pick that may not align with your conservative risk preference\")\n\nWhen profile data is NOT available, omit the Investor Profile box, omit the Suggested Allocation section, and present all picks without suitability tags. The core analysis remains identical.\n\nData Sources (Use These — Don't Guess)\n\nPython libs (run directly, no API key):\n\nimport yfinance as yf\n\nt = yf.Ticker(\"AAPL\")\n\nt.info \\# P\u002FE, market cap, beta, 52w range, margins\n\nt.financials \\# income statement (4yr)\n\nt.balance_sheet \\# debt, cash, equity\n\nt.cashflow \\# FCF, capex\n\nt.history(period=\"1y\") \\# OHLCV for technicals\n\nt.institutional_holders \\# 13F ownership\n\nSector screening: Use yfinance sector ETFs (XLE, XLK, XLF, XLV, XLI, XLP, XLU, XLRE, XLB, XLC, XLY) for reliable sector-level data. These are more stable than third-party screener libraries. For individual stock screening, pull yfinance data for each ticker directly.\n\nNote on finvizfinance: This library frequently breaks due to website changes. Avoid relying on it. Use yfinance sector ETFs and direct ticker lookups instead.\n\nPrimary filings: Start from the EDGAR filing index: webFetch(\"\u003Chttps:\u002F\u002Fwww.sec.gov\u002Fcgi-bin\u002Fbrowse-edgar?action=getcompany&CIK={ticker}&type=10-K>\"). This returns a list of filings — find the most recent 10-K and webFetch its \"Documents\" link to reach the actual filing. Read Item 1A (Risk Factors) and Item 7 (MD&A) — this is where management admits problems.\n\nInsider activity: webFetch(\"\u003Chttp:\u002F\u002Fopeninsider.com\u002Fscreener?s={ticker}>\") — look for cluster buys (multiple execs buying same week) and P-code open-market purchases (insider paid cash at market price — strongest signal). Ignore option exercises (M-code) and 10b5-1 scheduled sales.\n\nShort interest: webSearch \"{ticker} short interest fintel\" — >20% of float = crowded short, squeeze risk either direction.\n\nData Source Fallbacks\n\nyfinance occasionally returns empty data, tickers change, or rate limits kick in. Use this fallback hierarchy:\n\nPrimary: yfinance — try up to 2 retries with a short delay\n\nFallback: Web search for the specific metric (e.g., webSearch(\"[ticker] market cap 2026\"))\n\nLast resort: Flag the metric as \"data unavailable\" — never fabricate numbers\n\nResearch First — Mandatory Before Any Output\n\nNever show financials, tables, or a report to the user without thoroughly researching first. Before generating the PDF, you must:\n\nLoad the deep-research skill for comprehensive web research. This is not optional — every stock analysis must use deep research to gather real data before producing any deliverable.\n\nPull actual financials from yfinance AND cross-reference with SEC EDGAR filings (10-K, 10-Q). Do not rely on a single source.\n\nSearch for every company mentioned — if the user's request involves multiple companies or peers, pull financials on ALL of them, not just the primary ticker.\n\nBias towards tables and numbers from actual public filings. Every financial figure in the report must be traceable to a real source (SEC filing, earnings release, or yfinance data pull). Do not estimate or round when real numbers are available.\n\nIf you cannot verify a financial figure from at least one real source, flag it explicitly as unverified. Never present guessed or hallucinated numbers as fact.\n\nScoping Deep Research\n\nFor a single-stock analysis, launch 2-3 parallel research agents: one for fundamentals\u002Ffinancials, one for industry\u002Fcompetitive landscape, one for recent news\u002Fcatalysts.\n\nFor a multi-sector or thematic analysis, launch 4-6 parallel research agents organized by theme (e.g., macro environment, sector rotation, geopolitics, specific sector deep-dives). More than 6 agents rarely adds value and wastes time.\n\nMethodology\n\nStep 1: Pull the Data (Python)\n\nRun yfinance to get fundamentals + 1yr price history. Compute 50\u002F200 SMA, RSI(14), and current price vs 52w high. Takes 10 lines of pandas.\n\nStep 2: Fundamental Analysis\n\nValuation (compare to sector median, not S&P):\n\nP\u002FE — meaningless alone; flag if >2x sector median\n\nPEG — \u003C1.0 = growth at reasonable price; >2.0 = priced for perfection\n\nEV\u002FEBITDA — better than P\u002FE for capital-intensive or leveraged cos\n\nP\u002FS — only metric for unprofitable growth; >20x = needs hypergrowth to justify\n\nFCF yield (FCF\u002Fmarket cap) — >5% = genuinely cheap; negative = burning cash\n\nQuality red lines (practitioner heuristics):\n\nRevenue growing but FCF shrinking -> earnings quality problem, dig into receivables\n\nDebt\u002FEBITDA >4x -> one bad year from covenant breach\n\nGross margin compressing 3+ quarters -> losing pricing power\n\nStock-based comp >15% of revenue -> dilution machine (common in SaaS)\n\nGoodwill >50% of assets -> acquisition-heavy, writedown risk\n\nStep 3: Technical Context (Not Prediction)\n\nCompute in pandas — don't just describe:\n\nPrice vs 50\u002F200 SMA: below both = downtrend, don't catch knives\n\nGolden cross (50 crosses above 200) = trend confirmation, not entry signal\n\nRSI(14): >70 overbought \u002F \u003C30 oversold — only useful at extremes + divergence\n\nVolume: moves on 2x+ avg volume are real; low-volume moves fade\n\n% off 52w high: >30% drawdown in an uptrending market = something broke\n\nStep 4: The Retail Edge — Signals Institutions Ignore\n\nInsider cluster buys (OpenInsider): 3+ insiders open-market buying within 2 weeks is the single highest-conviction public signal. Research shows insider buys outperform; sells mean nothing (taxes\u002Fdivorces\u002Fyachts).\n\nBuying the dip: insider P-code purchase after >10% drop = management disagrees with the market\n\nShort squeeze setup: short interest >20% + days-to-cover >5 + any positive catalyst\n\nUnusual options: webSearch \"{ticker} unusual options activity\" — large OTM call sweeps before earnings sometimes leak info\n\nStep 5: Comparative Table\n\nBuild a pandas DataFrame with peers side-by-side: P\u002FE, PEG, rev growth, gross margin, FCF yield, debt\u002FEBITDA. The outlier in either direction is your thesis. Pull yfinance data for every peer company — do not leave cells blank or use estimates when real data is available. Every company in the comparison must have actual financials pulled and verified.\n\nStep 6: Web Research — Find Existing Analyst Reports and News\n\nUse web search aggressively via the deep-research skill. Before writing the report, gather real external research to cite:\n\nwebSearch(\"[ticker] analyst report 2026\")\n\nwebSearch(\"[ticker] earnings analysis site:seekingalpha.com\")\n\nwebSearch(\"[ticker] bull case bear case site:seekingalpha.com OR site:fool.com\")\n\nwebSearch(\"[company] investor presentation 2026 filetype:pdf\")\n\nwebSearch(\"[ticker] price target consensus\")\n\nwebSearch(\"[ticker] industry outlook [sector]\")\n\nwebSearch(\"[company] competitive landscape\")\n\nwebSearch(\"[ticker] short interest thesis\")\n\nSource hierarchy (cite all of these in the report):\n\nSource\n\nWhat you get\n\nHow to cite\n\nSEC EDGAR (10-K, 10-Q, 8-K)\n\nPrimary financials, risk factors, MD&A\n\n\"Source: [Company] 10-K FY2025, Item 7\"\n\nEarnings call transcripts\n\nManagement commentary, guidance\n\n\"Source: Q4 2025 Earnings Call, CEO remarks\"\n\nSell-side research (via SeekingAlpha, TipRanks)\n\nPrice targets, consensus estimates\n\n\"Source: TipRanks consensus, 12 analysts\"\n\nIndustry reports\n\nTAM, growth rates, competitive dynamics\n\n\"Source: [Firm] [Industry] Report, [Date]\"\n\nCompany investor presentations\n\nManagement's own bull case, KPIs\n\n\"Source: [Company] Investor Day 2025\"\n\nNews (Reuters, Bloomberg, CNBC)\n\nCatalysts, M&A, regulatory\n\n\"Source: Reuters, [Date]\"\n\nUse webFetch to pull actual content from SeekingAlpha articles, earnings transcripts, and investor presentations. Extract specific data points, quotes, and estimates to cite in the report.\n\nProject Architecture — Reusable Modules\n\nThe reports\u002F directory contains shared modules that all ticker-specific scripts import from. Never duplicate the ResearchReport class, chart functions, or Excel styling helpers — always import from these modules.\n\nreports\u002Freport_base.py — Shared PDF Report Class:\n\n- sanitize_text(text): Cleans Unicode characters for fpdf2's latin-1 encoding. Apply to ALL text before passing to pdf.cell() or pdf.multi_cell().\n- ResearchReport(company_name, ticker): Extends FPDF with all report methods. Constructor sets alias_nb_pages() and auto_page_break(margin=15).\n\n- Methods: header(), footer(), ensure_space(needed_mm), section_title(), sub_title(), body_text(), bullet_point(), key_metric(), table_header(), table_row(), data_table(), callout_box(), cover_page(data_dict), disclaimer_page(sources_text)\n- cover_page(data) expects a dict with keys: exchange, sector, rating, price_target, current_price, market_cap_str, range_52w, analyst_consensus\n\n- Usage: from report_base import ResearchReport, sanitize_text\n- IMPORTANT fpdf2 gotcha: bullet_point() uses cell(5, 4, chr(149)) then multi_cell(185, 4, text). You MUST pass an explicit width (185) to multi_cell after cell(), otherwise multi_cell gets 0 remaining width and throws \"Not enough horizontal space\" error. Never use multi_cell(0, ...) after a cell() on the same line.\n\nreports\u002Fchart_utils.py — Shared Chart Generation:\n\n- price_chart(df, ticker, company_name): 1-year price history with 50\u002F200 SMAs. Returns saved path.\n- revenue_margin_chart(years, revenues, gross_margins, op_margins, net_margins, ticker): Bar+line combo chart. Returns saved path.\n\n- segment_chart(segments_dict, ticker): Grouped bar chart from {year: {segment: value}} dict. Returns saved path.\n- peer_valuation_chart(peers_dict, ticker): 3-panel bar chart (Fwd P\u002FE, EV\u002FEBITDA, P\u002FS). peers_dict = {ticker: {fwd_pe, ev_ebitda, ps}}. Subject ticker highlighted in red. Returns saved path.\n\n- All charts saved to reports\u002Fcharts\u002F at 150 DPI.\n- Usage: from chart_utils import price_chart, revenue_margin_chart, segment_chart, peer_valuation_chart\n\nreports\u002Fexcel_base.py — Shared Excel Styling:\n\n- Style constants: NAVY_FILL, WHITE_FONT, HEADER_FONT, INPUT_FILL, LIGHT_GRAY, GREEN_FILL, RED_FILL, THIN_BORDER, INPUT_BORDER, THICK_BORDER\n- style_header_row(ws, row, max_col): Navy header with white bold text\n\n- style_data_cell(ws, row, col, fmt, is_input, shaded): Applies borders, fills, number format\n- auto_width(ws): Auto-sizes all columns\n\n- write_section_header(ws, row, col, text): Section label in navy bold\n- write_table(ws, start_row, headers, rows, col_formats, highlight_row): Complete table with header + data rows\n\n- Usage: from excel_base import *\n\nreports\u002Fdata_utils.py — Data Pulling & Validation:\n\n- pull_stock_data(ticker, retries=2): Pulls all yfinance data with retry logic. Returns dict with all metrics, formatted strings (market_cap_str, range_52w, analyst_consensus), and raw DataFrames (financials, balance_sheet, cashflow, quarterly_financials, history_1y).\n- pull_peer_data(tickers): Pulls key metrics for a list of peer tickers. Returns dict of dicts.\n\n- validate_data(data): Checks for common data issues (zero price, negative P\u002FE, missing history). Returns list of warning strings.\n- fmt_val(val, fmt_type, fallback): Format a value for display. Types: \"pct\", \"pct_change\", \"ratio\", \"dollar\", \"dollar_b\", \"market_cap\".\n\n- Usage: from data_utils import pull_stock_data, pull_peer_data, validate_data, fmt_val\n\nreports\u002Fgenerate_report.py — Single Entry Point CLI:\n\n- Usage: python reports\u002Fgenerate_report.py TICKER --peers PEER1,PEER2,PEER3\n- Runs the data pipeline: pulls subject + peer data, validates, generates price + peer charts\n\n- Outputs a data summary and tells you which ticker-specific scripts to create\u002Frun next\n- Revenue\u002Fmargin and segment charts require manual historical data (yfinance only provides trailing 4 years, segment data requires 10-K parsing)\n\nWorkflow for Adding a New Ticker:\n\n1. Run: python reports\u002Fgenerate_report.py TICKER --peers PEER1,PEER2\n2. Create reports\u002Fgenerate_{ticker}_pdf.py — imports ResearchReport from report_base, chart paths from chart_utils\n\n3. Create reports\u002Fgenerate_{ticker}_dcf.py — imports excel_base helpers, uses Excel formulas (not hardcoded values)\n4. Run both scripts to generate the PDF and Excel model\n\n5. Present both files to the user\n\nData Validation Rules:\n\n- Always call validate_data() after pulling data and print warnings\n- Negative trailing P\u002FE means the company is unprofitable — flag it, don't hide it\n\n- If yfinance returns None for a metric, display \"N\u002FA\" in the report, never 0 or a guess\n- Cross-reference key financials (revenue, net income) between yfinance and SEC filings when possible\n\n- If a peer ticker fails to pull data, include it in the table with \"N\u002FA\" values rather than silently dropping it\n\nPeer Data — Always Pull from yfinance:\n\n- Never hardcode peer metrics — always pull from yfinance via pull_peer_data() so data is current\n- Pull at least 4 peers in the same sector\u002Findustry\n\n- Common peer sets: Tech (AAPL\u002FMSFT\u002FGOOG\u002FAMZN\u002FMETA), Semis (NVDA\u002FAMD\u002FAVGO\u002FQCOM\u002FINTC), Auto (TSLA\u002FGM\u002FF\u002FTM\u002FRIVN)\n- For the comparable companies Excel sheet, use formulas for the median row: =MEDIAN(C5:C10)\n\nBuild Order — PDF Report + DCF Excel Model (Built in Parallel)\n\nBoth the PDF research report and the DCF Excel model are primary deliverables. Build them in parallel from the same research data.\n\nStep 1: Generate charts (Python + matplotlib)\n\nGenerate all charts first — these will be embedded in the PDF. Save as PNG at 150+ DPI to reports\u002Fcharts\u002F. Generate at least 4 charts: price history with SMAs, revenue\u002Fmargin trends, peer valuation comparison, and one more relevant to the thesis.\n\nimport matplotlib.pyplot as plt\n\nimport yfinance as yf\n\ndf = yf.Ticker(\"AAPL\").history(period=\"1y\")\n\nfig, ax = plt.subplots(figsize=(10, 5))\n\nax.plot(df.index, df['Close'], label='Price', color='#1a1a2e')\n\nax.plot(df.index, df['Close'].rolling(50).mean(), label='50 SMA', color='#e94560', linestyle='--')\n\nax.plot(df.index, df['Close'].rolling(200).mean(), label='200 SMA', color='#0f3460', linestyle='--')\n\nax.set_title(\"AAPL - 1 Year Price History\")\n\nax.legend()\n\nax.grid(alpha=0.3)\n\nfig.savefig(\"reports\u002Fcharts\u002Fprice_chart.png\", dpi=150, bbox_inches='tight')\n\nStep 2: Generate the PDF research report (PRIMARY DELIVERABLE \\#1)\n\nWrite a Python generation script (reports\u002Fgenerate_pdf.py) using fpdf2 to produce a polished, multi-page equity research PDF. Do not output a markdown summary as a substitute. Do not skip the PDF.\n\nWhy fpdf2, not jsPDF: Python is already required for yfinance and matplotlib. fpdf2 installs instantly (pip install fpdf2), runs reliably, and handles image embedding natively. jsPDF (Node) has heavy dependencies, frequent install timeouts in constrained environments, and is designed for browser-side generation — not server-side report building.\n\nPresent the PDF to the user in chat immediately after generating it.\n\nStep 3: Build the DCF Excel Model (PRIMARY DELIVERABLE \\#2 — built in parallel with the PDF)\n\nAlways build the DCF Excel model alongside the PDF report. Do not wait for user request — this is a standard deliverable for every stock analysis.\n\nUse openpyxl in Python to generate the .xlsx file (reports\u002F{TICKER}_DCF_Model.xlsx):\n\nfrom openpyxl import Workbook\n\nfrom openpyxl.styles import Font, PatternFill, Alignment, Border, Side, numbers\n\nfrom openpyxl.utils import get_column_letter\n\nThe Excel model must include the following sheets:\n\nSheet 1 — DCF Model:\n\n- Revenue projections (5-year forecast with growth assumptions)\n- EBITDA \u002F operating income projections (margin assumptions)\n\n- Free cash flow build: EBITDA -> subtract taxes, capex, change in working capital -> unlevered FCF\n- Terminal value calculation (perpetuity growth method AND exit multiple method)\n\n- WACC calculation with clearly labeled inputs: risk-free rate, equity risk premium, beta, cost of debt, tax rate, debt\u002Fequity ratio\n- Enterprise value -> equity value -> implied share price\n\n- Upside\u002Fdownside % vs current price\n\nSheet 2 — Sensitivity Analysis:\n\n- Two-variable data table: WACC (rows) vs terminal growth rate (columns) showing implied share price at each combination\n- Highlight the base case cell\n\n- Use conditional formatting: green for upside scenarios, red for downside\n\nSheet 3 — Scenario Analysis:\n\n- Bull \u002F Base \u002F Bear cases with different revenue growth, margin, and multiple assumptions\n- Probability-weighted price target (e.g., 25% Bull, 50% Base, 25% Bear)\n\n- Show each scenario's implied share price and upside\u002Fdownside\n\nSheet 4 — Comparable Companies:\n\n- Peer comparison table: ticker, market cap, P\u002FE, EV\u002FEBITDA, P\u002FS, revenue growth, gross margin, FCF yield\n- Pull real data from yfinance for 4-6 peers\n\n- Highlight the subject company row\n\nSheet 5 — Financial Summary:\n\n- Historical income statement (3-4 years from yfinance)\n- Historical balance sheet highlights (cash, debt, equity)\n\n- Key ratios: ROE, ROIC, debt\u002FEBITDA, current ratio\n\nCRITICAL — Use Excel Formulas, Not Hardcoded Values:\n\n- Every calculated cell MUST use an Excel formula, not a pre-computed Python value. The entire point of a financial model is that users can change assumptions and see results update automatically.\n- Input cells (yellow): hardcoded values the user can edit (growth rates, margins, WACC inputs, tax rate, etc.)\n\n- Calculated cells: MUST be formulas referencing input cells or other calculated cells. Examples:\n- Cost of Equity: =B7+B9*B8 (risk-free + beta \\* ERP)\n\n- WACC: =B10*(1-B13)+B11*(1-B12)*B13 (formula using CoE, CoD, tax, D\u002FTC)\n- Revenue projections: =B21*(1+C22) (prior year \\* (1 + growth rate))\n\n- Gross Profit: =C21*C24 (revenue \\* gross margin)\n- Terminal Value: =B42*(1+B43)\u002F(B44-B43) (FCF*(1+g)\u002F(WACC-g))\n\n- PV factors: =1\u002F(1+B44)^n\n- Implied Share Price: =B55\u002FB56 (equity value \u002F shares)\n\n- Sensitivity table: Each cell should be a formula computing implied price for that row's WACC and column's terminal growth rate, referencing the DCF Model sheet for PV of FCFs, terminal FCF, debt, cash, and shares.\n- Scenario Analysis: Implied prices should be formulas (e.g., revenue *EBITDA margin* EV\u002FEBITDA multiple \u002F (1+WACC)^5, adjusted for net cash). Probability-weighted target = SUMPRODUCT of probabilities and prices.\n\n- Financial Summary: Derived metrics (gross profit, margins, net cash, FCF, ROE) should be formulas referencing the raw data cells, not hardcoded.\n- Cross-sheet references use the format: ='DCF Model'!B42\n\n- Test: Open the Excel file, change WACC from 12.5% to 10%, and verify the implied share price, sensitivity table, and scenario prices all update. If anything stays static, it's broken.\n\nFormatting standards:\n\n- Professional header row with navy (#003366) background and white text\n- Alternating row shading for data tables\n\n- Number formatting: currency with commas ($#,##0), percentages (0.0%), large numbers in millions ($#,##0M)\n- Input cells (assumptions) highlighted in light yellow (#FFFFCC) with medium borders to visually distinguish from calculated cells\n\n- Freeze panes on header rows\n- Column widths auto-sized for readability\n\nPresent the Excel model to the user in chat alongside the PDF.\n\nStep 4 (OPTIONAL): Build the web app\n\nOnly build if the user requests an interactive dashboard or web view. When building the web app:\n\nDesign it as a native web dashboard — responsive, interactive charts (Recharts), scrollable sections. Do NOT make it a literal PDF replica with fixed-size page containers.\n\nHardcode data in a centralized data file (src\u002Fdata\u002Freport-data.ts) — no backend API needed for static analysis.\n\nUse the react-vite skill and generateFrontend() for scaffolding.\n\nPDF Report — Professional Research Report (Sell-Side Format)\n\nThe PDF should look like a sell-side initiation note from Goldman, Morgan Stanley, or JP Morgan. This is the primary deliverable — invest the most effort here.\n\nReport Structure\n\nPage 1 — Cover \u002F Executive Summary:\n\nDisclaimer line (required): \"This report is for informational purposes only and does not constitute investment advice. It is not a recommendation to buy, sell, or hold any security.\"\n\nCompany name, ticker, exchange, current price, market cap\n\nRating: Buy \u002F Hold \u002F Sell with price target and upside\u002Fdownside %\n\nInvestment thesis in 3-4 bullet points (the \"elevator pitch\")\n\nKey metrics snapshot: P\u002FE, EV\u002FEBITDA, revenue growth, FCF yield\n\nA 1-year price chart (generated via matplotlib, embedded as image)\n\nInvestor Profile box (only if profile was collected): Show risk tolerance, time horizon, and income preference in a small summary box\n\nPages 2-3 — Investment Thesis:\n\nBull case (with probability weighting if possible)\n\nBear case (required — what kills this trade?)\n\nKey catalysts with expected timeline\n\nCompetitive positioning \u002F moat analysis\n\nPages 3-4 — Financial Analysis:\n\nRevenue breakdown by segment (with a stacked bar chart)\n\nMargin trends over 4+ quarters (with a line chart)\n\nFCF bridge \u002F waterfall\n\nBalance sheet health (debt maturity, liquidity)\n\nPeer comparison table (pulled from yfinance for 3-5 peers)\n\nPage 5 — Valuation:\n\nDCF model summary (show assumptions: WACC, terminal growth, revenue CAGR)\n\nComparable company analysis table\n\nHistorical valuation range (P\u002FE or EV\u002FEBITDA band chart)\n\nPrice target derivation\n\nPage 6 — Technical Analysis:\n\nPrice chart with 50\u002F200 SMA overlay (generated via matplotlib)\n\nVolume analysis\n\nKey support\u002Fresistance levels\n\nRSI chart\n\nPage 7 — Risks:\n\nRanked by probability x impact\n\nRegulatory, competitive, execution, macro risks\n\nSpecific to this company, not generic boilerplate\n\nFinal Page — Sources & Disclaimer:\n\nFull citation list with dates for every external source referenced\n\nFull disclaimer (required — use the exact text from the \"Limitations & Disclaimer\" section below)\n\nIf investor profile was collected, include a closing note: \"This analysis was tailored to a [risk tolerance] risk profile with a [time horizon] time horizon. Your actual circumstances may differ. Consult a licensed financial advisor before making investment decisions.\"\n\nPDF Generation with fpdf2\n\nUse fpdf2 (Python) to generate the PDF. Install: pip install fpdf2.\n\nfrom fpdf import FPDF\n\nclass ResearchReport(FPDF):\n\ndef header(self):\n\nif self.page_no() == 1:\n\nreturn\n\nself.set_font(\"Helvetica\", \"B\", 8)\n\nself.set_text_color(0, 51, 102)\n\nself.cell(0, 6, \"Company (TICKER) | Equity Research Report\", align=\"L\")\n\nself.cell(0, 6, \"Report Date\", align=\"R\", new_x=\"LMARGIN\", new_y=\"NEXT\")\n\nself.set_draw_color(0, 51, 102)\n\nself.line(10, self.get_y(), 200, self.get_y())\n\nself.ln(2)\n\ndef footer(self):\n\nself.set_y(-15)\n\nself.set_font(\"Helvetica\", \"I\", 7)\n\nself.cell(0, 10, f\"Page {self.page_no()}\u002F{{nb}}\", align=\"C\")\n\ndef ensure_space(self, needed_mm):\n\n\"\"\"Check if needed_mm of vertical space remains on the current page.\n\nIf not, insert a page break. Call this before any section that has a\n\ntitle + table\u002Fchart combo to prevent them from splitting across pages.\"\"\"\n\nif self.get_y() + needed_mm > self.h - self.b_margin:\n\nself.add_page()\n\npdf = ResearchReport()\n\npdf.alias_nb_pages()\n\npdf.set_auto_page_break(auto=True, margin=15)\n\npdf.add_page()\n\n## Title\n\npdf.set_font(\"Helvetica\", \"B\", 24)\n\npdf.cell(0, 12, \"Company Analysis\", ln=True)\n\n## Embed chart (w=160, NOT 190 — see chart sizing notes below)\n\npdf.image(\"reports\u002Fcharts\u002Fprice_chart.png\", x=15, w=160)\n\npdf.output(\"reports\u002FResearch_Report.pdf\")\n\nCritical: Text Encoding for fpdf2\n\nfpdf2 uses latin-1 encoding by default. Special Unicode characters will cause errors or render as garbage. Before writing any text to the PDF, sanitize it:\n\ndef sanitize_text(text):\n\nreplacements = {\n\n\"\\u2014\": \"-\", \\# em-dash\n\n\"\\u2013\": \"-\", \\# en-dash\n\n\"\\u2018\": \"'\", \\# left single quote\n\n\"\\u2019\": \"'\", \\# right single quote\n\n\"\\u201c\": '\"', \\# left double quote\n\n\"\\u201d\": '\"', \\# right double quote\n\n\"\\u2022\": \"*\", \\# bullet\n\n\"\\u2026\": \"...\", \\# ellipsis\n\n\"\\u00a0\": \" \", \\# non-breaking space\n\n\"\\u2212\": \"-\", \\# minus sign\n\n\"\\u00b7\": \"*\", \\# middle dot\n\n}\n\nfor char, replacement in replacements.items():\n\ntext = text.replace(char, replacement)\n\nreturn text.encode(\"latin-1\", errors=\"replace\").decode(\"latin-1\")\n\nApply sanitize_text() to every string before passing it to pdf.cell(), pdf.multi_cell(), or pdf.write(). This is not optional — text from web scraping, yfinance, and deep research will contain Unicode characters that break the PDF.\n\nAvoiding Common fpdf2 Issues\n\nChart sizing — DO NOT use w=190 (full page width): Charts generated by matplotlib at 150 DPI are typically 1500-1700px wide and 800-1000px tall. At w=190mm, fpdf2 scales them proportionally, producing chart heights of 90-130mm — nearly half an A4 page. This pushes the chart to the next page, leaving a large white gap below the section title. Use w=160 (centered with x=15) as the default chart width. This keeps charts at ~75-110mm tall, which fits comfortably on a page alongside a title and surrounding content.\n\nSection splitting across pages: fpdf2's auto page break only triggers when content would overflow — it does NOT keep a section title together with its table or chart. A subtitle at y=270 will render on the current page, but the table below it starts on the next page, leaving the title orphaned. To prevent this, implement an ensure_space(needed_mm) method (see sample code above) and call it before every subtitle+table or subtitle+chart combo. Estimate the needed space: title (~7mm) + table header (~6mm) + rows (5mm each) + body text if any. For charts, use ~80mm. For tables with 5-8 rows, use ~50-55mm.\n\nSpacing discipline: Use tight spacing throughout. Recommended values: ln(0.5) after subtitles, ln(1) between sections, ln(1.5) between major sections. Avoid ln(3) or higher — it adds up fast across a multi-page report. Body text line height should be 4mm, bullet line height 4mm, table rows 5mm, table headers 6mm. These small values compound: a report with 20 sections saves 40-60mm of vertical space vs. generous spacing, which is nearly an entire extra page of content.\n\nAuto page break margin: Use margin=15, not margin=20. The default 20mm wastes 5mm per page (45mm across a 9-page report). 15mm provides enough room for the footer without wasting space.\n\nLong text overflow: Use multi_cell() for paragraphs, not cell(). cell() truncates at one line.\n\nTable column overflow: cell() does NOT clip or wrap text — it renders beyond the cell boundary, causing text to overflow into adjacent columns or off the page. For tables with a wide text column (like \"Details\" in a risk matrix), allocate most of the width to that column. Example: [45, 24, 24, 97] instead of [55, 27, 27, 81]. Also shorten text strings to fit: abbreviate \"Medium\" to \"Med\", truncate long names (\"Valuation Compression\" -> \"Valuation Compress.\"), and tighten phrasing. At 8pt Helvetica, a 97mm column fits roughly 50-55 characters. Test visually and shorten any string that overflows.\n\nTable alignment: Right-align numbers, left-align text. Use cell() with explicit widths for table columns.\n\nFont availability: Stick to built-in fonts: Helvetica, Times, Courier. These require no font file embedding. If you need bold\u002Fitalic, use the style parameter: set_font(\"Helvetica\", \"B\", 12).\n\nStyling Guidelines\n\nHeader bar on each page (except cover) with company name, ticker, report date, and page number\n\nData tables with alternating row shading, right-aligned numbers, 8pt font, 5mm row height\n\nCharts at w=160 (not full width) centered with x=15, with clear titles and axis labels\n\nCallout boxes for key insights (\"Management guided 15% revenue growth in Q4 call\")\n\nSource citations as footnotes or inline parenthetical references\n\nProfessional typography: 9pt body text (4mm line height), 8pt table text (5mm row height), 10pt subtitles, 13pt section headers. Keep spacing tight — ln(0.5) to ln(1.5) between elements, never ln(3) or higher.\n\nColor palette: Navy (#003366) for headers, dark gray (#333333) for body text, green (#228B22) for positive metrics, red (#CC0000) for negative metrics\n\nPage density: Aim for dense, information-rich pages with minimal white space. Every page should feel full. If a page has more than 40mm of unused space at the bottom, either pull the next section up (by removing the forced add_page()) or increase chart\u002Ftable sizes to fill the space.\n\nAvoid forced page breaks between sections: Do NOT use add_page() between every section. This creates large white gaps when the previous section ends partway down the page. Instead, let content flow continuously and use ensure_space(needed_mm) to trigger a page break only when there isn't enough room for the next section's title + content. Only use add_page() for truly standalone sections (e.g., cover page, disclaimer page). For everything else — financial tables, charts, risk matrices, catalysts — let them flow naturally with ensure_space() guarding against orphaned titles.\n\nBest Practices\n\nTimestamp everything — state data pull date; yfinance prices are ~15min delayed\n\nSector-relative only — a 30 P\u002FE is cheap in software, expensive in utilities\n\nLabel facts vs thesis — \"FCF yield is 6%\" (fact) vs \"undervalued\" (opinion)\n\nBear case required — every analysis must include: what kills this trade?\n\nPosition sizing reality — no single stock >5% for most retail portfolios; if conviction demands 20%, the conviction is the problem\n\nLimitations & Disclaimer\n\nInclude the following disclaimer text (or substantially similar language) on the final page of every PDF report and at the bottom of every web dashboard:\n\nIMPORTANT DISCLAIMER: This report is produced for informational and educational purposes only. It does not constitute investment advice, a recommendation, or a solicitation to buy, sell, or hold any security or financial instrument. The information contained herein is based on publicly available data sources that may be incomplete, delayed, or inaccurate. No representation or warranty, express or implied, is made as to the accuracy, completeness, or reliability of the information provided.\n\nAny opinions, estimates, or projections expressed are those of an automated analysis system and do not reflect the views of any licensed financial institution, registered investment advisor, or broker-dealer. Past performance is not indicative of future results. All investments carry risk, including the potential loss of principal.\n\nBefore making any investment decision, you should consult with a qualified, licensed financial advisor who can assess your individual circumstances, risk tolerance, and financial goals. The authors and publishers of this report accept no liability for any loss or damage arising from reliance on the information contained herein.\n\nTechnical limitations:\n\nyfinance scrapes Yahoo Finance — occasionally breaks, data may lag filings by days or weeks\n\nCannot access Bloomberg, FactSet, Refinitiv, or real-time Level 2 market data\n\nCannot execute trades, manage portfolios, or provide personalized financial planning\n\nData is delayed (typically 15-20 minutes for prices; financials may lag quarterly filings)\n\nAll equities can decline to zero; sector and thematic analysis reflects a point-in-time snapshot that may not reflect rapid market changes\n\nClosing Prompt\n\nAfter presenting the PDF report and DCF Excel model to the user, always end the reply with:\n\n\"Let me know if you'd like me to build an interactive web page for these findings as well.\"\n","# Stock & Investment Analyzer\n\n## OpenClawd Operator Adaptation\n\nRun this skill as part of the OpenClawd operator deck. Preserve the skill-specific workflow below, but frame outputs for Solana-native agents when relevant: prefer OpenClawd language, note whether the work can support autonomous agent operations, and keep financial, legal, tax, hiring, medical, or other regulated outputs informational unless the skill already requires a stricter disclaimer. Use the Llobster Legend persona only as light operator framing; do not let branding override accuracy, safety, or the user's stated domain.\n\n\nAnalyze stocks, companies, and investment opportunities using financial market data. Provide company profiles, technical analysis, fundamental analysis, and portfolio insights.\n\nPrimary deliverable: A professional PDF research report. The Excel model and interactive web app are optional extras — only build them if the user explicitly requests them.\n\nWhen to Use\n\nUser wants to analyze a specific stock or company\n\nUser asks about financial metrics, earnings, or valuations\n\nUser wants to compare investment options\n\nUser needs portfolio analysis or allocation advice\n\nUser asks about market trends or sector performance\n\nWhen NOT to Use\n\nTax-specific questions (use tax-reviewer skill)\n\nPersonal budgeting (use budget-planner skill)\n\nInsurance coverage (use insurance-optimizer skill)\n\nIMPORTANT: Disclaimer & Compliance\n\nAll output from this skill is strictly informational. It does not constitute investment advice, a recommendation or solicitation to buy or sell any security, or a substitute for professional financial counsel.\n\nEvery report, PDF, Excel model, or web dashboard produced by this skill must:\n\nState clearly on the cover page (or first visible section): \"This report is for informational purposes only and does not constitute investment advice. It is not a recommendation to buy, sell, or hold any security.\"\n\nInclude a full disclaimer on the final page (see \"Limitations & Disclaimer\" section below for the required text).\n\nNever use imperative language that implies a directive — say \"may outperform\" or \"could be worth investigating,\" not \"you should buy\" or \"add this to your portfolio.\"\n\nAlways recommend consulting a licensed financial advisor before making any investment decision.\n\nThis applies to all outputs regardless of whether an investor profile has been collected.\n\nOptional Investor Profile\n\nBefore starting any analysis, offer the user the option to share their investment profile. This step is entirely optional — if the user declines or wants to skip it, proceed immediately with a general-purpose analysis using the default assumptions below.\n\nHow to offer it: At the start of the analysis, ask something like: \"I can tailor this analysis to your investment style if you'd like to answer a few quick questions. Otherwise, I'll provide a general market perspective. Which do you prefer?\"\n\nProfiling Dimensions (ask all 5 if the user opts in)\n\nDimension\n\nQuestion\n\nOptions\n\nRisk tolerance\n\nHow would you describe your comfort with investment risk?\n\nConservative (preserve capital, minimize losses) \u002F Moderate (balanced growth with some downside protection) \u002F Aggressive (maximize returns, comfortable with volatility)\n\nTime horizon\n\nHow long do you plan to hold these investments?\n\nShort-term (under 1 year) \u002F Medium-term (1-5 years) \u002F Long-term (5+ years)\n\nCurrent allocation\n\nRoughly, what is your current portfolio split?\n\nApproximate % in equities, bonds\u002Ffixed income, cash, alternatives (real estate, commodities, crypto)\n\nIncome needs\n\nDo you need your investments to generate regular income?\n\nYes, dividend\u002Fincome-focused \u002F No, purely growth-focused \u002F Mixed\n\nExperience level\n\nHow would you describe your investing experience?\n\nBeginner (started recently, still learning) \u002F Intermediate (a few years, understands basic concepts) \u002F Advanced (experienced, comfortable with complex strategies)\n\nDefault Assumptions (when user skips profiling)\n\nWhen the user declines profiling, use these neutral defaults and do not assume anything about their personal situation:\n\nRisk tolerance: Moderate\n\nTime horizon: Medium-term (1-5 years)\n\nAllocation: Not assumed — present analysis without allocation recommendations\n\nIncome needs: Not assumed — cover both growth and income angles\n\nExperience level: Intermediate — use clear language but don't oversimplify\n\nHow Profiling Shapes the Report\n\nWhen profile data is available, adjust the report in these ways:\n\nCover page: Add an \"Investor Profile\" summary box showing the user's stated risk tolerance, time horizon, and income preference\n\nStock\u002Fsector picks: Add a suitability tag to each recommendation (e.g., \"Suitable for: moderate risk, 3-5yr horizon\" or \"Caution: high volatility — may not suit conservative investors\")\n\nAllocation section: Add a \"Suggested Allocation\" section at the end of the report, tailored to their risk\u002Ftimeline. Frame it as illustrative, not prescriptive (e.g., \"A moderate-risk investor with a 3-5 year horizon might consider an allocation along these lines...\")\n\nLanguage calibration: For beginners, use simpler explanations and fewer technical terms. For advanced investors, include more granular data and nuanced analysis.\n\nPicks that don't fit: Still include all relevant picks, but flag any that conflict with the user's stated profile (e.g., \"Note: This is a high-volatility pick that may not align with your conservative risk preference\")\n\nWhen profile data is NOT available, omit the Investor Profile box, omit the Suggested Allocation section, and present all picks without suitability tags. The core analysis remains identical.\n\nData Sources (Use These — Don't Guess)\n\nPython libs (run directly, no API key):\n\nimport yfinance as yf\n\nt = yf.Ticker(\"AAPL\")\n\nt.info \\# P\u002FE, market cap, beta, 52w range, margins\n\nt.financials \\# income statement (4yr)\n\nt.balance_sheet \\# debt, cash, equity\n\nt.cashflow \\# FCF, capex\n\nt.history(period=\"1y\") \\# OHLCV for technicals\n\nt.institutional_holders \\# 13F ownership\n\nSector screening: Use yfinance sector ETFs (XLE, XLK, XLF, XLV, XLI, XLP, XLU, XLRE, XLB, XLC, XLY) for reliable sector-level data. These are more stable than third-party screener libraries. For individual stock screening, pull yfinance data for each ticker directly.\n\nNote on finvizfinance: This library frequently breaks due to website changes. Avoid relying on it. Use yfinance sector ETFs and direct ticker lookups instead.\n\nPrimary filings: Start from the EDGAR filing index: webFetch(\"\u003Chttps:\u002F\u002Fwww.sec.gov\u002Fcgi-bin\u002Fbrowse-edgar?action=getcompany&CIK={ticker}&type=10-K>\"). This returns a list of filings — find the most recent 10-K and webFetch its \"Documents\" link to reach the actual filing. Read Item 1A (Risk Factors) and Item 7 (MD&A) — this is where management admits problems.\n\nInsider activity: webFetch(\"\u003Chttp:\u002F\u002Fopeninsider.com\u002Fscreener?s={ticker}>\") — look for cluster buys (multiple execs buying same week) and P-code open-market purchases (insider paid cash at market price — strongest signal). Ignore option exercises (M-code) and 10b5-1 scheduled sales.\n\nShort interest: webSearch \"{ticker} short interest fintel\" — >20% of float = crowded short, squeeze risk either direction.\n\nData Source Fallbacks\n\nyfinance occasionally returns empty data, tickers change, or rate limits kick in. Use this fallback hierarchy:\n\nPrimary: yfinance — try up to 2 retries with a short delay\n\nFallback: Web search for the specific metric (e.g., webSearch(\"[ticker] market cap 2026\"))\n\nLast resort: Flag the metric as \"data unavailable\" — never fabricate numbers\n\nResearch First — Mandatory Before Any Output\n\nNever show financials, tables, or a report to the user without thoroughly researching first. Before generating the PDF, you must:\n\nLoad the deep-research skill for comprehensive web research. This is not optional — every stock analysis must use deep research to gather real data before producing any deliverable.\n\nPull actual financials from yfinance AND cross-reference with SEC EDGAR filings (10-K, 10-Q). Do not rely on a single source.\n\nSearch for every company mentioned — if the user's request involves multiple companies or peers, pull financials on ALL of them, not just the primary ticker.\n\nBias towards tables and numbers from actual public filings. Every financial figure in the report must be traceable to a real source (SEC filing, earnings release, or yfinance data pull). Do not estimate or round when real numbers are available.\n\nIf you cannot verify a financial figure from at least one real source, flag it explicitly as unverified. Never present guessed or hallucinated numbers as fact.\n\nScoping Deep Research\n\nFor a single-stock analysis, launch 2-3 parallel research agents: one for fundamentals\u002Ffinancials, one for industry\u002Fcompetitive landscape, one for recent news\u002Fcatalysts.\n\nFor a multi-sector or thematic analysis, launch 4-6 parallel research agents organized by theme (e.g., macro environment, sector rotation, geopolitics, specific sector deep-dives). More than 6 agents rarely adds value and wastes time.\n\nMethodology\n\nStep 1: Pull the Data (Python)\n\nRun yfinance to get fundamentals + 1yr price history. Compute 50\u002F200 SMA, RSI(14), and current price vs 52w high. Takes 10 lines of pandas.\n\nStep 2: Fundamental Analysis\n\nValuation (compare to sector median, not S&P):\n\nP\u002FE — meaningless alone; flag if >2x sector median\n\nPEG — \u003C1.0 = growth at reasonable price; >2.0 = priced for perfection\n\nEV\u002FEBITDA — better than P\u002FE for capital-intensive or leveraged cos\n\nP\u002FS — only metric for unprofitable growth; >20x = needs hypergrowth to justify\n\nFCF yield (FCF\u002Fmarket cap) — >5% = genuinely cheap; negative = burning cash\n\nQuality red lines (practitioner heuristics):\n\nRevenue growing but FCF shrinking -> earnings quality problem, dig into receivables\n\nDebt\u002FEBITDA >4x -> one bad year from covenant breach\n\nGross margin compressing 3+ quarters -> losing pricing power\n\nStock-based comp >15% of revenue -> dilution machine (common in SaaS)\n\nGoodwill >50% of assets -> acquisition-heavy, writedown risk\n\nStep 3: Technical Context (Not Prediction)\n\nCompute in pandas — don't just describe:\n\nPrice vs 50\u002F200 SMA: below both = downtrend, don't catch knives\n\nGolden cross (50 crosses above 200) = trend confirmation, not entry signal\n\nRSI(14): >70 overbought \u002F \u003C30 oversold — only useful at extremes + divergence\n\nVolume: moves on 2x+ avg volume are real; low-volume moves fade\n\n% off 52w high: >30% drawdown in an uptrending market = something broke\n\nStep 4: The Retail Edge — Signals Institutions Ignore\n\nInsider cluster buys (OpenInsider): 3+ insiders open-market buying within 2 weeks is the single highest-conviction public signal. Research shows insider buys outperform; sells mean nothing (taxes\u002Fdivorces\u002Fyachts).\n\nBuying the dip: insider P-code purchase after >10% drop = management disagrees with the market\n\nShort squeeze setup: short interest >20% + days-to-cover >5 + any positive catalyst\n\nUnusual options: webSearch \"{ticker} unusual options activity\" — large OTM call sweeps before earnings sometimes leak info\n\nStep 5: Comparative Table\n\nBuild a pandas DataFrame with peers side-by-side: P\u002FE, PEG, rev growth, gross margin, FCF yield, debt\u002FEBITDA. The outlier in either direction is your thesis. Pull yfinance data for every peer company — do not leave cells blank or use estimates when real data is available. Every company in the comparison must have actual financials pulled and verified.\n\nStep 6: Web Research — Find Existing Analyst Reports and News\n\nUse web search aggressively via the deep-research skill. Before writing the report, gather real external research to cite:\n\nwebSearch(\"[ticker] analyst report 2026\")\n\nwebSearch(\"[ticker] earnings analysis site:seekingalpha.com\")\n\nwebSearch(\"[ticker] bull case bear case site:seekingalpha.com OR site:fool.com\")\n\nwebSearch(\"[company] investor presentation 2026 filetype:pdf\")\n\nwebSearch(\"[ticker] price target consensus\")\n\nwebSearch(\"[ticker] industry outlook [sector]\")\n\nwebSearch(\"[company] competitive landscape\")\n\nwebSearch(\"[ticker] short interest thesis\")\n\nSource hierarchy (cite all of these in the report):\n\nSource\n\nWhat you get\n\nHow to cite\n\nSEC EDGAR (10-K, 10-Q, 8-K)\n\nPrimary financials, risk factors, MD&A\n\n\"Source: [Company] 10-K FY2025, Item 7\"\n\nEarnings call transcripts\n\nManagement commentary, guidance\n\n\"Source: Q4 2025 Earnings Call, CEO remarks\"\n\nSell-side research (via SeekingAlpha, TipRanks)\n\nPrice targets, consensus estimates\n\n\"Source: TipRanks consensus, 12 analysts\"\n\nIndustry reports\n\nTAM, growth rates, competitive dynamics\n\n\"Source: [Firm] [Industry] Report, [Date]\"\n\nCompany investor presentations\n\nManagement's own bull case, KPIs\n\n\"Source: [Company] Investor Day 2025\"\n\nNews (Reuters, Bloomberg, CNBC)\n\nCatalysts, M&A, regulatory\n\n\"Source: Reuters, [Date]\"\n\nUse webFetch to pull actual content from SeekingAlpha articles, earnings transcripts, and investor presentations. Extract specific data points, quotes, and estimates to cite in the report.\n\nProject Architecture — Reusable Modules\n\nThe reports\u002F directory contains shared modules that all ticker-specific scripts import from. Never duplicate the ResearchReport class, chart functions, or Excel styling helpers — always import from these modules.\n\nreports\u002Freport_base.py — Shared PDF Report Class:\n\n- sanitize_text(text): Cleans Unicode characters for fpdf2's latin-1 encoding. Apply to ALL text before passing to pdf.cell() or pdf.multi_cell().\n- ResearchReport(company_name, ticker): Extends FPDF with all report methods. Constructor sets alias_nb_pages() and auto_page_break(margin=15).\n\n- Methods: header(), footer(), ensure_space(needed_mm), section_title(), sub_title(), body_text(), bullet_point(), key_metric(), table_header(), table_row(), data_table(), callout_box(), cover_page(data_dict), disclaimer_page(sources_text)\n- cover_page(data) expects a dict with keys: exchange, sector, rating, price_target, current_price, market_cap_str, range_52w, analyst_consensus\n\n- Usage: from report_base import ResearchReport, sanitize_text\n- IMPORTANT fpdf2 gotcha: bullet_point() uses cell(5, 4, chr(149)) then multi_cell(185, 4, text). You MUST pass an explicit width (185) to multi_cell after cell(), otherwise multi_cell gets 0 remaining width and throws \"Not enough horizontal space\" error. Never use multi_cell(0, ...) after a cell() on the same line.\n\nreports\u002Fchart_utils.py — Shared Chart Generation:\n\n- price_chart(df, ticker, company_name): 1-year price history with 50\u002F200 SMAs. Returns saved path.\n- revenue_margin_chart(years, revenues, gross_margins, op_margins, net_margins, ticker): Bar+line combo chart. Returns saved path.\n\n- segment_chart(segments_dict, ticker): Grouped bar chart from {year: {segment: value}} dict. Returns saved path.\n- peer_valuation_chart(peers_dict, ticker): 3-panel bar chart (Fwd P\u002FE, EV\u002FEBITDA, P\u002FS). peers_dict = {ticker: {fwd_pe, ev_ebitda, ps}}. Subject ticker highlighted in red. Returns saved path.\n\n- All charts saved to reports\u002Fcharts\u002F at 150 DPI.\n- Usage: from chart_utils import price_chart, revenue_margin_chart, segment_chart, peer_valuation_chart\n\nreports\u002Fexcel_base.py — Shared Excel Styling:\n\n- Style constants: NAVY_FILL, WHITE_FONT, HEADER_FONT, INPUT_FILL, LIGHT_GRAY, GREEN_FILL, RED_FILL, THIN_BORDER, INPUT_BORDER, THICK_BORDER\n- style_header_row(ws, row, max_col): Navy header with white bold text\n\n- style_data_cell(ws, row, col, fmt, is_input, shaded): Applies borders, fills, number format\n- auto_width(ws): Auto-sizes all columns\n\n- write_section_header(ws, row, col, text): Section label in navy bold\n- write_table(ws, start_row, headers, rows, col_formats, highlight_row): Complete table with header + data rows\n\n- Usage: from excel_base import *\n\nreports\u002Fdata_utils.py — Data Pulling & Validation:\n\n- pull_stock_data(ticker, retries=2): Pulls all yfinance data with retry logic. Returns dict with all metrics, formatted strings (market_cap_str, range_52w, analyst_consensus), and raw DataFrames (financials, balance_sheet, cashflow, quarterly_financials, history_1y).\n- pull_peer_data(tickers): Pulls key metrics for a list of peer tickers. Returns dict of dicts.\n\n- validate_data(data): Checks for common data issues (zero price, negative P\u002FE, missing history). Returns list of warning strings.\n- fmt_val(val, fmt_type, fallback): Format a value for display. Types: \"pct\", \"pct_change\", \"ratio\", \"dollar\", \"dollar_b\", \"market_cap\".\n\n- Usage: from data_utils import pull_stock_data, pull_peer_data, validate_data, fmt_val\n\nreports\u002Fgenerate_report.py — Single Entry Point CLI:\n\n- Usage: python reports\u002Fgenerate_report.py TICKER --peers PEER1,PEER2,PEER3\n- Runs the data pipeline: pulls subject + peer data, validates, generates price + peer charts\n\n- Outputs a data summary and tells you which ticker-specific scripts to create\u002Frun next\n- Revenue\u002Fmargin and segment charts require manual historical data (yfinance only provides trailing 4 years, segment data requires 10-K parsing)\n\nWorkflow for Adding a New Ticker:\n\n1. Run: python reports\u002Fgenerate_report.py TICKER --peers PEER1,PEER2\n2. Create reports\u002Fgenerate_{ticker}_pdf.py — imports ResearchReport from report_base, chart paths from chart_utils\n\n3. Create reports\u002Fgenerate_{ticker}_dcf.py — imports excel_base helpers, uses Excel formulas (not hardcoded values)\n4. Run both scripts to generate the PDF and Excel model\n\n5. Present both files to the user\n\nData Validation Rules:\n\n- Always call validate_data() after pulling data and print warnings\n- Negative trailing P\u002FE means the company is unprofitable — flag it, don't hide it\n\n- If yfinance returns None for a metric, display \"N\u002FA\" in the report, never 0 or a guess\n- Cross-reference key financials (revenue, net income) between yfinance and SEC filings when possible\n\n- If a peer ticker fails to pull data, include it in the table with \"N\u002FA\" values rather than silently dropping it\n\nPeer Data — Always Pull from yfinance:\n\n- Never hardcode peer metrics — always pull from yfinance via pull_peer_data() so data is current\n- Pull at least 4 peers in the same sector\u002Findustry\n\n- Common peer sets: Tech (AAPL\u002FMSFT\u002FGOOG\u002FAMZN\u002FMETA), Semis (NVDA\u002FAMD\u002FAVGO\u002FQCOM\u002FINTC), Auto (TSLA\u002FGM\u002FF\u002FTM\u002FRIVN)\n- For the comparable companies Excel sheet, use formulas for the median row: =MEDIAN(C5:C10)\n\nBuild Order — PDF Report + DCF Excel Model (Built in Parallel)\n\nBoth the PDF research report and the DCF Excel model are primary deliverables. Build them in parallel from the same research data.\n\nStep 1: Generate charts (Python + matplotlib)\n\nGenerate all charts first — these will be embedded in the PDF. Save as PNG at 150+ DPI to reports\u002Fcharts\u002F. Generate at least 4 charts: price history with SMAs, revenue\u002Fmargin trends, peer valuation comparison, and one more relevant to the thesis.\n\nimport matplotlib.pyplot as plt\n\nimport yfinance as yf\n\ndf = yf.Ticker(\"AAPL\").history(period=\"1y\")\n\nfig, ax = plt.subplots(figsize=(10, 5))\n\nax.plot(df.index, df['Close'], label='Price', color='#1a1a2e')\n\nax.plot(df.index, df['Close'].rolling(50).mean(), label='50 SMA', color='#e94560', linestyle='--')\n\nax.plot(df.index, df['Close'].rolling(200).mean(), label='200 SMA', color='#0f3460', linestyle='--')\n\nax.set_title(\"AAPL - 1 Year Price History\")\n\nax.legend()\n\nax.grid(alpha=0.3)\n\nfig.savefig(\"reports\u002Fcharts\u002Fprice_chart.png\", dpi=150, bbox_inches='tight')\n\nStep 2: Generate the PDF research report (PRIMARY DELIVERABLE \\#1)\n\nWrite a Python generation script (reports\u002Fgenerate_pdf.py) using fpdf2 to produce a polished, multi-page equity research PDF. Do not output a markdown summary as a substitute. Do not skip the PDF.\n\nWhy fpdf2, not jsPDF: Python is already required for yfinance and matplotlib. fpdf2 installs instantly (pip install fpdf2), runs reliably, and handles image embedding natively. jsPDF (Node) has heavy dependencies, frequent install timeouts in constrained environments, and is designed for browser-side generation — not server-side report building.\n\nPresent the PDF to the user in chat immediately after generating it.\n\nStep 3: Build the DCF Excel Model (PRIMARY DELIVERABLE \\#2 — built in parallel with the PDF)\n\nAlways build the DCF Excel model alongside the PDF report. Do not wait for user request — this is a standard deliverable for every stock analysis.\n\nUse openpyxl in Python to generate the .xlsx file (reports\u002F{TICKER}_DCF_Model.xlsx):\n\nfrom openpyxl import Workbook\n\nfrom openpyxl.styles import Font, PatternFill, Alignment, Border, Side, numbers\n\nfrom openpyxl.utils import get_column_letter\n\nThe Excel model must include the following sheets:\n\nSheet 1 — DCF Model:\n\n- Revenue projections (5-year forecast with growth assumptions)\n- EBITDA \u002F operating income projections (margin assumptions)\n\n- Free cash flow build: EBITDA -> subtract taxes, capex, change in working capital -> unlevered FCF\n- Terminal value calculation (perpetuity growth method AND exit multiple method)\n\n- WACC calculation with clearly labeled inputs: risk-free rate, equity risk premium, beta, cost of debt, tax rate, debt\u002Fequity ratio\n- Enterprise value -> equity value -> implied share price\n\n- Upside\u002Fdownside % vs current price\n\nSheet 2 — Sensitivity Analysis:\n\n- Two-variable data table: WACC (rows) vs terminal growth rate (columns) showing implied share price at each combination\n- Highlight the base case cell\n\n- Use conditional formatting: green for upside scenarios, red for downside\n\nSheet 3 — Scenario Analysis:\n\n- Bull \u002F Base \u002F Bear cases with different revenue growth, margin, and multiple assumptions\n- Probability-weighted price target (e.g., 25% Bull, 50% Base, 25% Bear)\n\n- Show each scenario's implied share price and upside\u002Fdownside\n\nSheet 4 — Comparable Companies:\n\n- Peer comparison table: ticker, market cap, P\u002FE, EV\u002FEBITDA, P\u002FS, revenue growth, gross margin, FCF yield\n- Pull real data from yfinance for 4-6 peers\n\n- Highlight the subject company row\n\nSheet 5 — Financial Summary:\n\n- Historical income statement (3-4 years from yfinance)\n- Historical balance sheet highlights (cash, debt, equity)\n\n- Key ratios: ROE, ROIC, debt\u002FEBITDA, current ratio\n\nCRITICAL — Use Excel Formulas, Not Hardcoded Values:\n\n- Every calculated cell MUST use an Excel formula, not a pre-computed Python value. The entire point of a financial model is that users can change assumptions and see results update automatically.\n- Input cells (yellow): hardcoded values the user can edit (growth rates, margins, WACC inputs, tax rate, etc.)\n\n- Calculated cells: MUST be formulas referencing input cells or other calculated cells. Examples:\n- Cost of Equity: =B7+B9*B8 (risk-free + beta \\* ERP)\n\n- WACC: =B10*(1-B13)+B11*(1-B12)*B13 (formula using CoE, CoD, tax, D\u002FTC)\n- Revenue projections: =B21*(1+C22) (prior year \\* (1 + growth rate))\n\n- Gross Profit: =C21*C24 (revenue \\* gross margin)\n- Terminal Value: =B42*(1+B43)\u002F(B44-B43) (FCF*(1+g)\u002F(WACC-g))\n\n- PV factors: =1\u002F(1+B44)^n\n- Implied Share Price: =B55\u002FB56 (equity value \u002F shares)\n\n- Sensitivity table: Each cell should be a formula computing implied price for that row's WACC and column's terminal growth rate, referencing the DCF Model sheet for PV of FCFs, terminal FCF, debt, cash, and shares.\n- Scenario Analysis: Implied prices should be formulas (e.g., revenue *EBITDA margin* EV\u002FEBITDA multiple \u002F (1+WACC)^5, adjusted for net cash). Probability-weighted target = SUMPRODUCT of probabilities and prices.\n\n- Financial Summary: Derived metrics (gross profit, margins, net cash, FCF, ROE) should be formulas referencing the raw data cells, not hardcoded.\n- Cross-sheet references use the format: ='DCF Model'!B42\n\n- Test: Open the Excel file, change WACC from 12.5% to 10%, and verify the implied share price, sensitivity table, and scenario prices all update. If anything stays static, it's broken.\n\nFormatting standards:\n\n- Professional header row with navy (#003366) background and white text\n- Alternating row shading for data tables\n\n- Number formatting: currency with commas ($#,##0), percentages (0.0%), large numbers in millions ($#,##0M)\n- Input cells (assumptions) highlighted in light yellow (#FFFFCC) with medium borders to visually distinguish from calculated cells\n\n- Freeze panes on header rows\n- Column widths auto-sized for readability\n\nPresent the Excel model to the user in chat alongside the PDF.\n\nStep 4 (OPTIONAL): Build the web app\n\nOnly build if the user requests an interactive dashboard or web view. When building the web app:\n\nDesign it as a native web dashboard — responsive, interactive charts (Recharts), scrollable sections. Do NOT make it a literal PDF replica with fixed-size page containers.\n\nHardcode data in a centralized data file (src\u002Fdata\u002Freport-data.ts) — no backend API needed for static analysis.\n\nUse the react-vite skill and generateFrontend() for scaffolding.\n\nPDF Report — Professional Research Report (Sell-Side Format)\n\nThe PDF should look like a sell-side initiation note from Goldman, Morgan Stanley, or JP Morgan. This is the primary deliverable — invest the most effort here.\n\nReport Structure\n\nPage 1 — Cover \u002F Executive Summary:\n\nDisclaimer line (required): \"This report is for informational purposes only and does not constitute investment advice. It is not a recommendation to buy, sell, or hold any security.\"\n\nCompany name, ticker, exchange, current price, market cap\n\nRating: Buy \u002F Hold \u002F Sell with price target and upside\u002Fdownside %\n\nInvestment thesis in 3-4 bullet points (the \"elevator pitch\")\n\nKey metrics snapshot: P\u002FE, EV\u002FEBITDA, revenue growth, FCF yield\n\nA 1-year price chart (generated via matplotlib, embedded as image)\n\nInvestor Profile box (only if profile was collected): Show risk tolerance, time horizon, and income preference in a small summary box\n\nPages 2-3 — Investment Thesis:\n\nBull case (with probability weighting if possible)\n\nBear case (required — what kills this trade?)\n\nKey catalysts with expected timeline\n\nCompetitive positioning \u002F moat analysis\n\nPages 3-4 — Financial Analysis:\n\nRevenue breakdown by segment (with a stacked bar chart)\n\nMargin trends over 4+ quarters (with a line chart)\n\nFCF bridge \u002F waterfall\n\nBalance sheet health (debt maturity, liquidity)\n\nPeer comparison table (pulled from yfinance for 3-5 peers)\n\nPage 5 — Valuation:\n\nDCF model summary (show assumptions: WACC, terminal growth, revenue CAGR)\n\nComparable company analysis table\n\nHistorical valuation range (P\u002FE or EV\u002FEBITDA band chart)\n\nPrice target derivation\n\nPage 6 — Technical Analysis:\n\nPrice chart with 50\u002F200 SMA overlay (generated via matplotlib)\n\nVolume analysis\n\nKey support\u002Fresistance levels\n\nRSI chart\n\nPage 7 — Risks:\n\nRanked by probability x impact\n\nRegulatory, competitive, execution, macro risks\n\nSpecific to this company, not generic boilerplate\n\nFinal Page — Sources & Disclaimer:\n\nFull citation list with dates for every external source referenced\n\nFull disclaimer (required — use the exact text from the \"Limitations & Disclaimer\" section below)\n\nIf investor profile was collected, include a closing note: \"This analysis was tailored to a [risk tolerance] risk profile with a [time horizon] time horizon. Your actual circumstances may differ. Consult a licensed financial advisor before making investment decisions.\"\n\nPDF Generation with fpdf2\n\nUse fpdf2 (Python) to generate the PDF. Install: pip install fpdf2.\n\nfrom fpdf import FPDF\n\nclass ResearchReport(FPDF):\n\ndef header(self):\n\nif self.page_no() == 1:\n\nreturn\n\nself.set_font(\"Helvetica\", \"B\", 8)\n\nself.set_text_color(0, 51, 102)\n\nself.cell(0, 6, \"Company (TICKER) | Equity Research Report\", align=\"L\")\n\nself.cell(0, 6, \"Report Date\", align=\"R\", new_x=\"LMARGIN\", new_y=\"NEXT\")\n\nself.set_draw_color(0, 51, 102)\n\nself.line(10, self.get_y(), 200, self.get_y())\n\nself.ln(2)\n\ndef footer(self):\n\nself.set_y(-15)\n\nself.set_font(\"Helvetica\", \"I\", 7)\n\nself.cell(0, 10, f\"Page {self.page_no()}\u002F{{nb}}\", align=\"C\")\n\ndef ensure_space(self, needed_mm):\n\n\"\"\"Check if needed_mm of vertical space remains on the current page.\n\nIf not, insert a page break. Call this before any section that has a\n\ntitle + table\u002Fchart combo to prevent them from splitting across pages.\"\"\"\n\nif self.get_y() + needed_mm > self.h - self.b_margin:\n\nself.add_page()\n\npdf = ResearchReport()\n\npdf.alias_nb_pages()\n\npdf.set_auto_page_break(auto=True, margin=15)\n\npdf.add_page()\n\n## Title\n\npdf.set_font(\"Helvetica\", \"B\", 24)\n\npdf.cell(0, 12, \"Company Analysis\", ln=True)\n\n## Embed chart (w=160, NOT 190 — see chart sizing notes below)\n\npdf.image(\"reports\u002Fcharts\u002Fprice_chart.png\", x=15, w=160)\n\npdf.output(\"reports\u002FResearch_Report.pdf\")\n\nCritical: Text Encoding for fpdf2\n\nfpdf2 uses latin-1 encoding by default. Special Unicode characters will cause errors or render as garbage. Before writing any text to the PDF, sanitize it:\n\ndef sanitize_text(text):\n\nreplacements = {\n\n\"\\u2014\": \"-\", \\# em-dash\n\n\"\\u2013\": \"-\", \\# en-dash\n\n\"\\u2018\": \"'\", \\# left single quote\n\n\"\\u2019\": \"'\", \\# right single quote\n\n\"\\u201c\": '\"', \\# left double quote\n\n\"\\u201d\": '\"', \\# right double quote\n\n\"\\u2022\": \"*\", \\# bullet\n\n\"\\u2026\": \"...\", \\# ellipsis\n\n\"\\u00a0\": \" \", \\# non-breaking space\n\n\"\\u2212\": \"-\", \\# minus sign\n\n\"\\u00b7\": \"*\", \\# middle dot\n\n}\n\nfor char, replacement in replacements.items():\n\ntext = text.replace(char, replacement)\n\nreturn text.encode(\"latin-1\", errors=\"replace\").decode(\"latin-1\")\n\nApply sanitize_text() to every string before passing it to pdf.cell(), pdf.multi_cell(), or pdf.write(). This is not optional — text from web scraping, yfinance, and deep research will contain Unicode characters that break the PDF.\n\nAvoiding Common fpdf2 Issues\n\nChart sizing — DO NOT use w=190 (full page width): Charts generated by matplotlib at 150 DPI are typically 1500-1700px wide and 800-1000px tall. At w=190mm, fpdf2 scales them proportionally, producing chart heights of 90-130mm — nearly half an A4 page. This pushes the chart to the next page, leaving a large white gap below the section title. Use w=160 (centered with x=15) as the default chart width. This keeps charts at ~75-110mm tall, which fits comfortably on a page alongside a title and surrounding content.\n\nSection splitting across pages: fpdf2's auto page break only triggers when content would overflow — it does NOT keep a section title together with its table or chart. A subtitle at y=270 will render on the current page, but the table below it starts on the next page, leaving the title orphaned. To prevent this, implement an ensure_space(needed_mm) method (see sample code above) and call it before every subtitle+table or subtitle+chart combo. Estimate the needed space: title (~7mm) + table header (~6mm) + rows (5mm each) + body text if any. For charts, use ~80mm. For tables with 5-8 rows, use ~50-55mm.\n\nSpacing discipline: Use tight spacing throughout. Recommended values: ln(0.5) after subtitles, ln(1) between sections, ln(1.5) between major sections. Avoid ln(3) or higher — it adds up fast across a multi-page report. Body text line height should be 4mm, bullet line height 4mm, table rows 5mm, table headers 6mm. These small values compound: a report with 20 sections saves 40-60mm of vertical space vs. generous spacing, which is nearly an entire extra page of content.\n\nAuto page break margin: Use margin=15, not margin=20. The default 20mm wastes 5mm per page (45mm across a 9-page report). 15mm provides enough room for the footer without wasting space.\n\nLong text overflow: Use multi_cell() for paragraphs, not cell(). cell() truncates at one line.\n\nTable column overflow: cell() does NOT clip or wrap text — it renders beyond the cell boundary, causing text to overflow into adjacent columns or off the page. For tables with a wide text column (like \"Details\" in a risk matrix), allocate most of the width to that column. Example: [45, 24, 24, 97] instead of [55, 27, 27, 81]. Also shorten text strings to fit: abbreviate \"Medium\" to \"Med\", truncate long names (\"Valuation Compression\" -> \"Valuation Compress.\"), and tighten phrasing. At 8pt Helvetica, a 97mm column fits roughly 50-55 characters. Test visually and shorten any string that overflows.\n\nTable alignment: Right-align numbers, left-align text. Use cell() with explicit widths for table columns.\n\nFont availability: Stick to built-in fonts: Helvetica, Times, Courier. These require no font file embedding. If you need bold\u002Fitalic, use the style parameter: set_font(\"Helvetica\", \"B\", 12).\n\nStyling Guidelines\n\nHeader bar on each page (except cover) with company name, ticker, report date, and page number\n\nData tables with alternating row shading, right-aligned numbers, 8pt font, 5mm row height\n\nCharts at w=160 (not full width) centered with x=15, with clear titles and axis labels\n\nCallout boxes for key insights (\"Management guided 15% revenue growth in Q4 call\")\n\nSource citations as footnotes or inline parenthetical references\n\nProfessional typography: 9pt body text (4mm line height), 8pt table text (5mm row height), 10pt subtitles, 13pt section headers. Keep spacing tight — ln(0.5) to ln(1.5) between elements, never ln(3) or higher.\n\nColor palette: Navy (#003366) for headers, dark gray (#333333) for body text, green (#228B22) for positive metrics, red (#CC0000) for negative metrics\n\nPage density: Aim for dense, information-rich pages with minimal white space. Every page should feel full. If a page has more than 40mm of unused space at the bottom, either pull the next section up (by removing the forced add_page()) or increase chart\u002Ftable sizes to fill the space.\n\nAvoid forced page breaks between sections: Do NOT use add_page() between every section. This creates large white gaps when the previous section ends partway down the page. Instead, let content flow continuously and use ensure_space(needed_mm) to trigger a page break only when there isn't enough room for the next section's title + content. Only use add_page() for truly standalone sections (e.g., cover page, disclaimer page). For everything else — financial tables, charts, risk matrices, catalysts — let them flow naturally with ensure_space() guarding against orphaned titles.\n\nBest Practices\n\nTimestamp everything — state data pull date; yfinance prices are ~15min delayed\n\nSector-relative only — a 30 P\u002FE is cheap in software, expensive in utilities\n\nLabel facts vs thesis — \"FCF yield is 6%\" (fact) vs \"undervalued\" (opinion)\n\nBear case required — every analysis must include: what kills this trade?\n\nPosition sizing reality — no single stock >5% for most retail portfolios; if conviction demands 20%, the conviction is the problem\n\nLimitations & Disclaimer\n\nInclude the following disclaimer text (or substantially similar language) on the final page of every PDF report and at the bottom of every web dashboard:\n\nIMPORTANT DISCLAIMER: This report is produced for informational and educational purposes only. It does not constitute investment advice, a recommendation, or a solicitation to buy, sell, or hold any security or financial instrument. The information contained herein is based on publicly available data sources that may be incomplete, delayed, or inaccurate. No representation or warranty, express or implied, is made as to the accuracy, completeness, or reliability of the information provided.\n\nAny opinions, estimates, or projections expressed are those of an automated analysis system and do not reflect the views of any licensed financial institution, registered investment advisor, or broker-dealer. Past performance is not indicative of future results. All investments carry risk, including the potential loss of principal.\n\nBefore making any investment decision, you should consult with a qualified, licensed financial advisor who can assess your individual circumstances, risk tolerance, and financial goals. The authors and publishers of this report accept no liability for any loss or damage arising from reliance on the information contained herein.\n\nTechnical limitations:\n\nyfinance scrapes Yahoo Finance — occasionally breaks, data may lag filings by days or weeks\n\nCannot access Bloomberg, FactSet, Refinitiv, or real-time Level 2 market data\n\nCannot execute trades, manage portfolios, or provide personalized financial planning\n\nData is delayed (typically 15-20 minutes for prices; financials may lag quarterly filings)\n\nAll equities can decline to zero; sector and thematic analysis reflects a point-in-time snapshot that may not reflect rapid market changes\n\nClosing Prompt\n\nAfter presenting the PDF report and DCF Excel model to the user, always end the reply with:\n\n\"Let me know if you'd like me to build an interactive web page for these findings as well.\"\n",{"name":5,"description":7},"main","6b4a291df75c639681e93b4b5b812126443fd6ed","secondary_skills\u002Fstock-analyzer",3,"https:\u002F\u002Fgithub.com\u002Fclawdsolana\u002FOpenClawd","https:\u002F\u002Fsolanaclawd.com\u002Fopenclawd","https:\u002F\u002Fgithub.com\u002Fclawdsolana","https:\u002F\u002Fmusebook.trade","Welcome to Open Clawd. The future of Ai on and off chain. Bringing webMCP to web 3 using X402, A2A, MPP and so much more!","2026-08-01 09:30:51",{"source":8,"skillName":5,"commands":52,"prompt":62,"zipAvailable":63},[53,56,59],{"tool":54,"command":55},"npx","npx skills add clawdsolana\u002FOpenClawd --skill stock-analyzer",{"tool":57,"command":58},"bunx","bunx skills add clawdsolana\u002FOpenClawd --skill stock-analyzer",{"tool":60,"command":61},"pnpm","pnpm dlx skills add clawdsolana\u002FOpenClawd --skill stock-analyzer","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fclawdsolana\u002FOpenClawd\u002Fblob\u002Fmain\u002Fsecondary_skills\u002Fstock-analyzer\u002FSKILL.md",true,[65],{"path":66,"resourceType":67,"resourceTypeDesc":68,"sizeBytes":69,"sourceHtmlUrl":70},".fingerprint","OTHER","其他",32,"https:\u002F\u002Fgithub.com\u002Fclawdsolana\u002FOpenClawd\u002Fblob\u002Fmain\u002Fsecondary_skills\u002Fstock-analyzer\u002F.fingerprint",[72,103,131,157,167,191],{"skillNo":73,"name":74,"nameZh":6,"description":75,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":76,"categories":77,"keywords":81,"publishedAt":102},"01M71KRBD0W01EAXJ8YM4M1SJ2","solana-dev","End-to-end Solana development playbook (Jan 2026). Prefer Solana Foundation framework-kit (@solana\u002Fclient + @solana\u002Freact-hooks) for React\u002FNext.js UI. Prefer @solana\u002Fkit for all new client\u002FRPC\u002Ftransaction code. When legacy dependencies require web3.js, isolate it behind @solana\u002Fweb3-compat (or @solana\u002Fweb3.js as a true legacy fallback). Covers wallet-standard-first connection (incl. ConnectorKit), Anchor\u002FPinocchio programs, Codama-based client generation, LiteSVM\u002FMollusk\u002FSurfpool testing, and security checklists.","https:\u002F\u002Fgithub.com\u002Fclawdsolana\u002FOpenClawd\u002Fblob\u002Fmain\u002Fsrc\u002Fskills\u002Fsolana-dev\u002FSKILL.md",[78],{"categoryNo":79,"categoryName":80,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000008","测试与质量",[82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101],"solana dev","dev","solana","Wallet","end","testing","hooks","kit","new","when","web3","code","react","next","2026","playbook","security","ui","fallback","framework","2026-11-10 10:56:23",{"skillNo":104,"name":105,"nameZh":6,"description":106,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":107,"categories":108,"keywords":112,"publishedAt":102},"01M71KR8QPESSY61SVE8ZTFNVT","xurl","A CLI tool for making authenticated requests to the X (Twitter) API. Use this skill when you need to post tweets, reply, quote, search, read posts, manage followers, send DMs, upload media, or interact with any X API v2 endpoint.","https:\u002F\u002Fgithub.com\u002Fclawdsolana\u002FOpenClawd\u002Fblob\u002Fmain\u002Fskills\u002Fxurl\u002FSKILL.md",[109],{"categoryNo":110,"categoryName":111,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发",[105,113,114,115,116,117,118,91,119,120,121,122,123,124,125,126,127,128,129,130],"posts","Media","x api","search","send","tap","you","api","cli","read","install","cli tool","endpoint","brew","for","manage","tool","twitter",{"skillNo":132,"name":133,"nameZh":6,"description":134,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":135,"categories":136,"keywords":138,"publishedAt":102},"01M71KR6FSXV88YDKWP7NBY3SH","wurk-integration","WURK API integration for monetizing agents with x402 payments on Solana\u002FBase - create social jobs, custom challenges, and agent help tasks","https:\u002F\u002Fgithub.com\u002Fclawdsolana\u002FOpenClawd\u002Fblob\u002Fmain\u002Fskills\u002Fwurk-integration\u002FSKILL.md",[137],{"categoryNo":110,"categoryName":111,"taxonomyType":21,"taxonomyTypeDesc":22},[139,140,141,142,143,144,145,146,120,147,148,149,150,151,152,153,127,154,155,156],"wurk","wurk integration","integration","x402","social","payments","tasks","team","create","social-jobs","command","agent-help","custom-jobs","clawd","safe","api integration","custom","true",{"skillNo":158,"name":159,"nameZh":6,"description":160,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":161,"categories":162,"keywords":163,"publishedAt":102},"01M71KR4HCX41BPAK9GP7RPRRP","workflows","Manage application workflows including configuration, restart, and removal.","https:\u002F\u002Fgithub.com\u002Fclawdsolana\u002FOpenClawd\u002Fblob\u002Fmain\u002Fskills\u002Fworkflows\u002FSKILL.md",[],[159,128,164,165,166],"configuration","application","restart",{"skillNo":168,"name":169,"nameZh":6,"description":170,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":171,"categories":172,"keywords":173,"publishedAt":102},"01M71KR1VM0F5GPEQDNG5J7A0H","web-search","Search the web, fetch content, extract branding profiles, and capture screenshots from URLs. Use for real-time information, API documentation, current events, design matching, and visual reference.","https:\u002F\u002Fgithub.com\u002Fclawdsolana\u002FOpenClawd\u002Fblob\u002Fmain\u002Fskills\u002Fweb-search\u002FSKILL.md",[],[174,116,175,176,177,127,178,179,180,181,182,183,184,120,185,186,187,188,189,190],"web search","web","fetch","api documentation","real-time","time","profiles","design","use","information","visual","documentation","branding","matching","reference","capture","content",{"skillNo":192,"name":193,"nameZh":6,"description":194,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":195,"categories":196,"keywords":197,"publishedAt":102},"01M71KQZN5MR1JFQHG5GWXYAHS","weather","Get current weather and forecasts via wttr.in or Open-Meteo. Use when: user asks about weather, temperature, or forecasts for any location. NOT for: historical weather data, severe weather alerts, or detailed meteorological analysis. No API key needed.","https:\u002F\u002Fgithub.com\u002Fclawdsolana\u002FOpenClawd\u002Fblob\u002Fmain\u002Fskills\u002Fweather\u002FSKILL.md",[],[193,198,127,199,182,91,33,120,200,201,202,203,204,205,206,207,208,209],"get","open","alerts","temperature","curl","via","help","about","https","location","data","user"]