cfquant 0.2.36__tar.gz → 0.2.38__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 (154) hide show
  1. {cfquant-0.2.36 → cfquant-0.2.38}/PKG-INFO +1 -1
  2. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/qmt_bridge.py +3 -1
  3. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/qmt_strategy_deploy.py +10 -12
  4. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/tests/test_qmt_strategy_deploy.py +11 -3
  5. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/tx_trade_bridge.py +4 -2
  6. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/version.py +2 -2
  7. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/xttype.py +60 -18
  8. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant.egg-info/PKG-INFO +1 -1
  9. {cfquant-0.2.36 → cfquant-0.2.38}/pyproject.toml +1 -1
  10. {cfquant-0.2.36 → cfquant-0.2.38}/web_dashboard/app.js +45 -7
  11. {cfquant-0.2.36 → cfquant-0.2.38}/web_dashboard/index.html +2 -3
  12. {cfquant-0.2.36 → cfquant-0.2.38}/LICENSE +0 -0
  13. {cfquant-0.2.36 → cfquant-0.2.38}/LTtx/__init__.py +0 -0
  14. {cfquant-0.2.36 → cfquant-0.2.38}/LTtx/tx/Config.txt +0 -0
  15. {cfquant-0.2.36 → cfquant-0.2.38}/LTtx/tx/LTtx_server.py +0 -0
  16. {cfquant-0.2.36 → cfquant-0.2.38}/LTtx/tx/__init__.py +0 -0
  17. {cfquant-0.2.36 → cfquant-0.2.38}/LTtx/tx/tx.py +0 -0
  18. {cfquant-0.2.36 → cfquant-0.2.38}/MANIFEST.in +0 -0
  19. {cfquant-0.2.36 → cfquant-0.2.38}/README.md +0 -0
  20. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/__init__.py +0 -0
  21. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/_editable_install.py +0 -0
  22. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/account_routing.py +0 -0
  23. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/batch_orders.py +0 -0
  24. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/cftrader.py +0 -0
  25. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/channels.py +0 -0
  26. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/cli.py +0 -0
  27. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/client.py +0 -0
  28. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/config.py +0 -0
  29. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/hub.py +0 -0
  30. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/level2.py +0 -0
  31. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/log_management.py +0 -0
  32. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/logging_i18n.py +0 -0
  33. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/normal_bridge.py +0 -0
  34. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/order_meta.py +0 -0
  35. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/pipe_bridge.py +0 -0
  36. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/pipe_client.py +0 -0
  37. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/pipe_hub.py +0 -0
  38. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/pipe_transport.py +0 -0
  39. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/protocol.py +0 -0
  40. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/qmt_strategy_package.py +0 -0
  41. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/qmt_strategy_runtime.py +0 -0
  42. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/runtime_report.py +0 -0
  43. {cfquant-0.2.36 → cfquant-0.2.38}/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
  44. {cfquant-0.2.36 → cfquant-0.2.38}/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
  45. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/tests/12_/350/256/242/345/215/225/345/244/207/346/263/250/346/265/213/350/257/225.py" +0 -0
  46. {cfquant-0.2.36 → cfquant-0.2.38}/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
  47. {cfquant-0.2.36 → cfquant-0.2.38}/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
  48. {cfquant-0.2.36 → cfquant-0.2.38}/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
  49. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/tests/16_/345/220/257/345/212/250/345/256/211/350/243/205/346/265/213/350/257/225.py" +0 -0
  50. {cfquant-0.2.36 → cfquant-0.2.38}/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
  51. {cfquant-0.2.36 → cfquant-0.2.38}/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
  52. {cfquant-0.2.36 → cfquant-0.2.38}/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
  53. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/tests/1_/350/241/214/346/203/205/346/216/245/346/224/266/346/265/213/350/257/225.py" +0 -0
  54. {cfquant-0.2.36 → cfquant-0.2.38}/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
  55. {cfquant-0.2.36 → cfquant-0.2.38}/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
  56. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/tests/22_/350/277/236/346/216/245/350/257/212/346/226/255/346/265/213/350/257/225.py" +0 -0
  57. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/tests/23_/347/244/272/344/276/213/351/205/215/347/275/256/346/265/213/350/257/225.py" +0 -0
  58. {cfquant-0.2.36 → cfquant-0.2.38}/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
  59. {cfquant-0.2.36 → cfquant-0.2.38}/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
  60. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/tests/26_/345/205/250/346/216/250/350/256/242/351/230/205/346/265/213/350/257/225.py" +0 -0
  61. {cfquant-0.2.36 → cfquant-0.2.38}/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
  62. {cfquant-0.2.36 → cfquant-0.2.38}/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
  63. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/tests/29_Level2/350/241/214/346/203/205/346/265/213/350/257/225.py" +0 -0
  64. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/tests/2_/346/225/260/346/215/256/350/216/267/345/217/226/346/265/213/350/257/225.py" +0 -0
  65. {cfquant-0.2.36 → cfquant-0.2.38}/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
  66. {cfquant-0.2.36 → cfquant-0.2.38}/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
  67. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/tests/32_/345/274/202/346/255/245/344/270/213/345/215/225/346/265/213/350/257/225.py" +0 -0
  68. {cfquant-0.2.36 → cfquant-0.2.38}/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
  69. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/tests/3_/346/225/260/346/215/256/344/270/213/350/275/275/346/265/213/350/257/225.py" +0 -0
  70. {cfquant-0.2.36 → cfquant-0.2.38}/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
  71. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/tests/5_/347/234/237/345/256/236/344/270/213/345/215/225/346/265/213/350/257/225.py" +0 -0
  72. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/tests/6_/345/233/236/350/260/203/346/265/213/350/257/225.py" +0 -0
  73. {cfquant-0.2.36 → cfquant-0.2.38}/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
  74. {cfquant-0.2.36 → cfquant-0.2.38}/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
  75. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/tests/9_/345/217/257/346/222/244/350/256/242/345/215/225/346/265/213/350/257/225.py" +0 -0
  76. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/tests/_helpers.py +0 -0
  77. {cfquant-0.2.36 → cfquant-0.2.38}/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
  78. {cfquant-0.2.36 → cfquant-0.2.38}/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
  79. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/tests/readme.md +0 -0
  80. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/tests/test_api_tester_browser.py +0 -0
  81. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/tests/test_api_tester_web.py +0 -0
  82. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/tests/test_binding_delete_confirmation.py +0 -0
  83. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/tests/test_cftrader.py +0 -0
  84. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/tests/test_cftrader_tutorial.py +0 -0
  85. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/tests/test_cftrader_web.py +0 -0
  86. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/tests/test_credit_compat.py +0 -0
  87. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/tests/test_json_serialization.py +0 -0
  88. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/tests/test_log_management.py +0 -0
  89. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/tests/test_management_batch.py +0 -0
  90. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/tests/test_project_updater.py +0 -0
  91. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/tests/test_python_api_reference.py +0 -0
  92. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/tests/test_qmt_strategy_browser.py +0 -0
  93. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/tests/test_qmt_strategy_web.py +0 -0
  94. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/tests/test_tutorial_reader.py +0 -0
  95. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/tests/test_tx_auto_import.py +0 -0
  96. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/tx.py +0 -0
  97. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/xtconstant.py +0 -0
  98. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/xtdata.py +0 -0
  99. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant/xttrader.py +0 -0
  100. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant.egg-info/SOURCES.txt +0 -0
  101. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant.egg-info/dependency_links.txt +0 -0
  102. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant.egg-info/entry_points.txt +0 -0
  103. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant.egg-info/requires.txt +0 -0
  104. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant.egg-info/top_level.txt +0 -0
  105. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant_pipe_hub.py +0 -0
  106. {cfquant-0.2.36 → cfquant-0.2.38}/cfquant_web_server.py +0 -0
  107. {cfquant-0.2.36 → cfquant-0.2.38}/qmt_scripts/CFQUANT.py +0 -0
  108. {cfquant-0.2.36 → cfquant-0.2.38}/qmt_scripts/CFQUANT_CTYPE_ALL_LOWLAT.py +0 -0
  109. {cfquant-0.2.36 → cfquant-0.2.38}/qmt_scripts/CFQUANT_LITE.py +0 -0
  110. {cfquant-0.2.36 → cfquant-0.2.38}/qmt_scripts/CFQUANT_TRADE_LOWLAT.py +0 -0
  111. {cfquant-0.2.36 → cfquant-0.2.38}/qmt_scripts/__init__.py +0 -0
  112. {cfquant-0.2.36 → cfquant-0.2.38}/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
  113. {cfquant-0.2.36 → cfquant-0.2.38}/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
  114. {cfquant-0.2.36 → cfquant-0.2.38}/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
  115. {cfquant-0.2.36 → cfquant-0.2.38}/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
  116. {cfquant-0.2.36 → cfquant-0.2.38}/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
  117. {cfquant-0.2.36 → cfquant-0.2.38}/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
  118. {cfquant-0.2.36 → cfquant-0.2.38}/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
  119. {cfquant-0.2.36 → cfquant-0.2.38}/requirements.txt +0 -0
  120. {cfquant-0.2.36 → cfquant-0.2.38}/setup.cfg +0 -0
  121. {cfquant-0.2.36 → cfquant-0.2.38}/web_dashboard/__init__.py +0 -0
  122. {cfquant-0.2.36 → cfquant-0.2.38}/web_dashboard/api-tester.js +0 -0
  123. {cfquant-0.2.36 → cfquant-0.2.38}/web_dashboard/avatars/amber-pulse.svg +0 -0
  124. {cfquant-0.2.36 → cfquant-0.2.38}/web_dashboard/avatars/copper-grid.svg +0 -0
  125. {cfquant-0.2.36 → cfquant-0.2.38}/web_dashboard/avatars/market-blue.svg +0 -0
  126. {cfquant-0.2.36 → cfquant-0.2.38}/web_dashboard/avatars/rose-circuit.svg +0 -0
  127. {cfquant-0.2.36 → cfquant-0.2.38}/web_dashboard/avatars/signal-green.svg +0 -0
  128. {cfquant-0.2.36 → cfquant-0.2.38}/web_dashboard/avatars/slate-wave.svg +0 -0
  129. {cfquant-0.2.36 → cfquant-0.2.38}/web_dashboard/avatars/teal-orbit.svg +0 -0
  130. {cfquant-0.2.36 → cfquant-0.2.38}/web_dashboard/avatars/violet-node.svg +0 -0
  131. {cfquant-0.2.36 → cfquant-0.2.38}/web_dashboard/cftrader-api-data.js +0 -0
  132. {cfquant-0.2.36 → cfquant-0.2.38}/web_dashboard/favicon.svg +0 -0
  133. {cfquant-0.2.36 → cfquant-0.2.38}/web_dashboard/image/qmt_deploy/image-10.png +0 -0
  134. {cfquant-0.2.36 → cfquant-0.2.38}/web_dashboard/image/qmt_deploy/image-11.png +0 -0
  135. {cfquant-0.2.36 → cfquant-0.2.38}/web_dashboard/image/qmt_deploy/image-12.png +0 -0
  136. {cfquant-0.2.36 → cfquant-0.2.38}/web_dashboard/image/qmt_deploy/image-13.png +0 -0
  137. {cfquant-0.2.36 → cfquant-0.2.38}/web_dashboard/image/qmt_deploy/image-14.png +0 -0
  138. {cfquant-0.2.36 → cfquant-0.2.38}/web_dashboard/image/qmt_deploy/image-15.png +0 -0
  139. {cfquant-0.2.36 → cfquant-0.2.38}/web_dashboard/image/qmt_deploy/image-16.png +0 -0
  140. {cfquant-0.2.36 → cfquant-0.2.38}/web_dashboard/image/qmt_deploy/image-17.png +0 -0
  141. {cfquant-0.2.36 → cfquant-0.2.38}/web_dashboard/image/qmt_deploy/image-18.png +0 -0
  142. {cfquant-0.2.36 → cfquant-0.2.38}/web_dashboard/image/qmt_deploy/image-19.png +0 -0
  143. {cfquant-0.2.36 → cfquant-0.2.38}/web_dashboard/image/qmt_deploy/image-20.png +0 -0
  144. {cfquant-0.2.36 → cfquant-0.2.38}/web_dashboard/image/qmt_deploy/image-21.png +0 -0
  145. {cfquant-0.2.36 → cfquant-0.2.38}/web_dashboard/image/qmt_deploy/image-4.png +0 -0
  146. {cfquant-0.2.36 → cfquant-0.2.38}/web_dashboard/image/qmt_deploy/image-5.png +0 -0
  147. {cfquant-0.2.36 → cfquant-0.2.38}/web_dashboard/image/qmt_deploy/image-6.png +0 -0
  148. {cfquant-0.2.36 → cfquant-0.2.38}/web_dashboard/image/qmt_deploy/image-7.png +0 -0
  149. {cfquant-0.2.36 → cfquant-0.2.38}/web_dashboard/image/qmt_deploy/image-8.png +0 -0
  150. {cfquant-0.2.36 → cfquant-0.2.38}/web_dashboard/image/qmt_deploy/image-9.png +0 -0
  151. {cfquant-0.2.36 → cfquant-0.2.38}/web_dashboard/python-api-data.js +0 -0
  152. {cfquant-0.2.36 → cfquant-0.2.38}/web_dashboard/python-api.js +0 -0
  153. {cfquant-0.2.36 → cfquant-0.2.38}/web_dashboard/styles.css +0 -0
  154. {cfquant-0.2.36 → cfquant-0.2.38}/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.36
3
+ Version: 0.2.38
4
4
  Summary: QMT local bridge and xtquant-compatible Python client.
5
5
  License: MIT
6
6
  Requires-Python: >=3.8
@@ -1109,7 +1109,8 @@ class CfquantQmtBridge(object):
1109
1109
  "order_sysid": self._get_value(obj, "m_strOrderSysID"),
1110
1110
  "order_type": self._stock_order_type(obj),
1111
1111
  "order_time": self._first_value(obj, (
1112
- "order_time",
1112
+ "time",
1113
+ "order_time",
1113
1114
  "entrust_time",
1114
1115
  "insert_time",
1115
1116
  "m_strOrderTime",
@@ -1197,6 +1198,7 @@ class CfquantQmtBridge(object):
1197
1198
  "order_sysid": self._get_value(obj, "m_strOrderSysID"),
1198
1199
  "traded_id": self._first_value(obj, ("m_strTradeID", "m_strDealID", "m_nTradeID", "m_nDealID")),
1199
1200
  "trade_time": self._first_value(obj, (
1201
+ "time",
1200
1202
  "trade_time",
1201
1203
  "deal_time",
1202
1204
  "m_strTradeTime",
@@ -188,17 +188,15 @@ def _model_items(document):
188
188
  return section, list(section.getElementsByTagName("item"))
189
189
 
190
190
 
191
- def _strategy_slot_name(account_id, role="normal"):
192
- """Return a deterministic name shared by all modes for one account.
193
-
194
- A mode switch must replace the existing model instead of creating a
195
- second autorun model. Independent SH/SZ routes are the one case where a
196
- single account receives distinct names.
197
- """
198
- account = re.sub(r"[^A-Z0-9]+", "_", str(account_id or "").upper()).strip("_") or "ACCOUNT"
199
- market = str(role or "normal").upper()
200
- suffix = "_%s" % market if market in ("SH", "SZ") else ""
201
- return ("CFQ_%s%s" % (account, suffix))[:64]
191
+ def _strategy_slot_name(account_id, mode="ctypes", role="normal"):
192
+ """Return a deterministic, mode-specific name in cfquant's namespace."""
193
+ account = re.sub(r"[^A-Z0-9]+", "_", str(account_id or "").upper()).strip("_") or "ACCOUNT"
194
+ if len(account) > 20:
195
+ account = account[-5:]
196
+ mode = normalize_strategy_mode(mode).upper()
197
+ market = str(role or "normal").upper()
198
+ suffix = "_%s" % market if market in ("SH", "SZ") else ""
199
+ return ("CFQ_%s_%s%s" % (account, mode, suffix))[:64]
202
200
 
203
201
 
204
202
  def _is_managed_strategy_name(name):
@@ -533,7 +531,7 @@ class QmtStrategyManager:
533
531
  for role in group["roles"]:
534
532
  old_role = previous_roles.get(role["role"])
535
533
  old_name = old_role.get("name") if isinstance(old_role, dict) else ""
536
- expected_name = _strategy_slot_name(row["account_id"], role["role"])
534
+ expected_name = _strategy_slot_name(row["account_id"], mode, role["role"])
537
535
  if old_name == expected_name:
538
536
  role_names[role["role"]] = old_name
539
537
  else:
@@ -14,6 +14,7 @@ from cfquant.qmt_strategy_deploy import (
14
14
  QmtStrategyManager, _account_binding, _atomic_write, _read_document,
15
15
  _write_json, _resolve_account_binding, account_qmt_roots, managed_source, normalize_strategy_settings,
16
16
  normalize_strategy_mode,
17
+ _strategy_slot_name,
17
18
  )
18
19
  from cfquant.qmt_strategy_package import _cipher, build_package
19
20
  from cfquant.qmt_strategy_runtime import _CqStrategyLease
@@ -29,6 +30,11 @@ def test_strategy_mode_aliases_are_canonical(value, expected):
29
30
  assert normalize_strategy_mode(value) == expected
30
31
 
31
32
 
33
+ def test_strategy_name_contains_mode_and_shortens_long_account():
34
+ name = _strategy_slot_name("ACCOUNT-12345678901234567890", "lite")
35
+ assert name == "CFQ_67890_LITE"
36
+
37
+
32
38
  @pytest.fixture
33
39
  def deployment(tmp_path):
34
40
  root = tmp_path / "QMT"
@@ -261,7 +267,9 @@ def test_switch_and_delete_revoke_old_generation_and_disable_its_model(deploymen
261
267
  manager.configure(row, infos)
262
268
  new = next(iter(manager.jobs.values()))
263
269
  assert old["generation"] != new["generation"]
264
- assert old["roles"][0]["name"] == new["roles"][0]["name"]
270
+ assert old["roles"][0]["name"] != new["roles"][0]["name"]
271
+ assert old["roles"][0]["name"].endswith("_CTYPES")
272
+ assert new["roles"][0]["name"].endswith("_LITE")
265
273
  assert json.loads(Path(old["control_path"]).read_text())["mode"] == "lite"
266
274
  running[0] = False
267
275
  manager.process_once()
@@ -289,7 +297,7 @@ def test_historical_generated_name_is_disabled_for_the_same_account(deployment):
289
297
  manager.configure(row, infos)
290
298
  manager.process_once()
291
299
  assert models(root)["CFQ_CTYPES_DEADBEEF_NORMAL"].getAttribute("startupAutorun") == "0"
292
- assert next(iter(manager.jobs.values()))["roles"][0]["name"] == "CFQ_%s" % ACCOUNT
300
+ assert next(iter(manager.jobs.values()))["roles"][0]["name"] == "CFQ_%s_CTYPES" % ACCOUNT
293
301
 
294
302
 
295
303
  def test_independent_identity_for_two_accounts_in_one_qmt(deployment):
@@ -501,7 +509,7 @@ def test_market_routes_only_import_market_entries_and_use_their_own_stock(deploy
501
509
  for role in job["roles"]:
502
510
  model = models(root)[role["name"]]
503
511
  assert model.getAttribute("stock") == {"SH": "SH000300", "SZ": "SZ399001"}[role["role"]]
504
- assert {role["name"] for role in job["roles"]} == {"CFQ_%s_SH" % ACCOUNT, "CFQ_%s_SZ" % ACCOUNT}
512
+ assert {role["name"] for role in job["roles"]} == {"CFQ_%s_CTYPES_SH" % ACCOUNT, "CFQ_%s_CTYPES_SZ" % ACCOUNT}
505
513
 
506
514
 
507
515
  def descriptor(tmp_path, generation, role="normal"):
@@ -3067,7 +3067,8 @@ class TxTradeBridge(object):
3067
3067
  "order_sysid": self._get_value(obj, "m_strOrderSysID"),
3068
3068
  "order_type": self._stock_order_type(obj),
3069
3069
  "order_time": self._first_value(obj, (
3070
- "order_time",
3070
+ "time",
3071
+ "order_time",
3071
3072
  "entrust_time",
3072
3073
  "insert_time",
3073
3074
  "m_strOrderTime",
@@ -3163,7 +3164,8 @@ class TxTradeBridge(object):
3163
3164
  "contract_no": self._first_value(obj, ("contract_no", "m_strCompactNo", "m_strContractNo", "m_strCompactID")),
3164
3165
  "stock_code1": self._first_value(obj, ("stock_code1", "m_stockCode", "m_strStockCode1", "m_strUnderCode")),
3165
3166
  "trade_time": self._first_value(obj, (
3166
- "trade_time",
3167
+ "time",
3168
+ "trade_time",
3167
3169
  "deal_time",
3168
3170
  "m_strTradeTime",
3169
3171
  "m_strDealTime",
@@ -1,5 +1,5 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  """cfquant core version metadata."""
3
3
 
4
- __version__ = "0.2.35"
5
- WEB_VERSION = "web_20260922_03"
4
+ __version__ = "0.2.38"
5
+ WEB_VERSION = "web_20260923_02"
@@ -1,4 +1,5 @@
1
1
  # -*- coding: utf-8 -*-
2
+ import datetime
2
3
  import re
3
4
 
4
5
  from . import xtconstant
@@ -198,31 +199,62 @@ def _coerce_bool(value, default=False):
198
199
  return default
199
200
 
200
201
 
201
- def _coerce_time_int(value, default=0):
202
- """Convert QMT's string trade/order time to xtquant's integer form."""
202
+ def _coerce_timestamp(value, date_value=None, default=0):
203
+ """Convert QMT's display time to MiniQMT's Unix timestamp in seconds."""
203
204
  if value is None or isinstance(value, bool):
204
205
  return default
205
206
  if isinstance(value, int):
206
- return value
207
- if isinstance(value, float):
208
- return int(value) if value.is_integer() else default
209
- text = str(value).strip()
207
+ if value >= 100000000000:
208
+ return value // 1000
209
+ if value >= 100000000:
210
+ return value
211
+ text = str(value)
212
+ elif isinstance(value, float):
213
+ if value >= 100000000000:
214
+ return int(value / 1000)
215
+ if value >= 100000000:
216
+ return int(value)
217
+ text = str(int(value)) if value.is_integer() else str(value)
218
+ else:
219
+ text = str(value).strip()
210
220
  if not text:
211
221
  return default
212
- if re.fullmatch(r"[+-]?\d+", text):
222
+ if re.fullmatch(r"[+-]?\d{9,}", text):
213
223
  try:
214
- return int(text)
224
+ number = int(text)
225
+ return number // 1000 if number >= 100000000000 else number
215
226
  except ValueError:
216
227
  return default
217
228
  digits = re.sub(r"\D", "", text)
218
229
  if not digits:
219
230
  return default
231
+ # HHMMSS / HHMMSSmmm values need the trading date before they can
232
+ # represent the same Unix timestamp exposed by native xtquant.
233
+ if len(digits) not in (5, 6, 8, 9):
234
+ return default
235
+ if len(digits) == 5:
236
+ digits = "0" + digits
237
+ hour, minute, second = int(digits[:2]), int(digits[2:4]), int(digits[4:6])
238
+ microsecond = int((digits[6:] + "000000")[:6]) if len(digits) > 6 else 0
239
+ date_text = re.sub(r"\D", "", str(date_value or ""))
240
+ if len(date_text) != 8:
241
+ return default
220
242
  try:
221
- return int(digits)
222
- except ValueError:
243
+ dt = datetime.datetime.strptime(date_text, "%Y%m%d").replace(
244
+ hour=hour, minute=minute, second=second, microsecond=microsecond,
245
+ tzinfo=datetime.timezone(datetime.timedelta(hours=8)),
246
+ )
247
+ return int(dt.timestamp())
248
+ except (TypeError, ValueError, OverflowError):
223
249
  return default
224
250
 
225
251
 
252
+ def _time_field_timestamp(data, time_names, date_names):
253
+ value = _first_value(data, time_names, default=None)
254
+ date_value = _first_value(data, date_names, default=None)
255
+ return _coerce_timestamp(value, date_value)
256
+
257
+
226
258
  def normalize_order_price_type(value, market=""):
227
259
  """Translate documented QMT broker price enums, preserving unknown values."""
228
260
  try:
@@ -525,8 +557,9 @@ class XtOrder(DictObject):
525
557
  "sysid",
526
558
  "m_strOrderID",
527
559
  ), default="")
528
- _set_first(data, "order_time", (
529
- "entrust_time",
560
+ _set_first(data, "order_time", (
561
+ "time",
562
+ "entrust_time",
530
563
  "insert_time",
531
564
  "m_strOrderTime",
532
565
  "m_strEntrustTime",
@@ -600,7 +633,11 @@ class XtOrder(DictObject):
600
633
  data["stock_code"] = _coerce_text(data.get("stock_code"))
601
634
  data["order_id"] = _coerce_int(data.get("order_id"), -1)
602
635
  data["order_sysid"] = _coerce_text(data.get("order_sysid"))
603
- data["order_time"] = _coerce_time_int(data.get("order_time"))
636
+ data["order_time"] = _time_field_timestamp(
637
+ data,
638
+ ("order_time", "time", "entrust_time", "insert_time", "m_strOrderTime", "m_strEntrustTime", "m_strInsertTime", "m_nOrderTime", "m_nEntrustTime", "m_nInsertTime"),
639
+ ("order_date", "entrust_date", "m_strOrderDate", "m_strEntrustDate", "m_strTradingDay", "m_nOrderDate", "m_nEntrustDate"),
640
+ )
604
641
  for name in ("order_type", "order_volume", "price_type", "traded_volume", "order_status", "direction", "offset_flag"):
605
642
  data[name] = _coerce_int(data.get(name))
606
643
  for name in ("price", "traded_price"):
@@ -636,7 +673,7 @@ class XtCreditOrder(XtOrder):
636
673
  account_id=_coerce_text(data.get("account_id")),
637
674
  stock_code=_coerce_text(data.get("stock_code")),
638
675
  order_id=_coerce_int(data.get("order_id"), -1),
639
- order_time=_coerce_time_int(data.get("order_time")),
676
+ order_time=data.get("order_time", 0),
640
677
  order_type=_coerce_int(data.get("order_type")),
641
678
  order_volume=_coerce_int(data.get("order_volume")),
642
679
  price_type=_coerce_int(data.get("price_type")),
@@ -698,8 +735,9 @@ class XtTrade(DictObject):
698
735
  "m_nTradeID",
699
736
  "m_nDealID",
700
737
  ), default="")
701
- _set_first(data, "traded_time", (
702
- "trade_time",
738
+ _set_first(data, "traded_time", (
739
+ "time",
740
+ "trade_time",
703
741
  "deal_time",
704
742
  "m_strTradeTime",
705
743
  "m_strDealTime",
@@ -763,7 +801,11 @@ class XtTrade(DictObject):
763
801
  data["stock_code"] = _coerce_text(data.get("stock_code"))
764
802
  data["order_type"] = _coerce_int(data.get("order_type"))
765
803
  data["traded_id"] = _coerce_text(data.get("traded_id"))
766
- data["traded_time"] = _coerce_time_int(data.get("traded_time"))
804
+ data["traded_time"] = _time_field_timestamp(
805
+ data,
806
+ ("traded_time", "time", "trade_time", "deal_time", "m_strTradeTime", "m_strDealTime", "m_nTradeTime", "m_nDealTime"),
807
+ ("trade_date", "deal_date", "m_strTradeDate", "m_strDealDate", "m_strTradingDay", "m_nTradeDate", "m_nDealDate"),
808
+ )
767
809
  for name in ("traded_volume",):
768
810
  data[name] = _coerce_int(data.get(name))
769
811
  for name in ("traded_price", "traded_amount", "commission"):
@@ -803,7 +845,7 @@ class XtCreditDeal(DictObject):
803
845
  account_id=_coerce_text(data.get("account_id")),
804
846
  stock_code=_coerce_text(data.get("stock_code")),
805
847
  traded_id=_coerce_text(data.get("traded_id")),
806
- traded_time=_coerce_time_int(data.get("traded_time")),
848
+ traded_time=data.get("traded_time", 0),
807
849
  traded_price=_coerce_float(data.get("traded_price")),
808
850
  traded_volume=_coerce_int(data.get("traded_volume")),
809
851
  order_id=_coerce_int(data.get("order_id"), -1),
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: cfquant
3
- Version: 0.2.36
3
+ Version: 0.2.38
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.36"
7
+ version = "0.2.38"
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_20260922_03';
1
+ const FRONTEND_VERSION = 'web_20260923_02';
2
2
 
3
3
  const state = {
4
4
  accountId: '',
@@ -2500,9 +2500,42 @@ function normalizeApiBaseUrl(value) {
2500
2500
  }
2501
2501
  }
2502
2502
 
2503
+ function isPrivateApiHost(host) {
2504
+ host = String(host || '').trim().toLowerCase();
2505
+ if (!host || host === 'localhost' || host === '::1' || host === '[::1]') return true;
2506
+ if (/^127\./.test(host)) return true;
2507
+ const parts = host.split('.').map((item) => Number(item));
2508
+ if (parts.length !== 4 || parts.some((item) => !Number.isInteger(item) || item < 0 || item > 255)) return false;
2509
+ return parts[0] === 10
2510
+ || parts[0] === 192 && parts[1] === 168
2511
+ || parts[0] === 172 && parts[1] >= 16 && parts[1] <= 31
2512
+ || parts[0] === 169 && parts[1] === 254;
2513
+ }
2514
+
2515
+ function apiBaseUrlForPage(value) {
2516
+ const configured = normalizeApiBaseUrl(value);
2517
+ const page = normalizeApiBaseUrl(window.location.origin);
2518
+ try {
2519
+ const configuredUrl = new URL(configured);
2520
+ const pageUrl = new URL(page);
2521
+ // A saved LAN address belongs to the machine that last ran Web. When the
2522
+ // page is opened from another private/loopback address, follow the current
2523
+ // page so changing the host IP does not leave the tester pointing at the
2524
+ // old machine. Public/explicit remote URLs remain user-configurable.
2525
+ if (isPrivateApiHost(configuredUrl.hostname)
2526
+ && isPrivateApiHost(pageUrl.hostname)
2527
+ && configuredUrl.hostname !== pageUrl.hostname) {
2528
+ return page;
2529
+ }
2530
+ } catch (error) {
2531
+ return page;
2532
+ }
2533
+ return configured;
2534
+ }
2535
+
2503
2536
  function currentApiBaseUrl() {
2504
2537
  const input = $('apiBaseUrlInput');
2505
- if (input && input.value.trim()) return normalizeApiBaseUrl(input.value);
2538
+ if (input && input.value.trim()) return apiBaseUrlForPage(input.value);
2506
2539
  return normalizeApiBaseUrl(window.location.origin);
2507
2540
  }
2508
2541
 
@@ -4616,7 +4649,10 @@ function renderServerAccessLegacy(info) {
4616
4649
 
4617
4650
  const baseInput = $('apiBaseUrlInput');
4618
4651
  if (baseInput && !baseInput.value.trim()) {
4619
- baseInput.value = normalizeApiBaseUrl(state.serverAccess.api_base_url || window.location.origin);
4652
+ baseInput.value = apiBaseUrlForPage(state.serverAccess.api_base_url || window.location.origin);
4653
+ } else if (baseInput && baseInput.value.trim()) {
4654
+ const effectiveBaseUrl = apiBaseUrlForPage(baseInput.value);
4655
+ if (effectiveBaseUrl !== normalizeApiBaseUrl(baseInput.value)) baseInput.value = effectiveBaseUrl;
4620
4656
  }
4621
4657
  updateApiRequestPreview();
4622
4658
  }
@@ -4732,7 +4768,10 @@ function renderServerAccess(info) {
4732
4768
  const baseInput = $('apiBaseUrlInput');
4733
4769
  if (baseInput && !baseInput.value.trim()) {
4734
4770
  const currentUrl = state.serverAccess.api_base_url || state.serverAccess.local_url || window.location.origin;
4735
- baseInput.value = normalizeApiBaseUrl(currentUrl);
4771
+ baseInput.value = apiBaseUrlForPage(currentUrl);
4772
+ } else if (baseInput && baseInput.value.trim()) {
4773
+ const effectiveBaseUrl = apiBaseUrlForPage(baseInput.value);
4774
+ if (effectiveBaseUrl !== normalizeApiBaseUrl(baseInput.value)) baseInput.value = effectiveBaseUrl;
4736
4775
  }
4737
4776
  if (!authEnabled) {
4738
4777
  clearWebAuthToken();
@@ -10369,7 +10408,7 @@ async function loadConfigLegacy() {
10369
10408
  const savedBaseUrl = data.server_access && data.server_access.api_base_url
10370
10409
  ? data.server_access.api_base_url
10371
10410
  : window.location.origin;
10372
- apiBaseInput.value = normalizeApiBaseUrl(savedBaseUrl);
10411
+ apiBaseInput.value = apiBaseUrlForPage(savedBaseUrl);
10373
10412
  }
10374
10413
  renderServerAccess(data.server_access);
10375
10414
  renderUserProfile(data.user_profile);
@@ -10430,7 +10469,7 @@ async function loadConfig() {
10430
10469
  const savedBaseUrl = data.server_access && data.server_access.api_base_url
10431
10470
  ? data.server_access.api_base_url
10432
10471
  : window.location.origin;
10433
- apiBaseInput.value = normalizeApiBaseUrl(savedBaseUrl);
10472
+ apiBaseInput.value = apiBaseUrlForPage(savedBaseUrl);
10434
10473
  }
10435
10474
  renderServerAccess(data.server_access);
10436
10475
  renderUserProfile(data.user_profile);
@@ -14027,4 +14066,3 @@ async function boot() {
14027
14066
  }
14028
14067
 
14029
14068
  boot().catch((error) => log('启动失败', { error: error.message }));
14030
-
@@ -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_20260922_03">
8
+ <link rel="stylesheet" href="/styles.css?v=web_20260923_02">
9
9
  </head>
10
10
  <body>
11
11
  <div id="webAuthOverlay" class="auth-overlay hidden" aria-live="polite">
@@ -3202,7 +3202,6 @@ else:
3202
3202
  <script src="/cftrader-api-data.js?v=20260911_02"></script>
3203
3203
  <script src="/api-tester.js?v=20260916_apikey_01"></script>
3204
3204
  <script src="/python-api.js?v=20260911_api_test_01"></script>
3205
- <script src="/app.js?v=web_20260922_03"></script>
3205
+ <script src="/app.js?v=web_20260923_02"></script>
3206
3206
  </body>
3207
3207
  </html>
3208
-
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