cfquant 0.2.22__tar.gz → 0.2.24__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 (148) hide show
  1. {cfquant-0.2.22 → cfquant-0.2.24}/PKG-INFO +1 -1
  2. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant/_editable_install.py +41 -8
  3. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant/tests/16_/345/220/257/345/212/250/345/256/211/350/243/205/346/265/213/350/257/225.py +49 -3
  4. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant/tests/test_project_updater.py +1 -1
  5. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant/version.py +1 -1
  6. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant.egg-info/PKG-INFO +1 -1
  7. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant_web_server.py +2 -17
  8. {cfquant-0.2.22 → cfquant-0.2.24}/pyproject.toml +1 -1
  9. {cfquant-0.2.22 → cfquant-0.2.24}/web_dashboard/app.js +4 -7
  10. {cfquant-0.2.22 → cfquant-0.2.24}/web_dashboard/index.html +2 -2
  11. {cfquant-0.2.22 → cfquant-0.2.24}/LICENSE +0 -0
  12. {cfquant-0.2.22 → cfquant-0.2.24}/LTtx/__init__.py +0 -0
  13. {cfquant-0.2.22 → cfquant-0.2.24}/LTtx/tx/Config.txt +0 -0
  14. {cfquant-0.2.22 → cfquant-0.2.24}/LTtx/tx/LTtx_server.py +0 -0
  15. {cfquant-0.2.22 → cfquant-0.2.24}/LTtx/tx/__init__.py +0 -0
  16. {cfquant-0.2.22 → cfquant-0.2.24}/LTtx/tx/tx.py +0 -0
  17. {cfquant-0.2.22 → cfquant-0.2.24}/MANIFEST.in +0 -0
  18. {cfquant-0.2.22 → cfquant-0.2.24}/README.md +0 -0
  19. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant/__init__.py +0 -0
  20. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant/account_routing.py +0 -0
  21. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant/batch_orders.py +0 -0
  22. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant/cftrader.py +0 -0
  23. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant/channels.py +0 -0
  24. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant/cli.py +0 -0
  25. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant/client.py +0 -0
  26. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant/config.py +0 -0
  27. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant/hub.py +0 -0
  28. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant/level2.py +0 -0
  29. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant/logging_i18n.py +0 -0
  30. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant/normal_bridge.py +0 -0
  31. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant/order_meta.py +0 -0
  32. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant/pipe_bridge.py +0 -0
  33. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant/pipe_client.py +0 -0
  34. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant/pipe_hub.py +0 -0
  35. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant/pipe_transport.py +0 -0
  36. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant/protocol.py +0 -0
  37. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant/qmt_bridge.py +0 -0
  38. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant/qmt_strategy_deploy.py +0 -0
  39. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant/qmt_strategy_package.py +0 -0
  40. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant/qmt_strategy_runtime.py +0 -0
  41. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant/runtime_report.py +0 -0
  42. {cfquant-0.2.22 → cfquant-0.2.24}/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
  43. {cfquant-0.2.22 → cfquant-0.2.24}/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
  44. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant/tests/12_/350/256/242/345/215/225/345/244/207/346/263/250/346/265/213/350/257/225.py" +0 -0
  45. {cfquant-0.2.22 → cfquant-0.2.24}/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
  46. {cfquant-0.2.22 → cfquant-0.2.24}/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
  47. {cfquant-0.2.22 → cfquant-0.2.24}/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
  48. {cfquant-0.2.22 → cfquant-0.2.24}/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
  49. {cfquant-0.2.22 → cfquant-0.2.24}/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
  50. {cfquant-0.2.22 → cfquant-0.2.24}/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
  51. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant/tests/1_/350/241/214/346/203/205/346/216/245/346/224/266/346/265/213/350/257/225.py" +0 -0
  52. {cfquant-0.2.22 → cfquant-0.2.24}/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
  53. {cfquant-0.2.22 → cfquant-0.2.24}/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
  54. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant/tests/22_/350/277/236/346/216/245/350/257/212/346/226/255/346/265/213/350/257/225.py" +0 -0
  55. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant/tests/23_/347/244/272/344/276/213/351/205/215/347/275/256/346/265/213/350/257/225.py" +0 -0
  56. {cfquant-0.2.22 → cfquant-0.2.24}/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
  57. {cfquant-0.2.22 → cfquant-0.2.24}/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
  58. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant/tests/26_/345/205/250/346/216/250/350/256/242/351/230/205/346/265/213/350/257/225.py" +0 -0
  59. {cfquant-0.2.22 → cfquant-0.2.24}/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
  60. {cfquant-0.2.22 → cfquant-0.2.24}/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
  61. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant/tests/29_Level2/350/241/214/346/203/205/346/265/213/350/257/225.py" +0 -0
  62. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant/tests/2_/346/225/260/346/215/256/350/216/267/345/217/226/346/265/213/350/257/225.py" +0 -0
  63. {cfquant-0.2.22 → cfquant-0.2.24}/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
  64. {cfquant-0.2.22 → cfquant-0.2.24}/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
  65. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant/tests/3_/346/225/260/346/215/256/344/270/213/350/275/275/346/265/213/350/257/225.py" +0 -0
  66. {cfquant-0.2.22 → cfquant-0.2.24}/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
  67. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant/tests/5_/347/234/237/345/256/236/344/270/213/345/215/225/346/265/213/350/257/225.py" +0 -0
  68. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant/tests/6_/345/233/236/350/260/203/346/265/213/350/257/225.py" +0 -0
  69. {cfquant-0.2.22 → cfquant-0.2.24}/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
  70. {cfquant-0.2.22 → cfquant-0.2.24}/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
  71. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant/tests/9_/345/217/257/346/222/244/350/256/242/345/215/225/346/265/213/350/257/225.py" +0 -0
  72. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant/tests/_helpers.py +0 -0
  73. {cfquant-0.2.22 → cfquant-0.2.24}/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
  74. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant/tests/readme.md +0 -0
  75. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant/tests/test_api_tester_browser.py +0 -0
  76. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant/tests/test_api_tester_web.py +0 -0
  77. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant/tests/test_binding_delete_confirmation.py +0 -0
  78. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant/tests/test_cftrader.py +0 -0
  79. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant/tests/test_cftrader_tutorial.py +0 -0
  80. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant/tests/test_cftrader_web.py +0 -0
  81. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant/tests/test_credit_compat.py +0 -0
  82. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant/tests/test_json_serialization.py +0 -0
  83. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant/tests/test_python_api_reference.py +0 -0
  84. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant/tests/test_qmt_strategy_browser.py +0 -0
  85. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant/tests/test_qmt_strategy_deploy.py +0 -0
  86. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant/tests/test_qmt_strategy_web.py +0 -0
  87. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant/tests/test_tutorial_reader.py +0 -0
  88. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant/tests/test_tx_auto_import.py +0 -0
  89. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant/tx.py +0 -0
  90. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant/tx_trade_bridge.py +0 -0
  91. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant/xtconstant.py +0 -0
  92. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant/xtdata.py +0 -0
  93. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant/xttrader.py +0 -0
  94. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant/xttype.py +0 -0
  95. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant.egg-info/SOURCES.txt +0 -0
  96. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant.egg-info/dependency_links.txt +0 -0
  97. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant.egg-info/entry_points.txt +0 -0
  98. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant.egg-info/requires.txt +0 -0
  99. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant.egg-info/top_level.txt +0 -0
  100. {cfquant-0.2.22 → cfquant-0.2.24}/cfquant_pipe_hub.py +0 -0
  101. {cfquant-0.2.22 → cfquant-0.2.24}/qmt_scripts/CFQUANT.py +0 -0
  102. {cfquant-0.2.22 → cfquant-0.2.24}/qmt_scripts/CFQUANT_CTYPE_ALL_LOWLAT.py +0 -0
  103. {cfquant-0.2.22 → cfquant-0.2.24}/qmt_scripts/CFQUANT_LITE.py +0 -0
  104. {cfquant-0.2.22 → cfquant-0.2.24}/qmt_scripts/CFQUANT_TRADE_LOWLAT.py +0 -0
  105. {cfquant-0.2.22 → cfquant-0.2.24}/qmt_scripts/__init__.py +0 -0
  106. {cfquant-0.2.22 → cfquant-0.2.24}/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
  107. {cfquant-0.2.22 → cfquant-0.2.24}/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
  108. {cfquant-0.2.22 → cfquant-0.2.24}/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" +0 -0
  109. {cfquant-0.2.22 → cfquant-0.2.24}/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" +0 -0
  110. {cfquant-0.2.22 → cfquant-0.2.24}/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
  111. {cfquant-0.2.22 → cfquant-0.2.24}/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
  112. {cfquant-0.2.22 → cfquant-0.2.24}/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
  113. {cfquant-0.2.22 → cfquant-0.2.24}/requirements.txt +0 -0
  114. {cfquant-0.2.22 → cfquant-0.2.24}/setup.cfg +0 -0
  115. {cfquant-0.2.22 → cfquant-0.2.24}/web_dashboard/__init__.py +0 -0
  116. {cfquant-0.2.22 → cfquant-0.2.24}/web_dashboard/api-tester.js +0 -0
  117. {cfquant-0.2.22 → cfquant-0.2.24}/web_dashboard/avatars/amber-pulse.svg +0 -0
  118. {cfquant-0.2.22 → cfquant-0.2.24}/web_dashboard/avatars/copper-grid.svg +0 -0
  119. {cfquant-0.2.22 → cfquant-0.2.24}/web_dashboard/avatars/market-blue.svg +0 -0
  120. {cfquant-0.2.22 → cfquant-0.2.24}/web_dashboard/avatars/rose-circuit.svg +0 -0
  121. {cfquant-0.2.22 → cfquant-0.2.24}/web_dashboard/avatars/signal-green.svg +0 -0
  122. {cfquant-0.2.22 → cfquant-0.2.24}/web_dashboard/avatars/slate-wave.svg +0 -0
  123. {cfquant-0.2.22 → cfquant-0.2.24}/web_dashboard/avatars/teal-orbit.svg +0 -0
  124. {cfquant-0.2.22 → cfquant-0.2.24}/web_dashboard/avatars/violet-node.svg +0 -0
  125. {cfquant-0.2.22 → cfquant-0.2.24}/web_dashboard/cftrader-api-data.js +0 -0
  126. {cfquant-0.2.22 → cfquant-0.2.24}/web_dashboard/favicon.svg +0 -0
  127. {cfquant-0.2.22 → cfquant-0.2.24}/web_dashboard/image/qmt_deploy/image-10.png +0 -0
  128. {cfquant-0.2.22 → cfquant-0.2.24}/web_dashboard/image/qmt_deploy/image-11.png +0 -0
  129. {cfquant-0.2.22 → cfquant-0.2.24}/web_dashboard/image/qmt_deploy/image-12.png +0 -0
  130. {cfquant-0.2.22 → cfquant-0.2.24}/web_dashboard/image/qmt_deploy/image-13.png +0 -0
  131. {cfquant-0.2.22 → cfquant-0.2.24}/web_dashboard/image/qmt_deploy/image-14.png +0 -0
  132. {cfquant-0.2.22 → cfquant-0.2.24}/web_dashboard/image/qmt_deploy/image-15.png +0 -0
  133. {cfquant-0.2.22 → cfquant-0.2.24}/web_dashboard/image/qmt_deploy/image-16.png +0 -0
  134. {cfquant-0.2.22 → cfquant-0.2.24}/web_dashboard/image/qmt_deploy/image-17.png +0 -0
  135. {cfquant-0.2.22 → cfquant-0.2.24}/web_dashboard/image/qmt_deploy/image-18.png +0 -0
  136. {cfquant-0.2.22 → cfquant-0.2.24}/web_dashboard/image/qmt_deploy/image-19.png +0 -0
  137. {cfquant-0.2.22 → cfquant-0.2.24}/web_dashboard/image/qmt_deploy/image-20.png +0 -0
  138. {cfquant-0.2.22 → cfquant-0.2.24}/web_dashboard/image/qmt_deploy/image-21.png +0 -0
  139. {cfquant-0.2.22 → cfquant-0.2.24}/web_dashboard/image/qmt_deploy/image-4.png +0 -0
  140. {cfquant-0.2.22 → cfquant-0.2.24}/web_dashboard/image/qmt_deploy/image-5.png +0 -0
  141. {cfquant-0.2.22 → cfquant-0.2.24}/web_dashboard/image/qmt_deploy/image-6.png +0 -0
  142. {cfquant-0.2.22 → cfquant-0.2.24}/web_dashboard/image/qmt_deploy/image-7.png +0 -0
  143. {cfquant-0.2.22 → cfquant-0.2.24}/web_dashboard/image/qmt_deploy/image-8.png +0 -0
  144. {cfquant-0.2.22 → cfquant-0.2.24}/web_dashboard/image/qmt_deploy/image-9.png +0 -0
  145. {cfquant-0.2.22 → cfquant-0.2.24}/web_dashboard/python-api-data.js +0 -0
  146. {cfquant-0.2.22 → cfquant-0.2.24}/web_dashboard/python-api.js +0 -0
  147. {cfquant-0.2.22 → cfquant-0.2.24}/web_dashboard/styles.css +0 -0
  148. {cfquant-0.2.22 → cfquant-0.2.24}/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.22
3
+ Version: 0.2.24
4
4
  Summary: QMT local bridge and xtquant-compatible Python client.
5
5
  License: MIT
6
6
  Requires-Python: >=3.8
@@ -114,7 +114,7 @@ def _run_pip_command(
114
114
  ):
115
115
  kwargs = {
116
116
  "cwd": str(project_root),
117
- "env": python_environment(clear_pythonpath=True),
117
+ "env": pip_environment(),
118
118
  "stdout": subprocess.PIPE,
119
119
  "stderr": subprocess.STDOUT,
120
120
  "text": True,
@@ -318,7 +318,7 @@ def run_editable_install(
318
318
  })
319
319
  kwargs = {
320
320
  "cwd": str(project_root),
321
- "env": python_environment(clear_pythonpath=True),
321
+ "env": pip_environment(),
322
322
  "stdout": subprocess.PIPE,
323
323
  "stderr": subprocess.STDOUT,
324
324
  "text": True,
@@ -413,6 +413,15 @@ def python_environment(clear_pythonpath=False):
413
413
  return env
414
414
 
415
415
 
416
+ def pip_environment():
417
+ env = python_environment(clear_pythonpath=True)
418
+ # Legacy Windows egg-link files use the system encoding, not UTF-8.
419
+ if sys.platform == "win32":
420
+ env["PYTHONUTF8"] = "0"
421
+ env["PYTHONIOENCODING"] = "utf-8"
422
+ return env
423
+
424
+
416
425
  def neutral_check_cwd():
417
426
  return Path(tempfile.gettempdir()).resolve()
418
427
 
@@ -423,7 +432,8 @@ def run(args, cwd, quiet=False, clear_pythonpath=False):
423
432
  return subprocess.run(
424
433
  list(args),
425
434
  cwd=str(cwd),
426
- env=python_environment(clear_pythonpath=clear_pythonpath),
435
+ env=(pip_environment() if list(args)[1:3] == ["-m", "pip"]
436
+ else python_environment(clear_pythonpath=clear_pythonpath)),
427
437
  stdout=stdout,
428
438
  stderr=stderr,
429
439
  )
@@ -441,6 +451,27 @@ def is_cfquant_installed(python_exe=None):
441
451
  return completed.returncode == 0
442
452
 
443
453
 
454
+ def project_package_version(project_root):
455
+ try:
456
+ import tomllib
457
+ except ImportError:
458
+ from pip._vendor import tomli as tomllib
459
+ with (Path(project_root) / "pyproject.toml").open("rb") as stream:
460
+ return str(tomllib.load(stream)["project"]["version"])
461
+
462
+
463
+ def installed_package_version(python_exe=None):
464
+ completed = subprocess.run(
465
+ [python_exe or sys.executable, "-c",
466
+ "from importlib.metadata import version; print(version('cfquant'))"],
467
+ cwd=str(neutral_check_cwd()),
468
+ env=python_environment(clear_pythonpath=True),
469
+ stdout=subprocess.PIPE, stderr=subprocess.PIPE,
470
+ text=True, encoding="utf-8", timeout=30,
471
+ )
472
+ return completed.stdout.strip() if completed.returncode == 0 else ""
473
+
474
+
444
475
  def ensure_cfquant_installed(project_root, python_exe=None):
445
476
  project_root = Path(project_root).resolve()
446
477
  if not (project_root / "pyproject.toml").is_file():
@@ -448,9 +479,11 @@ def ensure_cfquant_installed(project_root, python_exe=None):
448
479
  return 2
449
480
 
450
481
  python_exe = python_exe or sys.executable
451
- print("Checking whether %s is installed in this Python environment..." % PACKAGE_NAME)
452
- if is_cfquant_installed(python_exe):
453
- print("%s and its runtime dependencies are ready; skip editable source install." % PACKAGE_NAME)
482
+ expected = project_package_version(project_root)
483
+ installed = installed_package_version(python_exe)
484
+ print("cfquant installed=%s project=%s" % (installed or "missing", expected))
485
+ if installed == expected:
486
+ print("Package versions match; skip installation.")
454
487
  return 0
455
488
 
456
489
  command = editable_install_args(project_root, python_exe)
@@ -470,8 +503,8 @@ def ensure_cfquant_installed(project_root, python_exe=None):
470
503
  print("[ERROR] source install failed with exit code %s." % completed.returncode, file=sys.stderr)
471
504
  return completed.returncode
472
505
 
473
- if not is_cfquant_installed(python_exe):
474
- print("[ERROR] %s is still not visible after source install." % PACKAGE_NAME, file=sys.stderr)
506
+ if installed_package_version(python_exe) != expected:
507
+ print("[ERROR] Installed cfquant version does not match the project after installation.", file=sys.stderr)
475
508
  return 1
476
509
  print("%s package is installed." % PACKAGE_NAME)
477
510
  return 0
@@ -1,12 +1,44 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  import json
3
3
  import subprocess
4
+ import sys
4
5
  from pathlib import Path
5
6
 
7
+ import pytest
6
8
  from cfquant import _editable_install
7
9
  from cfquant import cli
8
10
 
9
11
 
12
+ def test_pip_environment_preserves_parent_settings(monkeypatch):
13
+ monkeypatch.setenv("PYTHONUTF8", "1")
14
+ monkeypatch.setenv("PYTHONPATH", "inherited-source")
15
+ monkeypatch.setattr(sys, "platform", "win32")
16
+ env = _editable_install.pip_environment()
17
+ assert env["PYTHONUTF8"] == "0"
18
+ assert env["PYTHONIOENCODING"] == "utf-8"
19
+ assert "PYTHONPATH" not in env
20
+ assert _editable_install.os.environ["PYTHONUTF8"] == "1"
21
+
22
+
23
+ @pytest.mark.skipif(sys.platform != "win32", reason="Windows legacy encoding")
24
+ def test_pip_scans_legacy_egg_link_without_modifying_it(tmp_path):
25
+ target = tmp_path / "\u79c1\u4eba\u6587\u4ef6"
26
+ try:
27
+ contents = (str(target) + "\n").encode("mbcs")
28
+ except UnicodeEncodeError:
29
+ pytest.skip("System encoding cannot represent the fixture path")
30
+ target.mkdir()
31
+ link = tmp_path / "legacy.egg-link"
32
+ link.write_bytes(contents)
33
+ result = _editable_install._run_pip_command(
34
+ [sys.executable, "-m", "pip", "list", "--path", str(tmp_path),
35
+ "--format=json", "--disable-pip-version-check"],
36
+ tmp_path,
37
+ )
38
+ assert result["ok"], result["output"]
39
+ assert link.read_bytes() == contents
40
+
41
+
10
42
  def test_editable_install_uses_split_arguments(tmp_path):
11
43
  command = _editable_install.editable_install_args(tmp_path, python_exe="python")
12
44
 
@@ -260,8 +292,8 @@ def test_run_editable_install_falls_back_to_source_install(monkeypatch, tmp_path
260
292
 
261
293
 
262
294
  def test_ensure_cfquant_installed_falls_back_to_source_install(monkeypatch, tmp_path):
263
- (tmp_path / "pyproject.toml").write_text("[project]\nname = 'cfquant'\n", encoding="utf-8")
264
- installed_checks = [False, True]
295
+ (tmp_path / "pyproject.toml").write_text("[project]\nname = 'cfquant'\nversion = '0.2.23'\n", encoding="utf-8")
296
+ installed_checks = ["0.2.22", "0.2.23"]
265
297
  calls = []
266
298
 
267
299
  def fake_is_installed(python_exe=None):
@@ -277,7 +309,7 @@ def test_ensure_cfquant_installed_falls_back_to_source_install(monkeypatch, tmp_
277
309
  result.returncode = 7 if "--editable" in args else 0
278
310
  return result
279
311
 
280
- monkeypatch.setattr(_editable_install, "is_cfquant_installed", fake_is_installed)
312
+ monkeypatch.setattr(_editable_install, "installed_package_version", fake_is_installed)
281
313
  monkeypatch.setattr(_editable_install, "run", fake_run)
282
314
 
283
315
  assert _editable_install.ensure_cfquant_installed(tmp_path, python_exe="python") == 0
@@ -286,6 +318,20 @@ def test_ensure_cfquant_installed_falls_back_to_source_install(monkeypatch, tmp_
286
318
  assert calls[1][0][-1] == "."
287
319
 
288
320
 
321
+ @pytest.mark.parametrize("installed", ["", "0.2.22", "0.2.24", "0.2.23"])
322
+ def test_startup_installs_only_when_version_differs(monkeypatch, tmp_path, installed):
323
+ (tmp_path / "pyproject.toml").write_text("[project]\nversion='0.2.23'\n", encoding="utf-8")
324
+ versions = iter([installed, "0.2.23"])
325
+ monkeypatch.setattr(_editable_install, "installed_package_version", lambda exe: next(versions))
326
+ calls = []
327
+ def install(args, cwd):
328
+ calls.append(args)
329
+ return subprocess.CompletedProcess(args, 0)
330
+ monkeypatch.setattr(_editable_install, "run", install)
331
+ assert _editable_install.ensure_cfquant_installed(tmp_path, "python") == 0
332
+ assert len(calls) == (0 if installed == "0.2.23" else 1)
333
+
334
+
289
335
  def test_start_script_passes_dot_not_trailing_dp0():
290
336
  script = (Path(__file__).resolve().parents[2] / "start_cfquant.bat").read_text(encoding="utf-8")
291
337
 
@@ -36,7 +36,7 @@ def project_updater(tmp_path, monkeypatch):
36
36
  monkeypatch.setattr(
37
37
  updater,
38
38
  "_run_editable_install",
39
- lambda reason: {"attempted": False, "ok": True, "reason": reason},
39
+ lambda reason: pytest.fail("Web updates must not run pip"),
40
40
  )
41
41
  monkeypatch.setattr(updater, "_require_editable_install", lambda result: None)
42
42
  return web, updater, project_root
@@ -2,4 +2,4 @@
2
2
  """cfquant core version metadata."""
3
3
 
4
4
  __version__ = "core_20260917_01"
5
- WEB_VERSION = "web_20260917_01"
5
+ WEB_VERSION = "web_20260917_03"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cfquant
3
- Version: 0.2.22
3
+ Version: 0.2.24
4
4
  Summary: QMT local bridge and xtquant-compatible Python client.
5
5
  License: MIT
6
6
  Requires-Python: >=3.8
@@ -9038,18 +9038,11 @@ class CfquantProjectUpdater(object):
9038
9038
  try:
9039
9039
  self._set_operation_phase("restore", "正在恢复选中的项目版本")
9040
9040
  self._restore_backup(selected)
9041
- self._set_operation_phase("install", "正在刷新当前 Python 环境中的源码安装")
9042
- editable_install = self._run_editable_install("project_rollback")
9043
- self._require_editable_install(editable_install)
9041
+ editable_install = {"deferred": True, "attempted": False}
9044
9042
  self._set_operation_phase("deploy", "正在同步回滚后的 cfquant 核心到 QMT 目录")
9045
9043
  qmt_core_deploy = auto_deploy_qmt_core_for_all_accounts(source_dir=BASE_DIR)
9046
9044
  except Exception:
9047
9045
  self._restore_backup(rollback_backup)
9048
- try:
9049
- recovery_install = self._run_editable_install("project_rollback_recovery")
9050
- self._require_editable_install(recovery_install)
9051
- except Exception as recovery_error:
9052
- safe_print("project rollback editable install recovery failed: %s" % recovery_error)
9053
9046
  raise
9054
9047
  self._write_rollback_meta(
9055
9048
  selected,
@@ -9113,9 +9106,7 @@ class CfquantProjectUpdater(object):
9113
9106
  shutil.copy2(src, dst)
9114
9107
  copied.append(rel_path)
9115
9108
  entry_info = self._entry_update_info(changed)
9116
- self._set_operation_phase("install", "正在刷新当前 Python 环境中的源码安装")
9117
- editable_install = self._run_editable_install("project_update")
9118
- self._require_editable_install(editable_install)
9109
+ editable_install = {"deferred": True, "attempted": False}
9119
9110
  self._set_operation_phase("deploy", "正在同步最新 cfquant 核心到 QMT 目录")
9120
9111
  qmt_core_deploy = auto_deploy_qmt_core_for_all_accounts(source_dir=BASE_DIR)
9121
9112
  qmt_deploy_summary = qmt_core_deploy.get("summary") or {}
@@ -9156,12 +9147,6 @@ class CfquantProjectUpdater(object):
9156
9147
  }
9157
9148
  except Exception:
9158
9149
  self._restore_backup(backup)
9159
- if editable_install and editable_install.get("attempted"):
9160
- try:
9161
- recovery_install = self._run_editable_install("project_update_recovery")
9162
- self._require_editable_install(recovery_install)
9163
- except Exception as recovery_error:
9164
- safe_print("project update editable install recovery failed: %s" % recovery_error)
9165
9150
  raise
9166
9151
 
9167
9152
  def _run_editable_install(self, reason):
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "cfquant"
7
- version = "0.2.22"
7
+ version = "0.2.24"
8
8
  description = "QMT local bridge and xtquant-compatible Python client."
9
9
  license = {text = "MIT"}
10
10
  readme = "README.md"
@@ -1,4 +1,4 @@
1
- const FRONTEND_VERSION = 'web_20260917_01';
1
+ const FRONTEND_VERSION = 'web_20260917_03';
2
2
 
3
3
  const state = {
4
4
  accountId: '',
@@ -5466,8 +5466,7 @@ function qmtUpdateProgressSteps(kind) {
5466
5466
  { key: 'upload', label: '上传项目源码 zip', percent: 42 },
5467
5467
  { key: 'backup', label: '备份当前 Web 项目', percent: 58 },
5468
5468
  { key: 'install', label: '替换 Web 项目文件', percent: 82 },
5469
- { key: 'editable-install', label: '更新 Python SDK', percent: 88 },
5470
- { key: 'restart', label: '重启 Web 服务', percent: 94 },
5469
+ { key: 'restart', label: '重启并检查 SDK 版本', percent: 94 },
5471
5470
  { key: 'done', label: '更新完成', percent: 100 },
5472
5471
  ];
5473
5472
  }
@@ -5476,8 +5475,7 @@ function qmtUpdateProgressSteps(kind) {
5476
5475
  { key: 'prepare', label: '确认 Web 回滚目标', percent: 12 },
5477
5476
  { key: 'backup', label: '备份当前 Web 项目', percent: 36 },
5478
5477
  { key: 'restore', label: '恢复选中备份', percent: 76 },
5479
- { key: 'editable-install', label: '更新 Python SDK', percent: 88 },
5480
- { key: 'restart', label: '重启 Web 服务', percent: 94 },
5478
+ { key: 'restart', label: '重启并检查 SDK 版本', percent: 94 },
5481
5479
  { key: 'done', label: '回滚完成', percent: 100 },
5482
5480
  ];
5483
5481
  }
@@ -5487,8 +5485,7 @@ function qmtUpdateProgressSteps(kind) {
5487
5485
  { key: 'download', label: '连接官网并下载发布包', percent: 38 },
5488
5486
  { key: 'backup', label: '备份当前 Web 项目', percent: 58 },
5489
5487
  { key: 'install', label: '替换 Web 项目文件', percent: 82 },
5490
- { key: 'editable-install', label: '更新 Python SDK', percent: 88 },
5491
- { key: 'restart', label: '重启 Web 服务', percent: 94 },
5488
+ { key: 'restart', label: '重启并检查 SDK 版本', percent: 94 },
5492
5489
  { key: 'done', label: '更新完成', percent: 100 },
5493
5490
  ];
5494
5491
  }
@@ -5,7 +5,7 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1">
6
6
  <title>cfquant 控制台</title>
7
7
  <link rel="icon" type="image/svg+xml" href="/favicon.svg">
8
- <link rel="stylesheet" href="/styles.css?v=web_20260917_01">
8
+ <link rel="stylesheet" href="/styles.css?v=web_20260917_03">
9
9
  </head>
10
10
  <body>
11
11
  <div id="webAuthOverlay" class="auth-overlay hidden" aria-live="polite">
@@ -3176,6 +3176,6 @@ else:
3176
3176
  <script src="/cftrader-api-data.js?v=20260911_02"></script>
3177
3177
  <script src="/api-tester.js?v=20260916_apikey_01"></script>
3178
3178
  <script src="/python-api.js?v=20260911_api_test_01"></script>
3179
- <script src="/app.js?v=web_20260917_01"></script>
3179
+ <script src="/app.js?v=web_20260917_03"></script>
3180
3180
  </body>
3181
3181
  </html>
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes