analyser_hj3415 2.7.8__py2.py3-none-any.whl → 2.8.0__py2.py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- analyser_hj3415/{myprophet.py → tsa.py} +86 -26
- {analyser_hj3415-2.7.8.dist-info → analyser_hj3415-2.8.0.dist-info}/METADATA +1 -1
- {analyser_hj3415-2.7.8.dist-info → analyser_hj3415-2.8.0.dist-info}/RECORD +6 -6
- {analyser_hj3415-2.7.8.dist-info → analyser_hj3415-2.8.0.dist-info}/LICENSE +0 -0
- {analyser_hj3415-2.7.8.dist-info → analyser_hj3415-2.8.0.dist-info}/WHEEL +0 -0
- {analyser_hj3415-2.7.8.dist-info → analyser_hj3415-2.8.0.dist-info}/entry_points.txt +0 -0
@@ -1,24 +1,36 @@
|
|
1
|
+
"""
|
2
|
+
Time Series Analysis
|
3
|
+
"""
|
1
4
|
import yfinance as yf
|
2
5
|
from datetime import datetime, timedelta
|
3
6
|
import pandas as pd
|
4
7
|
from prophet import Prophet
|
5
8
|
from sklearn.preprocessing import StandardScaler
|
6
|
-
from utils_hj3415 import utils
|
9
|
+
from utils_hj3415 import utils, helpers
|
7
10
|
from typing import Optional
|
8
11
|
import plotly.graph_objs as go
|
9
12
|
from plotly.offline import plot
|
10
13
|
import matplotlib.pyplot as plt # Matplotlib 수동 임포트
|
11
14
|
from db_hj3415 import myredis
|
15
|
+
from collections import OrderedDict
|
16
|
+
from analyser_hj3415 import eval
|
17
|
+
|
18
|
+
import logging
|
19
|
+
analyser_logger = helpers.setup_logger('analyser_logger', logging.WARNING)
|
20
|
+
|
21
|
+
expire_time = 3600 * 12
|
12
22
|
|
13
23
|
class MyProphet:
|
14
24
|
def __init__(self, code: str):
|
15
25
|
assert utils.is_6digit(code), f'Invalid value : {code}'
|
16
|
-
self._code = code
|
17
|
-
self.name = myredis.Corps(code, 'c101').get_name()
|
18
|
-
self.raw_data = self.get_raw_data()
|
19
|
-
|
20
26
|
self.scaler = StandardScaler()
|
27
|
+
|
21
28
|
self.model = Prophet()
|
29
|
+
self._code = code
|
30
|
+
self.name = myredis.Corps(code, 'c101').get_name()
|
31
|
+
self.raw_data = self._get_raw_data()
|
32
|
+
self.df_real = self._preprocessing_for_prophet()
|
33
|
+
self.df_forecast = self._make_forecast()
|
22
34
|
|
23
35
|
@property
|
24
36
|
def code(self) -> str:
|
@@ -27,11 +39,25 @@ class MyProphet:
|
|
27
39
|
@code.setter
|
28
40
|
def code(self, code: str):
|
29
41
|
assert utils.is_6digit(code), f'Invalid value : {code}'
|
42
|
+
analyser_logger.info(f'change code : {self.code} -> {code}')
|
43
|
+
self.model = Prophet()
|
30
44
|
self._code = code
|
31
45
|
self.name = myredis.Corps(code, 'c101').get_name()
|
32
|
-
self.raw_data = self.
|
33
|
-
|
34
|
-
|
46
|
+
self.raw_data = self._get_raw_data()
|
47
|
+
self.df_real = self._preprocessing_for_prophet()
|
48
|
+
self.df_forecast = self._make_forecast()
|
49
|
+
|
50
|
+
@staticmethod
|
51
|
+
def is_valid_date(date_string):
|
52
|
+
try:
|
53
|
+
# %Y-%m-%d 형식으로 문자열을 datetime 객체로 변환 시도
|
54
|
+
datetime.strptime(date_string, '%Y-%m-%d')
|
55
|
+
return True
|
56
|
+
except ValueError:
|
57
|
+
# 변환이 실패하면 ValueError가 발생, 형식이 맞지 않음
|
58
|
+
return False
|
59
|
+
|
60
|
+
def _get_raw_data(self) -> pd.DataFrame:
|
35
61
|
"""
|
36
62
|
야후에서 해당 종목의 4년간 주가 raw data를 받아온다.
|
37
63
|
:return:
|
@@ -48,7 +74,7 @@ class MyProphet:
|
|
48
74
|
end=today.strftime('%Y-%m-%d')
|
49
75
|
)
|
50
76
|
|
51
|
-
def
|
77
|
+
def _preprocessing_for_prophet(self) -> pd.DataFrame:
|
52
78
|
"""
|
53
79
|
Prophet이 사용할 수 있도록 데이터 준비
|
54
80
|
ds는 날짜, y는 주가
|
@@ -61,14 +87,11 @@ class MyProphet:
|
|
61
87
|
df['volume_scaled'] = self.scaler.fit_transform(df[['volume']])
|
62
88
|
return df
|
63
89
|
|
64
|
-
def
|
65
|
-
# Prophet을 위한 dataframe 만들기
|
66
|
-
df_for_prophet = self.preprocessing_for_prophet()
|
67
|
-
|
90
|
+
def _make_forecast(self) -> pd.DataFrame:
|
68
91
|
# 정규화된 'volume_scaled' 변수를 외부 변수로 추가
|
69
92
|
self.model.add_regressor('volume_scaled')
|
70
93
|
|
71
|
-
self.model.fit(
|
94
|
+
self.model.fit(self.df_real)
|
72
95
|
|
73
96
|
# 향후 180일 동안의 주가 예측
|
74
97
|
future = self.model.make_future_dataframe(periods=180)
|
@@ -80,30 +103,36 @@ class MyProphet:
|
|
80
103
|
forecast = self.model.predict(future)
|
81
104
|
return forecast
|
82
105
|
|
106
|
+
def get_yhat(self) -> dict:
|
107
|
+
"""
|
108
|
+
예측하는 첫째날의 예측 데이터를 반환한다.
|
109
|
+
:return: {'ds':..., 'yhat':.., 'yhat_lower':.., 'yhat_upper':..,}
|
110
|
+
"""
|
111
|
+
df = self.df_forecast
|
112
|
+
analyser_logger.debug(df)
|
113
|
+
yhat_dict = df.iloc[0][['ds', 'yhat_lower', 'yhat_upper', 'yhat']].to_dict()
|
114
|
+
analyser_logger.info(yhat_dict)
|
115
|
+
return yhat_dict
|
116
|
+
|
83
117
|
def export_to(self, to="str") -> Optional[str]:
|
84
118
|
"""
|
85
119
|
prophet과 plotly로 그래프를 그려서 html을 문자열로 반환
|
86
120
|
:param to: str, png, htmlfile, show
|
87
121
|
:return:
|
88
122
|
"""
|
89
|
-
# 실제 데이터
|
90
|
-
df = self.preprocessing_for_prophet()
|
91
|
-
# 예측 데이터
|
92
|
-
forecast = self.make_forecast()
|
93
|
-
|
94
123
|
# Plotly를 사용한 시각화
|
95
124
|
fig = go.Figure()
|
96
125
|
|
97
126
|
# 실제 데이터
|
98
|
-
fig.add_trace(go.Scatter(x=
|
127
|
+
fig.add_trace(go.Scatter(x=self.df_real['ds'], y=self.df_real['y'], mode='markers', name='실제주가'))
|
99
128
|
# 예측 데이터
|
100
|
-
fig.add_trace(go.Scatter(x=
|
129
|
+
fig.add_trace(go.Scatter(x=self.df_forecast['ds'], y=self.df_forecast['yhat'], mode='lines', name='예측치'))
|
101
130
|
|
102
131
|
# 상한/하한 구간
|
103
132
|
fig.add_trace(
|
104
|
-
go.Scatter(x=
|
133
|
+
go.Scatter(x=self.df_forecast['ds'], y=self.df_forecast['yhat_upper'], fill=None, mode='lines', name='상한'))
|
105
134
|
fig.add_trace(
|
106
|
-
go.Scatter(x=
|
135
|
+
go.Scatter(x=self.df_forecast['ds'], y=self.df_forecast['yhat_lower'], fill='tonexty', mode='lines', name='하한'))
|
107
136
|
|
108
137
|
fig.update_layout(
|
109
138
|
title=f'{self.code} {self.name} 주가 예측 그래프(prophet)',
|
@@ -130,15 +159,46 @@ class MyProphet:
|
|
130
159
|
return None
|
131
160
|
elif to == 'show':
|
132
161
|
# 예측 결과 출력
|
133
|
-
print(
|
162
|
+
print(self.df_forecast[['ds', 'yhat', 'yhat_lower', 'yhat_upper']].tail())
|
134
163
|
# 예측 결과 시각화 (Matplotlib 사용)
|
135
|
-
fig = self.model.plot(
|
164
|
+
fig = self.model.plot(self.df_forecast)
|
136
165
|
# 추세 및 계절성 시각화
|
137
|
-
fig2 = self.model.plot_components(
|
166
|
+
fig2 = self.model.plot_components(self.df_forecast)
|
138
167
|
plt.show() # 시각화 창 띄우기
|
139
168
|
else:
|
140
169
|
Exception("to 인자가 맞지 않습니다.")
|
141
170
|
|
171
|
+
@classmethod
|
172
|
+
def ranking(cls, refresh = False) -> OrderedDict:
|
173
|
+
"""
|
174
|
+
가장 최근 날짜의 랭킹 분석
|
175
|
+
:param refresh:
|
176
|
+
:return:
|
177
|
+
"""
|
178
|
+
print("**** Start myprophet_ranking... ****")
|
179
|
+
redis_name = 'myprophet_ranking'
|
180
|
+
|
181
|
+
print(
|
182
|
+
f"redisname: '{redis_name}' / refresh : {refresh} / expire_time : {expire_time / 3600}h")
|
183
|
+
|
184
|
+
def fetch_ranking() -> dict:
|
185
|
+
data = {}
|
186
|
+
p = MyProphet('005930')
|
187
|
+
for i, code in enumerate(myredis.Corps.list_all_codes()):
|
188
|
+
p.code = code
|
189
|
+
recent_price = p._preprocessing_for_prophet().iloc[-1]['y']
|
190
|
+
yhat_dict = p.get_yhat()
|
191
|
+
analyser_logger.info(f'recent_price: {recent_price}, yhat_dict: {yhat_dict}')
|
192
|
+
yhat_lower = int(yhat_dict['yhat_lower'])
|
193
|
+
if recent_price < yhat_lower:
|
194
|
+
deviation = int(eval.Tools.cal_deviation(recent_price, yhat_lower))
|
195
|
+
data[code] = deviation
|
196
|
+
print(f"{i}.{p.code}/{p.name} 최근가격:{recent_price} 기대하한값:{yhat_lower} 편차:{deviation}")
|
197
|
+
return data
|
198
|
+
|
199
|
+
data_dict = myredis.Base.fetch_and_cache_data(redis_name, refresh, fetch_ranking, timer=expire_time)
|
200
|
+
|
201
|
+
return OrderedDict(sorted(data_dict.items(), key=lambda item: item[1], reverse=True))
|
142
202
|
|
143
203
|
|
144
204
|
|
@@ -2,15 +2,15 @@ analyser_hj3415/.DS_Store,sha256=qr9-0FPn5CFKe6kEu8_dWCNhzQ0sN7bwQgffKsaJEEo,614
|
|
2
2
|
analyser_hj3415/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
3
|
analyser_hj3415/cli.py,sha256=7ys-b9H_tJAbLLi_XpjmEvDiyNHddpg9F1FNAACfWnE,11005
|
4
4
|
analyser_hj3415/eval.py,sha256=4F0GIknCogAhv_iTq8auLrmwW20u8kH0HY0fP4SaVa4,39099
|
5
|
-
analyser_hj3415/
|
5
|
+
analyser_hj3415/tsa.py,sha256=4fg92xzEnp9kChvEzXbnAv6jGLuDK4zMaZjBoobp6I0,7649
|
6
6
|
analyser_hj3415/workroom/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
7
7
|
analyser_hj3415/workroom/lstm.py,sha256=gF7i1NqPOhNysvkSQcC_mkOmvLk96jPxoqajZ-FFD8I,4078
|
8
8
|
analyser_hj3415/workroom/mysklearn.py,sha256=wJXKz5MqqTzADdG2mqRMMzc_G9RzwYjj5_j4gyOopxQ,2030
|
9
9
|
analyser_hj3415/workroom/mysklearn2.py,sha256=1lIy6EWEQHkOzDS-av8U0zQH6DuCLKWMI73dnJx5KRs,1495
|
10
10
|
analyser_hj3415/workroom/score.py,sha256=P6nHBJYmyhigGtT4qna4BmNtvt4B93b7SKyzdstJK24,17376
|
11
11
|
analyser_hj3415/workroom/trash.py,sha256=zF-W0piqkGr66UP6-iybo9EXh2gO0RP6R1FnIpsGkl8,12262
|
12
|
-
analyser_hj3415-2.
|
13
|
-
analyser_hj3415-2.
|
14
|
-
analyser_hj3415-2.
|
15
|
-
analyser_hj3415-2.
|
16
|
-
analyser_hj3415-2.
|
12
|
+
analyser_hj3415-2.8.0.dist-info/entry_points.txt,sha256=ZfjPnJuH8SzvhE9vftIPMBIofsc65IAWYOhqOC_L5ck,65
|
13
|
+
analyser_hj3415-2.8.0.dist-info/LICENSE,sha256=QVKTp0dTnB5xG8RLgG17LwSWCKNEzYoVVM6KjoCPKc0,1079
|
14
|
+
analyser_hj3415-2.8.0.dist-info/WHEEL,sha256=Sgu64hAMa6g5FdzHxXv9Xdse9yxpGGMeagVtPMWpJQY,99
|
15
|
+
analyser_hj3415-2.8.0.dist-info/METADATA,sha256=4Zf9g7uum4fIQqqY1IF0_HRzQwT-JAJvAPdfRY0eW9Q,6607
|
16
|
+
analyser_hj3415-2.8.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|