ezKit 1.9.4__tar.gz → 1.9.5__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ezKit
3
- Version: 1.9.4
3
+ Version: 1.9.5
4
4
  Summary: Easy Kit
5
5
  Author: septvean
6
6
  Author-email: septvean@gmail.com
@@ -14,9 +14,13 @@ def up_down_analysis(
14
14
  ) -> list | pd.DataFrame | None:
15
15
  """涨停跌停数据"""
16
16
 
17
- if not utils.isTrue(target, str):
18
- logger.error(f"error type: {target}")
19
- return None
17
+ # 判断参数是否正确
18
+ match True:
19
+ case True if not utils.isTrue(target, str):
20
+ logger.error("argument error: target")
21
+ return None
22
+ case _:
23
+ pass
20
24
 
21
25
  info: str = "获取涨停池股票"
22
26
  match True:
@@ -113,11 +117,11 @@ def latest_data(
113
117
 
114
118
  # 判断参数类型
115
119
  match True:
116
- case True if True not in [isinstance(payload, str), isinstance(payload, dict)]:
117
- logger.error("Incorrect function argument type: payload")
120
+ case True if not utils.isTrue(payload, (str, dict)):
121
+ logger.error("argument error: payload")
118
122
  return None
119
- case True if False in [isinstance(data_type, str), utils.isTrue(data_type, str)]:
120
- logger.error("Incorrect function argument type: data_type")
123
+ case True if not utils.isTrue(data_type, str):
124
+ logger.error("argument error: data_type")
121
125
  return None
122
126
  case _:
123
127
  pass
@@ -273,12 +277,18 @@ def latest_data(
273
277
  # --------------------------------------------------------------------------------------------------
274
278
 
275
279
 
276
- def plate_codes(plate: str) -> list | None:
280
+ def plate_codes(
281
+ plate: str
282
+ ) -> list | None:
277
283
  """获取板块成分股代码"""
278
284
 
279
- if not utils.isTrue(plate, str):
280
- logger.error("Incorrect function argument type: plate")
281
- return None
285
+ # 判断参数是否正确
286
+ match True:
287
+ case True if not utils.isTrue(plate, str):
288
+ logger.error("argument error: plate")
289
+ return None
290
+ case _:
291
+ pass
282
292
 
283
293
  info: str = "获取板块成分股代码"
284
294
 
@@ -70,24 +70,6 @@ def sendemail(
70
70
  path 图片路径
71
71
  """
72
72
 
73
- # 参数判断
74
- # match True:
75
- # case True if utils.vTrue(smtp, dict) == False:
76
- # logger.error('ERROR!! {} is not dictionary or none'.format('smtp'))
77
- # return False
78
- # case True if utils.vTrue(sender, dict) == False:
79
- # logger.error('ERROR!! {} is not dictionary or none'.format('sender'))
80
- # return False
81
- # case True if (utils.vTrue(recipients, str) == False) and (utils.vTrue(recipients, list) == False):
82
- # logger.error('ERROR!! {} is not list or none'.format('recipients'))
83
- # return False
84
- # case True if utils.vTrue(subject, str) == False:
85
- # logger.error('ERROR!! {} is not string or none'.format('subject'))
86
- # return False
87
- # case True if utils.vTrue(html_file, str) == False:
88
- # logger.error('ERROR!! {} is not string or none'.format('html_file'))
89
- # return False
90
-
91
73
  logger.success("sendemail start")
92
74
 
93
75
  try:
@@ -12,7 +12,10 @@ from sqlalchemy.engine import Engine
12
12
  from . import utils
13
13
 
14
14
 
15
- def coderename(target: str | dict, restore: bool = False) -> str | dict | None:
15
+ def coderename(
16
+ target: str | dict,
17
+ restore: bool = False
18
+ ) -> str | dict | None:
16
19
  """代码重命名"""
17
20
 
18
21
  # 正向:
@@ -22,18 +25,10 @@ def coderename(target: str | dict, restore: bool = False) -> str | dict | None:
22
25
  # coderename('sz000001', restore=True) => '000001'
23
26
  # coderename({'code': 'sz000001', 'name': '平安银行'}) => {'code': '000001', 'name': '平安银行'}
24
27
 
25
- # 判断参数类型
28
+ # 判断参数是否正确
26
29
  match True:
27
- case True if True not in [isinstance(target, str), isinstance(target, dict)]:
28
- logger.error("argument type error: target")
29
- return None
30
- case _:
31
- pass
32
-
33
- # 判断参数数据
34
- match True:
35
- case True if True not in [utils.isTrue(target, str), utils.isTrue(target, dict)]:
36
- logger.error("argument data error: data")
30
+ case True if not utils.isTrue(target, (str, dict)):
31
+ logger.error("argument error: target")
37
32
  return None
38
33
  case _:
39
34
  pass
@@ -85,7 +80,10 @@ def coderename(target: str | dict, restore: bool = False) -> str | dict | None:
85
80
  # --------------------------------------------------------------------------------------------------
86
81
 
87
82
 
88
- def kdj_vector(df: DataFrame, cp: int = 9, sp1: int = 3, sp2: int = 3) -> DataFrame | None:
83
+ def kdj_vector(
84
+ df: DataFrame,
85
+ kdj_options: tuple[int, int, int] = (9, 3, 3)
86
+ ) -> DataFrame | None:
89
87
  """KDJ计算器"""
90
88
 
91
89
  # 计算周期:Calculation Period, 也可使用 Lookback Period 表示回溯周期, 指用于计算指标值的时间周期.
@@ -96,20 +94,23 @@ def kdj_vector(df: DataFrame, cp: int = 9, sp1: int = 3, sp2: int = 3) -> DataFr
96
94
  # 有采用 ewm 使用 com=2 的, 但是如果使用 com=2 在默认值的情况下KDJ值是正确的.
97
95
  # 但是非默认值, 比如调整参数, 尝试慢速 KDJ 时就不对了, 最终采用 alpha = 1/m 的情况, 对比同花顺数据, 是正确的.
98
96
 
99
- # 判断参数类型
97
+ # 判断参数是否正确
100
98
  match True:
101
- case True if not isinstance(df, DataFrame):
102
- logger.error("argument type error: df")
99
+ case True if not utils.isTrue(df, DataFrame):
100
+ logger.error("argument error: df")
101
+ return None
102
+ case True if True not in [utils.isTrue(kdj_options, tuple), all(utils.isTrue(item, int) for item in kdj_options)]:
103
+ logger.error("argument error: kdj_options")
103
104
  return None
104
105
  case _:
105
106
  pass
106
107
 
107
108
  try:
108
- low_list = df['low'].rolling(cp).min()
109
- high_list = df['high'].rolling(cp).max()
109
+ low_list = df['low'].rolling(kdj_options[0]).min()
110
+ high_list = df['high'].rolling(kdj_options[0]).max()
110
111
  rsv = (df['close'] - low_list) / (high_list - low_list) * 100
111
- df['K'] = rsv.ewm(alpha=1 / sp1, adjust=False).mean()
112
- df['D'] = df['K'].ewm(alpha=1 / sp2, adjust=False).mean()
112
+ df['K'] = rsv.ewm(alpha=1 / kdj_options[1], adjust=False).mean()
113
+ df['D'] = df['K'].ewm(alpha=1 / kdj_options[2], adjust=False).mean()
113
114
  df['J'] = (3 * df['K']) - (2 * df['D'])
114
115
  return df
115
116
  except Exception as e:
@@ -127,6 +128,20 @@ def data_vector(
127
128
  ) -> DataFrame | None:
128
129
  """数据运算"""
129
130
 
131
+ # 判断参数是否正确
132
+ match True:
133
+ case True if not utils.isTrue(df, DataFrame):
134
+ logger.error("argument error: df")
135
+ return None
136
+ case True if True not in [utils.isTrue(macd_options, tuple), all(utils.isTrue(item, int) for item in macd_options)]:
137
+ logger.error("argument error: macd_options")
138
+ return None
139
+ case True if True not in [utils.isTrue(kdj_options, tuple), all(utils.isTrue(item, int) for item in kdj_options)]:
140
+ logger.error("argument error: kdj_options")
141
+ return None
142
+ case _:
143
+ pass
144
+
130
145
  try:
131
146
 
132
147
  # 数据为空
@@ -176,7 +191,7 @@ def data_vector(
176
191
  # ------------------------------------------------------------------------------------------
177
192
 
178
193
  # 计算 KDJ: : 默认参数 9 3 3
179
- kdj_data = kdj_vector(df, kdj_options[0], kdj_options[1], kdj_options[2])
194
+ kdj_data = kdj_vector(df, kdj_options)
180
195
 
181
196
  if kdj_data is not None:
182
197
 
@@ -226,12 +241,29 @@ def get_code_name_from_akshare() -> DataFrame | None:
226
241
  # --------------------------------------------------------------------------------------------------
227
242
 
228
243
 
229
- def get_stock_data_from_akshare(code: str) -> DataFrame | None:
244
+ def get_stock_data_from_akshare(
245
+ code: str,
246
+ adjust: str = "qfq",
247
+ period: str = "daily",
248
+ start_date: str = "19700101",
249
+ end_date: str = "20500101",
250
+ timeout: float = 10
251
+ ) -> DataFrame | None:
230
252
  """从 akshare 获取数据"""
231
253
  info = f"获取股票所有数据: {code}"
232
254
  try:
233
255
  logger.info(f"{info} ......")
234
- df: DataFrame = ak.stock_zh_a_daily(symbol=code, adjust="qfq")
256
+ # https://akshare.akfamily.xyz/data/stock/stock.html#id22
257
+ df: DataFrame = ak.stock_zh_a_hist(symbol=code, adjust=adjust, period=period, start_date=start_date, end_date=end_date, timeout=timeout)
258
+ df = df.rename(columns={
259
+ "日期": "date",
260
+ "开盘": "open",
261
+ "收盘": "close",
262
+ "最高": "high",
263
+ "最低": "low",
264
+ "成交量": "volume",
265
+ "成交额": "turnover"
266
+ })
235
267
  df = df.round({'turnover': 4})
236
268
  logger.success(f"{info} [成功]")
237
269
  return df[['date', 'open', 'close', 'high', 'low', 'volume', 'turnover']].copy()
@@ -50,6 +50,11 @@ def isTrue(
50
50
  # 判断变量类型: isinstance(x, str)
51
51
  #
52
52
  # 函数使用 callable(func) 判断
53
+ #
54
+ # 判断多个类型:
55
+ #
56
+ # isTrue("abc", (str, int))
57
+ # isTrue("abc", (str | int))
53
58
 
54
59
  try:
55
60
 
@@ -100,33 +105,39 @@ def os_environ(
100
105
  name: str,
101
106
  value: Any = None
102
107
  ) -> Any:
103
- """
104
- 系统变量
108
+ """系统变量"""
109
+
110
+ # 伪全局变量
111
+ # Python 没有全局变量, 多个文件无法调用同一个变量.
112
+ # 为了解决这个问题, 将变量设置为系统变量, 从而实现多个文件调用同一个变量.
113
+
114
+ # 判断参数是否正确
115
+ match True:
116
+ case True if not isTrue(name, str):
117
+ logger.error("argument error: name")
118
+ return None
119
+ case _:
120
+ pass
105
121
 
106
- 伪全局变量
107
- Python 没有全局变量, 多个文件无法调用同一个变量.
108
- 为了解决这个问题, 将变量设置为系统变量, 从而实现多个文件调用同一个变量.
109
- """
110
122
  try:
111
123
 
112
124
  # 变量名添加一个前缀, 防止和系统中其它变量名冲突
113
125
  _variable_name = f'PYTHON_VARIABLE_{name}'
114
126
 
127
+ # 如果 value 的值是 None, 则从系统环境获取变量数据
115
128
  if value is None:
116
129
 
117
130
  _data = os.environ.get(_variable_name)
118
131
 
119
132
  # 判断是否有数据
120
- if _data:
121
- try:
122
- # 如果环境变量有值, 使用 json.loads() 解析
123
- parsed_data = json.loads(_data)
124
- return parsed_data
125
- except json.JSONDecodeError:
126
- return None
127
- else:
133
+ if _data is None or not isTrue(_data, str):
128
134
  return None
129
135
 
136
+ # 使用 json.loads() 解析数据
137
+ parsed_data = json.loads(_data)
138
+ return parsed_data
139
+
140
+ # 如果 value 的值不是 None, 则保存数据到系统环境变量
130
141
  _data = json.dumps(value)
131
142
  os.environ[_variable_name] = _data
132
143
 
@@ -143,29 +154,52 @@ def os_environ(
143
154
  def mam_of_numbers(
144
155
  numbers: list | tuple,
145
156
  dest_type: str | None = None
146
- ) -> tuple[int | float, int | float, int | float] | tuple[None, None, None]:
147
- """
148
- (maximum, average, minimum)
157
+ ) -> tuple | None:
158
+ """返回一组数字中的 最大值(maximum), 平均值(average), 最小值(minimum)"""
149
159
 
150
- 返回一组数字中的 最大值(maximum), 平均值(average), 最小值(minimum)
151
- numbers 数字列表 (仅支持 list 和 tuple, 不支 set)
152
- dest_type 目标类型 (将数字列表中的数字转换成统一的类型)
153
- """
160
+ # numbers 数字列表 (仅支持 list 和 tuple, 不支 set)
161
+ # dest_type 目标类型 (将数字列表中的数字转换成统一的类型)
162
+
163
+ # 判断参数是否正确
164
+ match True:
165
+ case True if not isTrue(numbers, (list, tuple)):
166
+ logger.error("argument error: numbers")
167
+ return None
168
+ case True if True not in [isTrue(dest_type, str), dest_type is None]:
169
+ logger.error("argument error: dest_type")
170
+ return None
171
+ case _:
172
+ pass
154
173
 
155
174
  try:
175
+
156
176
  _numbers = deepcopy(numbers)
177
+
178
+ # 转换数据类型
157
179
  match True:
158
- case True if dest_type == 'float':
180
+ case True if dest_type == "float":
159
181
  _numbers = [float(i) for i in numbers]
160
- case True if dest_type == 'int':
182
+ case True if dest_type == "int":
161
183
  _numbers = [int(i) for i in numbers]
184
+ case _:
185
+ pass
186
+
187
+ # 提取数据
162
188
  _num_max = max(_numbers)
163
- _num_avg = sum(_numbers) / len(_numbers)
189
+ _num_avg = f"{sum(_numbers) / len(_numbers):.2f}"
164
190
  _num_min = min(_numbers)
191
+
192
+ if dest_type == int:
193
+ _num_avg = int(_num_avg)
194
+ else:
195
+ _num_avg = float(_num_avg)
196
+
197
+ # 返回数据
165
198
  return _num_max, _num_avg, _num_min
199
+
166
200
  except Exception as e:
167
201
  logger.exception(e)
168
- return None, None, None
202
+ return None
169
203
 
170
204
 
171
205
  # --------------------------------------------------------------------------------------------------
@@ -187,10 +221,10 @@ def step_number_for_split_equally(
187
221
 
188
222
  [1, 2, 3, 4, 5, 6, 7, 8, 9]
189
223
 
190
- 分成 2 份 -> [[1, 2, 3, 4, 5], [6, 7, 8, 9]] -> 返回 5
191
- 分成 3 份 -> [[1, 2, 3], [4, 5, 6], [7, 8, 9]] -> 返回 3
192
- 分成 4 份 -> [[1, 2, 3], [4, 5], [6, 7], [8, 9]] -> 返回 3
193
- 分成 5 份 -> [[1, 2], [3, 4], [5, 6], [7, 8], [9]] -> 返回 2
224
+ 分成 2 份 -> [[1, 2, 3, 4, 5], [6, 7, 8, 9]] -> 返回 5
225
+ 分成 3 份 -> [[1, 2, 3], [4, 5, 6], [7, 8, 9]] -> 返回 3
226
+ 分成 4 份 -> [[1, 2, 3], [4, 5], [6, 7], [8, 9]] -> 返回 3
227
+ 分成 5 份 -> [[1, 2], [3, 4], [5, 6], [7, 8], [9]] -> 返回 2
194
228
  """
195
229
  try:
196
230
  if integer % split_equally_number == 0:
@@ -411,27 +445,16 @@ def list_print_by_step(
411
445
  ) -> bool:
412
446
  """根据 步长 和 分隔符 有规律的打印列表中的数据"""
413
447
 
414
- # 判断参数类型
415
- match True:
416
- case True if not isinstance(data, list):
417
- logger.error("argument type error: data")
418
- return False
419
- case True if not isinstance(step, int):
420
- logger.error("argument type error: step")
421
- return False
422
- case True if not isinstance(separator, str):
423
- logger.error("argument type error: separator")
424
- return False
425
- case _:
426
- pass
427
-
428
- # 判断参数数据
448
+ # 判断参数是否正确
429
449
  match True:
430
450
  case True if not isTrue(data, list):
431
- logger.error("argument data error: data")
451
+ logger.error("argument error: data")
432
452
  return False
433
453
  case True if not isTrue(step, int):
434
- logger.error("argument data error: step")
454
+ logger.error("argument error: step")
455
+ return False
456
+ case True if not isTrue(separator, str):
457
+ logger.error("argument error: separator")
435
458
  return False
436
459
  case _:
437
460
  pass
@@ -943,7 +966,7 @@ def datetime_local_to_timezone(
943
966
  tz: datetime.timezone = datetime.timezone.utc
944
967
  ) -> datetime.datetime | None:
945
968
  """
946
- Local datetime to TimeZone datetime (默认转换为 UTC datetime)
969
+ Local datetime to TimeZone datetime(默认转换为 UTC datetime)
947
970
  replace(tzinfo=None) 移除结尾的时区信息
948
971
  """
949
972
  try:
@@ -960,7 +983,7 @@ def datetime_utc_to_timezone(
960
983
  tz: Any = datetime.datetime.now(datetime.timezone.utc).astimezone().tzinfo
961
984
  ) -> datetime.datetime | None:
962
985
  """
963
- UTC datetime to TimeZone datetime (默认转换为 Local datetime)
986
+ UTC datetime to TimeZone datetime(默认转换为 Local datetime)
964
987
  replace(tzinfo=None) 移除结尾的时区信息
965
988
  """
966
989
  try:
@@ -1173,25 +1196,25 @@ def delete_directory(
1173
1196
  """
1174
1197
  delete directory
1175
1198
 
1176
- https://docs.python.org/3/library/os.html#os.rmdir
1199
+ https: // docs.python.org / 3 / library / os.html # os.rmdir
1177
1200
 
1178
1201
  os.rmdir(path, *, dir_fd=None)
1179
1202
 
1180
- Remove (delete) the directory path.
1203
+ Remove(delete) the directory path.
1181
1204
 
1182
1205
  If the directory does not exist or is not empty, an FileNotFoundError or an OSError is raised respectively.
1183
1206
 
1184
1207
  In order to remove whole directory trees, shutil.rmtree() can be used.
1185
1208
 
1186
- https://docs.python.org/3/library/shutil.html#shutil.rmtree
1209
+ https: // docs.python.org / 3 / library / shutil.html # shutil.rmtree
1187
1210
 
1188
1211
  shutil.rmtree(path, ignore_errors=False, onerror=None)
1189
1212
 
1190
- Delete an entire directory tree; path must point to a directory (but not a symbolic link to a directory).
1213
+ Delete an entire directory tree; path must point to a directory(but not a symbolic link to a directory).
1191
1214
 
1192
1215
  If ignore_errors is true, errors resulting from failed removals will be ignored;
1193
1216
 
1194
- if false or omitted, such errors are handled by calling a handler specified by onerror or, if that is omitted, they raise an exception.
1217
+ if false or omitted, such errors are handled by calling a handler specified by onerror or , if that is omitted, they raise an exception.
1195
1218
  """
1196
1219
  try:
1197
1220
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ezKit
3
- Version: 1.9.4
3
+ Version: 1.9.5
4
4
  Summary: Easy Kit
5
5
  Author: septvean
6
6
  Author-email: septvean@gmail.com
@@ -3,7 +3,7 @@ from setuptools import find_packages, setup
3
3
 
4
4
  setup(
5
5
  name='ezKit',
6
- version='1.9.4',
6
+ version='1.9.5',
7
7
  author='septvean',
8
8
  author_email='septvean@gmail.com',
9
9
  description='Easy Kit',
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes