import pandas as pd
import os
import json
# json파일를 저장함
json_file_path = '/Users/sungsupark/Desktop/데스크탑 - 박성수의 MacBook Air/24-2수업자료/데이터분석캡스톤디자인/survey.json'
# correlation이 저장된 파일 경로를 저장함
folder_path = '/Users/sungsupark/Desktop/데스크탑 - 박성수의 MacBook Air/24-2수업자료/데이터분석캡스톤디자인/correlation'
def customized_recommend_stocks(user_id, input_stock):
file_path = os.path.join(folder_path, f"{input_stock}_corr.csv")
try:
correlation_data = pd.read_csv(file_path, index_col=0)
except FileNotFoundError:
return f"{input_stock}에 대한 파일을 {folder_path}에서 찾을 수 없습니다."
try:
with open(json_file_path, 'r') as fp:
survey_data = json.load(fp)
except Exception as e:
return f"JSON 파일을 읽는 중 오류 발생: {e}"
user_info = survey_data[user_id] # 입력으로 들어온 user_id를 바탕으로 user의 정보를 저장한다.
final_score = float(user_info["final_score"]) # user의 final_score는 str타입이기 때문에 float타입으로 변환하여 저장한다.
correlation_data_sorted = correlation_data.sort_values(by='Correlation') # 상관계수를 오름차순으로 정렬한다.
corr_values = correlation_data_sorted['Correlation'].values # 상관계수를 배열로 저장한다.
etf_list = correlation_data_sorted.index.values # ETF 종목 이름을 배열로 저장한다.
# 고정으로 추천할 ETF 3개 ('공격적', '중립적', '보수적')
etf_max = etf_list[corr_values.argmax()] # argmax()는 corr_values에서 값이 가장 큰 인덱스 값을 반환한다.
etf_mid = etf_list[len(corr_values) // 2]
etf_min = etf_list[corr_values.argmin()] # argmax()는 corr_values에서 값이 가장 작은 인덱스 값을 반환한다.
# 고정으로 추천할 ETF를 리스트에서 제거한다.
etf_list = [etf for etf in etf_list if etf not in (etf_max, etf_mid, etf_min)]
# 고정으로 추천할 ETF를 튜플로 리스트에 저장한다.
fixed_recommendations = [
(etf_max, corr_values[corr_values.argmax()]),
(etf_mid, corr_values[len(corr_values) // 2]),
(etf_min, corr_values[corr_values.argmin()])
]
# user의 투자 성향을 기반으로 추천할 ETF 2개
score_range = 200
ratio = final_score / score_range
# '공격적' 투자자라면 중간 상관계수를 기준으로 앞쪽 ETF를 추천한다.
if final_score > 40:
# 인덱스이기 때문에 값이 정수로 저장되어야 한다. //를 사용하여 '정수 나눗셈'을 수행한다.
# 중간 상관계수 값의 인덱스를 구한 후, 앞서 구한 비율에 중간 상관계수 값의 인덱스를 곱하여 빼준다. 빼주는 이유는 '공격적' 투자자라면 상관계수가 적은 것을 추천해야 하기 때문이다.
index = (len(corr_values) // 2) - int(ratio * (len(corr_values) // 2))
index = max(0, index) # 위에 계산에서 index가 0보다 작아질 수 있으니, 0보다 작다면 인덱스 0을 선택하도록 한다.
etf_dynamic_1 = etf_list[index]
etf_dynamic_2 = etf_list[index - 1 if index - 1 >= 0 else index + 1] # 만약 index-1이 0이거나 0보다 크다면 그대로 값을 쓰고, 원래 index가 0이여서 -1이 되었다면 +1을 해주어 0으로 바꾼다.
# 위에 계산에서 원래 index가 0이여서 etf_dynamic_1와 etf_dynamic_2의 index가 전부 0으로 되어 같은 ETF가 된다면 하나만 출력한다.
if(etf_dynamic_1 == etf_dynamic_2):
dynamic_recommendations = [
(etf_dynamic_1, corr_values[index])
]
else:
dynamic_recommendations = [
(etf_dynamic_1, corr_values[index]),
(etf_dynamic_2, corr_values[index - 1 if index - 1 >= 0 else index + 1])
]
# '중립적' 투자자라면 중간 상관계수 근처에 있는 ETF를 추천한다.
elif -40 <= final_score <= 40:
etf_dynamic_1 = etf_list[len(corr_values) // 2 - 1] # 위에 계산에서 고정적으로 추천할 ETF의 index를 'len(corr_values) // 2'로 계산했기 때문에 -1과 +1을 해주어 중복이 되지 않도록 한다.
etf_dynamic_2 = etf_list[len(corr_values) // 2 + 1]
dynamic_recommendations = [
(etf_dynamic_1, corr_values[len(corr_values) // 2 - 1]),
(etf_dynamic_2, corr_values[len(corr_values) // 2 + 1])
]
# '보수적' 투자자라면 중간 상관계수를 기준으로 뒤쪽에 있는 ETF를 추천한다.
else:
index = (len(corr_values) // 2) + int(abs(ratio) * (len(corr_values) // 2))
index = min(len(corr_values) - 1, index)
etf_dynamic_1 = etf_list[index]
etf_dynamic_2 = etf_list[index + 1 if index + 1 < len(corr_values) else index - 1]
if(etf_dynamic_1 == etf_dynamic_2):
dynamic_recommendations = [
(etf_dynamic_1, corr_values[index]),
]
else:
dynamic_recommendations = [
(etf_dynamic_1, corr_values[index]),
(etf_dynamic_2, corr_values[index + 1 if index + 1 < len(corr_values) else index - 1])
]
# 최종적으로 추천할 ETF 리스트를 return
final_recommendations = fixed_recommendations + dynamic_recommendations
return final_recommendations
# 테스트:
input_stock = 'AAXJ'
recommendations = customized_recommend_stocks("user1", input_stock)
print(recommendations)
- 위에 코드는 사용자의 투자성향과 관련 없이 ‘보수적’, ‘중립적’, ‘공격적’성향에 맞춘 총 3개의 종목과 사용자의 투자성향에 맞춰 총 2개의 종목을 추천한다. 즉, 총 5개의 종목을 추천하는 코드이다.
- 사용자의 투자성향과 관계 없이 각 투자성향에 맞춘 종목 1개 씩은 고정으로 추천