MultiFactor 0.1.7__tar.gz → 0.2.1__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.
- {multifactor-0.1.7 → multifactor-0.2.1}/PKG-INFO +1 -1
- {multifactor-0.1.7 → multifactor-0.2.1}/pyproject.toml +1 -1
- multifactor-0.2.1/src/MultiFactor/__init__.py +8 -0
- {multifactor-0.1.7 → multifactor-0.2.1}/src/MultiFactor/core.py +168 -168
- {multifactor-0.1.7 → multifactor-0.2.1}/src/MultiFactor/momentum.py +82 -82
- {multifactor-0.1.7 → multifactor-0.2.1}/src/MultiFactor/momentum_one.py +74 -74
- {multifactor-0.1.7 → multifactor-0.2.1}/src/MultiFactor/quality.py +35 -35
- {multifactor-0.1.7 → multifactor-0.2.1}/src/MultiFactor/quality_one.py +20 -20
- {multifactor-0.1.7 → multifactor-0.2.1}/src/MultiFactor/score.py +54 -54
- {multifactor-0.1.7 → multifactor-0.2.1}/src/MultiFactor/score_adj_weight.py +57 -57
- {multifactor-0.1.7 → multifactor-0.2.1}/src/MultiFactor/stockinfo.py +41 -41
- multifactor-0.2.1/src/MultiFactor/us_core.py +108 -0
- multifactor-0.2.1/src/MultiFactor/us_momentum.py +78 -0
- multifactor-0.2.1/src/MultiFactor/us_quality.py +42 -0
- multifactor-0.2.1/src/MultiFactor/us_stockinfo.py +49 -0
- multifactor-0.2.1/src/MultiFactor/us_value.py +39 -0
- {multifactor-0.1.7 → multifactor-0.2.1}/src/MultiFactor/value.py +33 -33
- {multifactor-0.1.7 → multifactor-0.2.1}/src/MultiFactor/value_one.py +22 -22
- {multifactor-0.1.7 → multifactor-0.2.1}/src/MultiFactor.egg-info/PKG-INFO +1 -1
- {multifactor-0.1.7 → multifactor-0.2.1}/src/MultiFactor.egg-info/SOURCES.txt +5 -0
- multifactor-0.1.7/src/MultiFactor/__init__.py +0 -7
- {multifactor-0.1.7 → multifactor-0.2.1}/README.md +0 -0
- {multifactor-0.1.7 → multifactor-0.2.1}/setup.cfg +0 -0
- {multifactor-0.1.7 → multifactor-0.2.1}/src/MultiFactor.egg-info/dependency_links.txt +0 -0
- {multifactor-0.1.7 → multifactor-0.2.1}/src/MultiFactor.egg-info/requires.txt +0 -0
- {multifactor-0.1.7 → multifactor-0.2.1}/src/MultiFactor.egg-info/top_level.txt +0 -0
|
@@ -1,168 +1,168 @@
|
|
|
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
|
-
from . import stockinfo as sinfo
|
|
11
|
-
|
|
12
|
-
from . import momentum as mom
|
|
13
|
-
from . import value as val
|
|
14
|
-
from . import quality as qual
|
|
15
|
-
|
|
16
|
-
from . import momentum_one as mom1
|
|
17
|
-
from . import value_one as val1
|
|
18
|
-
from . import quality_one as qual1
|
|
19
|
-
|
|
20
|
-
from . import score as sc
|
|
21
|
-
from . import score_adj_weight as scw
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
class MultiFactor:
|
|
26
|
-
def __init__(self, N: int):
|
|
27
|
-
"""
|
|
28
|
-
멀티팩터 : 분석 대상 종목수 (시가총액 상위 N개)
|
|
29
|
-
"""
|
|
30
|
-
self.N = N
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
def get_stockinfo(self, dtype='dataframe') -> pd.DataFrame:
|
|
34
|
-
|
|
35
|
-
df = sinfo.get_stockinfo(self.N)
|
|
36
|
-
|
|
37
|
-
if dtype=='dic':
|
|
38
|
-
# 종목 정보 사전 저장
|
|
39
|
-
stock_list = dict(zip(df['Code'], df['Name']))
|
|
40
|
-
return stock_list
|
|
41
|
-
|
|
42
|
-
else:
|
|
43
|
-
return df
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
def get_momentum(self, stock_list) -> pd.DataFrame:
|
|
48
|
-
"""
|
|
49
|
-
모멘텀
|
|
50
|
-
"""
|
|
51
|
-
data_mom = mom.get_momentum(stock_list)
|
|
52
|
-
return data_mom
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
def get_momentum_one(self, scode) -> pd.DataFrame:
|
|
56
|
-
"""
|
|
57
|
-
모멘텀 1개 종목
|
|
58
|
-
"""
|
|
59
|
-
return mom1.get_momentum_one(scode)
|
|
60
|
-
|
|
61
|
-
def get_value(self, stock_list) -> pd.DataFrame:
|
|
62
|
-
"""
|
|
63
|
-
밸류
|
|
64
|
-
"""
|
|
65
|
-
data_val = val.get_value(stock_list)
|
|
66
|
-
return data_val
|
|
67
|
-
|
|
68
|
-
def get_value_one(self, scode) -> pd.DataFrame:
|
|
69
|
-
"""
|
|
70
|
-
밸류 1개 종목
|
|
71
|
-
"""
|
|
72
|
-
return val1.get_value_one(scode)
|
|
73
|
-
|
|
74
|
-
def get_quality(self, stock_list) -> pd.DataFrame:
|
|
75
|
-
"""
|
|
76
|
-
퀄리티
|
|
77
|
-
"""
|
|
78
|
-
data_fin = qual.get_quality(stock_list)
|
|
79
|
-
return data_fin
|
|
80
|
-
|
|
81
|
-
def get_quality_one(self, scode) -> pd.DataFrame:
|
|
82
|
-
"""
|
|
83
|
-
퀄리티 1개 종목
|
|
84
|
-
"""
|
|
85
|
-
return qual1.get_quality_one(scode)
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
def get_score(self) -> pd.DataFrame:
|
|
90
|
-
"""
|
|
91
|
-
스코어 마스터 자동계산
|
|
92
|
-
"""
|
|
93
|
-
print("(1/5) 종목코드 생성")
|
|
94
|
-
stock_list = self.get_stockinfo(dtype='dic')
|
|
95
|
-
|
|
96
|
-
print("\n(2/5) 모멘텀 지표 생성")
|
|
97
|
-
data_mom = self.get_momentum(stock_list)
|
|
98
|
-
|
|
99
|
-
print("\n(3/5) 밸류 지표 생성")
|
|
100
|
-
data_val = self.get_value(stock_list)
|
|
101
|
-
|
|
102
|
-
print("\n(4/5) 퀄리티 지표 생성")
|
|
103
|
-
data_fin = self.get_quality(stock_list)
|
|
104
|
-
|
|
105
|
-
print("\n(5/5) 멀티팩터 마스터 생성")
|
|
106
|
-
data_mast = sc.get_score(data_mom, data_val, data_fin)
|
|
107
|
-
|
|
108
|
-
data_mast.reset_index(drop=True, inplace=True)
|
|
109
|
-
|
|
110
|
-
return data_mast
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
def get_score_by_data(self, data_mom, data_val, data_fin) -> pd.DataFrame:
|
|
114
|
-
"""
|
|
115
|
-
스코어 마스터 : 직접 데이터 입력
|
|
116
|
-
"""
|
|
117
|
-
data_mast = sc.get_score(data_mom, data_val, data_fin)
|
|
118
|
-
data_mast.reset_index(drop=True, inplace=True)
|
|
119
|
-
return data_mast
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
def get_score_adj_weight(self, data_mast, weight='균등') -> pd.DataFrame:
|
|
123
|
-
"""
|
|
124
|
-
스코어 마스터를 받아서, 가중치 주기
|
|
125
|
-
weight : 균등(기본), 가치성장, 추세성장, 역발생
|
|
126
|
-
"""
|
|
127
|
-
data_mast = scw.get_score_adj_weight(data_mast, weight)
|
|
128
|
-
data_mast.reset_index(drop=True, inplace=True)
|
|
129
|
-
return data_mast
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
def get_Ngroup(self, data_mast, Ngroup=10) -> pd.DataFrame:
|
|
133
|
-
"""
|
|
134
|
-
지정한 그룹수(Ngroup)에 따라 종목명만 출력
|
|
135
|
-
Ngroup : 2부터 100 사이값 & data_mast보다 건수 적음
|
|
136
|
-
"""
|
|
137
|
-
Ngroup = int(Ngroup)
|
|
138
|
-
if Ngroup > 1 and Ngroup <= 100 and Ngroup < len(data_mast):
|
|
139
|
-
|
|
140
|
-
# 1. 종합순위를 기준으로 오름차순 정렬
|
|
141
|
-
data_mast = data_mast.sort_values(by='종합순위')
|
|
142
|
-
|
|
143
|
-
# 2. 정렬된 데이터프레임을 Ngroup 개수만큼 분할
|
|
144
|
-
chunks = np.array_split(data_mast, Ngroup)
|
|
145
|
-
|
|
146
|
-
# 3. 각 그룹별로 번호와 종목명 출력
|
|
147
|
-
for i, chunk in enumerate(chunks, 1):
|
|
148
|
-
stock_list = ", ".join(chunk['sname'].astype(str).tolist())
|
|
149
|
-
print(f"{i} : {stock_list}")
|
|
150
|
-
|
|
151
|
-
else:
|
|
152
|
-
print("Ngroup를 다시 입력하세요 : 2부터 100 사이값으로, 멀티팩터 데이터보다 건수가 적어야 합니다")
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
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
|
+
from . import stockinfo as sinfo
|
|
11
|
+
|
|
12
|
+
from . import momentum as mom
|
|
13
|
+
from . import value as val
|
|
14
|
+
from . import quality as qual
|
|
15
|
+
|
|
16
|
+
from . import momentum_one as mom1
|
|
17
|
+
from . import value_one as val1
|
|
18
|
+
from . import quality_one as qual1
|
|
19
|
+
|
|
20
|
+
from . import score as sc
|
|
21
|
+
from . import score_adj_weight as scw
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class MultiFactor:
|
|
26
|
+
def __init__(self, N: int):
|
|
27
|
+
"""
|
|
28
|
+
멀티팩터 : 분석 대상 종목수 (시가총액 상위 N개)
|
|
29
|
+
"""
|
|
30
|
+
self.N = N
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def get_stockinfo(self, dtype='dataframe') -> pd.DataFrame:
|
|
34
|
+
|
|
35
|
+
df = sinfo.get_stockinfo(self.N)
|
|
36
|
+
|
|
37
|
+
if dtype=='dic':
|
|
38
|
+
# 종목 정보 사전 저장
|
|
39
|
+
stock_list = dict(zip(df['Code'], df['Name']))
|
|
40
|
+
return stock_list
|
|
41
|
+
|
|
42
|
+
else:
|
|
43
|
+
return df
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def get_momentum(self, stock_list) -> pd.DataFrame:
|
|
48
|
+
"""
|
|
49
|
+
모멘텀
|
|
50
|
+
"""
|
|
51
|
+
data_mom = mom.get_momentum(stock_list)
|
|
52
|
+
return data_mom
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def get_momentum_one(self, scode) -> pd.DataFrame:
|
|
56
|
+
"""
|
|
57
|
+
모멘텀 1개 종목
|
|
58
|
+
"""
|
|
59
|
+
return mom1.get_momentum_one(scode)
|
|
60
|
+
|
|
61
|
+
def get_value(self, stock_list) -> pd.DataFrame:
|
|
62
|
+
"""
|
|
63
|
+
밸류
|
|
64
|
+
"""
|
|
65
|
+
data_val = val.get_value(stock_list)
|
|
66
|
+
return data_val
|
|
67
|
+
|
|
68
|
+
def get_value_one(self, scode) -> pd.DataFrame:
|
|
69
|
+
"""
|
|
70
|
+
밸류 1개 종목
|
|
71
|
+
"""
|
|
72
|
+
return val1.get_value_one(scode)
|
|
73
|
+
|
|
74
|
+
def get_quality(self, stock_list) -> pd.DataFrame:
|
|
75
|
+
"""
|
|
76
|
+
퀄리티
|
|
77
|
+
"""
|
|
78
|
+
data_fin = qual.get_quality(stock_list)
|
|
79
|
+
return data_fin
|
|
80
|
+
|
|
81
|
+
def get_quality_one(self, scode) -> pd.DataFrame:
|
|
82
|
+
"""
|
|
83
|
+
퀄리티 1개 종목
|
|
84
|
+
"""
|
|
85
|
+
return qual1.get_quality_one(scode)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def get_score(self) -> pd.DataFrame:
|
|
90
|
+
"""
|
|
91
|
+
스코어 마스터 자동계산
|
|
92
|
+
"""
|
|
93
|
+
print("(1/5) 종목코드 생성")
|
|
94
|
+
stock_list = self.get_stockinfo(dtype='dic')
|
|
95
|
+
|
|
96
|
+
print("\n(2/5) 모멘텀 지표 생성")
|
|
97
|
+
data_mom = self.get_momentum(stock_list)
|
|
98
|
+
|
|
99
|
+
print("\n(3/5) 밸류 지표 생성")
|
|
100
|
+
data_val = self.get_value(stock_list)
|
|
101
|
+
|
|
102
|
+
print("\n(4/5) 퀄리티 지표 생성")
|
|
103
|
+
data_fin = self.get_quality(stock_list)
|
|
104
|
+
|
|
105
|
+
print("\n(5/5) 멀티팩터 마스터 생성")
|
|
106
|
+
data_mast = sc.get_score(data_mom, data_val, data_fin)
|
|
107
|
+
|
|
108
|
+
data_mast.reset_index(drop=True, inplace=True)
|
|
109
|
+
|
|
110
|
+
return data_mast
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def get_score_by_data(self, data_mom, data_val, data_fin) -> pd.DataFrame:
|
|
114
|
+
"""
|
|
115
|
+
스코어 마스터 : 직접 데이터 입력
|
|
116
|
+
"""
|
|
117
|
+
data_mast = sc.get_score(data_mom, data_val, data_fin)
|
|
118
|
+
data_mast.reset_index(drop=True, inplace=True)
|
|
119
|
+
return data_mast
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def get_score_adj_weight(self, data_mast, weight='균등') -> pd.DataFrame:
|
|
123
|
+
"""
|
|
124
|
+
스코어 마스터를 받아서, 가중치 주기
|
|
125
|
+
weight : 균등(기본), 가치성장, 추세성장, 역발생
|
|
126
|
+
"""
|
|
127
|
+
data_mast = scw.get_score_adj_weight(data_mast, weight)
|
|
128
|
+
data_mast.reset_index(drop=True, inplace=True)
|
|
129
|
+
return data_mast
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def get_Ngroup(self, data_mast, Ngroup=10) -> pd.DataFrame:
|
|
133
|
+
"""
|
|
134
|
+
지정한 그룹수(Ngroup)에 따라 종목명만 출력
|
|
135
|
+
Ngroup : 2부터 100 사이값 & data_mast보다 건수 적음
|
|
136
|
+
"""
|
|
137
|
+
Ngroup = int(Ngroup)
|
|
138
|
+
if Ngroup > 1 and Ngroup <= 100 and Ngroup < len(data_mast):
|
|
139
|
+
|
|
140
|
+
# 1. 종합순위를 기준으로 오름차순 정렬
|
|
141
|
+
data_mast = data_mast.sort_values(by='종합순위')
|
|
142
|
+
|
|
143
|
+
# 2. 정렬된 데이터프레임을 Ngroup 개수만큼 분할
|
|
144
|
+
chunks = np.array_split(data_mast, Ngroup)
|
|
145
|
+
|
|
146
|
+
# 3. 각 그룹별로 번호와 종목명 출력
|
|
147
|
+
for i, chunk in enumerate(chunks, 1):
|
|
148
|
+
stock_list = ", ".join(chunk['sname'].astype(str).tolist())
|
|
149
|
+
print(f"{i} : {stock_list}")
|
|
150
|
+
|
|
151
|
+
else:
|
|
152
|
+
print("Ngroup를 다시 입력하세요 : 2부터 100 사이값으로, 멀티팩터 데이터보다 건수가 적어야 합니다")
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
|
|
@@ -1,83 +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
|
-
|
|
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
83
|
return data_mom
|