carrotquant-data 1.2.1__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.
Files changed (182) hide show
  1. carrotquant_data-1.2.1/.github/workflows/tests.yml +47 -0
  2. carrotquant_data-1.2.1/.github/workflows/workflow.yml +52 -0
  3. carrotquant_data-1.2.1/.gitignore +28 -0
  4. carrotquant_data-1.2.1/.idea/.gitignore +5 -0
  5. carrotquant_data-1.2.1/.idea/CarrotQuant.Data.iml +17 -0
  6. carrotquant_data-1.2.1/.idea/inspectionProfiles/profiles_settings.xml +6 -0
  7. carrotquant_data-1.2.1/.idea/misc.xml +7 -0
  8. carrotquant_data-1.2.1/.idea/modules.xml +8 -0
  9. carrotquant_data-1.2.1/.idea/vcs.xml +6 -0
  10. carrotquant_data-1.2.1/AGENTS.md +381 -0
  11. carrotquant_data-1.2.1/PKG-INFO +327 -0
  12. carrotquant_data-1.2.1/README.md +308 -0
  13. carrotquant_data-1.2.1/config/config.yaml.sample +21 -0
  14. carrotquant_data-1.2.1/cqdata/__init__.py +56 -0
  15. carrotquant_data-1.2.1/cqdata/config/__init__.py +3 -0
  16. carrotquant_data-1.2.1/cqdata/config/settings.py +106 -0
  17. carrotquant_data-1.2.1/cqdata/entrypoints/__init__.py +39 -0
  18. carrotquant_data-1.2.1/cqdata/entrypoints/accessors/__init__.py +38 -0
  19. carrotquant_data-1.2.1/cqdata/entrypoints/accessors/aindex.py +60 -0
  20. carrotquant_data-1.2.1/cqdata/entrypoints/accessors/ashare.py +162 -0
  21. carrotquant_data-1.2.1/cqdata/entrypoints/accessors/base.py +134 -0
  22. carrotquant_data-1.2.1/cqdata/entrypoints/cli.py +185 -0
  23. carrotquant_data-1.2.1/cqdata/entrypoints/python_api.py +159 -0
  24. carrotquant_data-1.2.1/cqdata/entrypoints/rest_api.py +745 -0
  25. carrotquant_data-1.2.1/cqdata/provider/__init__.py +0 -0
  26. carrotquant_data-1.2.1/cqdata/provider/baostock_provider.py +395 -0
  27. carrotquant_data-1.2.1/cqdata/provider/base.py +71 -0
  28. carrotquant_data-1.2.1/cqdata/provider/data_cleaner.py +105 -0
  29. carrotquant_data-1.2.1/cqdata/provider/eastmoney_provider.py +713 -0
  30. carrotquant_data-1.2.1/cqdata/provider/em_utils.py +154 -0
  31. carrotquant_data-1.2.1/cqdata/provider/provider_manager.py +43 -0
  32. carrotquant_data-1.2.1/cqdata/provider/tdx_provider.py +235 -0
  33. carrotquant_data-1.2.1/cqdata/provider/tdx_utils.py +478 -0
  34. carrotquant_data-1.2.1/cqdata/service/__init__.py +1 -0
  35. carrotquant_data-1.2.1/cqdata/service/data_reader.py +308 -0
  36. carrotquant_data-1.2.1/cqdata/service/metadata_manager.py +51 -0
  37. carrotquant_data-1.2.1/cqdata/service/metadata_reader.py +263 -0
  38. carrotquant_data-1.2.1/cqdata/service/sync_manager.py +303 -0
  39. carrotquant_data-1.2.1/cqdata/service/sync_tracker.py +128 -0
  40. carrotquant_data-1.2.1/cqdata/service/task_planner.py +108 -0
  41. carrotquant_data-1.2.1/cqdata/static/assets/index-BDBCqr4Y.js +19 -0
  42. carrotquant_data-1.2.1/cqdata/static/assets/index-Cpa46ehI.css +2 -0
  43. carrotquant_data-1.2.1/cqdata/static/favicon.svg +1 -0
  44. carrotquant_data-1.2.1/cqdata/static/icons.svg +24 -0
  45. carrotquant_data-1.2.1/cqdata/static/index.html +14 -0
  46. carrotquant_data-1.2.1/cqdata/storage/__init__.py +0 -0
  47. carrotquant_data-1.2.1/cqdata/storage/base.py +104 -0
  48. carrotquant_data-1.2.1/cqdata/storage/csv_storage.py +281 -0
  49. carrotquant_data-1.2.1/cqdata/storage/data_merger.py +68 -0
  50. carrotquant_data-1.2.1/cqdata/storage/parquet_storage.py +286 -0
  51. carrotquant_data-1.2.1/cqdata/storage/storage_factory.py +27 -0
  52. carrotquant_data-1.2.1/cqdata/utils/__init__.py +1 -0
  53. carrotquant_data-1.2.1/cqdata/utils/logger_utils.py +166 -0
  54. carrotquant_data-1.2.1/cqdata/utils/time_utils.py +99 -0
  55. carrotquant_data-1.2.1/demo/em/a_stock.py +2023 -0
  56. carrotquant_data-1.2.1/demo/em/concept_board_em.py +302 -0
  57. carrotquant_data-1.2.1/demo/em/dragon_tiger_em.py +358 -0
  58. carrotquant_data-1.2.1/demo/em/em_utils.py +167 -0
  59. carrotquant_data-1.2.1/demo/em/industry_board_em.py +309 -0
  60. carrotquant_data-1.2.1/demo/em/institutional_trade_daily_em.py +353 -0
  61. carrotquant_data-1.2.1/demo/tdx/compare_with_baostock.py +195 -0
  62. carrotquant_data-1.2.1/demo/tdx/explore_tdx.py +124 -0
  63. carrotquant_data-1.2.1/demo/tdx/mootdx_online_demo.py +45 -0
  64. carrotquant_data-1.2.1/demo/tdx/test_max_offset.py +105 -0
  65. carrotquant_data-1.2.1/demo/tdx/test_tdx_online.py +178 -0
  66. carrotquant_data-1.2.1/demo/tdx/test_tdx_provider.py +85 -0
  67. carrotquant_data-1.2.1/dev.bat +25 -0
  68. carrotquant_data-1.2.1/docs/python_sdk_guide.md +407 -0
  69. carrotquant_data-1.2.1/docs/rest_api_guide.md +301 -0
  70. carrotquant_data-1.2.1/docs/web_terminal_guide.md +83 -0
  71. carrotquant_data-1.2.1/examples/01_quickstart.py +46 -0
  72. carrotquant_data-1.2.1/examples/02_sync_data.py +36 -0
  73. carrotquant_data-1.2.1/examples/03_read_series.py +36 -0
  74. carrotquant_data-1.2.1/examples/04_read_events.py +25 -0
  75. carrotquant_data-1.2.1/examples/05_export_pandas.py +25 -0
  76. carrotquant_data-1.2.1/examples/05_read_concept_boards.py +25 -0
  77. carrotquant_data-1.2.1/examples/06_metadata_inspection.py +32 -0
  78. carrotquant_data-1.2.1/examples/README.md +32 -0
  79. carrotquant_data-1.2.1/pyproject.toml +78 -0
  80. carrotquant_data-1.2.1/scripts/download_tdx.py +181 -0
  81. carrotquant_data-1.2.1/scripts/download_test_data.ps1 +76 -0
  82. carrotquant_data-1.2.1/scripts/wizard.py +229 -0
  83. carrotquant_data-1.2.1/tests/conftest.py +49 -0
  84. carrotquant_data-1.2.1/tests/integration/__init__.py +0 -0
  85. carrotquant_data-1.2.1/tests/integration/test_cqdata_cli.py +112 -0
  86. carrotquant_data-1.2.1/tests/integration/test_cqdata_sdk.py +92 -0
  87. carrotquant_data-1.2.1/tests/integration/test_integration_fs_list.py +51 -0
  88. carrotquant_data-1.2.1/tests/integration/test_integration_tdx_sync.py +94 -0
  89. carrotquant_data-1.2.1/tests/integration/test_provider_baostock.py +138 -0
  90. carrotquant_data-1.2.1/tests/integration/test_provider_eastmoney.py +181 -0
  91. carrotquant_data-1.2.1/tests/integration/test_rest_api_integration.py +105 -0
  92. carrotquant_data-1.2.1/tests/integration/test_sync_defense.py +300 -0
  93. carrotquant_data-1.2.1/tests/integration/test_sync_full_flow.py +180 -0
  94. carrotquant_data-1.2.1/tests/integration/test_sync_multi_storage.py +366 -0
  95. carrotquant_data-1.2.1/tests/unit/__init__.py +0 -0
  96. carrotquant_data-1.2.1/tests/unit/test_accessors.py +105 -0
  97. carrotquant_data-1.2.1/tests/unit/test_config_settings.py +54 -0
  98. carrotquant_data-1.2.1/tests/unit/test_data_reader.py +121 -0
  99. carrotquant_data-1.2.1/tests/unit/test_entrypoint_python_api.py +86 -0
  100. carrotquant_data-1.2.1/tests/unit/test_entrypoint_rest_api.py +301 -0
  101. carrotquant_data-1.2.1/tests/unit/test_examples.py +57 -0
  102. carrotquant_data-1.2.1/tests/unit/test_logger_utils.py +120 -0
  103. carrotquant_data-1.2.1/tests/unit/test_metadata_manager.py +146 -0
  104. carrotquant_data-1.2.1/tests/unit/test_metadata_reader.py +134 -0
  105. carrotquant_data-1.2.1/tests/unit/test_provider_baostock.py +215 -0
  106. carrotquant_data-1.2.1/tests/unit/test_provider_eastmoney.py +527 -0
  107. carrotquant_data-1.2.1/tests/unit/test_provider_tdx.py +568 -0
  108. carrotquant_data-1.2.1/tests/unit/test_rest_api_boards.py +107 -0
  109. carrotquant_data-1.2.1/tests/unit/test_rest_api_sync.py +84 -0
  110. carrotquant_data-1.2.1/tests/unit/test_script_wizard.py +66 -0
  111. carrotquant_data-1.2.1/tests/unit/test_service_batch_sync.py +115 -0
  112. carrotquant_data-1.2.1/tests/unit/test_service_planner.py +469 -0
  113. carrotquant_data-1.2.1/tests/unit/test_storage_concurrency.py +79 -0
  114. carrotquant_data-1.2.1/tests/unit/test_storage_csv.py +471 -0
  115. carrotquant_data-1.2.1/tests/unit/test_storage_factory.py +52 -0
  116. carrotquant_data-1.2.1/tests/unit/test_storage_flat.py +295 -0
  117. carrotquant_data-1.2.1/tests/unit/test_storage_merger.py +243 -0
  118. carrotquant_data-1.2.1/tests/unit/test_storage_parquet.py +383 -0
  119. carrotquant_data-1.2.1/tests/unit/test_sync_and_metadata.py +252 -0
  120. carrotquant_data-1.2.1/tests/unit/test_sync_tracker.py +54 -0
  121. carrotquant_data-1.2.1/tests/unit/test_update_metadata.py +349 -0
  122. carrotquant_data-1.2.1/tests/unit/test_utils_time.py +212 -0
  123. carrotquant_data-1.2.1/uv.lock +1087 -0
  124. carrotquant_data-1.2.1/web/.gitignore +29 -0
  125. carrotquant_data-1.2.1/web/.oxlintrc.json +8 -0
  126. carrotquant_data-1.2.1/web/README.md +32 -0
  127. carrotquant_data-1.2.1/web/bun.lock +509 -0
  128. carrotquant_data-1.2.1/web/e2e/terminal.spec.ts +15 -0
  129. carrotquant_data-1.2.1/web/index.html +13 -0
  130. carrotquant_data-1.2.1/web/package.json +39 -0
  131. carrotquant_data-1.2.1/web/postcss.config.js +6 -0
  132. carrotquant_data-1.2.1/web/public/favicon.svg +1 -0
  133. carrotquant_data-1.2.1/web/public/icons.svg +24 -0
  134. carrotquant_data-1.2.1/web/src/App.tsx +183 -0
  135. carrotquant_data-1.2.1/web/src/__tests__/ConceptData.test.ts +37 -0
  136. carrotquant_data-1.2.1/web/src/__tests__/DataTable.test.ts +40 -0
  137. carrotquant_data-1.2.1/web/src/__tests__/SearchInput.test.ts +38 -0
  138. carrotquant_data-1.2.1/web/src/__tests__/apiClient.test.ts +149 -0
  139. carrotquant_data-1.2.1/web/src/__tests__/chartEngine.test.ts +97 -0
  140. carrotquant_data-1.2.1/web/src/__tests__/chartPerformance.test.ts +25 -0
  141. carrotquant_data-1.2.1/web/src/__tests__/indicators.test.ts +58 -0
  142. carrotquant_data-1.2.1/web/src/__tests__/marketDataHook.test.ts +35 -0
  143. carrotquant_data-1.2.1/web/src/__tests__/performance.test.ts +65 -0
  144. carrotquant_data-1.2.1/web/src/__tests__/pinyin.test.ts +32 -0
  145. carrotquant_data-1.2.1/web/src/__tests__/stockDictionary.test.ts +42 -0
  146. carrotquant_data-1.2.1/web/src/__tests__/transformers.test.ts +72 -0
  147. carrotquant_data-1.2.1/web/src/__tests__/viewsRender.test.tsx +98 -0
  148. carrotquant_data-1.2.1/web/src/components/DataTable.tsx +164 -0
  149. carrotquant_data-1.2.1/web/src/components/ErrorBoundary.tsx +89 -0
  150. carrotquant_data-1.2.1/web/src/components/FileExplorerModal.tsx +260 -0
  151. carrotquant_data-1.2.1/web/src/components/FloatingSyncWidget.tsx +122 -0
  152. carrotquant_data-1.2.1/web/src/components/HeaderBar.tsx +91 -0
  153. carrotquant_data-1.2.1/web/src/components/LogTerminal.tsx +230 -0
  154. carrotquant_data-1.2.1/web/src/components/SearchInput.tsx +244 -0
  155. carrotquant_data-1.2.1/web/src/components/SidebarNav.tsx +144 -0
  156. carrotquant_data-1.2.1/web/src/components/SyncModal.tsx +333 -0
  157. carrotquant_data-1.2.1/web/src/components/TableManagementGrid.tsx +540 -0
  158. carrotquant_data-1.2.1/web/src/components/TradingViewKLineChart.tsx +158 -0
  159. carrotquant_data-1.2.1/web/src/hooks/useConceptData.ts +158 -0
  160. carrotquant_data-1.2.1/web/src/hooks/useMarketData.ts +176 -0
  161. carrotquant_data-1.2.1/web/src/hooks/useTables.ts +75 -0
  162. carrotquant_data-1.2.1/web/src/index.css +45 -0
  163. carrotquant_data-1.2.1/web/src/main.tsx +10 -0
  164. carrotquant_data-1.2.1/web/src/services/apiClient.ts +188 -0
  165. carrotquant_data-1.2.1/web/src/services/chartEngine.ts +259 -0
  166. carrotquant_data-1.2.1/web/src/services/indicators.ts +218 -0
  167. carrotquant_data-1.2.1/web/src/services/pinyin.ts +133 -0
  168. carrotquant_data-1.2.1/web/src/services/stockDictionary.ts +129 -0
  169. carrotquant_data-1.2.1/web/src/services/transformers.ts +161 -0
  170. carrotquant_data-1.2.1/web/src/types/api.ts +342 -0
  171. carrotquant_data-1.2.1/web/src/views/ConceptIndustryView.tsx +208 -0
  172. carrotquant_data-1.2.1/web/src/views/DataManagementView.tsx +159 -0
  173. carrotquant_data-1.2.1/web/src/views/DataMatrixView.tsx +162 -0
  174. carrotquant_data-1.2.1/web/src/views/LogCenterView.tsx +29 -0
  175. carrotquant_data-1.2.1/web/src/views/SettingsView.tsx +208 -0
  176. carrotquant_data-1.2.1/web/src/views/StockDetailView.tsx +130 -0
  177. carrotquant_data-1.2.1/web/src/views/StockListView.tsx +231 -0
  178. carrotquant_data-1.2.1/web/tailwind.config.js +27 -0
  179. carrotquant_data-1.2.1/web/tsconfig.app.json +29 -0
  180. carrotquant_data-1.2.1/web/tsconfig.json +7 -0
  181. carrotquant_data-1.2.1/web/tsconfig.node.json +23 -0
  182. carrotquant_data-1.2.1/web/vite.config.ts +28 -0
@@ -0,0 +1,47 @@
1
+ name: Tests
2
+
3
+ on:
4
+ push:
5
+ branches: [ "main", "master" ]
6
+ pull_request:
7
+ branches: [ "main", "master" ]
8
+
9
+ jobs:
10
+ test:
11
+ name: Fullstack CI Tests
12
+ runs-on: windows-latest
13
+ strategy:
14
+ matrix:
15
+ python-version: ["3.12"]
16
+
17
+ steps:
18
+ - name: Checkout Code
19
+ uses: actions/checkout@v4
20
+
21
+ - name: Setup Bun Environment
22
+ uses: oven-sh/setup-bun@v2
23
+ with:
24
+ bun-version: latest
25
+
26
+ - name: Install Frontend Dependencies, Run Unit Tests & Build UI
27
+ run: |
28
+ cd web
29
+ bun install
30
+ bun run test:unit
31
+ bun run build
32
+ shell: pwsh
33
+
34
+ - name: Install uv
35
+ uses: astral-sh/setup-uv@v5
36
+ with:
37
+ version: "latest"
38
+ enable-cache: true
39
+
40
+ - name: Set up Python ${{ matrix.python-version }}
41
+ run: uv python install ${{ matrix.python-version }}
42
+
43
+ - name: Install Python Dependencies
44
+ run: uv sync --all-extras --dev
45
+
46
+ - name: Run Pytest (Backend)
47
+ run: uv run pytest -m "not network"
@@ -0,0 +1,52 @@
1
+ name: Publish to PyPI and Create Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - 'v*'
7
+ workflow_dispatch:
8
+
9
+ jobs:
10
+ pypi-publish:
11
+ name: Build and publish to PyPI
12
+ runs-on: ubuntu-latest
13
+ permissions:
14
+ id-token: write # PyPI Trusted Publishing (OIDC) 必需权限
15
+ contents: write # 创建 GitHub Release 权限
16
+
17
+ steps:
18
+ - name: Checkout code
19
+ uses: actions/checkout@v4
20
+
21
+ # 1. 构建 Web 前端静态资源 (使用 Bun 极速构建,输出到 cqdata/static)
22
+ - name: Setup Bun
23
+ uses: oven-sh/setup-bun@v2
24
+ with:
25
+ bun-version: latest
26
+
27
+ - name: Build Web UI
28
+ run: |
29
+ cd web
30
+ bun install --frozen-lockfile
31
+ bun run build
32
+
33
+ # 2. 安装 uv 并打包 Python Wheel (包含 static 资源)
34
+ - name: Install uv
35
+ uses: astral-sh/setup-uv@v5
36
+ with:
37
+ enable-cache: true
38
+
39
+ - name: Build package
40
+ run: uv build
41
+
42
+ # 3. 通过 OIDC 免密发布到 PyPI
43
+ - name: Publish to PyPI
44
+ uses: pypa/gh-action-pypi-publish@release/v1
45
+
46
+ # 4. 自动创建 GitHub Release 并附加 dist/ 产物
47
+ - name: Create GitHub Release
48
+ uses: softprops/action-gh-release@v2
49
+ if: startsWith(github.ref, 'refs/tags/')
50
+ with:
51
+ generate_release_notes: true
52
+ files: dist/*
@@ -0,0 +1,28 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+
7
+ # Project specific
8
+ data/
9
+ test_*data*/
10
+ logs/
11
+ tdx_data/
12
+
13
+ config/config.yaml
14
+
15
+ # Testing & Coverage
16
+ .coverage
17
+ .coverage.*
18
+ htmlcov/
19
+ .pytest_cache/
20
+
21
+ # Virtual Environment & Build
22
+ .venv/
23
+ *.egg-info/
24
+ dist/
25
+ build/
26
+ cqdata/static/
27
+ web/dist/
28
+ web/node_modules/
@@ -0,0 +1,5 @@
1
+ # 默认忽略的文件
2
+ /shelf/
3
+ /workspace.xml
4
+ # 基于编辑器的 HTTP 客户端请求
5
+ /httpRequests/
@@ -0,0 +1,17 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <module type="PYTHON_MODULE" version="4">
3
+ <component name="NewModuleRootManager">
4
+ <content url="file://$MODULE_DIR$">
5
+ <excludeFolder url="file://$MODULE_DIR$/.venv" />
6
+ </content>
7
+ <orderEntry type="jdk" jdkName="uv (CarrotQuant.Data)" jdkType="Python SDK" />
8
+ <orderEntry type="sourceFolder" forTests="false" />
9
+ </component>
10
+ <component name="PyDocumentationSettings">
11
+ <option name="format" value="PLAIN" />
12
+ <option name="myDocStringFormat" value="Plain" />
13
+ </component>
14
+ <component name="TestRunnerService">
15
+ <option name="PROJECT_TEST_RUNNER" value="py.test" />
16
+ </component>
17
+ </module>
@@ -0,0 +1,6 @@
1
+ <component name="InspectionProjectProfileManager">
2
+ <settings>
3
+ <option name="USE_PROJECT_PROFILE" value="false" />
4
+ <version value="1.0" />
5
+ </settings>
6
+ </component>
@@ -0,0 +1,7 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="Black">
4
+ <option name="sdkName" value="uv (CarrotQuant.Data)" />
5
+ </component>
6
+ <component name="ProjectRootManager" version="2" project-jdk-name="uv (CarrotQuant.Data)" project-jdk-type="Python SDK" />
7
+ </project>
@@ -0,0 +1,8 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="ProjectModuleManager">
4
+ <modules>
5
+ <module fileurl="file://$PROJECT_DIR$/.idea/CarrotQuant.Data.iml" filepath="$PROJECT_DIR$/.idea/CarrotQuant.Data.iml" />
6
+ </modules>
7
+ </component>
8
+ </project>
@@ -0,0 +1,6 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="VcsDirectoryMappings">
4
+ <mapping directory="" vcs="Git" />
5
+ </component>
6
+ </project>
@@ -0,0 +1,381 @@
1
+ # AGENTS.md - CarrotQuant.Data 代码指南
2
+
3
+ 本文档为 AI Agent 提供对 CarrotQuant.Data 项目的完整理解,包含架构、模块职责、数据流、物理存储布局与开发约束。
4
+
5
+ ---
6
+
7
+ ## 1. 项目概述
8
+
9
+ CarrotQuant.Data 是一个轻量级、模块化的本地金融数据同步与管理工具。它从免费数据源(Baostock、东方财富、通达信)获取 A 股/指数数据,清洗后持久化到本地 CSV/Parquet 文件,供量化研究和回测使用。
10
+
11
+ **核心能力**:
12
+ - 支持 Baostock(日线/5分线/复权因子)、东方财富(概念/行业板块/龙虎榜/机构交易)、通达信(日线/5分/1分线)
13
+ - 支持 CSV 和 Parquet 两种存储格式
14
+ - 基于时间戳水位线的增量同步与断点续接
15
+ - 四种入口:Python SDK (OOP `cqdata.ashare.kline.get()` / 统一 `cqdata.read()`)、Typer CLI 控制台 (`cqdata`)、FastAPI REST API、**极速 React Web 终端 (`web/` 统一 Header 工作区 + TradingView 3-Pane 单屏无滚动图表 + 拼音/代码/名称通用搜索组件 `SearchInput` + 独立 `数据矩阵` 视图 + 数据中心与 Loguru SSE 日志流)**
16
+
17
+ **技术栈**:
18
+ - **后端**:Python >= 3.12, Polars (数据处理), Baostock, curl_cffi, tdxpy, FastAPI, Typer, Loguru, PyYAML
19
+ - **前端**:Bun, React 19, Vite 6, TypeScript 7, Tailwind CSS v4, TradingView Lightweight Charts
20
+
21
+ ---
22
+
23
+ ## 2. 目录结构与模块职责
24
+
25
+ ```
26
+ CarrotQuant.Data/
27
+ ├── cqdata/
28
+ │ ├── __init__.py # 统一导出符号 (ashare, aindex, read, list_tables 等),0 业务逻辑
29
+ │ ├── entrypoints/ # 接入层 (accessors/ OOP子包, python_api, cli, rest_api)
30
+ │ │ ├── accessors/ # OOP 便捷访问层 (base.py, ashare.py, aindex.py)
31
+ │ │ ├── python_api.py # Python SDK 底层切片与探查 API
32
+ │ │ ├── cli.py # Typer CLI 控制台主入口 (cqdata sync/server/info/tables)
33
+ │ │ └── rest_api.py # FastAPI REST HTTP 服务 (含 Loguru SSE 日志流 & /tables/detailed)
34
+ │ ├── config/ # 配置管理 (支持 CQDATA_DATA_DIR 环境变量与 YAML)
35
+ │ ├── provider/ # 数据源驱动层 (Baostock, EastMoney, TDX, DataCleaner, ProviderManager)
36
+ │ ├── service/ # 业务逻辑层 (SyncManager, SyncProgressTracker, DataReader, TaskPlanner, MetadataManager)
37
+ │ ├── storage/ # 持久化存储层 (CSVStorage, ParquetStorage, StorageFactory, DataMerger)
38
+ │ └── utils/ # 工具箱 (logger_utils, time_utils)
39
+ ├── web/ # React Web 金融终端 frontend (Bun + Vite 6 + Tailwind v4 + TradingView 3-Pane)
40
+ │ ├── src/
41
+ │ │ ├── components/ # HeaderBar, SearchInput, TradingViewKLineChart, TableManagementGrid, LogTerminal, FloatingSyncWidget 等
42
+ │ │ ├── views/ # StockListView (搜索与自选), ConceptIndustryView, StockDetailView, DataMatrixView (数据矩阵), DataManagementView (数据中心)
43
+ │ │ ├── services/ # apiClient, pinyin, transformers, indicators
44
+ │ │ └── hooks/ # useMarketData, useConceptData, useTables
45
+ │ └── package.json
46
+ ├── scripts/ # 辅助脚本 (wizard.py 交互向导, download_tdx.py)
47
+ ├── tests/ # 测试集 (unit, integration)
48
+ └── pyproject.toml # 项目依赖与构建配置
49
+ ```
50
+
51
+ ---
52
+
53
+ ## 3. 系统架构与数据流
54
+
55
+ ### 3.1 分层架构图
56
+
57
+ ```mermaid
58
+ graph TB
59
+ subgraph Entrypoints["Entrypoints 接入层 (cqdata/entrypoints)"]
60
+ WEB["web/ (React Web 终端)"]
61
+ PYTHON_API["python_api.py (Python SDK)"]
62
+ CLI["cli.py (Typer CLI)"]
63
+ REST["rest_api.py (FastAPI REST)"]
64
+ WIZARD["wizard.py (交互向导)"]
65
+ end
66
+
67
+ subgraph Service["Service 业务逻辑层 (cqdata/service)"]
68
+ SM["SyncManager 同步总调度"]
69
+ DR["DataReader 切片与投影"]
70
+ MR["MetadataReader 探查"]
71
+ TP["TaskPlanner 任务规划器"]
72
+ MM["MetadataManager 元数据 IO"]
73
+ end
74
+
75
+ subgraph Provider["Provider 数据采集层"]
76
+ PM["ProviderManager 单例工厂"]
77
+ BP["BaostockProvider"]
78
+ EP["EastMoneyProvider"]
79
+ TP_DRV["TDXProvider"]
80
+ DC["DataCleaner 时间标准化"]
81
+ end
82
+
83
+ subgraph Storage["Storage 持久化层"]
84
+ SF["StorageFactory 格式工厂"]
85
+ CSV["CSVStorage 按 symbol/年分片"]
86
+ PQ["ParquetStorage 年度大表"]
87
+ DM["DataMerger 去重/排序"]
88
+ end
89
+
90
+ subgraph External["外部数据源"]
91
+ BAOSTOCK["Baostock API"]
92
+ EASTMONEY["东财 push2 / datacenter API"]
93
+ TDX["通达信 TCP / vipdoc"]
94
+ end
95
+
96
+ subgraph Disk["磁盘存储"]
97
+ CSV_FILES[("CSV 文件")]
98
+ PQ_FILES[("Parquet 文件")]
99
+ META[("metadata.json")]
100
+ end
101
+
102
+ CLI --> SM
103
+ PYTHON_API --> SM
104
+ WIZARD --> SM
105
+
106
+ SM -->|"① get_provider()"| PM
107
+ SM -->|"② plan()"| TP
108
+ SM -->|"③ get_storage()"| SF
109
+ SM -->|"④ write_*()"| CSV
110
+ SM -->|"④ write_*()"| PQ
111
+ SM -->|"⑤ save()"| MM
112
+
113
+ TP -->|"load()"| MM
114
+ PM --> BP & EP & TP_DRV
115
+ BP --> BAOSTOCK
116
+ EP --> EASTMONEY
117
+ TP_DRV --> TDX
118
+ BP & EP & TP_DRV --> DC
119
+
120
+ SF --> CSV & PQ
121
+ CSV & PQ --> DM
122
+ CSV --> CSV_FILES
123
+ PQ --> PQ_FILES
124
+ MM --> META
125
+ ```
126
+
127
+ ### 3.2 同步数据流
128
+
129
+ ```
130
+ 用户请求 (CLI / Python SDK / API / Wizard)
131
+
132
+
133
+ SyncManager.sync()
134
+ ├── 1. ProviderManager.get_provider(table_id) -> 获得 Provider 实例
135
+ ├── 2. TaskPlanner.plan() -> 比较本地 metadata 水位线与目标时间,规划补充任务
136
+ ├── 3. 批处理循环 (batch_size 切分 symbols):
137
+ │ ├── Provider.fetch() -> 拉取原始数据 -> DataCleaner.standardize() 标准化
138
+ │ └── Batch pl.concat() -> 批量写入 CSVStorage / ParquetStorage
139
+ └── 4. 物理巡检与元数据更新:
140
+ Storage 检查物理状态 -> MetadataManager.save() 原子化更新 metadata.json
141
+ ```
142
+
143
+ ---
144
+
145
+ ## 4. 核心模块与类职责
146
+
147
+ ### 4.1 接入层与配置 (Gateway & Config)
148
+ - **`config/settings.py`**: 全局 `Settings` 配置管理,支持通过 `cqdata.configure()` 加载 YAML 配置,或优先使用环境变量 `CQDATA_DATA_DIR` 和 `CQDATA_CONFIG_PATH`。完整 YAML 配置示例见 [config.yaml.sample](file:///d:/Quant/CarrotQuant.Data/config/config.yaml.sample)。
149
+ - **`accessors/` 包**: 提供 OOP 便捷访问层子包(`ashare.kline`, `aindex.kline` 等)与 `DefaultConfig` 三层链式继承解析器,支持极其直观的 `.get()` 参数补全与智能默认值支持。
150
+ - **`python_api.py`**: 提供 SDK 高阶 API (`read`, `list_tables`, `sync`, `configure`, `get_schema`, `get_time_range` 等),以磁盘物理 `metadata.json` 为单事实来源直接高效路由。
151
+ - **`cli.py`**: 基于 Typer 的 CLI 工具 (`cqdata sync`, `cqdata tables`, `cqdata info`, `cqdata server`, `cqdata wizard`),支持通过 `cqdata server --open` 自动唤醒系统浏览器访问内置 Web 终端。
152
+ - **`rest_api.py`**: 基于 FastAPI 的 RESTful HTTP 服务,挂载 CORS 跨域中间件,提供 `GET /api/v1/tables` 探查与 `GET /api/v1/query` 统一切片查询,所有 Polars IO/磁盘读取端点均采用普通 `def` 函数声明派发至底层的 Worker 线程池并发处理,杜绝主事件循环卡顿,并内置托管 `cqdata/static/` 前端 SPA 静态资源。
153
+
154
+
155
+
156
+ ### 4.2 业务服务层 (Service)
157
+ - **`SyncManager`**: 数据同步总调度器,贯穿 Provider 拉取、批处理、Storage 写入与元数据盖章。
158
+ - **`DataReader` / `MetadataReader`**: 提供多年份切片读取、按列投影选择与元数据探查。
159
+ - **`TaskPlanner`**: 根据各格式的水位线(取保守交集)规划前向补全与后向拓展的任务区间(首次无水位且未指定 start_date 时默认 fallback 至 2020-01-01)。
160
+ - **`MetadataManager`**: 负责 `metadata.json` 的原子化读写(`.tmp` -> `os.replace` -> `fsync`)。
161
+
162
+ ### 4.3 数据驱动层 (Provider)
163
+ - **`BaseProvider` (ABC)**: 驱动抽象基类,规范 `fetch`, `get_all_symbols`, `get_supported_tables`, `get_table_category`, `get_sort_keys` 接口。
164
+ - **`BaostockProvider`**: Baostock 数据驱动,处理个股/指数 K 线与复权因子,支持 RLock 线程锁并发防护、API 错误码 (如网络接收错误/未登录) 识别与自动重新登录 (`_relogin`) 重试。
165
+ - **`EastMoneyProvider`**: 东方财富数据驱动,处理板块成分股、龙虎榜与机构交易,采用 TLS 指纹防封与节流重试。无 symbol 的宏观表 `get_all_symbols` 返回 `["_ALL_"]`。
166
+ - **`TDXProvider`**: 通达信数据驱动,支持 `online` (TCP 在线) 与 `local` (vipdoc 离线) 两种模式,完整覆盖沪深主板、创业板、科创板与北交所(注:`online` 模式受云端 API 限制仅覆盖在交易股票,获取已退市股票建议使用 `local` 离线模式或 Baostock 驱动)。
167
+ - **`DataCleaner`**: 统一清洗时间轴,转换产生 `timestamp` (Int64 ms) 与 `datetime` (ISO8601) 标准列。
168
+ - **`ProviderManager`**: Provider 单例工厂,根据 `table_id` 末段标识路由驱动。
169
+
170
+ ### 4.4 持久化存储层 (Storage)
171
+ - **`StorageManager` (ABC)**: 存储抽象基类,统一 `read_series`, `read_event`, `write_series`, `write_event` 接口。
172
+ - **`CSVStorage`**: TS 数据按 `[symbol, year]` 分片 CSV;EV 数据按 `[year]` 或平铺存储。
173
+ - **`ParquetStorage`**: TS/EV 数据按 `[year]` 保存为 zstd 压缩 Parquet 大表或平铺大表。
174
+ - **`DataMerger`**: 负责新旧 Polars DataFrame 的增量合并(`unique keep='last'`)与多维列排序。
175
+
176
+ ---
177
+
178
+ ## 5. Table ID 命名规范
179
+
180
+ 格式: `{market}.{category}.[sub_category/freq/adj].{source}`
181
+
182
+ | 字段 | 说明 | 示例 |
183
+ |:---|:---|:---|
184
+ | market | 市场标识 | `ashare` (A股个股), `aindex` (A股指数) |
185
+ | category | 数据类别 | `kline` (K线), `adj_factor` (复权因子), `dragon_tiger` (龙虎榜), `concept` (概念板块) |
186
+ | freq/adj | 频率/复权 (可选) | `1d` (日线), `5m` (5分钟), `adj` (后复权), `raw` (不复权) |
187
+ | source | 数据源 (末段,路由依据) | `baostock`, `eastmoney`, `tdx` |
188
+
189
+ **主要注册表**:
190
+ - `ashare.kline.1d.adj.baostock` / `ashare.kline.1d.raw.baostock` (TS)
191
+ - `ashare.kline.5m.adj.baostock` / `ashare.kline.5m.raw.baostock` (TS)
192
+ - `ashare.adj_factor.baostock` (EV)
193
+ - `ashare.concept.eastmoney` / `ashare.industry.eastmoney` / `ashare.dragon_tiger.eastmoney` (EV)
194
+ - `ashare.kline.1d.raw.tdx` / `ashare.kline.5m.raw.tdx` / `ashare.kline.1m.raw.tdx` (TS)
195
+
196
+ ---
197
+
198
+ ## 6. 存储布局与元数据协议
199
+
200
+ ### 6.1 Hive 分区存储结构
201
+
202
+ ### 6.1 Hive 分区存储结构
203
+
204
+ ```
205
+ data/
206
+ ├── csv/
207
+ │ └── {table_id}/
208
+ │ ├── year={yyyy}/{symbol}.csv # TS (TimeSeries) 模式
209
+ │ ├── year={yyyy}/data.csv # EV (Event 有 timestamp) 模式
210
+ │ ├── data.csv # EV (Event 无 timestamp) 平铺模式
211
+ │ └── metadata.json
212
+ └── parquet/
213
+ └── {table_id}/
214
+ ├── year={yyyy}/data.parquet # TS & EV (有 timestamp) 模式
215
+ ├── data.parquet # EV (无 timestamp) 平铺模式
216
+ └── metadata.json
217
+ ```
218
+
219
+ ### 6.2 路径模板表
220
+
221
+ | 数据类型 | CSV 路径 | Parquet 路径 |
222
+ |:---|:---|:---|
223
+ | TimeSeries (TS) | `csv/{table_id}/year={yyyy}/{symbol}.csv` | `parquet/{table_id}/year={yyyy}/data.parquet` |
224
+ | Event (EV) 有 timestamp | `csv/{table_id}/year={yyyy}/data.csv` | `parquet/{table_id}/year={yyyy}/data.parquet` |
225
+ | Event (EV) 无 timestamp | `csv/{table_id}/data.csv` | `parquet/{table_id}/data.parquet` |
226
+ | 元数据 | `{format}/{table_id}/metadata.json` | `{format}/{table_id}/metadata.json` |
227
+
228
+ ### 6.3 元数据规范 (metadata.json)
229
+
230
+ 每个表及格式维护独立的 `metadata.json`,包含了顶层显式版本号 `"version": 1`、`schema` 与 `statistics`(包括上次同步的系统挂钟时间 `updated_at`、起止时间戳、ISO时间与 `total_bars`)。
231
+ > **更新时间说明**:`statistics.updated_at` 为每次数据刷盘成功时的系统 ISO8601 时间戳(如 `"2026-08-10T16:25:00.000+08:00"`),方便前端与 API 精准识别物理数据的最新刷新时刻。
232
+ > **EV 表性能优化**:EV 表的 `metadata.json` 严禁包含 `symbol_count` 和 `time_steps` 字段,巡检时跳过大文件全量扫描。对于无 timestamp 的平铺模式(如板块成分股),`statistics` 包含 `updated_at` 和 `total_bars`。
233
+ > **复权因子说明**:仅保留后复权因子 `back_adj_factor`,剔除可变的历史前复权因子,防止历史数据变更污染增量水位线。
234
+
235
+ ### 6.4 TS 与 EV 存储行为差异
236
+
237
+ | 维度 | TS (TimeSeries) | EV (Event) 有 timestamp | EV (Event) 无 timestamp |
238
+ |:---|:---|:---|:---|
239
+ | 分区模式 | CSV: `[symbol, year]` / PQ: `[year]` | 按 `[year]` | 平铺文件 |
240
+ | 去重策略 | `subset=["symbol", "timestamp"]` | 全行去重 `subset=None` | 全行去重 `subset=None` |
241
+ | 默认排序 | CSV: `["timestamp"]` / PQ: `["symbol", "timestamp"]` | `["timestamp", "symbol"]` | 由 Provider `get_sort_keys()` 指定 |
242
+
243
+ ---
244
+
245
+ ## 7. 数据协议与双时间轴
246
+
247
+ ### 7.1 核心字段契约
248
+ 入库数据必须包含以下时间与主键字段:
249
+ - `timestamp`: Int64 (UTC 毫秒级时间戳) — 去重、分区、排序的主键
250
+ - `datetime`: String (ISO8601 带偏移,如 `"2024-01-01T15:00:00.000+08:00"`) — 强时区带偏移的可读列
251
+ - `symbol`: String (证券代码,如 `"sh.600000"`) — 复合主键之一
252
+
253
+ ### 7.2 双时区机制
254
+ - **`source_tz`**: 用于解析原始挂钟时间对齐到 UTC 0(默认 `"Asia/Shanghai"`)。
255
+ - **`display_tz`**: 用于生成带偏移量的 ISO8601 `datetime` 显示列(默认 `"Asia/Shanghai"`)。
256
+ - **A股日线对齐规则**: 日线数据默认统一对齐至 `15:00:00`(A股收盘时间),防止跨日边界物理分区错位。
257
+ - **时间戳下限归一化 (Epoch 0)**:
258
+ - `ts_to_str(0)` 格式化返回 `"1970-01-01"`,防止受系统 C 库负时间戳影响抛出 `OSError` 或返回空字符串。
259
+ - 对于早于 1970 年的日期字符串(如 `"1900-01-01"`)或空字符串,`parse_date_to_ts` 自动将其截断修剪为 Unix 起始零点 `ts = 0`(`1970-01-01`),以满足“全量拉取有记录完整历史数据”的标准语义。
260
+ - 必须使用标准 `zoneinfo` 库处理时区,严禁手动计算偏移。
261
+
262
+ ### 7.3 类型映射规约 (type_map)
263
+ 读取数据时,必须依据 `metadata.json` 定义的 schema 显式执行 `pl.cast()`:
264
+ ```python
265
+ type_map = {
266
+ "Int64": pl.Int64, "Float64": pl.Float64, "String": pl.String,
267
+ "Boolean": pl.Boolean, "Date": pl.Date, "Datetime": pl.Datetime
268
+ }
269
+ ```
270
+
271
+ ---
272
+
273
+ ## 8. 核心设计原则
274
+
275
+ ### 8.1 架构哲学与设计指导
276
+ 1. **第一性原理 (First Principles)**:
277
+ 从问题本质出发设计,绝不增加无必要的中间层或冗余形参。代码逻辑追求极简、直观与高执行效率,拒绝任何花里胡哨的过度设计与魔术推测。
278
+ 2. **单一事实来源 (Single Source of Truth, SSOT)**:
279
+ - 物理磁盘的 `metadata.json` 是数据属性、Schema 与起止范围的唯一事实标准。
280
+ - 探查与读取数据时严格以磁盘 `metadata.json` 为准,绝不在代码中凭空假设或做模棱两可的降级猜测。
281
+ 3. **高内聚低耦合的分层架构 (Layered Architecture)**:
282
+ - **Entrypoints (接入层)**: 仅负责参数校验与路由,0 业务逻辑。
283
+ - **Service (服务层)**: 负责同步规划、数据切片调度与元数据原子刷盘。
284
+ - **Provider (驱动层)**: 专一负责网络/二进制数据拉取与 `DataCleaner` 标准化。
285
+ - **Storage (存储层)**: 专一负责增量合并 (`DataMerger`)、去重、排序与 Hive 物理落盘。
286
+ - **单向依赖规则**: 接入层 ➔ 服务层 ➔ 驱动层/存储层,严格禁止跨层反向调用。
287
+ 4. **前端文案与 UI 专业性规范 (Professional UI & Tone)**:
288
+ - **拒绝 AI 营销修饰感**:严禁在 UI 标题、描述与注释中使用形如“极速引擎”、“强同步研判”、“底层透视”、“全量数据共享”、“0ms 内存极速切片”等夸张、AI 化、营销感重的冗长词汇。
289
+ - **回归金融终端本质**:UI 文案必须精炼、概要、间接、专业,保持类似 Bloomberg / TradingView 官方终端的干练风格(如:“K 线行情”、“数据矩阵”、“按时间序列展现 OHLC 与成交量明细”)。
290
+
291
+ ### 8.2 物理与鲁棒性原则
292
+ 1. **原子落盘**: 任何写操作均采用 `.tmp` 文件写入 -> `os.replace` -> `fsync` 刷盘,杜绝写入中断导致文件损坏。
293
+ 2. **读取强锁与显式 Cast**: 读取数据时必须读取 `metadata.json` 的 schema 进行显式 `pl.cast()`,严禁使用 Polars 自动类型推断。
294
+ 3. **空数据防御三层机制**:
295
+ - *驱动层*: 无数据时也必须通过 `DataCleaner` 返回带完整 schema 的空 DataFrame。
296
+ - *存储层*: 传入空 DataFrame 时静默拦截,不创建空文件或残留目录。
297
+ - *元数据层*: `total_bars=0` 且无元数据时不创建文件;无新数据且已有元数据时不重复更新。
298
+ 4. **Fail-Fast 异常分发与元数据延迟盖章**:
299
+ - 网络中断、网络授权失效或非法参数:必须抛出异常中断流水线,防止水位线被误推进。
300
+ - 元数据盖章 `_update_metadata()` 严格在全量批次成功落盘下沉后统一调用,确保中途中断时水位线不虚高,保障下一次重新同步能无损补全全量历史数据。
301
+ - 业务合法空数据(如停牌、无龙虎榜记录):允许返回带 Schema 的空表。
302
+ 5. **增量水位线**: 多格式同步时水位线取交集保守计算 (`start` 取 max, `end` 取 min),保证各存储格式完整覆盖。
303
+ 6. **无损降级处理**: EV 数据在缺少 `symbol` 列时自动回退为仅按 `timestamp` 排序,严禁抛出 `ColumnNotFoundError`。
304
+
305
+ ---
306
+
307
+ ## 9. 开发与测试约束
308
+
309
+ - **零盲目假设与沟通契约**:
310
+ - **严禁盲目假设**:当对需求意图、架构变动或接口设计存在不确切或多种可选方案时,必须先提出疑问与方案选择,与用户讨论确认后再执行代码修改,严禁擅自做主更改逻辑。
311
+ - **严禁擅自编写兜底/降级逻辑**:当遇到不确定的边界条件、第三方 API 异常或逻辑未尽事项时,**严禁自动编写隐式兜底/降级代码**(如静默 try-except 容错、假默认值 fallback、模棱两可的硬编码兜底),必须第一时间向用户提出疑问与方案选择,经用户明确确认后再执行代码实现。
312
+ - **最小改动原则**:代码修改严格遵循最小可行改动原则,严禁删除不相关的代码与注释。
313
+ - **技术栈禁令**: 强制全系统使用 Polars (`pl`),**严禁使用 pandas**(包括 SDK 接口、类型提示与数据转换,全量基于 Polars)。
314
+ - **包管理与安装禁令**: **严禁使用 `pip install`** 方式安装依赖包。项目包管理一律统一使用 `uv` 工具链(如 `uv sync` / `uv add`)。
315
+ - **复权约束**: 仅支持 `raw` (不复权) 或 `adj` (后复权),禁止前复权。
316
+ - **Import 规范**: 代码文件中所有的 `import` 语句(包含标准库、第三方库与本地模块)**默认统一集中放在文件最开始顶部位置**,严禁在函数、类或文件中间随意分散放置 `import`。
317
+ - **干净入口**: `cqdata/__init__.py` 仅用于符号导出,实现 **0 业务逻辑**。
318
+ - **时区规范**: 统一使用 `zoneinfo`,禁止手动加减小时偏移。
319
+ - **日志与输出**:
320
+ - 系统日志使用 Loguru,输出挂载至 `stderr` 及 `logs/{prefix}_{YYYYMMDD_HHMMSS}.log`。
321
+ - 核心驱动层调第三方库(如 Baostock)必须使用 `SuppressOutput` 包裹,防止垃圾 `print` 污染 CLI 控台。
322
+ - **测试覆盖与质量约束**:
323
+ - **全层级测试同步**:更新代码、重构逻辑或增加新功能时,必须同步补充与修改对应的**单元测试 (Unit Test)**、**集成测试 (Integration Test)** 以及 **端到端测试 (E2E Test)** 代码。
324
+ - **边界与异常防御**:必须深入分析并合理考虑**边界条件**(如空 DataFrame、极值起止时间戳、重复主键去重、缺失字段、物理盘未落盘状态)与**异常情况**(如网络中断重连、格式解析失败、第三方 API 崩溃、未定义市场代码),增加针对性的防错校验与断言测试。
325
+ - **后端与前端自动化运行**:后端测试统一使用 `uv run pytest tests/ -v` 命令;前端与 E2E 测试使用 Bun 工具链(`bun run test:unit` / `bun run test:e2e`)。
326
+ - **Git 提交**:
327
+ - 消息语言为中文,遵从 Conventional Commits 规范(如 `feat:` / `fix:` / `refactor:`)。
328
+ - **默认生成完整多行 Commit 消息**:包含首行简短 Summary(如 `feat(web): ...`)以及详细的多点 List 说明(`- ...`),供用户审核确认。
329
+ - 未经用户明确确认,禁止自动执行 `git add/commit/push` 操作。
330
+ - **版本发布与 bump 规范**:
331
+ - 项目在 `pyproject.toml` 中配置了 `[tool.bumpversion]` 自动化关联,将 `pyproject.toml` 和 `cqdata/__init__.py` 的版本号保持强同步。
332
+ - 严禁手动多处修改版本号。更新版本统一通过 `bump-my-version` 工具链自动升级并打 Tag:
333
+ - 升级修补版本号 (`1.1.0` ➔ `1.1.1`): `uv run bump-my-version bump patch`
334
+ - 升级次版本号 (`1.1.0` ➔ `1.2.0`): `uv run bump-my-version bump minor`
335
+ - 升级主版本号 (`1.1.0` ➔ `2.0.0`): `uv run bump-my-version bump major`
336
+ - **文档与示例同步契约**: 任何涉及架构、核心 API、接入面 (Entrypoint) 或数据结构的改动,必须**同时同步更新**以下 4 处内容:
337
+ 1. `AGENTS.md`
338
+ 2. `README.md`
339
+ 3. `docs/` 目录下的相关指南文档 (如 `docs/rest_api_guide.md` 与 `docs/python_sdk_guide.md` 等)。
340
+ 4. `examples/` 目录下的示例代码脚本。
341
+
342
+ ---
343
+
344
+ ## 10. 测试结构与运行
345
+
346
+ ### 10.1 测试目录结构
347
+ ```
348
+ CarrotQuant.Data/
349
+ ├── tests/ # 后端 Python 测试集
350
+ │ ├── conftest.py # 全局 fixtures (temp_storage_root, mock_baostock)
351
+ │ ├── unit/ # 工具、Provider、Storage、Service 及 Entrypoints 单元测试 (mock IO)
352
+ │ └── integration/ # 真实 API 与全流程同步测试 (包含全量/增量/防封/空数据测试)
353
+ └── web/ # 前端 React / Web 终端测试集
354
+ ├── src/__tests__/ # 前端 Component、Hook、Service 单元测试 (Vitest)
355
+ └── e2e/ # 端到端 API 与 UI 自动化测试 (Playwright)
356
+ ```
357
+
358
+ ### 10.2 常用测试命令
359
+ ```bash
360
+ # 后端 pytest 测试
361
+ uv run pytest tests/ -v # 运行全部后端测试 (单元 + 集成)
362
+ uv run pytest tests/unit/ -v # 仅运行单元测试
363
+ uv run pytest tests/integration/ -v # 仅运行集成测试
364
+
365
+ # 前端与 E2E 测试
366
+ cd web && bun run test:unit # 运行前端单元测试 (Vitest)
367
+ cd web && bun run test:e2e # 运行前端与 UI 端到端测试 (Playwright)
368
+ ```
369
+
370
+ ---
371
+
372
+ ## 11. 新增数据源指南
373
+
374
+ 新增数据源驱动时遵循以下步骤:
375
+ 1. 在 `cqdata/provider/` 下新建 `{source}_provider.py`,继承 `BaseProvider`。
376
+ 2. 实现 `fetch`, `get_all_symbols`, `get_supported_tables`, `get_table_category`, `get_sort_keys` 方法。
377
+ 3. 在类属性 `_SUPPORTED_TABLE_MAP` 中注册可用的 `table_id`。
378
+ 4. 在 `ProviderManager.get_provider()` 中加入该驱动的路由逻辑。
379
+ 5. 驱动调第三方 API 时使用 `SuppressOutput` 包裹控制台输出。
380
+ 6. `fetch()` 返回数据需经过 `DataCleaner.standardize()`,无数据时返回带完整 Schema 的空表。
381
+ 7. 在 `tests/unit/` 和 `tests/integration/` 中补充对应的单元与集成测试。