axdata 0.1.0__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.
Files changed (427) hide show
  1. axdata/__init__.py +16 -0
  2. axdata/cache.py +19 -0
  3. axdata/client.py +1223 -0
  4. axdata-0.1.0.dist-info/METADATA +238 -0
  5. axdata-0.1.0.dist-info/RECORD +427 -0
  6. axdata-0.1.0.dist-info/WHEEL +5 -0
  7. axdata-0.1.0.dist-info/entry_points.txt +8 -0
  8. axdata-0.1.0.dist-info/licenses/LICENSE +162 -0
  9. axdata-0.1.0.dist-info/top_level.txt +6 -0
  10. axdata_core/__init__.py +221 -0
  11. axdata_core/_tdx_wire/__init__.py +35 -0
  12. axdata_core/_tdx_wire/_shim.py +140 -0
  13. axdata_core/_tdx_wire/api/__init__.py +51 -0
  14. axdata_core/_tdx_wire/api/auction.py +24 -0
  15. axdata_core/_tdx_wire/api/bars.py +24 -0
  16. axdata_core/_tdx_wire/api/base.py +26 -0
  17. axdata_core/_tdx_wire/api/codes.py +25 -0
  18. axdata_core/_tdx_wire/api/corporate.py +24 -0
  19. axdata_core/_tdx_wire/api/finance.py +24 -0
  20. axdata_core/_tdx_wire/api/intraday.py +24 -0
  21. axdata_core/_tdx_wire/api/quotes.py +26 -0
  22. axdata_core/_tdx_wire/api/resources.py +25 -0
  23. axdata_core/_tdx_wire/api/session.py +24 -0
  24. axdata_core/_tdx_wire/api/trades.py +24 -0
  25. axdata_core/_tdx_wire/client.py +33 -0
  26. axdata_core/_tdx_wire/exceptions.py +27 -0
  27. axdata_core/_tdx_wire/hosts.py +106 -0
  28. axdata_core/_tdx_wire/models/__init__.py +93 -0
  29. axdata_core/_tdx_wire/models/auction.py +26 -0
  30. axdata_core/_tdx_wire/models/corporate.py +27 -0
  31. axdata_core/_tdx_wire/models/finance.py +26 -0
  32. axdata_core/_tdx_wire/models/intraday.py +31 -0
  33. axdata_core/_tdx_wire/models/kline.py +27 -0
  34. axdata_core/_tdx_wire/models/quote.py +32 -0
  35. axdata_core/_tdx_wire/models/resource.py +24 -0
  36. axdata_core/_tdx_wire/models/security.py +24 -0
  37. axdata_core/_tdx_wire/models/session.py +27 -0
  38. axdata_core/_tdx_wire/models/subchart.py +25 -0
  39. axdata_core/_tdx_wire/models/trade.py +28 -0
  40. axdata_core/_tdx_wire/protocol/__init__.py +49 -0
  41. axdata_core/_tdx_wire/protocol/commands/__init__.py +43 -0
  42. axdata_core/_tdx_wire/protocol/commands/auction.py +110 -0
  43. axdata_core/_tdx_wire/protocol/commands/codec.py +43 -0
  44. axdata_core/_tdx_wire/protocol/commands/corporate.py +98 -0
  45. axdata_core/_tdx_wire/protocol/commands/finance.py +96 -0
  46. axdata_core/_tdx_wire/protocol/commands/intraday.py +122 -0
  47. axdata_core/_tdx_wire/protocol/commands/klines.py +102 -0
  48. axdata_core/_tdx_wire/protocol/commands/lookup.py +36 -0
  49. axdata_core/_tdx_wire/protocol/commands/price_limits.py +89 -0
  50. axdata_core/_tdx_wire/protocol/commands/quotes.py +102 -0
  51. axdata_core/_tdx_wire/protocol/commands/registry.py +36 -0
  52. axdata_core/_tdx_wire/protocol/commands/resources.py +99 -0
  53. axdata_core/_tdx_wire/protocol/commands/security.py +103 -0
  54. axdata_core/_tdx_wire/protocol/commands/session.py +82 -0
  55. axdata_core/_tdx_wire/protocol/commands/subchart.py +97 -0
  56. axdata_core/_tdx_wire/protocol/commands/trades.py +107 -0
  57. axdata_core/_tdx_wire/protocol/constants.py +124 -0
  58. axdata_core/_tdx_wire/protocol/frame.py +68 -0
  59. axdata_core/_tdx_wire/protocol/unit.py +85 -0
  60. axdata_core/_tdx_wire/py.typed +1 -0
  61. axdata_core/_tdx_wire/transport/__init__.py +34 -0
  62. axdata_core/_tdx_wire/transport/base.py +25 -0
  63. axdata_core/_tdx_wire/transport/memory.py +24 -0
  64. axdata_core/_tdx_wire/transport/pool.py +32 -0
  65. axdata_core/_tdx_wire/transport/socket.py +39 -0
  66. axdata_core/adapters/__init__.py +0 -0
  67. axdata_core/adapters/cls/__init__.py +17 -0
  68. axdata_core/adapters/cls/provider_bridge.py +23 -0
  69. axdata_core/adapters/cls/request.py +632 -0
  70. axdata_core/adapters/cls/source_adapter_registry.py +31 -0
  71. axdata_core/adapters/cninfo/__init__.py +17 -0
  72. axdata_core/adapters/cninfo/provider_bridge.py +25 -0
  73. axdata_core/adapters/cninfo/request.py +2425 -0
  74. axdata_core/adapters/cninfo/source_adapter_registry.py +42 -0
  75. axdata_core/adapters/eastmoney/__init__.py +17 -0
  76. axdata_core/adapters/eastmoney/provider_bridge.py +25 -0
  77. axdata_core/adapters/eastmoney/request.py +1128 -0
  78. axdata_core/adapters/eastmoney/source_adapter_registry.py +42 -0
  79. axdata_core/adapters/exchange/__init__.py +17 -0
  80. axdata_core/adapters/exchange/provider_bridge.py +25 -0
  81. axdata_core/adapters/exchange/request.py +1214 -0
  82. axdata_core/adapters/exchange/source_adapter_registry.py +42 -0
  83. axdata_core/adapters/kph/__init__.py +17 -0
  84. axdata_core/adapters/kph/provider_bridge.py +23 -0
  85. axdata_core/adapters/kph/request.py +509 -0
  86. axdata_core/adapters/kph/source_adapter_registry.py +31 -0
  87. axdata_core/adapters/sina/__init__.py +17 -0
  88. axdata_core/adapters/sina/provider_bridge.py +25 -0
  89. axdata_core/adapters/sina/request.py +5155 -0
  90. axdata_core/adapters/sina/source_adapter_registry.py +42 -0
  91. axdata_core/adapters/tdx/__init__.py +32 -0
  92. axdata_core/adapters/tdx/adjustment.py +50 -0
  93. axdata_core/adapters/tdx/adjustment_fetch.py +51 -0
  94. axdata_core/adapters/tdx/auction_fetch.py +51 -0
  95. axdata_core/adapters/tdx/client_factory.py +110 -0
  96. axdata_core/adapters/tdx/code_fetch.py +62 -0
  97. axdata_core/adapters/tdx/codes.py +54 -0
  98. axdata_core/adapters/tdx/derived_rows.py +69 -0
  99. axdata_core/adapters/tdx/downloader.py +193 -0
  100. axdata_core/adapters/tdx/downloader_interface_sets.py +50 -0
  101. axdata_core/adapters/tdx/downloader_profiles.py +43 -0
  102. axdata_core/adapters/tdx/downloader_registry.py +115 -0
  103. axdata_core/adapters/tdx/execution_utils.py +55 -0
  104. axdata_core/adapters/tdx/f10_executor.py +57 -0
  105. axdata_core/adapters/tdx/f10_normalize.py +61 -0
  106. axdata_core/adapters/tdx/f10_params.py +57 -0
  107. axdata_core/adapters/tdx/f10_postprocess.py +67 -0
  108. axdata_core/adapters/tdx/f10_render.py +48 -0
  109. axdata_core/adapters/tdx/f10_request.py +62 -0
  110. axdata_core/adapters/tdx/finance_fetch.py +61 -0
  111. axdata_core/adapters/tdx/finance_maps.py +54 -0
  112. axdata_core/adapters/tdx/finance_normalize.py +57 -0
  113. axdata_core/adapters/tdx/host_config.py +115 -0
  114. axdata_core/adapters/tdx/interface_sets.py +50 -0
  115. axdata_core/adapters/tdx/intraday_fetch.py +68 -0
  116. axdata_core/adapters/tdx/kline_helpers.py +58 -0
  117. axdata_core/adapters/tdx/limit_ladder_fetch.py +60 -0
  118. axdata_core/adapters/tdx/limit_ladder_topics.py +53 -0
  119. axdata_core/adapters/tdx/normalize_utils.py +75 -0
  120. axdata_core/adapters/tdx/options.py +61 -0
  121. axdata_core/adapters/tdx/price_limit_calendar.py +49 -0
  122. axdata_core/adapters/tdx/price_limit_fetch.py +53 -0
  123. axdata_core/adapters/tdx/price_limit_history.py +50 -0
  124. axdata_core/adapters/tdx/price_limits.py +58 -0
  125. axdata_core/adapters/tdx/provider_bridge.py +68 -0
  126. axdata_core/adapters/tdx/quote_fetch.py +61 -0
  127. axdata_core/adapters/tdx/quote_identity.py +49 -0
  128. axdata_core/adapters/tdx/rank_fetch.py +55 -0
  129. axdata_core/adapters/tdx/rank_params.py +52 -0
  130. axdata_core/adapters/tdx/realtime_refresh.py +45 -0
  131. axdata_core/adapters/tdx/request.py +11 -0
  132. axdata_core/adapters/tdx/request_adapter_runtime.py +41 -0
  133. axdata_core/adapters/tdx/request_client.py +51 -0
  134. axdata_core/adapters/tdx/request_compat.py +89 -0
  135. axdata_core/adapters/tdx/request_dispatch.py +50 -0
  136. axdata_core/adapters/tdx/request_filters.py +60 -0
  137. axdata_core/adapters/tdx/request_host_config.py +110 -0
  138. axdata_core/adapters/tdx/request_limits.py +59 -0
  139. axdata_core/adapters/tdx/request_methods.py +41 -0
  140. axdata_core/adapters/tdx/request_params.py +62 -0
  141. axdata_core/adapters/tdx/request_seams.py +41 -0
  142. axdata_core/adapters/tdx/resources/__init__.py +0 -0
  143. axdata_core/adapters/tdx/resources/finance_maps/__init__.py +0 -0
  144. axdata_core/adapters/tdx/resources/finance_maps/incon.dat +3702 -0
  145. axdata_core/adapters/tdx/resources/finance_maps/tdxhy.cfg +5620 -0
  146. axdata_core/adapters/tdx/resources/finance_maps/tdxzs.cfg +605 -0
  147. axdata_core/adapters/tdx/security_codes.py +54 -0
  148. axdata_core/adapters/tdx/series_history.py +57 -0
  149. axdata_core/adapters/tdx/server_cache.py +58 -0
  150. axdata_core/adapters/tdx/snapshot_normalize.py +56 -0
  151. axdata_core/adapters/tdx/source_adapter_registry.py +69 -0
  152. axdata_core/adapters/tdx/source_execution.py +44 -0
  153. axdata_core/adapters/tdx/source_execution_registry.py +70 -0
  154. axdata_core/adapters/tdx/stats_cache.py +184 -0
  155. axdata_core/adapters/tdx/stats_models.py +159 -0
  156. axdata_core/adapters/tdx/stats_resource.py +304 -0
  157. axdata_core/adapters/tdx/status_fetch.py +55 -0
  158. axdata_core/adapters/tdx/time_series_normalize.py +67 -0
  159. axdata_core/adapters/tdx/tqlex.py +50 -0
  160. axdata_core/adapters/tdx/wire_requests.py +60 -0
  161. axdata_core/adapters/tdx_ext/__init__.py +66 -0
  162. axdata_core/adapters/tdx_ext/client.py +688 -0
  163. axdata_core/adapters/tdx_ext/exceptions.py +51 -0
  164. axdata_core/adapters/tdx_ext/host_config.py +40 -0
  165. axdata_core/adapters/tdx_ext/interface_sets.py +65 -0
  166. axdata_core/adapters/tdx_ext/local_cache.py +649 -0
  167. axdata_core/adapters/tdx_ext/models.py +96 -0
  168. axdata_core/adapters/tdx_ext/options.py +36 -0
  169. axdata_core/adapters/tdx_ext/pool.py +153 -0
  170. axdata_core/adapters/tdx_ext/provider_bridge.py +35 -0
  171. axdata_core/adapters/tdx_ext/request.py +498 -0
  172. axdata_core/adapters/tdx_ext/request_execution.py +118 -0
  173. axdata_core/adapters/tdx_ext/request_instruments.py +221 -0
  174. axdata_core/adapters/tdx_ext/request_normalize.py +356 -0
  175. axdata_core/adapters/tdx_ext/request_params.py +157 -0
  176. axdata_core/adapters/tdx_ext/request_series.py +98 -0
  177. axdata_core/adapters/tdx_ext/server_cache.py +13 -0
  178. axdata_core/adapters/tdx_ext/servers.py +35 -0
  179. axdata_core/adapters/tdx_ext/source_adapter_registry.py +42 -0
  180. axdata_core/adapters/tdx_ext/source_execution.py +19 -0
  181. axdata_core/adapters/tdx_ext/source_execution_registry.py +43 -0
  182. axdata_core/adapters/tencent/__init__.py +17 -0
  183. axdata_core/adapters/tencent/provider_bridge.py +25 -0
  184. axdata_core/adapters/tencent/request.py +729 -0
  185. axdata_core/adapters/tencent/source_adapter_registry.py +42 -0
  186. axdata_core/axp.py +1843 -0
  187. axdata_core/builtin_providers.py +586 -0
  188. axdata_core/builtin_source_declarations.py +156 -0
  189. axdata_core/cli.py +1818 -0
  190. axdata_core/collector_registry.py +227 -0
  191. axdata_core/collector_runner.py +1016 -0
  192. axdata_core/collector_scheduler.py +3155 -0
  193. axdata_core/collector_templates.py +339 -0
  194. axdata_core/data_browser.py +1696 -0
  195. axdata_core/diagnostics.py +734 -0
  196. axdata_core/downloader_engine.py +1480 -0
  197. axdata_core/downloader_registry.py +62 -0
  198. axdata_core/downloaders.py +1619 -0
  199. axdata_core/legacy_provider_adapter.py +68 -0
  200. axdata_core/paths.py +51 -0
  201. axdata_core/plugin_config.py +272 -0
  202. axdata_core/plugin_status.py +419 -0
  203. axdata_core/plugins.py +1363 -0
  204. axdata_core/provider_catalog.py +462 -0
  205. axdata_core/provider_registry.py +833 -0
  206. axdata_core/quality.py +86 -0
  207. axdata_core/query.py +242 -0
  208. axdata_core/resources/__init__.py +2 -0
  209. axdata_core/resources/tdx_extended_servers.json +22 -0
  210. axdata_core/resources/tdx_quote_servers.json +49 -0
  211. axdata_core/sample_collectors.py +42 -0
  212. axdata_core/schema.py +385 -0
  213. axdata_core/source_adapter_factory.py +95 -0
  214. axdata_core/source_adapter_options.py +18 -0
  215. axdata_core/source_errors.py +43 -0
  216. axdata_core/source_execution_options.py +37 -0
  217. axdata_core/source_execution_registry.py +27 -0
  218. axdata_core/source_projection.py +21 -0
  219. axdata_core/source_request.py +441 -0
  220. axdata_core/source_session.py +355 -0
  221. axdata_core/sources/__init__.py +40 -0
  222. axdata_core/sources/base.py +112 -0
  223. axdata_core/sources/catalog.py +60 -0
  224. axdata_core/sources/cls/__init__.py +5 -0
  225. axdata_core/sources/cls/catalog.py +231 -0
  226. axdata_core/sources/cninfo/__init__.py +5 -0
  227. axdata_core/sources/cninfo/catalog.py +1994 -0
  228. axdata_core/sources/display_docs.py +139 -0
  229. axdata_core/sources/eastmoney/__init__.py +5 -0
  230. axdata_core/sources/eastmoney/catalog.py +509 -0
  231. axdata_core/sources/exchange/__init__.py +5 -0
  232. axdata_core/sources/exchange/catalog.py +361 -0
  233. axdata_core/sources/kph/__init__.py +5 -0
  234. axdata_core/sources/kph/catalog.py +255 -0
  235. axdata_core/sources/sina/__init__.py +5 -0
  236. axdata_core/sources/sina/catalog.py +2951 -0
  237. axdata_core/sources/tdx/__init__.py +5 -0
  238. axdata_core/sources/tdx/catalog.py +4546 -0
  239. axdata_core/sources/tdx_ext/__init__.py +5 -0
  240. axdata_core/sources/tdx_ext/catalog.py +1518 -0
  241. axdata_core/sources/tencent/__init__.py +5 -0
  242. axdata_core/sources/tencent/catalog.py +370 -0
  243. axdata_core/storage.py +186 -0
  244. axdata_core/tdx_f10_catalog.py +52 -0
  245. axdata_core/tdx_f10_models.py +50 -0
  246. axdata_core/tdx_f10_names.py +47 -0
  247. axdata_core/tdx_f10_specs.py +56 -0
  248. axdata_core/tdx_plugin_required.py +43 -0
  249. axdata_core/tdx_server_config.py +65 -0
  250. axdata_core/trade_calendar_cache.py +397 -0
  251. axdata_source_cninfo/__init__.py +15 -0
  252. axdata_source_cninfo/adapter.py +31 -0
  253. axdata_source_cninfo/axdata-provider.json +384 -0
  254. axdata_source_cninfo/catalog.py +209 -0
  255. axdata_source_cninfo/metadata.py +8 -0
  256. axdata_source_cninfo/provider.py +41 -0
  257. axdata_source_tdx/__init__.py +15 -0
  258. axdata_source_tdx/_tdx_wire/__init__.py +26 -0
  259. axdata_source_tdx/_tdx_wire/_binary.py +218 -0
  260. axdata_source_tdx/_tdx_wire/_code_utils.py +42 -0
  261. axdata_source_tdx/_tdx_wire/_command_codec.py +186 -0
  262. axdata_source_tdx/_tdx_wire/_command_codes.py +74 -0
  263. axdata_source_tdx/_tdx_wire/_command_defaults.py +15 -0
  264. axdata_source_tdx/_tdx_wire/_command_dispatch.py +96 -0
  265. axdata_source_tdx/_tdx_wire/_command_layouts.py +96 -0
  266. axdata_source_tdx/_tdx_wire/_command_lookup.py +25 -0
  267. axdata_source_tdx/_tdx_wire/_command_metadata.py +32 -0
  268. axdata_source_tdx/_tdx_wire/_command_registry.py +61 -0
  269. axdata_source_tdx/_tdx_wire/_connection_defaults.py +13 -0
  270. axdata_source_tdx/_tdx_wire/_host_defaults.py +51 -0
  271. axdata_source_tdx/_tdx_wire/_host_probe.py +81 -0
  272. axdata_source_tdx/_tdx_wire/_host_resource.py +70 -0
  273. axdata_source_tdx/_tdx_wire/_host_utils.py +31 -0
  274. axdata_source_tdx/_tdx_wire/_market.py +66 -0
  275. axdata_source_tdx/_tdx_wire/_request_defaults.py +11 -0
  276. axdata_source_tdx/_tdx_wire/_security_classification.py +61 -0
  277. axdata_source_tdx/_tdx_wire/_time_utils.py +7 -0
  278. axdata_source_tdx/_tdx_wire/api/__init__.py +45 -0
  279. axdata_source_tdx/_tdx_wire/api/auction.py +25 -0
  280. axdata_source_tdx/_tdx_wire/api/bars.py +31 -0
  281. axdata_source_tdx/_tdx_wire/api/base.py +39 -0
  282. axdata_source_tdx/_tdx_wire/api/codes.py +25 -0
  283. axdata_source_tdx/_tdx_wire/api/corporate.py +11 -0
  284. axdata_source_tdx/_tdx_wire/api/finance.py +10 -0
  285. axdata_source_tdx/_tdx_wire/api/intraday.py +38 -0
  286. axdata_source_tdx/_tdx_wire/api/quotes.py +78 -0
  287. axdata_source_tdx/_tdx_wire/api/resources.py +34 -0
  288. axdata_source_tdx/_tdx_wire/api/session.py +16 -0
  289. axdata_source_tdx/_tdx_wire/api/trades.py +34 -0
  290. axdata_source_tdx/_tdx_wire/client.py +470 -0
  291. axdata_source_tdx/_tdx_wire/exceptions.py +25 -0
  292. axdata_source_tdx/_tdx_wire/hosts.py +27 -0
  293. axdata_source_tdx/_tdx_wire/models/__init__.py +87 -0
  294. axdata_source_tdx/_tdx_wire/models/auction.py +58 -0
  295. axdata_source_tdx/_tdx_wire/models/corporate.py +59 -0
  296. axdata_source_tdx/_tdx_wire/models/finance.py +80 -0
  297. axdata_source_tdx/_tdx_wire/models/intraday.py +112 -0
  298. axdata_source_tdx/_tdx_wire/models/kline.py +58 -0
  299. axdata_source_tdx/_tdx_wire/models/quote.py +262 -0
  300. axdata_source_tdx/_tdx_wire/models/resource.py +18 -0
  301. axdata_source_tdx/_tdx_wire/models/security.py +28 -0
  302. axdata_source_tdx/_tdx_wire/models/session.py +30 -0
  303. axdata_source_tdx/_tdx_wire/models/subchart.py +39 -0
  304. axdata_source_tdx/_tdx_wire/models/trade.py +46 -0
  305. axdata_source_tdx/_tdx_wire/protocol/__init__.py +43 -0
  306. axdata_source_tdx/_tdx_wire/protocol/_frame_constants.py +5 -0
  307. axdata_source_tdx/_tdx_wire/protocol/commands/__init__.py +37 -0
  308. axdata_source_tdx/_tdx_wire/protocol/commands/auction.py +165 -0
  309. axdata_source_tdx/_tdx_wire/protocol/commands/codec.py +37 -0
  310. axdata_source_tdx/_tdx_wire/protocol/commands/corporate.py +163 -0
  311. axdata_source_tdx/_tdx_wire/protocol/commands/finance.py +180 -0
  312. axdata_source_tdx/_tdx_wire/protocol/commands/intraday.py +382 -0
  313. axdata_source_tdx/_tdx_wire/protocol/commands/klines.py +396 -0
  314. axdata_source_tdx/_tdx_wire/protocol/commands/lookup.py +31 -0
  315. axdata_source_tdx/_tdx_wire/protocol/commands/price_limits.py +110 -0
  316. axdata_source_tdx/_tdx_wire/protocol/commands/quotes.py +804 -0
  317. axdata_source_tdx/_tdx_wire/protocol/commands/registry.py +30 -0
  318. axdata_source_tdx/_tdx_wire/protocol/commands/resources.py +107 -0
  319. axdata_source_tdx/_tdx_wire/protocol/commands/security.py +163 -0
  320. axdata_source_tdx/_tdx_wire/protocol/commands/session.py +129 -0
  321. axdata_source_tdx/_tdx_wire/protocol/commands/subchart.py +182 -0
  322. axdata_source_tdx/_tdx_wire/protocol/commands/trades.py +275 -0
  323. axdata_source_tdx/_tdx_wire/protocol/constants.py +70 -0
  324. axdata_source_tdx/_tdx_wire/protocol/frame.py +141 -0
  325. axdata_source_tdx/_tdx_wire/protocol/unit.py +98 -0
  326. axdata_source_tdx/_tdx_wire/py.typed +1 -0
  327. axdata_source_tdx/_tdx_wire/transport/__init__.py +28 -0
  328. axdata_source_tdx/_tdx_wire/transport/base.py +21 -0
  329. axdata_source_tdx/_tdx_wire/transport/memory.py +42 -0
  330. axdata_source_tdx/_tdx_wire/transport/pool.py +148 -0
  331. axdata_source_tdx/_tdx_wire/transport/socket.py +373 -0
  332. axdata_source_tdx/adapter.py +31 -0
  333. axdata_source_tdx/adjustment.py +163 -0
  334. axdata_source_tdx/adjustment_fetch.py +148 -0
  335. axdata_source_tdx/auction_fetch.py +162 -0
  336. axdata_source_tdx/axdata-provider.json +23680 -0
  337. axdata_source_tdx/catalog.py +39 -0
  338. axdata_source_tdx/client_factory.py +61 -0
  339. axdata_source_tdx/code_fetch.py +579 -0
  340. axdata_source_tdx/codes.py +69 -0
  341. axdata_source_tdx/collectors.py +525 -0
  342. axdata_source_tdx/derived_rows.py +500 -0
  343. axdata_source_tdx/downloader.py +139 -0
  344. axdata_source_tdx/downloader_interface_sets.py +29 -0
  345. axdata_source_tdx/downloader_profiles.py +731 -0
  346. axdata_source_tdx/downloader_registry.py +85 -0
  347. axdata_source_tdx/execution_utils.py +141 -0
  348. axdata_source_tdx/f10_executor.py +414 -0
  349. axdata_source_tdx/f10_normalize.py +169 -0
  350. axdata_source_tdx/f10_params.py +221 -0
  351. axdata_source_tdx/f10_postprocess.py +405 -0
  352. axdata_source_tdx/f10_render.py +29 -0
  353. axdata_source_tdx/f10_request.py +365 -0
  354. axdata_source_tdx/finance_fetch.py +514 -0
  355. axdata_source_tdx/finance_maps.py +377 -0
  356. axdata_source_tdx/finance_normalize.py +273 -0
  357. axdata_source_tdx/host_config.py +51 -0
  358. axdata_source_tdx/interface_sets.py +180 -0
  359. axdata_source_tdx/intraday_fetch.py +600 -0
  360. axdata_source_tdx/kline_helpers.py +333 -0
  361. axdata_source_tdx/limit_ladder_fetch.py +614 -0
  362. axdata_source_tdx/limit_ladder_topics.py +280 -0
  363. axdata_source_tdx/metadata.py +8 -0
  364. axdata_source_tdx/normalize_utils.py +264 -0
  365. axdata_source_tdx/options.py +195 -0
  366. axdata_source_tdx/price_limit_calendar.py +106 -0
  367. axdata_source_tdx/price_limit_fetch.py +264 -0
  368. axdata_source_tdx/price_limit_history.py +100 -0
  369. axdata_source_tdx/price_limits.py +142 -0
  370. axdata_source_tdx/provider.py +44 -0
  371. axdata_source_tdx/provider_bridge.py +43 -0
  372. axdata_source_tdx/quote_fetch.py +485 -0
  373. axdata_source_tdx/quote_identity.py +50 -0
  374. axdata_source_tdx/rank_fetch.py +316 -0
  375. axdata_source_tdx/rank_params.py +181 -0
  376. axdata_source_tdx/realtime_refresh.py +51 -0
  377. axdata_source_tdx/request_adapter.py +533 -0
  378. axdata_source_tdx/request_adapter_runtime.py +302 -0
  379. axdata_source_tdx/request_client.py +98 -0
  380. axdata_source_tdx/request_compat.py +432 -0
  381. axdata_source_tdx/request_dispatch.py +138 -0
  382. axdata_source_tdx/request_entrypoints.py +146 -0
  383. axdata_source_tdx/request_filters.py +154 -0
  384. axdata_source_tdx/request_host_config.py +103 -0
  385. axdata_source_tdx/request_limits.py +15 -0
  386. axdata_source_tdx/request_methods.py +1910 -0
  387. axdata_source_tdx/request_params.py +225 -0
  388. axdata_source_tdx/request_seams.py +534 -0
  389. axdata_source_tdx/resources/__init__.py +0 -0
  390. axdata_source_tdx/resources/finance_maps/__init__.py +1 -0
  391. axdata_source_tdx/resources/finance_maps/incon.dat +3702 -0
  392. axdata_source_tdx/resources/finance_maps/tdxhy.cfg +5620 -0
  393. axdata_source_tdx/resources/finance_maps/tdxzs.cfg +605 -0
  394. axdata_source_tdx/resources/tdx_extended_servers.json +22 -0
  395. axdata_source_tdx/resources/tdx_quote_servers.json +49 -0
  396. axdata_source_tdx/security_codes.py +127 -0
  397. axdata_source_tdx/series_history.py +348 -0
  398. axdata_source_tdx/server_cache.py +21 -0
  399. axdata_source_tdx/snapshot_normalize.py +232 -0
  400. axdata_source_tdx/source_adapter_registry.py +42 -0
  401. axdata_source_tdx/source_execution.py +22 -0
  402. axdata_source_tdx/source_execution_registry.py +43 -0
  403. axdata_source_tdx/stats_cache.py +150 -0
  404. axdata_source_tdx/stats_models.py +161 -0
  405. axdata_source_tdx/stats_resource.py +242 -0
  406. axdata_source_tdx/status_fetch.py +209 -0
  407. axdata_source_tdx/tdx_f10_catalog.py +957 -0
  408. axdata_source_tdx/tdx_f10_models.py +47 -0
  409. axdata_source_tdx/tdx_f10_names.py +48 -0
  410. axdata_source_tdx/tdx_f10_specs.py +1142 -0
  411. axdata_source_tdx/tdx_server_config.py +389 -0
  412. axdata_source_tdx/time_series_normalize.py +370 -0
  413. axdata_source_tdx/tqlex.py +138 -0
  414. axdata_source_tdx/wire.py +32 -0
  415. axdata_source_tdx/wire_requests.py +190 -0
  416. axdata_source_tdx_ext/__init__.py +15 -0
  417. axdata_source_tdx_ext/adapter.py +31 -0
  418. axdata_source_tdx_ext/axdata-provider.json +9424 -0
  419. axdata_source_tdx_ext/catalog.py +33 -0
  420. axdata_source_tdx_ext/metadata.py +8 -0
  421. axdata_source_tdx_ext/provider.py +44 -0
  422. axdata_source_tencent/__init__.py +15 -0
  423. axdata_source_tencent/adapter.py +31 -0
  424. axdata_source_tencent/axdata-provider.json +291 -0
  425. axdata_source_tencent/catalog.py +190 -0
  426. axdata_source_tencent/metadata.py +8 -0
  427. axdata_source_tencent/provider.py +41 -0
@@ -0,0 +1,47 @@
1
+ """Compatibility wrapper for TDX F10 interface name constants."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from importlib import import_module
6
+ from types import ModuleType
7
+ from typing import Any as _Any
8
+
9
+
10
+ _EXPORTS = (
11
+ 'F10_INTERFACE_NAMES',
12
+ )
13
+ __all__ = list(_EXPORTS)
14
+ _IMPLEMENTATION: ModuleType | None = None
15
+
16
+
17
+ def _provider_package_tdx_f10_names() -> ModuleType | None:
18
+ try:
19
+ return import_module("axdata_source_tdx.tdx_f10_names")
20
+ except ModuleNotFoundError as exc:
21
+ if exc.name in {'axdata_source_tdx', 'axdata_source_tdx.tdx_f10_names'}:
22
+ return None
23
+ raise
24
+
25
+
26
+ def _fallback_tdx_f10_names() -> ModuleType:
27
+ from axdata_core.tdx_plugin_required import raise_tdx_plugin_required
28
+
29
+ raise_tdx_plugin_required()
30
+
31
+
32
+ def _implementation() -> ModuleType:
33
+ global _IMPLEMENTATION
34
+ if _IMPLEMENTATION is None:
35
+ implementation = _provider_package_tdx_f10_names()
36
+ _IMPLEMENTATION = implementation if implementation is not None else _fallback_tdx_f10_names()
37
+ return _IMPLEMENTATION
38
+
39
+
40
+ def __getattr__(name: str) -> _Any:
41
+ try:
42
+ value = getattr(_implementation(), name)
43
+ except AttributeError as exc:
44
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}") from exc
45
+ if name in _EXPORTS:
46
+ globals()[name] = value
47
+ return value
@@ -0,0 +1,56 @@
1
+ """Compatibility wrapper for TDX 7615 F10 interface specifications."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from importlib import import_module
6
+ from types import ModuleType
7
+ from typing import Any as _Any
8
+
9
+
10
+ _EXPORTS = (
11
+ 'Any',
12
+ 'F10FieldSpec',
13
+ 'F10InterfaceSpec',
14
+ 'F10ParamSpec',
15
+ 'COMMON_STOCK_PARAMS',
16
+ 'COUNT_PARAM',
17
+ 'CURSOR_PARAM',
18
+ 'field',
19
+ 'param',
20
+ 'F10_INTERFACE_SPECS',
21
+ )
22
+ __all__ = list(_EXPORTS)
23
+ _IMPLEMENTATION: ModuleType | None = None
24
+
25
+
26
+ def _provider_package_tdx_f10_specs() -> ModuleType | None:
27
+ try:
28
+ return import_module("axdata_source_tdx.tdx_f10_specs")
29
+ except ModuleNotFoundError as exc:
30
+ if exc.name in {'axdata_source_tdx', 'axdata_source_tdx.tdx_f10_specs'}:
31
+ return None
32
+ raise
33
+
34
+
35
+ def _fallback_tdx_f10_specs() -> ModuleType:
36
+ from axdata_core.tdx_plugin_required import raise_tdx_plugin_required
37
+
38
+ raise_tdx_plugin_required()
39
+
40
+
41
+ def _implementation() -> ModuleType:
42
+ global _IMPLEMENTATION
43
+ if _IMPLEMENTATION is None:
44
+ implementation = _provider_package_tdx_f10_specs()
45
+ _IMPLEMENTATION = implementation if implementation is not None else _fallback_tdx_f10_specs()
46
+ return _IMPLEMENTATION
47
+
48
+
49
+ def __getattr__(name: str) -> _Any:
50
+ try:
51
+ value = getattr(_implementation(), name)
52
+ except AttributeError as exc:
53
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}") from exc
54
+ if name in _EXPORTS:
55
+ globals()[name] = value
56
+ return value
@@ -0,0 +1,43 @@
1
+ """Shared TDX plugin availability helpers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from importlib import import_module
6
+ from types import ModuleType
7
+ from typing import Any
8
+
9
+ from .source_errors import SourceUnavailableError
10
+
11
+ TDX_PLUGIN_REQUIRED_MESSAGE = "TDX 插件未安装或不可用,请安装并启用 TDX 插件。"
12
+
13
+
14
+ def raise_tdx_plugin_required() -> None:
15
+ raise SourceUnavailableError(TDX_PLUGIN_REQUIRED_MESSAGE)
16
+
17
+
18
+ def missing_tdx_provider_module(exc: ModuleNotFoundError, module_name: str) -> bool:
19
+ """Return whether a ModuleNotFoundError means the TDX plugin is missing."""
20
+
21
+ missing = str(exc.name or "")
22
+ root = module_name.split(".", 1)[0]
23
+ return missing == root or missing == module_name or missing.startswith(f"{root}.")
24
+
25
+
26
+ def load_tdx_provider_module(module_name: str) -> ModuleType:
27
+ """Load one module from the TDX plugin or raise the shared availability error."""
28
+
29
+ try:
30
+ return import_module(module_name)
31
+ except ModuleNotFoundError as exc:
32
+ if missing_tdx_provider_module(exc, module_name):
33
+ raise SourceUnavailableError(TDX_PLUGIN_REQUIRED_MESSAGE) from exc
34
+ raise
35
+
36
+
37
+ def empty_downloader_profile_declarations(
38
+ concurrency_profile_cls: type[Any],
39
+ downloader_profile_cls: type[Any],
40
+ ) -> dict[str, Any]:
41
+ """Downloader declarations shape used when the TDX plugin is absent."""
42
+
43
+ return {}
@@ -0,0 +1,65 @@
1
+ """Compatibility wrapper for TDX server list configuration helpers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from importlib import import_module
6
+ from types import ModuleType
7
+ from typing import Any as _Any
8
+
9
+
10
+ _EXPORTS = (
11
+ "LOCAL_TIMEZONE",
12
+ "SERVER_KIND_VALUES",
13
+ "DEFAULT_CACHE_DIR",
14
+ "QUOTE_RESOURCE",
15
+ "EXT_RESOURCE",
16
+ "ServerKind",
17
+ "TdxServerEntry",
18
+ "TdxServerProbeResult",
19
+ "default_server_cache_root",
20
+ "data_root_server_cache_root",
21
+ "built_in_servers",
22
+ "effective_servers",
23
+ "effective_host_strings",
24
+ "read_user_servers",
25
+ "write_user_servers",
26
+ "reset_user_servers",
27
+ "probe_server",
28
+ "probe_servers",
29
+ "server_status",
30
+ )
31
+ __all__ = list(_EXPORTS)
32
+ _IMPLEMENTATION: ModuleType | None = None
33
+
34
+
35
+ def _provider_package_tdx_server_config() -> ModuleType | None:
36
+ try:
37
+ return import_module("axdata_source_tdx.tdx_server_config")
38
+ except ModuleNotFoundError as exc:
39
+ if exc.name in {"axdata_source_tdx", "axdata_source_tdx.tdx_server_config"}:
40
+ return None
41
+ raise
42
+
43
+
44
+ def _fallback_tdx_server_config() -> ModuleType:
45
+ from axdata_core.tdx_plugin_required import raise_tdx_plugin_required
46
+
47
+ raise_tdx_plugin_required()
48
+
49
+
50
+ def _implementation() -> ModuleType:
51
+ global _IMPLEMENTATION
52
+ if _IMPLEMENTATION is None:
53
+ implementation = _provider_package_tdx_server_config()
54
+ _IMPLEMENTATION = implementation if implementation is not None else _fallback_tdx_server_config()
55
+ return _IMPLEMENTATION
56
+
57
+
58
+ def __getattr__(name: str) -> _Any:
59
+ try:
60
+ value = getattr(_implementation(), name)
61
+ except AttributeError as exc:
62
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}") from exc
63
+ if name in _EXPORTS:
64
+ globals()[name] = value
65
+ return value
@@ -0,0 +1,397 @@
1
+ """Local cache for official exchange trading calendars."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass
6
+ from datetime import date, datetime, timedelta, timezone
7
+ from pathlib import Path
8
+ from typing import Any, Iterable
9
+
10
+ import pandas as pd
11
+
12
+ from .source_request import request_interface
13
+
14
+ DEFAULT_PAST_DAYS = 180
15
+ DEFAULT_FUTURE_DAYS = 180
16
+ DEFAULT_RECHECK_PAST_DAYS = 30
17
+ DEFAULT_EXCHANGE = "SZSE"
18
+ CALENDAR_COLUMNS = ("exchange", "cal_date", "is_open", "pretrade_date", "next_trade_date")
19
+
20
+
21
+ class TradeCalendarCacheError(RuntimeError):
22
+ """Raised when the trading calendar cache cannot be used."""
23
+
24
+
25
+ @dataclass(frozen=True)
26
+ class CalendarCoverage:
27
+ start_date: str | None
28
+ end_date: str | None
29
+ row_count: int
30
+ open_count: int
31
+
32
+ @property
33
+ def is_available(self) -> bool:
34
+ return self.row_count > 0 and self.start_date is not None and self.end_date is not None
35
+
36
+
37
+ def trade_calendar_cache_path(data_root: str | Path) -> Path:
38
+ """Return the default local calendar cache file path."""
39
+
40
+ return Path(data_root).expanduser().resolve() / "cache" / "exchange" / "trade_calendar" / "trade_calendar.parquet"
41
+
42
+
43
+ def get_trade_calendar_cache_status(data_root: str | Path, *, today: date | None = None) -> dict[str, Any]:
44
+ """Return local cache status without requesting upstream."""
45
+
46
+ path = trade_calendar_cache_path(data_root)
47
+ frame = _read_cache(path)
48
+ coverage = _coverage(frame)
49
+ today_text = _format_date(today or _local_today())
50
+ today_row = _row_for_date(frame, today_text)
51
+ return {
52
+ "path": str(path),
53
+ "exists": path.exists(),
54
+ "row_count": coverage.row_count,
55
+ "open_count": coverage.open_count,
56
+ "start_date": coverage.start_date,
57
+ "end_date": coverage.end_date,
58
+ "today": today_text,
59
+ "today_is_open": _bool_or_none(today_row.get("is_open") if today_row else None),
60
+ "today_pretrade_date": today_row.get("pretrade_date") if today_row else None,
61
+ "today_next_trade_date": today_row.get("next_trade_date") if today_row else None,
62
+ "covers_today": today_row is not None,
63
+ "updated_at": _file_updated_at(path),
64
+ }
65
+
66
+
67
+ def refresh_trade_calendar_cache(
68
+ data_root: str | Path,
69
+ *,
70
+ start_date: str | date | None = None,
71
+ end_date: str | date | None = None,
72
+ past_days: int = DEFAULT_PAST_DAYS,
73
+ future_days: int = DEFAULT_FUTURE_DAYS,
74
+ recheck_past_days: int = DEFAULT_RECHECK_PAST_DAYS,
75
+ today: date | None = None,
76
+ ) -> dict[str, Any]:
77
+ """Refresh local cache for the requested range and return status metadata."""
78
+
79
+ today_value = today or _local_today()
80
+ requested_start, requested_end = _resolve_refresh_range(
81
+ start_date=start_date,
82
+ end_date=end_date,
83
+ past_days=past_days,
84
+ future_days=future_days,
85
+ today=today_value,
86
+ )
87
+ if requested_start > requested_end:
88
+ raise TradeCalendarCacheError("start_date must be before or equal to end_date")
89
+
90
+ cache_path = trade_calendar_cache_path(data_root)
91
+ existing = _read_cache(cache_path)
92
+ coverage = _coverage(existing)
93
+ fetch_ranges = _missing_ranges(
94
+ requested_start,
95
+ requested_end,
96
+ coverage,
97
+ recheck_start=max(requested_start, today_value - timedelta(days=max(0, recheck_past_days))),
98
+ )
99
+
100
+ fetched_frames: list[pd.DataFrame] = []
101
+ fetched_ranges: list[dict[str, str]] = []
102
+ for fetch_start, fetch_end in fetch_ranges:
103
+ records = request_interface(
104
+ "stock_trade_calendar_exchange",
105
+ params={"start_date": _format_date(fetch_start), "end_date": _format_date(fetch_end)},
106
+ fields=list(CALENDAR_COLUMNS[1:]),
107
+ persist=False,
108
+ ).records
109
+ fetched_ranges.append({"start_date": _format_date(fetch_start), "end_date": _format_date(fetch_end)})
110
+ if records:
111
+ fetched_frames.append(_records_to_frame(records, exchange=DEFAULT_EXCHANGE))
112
+
113
+ merged = _merge_frames([existing, *fetched_frames])
114
+ _write_cache(cache_path, merged)
115
+ status = get_trade_calendar_cache_status(data_root, today=today_value)
116
+ status.update(
117
+ {
118
+ "requested_start_date": _format_date(requested_start),
119
+ "requested_end_date": _format_date(requested_end),
120
+ "fetched_ranges": fetched_ranges,
121
+ "fetched_row_count": int(sum(len(frame) for frame in fetched_frames)),
122
+ }
123
+ )
124
+ return status
125
+
126
+
127
+ def ensure_trade_calendar_cache(
128
+ data_root: str | Path,
129
+ *,
130
+ past_days: int = DEFAULT_PAST_DAYS,
131
+ future_days: int = DEFAULT_FUTURE_DAYS,
132
+ monthly_recheck_past_days: int = DEFAULT_RECHECK_PAST_DAYS,
133
+ today: date | None = None,
134
+ ) -> dict[str, Any]:
135
+ """Ensure the default rolling range exists, with monthly recent rechecks."""
136
+
137
+ today_value = today or _local_today()
138
+ recheck_past_days = monthly_recheck_past_days if today_value.day == 1 else 0
139
+ status = refresh_trade_calendar_cache(
140
+ data_root,
141
+ past_days=past_days,
142
+ future_days=future_days,
143
+ recheck_past_days=recheck_past_days,
144
+ today=today_value,
145
+ )
146
+ status["maintenance_mode"] = "monthly_recheck" if today_value.day == 1 else "startup_gap_check"
147
+ return status
148
+
149
+
150
+ def check_trade_calendar_cache(
151
+ data_root: str | Path,
152
+ *,
153
+ start_date: str | date,
154
+ end_date: str | date | None = None,
155
+ ) -> dict[str, Any]:
156
+ """Check whether local cache covers a requested date range."""
157
+
158
+ start = _parse_date(start_date, "start_date")
159
+ end = _parse_date(end_date if end_date is not None else start_date, "end_date")
160
+ if start > end:
161
+ raise TradeCalendarCacheError("start_date must be before or equal to end_date")
162
+
163
+ path = trade_calendar_cache_path(data_root)
164
+ frame = _read_cache(path)
165
+ coverage = _coverage(frame)
166
+ start_text = _format_date(start)
167
+ end_text = _format_date(end)
168
+ missing_dates = _missing_dates(frame, start, end)
169
+ return {
170
+ "path": str(path),
171
+ "exists": path.exists(),
172
+ "start_date": start_text,
173
+ "end_date": end_text,
174
+ "is_available": not missing_dates,
175
+ "missing_count": len(missing_dates),
176
+ "missing_dates": missing_dates[:20],
177
+ "cache_start_date": coverage.start_date,
178
+ "cache_end_date": coverage.end_date,
179
+ "row_count": coverage.row_count,
180
+ }
181
+
182
+
183
+ def update_trade_calendar_cache_from_records(
184
+ data_root: str | Path,
185
+ records: Iterable[dict[str, Any]],
186
+ *,
187
+ exchange: str = DEFAULT_EXCHANGE,
188
+ today: date | None = None,
189
+ ) -> dict[str, Any]:
190
+ """Merge already-fetched trading calendar rows into the local cache."""
191
+
192
+ cache_path = trade_calendar_cache_path(data_root)
193
+ existing = _read_cache(cache_path)
194
+ frame = _records_to_frame([dict(record) for record in records], exchange=exchange)
195
+ merged = _merge_frames([existing, frame])
196
+ _write_cache(cache_path, merged)
197
+ status = get_trade_calendar_cache_status(data_root, today=today)
198
+ status.update({"merged_row_count": int(len(frame))})
199
+ return status
200
+
201
+
202
+ def _resolve_refresh_range(
203
+ *,
204
+ start_date: str | date | None,
205
+ end_date: str | date | None,
206
+ past_days: int,
207
+ future_days: int,
208
+ today: date,
209
+ ) -> tuple[date, date]:
210
+ if start_date is not None or end_date is not None:
211
+ start = _parse_date(start_date if start_date is not None else end_date, "start_date")
212
+ end = _parse_date(end_date if end_date is not None else start_date, "end_date")
213
+ return start, end
214
+ return today - timedelta(days=max(0, past_days)), today + timedelta(days=max(0, future_days))
215
+
216
+
217
+ def _missing_ranges(
218
+ start: date,
219
+ end: date,
220
+ coverage: CalendarCoverage,
221
+ *,
222
+ recheck_start: date,
223
+ ) -> list[tuple[date, date]]:
224
+ if not coverage.is_available:
225
+ return [(start, end)]
226
+
227
+ ranges: list[tuple[date, date]] = []
228
+ cache_start = _parse_date(coverage.start_date, "cache_start_date")
229
+ cache_end = _parse_date(coverage.end_date, "cache_end_date")
230
+ if start < cache_start:
231
+ ranges.append((start, min(end, cache_start - timedelta(days=1))))
232
+ if end > cache_end:
233
+ ranges.append((max(start, cache_end + timedelta(days=1)), end))
234
+ refresh_start = max(start, recheck_start)
235
+ refresh_end = min(end, cache_end)
236
+ if refresh_start <= refresh_end:
237
+ ranges.append((refresh_start, refresh_end))
238
+ return _merge_ranges(ranges)
239
+
240
+
241
+ def _merge_ranges(ranges: Iterable[tuple[date, date]]) -> list[tuple[date, date]]:
242
+ normalized = sorted((start, end) for start, end in ranges if start <= end)
243
+ merged: list[tuple[date, date]] = []
244
+ for start, end in normalized:
245
+ if not merged or start > merged[-1][1] + timedelta(days=1):
246
+ merged.append((start, end))
247
+ else:
248
+ merged[-1] = (merged[-1][0], max(merged[-1][1], end))
249
+ return merged
250
+
251
+
252
+ def _read_cache(path: Path) -> pd.DataFrame:
253
+ if not path.exists():
254
+ return pd.DataFrame(columns=CALENDAR_COLUMNS)
255
+ frame = pd.read_parquet(path)
256
+ return _normalize_frame(frame)
257
+
258
+
259
+ def _write_cache(path: Path, frame: pd.DataFrame) -> None:
260
+ path.parent.mkdir(parents=True, exist_ok=True)
261
+ _normalize_frame(frame).to_parquet(path, engine="pyarrow", index=False)
262
+
263
+
264
+ def _records_to_frame(records: list[dict[str, Any]], *, exchange: str) -> pd.DataFrame:
265
+ rows = []
266
+ for record in records:
267
+ rows.append(
268
+ {
269
+ "exchange": exchange,
270
+ "cal_date": _normalize_date_text(record.get("cal_date")),
271
+ "is_open": bool(record.get("is_open")),
272
+ "pretrade_date": _normalize_date_text(record.get("pretrade_date")),
273
+ "next_trade_date": _normalize_date_text(record.get("next_trade_date")),
274
+ }
275
+ )
276
+ return _normalize_frame(pd.DataFrame.from_records(rows))
277
+
278
+
279
+ def _merge_frames(frames: list[pd.DataFrame]) -> pd.DataFrame:
280
+ non_empty = [_normalize_frame(frame) for frame in frames if frame is not None and not frame.empty]
281
+ if not non_empty:
282
+ return pd.DataFrame(columns=CALENDAR_COLUMNS)
283
+ merged = pd.concat(non_empty, ignore_index=True)
284
+ merged = _normalize_frame(merged)
285
+ return merged.drop_duplicates(subset=["exchange", "cal_date"], keep="last").sort_values(["exchange", "cal_date"])
286
+
287
+
288
+ def _normalize_frame(frame: pd.DataFrame) -> pd.DataFrame:
289
+ if frame.empty:
290
+ return pd.DataFrame(columns=CALENDAR_COLUMNS)
291
+ normalized = frame.copy()
292
+ for column in CALENDAR_COLUMNS:
293
+ if column not in normalized.columns:
294
+ normalized[column] = None
295
+ normalized = normalized.loc[:, list(CALENDAR_COLUMNS)]
296
+ normalized["exchange"] = normalized["exchange"].fillna(DEFAULT_EXCHANGE).astype(str)
297
+ normalized["cal_date"] = normalized["cal_date"].map(_normalize_date_text)
298
+ normalized["pretrade_date"] = normalized["pretrade_date"].map(_normalize_date_text)
299
+ normalized["next_trade_date"] = normalized["next_trade_date"].map(_normalize_date_text)
300
+ normalized["is_open"] = normalized["is_open"].map(_normalize_bool)
301
+ normalized = normalized[normalized["cal_date"].notna()]
302
+ return normalized
303
+
304
+
305
+ def _coverage(frame: pd.DataFrame) -> CalendarCoverage:
306
+ if frame.empty:
307
+ return CalendarCoverage(start_date=None, end_date=None, row_count=0, open_count=0)
308
+ dates = sorted(str(item) for item in frame["cal_date"].dropna().unique())
309
+ return CalendarCoverage(
310
+ start_date=dates[0] if dates else None,
311
+ end_date=dates[-1] if dates else None,
312
+ row_count=len(frame),
313
+ open_count=int(frame["is_open"].sum()) if "is_open" in frame.columns else 0,
314
+ )
315
+
316
+
317
+ def _row_for_date(frame: pd.DataFrame, cal_date: str) -> dict[str, Any] | None:
318
+ if frame.empty:
319
+ return None
320
+ matches = frame[frame["cal_date"] == cal_date]
321
+ if matches.empty:
322
+ return None
323
+ return matches.iloc[-1].to_dict()
324
+
325
+
326
+ def _missing_dates(frame: pd.DataFrame, start: date, end: date) -> list[str]:
327
+ available = set(str(item) for item in frame["cal_date"].dropna()) if not frame.empty else set()
328
+ missing: list[str] = []
329
+ current = start
330
+ while current <= end:
331
+ text = _format_date(current)
332
+ if text not in available:
333
+ missing.append(text)
334
+ current += timedelta(days=1)
335
+ return missing
336
+
337
+
338
+ def _covers(coverage: CalendarCoverage, start_date: str, end_date: str) -> bool:
339
+ return bool(
340
+ coverage.start_date
341
+ and coverage.end_date
342
+ and coverage.start_date <= start_date
343
+ and coverage.end_date >= end_date
344
+ )
345
+
346
+
347
+ def _parse_date(value: str | date | None, field_name: str) -> date:
348
+ if isinstance(value, date):
349
+ return value
350
+ text = str(value or "").strip()
351
+ digits = "".join(ch for ch in text if ch.isdigit())
352
+ if len(digits) != 8:
353
+ raise TradeCalendarCacheError(f"{field_name} must be YYYYMMDD or YYYY-MM-DD")
354
+ try:
355
+ return date(int(digits[:4]), int(digits[4:6]), int(digits[6:8]))
356
+ except ValueError as exc:
357
+ raise TradeCalendarCacheError(f"{field_name} is not a valid date") from exc
358
+
359
+
360
+ def _format_date(value: date) -> str:
361
+ return value.strftime("%Y%m%d")
362
+
363
+
364
+ def _normalize_date_text(value: Any) -> str | None:
365
+ if value in (None, ""):
366
+ return None
367
+ digits = "".join(ch for ch in str(value) if ch.isdigit())
368
+ return digits[:8] if len(digits) >= 8 else None
369
+
370
+
371
+ def _local_today() -> date:
372
+ return datetime.now().astimezone().date()
373
+
374
+
375
+ def _file_updated_at(path: Path) -> str | None:
376
+ if not path.exists():
377
+ return None
378
+ return datetime.fromtimestamp(path.stat().st_mtime, tz=timezone.utc).isoformat()
379
+
380
+
381
+ def _bool_or_none(value: Any) -> bool | None:
382
+ if value is None:
383
+ return None
384
+ return bool(value)
385
+
386
+
387
+ def _normalize_bool(value: Any) -> bool:
388
+ if isinstance(value, bool):
389
+ return value
390
+ if value is None:
391
+ return False
392
+ text = str(value).strip().lower()
393
+ if text in {"1", "true", "yes", "y", "open"}:
394
+ return True
395
+ if text in {"0", "false", "no", "n", "closed", ""}:
396
+ return False
397
+ return bool(value)
@@ -0,0 +1,15 @@
1
+ """Cninfo Provider package for AxData."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Any
6
+
7
+ __all__ = ["CninfoProvider", "provider"]
8
+
9
+
10
+ def __getattr__(name: str) -> Any:
11
+ if name in __all__:
12
+ from .provider import CninfoProvider, provider
13
+
14
+ return {"CninfoProvider": CninfoProvider, "provider": provider}[name]
15
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
@@ -0,0 +1,31 @@
1
+ """Cninfo Provider adapter."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Mapping
6
+ from typing import TYPE_CHECKING
7
+
8
+ from axdata_core.legacy_provider_adapter import LegacyProviderAdapter
9
+
10
+ from .metadata import PROVIDER_ID, SOURCE_CODE
11
+
12
+ if TYPE_CHECKING:
13
+ from axdata_core.adapters.cninfo import CninfoRequestAdapter
14
+
15
+
16
+ class CninfoProviderAdapter(LegacyProviderAdapter):
17
+ """Adapter that exposes Cninfo announcement interfaces through the Provider API."""
18
+
19
+ def __init__(self, options: Mapping[str, object] | None = None) -> None:
20
+ super().__init__(
21
+ source=SOURCE_CODE,
22
+ provider_id=PROVIDER_ID,
23
+ create_adapter=lambda call_options: _create_legacy_adapter(call_options),
24
+ options=options,
25
+ )
26
+
27
+
28
+ def _create_legacy_adapter(options: Mapping[str, object]) -> CninfoRequestAdapter:
29
+ from axdata_core.adapters.cninfo.provider_bridge import create_cninfo_request_adapter
30
+
31
+ return create_cninfo_request_adapter(options)