랭그래프 활용한 주식 정보 도출 프로젝트
- yahooquery, ta 라이브러리 활용
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
import yahooquery as yf
|
||||
|
||||
async def get_financial_info(ticker: str):
|
||||
"""
|
||||
yfinance api 이용
|
||||
"""
|
||||
company = yf.Ticker(ticker)
|
||||
|
||||
info = company.info
|
||||
income = company.income_stmtf
|
||||
balance = company.balance_sheet
|
||||
cashflow = company.cash_flow
|
||||
|
||||
return {
|
||||
# 시가 총액
|
||||
"market_cap":info.get("marketCap"),
|
||||
# 현재 주가
|
||||
"current_price":info.get("currentPrice"),
|
||||
# 매출
|
||||
"revenue":income.loc["Total Revenue"].iloc[0],
|
||||
# 손익계산
|
||||
"operating_income":income.loc["Operating Income"].iloc[0],
|
||||
# 순이익
|
||||
"net_income":income.loc["Net Income"].iloc[0],
|
||||
# 총자산
|
||||
"total_assets":balance.loc["Total Assets"].iloc[0],
|
||||
# 총부채
|
||||
"total_debt":balance.loc["Total Debt"].iloc[0],
|
||||
# 현금흐름
|
||||
"free_cash_flow":cashflow.loc["Free Cash Flow"].iloc[0],
|
||||
# per
|
||||
"pe_ratio":info.get("trailingPE"),
|
||||
# pbr
|
||||
"pb_ratio":info.get("priceToBook")
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
from langchain_community.utilities import GoogleSerperAPIWrapper
|
||||
|
||||
from backend.schemas.news_schemas import NewsItem
|
||||
|
||||
|
||||
async def get_news(company_name: str):
|
||||
"""
|
||||
구글 뉴스 검색 후
|
||||
title, snippet, url, source, date 추출
|
||||
"""
|
||||
search = GoogleSerperAPIWrapper(type="news")
|
||||
results = await search.results(f"{company_name} stock news")
|
||||
news_list = []
|
||||
|
||||
for item in results['news'][:10]:
|
||||
news_list.append(
|
||||
NewsItem(
|
||||
title=item.get("title", ""),
|
||||
snippet=item.get("snippet", ""),
|
||||
url=item.get("link", ""),
|
||||
source=item.get("source", ""),
|
||||
date=item.get("date", "")
|
||||
)
|
||||
)
|
||||
|
||||
return news_list
|
||||
@@ -0,0 +1,148 @@
|
||||
from backend.schemas.stock_schemas import TickerInfo
|
||||
import yahooquery as yq
|
||||
from fastapi import HTTPException
|
||||
|
||||
COMPANY_MAP = {
|
||||
"NVDA": {
|
||||
"company_name": "NVIDIA",
|
||||
"aliases": ["엔비디아", "nvidia", "nvda"],
|
||||
},
|
||||
"AAPL": {
|
||||
"company_name": "Apple",
|
||||
"aliases": ["애플", "apple", "aapl"],
|
||||
},
|
||||
"MSFT": {
|
||||
"company_name": "Microsoft",
|
||||
"aliases": ["마이크로소프트", "microsoft", "msft"],
|
||||
},
|
||||
"TSLA": {
|
||||
"company_name": "Tesla",
|
||||
"aliases": ["테슬라", "tesla", "tsla"],
|
||||
},
|
||||
"GOOGL": {
|
||||
"company_name": "Alphabet (Google)",
|
||||
"aliases": ["구글", "google", "알파벳", "alphabet", "googl"],
|
||||
},
|
||||
"AMZN": {
|
||||
"company_name": "Amazon",
|
||||
"aliases": ["아마존", "amazon", "amzn"],
|
||||
},
|
||||
"META": {
|
||||
"company_name": "Meta Platforms",
|
||||
"aliases": ["메타", "meta", "페이스북", "facebook", "fb"],
|
||||
},
|
||||
"AMD": {
|
||||
"company_name": "Advanced Micro Devices",
|
||||
"aliases": ["amd"],
|
||||
},
|
||||
"INTC": {
|
||||
"company_name": "Intel",
|
||||
"aliases": ["인텔", "intel", "intc"],
|
||||
},
|
||||
"TSM": {
|
||||
"company_name": "TSMC",
|
||||
"aliases": ["tsmc", "tsm", "대만반도체"],
|
||||
},
|
||||
"QCOM": {
|
||||
"company_name": "Qualcomm",
|
||||
"aliases": ["퀄컴", "qualcomm", "qcom"],
|
||||
},
|
||||
"AVGO": {
|
||||
"company_name": "Broadcom",
|
||||
"aliases": ["브로드컴", "broadcom", "avgo"],
|
||||
},
|
||||
"MU": {
|
||||
"company_name": "Micron Technology",
|
||||
"aliases": ["마이크론", "micron", "mu"],
|
||||
},
|
||||
"V": {
|
||||
"company_name": "Visa",
|
||||
"aliases": ["비자", "visa", "v"],
|
||||
},
|
||||
"MA": {
|
||||
"company_name": "Mastercard",
|
||||
"aliases": ["마스터카드", "mastercard", "ma"],
|
||||
},
|
||||
"JPM": {
|
||||
"company_name": "JPMorgan Chase",
|
||||
"aliases": ["jp모건", "jpmorgan", "jpm"],
|
||||
},
|
||||
"JNJ": {
|
||||
"company_name": "Johnson & Johnson",
|
||||
"aliases": ["존슨앤존슨", "johnson", "jnj"],
|
||||
},
|
||||
"MSFT": {
|
||||
"company_name": "Microsoft",
|
||||
"aliases": ["마이크로소프트", "microsoft", "msft"],
|
||||
},
|
||||
}
|
||||
|
||||
class StockService:
|
||||
"""주식 분석"""
|
||||
async def _extract(self, query: str):
|
||||
"""
|
||||
사용자 쿼리에서 티커와 회사명 추출
|
||||
1차 : Map 이용
|
||||
2차 : yahooquery 이용
|
||||
|
||||
query : 엔비디아 분석해줘
|
||||
"""
|
||||
keyword = self._extract_company_keyword(query)
|
||||
|
||||
# 1 차 Map
|
||||
result = self._extract_ticker_from_query(keyword)
|
||||
|
||||
if result:
|
||||
return result
|
||||
|
||||
# 2 차 yahooquery
|
||||
result = await self._search_yahoo_symbol(keyword)
|
||||
|
||||
if result:
|
||||
return result
|
||||
|
||||
raise HTTPException(status_code=30000, detail="종목을 찾을 수 없습니다.")
|
||||
|
||||
def _extract_ticker_from_query(self, query:str):
|
||||
"""
|
||||
COMPANY_MAP 안에서 일치하는 회사 찾기
|
||||
"""
|
||||
|
||||
query = query.lower()
|
||||
|
||||
for ticker, info in COMPANY_MAP.items():
|
||||
for alias in info["aliases"]:
|
||||
if alias.lower() in query:
|
||||
return TickerInfo(ticker = ticker, company_name = info["company_name"])
|
||||
|
||||
def _extract_company_keyword(self, query:str):
|
||||
"""
|
||||
query : 엔비디아 분석해줘
|
||||
=> 필요없는 문장 제거한 후 키워드만 추출
|
||||
"""
|
||||
|
||||
stop_words = [
|
||||
"분석해줘", "분석해", "분석", "해줘", "해", "주가", "전망", "재무", "알려줘", "어때", "어떻게", ""
|
||||
]
|
||||
|
||||
keyword = query
|
||||
|
||||
for word in stop_words:
|
||||
keyword = keyword.replace(word, "")
|
||||
|
||||
return keyword.strip()
|
||||
|
||||
async def _search_yahoo_symbol(self, keyword: str):
|
||||
"""
|
||||
yahooquery 이용
|
||||
"""
|
||||
try:
|
||||
result = yq.search(keyword, first_quote=True)
|
||||
|
||||
if not result or (isinstance(result, dict) and "explanation" in result):
|
||||
return None
|
||||
|
||||
return TickerInfo(ticker=result["symbol"], company_name=result["longname"])
|
||||
|
||||
except Exception:
|
||||
return None
|
||||
@@ -0,0 +1,36 @@
|
||||
import yahooquery as yf
|
||||
from ta.trend import MACD, SMAIndicator
|
||||
from ta.momentum import RSIIndicator
|
||||
|
||||
async def get_technical_info(ticker: str):
|
||||
"""
|
||||
yfinance, ta api 이용
|
||||
"""
|
||||
|
||||
# 주가 데이터 다운로드
|
||||
df = yf.download(ticker, period="1y", interval="1d")
|
||||
# 종가 가져오기
|
||||
close = df['Close'].squeeze()
|
||||
|
||||
macd = MACD(close)
|
||||
macd_value = float(macd.macd().iloc[-1])
|
||||
signal_value = float(macd.macd_signal().iloc[-1])
|
||||
trend = "bullish" if macd_value > signal_value else "bearish"
|
||||
|
||||
return {
|
||||
# 현재 주가
|
||||
"current_price":close.iloc[-1],
|
||||
# 20 일선
|
||||
"sma20":float(SMAIndicator(close, window=20).sma_indicator().iloc[-1]),
|
||||
# 60 일선
|
||||
"sma60":float(SMAIndicator(close, window=60).sma_indicator().iloc[-1]),
|
||||
# rsi
|
||||
"rsi":float(SMAIndicator(close, window=14).rsi().iloc[-1]),
|
||||
# macd
|
||||
"macd":macd.macd(),
|
||||
# macd_signal
|
||||
"macd_signal": signal_value,
|
||||
# trend
|
||||
"trend": trend,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user