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

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,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
 
@@ -78,7 +48,6 @@ def analyser_manager():
78
48
  # mil - get 파서
79
49
  mil_get_parser = mil_subparser.add_parser('get', help='mil get 책정 및 레디스 저장')
80
50
  mil_get_parser.add_argument('code', type=str, help='종목코드 or all')
81
- mil_get_parser.add_argument('-e', '--expect_earn', type=float, help='기대수익률 (실수 값 입력)')
82
51
  mil_get_parser.add_argument('-r', '--refresh', action='store_true', help='래디스 캐시를 사용하지 않고 강제로 재계산 할지')
83
52
 
84
53
  # blue 명령어 서브파서
@@ -87,7 +56,6 @@ def analyser_manager():
87
56
  # blue - get 파서
88
57
  blue_get_parser = blue_subparser.add_parser('get', help='blue get 책정 및 레디스 저장')
89
58
  blue_get_parser.add_argument('code', type=str, help='종목코드 or all')
90
- blue_get_parser.add_argument('-e', '--expect_earn', type=float, help='기대수익률 (실수 값 입력)')
91
59
  blue_get_parser.add_argument('-r', '--refresh', action='store_true', help='래디스 캐시를 사용하지 않고 강제로 재계산 할지')
92
60
 
93
61
  # growth 명령어 서브파서
@@ -96,64 +64,37 @@ def analyser_manager():
96
64
  # growth - get 파서
97
65
  growth_get_parser = growth_subparser.add_parser('get', help='growth get 책정 및 레디스 저장')
98
66
  growth_get_parser.add_argument('code', type=str, help='종목코드 or all')
99
- growth_get_parser.add_argument('-e', '--expect_earn', type=float, help='기대수익률 (실수 값 입력)')
100
67
  growth_get_parser.add_argument('-r', '--refresh', action='store_true', help='래디스 캐시를 사용하지 않고 강제로 재계산 할지')
101
68
 
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
69
  args = parser.parse_args()
116
70
 
117
- from . import eval, tsa, score
118
-
119
71
  if args.type == 'red':
120
72
  if args.command == 'get':
121
- if args.code == 'all':
122
- # 저장된 기대수익률을 불러서 임시저장
123
- ee_orig = eval.Red.expect_earn
124
-
73
+ if args.expect_earn is None:
125
74
  red = eval.Red('005930')
126
- if args.expect_earn:
127
- eval.Red.expect_earn = float(args.expect_earn)
75
+ else:
76
+ red = eval.Red('005930', expect_earn=args.expect_earn)
77
+ if args.code == 'all':
128
78
 
129
79
  print("**** Red - all codes ****")
130
80
  for i, code in enumerate(myredis.Corps.list_all_codes()):
131
81
  red.code = code
132
82
  print(f"*** {i} / {red} ***")
133
83
  pprint.pprint(red.get(args.refresh, verbose=False))
134
-
135
- # 원래 저장되었던 기대수익률로 다시 복원
136
- eval.Red.expect_earn = ee_orig
137
84
  else:
138
- assert utils.is_6digit(args.code), "code 인자는 6자리 숫자이어야 합니다."
85
+ assert tools.is_6digit(args.code), "code 인자는 6자리 숫자이어야 합니다."
139
86
  # 저장된 기대수익률을 불러서 임시저장
140
- ee_orig = eval.Red.expect_earn
141
-
142
- red = eval.Red(args.code)
143
- if args.expect_earn:
144
- args.refresh = True
145
- eval.Red.expect_earn = float(args.expect_earn)
146
-
87
+ red.code = args.code
147
88
  print(f"*** Red - {red} ***")
148
89
  pprint.pprint(red.get(args.refresh))
149
-
150
- # 원래 저장되었던 기대수익률로 다시 복원
151
- eval.Red.expect_earn = ee_orig
152
90
  mymongo.Logs.save('cli','INFO', f'run >> analyser red get {args.code}')
153
91
 
154
92
  elif args.command == 'ranking':
155
93
  mymongo.Logs.save('cli','INFO', 'run >> analyser red ranking')
156
- result = eval.Red.ranking(expect_earn=args.expect_earn, refresh=args.refresh)
94
+ if args.expect_earn is None:
95
+ result = eval.Red.ranking(refresh=args.refresh)
96
+ else:
97
+ result = eval.Red.ranking(expect_earn=args.expect_earn, refresh=args.refresh)
157
98
  print(result)
158
99
 
159
100
  elif args.type == 'mil':
@@ -166,7 +107,7 @@ def analyser_manager():
166
107
  print(f"*** {i} / {mil} ***")
167
108
  pprint.pprint(mil.get(args.refresh, verbose=False))
168
109
  else:
169
- assert utils.is_6digit(args.code), "code 인자는 6자리 숫자이어야 합니다."
110
+ assert tools.is_6digit(args.code), "code 인자는 6자리 숫자이어야 합니다."
170
111
  mil = eval.Mil(args.code)
171
112
  print(f"*** Mil - {mil} ***")
172
113
  pprint.pprint(mil.get(args.refresh))
@@ -182,7 +123,7 @@ def analyser_manager():
182
123
  print(f"*** {i} / {blue} ***")
183
124
  pprint.pprint(blue.get(args.refresh, verbose=False))
184
125
  else:
185
- assert utils.is_6digit(args.code), "code 인자는 6자리 숫자이어야 합니다."
126
+ assert tools.is_6digit(args.code), "code 인자는 6자리 숫자이어야 합니다."
186
127
  blue = eval.Blue(args.code)
187
128
  print(f"*** Blue - {blue} ***")
188
129
  pprint.pprint(blue.get(args.refresh))
@@ -198,7 +139,7 @@ def analyser_manager():
198
139
  print(f"*** {i} / {growth} ***")
199
140
  pprint.pprint(growth.get(args.refresh, verbose=False))
200
141
  else:
201
- assert utils.is_6digit(args.code), "code 인자는 6자리 숫자이어야 합니다."
142
+ assert tools.is_6digit(args.code), "code 인자는 6자리 숫자이어야 합니다."
202
143
  growth = eval.Growth(args.code)
203
144
  print(f"*** growth - {growth} ***")
204
145
  pprint.pprint(growth.get(args.refresh))
@@ -206,7 +147,7 @@ def analyser_manager():
206
147
 
207
148
  elif args.type == 'prophet':
208
149
  if args.command == 'ranking':
209
- result = score.Score.ranking(refresh=args.refresh, expire_time_h=48)
150
+ result = score.Score.ranking(refresh=args.refresh)
210
151
  print(result)
211
152
  mymongo.Logs.save('cli','INFO', 'run >> analyser prophet ranking')
212
153
 
@@ -214,23 +155,13 @@ def analyser_manager():
214
155
  mylstm = tsa.MyLSTM
215
156
  if args.command == 'caching':
216
157
  if args.top:
217
- mylstm.caching_based_on_prophet_ranking(refresh=args.refresh, expire_time_h=96, top=args.top)
158
+ mylstm.caching_based_on_prophet_ranking(refresh=args.refresh, top=args.top)
218
159
  else:
219
- mylstm.caching_based_on_prophet_ranking(refresh=args.refresh, expire_time_h=96)
160
+ mylstm.caching_based_on_prophet_ranking(refresh=args.refresh)
220
161
  mymongo.Logs.save('cli','INFO', f'run >> analyser lstm caching / top={args.top if args.top else 20}')
221
162
  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)
163
+ assert tools.is_6digit(args.code), "code 인자는 6자리 숫자이어야 합니다."
164
+ result = mylstm(args.code).get_final_predictions(refresh=args.refresh)
224
165
  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
166
  else:
236
167
  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.1
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=b1oK0_mt248eCQuavKh6R4irgtA9XxkSUFfAgLf8hAo,9204
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.1.dist-info/entry_points.txt,sha256=ZfjPnJuH8SzvhE9vftIPMBIofsc65IAWYOhqOC_L5ck,65
20
+ analyser_hj3415-3.0.1.dist-info/WHEEL,sha256=CpUCUxeHQbRN5UGRQHYRJorO5Af-Qy_fHMctcQ8DSGI,82
21
+ analyser_hj3415-3.0.1.dist-info/METADATA,sha256=a1hvHhQURatp_A7dL52s5HKQ2KU8Ok6Q_-2AEHsgldQ,6776
22
+ analyser_hj3415-3.0.1.dist-info/RECORD,,