import streamlit as st import requests import pandas as pd import os import asyncio import aiohttp # Global API key and default parameters API_KEY = os.getenv("FMP_API_KEY") DEFAULT_COMPANY = "syros" NUM_PAGES = 10 # Backend variable: number of pages to fetch # Initialize session state run flags and inputs if not already set if "run_mna_feed" not in st.session_state: st.session_state.run_mna_feed = False if "run_mna_search" not in st.session_state: st.session_state.run_mna_search = False if "search_company" not in st.session_state: st.session_state.search_company = DEFAULT_COMPANY if "feed_date" not in st.session_state: st.session_state.feed_date = pd.to_datetime("2025-01-01").date() ############################################## # ASYNCHRONOUS FUNCTIONS FOR M&A FEED ############################################## async def fetch_mna_feed_page(session, page): url = f"https://financialmodelingprep.com/api/v4/mergers-acquisitions-rss-feed?page={page}&apikey={API_KEY}" try: async with session.get(url) as response: if response.status == 200: data = await response.json() return pd.DataFrame(data) else: return pd.DataFrame() except Exception: # Fail gracefully without exposing the data source return pd.DataFrame() async def fetch_all_mna_feed(num_pages=NUM_PAGES): async with aiohttp.ClientSession() as session: tasks = [fetch_mna_feed_page(session, page) for page in range(num_pages)] results = [] progress_bar = st.progress(0) completed = 0 for task in asyncio.as_completed(tasks): result = await task results.append(result) completed += 1 progress_bar.progress(completed / num_pages) return results def async_fetch_mna_feed(num_pages=NUM_PAGES) -> pd.DataFrame: results = asyncio.run(fetch_all_mna_feed(num_pages)) if results: df = pd.concat(results, ignore_index=True) if "transactionDate" in df.columns: df["transactionDate"] = pd.to_datetime(df["transactionDate"], errors="coerce") return df return pd.DataFrame() ############################################## # CACHED FUNCTION FOR M&A SEARCH ############################################## @st.cache_data(show_spinner=False) def fetch_mna_search(company_name: str) -> pd.DataFrame: """ Fetch M&A news filtered by company name using the search endpoint. """ url = f"https://financialmodelingprep.com/api/v4/mergers-acquisitions/search?name={company_name}&apikey={API_KEY}" try: response = requests.get(url) response.raise_for_status() data = response.json() except: return pd.DataFrame() if not data: return pd.DataFrame() return pd.DataFrame(data) ############################################## # MAIN APP ############################################## def main(): st.set_page_config(page_title="M&A Feed Dashboard", layout="wide") st.title("M&A Transaction Analysis") st.write( "This dashboard provides a real-time stream of Mergers & Acquisitions news and announcements. " "Use the side menu below to choose between viewing the full M&A live feed or searching for M&A news by company name." ) # Sidebar (inside an expander) for navigation and options with st.sidebar.expander("Navigation and Options", expanded=True): page = st.radio( "Select Page", ("M&A Feed", "M&A Search"), help="Choose 'M&A Feed' to view the complete RSS feed or 'M&A Search' to search for news by company name." ) if page == "M&A Feed": feed_date = st.date_input( "From Date", value=pd.to_datetime("2025-01-01").date(), help="Select a starting date. Only M&A transactions on or after this date will be shown." ) st.session_state.feed_date = feed_date if st.button("Run M&A Feed"): st.session_state.run_mna_feed = True else: company = st.text_input( "Company Name", value=DEFAULT_COMPANY, help="Enter the company name to search for M&A news (default is 'syros')." ) st.session_state.search_company = company if st.button("Run M&A Search"): st.session_state.run_mna_search = True # Display page content based on the selected page if page == "M&A Feed": st.header("M&A RSS Feed") st.write( "Below is the latest M&A live feed data filtered by transaction date. " "Only M&A transactions on or after the selected date are shown. " "The table displays details such as the acquiring company, target company, transaction date, and URL." ) if st.session_state.run_mna_feed: df_feed = async_fetch_mna_feed() if df_feed.empty: st.error("No data returned from the M&A feed.") else: if "transactionDate" in df_feed.columns: filtered_df = df_feed[df_feed["transactionDate"].dt.date >= st.session_state.feed_date] else: filtered_df = df_feed if filtered_df.empty: st.error("No M&A transactions found on or after the selected date.") else: st.dataframe(filtered_df, use_container_width=True) else: st.info("Click 'Run M&A Feed' in the sidebar to fetch data.") else: st.header("M&A Search") st.write( "Search for M&A news by company name. " "The table below displays the matching announcements, including details such as the acquiring company, " "target company, transaction date, and announcement URL." ) if st.session_state.run_mna_search: df_search = fetch_mna_search(st.session_state.search_company) if df_search.empty: st.error("No M&A news found for the specified company name.") else: st.dataframe(df_search, use_container_width=True) else: st.info("Enter a company name in the sidebar and click 'Run M&A Search' to fetch data.") if __name__ == "__main__": main() hide_streamlit_style = """ """ st.markdown(hide_streamlit_style, unsafe_allow_html=True)