ezKit 1.9.12__tar.gz → 1.10.0__tar.gz

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ezKit
3
- Version: 1.9.12
3
+ Version: 1.10.0
4
4
  Summary: Easy Kit
5
5
  Author: septvean
6
6
  Author-email: septvean@gmail.com
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ezKit
3
- Version: 1.9.12
3
+ Version: 1.10.0
4
4
  Summary: Easy Kit
5
5
  Author: septvean
6
6
  Author-email: septvean@gmail.com
@@ -6,14 +6,12 @@ ezKit/__init__.py
6
6
  ezKit/bottle.py
7
7
  ezKit/bottle_extensions.py
8
8
  ezKit/cipher.py
9
- ezKit/cls.py
10
9
  ezKit/database.py
11
10
  ezKit/http.py
12
11
  ezKit/mongo.py
13
12
  ezKit/qywx.py
14
13
  ezKit/redis.py
15
14
  ezKit/sendemail.py
16
- ezKit/stock.py
17
15
  ezKit/token.py
18
16
  ezKit/utils.py
19
17
  ezKit/xftp.py
@@ -3,7 +3,7 @@ from setuptools import find_packages, setup
3
3
 
4
4
  setup(
5
5
  name='ezKit',
6
- version='1.9.12',
6
+ version='1.10.0',
7
7
  author='septvean',
8
8
  author_email='septvean@gmail.com',
9
9
  description='Easy Kit',
ezkit-1.9.12/ezKit/cls.py DELETED
@@ -1,313 +0,0 @@
1
- """财联社数据"""
2
- import re
3
-
4
- import pandas as pd
5
- import requests
6
- from loguru import logger
7
-
8
- from . import stock, utils
9
-
10
-
11
- def up_down_analysis(
12
- target: str = "up_pool",
13
- df: bool = False
14
- ) -> list | pd.DataFrame | None:
15
- """涨停跌停数据"""
16
-
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
24
-
25
- info: str = "获取涨停池股票"
26
- match True:
27
- case True if target == "up_pool":
28
- info = "获取涨停池股票"
29
- case True if target == "continuous_up_pool":
30
- info = "获取连板池股票"
31
- case True if target == "up_open_pool":
32
- info = "获取炸板池股票"
33
- case True if target == "down_pool":
34
- info = "获取跌停池股票"
35
- case _:
36
- pass
37
-
38
- try:
39
- logger.info(f"{info} ......")
40
-
41
- user_agent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"
42
- headers = {"User-Agent": user_agent}
43
-
44
- # 涨停池: https://x-quote.cls.cn/quote/index/up_down_analysis?rever=1&way=last_px&type=up_pool
45
- # 连板池: https://x-quote.cls.cn/quote/index/up_down_analysis?rever=1&way=last_px&type=continuous_up_pool
46
- # 炸板池: https://x-quote.cls.cn/quote/index/up_down_analysis?rever=1&way=last_px&type=up_open_pool
47
- # 跌停池: https://x-quote.cls.cn/quote/index/up_down_analysis?rever=1&way=last_px&type=down_pool
48
- api = f"https://x-quote.cls.cn/quote/index/up_down_analysis?rever=1&way=last_px&type={target}"
49
-
50
- response = requests.get(api, headers=headers, timeout=10)
51
-
52
- response_dict: dict = response.json()
53
-
54
- result: list = []
55
-
56
- for i in response_dict["data"]:
57
-
58
- # if re.match(r"^(sz00|sh60)", i["secu_code"]):
59
- # print(i["secu_code"])
60
-
61
- # if re.search(r"ST|银行", i["secu_name"]):
62
- # print(i["secu_name"])
63
-
64
- # 主板, 非ST, 非银行, 非证券
65
- if (not re.match(r"^(sz00|sh60)", i["secu_code"])) or re.search(r"ST|银行|证券", i["secu_name"]):
66
- continue
67
-
68
- if target in ["up_pool", "up_pool"]:
69
- result.append({
70
- "code": stock.coderename(i["secu_code"], restore=True),
71
- "name": i["secu_name"],
72
- "up_days": i["limit_up_days"],
73
- "reason": i["up_reason"]
74
- })
75
-
76
- if target in ["up_open_pool", "down_pool"]:
77
- result.append({
78
- "code": stock.coderename(i["secu_code"], restore=True),
79
- "name": i["secu_name"]
80
- })
81
-
82
- if not utils.isTrue(df, bool):
83
- logger.success(f"{info} [成功]")
84
- return result
85
-
86
- # data: pd.DataFrame = pd.DataFrame(response_dict["data"], columns=["secu_code", "secu_name", "limit_up_days", "up_reason"])
87
- # data = data.rename(columns={"secu_code": "code", "secu_name": "name", "limit_up_days": "up_days", "up_reason": "reason"})
88
-
89
- return pd.DataFrame(data=pd.DataFrame(result))
90
-
91
- except Exception as e:
92
- logger.error(f"{info} [失败]")
93
- logger.exception(e)
94
- return None
95
-
96
-
97
- # --------------------------------------------------------------------------------------------------
98
-
99
-
100
- def latest_data(
101
- payload: str | dict,
102
- data_type: str = "stock",
103
- df: bool = False
104
- ) -> list | pd.DataFrame | None:
105
- """股票或板块的最新数据"""
106
-
107
- # 热门板块
108
- # https://www.cls.cn/hotPlate
109
- # 行业板块
110
- # https://x-quote.cls.cn/web_quote/plate/plate_list?rever=1&way=change&type=industry
111
- # 概念板块
112
- # https://x-quote.cls.cn/web_quote/plate/plate_list?rever=1&way=change&type=concept
113
- # 地域板块
114
- # https://x-quote.cls.cn/web_quote/plate/plate_list?rever=1&way=change&type=area
115
-
116
- # ----------------------------------------------------------------------------------------------
117
-
118
- # 判断参数类型
119
- match True:
120
- case True if not utils.isTrue(payload, (str, dict)):
121
- logger.error("argument error: payload")
122
- return None
123
- case True if not utils.isTrue(data_type, str):
124
- logger.error("argument error: data_type")
125
- return None
126
- case _:
127
- pass
128
-
129
- # ----------------------------------------------------------------------------------------------
130
-
131
- # 判断数据类型. 数据类型: 个股, 板块 (产业链: industry)
132
- if data_type not in ["stock", "plate"]:
133
- logger.error("data_type error")
134
- return None
135
-
136
- # ----------------------------------------------------------------------------------------------
137
-
138
- # 日志信息
139
-
140
- # 个股 (默认)
141
- info: str = "获取股票最新数据"
142
-
143
- # 板块
144
- if data_type == "plate":
145
- info = "获取板块最新数据"
146
-
147
- # match True:
148
- # case True if data_type == "plate":
149
- # info = "获取板块最新数据"
150
- # case True if data_type == "industry":
151
- # info = "获取产业链最新数据"
152
- # case _:
153
- # pass
154
-
155
- # ----------------------------------------------------------------------------------------------
156
-
157
- try:
158
-
159
- logger.info(f"{info} ......")
160
-
161
- # ------------------------------------------------------------------------------------------
162
-
163
- # HTTP User Agent
164
- user_agent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"
165
-
166
- # HTTP Headers
167
- headers = {"User-Agent": user_agent}
168
-
169
- # ------------------------------------------------------------------------------------------
170
-
171
- # 请求参数
172
- params: dict = {}
173
-
174
- # 默认请求参数
175
- if isinstance(payload, str) and utils.isTrue(payload, str):
176
- params = {"secu_code": payload}
177
-
178
- # 请求参数
179
- if isinstance(payload, dict) and utils.isTrue(payload, dict):
180
- params = payload
181
-
182
- # ------------------------------------------------------------------------------------------
183
-
184
- # 不直接在API后面跟参数, 使用 params 传递参数
185
-
186
- # API: 股票
187
- # api: str = f"https://x-quote.cls.cn/quote/stock/basic?secu_code={code}"
188
- api: str = "https://x-quote.cls.cn/quote/stock/basic"
189
-
190
- # API: 板块
191
- if data_type == "plate":
192
- # api = f"https://x-quote.cls.cn/web_quote/plate/stocks?secu_code={code}"
193
- api = "https://x-quote.cls.cn/web_quote/plate/stocks"
194
-
195
- # match True:
196
- # case True if data_type == "plate":
197
- # # 板块
198
- # # api = f"https://x-quote.cls.cn/web_quote/plate/stocks?secu_code={code}"
199
- # api = "https://x-quote.cls.cn/web_quote/plate/stocks"
200
- # case True if data_type == "industry":
201
- # # 产业链
202
- # # api = f"https://x-quote.cls.cn/web_quote/plate/industry?secu_code={code}"
203
- # api = "https://x-quote.cls.cn/web_quote/plate/industry"
204
- # case _:
205
- # pass
206
-
207
- # ------------------------------------------------------------------------------------------
208
-
209
- # 获取数据
210
- # response = requests.get(api, headers=headers, timeout=10)
211
- response = requests.get(api, headers=headers, params=params, timeout=10)
212
-
213
- # 转换数据类型
214
- response_dict: dict = response.json()
215
-
216
- # 判断数据是否正确
217
- if True not in [utils.isTrue(response_dict["data"], dict), utils.isTrue(response_dict["data"], list)]:
218
- logger.error(f"{info} [失败]")
219
- return None
220
-
221
- # ------------------------------------------------------------------------------------------
222
-
223
- # 个股
224
-
225
- if data_type == "stock":
226
-
227
- # 停牌, 返回 None
228
- if response_dict["data"]["trade_status"] == "STOPT":
229
- logger.error(f"{info} [停牌]")
230
- return None
231
-
232
- # pd.DataFrame 数据
233
- if utils.isTrue(df, bool):
234
- df_data = {
235
- # "date": [pd.to_datetime(date_today)],
236
- "open": [float(response_dict["data"]["open_px"])],
237
- "close": [float(response_dict["data"]["last_px"])],
238
- "high": [float(response_dict["data"]["high_px"])],
239
- "low": [float(response_dict["data"]["low_px"])],
240
- "volume": [int(response_dict["data"]["business_amount"])],
241
- "turnover": [float(response_dict["data"]["tr"])]
242
- }
243
- logger.success(f"{info} [成功]")
244
- return pd.DataFrame(data=df_data)
245
-
246
- # 默认返回的数据
247
- logger.success(f"{info} [成功]")
248
- return response_dict["data"]
249
-
250
- # ------------------------------------------------------------------------------------------
251
-
252
- # 板块
253
-
254
- # 板块数据不能转换为 pd.DataFrame
255
- if (data_type == "plate") and utils.isTrue(df, bool):
256
- logger.error(f"{info} [错误]")
257
- return None
258
-
259
- # 数据结果
260
- result: list = []
261
-
262
- # 筛选 主板, 非ST, 非银行, 非证券 的股票
263
- for i in response_dict["data"]["stocks"]:
264
- if (re.match(r"^(sz00|sh60)", i["secu_code"])) and (not re.search(r"ST|银行|证券", i["secu_name"])):
265
- result.append(i)
266
-
267
- # 返回数据
268
- logger.success(f"{info} [成功]")
269
- return result
270
-
271
- except Exception as e:
272
- logger.error(f"{info} [失败]")
273
- logger.exception(e)
274
- return None
275
-
276
-
277
- # --------------------------------------------------------------------------------------------------
278
-
279
-
280
- def plate_codes(
281
- plate: str
282
- ) -> list | None:
283
- """获取板块成分股代码"""
284
-
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
292
-
293
- info: str = "获取板块成分股代码"
294
-
295
- try:
296
-
297
- logger.info(f"{info} ......")
298
-
299
- items = latest_data(payload=plate, data_type="plate")
300
-
301
- if isinstance(items, list):
302
- codes: list = [stock.coderename(i["secu_code"], restore=True) for i in items]
303
- codes.sort()
304
- logger.success(f"{info} [成功]")
305
- return codes
306
-
307
- logger.error(f"{info} [失败]")
308
- return None
309
-
310
- except Exception as e:
311
- logger.error(f"{info} [失败]")
312
- logger.exception(e)
313
- return None
@@ -1,355 +0,0 @@
1
- """股票"""
2
- import re
3
- from copy import deepcopy
4
-
5
- import akshare as ak
6
- import numpy as np
7
- import talib as ta
8
- from loguru import logger
9
- from pandas import DataFrame
10
- from sqlalchemy.engine import Engine
11
-
12
- from . import utils
13
-
14
-
15
- def coderename(
16
- target: str | dict,
17
- restore: bool = False
18
- ) -> str | dict | None:
19
- """代码重命名"""
20
-
21
- # 正向:
22
- # coderename('000001') => 'sz000001'
23
- # coderename({'code': '000001', 'name': '平安银行'}) => {'code': 'sz000001', 'name': '平安银行'}
24
- # 反向:
25
- # coderename('sz000001', restore=True) => '000001'
26
- # coderename({'code': 'sz000001', 'name': '平安银行'}) => {'code': '000001', 'name': '平安银行'}
27
-
28
- # 判断参数是否正确
29
- match True:
30
- case True if not utils.isTrue(target, (str, dict)):
31
- logger.error("argument error: target")
32
- return None
33
- case _:
34
- pass
35
-
36
- try:
37
-
38
- # 初始化
39
- code_object: dict = {}
40
- code_name: str | dict = ""
41
-
42
- # 判断 target 是 string 还是 dictionary
43
- if isinstance(target, str) and utils.isTrue(target, str):
44
- code_name = target
45
- elif isinstance(target, dict) and utils.isTrue(target, dict):
46
- code_object = deepcopy(target)
47
- code_name = str(deepcopy(target["code"]))
48
- else:
49
- return None
50
-
51
- # 是否还原
52
- if utils.isTrue(restore, bool):
53
- if len(code_name) == 8 and re.match(r"^(sz|sh)", code_name):
54
- code_name = deepcopy(code_name[2:8])
55
- else:
56
- return None
57
- else:
58
- if code_name[0:2] == "00":
59
- code_name = f"sz{code_name}"
60
- elif code_name[0:2] == "60":
61
- code_name = f"sh{code_name}"
62
- else:
63
- return None
64
-
65
- # 返回结果
66
- if utils.isTrue(target, str):
67
- return code_name
68
-
69
- if utils.isTrue(target, dict):
70
- code_object["code"] = code_name
71
- return code_object
72
-
73
- return None
74
-
75
- except Exception as e:
76
- logger.exception(e)
77
- return None
78
-
79
-
80
- # --------------------------------------------------------------------------------------------------
81
-
82
-
83
- def kdj_vector(
84
- df: DataFrame,
85
- kdj_options: tuple[int, int, int] = (9, 3, 3)
86
- ) -> DataFrame | None:
87
- """KDJ计算器"""
88
-
89
- # 计算周期:Calculation Period, 也可使用 Lookback Period 表示回溯周期, 指用于计算指标值的时间周期.
90
- # 移动平均周期: Smoothing Period 或 Moving Average Period, 指对指标进行平滑处理时采用的周期.
91
- # 同花顺默认参数: 9 3 3
92
- # https://www.daimajiaoliu.com/daima/4ed4ffa26100400
93
- # 说明: KDJ 指标的中文名称又叫随机指标, 融合了动量观念、强弱指标和移动平均线的一些优点, 能够比较迅速、快捷、直观地研判行情, 被广泛用于股市的中短期趋势分析.
94
- # 有采用 ewm 使用 com=2 的, 但是如果使用 com=2 在默认值的情况下KDJ值是正确的.
95
- # 但是非默认值, 比如调整参数, 尝试慢速 KDJ 时就不对了, 最终采用 alpha = 1/m 的情况, 对比同花顺数据, 是正确的.
96
-
97
- # 检查参数
98
- if isinstance(df, DataFrame) and df.empty:
99
- logger.error("argument error: df")
100
- return None
101
-
102
- if not utils.check_arguments([(kdj_options, tuple, "kdj_options")]):
103
- return None
104
-
105
- if not all(utils.isTrue(item, int) for item in kdj_options):
106
- logger.error("argument error: kdj_options")
107
- return None
108
-
109
- try:
110
- low_list = df['low'].rolling(kdj_options[0]).min()
111
- high_list = df['high'].rolling(kdj_options[0]).max()
112
- rsv = (df['close'] - low_list) / (high_list - low_list) * 100
113
- df['K'] = rsv.ewm(alpha=1 / kdj_options[1], adjust=False).mean()
114
- df['D'] = df['K'].ewm(alpha=1 / kdj_options[2], adjust=False).mean()
115
- df['J'] = (3 * df['K']) - (2 * df['D'])
116
- return df
117
- except Exception as e:
118
- logger.exception(e)
119
- return None
120
-
121
-
122
- # --------------------------------------------------------------------------------------------------
123
-
124
-
125
- def data_vector(
126
- df: DataFrame,
127
- macd_options: tuple[int, int, int] = (12, 26, 9),
128
- kdj_options: tuple[int, int, int] = (9, 3, 3)
129
- ) -> DataFrame | None:
130
- """数据运算"""
131
-
132
- # 检查参数
133
- if isinstance(df, DataFrame) and df.empty:
134
- logger.error("argument error: df")
135
- return None
136
-
137
- if not utils.check_arguments([(macd_options, tuple, "macd_options"), (kdj_options, tuple, "kdj_options")]):
138
- return None
139
-
140
- if not all(utils.isTrue(item, int) for item in macd_options):
141
- logger.error("argument error: macd_options")
142
- return None
143
-
144
- if not all(utils.isTrue(item, int) for item in kdj_options):
145
- logger.error("argument error: kdj_options")
146
- return None
147
-
148
- try:
149
-
150
- # ------------------------------------------------------------------------------------------
151
-
152
- # 计算均线: 3,7日均线
153
- # pylint: disable=E1101
154
- # df['SMA03'] = ta.SMA(df['close'], timeperiod=3) # type: ignore
155
- # df['SMA07'] = ta.SMA(df['close'], timeperiod=7) # type: ignore
156
-
157
- # 3,7日均线金叉: 0 无, 1 金叉, 2 死叉
158
- # df['SMA37_X'] = 0
159
- # sma37_position = df['SMA03'] > df['SMA07']
160
- # df.loc[sma37_position[(sma37_position is True) & (sma37_position.shift() is False)].index, 'SMA37_X'] = 1 # type: ignore
161
- # df.loc[sma37_position[(sma37_position is False) & (sma37_position.shift() is True)].index, 'SMA37_X'] = 2 # type: ignore
162
-
163
- # 计算均线: 20,25日均线
164
- # df['SMA20'] = ta.SMA(df['close'], timeperiod=20) # type: ignore
165
- # df['SMA25'] = ta.SMA(df['close'], timeperiod=25) # type: ignore
166
-
167
- # 20,25日均线金叉: 0 无, 1 金叉, 2 死叉
168
- # df['SMA225_X'] = 0
169
- # sma225_position = df['SMA20'] > df['SMA25']
170
- # df.loc[sma225_position[(sma225_position is True) & (sma225_position.shift() is False)].index, 'SMA225_X'] = 1 # type: ignore
171
- # df.loc[sma225_position[(sma225_position is False) & (sma225_position.shift() is True)].index, 'SMA225_X'] = 2 # type: ignore
172
-
173
- # ------------------------------------------------------------------------------------------
174
-
175
- # 计算 MACD: 默认参数 12 26 9
176
- macd_dif, macd_dea, macd_bar = ta.MACD( # type: ignore
177
- df['close'].values,
178
- fastperiod=macd_options[0],
179
- slowperiod=macd_options[1],
180
- signalperiod=macd_options[2]
181
- )
182
-
183
- macd_dif[np.isnan(macd_dif)], macd_dea[np.isnan(macd_dea)], macd_bar[np.isnan(macd_bar)] = 0, 0, 0
184
-
185
- # https://www.bilibili.com/read/cv10185856
186
- df['MACD'] = 2 * (macd_dif - macd_dea)
187
- df['MACD_DIF'] = macd_dif
188
- df['MACD_DEA'] = macd_dea
189
-
190
- # 初始化 MACD_X 列(0 无, 1 金叉, 2 死叉)
191
- df['MACD_X'] = 0
192
-
193
- # 计算 MACD 条件
194
- macd_position = df['MACD_DIF'] > df['MACD_DEA']
195
-
196
- # 设置 MACD_X = 1: 从 False 变为 True 的位置
197
- df.loc[macd_position & ~macd_position.shift(fill_value=False), 'MACD_X'] = 1
198
-
199
- # 设置 MACD_X = 2: 从 True 变为 False 的位置
200
- df.loc[~macd_position & macd_position.shift(fill_value=False), 'MACD_X'] = 2
201
-
202
- # 将浮点数限制为小数点后两位
203
- df['MACD'] = df['MACD'].round(2)
204
- df['MACD_DIF'] = df['MACD_DIF'].round(2)
205
- df['MACD_DEA'] = df['MACD_DEA'].round(2)
206
-
207
- # ------------------------------------------------------------------------------------------
208
-
209
- # # 计算 KDJ: : 默认参数 9 3 3
210
- kdj_data = kdj_vector(df, kdj_options)
211
-
212
- if kdj_data is not None:
213
-
214
- # KDJ 数据
215
- df['K'] = kdj_data['K'].values
216
- df['D'] = kdj_data['D'].values
217
- df['J'] = kdj_data['J'].values
218
-
219
- # 初始化 KDJ_X 列(0 无, 1 金叉, 2 死叉)
220
- df['KDJ_X'] = 0
221
-
222
- # 计算 MACD 条件
223
- kdj_position = df['J'] > df['D']
224
-
225
- # 设置 KDJ_X = 1: 从 False 变为 True 的位置
226
- df.loc[kdj_position & ~kdj_position.shift(fill_value=False), 'KDJ_X'] = 1
227
-
228
- # 设置 KDJ_X = 2: 从 True 变为 False 的位置
229
- df.loc[~kdj_position & kdj_position.shift(fill_value=False), 'KDJ_X'] = 2
230
-
231
- # 将浮点数限制为小数点后两位
232
- df['K'] = df['K'].round(2)
233
- df['D'] = df['D'].round(2)
234
- df['J'] = df['J'].round(2)
235
-
236
- # ------------------------------------------------------------------------------------------
237
-
238
- return df
239
-
240
- except Exception as e:
241
- logger.exception(e)
242
- return None
243
-
244
-
245
- # --------------------------------------------------------------------------------------------------
246
-
247
-
248
- def get_code_name_from_akshare() -> DataFrame | None:
249
- """获取股票代码和名称"""
250
- info = "获取股票代码和名称"
251
- try:
252
- logger.info(f"{info} ......")
253
- df: DataFrame = ak.stock_info_a_code_name()
254
- if df.empty:
255
- logger.error(f"{info} [失败]")
256
- return None
257
- # 排除 ST、证券和银行
258
- # https://towardsdatascience.com/8-ways-to-filter-pandas-dataframes-d34ba585c1b8
259
- df = df[df.code.str.contains("^00|^60") & ~df.name.str.contains("ST|证券|银行")]
260
- logger.success(f"{info} [成功]")
261
- return df
262
- except Exception as e:
263
- logger.error(f"{info} [失败]")
264
- logger.exception(e)
265
- return None
266
-
267
-
268
- # --------------------------------------------------------------------------------------------------
269
-
270
-
271
- def get_stock_data_from_akshare(
272
- code: str,
273
- adjust: str = "qfq",
274
- period: str = "daily",
275
- start_date: str = "19700101",
276
- end_date: str = "20500101",
277
- timeout: float = 10
278
- ) -> DataFrame | None:
279
- """从 akshare 获取股票数据"""
280
- info = f"获取股票数据: {code}"
281
- try:
282
- logger.info(f"{info} ......")
283
- # https://akshare.akfamily.xyz/data/stock/stock.html#id22
284
- df: DataFrame = ak.stock_zh_a_hist(symbol=code, adjust=adjust, period=period, start_date=start_date, end_date=end_date, timeout=timeout)
285
- df = df.rename(columns={
286
- "日期": "date",
287
- "开盘": "open",
288
- "收盘": "close",
289
- "最高": "high",
290
- "最低": "low",
291
- "成交量": "volume"
292
- })
293
- logger.success(f"{info} [成功]")
294
- return df[['date', 'open', 'close', 'high', 'low', 'volume']].copy()
295
- except Exception as e:
296
- logger.error(f"{info} [失败]")
297
- logger.exception(e)
298
- return None
299
-
300
-
301
- # --------------------------------------------------------------------------------------------------
302
-
303
-
304
- def save_data_to_database(engine: Engine, code: str, latest: bool = False) -> bool:
305
- """保存股票所有数据到数据库"""
306
-
307
- # 默认将所有数据保存到数据库中的表里
308
- # 如果 latest 为 True, 插入最新的数据到数据库中的表里
309
- # 即: 将最后一条数据插入到数据库中的表里
310
-
311
- info: str = "保存股票所有数据到数据库"
312
-
313
- if utils.isTrue(latest, bool):
314
- info = "保存股票最新数据到数据库"
315
-
316
- try:
317
-
318
- logger.info(f"{info} ......")
319
-
320
- # 代码名称转换
321
- name = coderename(code)
322
-
323
- if not isinstance(name, str):
324
- logger.error(f"{info} [代码名称转换错误]")
325
- return False
326
-
327
- # 获取数据
328
- df: DataFrame | None = get_stock_data_from_akshare(code)
329
-
330
- if df is None:
331
- logger.error(f"{info} [获取数据错误]")
332
- return False
333
-
334
- # 计算数据
335
- df: DataFrame | None = data_vector(df)
336
-
337
- if df is None:
338
- logger.error(f"{info} [计算数据错误]")
339
- return False
340
-
341
- # 保存到数据库
342
- if utils.isTrue(latest, bool):
343
- df = df.tail(1)
344
- df.to_sql(name=name, con=engine, if_exists="append", index=False)
345
- else:
346
- df.to_sql(name=name, con=engine, if_exists="replace", index=False)
347
-
348
- logger.success(f"{info} [成功]")
349
-
350
- return True
351
-
352
- except Exception as e:
353
- logger.success(f"{info} [失败]")
354
- logger.exception(e)
355
- return False
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