ezKit 1.9.1__py3-none-any.whl → 1.9.3__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.
ezKit/stock.py CHANGED
@@ -1,60 +1,261 @@
1
1
  """股票"""
2
+ import re
2
3
  from copy import deepcopy
3
- from typing import Any
4
4
 
5
+ import akshare as ak
6
+ import numpy as np
7
+ import talib as ta
5
8
  from loguru import logger
9
+ from pandas import DataFrame
10
+ from sqlalchemy.engine import Engine
6
11
 
7
12
  from . import utils
8
13
 
9
14
 
10
15
  def coderename(target: str | dict, restore: bool = False) -> str | dict | None:
11
- """
12
- 正向:
13
- coderename('000001') => 'sz000001'
14
- coderename({'code': '000001', 'name': '平安银行'}) => {'code': 'sz000001', 'name': '平安银行'}
15
- 反向:
16
- coderename('sz000001', restore=True) => '000001'
17
- coderename({'code': 'sz000001', 'name': '平安银行'}) => {'code': '000001', 'name': '平安银行'}
18
- """
16
+ """代码重命名"""
17
+
18
+ # 正向:
19
+ # coderename('000001') => 'sz000001'
20
+ # coderename({'code': '000001', 'name': '平安银行'}) => {'code': 'sz000001', 'name': '平安银行'}
21
+ # 反向:
22
+ # coderename('sz000001', restore=True) => '000001'
23
+ # coderename({'code': 'sz000001', 'name': '平安银行'}) => {'code': '000001', 'name': '平安银行'}
24
+
25
+ # 判断参数类型
26
+ 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")
37
+ return None
38
+ case _:
39
+ pass
19
40
 
20
41
  try:
21
42
 
22
- _object: Any = None
23
- _code_name: Any = None
43
+ # 初始化
44
+ code_object: dict = {}
45
+ code_name: str | dict = ""
24
46
 
25
47
  # 判断 target 是 string 还是 dictionary
26
48
  if isinstance(target, str) and utils.isTrue(target, str):
27
- _code_name = target
49
+ code_name = target
28
50
  elif isinstance(target, dict) and utils.isTrue(target, dict):
29
- _object = deepcopy(target)
30
- _code_name = str(deepcopy(target["code"]))
51
+ code_object = deepcopy(target)
52
+ code_name = str(deepcopy(target["code"]))
31
53
  else:
32
54
  return None
33
55
 
34
56
  # 是否还原
35
- if restore:
36
- if len(_code_name) == 8 and ("sh" in _code_name or "sz" in _code_name):
37
- _code_name = _code_name[2:8]
57
+ if utils.isTrue(restore, bool):
58
+ if len(code_name) == 8 and re.match(r"^(sz|sh)", code_name):
59
+ code_name = deepcopy(code_name[2:8])
38
60
  else:
39
61
  return None
40
62
  else:
41
- if _code_name[0:2] == "00":
42
- _code_name = "sz" + _code_name
43
- elif _code_name[0:2] == "60":
44
- _code_name = "sh" + _code_name
63
+ if code_name[0:2] == "00":
64
+ code_name = f"sz{code_name}"
65
+ elif code_name[0:2] == "60":
66
+ code_name = f"sh{code_name}"
45
67
  else:
46
68
  return None
47
69
 
48
70
  # 返回结果
49
71
  if utils.isTrue(target, str):
50
- return _code_name
72
+ return code_name
51
73
 
52
74
  if utils.isTrue(target, dict):
53
- _object["code"] = _code_name
54
- return _object
75
+ code_object["code"] = code_name
76
+ return code_object
55
77
 
56
78
  return None
57
79
 
58
80
  except Exception as e:
59
81
  logger.exception(e)
60
82
  return None
83
+
84
+
85
+ # --------------------------------------------------------------------------------------------------
86
+
87
+
88
+ def kdj_vector(df: DataFrame, cp: int = 9, sp1: int = 3, sp2: int = 3) -> DataFrame | None:
89
+ """KDJ计算器"""
90
+
91
+ # 计算周期:Calculation Period, 也可使用 Lookback Period 表示回溯周期, 指用于计算指标值的时间周期.
92
+ # 移动平均周期: Smoothing Period 或 Moving Average Period, 指对指标进行平滑处理时采用的周期.
93
+ # 同花顺默认参数: 9 3 3
94
+ # https://www.daimajiaoliu.com/daima/4ed4ffa26100400
95
+ # 说明: KDJ 指标的中文名称又叫随机指标, 融合了动量观念、强弱指标和移动平均线的一些优点, 能够比较迅速、快捷、直观地研判行情, 被广泛用于股市的中短期趋势分析.
96
+ # 有采用 ewm 使用 com=2 的, 但是如果使用 com=2 在默认值的情况下KDJ值是正确的.
97
+ # 但是非默认值, 比如调整参数, 尝试慢速 KDJ 时就不对了, 最终采用 alpha = 1/m 的情况, 对比同花顺数据, 是正确的.
98
+
99
+ # 判断参数类型
100
+ match True:
101
+ case True if not isinstance(df, DataFrame):
102
+ logger.error("argument type error: df")
103
+ return None
104
+ case _:
105
+ pass
106
+
107
+ try:
108
+ low_list = df['low'].rolling(cp).min()
109
+ high_list = df['high'].rolling(cp).max()
110
+ 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()
113
+ df['J'] = (3 * df['K']) - (2 * df['D'])
114
+ return df
115
+ except Exception as e:
116
+ logger.exception(e)
117
+ return None
118
+
119
+
120
+ # --------------------------------------------------------------------------------------------------
121
+
122
+
123
+ def data_vector(
124
+ df: DataFrame,
125
+ macd_options: tuple[int, int, int] = (12, 26, 9),
126
+ kdj_options: tuple[int, int, int] = (9, 3, 3)
127
+ ) -> DataFrame | None:
128
+ """数据运算"""
129
+
130
+ try:
131
+
132
+ # 数据为空
133
+ if isinstance(df, DataFrame) and df.empty:
134
+ return None
135
+
136
+ # ------------------------------------------------------------------------------------------
137
+
138
+ # 计算均线: 3,7日均线
139
+ # pylint: disable=E1101
140
+ # df['SMA03'] = ta.SMA(df['close'], timeperiod=3) # type: ignore
141
+ # df['SMA07'] = ta.SMA(df['close'], timeperiod=7) # type: ignore
142
+
143
+ # 3,7日均线金叉: 0 无, 1 金叉, 2 死叉
144
+ # df['SMA37_X'] = 0
145
+ # sma37_position = df['SMA03'] > df['SMA07']
146
+ # df.loc[sma37_position[(sma37_position is True) & (sma37_position.shift() is False)].index, 'SMA37_X'] = 1 # type: ignore
147
+ # df.loc[sma37_position[(sma37_position is False) & (sma37_position.shift() is True)].index, 'SMA37_X'] = 2 # type: ignore
148
+
149
+ # 计算均线: 20,25日均线
150
+ # df['SMA20'] = ta.SMA(df['close'], timeperiod=20) # type: ignore
151
+ # df['SMA25'] = ta.SMA(df['close'], timeperiod=25) # type: ignore
152
+
153
+ # 20,25日均线金叉: 0 无, 1 金叉, 2 死叉
154
+ # df['SMA225_X'] = 0
155
+ # sma225_position = df['SMA20'] > df['SMA25']
156
+ # df.loc[sma225_position[(sma225_position is True) & (sma225_position.shift() is False)].index, 'SMA225_X'] = 1 # type: ignore
157
+ # df.loc[sma225_position[(sma225_position is False) & (sma225_position.shift() is True)].index, 'SMA225_X'] = 2 # type: ignore
158
+
159
+ # ------------------------------------------------------------------------------------------
160
+
161
+ # 计算 MACD: 默认参数 12 26 9
162
+ macd_dif, macd_dea, macd_bar = ta.MACD(df['close'].values, fastperiod=macd_options[0], slowperiod=macd_options[1], signalperiod=macd_options[2]) # type: ignore
163
+ macd_dif[np.isnan(macd_dif)], macd_dea[np.isnan(macd_dea)], macd_bar[np.isnan(macd_bar)] = 0, 0, 0
164
+
165
+ # https://www.bilibili.com/read/cv10185856
166
+ df['MACD'] = 2 * (macd_dif - macd_dea)
167
+ df['MACD_DIF'] = macd_dif
168
+ df['MACD_DEA'] = macd_dea
169
+
170
+ # MACD 金叉死叉: 0 无, 1 金叉, 2 死叉
171
+ df['MACD_X'] = 0
172
+ macd_position = df['MACD_DIF'] > df['MACD_DEA']
173
+ df.loc[macd_position[(macd_position is True) & (macd_position.shift() is False)].index, 'MACD_X'] = 1 # type: ignore
174
+ df.loc[macd_position[(macd_position is False) & (macd_position.shift() is True)].index, 'MACD_X'] = 2 # type: ignore
175
+
176
+ # ------------------------------------------------------------------------------------------
177
+
178
+ # 计算 KDJ: : 默认参数 9 3 3
179
+ kdj_data = kdj_vector(df, kdj_options[0], kdj_options[1], kdj_options[2])
180
+
181
+ if kdj_data is not None:
182
+
183
+ # KDJ 数据
184
+ df['K'] = kdj_data['K'].values
185
+ df['D'] = kdj_data['D'].values
186
+ df['J'] = kdj_data['J'].values
187
+
188
+ # KDJ 金叉死叉: 0 无, 1 金叉, 2 死叉
189
+ df['KDJ_X'] = 0
190
+ kdj_position = df['J'] > df['D']
191
+ df.loc[kdj_position[(kdj_position is True) & (kdj_position.shift() is False)].index, 'KDJ_X'] = 1 # type: ignore
192
+ df.loc[kdj_position[(kdj_position is False) & (kdj_position.shift() is True)].index, 'KDJ_X'] = 2 # type: ignore
193
+
194
+ # ------------------------------------------------------------------------------------------
195
+
196
+ return df
197
+
198
+ except Exception as e:
199
+ logger.exception(e)
200
+ return None
201
+
202
+
203
+ # --------------------------------------------------------------------------------------------------
204
+
205
+
206
+ def get_code_name_from_akshare() -> DataFrame | None:
207
+ """获取股票代码和名称"""
208
+ info = "获取股票代码和名称"
209
+ try:
210
+ logger.info(f"{info} ......")
211
+ df: DataFrame = ak.stock_info_a_code_name()
212
+ if df.empty:
213
+ logger.error(f"{info} [失败]")
214
+ return None
215
+ # 排除 ST、证券和银行
216
+ # https://towardsdatascience.com/8-ways-to-filter-pandas-dataframes-d34ba585c1b8
217
+ df = df[df.code.str.contains("^00|^60") & ~df.name.str.contains("ST|证券|银行")]
218
+ logger.success(f"{info} [成功]")
219
+ return df
220
+ except Exception as e:
221
+ logger.error(f"{info} [失败]")
222
+ logger.exception(e)
223
+ return None
224
+
225
+
226
+ # --------------------------------------------------------------------------------------------------
227
+
228
+
229
+ def get_stock_data_from_akshare(code: str) -> DataFrame | None:
230
+ """从 akshare 获取数据"""
231
+ info = f"获取股票所有数据: {code}"
232
+ try:
233
+ logger.info(f"{info} ......")
234
+ df: DataFrame = ak.stock_zh_a_daily(symbol=code, adjust="qfq")
235
+ df = df.round({'turnover': 4})
236
+ logger.success(f"{info} [成功]")
237
+ return df[['date', 'open', 'close', 'high', 'low', 'volume', 'turnover']].copy()
238
+ except Exception as e:
239
+ logger.error(f"{info} [失败]")
240
+ logger.exception(e)
241
+ return None
242
+
243
+
244
+ # --------------------------------------------------------------------------------------------------
245
+
246
+
247
+ def save_data_to_database(engine: Engine, code: str) -> bool:
248
+ """保存股票数据到数据库"""
249
+ info: str = "保存股票数据到数据库"
250
+ try:
251
+ logger.info(f"{info} ......")
252
+ df: DataFrame | None = get_stock_data_from_akshare(code)
253
+ if df is None:
254
+ return False
255
+ df.to_sql(name=code, con=engine, if_exists="replace", index=False)
256
+ logger.success(f"{info} [成功]")
257
+ return True
258
+ except Exception as e:
259
+ logger.success(f"{info} [失败]")
260
+ logger.exception(e)
261
+ return False
ezKit/utils.py CHANGED
@@ -35,23 +35,21 @@ def isTrue(
35
35
  true_list: list | tuple | set | str | None = None,
36
36
  false_list: list | tuple | set | str | None = None
37
37
  ) -> bool:
38
- """
39
- 检查对象类型以及对象数据是否为 True
40
-
41
- 常见类型:
42
-
43
- Boolean bool False
44
- Numbers int/float 0/0.0
45
- String str ""
46
- List list/tuple/set []/()/{}
47
- Dictionary dict {}
48
-
49
- 查看变量类型: type(x)
38
+ """检查对象类型以及对象数据是否为 True"""
50
39
 
51
- 判断变量类型: isinstance(x, str)
52
-
53
- 函数使用 callable(func) 判断
54
- """
40
+ # 常见类型:
41
+ #
42
+ # Boolean bool False
43
+ # Numbers int/float 0/0.0
44
+ # String str ""
45
+ # List list/tuple/set []/()/{}
46
+ # Dictionary dict {}
47
+ #
48
+ # 查看变量类型: type(x)
49
+ #
50
+ # 判断变量类型: isinstance(x, str)
51
+ #
52
+ # 函数使用 callable(func) 判断
55
53
 
56
54
  try:
57
55
 
@@ -411,9 +409,33 @@ def list_print_by_step(
411
409
  step: int,
412
410
  separator: str = " "
413
411
  ) -> bool:
414
- """
415
- 根据 步长 和 分隔符 有规律的打印列表中的数据
416
- """
412
+ """根据 步长 和 分隔符 有规律的打印列表中的数据"""
413
+
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
+ # 判断参数数据
429
+ match True:
430
+ case True if not isTrue(data, list):
431
+ logger.error("argument data error: data")
432
+ return False
433
+ case True if not isTrue(step, int):
434
+ logger.error("argument data error: step")
435
+ return False
436
+ case _:
437
+ pass
438
+
417
439
  try:
418
440
 
419
441
  # result: list = []
@@ -431,31 +453,6 @@ def list_print_by_step(
431
453
 
432
454
  # return True
433
455
 
434
- # 判断参数类型
435
- match True:
436
- case True if isinstance(data, list) is False:
437
- logger.error("argument type error: data")
438
- return False
439
- case True if isinstance(step, int) is False:
440
- logger.error("argument type error: step")
441
- return False
442
- case True if isinstance(separator, str) is False:
443
- logger.error("argument type error: separator")
444
- return False
445
- case _:
446
- pass
447
-
448
- # 判断参数数据
449
- match True:
450
- case True if not isTrue(data, list):
451
- logger.error("argument error: data")
452
- return False
453
- case True if not isTrue(step, int):
454
- logger.error("argument error: step")
455
- return False
456
- case _:
457
- pass
458
-
459
456
  # 打印
460
457
  for i, v in enumerate(data):
461
458
  if i > 0 and i % step == 0:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ezKit
3
- Version: 1.9.1
3
+ Version: 1.9.3
4
4
  Summary: Easy Kit
5
5
  Author: septvean
6
6
  Author-email: septvean@gmail.com
@@ -9,12 +9,12 @@ ezKit/mongo.py,sha256=5KDDYQnOE3lw4QJlLp-EAuzbKM6VzbU0w0GR9iklldA,2379
9
9
  ezKit/qywx.py,sha256=X_H4fzP-iEqeDEbumr7D1bXi6dxczaxfO8iyutzy02s,7171
10
10
  ezKit/redis.py,sha256=5TQ7s8fkc-ArXuHT5gOlkR0EBgm3Z-9yTTtS1BgI5Qk,1951
11
11
  ezKit/sendemail.py,sha256=3YyBqFHPDZl9CwgWtmGABubARE_ih8P473mIcEp5VsY,8193
12
- ezKit/stock.py,sha256=lVzIL1BeB-7Stsm-xToTF7ivZiujozmKKpLErAIN3_A,1730
12
+ ezKit/stock.py,sha256=BWs3G2N3xRfva9yf6K40brnH31V2TROeo9pnl8kgEK8,9954
13
13
  ezKit/token.py,sha256=HKREyZj_T2S8-aFoFIrBXTaCKExQq4zE66OHXhGHqQg,1750
14
- ezKit/utils.py,sha256=H130-ITDTmd4jwiie6pdyv20mPOk0KZxBcgmtey4rf4,43005
14
+ ezKit/utils.py,sha256=q6DbXJNgC6sqzjqUPmxbiSiiXqiQFCVnI3jp_zi1ujs,42927
15
15
  ezKit/xftp.py,sha256=XyIdr_2rxRVLqPofG6fIYWhAMVsFwTyp46dg5P9FLW4,7774
16
- ezKit-1.9.1.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
17
- ezKit-1.9.1.dist-info/METADATA,sha256=Jj9sOr2ut2eUgx6kVPqYkgZPmhQlnQHNltlRFxfqXY4,190
18
- ezKit-1.9.1.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
19
- ezKit-1.9.1.dist-info/top_level.txt,sha256=aYLB_1WODsqNTsTFWcKP-BN0KCTKcV-HZJ4zlHkCFw8,6
20
- ezKit-1.9.1.dist-info/RECORD,,
16
+ ezKit-1.9.3.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
17
+ ezKit-1.9.3.dist-info/METADATA,sha256=i9lzRl59vhpJ4az8LovuKDj_NjDISMpuBQW9zk_gf6w,190
18
+ ezKit-1.9.3.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
19
+ ezKit-1.9.3.dist-info/top_level.txt,sha256=aYLB_1WODsqNTsTFWcKP-BN0KCTKcV-HZJ4zlHkCFw8,6
20
+ ezKit-1.9.3.dist-info/RECORD,,
File without changes
File without changes