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,500 @@
|
|
|
1
|
+
"""Derived TDX request row builders."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Mapping, Sequence
|
|
6
|
+
from datetime import datetime
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from .codes import MARKET_TO_EXCHANGE, MARKET_TO_ID, tdx_code_to_instrument_id
|
|
10
|
+
from .normalize_utils import (
|
|
11
|
+
auction_open_volume_hand,
|
|
12
|
+
bar_trade_date,
|
|
13
|
+
get_value,
|
|
14
|
+
limit_board_text,
|
|
15
|
+
market_value,
|
|
16
|
+
open_volume_ratio,
|
|
17
|
+
optional_int,
|
|
18
|
+
round_optional_float,
|
|
19
|
+
safe_ratio,
|
|
20
|
+
safe_ratio_pct,
|
|
21
|
+
tenk_shares_to_lots,
|
|
22
|
+
tenk_to_unit,
|
|
23
|
+
tenk_yuan,
|
|
24
|
+
)
|
|
25
|
+
from .price_limits import (
|
|
26
|
+
limit_ladder_status,
|
|
27
|
+
positive_number,
|
|
28
|
+
price_limit_name_flag,
|
|
29
|
+
price_limit_ratio_from_rule,
|
|
30
|
+
price_limit_rule,
|
|
31
|
+
rule_price_limits,
|
|
32
|
+
st_type_from_name,
|
|
33
|
+
)
|
|
34
|
+
from .request_filters import board_matches
|
|
35
|
+
from .security_codes import board_from_tdx_code
|
|
36
|
+
from .snapshot_normalize import (
|
|
37
|
+
normalize_realtime_snapshot_row,
|
|
38
|
+
quote_level_at,
|
|
39
|
+
quote_tdx_code,
|
|
40
|
+
)
|
|
41
|
+
from axdata_core.source_errors import SourceRequestValidationError
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def normalize_auction_indicator_row(
|
|
45
|
+
quote: Any,
|
|
46
|
+
*,
|
|
47
|
+
stat_row: Any | None,
|
|
48
|
+
stat2_row: Any | None,
|
|
49
|
+
recent_daily_bars: Sequence[Any],
|
|
50
|
+
finance_row: Mapping[str, Any] | None,
|
|
51
|
+
) -> dict[str, Any]:
|
|
52
|
+
snapshot = normalize_realtime_snapshot_row(quote)
|
|
53
|
+
open_price = snapshot.get("open")
|
|
54
|
+
last_price = snapshot.get("last_price")
|
|
55
|
+
open_amount = snapshot.get("open_amount")
|
|
56
|
+
amount = snapshot.get("amount")
|
|
57
|
+
locked_amount = snapshot.get("locked_amount")
|
|
58
|
+
open_volume_hand_value = auction_open_volume_hand(open_amount, open_price)
|
|
59
|
+
free_float_shares = tenk_to_unit(get_value(stat_row, "free_float_shares_10k"))
|
|
60
|
+
free_float_market_value = market_value(free_float_shares, last_price)
|
|
61
|
+
float_shares = get_value(finance_row, "float_share") if finance_row else None
|
|
62
|
+
float_market_value = market_value(float_shares, last_price)
|
|
63
|
+
prev_amount = tenk_yuan(get_value(stat2_row, "prev_amount_10k"))
|
|
64
|
+
prev_seal_amount = tenk_yuan(get_value(stat2_row, "prev_seal_amount_10k"))
|
|
65
|
+
prev2_seal_amount = tenk_yuan(get_value(stat2_row, "prev2_seal_amount_10k"))
|
|
66
|
+
prev_open_amount = tenk_yuan(get_value(stat2_row, "prev_open_amount_10k"))
|
|
67
|
+
prev_open_volume_hand = round_optional_float(get_value(stat2_row, "prev_open_volume_hand"))
|
|
68
|
+
limit_stat_days = optional_int(get_value(stat_row, "limit_stat_days"))
|
|
69
|
+
limit_up_count = optional_int(get_value(stat_row, "limit_up_count_in_stat_days"))
|
|
70
|
+
return {
|
|
71
|
+
"instrument_id": snapshot.get("instrument_id"),
|
|
72
|
+
"symbol": snapshot.get("symbol"),
|
|
73
|
+
"tdx_code": str(snapshot.get("tdx_code") or "").lower(),
|
|
74
|
+
"exchange": snapshot.get("exchange"),
|
|
75
|
+
"stats_date": get_value(stat2_row, "stats_date") or get_value(stat_row, "stats_date"),
|
|
76
|
+
"open_price": open_price,
|
|
77
|
+
"pre_close": snapshot.get("pre_close"),
|
|
78
|
+
"open_change_pct": snapshot.get("open_change_pct"),
|
|
79
|
+
"open_amount": open_amount,
|
|
80
|
+
"open_volume_hand": open_volume_hand_value,
|
|
81
|
+
"open_volume_ratio": open_volume_ratio(open_volume_hand_value, recent_daily_bars),
|
|
82
|
+
"open_turnover_z": safe_ratio_pct(open_volume_hand_value, tenk_shares_to_lots(get_value(stat_row, "free_float_shares_10k"))),
|
|
83
|
+
"open_prev_amount_ratio": safe_ratio_pct(open_amount, prev_amount),
|
|
84
|
+
"auction_prev_volume_ratio": safe_ratio(open_volume_hand_value, prev_open_volume_hand),
|
|
85
|
+
"opening_rush": snapshot.get("opening_rush"),
|
|
86
|
+
"open_prev_seal_ratio": safe_ratio_pct(open_amount, prev_seal_amount),
|
|
87
|
+
"prev_amount": prev_amount,
|
|
88
|
+
"prev_seal_amount": prev_seal_amount,
|
|
89
|
+
"prev2_seal_amount": prev2_seal_amount,
|
|
90
|
+
"prev_open_volume_hand": prev_open_volume_hand,
|
|
91
|
+
"prev_open_amount": prev_open_amount,
|
|
92
|
+
"float_shares": round_optional_float(float_shares),
|
|
93
|
+
"float_market_value": float_market_value,
|
|
94
|
+
"free_float_shares": free_float_shares,
|
|
95
|
+
"free_float_market_value": free_float_market_value,
|
|
96
|
+
"seal_amount": locked_amount,
|
|
97
|
+
"seal_to_amount_ratio": safe_ratio(locked_amount, amount),
|
|
98
|
+
"seal_to_float_ratio": safe_ratio_pct(locked_amount, free_float_market_value),
|
|
99
|
+
"seal_prev_ratio": safe_ratio(locked_amount, prev_seal_amount),
|
|
100
|
+
"limit_stat_days": limit_stat_days,
|
|
101
|
+
"limit_up_count_in_stat_days": limit_up_count,
|
|
102
|
+
"limit_board_text": limit_board_text(limit_stat_days, limit_up_count),
|
|
103
|
+
"limit_up_streak_days": optional_int(get_value(stat_row, "limit_up_streak_days")),
|
|
104
|
+
"year_limit_up_days": optional_int(get_value(stat_row, "year_limit_up_days")),
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def normalize_daily_share_row(
|
|
109
|
+
tdx_code: str,
|
|
110
|
+
*,
|
|
111
|
+
stats_date: Any,
|
|
112
|
+
stat_row: Any | None,
|
|
113
|
+
finance_row: Mapping[str, Any] | None,
|
|
114
|
+
) -> dict[str, Any]:
|
|
115
|
+
instrument_id = tdx_code_to_instrument_id(tdx_code)
|
|
116
|
+
symbol = instrument_id.split(".", 1)[0]
|
|
117
|
+
total_share = get_value(finance_row, "total_share") if finance_row else None
|
|
118
|
+
float_share = get_value(finance_row, "float_share") if finance_row else None
|
|
119
|
+
free_float_share = tenk_to_unit(get_value(stat_row, "free_float_shares_10k"))
|
|
120
|
+
row_stats_date = get_value(stat_row, "stats_date") or stats_date
|
|
121
|
+
row_stats_date = str(row_stats_date) if row_stats_date not in (None, "") else None
|
|
122
|
+
return {
|
|
123
|
+
"trade_date": row_stats_date,
|
|
124
|
+
"instrument_id": instrument_id,
|
|
125
|
+
"symbol": symbol,
|
|
126
|
+
"tdx_code": tdx_code,
|
|
127
|
+
"exchange": MARKET_TO_EXCHANGE.get(tdx_code[:2], tdx_code[:2].upper()),
|
|
128
|
+
"total_share": round_optional_float(total_share),
|
|
129
|
+
"float_share": round_optional_float(float_share),
|
|
130
|
+
"free_float_share_z": free_float_share,
|
|
131
|
+
"finance_updated_date": get_value(finance_row, "updated_date") if finance_row else None,
|
|
132
|
+
"share_source": daily_share_source(total_share, float_share, free_float_share),
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def normalize_daily_price_limit_row(
|
|
137
|
+
tdx_code: str,
|
|
138
|
+
*,
|
|
139
|
+
target_trade_date: Any,
|
|
140
|
+
base: Any,
|
|
141
|
+
name: str | None,
|
|
142
|
+
) -> dict[str, Any]:
|
|
143
|
+
base_bar = getattr(base, "bar", None)
|
|
144
|
+
pre_close = round_optional_float(get_value(base_bar, "close")) if base_bar is not None else None
|
|
145
|
+
pre_close_trade_date = bar_trade_date(base_bar) if base_bar is not None else None
|
|
146
|
+
return normalize_daily_price_limit_from_pre_close(
|
|
147
|
+
tdx_code,
|
|
148
|
+
target_trade_date=target_trade_date,
|
|
149
|
+
pre_close_trade_date=pre_close_trade_date,
|
|
150
|
+
pre_close=pre_close,
|
|
151
|
+
pre_close_source="tdx_daily_kline" if pre_close_trade_date else None,
|
|
152
|
+
name=name,
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def normalize_daily_price_limit_snapshot_row(
|
|
157
|
+
tdx_code: str,
|
|
158
|
+
*,
|
|
159
|
+
target_trade_date: Any,
|
|
160
|
+
pre_close_trade_date: Any,
|
|
161
|
+
snapshot: Mapping[str, Any] | None,
|
|
162
|
+
snapshot_base_field: str,
|
|
163
|
+
name: str | None,
|
|
164
|
+
) -> dict[str, Any]:
|
|
165
|
+
if snapshot_base_field == "last_price":
|
|
166
|
+
pre_close = snapshot.get("last_price") if snapshot is not None else None
|
|
167
|
+
else:
|
|
168
|
+
pre_close = snapshot.get("pre_close") if snapshot is not None else None
|
|
169
|
+
return normalize_daily_price_limit_from_pre_close(
|
|
170
|
+
tdx_code,
|
|
171
|
+
target_trade_date=target_trade_date,
|
|
172
|
+
pre_close_trade_date=pre_close_trade_date,
|
|
173
|
+
pre_close=pre_close,
|
|
174
|
+
pre_close_source="tdx_realtime_snapshot" if snapshot is not None else None,
|
|
175
|
+
name=name,
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def normalize_daily_price_limit_from_pre_close(
|
|
180
|
+
tdx_code: str,
|
|
181
|
+
*,
|
|
182
|
+
target_trade_date: Any,
|
|
183
|
+
pre_close_trade_date: Any,
|
|
184
|
+
pre_close: Any,
|
|
185
|
+
pre_close_source: str | None,
|
|
186
|
+
name: str | None,
|
|
187
|
+
) -> dict[str, Any]:
|
|
188
|
+
instrument_id = tdx_code_to_instrument_id(tdx_code)
|
|
189
|
+
symbol = instrument_id.split(".", 1)[0]
|
|
190
|
+
exchange = MARKET_TO_EXCHANGE.get(tdx_code[:2], tdx_code[:2].upper())
|
|
191
|
+
normalized_pre_close = round_optional_float(pre_close)
|
|
192
|
+
normalized_pre_close_trade_date = str(pre_close_trade_date) if pre_close_trade_date not in (None, "") else None
|
|
193
|
+
name_flag = price_limit_name_flag(name)
|
|
194
|
+
limit_ratio = price_limit_ratio_from_rule(tdx_code, name_flag)
|
|
195
|
+
limit_rule = price_limit_rule(tdx_code, name_flag)
|
|
196
|
+
limit_status = "normal"
|
|
197
|
+
if limit_ratio is None:
|
|
198
|
+
limit_up_price = None
|
|
199
|
+
limit_down_price = None
|
|
200
|
+
limit_status = "no_price_limit"
|
|
201
|
+
elif normalized_pre_close in (None, ""):
|
|
202
|
+
limit_up_price = None
|
|
203
|
+
limit_down_price = None
|
|
204
|
+
limit_status = "missing_pre_close"
|
|
205
|
+
else:
|
|
206
|
+
limit_up_price, limit_down_price = rule_price_limits(normalized_pre_close, limit_ratio)
|
|
207
|
+
|
|
208
|
+
return {
|
|
209
|
+
"trade_date": str(target_trade_date) if target_trade_date not in (None, "") else None,
|
|
210
|
+
"instrument_id": instrument_id,
|
|
211
|
+
"symbol": symbol,
|
|
212
|
+
"tdx_code": tdx_code,
|
|
213
|
+
"exchange": exchange,
|
|
214
|
+
"name": name,
|
|
215
|
+
"name_flag": name_flag,
|
|
216
|
+
"pre_close_trade_date": normalized_pre_close_trade_date,
|
|
217
|
+
"pre_close": normalized_pre_close,
|
|
218
|
+
"pre_close_source": pre_close_source if normalized_pre_close not in (None, "") else None,
|
|
219
|
+
"limit_up_price": limit_up_price,
|
|
220
|
+
"limit_down_price": limit_down_price,
|
|
221
|
+
"limit_ratio_pct": limit_ratio,
|
|
222
|
+
"limit_rule": limit_rule,
|
|
223
|
+
"limit_status": limit_status,
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def normalize_limit_ladder_row(
|
|
228
|
+
snapshot: Mapping[str, Any],
|
|
229
|
+
*,
|
|
230
|
+
stats: Any,
|
|
231
|
+
name: str | None,
|
|
232
|
+
boards: set[str] | None,
|
|
233
|
+
include_touched: bool,
|
|
234
|
+
) -> dict[str, Any] | None:
|
|
235
|
+
tdx_code = str(snapshot.get("tdx_code") or "").lower()
|
|
236
|
+
if not tdx_code:
|
|
237
|
+
return None
|
|
238
|
+
board = board_from_tdx_code(tdx_code)
|
|
239
|
+
if not board_matches(board, boards):
|
|
240
|
+
return None
|
|
241
|
+
name_flag = price_limit_name_flag(name)
|
|
242
|
+
if name_flag in {"ST", "*ST"} or st_type_from_name(name):
|
|
243
|
+
return None
|
|
244
|
+
limit_ratio = price_limit_ratio_from_rule(tdx_code, name_flag)
|
|
245
|
+
if limit_ratio is None:
|
|
246
|
+
return None
|
|
247
|
+
limit_up_price, _limit_down_price = rule_price_limits(snapshot.get("pre_close"), limit_ratio)
|
|
248
|
+
last_price = snapshot.get("last_price")
|
|
249
|
+
pre_close = snapshot.get("pre_close")
|
|
250
|
+
if not positive_number(last_price) or not positive_number(pre_close) or not positive_number(limit_up_price):
|
|
251
|
+
return None
|
|
252
|
+
status = limit_ladder_status(snapshot, limit_up_price)
|
|
253
|
+
if status == "none" or (status == "touched" and not include_touched):
|
|
254
|
+
return None
|
|
255
|
+
market_id = MARKET_TO_ID.get(tdx_code[:2], 0)
|
|
256
|
+
stat_row, _stat2_row = stats.row(market_id, tdx_code[2:])
|
|
257
|
+
ladder_level = limit_ladder_level(stat_row, stats.stats_date)
|
|
258
|
+
recent_days = optional_int(get_value(stat_row, "limit_stat_days"))
|
|
259
|
+
recent_count = optional_int(get_value(stat_row, "limit_up_count_in_stat_days"))
|
|
260
|
+
if status == "sealed":
|
|
261
|
+
recent_days, recent_count = today_limit_board_window(recent_days, recent_count, stats.stats_date)
|
|
262
|
+
amount = snapshot.get("amount")
|
|
263
|
+
seal_amount = snapshot.get("locked_amount") if status == "sealed" else None
|
|
264
|
+
free_float_shares = tenk_to_unit(get_value(stat_row, "free_float_shares_10k"))
|
|
265
|
+
free_float_market_value = market_value(free_float_shares, last_price)
|
|
266
|
+
return {
|
|
267
|
+
"ladder_level": ladder_level if status == "sealed" else None,
|
|
268
|
+
"limit_board_text": limit_board_text(recent_days, recent_count),
|
|
269
|
+
"trade_date": limit_ladder_trade_date(stats.stats_date),
|
|
270
|
+
"stats_date": stats.stats_date,
|
|
271
|
+
"rank": snapshot.get("rank"),
|
|
272
|
+
"limit_status": status,
|
|
273
|
+
"instrument_id": snapshot.get("instrument_id"),
|
|
274
|
+
"symbol": snapshot.get("symbol"),
|
|
275
|
+
"tdx_code": tdx_code,
|
|
276
|
+
"exchange": snapshot.get("exchange"),
|
|
277
|
+
"name": name,
|
|
278
|
+
"last_price": last_price,
|
|
279
|
+
"pre_close": snapshot.get("pre_close"),
|
|
280
|
+
"limit_up_price": limit_up_price,
|
|
281
|
+
"change_pct": snapshot.get("change_pct"),
|
|
282
|
+
"high": snapshot.get("high"),
|
|
283
|
+
"amount": amount,
|
|
284
|
+
"seal_amount": seal_amount,
|
|
285
|
+
"free_float_market_value": free_float_market_value,
|
|
286
|
+
"seal_to_amount_ratio": safe_ratio(seal_amount, amount),
|
|
287
|
+
"limit_stat_days": recent_days,
|
|
288
|
+
"limit_up_count_in_stat_days": recent_count,
|
|
289
|
+
"limit_up_streak_days": optional_int(get_value(stat_row, "limit_up_streak_days")),
|
|
290
|
+
"year_limit_up_days": limit_ladder_year_limit_up_days(stat_row, status, stats.stats_date),
|
|
291
|
+
"primary_theme": None,
|
|
292
|
+
"secondary_themes": None,
|
|
293
|
+
"top_theme_names": None,
|
|
294
|
+
"top_themes": [],
|
|
295
|
+
"themes": [],
|
|
296
|
+
"theme_count": 0,
|
|
297
|
+
"theme_strength_score": None,
|
|
298
|
+
"same_theme_limit_up_count": None,
|
|
299
|
+
"same_theme_highest_board": None,
|
|
300
|
+
"same_theme_lianban_count": None,
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
def daily_share_source(total_share: Any, float_share: Any, free_float_share: Any) -> str:
|
|
305
|
+
sources = []
|
|
306
|
+
if total_share not in (None, "") or float_share not in (None, ""):
|
|
307
|
+
sources.append("finance_snapshot")
|
|
308
|
+
if free_float_share not in (None, ""):
|
|
309
|
+
sources.append("tdxstat")
|
|
310
|
+
return "+".join(sources) if sources else "empty"
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
def limit_ladder_rank_page_below_threshold(rows: Sequence[Mapping[str, Any]], threshold: float) -> bool:
|
|
314
|
+
if not rows:
|
|
315
|
+
return True
|
|
316
|
+
return all(not numeric_at_or_above(row.get("change_pct"), threshold) for row in rows)
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
def limit_ladder_candidate(row: Mapping[str, Any], *, include_touched: bool) -> bool:
|
|
320
|
+
change_pct = row.get("change_pct")
|
|
321
|
+
high_change_pct = row.get("high_change_pct")
|
|
322
|
+
threshold = 4.5 if include_touched else 8.5
|
|
323
|
+
for value in (change_pct, high_change_pct):
|
|
324
|
+
try:
|
|
325
|
+
if value is not None and float(value) >= threshold:
|
|
326
|
+
return True
|
|
327
|
+
except (TypeError, ValueError):
|
|
328
|
+
continue
|
|
329
|
+
return False
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
def limit_ladder_needs_name_lookup(
|
|
333
|
+
snapshot: Mapping[str, Any],
|
|
334
|
+
*,
|
|
335
|
+
boards: set[str] | None,
|
|
336
|
+
include_touched: bool,
|
|
337
|
+
) -> bool:
|
|
338
|
+
tdx_code = str(snapshot.get("tdx_code") or "").lower()
|
|
339
|
+
if not tdx_code:
|
|
340
|
+
return False
|
|
341
|
+
if not board_matches(board_from_tdx_code(tdx_code), boards):
|
|
342
|
+
return False
|
|
343
|
+
if not limit_ladder_candidate(snapshot, include_touched=include_touched):
|
|
344
|
+
return False
|
|
345
|
+
limit_ratio = price_limit_ratio_from_rule(tdx_code, None)
|
|
346
|
+
if limit_ratio is None:
|
|
347
|
+
return False
|
|
348
|
+
limit_up_price, _limit_down_price = rule_price_limits(snapshot.get("pre_close"), limit_ratio)
|
|
349
|
+
if not positive_number(snapshot.get("last_price")) or not positive_number(snapshot.get("pre_close")):
|
|
350
|
+
return False
|
|
351
|
+
if not positive_number(limit_up_price):
|
|
352
|
+
return False
|
|
353
|
+
status = limit_ladder_status(snapshot, limit_up_price)
|
|
354
|
+
return status != "none" and (include_touched or status != "touched")
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
def numeric_at_or_above(value: Any, threshold: float) -> bool:
|
|
358
|
+
try:
|
|
359
|
+
return value is not None and float(value) >= threshold
|
|
360
|
+
except (TypeError, ValueError):
|
|
361
|
+
return False
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
_LIMIT_LADDER_PUBLIC_FIELDS = (
|
|
365
|
+
"trade_date",
|
|
366
|
+
"ladder_level",
|
|
367
|
+
"limit_board_text",
|
|
368
|
+
"instrument_id",
|
|
369
|
+
"name",
|
|
370
|
+
"last_price",
|
|
371
|
+
"change_pct",
|
|
372
|
+
"limit_status",
|
|
373
|
+
"amount",
|
|
374
|
+
"seal_amount",
|
|
375
|
+
"seal_to_amount_ratio",
|
|
376
|
+
"free_float_market_value",
|
|
377
|
+
"primary_theme",
|
|
378
|
+
"secondary_themes",
|
|
379
|
+
"year_limit_up_days",
|
|
380
|
+
"symbol",
|
|
381
|
+
"exchange",
|
|
382
|
+
"pre_close",
|
|
383
|
+
"limit_up_price",
|
|
384
|
+
)
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
def limit_ladder_public_row(row: Mapping[str, Any]) -> dict[str, Any]:
|
|
388
|
+
return {field: row.get(field) for field in _LIMIT_LADDER_PUBLIC_FIELDS}
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
def limit_ladder_trade_date(stats_date: Any) -> str:
|
|
392
|
+
return default_daily_price_limit_trade_date(stats_date)
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
def stats_date_is_today(stats_date: Any) -> bool:
|
|
396
|
+
stats_text = str(stats_date or "").strip()
|
|
397
|
+
return bool(stats_text) and stats_text == datetime.now().strftime("%Y%m%d")
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
def limit_ladder_level(stat_row: Any | None, stats_date: Any) -> int:
|
|
401
|
+
prior_streak = optional_int(get_value(stat_row, "limit_up_streak_days")) or 0
|
|
402
|
+
if stats_date_is_today(stats_date):
|
|
403
|
+
return max(1, prior_streak)
|
|
404
|
+
return max(1, prior_streak + 1)
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
def today_limit_board_window(days: int | None, count: int | None, stats_date: Any) -> tuple[int | None, int | None]:
|
|
408
|
+
if stats_date_is_today(stats_date):
|
|
409
|
+
return days, count
|
|
410
|
+
next_days = (days or 0) + 1
|
|
411
|
+
next_count = (count or 0) + 1
|
|
412
|
+
return next_days if next_days > 0 else None, next_count if next_count > 0 else None
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
def limit_ladder_year_limit_up_days(stat_row: Any | None, status: str, stats_date: Any) -> int | None:
|
|
416
|
+
value = optional_int(get_value(stat_row, "year_limit_up_days"))
|
|
417
|
+
if value is None:
|
|
418
|
+
return None
|
|
419
|
+
if status == "sealed" and not stats_date_is_today(stats_date):
|
|
420
|
+
return value + 1
|
|
421
|
+
return value
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
def limit_ladder_topic_type(value: Any) -> str:
|
|
425
|
+
text = str(value or "theme").strip().lower()
|
|
426
|
+
if text in {"theme", "主题", "题材"}:
|
|
427
|
+
return "theme"
|
|
428
|
+
if text in {"sector", "板块"}:
|
|
429
|
+
return "sector"
|
|
430
|
+
raise SourceRequestValidationError("topic_type must be theme or sector")
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
def limit_ladder_count_param(value: Any) -> int | None:
|
|
434
|
+
if value in (None, ""):
|
|
435
|
+
return None
|
|
436
|
+
if isinstance(value, str) and value.strip().lower() == "all":
|
|
437
|
+
return None
|
|
438
|
+
try:
|
|
439
|
+
parsed = int(value)
|
|
440
|
+
except (TypeError, ValueError) as exc:
|
|
441
|
+
raise SourceRequestValidationError("count must be an integer or 'all'") from exc
|
|
442
|
+
if parsed < 1:
|
|
443
|
+
raise SourceRequestValidationError("count must be >= 1")
|
|
444
|
+
if parsed > 500:
|
|
445
|
+
raise SourceRequestValidationError("count must be <= 500")
|
|
446
|
+
return parsed
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
def normalize_order_book_rows(quote: Any) -> list[dict[str, Any]]:
|
|
450
|
+
tdx_code = quote_tdx_code(quote)
|
|
451
|
+
instrument_id = tdx_code_to_instrument_id(tdx_code)
|
|
452
|
+
symbol = instrument_id.split(".", 1)[0]
|
|
453
|
+
exchange = MARKET_TO_EXCHANGE.get(tdx_code[:2], str(get_value(quote, "exchange") or "").upper())
|
|
454
|
+
bid_levels = list(get_value(quote, "bid_levels", ()) or ())
|
|
455
|
+
ask_levels = list(get_value(quote, "ask_levels", ()) or ())
|
|
456
|
+
|
|
457
|
+
rows: list[dict[str, Any]] = []
|
|
458
|
+
for index in range(5):
|
|
459
|
+
bid_level = quote_level_at(bid_levels, index)
|
|
460
|
+
ask_level = quote_level_at(ask_levels, index)
|
|
461
|
+
rows.append(
|
|
462
|
+
{
|
|
463
|
+
"instrument_id": instrument_id,
|
|
464
|
+
"symbol": symbol,
|
|
465
|
+
"tdx_code": tdx_code,
|
|
466
|
+
"exchange": exchange,
|
|
467
|
+
"level": index + 1,
|
|
468
|
+
"bid_price": round_optional_float(get_value(bid_level, "price")) if bid_level is not None else None,
|
|
469
|
+
"bid_volume": optional_int(get_value(bid_level, "volume")) if bid_level is not None else None,
|
|
470
|
+
"ask_price": round_optional_float(get_value(ask_level, "price")) if ask_level is not None else None,
|
|
471
|
+
"ask_volume": optional_int(get_value(ask_level, "volume")) if ask_level is not None else None,
|
|
472
|
+
}
|
|
473
|
+
)
|
|
474
|
+
return rows
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
def normalize_adj_factor_row(tdx_code: str, trade_date: str, adj_factor: float) -> dict[str, Any]:
|
|
478
|
+
instrument_id = tdx_code_to_instrument_id(tdx_code)
|
|
479
|
+
return {
|
|
480
|
+
"instrument_id": instrument_id,
|
|
481
|
+
"ts_code": instrument_id,
|
|
482
|
+
"symbol": instrument_id.split(".", 1)[0],
|
|
483
|
+
"tdx_code": tdx_code,
|
|
484
|
+
"exchange": MARKET_TO_EXCHANGE.get(tdx_code[:2], tdx_code[:2].upper()),
|
|
485
|
+
"trade_date": trade_date,
|
|
486
|
+
"adj_factor": round(float(adj_factor), 10),
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
def default_daily_price_limit_trade_date(stats_date: Any) -> str | None:
|
|
491
|
+
stats_text = str(stats_date or "").strip()
|
|
492
|
+
today_text = datetime.now().strftime("%Y%m%d")
|
|
493
|
+
weekday = datetime.now().weekday()
|
|
494
|
+
if weekday < 5 and (not stats_text or today_text > stats_text):
|
|
495
|
+
return today_text
|
|
496
|
+
return stats_text or today_text
|
|
497
|
+
|
|
498
|
+
|
|
499
|
+
def before_daily_close_buffer(value: datetime) -> bool:
|
|
500
|
+
return value.hour < 15 or (value.hour == 15 and value.minute < 30)
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"""TDX downloader adapter factory owned by the provider package."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Callable
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any, Protocol
|
|
8
|
+
|
|
9
|
+
from .downloader_interface_sets import (
|
|
10
|
+
DOWNLOADER_F10_TOPIC_PREFILL_INTERFACES,
|
|
11
|
+
DOWNLOADER_PARALLEL_SUSPENSION_INTERFACES,
|
|
12
|
+
DOWNLOADER_RUNTIME_SOURCE_SERVER_MAX_INTERFACES,
|
|
13
|
+
DOWNLOADER_STATS_RESOURCE_INTERFACES,
|
|
14
|
+
)
|
|
15
|
+
from .server_cache import tdx_server_cache_root, tdx_stats_cache_root
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class _SourceRequestAdapterLike(Protocol):
|
|
19
|
+
source: str
|
|
20
|
+
|
|
21
|
+
def supports(self, interface_name: str) -> bool: ...
|
|
22
|
+
|
|
23
|
+
def request(self, interface_name: str, params: dict[str, Any]) -> list[dict[str, Any]]: ...
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def create_tdx_client(**kwargs: Any) -> Any:
|
|
27
|
+
"""Create a TDX client through the regular factory lazily."""
|
|
28
|
+
|
|
29
|
+
from .client_factory import create_tdx_client as create_client
|
|
30
|
+
|
|
31
|
+
return create_client(**kwargs)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def effective_host_strings(kind: str, *, cache_root: str | None = None) -> list[str]:
|
|
35
|
+
"""Resolve configured TDX quote hosts lazily."""
|
|
36
|
+
|
|
37
|
+
from .host_config import effective_host_strings as resolve_hosts
|
|
38
|
+
|
|
39
|
+
return list(resolve_hosts(kind, cache_root=cache_root))
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def create_tdx_download_adapter(
|
|
43
|
+
*,
|
|
44
|
+
interface_name: str,
|
|
45
|
+
source_server_count: int,
|
|
46
|
+
pool_size: int,
|
|
47
|
+
data_root: str | Path | None = None,
|
|
48
|
+
progress_callback: Callable[..., None] | None = None,
|
|
49
|
+
) -> tuple[_SourceRequestAdapterLike, Any]:
|
|
50
|
+
"""Create the long-connection adapter used by TDX downloader runs."""
|
|
51
|
+
|
|
52
|
+
server_cache_root = tdx_server_cache_root(data_root)
|
|
53
|
+
stats_cache_root = tdx_stats_cache_root(data_root)
|
|
54
|
+
hosts = effective_host_strings("quote", cache_root=server_cache_root)[: max(1, source_server_count)]
|
|
55
|
+
client = create_tdx_client(
|
|
56
|
+
hosts=hosts or None,
|
|
57
|
+
pool_size=max(1, pool_size),
|
|
58
|
+
heartbeat_interval=None,
|
|
59
|
+
)
|
|
60
|
+
adapter_cls = _tdx_request_adapter_class()
|
|
61
|
+
return (
|
|
62
|
+
adapter_cls(
|
|
63
|
+
client=client,
|
|
64
|
+
progress_callback=progress_callback,
|
|
65
|
+
use_parallel_suspension_quotes=interface_name in DOWNLOADER_PARALLEL_SUSPENSION_INTERFACES,
|
|
66
|
+
options=tdx_adapter_options(
|
|
67
|
+
interface_name=interface_name,
|
|
68
|
+
pool_size=pool_size,
|
|
69
|
+
server_cache_root=server_cache_root,
|
|
70
|
+
stats_cache_root=stats_cache_root,
|
|
71
|
+
),
|
|
72
|
+
),
|
|
73
|
+
client,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def tdx_adapter_options(
|
|
78
|
+
*,
|
|
79
|
+
interface_name: str,
|
|
80
|
+
pool_size: int,
|
|
81
|
+
server_cache_root: str | None = None,
|
|
82
|
+
stats_cache_root: str | None = None,
|
|
83
|
+
) -> dict[str, Any] | None:
|
|
84
|
+
"""Return request adapter options for a TDX downloader interface."""
|
|
85
|
+
|
|
86
|
+
options: dict[str, Any] = {}
|
|
87
|
+
if server_cache_root is not None:
|
|
88
|
+
options["server_cache_root"] = server_cache_root
|
|
89
|
+
if stats_cache_root is not None and interface_name in DOWNLOADER_STATS_RESOURCE_INTERFACES:
|
|
90
|
+
options["stats_cache_root"] = stats_cache_root
|
|
91
|
+
if interface_name in DOWNLOADER_F10_TOPIC_PREFILL_INTERFACES:
|
|
92
|
+
options.update(
|
|
93
|
+
{
|
|
94
|
+
"f10_topic_workers": 6,
|
|
95
|
+
"f10_topic_refill_workers": 6,
|
|
96
|
+
"f10_topic_refill_rounds": 1,
|
|
97
|
+
}
|
|
98
|
+
)
|
|
99
|
+
return options or None
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def tdx_runtime_source_server_max(
|
|
103
|
+
interface_name: str,
|
|
104
|
+
*,
|
|
105
|
+
configured_max: int,
|
|
106
|
+
source_server_count_editable: bool,
|
|
107
|
+
data_root: str | Path | None = None,
|
|
108
|
+
) -> int:
|
|
109
|
+
"""Return the runtime source-server cap for TDX downloader concurrency."""
|
|
110
|
+
|
|
111
|
+
resolved_configured_max = max(1, int(configured_max))
|
|
112
|
+
if (
|
|
113
|
+
interface_name not in DOWNLOADER_RUNTIME_SOURCE_SERVER_MAX_INTERFACES
|
|
114
|
+
or not source_server_count_editable
|
|
115
|
+
):
|
|
116
|
+
return resolved_configured_max
|
|
117
|
+
try:
|
|
118
|
+
available_count = len(effective_host_strings("quote", cache_root=tdx_server_cache_root(data_root)))
|
|
119
|
+
except Exception:
|
|
120
|
+
available_count = 0
|
|
121
|
+
if available_count <= 0:
|
|
122
|
+
return resolved_configured_max
|
|
123
|
+
return max(1, min(resolved_configured_max, available_count))
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def _tdx_request_adapter_class() -> Any:
|
|
127
|
+
adapter_cls = globals().get("TdxRequestAdapter")
|
|
128
|
+
if adapter_cls is not None:
|
|
129
|
+
return adapter_cls
|
|
130
|
+
from .request_adapter import TdxRequestAdapter as loaded_adapter_cls
|
|
131
|
+
|
|
132
|
+
globals()["TdxRequestAdapter"] = loaded_adapter_cls
|
|
133
|
+
return loaded_adapter_cls
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def __getattr__(name: str) -> Any:
|
|
137
|
+
if name == "TdxRequestAdapter":
|
|
138
|
+
return _tdx_request_adapter_class()
|
|
139
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""TDX downloader interface groups used by adapter factory rules."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
DOWNLOADER_PARALLEL_SUSPENSION_INTERFACES = frozenset({"stock_suspensions_tdx"})
|
|
7
|
+
|
|
8
|
+
DOWNLOADER_STATS_RESOURCE_INTERFACES = frozenset(
|
|
9
|
+
{
|
|
10
|
+
"stock_daily_share_tdx",
|
|
11
|
+
"stock_limit_ladder_tdx",
|
|
12
|
+
"stock_theme_strength_rank_tdx",
|
|
13
|
+
"stock_shortline_indicators_tdx",
|
|
14
|
+
}
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
DOWNLOADER_F10_TOPIC_PREFILL_INTERFACES = frozenset(
|
|
18
|
+
{
|
|
19
|
+
"stock_limit_ladder_tdx",
|
|
20
|
+
"stock_theme_strength_rank_tdx",
|
|
21
|
+
}
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
DOWNLOADER_RUNTIME_SOURCE_SERVER_MAX_INTERFACES = frozenset(
|
|
25
|
+
{
|
|
26
|
+
"stock_daily_share_tdx",
|
|
27
|
+
"stock_daily_price_limit_tdx",
|
|
28
|
+
}
|
|
29
|
+
)
|