|
|
import os |
|
|
from textwrap import dedent |
|
|
from typing import Annotated |
|
|
from datetime import timedelta, datetime |
|
|
from ..data_source import YFinanceUtils, SECUtils, FMPUtils |
|
|
|
|
|
|
|
|
def combine_prompt(instruction, resource, table_str=None): |
|
|
if table_str: |
|
|
prompt = f"{table_str}\n\nResource: {resource}\n\nInstruction: {instruction}" |
|
|
else: |
|
|
prompt = f"Resource: {resource}\n\nInstruction: {instruction}" |
|
|
return prompt |
|
|
|
|
|
|
|
|
def save_to_file(data: str, file_path: str): |
|
|
os.makedirs(os.path.dirname(file_path), exist_ok=True) |
|
|
with open(file_path, "w") as f: |
|
|
f.write(data) |
|
|
|
|
|
|
|
|
class ReportAnalysisUtils: |
|
|
|
|
|
def analyze_income_stmt( |
|
|
ticker_symbol: Annotated[str, "ticker symbol"], |
|
|
fyear: Annotated[str, "fiscal year of the 10-K report"], |
|
|
save_path: Annotated[str, "txt file path, to which the returned instruction & resources are written."] |
|
|
) -> str: |
|
|
""" |
|
|
Retrieve the income statement for the given ticker symbol with the related section of its 10-K report. |
|
|
Then return with an instruction on how to analyze the income statement. |
|
|
""" |
|
|
|
|
|
income_stmt = YFinanceUtils.get_income_stmt(ticker_symbol) |
|
|
df_string = "Income statement:\n" + income_stmt.to_string().strip() |
|
|
|
|
|
|
|
|
instruction = dedent( |
|
|
""" |
|
|
Conduct a comprehensive analysis of the company's income statement for the current fiscal year. |
|
|
Start with an overall revenue record, including Year-over-Year or Quarter-over-Quarter comparisons, |
|
|
and break down revenue sources to identify primary contributors and trends. Examine the Cost of |
|
|
Goods Sold for potential cost control issues. Review profit margins such as gross, operating, |
|
|
and net profit margins to evaluate cost efficiency, operational effectiveness, and overall profitability. |
|
|
Analyze Earnings Per Share to understand investor perspectives. Compare these metrics with historical |
|
|
data and industry or competitor benchmarks to identify growth patterns, profitability trends, and |
|
|
operational challenges. The output should be a strategic overview of the company’s financial health |
|
|
in a single paragraph, less than 130 words, summarizing the previous analysis into 4-5 key points under |
|
|
respective subheadings with specific discussion and strong data support. |
|
|
""" |
|
|
) |
|
|
|
|
|
|
|
|
section_text = SECUtils.get_10k_section(ticker_symbol, fyear, 7) |
|
|
|
|
|
|
|
|
prompt = combine_prompt(instruction, section_text, df_string) |
|
|
|
|
|
save_to_file(prompt, save_path) |
|
|
return f"instruction & resources saved to {save_path}" |
|
|
|
|
|
def analyze_balance_sheet( |
|
|
ticker_symbol: Annotated[str, "ticker symbol"], |
|
|
fyear: Annotated[str, "fiscal year of the 10-K report"], |
|
|
save_path: Annotated[str, "txt file path, to which the returned instruction & resources are written."] |
|
|
) -> str: |
|
|
""" |
|
|
Retrieve the balance sheet for the given ticker symbol with the related section of its 10-K report. |
|
|
Then return with an instruction on how to analyze the balance sheet. |
|
|
""" |
|
|
balance_sheet = YFinanceUtils.get_balance_sheet(ticker_symbol) |
|
|
df_string = "Balance sheet:\n" + balance_sheet.to_string().strip() |
|
|
|
|
|
instruction = dedent( |
|
|
""" |
|
|
Delve into a detailed scrutiny of the company's balance sheet for the most recent fiscal year, pinpointing |
|
|
the structure of assets, liabilities, and shareholders' equity to decode the firm's financial stability and |
|
|
operational efficiency. Focus on evaluating the liquidity through current assets versus current liabilities, |
|
|
the solvency via long-term debt ratios, and the equity position to gauge long-term investment potential. |
|
|
Contrast these metrics with previous years' data to highlight financial trends, improvements, or deteriorations. |
|
|
Finalize with a strategic assessment of the company's financial leverage, asset management, and capital structure, |
|
|
providing insights into its fiscal health and future prospects in a single paragraph. Less than 130 words. |
|
|
""" |
|
|
) |
|
|
|
|
|
section_text = SECUtils.get_10k_section(ticker_symbol, fyear, 7) |
|
|
prompt = combine_prompt(instruction, section_text, df_string) |
|
|
save_to_file(prompt, save_path) |
|
|
return f"instruction & resources saved to {save_path}" |
|
|
|
|
|
def analyze_cash_flow( |
|
|
ticker_symbol: Annotated[str, "ticker symbol"], |
|
|
fyear: Annotated[str, "fiscal year of the 10-K report"], |
|
|
save_path: Annotated[str, "txt file path, to which the returned instruction & resources are written."] |
|
|
) -> str: |
|
|
""" |
|
|
Retrieve the cash flow statement for the given ticker symbol with the related section of its 10-K report. |
|
|
Then return with an instruction on how to analyze the cash flow statement. |
|
|
""" |
|
|
cash_flow = YFinanceUtils.get_cash_flow(ticker_symbol) |
|
|
df_string = "Cash flow statement:\n" + cash_flow.to_string().strip() |
|
|
|
|
|
instruction = dedent( |
|
|
""" |
|
|
Dive into a comprehensive evaluation of the company's cash flow for the latest fiscal year, focusing on cash inflows |
|
|
and outflows across operating, investing, and financing activities. Examine the operational cash flow to assess the |
|
|
core business profitability, scrutinize investing activities for insights into capital expenditures and investments, |
|
|
and review financing activities to understand debt, equity movements, and dividend policies. Compare these cash movements |
|
|
to prior periods to discern trends, sustainability, and liquidity risks. Conclude with an informed analysis of the company's |
|
|
cash management effectiveness, liquidity position, and potential for future growth or financial challenges in a single paragraph. |
|
|
Less than 130 words. |
|
|
""" |
|
|
) |
|
|
|
|
|
section_text = SECUtils.get_10k_section(ticker_symbol, fyear, 7) |
|
|
prompt = combine_prompt(instruction, section_text, df_string) |
|
|
save_to_file(prompt, save_path) |
|
|
return f"instruction & resources saved to {save_path}" |
|
|
|
|
|
def analyze_segment_stmt( |
|
|
ticker_symbol: Annotated[str, "ticker symbol"], |
|
|
fyear: Annotated[str, "fiscal year of the 10-K report"], |
|
|
save_path: Annotated[str, "txt file path, to which the returned instruction & resources are written."] |
|
|
) -> str: |
|
|
""" |
|
|
Retrieve the income statement and the related section of its 10-K report for the given ticker symbol. |
|
|
Then return with an instruction on how to create a segment analysis. |
|
|
""" |
|
|
income_stmt = YFinanceUtils.get_income_stmt(ticker_symbol) |
|
|
df_string = ( |
|
|
"Income statement (Segment Analysis):\n" + income_stmt.to_string().strip() |
|
|
) |
|
|
|
|
|
instruction = dedent( |
|
|
""" |
|
|
Identify the company's business segments and create a segment analysis using the Management's Discussion and Analysis |
|
|
and the income statement, subdivided by segment with clear headings. Address revenue and net profit with specific data, |
|
|
and calculate the changes. Detail strategic partnerships and their impacts, including details like the companies or organizations. |
|
|
Describe product innovations and their effects on income growth. Quantify market share and its changes, or state market position |
|
|
and its changes. Analyze market dynamics and profit challenges, noting any effects from national policy changes. Include the cost side, |
|
|
detailing operational costs, innovation investments, and expenses from channel expansion, etc. Support each statement with evidence, |
|
|
keeping each segment analysis concise and under 60 words, accurately sourcing information. For each segment, consolidate the most |
|
|
significant findings into one clear, concise paragraph, excluding less critical or vaguely described aspects to ensure clarity and |
|
|
reliance on evidence-backed information. For each segment, the output should be one single paragraph within 150 words. |
|
|
""" |
|
|
) |
|
|
section_text = SECUtils.get_10k_section(ticker_symbol, fyear, 7) |
|
|
prompt = combine_prompt(instruction, section_text, df_string) |
|
|
save_to_file(prompt, save_path) |
|
|
return f"instruction & resources saved to {save_path}" |
|
|
|
|
|
def income_summarization( |
|
|
ticker_symbol: Annotated[str, "ticker symbol"], |
|
|
fyear: Annotated[str, "fiscal year of the 10-K report"], |
|
|
income_stmt_analysis: Annotated[str, "in-depth income statement analysis"], |
|
|
segment_analysis: Annotated[str, "in-depth segment analysis"], |
|
|
save_path: Annotated[str, "txt file path, to which the returned instruction & resources are written."] |
|
|
) -> str: |
|
|
""" |
|
|
With the income statement and segment analysis for the given ticker symbol. |
|
|
Then return with an instruction on how to synthesize these analyses into a single coherent paragraph. |
|
|
""" |
|
|
|
|
|
|
|
|
|
|
|
instruction = dedent( |
|
|
f""" |
|
|
Income statement analysis: {income_stmt_analysis}, |
|
|
Segment analysis: {segment_analysis}, |
|
|
Synthesize the findings from the in-depth income statement analysis and segment analysis into a single, coherent paragraph. |
|
|
It should be fact-based and data-driven. First, present and assess overall revenue and profit situation, noting significant |
|
|
trends and changes. Second, examine the performance of the various business segments, with an emphasis on their revenue and |
|
|
profit changes, revenue contributions and market dynamics. For information not covered in the first two areas, identify and |
|
|
integrate key findings related to operation, potential risks and strategic opportunities for growth and stability into the analysis. |
|
|
For each part, integrate historical data comparisons and provide relevant facts, metrics or data as evidence. The entire synthesis |
|
|
should be presented as a continuous paragraph without the use of bullet points. Use subtitles and numbering for each key point. |
|
|
The total output should be less than 160 words. |
|
|
""" |
|
|
) |
|
|
|
|
|
section_text = SECUtils.get_10k_section(ticker_symbol, fyear, 7) |
|
|
prompt = combine_prompt(instruction, section_text, "") |
|
|
save_to_file(prompt, save_path) |
|
|
return f"instruction & resources saved to {save_path}" |
|
|
|
|
|
def get_risk_assessment( |
|
|
ticker_symbol: Annotated[str, "ticker symbol"], |
|
|
fyear: Annotated[str, "fiscal year of the 10-K report"], |
|
|
save_path: Annotated[str, "txt file path, to which the returned instruction & resources are written."] |
|
|
) -> str: |
|
|
""" |
|
|
Retrieve the risk factors for the given ticker symbol with the related section of its 10-K report. |
|
|
Then return with an instruction on how to summarize the top 3 key risks of the company. |
|
|
""" |
|
|
company_name = YFinanceUtils.get_stock_info(ticker_symbol)["shortName"] |
|
|
risk_factors = SECUtils.get_10k_section(ticker_symbol, fyear, "1A") |
|
|
section_text = ( |
|
|
"Company Name: " |
|
|
+ company_name |
|
|
+ "\n\n" |
|
|
+ "Risk factors:\n" |
|
|
+ risk_factors |
|
|
+ "\n\n" |
|
|
) |
|
|
instruction = "According to the given information, summarize the top 3 key risks of the company. Less than 100 words." |
|
|
prompt = combine_prompt(instruction, section_text, "") |
|
|
save_to_file(prompt, save_path) |
|
|
return f"instruction & resources saved to {save_path}" |
|
|
|
|
|
def analyze_business_highlights( |
|
|
ticker_symbol: Annotated[str, "ticker symbol"], |
|
|
fyear: Annotated[str, "fiscal year of the 10-K report"], |
|
|
save_path: Annotated[str, "txt file path, to which the returned instruction & resources are written."] |
|
|
) -> str: |
|
|
""" |
|
|
Retrieve the business summary and related section of its 10-K report for the given ticker symbol. |
|
|
Then return with an instruction on how to describe the performance highlights per business of the company. |
|
|
""" |
|
|
business_summary = SECUtils.get_10k_section(ticker_symbol, fyear, 1) |
|
|
section_7 = SECUtils.get_10k_section(ticker_symbol, fyear, 7) |
|
|
section_text = ( |
|
|
"Business summary:\n" |
|
|
+ business_summary |
|
|
+ "\n\n" |
|
|
+ "Management's Discussion and Analysis of Financial Condition and Results of Operations:\n" |
|
|
+ section_7 |
|
|
) |
|
|
instruction = dedent( |
|
|
""" |
|
|
According to the given information, describe the performance highlights per business of the company. |
|
|
Each business description should contain one sentence of a summarization and one sentence of explanation. |
|
|
Less than 130 words. |
|
|
""" |
|
|
) |
|
|
prompt = combine_prompt(instruction, section_text, "") |
|
|
save_to_file(prompt, save_path) |
|
|
return f"instruction & resources saved to {save_path}" |
|
|
|
|
|
def analyze_company_description( |
|
|
ticker_symbol: Annotated[str, "ticker symbol"], |
|
|
fyear: Annotated[str, "fiscal year of the 10-K report"], |
|
|
save_path: Annotated[str, "txt file path, to which the returned instruction & resources are written."] |
|
|
) -> str: |
|
|
""" |
|
|
Retrieve the company description and related sections of its 10-K report for the given ticker symbol. |
|
|
Then return with an instruction on how to describe the company's industry, strengths, trends, and strategic initiatives. |
|
|
""" |
|
|
company_name = YFinanceUtils.get_stock_info(ticker_symbol).get( |
|
|
"shortName", "N/A" |
|
|
) |
|
|
business_summary = SECUtils.get_10k_section(ticker_symbol, fyear, 1) |
|
|
section_7 = SECUtils.get_10k_section(ticker_symbol, fyear, 7) |
|
|
section_text = ( |
|
|
"Company Name: " |
|
|
+ company_name |
|
|
+ "\n\n" |
|
|
+ "Business summary:\n" |
|
|
+ business_summary |
|
|
+ "\n\n" |
|
|
+ "Management's Discussion and Analysis of Financial Condition and Results of Operations:\n" |
|
|
+ section_7 |
|
|
) |
|
|
instruction = dedent( |
|
|
""" |
|
|
According to the given information, |
|
|
1. Briefly describe the company’s industry, |
|
|
2. Highlight core strengths and competitive advantages key products or services, |
|
|
3. Identify current industry trends, opportunities, and challenges that influence the company’s strategy, |
|
|
4. Outline recent strategic initiatives such as product launches, acquisitions, or new partnerships, and describe the company's response to market conditions. |
|
|
Less than 400 words. |
|
|
""" |
|
|
) |
|
|
step_prompt = combine_prompt(instruction, section_text, "") |
|
|
instruction2 = "Summarize the analysis, less than 130 words." |
|
|
prompt = combine_prompt(instruction=instruction2, resource=step_prompt) |
|
|
save_to_file(prompt, save_path) |
|
|
return f"instruction & resources saved to {save_path}" |
|
|
|
|
|
def get_key_data( |
|
|
ticker_symbol: Annotated[str, "ticker symbol"], |
|
|
filing_date: Annotated[ |
|
|
str | datetime, "the filing date of the financial report being analyzed" |
|
|
], |
|
|
) -> dict: |
|
|
""" |
|
|
return key financial data used in annual report for the given ticker symbol and filing date |
|
|
""" |
|
|
|
|
|
if not isinstance(filing_date, datetime): |
|
|
filing_date = datetime.strptime(filing_date, "%Y-%m-%d") |
|
|
|
|
|
|
|
|
start = (filing_date - timedelta(weeks=52)).strftime("%Y-%m-%d") |
|
|
end = filing_date.strftime("%Y-%m-%d") |
|
|
|
|
|
hist = YFinanceUtils.get_stock_data(ticker_symbol, start, end) |
|
|
|
|
|
|
|
|
info = YFinanceUtils.get_stock_info(ticker_symbol) |
|
|
close_price = hist["Close"].iloc[-1] |
|
|
|
|
|
|
|
|
six_months_start = (filing_date - timedelta(weeks=26)).strftime("%Y-%m-%d") |
|
|
hist_last_6_months = hist[ |
|
|
(hist.index >= six_months_start) & (hist.index <= end) |
|
|
] |
|
|
|
|
|
|
|
|
avg_daily_volume_6m = ( |
|
|
hist_last_6_months["Volume"].mean() |
|
|
if not hist_last_6_months["Volume"].empty |
|
|
else 0 |
|
|
) |
|
|
|
|
|
fiftyTwoWeekLow = hist["High"].min() |
|
|
fiftyTwoWeekHigh = hist["Low"].max() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
filing_date = filing_date.strftime("%Y-%m-%d") |
|
|
|
|
|
|
|
|
|
|
|
rating, _ = YFinanceUtils.get_analyst_recommendations(ticker_symbol) |
|
|
target_price = FMPUtils.get_target_price(ticker_symbol, filing_date) |
|
|
result = { |
|
|
"Rating": rating, |
|
|
"Target Price": target_price, |
|
|
f"6m avg daily vol ({info['currency']}mn)": "{:.2f}".format( |
|
|
avg_daily_volume_6m / 1e6 |
|
|
), |
|
|
f"Closing Price ({info['currency']})": "{:.2f}".format(close_price), |
|
|
f"Market Cap ({info['currency']}mn)": "{:.2f}".format( |
|
|
FMPUtils.get_historical_market_cap(ticker_symbol, filing_date) / 1e6 |
|
|
), |
|
|
f"52 Week Price Range ({info['currency']})": "{:.2f} - {:.2f}".format( |
|
|
fiftyTwoWeekLow, fiftyTwoWeekHigh |
|
|
), |
|
|
f"BVPS ({info['currency']})": "{:.2f}".format( |
|
|
FMPUtils.get_historical_bvps(ticker_symbol, filing_date) |
|
|
), |
|
|
} |
|
|
return result |
|
|
|