LangChain Project

llm 투자보고서

1. llm 연결 테스트 및 프롬프트 작성

from dotenv import load_dotenv
from langchain_ollama import OllamaLLM
from langchain_openai import ChatOpenAI
import os

load_dotenv()

# print(os.getenv("OPENAI_API_KEY"))

ollama_url = "http://127.0.0.1:11434"  
lmstudio_url = "http://127.0.0.1:1234/v1"

# llm = OllamaLLM(model="gemma3:1b", base_url=ollama_url)
# llm = ChatOpenAI(model="gemma-3-1b-it", base_url=lmstudio_url, api_key="dummy")
llm = ChatOpenAI(model="gpt-4.1-nano", temperature=0.2)

ChatGPT Prompts
stock으로 검색해서 ‘Act As A Financial Analyst’의 내용을 주식 시장 상황에 대한 답변 생성을 위한 프롬프트 복사해서 사용

from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser

prompt = ChatPromptTemplate.from_messages([
    ("system", """
        기술적 분석 도구를 활용해 차트를 이해하는 데 경험이 풍부한 자격을 갖춘 전문가의 지원을 원합니다. 
        이는 전 세계적으로 지배적인 거시경제 환경을 해석하면서 고객이 장기적 이점을 획득하도록 돕기 위함입니다. 
        명확한 결론이 필요하므로, 정확히 기록된 정보에 기반한 예측을 통해 이를 추구합니다! 첫 번째 진술은 다음과 같은 내용을 포함합니다- 
        "현재 상황을 바탕으로 향후 주식 시장이 어떻게 될지 알려주실 수 있나요?"
     """),
    ("user", """
        {company} 주식에 투자해도 될까요? 마크다운 형식의 투자 보고서를 한글로 작성해 주세요.
     """)
])

output_parser = StrOutputParser()

# 체인 구성: 프롬프트 -> 모델 -> 출력 파서
chain = prompt | llm | output_parser

company = "Microsoft"
response = chain.invoke({"company": company})
print(response)

2. 데이터 수집

yfinance는 Yahoo Finance에서 금융 데이터를 쉽게 다운로드할 수 있게 해주는 파이썬 라이브러리
무료 데이터 접근 : Yahoo Finance API를 통해 주식, ETF, 뮤추얼 펀드, 통화, 암호화폐 등의 금융 데이터에 무료로 접근.
데이터 다운로드 : 과거 주가, 거래량, 분배금, 기업 실적 등 다양한 금융 데이터를 쉽게 다운로드.
간단한 사용법 : 적은 코드로 빠르게 데이터를 가져올 수 있도록 설계.
판다스 통합 : 데이터를 판다스(pandas) DataFrame 형태로 반환하여 데이터 분석이 용이.
기업 정보 : 주가 데이터 외에도 기업의 기본 정보, 재무제표, 대차대조표, 현금흐름표 등의 데이터도 가져올 수 있음.

pip install yfinance tabulate
import yfinance as yf

msft = yf.Ticker("NVDA")

basic_info = {
    "회사명": msft.info.get('longName'),
    "산업": msft.info.get("industry"),
    "부문": msft.info.get("sector"),
    "시가총액": msft.info.get("marketCap"),
    "총 발행 주식 수": msft.info.get("sharesOutstanding"),
    "본사 위치": msft.info.get("country"),
    "CEO": msft.info.get("ceo"),
    "웹사이트": msft.info.get("website")
}

print(basic_info)
income_statement = msft.financials # 손익계산서
balance_sheet = msft.balance_sheet # 대차대조표
cashflow_statement = msft.cash_flow # 현금흐름표
pe_ratio = msft.info.get('forwardPE') # P/E 비율 (예상)
pb_ratio = msft.info.get('priceToBook') # P/B 비율
eps = msft.info.get('trailingEps') # EPS (주당순이익, 최근 12개월)
roe = msft.info.get("returnOnEquity") # ROE (자기자본이익율)
print(income_statement)
current_volume = msft.info.get("volume")
print(current_volume)
# 최근 1개월 동안의 데이터를 요청
historical_data = msft.history(period="1mo")
# 각 거래일의 거래량(주식이 얼마나 많이 거래되었는지)을 나타냄
historical_data["Volume"]

회사 정보를 찾아서 필요한 형식으로 정리해서 가져올 수 있는 클래스 생성

import yfinance as yf
import pandas as pd

class Stock:
    def __init__(self, symbol):
        self.symbol = symbol
        self.ticker = yf.Ticker(symbol)

    def get_basic_info(self):
        basic_info = pd.DataFrame.from_dict(self.ticker.info, orient="index", columns=['Value'])
        return basic_info.loc[["longName", "industry", "sector", "marketCap", "sharesOutstanding"]].to_markdown()
    
    def get_financial_statement(self):
        return f"""
        ### Quarterly Income Statement
        {self.ticker.quarterly_income_stmt.loc[["Total Revenue", "Gross Profit", "Operating Income", "Net Income"]].to_markdown()}
        
        ### Quarterly Balance Sheet
        {self.ticker.quarterly_balance_sheet.loc[['Total Assets', 'Total Liabilities Net Minority Interest', 'Stockholders Equity']].to_markdown()}
        
        ### Quarterly Cash Flow
        {self.ticker.quarterly_cash_flow.loc[['Operating Cash Flow', 'Investing Cash Flow', 'Financing Cash Flow']].to_markdown()}
        """

3. 사용자 프롬프트 생성

from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser

prompt = ChatPromptTemplate.from_messages([
    ("system", """
        기술적 분석 도구를 활용해 차트를 이해하는 데 경험이 풍부한 자격을 갖춘 전문가의 지원을 원하십니까?
        전 세계에 걸쳐 지배적인 거시경제 환경을 해석하면서 고객이 장기적인 이점을 획득하도록 지원하려면
        명확한 판단이 필요하므로, 정확하게 기록된 정보에 기반한 예측을 통해 이를 추구합니다!
        첫 번째 진술에는 다음과 같은 내용이 포함됩니다- "현재 상황을 바탕으로 향후 주식 시장이 어떻게 될지 알려주실 수 있나요?"
     """),
    ("user", """
        {company} 주식에 투자해도 될까요? 
        아래의 기본정보, 재무제표를 참고해 마크다운 형식의 투자 보고서를 한글로 작성해 주세요.

        기본정보:
        {basic_info}
     
        재무제표:
        {financial_statement}
     """)
])

output_parser = StrOutputParser()

chain = prompt | llm | output_parser

company = "Microsoft"
symbol = "MSFT"

stock = Stock(symbol)

response = chain.invoke({
    "company": company,
    "basic_info": stock.get_basic_info(),
    "financial_statement": stock.get_financial_statement()
})
print(response)

print(stock.get_basic_info())
print(stock.get_financial_statement())

4. 검색 인덱싱

Stock Screener.

csv 파일 다운로드.

import pandas as pd

df = pd.read_csv("nasdaq_screener_1734240693172.csv", na_filter=False)
df.head()

특수 문자 포함 여부 확인

df[df['Symbol'].str.contains(r'[/^ ]', regex=True)]

특수 문자 찾아서 _ 로 변경

df['id'] = df['Symbol'].str.strip().replace(r'[/^]', '_', regex=True)
df.head()

변경된 내용 확인

df[df['id'].str.contains(r'[/^ ]', regex=True)]

딕셔너리로 변경 - 각 행(row)을 딕셔너리로 변환하고 리스트로 묶음

result_d = df.to_dict(orient='records')
result_d

회사명을 회사코드로 변경할때 사용 할 meilisearch 설치
meilisearch

Windows

# powershell에서 실행
# 최신 릴리스 버전 확인
$latestRelease = Invoke-RestMethod -Uri "https://api.github.com/repos/meilisearch/meilisearch/releases/latest"
$windowsAsset = $latestRelease.assets | Where-Object { $_.name -like "*windows-amd64*" }
$downloadUrl = $windowsAsset.browser_download_url

# 다운로드
Invoke-WebRequest -Uri $downloadUrl -OutFile "meilisearch.exe"

# 실행
./meilisearch.exe  --master-key="aSampleMasterKey"

WSL (Windows Subsystem for Linux)이 설치되어 있다면 WSL 터미널에서 아래의 명령어로 실행해도 됨

MacOS,Linux

# Install Meilisearch (맥, 리눅스)
curl -L https://install.meilisearch.com | sh

# Launch Meilisearch
./meilisearch --master-key="aSampleMasterKey"

meilisearch에 인덱스 데이터 생성

pip install meilisearch
import meilisearch

client = meilisearch.Client('http://localhost:7700', 'aSampleMasterKey')

client.index('nasdaq').add_documents(result_d, primary_key='id')

검색하기

client.index('nasdaq').search('Microsoft')

5. 투자보고서 작성 프로그램 작성

reporting_service.py

from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain_ollama import OllamaLLM
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser

from stock_info import Stock

load_dotenv()

ollama_url = "http://127.0.0.1:11434"  
lmstudio_url = "http://127.0.0.1:1234/v1"

# llm = OllamaLLM(model="gemma3:1b", base_url=ollama_url)
# llm = ChatOpenAI(model="gemma-3-1b-it", base_url=lmstudio_url, api_key="dummy")
llm = ChatOpenAI(model="gpt-4.1-nano", temperature=0.2)

def investment_report(company, symbol):
    prompt = ChatPromptTemplate.from_messages([
        ("system", """
            기술적 분석 도구를 활용해 차트를 이해하는 데 경험이 풍부한 자격을 갖춘 전문가의 지원을 원하십니까?
            전 세계에 걸쳐 지배적인 거시경제 환경을 해석하면서 고객이 장기적인 이점을 획득하도록 지원하려면
            명확한 판단이 필요하므로, 정확하게 기록된 정보에 기반한 예측을 통해 이를 추구합니다!
            첫 번째 진술에는 다음과 같은 내용이 포함됩니다- "현재 상황을 바탕으로 향후 주식 시장이 어떻게 될지 알려주실 수 있나요?"
        """),
        ("user", """
            {company} 주식에 투자해도 될까요? 
            아래의 기본정보, 재무제표를 참고해 마크다운 형식의 투자 보고서를 한글로 작성해 주세요.

            기본정보:
            {basic_info}
        
            재무제표:
            {financial_statement}
        """)
    ])

    output_parser = StrOutputParser()

    # 프롬프트 + 모델 + 출력 파서
    chain = prompt | llm | output_parser

    stock = Stock(symbol)

    response = chain.invoke({
        "company": company,
        "basic_info": stock.get_basic_info(),
        "financial_statement": stock.get_financial_statement()
    })

    return response

stock_info.py

import yfinance as yf
import pandas as pd

class Stock:
    def __init__(self, symbol):
        self.symbol = symbol
        self.ticker = yf.Ticker(symbol)

    def get_basic_info(self):
        basic_info = pd.DataFrame.from_dict(self.ticker.info, orient="index", columns=['Value'])
        return basic_info.loc[["longName", "industry", "sector", "marketCap", "sharesOutstanding"]].to_markdown() # tabluate 설치 해야됨
    
    def get_basic_info(self):
        basic_info = pd.DataFrame.from_dict(self.ticker.info, orient="index", columns=['Value'])
        return basic_info.loc[["longName", "industry", "sector", "marketCap", "sharesOutstanding"]].to_markdown() # tabluate 설치 해야됨
    
    def get_income(self):
        return self.ticker.quarterly_income_stmt.loc[["Total Revenue", "Gross Profit", "Operating Income", "Net Income"]]
    
    def get_Balance(self):
        return self.ticker.quarterly_balance_sheet.loc[['Total Assets', 'Total Liabilities Net Minority Interest', 'Stockholders Equity']]
    
    def get_CashFlow(self):
        return self.ticker.quarterly_cash_flow.loc[['Operating Cash Flow', 'Investing Cash Flow', 'Financing Cash Flow']]

    def get_financial_statement(self):
        return f"""
        ### Quarterly Income Statement
        {self.ticker.quarterly_income_stmt.loc[["Total Revenue", "Gross Profit", "Operating Income", "Net Income"]].to_markdown()}
        
        ### Quarterly Balance Sheet
        {self.ticker.quarterly_balance_sheet.loc[['Total Assets', 'Total Liabilities Net Minority Interest', 'Stockholders Equity']].to_markdown()}
        
        ### Quarterly Cash Flow
        {self.ticker.quarterly_cash_flow.loc[['Operating Cash Flow', 'Investing Cash Flow', 'Financing Cash Flow']].to_markdown()}
        """

search.py

import meilisearch

client = meilisearch.Client('http://localhost:7700', 'aSampleMasterKey')

def stock_search(query):
    return client.index('nasdaq').search(query)

app.py

import streamlit as st

from stock_info import Stock
from search import stock_search
from reporting_service import investment_report

class SearchResult:
    def __init__(self, item):
        self.item = item

    @property
    def symbol(self):
        return self.item['Symbol']
    
    @property
    def name(self):
        return self.item['Name']
    
    def __str__(self):
        return f"{self.symbol}: {self.name}"


st.title("AI 투자보고서 생성 서비스")

query = st.text_input("회사명", "Apple")
hits = stock_search(query)['hits']
search_results = [SearchResult(hit) for hit in hits]

selected = st.selectbox("검색 결과 리스트", search_results)

tabs = ["회사 기본 정보", "AI 투자 보고서"]
tab1, tab2 = st.tabs(tabs)

with tab1:
    stock = Stock(selected.symbol)
    st.header(selected)

    st.write(stock.get_basic_info())
    st.write(stock.get_income())
    st.write(stock.get_Balance())
    st.write(stock.get_CashFlow())

with tab2:
    st.header("AI 투자 보고서")
    if st.button("보고서 생성"):
        with st.spinner():
            report = investment_report(selected.name, selected.symbol)
            st.success('Done')
        st.write(report)

6. 실행

meilisearch --master-key="aSampleMasterKey"
streamlit run app.py

7. requirements 파일 작성

pip list --format=freeze > requirements.txt