Spaces:
Runtime error
Runtime error
| import argparse | |
| import os | |
| import tempfile | |
| from datetime import datetime | |
| from typing import List, Optional, Dict, Any | |
| from fastapi import FastAPI, HTTPException, File, UploadFile | |
| from pydantic import BaseModel | |
| from industry_info import industry_info | |
| from investment_company import investment_company | |
| from similar_investors import SimilarInvestors | |
| from stock_news import StockNews | |
| from stock_price import StockPrice | |
| from theme_info import theme_info | |
| from portfolio import build_final_prompt, chatgpt, read_file_text, load_json | |
| app = FastAPI(title="Portfolio Analysis") | |
| # ๋ฐ์ดํฐ ํ์ผ ๊ฒฝ๋ก ์ค์ | |
| DATA_PATHS = { | |
| "similar_investors_us": "/work/portfolio/data/SEC_Filing_Manager.csv", | |
| "similar_investors_ko": "/work/portfolio/data/ETF.csv", | |
| "investment_company": "/work/portfolio/data/investment_company.jsonl", | |
| "industry_info": "/work/portfolio/data/industry_info.csv", | |
| "theme_info":"/work/portfolio/data/theme_info.csv" | |
| } | |
| # Request Models | |
| class IndustryInfoRequest(BaseModel): | |
| stock: List[str] = ["Meta"] | |
| class InvestmentCompanyRequest(BaseModel): | |
| name: str = "GEODE CAPITAL MANAGEMENT, LLC" | |
| class SimilarInvestorsRequest(BaseModel): | |
| csv_text: str = """์ข ๋ชฉ์ฝ๋,์ข ๋ชฉ๋ช ,๋งค์ ์ฃผ๊ฐ,๋ณด์ ์ฃผ์์\nNVDA,NVIDIA Corporation,159.69,167\nAMZN,"Amazon.com, Inc.",211.58,140""" | |
| region: str | |
| top: int = 1 | |
| class StockNewsRequest(BaseModel): | |
| stock: str = "META" | |
| period: int = 1 | |
| class StockPriceRequest(BaseModel): | |
| stock: str = "META" | |
| class ThemeInfoRequest(BaseModel): | |
| stock: List[str] = ["Meta"] | |
| class PortfolioRequest(BaseModel): | |
| date:str = datetime.now().strftime("%Y๋ %m์ %d์ผ") | |
| user_name: str = "์ด์์ค" | |
| csv_text: str = "์ข ๋ชฉ์ฝ๋,์ข ๋ชฉ๋ช ,๋งค์ ์ฃผ๊ฐ,๋ณด์ ์ฃผ์์\nAAPL,Apple Inc.,150,10" | |
| json_text: Dict[str, Any] = {"similar_investors":[{"ID":"37833100","NAME":"LAZARI CAPITAL MANAGEMENT, INC.","COMPANY":"APPLE INC","VALUE":33972687.0,"AMOUNT":165583,"PERCENTAGE":12.8}],"stock_price":{"META":[{"Date":"2025-10-20","Name":"Meta Platforms","Open":721.2,"High":733.8,"Low":720.2,"Close":732.2,"Volume":8900200},{"Date":"2025-10-21","Name":"Meta Platforms","Open":736.0,"High":738.5,"Low":728.8,"Close":733.3,"Volume":7647300},{"Date":"2025-10-22","Name":"Meta Platforms","Open":733.8,"High":740.6,"Low":724.0,"Close":733.4,"Volume":8734500}]},"stock_news":{"META":[{"Name":"Meta Platforms","date":"2025-11-20","time":"08:37","news":"๋ฅ๋ฌ๋ ๊ฑฐ์ฅ ์ ๋ฅด์ฟค, Meta Platforms ๋ ๋ AI ์คํํธ์ ์ค๋ฆฝ ์ ์ธ\nโ์ค๋๋ AI๋ ์ธ๊ฐ ์์ค์ ์ง๋ฅ์ ํฅํด ๊ฐ๊ธฐ ์ํด ๋ฐ๋์ ์๋ก์ด ์ ๊ทผ์ด ํ์ํ๋ค.โ ๋ฅ๋ฌ๋์ ๊ฐ์ฒ์ ์ ๋ฅด์ฟค(Yann LeCun)์ด Meta๋ฅผ ๋ ๋๋ฉฐ ๋จ๊ธด ์ด ์ฝ๋ฉํธ๋ AI ์ ๊ณ..."}]},"industry_info":[{"stock":"Meta","description":"Industry: Internet Content & Information | Sector: Communication Services"}],"theme_info":[{"stock":"Meta","desc":"Theme: Artificial Intelligence | Theme 2: \"Social Media\" | Theme 3: \"Virtual Reality\""}]} | |
| # 1. Industry Info API | |
| def get_industry_info(request: IndustryInfoRequest): | |
| """์ข ๋ชฉ์ ์ฐ์ ๊ตฐ/์นํฐ ๊ฒ์""" | |
| try: | |
| print(f"[INFO] Processing stocks: {request.stock}") | |
| # industry_info ๊ฐ์ฒด ์์ฑ | |
| ic = industry_info(DATA_PATHS["industry_info"]) | |
| all_records = [] | |
| for stock in request.stock: | |
| print(f"[INFO] ์ฒ๋ฆฌ ์ค: {stock}") | |
| result = ic.make(stock) | |
| if not result: | |
| print(f"[WARN] ์ข ๋ชฉ ์ฒ๋ฆฌ ์คํจ: {stock}") | |
| continue | |
| full_name = result["stock"] | |
| desc = ic.get(full_name) | |
| if desc: | |
| print(f"[INFO] {full_name} ์กฐํ ์๋ฃ") | |
| all_records.append({ | |
| "stock": full_name, | |
| "description": desc | |
| }) | |
| else: | |
| print(f"[WARN] {full_name} description ์์") | |
| return { | |
| "success": True, | |
| "data": all_records, | |
| "count": len(all_records) | |
| } | |
| except Exception as e: | |
| raise HTTPException(status_code=500, detail=str(e)) | |
| # 2. Investment Company API | |
| def get_investment_company(request: InvestmentCompanyRequest): | |
| """ํฌ์ํ์ฌ ์ ๋ณด ์กฐํ""" | |
| try: | |
| print(f"[INFO] Processing company: {request.name}") | |
| ic = investment_company(DATA_PATHS["investment_company"]) | |
| ic.make() | |
| desc = ic.get(request.name) | |
| return { | |
| "success": True, | |
| "data": { | |
| "company_name": request.name, | |
| "description": desc | |
| } | |
| } | |
| except Exception as e: | |
| raise HTTPException(status_code=500, detail=str(e)) | |
| # 3. Similar Investors API | |
| def get_similar_investors(request: SimilarInvestorsRequest): | |
| """์ฌ์ฉ์์ ์ ์ฌํ ํฌ์ํ์ฌ ๊ฒ์ (CSV ๋ฌธ์์ด ์ ๋ ฅ)""" | |
| try: | |
| print(f"[INFO] Processing CSV text (length={len(request.csv_text)}), top: {request.top}") | |
| # SimilarInvestors ์ค๋น | |
| si = SimilarInvestors(DATA_PATHS["similar_investors_ko"], DATA_PATHS["similar_investors_us"]) | |
| if request.region == 'ko': | |
| created_path = si.make_ko() | |
| elif request.region == 'us': | |
| created_path = si.make_us() | |
| else: | |
| print("[INFO] region ์ ๋ ฅ ์ค๋ฅ.") | |
| raise HTTPException(status_code=400, detail="region์ 'ko' ๋๋ 'us' ์ฌ์ผ ํฉ๋๋ค.") | |
| print(f"[INFO] ๋ฐ์ดํฐ ๊ฒฝ๋ก: {created_path}") | |
| # 1) csv_text ์ ํจ์ฑ ๊ฒ์ฌ (๊ฐ๋จ) | |
| header = request.csv_text.strip().splitlines()[0] | |
| required_cols = ["์ข ๋ชฉ์ฝ๋", "์ข ๋ชฉ๋ช ", "๋งค์ ์ฃผ๊ฐ", "๋ณด์ ์ฃผ์์"] | |
| for col in required_cols: | |
| if col not in header.split(","): | |
| raise HTTPException(status_code=400, detail=f"CSV ํค๋์ '{col}' ์ปฌ๋ผ์ด ์์ต๋๋ค.") | |
| print("1์๋ฃ \n") | |
| # 2) ์์ ํ์ผ๋ก ์ ์ฅํ์ฌ ๊ธฐ์กด si.get ์ฌ์ฌ์ฉ | |
| with tempfile.NamedTemporaryFile(mode="w", suffix=".csv", delete=False) as tmp: | |
| tmp_path = tmp.name | |
| tmp.write(request.csv_text) | |
| print(f"[INFO] ์์ CSV ํ์ผ ์์ฑ: {tmp_path}") | |
| try: | |
| result = si.get(tmp_path, request.region, output_path=None, top_n=request.top) | |
| count = sum(len(rows) for rows in result.values()) | |
| finally: | |
| # ์์ ํ์ผ ์ญ์ | |
| if os.path.exists(tmp_path): | |
| os.remove(tmp_path) | |
| print(f"[INFO] ์์ ํ์ผ ์ญ์ ์๋ฃ: {tmp_path}") | |
| return { | |
| "success": True, | |
| "data": result, | |
| "count": count, | |
| "top_n": request.top | |
| } | |
| except HTTPException as e: | |
| raise e | |
| except Exception as e: | |
| import traceback | |
| traceback.print_exc() | |
| raise HTTPException(status_code=500, detail=str(e)) | |
| # 4. Stock News API | |
| def get_stock_news(request: StockNewsRequest): | |
| """๊ธฐ์ ๋ด์ค ํฌ๋กค๋ง""" | |
| try: | |
| print(f"[INFO] Processing stock: {request.stock}, period: {request.period}") | |
| data_folder = "data_stock_news" | |
| sn = StockNews(data_folder) | |
| # make ํจ์ ์คํ | |
| created = sn.make(request.stock, request.period) | |
| print(f"[INFO] ์์ฑ๋ ๊ฑด์: {len(created) if created is not None else 0}") | |
| # get ํจ์ ์คํ | |
| loaded = sn.get(request.stock) | |
| print(f"[INFO] ๋ก๋๋ ๊ฑด์: {len(loaded) if loaded is not None else 0}") | |
| stock = sn.normalize_ticker(request.stock) | |
| records = loaded.to_dict('records') if loaded is not None else [] | |
| return { | |
| "success": True, | |
| "data": { | |
| stock: records | |
| }, | |
| "count": len(records) | |
| } | |
| except Exception as e: | |
| raise HTTPException(status_code=500, detail=str(e)) | |
| # 5. Stock Price API | |
| def get_stock_price(request: StockPriceRequest): | |
| """์ฃผ๊ฐ ๋ฐ์ดํฐ ์กฐํ""" | |
| try: | |
| stocks = [t.strip().upper() for t in request.stock.split(",") if t.strip()] | |
| print(f"[INFO] Processing {len(stocks)} stocks") | |
| data_folder = "data_stock_price/" | |
| sp = StockPrice(data_folder) | |
| stock_to_records = {} | |
| for stock in stocks: | |
| df = sp.make(stock) | |
| if df is None: | |
| print(f"[WARN] ์์ฑ ์คํจ: {stock}") | |
| continue | |
| df = sp.get(stock) | |
| stock_to_records[sp.normalize_ticker(stock)] = df.to_dict('records') | |
| return { | |
| "success": True, | |
| "data": stock_to_records, | |
| "stocks_processed": len(stock_to_records) | |
| } | |
| except Exception as e: | |
| raise HTTPException(status_code=500, detail=str(e)) | |
| # 6. Theme Info API | |
| def get_theme_info(request: ThemeInfoRequest): | |
| """์ข ๋ชฉ์ ํ ๋ง ๊ฒ์""" | |
| try: | |
| print(f"[INFO] Processing stocks: {request.stock}") | |
| ti = theme_info(DATA_PATHS["theme_info"]) | |
| # make ํจ์ ์คํ | |
| make_results = [] | |
| for stock in request.stock: | |
| print(f"[INFO] ์ฒ๋ฆฌ ์ค: {stock}") | |
| rows = ti.make(stock) | |
| if rows: | |
| make_results.extend(rows) | |
| # get ํจ์ ์คํ | |
| all_records = [] | |
| for row in make_results: | |
| official_name = row["NAME"] | |
| desc = ti.get(official_name) | |
| all_records.append({ | |
| "stock": official_name, | |
| "desc": desc | |
| }) | |
| print(f"[INFO] {official_name} ์กฐํ ์๋ฃ") | |
| return { | |
| "success": True, | |
| "data": all_records, | |
| "count": len(all_records) | |
| } | |
| except Exception as e: | |
| raise HTTPException(status_code=500, detail=str(e)) | |
| # 7. Portfolio Report API | |
| def generate_portfolio_report(request: PortfolioRequest): | |
| try: | |
| print(f"[INFO] Generating report for user: {request.user_name}") | |
| csv_text = request.csv_text | |
| json_obj = request.json_text | |
| print("[INFO] ์ ๋ ฅ ๋ฐ์ดํฐ ๋ก๋ ์๋ฃ") | |
| final_prompt = build_final_prompt(request.date, request.user_name, csv_text, json_obj) | |
| print("[INFO] ํ๋กฌํํธ ์์ฑ ์๋ฃ") | |
| report_text = chatgpt(final_prompt) | |
| print("[INFO] ๋ฆฌํฌํธ ์์ฑ ์๋ฃ") | |
| return { | |
| "success": True, | |
| "data": { | |
| "user_name": request.user_name, | |
| "report": report_text | |
| } | |
| } | |
| except Exception as e: | |
| raise HTTPException(status_code=500, detail=str(e)) | |
| if __name__ == "__main__": | |
| parser = argparse.ArgumentParser() | |
| parser.add_argument('--port', type=int, default=8080) | |
| args = parser.parse_args() | |
| import uvicorn | |
| uvicorn.run(app, host="0.0.0.0", port=args.port) |