랭그래프 활용한 주식 정보 도출 프로젝트
- yahooquery, ta 라이브러리 활용
This commit is contained in:
Generated
+10
@@ -0,0 +1,10 @@
|
|||||||
|
# 디폴트 무시된 파일
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# 에디터 기반 HTTP 클라이언트 요청
|
||||||
|
/httpRequests/
|
||||||
|
# 쿼리 파일을 포함한 무시된 디폴트 폴더
|
||||||
|
/queries/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
||||||
Generated
+14
@@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="PYTHON_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/.venv" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="jdk" jdkName="~/Source/project/STOCK_APP/.venv" jdkType="Python SDK" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
<component name="PackageRequirementsSettings" />
|
||||||
|
<component name="PyDocumentationSettings" />
|
||||||
|
<component name="ReSTService" />
|
||||||
|
<component name="TestRunnerService" />
|
||||||
|
</module>
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<settings>
|
||||||
|
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||||
|
<version value="1.0" />
|
||||||
|
</settings>
|
||||||
|
</component>
|
||||||
Generated
+8
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/STOCK_APP.iml" filepath="$PROJECT_DIR$/.idea/STOCK_APP.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
Generated
+34
@@ -0,0 +1,34 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="PyToolsState">
|
||||||
|
<option name="tools">
|
||||||
|
<map>
|
||||||
|
<entry key="black">
|
||||||
|
<value>
|
||||||
|
<ToolEntry />
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
|
<entry key="pyrefly">
|
||||||
|
<value>
|
||||||
|
<ToolEntry />
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
|
<entry key="pyright">
|
||||||
|
<value>
|
||||||
|
<ToolEntry />
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
|
<entry key="ruff">
|
||||||
|
<value>
|
||||||
|
<ToolEntry />
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
|
<entry key="ty">
|
||||||
|
<value>
|
||||||
|
<ToolEntry />
|
||||||
|
</value>
|
||||||
|
</entry>
|
||||||
|
</map>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
Generated
+6
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$/../.." vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
from langchain_ibm import WatsonxEmbeddings
|
||||||
|
from backend.config.settings import settings
|
||||||
|
|
||||||
|
watson_embedding = WatsonxEmbeddings(
|
||||||
|
model_id="ibm/granite-embedding-278m-multilingual",
|
||||||
|
url=f"{settings.watsonx_url}",
|
||||||
|
api_key=f"{settings.watsonx_api_key}",
|
||||||
|
project_id=f"{settings.watsonx_project_id}",
|
||||||
|
)
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
from langchain_ibm import ChatWatsonx
|
||||||
|
from backend.config.settings import settings
|
||||||
|
from langchain_openai import ChatOpenAI
|
||||||
|
|
||||||
|
watson_llm = ChatWatsonx(
|
||||||
|
model_id="ibm/granite-4-h-small",
|
||||||
|
url=f"{settings.watsonx_url}",
|
||||||
|
api_key=f"{settings.watsonx_api_key}",
|
||||||
|
project_id=f"{settings.watsonx_project_id}",
|
||||||
|
max_tokens=2000,
|
||||||
|
)
|
||||||
|
|
||||||
|
hugging_llm = ChatOpenAI(
|
||||||
|
base_url="https://router.huggingface.co/v1",
|
||||||
|
api_key=f"{settings.hf_token}",
|
||||||
|
model_name="Qwen/Qwen3-8B:nscale",
|
||||||
|
)
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
from pydantic import Field
|
||||||
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||||
|
|
||||||
|
class Settings(BaseSettings):
|
||||||
|
model_config = SettingsConfigDict(env_file="backend/.env", extra="ignore")
|
||||||
|
# 사용할 모델
|
||||||
|
watsonx_api_key: str = Field(alias="WATSONX_API_KEY")
|
||||||
|
watsonx_project_id: str = Field(alias="WATSONX_PROJECT_ID")
|
||||||
|
watsonx_url: str = Field(alias="WATSONX_URL")
|
||||||
|
hf_token: str = Field(alias="HF_TOKEN")
|
||||||
|
|
||||||
|
settings = Settings()
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
from backend.services.news_service import get_news
|
||||||
|
from backend.services.financial_service import get_financial_info
|
||||||
|
from backend.services.technical_service import get_technical_info
|
||||||
|
|
||||||
|
async def news_node(state):
|
||||||
|
news_list = await get_news(state["company_name"])
|
||||||
|
return {"news": news_list}
|
||||||
|
|
||||||
|
async def financial_node(state):
|
||||||
|
financials = await get_financial_info(state["ticker"])
|
||||||
|
return {"financials": financials}
|
||||||
|
|
||||||
|
async def technical_node(state):
|
||||||
|
technicals = await get_technical_info(state["ticker"])
|
||||||
|
return {"technicals": technicals}
|
||||||
|
|
||||||
|
async def competitor_node(state):
|
||||||
|
pass
|
||||||
|
|
||||||
|
async def report_node(state):
|
||||||
|
pass
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
from typing import TypedDict
|
||||||
|
|
||||||
|
class StockAnalysis(TypedDict):
|
||||||
|
query:str
|
||||||
|
ticker:str
|
||||||
|
company_name:str
|
||||||
|
new:list
|
||||||
|
financials:dict
|
||||||
|
technicals:dict
|
||||||
|
competitors:list
|
||||||
|
report:str
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
from fastapi import FastAPI
|
||||||
|
from starlette.staticfiles import StaticFiles
|
||||||
|
from contextlib import asynccontextmanager
|
||||||
|
|
||||||
|
from backend.routers.stock_router import router as stock_router
|
||||||
|
|
||||||
|
# from backend.repository.db_init import Base, SessionLocal, engine
|
||||||
|
#
|
||||||
|
# from backend.routers.evalu_router import router as evalu_router
|
||||||
|
# from backend.routers.assistant_router import router as assistant_router
|
||||||
|
# from backend.routers.call_router import router as call_router
|
||||||
|
# from backend.repository.seed import seed_customers
|
||||||
|
|
||||||
|
# @asynccontextmanager
|
||||||
|
# async def lifespan(app: FastAPI):
|
||||||
|
# print("서버 시작")
|
||||||
|
#
|
||||||
|
# # Base 에 등록된 모든 모델에 테이블 자동 생성
|
||||||
|
# Base.metadata.create_all(bind=engine)
|
||||||
|
# print("[DB] 테이블 생성 완료 (또는 이미 존재)")
|
||||||
|
#
|
||||||
|
# db = SessionLocal()
|
||||||
|
# try:
|
||||||
|
# # 기본 user 데이터 삽입
|
||||||
|
# seed_customers(db)
|
||||||
|
# finally:
|
||||||
|
# db.close()
|
||||||
|
#
|
||||||
|
# yield
|
||||||
|
# print("서버 종료")
|
||||||
|
|
||||||
|
# app = FastAPI()
|
||||||
|
# app = FastAPI(title="상담 LLM", version="1.0", lifespan=lifespan)
|
||||||
|
app = FastAPI(title="$ Stock AI", version="1.0", description="""
|
||||||
|
#### $주식 AI 분석 FastAPI + LangGraph + yfinance + ChromaDB
|
||||||
|
|
||||||
|
### 주요 기능
|
||||||
|
| 번호 | 기능 | 엔드포인트 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
|1 | 기업 종합 분석 | 'POST /api/stock/analysis'
|
||||||
|
""",
|
||||||
|
)
|
||||||
|
|
||||||
|
# static 폴더 지정
|
||||||
|
# app.mount("/static", StaticFiles(directory="backend/static"), name="static")
|
||||||
|
|
||||||
|
# 라우터 등록
|
||||||
|
app.include_router(stock_router)
|
||||||
|
# app.include_router(assistant_router)
|
||||||
|
# app.include_router(evalu_router)
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
from fastapi import APIRouter, Depends
|
||||||
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
|
from backend.schemas.stock_schemas import StockAnalyzeReq
|
||||||
|
from backend.services.stock_service import StockService
|
||||||
|
|
||||||
|
router = APIRouter(prefix="/api/stock", tags=["기헙종합분석"])
|
||||||
|
|
||||||
|
@router.post("/analyze", summary="기업 종합 분석", description="자연어로 해당기업을 요청하면 뉴스, 재무, 기술적, 경쟁사 분석을 수행합니다.", )
|
||||||
|
async def stock_analyze(req:StockAnalyzeReq):
|
||||||
|
service = StockService()
|
||||||
|
return await service._extract(req.query)
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
|
||||||
|
class NewsItem(BaseModel):
|
||||||
|
title: str
|
||||||
|
snippet: str
|
||||||
|
url: str
|
||||||
|
source: str
|
||||||
|
date: str
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
|
||||||
|
class StockAnalyzeReq(BaseModel):
|
||||||
|
query: str
|
||||||
|
|
||||||
|
class TickerInfo(BaseModel):
|
||||||
|
ticker: str
|
||||||
|
company_name: str
|
||||||
@@ -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,
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
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"],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
COMPETITOR_MAP = {
|
||||||
|
"NVDA": ["AMD", "INTC", "TSM", "AVGO"],
|
||||||
|
"AMD": ["NVDA", "INTC", "TSM"],
|
||||||
|
"TSLA": ["RIVN", "GM", "F"],
|
||||||
|
"AAPL": ["MSFT", "GOOGL", "AMZN"],
|
||||||
|
"MSFT": ["AAPL", "GOOGL", "AMZN"],
|
||||||
|
"GOOGL": ["MSFT", "META", "AMZN"],
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user