import os import requests import numpy as np import pandas as pd from datetime import datetime, timedelta from ..utils import decorate_all_methods, get_next_weekday # from finrobot.utils import decorate_all_methods, get_next_weekday from functools import wraps from typing import Annotated def init_fmp_api(func): @wraps(func) def wrapper(*args, **kwargs): global fmp_api_key if os.environ.get("FMP_API_KEY") is None: print("Please set the environment variable FMP_API_KEY to use the FMP API.") return None else: fmp_api_key = os.environ["FMP_API_KEY"] print("FMP api key found successfully.") return func(*args, **kwargs) return wrapper @decorate_all_methods(init_fmp_api) class FMPUtils: def get_target_price( ticker_symbol: Annotated[str, "ticker symbol"], date: Annotated[str, "date of the target price, should be 'yyyy-mm-dd'"], ) -> str: """Get the target price for a given stock on a given date""" # API URL url = f"https://financialmodelingprep.com/api/v4/price-target?symbol={ticker_symbol}&apikey={fmp_api_key}" # 发送GET请求 price_target = "Not Given" response = requests.get(url) # 确保请求成功 if response.status_code == 200: # 解析JSON数据 data = response.json() est = [] date = datetime.strptime(date, "%Y-%m-%d") for tprice in data: tdate = tprice["publishedDate"].split("T")[0] tdate = datetime.strptime(tdate, "%Y-%m-%d") if abs((tdate - date).days) <= 1: est.append(tprice["priceTarget"]) if est: price_target = f"{np.min(est)} - {np.max(est)} (md. {np.median(est)})" else: price_target = "N/A" else: return f"Failed to retrieve data: {response.status_code}" return price_target def get_sec_report( ticker_symbol: Annotated[str, "ticker symbol"], fyear: Annotated[ str, "year of the 10-K report, should be 'yyyy' or 'latest'. Default to 'latest'", ] = "latest", ) -> str: """Get the url and filing date of the 10-K report for a given stock and year""" url = f"https://financialmodelingprep.com/api/v3/sec_filings/{ticker_symbol}?type=10-k&page=0&apikey={fmp_api_key}" # 发送GET请求 filing_url = None response = requests.get(url) # 确保请求成功 if response.status_code == 200: # 解析JSON数据 data = response.json() # print(data) if fyear == "latest": filing_url = data[0]["finalLink"] filing_date = data[0]["fillingDate"] else: for filing in data: if filing["fillingDate"].split("-")[0] == fyear: filing_url = filing["finalLink"] filing_date = filing["fillingDate"] break return f"Link: {filing_url}\nFiling Date: {filing_date}" else: return f"Failed to retrieve data: {response.status_code}" def get_historical_market_cap( ticker_symbol: Annotated[str, "ticker symbol"], date: Annotated[str, "date of the market cap, should be 'yyyy-mm-dd'"], ) -> str: """Get the historical market capitalization for a given stock on a given date""" date = get_next_weekday(date).strftime("%Y-%m-%d") url = f"https://financialmodelingprep.com/api/v3/historical-market-capitalization/{ticker_symbol}?limit=100&from={date}&to={date}&apikey={fmp_api_key}" # 发送GET请求 mkt_cap = None response = requests.get(url) # 确保请求成功 if response.status_code == 200: # 解析JSON数据 data = response.json() mkt_cap = data[0]["marketCap"] return mkt_cap else: return f"Failed to retrieve data: {response.status_code}" def get_historical_bvps( ticker_symbol: Annotated[str, "ticker symbol"], target_date: Annotated[str, "date of the BVPS, should be 'yyyy-mm-dd'"], ) -> str: """Get the historical book value per share for a given stock on a given date""" # 从FMP API获取历史关键财务指标数据 url = f"https://financialmodelingprep.com/api/v3/key-metrics/{ticker_symbol}?limit=40&apikey={fmp_api_key}" response = requests.get(url) data = response.json() if not data: return "No data available" # 找到最接近目标日期的数据 closest_data = None min_date_diff = float("inf") target_date = datetime.strptime(target_date, "%Y-%m-%d") for entry in data: date_of_data = datetime.strptime(entry["date"], "%Y-%m-%d") date_diff = abs(target_date - date_of_data).days if date_diff < min_date_diff: min_date_diff = date_diff closest_data = entry if closest_data: return closest_data.get("bookValuePerShare", "No BVPS data available") else: return "No close date data found" def get_financial_metrics( ticker_symbol: Annotated[str, "ticker symbol"], years: Annotated[int, "number of the years to search from, default to 4"] = 4, ): """Get the financial metrics for a given stock for the last 'years' years""" # Base URL setup for FMP API base_url = "https://financialmodelingprep.com/api/v3" # Create DataFrame df = pd.DataFrame() # Iterate over the last 'years' years of data for year_offset in range(years): # Construct URL for income statement and ratios for each year income_statement_url = f"{base_url}/income-statement/{ticker_symbol}?limit={years}&apikey={fmp_api_key}" ratios_url = ( f"{base_url}/ratios/{ticker_symbol}?limit={years}&apikey={fmp_api_key}" ) key_metrics_url = f"{base_url}/key-metrics/{ticker_symbol}?limit={years}&apikey={fmp_api_key}" # Requesting data from the API income_data = requests.get(income_statement_url).json() key_metrics_data = requests.get(key_metrics_url).json() ratios_data = requests.get(ratios_url).json() # Extracting needed metrics for each year if income_data and key_metrics_data and ratios_data: metrics = { "Operating Revenue": income_data[year_offset]["revenue"] / 1e6, "Adjusted Net Profit": income_data[year_offset]["netIncome"] / 1e6, "Adjusted EPS": income_data[year_offset]["eps"], "EBIT Margin": ratios_data[year_offset]["ebitPerRevenue"], "ROE": key_metrics_data[year_offset]["roe"], "PE Ratio": ratios_data[year_offset]["priceEarningsRatio"], "EV/EBITDA": key_metrics_data[year_offset][ "enterpriseValueOverEBITDA" ], "PB Ratio": key_metrics_data[year_offset]["pbRatio"], } # Append the year and metrics to the DataFrame # Extracting the year from the date year = income_data[year_offset]["date"][:4] df[year] = pd.Series(metrics) df = df.sort_index(axis=1) df = df.round(2) return df if __name__ == "__main__": from finrobot.utils import register_keys_from_json register_keys_from_json("config_api_keys") FMPUtils.get_sec_report("MSFT", "2023")