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.
- axdata/__init__.py +16 -0
- axdata/cache.py +19 -0
- axdata/client.py +1223 -0
- axdata-0.1.0.dist-info/METADATA +238 -0
- axdata-0.1.0.dist-info/RECORD +427 -0
- axdata-0.1.0.dist-info/WHEEL +5 -0
- axdata-0.1.0.dist-info/entry_points.txt +8 -0
- axdata-0.1.0.dist-info/licenses/LICENSE +162 -0
- axdata-0.1.0.dist-info/top_level.txt +6 -0
- axdata_core/__init__.py +221 -0
- axdata_core/_tdx_wire/__init__.py +35 -0
- axdata_core/_tdx_wire/_shim.py +140 -0
- axdata_core/_tdx_wire/api/__init__.py +51 -0
- axdata_core/_tdx_wire/api/auction.py +24 -0
- axdata_core/_tdx_wire/api/bars.py +24 -0
- axdata_core/_tdx_wire/api/base.py +26 -0
- axdata_core/_tdx_wire/api/codes.py +25 -0
- axdata_core/_tdx_wire/api/corporate.py +24 -0
- axdata_core/_tdx_wire/api/finance.py +24 -0
- axdata_core/_tdx_wire/api/intraday.py +24 -0
- axdata_core/_tdx_wire/api/quotes.py +26 -0
- axdata_core/_tdx_wire/api/resources.py +25 -0
- axdata_core/_tdx_wire/api/session.py +24 -0
- axdata_core/_tdx_wire/api/trades.py +24 -0
- axdata_core/_tdx_wire/client.py +33 -0
- axdata_core/_tdx_wire/exceptions.py +27 -0
- axdata_core/_tdx_wire/hosts.py +106 -0
- axdata_core/_tdx_wire/models/__init__.py +93 -0
- axdata_core/_tdx_wire/models/auction.py +26 -0
- axdata_core/_tdx_wire/models/corporate.py +27 -0
- axdata_core/_tdx_wire/models/finance.py +26 -0
- axdata_core/_tdx_wire/models/intraday.py +31 -0
- axdata_core/_tdx_wire/models/kline.py +27 -0
- axdata_core/_tdx_wire/models/quote.py +32 -0
- axdata_core/_tdx_wire/models/resource.py +24 -0
- axdata_core/_tdx_wire/models/security.py +24 -0
- axdata_core/_tdx_wire/models/session.py +27 -0
- axdata_core/_tdx_wire/models/subchart.py +25 -0
- axdata_core/_tdx_wire/models/trade.py +28 -0
- axdata_core/_tdx_wire/protocol/__init__.py +49 -0
- axdata_core/_tdx_wire/protocol/commands/__init__.py +43 -0
- axdata_core/_tdx_wire/protocol/commands/auction.py +110 -0
- axdata_core/_tdx_wire/protocol/commands/codec.py +43 -0
- axdata_core/_tdx_wire/protocol/commands/corporate.py +98 -0
- axdata_core/_tdx_wire/protocol/commands/finance.py +96 -0
- axdata_core/_tdx_wire/protocol/commands/intraday.py +122 -0
- axdata_core/_tdx_wire/protocol/commands/klines.py +102 -0
- axdata_core/_tdx_wire/protocol/commands/lookup.py +36 -0
- axdata_core/_tdx_wire/protocol/commands/price_limits.py +89 -0
- axdata_core/_tdx_wire/protocol/commands/quotes.py +102 -0
- axdata_core/_tdx_wire/protocol/commands/registry.py +36 -0
- axdata_core/_tdx_wire/protocol/commands/resources.py +99 -0
- axdata_core/_tdx_wire/protocol/commands/security.py +103 -0
- axdata_core/_tdx_wire/protocol/commands/session.py +82 -0
- axdata_core/_tdx_wire/protocol/commands/subchart.py +97 -0
- axdata_core/_tdx_wire/protocol/commands/trades.py +107 -0
- axdata_core/_tdx_wire/protocol/constants.py +124 -0
- axdata_core/_tdx_wire/protocol/frame.py +68 -0
- axdata_core/_tdx_wire/protocol/unit.py +85 -0
- axdata_core/_tdx_wire/py.typed +1 -0
- axdata_core/_tdx_wire/transport/__init__.py +34 -0
- axdata_core/_tdx_wire/transport/base.py +25 -0
- axdata_core/_tdx_wire/transport/memory.py +24 -0
- axdata_core/_tdx_wire/transport/pool.py +32 -0
- axdata_core/_tdx_wire/transport/socket.py +39 -0
- axdata_core/adapters/__init__.py +0 -0
- axdata_core/adapters/cls/__init__.py +17 -0
- axdata_core/adapters/cls/provider_bridge.py +23 -0
- axdata_core/adapters/cls/request.py +632 -0
- axdata_core/adapters/cls/source_adapter_registry.py +31 -0
- axdata_core/adapters/cninfo/__init__.py +17 -0
- axdata_core/adapters/cninfo/provider_bridge.py +25 -0
- axdata_core/adapters/cninfo/request.py +2425 -0
- axdata_core/adapters/cninfo/source_adapter_registry.py +42 -0
- axdata_core/adapters/eastmoney/__init__.py +17 -0
- axdata_core/adapters/eastmoney/provider_bridge.py +25 -0
- axdata_core/adapters/eastmoney/request.py +1128 -0
- axdata_core/adapters/eastmoney/source_adapter_registry.py +42 -0
- axdata_core/adapters/exchange/__init__.py +17 -0
- axdata_core/adapters/exchange/provider_bridge.py +25 -0
- axdata_core/adapters/exchange/request.py +1214 -0
- axdata_core/adapters/exchange/source_adapter_registry.py +42 -0
- axdata_core/adapters/kph/__init__.py +17 -0
- axdata_core/adapters/kph/provider_bridge.py +23 -0
- axdata_core/adapters/kph/request.py +509 -0
- axdata_core/adapters/kph/source_adapter_registry.py +31 -0
- axdata_core/adapters/sina/__init__.py +17 -0
- axdata_core/adapters/sina/provider_bridge.py +25 -0
- axdata_core/adapters/sina/request.py +5155 -0
- axdata_core/adapters/sina/source_adapter_registry.py +42 -0
- axdata_core/adapters/tdx/__init__.py +32 -0
- axdata_core/adapters/tdx/adjustment.py +50 -0
- axdata_core/adapters/tdx/adjustment_fetch.py +51 -0
- axdata_core/adapters/tdx/auction_fetch.py +51 -0
- axdata_core/adapters/tdx/client_factory.py +110 -0
- axdata_core/adapters/tdx/code_fetch.py +62 -0
- axdata_core/adapters/tdx/codes.py +54 -0
- axdata_core/adapters/tdx/derived_rows.py +69 -0
- axdata_core/adapters/tdx/downloader.py +193 -0
- axdata_core/adapters/tdx/downloader_interface_sets.py +50 -0
- axdata_core/adapters/tdx/downloader_profiles.py +43 -0
- axdata_core/adapters/tdx/downloader_registry.py +115 -0
- axdata_core/adapters/tdx/execution_utils.py +55 -0
- axdata_core/adapters/tdx/f10_executor.py +57 -0
- axdata_core/adapters/tdx/f10_normalize.py +61 -0
- axdata_core/adapters/tdx/f10_params.py +57 -0
- axdata_core/adapters/tdx/f10_postprocess.py +67 -0
- axdata_core/adapters/tdx/f10_render.py +48 -0
- axdata_core/adapters/tdx/f10_request.py +62 -0
- axdata_core/adapters/tdx/finance_fetch.py +61 -0
- axdata_core/adapters/tdx/finance_maps.py +54 -0
- axdata_core/adapters/tdx/finance_normalize.py +57 -0
- axdata_core/adapters/tdx/host_config.py +115 -0
- axdata_core/adapters/tdx/interface_sets.py +50 -0
- axdata_core/adapters/tdx/intraday_fetch.py +68 -0
- axdata_core/adapters/tdx/kline_helpers.py +58 -0
- axdata_core/adapters/tdx/limit_ladder_fetch.py +60 -0
- axdata_core/adapters/tdx/limit_ladder_topics.py +53 -0
- axdata_core/adapters/tdx/normalize_utils.py +75 -0
- axdata_core/adapters/tdx/options.py +61 -0
- axdata_core/adapters/tdx/price_limit_calendar.py +49 -0
- axdata_core/adapters/tdx/price_limit_fetch.py +53 -0
- axdata_core/adapters/tdx/price_limit_history.py +50 -0
- axdata_core/adapters/tdx/price_limits.py +58 -0
- axdata_core/adapters/tdx/provider_bridge.py +68 -0
- axdata_core/adapters/tdx/quote_fetch.py +61 -0
- axdata_core/adapters/tdx/quote_identity.py +49 -0
- axdata_core/adapters/tdx/rank_fetch.py +55 -0
- axdata_core/adapters/tdx/rank_params.py +52 -0
- axdata_core/adapters/tdx/realtime_refresh.py +45 -0
- axdata_core/adapters/tdx/request.py +11 -0
- axdata_core/adapters/tdx/request_adapter_runtime.py +41 -0
- axdata_core/adapters/tdx/request_client.py +51 -0
- axdata_core/adapters/tdx/request_compat.py +89 -0
- axdata_core/adapters/tdx/request_dispatch.py +50 -0
- axdata_core/adapters/tdx/request_filters.py +60 -0
- axdata_core/adapters/tdx/request_host_config.py +110 -0
- axdata_core/adapters/tdx/request_limits.py +59 -0
- axdata_core/adapters/tdx/request_methods.py +41 -0
- axdata_core/adapters/tdx/request_params.py +62 -0
- axdata_core/adapters/tdx/request_seams.py +41 -0
- axdata_core/adapters/tdx/resources/__init__.py +0 -0
- axdata_core/adapters/tdx/resources/finance_maps/__init__.py +0 -0
- axdata_core/adapters/tdx/resources/finance_maps/incon.dat +3702 -0
- axdata_core/adapters/tdx/resources/finance_maps/tdxhy.cfg +5620 -0
- axdata_core/adapters/tdx/resources/finance_maps/tdxzs.cfg +605 -0
- axdata_core/adapters/tdx/security_codes.py +54 -0
- axdata_core/adapters/tdx/series_history.py +57 -0
- axdata_core/adapters/tdx/server_cache.py +58 -0
- axdata_core/adapters/tdx/snapshot_normalize.py +56 -0
- axdata_core/adapters/tdx/source_adapter_registry.py +69 -0
- axdata_core/adapters/tdx/source_execution.py +44 -0
- axdata_core/adapters/tdx/source_execution_registry.py +70 -0
- axdata_core/adapters/tdx/stats_cache.py +184 -0
- axdata_core/adapters/tdx/stats_models.py +159 -0
- axdata_core/adapters/tdx/stats_resource.py +304 -0
- axdata_core/adapters/tdx/status_fetch.py +55 -0
- axdata_core/adapters/tdx/time_series_normalize.py +67 -0
- axdata_core/adapters/tdx/tqlex.py +50 -0
- axdata_core/adapters/tdx/wire_requests.py +60 -0
- axdata_core/adapters/tdx_ext/__init__.py +66 -0
- axdata_core/adapters/tdx_ext/client.py +688 -0
- axdata_core/adapters/tdx_ext/exceptions.py +51 -0
- axdata_core/adapters/tdx_ext/host_config.py +40 -0
- axdata_core/adapters/tdx_ext/interface_sets.py +65 -0
- axdata_core/adapters/tdx_ext/local_cache.py +649 -0
- axdata_core/adapters/tdx_ext/models.py +96 -0
- axdata_core/adapters/tdx_ext/options.py +36 -0
- axdata_core/adapters/tdx_ext/pool.py +153 -0
- axdata_core/adapters/tdx_ext/provider_bridge.py +35 -0
- axdata_core/adapters/tdx_ext/request.py +498 -0
- axdata_core/adapters/tdx_ext/request_execution.py +118 -0
- axdata_core/adapters/tdx_ext/request_instruments.py +221 -0
- axdata_core/adapters/tdx_ext/request_normalize.py +356 -0
- axdata_core/adapters/tdx_ext/request_params.py +157 -0
- axdata_core/adapters/tdx_ext/request_series.py +98 -0
- axdata_core/adapters/tdx_ext/server_cache.py +13 -0
- axdata_core/adapters/tdx_ext/servers.py +35 -0
- axdata_core/adapters/tdx_ext/source_adapter_registry.py +42 -0
- axdata_core/adapters/tdx_ext/source_execution.py +19 -0
- axdata_core/adapters/tdx_ext/source_execution_registry.py +43 -0
- axdata_core/adapters/tencent/__init__.py +17 -0
- axdata_core/adapters/tencent/provider_bridge.py +25 -0
- axdata_core/adapters/tencent/request.py +729 -0
- axdata_core/adapters/tencent/source_adapter_registry.py +42 -0
- axdata_core/axp.py +1843 -0
- axdata_core/builtin_providers.py +586 -0
- axdata_core/builtin_source_declarations.py +156 -0
- axdata_core/cli.py +1818 -0
- axdata_core/collector_registry.py +227 -0
- axdata_core/collector_runner.py +1016 -0
- axdata_core/collector_scheduler.py +3155 -0
- axdata_core/collector_templates.py +339 -0
- axdata_core/data_browser.py +1696 -0
- axdata_core/diagnostics.py +734 -0
- axdata_core/downloader_engine.py +1480 -0
- axdata_core/downloader_registry.py +62 -0
- axdata_core/downloaders.py +1619 -0
- axdata_core/legacy_provider_adapter.py +68 -0
- axdata_core/paths.py +51 -0
- axdata_core/plugin_config.py +272 -0
- axdata_core/plugin_status.py +419 -0
- axdata_core/plugins.py +1363 -0
- axdata_core/provider_catalog.py +462 -0
- axdata_core/provider_registry.py +833 -0
- axdata_core/quality.py +86 -0
- axdata_core/query.py +242 -0
- axdata_core/resources/__init__.py +2 -0
- axdata_core/resources/tdx_extended_servers.json +22 -0
- axdata_core/resources/tdx_quote_servers.json +49 -0
- axdata_core/sample_collectors.py +42 -0
- axdata_core/schema.py +385 -0
- axdata_core/source_adapter_factory.py +95 -0
- axdata_core/source_adapter_options.py +18 -0
- axdata_core/source_errors.py +43 -0
- axdata_core/source_execution_options.py +37 -0
- axdata_core/source_execution_registry.py +27 -0
- axdata_core/source_projection.py +21 -0
- axdata_core/source_request.py +441 -0
- axdata_core/source_session.py +355 -0
- axdata_core/sources/__init__.py +40 -0
- axdata_core/sources/base.py +112 -0
- axdata_core/sources/catalog.py +60 -0
- axdata_core/sources/cls/__init__.py +5 -0
- axdata_core/sources/cls/catalog.py +231 -0
- axdata_core/sources/cninfo/__init__.py +5 -0
- axdata_core/sources/cninfo/catalog.py +1994 -0
- axdata_core/sources/display_docs.py +139 -0
- axdata_core/sources/eastmoney/__init__.py +5 -0
- axdata_core/sources/eastmoney/catalog.py +509 -0
- axdata_core/sources/exchange/__init__.py +5 -0
- axdata_core/sources/exchange/catalog.py +361 -0
- axdata_core/sources/kph/__init__.py +5 -0
- axdata_core/sources/kph/catalog.py +255 -0
- axdata_core/sources/sina/__init__.py +5 -0
- axdata_core/sources/sina/catalog.py +2951 -0
- axdata_core/sources/tdx/__init__.py +5 -0
- axdata_core/sources/tdx/catalog.py +4546 -0
- axdata_core/sources/tdx_ext/__init__.py +5 -0
- axdata_core/sources/tdx_ext/catalog.py +1518 -0
- axdata_core/sources/tencent/__init__.py +5 -0
- axdata_core/sources/tencent/catalog.py +370 -0
- axdata_core/storage.py +186 -0
- axdata_core/tdx_f10_catalog.py +52 -0
- axdata_core/tdx_f10_models.py +50 -0
- axdata_core/tdx_f10_names.py +47 -0
- axdata_core/tdx_f10_specs.py +56 -0
- axdata_core/tdx_plugin_required.py +43 -0
- axdata_core/tdx_server_config.py +65 -0
- axdata_core/trade_calendar_cache.py +397 -0
- axdata_source_cninfo/__init__.py +15 -0
- axdata_source_cninfo/adapter.py +31 -0
- axdata_source_cninfo/axdata-provider.json +384 -0
- axdata_source_cninfo/catalog.py +209 -0
- axdata_source_cninfo/metadata.py +8 -0
- axdata_source_cninfo/provider.py +41 -0
- axdata_source_tdx/__init__.py +15 -0
- axdata_source_tdx/_tdx_wire/__init__.py +26 -0
- axdata_source_tdx/_tdx_wire/_binary.py +218 -0
- axdata_source_tdx/_tdx_wire/_code_utils.py +42 -0
- axdata_source_tdx/_tdx_wire/_command_codec.py +186 -0
- axdata_source_tdx/_tdx_wire/_command_codes.py +74 -0
- axdata_source_tdx/_tdx_wire/_command_defaults.py +15 -0
- axdata_source_tdx/_tdx_wire/_command_dispatch.py +96 -0
- axdata_source_tdx/_tdx_wire/_command_layouts.py +96 -0
- axdata_source_tdx/_tdx_wire/_command_lookup.py +25 -0
- axdata_source_tdx/_tdx_wire/_command_metadata.py +32 -0
- axdata_source_tdx/_tdx_wire/_command_registry.py +61 -0
- axdata_source_tdx/_tdx_wire/_connection_defaults.py +13 -0
- axdata_source_tdx/_tdx_wire/_host_defaults.py +51 -0
- axdata_source_tdx/_tdx_wire/_host_probe.py +81 -0
- axdata_source_tdx/_tdx_wire/_host_resource.py +70 -0
- axdata_source_tdx/_tdx_wire/_host_utils.py +31 -0
- axdata_source_tdx/_tdx_wire/_market.py +66 -0
- axdata_source_tdx/_tdx_wire/_request_defaults.py +11 -0
- axdata_source_tdx/_tdx_wire/_security_classification.py +61 -0
- axdata_source_tdx/_tdx_wire/_time_utils.py +7 -0
- axdata_source_tdx/_tdx_wire/api/__init__.py +45 -0
- axdata_source_tdx/_tdx_wire/api/auction.py +25 -0
- axdata_source_tdx/_tdx_wire/api/bars.py +31 -0
- axdata_source_tdx/_tdx_wire/api/base.py +39 -0
- axdata_source_tdx/_tdx_wire/api/codes.py +25 -0
- axdata_source_tdx/_tdx_wire/api/corporate.py +11 -0
- axdata_source_tdx/_tdx_wire/api/finance.py +10 -0
- axdata_source_tdx/_tdx_wire/api/intraday.py +38 -0
- axdata_source_tdx/_tdx_wire/api/quotes.py +78 -0
- axdata_source_tdx/_tdx_wire/api/resources.py +34 -0
- axdata_source_tdx/_tdx_wire/api/session.py +16 -0
- axdata_source_tdx/_tdx_wire/api/trades.py +34 -0
- axdata_source_tdx/_tdx_wire/client.py +470 -0
- axdata_source_tdx/_tdx_wire/exceptions.py +25 -0
- axdata_source_tdx/_tdx_wire/hosts.py +27 -0
- axdata_source_tdx/_tdx_wire/models/__init__.py +87 -0
- axdata_source_tdx/_tdx_wire/models/auction.py +58 -0
- axdata_source_tdx/_tdx_wire/models/corporate.py +59 -0
- axdata_source_tdx/_tdx_wire/models/finance.py +80 -0
- axdata_source_tdx/_tdx_wire/models/intraday.py +112 -0
- axdata_source_tdx/_tdx_wire/models/kline.py +58 -0
- axdata_source_tdx/_tdx_wire/models/quote.py +262 -0
- axdata_source_tdx/_tdx_wire/models/resource.py +18 -0
- axdata_source_tdx/_tdx_wire/models/security.py +28 -0
- axdata_source_tdx/_tdx_wire/models/session.py +30 -0
- axdata_source_tdx/_tdx_wire/models/subchart.py +39 -0
- axdata_source_tdx/_tdx_wire/models/trade.py +46 -0
- axdata_source_tdx/_tdx_wire/protocol/__init__.py +43 -0
- axdata_source_tdx/_tdx_wire/protocol/_frame_constants.py +5 -0
- axdata_source_tdx/_tdx_wire/protocol/commands/__init__.py +37 -0
- axdata_source_tdx/_tdx_wire/protocol/commands/auction.py +165 -0
- axdata_source_tdx/_tdx_wire/protocol/commands/codec.py +37 -0
- axdata_source_tdx/_tdx_wire/protocol/commands/corporate.py +163 -0
- axdata_source_tdx/_tdx_wire/protocol/commands/finance.py +180 -0
- axdata_source_tdx/_tdx_wire/protocol/commands/intraday.py +382 -0
- axdata_source_tdx/_tdx_wire/protocol/commands/klines.py +396 -0
- axdata_source_tdx/_tdx_wire/protocol/commands/lookup.py +31 -0
- axdata_source_tdx/_tdx_wire/protocol/commands/price_limits.py +110 -0
- axdata_source_tdx/_tdx_wire/protocol/commands/quotes.py +804 -0
- axdata_source_tdx/_tdx_wire/protocol/commands/registry.py +30 -0
- axdata_source_tdx/_tdx_wire/protocol/commands/resources.py +107 -0
- axdata_source_tdx/_tdx_wire/protocol/commands/security.py +163 -0
- axdata_source_tdx/_tdx_wire/protocol/commands/session.py +129 -0
- axdata_source_tdx/_tdx_wire/protocol/commands/subchart.py +182 -0
- axdata_source_tdx/_tdx_wire/protocol/commands/trades.py +275 -0
- axdata_source_tdx/_tdx_wire/protocol/constants.py +70 -0
- axdata_source_tdx/_tdx_wire/protocol/frame.py +141 -0
- axdata_source_tdx/_tdx_wire/protocol/unit.py +98 -0
- axdata_source_tdx/_tdx_wire/py.typed +1 -0
- axdata_source_tdx/_tdx_wire/transport/__init__.py +28 -0
- axdata_source_tdx/_tdx_wire/transport/base.py +21 -0
- axdata_source_tdx/_tdx_wire/transport/memory.py +42 -0
- axdata_source_tdx/_tdx_wire/transport/pool.py +148 -0
- axdata_source_tdx/_tdx_wire/transport/socket.py +373 -0
- axdata_source_tdx/adapter.py +31 -0
- axdata_source_tdx/adjustment.py +163 -0
- axdata_source_tdx/adjustment_fetch.py +148 -0
- axdata_source_tdx/auction_fetch.py +162 -0
- axdata_source_tdx/axdata-provider.json +23680 -0
- axdata_source_tdx/catalog.py +39 -0
- axdata_source_tdx/client_factory.py +61 -0
- axdata_source_tdx/code_fetch.py +579 -0
- axdata_source_tdx/codes.py +69 -0
- axdata_source_tdx/collectors.py +525 -0
- axdata_source_tdx/derived_rows.py +500 -0
- axdata_source_tdx/downloader.py +139 -0
- axdata_source_tdx/downloader_interface_sets.py +29 -0
- axdata_source_tdx/downloader_profiles.py +731 -0
- axdata_source_tdx/downloader_registry.py +85 -0
- axdata_source_tdx/execution_utils.py +141 -0
- axdata_source_tdx/f10_executor.py +414 -0
- axdata_source_tdx/f10_normalize.py +169 -0
- axdata_source_tdx/f10_params.py +221 -0
- axdata_source_tdx/f10_postprocess.py +405 -0
- axdata_source_tdx/f10_render.py +29 -0
- axdata_source_tdx/f10_request.py +365 -0
- axdata_source_tdx/finance_fetch.py +514 -0
- axdata_source_tdx/finance_maps.py +377 -0
- axdata_source_tdx/finance_normalize.py +273 -0
- axdata_source_tdx/host_config.py +51 -0
- axdata_source_tdx/interface_sets.py +180 -0
- axdata_source_tdx/intraday_fetch.py +600 -0
- axdata_source_tdx/kline_helpers.py +333 -0
- axdata_source_tdx/limit_ladder_fetch.py +614 -0
- axdata_source_tdx/limit_ladder_topics.py +280 -0
- axdata_source_tdx/metadata.py +8 -0
- axdata_source_tdx/normalize_utils.py +264 -0
- axdata_source_tdx/options.py +195 -0
- axdata_source_tdx/price_limit_calendar.py +106 -0
- axdata_source_tdx/price_limit_fetch.py +264 -0
- axdata_source_tdx/price_limit_history.py +100 -0
- axdata_source_tdx/price_limits.py +142 -0
- axdata_source_tdx/provider.py +44 -0
- axdata_source_tdx/provider_bridge.py +43 -0
- axdata_source_tdx/quote_fetch.py +485 -0
- axdata_source_tdx/quote_identity.py +50 -0
- axdata_source_tdx/rank_fetch.py +316 -0
- axdata_source_tdx/rank_params.py +181 -0
- axdata_source_tdx/realtime_refresh.py +51 -0
- axdata_source_tdx/request_adapter.py +533 -0
- axdata_source_tdx/request_adapter_runtime.py +302 -0
- axdata_source_tdx/request_client.py +98 -0
- axdata_source_tdx/request_compat.py +432 -0
- axdata_source_tdx/request_dispatch.py +138 -0
- axdata_source_tdx/request_entrypoints.py +146 -0
- axdata_source_tdx/request_filters.py +154 -0
- axdata_source_tdx/request_host_config.py +103 -0
- axdata_source_tdx/request_limits.py +15 -0
- axdata_source_tdx/request_methods.py +1910 -0
- axdata_source_tdx/request_params.py +225 -0
- axdata_source_tdx/request_seams.py +534 -0
- axdata_source_tdx/resources/__init__.py +0 -0
- axdata_source_tdx/resources/finance_maps/__init__.py +1 -0
- axdata_source_tdx/resources/finance_maps/incon.dat +3702 -0
- axdata_source_tdx/resources/finance_maps/tdxhy.cfg +5620 -0
- axdata_source_tdx/resources/finance_maps/tdxzs.cfg +605 -0
- axdata_source_tdx/resources/tdx_extended_servers.json +22 -0
- axdata_source_tdx/resources/tdx_quote_servers.json +49 -0
- axdata_source_tdx/security_codes.py +127 -0
- axdata_source_tdx/series_history.py +348 -0
- axdata_source_tdx/server_cache.py +21 -0
- axdata_source_tdx/snapshot_normalize.py +232 -0
- axdata_source_tdx/source_adapter_registry.py +42 -0
- axdata_source_tdx/source_execution.py +22 -0
- axdata_source_tdx/source_execution_registry.py +43 -0
- axdata_source_tdx/stats_cache.py +150 -0
- axdata_source_tdx/stats_models.py +161 -0
- axdata_source_tdx/stats_resource.py +242 -0
- axdata_source_tdx/status_fetch.py +209 -0
- axdata_source_tdx/tdx_f10_catalog.py +957 -0
- axdata_source_tdx/tdx_f10_models.py +47 -0
- axdata_source_tdx/tdx_f10_names.py +48 -0
- axdata_source_tdx/tdx_f10_specs.py +1142 -0
- axdata_source_tdx/tdx_server_config.py +389 -0
- axdata_source_tdx/time_series_normalize.py +370 -0
- axdata_source_tdx/tqlex.py +138 -0
- axdata_source_tdx/wire.py +32 -0
- axdata_source_tdx/wire_requests.py +190 -0
- axdata_source_tdx_ext/__init__.py +15 -0
- axdata_source_tdx_ext/adapter.py +31 -0
- axdata_source_tdx_ext/axdata-provider.json +9424 -0
- axdata_source_tdx_ext/catalog.py +33 -0
- axdata_source_tdx_ext/metadata.py +8 -0
- axdata_source_tdx_ext/provider.py +44 -0
- axdata_source_tencent/__init__.py +15 -0
- axdata_source_tencent/adapter.py +31 -0
- axdata_source_tencent/axdata-provider.json +291 -0
- axdata_source_tencent/catalog.py +190 -0
- axdata_source_tencent/metadata.py +8 -0
- axdata_source_tencent/provider.py +41 -0
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
"""Trade-detail command builders and parsers."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from datetime import datetime, time
|
|
6
|
+
from importlib import import_module
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from axdata_source_tdx._tdx_wire.protocol.frame import RequestFrame, ResponseFrame
|
|
10
|
+
from axdata_source_tdx._tdx_wire._code_utils import split_code
|
|
11
|
+
from axdata_source_tdx._tdx_wire._command_layouts import SIDE_MAP
|
|
12
|
+
|
|
13
|
+
from axdata_source_tdx._tdx_wire._command_codes import command_code
|
|
14
|
+
|
|
15
|
+
TYPE_HISTORICAL_TRADES = command_code("historical_trades")
|
|
16
|
+
TYPE_TODAY_TRADES = command_code("today_trades")
|
|
17
|
+
_BINARY_MODULE = "axdata_source_tdx._tdx_wire._binary"
|
|
18
|
+
_EXCEPTIONS_MODULE = "axdata_source_tdx._tdx_wire.exceptions"
|
|
19
|
+
_MODEL_MODULE = "axdata_source_tdx._tdx_wire.models.trade"
|
|
20
|
+
_TIME_UTILS_MODULE = "axdata_source_tdx._tdx_wire._time_utils"
|
|
21
|
+
_BINARY_EXPORTS = {
|
|
22
|
+
"consume_tdx_signed_varint",
|
|
23
|
+
"consume_tdx_varint",
|
|
24
|
+
"date_from_yyyymmdd",
|
|
25
|
+
"little_f32",
|
|
26
|
+
"little_u16",
|
|
27
|
+
"yyyymmdd",
|
|
28
|
+
}
|
|
29
|
+
_EXCEPTION_EXPORTS = {"ProtocolError"}
|
|
30
|
+
_MODEL_EXPORTS = {"TradeDetailRecord", "TradeDetailSeries"}
|
|
31
|
+
_TIME_EXPORTS = {"SHANGHAI_TZ"}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _protocol_error():
|
|
35
|
+
return import_module(_EXCEPTIONS_MODULE).ProtocolError
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _binary():
|
|
39
|
+
return import_module(_BINARY_MODULE)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _consume_tdx_signed_varint(payload: bytes, offset: int) -> tuple[int, int]:
|
|
43
|
+
return _binary().consume_tdx_signed_varint(payload, offset)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _consume_tdx_varint(payload: bytes, offset: int) -> tuple[int, int]:
|
|
47
|
+
return _binary().consume_tdx_varint(payload, offset)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _date_from_yyyymmdd(raw: int):
|
|
51
|
+
return _binary().date_from_yyyymmdd(raw)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _little_f32(data: bytes) -> float:
|
|
55
|
+
return _binary().little_f32(data)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _little_u16(data: bytes) -> int:
|
|
59
|
+
return _binary().little_u16(data)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _yyyymmdd(value) -> int:
|
|
63
|
+
return _binary().yyyymmdd(value)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _trade_detail_record_cls():
|
|
67
|
+
return import_module(_MODEL_MODULE).TradeDetailRecord
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _trade_detail_series_cls():
|
|
71
|
+
return import_module(_MODEL_MODULE).TradeDetailSeries
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _shanghai_tz():
|
|
75
|
+
return import_module(_TIME_UTILS_MODULE).SHANGHAI_TZ
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def build_today_trades_frame(payload: dict[str, Any], msg_id: int) -> RequestFrame:
|
|
79
|
+
market_id, _, number = split_code(payload["code"])
|
|
80
|
+
start = normalize_trade_start(payload.get("start", 0))
|
|
81
|
+
count = normalize_trade_count(payload.get("count", 115))
|
|
82
|
+
data = (
|
|
83
|
+
market_id.to_bytes(1, "little", signed=False)
|
|
84
|
+
+ b"\x00"
|
|
85
|
+
+ number.encode("ascii")
|
|
86
|
+
+ start.to_bytes(2, "little", signed=False)
|
|
87
|
+
+ count.to_bytes(2, "little", signed=False)
|
|
88
|
+
)
|
|
89
|
+
return RequestFrame(msg_id=msg_id, msg_type=TYPE_TODAY_TRADES, data=data)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def build_historical_trades_frame(payload: dict[str, Any], msg_id: int) -> RequestFrame:
|
|
93
|
+
market_id, _, number = split_code(payload["code"])
|
|
94
|
+
trade_date_raw = historical_trade_date(payload)
|
|
95
|
+
start = normalize_trade_start(payload.get("start", 0))
|
|
96
|
+
count = normalize_trade_count(payload.get("count", 900))
|
|
97
|
+
data = (
|
|
98
|
+
trade_date_raw.to_bytes(4, "little", signed=False)
|
|
99
|
+
+ market_id.to_bytes(2, "little", signed=False)
|
|
100
|
+
+ number.encode("ascii")
|
|
101
|
+
+ start.to_bytes(2, "little", signed=False)
|
|
102
|
+
+ count.to_bytes(2, "little", signed=False)
|
|
103
|
+
)
|
|
104
|
+
return RequestFrame(msg_id=msg_id, msg_type=TYPE_HISTORICAL_TRADES, data=data)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def parse_today_trades_payload(
|
|
108
|
+
response: ResponseFrame,
|
|
109
|
+
request_payload: dict[str, Any] | None = None,
|
|
110
|
+
) -> TradeDetailSeries:
|
|
111
|
+
request_payload = request_payload or {}
|
|
112
|
+
requested_code = request_payload.get("code", "sz000001")
|
|
113
|
+
market_id, exchange, number = split_code(requested_code)
|
|
114
|
+
start = normalize_trade_start(request_payload.get("start", 0))
|
|
115
|
+
count = normalize_trade_count(request_payload.get("count", 115))
|
|
116
|
+
payload = response.data
|
|
117
|
+
if len(payload) < 2:
|
|
118
|
+
raise _protocol_error()("invalid today trades payload")
|
|
119
|
+
|
|
120
|
+
record_count = _little_u16(payload[:2])
|
|
121
|
+
records = parse_trade_records(payload, offset=2, record_count=record_count, start=start, trade_date_raw=None)
|
|
122
|
+
return _trade_detail_series_cls()(
|
|
123
|
+
exchange=exchange,
|
|
124
|
+
market_id=market_id,
|
|
125
|
+
code=number,
|
|
126
|
+
start=start,
|
|
127
|
+
request_count=count,
|
|
128
|
+
records=tuple(records),
|
|
129
|
+
raw_payload=payload if request_payload.get("include_raw") else b"",
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def parse_historical_trades_payload(
|
|
134
|
+
response: ResponseFrame,
|
|
135
|
+
request_payload: dict[str, Any] | None = None,
|
|
136
|
+
) -> TradeDetailSeries:
|
|
137
|
+
request_payload = request_payload or {}
|
|
138
|
+
requested_code = request_payload.get("code", "sz000001")
|
|
139
|
+
market_id, exchange, number = split_code(requested_code)
|
|
140
|
+
trade_date_raw = historical_trade_date(request_payload)
|
|
141
|
+
trade_date = _date_from_yyyymmdd(trade_date_raw)
|
|
142
|
+
if trade_date is None:
|
|
143
|
+
raise _protocol_error()(f"invalid historical trades trade_date: {trade_date_raw}")
|
|
144
|
+
start = normalize_trade_start(request_payload.get("start", 0))
|
|
145
|
+
count = normalize_trade_count(request_payload.get("count", 900))
|
|
146
|
+
payload = response.data
|
|
147
|
+
if len(payload) < 6:
|
|
148
|
+
raise _protocol_error()("invalid historical trades payload")
|
|
149
|
+
|
|
150
|
+
record_count = _little_u16(payload[:2])
|
|
151
|
+
price_base_raw_f32 = float(_little_f32(payload[2:6]))
|
|
152
|
+
records = parse_trade_records(payload, offset=6, record_count=record_count, start=start, trade_date_raw=trade_date_raw)
|
|
153
|
+
return _trade_detail_series_cls()(
|
|
154
|
+
exchange=exchange,
|
|
155
|
+
market_id=market_id,
|
|
156
|
+
code=number,
|
|
157
|
+
start=start,
|
|
158
|
+
request_count=count,
|
|
159
|
+
records=tuple(records),
|
|
160
|
+
trade_date=trade_date,
|
|
161
|
+
price_base_raw_f32=price_base_raw_f32,
|
|
162
|
+
raw_payload=payload if request_payload.get("include_raw") else b"",
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def parse_trade_records(
|
|
167
|
+
payload: bytes,
|
|
168
|
+
*,
|
|
169
|
+
offset: int,
|
|
170
|
+
record_count: int,
|
|
171
|
+
start: int,
|
|
172
|
+
trade_date_raw: int | None,
|
|
173
|
+
) -> list[TradeDetailRecord]:
|
|
174
|
+
trade_date = _date_from_yyyymmdd(trade_date_raw) if trade_date_raw is not None else None
|
|
175
|
+
trade_detail_record = _trade_detail_record_cls()
|
|
176
|
+
records: list[TradeDetailRecord] = []
|
|
177
|
+
price_acc_raw = 0
|
|
178
|
+
for index in range(record_count):
|
|
179
|
+
record_start = offset
|
|
180
|
+
if offset + 2 > len(payload):
|
|
181
|
+
raise _protocol_error()("truncated trade time field")
|
|
182
|
+
time_minutes = _little_u16(payload[offset : offset + 2])
|
|
183
|
+
offset += 2
|
|
184
|
+
price_delta_raw, offset = _consume_tdx_signed_varint(payload, offset)
|
|
185
|
+
volume, offset = _consume_tdx_varint(payload, offset)
|
|
186
|
+
order_count, offset = _consume_tdx_varint(payload, offset)
|
|
187
|
+
status_raw, offset = _consume_tdx_varint(payload, offset)
|
|
188
|
+
tail_raw, offset = _consume_tdx_varint(payload, offset)
|
|
189
|
+
price_acc_raw += price_delta_raw
|
|
190
|
+
trade_time = trade_time_from_minutes(time_minutes)
|
|
191
|
+
trade_datetime = (
|
|
192
|
+
datetime.combine(trade_date, trade_time, tzinfo=_shanghai_tz())
|
|
193
|
+
if trade_date is not None
|
|
194
|
+
else None
|
|
195
|
+
)
|
|
196
|
+
records.append(
|
|
197
|
+
trade_detail_record(
|
|
198
|
+
trade_time=trade_time,
|
|
199
|
+
trade_datetime=trade_datetime,
|
|
200
|
+
trade_date=trade_date,
|
|
201
|
+
index=index,
|
|
202
|
+
absolute_index=start + index,
|
|
203
|
+
time_minutes=time_minutes,
|
|
204
|
+
price=price_acc_raw / 10000.0,
|
|
205
|
+
price_acc_raw=price_acc_raw,
|
|
206
|
+
price_delta_raw=price_delta_raw,
|
|
207
|
+
volume=volume,
|
|
208
|
+
order_count=order_count,
|
|
209
|
+
status_raw=status_raw,
|
|
210
|
+
side=SIDE_MAP.get(status_raw, f"status_{status_raw}"),
|
|
211
|
+
tail_raw=tail_raw,
|
|
212
|
+
record_hex=payload[record_start:offset].hex(),
|
|
213
|
+
)
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
if offset != len(payload):
|
|
217
|
+
raise _protocol_error()(f"unexpected trailing trade payload bytes: {len(payload) - offset}")
|
|
218
|
+
return records
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def trade_time_from_minutes(time_minutes: int) -> time:
|
|
222
|
+
if time_minutes < 0 or time_minutes >= 24 * 60:
|
|
223
|
+
raise _protocol_error()(f"invalid trade time minutes: {time_minutes}")
|
|
224
|
+
return time(time_minutes // 60, time_minutes % 60)
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def historical_trade_date(payload: dict[str, Any]) -> int:
|
|
228
|
+
value = payload.get("trade_date", payload.get("date"))
|
|
229
|
+
if value in (None, "", 0, "0"):
|
|
230
|
+
raise _protocol_error()("historical trades trade_date is required")
|
|
231
|
+
return _yyyymmdd(value)
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def normalize_trade_start(value: Any) -> int:
|
|
235
|
+
try:
|
|
236
|
+
parsed = int(value)
|
|
237
|
+
except (TypeError, ValueError) as exc:
|
|
238
|
+
raise _protocol_error()("start must be an integer") from exc
|
|
239
|
+
if parsed < 0 or parsed > 0xFFFF:
|
|
240
|
+
raise _protocol_error()("start must be between 0 and 65535")
|
|
241
|
+
return parsed
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def normalize_trade_count(value: Any) -> int:
|
|
245
|
+
try:
|
|
246
|
+
parsed = int(value)
|
|
247
|
+
except (TypeError, ValueError) as exc:
|
|
248
|
+
raise _protocol_error()("count must be an integer") from exc
|
|
249
|
+
if parsed <= 0 or parsed > 0xFFFF:
|
|
250
|
+
raise _protocol_error()("count must be between 1 and 65535")
|
|
251
|
+
return parsed
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def __getattr__(name: str) -> Any:
|
|
255
|
+
if name in _EXCEPTION_EXPORTS:
|
|
256
|
+
value = getattr(import_module(_EXCEPTIONS_MODULE), name)
|
|
257
|
+
globals()[name] = value
|
|
258
|
+
return value
|
|
259
|
+
if name in _MODEL_EXPORTS:
|
|
260
|
+
value = getattr(import_module(_MODEL_MODULE), name)
|
|
261
|
+
globals()[name] = value
|
|
262
|
+
return value
|
|
263
|
+
if name in _BINARY_EXPORTS:
|
|
264
|
+
value = getattr(import_module(_BINARY_MODULE), name)
|
|
265
|
+
globals()[name] = value
|
|
266
|
+
return value
|
|
267
|
+
if name in _TIME_EXPORTS:
|
|
268
|
+
value = getattr(import_module(_TIME_UTILS_MODULE), name)
|
|
269
|
+
globals()[name] = value
|
|
270
|
+
return value
|
|
271
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def __dir__() -> list[str]:
|
|
275
|
+
return sorted(set(globals()) | _EXCEPTION_EXPORTS | _MODEL_EXPORTS | _BINARY_EXPORTS | _TIME_EXPORTS)
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""7709 protocol constants."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from importlib import import_module
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
_COMMAND_CODES_MODULE = "axdata_source_tdx._tdx_wire._command_codes"
|
|
9
|
+
_FRAME_CONSTANTS_MODULE = "axdata_source_tdx._tdx_wire.protocol._frame_constants"
|
|
10
|
+
_REQUEST_DEFAULTS_MODULE = "axdata_source_tdx._tdx_wire._request_defaults"
|
|
11
|
+
_COMMAND_EXPORTS = {
|
|
12
|
+
"TYPE_AUCTION_PROCESS": "auction_process",
|
|
13
|
+
"TYPE_CAPITAL_CHANGES": "capital_changes",
|
|
14
|
+
"TYPE_CATEGORY_QUOTES": "category_quotes",
|
|
15
|
+
"TYPE_EXPLICIT_QUOTES": "explicit_quotes",
|
|
16
|
+
"TYPE_FILE_CONTENT": "file_content",
|
|
17
|
+
"TYPE_FINANCE_INFO": "finance_info",
|
|
18
|
+
"TYPE_HANDSHAKE": "handshake",
|
|
19
|
+
"TYPE_HEARTBEAT": "heartbeat",
|
|
20
|
+
"TYPE_HISTORICAL_INTRADAY": "historical_intraday",
|
|
21
|
+
"TYPE_HISTORICAL_TRADES": "historical_trades",
|
|
22
|
+
"TYPE_INTRADAY_SUBCHART": "intraday_subchart",
|
|
23
|
+
"TYPE_KLINES": "klines",
|
|
24
|
+
"TYPE_LEGACY_QUOTES": "legacy_quotes",
|
|
25
|
+
"TYPE_PRICE_LIMITS": "price_limits",
|
|
26
|
+
"TYPE_RECENT_HISTORICAL_INTRADAY": "recent_historical_intraday",
|
|
27
|
+
"TYPE_REFRESH_QUOTES": "refresh_quotes",
|
|
28
|
+
"TYPE_SECURITY_COUNT": "security_count",
|
|
29
|
+
"TYPE_SECURITY_LIST": "security_list",
|
|
30
|
+
"TYPE_TODAY_INTRADAY": "today_intraday",
|
|
31
|
+
"TYPE_TODAY_TRADES": "today_trades",
|
|
32
|
+
}
|
|
33
|
+
_FRAME_EXPORTS = {"CONTROL_DEFAULT", "PREFIX", "PREFIX_RESP"}
|
|
34
|
+
_REQUEST_DEFAULT_EXPORTS = {"DEFAULT_CODE_PAGE_SIZE", "DEFAULT_QUOTE_BATCH_SIZE"}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _command_codes_module():
|
|
38
|
+
return import_module(_COMMAND_CODES_MODULE)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _frame_constants_module():
|
|
42
|
+
return import_module(_FRAME_CONSTANTS_MODULE)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _request_defaults_module():
|
|
46
|
+
return import_module(_REQUEST_DEFAULTS_MODULE)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def __getattr__(name: str):
|
|
50
|
+
if name in _COMMAND_EXPORTS:
|
|
51
|
+
value = _command_codes_module().command_code(_COMMAND_EXPORTS[name])
|
|
52
|
+
elif name == "command_code":
|
|
53
|
+
value = _command_codes_module().command_code
|
|
54
|
+
elif name == "COMMAND_CODES":
|
|
55
|
+
value = _command_codes_module().COMMAND_CODES
|
|
56
|
+
elif name in _FRAME_EXPORTS:
|
|
57
|
+
value = getattr(_frame_constants_module(), name)
|
|
58
|
+
elif name in _REQUEST_DEFAULT_EXPORTS:
|
|
59
|
+
value = getattr(_request_defaults_module(), name)
|
|
60
|
+
else:
|
|
61
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
62
|
+
globals()[name] = value
|
|
63
|
+
return value
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def __dir__() -> list[str]:
|
|
67
|
+
return sorted(set(globals()) | set(__all__))
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
__all__ = sorted(set(_COMMAND_EXPORTS) | _FRAME_EXPORTS | _REQUEST_DEFAULT_EXPORTS | {"COMMAND_CODES", "command_code"})
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"""7709 TCP frame encoding and decoding."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from importlib import import_module
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
from ._frame_constants import CONTROL_DEFAULT, PREFIX, PREFIX_RESP
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
_EXCEPTIONS_MODULE = "axdata_source_tdx._tdx_wire.exceptions"
|
|
13
|
+
_EXCEPTION_EXPORTS = {"ConnectionClosedError", "ProtocolError"}
|
|
14
|
+
_STDLIB_EXPORTS = {"socket", "struct", "zlib"}
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _connection_closed_error():
|
|
18
|
+
return import_module(_EXCEPTIONS_MODULE).ConnectionClosedError
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _protocol_error():
|
|
22
|
+
return import_module(_EXCEPTIONS_MODULE).ProtocolError
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def __getattr__(name: str):
|
|
26
|
+
if name in _EXCEPTION_EXPORTS:
|
|
27
|
+
value = getattr(import_module(_EXCEPTIONS_MODULE), name)
|
|
28
|
+
globals()[name] = value
|
|
29
|
+
return value
|
|
30
|
+
if name in _STDLIB_EXPORTS:
|
|
31
|
+
value = import_module(name)
|
|
32
|
+
globals()[name] = value
|
|
33
|
+
return value
|
|
34
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def __dir__() -> list[str]:
|
|
38
|
+
return sorted(set(globals()) | _EXCEPTION_EXPORTS | _STDLIB_EXPORTS)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@dataclass(frozen=True, slots=True)
|
|
42
|
+
class RequestFrame:
|
|
43
|
+
msg_id: int
|
|
44
|
+
msg_type: int
|
|
45
|
+
data: bytes = b""
|
|
46
|
+
control: int = CONTROL_DEFAULT
|
|
47
|
+
|
|
48
|
+
def to_bytes(self) -> bytes:
|
|
49
|
+
import struct
|
|
50
|
+
|
|
51
|
+
length = len(self.data) + 2
|
|
52
|
+
return struct.pack("<BIBHHH", PREFIX, self.msg_id, self.control, length, length, self.msg_type) + self.data
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@dataclass(frozen=True, slots=True)
|
|
56
|
+
class ResponseFrame:
|
|
57
|
+
control: int
|
|
58
|
+
msg_id: int
|
|
59
|
+
msg_type: int
|
|
60
|
+
zip_length: int
|
|
61
|
+
length: int
|
|
62
|
+
data: bytes
|
|
63
|
+
raw: bytes
|
|
64
|
+
response_header_reserved: int = 0
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def read_exact(sock: socket.socket, size: int) -> bytes:
|
|
68
|
+
chunks = bytearray()
|
|
69
|
+
while len(chunks) < size:
|
|
70
|
+
piece = sock.recv(size - len(chunks))
|
|
71
|
+
if not piece:
|
|
72
|
+
raise _connection_closed_error()("socket closed by remote peer")
|
|
73
|
+
chunks.extend(piece)
|
|
74
|
+
return bytes(chunks)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def read_response_frame(sock: socket.socket) -> bytes:
|
|
78
|
+
window = bytearray(read_exact(sock, 4))
|
|
79
|
+
while bytes(window) != PREFIX_RESP:
|
|
80
|
+
window = window[1:] + read_exact(sock, 1)
|
|
81
|
+
|
|
82
|
+
header = read_exact(sock, 12)
|
|
83
|
+
zip_length = int.from_bytes(header[8:10], "little", signed=False)
|
|
84
|
+
payload = read_exact(sock, zip_length)
|
|
85
|
+
return bytes(window) + header + payload
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def decode_response(raw: bytes) -> ResponseFrame:
|
|
89
|
+
if len(raw) < 16:
|
|
90
|
+
raise _protocol_error()(f"invalid response length: {len(raw)}")
|
|
91
|
+
if raw[:4] != PREFIX_RESP:
|
|
92
|
+
raise _protocol_error()(f"invalid response prefix: {raw[:4].hex()}")
|
|
93
|
+
|
|
94
|
+
control = raw[4]
|
|
95
|
+
msg_id = int.from_bytes(raw[5:9], "little", signed=False)
|
|
96
|
+
reserved = raw[9]
|
|
97
|
+
msg_type = int.from_bytes(raw[10:12], "little", signed=False)
|
|
98
|
+
zip_length = int.from_bytes(raw[12:14], "little", signed=False)
|
|
99
|
+
length = int.from_bytes(raw[14:16], "little", signed=False)
|
|
100
|
+
payload = raw[16:]
|
|
101
|
+
|
|
102
|
+
if len(payload) != zip_length:
|
|
103
|
+
raise _protocol_error()(f"zip length mismatch: expected {zip_length}, got {len(payload)}")
|
|
104
|
+
|
|
105
|
+
if zip_length != length:
|
|
106
|
+
import zlib
|
|
107
|
+
|
|
108
|
+
data = zlib.decompress(payload)
|
|
109
|
+
else:
|
|
110
|
+
data = payload
|
|
111
|
+
if len(data) != length:
|
|
112
|
+
raise _protocol_error()(f"decoded length mismatch: expected {length}, got {len(data)}")
|
|
113
|
+
|
|
114
|
+
return ResponseFrame(
|
|
115
|
+
control=control,
|
|
116
|
+
msg_id=msg_id,
|
|
117
|
+
msg_type=msg_type,
|
|
118
|
+
zip_length=zip_length,
|
|
119
|
+
length=length,
|
|
120
|
+
data=data,
|
|
121
|
+
raw=raw,
|
|
122
|
+
response_header_reserved=reserved,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
__all__ = [
|
|
127
|
+
"ConnectionClosedError",
|
|
128
|
+
"CONTROL_DEFAULT",
|
|
129
|
+
"PREFIX",
|
|
130
|
+
"PREFIX_RESP",
|
|
131
|
+
"ProtocolError",
|
|
132
|
+
"RequestFrame",
|
|
133
|
+
"ResponseFrame",
|
|
134
|
+
"dataclass",
|
|
135
|
+
"decode_response",
|
|
136
|
+
"read_exact",
|
|
137
|
+
"read_response_frame",
|
|
138
|
+
"socket",
|
|
139
|
+
"struct",
|
|
140
|
+
"zlib",
|
|
141
|
+
]
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"""Compatibility binary helpers for 7709 command parsers."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from datetime import date, datetime
|
|
6
|
+
from importlib import import_module
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
_BINARY_MODULE_NAME = "axdata_source_tdx._tdx_wire._binary"
|
|
10
|
+
_MARKET_MODULE_NAME = "axdata_source_tdx._tdx_wire._market"
|
|
11
|
+
_MARKET_EXPORTS = {"ID_TO_MARKET", "MARKET_TO_ID", "market_to_id", "normalize_market"}
|
|
12
|
+
|
|
13
|
+
_BINARY_MODULE = None
|
|
14
|
+
_MARKET_MODULE = None
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _load_binary_module():
|
|
18
|
+
global _BINARY_MODULE
|
|
19
|
+
if _BINARY_MODULE is None:
|
|
20
|
+
_BINARY_MODULE = import_module(_BINARY_MODULE_NAME)
|
|
21
|
+
return _BINARY_MODULE
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _load_market_module():
|
|
25
|
+
global _MARKET_MODULE
|
|
26
|
+
if _MARKET_MODULE is None:
|
|
27
|
+
_MARKET_MODULE = import_module(_MARKET_MODULE_NAME)
|
|
28
|
+
return _MARKET_MODULE
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def little_u16(data: bytes) -> int:
|
|
32
|
+
return _load_binary_module().little_u16(data)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def little_u32(data: bytes) -> int:
|
|
36
|
+
return _load_binary_module().little_u32(data)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def little_f32(data: bytes) -> float:
|
|
40
|
+
return _load_binary_module().little_f32(data)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def tdx_quantity_u32(data: bytes) -> float:
|
|
44
|
+
return _load_binary_module().tdx_quantity_u32(data)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def tdx_quantity_from_u32(raw: int) -> float:
|
|
48
|
+
return _load_binary_module().tdx_quantity_from_u32(raw)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def decode_gbk_text(data: bytes) -> str:
|
|
52
|
+
return _load_binary_module().decode_gbk_text(data)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def yyyymmdd(value: str | int | date | datetime | None = None) -> int:
|
|
56
|
+
return _load_binary_module().yyyymmdd(value)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def date_from_yyyymmdd(raw: int) -> date | None:
|
|
60
|
+
return _load_binary_module().date_from_yyyymmdd(raw)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def consume_tdx_signed_varint(payload: bytes, offset: int) -> tuple[int, int]:
|
|
64
|
+
return _load_binary_module().consume_tdx_signed_varint(payload, offset)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def consume_tdx_varint(payload: bytes, offset: int) -> tuple[int, int]:
|
|
68
|
+
return _load_binary_module().consume_tdx_varint(payload, offset)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def __getattr__(name: str):
|
|
72
|
+
if name in _MARKET_EXPORTS:
|
|
73
|
+
value = getattr(_load_market_module(), name)
|
|
74
|
+
globals()[name] = value
|
|
75
|
+
return value
|
|
76
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def __dir__() -> list[str]:
|
|
80
|
+
return sorted(set(globals()) | set(__all__))
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
__all__ = [
|
|
84
|
+
"ID_TO_MARKET",
|
|
85
|
+
"MARKET_TO_ID",
|
|
86
|
+
"consume_tdx_signed_varint",
|
|
87
|
+
"consume_tdx_varint",
|
|
88
|
+
"date_from_yyyymmdd",
|
|
89
|
+
"decode_gbk_text",
|
|
90
|
+
"little_f32",
|
|
91
|
+
"little_u16",
|
|
92
|
+
"little_u32",
|
|
93
|
+
"market_to_id",
|
|
94
|
+
"normalize_market",
|
|
95
|
+
"tdx_quantity_from_u32",
|
|
96
|
+
"tdx_quantity_u32",
|
|
97
|
+
"yyyymmdd",
|
|
98
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""Transport abstractions for axdata_source_tdx._tdx_wire."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from importlib import import_module
|
|
6
|
+
|
|
7
|
+
__all__ = ["InMemoryTransport", "PooledSocketTransport", "SocketTransport", "Transport"]
|
|
8
|
+
|
|
9
|
+
_EXPORT_MODULES = {
|
|
10
|
+
"InMemoryTransport": ".memory",
|
|
11
|
+
"PooledSocketTransport": ".pool",
|
|
12
|
+
"SocketTransport": ".socket",
|
|
13
|
+
"Transport": ".base",
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def __getattr__(name: str):
|
|
18
|
+
module_name = _EXPORT_MODULES.get(name)
|
|
19
|
+
if module_name is None:
|
|
20
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
21
|
+
module = import_module(module_name, __name__)
|
|
22
|
+
value = getattr(module, name)
|
|
23
|
+
globals()[name] = value
|
|
24
|
+
return value
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def __dir__() -> list[str]:
|
|
28
|
+
return sorted(set(globals()) | set(__all__))
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""Transport interface definitions."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any, Protocol
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Transport(Protocol):
|
|
9
|
+
"""Request/response transport used by API services."""
|
|
10
|
+
|
|
11
|
+
def connect(self) -> None:
|
|
12
|
+
"""Open transport resources."""
|
|
13
|
+
|
|
14
|
+
def close(self) -> None:
|
|
15
|
+
"""Close transport resources."""
|
|
16
|
+
|
|
17
|
+
def execute(self, command: int, payload: dict[str, Any] | None = None) -> Any:
|
|
18
|
+
"""Execute one logical protocol command."""
|
|
19
|
+
|
|
20
|
+
def request(self, command: str) -> str:
|
|
21
|
+
"""Legacy text request used by early health tests."""
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""测试和示例使用的内存 transport。"""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class InMemoryTransport:
|
|
9
|
+
"""不依赖 socket 的确定性 transport。
|
|
10
|
+
|
|
11
|
+
用于在接入真实 7709 服务器前验证对外 API 和单元测试。
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
def __init__(self) -> None:
|
|
15
|
+
self.connected = False
|
|
16
|
+
self.calls: list[tuple[int, dict[str, Any]]] = []
|
|
17
|
+
self.responses: dict[int, Any] = {}
|
|
18
|
+
|
|
19
|
+
def connect(self) -> None:
|
|
20
|
+
self.connected = True
|
|
21
|
+
|
|
22
|
+
def close(self) -> None:
|
|
23
|
+
self.connected = False
|
|
24
|
+
|
|
25
|
+
def register_response(self, command: int, response: Any) -> None:
|
|
26
|
+
self.responses[command] = response
|
|
27
|
+
|
|
28
|
+
def execute(self, command: int, payload: dict[str, Any] | None = None) -> Any:
|
|
29
|
+
request_payload = dict(payload or {})
|
|
30
|
+
self.calls.append((command, request_payload))
|
|
31
|
+
if command in self.responses:
|
|
32
|
+
return self.responses[command]
|
|
33
|
+
return {
|
|
34
|
+
"command": f"0x{command:04x}",
|
|
35
|
+
"payload": request_payload,
|
|
36
|
+
"status": "memory_transport",
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
def request(self, command: str) -> str:
|
|
40
|
+
if command == "ping":
|
|
41
|
+
return "pong"
|
|
42
|
+
raise ValueError(f"unsupported command: {command}")
|