├── .github
├── CODE_OF_CONDUCT.md
├── ISSUE_TEMPLATE.md
├── PULL_REQUEST_TEMPLATE.md
├── SUPPORT.md
└── workflows
│ └── pythonapp.yml
├── .gitignore
├── .vscode
└── settings.json
├── CHANGELOG.md
├── LICENSE
├── README.md
├── pyproject.toml
└── vnpy_mongodb
├── __init__.py
└── mongodb_database.py
/.github/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # 行为准则
2 |
3 | 这是一份VeighNa项目社区的行为准则,也是项目作者自己在刚入行量化金融行业时对于理想中的社区的期望:
4 |
5 | * 为交易员而生:作为一款从金融机构量化业务中诞生的交易系统开发框架,设计上都优先满足机构专业交易员的使用习惯,而不是其他用户(散户、爱好者、技术人员等)
6 |
7 | * 对新用户友好,保持耐心:大部分人在接触新东西的时候都是磕磕碰碰、有很多的问题,请记住此时别人对你伸出的援助之手,并把它传递给未来需要的人
8 |
9 | * 尊重他人,慎重言行:礼貌文明的交流方式除了能得到别人同样的回应,更能减少不必要的摩擦,保证高效的交流
10 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ## 环境
2 |
3 | * 操作系统: 如Windows 11或者Ubuntu 22.04
4 | * Python版本: 如VeighNa Studio-4.0.0
5 | * VeighNa版本: 如v4.0.0发行版或者dev branch 20250320(下载日期)
6 |
7 | ## Issue类型
8 | 三选一:Bug/Enhancement/Question
9 |
10 | ## 预期程序行为
11 |
12 |
13 | ## 实际程序行为
14 |
15 |
16 | ## 重现步骤
17 |
18 | 针对Bug类型Issue,请提供具体重现步骤以及报错截图
19 |
20 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | 建议每次发起的PR内容尽可能精简,复杂的修改请拆分为多次PR,便于管理合并。
2 |
3 | ## 改进内容
4 |
5 | 1.
6 | 2.
7 | 3.
8 |
9 | ## 相关的Issue号(如有)
10 |
11 | Close #
--------------------------------------------------------------------------------
/.github/SUPPORT.md:
--------------------------------------------------------------------------------
1 | # 获取帮助
2 |
3 | 在开发和使用VeighNa项目的过程中遇到问题时,获取帮助的渠道包括:
4 |
5 | * Github Issues:[Issues页面](https://github.com/vnpy/vnpy/issues)
6 | * 官方QQ群: 262656087
7 | * 项目论坛:[VeighNa量化社区](http://www.vnpy.com/forum)
8 | * 项目邮箱: vn.py@foxmail.com
9 |
--------------------------------------------------------------------------------
/.github/workflows/pythonapp.yml:
--------------------------------------------------------------------------------
1 | name: Python application
2 |
3 | on: [push]
4 |
5 | jobs:
6 | build:
7 |
8 | runs-on: windows-latest
9 |
10 | steps:
11 | - uses: actions/checkout@v1
12 | - name: Set up Python 3.13
13 | uses: actions/setup-python@v1
14 | with:
15 | python-version: '3.13'
16 | - name: Install dependencies
17 | run: |
18 | python -m pip install --upgrade pip
19 | pip install ta-lib==0.6.3 --index=https://pypi.vnpy.com
20 | pip install vnpy ruff mypy uv
21 | - name: Lint with ruff
22 | run: |
23 | # Run ruff linter based on pyproject.toml configuration
24 | ruff check .
25 | - name: Type check with mypy
26 | run: |
27 | # Run mypy type checking based on pyproject.toml configuration
28 | mypy vnpy_mongodb
29 | - name: Build packages with uv
30 | run: |
31 | # Build source distribution and wheel distribution
32 | uv build
33 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | pip-wheel-metadata/
24 | share/python-wheels/
25 | *.egg-info/
26 | .installed.cfg
27 | *.egg
28 | MANIFEST
29 |
30 | # PyInstaller
31 | # Usually these files are written by a python script from a template
32 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
33 | *.manifest
34 | *.spec
35 |
36 | # Installer logs
37 | pip-log.txt
38 | pip-delete-this-directory.txt
39 |
40 | # Unit test / coverage reports
41 | htmlcov/
42 | .tox/
43 | .nox/
44 | .coverage
45 | .coverage.*
46 | .cache
47 | nosetests.xml
48 | coverage.xml
49 | *.cover
50 | *.py,cover
51 | .hypothesis/
52 | .pytest_cache/
53 |
54 | # Translations
55 | *.mo
56 | *.pot
57 |
58 | # Django stuff:
59 | *.log
60 | local_settings.py
61 | db.sqlite3
62 | db.sqlite3-journal
63 |
64 | # Flask stuff:
65 | instance/
66 | .webassets-cache
67 |
68 | # Scrapy stuff:
69 | .scrapy
70 |
71 | # Sphinx documentation
72 | docs/_build/
73 |
74 | # PyBuilder
75 | target/
76 |
77 | # Jupyter Notebook
78 | .ipynb_checkpoints
79 |
80 | # IPython
81 | profile_default/
82 | ipython_config.py
83 |
84 | # pyenv
85 | .python-version
86 |
87 | # pipenv
88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
91 | # install all needed dependencies.
92 | #Pipfile.lock
93 |
94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
95 | __pypackages__/
96 |
97 | # Celery stuff
98 | celerybeat-schedule
99 | celerybeat.pid
100 |
101 | # SageMath parsed files
102 | *.sage.py
103 |
104 | # Environments
105 | .env
106 | .venv
107 | env/
108 | venv/
109 | ENV/
110 | env.bak/
111 | venv.bak/
112 |
113 | # Spyder project settings
114 | .spyderproject
115 | .spyproject
116 |
117 | # Rope project settings
118 | .ropeproject
119 |
120 | # mkdocs documentation
121 | /site
122 |
123 | # mypy
124 | .mypy_cache/
125 | .dmypy.json
126 | dmypy.json
127 |
128 | # Pyre type checker
129 | .pyre/
130 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "python.linting.flake8Enabled": true,
3 | "python.linting.enabled": true
4 | }
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # 1.1.0版本
2 |
3 | 1. vnpy框架4.0版本升级适配
4 |
5 | # 1.0.5版本
6 |
7 | 1. 修复Tick汇总数据的条数统计错误
8 |
9 | # 1.0.4版本
10 |
11 | 1. 增加TickOverview支持
12 | 2. 增加stream流式写入支持
13 |
14 | # 1.0.3版本
15 |
16 | 1. 修复K线数据量统计的问题(使用新的count_documents函数)
17 |
18 | # 1.0.2版本
19 |
20 | 1. 支持pymongo 4.0版本新增的批量写入功能,提高数据保存速度
21 | 2. 完善函数和变量的类型声明
22 | 3. 修复由于时间戳的时区信息缺失,导致的数据加载范围偏差问题
23 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
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.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # VeighNa框架的MongoDB数据库接口
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | ## 说明
14 |
15 | 基于pymongo 4.11.3 开发的MongoDB数据库接口。
16 |
17 | ## 使用
18 |
19 | 在VeighNa中使用MongoDB时,需要在全局配置中填写以下字段信息:
20 |
21 | |名称|含义|必填|举例|
22 | |---------|----|---|---|
23 | |database.name|名称|是|mongodb|
24 | |database.host|地址|是|localhost|
25 | |database.port|端口|是|27017|
26 | |database.database|实例|是|vnpy|
27 | |database.user|用户名|否||
28 | |database.password|密码|否||
29 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [project]
2 | name = "vnpy_mongodb"
3 | dynamic = ["version"]
4 | description = "MongoDB database adapter for VeighNa quant trading framework."
5 | readme = "README.md"
6 | license = {text = "MIT"}
7 | authors = [{name = "Xiaoyou Chen", email = "xiaoyou.chen@mail.vnpy.com"}]
8 | classifiers = [
9 | "Development Status :: 5 - Production/Stable",
10 | "License :: OSI Approved :: MIT License",
11 | "Operating System :: OS Independent",
12 | "Programming Language :: Python :: 3",
13 | "Programming Language :: Python :: 3.10",
14 | "Programming Language :: Python :: 3.11",
15 | "Programming Language :: Python :: 3.12",
16 | "Programming Language :: Python :: 3.13",
17 | "Topic :: Office/Business :: Financial :: Investment",
18 | "Programming Language :: Python :: Implementation :: CPython",
19 | "Natural Language :: Chinese (Simplified)",
20 | "Typing :: Typed"
21 | ]
22 | requires-python = ">=3.10"
23 | dependencies = [
24 | "pymongo>=4.11.3",
25 | ]
26 | keywords = ["quant", "quantitative", "investment", "trading", "algotrading"]
27 |
28 | [project.urls]
29 | "Homepage" = "https://www.vnpy.com"
30 | "Documentation" = "https://www.vnpy.com/docs"
31 | "Changes" = "https://github.com/vnpy/vnpy_mongodb/blob/master/CHANGELOG.md"
32 | "Source" = "https://github.com/vnpy/vnpy_mongodb/"
33 | "Forum" = "https://www.vnpy.com/forum"
34 |
35 | [build-system]
36 | requires = ["hatchling>=1.27.0"]
37 | build-backend = "hatchling.build"
38 |
39 | [tool.hatch.version]
40 | path = "vnpy_mongodb/__init__.py"
41 | pattern = "__version__ = ['\"](?P[^'\"]+)['\"]"
42 |
43 | [tool.hatch.build.targets.wheel]
44 | packages = ["vnpy_mongodb"]
45 | include-package-data = true
46 |
47 | [tool.hatch.build.targets.sdist]
48 | include = ["vnpy_mongodb*"]
49 |
50 | [tool.ruff]
51 | target-version = "py310"
52 | output-format = "full"
53 |
54 | [tool.ruff.lint]
55 | select = [
56 | "B", # flake8-bugbear
57 | "E", # pycodestyle error
58 | "F", # pyflakes
59 | "UP", # pyupgrade
60 | "W", # pycodestyle warning
61 | ]
62 | ignore = ["E501"]
63 |
64 | [tool.mypy]
65 | python_version = "3.10"
66 | warn_return_any = true
67 | warn_unused_configs = true
68 | disallow_untyped_defs = true
69 | disallow_incomplete_defs = true
70 | check_untyped_defs = true
71 | disallow_untyped_decorators = true
72 | no_implicit_optional = true
73 | strict_optional = true
74 | warn_redundant_casts = true
75 | warn_unused_ignores = true
76 | warn_no_return = true
77 | ignore_missing_imports = true
78 |
--------------------------------------------------------------------------------
/vnpy_mongodb/__init__.py:
--------------------------------------------------------------------------------
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.
22 |
23 |
24 | from .mongodb_database import MongodbDatabase as Database
25 |
26 |
27 | __all__ = ["Database"]
28 |
29 |
30 | __version__ = "1.1.0"
31 |
--------------------------------------------------------------------------------
/vnpy_mongodb/mongodb_database.py:
--------------------------------------------------------------------------------
1 | """"""
2 | from datetime import datetime
3 | from typing import Any
4 |
5 | from pymongo import ASCENDING, MongoClient, ReplaceOne
6 | from pymongo.database import Database
7 | from pymongo.cursor import Cursor
8 | from pymongo.collection import Collection
9 | from pymongo.results import DeleteResult
10 |
11 | from vnpy.trader.constant import Exchange, Interval
12 | from vnpy.trader.object import BarData, TickData
13 | from vnpy.trader.database import BaseDatabase, BarOverview, TickOverview, DB_TZ
14 | from vnpy.trader.setting import SETTINGS
15 |
16 |
17 | class MongodbDatabase(BaseDatabase):
18 | """MongoDB数据库接口"""
19 |
20 | def __init__(self) -> None:
21 | """"""
22 | # 读取配置
23 | self.database: str = SETTINGS["database.database"]
24 | self.host: str = SETTINGS["database.host"]
25 | self.port: int = SETTINGS["database.port"]
26 | self.username: str = SETTINGS["database.user"]
27 | self.password: str = SETTINGS["database.password"]
28 |
29 | # 创建客户端
30 | if self.username and self.password:
31 | self.client: MongoClient = MongoClient(
32 | host=self.host,
33 | port=self.port,
34 | tz_aware=True,
35 | username=self.username,
36 | password=self.password,
37 | tzinfo=DB_TZ
38 | )
39 | else:
40 | self.client = MongoClient(
41 | host=self.host,
42 | port=self.port,
43 | tz_aware=True,
44 | tzinfo=DB_TZ
45 | )
46 |
47 | # 初始化数据库
48 | self.db: Database = self.client[self.database]
49 |
50 | # 初始化K线数据表
51 | self.bar_collection: Collection = self.db["bar_data"]
52 | self.bar_collection.create_index(
53 | [
54 | ("exchange", ASCENDING),
55 | ("symbol", ASCENDING),
56 | ("interval", ASCENDING),
57 | ("datetime", ASCENDING),
58 | ],
59 | unique=True
60 | )
61 |
62 | # 初始化Tick数据表
63 | self.tick_collection: Collection = self.db["tick_data"]
64 | self.tick_collection.create_index(
65 | [
66 | ("exchange", ASCENDING),
67 | ("symbol", ASCENDING),
68 | ("datetime", ASCENDING),
69 | ],
70 | unique=True
71 | )
72 |
73 | # 初始化K线概览表
74 | self.bar_overview_collection: Collection = self.db["bar_overview"]
75 | self.bar_overview_collection.create_index(
76 | [
77 | ("exchange", ASCENDING),
78 | ("symbol", ASCENDING),
79 | ("interval", ASCENDING),
80 | ],
81 | unique=True
82 | )
83 |
84 | # 初始化Tick概览表
85 | self.tick_overview_collection: Collection = self.db["tick_overview"]
86 | self.tick_overview_collection.create_index(
87 | [
88 | ("exchange", ASCENDING),
89 | ("symbol", ASCENDING),
90 | ],
91 | unique=True
92 | )
93 |
94 | def save_bar_data(self, bars: list[BarData], stream: bool = False) -> bool:
95 | """保存K线数据"""
96 | requests: list[ReplaceOne] = []
97 |
98 | for bar in bars:
99 | # 逐个插入
100 | filter: dict = {
101 | "symbol": bar.symbol,
102 | "exchange": bar.exchange.value,
103 | "datetime": bar.datetime,
104 | "interval": bar.interval.value,
105 | }
106 |
107 | d: dict = {
108 | "symbol": bar.symbol,
109 | "exchange": bar.exchange.value,
110 | "datetime": bar.datetime,
111 | "interval": bar.interval.value,
112 | "volume": bar.volume,
113 | "turnover": bar.turnover,
114 | "open_interest": bar.open_interest,
115 | "open_price": bar.open_price,
116 | "high_price": bar.high_price,
117 | "low_price": bar.low_price,
118 | "close_price": bar.close_price,
119 | }
120 |
121 | requests.append(ReplaceOne(filter, d, upsert=True))
122 |
123 | self.bar_collection.bulk_write(requests, ordered=False)
124 |
125 | # 更新汇总
126 | filter = {
127 | "symbol": bar.symbol,
128 | "exchange": bar.exchange.value,
129 | "interval": bar.interval.value
130 | }
131 |
132 | overview: dict | None = self.bar_overview_collection.find_one(filter)
133 |
134 | if not overview:
135 | overview = {
136 | "symbol": bar.symbol,
137 | "exchange": bar.exchange.value,
138 | "interval": bar.interval.value,
139 | "count": len(bars),
140 | "start": bars[0].datetime,
141 | "end": bars[-1].datetime
142 | }
143 | elif stream:
144 | overview["end"] = bars[-1].datetime
145 | overview["count"] += len(bars)
146 | else:
147 | overview["start"] = min(bars[0].datetime, overview["start"])
148 | overview["end"] = max(bars[-1].datetime, overview["end"])
149 | overview["count"] = self.bar_collection.count_documents(filter)
150 |
151 | self.bar_overview_collection.update_one(filter, {"$set": overview}, upsert=True)
152 |
153 | return True
154 |
155 | def save_tick_data(self, ticks: list[TickData], stream: bool = False) -> bool:
156 | """保存TICK数据"""
157 | requests: list[ReplaceOne] = []
158 |
159 | for tick in ticks:
160 | filter: dict = {
161 | "symbol": tick.symbol,
162 | "exchange": tick.exchange.value,
163 | "datetime": tick.datetime,
164 | }
165 |
166 | d: dict = {
167 | "symbol": tick.symbol,
168 | "exchange": tick.exchange.value,
169 | "datetime": tick.datetime,
170 | "name": tick.name,
171 | "volume": tick.volume,
172 | "turnover": tick.turnover,
173 | "open_interest": tick.open_interest,
174 | "last_price": tick.last_price,
175 | "last_volume": tick.last_volume,
176 | "limit_up": tick.limit_up,
177 | "limit_down": tick.limit_down,
178 | "open_price": tick.open_price,
179 | "high_price": tick.high_price,
180 | "low_price": tick.low_price,
181 | "pre_close": tick.pre_close,
182 | "bid_price_1": tick.bid_price_1,
183 | "bid_price_2": tick.bid_price_2,
184 | "bid_price_3": tick.bid_price_3,
185 | "bid_price_4": tick.bid_price_4,
186 | "bid_price_5": tick.bid_price_5,
187 | "ask_price_1": tick.ask_price_1,
188 | "ask_price_2": tick.ask_price_2,
189 | "ask_price_3": tick.ask_price_3,
190 | "ask_price_4": tick.ask_price_4,
191 | "ask_price_5": tick.ask_price_5,
192 | "bid_volume_1": tick.bid_volume_1,
193 | "bid_volume_2": tick.bid_volume_2,
194 | "bid_volume_3": tick.bid_volume_3,
195 | "bid_volume_4": tick.bid_volume_4,
196 | "bid_volume_5": tick.bid_volume_5,
197 | "ask_volume_1": tick.ask_volume_1,
198 | "ask_volume_2": tick.ask_volume_2,
199 | "ask_volume_3": tick.ask_volume_3,
200 | "ask_volume_4": tick.ask_volume_4,
201 | "ask_volume_5": tick.ask_volume_5,
202 | "localtime": tick.localtime,
203 | }
204 |
205 | requests.append(ReplaceOne(filter, d, upsert=True))
206 |
207 | self.tick_collection.bulk_write(requests, ordered=False)
208 |
209 | # 更新Tick汇总
210 | filter = {
211 | "symbol": tick.symbol,
212 | "exchange": tick.exchange.value
213 | }
214 |
215 | overview: dict | None = self.tick_overview_collection.find_one(filter)
216 |
217 | if not overview:
218 | overview = {
219 | "symbol": tick.symbol,
220 | "exchange": tick.exchange.value,
221 | "count": len(ticks),
222 | "start": ticks[0].datetime,
223 | "end": ticks[-1].datetime
224 | }
225 | elif stream:
226 | overview["end"] = ticks[-1].datetime
227 | overview["count"] += len(ticks)
228 | else:
229 | overview["start"] = min(ticks[0].datetime, overview["start"])
230 | overview["end"] = max(ticks[-1].datetime, overview["end"])
231 | overview["count"] = self.tick_collection.count_documents(filter)
232 |
233 | self.tick_overview_collection.update_one(filter, {"$set": overview}, upsert=True)
234 |
235 | return True
236 |
237 | def load_bar_data(
238 | self,
239 | symbol: str,
240 | exchange: Exchange,
241 | interval: Interval,
242 | start: datetime,
243 | end: datetime
244 | ) -> list[BarData]:
245 | """读取K线数据"""
246 | filter: dict = {
247 | "symbol": symbol,
248 | "exchange": exchange.value,
249 | "interval": interval.value,
250 | "datetime": {
251 | "$gte": start.astimezone(DB_TZ),
252 | "$lte": end.astimezone(DB_TZ)
253 | }
254 | }
255 |
256 | c: Cursor = self.bar_collection.find(filter)
257 |
258 | bars: list[BarData] = []
259 | for d in c:
260 | d["exchange"] = Exchange(d["exchange"])
261 | d["interval"] = Interval(d["interval"])
262 | d["gateway_name"] = "DB"
263 | d.pop("_id")
264 |
265 | bar = BarData(**d)
266 | bars.append(bar)
267 |
268 | return bars
269 |
270 | def load_tick_data(
271 | self,
272 | symbol: str,
273 | exchange: Exchange,
274 | start: datetime,
275 | end: datetime
276 | ) -> list[TickData]:
277 | """读取TICK数据"""
278 | filter: dict = {
279 | "symbol": symbol,
280 | "exchange": exchange.value,
281 | "datetime": {
282 | "$gte": start.astimezone(DB_TZ),
283 | "$lte": end.astimezone(DB_TZ)
284 | }
285 | }
286 |
287 | c: Cursor = self.tick_collection.find(filter)
288 |
289 | ticks: list[TickData] = []
290 | for d in c:
291 | d["exchange"] = Exchange(d["exchange"])
292 | d["gateway_name"] = "DB"
293 | d.pop("_id")
294 |
295 | tick: TickData = TickData(**d)
296 | ticks.append(tick)
297 |
298 | return ticks
299 |
300 | def delete_bar_data(
301 | self,
302 | symbol: str,
303 | exchange: Exchange,
304 | interval: Interval
305 | ) -> Any:
306 | """删除K线数据"""
307 | filter: dict = {
308 | "symbol": symbol,
309 | "exchange": exchange.value,
310 | "interval": interval.value,
311 | }
312 |
313 | result: DeleteResult = self.bar_collection.delete_many(filter)
314 | self.bar_overview_collection.delete_one(filter)
315 |
316 | return result.deleted_count
317 |
318 | def delete_tick_data(
319 | self,
320 | symbol: str,
321 | exchange: Exchange
322 | ) -> Any:
323 | """删除TICK数据"""
324 | filter: dict = {
325 | "symbol": symbol,
326 | "exchange": exchange.value
327 | }
328 |
329 | result: DeleteResult = self.tick_collection.delete_many(filter)
330 | self.tick_overview_collection.delete_one(filter)
331 |
332 | return result.deleted_count
333 |
334 | def get_bar_overview(self) -> list[BarOverview]:
335 | """查询数据库中的K线汇总信息"""
336 | c: Cursor = self.bar_overview_collection.find()
337 |
338 | overviews: list[BarOverview] = []
339 | for d in c:
340 | d["exchange"] = Exchange(d["exchange"])
341 | d["interval"] = Interval(d["interval"])
342 | d.pop("_id")
343 |
344 | overview: BarOverview = BarOverview(**d)
345 | overviews.append(overview)
346 |
347 | return overviews
348 |
349 | def get_tick_overview(self) -> list[TickOverview]:
350 | """查询数据库中的Tick汇总信息"""
351 | c: Cursor = self.tick_overview_collection.find()
352 |
353 | overviews: list[TickOverview] = []
354 | for d in c:
355 | d["exchange"] = Exchange(d["exchange"])
356 | d.pop("_id")
357 |
358 | overview: TickOverview = TickOverview(**d)
359 | overviews.append(overview)
360 |
361 | return overviews
362 |
--------------------------------------------------------------------------------