personal activity/stock

sector] 종목 순위 정의 하기 #2

ToolBOX01 2025. 9. 16. 14:26
반응형

섹터(Sector)

주식 시장에서 "섹터(Sector)"는 유사한 사업 활동이나 산업 특성을 가진 기업들을 그룹화한 분류 체계를 의미합니다. 이는 투자자들이 특정 산업의 트렌드나 경제 흐름을 분석하고, 포트폴리오를 효율적으로 구성하기 위한 기준으로 활용됩니다.

▶ 섹터 구분의 목적

  • 산업 트렌드 분석: 
    특정 섹터의 성과는 경제 상황과 밀접하게 연관되므로, 투자자들은 섹터별 성과를 비교해 시장 흐름을 파악합니다.
    (예: 경기 침체기에는 필수 소비재 섹터가 안정적일 수 있음)
  • 위험 분산:
    다양한 섹터에 투자해 특정 산업의 변동성으로부터 포트폴리오를 보호합니다.
  • 전략적 투자: 
    성장 잠재력이 높은 섹터(예: 기술, 재생에너지)에 집중하거나 방어적 섹터(예: 유틸리티)를 활용해 전략을 수립합니다.

업종(Industry): 섹터보다 세부적인 분류 단위 입니다. (예: "반도체"는 기술 섹터 내 하위 업종). KRX는 공식적으로 정보데이터시스템을 통해 업종 분류 현황 데이터를 CSV/Excel 형식으로 무료 제공하며, 이를 파이썬으로 자동 수집할 수 있습니다.

종목코드 종목명 업종 산업
005930 삼성전자 전자제품 반도체
000660 SK하이닉스 전자제품 반도체
035420 NAVER 정보통신 인터넷 서비스

업종 분류 파이썬 코드....

더보기

import pandas as pd
import requests
from io import BytesIO
from datetime import datetime

def get_recent_biz_day():
    """최근 영업일 구하기 (네이버 금융에서 스크랩)"""
    url = 'https://finance.naver.com/sise/sise_deposit.nhn'
    headers = {'User-Agent': 'Mozilla/5.0'}
    response = requests.get(url, headers=headers)
    # HTML 파싱으로 최근 영업일 추출 (간단히 구현)
    tables = pd.read_html(response.text, encoding='euc-kr')
    biz_day = tables[0].iloc[0, 0].replace('.', '')  # 예: '20250916'
    return biz_day

def download_krx_sector(biz_day):
    """KRX 업종 분류 데이터 다운로드"""
    gen_otp_url = 'http://data.krx.co.kr/comm/fileDn/GenerateOTP/generate.cmd'
    down_url = 'http://data.krx.co.kr/comm/fileDn/download_csv/download.cmd'
    
    gen_otp_data = {
        'mktId': 'STK',  # 주식 시장
        'trdDd': biz_day,  # 거래일 (최근 영업일)
        'money': '1',
        'csvxls_isNo': 'false',
        'name': 'fileDown',
        'url': 'dbms/MDC/STAT/standard/MDCSTAT03901'  # 업종분류 현황 URL 코드
    }
    
    headers = {
        'Referer': 'http://data.krx.co.kr/contents/MDC/MDI/mdiLoader',
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
    }
    
    # 1. OTP 생성
    otp = requests.post(gen_otp_url, gen_otp_data, headers=headers).text.strip()
    
    # 2. CSV 다운로드
    down_data = {
        'code': otp,
        'bld': 'dbms/MDC/STAT/standard/MDCSTAT03901',  # 업종분류 빌드 코드
        'locale': 'ko_KR'
    }
    df = pd.read_csv(BytesIO(requests.post(down_url, down_data, headers=headers).content), 
                     encoding='euc-kr')
    return df

# 실행 예시
biz_day = get_recent_biz_day()  # 20250916 (현재 날짜 기준)
df_sector = download_krx_sector(biz_day)
print(df_sector.head())  # 종목코드, 종목명, 업종, 산업 등 출력

# 저장
df_sector.to_csv('krx_sector.csv', index=False, encoding='utf-8-sig')


 섹터(Sector) 랭킹 프로그램

sector와 ticker를 이용하여 EPS,PER,BPS,PBR,ROE 값을 가져오고, 이것을 낮은 값을 높은 값 순서로 점수를 넣고, 이것들의 합산을 하여, 제일 높은 점수 부터  표시하는 기능을 만들었습니다.

프로그램 실행 화면

파이썬 프로그램 코드

import sys
import pandas as pd
from PyQt6.QtWidgets import QApplication, QMainWindow, QTableWidgetItem
from PyQt6 import uic

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        # .ui 파일 직접 로드
        uic.loadUi('stock.ui', self)  # stock.ui 파일 경로
        self.load_sectors()
        # 콤보박스 선택 이벤트 연결
        self.comboBoxsector.currentTextChanged.connect(self.update_ticker_table)
        # pushButtonscore 클릭 이벤트 연결
        self.pushButtonscore.clicked.connect(self.transfer_to_score_table)
        # pushButtonRanking 클릭 이벤트 연결 (새로 추가)
        self.pushButtonRanking.clicked.connect(self.calculate_total_and_sort)

    def load_sectors(self):
        try:
            df = pd.read_excel('ticker.xlsx', sheet_name='tickersheet')
            sectors = df.iloc[1:, 2].dropna().unique().tolist()  # C열(C2 아래) 데이터
            self.comboBoxsector.addItems(sectors)
            # 엑셀 데이터를 클래스 변수로 저장
            self.df = df
        except Exception as e:
            print(f"Error loading sectors: {e}")

    def update_ticker_table(self, selected_sector):
        try:
            # 테이블 위젯 초기화
            self.tableWidgetsetor.setRowCount(0)
            # 선택된 섹터에 해당하는 데이터 필터링
            filtered_df = self.df[self.df.iloc[:, 2] == selected_sector]
            # A열(티커)이 비어 있지 않은 행만 선택
            filtered_df = filtered_df[filtered_df.iloc[:, 0].notna()]
            # A, B, C, D, E, F, G, H열 데이터 추출
            data = filtered_df.iloc[:, [0, 1, 2, 3, 4, 5, 6, 7]].values.tolist()
            # 테이블 위젯에 데이터 추가
            self.tableWidgetsetor.setRowCount(len(data))
            for row, row_data in enumerate(data):
                for col, value in enumerate(row_data):
                    # 비어 있는 값은 "N/A"로 표시, 숫자는 그대로
                    cell_value = "N/A" if pd.isna(value) else str(value)
                    self.tableWidgetsetor.setItem(row, col, QTableWidgetItem(cell_value))
        except Exception as e:
            print(f"Error updating ticker table: {e}")

    def transfer_to_score_table(self):
        try:
            # tableWidgetscore 초기화
            self.tableWidgetscore.setRowCount(0)
            
            # tableWidgetsetor의 모든 행 데이터 가져오기
            row_count = self.tableWidgetsetor.rowCount()
            if row_count == 0:
                print("No data in tableWidgetsetor")
                return
            
            # ticker(0열), name(1열), EPS(3열), PER(4열), BPS(5열), PBR(6열), ROE(7열) 데이터 추출
            data = []
            for row in range(row_count):
                ticker = self.tableWidgetsetor.item(row, 0)
                name = self.tableWidgetsetor.item(row, 1)
                eps = self.tableWidgetsetor.item(row, 3)
                per = self.tableWidgetsetor.item(row, 4)
                bps = self.tableWidgetsetor.item(row, 5)
                pbr = self.tableWidgetsetor.item(row, 6)
                roe = self.tableWidgetsetor.item(row, 7)
                
                ticker_value = ticker.text() if ticker else "N/A"
                name_value = name.text() if name else "N/A"
                
                # EPS, PER, BPS, PBR, ROE 값 처리 ("N/A" 또는 빈 값은 0.0으로)
                def convert_to_float(value_item):
                    value = value_item.text() if value_item else "N/A"
                    if value == "N/A" or value == "":
                        return 0.0
                    try:
                        return float(value)
                    except (ValueError, TypeError):
                        return 0.0
                
                eps_numeric = convert_to_float(eps)
                per_numeric = convert_to_float(per)
                bps_numeric = convert_to_float(bps)
                pbr_numeric = convert_to_float(pbr)
                roe_numeric = convert_to_float(roe)
                
                data.append([ticker_value, name_value, eps_numeric, per_numeric, bps_numeric, pbr_numeric, roe_numeric])
            
            # 각 지표별로 점수 부여
            # EPS Score
            eps_sorted = sorted(enumerate(data), key=lambda x: x[1][2])  # EPS 기준 정렬
            eps_scores = [0] * len(data)
            score = 1
            for idx, _ in eps_sorted:
                eps_scores[idx] = score
                score += 1
            
            # PER Score
            per_sorted = sorted(enumerate(data), key=lambda x: x[1][3])  # PER 기준 정렬
            per_scores = [0] * len(data)
            score = 1
            for idx, _ in per_sorted:
                per_scores[idx] = score
                score += 1
            
            # BPS Score
            bps_sorted = sorted(enumerate(data), key=lambda x: x[1][4])  # BPS 기준 정렬
            bps_scores = [0] * len(data)
            score = 1
            for idx, _ in bps_sorted:
                bps_scores[idx] = score
                score += 1
            
            # PBR Score
            pbr_sorted = sorted(enumerate(data), key=lambda x: x[1][5])  # PBR 기준 정렬
            pbr_scores = [0] * len(data)
            score = 1
            for idx, _ in pbr_sorted:
                pbr_scores[idx] = score
                score += 1
            
            # ROE Score
            roe_sorted = sorted(enumerate(data), key=lambda x: x[1][6])  # ROE 기준 정렬
            roe_scores = [0] * len(data)
            score = 1
            for idx, _ in roe_sorted:
                roe_scores[idx] = score
                score += 1
            
            # tableWidgetscore에 데이터 추가
            self.tableWidgetscore.setRowCount(len(data))
            self.tableWidgetscore.setColumnCount(7)  # Ticker, Name, EPS Score, PER Score, BPS Score, PBR Score, ROE Score
            self.tableWidgetscore.setHorizontalHeaderLabels(["Ticker", "Name", "EPS Score", "PER Score", "BPS Score", "PBR Score", "ROE Score"])
            for row, (ticker, name, _, _, _, _, _) in enumerate(data):
                self.tableWidgetscore.setItem(row, 0, QTableWidgetItem(ticker))
                self.tableWidgetscore.setItem(row, 1, QTableWidgetItem(name))
                self.tableWidgetscore.setItem(row, 2, QTableWidgetItem(str(eps_scores[row])))
                self.tableWidgetscore.setItem(row, 3, QTableWidgetItem(str(per_scores[row])))
                self.tableWidgetscore.setItem(row, 4, QTableWidgetItem(str(bps_scores[row])))
                self.tableWidgetscore.setItem(row, 5, QTableWidgetItem(str(pbr_scores[row])))
                self.tableWidgetscore.setItem(row, 6, QTableWidgetItem(str(roe_scores[row])))
            
            # 열 너비 자동 조정
            self.tableWidgetscore.resizeColumnsToContents()
        except Exception as e:
            print(f"Error transferring to score table: {e}")

    def calculate_total_and_sort(self):
        try:
            # tableWidgetscore의 현재 행 수 확인
            row_count = self.tableWidgetscore.rowCount()
            if row_count == 0:
                print("No data in tableWidgetscore")
                return
            
            # 데이터 추출: Ticker(0), Name(1), EPS Score(2), PER Score(3), BPS Score(4), PBR Score(5), ROE Score(6)
            data = []
            for row in range(row_count):
                ticker = self.tableWidgetscore.item(row, 0).text() if self.tableWidgetscore.item(row, 0) else "N/A"
                name = self.tableWidgetscore.item(row, 1).text() if self.tableWidgetscore.item(row, 1) else "N/A"
                eps_score = int(self.tableWidgetscore.item(row, 2).text()) if self.tableWidgetscore.item(row, 2) else 0
                per_score = int(self.tableWidgetscore.item(row, 3).text()) if self.tableWidgetscore.item(row, 3) else 0
                bps_score = int(self.tableWidgetscore.item(row, 4).text()) if self.tableWidgetscore.item(row, 4) else 0
                pbr_score = int(self.tableWidgetscore.item(row, 5).text()) if self.tableWidgetscore.item(row, 5) else 0
                roe_score = int(self.tableWidgetscore.item(row, 6).text()) if self.tableWidgetscore.item(row, 6) else 0
                
                # Total Score 계산
                total_score = eps_score + per_score + bps_score + pbr_score + roe_score
                
                data.append([ticker, name, eps_score, per_score, bps_score, pbr_score, roe_score, total_score])
            
            # Total Score 기준으로 내림차순 정렬 (큰 숫자부터)
            data.sort(key=lambda x: x[7], reverse=True)
            
            # tableWidgetscore 업데이트: 열 수를 8로 늘림
            self.tableWidgetscore.setColumnCount(8)
            self.tableWidgetscore.setHorizontalHeaderLabels(["Ticker", "Name", "EPS Score", "PER Score", "BPS Score", "PBR Score", "ROE Score", "Total Score"])
            
            # 정렬된 데이터로 행 재설정
            self.tableWidgetscore.setRowCount(len(data))
            for row, row_data in enumerate(data):
                self.tableWidgetscore.setItem(row, 0, QTableWidgetItem(row_data[0]))
                self.tableWidgetscore.setItem(row, 1, QTableWidgetItem(row_data[1]))
                self.tableWidgetscore.setItem(row, 2, QTableWidgetItem(str(row_data[2])))
                self.tableWidgetscore.setItem(row, 3, QTableWidgetItem(str(row_data[3])))
                self.tableWidgetscore.setItem(row, 4, QTableWidgetItem(str(row_data[4])))
                self.tableWidgetscore.setItem(row, 5, QTableWidgetItem(str(row_data[5])))
                self.tableWidgetscore.setItem(row, 6, QTableWidgetItem(str(row_data[6])))
                self.tableWidgetscore.setItem(row, 7, QTableWidgetItem(str(row_data[7])))
            
            # 열 너비 자동 조정
            self.tableWidgetscore.resizeColumnsToContents()
        except Exception as e:
            print(f"Error calculating total and sorting: {e}")

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec())

엑셀파일 다운로드

ticker.xlsx
0.19MB

UI 파일 다운로드 ( Qt Designer 6 버전 사용)

stock.ui
0.00MB

파이썬 파일 다운로드

Ranking_Results.py
0.01MB

위 프로그램은 AI를 이용하여 생성 하였습니다. HTML, CSS에 대한 기본 지식이 있으면 만들수 있습니다.
Qt Designer의 objectname을 정의 할수 있어야 합니다. 이것이면 충분히 프로그램 개발이 가능 합니다.

by korealionkk@gmail.com


반응형