analyser_hj3415 2.10.5__py3-none-any.whl → 3.0.0__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
 
@@ -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.5
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,,