MultiFactor 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,53 @@
1
+ Metadata-Version: 2.4
2
+ Name: MultiFactor
3
+ Version: 0.1.0
4
+ Summary: 멀티팩터 기반 주식 데이터 수집 패키지
5
+ Author-email: HANHO <hanhoman@gmail.com>
6
+ Classifier: Programming Language :: Python :: 3
7
+ Classifier: License :: OSI Approved :: MIT License
8
+ Classifier: Operating System :: OS Independent
9
+ Classifier: Topic :: Office/Business :: Financial :: Investment
10
+ Requires-Python: >=3.8
11
+ Description-Content-Type: text/markdown
12
+ Requires-Dist: FinanceDataReader>=0.9.110
13
+ Requires-Dist: pandas
14
+ Requires-Dist: numpy
15
+
16
+ # MultiFactor
17
+
18
+ `MultiFactor`는 멀티팩터 전략을 구현하고 분석할 수 있도록 돕는 파이썬 라이브러리입니다.
19
+
20
+ ## 주요 기능
21
+ * FinanceDataReader 기반의 빠르고 안정적인 금융 데이터 수집
22
+ * 사용자 맞춤형 팩터(가치, 모멘텀, 퀄리티 등) 점수 산출
23
+ * 직관적인 데이터프레임(DataFrame) 형태의 결과물 반환
24
+
25
+ ## 설치 방법
26
+ ```bash
27
+ pip install MultiFactor
28
+ ```
29
+
30
+
31
+
32
+ ### 테스트 코드
33
+
34
+ import MultiFactor as mf
35
+
36
+ mf = core_real.MultiFactor(N=5)
37
+ mf.get_stockinfo()
38
+
39
+ stock_list = mf.get_stockinfo(dtype='dic')
40
+
41
+ data_mom = mf.get_momentum(stock_list)
42
+ data_val = mf.get_value(stock_list)
43
+ data_fin = mf.get_quality(stock_list)
44
+
45
+ mf.get_momentum_one('005930')
46
+ mf.get_value_one('005930')
47
+ mf.get_quality_one('005930')
48
+
49
+ data_mast = mf.get_score()
50
+ data_mast = mf.get_score_by_data(data_mom, data_val, data_fin)
51
+ mf.get_score_adj_weight(data_mast, weight='가치성장')
52
+
53
+ mf.get_Ngroup(data_mast, Ngroup=2)
@@ -0,0 +1,38 @@
1
+ # MultiFactor
2
+
3
+ `MultiFactor`는 멀티팩터 전략을 구현하고 분석할 수 있도록 돕는 파이썬 라이브러리입니다.
4
+
5
+ ## 주요 기능
6
+ * FinanceDataReader 기반의 빠르고 안정적인 금융 데이터 수집
7
+ * 사용자 맞춤형 팩터(가치, 모멘텀, 퀄리티 등) 점수 산출
8
+ * 직관적인 데이터프레임(DataFrame) 형태의 결과물 반환
9
+
10
+ ## 설치 방법
11
+ ```bash
12
+ pip install MultiFactor
13
+ ```
14
+
15
+
16
+
17
+ ### 테스트 코드
18
+
19
+ import MultiFactor as mf
20
+
21
+ mf = core_real.MultiFactor(N=5)
22
+ mf.get_stockinfo()
23
+
24
+ stock_list = mf.get_stockinfo(dtype='dic')
25
+
26
+ data_mom = mf.get_momentum(stock_list)
27
+ data_val = mf.get_value(stock_list)
28
+ data_fin = mf.get_quality(stock_list)
29
+
30
+ mf.get_momentum_one('005930')
31
+ mf.get_value_one('005930')
32
+ mf.get_quality_one('005930')
33
+
34
+ data_mast = mf.get_score()
35
+ data_mast = mf.get_score_by_data(data_mom, data_val, data_fin)
36
+ mf.get_score_adj_weight(data_mast, weight='가치성장')
37
+
38
+ mf.get_Ngroup(data_mast, Ngroup=2)
@@ -0,0 +1,32 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ # 1. PyPI에 등록될 배포 이름 (pip install MultiFactor)
7
+ name = "MultiFactor"
8
+ version = "0.1.0"
9
+ authors = [
10
+ { name="HANHO", email="hanhoman@gmail.com" },
11
+ ]
12
+ description = "멀티팩터 기반 주식 데이터 수집 패키지"
13
+ readme = "README.md"
14
+ requires-python = ">=3.8"
15
+ classifiers = [
16
+ "Programming Language :: Python :: 3",
17
+ "License :: OSI Approved :: MIT License",
18
+ "Operating System :: OS Independent",
19
+ "Topic :: Office/Business :: Financial :: Investment",
20
+ ]
21
+
22
+
23
+ # 2. 패키지 실행에 필요한 외부 라이브러리 명시
24
+ dependencies = [
25
+ "FinanceDataReader>=0.9.110",
26
+ "pandas",
27
+ "numpy",
28
+ ]
29
+
30
+ # 3. src 폴더 안의 패키지(OpenStock)를 자동으로 찾도록 설정
31
+ [tool.setuptools.packages.find]
32
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,7 @@
1
+ # src/MultiFactor/__init__.py
2
+
3
+ # 같은 폴더의 core.py에서 MultiFactor 클래스를 가져옵니다.
4
+ from .core import MultiFactor
5
+
6
+ # 패키지를 import할 때 노출할 항목을 명시합니다.
7
+ __all__ = ['MultiFactor']
@@ -0,0 +1,157 @@
1
+ #!pip install finance-datareader
2
+
3
+ import FinanceDataReader as fdr
4
+
5
+ import pandas as pd
6
+ import numpy as np
7
+
8
+ import time
9
+
10
+ import stockinfo as sinfo
11
+
12
+ import momentum as mom
13
+ import value as val
14
+ import quality as qual
15
+
16
+ import momentum_one as mom1
17
+ import value_one as val1
18
+ import quality_one as qual1
19
+
20
+ import score as sc
21
+ import score_adj_weight as scw
22
+
23
+
24
+ import warnings
25
+ warnings.filterwarnings('ignore')
26
+
27
+
28
+ class MultiFactor:
29
+ def __init__(self, N: int):
30
+ """
31
+ 멀티팩터
32
+ """
33
+ self.N = N
34
+
35
+
36
+ def get_stockinfo(self, dtype='dataframe') -> pd.DataFrame:
37
+
38
+ df = sinfo.get_stockinfo(self.N)
39
+
40
+ if dtype=='dic':
41
+ # 종목 정보 사전 저장
42
+ stock_list = dict(zip(df['Code'], df['Name']))
43
+ return stock_list
44
+
45
+ else:
46
+ return df
47
+
48
+
49
+
50
+ def get_momentum(self, stock_list) -> pd.DataFrame:
51
+ """
52
+ 모멘텀
53
+ """
54
+ data_mom = mom.get_momentum(stock_list)
55
+ return data_mom
56
+
57
+
58
+ def get_momentum_one(self, scode) -> pd.DataFrame:
59
+ """
60
+ 모멘텀 1개 종목
61
+ """
62
+ return mom1.get_momentum_one(scode)
63
+
64
+ def get_value(self, stock_list) -> pd.DataFrame:
65
+ """
66
+ 밸류
67
+ """
68
+ data_val = val.get_value(stock_list)
69
+ return data_val
70
+
71
+ def get_value_one(self, scode) -> pd.DataFrame:
72
+ """
73
+ 밸류 1개 종목
74
+ """
75
+ return val1.get_value_one(scode)
76
+
77
+ def get_quality(self, stock_list) -> pd.DataFrame:
78
+ """
79
+ 퀄리티
80
+ """
81
+ data_fin = qual.get_quality(stock_list)
82
+ return data_fin
83
+
84
+ def get_quality_one(self, scode) -> pd.DataFrame:
85
+ """
86
+ 퀄리티 1개 종목
87
+ """
88
+ return qual1.get_quality_one(scode)
89
+
90
+
91
+
92
+ def get_score(self) -> pd.DataFrame:
93
+ """
94
+ 스코어 마스터 자동계산
95
+ """
96
+ stock_list = self.get_stockinfo(dtype='dic')
97
+ data_mom = self.get_momentum(stock_list)
98
+ data_val = self.get_value(stock_list)
99
+ data_fin = self.get_quality(stock_list)
100
+
101
+ data_mast = sc.get_score(data_mom, data_val, data_fin)
102
+ return data_mast
103
+
104
+
105
+ def get_score_by_data(self, data_mom, data_val, data_fin) -> pd.DataFrame:
106
+ """
107
+ 스코어 마스터 : 직접 데이터 입력
108
+ """
109
+ data_mast = sc.get_score(data_mom, data_val, data_fin)
110
+ return data_mast
111
+
112
+
113
+ def get_score_adj_weight(self, data_mast, weight='균등') -> pd.DataFrame:
114
+ """
115
+ 스코어 마스터를 받아서, 가중치 주기
116
+ weight : 균등(기본), 가치성장, 추세성장, 역발생
117
+ """
118
+ data_mast = scw.get_score_adj_weight(data_mast, weight)
119
+ return data_mast
120
+
121
+ def get_Ngroup(self, data_mast, Ngroup=10) -> pd.DataFrame:
122
+ """
123
+ 지정한 그룹수(Ngroup)에 따라 종목명만 출력
124
+ Ngroup : 2부터 100 사이값 & data_mast보다 건수 적음
125
+ """
126
+ Ngroup = int(Ngroup)
127
+ if Ngroup > 1 and Ngroup <= 100 and Ngroup < len(data_mast):
128
+
129
+ # 1. 종합순위를 기준으로 오름차순 정렬
130
+ data_mast = data_mast.sort_values(by='종합순위')
131
+
132
+ # 2. 정렬된 데이터프레임을 Ngroup 개수만큼 분할
133
+ chunks = np.array_split(data_mast, Ngroup)
134
+
135
+ # 3. 각 그룹별로 번호와 종목명 출력
136
+ for i, chunk in enumerate(chunks, 1):
137
+ stock_list = ", ".join(chunk['sname'].astype(str).tolist())
138
+ print(f"{i} : {stock_list}")
139
+
140
+ else:
141
+ print("Ngroup를 다시 입력하세요 : 2부터 100 사이값 & data_mast보다 건수 적음")
142
+
143
+
144
+
145
+
146
+
147
+
148
+
149
+
150
+
151
+
152
+
153
+
154
+
155
+
156
+
157
+
@@ -0,0 +1,38 @@
1
+ #!pip install finance-datareader
2
+
3
+ import FinanceDataReader as fdr
4
+
5
+ import pandas as pd
6
+ import numpy as np
7
+
8
+ import stockinfo as sinfo
9
+ import momentum as mom
10
+ import value as val
11
+ import quality as qual
12
+ import score as sc
13
+
14
+
15
+
16
+ # 시가총액 N종목
17
+ df = sinfo.get_stockinfo(N=10)
18
+ #print(df.head())
19
+
20
+ # 종목 정보 사전 저장
21
+ stock_list = dict(zip(df['Code'], df['Name']))
22
+
23
+
24
+ # 모멘텀
25
+ data_mom = mom.get_momentum(stock_list)
26
+ #print(data_mom.tail())
27
+
28
+ # 밸류
29
+ data_val = val.get_value(stock_list)
30
+ #print(data_val.tail())
31
+
32
+ # 퀄리티 => data_qua
33
+ data_fin = qual.get_quality(stock_list)
34
+ #print(data_fin.tail())
35
+
36
+ # 스코어링 => data_qua
37
+ data_mast = sc.get_score(data_mom, data_val, data_fin)
38
+ print(data_mast.head())
@@ -0,0 +1,83 @@
1
+ import FinanceDataReader as fdr
2
+
3
+ import pandas as pd
4
+ import numpy as np
5
+
6
+ from datetime import date
7
+
8
+ def get_momentum(stock_list):
9
+
10
+ # 날짜 : SD 작년 1/2, ED는 오늘
11
+ ED = date.today().strftime('%Y%m%d') # 오늘
12
+
13
+ last_year = date.today().year - 1
14
+ SD = date(last_year, 1, 2).strftime('%Y%m%d') # 작년 1월2일
15
+
16
+
17
+ def get_momentum_one(scode, SD, ED):
18
+ """
19
+ 주가 모멘텀과 거래량 모멘텀 계산
20
+
21
+ 매개변수:
22
+ scode : 종목 코드 (예: '005930')
23
+ SD : 주가데이터 추출시작일 (예: '20250102')
24
+ ED : 주가데이터 추출종료일 (예: '20250131')
25
+
26
+ 반환값:
27
+ avg : 주가 모멘텀 지표 (기간별 수익률 평균)
28
+ vol : 거래량 모멘텀 지표
29
+ """
30
+
31
+ # 1. 주가 및 거래량 데이터 수집 (여기는 에러나면 멈춰서 확인해야 하므로 try-except 제거)
32
+ df = fdr.DataReader(scode, SD, ED)
33
+
34
+ # 2. 주가 모멘텀 지표 생성
35
+ # 단기 (1일, 1주, 1개월) - 데이터가 너무 없으면 에러가 나는 게 맞으므로 그대로 둠
36
+ mom_1d = ( df.iloc[-1]['Close'] / df.iloc[-2]['Close'] - 1 ) * 100 # 전일
37
+ mom_1w = ( df.iloc[-1]['Close'] / df.iloc[-5]['Close'] - 1 ) * 100 # 주간
38
+ mom_1m = ( df.iloc[-1]['Close'] / df.iloc[-20]['Close'] - 1 ) * 100 # 월간
39
+
40
+ # 3개월간 (데이터 부족 시 NaN 처리)
41
+ try:
42
+ mom_3m = ( df.iloc[-1]['Close'] / df.iloc[-60]['Close'] - 1 ) * 100
43
+ except:
44
+ mom_3m = np.nan
45
+
46
+ # 연간 (데이터 부족 시 NaN 처리)
47
+ try:
48
+ mom_1y = ( df.iloc[-1]['Close'] / df.iloc[-240]['Close'] - 1 ) * 100
49
+ except:
50
+ mom_1y = np.nan
51
+
52
+ # 3. 기간별 수익률의 평균 계산 (NaN을 제외하고 평균 산출)
53
+ avg = np.nanmean([mom_1d, mom_1w, mom_1m, mom_3m, mom_1y])
54
+
55
+ # 4. 거래량 모멘텀 지표 생성
56
+ try:
57
+ vol_short = df['Volume'].rolling(window=5).mean().iloc[-1] # 최근 5일
58
+ vol_long = df['Volume'].rolling(window=20).mean().iloc[-1] # 최근 20일
59
+ vol = ( vol_short / vol_long - 1) * 100
60
+ except:
61
+ vol = np.nan
62
+
63
+ return avg, vol
64
+
65
+
66
+ # 모멘텀 지표 계산 후 result에 적재
67
+ result = []
68
+ for scode, sname in stock_list.items():
69
+ try:
70
+ mom, vol = get_momentum_one(scode, SD, ED) # 주가 데이터 수집일(SD, ED) ★★★
71
+ result.append( [ scode, sname, mom, vol ] )
72
+
73
+ except:
74
+ print("추출오류 : ", sname) # 에러 종목코드 출력
75
+ result.append([scode, sname, None, None])
76
+
77
+ # 모멘텀 지표 결과(result)는 데이터프레임(data_mom)으로 저장
78
+ data_mom = pd.DataFrame(result, columns=['scode', 'sname', 'mom_price', 'mom_vol'])
79
+
80
+ # 추출 오류 종목 제외 (최근 상장 종목)
81
+ data_mom = data_mom.dropna().reset_index(drop=True)
82
+
83
+ return data_mom
@@ -0,0 +1,75 @@
1
+ import FinanceDataReader as fdr
2
+
3
+ import pandas as pd
4
+ import numpy as np
5
+ from datetime import date
6
+
7
+ def get_momentum_one(scode):
8
+ """
9
+ 주가 모멘텀과 거래량 모멘텀 계산
10
+
11
+ 매개변수:
12
+ scode : 종목 코드 (예: '005930')
13
+ SD : 주가데이터 추출시작일 (예: '20250102')
14
+ ED : 주가데이터 추출종료일 (예: '20250131')
15
+
16
+ 반환값:
17
+ avg : 주가 모멘텀 지표 (기간별 수익률 평균)
18
+ vol : 거래량 모멘텀 지표
19
+ """
20
+
21
+ # 날짜 : SD 작년 1/2, ED는 오늘
22
+ ED = date.today().strftime('%Y%m%d') # 오늘
23
+
24
+ last_year = date.today().year - 1
25
+ SD = date(last_year, 1, 2).strftime('%Y%m%d') # 작년 1월2일
26
+
27
+
28
+ # 모멘텀 지표 계산 후 result에 적재
29
+ result = []
30
+
31
+ try:
32
+
33
+ # 1. 주가 및 거래량 데이터 수집 (여기는 에러나면 멈춰서 확인해야 하므로 try-except 제거)
34
+ df = fdr.DataReader(scode, SD, ED)
35
+
36
+ # 2. 주가 모멘텀 지표 생성
37
+ # 단기 (1일, 1주, 1개월) - 데이터가 너무 없으면 에러가 나는 게 맞으므로 그대로 둠
38
+ mom_1d = ( df.iloc[-1]['Close'] / df.iloc[-2]['Close'] - 1 ) * 100 # 전일
39
+ mom_1w = ( df.iloc[-1]['Close'] / df.iloc[-5]['Close'] - 1 ) * 100 # 주간
40
+ mom_1m = ( df.iloc[-1]['Close'] / df.iloc[-20]['Close'] - 1 ) * 100 # 월간
41
+
42
+ # 3개월간 (데이터 부족 시 NaN 처리)
43
+ try:
44
+ mom_3m = ( df.iloc[-1]['Close'] / df.iloc[-60]['Close'] - 1 ) * 100
45
+ except:
46
+ mom_3m = np.nan
47
+
48
+ # 연간 (데이터 부족 시 NaN 처리)
49
+ try:
50
+ mom_1y = ( df.iloc[-1]['Close'] / df.iloc[-240]['Close'] - 1 ) * 100
51
+ except:
52
+ mom_1y = np.nan
53
+
54
+ # 3. 기간별 수익률의 평균 계산 (NaN을 제외하고 평균 산출)
55
+ avg = np.nanmean([mom_1d, mom_1w, mom_1m, mom_3m, mom_1y])
56
+
57
+ # 4. 거래량 모멘텀 지표 생성
58
+ try:
59
+ vol_short = df['Volume'].rolling(window=5).mean().iloc[-1] # 최근 5일
60
+ vol_long = df['Volume'].rolling(window=20).mean().iloc[-1] # 최근 20일
61
+ vol = ( vol_short / vol_long - 1) * 100
62
+ except:
63
+ vol = np.nan
64
+
65
+ result.append( [ scode, avg, vol ] )
66
+ data_mom = pd.DataFrame(result, columns=['scode', 'mom_price', 'mom_vol'])
67
+ return data_mom
68
+
69
+ except Exception as e:
70
+ print(f"에러가 발생했습니다. 상세 내용: {e}")
71
+ print("추출오류 : ", scode)
72
+ return None
73
+
74
+
75
+
@@ -0,0 +1,35 @@
1
+ import FinanceDataReader as fdr
2
+
3
+ import pandas as pd
4
+ import numpy as np
5
+
6
+ import time
7
+
8
+
9
+ def get_quality(stock_list):
10
+ result_fin = []
11
+ for i, (scode, sname) in enumerate(stock_list.items()):
12
+
13
+ # [중요] 1. 서버 차단 방지 (1초 대기)
14
+ time.sleep(1)
15
+
16
+ # 진행 상황 모니터링 (50개마다 출력)
17
+ if i % 50 == 0:
18
+ print(f"{i}/{len(stock_list)} 진행 중... ({sname})")
19
+
20
+ try:
21
+ # 재무 데이터 추출 : 분기 K-IFRS 연결 기준
22
+ data = fdr.SnapDataReader(f'NAVER/FINSTATE-2Q/{scode}').dropna(subset='영업이익') # 분기
23
+ fin = (data[['매출액','영업이익','당기순이익']].pct_change()*100).iloc[-1].to_list()
24
+ roe = data['ROE(%)'].iloc[-1]
25
+ result_fin.append([scode] + fin + [roe])
26
+
27
+ except:
28
+ print("추출오류 : ", sname) # 에러 종목코드 출력
29
+ result_fin.append([scode, None, None, None, None])
30
+
31
+
32
+ # ROE와 나머지 지표를 결합하여, 퀄러티 지표 완성
33
+ data_fin = pd.DataFrame(result_fin, columns=['scode', 'revenue_rate', 'oper_income_rate', 'net_income_rate', 'ROE'])
34
+
35
+ return data_fin
@@ -0,0 +1,21 @@
1
+ import FinanceDataReader as fdr
2
+
3
+ import pandas as pd
4
+ import numpy as np
5
+
6
+
7
+ def get_quality_one(scode):
8
+ result_fin = []
9
+ try:
10
+ # 재무 데이터 추출 : 분기 K-IFRS 연결 기준
11
+ data = fdr.SnapDataReader(f'NAVER/FINSTATE-2Q/{scode}').dropna(subset='영업이익') # 분기
12
+ fin = (data[['매출액','영업이익','당기순이익']].pct_change()*100).iloc[-1].to_list()
13
+ roe = data['ROE(%)'].iloc[-1]
14
+ result_fin.append([scode] + fin + [roe])
15
+ data_fin = pd.DataFrame(result_fin, columns=['scode', 'revenue_rate', 'oper_income_rate', 'net_income_rate', 'ROE'])
16
+ return data_fin
17
+
18
+ except Exception as e:
19
+ print(f"에러가 발생했습니다. 상세 내용: {e}")
20
+ print("추출오류 : ", sname) # 에러 종목코드 출력
21
+ return None
@@ -0,0 +1,55 @@
1
+ import pandas as pd
2
+ import numpy as np
3
+
4
+ def get_score(data_mom, data_val, data_fin):
5
+
6
+ ## 종합지표 생성
7
+ # 멀티팩터 마스터 파일 생성
8
+ data_mast = data_mom.merge(data_val, on='scode', how='left')\
9
+ .merge(data_fin, on='scode', how='left')
10
+
11
+
12
+ # 결측 보정
13
+ # 멀티팩터 마스터파일에서 수치형 컬럼명을 추출하여 cols로 저장
14
+ cols = data_mast.select_dtypes(exclude='object').columns
15
+
16
+ # for문으로 cols에 저장된 컬럼명(멀티팩터)별로 반복. 결측치 발생시 중앙값으로 대체
17
+ for col in cols:
18
+ data_mast[col] = data_mast[col].replace([np.inf, -np.inf], np.nan) # 무한대 처리
19
+ data_mast[col] = data_mast[col].fillna(data_mast[col].median()) # 결측치 처리
20
+
21
+ # 종합점수 생성
22
+ # 모멘텀 지표 순위화 : 값이 클수록 우수
23
+ data_mast['모멘텀_주가'] = data_mast['mom_price'].rank(ascending=False, axis=0) / len(data_mast) * 100
24
+ data_mast['모멘텀_거래량'] = data_mast['mom_vol'].rank(ascending=False, axis=0) / len(data_mast) * 100
25
+
26
+ # 밸류 지표 순위화 : 값이 작을수록 우수
27
+ data_mast['밸류_PER'] = data_mast['PER'].rank(ascending=True, axis=0) / len(data_mast) * 100
28
+ data_mast['밸류_PBR'] = data_mast['PBR'].rank(ascending=True, axis=0) / len(data_mast) * 100
29
+ data_mast['퀄러티_ROE'] = data_mast['ROE'].rank(ascending=False, axis=0) / len(data_mast) * 100
30
+
31
+ # 퀄러티 지표 순위화 : 값이 클수록 우수
32
+ data_mast['퀄러티_ROE'] = data_mast['ROE'].rank(ascending=False, axis=0) / len(data_mast) * 100
33
+ data_mast['퀄러티_매출증가'] = data_mast['revenue_rate'].rank(ascending=False, axis=0) / len(data_mast) * 100
34
+ data_mast['퀄러티_영업이익증가'] = data_mast['oper_income_rate'].rank(ascending=False, axis=0) / len(data_mast) * 100
35
+ data_mast['퀄러티_순이익증가'] = data_mast['net_income_rate'].rank(ascending=False, axis=0) / len(data_mast) * 100
36
+
37
+ # 멀티팩터 종합점수 계산 컬럼 정의
38
+ cols = ['모멘텀_주가', '모멘텀_거래량', '밸류_PER', '밸류_PBR', '퀄러티_ROE', '퀄러티_매출증가', '퀄러티_영업이익증가', '퀄러티_순이익증가']
39
+
40
+ # 종합점수 계산 (평균)
41
+ data_mast['종합점수'] = np.average( data_mast[cols], axis=1 )
42
+
43
+ # 종합순위 계산
44
+ data_mast['종합순위'] = data_mast['종합점수'].rank(ascending=True, axis=0)
45
+
46
+ # 종합순위 퍼센트 계산
47
+ data_mast['종합순위_퍼센트'] = data_mast['종합순위'] / len(data_mast) * 100
48
+
49
+ # 종합순위 상위 순으로 정렬
50
+ data_mast.sort_values(by='종합순위', inplace=True)
51
+
52
+ # 화면에 종합순위 상위 10개 종목 출력 (T옵션으로 행과 열을 교환하여 출력)
53
+ #data_mast.head(10).T
54
+
55
+ return data_mast
@@ -0,0 +1,57 @@
1
+ import pandas as pd
2
+ import numpy as np
3
+
4
+ def get_score_adj_weight(data_mast, weight='균등'):
5
+
6
+ def score_regenerate(data_mast, cols, df_w):
7
+ # 가중치 적용을 위한 멀티팩터 마스터(data_mast)의 복사본(dat_weight) 생성
8
+ data_weight = data_mast.copy()
9
+
10
+ # 가중치를 적용하여 종합점수 생성
11
+ data_weight['종합점수'] = np.average( data_weight[cols], axis=1, weights=df_w['weights'])
12
+
13
+ # 종합순위 및 종합순위 퍼센트 생성
14
+ data_weight['종합순위'] = data_weight['종합점수'].rank(ascending=True, axis=0)
15
+ data_weight['종합순위_퍼센트'] = data_weight['종합순위'] / len(data_weight) * 100
16
+
17
+ # 종합순위 순으로 정렬
18
+ data_weight.sort_values(by='종합순위', inplace=True)
19
+
20
+ return data_weight
21
+
22
+
23
+
24
+ # 가중치 벡터 초기화
25
+ cols = ['모멘텀_주가', '모멘텀_거래량', '밸류_PER', '밸류_PBR', '퀄러티_ROE', '퀄러티_매출증가', '퀄러티_영업이익증가', '퀄러티_순이익증가']
26
+ df_w = pd.DataFrame()
27
+ df_w['cols'] = cols
28
+
29
+
30
+ # ① 가치 성장 전략 (밸류 + 퀄리티 조합)
31
+ if weight == '가치성장':
32
+ df_w['weights'] = [0/8, 0/8, 1/8, 1/8, 1/8, 1/8, 1/8, 1/8]
33
+ data_mast = score_regenerate(data_mast, cols, df_w)
34
+ return data_mast
35
+
36
+ # ② 추세 성장 전략 (모멘텀 + 퀄리티 조합)
37
+ elif weight == '추세성장':
38
+ df_w['weights'] = [0/8, 0/8, 1/8, 1/8, 1/8, 1/8, 1/8, 1/8]
39
+ data_mast = score_regenerate(data_mast, cols, df_w)
40
+ return data_mast
41
+
42
+ # ③ 역발상 전략 (밸류 + 모멘텀 조합)
43
+ elif weight == '역발상':
44
+ df_w['weights'] = [1/8, 1/8, 1/8, 1/8, 0/8, 0/8, 0/8, 0/8]
45
+ data_mast = score_regenerate(data_mast, cols, df_w)
46
+ return data_mast
47
+
48
+ # ③ 균등
49
+ elif weight == '균등':
50
+ df_w['weights'] = [1/8, 1/8, 1/8, 1/8, 1/8, 1/8, 1/8, 1/8]
51
+ data_mast = score_regenerate(data_mast, cols, df_w)
52
+ return data_mast
53
+
54
+ else:
55
+ print("(옵션입력 오류) 다음 옵션 중에 하나를 입력하세요. : 가치성장,추세성장,역발상,균등")
56
+ return None
57
+
@@ -0,0 +1,42 @@
1
+ import FinanceDataReader as fdr
2
+
3
+ import pandas as pd
4
+ import numpy as np
5
+
6
+
7
+ def get_stockinfo(N=10):
8
+ """
9
+ 종목정보 추출
10
+ """
11
+ # 거래소 전체 추출 (코스피, 코스닥 대상)
12
+ df = fdr.StockListing('KRX').query("Market in ['KOSPI', 'KOSDAQ']")
13
+
14
+ # 종목별 종가(Close) 문자형에서 수치형(integer)으로 변환
15
+ df['Close'] = df['Close'].astype(int)
16
+
17
+ # 제외조건
18
+ # 소형주: 시가총액 1,000억 원 미만 종목 제외 적용
19
+ df = df.query(" Marcap >= 100000000000 ")
20
+
21
+ # 저유동성 : 일 거래량 2,000주 미만 제외 적용
22
+ df = df.query(" Volume >= 2000 ")
23
+
24
+ # 동전주: 높은 변동성 위험을 고려하여 주가 1,000원 미만 종목 제외 적용
25
+ df = df.query(" Close >= 1000 ")
26
+
27
+ # 우선주: 낮은 거래량과 본주와의 주가 차이(괴리율)로 인한 분석의 한계로 제외 (종목코드 끝자리 5,7,9,K) 적용
28
+ df = df[ df['Code'].apply(lambda x: False if x[-1] in ['5','7','9','K'] else True) ]
29
+
30
+ # 스팩(SPAC), 리츠, 벤처투자 등 : 재무 데이터의 실효성이 없어 제외 적용
31
+ df = df[ ~df['Name'].str.contains('스팩|리츠|리얼티|인프라|유전|벤처') ]
32
+
33
+ # 시가총액 N종목 추출
34
+ df = df.sort_values(by='Marcap', ascending=False).iloc[:N]
35
+
36
+ # 데이터프레임 인덱스 초기화
37
+ df = df.reset_index(drop=True)
38
+
39
+ # 최종 종목 추출 건수 출력
40
+ print("종목 추출 건수 ", len(df))
41
+
42
+ return df
@@ -0,0 +1,34 @@
1
+ import FinanceDataReader as fdr
2
+
3
+ import pandas as pd
4
+ import numpy as np
5
+
6
+ import time
7
+
8
+ def get_value(stock_list):
9
+
10
+ result = []
11
+
12
+ for i, (scode, sname) in enumerate(stock_list.items()):
13
+
14
+ # [중요] 데이터 수집 시 서버 차단 방지 (1초 대기)
15
+ time.sleep(1)
16
+
17
+ # 진행 상황 모니터링 (50번째 순서마다 종목명 출력)
18
+ if i % 50 == 0:
19
+ print(f"{i}/{len(stock_list)} 진행 중... ({sname})")
20
+
21
+ try:
22
+ # 재무제표 데이터 추출 (연간 K-IFRS 연결 기준)
23
+ data = fdr.SnapDataReader(f'NAVER/FINSTATE-2Y/{scode}').dropna(subset='영업이익') # 연간
24
+ per, pbr = data['PER(배)'].iloc[-1], data['PBR(배)'].iloc[-1]
25
+ result.append([scode, per, pbr])
26
+
27
+ except:
28
+ print("추출오류 : ", sname) # 에러 종목코드 출력
29
+ result.append([scode, None, None])
30
+
31
+
32
+ data_val = pd.DataFrame(result, columns=['scode', 'PER', 'PBR'])
33
+
34
+ return data_val
@@ -0,0 +1,22 @@
1
+ import FinanceDataReader as fdr
2
+
3
+ import pandas as pd
4
+ import numpy as np
5
+
6
+
7
+ def get_value_one(scode):
8
+
9
+ result = []
10
+
11
+ try:
12
+ # 재무제표 데이터 추출 (연간 K-IFRS 연결 기준)
13
+ data = fdr.SnapDataReader(f'NAVER/FINSTATE-2Y/{scode}').dropna(subset='영업이익') # 연간
14
+ per, pbr = data['PER(배)'].iloc[-1], data['PBR(배)'].iloc[-1]
15
+ result.append([scode, per, pbr])
16
+ data_val = pd.DataFrame(result, columns=['scode', 'PER', 'PBR'])
17
+ return data_val
18
+
19
+ except Exception as e:
20
+ print(f"에러가 발생했습니다. 상세 내용: {e}")
21
+ print("추출오류 : ", sname) # 에러 종목코드 출력
22
+ return None
@@ -0,0 +1,53 @@
1
+ Metadata-Version: 2.4
2
+ Name: MultiFactor
3
+ Version: 0.1.0
4
+ Summary: 멀티팩터 기반 주식 데이터 수집 패키지
5
+ Author-email: HANHO <hanhoman@gmail.com>
6
+ Classifier: Programming Language :: Python :: 3
7
+ Classifier: License :: OSI Approved :: MIT License
8
+ Classifier: Operating System :: OS Independent
9
+ Classifier: Topic :: Office/Business :: Financial :: Investment
10
+ Requires-Python: >=3.8
11
+ Description-Content-Type: text/markdown
12
+ Requires-Dist: FinanceDataReader>=0.9.110
13
+ Requires-Dist: pandas
14
+ Requires-Dist: numpy
15
+
16
+ # MultiFactor
17
+
18
+ `MultiFactor`는 멀티팩터 전략을 구현하고 분석할 수 있도록 돕는 파이썬 라이브러리입니다.
19
+
20
+ ## 주요 기능
21
+ * FinanceDataReader 기반의 빠르고 안정적인 금융 데이터 수집
22
+ * 사용자 맞춤형 팩터(가치, 모멘텀, 퀄리티 등) 점수 산출
23
+ * 직관적인 데이터프레임(DataFrame) 형태의 결과물 반환
24
+
25
+ ## 설치 방법
26
+ ```bash
27
+ pip install MultiFactor
28
+ ```
29
+
30
+
31
+
32
+ ### 테스트 코드
33
+
34
+ import MultiFactor as mf
35
+
36
+ mf = core_real.MultiFactor(N=5)
37
+ mf.get_stockinfo()
38
+
39
+ stock_list = mf.get_stockinfo(dtype='dic')
40
+
41
+ data_mom = mf.get_momentum(stock_list)
42
+ data_val = mf.get_value(stock_list)
43
+ data_fin = mf.get_quality(stock_list)
44
+
45
+ mf.get_momentum_one('005930')
46
+ mf.get_value_one('005930')
47
+ mf.get_quality_one('005930')
48
+
49
+ data_mast = mf.get_score()
50
+ data_mast = mf.get_score_by_data(data_mom, data_val, data_fin)
51
+ mf.get_score_adj_weight(data_mast, weight='가치성장')
52
+
53
+ mf.get_Ngroup(data_mast, Ngroup=2)
@@ -0,0 +1,19 @@
1
+ README.md
2
+ pyproject.toml
3
+ src/MultiFactor/__init_.py
4
+ src/MultiFactor/core.py
5
+ src/MultiFactor/core_backup.py
6
+ src/MultiFactor/momentum.py
7
+ src/MultiFactor/momentum_one.py
8
+ src/MultiFactor/quality.py
9
+ src/MultiFactor/quality_one.py
10
+ src/MultiFactor/score.py
11
+ src/MultiFactor/score_adj_weight.py
12
+ src/MultiFactor/stockinfo.py
13
+ src/MultiFactor/value.py
14
+ src/MultiFactor/value_one.py
15
+ src/MultiFactor.egg-info/PKG-INFO
16
+ src/MultiFactor.egg-info/SOURCES.txt
17
+ src/MultiFactor.egg-info/dependency_links.txt
18
+ src/MultiFactor.egg-info/requires.txt
19
+ src/MultiFactor.egg-info/top_level.txt
@@ -0,0 +1,3 @@
1
+ FinanceDataReader>=0.9.110
2
+ pandas
3
+ numpy
@@ -0,0 +1 @@
1
+ MultiFactor