analyser_hj3415 2.10.6__py3-none-any.whl → 3.0.0__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,207 @@
1
+ from datetime import datetime, timedelta
2
+ from typing import Optional
3
+ import yfinance as yf
4
+ import pandas as pd
5
+ from prophet import Prophet
6
+ from sklearn.preprocessing import StandardScaler
7
+ import matplotlib.pyplot as plt # Matplotlib 수동 임포트
8
+ import plotly.graph_objs as go
9
+ from plotly.offline import plot
10
+
11
+ from utils_hj3415 import tools, setup_logger
12
+ from db_hj3415 import myredis
13
+
14
+ from analyser_hj3415.analyser import eval
15
+
16
+ mylogger = setup_logger(__name__,'WARNING')
17
+
18
+
19
+ class MyProphet:
20
+ def __init__(self, code: str):
21
+ assert tools.is_6digit(code), f'Invalid value : {code}'
22
+ self.scaler = StandardScaler()
23
+
24
+ self.model = Prophet()
25
+ self._code = code
26
+ self.name = myredis.Corps(code, 'c101').get_name()
27
+ self.raw_data = self._get_raw_data()
28
+ self.df_real = self._preprocessing_for_prophet()
29
+ self.df_forecast = self._make_forecast()
30
+
31
+ @property
32
+ def code(self) -> str:
33
+ return self._code
34
+
35
+ @code.setter
36
+ def code(self, code: str):
37
+ assert tools.is_6digit(code), f'Invalid value : {code}'
38
+ mylogger.info(f'change code : {self.code} -> {code}')
39
+ self.model = Prophet()
40
+ self._code = code
41
+ self.name = myredis.Corps(code, 'c101').get_name()
42
+ self.raw_data = self._get_raw_data()
43
+ self.df_real = self._preprocessing_for_prophet()
44
+ self.df_forecast = self._make_forecast()
45
+
46
+ @staticmethod
47
+ def is_valid_date(date_string):
48
+ try:
49
+ # %Y-%m-%d 형식으로 문자열을 datetime 객체로 변환 시도
50
+ datetime.strptime(date_string, '%Y-%m-%d')
51
+ return True
52
+ except ValueError:
53
+ # 변환이 실패하면 ValueError가 발생, 형식이 맞지 않음
54
+ return False
55
+
56
+ def _get_raw_data(self) -> pd.DataFrame:
57
+ """
58
+ 야후에서 해당 종목의 4년간 주가 raw data를 받아온다.
59
+ :return:
60
+ """
61
+ # 오늘 날짜 가져오기
62
+ today = datetime.today()
63
+
64
+ # 4년 전 날짜 계산 (4년 = 365일 * 4)
65
+ four_years_ago = today - timedelta(days=365 * 4)
66
+
67
+ return yf.download(
68
+ self.code + '.KS',
69
+ start=four_years_ago.strftime('%Y-%m-%d'),
70
+ end=today.strftime('%Y-%m-%d')
71
+ )
72
+
73
+ def _preprocessing_for_prophet(self) -> pd.DataFrame:
74
+ """
75
+ Prophet이 사용할 수 있도록 데이터 준비
76
+ ds는 날짜, y는 주가
77
+ :return:
78
+ """
79
+ df = self.raw_data[['Close', 'Volume']].reset_index()
80
+ df.columns = ['ds', 'y', 'volume'] # Prophet의 형식에 맞게 열 이름 변경
81
+
82
+ # ds 열에서 타임존 제거
83
+ df['ds'] = df['ds'].dt.tz_localize(None)
84
+
85
+ # 추가 변수를 정규화
86
+ df['volume_scaled'] = self.scaler.fit_transform(df[['volume']])
87
+ mylogger.debug('_preprocessing_for_prophet')
88
+ mylogger.debug(df)
89
+ return df
90
+
91
+ def _make_forecast(self) -> pd.DataFrame:
92
+ # 정규화된 'volume_scaled' 변수를 외부 변수로 추가
93
+ self.model.add_regressor('volume_scaled')
94
+
95
+ self.model.fit(self.df_real)
96
+
97
+ # 향후 180일 동안의 주가 예측
98
+ future = self.model.make_future_dataframe(periods=180)
99
+ mylogger.debug('_make_forecast_future')
100
+ mylogger.debug(future)
101
+
102
+ # 미래 데이터에 거래량 추가 (평균 거래량을 사용해 정규화)
103
+ future_volume = pd.DataFrame({'volume': [self.raw_data['Volume'].mean()] * len(future)})
104
+ future['volume_scaled'] = self.scaler.transform(future_volume[['volume']])
105
+
106
+ forecast = self.model.predict(future)
107
+ mylogger.debug('_make_forecast')
108
+ mylogger.debug(forecast)
109
+ return forecast
110
+
111
+ def get_yhat(self) -> dict:
112
+ """
113
+ 최근 날짜의 예측데이터를 반환한다.
114
+ :return: {'ds':..., 'yhat':.., 'yhat_lower':.., 'yhat_upper':..,}
115
+ """
116
+ df = self.df_forecast
117
+ last_real_date = self.df_real.iloc[-1]['ds']
118
+ mylogger.info(last_real_date)
119
+ yhat_dict = df[df['ds']==last_real_date].iloc[0][['ds', 'yhat_lower', 'yhat_upper', 'yhat']].to_dict()
120
+ mylogger.info(yhat_dict)
121
+ return yhat_dict
122
+
123
+ def visualization(self):
124
+ # 예측 결과 출력
125
+ print(self.df_forecast[['ds', 'yhat', 'yhat_lower', 'yhat_upper']].tail())
126
+ # 예측 결과 시각화 (Matplotlib 사용)
127
+ fig = self.model.plot(self.df_forecast)
128
+ # 추세 및 계절성 시각화
129
+ fig2 = self.model.plot_components(self.df_forecast)
130
+ plt.show() # 시각화 창 띄우기
131
+
132
+ def export(self, to="str") -> Optional[str]:
133
+ """
134
+ prophet과 plotly로 그래프를 그려서 html을 문자열로 반환
135
+ :param to: str, png, htmlfile
136
+ :return:
137
+ """
138
+ # Plotly를 사용한 시각화
139
+ fig = go.Figure()
140
+
141
+ # 실제 데이터
142
+ fig.add_trace(go.Scatter(x=self.df_real['ds'], y=self.df_real['y'], mode='markers', name='실제주가'))
143
+ # 예측 데이터
144
+ fig.add_trace(go.Scatter(x=self.df_forecast['ds'], y=self.df_forecast['yhat'], mode='lines', name='예측치'))
145
+
146
+ # 상한/하한 구간
147
+ fig.add_trace(
148
+ go.Scatter(x=self.df_forecast['ds'], y=self.df_forecast['yhat_upper'], fill=None, mode='lines', name='상한'))
149
+ fig.add_trace(
150
+ go.Scatter(x=self.df_forecast['ds'], y=self.df_forecast['yhat_lower'], fill='tonexty', mode='lines', name='하한'))
151
+
152
+ fig.update_layout(
153
+ # title=f'{self.code} {self.name} 주가 예측 그래프(prophet)',
154
+ xaxis_title='일자',
155
+ yaxis_title='주가(원)',
156
+ xaxis = dict(
157
+ tickformat='%Y/%m', # X축을 '연/월' 형식으로 표시
158
+ ),
159
+ yaxis = dict(
160
+ tickformat=".0f", # 소수점 없이 원래 숫자 표시
161
+ ),
162
+ showlegend=False,
163
+ )
164
+
165
+ if to == 'str':
166
+ # 그래프 HTML로 변환 (string 형식으로 저장)
167
+ graph_html = plot(fig, output_type='div')
168
+ return graph_html
169
+ elif to == 'png':
170
+ # 그래프를 PNG 파일로 저장
171
+ fig.write_image(f"myprophet_{self.code}.png")
172
+ return None
173
+ elif to == 'htmlfile':
174
+ # 그래프를 HTML로 저장
175
+ plot(fig, filename=f'myprophet_{self.code}.html', auto_open=False)
176
+ return None
177
+ else:
178
+ Exception("to 인자가 맞지 않습니다.")
179
+
180
+ def scoring(self) -> int:
181
+ """
182
+ prophet의 yhat_lower 예측치와 주가를 비교하여 주가가 낮으면 양의 점수를 높으면 음의 점수를 준다.
183
+
184
+ Returns:
185
+ int: The calculated score based on the deviation between the recent price
186
+ and the expected lower limit.
187
+
188
+ Raises:
189
+ AttributeError: Raised if the necessary attributes like `df_real` or methods like `get_yhat`
190
+ are not correctly set or implemented.
191
+ KeyError: Raised if the expected keys (`'yhat_lower'` or `'y'`) are not found in the data involved.
192
+ ValueError: Raised if the format of data does not conform to expected structure for calculations.
193
+ """
194
+ last_real_data = self.df_real.iloc[-1]
195
+ recent_price = last_real_data['y']
196
+ recent_date = datetime.strftime(last_real_data['ds'], '%Y-%m-%d')
197
+ yhat_dict = self.get_yhat()
198
+ mylogger.info(f'recent_price: {recent_price}, yhat_dict: {yhat_dict}')
199
+ yhat_lower = int(yhat_dict['yhat_lower'])
200
+ deviation = int(eval.Tools.cal_deviation(recent_price, yhat_lower))
201
+ if recent_price > yhat_lower:
202
+ score = -deviation
203
+ else:
204
+ score = deviation
205
+ mylogger.info(f"{self.code}/{self.name} date: {recent_date} 가격:{recent_price} 기대하한값:{yhat_lower} 편차:{deviation} score:{score}")
206
+ return score
207
+
analyser_hj3415/cli.py CHANGED
@@ -1,42 +1,12 @@
1
1
  import argparse
2
- import os
3
2
  import pprint
4
3
 
5
- from utils_hj3415 import utils
6
- from utils_hj3415.helpers import SettingsManager
4
+ from utils_hj3415 import tools
5
+ from analyser_hj3415.analyser import eval, tsa, score
7
6
  from db_hj3415 import myredis, mymongo
8
7
 
9
- class AnalyserSettingsManager(SettingsManager):
10
- DEFAULT_SETTINGS = {
11
- 'EXPECT_EARN': 0.06,
12
- 'RED_RANKING_EXPECT_EARN': 0.06,
13
- }
14
- TITLES = DEFAULT_SETTINGS.keys()
15
-
16
- def __init__(self):
17
- settings_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'settings.json')
18
- super().__init__(settings_file)
19
-
20
- def set_value(self, title: str, value: str):
21
- assert title in self.TITLES, f"title 인자는 {self.TITLES} 중에 있어야 합니다."
22
- self.settings_dict[title] = value
23
- self.save_settings()
24
- print(f"{title}: {value}가 저장되었습니다")
25
-
26
- def get_value(self, title: str):
27
- assert title in self.TITLES, f"title 인자는 {self.TITLES} 중에 있어야 합니다."
28
- return self.settings_dict.get(title, self.DEFAULT_SETTINGS[title])
29
-
30
- def reset_value(self, title: str):
31
- assert title in self.TITLES, f"title 인자는 {self.TITLES} 중에 있어야 합니다."
32
- self.set_value(title, self.DEFAULT_SETTINGS[title])
33
- print(f"{title}이 기본값 ({self.DEFAULT_SETTINGS[title]}) 으로 초기화 되었습니다.")
34
-
35
8
 
36
9
  def analyser_manager():
37
- settings_manager = AnalyserSettingsManager()
38
- expect_earn_from_setting = settings_manager.get_value('EXPECT_EARN')
39
-
40
10
  parser = argparse.ArgumentParser(description="Analyser Commands")
41
11
  type_subparsers = parser.add_subparsers(dest='type', help='분석 타입')
42
12
 
@@ -69,7 +39,6 @@ def analyser_manager():
69
39
  # red - get 파서
70
40
  red_get_parser = red_subparser.add_parser('get', help='red get 책정 및 레디스 저장')
71
41
  red_get_parser.add_argument('code', type=str, help='종목코드 or all')
72
- red_get_parser.add_argument('-e', '--expect_earn', type=float, help='기대수익률 (실수 값 입력)')
73
42
  red_get_parser.add_argument('-r', '--refresh', action='store_true', help='래디스 캐시를 사용하지 않고 강제로 재계산 할지')
74
43
 
75
44
  # mil 명령어 서브파서
@@ -78,7 +47,6 @@ def analyser_manager():
78
47
  # mil - get 파서
79
48
  mil_get_parser = mil_subparser.add_parser('get', help='mil get 책정 및 레디스 저장')
80
49
  mil_get_parser.add_argument('code', type=str, help='종목코드 or all')
81
- mil_get_parser.add_argument('-e', '--expect_earn', type=float, help='기대수익률 (실수 값 입력)')
82
50
  mil_get_parser.add_argument('-r', '--refresh', action='store_true', help='래디스 캐시를 사용하지 않고 강제로 재계산 할지')
83
51
 
84
52
  # blue 명령어 서브파서
@@ -87,7 +55,6 @@ def analyser_manager():
87
55
  # blue - get 파서
88
56
  blue_get_parser = blue_subparser.add_parser('get', help='blue get 책정 및 레디스 저장')
89
57
  blue_get_parser.add_argument('code', type=str, help='종목코드 or all')
90
- blue_get_parser.add_argument('-e', '--expect_earn', type=float, help='기대수익률 (실수 값 입력)')
91
58
  blue_get_parser.add_argument('-r', '--refresh', action='store_true', help='래디스 캐시를 사용하지 않고 강제로 재계산 할지')
92
59
 
93
60
  # growth 명령어 서브파서
@@ -96,59 +63,25 @@ def analyser_manager():
96
63
  # growth - get 파서
97
64
  growth_get_parser = growth_subparser.add_parser('get', help='growth get 책정 및 레디스 저장')
98
65
  growth_get_parser.add_argument('code', type=str, help='종목코드 or all')
99
- growth_get_parser.add_argument('-e', '--expect_earn', type=float, help='기대수익률 (실수 값 입력)')
100
66
  growth_get_parser.add_argument('-r', '--refresh', action='store_true', help='래디스 캐시를 사용하지 않고 강제로 재계산 할지')
101
67
 
102
- # setting 명령어 서브파서
103
- setting_parser = type_subparsers.add_parser('setting', help='Set and Get settings')
104
- setting_subparser = setting_parser.add_subparsers(dest='command', help='setting 관련된 명령')
105
- # setting - set 파서
106
- set_parser = setting_subparser.add_parser('set', help='세팅값 저장')
107
- set_parser.add_argument('title', choices=AnalyserSettingsManager.TITLES, help='타이틀')
108
- set_parser.add_argument('value', help='세팅값')
109
- # setting - get 파서
110
- get_parser = setting_subparser.add_parser('get', help='타이틀 세팅값 불러오기')
111
- get_parser.add_argument('title', choices=AnalyserSettingsManager.TITLES, help='타이틀')
112
- # setting - print 파서
113
- setting_subparser.add_parser('print', help='전체 세팅값 출력')
114
-
115
68
  args = parser.parse_args()
116
69
 
117
- from . import eval, tsa, score
118
-
119
70
  if args.type == 'red':
120
71
  if args.command == 'get':
121
72
  if args.code == 'all':
122
- # 저장된 기대수익률을 불러서 임시저장
123
- ee_orig = eval.Red.expect_earn
124
-
125
73
  red = eval.Red('005930')
126
- if args.expect_earn:
127
- eval.Red.expect_earn = float(args.expect_earn)
128
-
129
74
  print("**** Red - all codes ****")
130
75
  for i, code in enumerate(myredis.Corps.list_all_codes()):
131
76
  red.code = code
132
77
  print(f"*** {i} / {red} ***")
133
78
  pprint.pprint(red.get(args.refresh, verbose=False))
134
-
135
- # 원래 저장되었던 기대수익률로 다시 복원
136
- eval.Red.expect_earn = ee_orig
137
79
  else:
138
- assert utils.is_6digit(args.code), "code 인자는 6자리 숫자이어야 합니다."
80
+ assert tools.is_6digit(args.code), "code 인자는 6자리 숫자이어야 합니다."
139
81
  # 저장된 기대수익률을 불러서 임시저장
140
- ee_orig = eval.Red.expect_earn
141
-
142
82
  red = eval.Red(args.code)
143
- if args.expect_earn:
144
- args.refresh = True
145
- eval.Red.expect_earn = float(args.expect_earn)
146
-
147
83
  print(f"*** Red - {red} ***")
148
84
  pprint.pprint(red.get(args.refresh))
149
-
150
- # 원래 저장되었던 기대수익률로 다시 복원
151
- eval.Red.expect_earn = ee_orig
152
85
  mymongo.Logs.save('cli','INFO', f'run >> analyser red get {args.code}')
153
86
 
154
87
  elif args.command == 'ranking':
@@ -166,7 +99,7 @@ def analyser_manager():
166
99
  print(f"*** {i} / {mil} ***")
167
100
  pprint.pprint(mil.get(args.refresh, verbose=False))
168
101
  else:
169
- assert utils.is_6digit(args.code), "code 인자는 6자리 숫자이어야 합니다."
102
+ assert tools.is_6digit(args.code), "code 인자는 6자리 숫자이어야 합니다."
170
103
  mil = eval.Mil(args.code)
171
104
  print(f"*** Mil - {mil} ***")
172
105
  pprint.pprint(mil.get(args.refresh))
@@ -182,7 +115,7 @@ def analyser_manager():
182
115
  print(f"*** {i} / {blue} ***")
183
116
  pprint.pprint(blue.get(args.refresh, verbose=False))
184
117
  else:
185
- assert utils.is_6digit(args.code), "code 인자는 6자리 숫자이어야 합니다."
118
+ assert tools.is_6digit(args.code), "code 인자는 6자리 숫자이어야 합니다."
186
119
  blue = eval.Blue(args.code)
187
120
  print(f"*** Blue - {blue} ***")
188
121
  pprint.pprint(blue.get(args.refresh))
@@ -198,7 +131,7 @@ def analyser_manager():
198
131
  print(f"*** {i} / {growth} ***")
199
132
  pprint.pprint(growth.get(args.refresh, verbose=False))
200
133
  else:
201
- assert utils.is_6digit(args.code), "code 인자는 6자리 숫자이어야 합니다."
134
+ assert tools.is_6digit(args.code), "code 인자는 6자리 숫자이어야 합니다."
202
135
  growth = eval.Growth(args.code)
203
136
  print(f"*** growth - {growth} ***")
204
137
  pprint.pprint(growth.get(args.refresh))
@@ -206,7 +139,7 @@ def analyser_manager():
206
139
 
207
140
  elif args.type == 'prophet':
208
141
  if args.command == 'ranking':
209
- result = score.Score.ranking(refresh=args.refresh, expire_time_h=48)
142
+ result = score.Score.ranking(refresh=args.refresh)
210
143
  print(result)
211
144
  mymongo.Logs.save('cli','INFO', 'run >> analyser prophet ranking')
212
145
 
@@ -214,23 +147,13 @@ def analyser_manager():
214
147
  mylstm = tsa.MyLSTM
215
148
  if args.command == 'caching':
216
149
  if args.top:
217
- mylstm.caching_based_on_prophet_ranking(refresh=args.refresh, expire_time_h=96, top=args.top)
150
+ mylstm.caching_based_on_prophet_ranking(refresh=args.refresh, top=args.top)
218
151
  else:
219
- mylstm.caching_based_on_prophet_ranking(refresh=args.refresh, expire_time_h=96)
152
+ mylstm.caching_based_on_prophet_ranking(refresh=args.refresh)
220
153
  mymongo.Logs.save('cli','INFO', f'run >> analyser lstm caching / top={args.top if args.top else 20}')
221
154
  elif args.command == 'get':
222
- assert utils.is_6digit(args.code), "code 인자는 6자리 숫자이어야 합니다."
223
- result = mylstm(args.code).get_final_predictions(refresh=args.refresh, expire_time_h=96)
155
+ assert tools.is_6digit(args.code), "code 인자는 6자리 숫자이어야 합니다."
156
+ result = mylstm(args.code).get_final_predictions(refresh=args.refresh)
224
157
  mymongo.Logs.save('cli','INFO', f'run >> analyser lstm get {args.code}')
225
-
226
- elif args.type == 'setting':
227
- if args.command == 'set':
228
- settings_manager.set_value(args.title, args.value)
229
- elif args.command == 'get':
230
- value = settings_manager.get_value(args.title)
231
- print(f"{args.title} 값: {value}")
232
- elif args.command == 'print':
233
- print(settings_manager.load_settings())
234
-
235
158
  else:
236
159
  parser.print_help()
@@ -1,11 +1,11 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: analyser_hj3415
3
- Version: 2.10.6
3
+ Version: 3.0.0
4
4
  Summary: Stock analyser and database processing programs
5
5
  Requires-Python: >=3.6
6
6
  Description-Content-Type: text/markdown
7
- Requires-Dist: utils-hj3415>=2.9.4
8
- Requires-Dist: db-hj3415>=4.2.3
7
+ Requires-Dist: utils-hj3415>=3.0.8
8
+ Requires-Dist: db-hj3415>=4.3.0
9
9
  Requires-Dist: scikit-learn>=1.5.2
10
10
  Requires-Dist: plotly>=5.24.1
11
11
  Requires-Dist: yfinance>=0.2.44
@@ -0,0 +1,22 @@
1
+ analyser_hj3415/__init__.py,sha256=huF0tm8fFC96sODN9TmVRmPhDizQ0yBTKNOw8miOBh0,448
2
+ analyser_hj3415/cli.py,sha256=pHFNP8CQb6czsxhPMVgXYY5mJ8uk5PF3k2f7IGw8EoQ,8840
3
+ analyser_hj3415/analyser/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ analyser_hj3415/analyser/score.py,sha256=cqTHXLBh38N9zlmNzChWZJ42O7gdXhjw_jVevOxDUo8,6420
5
+ analyser_hj3415/analyser/eval/__init__.py,sha256=IP1d0Q3nOCAD3zK1qxrC685MkJQfUh-qaXc7xptTxk8,80
6
+ analyser_hj3415/analyser/eval/blue.py,sha256=fq_eln7-EdwICNQ2sMXAfZKXGhQ29EaJwsGvn7xA29U,7632
7
+ analyser_hj3415/analyser/eval/common.py,sha256=JBTVvzO6YoeMjKl6X2MF7wgB6fvPTEToHLJSv-V7V4k,12425
8
+ analyser_hj3415/analyser/eval/growth.py,sha256=OXuBuPzVw3q6kpelNigiX7xvJNUP1gv26DNetc4aews,3315
9
+ analyser_hj3415/analyser/eval/mil.py,sha256=wN1jPu5zLJjoDk3kL7piL-4jWhpsAMeQpsTZll0a2dw,10568
10
+ analyser_hj3415/analyser/eval/red.py,sha256=SegWvklGFCCFpzg9qwKDEU0-iSq-d_lM6XRNhth8VWM,12529
11
+ analyser_hj3415/analyser/tsa/__init__.py,sha256=rPnn4b8aEym2O9l_sAaIRUpOeXJw6sDj2XfUzy-mpvg,42
12
+ analyser_hj3415/analyser/tsa/lstm.py,sha256=jrJlugPTugSH9liNGpiRMaigr3mq3FeGxDNVvcll3N8,28302
13
+ analyser_hj3415/analyser/tsa/prophet.py,sha256=zL6RNSiwFKKliQVsR3yOHz13S2SrCfuXvBj62lqP80Y,7984
14
+ analyser_hj3415/workroom/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
+ analyser_hj3415/workroom/mysklearn.py,sha256=wJXKz5MqqTzADdG2mqRMMzc_G9RzwYjj5_j4gyOopxQ,2030
16
+ analyser_hj3415/workroom/mysklearn2.py,sha256=1lIy6EWEQHkOzDS-av8U0zQH6DuCLKWMI73dnJx5KRs,1495
17
+ analyser_hj3415/workroom/score.py,sha256=P6nHBJYmyhigGtT4qna4BmNtvt4B93b7SKyzdstJK24,17376
18
+ analyser_hj3415/workroom/trash.py,sha256=zF-W0piqkGr66UP6-iybo9EXh2gO0RP6R1FnIpsGkl8,12262
19
+ analyser_hj3415-3.0.0.dist-info/entry_points.txt,sha256=ZfjPnJuH8SzvhE9vftIPMBIofsc65IAWYOhqOC_L5ck,65
20
+ analyser_hj3415-3.0.0.dist-info/WHEEL,sha256=CpUCUxeHQbRN5UGRQHYRJorO5Af-Qy_fHMctcQ8DSGI,82
21
+ analyser_hj3415-3.0.0.dist-info/METADATA,sha256=7ni3Z65IhX-5o5UrIP-h5mVVusPhrXnrAe4jL2_azbQ,6776
22
+ analyser_hj3415-3.0.0.dist-info/RECORD,,