kaq-quant-common 0.1.99__py3-none-any.whl → 0.2.2__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.
@@ -1,84 +1,84 @@
1
- import threading
2
- import time
3
- from abc import ABC, abstractmethod
4
-
5
- from kaq_quant_common.utils import logger_utils, signal_utils
6
-
7
-
8
- # 通用的抽象类
9
- class MonitorBase(ABC):
10
- def __init__(self):
11
- # 初始化
12
- self.init()
13
- # 用来标记最后一次收到数据的时间,超过指定时间没有收到数据,认为连接断开
14
- self._keep_alive_deadline = 0
15
-
16
- # 初始化
17
- def init(self):
18
- # 执行初始化
19
- self._do_init()
20
-
21
- @abstractmethod
22
- def _do_init(self):
23
- pass
24
-
25
- # 开始,需要确保这个方法在op线程执行,执行这个方法会阻塞当前线程!
26
- def start(self, block=True):
27
- # 全局退出事件,用于传递终止信号
28
- exit_event = threading.Event()
29
- self._exit_event = exit_event
30
-
31
- logger = logger_utils.get_logger(self)
32
-
33
- #
34
- def handle_terminate_signal(signum, frame=None):
35
- """信号处理函数:捕获终止信号并触发退出事件"""
36
- logger.info(f"收到终止信号 {signum}")
37
- exit_event.set()
38
- # 优雅地停止
39
- try:
40
- self.stop()
41
- except Exception as e:
42
- pass
43
-
44
- signal_utils.register_signal_handler(handle_terminate_signal)
45
-
46
- self._do_start()
47
-
48
- # 需要阻塞
49
- if block:
50
- # 监听退出事件
51
- while not exit_event.is_set():
52
- time.sleep(1)
53
- if self._check_exit():
54
- # 假装发送信号
55
- handle_terminate_signal("exit")
56
- if self._keep_alive_deadline > 0 and time.time() > self._keep_alive_deadline:
57
- logger.warning("MonitorBase 收据接收超时,需要停止 MonitorBase")
58
- # 假装发送信号
59
- handle_terminate_signal("recv time out")
60
-
61
- logger.warning("MonitorBase 线程退出")
62
-
63
- # 子类实现,判断连接是否断开,断开返回True
64
- def _check_exit(self):
65
- return False
66
-
67
- @abstractmethod
68
- def _do_start(self):
69
- pass
70
-
71
- # 停止
72
- def stop(self):
73
- self._do_stop()
74
-
75
- @abstractmethod
76
- def _do_stop(self):
77
- pass
78
-
79
- def keep_alive(self, deadline: int = 60):
80
- """
81
- 保持连接alive,超过指定时间不活跃,认为需要停止
82
- """
83
- cur_time = time.time()
84
- self._keep_alive_deadline = cur_time + deadline
1
+ import threading
2
+ import time
3
+ from abc import ABC, abstractmethod
4
+
5
+ from kaq_quant_common.utils import logger_utils, signal_utils
6
+
7
+
8
+ # 通用的抽象类
9
+ class MonitorBase(ABC):
10
+ def __init__(self):
11
+ # 初始化
12
+ self.init()
13
+ # 用来标记最后一次收到数据的时间,超过指定时间没有收到数据,认为连接断开
14
+ self._keep_alive_deadline = 0
15
+
16
+ # 初始化
17
+ def init(self):
18
+ # 执行初始化
19
+ self._do_init()
20
+
21
+ @abstractmethod
22
+ def _do_init(self):
23
+ pass
24
+
25
+ # 开始,需要确保这个方法在op线程执行,执行这个方法会阻塞当前线程!
26
+ def start(self, block=True):
27
+ # 全局退出事件,用于传递终止信号
28
+ exit_event = threading.Event()
29
+ self._exit_event = exit_event
30
+
31
+ logger = logger_utils.get_logger(self)
32
+
33
+ #
34
+ def handle_terminate_signal(signum, frame=None):
35
+ """信号处理函数:捕获终止信号并触发退出事件"""
36
+ logger.info(f"收到终止信号 {signum}")
37
+ exit_event.set()
38
+ # 优雅地停止
39
+ try:
40
+ self.stop()
41
+ except Exception as e:
42
+ pass
43
+
44
+ signal_utils.register_signal_handler(handle_terminate_signal)
45
+
46
+ self._do_start()
47
+
48
+ # 需要阻塞
49
+ if block:
50
+ # 监听退出事件
51
+ while not exit_event.is_set():
52
+ time.sleep(1)
53
+ if self._check_exit():
54
+ # 假装发送信号
55
+ handle_terminate_signal("exit")
56
+ if self._keep_alive_deadline > 0 and time.time() > self._keep_alive_deadline:
57
+ logger.warning("MonitorBase 收据接收超时,需要停止 MonitorBase")
58
+ # 假装发送信号
59
+ handle_terminate_signal("recv time out")
60
+
61
+ logger.warning("MonitorBase 线程退出")
62
+
63
+ # 子类实现,判断连接是否断开,断开返回True
64
+ def _check_exit(self):
65
+ return False
66
+
67
+ @abstractmethod
68
+ def _do_start(self):
69
+ pass
70
+
71
+ # 停止
72
+ def stop(self):
73
+ self._do_stop()
74
+
75
+ @abstractmethod
76
+ def _do_stop(self):
77
+ pass
78
+
79
+ def keep_alive(self, deadline: int = 60):
80
+ """
81
+ 保持连接alive,超过指定时间不活跃,认为需要停止
82
+ """
83
+ cur_time = time.time()
84
+ self._keep_alive_deadline = cur_time + deadline
@@ -1,97 +1,97 @@
1
- import threading
2
- import time
3
-
4
- from kaq_quant_common.common.monitor_base import MonitorBase
5
- from kaq_quant_common.utils import logger_utils, signal_utils
6
-
7
-
8
- class MonitorGroup:
9
-
10
- def __init__(self):
11
- #
12
- self._monitors: list[MonitorBase] = []
13
- self._logger = logger_utils.get_logger(self)
14
-
15
- def get_monitors(self) -> list[MonitorBase]:
16
- return self._monitors
17
-
18
- # 开始,需要确保这个方法在op线程执行,执行这个方法会阻塞当前线程!
19
- def start(self, block=True):
20
- # 全局退出事件,用于传递终止信号
21
- exit_event = threading.Event()
22
- self._exit_event = exit_event
23
-
24
- logger = self._logger
25
-
26
- #
27
- def handle_terminate_signal(signum, frame=None):
28
- """信号处理函数:捕获终止信号并触发退出事件"""
29
- logger.info(f"收到终止信号 {signum}")
30
- exit_event.set()
31
- # 优雅地停止
32
- try:
33
- self.stop()
34
- except Exception as e:
35
- pass
36
-
37
- signal_utils.register_signal_handler(handle_terminate_signal)
38
-
39
- # 需要阻塞
40
- if block:
41
- # 监听退出事件
42
- while not exit_event.is_set():
43
- time.sleep(1)
44
- if self._check_exit():
45
- # 假装发送信号
46
- handle_terminate_signal("exit")
47
-
48
- logger.warning("MonitorGroup 线程退出")
49
-
50
- # 停止
51
- def stop(self):
52
- #
53
- self.stop_monitors()
54
- #
55
- self._exit_event.set()
56
-
57
- def _check_exit(self):
58
- # 避免还没有链接monitor
59
- if len(self._monitors) == 0:
60
- return False
61
-
62
- # 标识是否全部都已经退出
63
- all_exit = True
64
- one_exit = False
65
-
66
- # 遍历检测是否全部都退出了
67
- for monitor in self._monitors:
68
- if not monitor._check_exit():
69
- all_exit = False
70
- else:
71
- one_exit = True
72
-
73
- return one_exit
74
-
75
- # 只停止 monitor
76
- def stop_monitors(self, clear=False):
77
- self._logger.info(f"停止 {len(self._monitors)} 个 monitor")
78
- tmp_monitors = self._monitors.copy()
79
- if clear:
80
- # 清空数组
81
- self._monitors.clear()
82
- for monitor in tmp_monitors:
83
- monitor.stop()
84
-
85
- #
86
- def link(self, monitors: list[MonitorBase], clear=False):
87
- # 如果需要clear,先清空之前的
88
- if clear:
89
- self.stop_monitors(clear=True)
90
- self._logger.info(f"开启 {len(monitors)} 个 monitor")
91
- # 遍历调用start
92
- for monitor in monitors:
93
- monitor.start(block=False)
94
- # 延迟一下,防止同时创建多个连接有问题
95
- time.sleep(1)
96
- # 追加新的
97
- self._monitors.extend(monitors)
1
+ import threading
2
+ import time
3
+
4
+ from kaq_quant_common.common.monitor_base import MonitorBase
5
+ from kaq_quant_common.utils import logger_utils, signal_utils
6
+
7
+
8
+ class MonitorGroup:
9
+
10
+ def __init__(self):
11
+ #
12
+ self._monitors: list[MonitorBase] = []
13
+ self._logger = logger_utils.get_logger(self)
14
+
15
+ def get_monitors(self) -> list[MonitorBase]:
16
+ return self._monitors
17
+
18
+ # 开始,需要确保这个方法在op线程执行,执行这个方法会阻塞当前线程!
19
+ def start(self, block=True):
20
+ # 全局退出事件,用于传递终止信号
21
+ exit_event = threading.Event()
22
+ self._exit_event = exit_event
23
+
24
+ logger = self._logger
25
+
26
+ #
27
+ def handle_terminate_signal(signum, frame=None):
28
+ """信号处理函数:捕获终止信号并触发退出事件"""
29
+ logger.info(f"收到终止信号 {signum}")
30
+ exit_event.set()
31
+ # 优雅地停止
32
+ try:
33
+ self.stop()
34
+ except Exception as e:
35
+ pass
36
+
37
+ signal_utils.register_signal_handler(handle_terminate_signal)
38
+
39
+ # 需要阻塞
40
+ if block:
41
+ # 监听退出事件
42
+ while not exit_event.is_set():
43
+ time.sleep(1)
44
+ if self._check_exit():
45
+ # 假装发送信号
46
+ handle_terminate_signal("exit")
47
+
48
+ logger.warning("MonitorGroup 线程退出")
49
+
50
+ # 停止
51
+ def stop(self):
52
+ #
53
+ self.stop_monitors()
54
+ #
55
+ self._exit_event.set()
56
+
57
+ def _check_exit(self):
58
+ # 避免还没有链接monitor
59
+ if len(self._monitors) == 0:
60
+ return False
61
+
62
+ # 标识是否全部都已经退出
63
+ all_exit = True
64
+ one_exit = False
65
+
66
+ # 遍历检测是否全部都退出了
67
+ for monitor in self._monitors:
68
+ if not monitor._check_exit():
69
+ all_exit = False
70
+ else:
71
+ one_exit = True
72
+
73
+ return one_exit
74
+
75
+ # 只停止 monitor
76
+ def stop_monitors(self, clear=False):
77
+ self._logger.info(f"停止 {len(self._monitors)} 个 monitor")
78
+ tmp_monitors = self._monitors.copy()
79
+ if clear:
80
+ # 清空数组
81
+ self._monitors.clear()
82
+ for monitor in tmp_monitors:
83
+ monitor.stop()
84
+
85
+ #
86
+ def link(self, monitors: list[MonitorBase], clear=False):
87
+ # 如果需要clear,先清空之前的
88
+ if clear:
89
+ self.stop_monitors(clear=True)
90
+ self._logger.info(f"开启 {len(monitors)} 个 monitor")
91
+ # 遍历调用start
92
+ for monitor in monitors:
93
+ monitor.start(block=False)
94
+ # 延迟一下,防止同时创建多个连接有问题
95
+ time.sleep(1)
96
+ # 追加新的
97
+ self._monitors.extend(monitors)
@@ -1,21 +1,21 @@
1
- from abc import abstractmethod
2
-
3
- from kaq_quant_common.common.monitor_base import MonitorBase
4
-
5
-
6
- # 通用的抽象类,包装一层ws操作
7
- class WsWrapper(MonitorBase):
8
- def __init__(self):
9
- super().__init__()
10
-
11
- # stop 就是调用close,ws 更好理解
12
- def _do_stop(self):
13
- self._do_close()
14
-
15
- # 断开连接,主动关闭
16
- def close(self):
17
- self.stop()
18
-
19
- @abstractmethod
20
- def _do_close(self):
21
- pass
1
+ from abc import abstractmethod
2
+
3
+ from kaq_quant_common.common.monitor_base import MonitorBase
4
+
5
+
6
+ # 通用的抽象类,包装一层ws操作
7
+ class WsWrapper(MonitorBase):
8
+ def __init__(self):
9
+ super().__init__()
10
+
11
+ # stop 就是调用close,ws 更好理解
12
+ def _do_stop(self):
13
+ self._do_close()
14
+
15
+ # 断开连接,主动关闭
16
+ def close(self):
17
+ self.stop()
18
+
19
+ @abstractmethod
20
+ def _do_close(self):
21
+ pass
@@ -1,8 +1,10 @@
1
1
  import os
2
2
  import queue
3
3
  import time
4
+ from typing import Optional, Union
4
5
  import dolphindb as ddb
5
6
  from kaq_quant_common.utils import yml_utils
7
+ import numpy as np
6
8
  import pandas as pd
7
9
  import threading
8
10
  from kaq_quant_common.utils.logger_utils import get_logger
@@ -35,19 +37,35 @@ class KaqQuantDdbStreamWriteRepository:
35
37
  self.logger.error(f'KaqQuantDdbStreamWriteRepository.__init__ is occured error: {str(e)} - {str(traceback.format_exc())}')
36
38
  finally:
37
39
  mutex.release()
38
-
39
- def save_row(self, ddb_table_name: str, row:list=[]):
40
+
41
+ def save_rows(self, ddb_table_name: str, rows: Optional[Union[np.ndarray, list]] = None):
40
42
  '''
41
43
  调用此方法之前, 需要将dataframe中的字符串类型的值 ,添加引号
42
44
  '''
43
- if row is None or len(row) <= 0:
45
+ if rows is None:
44
46
  return
45
- formatted_values = [f'\'{v}\'' if isinstance(v, str) else str(v) for v in row]
46
- script = f"insert into {ddb_table_name} values({', '.join(str(x) for x in formatted_values)})"
47
- try:
48
- self.session.run(script, clearMemory=True)
49
- except Exception as e:
50
- self.logger.error(f'KaqQuantDdbStreamWriteRepository.save2stream is occured error: tableName is {ddb_table_name} - {str(e)} - {str(traceback.format_exc())}')
47
+ # 获取维度
48
+ if isinstance(rows[0], (list, np.ndarray, tuple)):
49
+ # 多行数据
50
+ try:
51
+ formatted_values = []
52
+ for row in rows:
53
+ # 这里的 row 是 np.array([1767708564161, "BTC", 92500.1])
54
+ row = [f'\'{v}\'' if isinstance(v, str) else str(v) for v in row]
55
+ formatted_values.append(f'({', '.join(row)})')
56
+ script = f"insert into {ddb_table_name} values {', '.join(str(x) for x in formatted_values)}"
57
+
58
+ self.session.run(script, clearMemory=True)
59
+ except Exception as e:
60
+ self.logger.error(f'KaqQuantDdbStreamWriteRepository.save_rows is occured error: tableName is {ddb_table_name} - {str(e)} - {str(traceback.format_exc())}')
61
+ else:
62
+ # 是一行数据,调用 insert
63
+ formatted_values = [f'\'{v}\'' if isinstance(v, str) else str(v) for v in rows]
64
+ script = f"insert into {ddb_table_name} values({', '.join(str(x) for x in formatted_values)})"
65
+ try:
66
+ self.session.run(script, clearMemory=True)
67
+ except Exception as e:
68
+ self.logger.error(f'KaqQuantDdbStreamWriteRepository.save_rows is occured error: tableName is {ddb_table_name} - {str(e)} - {str(traceback.format_exc())}')
51
69
 
52
70
  def save2stream(self, ddb_table_name: str, df : pd.DataFrame):
53
71
  '''
@@ -230,7 +248,7 @@ class KaqQuantDdbStreamMTWWriteRepository:
230
248
  finally:
231
249
  mutex.release()
232
250
 
233
- def insert(self, df:pd.DataFrame=pd.DataFrame(), cols:list=[]):
251
+ def save2stream_batch(self, df:pd.DataFrame=pd.DataFrame(), cols:list=[]):
234
252
  try:
235
253
  if cols is None or len(cols) <= 0:
236
254
  for _, row in df.iterrows():
@@ -243,7 +261,7 @@ class KaqQuantDdbStreamMTWWriteRepository:
243
261
  except Exception as e:
244
262
  self.logger.error(f'KaqQuantDdbStreamMTWWriteRepository.insert is occured error: {str(e)} - {str(traceback.format_exc())}')
245
263
 
246
- def save2stream(self, row:list=[]):
264
+ def save2stream_list(self, row:list=[]):
247
265
  try:
248
266
  if row is None or len(row) <= 0:
249
267
  return
@@ -97,18 +97,13 @@ class KaqQuantMysqlRepository:
97
97
  session.close()
98
98
  return ret
99
99
 
100
- # 判断表是否存在
100
+ # 判断表是否存在
101
101
  def rename_table(self, table_name):
102
102
  # 从连接池获取连接
103
103
  session = self.session_maker()
104
104
  try:
105
- # 1. 计算上周一的日期字符串
106
- today = datetime.date.today()
107
- # 2. 先计算本周一,再减去 7 天
108
- last_monday = today - datetime.timedelta(days=today.weekday(), weeks=1)
109
- # 3. 格式化
110
- last_monday_str = last_monday.strftime('%Y%m%d')
111
- sql = f"RENAME TABLE {table_name} TO {table_name}_{last_monday_str};"
105
+ time_str = time.strftime("%Y_%m_%d_%H_%M", time.localtime(time.time()))
106
+ sql = f"RENAME TABLE {table_name} TO {table_name}_{time_str};"
112
107
  session.execute(text(sql))
113
108
  return True
114
109
  except Exception as e:
@@ -13,7 +13,7 @@ logger.add(
13
13
  # 输出到标准输出(控制台)
14
14
  sink=sys.stdout,
15
15
  # TODO 配置
16
- level="DEBUG",
16
+ level="INFO",
17
17
  #
18
18
  # format="{time} {level} [{extra[module]}] {message}", # 显示绑定的module
19
19
  format=(
@@ -36,9 +36,9 @@ def _is_dagster_env():
36
36
 
37
37
 
38
38
  def get_logger(obj: Union[str, object] = None):
39
- """
39
+ '''
40
40
  获取logger
41
- """
41
+ '''
42
42
 
43
43
  is_dagster_env = _is_dagster_env()
44
44
 
@@ -46,9 +46,9 @@ def get_logger(obj: Union[str, object] = None):
46
46
  if isinstance(obj, str):
47
47
  # do nothing
48
48
  name = obj
49
- elif hasattr(obj, "__class__"):
49
+ elif hasattr(obj, '__class__'):
50
50
  name = obj.__class__.__name__
51
- elif hasattr(obj, "__name__"):
51
+ elif hasattr(obj, '__name__'):
52
52
  name = obj.__name__
53
53
  else:
54
54
  name = ""
@@ -1,23 +1,23 @@
1
- import signal
2
- import sys
3
-
4
- # 1. 为目标信号(例如 SIGINT,即 Ctrl+C 触发)维护一个处理函数列表
5
- signal_handlers = []
6
-
7
-
8
- # 2. 定义「总处理函数」:触发时依次执行列表中的所有处理逻辑
9
- def total_handler(signalnum, frame):
10
- # 依次调用所有注册的处理函数
11
- for handler in signal_handlers:
12
- handler(signalnum, frame)
13
-
14
-
15
- # 3. 注册「总处理函数」到目标信号(例如 SIGINT)
16
- # SIGTERM:Dagster通常发送此信号进行终止
17
- # SIGINT:对应Ctrl+C,用于本地测试
18
- signal.signal(signal.SIGTERM, total_handler)
19
- signal.signal(signal.SIGINT, total_handler)
20
-
21
-
22
- def register_signal_handler(handler):
23
- signal_handlers.append(handler)
1
+ import signal
2
+ import sys
3
+
4
+ # 1. 为目标信号(例如 SIGINT,即 Ctrl+C 触发)维护一个处理函数列表
5
+ signal_handlers = []
6
+
7
+
8
+ # 2. 定义「总处理函数」:触发时依次执行列表中的所有处理逻辑
9
+ def total_handler(signalnum, frame):
10
+ # 依次调用所有注册的处理函数
11
+ for handler in signal_handlers:
12
+ handler(signalnum, frame)
13
+
14
+
15
+ # 3. 注册「总处理函数」到目标信号(例如 SIGINT)
16
+ # SIGTERM:Dagster通常发送此信号进行终止
17
+ # SIGINT:对应Ctrl+C,用于本地测试
18
+ signal.signal(signal.SIGTERM, total_handler)
19
+ signal.signal(signal.SIGINT, total_handler)
20
+
21
+
22
+ def register_signal_handler(handler):
23
+ signal_handlers.append(handler)
@@ -1,5 +1,5 @@
1
- import uuid
2
-
3
-
4
- def generate_uuid() -> str:
5
- return str(uuid.uuid4())
1
+ import uuid
2
+
3
+
4
+ def generate_uuid() -> str:
5
+ return str(uuid.uuid4())
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kaq_quant_common
3
- Version: 0.1.99
3
+ Version: 0.2.2
4
4
  Summary:
5
5
  Author: kevinfuture
6
6
  Author-email: liuenbofuture@foxmail.com