cfquant 0.2.48__tar.gz → 0.2.49__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.48 → cfquant-0.2.49}/PKG-INFO +1 -1
  2. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/qmt_strategy_deploy.py +45 -27
  3. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/tests/test_lite_deployment_paths.py +29 -0
  4. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/tests/test_qmt_strategy_deploy.py +62 -0
  5. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/tests/test_qmt_strategy_web.py +2 -2
  6. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/version.py +2 -2
  7. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant.egg-info/PKG-INFO +1 -1
  8. {cfquant-0.2.48 → cfquant-0.2.49}/pyproject.toml +1 -1
  9. {cfquant-0.2.48 → cfquant-0.2.49}/qmt_scripts/CFQUANT_LITE.py +1 -1
  10. {cfquant-0.2.48 → cfquant-0.2.49}/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
  11. {cfquant-0.2.48 → cfquant-0.2.49}/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
  12. {cfquant-0.2.48 → cfquant-0.2.49}/web_dashboard/app.js +1 -1
  13. {cfquant-0.2.48 → cfquant-0.2.49}/web_dashboard/index.html +2 -2
  14. {cfquant-0.2.48 → cfquant-0.2.49}/LICENSE +0 -0
  15. {cfquant-0.2.48 → cfquant-0.2.49}/LTtx/__init__.py +0 -0
  16. {cfquant-0.2.48 → cfquant-0.2.49}/LTtx/tx/Config.txt +0 -0
  17. {cfquant-0.2.48 → cfquant-0.2.49}/LTtx/tx/LTtx_server.py +0 -0
  18. {cfquant-0.2.48 → cfquant-0.2.49}/LTtx/tx/__init__.py +0 -0
  19. {cfquant-0.2.48 → cfquant-0.2.49}/LTtx/tx/tx.py +0 -0
  20. {cfquant-0.2.48 → cfquant-0.2.49}/MANIFEST.in +0 -0
  21. {cfquant-0.2.48 → cfquant-0.2.49}/README.md +0 -0
  22. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/__init__.py +0 -0
  23. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/_editable_install.py +0 -0
  24. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/account_routing.py +0 -0
  25. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/batch_orders.py +0 -0
  26. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/build_info.py +0 -0
  27. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/callback_dispatcher.py +0 -0
  28. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/cftrader.py +0 -0
  29. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/channels.py +0 -0
  30. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/cli.py +0 -0
  31. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/client.py +0 -0
  32. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/config.py +0 -0
  33. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/hub.py +0 -0
  34. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/level2.py +0 -0
  35. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/log_management.py +0 -0
  36. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/logging_i18n.py +0 -0
  37. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/management.py +0 -0
  38. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/normal_bridge.py +0 -0
  39. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/order_identity.py +0 -0
  40. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/order_meta.py +0 -0
  41. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/pipe_bridge.py +0 -0
  42. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/pipe_client.py +0 -0
  43. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/pipe_hub.py +0 -0
  44. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/pipe_transport.py +0 -0
  45. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/protocol.py +0 -0
  46. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/qmt_bridge.py +0 -0
  47. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/qmt_strategy_package.py +0 -0
  48. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/qmt_strategy_runtime.py +0 -0
  49. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/runtime_report.py +0 -0
  50. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/stock_connect.py +0 -0
  51. {cfquant-0.2.48 → cfquant-0.2.49}/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
  52. {cfquant-0.2.48 → cfquant-0.2.49}/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
  53. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/tests/12_/350/256/242/345/215/225/345/244/207/346/263/250/346/265/213/350/257/225.py" +0 -0
  54. {cfquant-0.2.48 → cfquant-0.2.49}/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
  55. {cfquant-0.2.48 → cfquant-0.2.49}/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
  56. {cfquant-0.2.48 → cfquant-0.2.49}/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
  57. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/tests/16_/345/220/257/345/212/250/345/256/211/350/243/205/346/265/213/350/257/225.py" +0 -0
  58. {cfquant-0.2.48 → cfquant-0.2.49}/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
  59. {cfquant-0.2.48 → cfquant-0.2.49}/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
  60. {cfquant-0.2.48 → cfquant-0.2.49}/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
  61. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/tests/1_/350/241/214/346/203/205/346/216/245/346/224/266/346/265/213/350/257/225.py" +0 -0
  62. {cfquant-0.2.48 → cfquant-0.2.49}/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
  63. {cfquant-0.2.48 → cfquant-0.2.49}/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
  64. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/tests/22_/350/277/236/346/216/245/350/257/212/346/226/255/346/265/213/350/257/225.py" +0 -0
  65. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/tests/23_/347/244/272/344/276/213/351/205/215/347/275/256/346/265/213/350/257/225.py" +0 -0
  66. {cfquant-0.2.48 → cfquant-0.2.49}/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
  67. {cfquant-0.2.48 → cfquant-0.2.49}/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
  68. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/tests/26_/345/205/250/346/216/250/350/256/242/351/230/205/346/265/213/350/257/225.py" +0 -0
  69. {cfquant-0.2.48 → cfquant-0.2.49}/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
  70. {cfquant-0.2.48 → cfquant-0.2.49}/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
  71. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/tests/29_Level2/350/241/214/346/203/205/346/265/213/350/257/225.py" +0 -0
  72. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/tests/2_/346/225/260/346/215/256/350/216/267/345/217/226/346/265/213/350/257/225.py" +0 -0
  73. {cfquant-0.2.48 → cfquant-0.2.49}/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
  74. {cfquant-0.2.48 → cfquant-0.2.49}/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
  75. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/tests/32_/345/274/202/346/255/245/344/270/213/345/215/225/346/265/213/350/257/225.py" +0 -0
  76. {cfquant-0.2.48 → cfquant-0.2.49}/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
  77. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/tests/3_/346/225/260/346/215/256/344/270/213/350/275/275/346/265/213/350/257/225.py" +0 -0
  78. {cfquant-0.2.48 → cfquant-0.2.49}/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
  79. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/tests/5_/347/234/237/345/256/236/344/270/213/345/215/225/346/265/213/350/257/225.py" +0 -0
  80. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/tests/6_/345/233/236/350/260/203/346/265/213/350/257/225.py" +0 -0
  81. {cfquant-0.2.48 → cfquant-0.2.49}/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
  82. {cfquant-0.2.48 → cfquant-0.2.49}/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
  83. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/tests/9_/345/217/257/346/222/244/350/256/242/345/215/225/346/265/213/350/257/225.py" +0 -0
  84. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/tests/_helpers.py +0 -0
  85. {cfquant-0.2.48 → cfquant-0.2.49}/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
  86. {cfquant-0.2.48 → cfquant-0.2.49}/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
  87. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/tests/readme.md +0 -0
  88. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/tests/test_api_tester_browser.py +0 -0
  89. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/tests/test_api_tester_web.py +0 -0
  90. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/tests/test_binding_delete_confirmation.py +0 -0
  91. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/tests/test_callback_requests.py +0 -0
  92. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/tests/test_cftrader.py +0 -0
  93. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/tests/test_cftrader_tutorial.py +0 -0
  94. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/tests/test_cftrader_web.py +0 -0
  95. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/tests/test_credit_compat.py +0 -0
  96. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/tests/test_json_serialization.py +0 -0
  97. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/tests/test_lite_account_routing.py +0 -0
  98. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/tests/test_local_trading_calendar.py +0 -0
  99. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/tests/test_log_management.py +0 -0
  100. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/tests/test_management.py +0 -0
  101. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/tests/test_management_batch.py +0 -0
  102. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/tests/test_market_data_fallback.py +0 -0
  103. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/tests/test_order_identity.py +0 -0
  104. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/tests/test_order_time_callback.py +0 -0
  105. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/tests/test_project_updater.py +0 -0
  106. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/tests/test_python_api_reference.py +0 -0
  107. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/tests/test_qmt_strategy_browser.py +0 -0
  108. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/tests/test_setup_qmt_prompt.py +0 -0
  109. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/tests/test_startup_recovery.py +0 -0
  110. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/tests/test_stock_connect.py +0 -0
  111. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/tests/test_stock_connect_browser.py +0 -0
  112. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/tests/test_trading_dates.py +0 -0
  113. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/tests/test_tutorial_reader.py +0 -0
  114. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/tests/test_tx_auto_import.py +0 -0
  115. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/trading_calendar.py +0 -0
  116. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/tx.py +0 -0
  117. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/tx_trade_bridge.py +0 -0
  118. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/xtconstant.py +0 -0
  119. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/xtdata.py +0 -0
  120. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/xttrader.py +0 -0
  121. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant/xttype.py +0 -0
  122. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant.egg-info/SOURCES.txt +0 -0
  123. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant.egg-info/dependency_links.txt +0 -0
  124. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant.egg-info/entry_points.txt +0 -0
  125. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant.egg-info/requires.txt +0 -0
  126. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant.egg-info/top_level.txt +0 -0
  127. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant_pipe_hub.py +0 -0
  128. {cfquant-0.2.48 → cfquant-0.2.49}/cfquant_web_server.py +0 -0
  129. {cfquant-0.2.48 → cfquant-0.2.49}/qmt_scripts/CFQUANT.py +0 -0
  130. {cfquant-0.2.48 → cfquant-0.2.49}/qmt_scripts/CFQUANT_CTYPE_ALL_LOWLAT.py +0 -0
  131. {cfquant-0.2.48 → cfquant-0.2.49}/qmt_scripts/CFQUANT_TRADE_LOWLAT.py +0 -0
  132. {cfquant-0.2.48 → cfquant-0.2.49}/qmt_scripts/__init__.py +0 -0
  133. {cfquant-0.2.48 → cfquant-0.2.49}/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.48 → cfquant-0.2.49}/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.48 → cfquant-0.2.49}/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.48 → cfquant-0.2.49}/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.48 → cfquant-0.2.49}/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.48 → cfquant-0.2.49}/requirements.txt +0 -0
  139. {cfquant-0.2.48 → cfquant-0.2.49}/setup.cfg +0 -0
  140. {cfquant-0.2.48 → cfquant-0.2.49}/web_dashboard/__init__.py +0 -0
  141. {cfquant-0.2.48 → cfquant-0.2.49}/web_dashboard/api-tester.js +0 -0
  142. {cfquant-0.2.48 → cfquant-0.2.49}/web_dashboard/avatars/amber-pulse.svg +0 -0
  143. {cfquant-0.2.48 → cfquant-0.2.49}/web_dashboard/avatars/copper-grid.svg +0 -0
  144. {cfquant-0.2.48 → cfquant-0.2.49}/web_dashboard/avatars/market-blue.svg +0 -0
  145. {cfquant-0.2.48 → cfquant-0.2.49}/web_dashboard/avatars/rose-circuit.svg +0 -0
  146. {cfquant-0.2.48 → cfquant-0.2.49}/web_dashboard/avatars/signal-green.svg +0 -0
  147. {cfquant-0.2.48 → cfquant-0.2.49}/web_dashboard/avatars/slate-wave.svg +0 -0
  148. {cfquant-0.2.48 → cfquant-0.2.49}/web_dashboard/avatars/teal-orbit.svg +0 -0
  149. {cfquant-0.2.48 → cfquant-0.2.49}/web_dashboard/avatars/violet-node.svg +0 -0
  150. {cfquant-0.2.48 → cfquant-0.2.49}/web_dashboard/cftrader-api-data.js +0 -0
  151. {cfquant-0.2.48 → cfquant-0.2.49}/web_dashboard/docs.js +0 -0
  152. {cfquant-0.2.48 → cfquant-0.2.49}/web_dashboard/favicon.svg +0 -0
  153. {cfquant-0.2.48 → cfquant-0.2.49}/web_dashboard/image/qmt_deploy/image-10.png +0 -0
  154. {cfquant-0.2.48 → cfquant-0.2.49}/web_dashboard/image/qmt_deploy/image-11.png +0 -0
  155. {cfquant-0.2.48 → cfquant-0.2.49}/web_dashboard/image/qmt_deploy/image-12.png +0 -0
  156. {cfquant-0.2.48 → cfquant-0.2.49}/web_dashboard/image/qmt_deploy/image-13.png +0 -0
  157. {cfquant-0.2.48 → cfquant-0.2.49}/web_dashboard/image/qmt_deploy/image-14.png +0 -0
  158. {cfquant-0.2.48 → cfquant-0.2.49}/web_dashboard/image/qmt_deploy/image-15.png +0 -0
  159. {cfquant-0.2.48 → cfquant-0.2.49}/web_dashboard/image/qmt_deploy/image-16.png +0 -0
  160. {cfquant-0.2.48 → cfquant-0.2.49}/web_dashboard/image/qmt_deploy/image-17.png +0 -0
  161. {cfquant-0.2.48 → cfquant-0.2.49}/web_dashboard/image/qmt_deploy/image-18.png +0 -0
  162. {cfquant-0.2.48 → cfquant-0.2.49}/web_dashboard/image/qmt_deploy/image-19.png +0 -0
  163. {cfquant-0.2.48 → cfquant-0.2.49}/web_dashboard/image/qmt_deploy/image-20.png +0 -0
  164. {cfquant-0.2.48 → cfquant-0.2.49}/web_dashboard/image/qmt_deploy/image-21.png +0 -0
  165. {cfquant-0.2.48 → cfquant-0.2.49}/web_dashboard/image/qmt_deploy/image-4.png +0 -0
  166. {cfquant-0.2.48 → cfquant-0.2.49}/web_dashboard/image/qmt_deploy/image-5.png +0 -0
  167. {cfquant-0.2.48 → cfquant-0.2.49}/web_dashboard/image/qmt_deploy/image-6.png +0 -0
  168. {cfquant-0.2.48 → cfquant-0.2.49}/web_dashboard/image/qmt_deploy/image-7.png +0 -0
  169. {cfquant-0.2.48 → cfquant-0.2.49}/web_dashboard/image/qmt_deploy/image-8.png +0 -0
  170. {cfquant-0.2.48 → cfquant-0.2.49}/web_dashboard/image/qmt_deploy/image-9.png +0 -0
  171. {cfquant-0.2.48 → cfquant-0.2.49}/web_dashboard/python-api-data.js +0 -0
  172. {cfquant-0.2.48 → cfquant-0.2.49}/web_dashboard/python-api.js +0 -0
  173. {cfquant-0.2.48 → cfquant-0.2.49}/web_dashboard/styles.css +0 -0
  174. {cfquant-0.2.48 → cfquant-0.2.49}/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.48
3
+ Version: 0.2.49
4
4
  Summary: QMT local bridge and xtquant-compatible Python client.
5
5
  License: MIT
6
6
  Requires-Python: >=3.8
@@ -343,13 +343,23 @@ def managed_source(source_path, identity_path, identity, descriptor):
343
343
  for start, end, text in sorted(replacements, reverse=True):
344
344
  lines[start:end] = [text]
345
345
  lines[0] = "# coding: utf-8\n"
346
- source = "".join(lines)
347
- ast.parse(source, feature_version=(3, 6))
348
- helper = Path(__file__).with_name("qmt_strategy_runtime.py").read_text(encoding="utf-8")
349
- generated = ("# coding: utf-8\n" + helper + "\n_cq_descriptor = " + ascii(descriptor)
350
- + "\n_cq_guard = _CqStrategyLease(_cq_descriptor).acquire()\n"
351
- + "_cq_guard.namespace = globals()\ntry:\n"
352
- + " exec(compile(%s, %s, 'exec'), globals())\n" % (ascii(source), ascii(str(source_path)))
346
+ source = "".join(lines)
347
+ ast.parse(source, feature_version=(3, 6))
348
+ helper = Path(__file__).with_name("qmt_strategy_runtime.py").read_text(encoding="utf-8")
349
+ source_lines = source.splitlines(keepends=True)
350
+ # Adjacent literals compile to the same source string, while keeping every
351
+ # entry line visible in QMT's editor. A single ascii(source) hid thousands
352
+ # of Lite lines behind the same short lease wrapper as the other modes.
353
+ embedded = "\n".join(" " + ascii(line) for line in source_lines)
354
+ header = ("# coding: utf-8\n# cfquant managed strategy\n"
355
+ + "# Mode: " + ascii(descriptor["mode"]) + "\n"
356
+ + "# Entry: " + ascii(Path(source_path).name) + "\n"
357
+ + "# Embedded entry: %d lines, preserved line by line below.\n" % len(source_lines)
358
+ + "# The lease wrapper is shared by all modes.\n\n")
359
+ generated = (header + helper + "\n_cq_descriptor = " + ascii(descriptor)
360
+ + "\n_cq_guard = _CqStrategyLease(_cq_descriptor).acquire()\n"
361
+ + "_cq_guard.namespace = globals()\ntry:\n"
362
+ + " exec(compile((\n%s\n ), %s, 'exec'), globals())\n" % (embedded, ascii(str(source_path)))
353
363
  + " for _cq_name in ('_cf_bridge', '_normal_bridge', '_trade_bridge'):\n"
354
364
  + " if globals().get(_cq_name) is not None:\n"
355
365
  + " globals()[_cq_name].account_type = %s\n" % ascii(identity["account_type"])
@@ -408,13 +418,17 @@ class QmtStrategyManager:
408
418
  job.pop("deploy_retry_count", None)
409
419
  job.pop("deploy_retry_after", None)
410
420
 
411
- def _clear_generation_transition(self, job):
412
- changed = False
413
- for key in ("control_generation", "accepted_generations"):
414
- if key in job:
415
- job.pop(key, None)
416
- changed = True
417
- return changed
421
+ def _clear_generation_transition(self, job):
422
+ changed = False
423
+ for key in ("control_generation", "accepted_generations"):
424
+ if key in job:
425
+ job.pop(key, None)
426
+ changed = True
427
+ for role in job.get("roles", []):
428
+ if "reimport_from_generation" in role:
429
+ role.pop("reimport_from_generation", None)
430
+ changed = True
431
+ return changed
418
432
 
419
433
  def _prepare_generation_transition(self, job, previous=None):
420
434
  """Keep old same-name containers valid until the replacement starts."""
@@ -467,19 +481,23 @@ class QmtStrategyManager:
467
481
  )
468
482
  job["control_generation"] = legacy
469
483
  job["accepted_generations"] = accepted
470
- if previous is None:
471
- for role in job.get("roles", []):
472
- if _runtime_generation(role) == legacy:
473
- updates = {
474
- "reimport_required": True,
475
- "imported": False,
476
- "queued": False,
477
- "model_prepared": False,
478
- }
479
- for key, value in updates.items():
480
- if role.get(key) != value:
481
- role[key] = value
482
- changed = True
484
+ for role in job.get("roles", []):
485
+ if (_runtime_generation(role) == legacy
486
+ and role.get("reimport_from_generation") != legacy):
487
+ # The old report survives QMT shutdown and import. Handle it
488
+ # once per replacement job, including across Web restarts;
489
+ # resetting every poll destroys the prepared/imported model.
490
+ updates = {
491
+ "reimport_from_generation": legacy,
492
+ "reimport_required": True,
493
+ "imported": False,
494
+ "queued": False,
495
+ "model_prepared": False,
496
+ }
497
+ for key, value in updates.items():
498
+ if role.get(key) != value:
499
+ role[key] = value
500
+ changed = True
483
501
  if changed and job.get("state") == "error":
484
502
  self._state(job, "waiting_exit")
485
503
  return changed
@@ -98,6 +98,35 @@ def test_edit_without_mode_preserves_lite_binding_and_payload(web_deployment):
98
98
  assert_lite_package(manager, root)
99
99
 
100
100
 
101
+ def test_edit_advanced_binding_to_lite_retires_both_advanced_models(web_deployment):
102
+ web, manager, row, infos, root = web_deployment
103
+ trade_root = root.with_name("QMT_trade")
104
+ shutil.copytree(root, trade_root)
105
+ row.update(mode="lttx", qmt_trade_dir=str(trade_root / "bin.x64"))
106
+ row["qmt_strategy"]["autorun"] = True
107
+ trade_info = dict(infos[0], qmt_role="trade", core_dir=str(trade_root / "bin.x64"),
108
+ path=str(trade_root / "bin.x64" / "cfquant_bridge_config.json"))
109
+ manager.configure(row, infos + [trade_info])
110
+ advanced_name = "CFQ_%s_LTTX" % row["account_id"]
111
+ assert models(root)[advanced_name].getAttribute("startupAutorun") == "1"
112
+ assert models(trade_root)[advanced_name].getAttribute("startupAutorun") == "1"
113
+
114
+ body = {key: row[key] for key in ("account_id", "account_type", "bridge_id", "qmt_dir")}
115
+ result = web.save_account_runtime_config(dict(body, mode="lite", qmt_trade_dir=""))
116
+
117
+ assert result["account"]["mode"] == "lite"
118
+ assert result["account"]["qmt_trade_dir"] == ""
119
+ assert not result["qmt_strategy_deploy"].get("error")
120
+ job = assert_lite_package(manager, root)
121
+ assert job["roles"][0]["name"] == "CFQ_%s_LITE" % row["account_id"]
122
+ assert models(root)[job["roles"][0]["name"]].getAttribute("startupAutorun") == "1"
123
+ assert models(root)[advanced_name].getAttribute("startupAutorun") == "0"
124
+ assert models(trade_root)[advanced_name].getAttribute("startupAutorun") == "0"
125
+ trade_job = next(job for job in manager.jobs.values() if job["root"] == str(trade_root))
126
+ assert trade_job["enabled"] is False
127
+ assert trade_job["state"] == "disabled"
128
+
129
+
101
130
  @pytest.mark.parametrize("single_account", [False, True])
102
131
  def test_shared_qmt_directory_does_not_drop_lite_market_entries(single_account, web_deployment):
103
132
  web, _, row, _, root = web_deployment
@@ -464,6 +464,8 @@ def test_all_managed_entry_variants_compile_without_executing(mode, deployment):
464
464
  assert document["detail"]["realName"] == role["name"] + ".py"
465
465
  assert document["formulaCatalog"][0].encode("utf-8", "surrogateescape").hex() == "ced2b5c4b2dfc2d4"
466
466
  ast.parse(document["content"], feature_version=(3, 6))
467
+ assert "# Mode: %r\n" % mode in document["content"]
468
+ assert "# Entry: %r\n" % Path(role["source"]).name in document["content"]
467
469
  if mode == "lite":
468
470
  embedded = [node.args[0].value for node in ast.walk(ast.parse(document["content"]))
469
471
  if isinstance(node, ast.Call) and isinstance(node.func, ast.Name)
@@ -477,6 +479,8 @@ def test_all_managed_entry_variants_compile_without_executing(mode, deployment):
477
479
  if isinstance(node, (ast.FunctionDef, ast.ClassDef))}
478
480
  assert definitions(actual) == definitions(original)
479
481
  assert len(embedded[0].splitlines()) > 7000
482
+ assert len(document["content"].splitlines()) > 7000
483
+ assert max(map(len, document["content"].splitlines())) < 2000
480
484
  descriptor = {"mode": mode, "generation": "test", "role": "SH", "control_path": "test", "lease_dir": "test"}
481
485
  variants = list((SCRIPTS / "同账号独立市场").glob("*.py")) + [SCRIPTS / "CFQUANT_TRADE_LOWLAT.py"]
482
486
  for path in variants:
@@ -522,6 +526,64 @@ def test_process_once_detects_stale_runtime_generation_and_queues_reimport(deplo
522
526
  assert (root / "formulas" / (role["name"] + ".rzrk")).is_file()
523
527
 
524
528
 
529
+ @pytest.mark.parametrize("report_state", ["running", "stopped"])
530
+ @pytest.mark.parametrize("previous_mode", ["lite", "lttx"])
531
+ def test_old_runtime_report_does_not_reset_prepared_replacement(deployment, report_state, previous_mode):
532
+ manager, row, infos, running, root = deployment
533
+ row["mode"] = "lite"
534
+ manager.configure(row, infos)
535
+ finish_import(manager, root)
536
+ manager.process_once()
537
+ first = copy.deepcopy(next(iter(manager.jobs.values())))
538
+ _write_json(first["roles"][0]["runtime_status_path"], {
539
+ "state": report_state, "generation": first["generation"], "updated_at": time.time() - 120,
540
+ })
541
+ if previous_mode != "lite":
542
+ row["mode"] = previous_mode
543
+ manager.configure(row, infos)
544
+ finish_import(manager, root)
545
+ manager.process_once()
546
+
547
+ row["mode"] = "lite"
548
+ manager.configure(row, infos, force=True)
549
+ job = next(iter(manager.jobs.values()))
550
+ role = job["roles"][0]
551
+ queue = root / "formulas" / (role["name"] + ".rzrk")
552
+ assert queue.is_file()
553
+ assert role["model_prepared"] is True
554
+
555
+ # The old heartbeat remains on disk until QMT has imported and started
556
+ # the new strategy. Repeated polling or a Web restart must not undo it.
557
+ manager = QmtStrategyManager(manager.path, SCRIPTS, lambda root: running[0])
558
+ job = next(iter(manager.jobs.values()))
559
+ role = job["roles"][0]
560
+ running[0] = True
561
+ manager.process_once()
562
+ manager.process_once()
563
+ assert role["reimport_required"] is False
564
+ assert role["model_prepared"] is True
565
+ assert role["queued"] is True
566
+ assert job["state"] == "waiting_manual_start"
567
+
568
+ finish_import(manager, root)
569
+ container = root / "python" / (role["name"] + ".py")
570
+ running[0] = False
571
+ manager.process_once()
572
+ manager.process_once()
573
+ assert container.is_file()
574
+ assert not queue.exists()
575
+ assert role["imported"] is True
576
+ assert job["state"] == "configured"
577
+
578
+ _write_json(role["runtime_status_path"], {
579
+ "state": "running", "generation": job["generation"], "updated_at": time.time(),
580
+ })
581
+ running[0] = True
582
+ manager.process_once()
583
+ assert job["state"] == "running"
584
+ assert not job.get("control_generation")
585
+
586
+
525
587
  @pytest.mark.parametrize("encoding", ["utf-8", "gbk", "gb18030"])
526
588
  def test_managed_package_executes_after_qmt_encoding_with_chinese_paths(tmp_path, encoding):
527
589
  directory = tmp_path / "\u534e\u6cf0\u8bc1\u5238 QMT \u6a21\u62df"
@@ -304,7 +304,7 @@ def test_save_and_delete_call_deployment_manager(web_config, monkeypatch):
304
304
  assert calls[-1] == ("reconcile", set())
305
305
 
306
306
 
307
- def test_save_can_start_qmt_auto_login_and_persist_restart_times(web_config, monkeypatch):
307
+ def test_save_can_start_qmt_auto_login_and_persist_restart_times(web_config, monkeypatch, tmp_path):
308
308
  web, config = web_config
309
309
  calls = []
310
310
  monkeypatch.setattr(web, "auto_deploy_qmt_core_for_account", lambda *args, **kwargs: {})
@@ -325,7 +325,7 @@ def test_save_can_start_qmt_auto_login_and_persist_restart_times(web_config, mon
325
325
  data = web.save_account_runtime_config({
326
326
  "account_id": "8885060548",
327
327
  "display_name": "国金证券",
328
- "qmt_dir": r"D:\国金证券QMT交易端\bin.x64",
328
+ "qmt_dir": str(tmp_path / "QMT" / "bin.x64"),
329
329
  "qmt_auto_login": {"enabled": True, "restart_times": ["6:30", "06:30", "18:05"]},
330
330
  })
331
331
  assert data["qmt_auto_login"]["pid"] == 1234
@@ -1,5 +1,5 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  """cfquant core version metadata."""
3
3
 
4
- __version__ = "0.2.48"
5
- WEB_VERSION = "web_20261011_02"
4
+ __version__ = "0.2.49"
5
+ WEB_VERSION = "web_20261012_01"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cfquant
3
- Version: 0.2.48
3
+ Version: 0.2.49
4
4
  Summary: QMT local bridge and xtquant-compatible Python client.
5
5
  License: MIT
6
6
  Requires-Python: >=3.8
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "cfquant"
7
- version = "0.2.48"
7
+ version = "0.2.49"
8
8
  description = "QMT local bridge and xtquant-compatible Python client."
9
9
  license = {text = "MIT"}
10
10
  readme = "README.md"
@@ -562,7 +562,7 @@ def _resolve_batch_order_ids(bridge, account, pending, before_ids):
562
562
  time.sleep(min(0.05, remaining))
563
563
  # END GENERATED CFTRADER BATCH
564
564
 
565
- CORE_VERSION = "0.2.48"
565
+ CORE_VERSION = "0.2.49"
566
566
  LITE_ENTRY_VERSION = "lite_20260828_01"
567
567
 
568
568
  _CANCELABLE_ORDER_STATUS_VALUES = set([48, 49, 50, 55])
@@ -616,7 +616,7 @@ def _resolve_batch_order_ids(bridge, account, pending, before_ids):
616
616
  time.sleep(min(0.05, remaining))
617
617
  # END GENERATED CFTRADER BATCH
618
618
 
619
- CORE_VERSION = "0.2.48"
619
+ CORE_VERSION = "0.2.49"
620
620
  LITE_ENTRY_VERSION = "lite_20260828_01"
621
621
 
622
622
  _CANCELABLE_ORDER_STATUS_VALUES = set([48, 49, 50, 55])
@@ -616,7 +616,7 @@ def _resolve_batch_order_ids(bridge, account, pending, before_ids):
616
616
  time.sleep(min(0.05, remaining))
617
617
  # END GENERATED CFTRADER BATCH
618
618
 
619
- CORE_VERSION = "0.2.48"
619
+ CORE_VERSION = "0.2.49"
620
620
  LITE_ENTRY_VERSION = "lite_20260828_01"
621
621
 
622
622
  _CANCELABLE_ORDER_STATUS_VALUES = set([48, 49, 50, 55])
@@ -1,4 +1,4 @@
1
- const FRONTEND_VERSION = 'web_20261011_02';
1
+ const FRONTEND_VERSION = 'web_20261012_01';
2
2
 
3
3
  const state = {
4
4
  accountId: '',
@@ -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_20261011_02">
8
+ <link rel="stylesheet" href="/styles.css?v=web_20261012_01">
9
9
  </head>
10
10
  <body>
11
11
  <div id="webAuthOverlay" class="auth-overlay hidden" aria-live="polite">
@@ -3223,6 +3223,6 @@ else:
3223
3223
  <script src="/api-tester.js?v=20260916_apikey_01"></script>
3224
3224
  <script src="/python-api.js?v=20260925_trading_dates_01"></script>
3225
3225
  <script src="/docs.js?v=20260925_data_docs_01"></script>
3226
- <script src="/app.js?v=web_20261011_02"></script>
3226
+ <script src="/app.js?v=web_20261012_01"></script>
3227
3227
  </body>
3228
3228
  </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
File without changes
File without changes
File without changes
File without changes