recommendation.py

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)