Spaces:
Sleeping
Sleeping
Sigrid De los Santos
commited on
Commit
Β·
33a4549
1
Parent(s):
f2e1cf5
debugging
Browse files- app.py +83 -74
- data/rejuvenation_2025-07-26.md +105 -0
- html/rejuvenation_2025-07-26.html +201 -0
- investing_topics_prev.csv +2 -0
- requirements.txt +1 -1
- src/main.py +120 -81
app.py
CHANGED
@@ -1,109 +1,118 @@
|
|
1 |
import os
|
2 |
import sys
|
|
|
3 |
import time
|
4 |
import itertools
|
5 |
import streamlit as st
|
6 |
import pandas as pd
|
7 |
-
from io import
|
|
|
8 |
|
9 |
# Add 'src' to Python path so we can import main.py
|
10 |
sys.path.append(os.path.join(os.path.dirname(__file__), 'src'))
|
11 |
-
from main import run_pipeline
|
12 |
|
13 |
-
# --- Page Config ---
|
14 |
st.set_page_config(page_title="π° AI News Analyzer", layout="wide")
|
15 |
st.title("π§ AI-Powered Investing News Analyzer")
|
16 |
|
17 |
-
#
|
18 |
st.subheader("π API Keys")
|
19 |
openai_api_key = st.text_input("OpenAI API Key", type="password").strip()
|
20 |
tavily_api_key = st.text_input("Tavily API Key", type="password").strip()
|
21 |
|
22 |
-
#
|
23 |
st.subheader("π Topics of Interest")
|
24 |
topics_data = []
|
25 |
-
|
26 |
with st.form("topics_form"):
|
27 |
topic_count = st.number_input("How many topics?", min_value=1, max_value=10, value=1, step=1)
|
|
|
28 |
for i in range(topic_count):
|
29 |
col1, col2 = st.columns(2)
|
30 |
with col1:
|
31 |
topic = st.text_input(f"Topic {i+1}", key=f"topic_{i}")
|
32 |
with col2:
|
33 |
-
|
34 |
-
topics_data.append(
|
35 |
-
run_button = st.form_submit_button("π Run Analysis")
|
36 |
|
37 |
-
|
38 |
-
log_placeholder = st.empty()
|
39 |
|
40 |
-
#
|
41 |
-
|
|
|
|
|
|
|
42 |
|
43 |
-
if run_button:
|
44 |
-
if not openai_api_key or not tavily_api_key:
|
45 |
-
st.error("Please provide both OpenAI and Tavily API keys.")
|
46 |
-
else:
|
47 |
-
run_button = False # Disable button
|
48 |
-
log_placeholder.info("π Starting analysis...")
|
49 |
-
|
50 |
-
# Rotating status messages
|
51 |
-
status_placeholder = st.empty()
|
52 |
-
steps = ["Running Tavily search...", "Analyzing with FinBERT & FinGPT...", "Generating LLM summary..."]
|
53 |
-
cycle = itertools.cycle(steps)
|
54 |
-
|
55 |
-
# Display rotating messages while running pipeline
|
56 |
-
with st.spinner("Working..."):
|
57 |
-
for _ in range(3):
|
58 |
-
status_placeholder.text(next(cycle))
|
59 |
-
time.sleep(0.8)
|
60 |
-
|
61 |
-
# Run the pipeline
|
62 |
-
try:
|
63 |
-
report_md, articles_df, insights_df = run_pipeline(
|
64 |
-
topics=topics_data,
|
65 |
-
openai_api_key=openai_api_key,
|
66 |
-
tavily_api_key=tavily_api_key
|
67 |
-
)
|
68 |
-
log_placeholder.success("β
Analysis completed.")
|
69 |
-
except Exception as e:
|
70 |
-
log_placeholder.error(f"β Error: {e}")
|
71 |
-
st.stop()
|
72 |
-
|
73 |
-
# --- Report Tab ---
|
74 |
-
with tabs[0]:
|
75 |
-
st.subheader("π AI-Generated Report")
|
76 |
-
st.markdown(report_md, unsafe_allow_html=True)
|
77 |
-
|
78 |
-
# --- Articles Tab ---
|
79 |
-
with tabs[1]:
|
80 |
-
st.subheader("π Articles & Priorities")
|
81 |
-
if not articles_df.empty:
|
82 |
-
# Color code priority
|
83 |
-
articles_df['Priority'] = articles_df['Priority'].map(
|
84 |
-
lambda x: "π΄ High" if str(x).lower() == "haute" or str(x).lower() == "high" else "π’ Low"
|
85 |
-
)
|
86 |
-
|
87 |
-
st.dataframe(articles_df, use_container_width=True)
|
88 |
-
|
89 |
-
# CSV download
|
90 |
-
csv = articles_df.to_csv(index=False)
|
91 |
-
st.download_button("β¬οΈ Download Articles CSV", data=csv, file_name="articles.csv", mime="text/csv")
|
92 |
-
else:
|
93 |
-
st.warning("No articles found.")
|
94 |
-
|
95 |
-
# --- Insights Tab ---
|
96 |
-
with tabs[2]:
|
97 |
-
st.subheader("π Company Insights (FinGPT + FinBERT)")
|
98 |
-
if insights_df is not None and not insights_df.empty:
|
99 |
-
st.dataframe(insights_df, use_container_width=True)
|
100 |
-
csv = insights_df.to_csv(index=False)
|
101 |
-
st.download_button("β¬οΈ Download Insights CSV", data=csv, file_name="insights.csv", mime="text/csv")
|
102 |
-
else:
|
103 |
-
st.info("No company insights generated yet.")
|
104 |
|
|
|
|
|
|
|
|
|
105 |
|
106 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
107 |
# import os
|
108 |
# import sys
|
109 |
# import tempfile
|
|
|
1 |
import os
|
2 |
import sys
|
3 |
+
import tempfile
|
4 |
import time
|
5 |
import itertools
|
6 |
import streamlit as st
|
7 |
import pandas as pd
|
8 |
+
from io import BytesIO
|
9 |
+
from xhtml2pdf import pisa
|
10 |
|
11 |
# Add 'src' to Python path so we can import main.py
|
12 |
sys.path.append(os.path.join(os.path.dirname(__file__), 'src'))
|
13 |
+
from main import run_pipeline
|
14 |
|
|
|
15 |
st.set_page_config(page_title="π° AI News Analyzer", layout="wide")
|
16 |
st.title("π§ AI-Powered Investing News Analyzer")
|
17 |
|
18 |
+
# === API Key Input ===
|
19 |
st.subheader("π API Keys")
|
20 |
openai_api_key = st.text_input("OpenAI API Key", type="password").strip()
|
21 |
tavily_api_key = st.text_input("Tavily API Key", type="password").strip()
|
22 |
|
23 |
+
# === Topic Input ===
|
24 |
st.subheader("π Topics of Interest")
|
25 |
topics_data = []
|
|
|
26 |
with st.form("topics_form"):
|
27 |
topic_count = st.number_input("How many topics?", min_value=1, max_value=10, value=1, step=1)
|
28 |
+
|
29 |
for i in range(topic_count):
|
30 |
col1, col2 = st.columns(2)
|
31 |
with col1:
|
32 |
topic = st.text_input(f"Topic {i+1}", key=f"topic_{i}")
|
33 |
with col2:
|
34 |
+
days = st.number_input(f"Timespan (days)", min_value=1, max_value=30, value=7, key=f"days_{i}")
|
35 |
+
topics_data.append({"topic": topic, "timespan_days": days})
|
|
|
36 |
|
37 |
+
submitted = st.form_submit_button("Run Analysis")
|
|
|
38 |
|
39 |
+
# === Tabs Setup ===
|
40 |
+
tab_report, tab_articles, tab_insights = st.tabs(["π Report", "π Articles", "π Insights"])
|
41 |
+
articles_df = pd.DataFrame()
|
42 |
+
insights_df = pd.DataFrame()
|
43 |
+
html_paths = []
|
44 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
45 |
|
46 |
+
def convert_html_to_pdf(source_html):
|
47 |
+
output = BytesIO()
|
48 |
+
pisa.CreatePDF(source_html, dest=output)
|
49 |
+
return output.getvalue()
|
50 |
|
51 |
|
52 |
+
# === Submission logic ===
|
53 |
+
if submitted:
|
54 |
+
if not openai_api_key or not tavily_api_key or not all([td['topic'] for td in topics_data]):
|
55 |
+
st.warning("Please fill in all fields.")
|
56 |
+
else:
|
57 |
+
os.environ["OPENAI_API_KEY"] = openai_api_key
|
58 |
+
os.environ["TAVILY_API_KEY"] = tavily_api_key
|
59 |
+
|
60 |
+
df = pd.DataFrame(topics_data)
|
61 |
+
with tempfile.NamedTemporaryFile(delete=False, suffix=".csv") as tmp_csv:
|
62 |
+
df.to_csv(tmp_csv.name, index=False)
|
63 |
+
csv_path = tmp_csv.name
|
64 |
+
|
65 |
+
spinner_box = st.empty()
|
66 |
+
log_box = st.empty()
|
67 |
+
logs = []
|
68 |
+
|
69 |
+
def log(msg):
|
70 |
+
logs.append(msg)
|
71 |
+
log_box.code("\n".join(logs))
|
72 |
+
|
73 |
+
try:
|
74 |
+
spinner_box.markdown("β³ Running analysis pipeline...")
|
75 |
+
|
76 |
+
# Run the full pipeline
|
77 |
+
html_paths, articles_df, insights_df = run_pipeline(csv_path, tavily_api_key, progress_callback=log)
|
78 |
+
|
79 |
+
spinner_box.success("β
Analysis complete!")
|
80 |
+
|
81 |
+
# --- Report Tab ---
|
82 |
+
with tab_report:
|
83 |
+
if html_paths:
|
84 |
+
for path in html_paths:
|
85 |
+
with open(path, 'r', encoding='utf-8') as f:
|
86 |
+
html_content = f.read()
|
87 |
+
st.components.v1.html(html_content, height=600, scrolling=True)
|
88 |
+
pdf_data = convert_html_to_pdf(html_content)
|
89 |
+
st.download_button(
|
90 |
+
label="β¬οΈ Download Report as PDF",
|
91 |
+
data=pdf_data,
|
92 |
+
file_name=os.path.basename(path).replace(".html", ".pdf"),
|
93 |
+
mime="application/pdf"
|
94 |
+
)
|
95 |
+
else:
|
96 |
+
st.error("β No reports were generated.")
|
97 |
+
|
98 |
+
# --- Articles Tab ---
|
99 |
+
with tab_articles:
|
100 |
+
if not articles_df.empty:
|
101 |
+
st.dataframe(articles_df, use_container_width=True)
|
102 |
+
else:
|
103 |
+
st.info("No articles available.")
|
104 |
+
|
105 |
+
# --- Insights Tab ---
|
106 |
+
with tab_insights:
|
107 |
+
if not insights_df.empty:
|
108 |
+
st.dataframe(insights_df, use_container_width=True)
|
109 |
+
else:
|
110 |
+
st.info("No insights available.")
|
111 |
+
|
112 |
+
except Exception as e:
|
113 |
+
spinner_box.error("β Failed.")
|
114 |
+
log_box.error(f"β Error: {e}")
|
115 |
+
|
116 |
# import os
|
117 |
# import sys
|
118 |
# import tempfile
|
data/rejuvenation_2025-07-26.md
ADDED
@@ -0,0 +1,105 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
> Topic: `Rejuvenation`
|
3 |
+
> Articles Collected: `297`
|
4 |
+
> Generated: `2025-07-26 15:26`
|
5 |
+
>
|
6 |
+
# Rejuvenation Sector β Value Investor Memo (Week of July 22, 2025)
|
7 |
+
|
8 |
+
---
|
9 |
+
|
10 |
+
## π Executive Summary
|
11 |
+
|
12 |
+
- **Sentiment:** The rejuvenation and longevity space is heating up, driven by new research, partnerships, and acquisitions, but is mostly in βwatchβ territory for public equity investors, with most innovation found in early-stage private startups.
|
13 |
+
- **Risks:** Many advances remain years from commercialization; the sector is hype-prone, and most investable stocks lack strong traditional value metrics (P/E, P/B, ROE).
|
14 |
+
- **Catalysts:** Partnerships (e.g., Novo Nordisk in China), growing M&A activity (ZimVie, OSR Holdings), significant fundraising, and emerging AI-driven biomedical platform technologies are worth monitoring.
|
15 |
+
- **Smart money:** VCs are backing AI/biotech hybrids and noninvasive monitoring startups, while large strategics remain cautious but active in select buyouts.
|
16 |
+
|
17 |
+
---
|
18 |
+
|
19 |
+
## π Signals and Analysis
|
20 |
+
|
21 |
+
### 1. **AI & Brain Aging Therapeutics**
|
22 |
+
|
23 |
+
- **What:** New research uses machine learning to pinpoint brain cell rejuvenation compounds, with aging clock tech as a key enabler.
|
24 |
+
- **Why it matters:** Early-stage, but suggests future pipelines for biotech firms targeting Alzheimerβs and neurodegeneration. No direct investable company is named, suggesting to watch academic/AI-bio platform spinouts.
|
25 |
+
- **Source:** [Aging Clock Unveils Compounds That Rejuvenate Brain Cells - Neuroscience News](https://neurosciencenews.com/aging-clock-neurogenesis-29510/)
|
26 |
+
|
27 |
+
### 2. **M&A & Undervalued Opportunities**
|
28 |
+
|
29 |
+
- **What:** **ZimVie** (ZIMV), a post-spinout dental device company, is being acquired for $730M after a series of divestitures.
|
30 |
+
- **Why it matters:** Potential undervaluation after dramatic slimming and focus; its P/B and FCF should be monitored if still publicly listed pre-acquisition. Indicates smart money (private equity) snapping up legacy assets with strong cash flows.
|
31 |
+
- **Source:** [ZimVie to sell to investment firm for about $730M - MedTech Dive](https://www.medtechdive.com/news/zimvie--sell-archimed-730m/753691/)
|
32 |
+
|
33 |
+
### 3. **AI x Health Management Partnerships in China**
|
34 |
+
|
35 |
+
- **What:** Fangzhou partners with **Novo Nordisk** to transform chronic disease management using AI.
|
36 |
+
- **Why it matters:** Global pharma tapping local AI, possibly opening up new data-driven health ecosystems in massive markets. Worth watching for new, scalable models if Fangzhou IPOs or if Novoβs deal delivers material financial impact.
|
37 |
+
- **Sources:**
|
38 |
+
- [Fangzhou and Novo Nordisk Partner to Transform Chronic Disease Management with AI in China - HIT Consultant](https://hitconsultant.net/2025/07/21/fangzhou-and-novo-nordisk-china-partnership/)
|
39 |
+
- [Fangzhou and Novo Nordisk Signed Collaboration Memorandum to Establish a New Ecosystem for Health Management - BioSpace](https://www.biospace.com/press-releases/fangzhou-and-novo-nordisk-signed-collaboration-memorandum-to-establish-a-new-ecosystem-for-health-management)
|
40 |
+
|
41 |
+
### 4. **Acquisition in Noninvasive Monitoring**
|
42 |
+
|
43 |
+
- **What:** **OSR Holdings** to acquire Woori IO, a pioneer in noninvasive glucose tech.
|
44 |
+
- **Why it matters:** As noninvasive monitoring is a potential βplatform moatβ for chronic conditions management, this type of tech could underpin future rejuvenation or wellness ecosystem companies. Acquisition signals strategic value; watch for Woori IOβs technology adoption curve.
|
45 |
+
- **Source:** [OSR Holdings Enters into Term Sheet to Acquire Woori IO, a Pioneer in Noninvasive Glucose Monitoring Technology - BioSpace](https://www.biospace.com/press-releases/osr-holdings-enters-into-term-sheet-to-acquire-woori-io-a-pioneer-in-noninvasive-glucose-monitoring-technology)
|
46 |
+
|
47 |
+
### 5. **Startups and Funding in Rejuvenation**
|
48 |
+
|
49 |
+
- **What:** Slingshot AI (mental healthcare chatbot) raised $50M; Makersite (manufacturing decisioning) raised β¬60M; 6 biotechs in the longevity space (undisclosed) highlighted.
|
50 |
+
- **Why it matters:** Rapid fundraising for health AI startups suggests a robust VC pipeline, but public market access is limited; premium on getting in at pre-IPO stages or via crossover funds.
|
51 |
+
- **Source:** [xAI's raise, Nudge's noninvasive brain implant, and Stripe's buy - Axios](https://www.axios.com/pro/all-deals/2025/07/22/pro-rata-premium-first-look)
|
52 |
+
|
53 |
+
### 6. **Fundamental Value Screen**
|
54 |
+
|
55 |
+
- Very few listed companies in the βrejuvenationβ/longevity/biotech AI fields have both value investor-friendly metrics and growth optionality. ZimVie possibly fits the bill short-term; **Novo Nordisk** is strong on fundamentals but is a large, fairly valued pharma stock.
|
56 |
+
- Sectors with cash generative characteristics (e.g., dental/divested medtech, as with ZimVie) present special situations for patient investors.
|
57 |
+
---
|
58 |
+
|
59 |
+
## π Stocks or Startups to Watch
|
60 |
+
|
61 |
+
- **ZimVie (ZIMV)**
|
62 |
+
- [ZimVie to sell to investment firm for about $730M](https://www.medtechdive.com/news/zimvie--sell-archimed-730m/753691/)
|
63 |
+
- *Special situation: acquisition at premium post-business focus, but no longer a long-term pure rejuvenation play if going private soon.*
|
64 |
+
- **Novo Nordisk (NVO)**
|
65 |
+
- [Fangzhou & Novo Nordisk partnership](https://www.biospace.com/press-releases/fangzhou-and-novo-nordisk-signed-collaboration-memorandum-to-establish-a-new-ecosystem-for-health-management)
|
66 |
+
- *Classic big pharma, but aggressively pursuing chronic disease management with AI. Strong FCF yield, moat, and attractive ROE.*
|
67 |
+
- **OSR Holdings** and **Woori IO** (private)
|
68 |
+
- [OSR Holdings Acquires Woori IO](https://www.biospace.com/press-releases/osr-holdings-enters-into-term-sheet-to-acquire-woori-io-a-pioneer-in-noninvasive-glucose-monitoring-technology)
|
69 |
+
- *Pure play in noninvasive diagnostics. If/when OSR lists or expands, worth screening for value metrics.*
|
70 |
+
|
71 |
+
---
|
72 |
+
|
73 |
+
## π§ Investment Thesis
|
74 |
+
|
75 |
+
**Current view:**
|
76 |
+
This weekβs rejuvenation news is most actionable for early-stage venture investors, with public equity players in βwatch and waitβ mode except for special situations in medtech (ZimVie) and established innovative pharma (Novo Nordisk).
|
77 |
+
|
78 |
+
**Risk/Reward:**
|
79 |
+
- **Risks:** Most developments are pre-commercial, limited liquidity and financial visibility in small caps, and hype-driven valuation risk in AI/biotech.
|
80 |
+
- **Rewards:** Secular tailwind from aging demographics, structural innovation in personalized/AI-driven health, and M&A tailwinds for asset-rich legacy firms.
|
81 |
+
- **Themes to Watch:**
|
82 |
+
- Partnerships between global pharma and AI/diagnostics startups (could drive reratings if partnerships scale).
|
83 |
+
- M&A among βorphanedβ divested medtech assets with robust FCF.
|
84 |
+
- Emergence of platform technologies (aging clocks, noninvasive monitors) as moats.
|
85 |
+
|
86 |
+
**Macro:**
|
87 |
+
- The value thesis remains challenging in the near term for public stocks; rising capital flowing into AI + longevity startups signals future wave of IPOs or M&A.
|
88 |
+
- Watch regulatory ramp-up in diagnostics and digital health (esp. in China and US); successful early adoption could leverage global scale.
|
89 |
+
|
90 |
+
---
|
91 |
+
|
92 |
+
## π References
|
93 |
+
|
94 |
+
- [Aging Clock Unveils Compounds That Rejuvenate Brain Cells - Neuroscience News](https://neurosciencenews.com/aging-clock-neurogenesis-29510/)
|
95 |
+
- [ZimVie to sell to investment firm for about $730M - MedTech Dive](https://www.medtechdive.com/news/zimvie--sell-archimed-730m/753691/)
|
96 |
+
- [Fangzhou and Novo Nordisk Partner to Transform Chronic Disease Management with AI in China - HIT Consultant](https://hitconsultant.net/2025/07/21/fangzhou-and-novo-nordisk-china-partnership/)
|
97 |
+
- [Fangzhou and Novo Nordisk Signed Collaboration Memorandum - BioSpace](https://www.biospace.com/press-releases/fangzhou-and-novo-nordisk-signed-collaboration-memorandum-to-establish-a-new-ecosystem-for-health-management)
|
98 |
+
- [OSR Holdings Acquires Woori IO - BioSpace](https://www.biospace.com/press-releases/osr-holdings-enters-into-term-sheet-to-acquire-woori-io-a-pioneer-in-noninvasive-glucose-monitoring-technology)
|
99 |
+
- [xAI's raise, Nudge's noninvasive brain implant, and Stripe's buy - Axios](https://www.axios.com/pro/all-deals/2025/07/22/pro-rata-premium-first-look)
|
100 |
+
|
101 |
+
---
|
102 |
+
|
103 |
+
# **Conclusion: Watch for Turnkey Partnerships, Asset Sales, and AI Moat Formation**
|
104 |
+
|
105 |
+
For deep value or moat strategies, eyes should remain on the strategic M&A trend and on emerging partnerships between established pharma and new tech platforms. Near-term plays are special situation value (ZimVie, possibly OSR Holdings if public), while longer-term exposure may require hybrid VC or holding company vehicles as the sector matures and lists more investable companies with traditional value metrics.
|
html/rejuvenation_2025-07-26.html
ADDED
@@ -0,0 +1,201 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
<!DOCTYPE html>
|
3 |
+
<html lang="en">
|
4 |
+
<head>
|
5 |
+
<meta charset="UTF-8">
|
6 |
+
<title>rejuvenation_2025-07-26</title>
|
7 |
+
<style>
|
8 |
+
body {
|
9 |
+
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
10 |
+
margin: 0;
|
11 |
+
background-color: #f8f9fa;
|
12 |
+
color: #212529;
|
13 |
+
line-height: 1.6;
|
14 |
+
}
|
15 |
+
header {
|
16 |
+
background-color: #ffffff;
|
17 |
+
text-align: center;
|
18 |
+
padding: 1em;
|
19 |
+
border-bottom: 2px solid #dee2e6;
|
20 |
+
}
|
21 |
+
header img {
|
22 |
+
width: 100%;
|
23 |
+
height: auto;
|
24 |
+
max-height: 50vh;
|
25 |
+
object-fit: cover;
|
26 |
+
}
|
27 |
+
.credit {
|
28 |
+
font-size: 0.85em;
|
29 |
+
color: #6c757d;
|
30 |
+
margin-top: 0.5em;
|
31 |
+
}
|
32 |
+
.container {
|
33 |
+
display: flex;
|
34 |
+
flex-direction: row;
|
35 |
+
max-width: 1200px;
|
36 |
+
margin: 2em auto;
|
37 |
+
padding: 0 1em;
|
38 |
+
gap: 2em;
|
39 |
+
}
|
40 |
+
main {
|
41 |
+
flex: 3;
|
42 |
+
}
|
43 |
+
aside {
|
44 |
+
flex: 1;
|
45 |
+
background-color: #ffffff;
|
46 |
+
border: 1px solid #dee2e6;
|
47 |
+
border-radius: 8px;
|
48 |
+
padding: 1em;
|
49 |
+
box-shadow: 0 2px 6px rgba(0,0,0,0.05);
|
50 |
+
height: fit-content;
|
51 |
+
}
|
52 |
+
main img {
|
53 |
+
max-width: 100%;
|
54 |
+
height: auto;
|
55 |
+
display: block;
|
56 |
+
margin: 1.5em auto;
|
57 |
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
58 |
+
}
|
59 |
+
h1, h2, h3 {
|
60 |
+
color: #0d6efd;
|
61 |
+
}
|
62 |
+
a {
|
63 |
+
color: #0d6efd;
|
64 |
+
text-decoration: none;
|
65 |
+
}
|
66 |
+
a:hover {
|
67 |
+
text-decoration: underline;
|
68 |
+
}
|
69 |
+
code {
|
70 |
+
background: #e9ecef;
|
71 |
+
padding: 0.2em 0.4em;
|
72 |
+
border-radius: 4px;
|
73 |
+
font-family: monospace;
|
74 |
+
}
|
75 |
+
pre {
|
76 |
+
background: #e9ecef;
|
77 |
+
padding: 1em;
|
78 |
+
overflow-x: auto;
|
79 |
+
border-radius: 6px;
|
80 |
+
}
|
81 |
+
blockquote {
|
82 |
+
border-left: 4px solid #0d6efd;
|
83 |
+
padding-left: 1em;
|
84 |
+
color: #495057;
|
85 |
+
margin: 1em 0;
|
86 |
+
background: #f1f3f5;
|
87 |
+
}
|
88 |
+
</style>
|
89 |
+
</head>
|
90 |
+
<body>
|
91 |
+
<header>
|
92 |
+
<img src="https://via.placeholder.com/1280x720.png?text=AI+News+Analyzer" alt="rejuvenation_2025-07-26 Banner">
|
93 |
+
</header>
|
94 |
+
<div class="container">
|
95 |
+
<main>
|
96 |
+
|
97 |
+
<h1 id="rejuvenation-sector-value-investor-memo-week-of-july-22-2025">Rejuvenation Sector β Value Investor Memo (Week of July 22, 2025)</h1>
|
98 |
+
<hr />
|
99 |
+
<h2 id="executive-summary">π Executive Summary</h2>
|
100 |
+
<ul>
|
101 |
+
<li><strong>Sentiment:</strong> The rejuvenation and longevity space is heating up, driven by new research, partnerships, and acquisitions, but is mostly in βwatchβ territory for public equity investors, with most innovation found in early-stage private startups.</li>
|
102 |
+
<li><strong>Risks:</strong> Many advances remain years from commercialization; the sector is hype-prone, and most investable stocks lack strong traditional value metrics (P/E, P/B, ROE).</li>
|
103 |
+
<li><strong>Catalysts:</strong> Partnerships (e.g., Novo Nordisk in China), growing M&A activity (ZimVie, OSR Holdings), significant fundraising, and emerging AI-driven biomedical platform technologies are worth monitoring.</li>
|
104 |
+
<li><strong>Smart money:</strong> VCs are backing AI/biotech hybrids and noninvasive monitoring startups, while large strategics remain cautious but active in select buyouts.</li>
|
105 |
+
</ul>
|
106 |
+
<hr />
|
107 |
+
<h2 id="signals-and-analysis">π Signals and Analysis</h2>
|
108 |
+
<h3 id="1-ai-brain-aging-therapeutics">1. <strong>AI & Brain Aging Therapeutics</strong></h3>
|
109 |
+
<ul>
|
110 |
+
<li><strong>What:</strong> New research uses machine learning to pinpoint brain cell rejuvenation compounds, with aging clock tech as a key enabler.</li>
|
111 |
+
<li><strong>Why it matters:</strong> Early-stage, but suggests future pipelines for biotech firms targeting Alzheimerβs and neurodegeneration. No direct investable company is named, suggesting to watch academic/AI-bio platform spinouts.</li>
|
112 |
+
<li><strong>Source:</strong> <a href="https://neurosciencenews.com/aging-clock-neurogenesis-29510/">Aging Clock Unveils Compounds That Rejuvenate Brain Cells - Neuroscience News</a></li>
|
113 |
+
</ul>
|
114 |
+
<h3 id="2-ma-undervalued-opportunities">2. <strong>M&A & Undervalued Opportunities</strong></h3>
|
115 |
+
<ul>
|
116 |
+
<li><strong>What:</strong> <strong>ZimVie</strong> (ZIMV), a post-spinout dental device company, is being acquired for $730M after a series of divestitures.</li>
|
117 |
+
<li><strong>Why it matters:</strong> Potential undervaluation after dramatic slimming and focus; its P/B and FCF should be monitored if still publicly listed pre-acquisition. Indicates smart money (private equity) snapping up legacy assets with strong cash flows.</li>
|
118 |
+
<li><strong>Source:</strong> <a href="https://www.medtechdive.com/news/zimvie--sell-archimed-730m/753691/">ZimVie to sell to investment firm for about $730M - MedTech Dive</a></li>
|
119 |
+
</ul>
|
120 |
+
<h3 id="3-ai-x-health-management-partnerships-in-china">3. <strong>AI x Health Management Partnerships in China</strong></h3>
|
121 |
+
<ul>
|
122 |
+
<li><strong>What:</strong> Fangzhou partners with <strong>Novo Nordisk</strong> to transform chronic disease management using AI.</li>
|
123 |
+
<li><strong>Why it matters:</strong> Global pharma tapping local AI, possibly opening up new data-driven health ecosystems in massive markets. Worth watching for new, scalable models if Fangzhou IPOs or if Novoβs deal delivers material financial impact.</li>
|
124 |
+
<li><strong>Sources:</strong> <ul>
|
125 |
+
<li><a href="https://hitconsultant.net/2025/07/21/fangzhou-and-novo-nordisk-china-partnership/">Fangzhou and Novo Nordisk Partner to Transform Chronic Disease Management with AI in China - HIT Consultant</a></li>
|
126 |
+
<li><a href="https://www.biospace.com/press-releases/fangzhou-and-novo-nordisk-signed-collaboration-memorandum-to-establish-a-new-ecosystem-for-health-management">Fangzhou and Novo Nordisk Signed Collaboration Memorandum to Establish a New Ecosystem for Health Management - BioSpace</a></li>
|
127 |
+
</ul>
|
128 |
+
</li>
|
129 |
+
</ul>
|
130 |
+
<h3 id="4-acquisition-in-noninvasive-monitoring">4. <strong>Acquisition in Noninvasive Monitoring</strong></h3>
|
131 |
+
<ul>
|
132 |
+
<li><strong>What:</strong> <strong>OSR Holdings</strong> to acquire Woori IO, a pioneer in noninvasive glucose tech.</li>
|
133 |
+
<li><strong>Why it matters:</strong> As noninvasive monitoring is a potential βplatform moatβ for chronic conditions management, this type of tech could underpin future rejuvenation or wellness ecosystem companies. Acquisition signals strategic value; watch for Woori IOβs technology adoption curve.</li>
|
134 |
+
<li><strong>Source:</strong> <a href="https://www.biospace.com/press-releases/osr-holdings-enters-into-term-sheet-to-acquire-woori-io-a-pioneer-in-noninvasive-glucose-monitoring-technology">OSR Holdings Enters into Term Sheet to Acquire Woori IO, a Pioneer in Noninvasive Glucose Monitoring Technology - BioSpace</a></li>
|
135 |
+
</ul>
|
136 |
+
<h3 id="5-startups-and-funding-in-rejuvenation">5. <strong>Startups and Funding in Rejuvenation</strong></h3>
|
137 |
+
<ul>
|
138 |
+
<li><strong>What:</strong> Slingshot AI (mental healthcare chatbot) raised $50M; Makersite (manufacturing decisioning) raised β¬60M; 6 biotechs in the longevity space (undisclosed) highlighted.</li>
|
139 |
+
<li><strong>Why it matters:</strong> Rapid fundraising for health AI startups suggests a robust VC pipeline, but public market access is limited; premium on getting in at pre-IPO stages or via crossover funds.</li>
|
140 |
+
<li><strong>Source:</strong> <a href="https://www.axios.com/pro/all-deals/2025/07/22/pro-rata-premium-first-look">xAI's raise, Nudge's noninvasive brain implant, and Stripe's buy - Axios</a></li>
|
141 |
+
</ul>
|
142 |
+
<h3 id="6-fundamental-value-screen">6. <strong>Fundamental Value Screen</strong></h3>
|
143 |
+
<ul>
|
144 |
+
<li>Very few listed companies in the βrejuvenationβ/longevity/biotech AI fields have both value investor-friendly metrics and growth optionality. ZimVie possibly fits the bill short-term; <strong>Novo Nordisk</strong> is strong on fundamentals but is a large, fairly valued pharma stock.</li>
|
145 |
+
<li>Sectors with cash generative characteristics (e.g., dental/divested medtech, as with ZimVie) present special situations for patient investors.</li>
|
146 |
+
</ul>
|
147 |
+
<hr />
|
148 |
+
<h2 id="stocks-or-startups-to-watch">π Stocks or Startups to Watch</h2>
|
149 |
+
<ul>
|
150 |
+
<li><strong>ZimVie (ZIMV)</strong><ul>
|
151 |
+
<li><a href="https://www.medtechdive.com/news/zimvie--sell-archimed-730m/753691/">ZimVie to sell to investment firm for about $730M</a></li>
|
152 |
+
<li><em>Special situation: acquisition at premium post-business focus, but no longer a long-term pure rejuvenation play if going private soon.</em></li>
|
153 |
+
</ul>
|
154 |
+
</li>
|
155 |
+
<li><strong>Novo Nordisk (NVO)</strong><ul>
|
156 |
+
<li><a href="https://www.biospace.com/press-releases/fangzhou-and-novo-nordisk-signed-collaboration-memorandum-to-establish-a-new-ecosystem-for-health-management">Fangzhou & Novo Nordisk partnership</a></li>
|
157 |
+
<li><em>Classic big pharma, but aggressively pursuing chronic disease management with AI. Strong FCF yield, moat, and attractive ROE.</em></li>
|
158 |
+
</ul>
|
159 |
+
</li>
|
160 |
+
<li><strong>OSR Holdings</strong> and <strong>Woori IO</strong> (private)<ul>
|
161 |
+
<li><a href="https://www.biospace.com/press-releases/osr-holdings-enters-into-term-sheet-to-acquire-woori-io-a-pioneer-in-noninvasive-glucose-monitoring-technology">OSR Holdings Acquires Woori IO</a></li>
|
162 |
+
<li><em>Pure play in noninvasive diagnostics. If/when OSR lists or expands, worth screening for value metrics.</em></li>
|
163 |
+
</ul>
|
164 |
+
</li>
|
165 |
+
</ul>
|
166 |
+
<hr />
|
167 |
+
<h2 id="investment-thesis">π§ Investment Thesis</h2>
|
168 |
+
<p><strong>Current view:</strong><br />
|
169 |
+
This weekβs rejuvenation news is most actionable for early-stage venture investors, with public equity players in βwatch and waitβ mode except for special situations in medtech (ZimVie) and established innovative pharma (Novo Nordisk).</p>
|
170 |
+
<p><strong>Risk/Reward:</strong><br />
|
171 |
+
- <strong>Risks:</strong> Most developments are pre-commercial, limited liquidity and financial visibility in small caps, and hype-driven valuation risk in AI/biotech.
|
172 |
+
- <strong>Rewards:</strong> Secular tailwind from aging demographics, structural innovation in personalized/AI-driven health, and M&A tailwinds for asset-rich legacy firms.
|
173 |
+
- <strong>Themes to Watch:</strong>
|
174 |
+
- Partnerships between global pharma and AI/diagnostics startups (could drive reratings if partnerships scale).
|
175 |
+
- M&A among βorphanedβ divested medtech assets with robust FCF.
|
176 |
+
- Emergence of platform technologies (aging clocks, noninvasive monitors) as moats.</p>
|
177 |
+
<p><strong>Macro:</strong><br />
|
178 |
+
- The value thesis remains challenging in the near term for public stocks; rising capital flowing into AI + longevity startups signals future wave of IPOs or M&A.
|
179 |
+
- Watch regulatory ramp-up in diagnostics and digital health (esp. in China and US); successful early adoption could leverage global scale.</p>
|
180 |
+
<hr />
|
181 |
+
<h2 id="references">π References</h2>
|
182 |
+
<ul>
|
183 |
+
<li><a href="https://neurosciencenews.com/aging-clock-neurogenesis-29510/">Aging Clock Unveils Compounds That Rejuvenate Brain Cells - Neuroscience News</a></li>
|
184 |
+
<li><a href="https://www.medtechdive.com/news/zimvie--sell-archimed-730m/753691/">ZimVie to sell to investment firm for about $730M - MedTech Dive</a></li>
|
185 |
+
<li><a href="https://hitconsultant.net/2025/07/21/fangzhou-and-novo-nordisk-china-partnership/">Fangzhou and Novo Nordisk Partner to Transform Chronic Disease Management with AI in China - HIT Consultant</a></li>
|
186 |
+
<li><a href="https://www.biospace.com/press-releases/fangzhou-and-novo-nordisk-signed-collaboration-memorandum-to-establish-a-new-ecosystem-for-health-management">Fangzhou and Novo Nordisk Signed Collaboration Memorandum - BioSpace</a></li>
|
187 |
+
<li><a href="https://www.biospace.com/press-releases/osr-holdings-enters-into-term-sheet-to-acquire-woori-io-a-pioneer-in-noninvasive-glucose-monitoring-technology">OSR Holdings Acquires Woori IO - BioSpace</a></li>
|
188 |
+
<li><a href="https://www.axios.com/pro/all-deals/2025/07/22/pro-rata-premium-first-look">xAI's raise, Nudge's noninvasive brain implant, and Stripe's buy - Axios</a></li>
|
189 |
+
</ul>
|
190 |
+
<hr />
|
191 |
+
<h1 id="conclusion-watch-for-turnkey-partnerships-asset-sales-and-ai-moat-formation"><strong>Conclusion: Watch for Turnkey Partnerships, Asset Sales, and AI Moat Formation</strong></h1>
|
192 |
+
<p>For deep value or moat strategies, eyes should remain on the strategic M&A trend and on emerging partnerships between established pharma and new tech platforms. Near-term plays are special situation value (ZimVie, possibly OSR Holdings if public), while longer-term exposure may require hybrid VC or holding company vehicles as the sector matures and lists more investable companies with traditional value metrics.</p>
|
193 |
+
</main>
|
194 |
+
<aside>
|
195 |
+
<h3>π§ Metrics</h3>
|
196 |
+
<ul><li>Topic: Rejuvenation</li><li>Articles Collected: 297</li><li>Generated: 2025-07-26 15:26</li></ul>
|
197 |
+
</aside>
|
198 |
+
</div>
|
199 |
+
</body>
|
200 |
+
</html>
|
201 |
+
|
investing_topics_prev.csv
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
1 |
+
topic,timespan_days
|
2 |
+
Rejuvenation,7
|
requirements.txt
CHANGED
@@ -86,4 +86,4 @@ tzdata==2025.2
|
|
86 |
urllib3==2.5.0
|
87 |
zipp==3.23.0
|
88 |
zstandard==0.23.0
|
89 |
-
|
|
|
86 |
urllib3==2.5.0
|
87 |
zipp==3.23.0
|
88 |
zstandard==0.23.0
|
89 |
+
xhtml2pdf
|
src/main.py
CHANGED
@@ -1,12 +1,14 @@
|
|
1 |
import os
|
2 |
import pandas as pd
|
3 |
from datetime import datetime
|
|
|
|
|
|
|
4 |
from md_html import convert_single_md_to_html as convert_md_to_html
|
5 |
from news_analysis import fetch_deep_news, generate_value_investor_report
|
6 |
from csv_utils import detect_changes
|
7 |
-
from fin_interpreter import analyze_article
|
8 |
|
9 |
-
# === Paths ===
|
10 |
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
|
11 |
DATA_DIR = os.path.join(BASE_DIR, "data")
|
12 |
HTML_DIR = os.path.join(BASE_DIR, "html")
|
@@ -15,69 +17,128 @@ CSV_PATH = os.path.join(BASE_DIR, "investing_topics.csv")
|
|
15 |
os.makedirs(DATA_DIR, exist_ok=True)
|
16 |
os.makedirs(HTML_DIR, exist_ok=True)
|
17 |
|
18 |
-
|
19 |
-
"""
|
20 |
-
Main pipeline:
|
21 |
-
1. Fetch articles for topics.
|
22 |
-
2. Analyze with FinBERT + FinGPT.
|
23 |
-
3. Generate markdown report.
|
24 |
-
4. Return (report_md, articles_df, insights_df).
|
25 |
-
"""
|
26 |
-
all_articles = []
|
27 |
|
28 |
-
# Fetch and analyze articles
|
29 |
-
for topic, days in topics:
|
30 |
-
try:
|
31 |
-
articles = fetch_deep_news(topic, days, tavily_api_key)
|
32 |
-
for article in articles:
|
33 |
-
sentiment, confidence, signal = analyze_article(article.get("summary", ""))
|
34 |
-
all_articles.append({
|
35 |
-
"Title": article.get("title"),
|
36 |
-
"URL": article.get("url"),
|
37 |
-
"Summary": article.get("summary"),
|
38 |
-
"Priority": article.get("priority", "Low"),
|
39 |
-
"Date": article.get("date"),
|
40 |
-
"Company": article.get("company", topic), # fallback if no company detected
|
41 |
-
"Sentiment": sentiment,
|
42 |
-
"Confidence": confidence,
|
43 |
-
"Signal": signal
|
44 |
-
})
|
45 |
-
except Exception as e:
|
46 |
-
print(f"Error fetching/analyzing articles for topic '{topic}': {e}")
|
47 |
-
|
48 |
-
# Convert to DataFrame
|
49 |
-
articles_df = pd.DataFrame(all_articles)
|
50 |
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
|
|
|
|
|
|
56 |
|
57 |
-
except Exception as e:
|
58 |
-
import traceback
|
59 |
-
traceback.print_exc()
|
60 |
-
report_md = f"Error generating report: {e}"
|
61 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
62 |
|
63 |
-
|
64 |
-
|
65 |
|
66 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
67 |
|
68 |
|
69 |
def build_company_insights(articles_df):
|
70 |
-
"""
|
71 |
-
Aggregates article data into a company-level insights table.
|
72 |
-
Columns: Company, Mentions, Avg Sentiment, Top Signal, Sector
|
73 |
-
"""
|
74 |
if articles_df.empty:
|
75 |
return pd.DataFrame()
|
76 |
-
|
77 |
-
# Simple aggregation
|
78 |
grouped = (
|
79 |
-
articles_df
|
80 |
-
.groupby("Company")
|
81 |
.agg({
|
82 |
"Title": "count",
|
83 |
"Sentiment": lambda x: x.mode()[0] if not x.mode().empty else "Neutral",
|
@@ -86,39 +147,16 @@ def build_company_insights(articles_df):
|
|
86 |
.reset_index()
|
87 |
.rename(columns={"Title": "Mentions"})
|
88 |
)
|
89 |
-
|
90 |
-
# Add a placeholder Sector column (can improve later with classification)
|
91 |
-
grouped["Sector"] = grouped["Company"].apply(lambda c: detect_sector_from_company(c))
|
92 |
-
|
93 |
return grouped
|
94 |
|
95 |
|
96 |
-
def detect_sector_from_company(company_name):
|
97 |
-
"""
|
98 |
-
Simple keyword-based sector detection (can be replaced with GPT classification).
|
99 |
-
"""
|
100 |
-
company_name = company_name.lower()
|
101 |
-
if "energy" in company_name or "nuclear" in company_name:
|
102 |
-
return "Energy"
|
103 |
-
elif "fin" in company_name or "bank" in company_name:
|
104 |
-
return "Finance"
|
105 |
-
elif "chip" in company_name or "semiconductor" in company_name:
|
106 |
-
return "Tech Hardware"
|
107 |
-
else:
|
108 |
-
return "General"
|
109 |
-
|
110 |
-
|
111 |
if __name__ == "__main__":
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
print(
|
116 |
-
print(art_df.head())
|
117 |
-
print(ins_df.head())
|
118 |
-
|
119 |
-
|
120 |
|
121 |
-
#
|
122 |
# import sys
|
123 |
# from datetime import datetime
|
124 |
# from dotenv import load_dotenv
|
@@ -227,3 +265,4 @@ if __name__ == "__main__":
|
|
227 |
# convert_md_to_html(md, HTML_DIR)
|
228 |
# print(f"π All reports converted to HTML at: {HTML_DIR}")
|
229 |
|
|
|
|
1 |
import os
|
2 |
import pandas as pd
|
3 |
from datetime import datetime
|
4 |
+
from dotenv import load_dotenv
|
5 |
+
import traceback
|
6 |
+
|
7 |
from md_html import convert_single_md_to_html as convert_md_to_html
|
8 |
from news_analysis import fetch_deep_news, generate_value_investor_report
|
9 |
from csv_utils import detect_changes
|
10 |
+
from fin_interpreter import analyze_article
|
11 |
|
|
|
12 |
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
|
13 |
DATA_DIR = os.path.join(BASE_DIR, "data")
|
14 |
HTML_DIR = os.path.join(BASE_DIR, "html")
|
|
|
17 |
os.makedirs(DATA_DIR, exist_ok=True)
|
18 |
os.makedirs(HTML_DIR, exist_ok=True)
|
19 |
|
20 |
+
load_dotenv()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
21 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
22 |
|
23 |
+
def build_metrics_box(topic, num_articles):
|
24 |
+
now = datetime.now().strftime("%Y-%m-%d %H:%M")
|
25 |
+
return f"""
|
26 |
+
> Topic: `{topic}`
|
27 |
+
> Articles Collected: `{num_articles}`
|
28 |
+
> Generated: `{now}`
|
29 |
+
>
|
30 |
+
"""
|
31 |
|
|
|
|
|
|
|
|
|
32 |
|
33 |
+
def run_value_investing_analysis(csv_path, progress_callback=None):
|
34 |
+
"""
|
35 |
+
Runs the analysis for all topics in the CSV.
|
36 |
+
Returns:
|
37 |
+
md_files (list of md file paths)
|
38 |
+
all_articles (list of article dicts)
|
39 |
+
"""
|
40 |
+
current_df = pd.read_csv(csv_path)
|
41 |
+
prev_path = os.path.join(BASE_DIR, "investing_topics_prev.csv")
|
42 |
+
|
43 |
+
if os.path.exists(prev_path):
|
44 |
+
previous_df = pd.read_csv(prev_path)
|
45 |
+
changed_df = detect_changes(current_df, previous_df)
|
46 |
+
if changed_df.empty:
|
47 |
+
if progress_callback:
|
48 |
+
progress_callback("β
No changes detected. Skipping processing.")
|
49 |
+
return [], []
|
50 |
+
else:
|
51 |
+
changed_df = current_df
|
52 |
|
53 |
+
new_md_files = []
|
54 |
+
all_articles = []
|
55 |
|
56 |
+
for _, row in changed_df.iterrows():
|
57 |
+
topic = row.get("topic")
|
58 |
+
timespan = row.get("timespan_days", 7)
|
59 |
+
msg = f"π Processing: {topic} ({timespan} days)"
|
60 |
+
print(msg)
|
61 |
+
if progress_callback:
|
62 |
+
progress_callback(msg)
|
63 |
+
|
64 |
+
news = fetch_deep_news(topic, timespan)
|
65 |
+
if not news:
|
66 |
+
warning = f"β οΈ No news found for: {topic}"
|
67 |
+
print(warning)
|
68 |
+
if progress_callback:
|
69 |
+
progress_callback(warning)
|
70 |
+
continue
|
71 |
+
|
72 |
+
# Add articles to all_articles
|
73 |
+
for article in news:
|
74 |
+
try:
|
75 |
+
res = analyze_article(article.get("summary", ""))
|
76 |
+
if isinstance(res, dict):
|
77 |
+
sentiment = res.get("sentiment")
|
78 |
+
confidence = res.get("confidence")
|
79 |
+
signal = res.get("signal")
|
80 |
+
else:
|
81 |
+
sentiment, confidence, signal = res[0], res[1], res[2]
|
82 |
+
except Exception as e:
|
83 |
+
sentiment, confidence, signal = "Unknown", 0.0, "None"
|
84 |
+
print(f"Error analyzing article: {e}")
|
85 |
+
|
86 |
+
all_articles.append({
|
87 |
+
"Title": article.get("title"),
|
88 |
+
"URL": article.get("url"),
|
89 |
+
"Summary": article.get("summary"),
|
90 |
+
"Priority": article.get("priority", "Low"),
|
91 |
+
"Date": article.get("date"),
|
92 |
+
"Company": article.get("company", topic),
|
93 |
+
"Sentiment": sentiment,
|
94 |
+
"Confidence": confidence,
|
95 |
+
"Signal": signal
|
96 |
+
})
|
97 |
+
|
98 |
+
# Generate report
|
99 |
+
report_body = generate_value_investor_report(topic, news)
|
100 |
+
metrics_md = build_metrics_box(topic, len(news))
|
101 |
+
full_md = metrics_md + report_body
|
102 |
+
|
103 |
+
filename = f"{topic.replace(' ', '_').lower()}_{datetime.now().strftime('%Y-%m-%d')}.md"
|
104 |
+
filepath = os.path.join(DATA_DIR, filename)
|
105 |
+
counter = 1
|
106 |
+
while os.path.exists(filepath):
|
107 |
+
filename = f"{topic.replace(' ', '_').lower()}_{datetime.now().strftime('%Y-%m-%d')}_{counter}.md"
|
108 |
+
filepath = os.path.join(DATA_DIR, filename)
|
109 |
+
counter += 1
|
110 |
+
|
111 |
+
with open(filepath, "w", encoding="utf-8") as f:
|
112 |
+
f.write(full_md)
|
113 |
+
|
114 |
+
new_md_files.append(filepath)
|
115 |
+
|
116 |
+
if progress_callback:
|
117 |
+
progress_callback(f"β
Markdown saved to: {DATA_DIR}")
|
118 |
+
current_df.to_csv(prev_path, index=False)
|
119 |
+
return new_md_files, all_articles
|
120 |
+
|
121 |
+
|
122 |
+
def run_pipeline(csv_path, tavily_api_key, progress_callback=None):
|
123 |
+
os.environ["TAVILY_API_KEY"] = tavily_api_key
|
124 |
+
|
125 |
+
new_md_files, all_articles = run_value_investing_analysis(csv_path, progress_callback)
|
126 |
+
new_html_paths = []
|
127 |
+
for md_path in new_md_files:
|
128 |
+
convert_md_to_html(md_path, HTML_DIR)
|
129 |
+
html_path = os.path.join(HTML_DIR, os.path.basename(md_path).replace(".md", ".html"))
|
130 |
+
new_html_paths.append(html_path)
|
131 |
+
|
132 |
+
articles_df = pd.DataFrame(all_articles)
|
133 |
+
insights_df = build_company_insights(articles_df)
|
134 |
+
return new_html_paths, articles_df, insights_df
|
135 |
|
136 |
|
137 |
def build_company_insights(articles_df):
|
|
|
|
|
|
|
|
|
138 |
if articles_df.empty:
|
139 |
return pd.DataFrame()
|
|
|
|
|
140 |
grouped = (
|
141 |
+
articles_df.groupby("Company")
|
|
|
142 |
.agg({
|
143 |
"Title": "count",
|
144 |
"Sentiment": lambda x: x.mode()[0] if not x.mode().empty else "Neutral",
|
|
|
147 |
.reset_index()
|
148 |
.rename(columns={"Title": "Mentions"})
|
149 |
)
|
|
|
|
|
|
|
|
|
150 |
return grouped
|
151 |
|
152 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
153 |
if __name__ == "__main__":
|
154 |
+
md_files, _ = run_value_investing_analysis(CSV_PATH)
|
155 |
+
for md in md_files:
|
156 |
+
convert_md_to_html(md, HTML_DIR)
|
157 |
+
print(f"π All reports converted to HTML at: {HTML_DIR}")
|
|
|
|
|
|
|
|
|
158 |
|
159 |
+
#import os
|
160 |
# import sys
|
161 |
# from datetime import datetime
|
162 |
# from dotenv import load_dotenv
|
|
|
265 |
# convert_md_to_html(md, HTML_DIR)
|
266 |
# print(f"π All reports converted to HTML at: {HTML_DIR}")
|
267 |
|
268 |
+
|