mirror of
https://github.com/shawnkim1997/All-in-one-Financial-Analysis.git
synced 2026-08-13 02:18:06 +00:00
docs: resolve sync issues and update readme
This commit is contained in:
@@ -329,8 +329,8 @@ Item 8 excerpt:
|
||||
return pd.DataFrame()
|
||||
|
||||
|
||||
def run_analysis(ticker: str, api_key: str, email: str, analysis_only: bool = False) -> tuple[str, str, str, pd.DataFrame]:
|
||||
"""Download 10-K, extract text, get Item 7/8, return detailed summary, CFA report, and optionally metrics table."""
|
||||
def download_and_extract_sections(ticker: str, email: str) -> tuple[str, str, str]:
|
||||
"""Download 10-K, pre-filter, extract Item 7 & 8 only. Returns (full_text, item7, item8)."""
|
||||
Downloader = get_edgar_downloader()
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
download_root = Path(tmpdir)
|
||||
@@ -343,20 +343,23 @@ def run_analysis(ticker: str, api_key: str, email: str, analysis_only: bool = Fa
|
||||
if not full_text:
|
||||
raise ValueError("Could not extract text from the 10-K.")
|
||||
|
||||
# Pre-filter: drop PART I, ITEM 1–6; work only from Item 7 onward (reduces tokens)
|
||||
text_from_item7 = prefilter_after_item7(full_text)
|
||||
|
||||
# Section extraction: only Item 7 (MD&A) and Item 8 (Financial Statements)
|
||||
item7 = find_item_section(text_from_item7, 7, ["Management's Discussion", "MD&A", "Analysis"])
|
||||
item8 = find_item_section(text_from_item7, 8, ["Financial Statements", "Consolidated"])
|
||||
|
||||
# Fallback if extraction fails: use trimmed slice (still no full document)
|
||||
if not item7:
|
||||
item7 = smart_chunk(text_from_item7[:120000], max_chars=30000)
|
||||
if not item8:
|
||||
remainder = text_from_item7[100000:220000] if len(text_from_item7) > 100000 else text_from_item7
|
||||
item8 = smart_chunk(remainder, max_chars=30000)
|
||||
|
||||
return full_text, item7, item8
|
||||
|
||||
|
||||
def run_analysis(ticker: str, api_key: str, email: str, analysis_only: bool = False) -> tuple[str, str, str, pd.DataFrame]:
|
||||
"""Download 10-K, extract Item 7/8, call Gemini; return summary, report, full_text, metrics table."""
|
||||
full_text, item7, item8 = download_and_extract_sections(ticker, email)
|
||||
|
||||
detailed_summary, cfa_report = get_ai_summary_and_report(api_key, full_text, item7, item8, ticker)
|
||||
if analysis_only:
|
||||
df_metrics = pd.DataFrame()
|
||||
@@ -396,10 +399,48 @@ with st.sidebar:
|
||||
|
||||
ticker = st.text_input("Stock Ticker (e.g. AAPL, MSFT)", value="AAPL", max_chars=10).strip().upper()
|
||||
if not ticker:
|
||||
st.info("Enter a ticker and click 'Run Analysis'.")
|
||||
st.stop()
|
||||
st.info("Enter a ticker and click 'Run Analysis', or pick one from the S&P 500 list below.")
|
||||
|
||||
# S&P 500 sample: (Company name, Ticker) – shown at bottom
|
||||
SP500_SAMPLE = [
|
||||
("Apple Inc.", "AAPL"), ("Microsoft Corporation", "MSFT"), ("Amazon.com Inc.", "AMZN"),
|
||||
("NVIDIA Corporation", "NVDA"), ("Alphabet Inc. (Google)", "GOOGL"), ("Meta Platforms Inc. (Facebook)", "META"),
|
||||
("Berkshire Hathaway Inc.", "BRK.B"), ("Tesla Inc.", "TSLA"), ("JPMorgan Chase & Co.", "JPM"),
|
||||
("Visa Inc.", "V"), ("UnitedHealth Group Inc.", "UNH"), ("Procter & Gamble Co.", "PG"),
|
||||
("Exxon Mobil Corporation", "XOM"), ("Johnson & Johnson", "JNJ"), ("Mastercard Inc.", "MA"),
|
||||
("Chevron Corporation", "CVX"), ("Home Depot Inc.", "HD"), ("Merck & Co. Inc.", "MRK"),
|
||||
("AbbVie Inc.", "ABBV"), ("Costco Wholesale Corporation", "COST"),
|
||||
("PepsiCo Inc.", "PEP"), ("Coca-Cola Company", "KO"), ("Pfizer Inc.", "PFE"),
|
||||
("Walmart Inc.", "WMT"), ("Netflix Inc.", "NFLX"), ("Adobe Inc.", "ADBE"),
|
||||
("Salesforce Inc.", "CRM"), ("Comcast Corporation", "CMCSA"), ("Cisco Systems Inc.", "CSCO"),
|
||||
("Oracle Corporation", "ORCL"), ("Intel Corporation", "INTC"), ("American Express Company", "AXP"),
|
||||
("Bank of America Corp.", "BAC"), ("Wells Fargo & Company", "WFC"), ("Verizon Communications Inc.", "VZ"),
|
||||
("AT&T Inc.", "T"), ("Disney (Walt Disney Co.)", "DIS"), ("Nike Inc.", "NKE"),
|
||||
("McDonald's Corporation", "MCD"), ("Starbucks Corporation", "SBUX"), ("Goldman Sachs Group Inc.", "GS"),
|
||||
("Morgan Stanley", "MS"), ("Boeing Company", "BA"), ("Caterpillar Inc.", "CAT"),
|
||||
("3M Company", "MMM"), ("Honeywell International Inc.", "HON"), ("IBM (International Business Machines)", "IBM"),
|
||||
("Qualcomm Inc.", "QCOM"), ("Texas Instruments Inc.", "TXN"), ("Amgen Inc.", "AMGN"),
|
||||
("Gilead Sciences Inc.", "GILD"), ("Bristol-Myers Squibb Company", "BMY"), ("Eli Lilly and Company", "LLY"),
|
||||
("Union Pacific Corporation", "UNP"), ("Lockheed Martin Corporation", "LMT"), ("Raytheon Technologies Corp.", "RTX"),
|
||||
("Target Corporation", "TGT"), ("Lowe's Companies Inc.", "LOW"), ("Booking Holdings Inc.", "BKNG"),
|
||||
("PayPal Holdings Inc.", "PYPL"), ("Broadcom Inc.", "AVGO"), ("Schlumberger Ltd.", "SLB"),
|
||||
("ConocoPhillips", "COP"), ("Phillips 66", "PSX"),
|
||||
("Ford Motor Company", "F"), ("General Motors Company", "GM"), ("General Electric Company", "GE"),
|
||||
("FedEx Corporation", "FDX"), ("United Parcel Service Inc.", "UPS"), ("Delta Air Lines Inc.", "DAL"),
|
||||
("American Airlines Group Inc.", "AAL"), ("Southwest Airlines Co.", "LUV"),
|
||||
("Abbott Laboratories", "ABT"), ("Thermo Fisher Scientific Inc.", "TMO"), ("Danaher Corporation", "DHR"),
|
||||
("Accenture plc", "ACN"), ("Intuit Inc.", "INTU"),
|
||||
("ServiceNow Inc.", "NOW"), ("Workday Inc.", "WDAY"), ("Snowflake Inc.", "SNOW"),
|
||||
("Zoom Video Communications Inc.", "ZM"), ("Spotify Technology S.A.", "SPOT"), ("Uber Technologies Inc.", "UBER"),
|
||||
("Airbnb Inc.", "ABNB"), ("Moderna Inc.", "MRNA"), ("Regeneron Pharmaceuticals Inc.", "REGN"),
|
||||
]
|
||||
|
||||
st.caption("Select a ticker above or choose from the list below.")
|
||||
|
||||
if st.button("Run Analysis"):
|
||||
if not ticker:
|
||||
st.error("Please enter or select a stock ticker.")
|
||||
st.stop()
|
||||
api_key = st.session_state.get("google_api_key", "")
|
||||
email = st.session_state.get("email", "")
|
||||
if not api_key:
|
||||
@@ -410,51 +451,63 @@ if st.button("Run Analysis"):
|
||||
st.stop()
|
||||
|
||||
analysis_only = st.session_state.get("analysis_only", False)
|
||||
with st.spinner("Downloading 10-K and running Gemini analysis (if rate limited, waiting up to 60s before retry)..."):
|
||||
try:
|
||||
detailed_summary, cfa_report, full_text, df_metrics = run_analysis(ticker, api_key, email, analysis_only=analysis_only)
|
||||
st.success("Analysis complete.")
|
||||
try:
|
||||
with st.spinner("Step 1/2: Downloading 10-K and extracting Item 7 & 8 (selective sections only)..."):
|
||||
full_text, item7, item8 = download_and_extract_sections(ticker, email)
|
||||
|
||||
st.subheader("Detailed Analysis (Financial Health, Profitability, Key Risks)")
|
||||
st.markdown(detailed_summary)
|
||||
|
||||
st.subheader("CFA Investment Report")
|
||||
st.markdown(cfa_report)
|
||||
|
||||
st.subheader("Key Financial Metrics (Revenue, Net Income, Operating Cash Flow)")
|
||||
if not df_metrics.empty:
|
||||
st.dataframe(df_metrics, use_container_width=True)
|
||||
elif analysis_only:
|
||||
st.info("Metrics skipped (Analysis only mode). Turn off 'Analysis only' in Settings to fetch metrics.")
|
||||
else:
|
||||
st.info("No metrics extracted. Check the full Item 8 text.")
|
||||
|
||||
with st.expander("View excerpt of extracted 10-K text"):
|
||||
st.text(full_text[:15000] + ("..." if len(full_text) > 15000 else ""))
|
||||
|
||||
except FileNotFoundError as e:
|
||||
st.error(str(e))
|
||||
except ValueError as e:
|
||||
st.error(str(e))
|
||||
except RuntimeError as e:
|
||||
st.error(str(e))
|
||||
with st.spinner("Step 2/2: Running Gemini analysis (typically 30–90s; if rate limited, we wait 60s then retry)..."):
|
||||
detailed_summary, cfa_report = get_ai_summary_and_report(api_key, full_text, item7, item8, ticker)
|
||||
if analysis_only:
|
||||
st.warning("You already have **Analysis only** on (1 API call). The limit is on Google's side — wait **2–5 minutes** without clicking, then press Run Analysis again.")
|
||||
df_metrics = pd.DataFrame()
|
||||
else:
|
||||
st.info("Wait 2–5 minutes, then try again. Or enable 'Analysis only (1 API call)' in Settings to reduce usage.")
|
||||
except Exception as e:
|
||||
err_msg = str(e).lower()
|
||||
if "429" in err_msg or ("resource" in err_msg and "exhausted" in err_msg):
|
||||
st.error("Rate limit exceeded. Please try again in a few minutes.")
|
||||
if analysis_only:
|
||||
st.warning("You already have **Analysis only** on. Google's free tier limit is reached — wait **2–5 minutes**, then press Run Analysis again (no need to change settings).")
|
||||
else:
|
||||
st.info("Wait 2–5 minutes, then retry. Or enable **Analysis only (1 API call)** in the sidebar.")
|
||||
elif "404" in err_msg or "not found" in err_msg:
|
||||
st.error("The selected model is not available. Please try again later or check Google AI Studio for available models.")
|
||||
time.sleep(DELAY_BETWEEN_CALLS_SEC)
|
||||
df_metrics = get_metrics_table_from_ai(api_key, item8, ticker)
|
||||
|
||||
st.success("Analysis complete.")
|
||||
st.subheader("Detailed Analysis (Financial Health, Profitability, Key Risks)")
|
||||
st.markdown(detailed_summary)
|
||||
st.subheader("CFA Investment Report")
|
||||
st.markdown(cfa_report)
|
||||
st.subheader("Key Financial Metrics (Revenue, Net Income, Operating Cash Flow)")
|
||||
if not df_metrics.empty:
|
||||
st.dataframe(df_metrics, use_container_width=True)
|
||||
elif analysis_only:
|
||||
st.info("Metrics skipped (Analysis only mode). Turn off 'Analysis only' in Settings to fetch metrics.")
|
||||
else:
|
||||
st.info("No metrics extracted. Check the full Item 8 text.")
|
||||
with st.expander("View excerpt of extracted 10-K text"):
|
||||
st.text(full_text[:15000] + ("..." if len(full_text) > 15000 else ""))
|
||||
|
||||
except FileNotFoundError as e:
|
||||
st.error(str(e))
|
||||
except ValueError as e:
|
||||
st.error(str(e))
|
||||
except RuntimeError as e:
|
||||
st.error(str(e))
|
||||
if analysis_only:
|
||||
st.warning("You already have **Analysis only** on (1 API call). The limit is on Google's side — wait **2–5 minutes** without clicking, then press Run Analysis again.")
|
||||
else:
|
||||
st.info("Wait 2–5 minutes, then try again. Or enable 'Analysis only (1 API call)' in Settings to reduce usage.")
|
||||
except Exception as e:
|
||||
err_msg = str(e).lower()
|
||||
if "429" in err_msg or ("resource" in err_msg and "exhausted" in err_msg):
|
||||
st.error("Rate limit exceeded. Please try again in a few minutes.")
|
||||
if analysis_only:
|
||||
st.warning("You already have **Analysis only** on. Google's free tier limit is reached — wait **2–5 minutes**, then press Run Analysis again (no need to change settings).")
|
||||
else:
|
||||
st.error("An error occurred. Please try again later.")
|
||||
st.caption("If the problem persists, check your API key and internet connection.")
|
||||
with st.expander("Error details (for troubleshooting)"):
|
||||
st.code(repr(e), language="text")
|
||||
st.caption("Share this with support if the issue continues.")
|
||||
st.info("Wait 2–5 minutes, then retry. Or enable **Analysis only (1 API call)** in the sidebar.")
|
||||
elif "404" in err_msg or "not found" in err_msg:
|
||||
st.error("The selected model is not available. Please try again later or check Google AI Studio for available models.")
|
||||
else:
|
||||
st.error("An error occurred. Please try again later.")
|
||||
st.caption("If the problem persists, check your API key and internet connection.")
|
||||
with st.expander("Error details (for troubleshooting)"):
|
||||
st.code(repr(e), language="text")
|
||||
st.caption("Share this with support if the issue continues.")
|
||||
|
||||
st.divider()
|
||||
st.subheader("S&P 500 companies (sample) — Company name & Ticker")
|
||||
st.caption("Click a row to copy the ticker, or type it in the box above.")
|
||||
df_sp = pd.DataFrame(SP500_SAMPLE, columns=["Company name", "Ticker"])
|
||||
with st.expander("Show list", expanded=True):
|
||||
st.dataframe(df_sp, use_container_width=True, hide_index=True)
|
||||
|
||||
Reference in New Issue
Block a user