portfolio / gen_client.py
eric2digit's picture
Upload folder using huggingface_hub
bf3714e verified
import argparse
import csv
import json
import pandas as pd
import requests
from datetime import datetime
from pathlib import Path
from io import StringIO
def get_user_df(input_path: str, top_n: int) -> pd.DataFrame:
df = pd.read_csv(input_path, dtype={'์ข…๋ชฉ์ฝ”๋“œ': str})
df['๋งค์ž…๊ธˆ์•ก'] = df['๋งค์ž…์ฃผ๊ฐ€'] * df['๋ณด์œ ์ฃผ์‹์ˆ˜']
df_sorted = df.sort_values(by='๋งค์ž…๊ธˆ์•ก', ascending=False)
df_top = df_sorted.head(top_n)
df_top = df_top[['์ข…๋ชฉ์ฝ”๋“œ', '์ข…๋ชฉ๋ช…', '๋งค์ž…์ฃผ๊ฐ€', '๋ณด์œ ์ฃผ์‹์ˆ˜']]
return df_top
def request_post(base_url: str, service: str, req_body: dict) -> dict:
try:
url = f'{base_url}/{service}'
res = requests.post(
url=url,
json=req_body
)
res.raise_for_status()
except Exception as e:
print(f'[ERROR] {service} / ์—๋Ÿฌ ๋ฐœ์ƒ: {str(e)}')
res_body = res.json()
if res_body['success']:
return res_body['data']
def update_user_with_stock_price(json_data, res_data):
"""
res_data ๋กœ ๋ฐ›์€ stock_price ์ •๋ณด๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ
json_data["user"] ํ•ญ๋ชฉ์— ํ˜„์žฌ์ฃผ๊ฐ€, ์ˆ˜์ต๋ฅ , ํ˜„์žฌ์ž์‚ฐ์„ ์—…๋ฐ์ดํŠธ
"""
def normalize(code):
return str(code).strip().upper()
# user ๋ชฉ๋ก ๊ฐ€์ ธ์˜ค๊ธฐ
user_list = json_data.get("user", [])
if not isinstance(user_list, list):
print("[WARN] user ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ๊ฐ€ list ์•„๋‹˜ โ†’ ์—…๋ฐ์ดํŠธ ๋ถˆ๊ฐ€")
return
# ์ข…๋ชฉ ๊ฐ€๊ฒฉ dict(normalized)
stock_price_map = {normalize(k): v for k, v in res_data.items()}
# user ๊ฐ ํ•ญ๋ชฉ ์—…๋ฐ์ดํŠธ
for user_item in user_list:
raw_code = user_item.get("์ข…๋ชฉ์ฝ”๋“œ")
if not raw_code:
continue
code = normalize(raw_code)
# stock_price ์— ํ•ด๋‹น ์ข…๋ชฉ์ด ์กด์žฌํ•˜๋Š”์ง€ ํ™•์ธ
prices = stock_price_map.get(code)
if not prices:
print(f"[WARN] {code} ์ข…๋ชฉ์˜ ์ฃผ๊ฐ€ ๋ฐ์ดํ„ฐ ์—†์Œ โ†’ user ์ถ”๊ฐ€์ •๋ณด ์Šคํ‚ต")
continue
# ์ตœ์‹  ์ข…๊ฐ€ (list ๋งˆ์ง€๋ง‰)
last_row = prices[-1]
try:
current_price = float(last_row.get("Close"))
buy_price = float(user_item.get("๋งค์ž…์ฃผ๊ฐ€"))
qty = float(user_item.get("๋ณด์œ ์ฃผ์‹์ˆ˜"))
except Exception:
print(f"[WARN] {code} ๊ฐ’ ๋ณ€ํ™˜ ์˜ค๋ฅ˜ โ†’ user ์ถ”๊ฐ€์ •๋ณด ์Šคํ‚ต")
continue
# ์ˆ˜์ต๋ฅ  ๊ณ„์‚ฐ
profit_rate = round(((current_price - buy_price) / buy_price) * 100, 2)
current_asset = round(current_price * qty, 2)
# ์—…๋ฐ์ดํŠธ
user_item["ํ˜„์žฌ์ฃผ๊ฐ€"] = str(current_price)
user_item["์ˆ˜์ต๋ฅ "] = f"{profit_rate}%"
user_item["ํ˜„์žฌ์ž์‚ฐ"] = str(current_asset)
# ์ €์žฅ
json_data["user"] = user_list
return json_data
def collect_data(region, base_url: str, portfolio: pd.DataFrame):
json_data = {
'user': [],
'similar_investors': {},
'investment_company': {},
'industry_info': [],
'theme_info': [],
'stock_price': {},
'stock_news': {},
}
# ------------------------------
# ์œ ์‚ฌ ํˆฌ์ž์ž
# ------------------------------
service = 'similar_investors'
req_body = {
'csv_text': portfolio.to_csv(index=False),
'region': region[0] if isinstance(region, list) else region,
'top': 5
}
print(f"[INFO] ์œ ์‚ฌ ํˆฌ์ž์ž {req_body['top']}๊ฐœ ์ˆ˜์ง‘ ์‹œ์ž‘..")
res_data = request_post(base_url, service, req_body)
if not res_data:
print(f'[ERROR] ์œ ์‚ฌ ํˆฌ์ž์ž ์ˆ˜์ง‘ ์‹คํŒจ..')
return None
print(f'[INFO] ์œ ์‚ฌ ํˆฌ์ž์ž ์ˆ˜์ง‘ ์™„๋ฃŒ:', res_data)
json_data[service] = res_data
# user ํ‚ค ์ €์žฅ
used_user_csv = portfolio.to_csv(index=False)
f = StringIO(used_user_csv)
reader = csv.reader(f)
rows = list(reader)
if not rows or len(rows) < 2:
print("[WARN] portfolio CSV ๋‚ด์šฉ์ด ๋น„์–ด ์žˆ์–ด user ๋ฐ์ดํ„ฐ ์ €์žฅ์„ ์ƒ๋žตํ•ฉ๋‹ˆ๋‹ค.")
json_data["user"] = []
else:
headers = rows[0]
user_list = [dict(zip(headers, cols)) for cols in rows[1:]]
json_data["user"] = user_list
# ์‚ฐ์—…, ํ…Œ๋งˆ ์š”์ฒญ์‹œ ํˆฌ์ž์ž ์ข…๋ชฉ๋„ ํฌํ•จ
si_names = list(set([d['NAME'] for rows in res_data.values() for d in rows]))
si_companies = list(set([d['COMPANY'] for rows in res_data.values() for d in rows]))
# ------------------------------
# ์œ ์‚ฌ ํˆฌ์žํšŒ์‚ฌ ์„ค๋ช…
# ------------------------------
print("[INFO] ํˆฌ์žํšŒ์‚ฌ name ๋ชฉ๋ก:", si_names)
service = "investment_company"
for name in si_names:
print(f"[INFO] {name} ์„ค๋ช… ์ˆ˜์ง‘ ์‹œ์ž‘..")
req_body = {"name": name}
res_data = request_post(base_url, service, req_body)
if not res_data:
print("[ERROR] ์„ค๋ช… ์ˆ˜์ง‘ ์‹คํŒจ:", name)
json_data[service][name] = {
"error": True,
"detail": "request_post failed"
}
continue
print("[INFO] ์„ค๋ช… ์ˆ˜์ง‘ ์™„๋ฃŒ:", res_data)
json_data[service][name] = res_data
if not json_data[service]:
print(f'[ERROR] ์œ ์‚ฌ ํˆฌ์ž์ž ์ˆ˜์ง‘ ์‹คํŒจ..')
return None
print()
# ------------------------------
# ์‚ฐ์—…์ •๋ณด
# ------------------------------
service = 'industry_info'
stock = portfolio['์ข…๋ชฉ์ฝ”๋“œ'].to_list() + si_companies
req_body = {
'stock': stock
}
print(f'[INFO] ์‚ฐ์—…์ •๋ณด {",".join(stock)} ์ˆ˜์ง‘ ์‹œ์ž‘..')
res_data = request_post(base_url, service, req_body)
if not res_data:
print(f'[ERROR] ์‚ฐ์—…์ •๋ณด ์ˆ˜์ง‘ ์‹คํŒจ..')
return None
# ์ค‘๋ณต ์ œ๊ฑฐ
seen = set()
unique_data = [d for d in res_data if not (d['stock'] in seen or seen.add(d['stock']))]
print(f'[INFO] ์‚ฐ์—…์ •๋ณด ์ˆ˜์ง‘ ์™„๋ฃŒ:', unique_data)
print()
json_data[service] = unique_data
# ------------------------------
# ํ…Œ๋งˆ์ •๋ณด
# ------------------------------
service = 'theme_info'
stock = portfolio['์ข…๋ชฉ์ฝ”๋“œ'].to_list() + si_companies
req_body = {
'stock': stock
}
print(f'[INFO] ํ…Œ๋งˆ์ •๋ณด {",".join(stock)} ์ˆ˜์ง‘ ์‹œ์ž‘..')
res_data = request_post(base_url, service, req_body)
if not res_data:
print(f'[ERROR] ํ…Œ๋งˆ์ •๋ณด ์ˆ˜์ง‘ ์‹คํŒจ..')
return None
# ์ค‘๋ณต ์ œ๊ฑฐ
seen = set()
unique_data = [d for d in res_data if not (d['stock'] in seen or seen.add(d['stock']))]
print(f'[INFO] ํ…Œ๋งˆ์ •๋ณด ์ˆ˜์ง‘ ์™„๋ฃŒ:', unique_data)
print()
json_data[service] = unique_data
# ------------------------------
# ์ข…๋ชฉ ๊ฐ€๊ฒฉ
# ------------------------------
service = 'stock_price'
stock = ','.join(portfolio['์ข…๋ชฉ์ฝ”๋“œ'])
req_body = {'stock': stock}
print(f'[INFO] ์ข…๋ชฉ๊ฐ€๊ฒฉ {stock} ์ˆ˜์ง‘ ์‹œ์ž‘..')
res_data = request_post(base_url, service, req_body)
if not res_data:
print(f'[ERROR] ์ข…๋ชฉ๊ฐ€๊ฒฉ ์ˆ˜์ง‘ ์‹คํŒจ..')
return None
print(f'[INFO] ์ข…๋ชฉ๊ฐ€๊ฒฉ ์ˆ˜์ง‘ ์™„๋ฃŒ:', res_data)
print()
json_data[service] = res_data
json_data = update_user_with_stock_price(json_data, res_data)
# ------------------------------
# ์ข…๋ชฉ ๋‰ด์Šค
# ------------------------------
service = 'stock_news'
for stock in portfolio['์ข…๋ชฉ์ฝ”๋“œ']:
print(f'[INFO] ์ข…๋ชฉ๋‰ด์Šค {stock} ์ˆ˜์ง‘ ์‹œ์ž‘..')
req_body = {
'stock': stock,
'period': 7
}
res_data = request_post(base_url, service, req_body)
if res_data:
print(f'[INFO] ์ข…๋ชฉ๋‰ด์Šค {stock} ์ˆ˜์ง‘ ์™„๋ฃŒ:', res_data)
json_data[service] |= res_data
else:
print(f'[ERROR] ์ข…๋ชฉ๋‰ด์Šค {stock} ์ˆ˜์ง‘ ์‹คํŒจ..')
if not json_data[service]:
print(f'[ERROR] ์ข…๋ชฉ๋‰ด์Šค ์ˆ˜์ง‘ ์‹คํŒจ..')
return None
print()
return json_data
def save_results(output_root: str, user_csv: str, output_json: dict, report: str):
output_dir = Path(output_root) / datetime.now().strftime('%y%m%d_%H%M%S')
output_dir.mkdir(parents=True, exist_ok=True)
# ------------------------------
# ์œ ์ € ํฌํŠธํด๋ฆฌ์˜ค ์ €์žฅ
# ------------------------------
user_path = output_dir / 'user.csv'
with open(user_path, 'w') as f:
f.write(user_csv)
print(f'[INFO] ์œ ์ € ํฌํŠธํด๋ฆฌ์˜ค ์ €์žฅ ์™„๋ฃŒ -> {user_path}')
# ------------------------------
# ๋ฐ์ดํ„ฐ ์ €์žฅ
# ------------------------------
output_path = output_dir / 'output.json'
with open(output_path, 'w') as f:
json.dump(output_json, f, ensure_ascii=False, indent=2)
print(f'[INFO] ๋ฐ์ดํ„ฐ ์ €์žฅ ์™„๋ฃŒ -> {output_path}')
# ------------------------------
# ๋ฆฌํฌํŠธ ์ €์žฅ
# ------------------------------
report_path = output_dir / 'report.txt'
with open(report_path, 'w') as f:
f.write(report)
print(f'[INFO] ๋ฆฌํฌํŠธ ์ €์žฅ ์™„๋ฃŒ -> {report_path}')
def main():
parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument('--server', type=str, default='http://localhost:8080', help='์„œ๋ฒ„ ์ฃผ์†Œ')
parser.add_argument("--region", choices=['ko', 'us'], required=True, help="์ง€์—ญ ์„ ํƒ: ko(ํ•œ๊ตญ) ๋˜๋Š” us(๋ฏธ๊ตญ)")
parser.add_argument('--user', type=str, default='user.csv', help='์œ ์ € ํฌํŠธํด๋ฆฌ์˜ค csv ํŒŒ์ผ ๊ฒฝ๋กœ')
parser.add_argument('--user_name', type=str, default='์ด์„œ์ค€', help='ํˆฌ์ž์ž ์ด๋ฆ„')
parser.add_argument('--output', type=str, default='results', help='๊ฒฐ๊ณผ ๊ฒฝ๋กœ')
args = parser.parse_args()
# ------------------------------
# ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘
# ------------------------------
user_df = get_user_df(args.user, top_n=5)
json_data = collect_data(args.region, args.server, user_df)
if not json_data:
print(f'[ERROR] ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘ ์‹คํŒจ.. ๋ณด๊ณ ์„œ ์ƒ์„ฑ ์ข…๋ฃŒ.')
return
# ------------------------------
# ๋ณด๊ณ ์„œ ์ƒ์„ฑ
# ------------------------------
service = 'report'
req_body = {
"date": datetime.now().strftime("%Y-%m-%d"), # ๋ฆฌํฌํŠธ ์ƒ์„ฑ ๋‚ ์งœ (๊ธฐ๋ณธ: ์˜ค๋Š˜ ๋‚ ์งœ)
'user_name': args.user_name,
'csv_text': user_df.to_csv(index=False),
'json_text': json_data
}
print(f'[INFO] ์œ ์ € [{args.user_name}] ๋ณด๊ณ ์„œ ์ƒ์„ฑ ์‹œ์ž‘..')
report_data = request_post(args.server, service, req_body)
if not report_data:
print(f'[ERROR] ๋ณด๊ณ ์„œ ์ƒ์„ฑ ์‹คํŒจ..')
return
report = report_data['report']
# ------------------------------
# ๊ฒฐ๊ณผ ์ €์žฅ
# ------------------------------
save_results(args.output, user_df.to_csv(index=False), json_data, report)
if __name__ == '__main__':
main()