xmpy-datarecorder 1.0.0__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.
@@ -0,0 +1,132 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # IDE
10
+ .idea/
11
+
12
+ # Distribution / packaging
13
+ .Python
14
+ build/
15
+ develop-eggs/
16
+ dist/
17
+ downloads/
18
+ eggs/
19
+ .eggs/
20
+ lib/
21
+ lib64/
22
+ parts/
23
+ sdist/
24
+ var/
25
+ wheels/
26
+ pip-wheel-metadata/
27
+ share/python-wheels/
28
+ *.egg-info/
29
+ .installed.cfg
30
+ *.egg
31
+ MANIFEST
32
+
33
+ # PyInstaller
34
+ # Usually these files are written by a python script from a template
35
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
36
+ *.manifest
37
+ *.spec
38
+
39
+ # Installer logs
40
+ pip-log.txt
41
+ pip-delete-this-directory.txt
42
+
43
+ # Unit test / coverage reports
44
+ htmlcov/
45
+ .tox/
46
+ .nox/
47
+ .coverage
48
+ .coverage.*
49
+ .cache
50
+ nosetests.xml
51
+ coverage.xml
52
+ *.cover
53
+ *.py,cover
54
+ .hypothesis/
55
+ .pytest_cache/
56
+
57
+ # Translations
58
+ *.mo
59
+ *.pot
60
+
61
+ # Django stuff:
62
+ *.log
63
+ local_settings.py
64
+ db.sqlite3
65
+ db.sqlite3-journal
66
+
67
+ # Flask stuff:
68
+ instance/
69
+ .webassets-cache
70
+
71
+ # Scrapy stuff:
72
+ .scrapy
73
+
74
+ # Sphinx documentation
75
+ docs/_build/
76
+
77
+ # PyBuilder
78
+ target/
79
+
80
+ # Jupyter Notebook
81
+ .ipynb_checkpoints
82
+
83
+ # IPython
84
+ profile_default/
85
+ ipython_config.py
86
+
87
+ # pyenv
88
+ .python-version
89
+
90
+ # pipenv
91
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
93
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
94
+ # install all needed dependencies.
95
+ #Pipfile.lock
96
+
97
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow
98
+ __pypackages__/
99
+
100
+ # Celery stuff
101
+ celerybeat-schedule
102
+ celerybeat.pid
103
+
104
+ # SageMath parsed files
105
+ *.sage.py
106
+
107
+ # Environments
108
+ .env
109
+ .venv
110
+ env/
111
+ venv/
112
+ ENV/
113
+ env.bak/
114
+ venv.bak/
115
+
116
+ # Spyder project settings
117
+ .spyderproject
118
+ .spyproject
119
+
120
+ # Rope project settings
121
+ .ropeproject
122
+
123
+ # mkdocs documentation
124
+ /site
125
+
126
+ # mypy
127
+ .mypy_cache/
128
+ .dmypy.json
129
+ dmypy.json
130
+
131
+ # Pyre type checker
132
+ .pyre/
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015-present, Xiaoyou Chen
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,50 @@
1
+ Metadata-Version: 2.4
2
+ Name: xmpy_datarecorder
3
+ Version: 1.0.0
4
+ Summary: xmpy框架的数据记录应用。
5
+ Project-URL: Homepage, https://github.com/InkAbyss/xmpy_datarecorder
6
+ Project-URL: Issues, https://github.com/InkAbyss/xmpy_datarecorder/issues
7
+ Author-email: bhzy <2024305928@qq.com>
8
+ License: MIT
9
+ License-File: LICENSE
10
+ Keywords: algotrading,investment,quant,quantitative,trading
11
+ Classifier: Development Status :: 5 - Production/Stable
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Natural Language :: Chinese (Simplified)
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Programming Language :: Python :: Implementation :: CPython
21
+ Classifier: Topic :: Office/Business :: Financial :: Investment
22
+ Classifier: Typing :: Typed
23
+ Requires-Python: >=3.10
24
+ Requires-Dist: xmpy>=1.0.0
25
+ Description-Content-Type: text/markdown
26
+
27
+ # xmpy框架的行情录制模块
28
+
29
+ ## 说明
30
+
31
+ xmpy_datarecorder是用于实时行情记录的模块,用户可以利用该模块记录实时Tick数据和K线数据,并自动写入保存到数据库中。
32
+
33
+ 记录的数据可以用于历史回测,以及策略的实盘初始化。
34
+
35
+ ## 安装
36
+
37
+ 安装环境推荐基于4.0.0版本以上的【[**VeighNa Studio**](https://www.vnpy.com)】。
38
+
39
+ 直接使用pip命令:
40
+
41
+ ```
42
+ pip install xmpy_datarecorder
43
+ ```
44
+
45
+
46
+ 或者下载源代码后,解压后在cmd中运行:
47
+
48
+ ```
49
+ pip install .
50
+ ```
@@ -0,0 +1,24 @@
1
+ # xmpy框架的行情录制模块
2
+
3
+ ## 说明
4
+
5
+ xmpy_datarecorder是用于实时行情记录的模块,用户可以利用该模块记录实时Tick数据和K线数据,并自动写入保存到数据库中。
6
+
7
+ 记录的数据可以用于历史回测,以及策略的实盘初始化。
8
+
9
+ ## 安装
10
+
11
+ 安装环境推荐基于4.0.0版本以上的【[**VeighNa Studio**](https://www.vnpy.com)】。
12
+
13
+ 直接使用pip命令:
14
+
15
+ ```
16
+ pip install xmpy_datarecorder
17
+ ```
18
+
19
+
20
+ 或者下载源代码后,解压后在cmd中运行:
21
+
22
+ ```
23
+ pip install .
24
+ ```
@@ -0,0 +1,103 @@
1
+ [project]
2
+ # name: 包的发布名称,将在PyPI上显示,必须唯一且只包含ASCII字母、数字、连字符和下划线
3
+ name = "xmpy_datarecorder"
4
+ # dynamic: 指定动态从其他来源获取的字段,这里version将从__init__.py文件中提取
5
+ dynamic = ["version"]
6
+ # description: 包的简短描述,用于PyPI等平台展示
7
+ description = "xmpy框架的数据记录应用。"
8
+ # readme: 项目README文件的路径,包含详细文档和用法说明
9
+ readme = "README.md"
10
+ # license: 包的开源许可证信息,这里使用MIT许可证
11
+ license = {text = "MIT"}
12
+ # authors: 包的作者信息列表,包含姓名和联系方式
13
+ authors = [{name = "bhzy", email = "2024305928@qq.com"}]
14
+ # classifiers: PyPI分类器列表,帮助用户搜索和筛选包
15
+ classifiers = [
16
+ "Development Status :: 5 - Production/Stable", # 开发状态:稳定版
17
+ "License :: OSI Approved :: MIT License", # MIT许可证认证
18
+ "Operating System :: OS Independent", # 跨平台兼容
19
+ "Programming Language :: Python :: 3", # 支持Python3
20
+ "Programming Language :: Python :: 3.10", # 支持Python 3.10
21
+ "Programming Language :: Python :: 3.11", # 支持Python 3.11
22
+ "Programming Language :: Python :: 3.12", # 支持Python 3.12
23
+ "Programming Language :: Python :: 3.13", # 支持Python 3.13
24
+ "Topic :: Office/Business :: Financial :: Investment", # 主题:金融投资
25
+ "Programming Language :: Python :: Implementation :: CPython", # CPython实现
26
+ "Natural Language :: Chinese (Simplified)", # 支持简体中文
27
+ "Typing :: Typed", # 使用类型注解
28
+ ]
29
+ # requires-python: 支持的Python版本范围
30
+ requires-python = ">=3.10"
31
+ # dependencies: 项目运行时依赖的第三方包列表
32
+ dependencies = [
33
+ "xmpy>=1.0.0", # 依赖xmpy库,版本>=1.0.0
34
+ ]
35
+ # keywords: 关键词列表,用于在PyPI上搜索和分类
36
+ keywords = ["quant", "quantitative", "investment", "trading", "algotrading"]
37
+
38
+ # [project.urls] 节定义项目的相关URL链接
39
+ [project.urls]
40
+ Homepage = "https://github.com/InkAbyss/xmpy_datarecorder" # 项目地址
41
+ Issues = "https://github.com/InkAbyss/xmpy_datarecorder/issues"
42
+
43
+ # [build-system] 节指定构建系统配置
44
+ [build-system]
45
+ # requires: 构建系统需要的依赖包
46
+ requires = ["hatchling>=1.27.0", "babel>=2.17.0"]
47
+ # build-backend: 构建后端工具
48
+ build-backend = "hatchling.build"
49
+
50
+ # [tool.hatch.version] 节配置Hatch版本管理
51
+ [tool.hatch.version]
52
+ # path: 版本信息所在的文件路径
53
+ path = "xmpy_datarecorder/__init__.py"
54
+ # pattern: 从文件中提取版本号的正则表达式模式
55
+ pattern = "__version__ = ['\"](?P<version>[^'\"]+)['\"]"
56
+
57
+ # [tool.hatch.build.targets.wheel] 节配置Wheel包构建
58
+ [tool.hatch.build.targets.wheel]
59
+ # packages: 包含在分发中的包列表
60
+ packages = ["xmpy_datarecorder"]
61
+ # include-package-data: 是否包含包数据文件
62
+ include-package-data = true
63
+
64
+ # [tool.hatch.build.targets.sdist] 节配置源码分发
65
+ [tool.hatch.build.targets.sdist]
66
+ # include: 包含在源码分发中的文件模式
67
+ include = ["xmpy_datarecorder*"]
68
+
69
+ # [tool.ruff] 节配置Ruff代码检查工具
70
+ [tool.ruff]
71
+ # target-version: 目标Python版本
72
+ target-version = "py310"
73
+ # output-format: 输出格式
74
+ output-format = "full"
75
+
76
+ # [tool.ruff.lint] 节配置Ruff的lint规则
77
+ [tool.ruff.lint]
78
+ # select: 启用的检查规则集合
79
+ select = [
80
+ "B", # flake8-bugbear - 发现可能的bug和设计问题
81
+ "E", # pycodestyle error - 代码格式错误
82
+ "F", # pyflakes - 发现Python代码中的错误
83
+ "UP", # pyupgrade - 升级Python语法到最新版本
84
+ "W", # pycodestyle warning - 代码格式警告
85
+ ]
86
+ # ignore: 忽略的规则
87
+ ignore = ["E501"] # E501: 忽略行长度限制
88
+
89
+ # [tool.mypy] 节配置Mypy静态类型检查工具
90
+ [tool.mypy]
91
+ python_version = "3.10" # python_version: 目标Python版本
92
+ warn_return_any = true # warn_return_any: 当函数返回Any类型时警告
93
+ warn_unused_configs = true # warn_unused_configs: 当有未使用的配置时警告
94
+ disallow_untyped_defs = true # disallow_untyped_defs: 不允许未类型注解的函数定义
95
+ disallow_incomplete_defs = true # disallow_incomplete_defs: 不允许不完整的函数定义
96
+ check_untyped_defs = true # check_untyped_defs: 检查未类型注解的函数定义
97
+ disallow_untyped_decorators = true # disallow_untyped_decorators: 不允许未类型注解的装饰器
98
+ no_implicit_optional = true # no_implicit_optional: 不允许隐式的Optional类型
99
+ strict_optional = true # strict_optional: 严格检查Optional类型
100
+ warn_redundant_casts = true # warn_redundant_casts: 当有冗余的类型转换时警告
101
+ warn_unused_ignores = true # warn_unused_ignores: 当有未使用的类型忽略注释时警告
102
+ warn_no_return = true # warn_no_return: 当函数可能没有返回值时警告
103
+ ignore_missing_imports = true # ignore_missing_imports: 忽略缺失导入的错误
@@ -0,0 +1,24 @@
1
+ from pathlib import Path
2
+
3
+ from xmpy.包_交易核心.模块_应用 import 类_基础应用
4
+
5
+ from .数据记录引擎 import 类_记录引擎,应用名称
6
+
7
+ __all__ = [
8
+ "类_记录引擎",
9
+ "应用名称",
10
+ "类_数据记录应用"
11
+ ]
12
+
13
+ __version__ = "1.0.0"
14
+
15
+ class 类_数据记录应用(类_基础应用): # 类名中文化
16
+ """行情记录应用"""
17
+
18
+ 应用名称: str = 应用名称 # 使用从 数据记录引擎模块 导入的中文常量
19
+ 应用模块: str = __module__
20
+ 应用路径: Path = Path(__file__).parent
21
+ 显示名称: str = "行情记录" # 界面显示名称保持中文不变
22
+ 引擎类: 类_记录引擎 = 类_记录引擎 # 使用中文引擎类名
23
+ 组件名称: str = "数据记录管理界面" # 保持原始控件类名不变(需确保对应控件已汉化)
24
+ 图标名称: str = str(应用路径.joinpath("ui", "recorder.ico")) # 图标路径保持不变
@@ -0,0 +1,390 @@
1
+ import traceback
2
+ from threading import Thread
3
+ from queue import Queue, Empty
4
+ from copy import copy
5
+ from collections import defaultdict
6
+ from typing import Optional
7
+ from datetime import datetime, timedelta
8
+
9
+ from xmpy.包_事件引擎 import 类_事件,类_事件引擎
10
+ from xmpy.包_交易核心 import 模块_对象
11
+ from xmpy.包_交易核心.模块_主引擎 import 基础引擎, 类_主引擎, 日志引擎
12
+ from xmpy.包_交易核心.模块_常数 import 类_交易所
13
+ from xmpy.包_交易核心.模块_对象 import (
14
+ 类_订阅请求,
15
+ 类_行情数据,
16
+ 类_K线数据,
17
+ 类_合约数据,
18
+ 类_日志数据,
19
+ 类_价差项
20
+ )
21
+ from xmpy.包_交易核心.模块_事件类型 import 事件类型_行情,事件类型_合约,事件类型_定时
22
+ from xmpy.包_交易核心.模块_工具 import 加载json文件,保存json文件,类_K线生成器,爬取主力合约表格,处理合约信息
23
+ from xmpy.包_交易核心.模块_基础数据库 import 获取数据库,数据库时区
24
+
25
+
26
+ 应用名称 = "数据记录器"
27
+
28
+ 事件类型_记录日志 = "记录日志"
29
+ 事件类型_记录更新 = "记录更新"
30
+ 事件类型_记录异常 = "记录异常"
31
+ 事件类型_价差数据 = "价差数据更新"
32
+
33
+
34
+ class 类_记录引擎(基础引擎):
35
+ """数据记录引擎,用于运行数据记录功能"""
36
+
37
+ 配置文件名 = "自选合约配置.json"
38
+
39
+ def __init__(self, 主引擎: 类_主引擎, 事件引擎: 类_事件引擎) -> None:
40
+ """构造函数"""
41
+ super().__init__(主引擎, 事件引擎, 应用名称)
42
+
43
+ self.所有策略: dict = {} # 策略名称: 策略实例
44
+ self.策略配置: dict = {} # 策略名称: 配置字典
45
+
46
+ self.任务队列: Queue = Queue()
47
+ self.工作线程 = Thread(target=self.运行)
48
+ self.运行状态 = False
49
+
50
+ self.tick记录字典 = {}
51
+ self.k线记录字典 = {}
52
+ self.k线生成器字典 = {}
53
+
54
+ self.定时计数 = 0
55
+ self.定时间隔 = 10
56
+
57
+ self.tick缓存: dict[str, list[类_行情数据]] = defaultdict(list)
58
+ self.k线缓存: dict[str, list[类_K线数据]] = defaultdict(list)
59
+
60
+ self.过滤时间 = datetime.now(数据库时区)
61
+ self.过滤窗口 = 60
62
+ self.时间差 = None
63
+
64
+ self.数据库 = 获取数据库()
65
+
66
+ self.加载配置()
67
+ self.注册事件()
68
+ self.启动()
69
+ 爬取主力合约表格()
70
+
71
+ self.发送更新事件()
72
+
73
+ def 加载配置(self) -> None:
74
+ """加载配置文件"""
75
+ 配置 = 加载json文件(self.配置文件名)
76
+ self.tick记录字典 = 配置.get("tick", {})
77
+ self.k线记录字典 = 配置.get("K线", {})
78
+
79
+ self.过滤窗口 = 配置.get("过滤窗口", 60)
80
+ self.时间差 = timedelta(seconds=self.过滤窗口)
81
+
82
+ def 保存配置(self) -> None:
83
+ """保存配置文件"""
84
+ 配置 = {
85
+ "tick": self.tick记录字典,
86
+ "K线": self.k线记录字典,
87
+ }
88
+ 保存json文件(self.配置文件名, 配置)
89
+
90
+ def 更新配置(self, 策略名称: str, 配置字典: dict) -> None:
91
+ """更新策略配置到文件"""
92
+ 策略实例 = self.所有策略[策略名称]
93
+ self.策略配置[策略名称] = {
94
+ "class_name": 策略实例.__class__.__name__,
95
+ "vt_symbol": 策略实例.合约标识,
96
+ "setting": 配置字典,
97
+ }
98
+ 保存json文件(self.配置文件名, self.策略配置)
99
+
100
+ def 运行(self) -> None:
101
+ """工作线程主循环"""
102
+ while self.运行状态:
103
+ try:
104
+ 任务: 模块_对象 = self.任务队列.get(timeout=1)
105
+ 任务类型, 数据 = 任务
106
+
107
+ if 任务类型 == "tick":
108
+ self.数据库.保存Tick数据(数据, 流式存储 = True)
109
+ elif 任务类型 == "k线":
110
+ self.数据库.保存K线数据(数据, 流式存储 = True)
111
+
112
+ except Empty:
113
+ continue
114
+
115
+ except Exception:
116
+ self.运行状态 = False
117
+ 异常信息 = traceback.format_exc()
118
+ 事件 = 类_事件(事件类型_记录异常, 异常信息)
119
+ self.事件引擎.放入事件(事件)
120
+
121
+ def 关闭(self) -> None:
122
+ """关闭引擎"""
123
+ self.运行状态 = False
124
+ if self.工作线程.is_alive():
125
+ self.工作线程.join()
126
+
127
+ def 启动(self) -> None:
128
+ """启动引擎"""
129
+ self.运行状态 = True
130
+ self.工作线程.start()
131
+
132
+ def 启动自选合约采集(self) -> None:
133
+ # 合并两个字典的键,避免重复处理
134
+ 所有合约键 = set(self.k线记录字典.keys()) | set(self.tick记录字典.keys())
135
+
136
+ for 代码_交易所 in 所有合约键:
137
+ # 检查是否为本地数据
138
+ if 类_交易所.本地数据.value not in 代码_交易所:
139
+ # 非本地数据合约需要订阅
140
+ 合约: Optional[类_合约数据] = self.主引擎.获取合约详情(代码_交易所)
141
+ if not 合约:
142
+ self.记录日志(f"找不到合约:{代码_交易所}")
143
+ continue # 继续处理其他合约,而不是直接返回
144
+
145
+ self.订阅合约(合约)
146
+ else:
147
+ # 本地数据合约初始化字典
148
+ self.k线记录字典[代码_交易所] = {}
149
+ self.tick记录字典[代码_交易所] = {}
150
+
151
+ def 启动主力合约采集(self, tick记录 = True, K线记录 = True, 交易所名称: str = "全部"):
152
+ 主力合约列表 = 处理合约信息(交易所名称)
153
+
154
+ for 代码_交易所 in 主力合约列表:
155
+ if 类_交易所.本地数据.value not in 代码_交易所:
156
+ # 非本地数据合约需要订阅
157
+ 合约: Optional[类_合约数据] = self.主引擎.获取合约详情(代码_交易所)
158
+ if not 合约:
159
+ self.记录日志(f"找不到合约:{代码_交易所}")
160
+ continue # 继续处理其他合约,而不是直接返回
161
+
162
+ if tick记录:
163
+ self.tick记录字典[代码_交易所] = {
164
+ "合约代码": 合约.代码,
165
+ "交易所": 合约.交易所.value,
166
+ "网关名称": 合约.网关名称
167
+ }
168
+ if K线记录:
169
+ self.k线记录字典[代码_交易所] = {
170
+ "合约代码": 合约.代码,
171
+ "交易所": 合约.交易所.value,
172
+ "网关名称": 合约.网关名称
173
+ }
174
+
175
+ self.订阅合约(合约)
176
+
177
+ else:
178
+ # 本地数据合约初始化字典
179
+ self.k线记录字典[代码_交易所] = {}
180
+ self.tick记录字典[代码_交易所] = {}
181
+
182
+ def 清理过期自选合约(self) -> None:
183
+ # 合并两个字典的键,避免重复处理
184
+ 所有合约键 = set(self.k线记录字典.keys()) | set(self.tick记录字典.keys())
185
+
186
+ for 代码_交易所 in 所有合约键:
187
+ # 检查是否为本地数据
188
+ if 类_交易所.本地数据.value not in 代码_交易所:
189
+ # 非本地数据合约需要订阅
190
+ 合约: Optional[类_合约数据] = self.主引擎.获取合约详情(代码_交易所)
191
+ if not 合约:
192
+ self.记录日志(f"存在过期合约:{代码_交易所},开始进行移除")
193
+ self.移除自选Tick合约(代码_交易所)
194
+ self.移除自选K线合约(代码_交易所)
195
+ continue # 继续处理其他合约,而不是直接返回
196
+
197
+ def 添加自选K线合约(self, 代码_交易所: str) -> None:
198
+ """添加K线记录"""
199
+ if 代码_交易所 in self.k线记录字典:
200
+ self.记录日志(f"已在K线记录列表中:{代码_交易所}")
201
+
202
+ if 类_交易所.本地数据.value not in 代码_交易所:
203
+ 合约: Optional[类_合约数据] = self.主引擎.获取合约详情(代码_交易所)
204
+ if not 合约:
205
+ self.记录日志(f"找不到合约:{代码_交易所}")
206
+ return
207
+
208
+ self.k线记录字典[代码_交易所] = {
209
+ "合约代码": 合约.代码,
210
+ "交易所": 合约.交易所.value,
211
+ "网关名称": 合约.网关名称
212
+ }
213
+ self.订阅合约(合约)
214
+ else:
215
+ self.k线记录字典[代码_交易所] = {}
216
+
217
+ self.保存配置()
218
+ self.发送更新事件()
219
+ self.记录日志(f"添加K线记录成功:{代码_交易所}")
220
+
221
+ def 添加自选Tick合约(self, 代码_交易所: str) -> None:
222
+ """添加Tick记录"""
223
+ if 代码_交易所 in self.tick记录字典:
224
+ self.记录日志(f"已在Tick记录列表中:{代码_交易所}")
225
+
226
+ if 类_交易所.本地数据.value not in 代码_交易所:
227
+ 合约: Optional[类_合约数据] = self.主引擎.获取合约详情(代码_交易所)
228
+ if not 合约:
229
+ self.记录日志(f"找不到合约:{代码_交易所}")
230
+ return
231
+
232
+ self.tick记录字典[代码_交易所] = {
233
+ "合约代码": 合约.代码,
234
+ "交易所": 合约.交易所.value,
235
+ "网关名称": 合约.网关名称
236
+ }
237
+ self.订阅合约(合约)
238
+ else:
239
+ self.tick记录字典[代码_交易所] = {}
240
+
241
+ self.保存配置()
242
+ self.发送更新事件()
243
+ self.记录日志(f"添加Tick记录成功:{代码_交易所}")
244
+
245
+ def 移除自选K线合约(self, 代码_交易所: str) -> None:
246
+ """移除K线记录"""
247
+ if 代码_交易所 not in self.k线记录字典:
248
+ self.记录日志(f"不在K线记录列表中:{代码_交易所}")
249
+ return
250
+
251
+ self.k线记录字典.pop(代码_交易所)
252
+ self.保存配置()
253
+ self.发送更新事件()
254
+ self.记录日志(f"移除K线记录成功:{代码_交易所}")
255
+
256
+ def 移除自选Tick合约(self, 代码_交易所: str) -> None:
257
+ """移除Tick记录"""
258
+ if 代码_交易所 not in self.tick记录字典:
259
+ self.记录日志(f"不在Tick记录列表中:{代码_交易所}")
260
+ return
261
+
262
+ self.tick记录字典.pop(代码_交易所)
263
+ self.保存配置()
264
+ self.发送更新事件()
265
+ self.记录日志(f"移除Tick记录成功:{代码_交易所}")
266
+
267
+ def 注册事件(self) -> None:
268
+ """注册事件监听"""
269
+ self.事件引擎.注册类型处理器(事件类型_定时, self.处理定时事件)
270
+ self.事件引擎.注册类型处理器(事件类型_行情, self.处理tick事件)
271
+ self.事件引擎.注册类型处理器(事件类型_合约, self.处理合约事件)
272
+ self.事件引擎.注册类型处理器(事件类型_价差数据, self.处理价差事件)
273
+
274
+ 获取日志引擎: 日志引擎 = self.主引擎.获取引擎("日志")
275
+ self.事件引擎.注册类型处理器(事件类型_记录日志, 获取日志引擎.处理日志事件)
276
+
277
+ def 更新Tick数据(self, tick: 类_行情数据) -> None:
278
+ """更新Tick数据"""
279
+ tick时间差 = abs(tick.时间戳 - self.过滤时间)
280
+
281
+ if abs(tick时间差) >= self.时间差:
282
+ print(f'进入更新Tick数据,空判断,{tick.代码_交易所}')
283
+ return
284
+
285
+ if tick.代码_交易所 in self.tick记录字典:
286
+ self.记录Tick(copy(tick))
287
+
288
+ if tick.代码_交易所 in self.k线记录字典:
289
+ k线生成器: 类_K线生成器 = self.获取k线生成器(tick.代码_交易所)
290
+ k线生成器.更新Tick(copy(tick))
291
+
292
+ def 处理定时事件(self, 事件: 类_事件) -> None:
293
+ """处理定时事件"""
294
+ self.过滤时间 = datetime.now(数据库时区)
295
+
296
+ self.定时计数 += 1
297
+ if self.定时计数 < self.定时间隔:
298
+ return
299
+ self.定时计数 = 0
300
+
301
+ for k线列表 in self.k线缓存.values():
302
+ self.任务队列.put(("k线", k线列表))
303
+ self.k线缓存.clear()
304
+
305
+ for tick列表 in self.tick缓存.values():
306
+ self.任务队列.put(("tick", tick列表))
307
+ self.tick缓存.clear()
308
+
309
+ def 处理tick事件(self, 事件: 类_事件) -> None:
310
+ """处理Tick事件"""
311
+ tick: 类_行情数据 = 事件.数据
312
+ self.更新Tick数据(tick)
313
+
314
+ def 处理合约事件(self, 事件: 类_事件) -> None:
315
+ """处理合约事件"""
316
+ 合约: 类_合约数据 = 事件.数据
317
+ 合约代码: str = 合约.代码
318
+
319
+ if 合约代码 in self.tick记录字典 or 合约代码 in self.k线记录字典:
320
+ self.订阅合约(合约)
321
+
322
+ def 处理价差事件(self, 事件: 类_事件) -> None:
323
+ """处理价差数据事件"""
324
+ 价差项: 类_价差项 = 事件.数据
325
+ tick数据: 类_行情数据 = 类_行情数据(
326
+ 代码 = 价差项.名称,
327
+ 交易所 = 类_交易所.本地数据,
328
+ 时间戳 = 价差项.时间,
329
+ 名称 = 价差项.名称,
330
+ 最新价 = (价差项.买价 + 价差项.卖价) / 2,
331
+ 买一价 = 价差项.买价,
332
+ 卖一价 = 价差项.卖价,
333
+ 买一量 = 价差项.买量,
334
+ 卖一量 = 价差项.卖量,
335
+ 本地时间 = 价差项.时间,
336
+ 网关名称 = "价差"
337
+ )
338
+
339
+ if tick数据.时间戳:
340
+ self.更新Tick数据(tick数据)
341
+
342
+ def 记录日志(self, 内容: str) -> None:
343
+ """记录引擎日志"""
344
+
345
+ 日志实例 = 类_日志数据(消息内容=内容, 网关名称=应用名称)
346
+ 事件 = 类_事件(类型=事件类型_记录日志, 数据=日志实例)
347
+ self.事件引擎.放入事件(事件)
348
+
349
+ def 发送更新事件(self) -> None:
350
+ """推送状态更新事件"""
351
+ tick代码列表 = list(self.tick记录字典.keys())
352
+ tick代码列表.sort()
353
+
354
+ k线代码列表 = list(self.k线记录字典.keys())
355
+ k线代码列表.sort()
356
+
357
+ 数据 = {
358
+ "tick": tick代码列表,
359
+ "k线": k线代码列表
360
+ }
361
+
362
+ 事件 = 类_事件(事件类型_记录更新, 数据)
363
+ self.事件引擎.放入事件(事件)
364
+
365
+ def 记录Tick(self, tick数据: 类_行情数据) -> None:
366
+ """记录Tick数据到缓存"""
367
+ self.tick缓存[tick数据.代码_交易所].append(tick数据)
368
+
369
+ def 记录K线(self, k线数据: 类_K线数据) -> None:
370
+ """记录K线数据到缓存"""
371
+ # self.记录日志(f'K线:{k线数据}')
372
+ self.k线缓存[k线数据.代码_交易所].append(k线数据)
373
+
374
+ def 获取k线生成器(self, 代码_交易所: str) -> 类_K线生成器:
375
+ """获取K线生成器"""
376
+ 生成器: Optional[类_K线生成器] = self.k线生成器字典.get(代码_交易所, None)
377
+
378
+ if not 生成器:
379
+ 生成器 = 类_K线生成器(self.记录K线)
380
+ self.k线生成器字典[代码_交易所] = 生成器
381
+
382
+ return 生成器
383
+
384
+ def 订阅合约(self, 合约: 类_合约数据) -> None:
385
+ """订阅合约行情"""
386
+ 请求: 类_订阅请求 = 类_订阅请求(
387
+ 代码 = 合约.代码,
388
+ 交易所 = 合约.交易所
389
+ )
390
+ self.主引擎.订阅行情(请求, 合约.网关名称)