analyser_hj3415 2.7.8__tar.gz → 2.8.1__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
Files changed (24) hide show
  1. {analyser_hj3415-2.7.8 → analyser_hj3415-2.8.1}/PKG-INFO +1 -1
  2. analyser_hj3415-2.7.8/analyser_hj3415/myprophet.py → analyser_hj3415-2.8.1/analyser_hj3415/tsa.py +89 -26
  3. {analyser_hj3415-2.7.8 → analyser_hj3415-2.8.1}/pyproject.toml +1 -1
  4. {analyser_hj3415-2.7.8 → analyser_hj3415-2.8.1}/.DS_Store +0 -0
  5. {analyser_hj3415-2.7.8 → analyser_hj3415-2.8.1}/.gitattributes +0 -0
  6. {analyser_hj3415-2.7.8 → analyser_hj3415-2.8.1}/.gitignore +0 -0
  7. {analyser_hj3415-2.7.8 → analyser_hj3415-2.8.1}/.idea/.gitignore +0 -0
  8. {analyser_hj3415-2.7.8 → analyser_hj3415-2.8.1}/.idea/analyser-hj3415.iml +0 -0
  9. {analyser_hj3415-2.7.8 → analyser_hj3415-2.8.1}/.idea/inspectionProfiles/profiles_settings.xml +0 -0
  10. {analyser_hj3415-2.7.8 → analyser_hj3415-2.8.1}/.idea/misc.xml +0 -0
  11. {analyser_hj3415-2.7.8 → analyser_hj3415-2.8.1}/.idea/modules.xml +0 -0
  12. {analyser_hj3415-2.7.8 → analyser_hj3415-2.8.1}/.idea/vcs.xml +0 -0
  13. {analyser_hj3415-2.7.8 → analyser_hj3415-2.8.1}/LICENSE +0 -0
  14. {analyser_hj3415-2.7.8 → analyser_hj3415-2.8.1}/README.md +0 -0
  15. {analyser_hj3415-2.7.8 → analyser_hj3415-2.8.1}/analyser_hj3415/.DS_Store +0 -0
  16. {analyser_hj3415-2.7.8 → analyser_hj3415-2.8.1}/analyser_hj3415/__init__.py +0 -0
  17. {analyser_hj3415-2.7.8 → analyser_hj3415-2.8.1}/analyser_hj3415/cli.py +0 -0
  18. {analyser_hj3415-2.7.8 → analyser_hj3415-2.8.1}/analyser_hj3415/eval.py +0 -0
  19. {analyser_hj3415-2.7.8 → analyser_hj3415-2.8.1}/analyser_hj3415/workroom/__init__.py +0 -0
  20. {analyser_hj3415-2.7.8 → analyser_hj3415-2.8.1}/analyser_hj3415/workroom/lstm.py +0 -0
  21. {analyser_hj3415-2.7.8 → analyser_hj3415-2.8.1}/analyser_hj3415/workroom/mysklearn.py +0 -0
  22. {analyser_hj3415-2.7.8 → analyser_hj3415-2.8.1}/analyser_hj3415/workroom/mysklearn2.py +0 -0
  23. {analyser_hj3415-2.7.8 → analyser_hj3415-2.8.1}/analyser_hj3415/workroom/score.py +0 -0
  24. {analyser_hj3415-2.7.8 → analyser_hj3415-2.8.1}/analyser_hj3415/workroom/trash.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: analyser_hj3415
3
- Version: 2.7.8
3
+ Version: 2.8.1
4
4
  Summary: Stock analyser and database processing programs
5
5
  Author-email: Hyungjin Kim <hj3415@gmail.com>
6
6
  Description-Content-Type: text/markdown
@@ -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.get_raw_data()
33
-
34
- def get_raw_data(self) -> pd.DataFrame:
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 preprocessing_for_prophet(self) -> pd.DataFrame:
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 make_forecast(self) -> pd.DataFrame:
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(df_for_prophet)
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,37 @@ 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
+ last_real_date = self.df_real.iloc[-1]['ds']
113
+ analyser_logger.info(last_real_date)
114
+ yhat_dict = df[df['ds']==last_real_date].iloc[0][['ds', 'yhat_lower', 'yhat_upper', 'yhat']].to_dict()
115
+ analyser_logger.info(yhat_dict)
116
+ return yhat_dict
117
+
83
118
  def export_to(self, to="str") -> Optional[str]:
84
119
  """
85
120
  prophet과 plotly로 그래프를 그려서 html을 문자열로 반환
86
121
  :param to: str, png, htmlfile, show
87
122
  :return:
88
123
  """
89
- # 실제 데이터
90
- df = self.preprocessing_for_prophet()
91
- # 예측 데이터
92
- forecast = self.make_forecast()
93
-
94
124
  # Plotly를 사용한 시각화
95
125
  fig = go.Figure()
96
126
 
97
127
  # 실제 데이터
98
- fig.add_trace(go.Scatter(x=df['ds'], y=df['y'], mode='markers', name='실제주가'))
128
+ fig.add_trace(go.Scatter(x=self.df_real['ds'], y=self.df_real['y'], mode='markers', name='실제주가'))
99
129
  # 예측 데이터
100
- fig.add_trace(go.Scatter(x=forecast['ds'], y=forecast['yhat'], mode='lines', name='예측치'))
130
+ fig.add_trace(go.Scatter(x=self.df_forecast['ds'], y=self.df_forecast['yhat'], mode='lines', name='예측치'))
101
131
 
102
132
  # 상한/하한 구간
103
133
  fig.add_trace(
104
- go.Scatter(x=forecast['ds'], y=forecast['yhat_upper'], fill=None, mode='lines', name='상한'))
134
+ go.Scatter(x=self.df_forecast['ds'], y=self.df_forecast['yhat_upper'], fill=None, mode='lines', name='상한'))
105
135
  fig.add_trace(
106
- go.Scatter(x=forecast['ds'], y=forecast['yhat_lower'], fill='tonexty', mode='lines', name='하한'))
136
+ go.Scatter(x=self.df_forecast['ds'], y=self.df_forecast['yhat_lower'], fill='tonexty', mode='lines', name='하한'))
107
137
 
108
138
  fig.update_layout(
109
139
  title=f'{self.code} {self.name} 주가 예측 그래프(prophet)',
@@ -130,15 +160,48 @@ class MyProphet:
130
160
  return None
131
161
  elif to == 'show':
132
162
  # 예측 결과 출력
133
- print(forecast[['ds', 'yhat', 'yhat_lower', 'yhat_upper']].tail())
163
+ print(self.df_forecast[['ds', 'yhat', 'yhat_lower', 'yhat_upper']].tail())
134
164
  # 예측 결과 시각화 (Matplotlib 사용)
135
- fig = self.model.plot(forecast)
165
+ fig = self.model.plot(self.df_forecast)
136
166
  # 추세 및 계절성 시각화
137
- fig2 = self.model.plot_components(forecast)
167
+ fig2 = self.model.plot_components(self.df_forecast)
138
168
  plt.show() # 시각화 창 띄우기
139
169
  else:
140
170
  Exception("to 인자가 맞지 않습니다.")
141
171
 
172
+ @classmethod
173
+ def ranking(cls, refresh = False) -> OrderedDict:
174
+ """
175
+ 가장 최근 날짜의 랭킹 분석
176
+ :param refresh:
177
+ :return:
178
+ """
179
+ print("**** Start myprophet_ranking... ****")
180
+ redis_name = 'myprophet_ranking'
181
+
182
+ print(
183
+ f"redisname: '{redis_name}' / refresh : {refresh} / expire_time : {expire_time / 3600}h")
184
+
185
+ def fetch_ranking() -> dict:
186
+ data = {}
187
+ p = MyProphet('005930')
188
+ for i, code in enumerate(myredis.Corps.list_all_codes()):
189
+ p.code = code
190
+ last_real_data = p._preprocessing_for_prophet().iloc[-1]
191
+ recent_price = last_real_data['y']
192
+ recent_date = datetime.strftime(last_real_data['ds'], '%Y-%m-%d')
193
+ yhat_dict = p.get_yhat()
194
+ analyser_logger.info(f'recent_price: {recent_price}, yhat_dict: {yhat_dict}')
195
+ yhat_lower = int(yhat_dict['yhat_lower'])
196
+ if recent_price < yhat_lower:
197
+ deviation = int(eval.Tools.cal_deviation(recent_price, yhat_lower))
198
+ data[code] = deviation
199
+ print(f"{i}.{p.code}/{p.name} date: {recent_date} 가격:{recent_price} 기대하한값:{yhat_lower} 편차:{deviation}")
200
+ return data
201
+
202
+ data_dict = myredis.Base.fetch_and_cache_data(redis_name, refresh, fetch_ranking, timer=expire_time)
203
+
204
+ return OrderedDict(sorted(data_dict.items(), key=lambda item: item[1], reverse=True))
142
205
 
143
206
 
144
207
 
@@ -4,7 +4,7 @@ build-backend = "flit_core.buildapi"
4
4
 
5
5
  [project]
6
6
  name = "analyser_hj3415"
7
- version = "2.7.8"
7
+ version = "2.8.1"
8
8
  authors = [{name = "Hyungjin Kim", email = "hj3415@gmail.com"}]
9
9
  description = "Stock analyser and database processing programs"
10
10
  readme = "README.md"
File without changes