cfquant 0.2.64__tar.gz → 0.2.66__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {cfquant-0.2.64 → cfquant-0.2.66}/PKG-INFO +1 -1
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/__init__.py +17 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/_editable_install.py +9 -1
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/algo.py +2 -2
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/algo_broker.py +3 -1
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/batch_orders.py +10 -2
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/callback_dispatcher.py +5 -5
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/client.py +38 -4
- cfquant-0.2.66/cfquant/diagnostic_context.py +35 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/flow_control.py +2 -2
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/hub.py +2 -1
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/management.py +3 -1
- cfquant-0.2.66/cfquant/monitor_notifications.py +593 -0
- cfquant-0.2.66/cfquant/monitor_performance.py +81 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/normal_bridge.py +145 -10
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/pipe_bridge.py +1 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/pipe_hub.py +132 -9
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/pipe_transport.py +14 -1
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/protocol.py +10 -5
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/qmt_bridge.py +5 -1
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/qmt_component_login.py +19 -13
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/qmt_dependencies.py +3 -1
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/qmt_inner.py +47 -14
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/qmt_native_worker.py +25 -13
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/qmt_pipe_runtime.py +4 -2
- cfquant-0.2.66/cfquant/qmt_python_runtime.py +641 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/qmt_strategy_deploy.py +89 -4
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/qmt_strategy_recovery.py +9 -5
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/qmt_strategy_runtime.py +14 -2
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/qmt_update.py +3 -1
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/quote_service.py +20 -2
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/remote_management.py +3 -1
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/shared_quotes.py +16 -3
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/smart_orders.py +2 -2
- cfquant-0.2.66/cfquant/subscription_bridge.py +167 -0
- cfquant-0.2.66/cfquant/subscription_client.py +190 -0
- cfquant-0.2.66/cfquant/subscription_manager.py +214 -0
- cfquant-0.2.66/cfquant/subscription_store.py +181 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/13_/347/256/241/351/201/223/344/270/255/345/277/203/350/277/236/346/216/245/346/265/213/350/257/225.py +42 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/24_/350/264/246/346/210/267/347/274/223/345/255/230/346/214/211/351/234/200/345/210/267/346/226/260/346/265/213/350/257/225.py +23 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/26_/345/205/250/346/216/250/350/256/242/351/230/205/346/265/213/350/257/225.py +58 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/28_Level2/346/216/245/345/217/243/351/200/202/351/205/215/346/265/213/350/257/225.py +5 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_account_query_timing.py +40 -0
- cfquant-0.2.66/cfquant/tests/test_activity_log_browser.py +256 -0
- cfquant-0.2.66/cfquant/tests/test_auto_login_app.py +166 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_cftrader_tutorial.py +7 -29
- cfquant-0.2.66/cfquant/tests/test_diagnostic_context.py +30 -0
- cfquant-0.2.66/cfquant/tests/test_monitor_notifications.py +63 -0
- cfquant-0.2.66/cfquant/tests/test_monitor_performance.py +49 -0
- cfquant-0.2.66/cfquant/tests/test_navigation_browser.py +96 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_qmt_binding_settings.py +2 -1
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_qmt_inner.py +18 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_qmt_inner_web.py +37 -0
- cfquant-0.2.66/cfquant/tests/test_qmt_package_import.py +86 -0
- cfquant-0.2.66/cfquant/tests/test_qmt_python_runtime.py +133 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_qmt_strategy_deploy.py +19 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_quote_service.py +77 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_smart_orders_docs.py +10 -9
- cfquant-0.2.66/cfquant/tests/test_subscription_persistence.py +107 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_trading_monitor.py +96 -1
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_trading_monitor_browser.py +40 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_tx_shutdown.py +2 -1
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/trading_monitor.py +203 -7
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tx_trade_bridge.py +7 -2
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/version.py +2 -2
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/xttrader.py +20 -8
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant.egg-info/PKG-INFO +1 -1
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant.egg-info/SOURCES.txt +20 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant_web_server.py +572 -13
- {cfquant-0.2.64 → cfquant-0.2.66}/pyproject.toml +1 -1
- {cfquant-0.2.64 → cfquant-0.2.66}/qmt_scripts/CFQUANT_LITE.py +420 -30
- {cfquant-0.2.64 → cfquant-0.2.66}/qmt_scripts//345/220/214/350/264/246/345/217/267/347/213/254/347/253/213/345/270/202/345/234/272/CFQUANT_LITE_SH.py +420 -30
- {cfquant-0.2.64 → cfquant-0.2.66}/qmt_scripts//345/220/214/350/264/246/345/217/267/347/213/254/347/253/213/345/270/202/345/234/272/CFQUANT_LITE_SZ.py +420 -30
- cfquant-0.2.66/web_dashboard/activity-log.js +162 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/web_dashboard/app.js +498 -89
- cfquant-0.2.66/web_dashboard/cfquant-architecture.html +14837 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/web_dashboard/index.html +161 -114
- cfquant-0.2.66/web_dashboard/navigation.js +151 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/web_dashboard/python-api.js +6 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/web_dashboard/qmt-inner-api-data.js +123 -105
- {cfquant-0.2.64 → cfquant-0.2.66}/web_dashboard/styles.css +791 -23
- cfquant-0.2.66/web_dashboard/trading-monitor.js +718 -0
- cfquant-0.2.64/web_dashboard/trading-monitor.js +0 -282
- {cfquant-0.2.64 → cfquant-0.2.66}/LICENSE +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/LTtx/__init__.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/LTtx/tx/Config.txt +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/LTtx/tx/LTtx_server.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/LTtx/tx/__init__.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/LTtx/tx/tx.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/MANIFEST.in +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/README.md +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/account_routing.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/build_info.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/cftrader.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/channels.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/cli.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/conditional_orders.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/config.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/diagnostics.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/level2.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/log_management.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/logging_i18n.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/order_identity.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/order_meta.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/pipe_client.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/planning_broker.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/position_sizing.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/qmt_binding_defaults.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/qmt_binding_settings.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/qmt_component_agent.js +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/qmt_credentials.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/qmt_inner_catalog.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/qmt_inner_runtime.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/qmt_native_files.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/qmt_strategy_agent.js +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/qmt_strategy_package.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/quote_history.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/quote_leases.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/remote_adapter.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/remote_supervisor.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/rpc_timing.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/runtime_report.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/stock_connect.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/10_/345/221/275/344/273/244/350/241/214/346/216/245/345/217/243/346/265/213/350/257/225.py" +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/11_/346/231/256/351/200/232/346/241/245/346/216/245/346/264/276/345/217/221/346/265/213/350/257/225.py" +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/12_/350/256/242/345/215/225/345/244/207/346/263/250/346/265/213/350/257/225.py" +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/14_/350/277/220/350/241/214/346/227/266/346/212/245/345/221/212/346/265/213/350/257/225.py" +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/15_/347/275/221/351/241/265/347/212/266/346/200/201/347/274/223/345/255/230/346/265/213/350/257/225.py" +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/16_/345/220/257/345/212/250/345/256/211/350/243/205/346/265/213/350/257/225.py" +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/17_/345/244/247QMT/346/216/245/345/217/243/351/200/202/351/205/215/346/265/213/350/257/225.py" +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/18_/351/253/230/347/272/247/346/250/241/345/274/217/345/256/236/346/234/272/350/201/224/350/260/203.py" +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/19_/345/256/236/346/234/272/350/201/224/350/260/203/345/210/244/345/256/232/346/265/213/350/257/225.py" +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/1_/350/241/214/346/203/205/346/216/245/346/224/266/346/265/213/350/257/225.py" +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/20_/350/201/224/350/260/203/351/227/256/351/242/230/344/277/256/345/244/215/346/265/213/350/257/225.py" +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/21_/345/215/225/350/202/241/350/241/214/346/203/205/350/256/242/351/230/205/346/265/213/350/257/225.py" +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/22_/350/277/236/346/216/245/350/257/212/346/226/255/346/265/213/350/257/225.py" +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/23_/347/244/272/344/276/213/351/205/215/347/275/256/346/265/213/350/257/225.py" +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/25_/350/264/246/346/210/267/347/274/223/345/255/230/351/241/265/351/235/242/346/265/213/350/257/225.py" +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/27_/345/205/250/346/216/250/350/256/242/351/230/205/351/241/265/351/235/242/346/265/213/350/257/225.py" +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/29_Level2/350/241/214/346/203/205/346/265/213/350/257/225.py" +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/2_/346/225/260/346/215/256/350/216/267/345/217/226/346/265/213/350/257/225.py" +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/30_cftrader/346/211/271/351/207/217/344/270/213/345/215/225/346/200/247/350/203/275/345/237/272/345/207/206.py" +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/31_cftrader/346/250/241/346/213/237/350/264/246/345/217/267/346/211/271/351/207/217/344/270/213/345/215/225/346/265/213/350/257/225.py" +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/32_/345/274/202/346/255/245/344/270/213/345/215/225/346/265/213/350/257/225.py" +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/33_/345/220/214/346/255/245/344/270/213/345/215/225/345/233/236/350/260/203/346/265/213/350/257/225.py" +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/34_/345/205/250/346/216/250/345/206/205/351/203/250/345/273/266/350/277/237/346/265/213/350/257/225.py" +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/3_/346/225/260/346/215/256/344/270/213/350/275/275/346/265/213/350/257/225.py" +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/4_/344/272/244/346/230/223/345/247/224/346/211/230/346/237/245/350/257/242/346/265/213/350/257/225.py" +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/5_/347/234/237/345/256/236/344/270/213/345/215/225/346/265/213/350/257/225.py" +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/6_/345/233/236/350/260/203/346/265/213/350/257/225.py" +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/7_/345/220/214/346/255/245/345/274/202/346/255/245/344/270/213/345/215/225/346/265/213/350/257/225.py" +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/8_/346/210/220/344/272/244/350/256/242/345/215/225/345/222/214/345/247/224/346/211/230/350/256/242/345/215/225/346/237/245/350/257/242/346/265/213/350/257/225.py" +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/9_/345/217/257/346/222/244/350/256/242/345/215/225/346/265/213/350/257/225.py" +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/_helpers.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/readme.md +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_account_subscription_probe.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_advanced_parity.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_algo.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_algo_broker.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_api_tester_browser.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_api_tester_web.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_binding_delete_confirmation.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_binding_live_refresh_browser.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_binding_runtime_browser.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_callback_recovery.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_callback_requests.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_cftrader.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_cftrader_web.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_credit_compat.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_dataframe_encoding.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_json_serialization.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_latency_flow_control.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_lite_account_routing.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_lite_deployment_paths.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_lite_parity.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_lite_qmt_dispatch.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_local_trading_calendar.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_log_management.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_management.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_management_batch.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_market_data_fallback.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_market_timing.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_order_identity.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_order_time_callback.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_pipe_shutdown.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_project_updater.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_python_api_reference.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_qmt_auto_login_browser.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_qmt_auto_login_integration.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_qmt_binding_defaults.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_qmt_component_login.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_qmt_dependencies.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_qmt_fileless.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_qmt_inner_reference.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_qmt_native_files.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_qmt_native_reuse.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_qmt_native_worker.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_qmt_strategy_browser.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_qmt_strategy_capabilities.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_qmt_strategy_recovery.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_qmt_strategy_web.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_qmt_update.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_quote_latency.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_quote_latency_encoding.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_remote_management.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_remote_management_browser.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_remote_version_check.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_setup_qmt_prompt.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_shared_quotes.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_shared_quotes_generated.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_smart_orders.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_smart_orders_api.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_startup_recovery.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_stock_connect.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_stock_connect_browser.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_strategy_recovery_api.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_subscription_congestion.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_table_conversion.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_trade_strategy_callback.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_trader_subscription_recovery.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_trading_dates.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_tutorial_reader.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tests/test_tx_auto_import.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/trading_calendar.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/tx.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/xtconstant.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/xtdata.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant/xttype.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant.egg-info/dependency_links.txt +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant.egg-info/entry_points.txt +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant.egg-info/requires.txt +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant.egg-info/top_level.txt +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/cfquant_pipe_hub.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/qmt_scripts/CFQUANT.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/qmt_scripts/CFQUANT_CTYPE_ALL_LOWLAT.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/qmt_scripts/CFQUANT_TRADE_LOWLAT.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/qmt_scripts/__init__.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/qmt_scripts//345/220/214/350/264/246/345/217/267/347/213/254/347/253/213/345/270/202/345/234/272/CFQUANT_CTYPE_ALL_LOWLAT_SH.py" +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/qmt_scripts//345/220/214/350/264/246/345/217/267/347/213/254/347/253/213/345/270/202/345/234/272/CFQUANT_CTYPE_ALL_LOWLAT_SZ.py" +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/qmt_scripts//345/220/214/350/264/246/345/217/267/347/213/254/347/253/213/345/270/202/345/234/272/CFQUANT_TRADE_LOWLAT_SH.py" +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/qmt_scripts//345/220/214/350/264/246/345/217/267/347/213/254/347/253/213/345/270/202/345/234/272/CFQUANT_TRADE_LOWLAT_SZ.py" +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/qmt_scripts//345/220/214/350/264/246/345/217/267/347/213/254/347/253/213/345/270/202/345/234/272/readme.md" +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/requirements.txt +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/setup.cfg +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/web_dashboard/__init__.py +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/web_dashboard/api-tester.js +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/web_dashboard/avatars/amber-pulse.svg +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/web_dashboard/avatars/copper-grid.svg +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/web_dashboard/avatars/market-blue.svg +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/web_dashboard/avatars/rose-circuit.svg +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/web_dashboard/avatars/signal-green.svg +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/web_dashboard/avatars/slate-wave.svg +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/web_dashboard/avatars/teal-orbit.svg +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/web_dashboard/avatars/violet-node.svg +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/web_dashboard/cftrader-api-data.js +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/web_dashboard/docs.js +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/web_dashboard/favicon.svg +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/web_dashboard/image/qmt_deploy/image-10.png +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/web_dashboard/image/qmt_deploy/image-11.png +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/web_dashboard/image/qmt_deploy/image-12.png +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/web_dashboard/image/qmt_deploy/image-13.png +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/web_dashboard/image/qmt_deploy/image-14.png +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/web_dashboard/image/qmt_deploy/image-15.png +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/web_dashboard/image/qmt_deploy/image-16.png +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/web_dashboard/image/qmt_deploy/image-17.png +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/web_dashboard/image/qmt_deploy/image-18.png +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/web_dashboard/image/qmt_deploy/image-19.png +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/web_dashboard/image/qmt_deploy/image-20.png +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/web_dashboard/image/qmt_deploy/image-21.png +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/web_dashboard/image/qmt_deploy/image-4.png +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/web_dashboard/image/qmt_deploy/image-5.png +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/web_dashboard/image/qmt_deploy/image-6.png +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/web_dashboard/image/qmt_deploy/image-7.png +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/web_dashboard/image/qmt_deploy/image-8.png +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/web_dashboard/image/qmt_deploy/image-9.png +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/web_dashboard/python-api-data.js +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/web_dashboard/remote-management.js +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/web_dashboard/smart-order-api-data.js +0 -0
- {cfquant-0.2.64 → cfquant-0.2.66}/web_dashboard/vendor/mermaid.min.js +0 -0
|
@@ -9,6 +9,8 @@ cfquant: 面向 QMT 的 xtquant 兼容层。
|
|
|
9
9
|
from cfquant.xttype import StockAccount
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
|
+
import sys as _sys
|
|
13
|
+
|
|
12
14
|
from . import xtconstant, xtdata, xttrader, xttype
|
|
13
15
|
from . import cftrader
|
|
14
16
|
from .qmt_inner import QmtInnerApi
|
|
@@ -39,3 +41,18 @@ __all__ = [
|
|
|
39
41
|
"get_git_commit",
|
|
40
42
|
"build_identity",
|
|
41
43
|
]
|
|
44
|
+
|
|
45
|
+
# QMT's embedded Python 3.6 imports this package before loading any bridge
|
|
46
|
+
# submodule. The external runtime manager needs Python 3.7 syntax/dataclasses
|
|
47
|
+
# and must not be imported (or exported by import *) inside that interpreter.
|
|
48
|
+
if _sys.version_info >= (3, 7):
|
|
49
|
+
from .qmt_python_runtime import (
|
|
50
|
+
QmtPythonRuntime, QmtPythonRuntimeError, QmtPythonPackage, QmtPythonProgress,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
__all__.extend([
|
|
54
|
+
"QmtPythonRuntime",
|
|
55
|
+
"QmtPythonRuntimeError",
|
|
56
|
+
"QmtPythonPackage",
|
|
57
|
+
"QmtPythonProgress",
|
|
58
|
+
])
|
|
@@ -12,7 +12,15 @@ from pathlib import Path
|
|
|
12
12
|
if __package__:
|
|
13
13
|
from .qmt_dependencies import FRIDA_REQUIREMENT, FRIDA_VERSION, report_startup_dependencies
|
|
14
14
|
else:
|
|
15
|
-
|
|
15
|
+
# The Windows launcher executes this file by path (``python
|
|
16
|
+
# cfquant\\_editable_install.py``), so Python puts the package directory on
|
|
17
|
+
# sys.path and leaves ``__package__`` empty. Import through the package in
|
|
18
|
+
# that mode as well; qmt_dependencies uses relative imports and cannot be
|
|
19
|
+
# loaded as a top-level module.
|
|
20
|
+
_PROJECT_ROOT = Path(__file__).resolve().parent.parent
|
|
21
|
+
if str(_PROJECT_ROOT) not in sys.path:
|
|
22
|
+
sys.path.insert(0, str(_PROJECT_ROOT))
|
|
23
|
+
from cfquant.qmt_dependencies import FRIDA_REQUIREMENT, FRIDA_VERSION, report_startup_dependencies
|
|
16
24
|
|
|
17
25
|
|
|
18
26
|
PACKAGE_NAME = "cfquant"
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import copy
|
|
3
3
|
import datetime as dt
|
|
4
4
|
import json
|
|
5
|
-
import
|
|
5
|
+
from .diagnostic_context import get_logger
|
|
6
6
|
import math
|
|
7
7
|
import os
|
|
8
8
|
import sqlite3
|
|
@@ -488,7 +488,7 @@ class AlgoEngine:
|
|
|
488
488
|
except Exception:
|
|
489
489
|
# Persistence is a prerequisite for further order submission.
|
|
490
490
|
task["status"] = "PAUSED"
|
|
491
|
-
|
|
491
|
+
get_logger(__name__).exception("algorithm persistence failed")
|
|
492
492
|
finally:
|
|
493
493
|
with self._lock:
|
|
494
494
|
self._busy.discard(algo_id)
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
"""Existing cfquant account/market routes adapted to the execution engine."""
|
|
2
|
+
|
|
3
|
+
from .diagnostic_context import VersionedErrorMixin
|
|
2
4
|
import datetime as dt
|
|
3
5
|
import math
|
|
4
6
|
import threading
|
|
@@ -10,7 +12,7 @@ from .xttype import XtAsset, XtOrder, XtPosition
|
|
|
10
12
|
from .batch_orders import prepare_batch_orders, prepare_batch_cancels
|
|
11
13
|
|
|
12
14
|
|
|
13
|
-
class AlgoOrderRejected(RuntimeError):
|
|
15
|
+
class AlgoOrderRejected(VersionedErrorMixin, RuntimeError):
|
|
14
16
|
dispatched = False
|
|
15
17
|
|
|
16
18
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import math
|
|
4
4
|
import os
|
|
5
|
+
import re
|
|
5
6
|
import time
|
|
6
7
|
from collections.abc import Mapping
|
|
7
8
|
from numbers import Integral, Real
|
|
@@ -19,6 +20,13 @@ _BATCH_ORDER_FIELDS = {"stock_code", "order_type", "order_volume", "price_type",
|
|
|
19
20
|
"strategy_name", "order_remark", "cfquant_order_remark"}
|
|
20
21
|
_BATCH_REQUIRED_FIELDS = {"stock_code", "order_type", "order_volume", "price_type", "price"}
|
|
21
22
|
_BATCH_CANCEL_FIELDS = {"order_id", "stock_code", "market", "order_remark"}
|
|
23
|
+
_VERSION_SUFFIX = re.compile(r"\s+\[cfquant\s+[^\]]+\]\s*$")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _batch_error_text(error):
|
|
27
|
+
"""Keep row-level compatibility while request errors retain diagnostics."""
|
|
28
|
+
text = str(error) or type(error).__name__
|
|
29
|
+
return _VERSION_SUFFIX.sub("", text)
|
|
22
30
|
|
|
23
31
|
|
|
24
32
|
def batch_positive_id(value):
|
|
@@ -206,14 +214,14 @@ def batch_result(account, batch_id, asynchronous, results, operation="order"):
|
|
|
206
214
|
|
|
207
215
|
def batch_unknown(rows, error):
|
|
208
216
|
for row in rows:
|
|
209
|
-
row.update(status="unknown", ok=None, error=
|
|
217
|
+
row.update(status="unknown", ok=None, error=_batch_error_text(error))
|
|
210
218
|
|
|
211
219
|
|
|
212
220
|
def batch_rejected(rows, error):
|
|
213
221
|
"""Mark a rejection known to happen before QMT dispatch."""
|
|
214
222
|
details = getattr(error, "error_details", None)
|
|
215
223
|
details = details if isinstance(details, dict) else {}
|
|
216
|
-
message =
|
|
224
|
+
message = _batch_error_text(error)
|
|
217
225
|
for row in rows:
|
|
218
226
|
row.update(status="failed", ok=False, error=message, dispatched=False,
|
|
219
227
|
safe_to_retry=details.get("safe_to_retry") is True,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
"""Serial user callbacks, independent of the RPC receive thread."""
|
|
3
3
|
from collections import deque
|
|
4
|
-
import
|
|
4
|
+
from .diagnostic_context import get_logger
|
|
5
5
|
import os
|
|
6
6
|
import tempfile
|
|
7
7
|
import threading
|
|
@@ -90,7 +90,7 @@ class CallbackDispatcher(object):
|
|
|
90
90
|
item[1] = item[3] = None
|
|
91
91
|
if self._queue and len(self._queue) >= 1000 and time.monotonic() - self._last_warning >= 5:
|
|
92
92
|
self._last_warning = time.monotonic()
|
|
93
|
-
|
|
93
|
+
get_logger(__name__).warning("callback backlog count=%s oldest_ms=%.1f spooled_bytes=%s",
|
|
94
94
|
len(self._queue), (time.monotonic() - self._queue[0][2]) * 1000, self._spool_live)
|
|
95
95
|
|
|
96
96
|
def _take_message(self, item):
|
|
@@ -168,7 +168,7 @@ class CallbackDispatcher(object):
|
|
|
168
168
|
if message is None:
|
|
169
169
|
raise ValueError("invalid preserved callback message")
|
|
170
170
|
except Exception as error:
|
|
171
|
-
|
|
171
|
+
get_logger(__name__).exception("callback backlog failed; connection must be restarted")
|
|
172
172
|
with self._condition:
|
|
173
173
|
if generation != self._generation:
|
|
174
174
|
continue # An old connection must not close its replacement.
|
|
@@ -185,7 +185,7 @@ class CallbackDispatcher(object):
|
|
|
185
185
|
if self.is_current_callback():
|
|
186
186
|
self._dispatch(message)
|
|
187
187
|
except Exception:
|
|
188
|
-
|
|
188
|
+
get_logger(__name__).exception("callback dispatch failed event=%s", message.get("event"))
|
|
189
189
|
finally:
|
|
190
190
|
self._last_callback_ms = (time.monotonic() - started) * 1000
|
|
191
191
|
del self._context.generation
|
|
@@ -215,4 +215,4 @@ def dispatch_callbacks(callbacks, message, is_current):
|
|
|
215
215
|
try:
|
|
216
216
|
callback(payload)
|
|
217
217
|
except Exception:
|
|
218
|
-
|
|
218
|
+
get_logger(__name__).exception("user callback failed event=%s", event)
|
|
@@ -10,14 +10,16 @@ from .config import get_config
|
|
|
10
10
|
from .callback_dispatcher import CallbackDispatcher, dispatch_callbacks
|
|
11
11
|
from .protocol import decode_value, dumps_message, loads_message, new_id, pack_request
|
|
12
12
|
from .rpc_timing import RpcTiming
|
|
13
|
+
from .diagnostic_context import CFQUANT_VERSION, VersionedErrorMixin
|
|
13
14
|
|
|
14
15
|
|
|
15
|
-
class CfquantError(RuntimeError):
|
|
16
|
+
class CfquantError(VersionedErrorMixin, RuntimeError):
|
|
16
17
|
"""RPC failure retaining the server's structured rejection details."""
|
|
17
18
|
|
|
18
19
|
def __init__(self, message="", details=None):
|
|
19
20
|
super(CfquantError, self).__init__(message)
|
|
20
21
|
self.error_details = dict(details) if isinstance(details, dict) else {}
|
|
22
|
+
self.error_details.setdefault("cfquant_version", CFQUANT_VERSION)
|
|
21
23
|
self.code = self.error_details.get("code", "")
|
|
22
24
|
self.stage = self.error_details.get("stage", "")
|
|
23
25
|
self.dispatched = self.error_details.get("dispatched")
|
|
@@ -25,7 +27,7 @@ class CfquantError(RuntimeError):
|
|
|
25
27
|
self.retry_when = self.error_details.get("retry_when", "")
|
|
26
28
|
|
|
27
29
|
|
|
28
|
-
class CfquantTimeout(TimeoutError):
|
|
30
|
+
class CfquantTimeout(VersionedErrorMixin, TimeoutError):
|
|
29
31
|
pass
|
|
30
32
|
|
|
31
33
|
|
|
@@ -157,7 +159,7 @@ class LTtxRpcClient(object):
|
|
|
157
159
|
if not getattr(self._tx, "run_on", True):
|
|
158
160
|
raise CfquantError("cfquant client closed during connection startup")
|
|
159
161
|
except BaseException:
|
|
160
|
-
|
|
162
|
+
LTtxRpcClient.close(self)
|
|
161
163
|
raise
|
|
162
164
|
self._started = True
|
|
163
165
|
self._closed = False
|
|
@@ -286,7 +288,7 @@ class LTtxRpcClient(object):
|
|
|
286
288
|
break
|
|
287
289
|
with self._lock:
|
|
288
290
|
if self._tx is expected_tx:
|
|
289
|
-
|
|
291
|
+
LTtxRpcClient.close(self)
|
|
290
292
|
|
|
291
293
|
def _dispatch_event(self, msg):
|
|
292
294
|
dispatch_callbacks(self._callbacks, msg, self._event_dispatcher.is_current_callback)
|
|
@@ -355,6 +357,38 @@ class WebLttxRpcClient(LTtxRpcClient):
|
|
|
355
357
|
timeout=timeout,
|
|
356
358
|
client_id=client_id,
|
|
357
359
|
)
|
|
360
|
+
self._subscription_session = None
|
|
361
|
+
self._subscription_protocol = None
|
|
362
|
+
self._subscription_init_lock = threading.Lock()
|
|
363
|
+
|
|
364
|
+
def _subscription_raw_request(self, action, params, timeout=None):
|
|
365
|
+
return super(WebLttxRpcClient, self).request(action, params, timeout=timeout)
|
|
366
|
+
|
|
367
|
+
def request(self, action, params=None, timeout=None, request_channel=None, response_meta=None):
|
|
368
|
+
from .subscription_client import managed_request
|
|
369
|
+
handled, result = managed_request(self, self._subscription_raw_request, action, params, timeout)
|
|
370
|
+
if handled:
|
|
371
|
+
return result
|
|
372
|
+
return super(WebLttxRpcClient, self).request(action, params, timeout, request_channel, response_meta)
|
|
373
|
+
|
|
374
|
+
def _dispatch_event(self, msg):
|
|
375
|
+
if msg.get('event') == 'cfquant.managed_subscription':
|
|
376
|
+
data = msg.get('data')
|
|
377
|
+
if isinstance(data, dict):
|
|
378
|
+
if self._subscription_session is None or data.get('key') not in self._subscription_session.specs:
|
|
379
|
+
return
|
|
380
|
+
msg = dict(msg, event=data.get('event'), data=data.get('payload'))
|
|
381
|
+
sid = (msg.get('meta') or {}).get('managed_subscription_id')
|
|
382
|
+
if sid is not None and (self._subscription_session is None or str(sid) not in self._subscription_session.specs):
|
|
383
|
+
return
|
|
384
|
+
super(WebLttxRpcClient, self)._dispatch_event(msg)
|
|
385
|
+
|
|
386
|
+
def close(self):
|
|
387
|
+
session = getattr(self, '_subscription_session', None)
|
|
388
|
+
if session is not None:
|
|
389
|
+
self._subscription_session = None
|
|
390
|
+
session.close()
|
|
391
|
+
super(WebLttxRpcClient, self).close()
|
|
358
392
|
|
|
359
393
|
|
|
360
394
|
def _registry_has_web_route(registry):
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""Version context for cfquant diagnostics without changing application logging."""
|
|
2
|
+
import logging
|
|
3
|
+
|
|
4
|
+
from .version import __version__ as CFQUANT_VERSION
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def versioned_message(message):
|
|
8
|
+
text = str(message)
|
|
9
|
+
tag = "[cfquant %s]" % CFQUANT_VERSION
|
|
10
|
+
return text if tag in text else "%s %s" % (text, tag)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class VersionedErrorMixin:
|
|
14
|
+
cfquant_version = CFQUANT_VERSION
|
|
15
|
+
|
|
16
|
+
def __str__(self):
|
|
17
|
+
# Keep .args and structured error codes intact for existing callers.
|
|
18
|
+
return versioned_message(super(VersionedErrorMixin, self).__str__())
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class _VersionFilter(logging.Filter):
|
|
22
|
+
def filter(self, record):
|
|
23
|
+
record.msg = versioned_message(record.getMessage())
|
|
24
|
+
record.args = ()
|
|
25
|
+
record.cfquant_version = CFQUANT_VERSION
|
|
26
|
+
return True
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
_VERSION_FILTER = _VersionFilter()
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def get_logger(name):
|
|
33
|
+
logger = logging.getLogger(name)
|
|
34
|
+
logger.addFilter(_VERSION_FILTER)
|
|
35
|
+
return logger
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Ordered, bounded background work. No QMT API calls run in these workers."""
|
|
2
2
|
from collections import deque
|
|
3
|
-
import
|
|
3
|
+
from .diagnostic_context import get_logger
|
|
4
4
|
import threading
|
|
5
5
|
import time
|
|
6
6
|
|
|
@@ -163,7 +163,7 @@ class KeyedExecutor(object):
|
|
|
163
163
|
try:
|
|
164
164
|
item[2](error)
|
|
165
165
|
except Exception:
|
|
166
|
-
|
|
166
|
+
get_logger(__name__).exception("Web route could not deliver request failure")
|
|
167
167
|
|
|
168
168
|
def status(self):
|
|
169
169
|
with self._condition:
|
|
@@ -7,6 +7,7 @@ import threading
|
|
|
7
7
|
import time
|
|
8
8
|
|
|
9
9
|
from .protocol import loads_message
|
|
10
|
+
from .diagnostic_context import versioned_message
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
def default_status_file(filename):
|
|
@@ -263,7 +264,7 @@ class CfquantHub(object):
|
|
|
263
264
|
|
|
264
265
|
def _log(self, msg):
|
|
265
266
|
if self.show:
|
|
266
|
-
print("%s %s" % (self._timestamp_ms(), msg), flush=True)
|
|
267
|
+
print("%s %s" % (self._timestamp_ms(), versioned_message(msg)), flush=True)
|
|
267
268
|
|
|
268
269
|
def _timestamp_ms(self):
|
|
269
270
|
now = time.time()
|
|
@@ -5,6 +5,8 @@ The service has its own source directory. Updating it never installs packages
|
|
|
5
5
|
into, or reloads modules in, the calling application's Python environment.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
+
from .diagnostic_context import VersionedErrorMixin
|
|
9
|
+
|
|
8
10
|
import importlib.util
|
|
9
11
|
import json
|
|
10
12
|
import os
|
|
@@ -23,7 +25,7 @@ import urllib.request
|
|
|
23
25
|
_UNSET = object()
|
|
24
26
|
|
|
25
27
|
|
|
26
|
-
class ManagementError(RuntimeError):
|
|
28
|
+
class ManagementError(VersionedErrorMixin, RuntimeError):
|
|
27
29
|
"""Management failure, including the server's status and response details."""
|
|
28
30
|
|
|
29
31
|
def __init__(self, message, code="management_error", status=None, details=None):
|