cfquant 0.2.47__tar.gz → 0.2.48__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (174) hide show
  1. {cfquant-0.2.47 → cfquant-0.2.48}/PKG-INFO +1 -1
  2. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/qmt_strategy_deploy.py +50 -17
  3. {cfquant-0.2.47 → cfquant-0.2.48}/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 +4 -4
  4. cfquant-0.2.48/cfquant/tests/test_lite_deployment_paths.py +140 -0
  5. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/tests/test_qmt_strategy_deploy.py +9 -0
  6. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/tests/test_qmt_strategy_web.py +2 -1
  7. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/version.py +2 -2
  8. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant.egg-info/PKG-INFO +1 -1
  9. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant.egg-info/SOURCES.txt +1 -0
  10. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant_web_server.py +115 -79
  11. {cfquant-0.2.47 → cfquant-0.2.48}/pyproject.toml +1 -1
  12. {cfquant-0.2.47 → cfquant-0.2.48}/qmt_scripts/CFQUANT_LITE.py +1 -1
  13. {cfquant-0.2.47 → cfquant-0.2.48}/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 +1 -1
  14. {cfquant-0.2.47 → cfquant-0.2.48}/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 +1 -1
  15. {cfquant-0.2.47 → cfquant-0.2.48}/web_dashboard/app.js +1 -1
  16. {cfquant-0.2.47 → cfquant-0.2.48}/web_dashboard/index.html +2 -2
  17. {cfquant-0.2.47 → cfquant-0.2.48}/LICENSE +0 -0
  18. {cfquant-0.2.47 → cfquant-0.2.48}/LTtx/__init__.py +0 -0
  19. {cfquant-0.2.47 → cfquant-0.2.48}/LTtx/tx/Config.txt +0 -0
  20. {cfquant-0.2.47 → cfquant-0.2.48}/LTtx/tx/LTtx_server.py +0 -0
  21. {cfquant-0.2.47 → cfquant-0.2.48}/LTtx/tx/__init__.py +0 -0
  22. {cfquant-0.2.47 → cfquant-0.2.48}/LTtx/tx/tx.py +0 -0
  23. {cfquant-0.2.47 → cfquant-0.2.48}/MANIFEST.in +0 -0
  24. {cfquant-0.2.47 → cfquant-0.2.48}/README.md +0 -0
  25. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/__init__.py +0 -0
  26. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/_editable_install.py +0 -0
  27. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/account_routing.py +0 -0
  28. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/batch_orders.py +0 -0
  29. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/build_info.py +0 -0
  30. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/callback_dispatcher.py +0 -0
  31. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/cftrader.py +0 -0
  32. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/channels.py +0 -0
  33. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/cli.py +0 -0
  34. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/client.py +0 -0
  35. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/config.py +0 -0
  36. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/hub.py +0 -0
  37. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/level2.py +0 -0
  38. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/log_management.py +0 -0
  39. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/logging_i18n.py +0 -0
  40. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/management.py +0 -0
  41. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/normal_bridge.py +0 -0
  42. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/order_identity.py +0 -0
  43. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/order_meta.py +0 -0
  44. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/pipe_bridge.py +0 -0
  45. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/pipe_client.py +0 -0
  46. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/pipe_hub.py +0 -0
  47. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/pipe_transport.py +0 -0
  48. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/protocol.py +0 -0
  49. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/qmt_bridge.py +0 -0
  50. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/qmt_strategy_package.py +0 -0
  51. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/qmt_strategy_runtime.py +0 -0
  52. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/runtime_report.py +0 -0
  53. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/stock_connect.py +0 -0
  54. {cfquant-0.2.47 → cfquant-0.2.48}/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
  55. {cfquant-0.2.47 → cfquant-0.2.48}/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
  56. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/tests/12_/350/256/242/345/215/225/345/244/207/346/263/250/346/265/213/350/257/225.py" +0 -0
  57. {cfquant-0.2.47 → cfquant-0.2.48}/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" +0 -0
  58. {cfquant-0.2.47 → cfquant-0.2.48}/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
  59. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/tests/16_/345/220/257/345/212/250/345/256/211/350/243/205/346/265/213/350/257/225.py" +0 -0
  60. {cfquant-0.2.47 → cfquant-0.2.48}/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
  61. {cfquant-0.2.47 → cfquant-0.2.48}/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
  62. {cfquant-0.2.47 → cfquant-0.2.48}/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
  63. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/tests/1_/350/241/214/346/203/205/346/216/245/346/224/266/346/265/213/350/257/225.py" +0 -0
  64. {cfquant-0.2.47 → cfquant-0.2.48}/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
  65. {cfquant-0.2.47 → cfquant-0.2.48}/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
  66. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/tests/22_/350/277/236/346/216/245/350/257/212/346/226/255/346/265/213/350/257/225.py" +0 -0
  67. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/tests/23_/347/244/272/344/276/213/351/205/215/347/275/256/346/265/213/350/257/225.py" +0 -0
  68. {cfquant-0.2.47 → cfquant-0.2.48}/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" +0 -0
  69. {cfquant-0.2.47 → cfquant-0.2.48}/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
  70. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/tests/26_/345/205/250/346/216/250/350/256/242/351/230/205/346/265/213/350/257/225.py" +0 -0
  71. {cfquant-0.2.47 → cfquant-0.2.48}/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
  72. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/tests/28_Level2/346/216/245/345/217/243/351/200/202/351/205/215/346/265/213/350/257/225.py" +0 -0
  73. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/tests/29_Level2/350/241/214/346/203/205/346/265/213/350/257/225.py" +0 -0
  74. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/tests/2_/346/225/260/346/215/256/350/216/267/345/217/226/346/265/213/350/257/225.py" +0 -0
  75. {cfquant-0.2.47 → cfquant-0.2.48}/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
  76. {cfquant-0.2.47 → cfquant-0.2.48}/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
  77. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/tests/32_/345/274/202/346/255/245/344/270/213/345/215/225/346/265/213/350/257/225.py" +0 -0
  78. {cfquant-0.2.47 → cfquant-0.2.48}/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
  79. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/tests/3_/346/225/260/346/215/256/344/270/213/350/275/275/346/265/213/350/257/225.py" +0 -0
  80. {cfquant-0.2.47 → cfquant-0.2.48}/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
  81. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/tests/5_/347/234/237/345/256/236/344/270/213/345/215/225/346/265/213/350/257/225.py" +0 -0
  82. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/tests/6_/345/233/236/350/260/203/346/265/213/350/257/225.py" +0 -0
  83. {cfquant-0.2.47 → cfquant-0.2.48}/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
  84. {cfquant-0.2.47 → cfquant-0.2.48}/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
  85. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/tests/9_/345/217/257/346/222/244/350/256/242/345/215/225/346/265/213/350/257/225.py" +0 -0
  86. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/tests/_helpers.py +0 -0
  87. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/tests/log/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_20260911_024617.md" +0 -0
  88. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/tests/log/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_20260921_175732.md" +0 -0
  89. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/tests/readme.md +0 -0
  90. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/tests/test_api_tester_browser.py +0 -0
  91. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/tests/test_api_tester_web.py +0 -0
  92. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/tests/test_binding_delete_confirmation.py +0 -0
  93. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/tests/test_callback_requests.py +0 -0
  94. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/tests/test_cftrader.py +0 -0
  95. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/tests/test_cftrader_tutorial.py +0 -0
  96. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/tests/test_cftrader_web.py +0 -0
  97. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/tests/test_credit_compat.py +0 -0
  98. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/tests/test_json_serialization.py +0 -0
  99. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/tests/test_lite_account_routing.py +0 -0
  100. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/tests/test_local_trading_calendar.py +0 -0
  101. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/tests/test_log_management.py +0 -0
  102. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/tests/test_management.py +0 -0
  103. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/tests/test_management_batch.py +0 -0
  104. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/tests/test_market_data_fallback.py +0 -0
  105. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/tests/test_order_identity.py +0 -0
  106. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/tests/test_order_time_callback.py +0 -0
  107. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/tests/test_project_updater.py +0 -0
  108. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/tests/test_python_api_reference.py +0 -0
  109. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/tests/test_qmt_strategy_browser.py +0 -0
  110. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/tests/test_setup_qmt_prompt.py +0 -0
  111. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/tests/test_startup_recovery.py +0 -0
  112. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/tests/test_stock_connect.py +0 -0
  113. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/tests/test_stock_connect_browser.py +0 -0
  114. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/tests/test_trading_dates.py +0 -0
  115. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/tests/test_tutorial_reader.py +0 -0
  116. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/tests/test_tx_auto_import.py +0 -0
  117. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/trading_calendar.py +0 -0
  118. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/tx.py +0 -0
  119. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/tx_trade_bridge.py +0 -0
  120. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/xtconstant.py +0 -0
  121. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/xtdata.py +0 -0
  122. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/xttrader.py +0 -0
  123. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant/xttype.py +0 -0
  124. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant.egg-info/dependency_links.txt +0 -0
  125. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant.egg-info/entry_points.txt +0 -0
  126. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant.egg-info/requires.txt +0 -0
  127. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant.egg-info/top_level.txt +0 -0
  128. {cfquant-0.2.47 → cfquant-0.2.48}/cfquant_pipe_hub.py +0 -0
  129. {cfquant-0.2.47 → cfquant-0.2.48}/qmt_scripts/CFQUANT.py +0 -0
  130. {cfquant-0.2.47 → cfquant-0.2.48}/qmt_scripts/CFQUANT_CTYPE_ALL_LOWLAT.py +0 -0
  131. {cfquant-0.2.47 → cfquant-0.2.48}/qmt_scripts/CFQUANT_TRADE_LOWLAT.py +0 -0
  132. {cfquant-0.2.47 → cfquant-0.2.48}/qmt_scripts/__init__.py +0 -0
  133. {cfquant-0.2.47 → cfquant-0.2.48}/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
  134. {cfquant-0.2.47 → cfquant-0.2.48}/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
  135. {cfquant-0.2.47 → cfquant-0.2.48}/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
  136. {cfquant-0.2.47 → cfquant-0.2.48}/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
  137. {cfquant-0.2.47 → cfquant-0.2.48}/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
  138. {cfquant-0.2.47 → cfquant-0.2.48}/requirements.txt +0 -0
  139. {cfquant-0.2.47 → cfquant-0.2.48}/setup.cfg +0 -0
  140. {cfquant-0.2.47 → cfquant-0.2.48}/web_dashboard/__init__.py +0 -0
  141. {cfquant-0.2.47 → cfquant-0.2.48}/web_dashboard/api-tester.js +0 -0
  142. {cfquant-0.2.47 → cfquant-0.2.48}/web_dashboard/avatars/amber-pulse.svg +0 -0
  143. {cfquant-0.2.47 → cfquant-0.2.48}/web_dashboard/avatars/copper-grid.svg +0 -0
  144. {cfquant-0.2.47 → cfquant-0.2.48}/web_dashboard/avatars/market-blue.svg +0 -0
  145. {cfquant-0.2.47 → cfquant-0.2.48}/web_dashboard/avatars/rose-circuit.svg +0 -0
  146. {cfquant-0.2.47 → cfquant-0.2.48}/web_dashboard/avatars/signal-green.svg +0 -0
  147. {cfquant-0.2.47 → cfquant-0.2.48}/web_dashboard/avatars/slate-wave.svg +0 -0
  148. {cfquant-0.2.47 → cfquant-0.2.48}/web_dashboard/avatars/teal-orbit.svg +0 -0
  149. {cfquant-0.2.47 → cfquant-0.2.48}/web_dashboard/avatars/violet-node.svg +0 -0
  150. {cfquant-0.2.47 → cfquant-0.2.48}/web_dashboard/cftrader-api-data.js +0 -0
  151. {cfquant-0.2.47 → cfquant-0.2.48}/web_dashboard/docs.js +0 -0
  152. {cfquant-0.2.47 → cfquant-0.2.48}/web_dashboard/favicon.svg +0 -0
  153. {cfquant-0.2.47 → cfquant-0.2.48}/web_dashboard/image/qmt_deploy/image-10.png +0 -0
  154. {cfquant-0.2.47 → cfquant-0.2.48}/web_dashboard/image/qmt_deploy/image-11.png +0 -0
  155. {cfquant-0.2.47 → cfquant-0.2.48}/web_dashboard/image/qmt_deploy/image-12.png +0 -0
  156. {cfquant-0.2.47 → cfquant-0.2.48}/web_dashboard/image/qmt_deploy/image-13.png +0 -0
  157. {cfquant-0.2.47 → cfquant-0.2.48}/web_dashboard/image/qmt_deploy/image-14.png +0 -0
  158. {cfquant-0.2.47 → cfquant-0.2.48}/web_dashboard/image/qmt_deploy/image-15.png +0 -0
  159. {cfquant-0.2.47 → cfquant-0.2.48}/web_dashboard/image/qmt_deploy/image-16.png +0 -0
  160. {cfquant-0.2.47 → cfquant-0.2.48}/web_dashboard/image/qmt_deploy/image-17.png +0 -0
  161. {cfquant-0.2.47 → cfquant-0.2.48}/web_dashboard/image/qmt_deploy/image-18.png +0 -0
  162. {cfquant-0.2.47 → cfquant-0.2.48}/web_dashboard/image/qmt_deploy/image-19.png +0 -0
  163. {cfquant-0.2.47 → cfquant-0.2.48}/web_dashboard/image/qmt_deploy/image-20.png +0 -0
  164. {cfquant-0.2.47 → cfquant-0.2.48}/web_dashboard/image/qmt_deploy/image-21.png +0 -0
  165. {cfquant-0.2.47 → cfquant-0.2.48}/web_dashboard/image/qmt_deploy/image-4.png +0 -0
  166. {cfquant-0.2.47 → cfquant-0.2.48}/web_dashboard/image/qmt_deploy/image-5.png +0 -0
  167. {cfquant-0.2.47 → cfquant-0.2.48}/web_dashboard/image/qmt_deploy/image-6.png +0 -0
  168. {cfquant-0.2.47 → cfquant-0.2.48}/web_dashboard/image/qmt_deploy/image-7.png +0 -0
  169. {cfquant-0.2.47 → cfquant-0.2.48}/web_dashboard/image/qmt_deploy/image-8.png +0 -0
  170. {cfquant-0.2.47 → cfquant-0.2.48}/web_dashboard/image/qmt_deploy/image-9.png +0 -0
  171. {cfquant-0.2.47 → cfquant-0.2.48}/web_dashboard/python-api-data.js +0 -0
  172. {cfquant-0.2.47 → cfquant-0.2.48}/web_dashboard/python-api.js +0 -0
  173. {cfquant-0.2.47 → cfquant-0.2.48}/web_dashboard/styles.css +0 -0
  174. {cfquant-0.2.47 → cfquant-0.2.48}/web_dashboard/vendor/mermaid.min.js +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cfquant
3
- Version: 0.2.47
3
+ Version: 0.2.48
4
4
  Summary: QMT local bridge and xtquant-compatible Python client.
5
5
  License: MIT
6
6
  Requires-Python: >=3.8
@@ -23,7 +23,10 @@ MODE_ALIASES = {
23
23
  "ctypes": "ctypes", "pipe": "ctypes", "named_pipe": "ctypes",
24
24
  "lite": "lite", "extreme": "lite", "extreme_lite": "lite",
25
25
  "lite_extreme": "lite", "ultimate": "lite",
26
+ "lite_extreme_pipe": "lite", "extreme_pipe": "lite", "cfquant_lite": "lite",
27
+ "named-pipe": "ctypes", "universal": "ctypes", "universal_ctypes": "ctypes",
26
28
  "lttx": "lttx", "socket": "lttx", "normal": "lttx",
29
+ "default": "lttx",
27
30
  }
28
31
  LEGACY_NAMES = {Path(name).stem for name in SCRIPTS.values()} | {"CFQUANT_TRADE_LOWLAT"}
29
32
  LEGACY_NAMES |= {name + "_" + market for name in tuple(LEGACY_NAMES) for market in ("SH", "SZ")}
@@ -68,9 +71,42 @@ def normalize_strategy_mode(value):
68
71
  return MODE_ALIASES[mode]
69
72
  except KeyError:
70
73
  raise ValueError("unknown QMT strategy mode: %s" % mode)
71
-
72
-
73
- def qmt_root(directory, validate=False):
74
+
75
+
76
+ def entry_script_for_mode(mode, role="normal", market=""):
77
+ """One mapping for raw copies and the packages QMT actually imports."""
78
+ mode = normalize_strategy_mode(mode)
79
+ market = str(market or "").strip().upper()
80
+ if market and market not in ("SH", "SZ"):
81
+ raise ValueError("unknown QMT entry market: %s" % market)
82
+ filename = SCRIPTS[mode]
83
+ if mode == "lttx" and (role != "normal" or market):
84
+ filename = "CFQUANT_TRADE_LOWLAT.py"
85
+ if market:
86
+ return "同账号独立市场/%s_%s.py" % (Path(filename).stem, market)
87
+ return filename
88
+
89
+
90
+ def read_entry_source(path):
91
+ """Reject a generic entry accidentally saved under a Lite filename."""
92
+ path = Path(path)
93
+ with tokenize.open(str(path)) as stream:
94
+ source = stream.read()
95
+ if path.stem.startswith("CFQUANT_LITE"):
96
+ tree = ast.parse(source)
97
+ constants = {target.id for node in tree.body if isinstance(node, ast.Assign)
98
+ for target in node.targets if isinstance(target, ast.Name)}
99
+ imports_core = any(
100
+ (isinstance(node, ast.ImportFrom) and (node.module or "").split(".")[0] == "cfquant")
101
+ or (isinstance(node, ast.Import) and any(alias.name.split(".")[0] == "cfquant"
102
+ for alias in node.names))
103
+ for node in ast.walk(tree))
104
+ if "LITE_ENTRY_VERSION" not in constants or imports_core:
105
+ raise ValueError("极致模式入口内容不匹配,拒绝部署通用模式代码: %s" % path)
106
+ return source
107
+
108
+
109
+ def qmt_root(directory, validate=False):
74
110
  path = Path(str(directory)).expanduser().resolve()
75
111
  if path.name.lower() in ("bin.x64", "bin", "python"):
76
112
  path = path.parent
@@ -283,9 +319,8 @@ def _resolve_account_binding(root, document, account_id, account_type, supplied=
283
319
  return None
284
320
 
285
321
 
286
- def managed_source(source_path, identity_path, identity, descriptor):
287
- with tokenize.open(str(source_path)) as stream:
288
- source = stream.read()
322
+ def managed_source(source_path, identity_path, identity, descriptor):
323
+ source = read_entry_source(source_path)
289
324
  lines = source.splitlines(keepends=True)
290
325
  tree = ast.parse(source)
291
326
  replacements = []
@@ -463,12 +498,13 @@ class QmtStrategyManager:
463
498
  result.update(error=self.load_error, message=self.load_error)
464
499
  return result
465
500
 
466
- def configure(self, row, identities, force=False):
501
+ def configure(self, row, identities, force=False, scripts_dir=None):
467
502
  if self.load_error:
468
503
  raise ValueError(self.load_error)
469
504
  settings = normalize_strategy_settings(row.get("qmt_strategy"))
470
505
  mode = normalize_strategy_mode(row.get("mode"))
471
- enabled = row.get("enabled", True) and settings["enabled"]
506
+ enabled = row.get("enabled", True) and settings["enabled"]
507
+ scripts_dir = Path(scripts_dir) if scripts_dir is not None else self.scripts_dir
472
508
  with self.lock:
473
509
  wanted = set()
474
510
  grouped = {}
@@ -486,19 +522,16 @@ class QmtStrategyManager:
486
522
  "account_key": row["account_key"]}])
487
523
  market = info.get("market") or identity.get("market") or ""
488
524
  role = market or info.get("qmt_role") or "normal"
489
- filename = SCRIPTS[mode]
490
- if mode == "lttx" and role != "normal":
491
- filename = "CFQUANT_TRADE_LOWLAT.py"
492
- source_path = self.scripts_dir / filename
493
- if market:
494
- source_path = self.scripts_dir / "同账号独立市场" / (Path(filename).stem + "_" + market + ".py")
495
- if not source_path.is_file():
496
- raise ValueError("QMT 内置脚本不存在: %s" % source_path)
525
+ filename = entry_script_for_mode(mode, role, market)
526
+ source_path = scripts_dir / filename
527
+ if not source_path.is_file():
528
+ raise ValueError("QMT 内置脚本不存在: %s" % source_path)
529
+ read_entry_source(source_path)
497
530
  for key in ("updated_at", "updated_at_text"):
498
531
  identity.pop(key, None)
499
532
  group_key = _digest([os.path.normcase(str(root)), row["account_id"]])[:24]
500
533
  grouped.setdefault(group_key, {"root": str(root), "roles": []})["roles"].append({
501
- "role": role, "source": str(source_path), "identity": identity,
534
+ "role": role, "source": str(source_path), "entry_script": filename, "identity": identity,
502
535
  "source_sha256": hashlib.sha256(source_path.read_bytes()).hexdigest(),
503
536
  "account_key": settings["account_keys"].get(role, "")})
504
537
  if not grouped:
@@ -372,7 +372,7 @@ def test_auto_deploy_local_core_skips_when_target_matches(tmp_path):
372
372
  def test_local_entry_sync_overwrites_bound_qmt_python_script(tmp_path):
373
373
  source = tmp_path / "source"
374
374
  (source / "qmt_scripts").mkdir(parents=True)
375
- (source / "qmt_scripts" / "CFQUANT_LITE.py").write_bytes(b"new-entry\n")
375
+ (source / "qmt_scripts" / "CFQUANT_LITE.py").write_bytes(b'LITE_ENTRY_VERSION = "test"\n')
376
376
  qmt_root = tmp_path / "QMT"
377
377
  (qmt_root / "bin.x64").mkdir(parents=True)
378
378
  (qmt_root / "python").mkdir()
@@ -389,7 +389,7 @@ def test_local_entry_sync_overwrites_bound_qmt_python_script(tmp_path):
389
389
  )
390
390
 
391
391
  assert result["updated"] is True
392
- assert entry.read_bytes() == b"new-entry\n"
392
+ assert entry.read_bytes() == b'LITE_ENTRY_VERSION = "test"\n'
393
393
  assert result["entry_file"] == str(entry)
394
394
  assert result["backup"]
395
395
  assert (qmt_root / "bin.x64" / ".cfquant_updates" / "entry_backups").is_dir()
@@ -402,7 +402,7 @@ def test_auto_deploy_project_update_copies_bound_mode_entry(tmp_path, monkeypatc
402
402
  for name in ("__init__.py", "client.py", "protocol.py"):
403
403
  (source_core / name).write_text("# source\n", encoding="utf-8")
404
404
  (source / "qmt_scripts").mkdir()
405
- (source / "qmt_scripts" / "CFQUANT_LITE.py").write_bytes(b"latest-lite\n")
405
+ (source / "qmt_scripts" / "CFQUANT_LITE.py").write_bytes(b'LITE_ENTRY_VERSION = "latest"\n')
406
406
 
407
407
  qmt_root = tmp_path / "QMT"
408
408
  target_core = qmt_root / "bin.x64" / "cfquant"
@@ -423,7 +423,7 @@ def test_auto_deploy_project_update_copies_bound_mode_entry(tmp_path, monkeypatc
423
423
 
424
424
  assert result["summary"]["ok"] is True
425
425
  assert result["summary"]["entry_updated_count"] == 1
426
- assert (qmt_root / "python" / "CFQUANT_LITE.py").read_bytes() == b"latest-lite\n"
426
+ assert (qmt_root / "python" / "CFQUANT_LITE.py").read_bytes() == b'LITE_ENTRY_VERSION = "latest"\n'
427
427
  assert result["results"][0]["entry_script"] == "CFQUANT_LITE.py"
428
428
 
429
429
 
@@ -0,0 +1,140 @@
1
+ """Verify the bytes copied and imported for Lite across binding/update paths."""
2
+
3
+ import ast
4
+ import json
5
+ from pathlib import Path
6
+ import shutil
7
+
8
+ import pytest
9
+
10
+ from cfquant.qmt_strategy_deploy import QmtStrategyManager
11
+ from cfquant.qmt_strategy_package import _cipher
12
+ from cfquant.tests.test_qmt_strategy_deploy import deployment, finish_import, models, SCRIPTS
13
+
14
+
15
+ def embedded_source(package):
16
+ content = json.loads(_cipher().decrypt(package.read_bytes()).decode("utf-8", "surrogateescape"))["content"]
17
+ return next(node.args[0].value for node in ast.walk(ast.parse(content))
18
+ if isinstance(node, ast.Call) and isinstance(node.func, ast.Name)
19
+ and node.func.id == "compile" and isinstance(node.args[0], ast.Constant))
20
+
21
+
22
+ def assert_lite_package(manager, root):
23
+ job = next(job for job in manager.jobs.values() if job.get("enabled"))
24
+ assert job["mode"] == "lite"
25
+ for role in job["roles"]:
26
+ source = embedded_source(root / "formulas" / (role["name"] + ".rzrk"))
27
+ assert "LITE_ENTRY_VERSION =" in source
28
+ assert "def account_route_client_ids(" in source
29
+ assert not any(isinstance(node, ast.ImportFrom) and (node.module or "").startswith("cfquant")
30
+ for node in ast.walk(ast.parse(source)))
31
+ assert Path(role["source"]).name.startswith("CFQUANT_LITE")
32
+ return job
33
+
34
+
35
+ @pytest.fixture
36
+ def web_deployment(deployment, tmp_path, monkeypatch):
37
+ import cfquant_web_server as web
38
+ manager, row, infos, running, root = deployment
39
+ config = web.WebRuntimeConfig(str(tmp_path / "web.json"), str(tmp_path / "web.db"))
40
+ config._data["account_configs"] = {row["account_key"]: row}
41
+ monkeypatch.setattr(web, "WEB_CONFIG", config)
42
+ monkeypatch.setattr(web, "BASE_DIR", str(SCRIPTS.parent))
43
+ monkeypatch.setattr(web, "QMT_STRATEGIES", manager)
44
+ updater = web.CfquantUpdater(config)
45
+ monkeypatch.setattr(updater, "install_local_core_to_qmt_dir", lambda *args, **kwargs: {"skipped": True})
46
+ monkeypatch.setattr(web, "UPDATER", updater)
47
+ monkeypatch.setattr(web, "write_qmt_bridge_identity", lambda row: dict(infos[0]))
48
+ monkeypatch.setattr(web, "write_qmt_market_bridge_identities", lambda row: [])
49
+ monkeypatch.setattr(web, "sync_qmt_bridge_identities", lambda: [])
50
+ monkeypatch.setattr(web, "qmt_process_preflight", lambda **kwargs: None)
51
+ monkeypatch.setattr(web, "qmt_auto_login_apply_for_account", lambda *args, **kwargs: {})
52
+ monkeypatch.setattr(web, "ensure_account_runtime", lambda mode: {})
53
+ monkeypatch.setattr(web.ACCOUNT_CACHE, "prime_configured_accounts", lambda: None)
54
+ monkeypatch.setattr(web.STATUS_MONITOR, "wake", lambda: None)
55
+ monkeypatch.setattr(web.CALLBACKS, "refresh_channels", lambda channels: None)
56
+ return web, manager, row, infos, root
57
+
58
+
59
+ @pytest.mark.parametrize("action", ["bind", "account_update", "project_update", "startup"])
60
+ def test_selected_lite_replaces_imported_generic_code(action, web_deployment, monkeypatch):
61
+ web, manager, row, infos, root = web_deployment
62
+ manager.configure(row, infos)
63
+ finish_import(manager, root)
64
+ manager.process_once()
65
+ generic_name = next(iter(manager.jobs.values()))["roles"][0]["name"]
66
+ # Simulate an older installation with a Lite binding and a generic import.
67
+ row["mode"] = "lite"
68
+ if action == "bind":
69
+ result = web.save_account_runtime_config({
70
+ key: row[key] for key in ("account_id", "account_type", "bridge_id", "qmt_dir", "mode")})
71
+ assert result["account"]["mode"] == "lite"
72
+ assert not result["qmt_strategy_deploy"].get("error")
73
+ elif action == "account_update":
74
+ monkeypatch.setattr(web, "account_config_for_request", lambda body: row)
75
+ result = web.update_account_qmt_core({"account_key": row["account_key"]})
76
+ assert not result["qmt_strategy_deploy"].get("error")
77
+ elif action == "project_update":
78
+ result = web.auto_deploy_qmt_core_for_all_accounts(str(SCRIPTS.parent), sync_entry_scripts=True)
79
+ assert result["summary"]["ok"]
80
+ else:
81
+ # Persisted jobs must be repaired on first start after an old updater.
82
+ manager = QmtStrategyManager(manager.path, SCRIPTS, lambda root: False)
83
+ monkeypatch.setattr(web, "QMT_STRATEGIES", manager)
84
+ result = web.refresh_bound_qmt_strategies()
85
+ assert not result[0].get("error")
86
+ assert_lite_package(manager, root)
87
+ assert models(root)[generic_name].getAttribute("startupAutorun") == "0"
88
+ if action != "startup":
89
+ assert (root / "python" / "CFQUANT_LITE.py").read_bytes() == (SCRIPTS / "CFQUANT_LITE.py").read_bytes()
90
+
91
+
92
+ def test_edit_without_mode_preserves_lite_binding_and_payload(web_deployment):
93
+ web, manager, row, _, root = web_deployment
94
+ row["mode"] = "lite"
95
+ result = web.save_account_runtime_config({
96
+ key: row[key] for key in ("account_id", "account_type", "bridge_id", "qmt_dir")})
97
+ assert result["account"]["mode"] == "lite"
98
+ assert_lite_package(manager, root)
99
+
100
+
101
+ @pytest.mark.parametrize("single_account", [False, True])
102
+ def test_shared_qmt_directory_does_not_drop_lite_market_entries(single_account, web_deployment):
103
+ web, _, row, _, root = web_deployment
104
+ row.update(mode="lite", market_routing_enabled=True,
105
+ qmt_strategy={"enabled": False}, market_bridges={
106
+ market: {"qmt_dir": str(root), "bridge_id": "bridge_" + market}
107
+ for market in ("SH", "SZ")})
108
+ if single_account:
109
+ result = web.auto_deploy_qmt_core_for_account(row)
110
+ else:
111
+ # The earlier binding must not win solely because it shares a directory.
112
+ generic = dict(row, account_key="generic", account_id="222", mode="ctypes", market_routing_enabled=False)
113
+ web.WEB_CONFIG._data["account_configs"] = {"generic": generic, row["account_key"]: row}
114
+ result = web.auto_deploy_qmt_core_for_all_accounts(str(SCRIPTS.parent), sync_entry_scripts=True)
115
+ assert result["summary"]["ok"]
116
+ for name, directory in (("CFQUANT_LITE.py", SCRIPTS),
117
+ ("CFQUANT_LITE_SH.py", SCRIPTS / "同账号独立市场"),
118
+ ("CFQUANT_LITE_SZ.py", SCRIPTS / "同账号独立市场")):
119
+ assert (root / "python" / name).read_bytes() == (directory / name).read_bytes()
120
+
121
+
122
+ def test_update_uses_new_source_and_rejects_mislabeled_generic_entry(web_deployment, tmp_path):
123
+ web, manager, row, _, root = web_deployment
124
+ row["mode"] = "lite"
125
+ source = tmp_path / "updated_project"
126
+ (source / "qmt_scripts").mkdir(parents=True)
127
+ entry = source / "qmt_scripts" / "CFQUANT_LITE.py"
128
+ entry.write_bytes((SCRIPTS / entry.name).read_bytes() + b'\nRELEASE_TEST_MARKER = "new-source"\n')
129
+ result = web.auto_deploy_qmt_core_for_all_accounts(str(source), sync_entry_scripts=True)
130
+ assert result["summary"]["ok"]
131
+ job = assert_lite_package(manager, root)
132
+ assert 'RELEASE_TEST_MARKER = "new-source"' in embedded_source(
133
+ root / "formulas" / (job["roles"][0]["name"] + ".rzrk"))
134
+ previous = (root / "python" / entry.name).read_bytes()
135
+ shutil.copyfile(SCRIPTS / "CFQUANT_CTYPE_ALL_LOWLAT.py", entry)
136
+ result = web.auto_deploy_qmt_core_for_all_accounts(str(source), sync_entry_scripts=True)
137
+ assert not result["summary"]["ok"]
138
+ assert result["summary"]["strategy_error_count"] == 1
139
+ assert "通用模式代码" in result["summary"]["message"]
140
+ assert (root / "python" / entry.name).read_bytes() == previous
@@ -15,6 +15,7 @@ from cfquant.qmt_strategy_deploy import (
15
15
  QmtStrategyManager, _account_binding, _atomic_write, _read_document,
16
16
  _write_json, _resolve_account_binding, account_qmt_roots, managed_source, normalize_strategy_settings,
17
17
  normalize_strategy_mode,
18
+ entry_script_for_mode, read_entry_source,
18
19
  _strategy_slot_name,
19
20
  )
20
21
  from cfquant.qmt_strategy_package import _cipher, build_package
@@ -31,6 +32,14 @@ def test_strategy_mode_aliases_are_canonical(value, expected):
31
32
  assert normalize_strategy_mode(value) == expected
32
33
 
33
34
 
35
+ def test_lite_entry_mapping_rejects_generic_source(tmp_path):
36
+ assert entry_script_for_mode("extreme") == "CFQUANT_LITE.py"
37
+ source = tmp_path / "CFQUANT_LITE.py"
38
+ source.write_text("from cfquant import __version__\n", encoding="utf-8")
39
+ with pytest.raises(ValueError, match="通用模式代码"):
40
+ read_entry_source(source)
41
+
42
+
34
43
  def test_strategy_name_contains_mode_and_shortens_long_account():
35
44
  name = _strategy_slot_name("ACCOUNT-12345678901234567890", "lite")
36
45
  assert name == "CFQ_67890_LITE"
@@ -282,8 +282,9 @@ def test_save_and_delete_call_deployment_manager(web_config, monkeypatch):
282
282
  class Manager:
283
283
  def reconcile(self, accounts):
284
284
  calls.append(("reconcile", set(accounts)))
285
- def configure(self, row, identities, force=False):
285
+ def configure(self, row, identities, force=False, scripts_dir=None):
286
286
  assert force is True
287
+ assert scripts_dir == os.path.join(web.BASE_DIR, "qmt_scripts")
287
288
  calls.append(("configure", row["qmt_strategy"], identities))
288
289
  return {"enabled": True, "targets": [{"state": "waiting_exit"}]}
289
290
  monkeypatch.setattr(web, "QMT_STRATEGIES", Manager())
@@ -1,5 +1,5 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  """cfquant core version metadata."""
3
3
 
4
- __version__ = "0.2.47"
5
- WEB_VERSION = "web_20261011_01"
4
+ __version__ = "0.2.48"
5
+ WEB_VERSION = "web_20261011_02"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cfquant
3
- Version: 0.2.47
3
+ Version: 0.2.48
4
4
  Summary: QMT local bridge and xtquant-compatible Python client.
5
5
  License: MIT
6
6
  Requires-Python: >=3.8
@@ -99,6 +99,7 @@ cfquant/tests/test_cftrader_web.py
99
99
  cfquant/tests/test_credit_compat.py
100
100
  cfquant/tests/test_json_serialization.py
101
101
  cfquant/tests/test_lite_account_routing.py
102
+ cfquant/tests/test_lite_deployment_paths.py
102
103
  cfquant/tests/test_local_trading_calendar.py
103
104
  cfquant/tests/test_log_management.py
104
105
  cfquant/tests/test_management.py