akquant 0.2.46__tar.gz → 0.2.48__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (509) hide show
  1. {akquant-0.2.46 → akquant-0.2.48}/CHANGELOG.md +5 -0
  2. {akquant-0.2.46 → akquant-0.2.48}/Cargo.lock +1 -1
  3. {akquant-0.2.46 → akquant-0.2.48}/Cargo.toml +1 -1
  4. {akquant-0.2.46 → akquant-0.2.48}/PKG-INFO +1 -1
  5. {akquant-0.2.46 → akquant-0.2.48}/pyproject.toml +1 -1
  6. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/__init__.py +1 -1
  7. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/backtest/engine.py +8 -0
  8. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/config.py +7 -0
  9. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/plot/report.py +6 -0
  10. {akquant-0.2.46 → akquant-0.2.48}/src/analysis/python.rs +2 -0
  11. {akquant-0.2.46 → akquant-0.2.48}/src/analysis/result.rs +16 -6
  12. {akquant-0.2.46 → akquant-0.2.48}/src/analysis/tests.rs +94 -0
  13. {akquant-0.2.46 → akquant-0.2.48}/src/engine/core.rs +4 -0
  14. {akquant-0.2.46 → akquant-0.2.48}/src/engine/python.rs +18 -0
  15. akquant-0.2.48/src/pipeline/stages/channel.rs +261 -0
  16. akquant-0.2.48/src/pipeline/stages/cleanup.rs +17 -0
  17. akquant-0.2.48/src/pipeline/stages/data.rs +437 -0
  18. akquant-0.2.48/src/pipeline/stages/execution.rs +72 -0
  19. akquant-0.2.48/src/pipeline/stages/mod.rs +358 -0
  20. akquant-0.2.48/src/pipeline/stages/shared.rs +200 -0
  21. akquant-0.2.48/src/pipeline/stages/statistics.rs +48 -0
  22. akquant-0.2.48/src/pipeline/stages/strategy.rs +169 -0
  23. {akquant-0.2.46 → akquant-0.2.48}/src/statistics/mod.rs +4 -0
  24. {akquant-0.2.46/tests/golden/current → akquant-0.2.48/tests/golden/baselines}/futures_margin/metrics.json +3 -3
  25. {akquant-0.2.46 → akquant-0.2.48}/tests/golden/baselines/option_basic/metrics.json +3 -3
  26. {akquant-0.2.46/tests/golden/current → akquant-0.2.48/tests/golden/baselines}/stock_t1/metrics.json +3 -3
  27. {akquant-0.2.46/tests/golden/baselines → akquant-0.2.48/tests/golden/current}/futures_margin/metrics.json +3 -3
  28. {akquant-0.2.46 → akquant-0.2.48}/tests/golden/current/option_basic/metrics.json +3 -3
  29. {akquant-0.2.46/tests/golden/baselines → akquant-0.2.48/tests/golden/current}/stock_t1/metrics.json +3 -3
  30. akquant-0.2.46/src/pipeline/stages.rs +0 -1513
  31. {akquant-0.2.46 → akquant-0.2.48}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  32. {akquant-0.2.46 → akquant-0.2.48}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  33. {akquant-0.2.46 → akquant-0.2.48}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  34. {akquant-0.2.46 → akquant-0.2.48}/.github/workflows/deploy-docs.yml +0 -0
  35. {akquant-0.2.46 → akquant-0.2.48}/.github/workflows/docs-quality.yml +0 -0
  36. {akquant-0.2.46 → akquant-0.2.48}/.github/workflows/release.yml +0 -0
  37. {akquant-0.2.46 → akquant-0.2.48}/.gitignore +0 -0
  38. {akquant-0.2.46 → akquant-0.2.48}/.pre-commit-config.yaml +0 -0
  39. {akquant-0.2.46 → akquant-0.2.48}/CODE_OF_CONDUCT.md +0 -0
  40. {akquant-0.2.46 → akquant-0.2.48}/CONTRIBUTING.md +0 -0
  41. {akquant-0.2.46 → akquant-0.2.48}/LICENSE +0 -0
  42. {akquant-0.2.46 → akquant-0.2.48}/README.md +0 -0
  43. {akquant-0.2.46 → akquant-0.2.48}/assets/dashboard_preview.png +0 -0
  44. {akquant-0.2.46 → akquant-0.2.48}/assets/logo.svg +0 -0
  45. {akquant-0.2.46 → akquant-0.2.48}/assets/social_preview.png +0 -0
  46. {akquant-0.2.46 → akquant-0.2.48}/docs/CNAME +0 -0
  47. {akquant-0.2.46 → akquant-0.2.48}/docs/assets/akquant-icon.svg +0 -0
  48. {akquant-0.2.46 → akquant-0.2.48}/docs/assets/akquant-logo.svg +0 -0
  49. {akquant-0.2.46 → akquant-0.2.48}/docs/assets/reports/akquant_report.html +0 -0
  50. {akquant-0.2.46 → akquant-0.2.48}/docs/en/advanced/analyzer_plugin_spec.md +0 -0
  51. {akquant-0.2.46 → akquant-0.2.48}/docs/en/advanced/broker_capability_matrix.md +0 -0
  52. {akquant-0.2.46 → akquant-0.2.48}/docs/en/advanced/custom_broker_production_checklist.md +0 -0
  53. {akquant-0.2.46 → akquant-0.2.48}/docs/en/advanced/custom_broker_registry.md +0 -0
  54. {akquant-0.2.46 → akquant-0.2.48}/docs/en/advanced/data_feed_adapter_spec.md +0 -0
  55. {akquant-0.2.46 → akquant-0.2.48}/docs/en/advanced/live_functional_quickstart.md +0 -0
  56. {akquant-0.2.46 → akquant-0.2.48}/docs/en/advanced/llm.md +0 -0
  57. {akquant-0.2.46 → akquant-0.2.48}/docs/en/advanced/ml.md +0 -0
  58. {akquant-0.2.46 → akquant-0.2.48}/docs/en/advanced/multi_strategy_guide.md +0 -0
  59. {akquant-0.2.46 → akquant-0.2.48}/docs/en/advanced/multi_timeframe_feed_api.md +0 -0
  60. {akquant-0.2.46 → akquant-0.2.48}/docs/en/advanced/runtime_config.md +0 -0
  61. {akquant-0.2.46 → akquant-0.2.48}/docs/en/advanced/strategy_style_decision.md +0 -0
  62. {akquant-0.2.46 → akquant-0.2.48}/docs/en/advanced/timezone.md +0 -0
  63. {akquant-0.2.46 → akquant-0.2.48}/docs/en/advanced/warm_start.md +0 -0
  64. {akquant-0.2.46 → akquant-0.2.48}/docs/en/guide/analysis.md +0 -0
  65. {akquant-0.2.46 → akquant-0.2.48}/docs/en/guide/cross_section_checklist.md +0 -0
  66. {akquant-0.2.46 → akquant-0.2.48}/docs/en/guide/custom_indicator.md +0 -0
  67. {akquant-0.2.46 → akquant-0.2.48}/docs/en/guide/data.md +0 -0
  68. {akquant-0.2.46 → akquant-0.2.48}/docs/en/guide/examples.md +0 -0
  69. {akquant-0.2.46 → akquant-0.2.48}/docs/en/guide/factor.md +0 -0
  70. {akquant-0.2.46 → akquant-0.2.48}/docs/en/guide/indicator_scenario_quickref.md +0 -0
  71. {akquant-0.2.46 → akquant-0.2.48}/docs/en/guide/optimization.md +0 -0
  72. {akquant-0.2.46 → akquant-0.2.48}/docs/en/guide/python_basics.md +0 -0
  73. {akquant-0.2.46 → akquant-0.2.48}/docs/en/guide/quant_basics.md +0 -0
  74. {akquant-0.2.46 → akquant-0.2.48}/docs/en/guide/rust_indicator_reference.md +0 -0
  75. {akquant-0.2.46 → akquant-0.2.48}/docs/en/guide/strategy.md +0 -0
  76. {akquant-0.2.46 → akquant-0.2.48}/docs/en/guide/talib_indicator_playbook.md +0 -0
  77. {akquant-0.2.46 → akquant-0.2.48}/docs/en/guide/testing.md +0 -0
  78. {akquant-0.2.46 → akquant-0.2.48}/docs/en/guide/visualization.md +0 -0
  79. {akquant-0.2.46 → akquant-0.2.48}/docs/en/index.md +0 -0
  80. {akquant-0.2.46 → akquant-0.2.48}/docs/en/meta/architecture.md +0 -0
  81. {akquant-0.2.46 → akquant-0.2.48}/docs/en/meta/internals.md +0 -0
  82. {akquant-0.2.46 → akquant-0.2.48}/docs/en/reference/api.md +0 -0
  83. {akquant-0.2.46 → akquant-0.2.48}/docs/en/start/first_strategy.md +0 -0
  84. {akquant-0.2.46 → akquant-0.2.48}/docs/en/start/installation.md +0 -0
  85. {akquant-0.2.46 → akquant-0.2.48}/docs/en/start/quickstart.md +0 -0
  86. {akquant-0.2.46 → akquant-0.2.48}/docs/en/start/setup_guide.md +0 -0
  87. {akquant-0.2.46 → akquant-0.2.48}/docs/en/textbook/01_foundations.md +0 -0
  88. {akquant-0.2.46 → akquant-0.2.48}/docs/en/textbook/02_programming.md +0 -0
  89. {akquant-0.2.46 → akquant-0.2.48}/docs/en/textbook/03_data.md +0 -0
  90. {akquant-0.2.46 → akquant-0.2.48}/docs/en/textbook/04_backtest_engine.md +0 -0
  91. {akquant-0.2.46 → akquant-0.2.48}/docs/en/textbook/05_strategy.md +0 -0
  92. {akquant-0.2.46 → akquant-0.2.48}/docs/en/textbook/06_stock_a.md +0 -0
  93. {akquant-0.2.46 → akquant-0.2.48}/docs/en/textbook/07_futures.md +0 -0
  94. {akquant-0.2.46 → akquant-0.2.48}/docs/en/textbook/08_options.md +0 -0
  95. {akquant-0.2.46 → akquant-0.2.48}/docs/en/textbook/09_funds.md +0 -0
  96. {akquant-0.2.46 → akquant-0.2.48}/docs/en/textbook/10_analysis.md +0 -0
  97. {akquant-0.2.46 → akquant-0.2.48}/docs/en/textbook/11_optimization.md +0 -0
  98. {akquant-0.2.46 → akquant-0.2.48}/docs/en/textbook/12_ml.md +0 -0
  99. {akquant-0.2.46 → akquant-0.2.48}/docs/en/textbook/13_visualization.md +0 -0
  100. {akquant-0.2.46 → akquant-0.2.48}/docs/en/textbook/14_factor.md +0 -0
  101. {akquant-0.2.46 → akquant-0.2.48}/docs/en/textbook/15_live_trading.md +0 -0
  102. {akquant-0.2.46 → akquant-0.2.48}/docs/en/textbook/16_rust_indicators.md +0 -0
  103. {akquant-0.2.46 → akquant-0.2.48}/docs/en/textbook/index.md +0 -0
  104. {akquant-0.2.46 → akquant-0.2.48}/docs/javascripts/mathjax.js +0 -0
  105. {akquant-0.2.46 → akquant-0.2.48}/docs/javascripts/search-lang-filter.js +0 -0
  106. {akquant-0.2.46 → akquant-0.2.48}/docs/robots.txt +0 -0
  107. {akquant-0.2.46 → akquant-0.2.48}/docs/zh/advanced/analyzer_plugin_spec.md +0 -0
  108. {akquant-0.2.46 → akquant-0.2.48}/docs/zh/advanced/broker_capability_matrix.md +0 -0
  109. {akquant-0.2.46 → akquant-0.2.48}/docs/zh/advanced/custom_broker_production_checklist.md +0 -0
  110. {akquant-0.2.46 → akquant-0.2.48}/docs/zh/advanced/custom_broker_registry.md +0 -0
  111. {akquant-0.2.46 → akquant-0.2.48}/docs/zh/advanced/data_feed_adapter_spec.md +0 -0
  112. {akquant-0.2.46 → akquant-0.2.48}/docs/zh/advanced/live_functional_quickstart.md +0 -0
  113. {akquant-0.2.46 → akquant-0.2.48}/docs/zh/advanced/llm.md +0 -0
  114. {akquant-0.2.46 → akquant-0.2.48}/docs/zh/advanced/ml.md +0 -0
  115. {akquant-0.2.46 → akquant-0.2.48}/docs/zh/advanced/multi_strategy_guide.md +0 -0
  116. {akquant-0.2.46 → akquant-0.2.48}/docs/zh/advanced/multi_timeframe_feed_api.md +0 -0
  117. {akquant-0.2.46 → akquant-0.2.48}/docs/zh/advanced/runtime_config.md +0 -0
  118. {akquant-0.2.46 → akquant-0.2.48}/docs/zh/advanced/strategy_style_decision.md +0 -0
  119. {akquant-0.2.46 → akquant-0.2.48}/docs/zh/advanced/timezone.md +0 -0
  120. {akquant-0.2.46 → akquant-0.2.48}/docs/zh/advanced/warm_start.md +0 -0
  121. {akquant-0.2.46 → akquant-0.2.48}/docs/zh/guide/analysis.md +0 -0
  122. {akquant-0.2.46 → akquant-0.2.48}/docs/zh/guide/cross_section_checklist.md +0 -0
  123. {akquant-0.2.46 → akquant-0.2.48}/docs/zh/guide/custom_indicator.md +0 -0
  124. {akquant-0.2.46 → akquant-0.2.48}/docs/zh/guide/data.md +0 -0
  125. {akquant-0.2.46 → akquant-0.2.48}/docs/zh/guide/examples.md +0 -0
  126. {akquant-0.2.46 → akquant-0.2.48}/docs/zh/guide/factor.md +0 -0
  127. {akquant-0.2.46 → akquant-0.2.48}/docs/zh/guide/indicator_scenario_quickref.md +0 -0
  128. {akquant-0.2.46 → akquant-0.2.48}/docs/zh/guide/optimization.md +0 -0
  129. {akquant-0.2.46 → akquant-0.2.48}/docs/zh/guide/python_basics.md +0 -0
  130. {akquant-0.2.46 → akquant-0.2.48}/docs/zh/guide/quant_basics.md +0 -0
  131. {akquant-0.2.46 → akquant-0.2.48}/docs/zh/guide/rust_indicator_reference.md +0 -0
  132. {akquant-0.2.46 → akquant-0.2.48}/docs/zh/guide/strategy.md +0 -0
  133. {akquant-0.2.46 → akquant-0.2.48}/docs/zh/guide/talib_indicator_playbook.md +0 -0
  134. {akquant-0.2.46 → akquant-0.2.48}/docs/zh/guide/testing.md +0 -0
  135. {akquant-0.2.46 → akquant-0.2.48}/docs/zh/guide/visualization.md +0 -0
  136. {akquant-0.2.46 → akquant-0.2.48}/docs/zh/index.md +0 -0
  137. {akquant-0.2.46 → akquant-0.2.48}/docs/zh/meta/architecture.md +0 -0
  138. {akquant-0.2.46 → akquant-0.2.48}/docs/zh/meta/internals.md +0 -0
  139. {akquant-0.2.46 → akquant-0.2.48}/docs/zh/reference/api.md +0 -0
  140. {akquant-0.2.46 → akquant-0.2.48}/docs/zh/start/first_strategy.md +0 -0
  141. {akquant-0.2.46 → akquant-0.2.48}/docs/zh/start/installation.md +0 -0
  142. {akquant-0.2.46 → akquant-0.2.48}/docs/zh/start/quickstart.md +0 -0
  143. {akquant-0.2.46 → akquant-0.2.48}/docs/zh/start/setup_guide.md +0 -0
  144. {akquant-0.2.46 → akquant-0.2.48}/docs/zh/textbook/00_glossary.md +0 -0
  145. {akquant-0.2.46 → akquant-0.2.48}/docs/zh/textbook/01_foundations.md +0 -0
  146. {akquant-0.2.46 → akquant-0.2.48}/docs/zh/textbook/02_programming.md +0 -0
  147. {akquant-0.2.46 → akquant-0.2.48}/docs/zh/textbook/03_data.md +0 -0
  148. {akquant-0.2.46 → akquant-0.2.48}/docs/zh/textbook/04_backtest_engine.md +0 -0
  149. {akquant-0.2.46 → akquant-0.2.48}/docs/zh/textbook/05_strategy.md +0 -0
  150. {akquant-0.2.46 → akquant-0.2.48}/docs/zh/textbook/06_stock_a.md +0 -0
  151. {akquant-0.2.46 → akquant-0.2.48}/docs/zh/textbook/07_futures.md +0 -0
  152. {akquant-0.2.46 → akquant-0.2.48}/docs/zh/textbook/08_options.md +0 -0
  153. {akquant-0.2.46 → akquant-0.2.48}/docs/zh/textbook/09_funds.md +0 -0
  154. {akquant-0.2.46 → akquant-0.2.48}/docs/zh/textbook/10_analysis.md +0 -0
  155. {akquant-0.2.46 → akquant-0.2.48}/docs/zh/textbook/11_optimization.md +0 -0
  156. {akquant-0.2.46 → akquant-0.2.48}/docs/zh/textbook/12_ml.md +0 -0
  157. {akquant-0.2.46 → akquant-0.2.48}/docs/zh/textbook/13_visualization.md +0 -0
  158. {akquant-0.2.46 → akquant-0.2.48}/docs/zh/textbook/14_factor.md +0 -0
  159. {akquant-0.2.46 → akquant-0.2.48}/docs/zh/textbook/15_live_trading.md +0 -0
  160. {akquant-0.2.46 → akquant-0.2.48}/docs/zh/textbook/16_rust_indicators.md +0 -0
  161. {akquant-0.2.46 → akquant-0.2.48}/docs/zh/textbook/ROADMAP.md +0 -0
  162. {akquant-0.2.46 → akquant-0.2.48}/docs/zh/textbook/appendix_cite.md +0 -0
  163. {akquant-0.2.46 → akquant-0.2.48}/docs/zh/textbook/appendix_pitfalls.md +0 -0
  164. {akquant-0.2.46 → akquant-0.2.48}/docs/zh/textbook/appendix_setup.md +0 -0
  165. {akquant-0.2.46 → akquant-0.2.48}/docs/zh/textbook/capstone.md +0 -0
  166. {akquant-0.2.46 → akquant-0.2.48}/docs/zh/textbook/index.md +0 -0
  167. {akquant-0.2.46 → akquant-0.2.48}/examples/01_quickstart.py +0 -0
  168. {akquant-0.2.46 → akquant-0.2.48}/examples/02_parameter_optimization.py +0 -0
  169. {akquant-0.2.46 → akquant-0.2.48}/examples/03_parameter_optimization_advanced.py +0 -0
  170. {akquant-0.2.46 → akquant-0.2.48}/examples/04_mixed_assets.py +0 -0
  171. {akquant-0.2.46 → akquant-0.2.48}/examples/05_live_trading_ctp.py +0 -0
  172. {akquant-0.2.46 → akquant-0.2.48}/examples/06_complex_orders.py +0 -0
  173. {akquant-0.2.46 → akquant-0.2.48}/examples/07_option_test.py +0 -0
  174. {akquant-0.2.46 → akquant-0.2.48}/examples/08_event_callbacks.py +0 -0
  175. {akquant-0.2.46 → akquant-0.2.48}/examples/09_ml_framework.py +0 -0
  176. {akquant-0.2.46 → akquant-0.2.48}/examples/10_ml_walk_forward.py +0 -0
  177. {akquant-0.2.46 → akquant-0.2.48}/examples/11_plot_visualization.py +0 -0
  178. {akquant-0.2.46 → akquant-0.2.48}/examples/12_wfo_integrated.py +0 -0
  179. {akquant-0.2.46 → akquant-0.2.48}/examples/13_quantstats_report.py +0 -0
  180. {akquant-0.2.46 → akquant-0.2.48}/examples/14_multi_frequency.py +0 -0
  181. {akquant-0.2.46 → akquant-0.2.48}/examples/15_plot_intraday.py +0 -0
  182. {akquant-0.2.46 → akquant-0.2.48}/examples/16_adj_returns_signal.py +0 -0
  183. {akquant-0.2.46 → akquant-0.2.48}/examples/17_readme_demo.py +0 -0
  184. {akquant-0.2.46 → akquant-0.2.48}/examples/18_benchmark_multisymbol.py +0 -0
  185. {akquant-0.2.46 → akquant-0.2.48}/examples/19_factor_expression.py +0 -0
  186. {akquant-0.2.46 → akquant-0.2.48}/examples/20_risk_management_demo.py +0 -0
  187. {akquant-0.2.46 → akquant-0.2.48}/examples/21_warm_start_demo.py +0 -0
  188. {akquant-0.2.46 → akquant-0.2.48}/examples/22_strategy_runtime_config_demo.py +0 -0
  189. {akquant-0.2.46 → akquant-0.2.48}/examples/23_functional_callbacks_demo.py +0 -0
  190. {akquant-0.2.46 → akquant-0.2.48}/examples/24_functional_tick_simulation_demo.py +0 -0
  191. {akquant-0.2.46 → akquant-0.2.48}/examples/25_streaming_backtest_demo.py +0 -0
  192. {akquant-0.2.46 → akquant-0.2.48}/examples/26_streaming_quickstart.py +0 -0
  193. {akquant-0.2.46 → akquant-0.2.48}/examples/27_streaming_monitoring_console.py +0 -0
  194. {akquant-0.2.46 → akquant-0.2.48}/examples/28_streaming_alerts_and_persist.py +0 -0
  195. {akquant-0.2.46 → akquant-0.2.48}/examples/29_streaming_event_report.py +0 -0
  196. {akquant-0.2.46 → akquant-0.2.48}/examples/30_streaming_report_oneclick.py +0 -0
  197. {akquant-0.2.46 → akquant-0.2.48}/examples/31_streaming_live_console.py +0 -0
  198. {akquant-0.2.46 → akquant-0.2.48}/examples/32_streaming_live_web.py +0 -0
  199. {akquant-0.2.46 → akquant-0.2.48}/examples/33_report_and_analysis_outputs.py +0 -0
  200. {akquant-0.2.46 → akquant-0.2.48}/examples/34_multi_strategy_demo.py +0 -0
  201. {akquant-0.2.46 → akquant-0.2.48}/examples/35_custom_broker_registry_demo.py +0 -0
  202. {akquant-0.2.46 → akquant-0.2.48}/examples/36_trailing_orders.py +0 -0
  203. {akquant-0.2.46 → akquant-0.2.48}/examples/37_feed_replay_alignment_demo.py +0 -0
  204. {akquant-0.2.46 → akquant-0.2.48}/examples/38_live_functional_strategy_demo.py +0 -0
  205. {akquant-0.2.46 → akquant-0.2.48}/examples/39_live_broker_submit_order_demo.py +0 -0
  206. {akquant-0.2.46 → akquant-0.2.48}/examples/40_functional_multi_slot_risk_demo.py +0 -0
  207. {akquant-0.2.46 → akquant-0.2.48}/examples/41_live_multi_slot_orchestration_demo.py +0 -0
  208. {akquant-0.2.46 → akquant-0.2.48}/examples/42_live_broker_event_audit_demo.py +0 -0
  209. {akquant-0.2.46 → akquant-0.2.48}/examples/43_target_weights_rebalance.py +0 -0
  210. {akquant-0.2.46 → akquant-0.2.48}/examples/44_strategy_source_loader_demo.py +0 -0
  211. {akquant-0.2.46 → akquant-0.2.48}/examples/45_talib_indicator_playbook_demo.py +0 -0
  212. {akquant-0.2.46 → akquant-0.2.48}/examples/46_broker_profile_demo.py +0 -0
  213. {akquant-0.2.46 → akquant-0.2.48}/examples/47_margin_liquidation_audit_demo.py +0 -0
  214. {akquant-0.2.46 → akquant-0.2.48}/examples/48_margin_liquidation_priority_compare.py +0 -0
  215. {akquant-0.2.46 → akquant-0.2.48}/examples/49_on_expiry_demo.py +0 -0
  216. {akquant-0.2.46 → akquant-0.2.48}/examples/50_framework_hooks_demo.py +0 -0
  217. {akquant-0.2.46 → akquant-0.2.48}/examples/51_class_tick_callbacks_demo.py +0 -0
  218. {akquant-0.2.46 → akquant-0.2.48}/examples/52_pre_open_demo.py +0 -0
  219. {akquant-0.2.46 → akquant-0.2.48}/examples/53_timer_to_pre_open_demo.py +0 -0
  220. {akquant-0.2.46 → akquant-0.2.48}/examples/54_functional_pre_open_demo.py +0 -0
  221. {akquant-0.2.46 → akquant-0.2.48}/examples/55_functional_ml_walk_forward.py +0 -0
  222. {akquant-0.2.46 → akquant-0.2.48}/examples/56_functional_warm_start_demo.py +0 -0
  223. {akquant-0.2.46 → akquant-0.2.48}/examples/57_functional_multi_slot_warm_start_demo.py +0 -0
  224. {akquant-0.2.46 → akquant-0.2.48}/examples/58_incremental_bootstrap_demo.py +0 -0
  225. {akquant-0.2.46 → akquant-0.2.48}/examples/59_akshare_etf_rotation.py +0 -0
  226. {akquant-0.2.46 → akquant-0.2.48}/examples/60_custom_indicator_demo.py +0 -0
  227. {akquant-0.2.46 → akquant-0.2.48}/examples/61_indicator_visualization_export_demo.py +0 -0
  228. {akquant-0.2.46 → akquant-0.2.48}/examples/62_indicator_streaming_demo.py +0 -0
  229. {akquant-0.2.46 → akquant-0.2.48}/examples/63_indicator_ws_bridge_demo.py +0 -0
  230. {akquant-0.2.46 → akquant-0.2.48}/examples/64_indicator_live_web.py +0 -0
  231. {akquant-0.2.46 → akquant-0.2.48}/examples/README.md +0 -0
  232. {akquant-0.2.46 → akquant-0.2.48}/examples/benchmark_utils.py +0 -0
  233. {akquant-0.2.46 → akquant-0.2.48}/examples/pb_mock.py +0 -0
  234. {akquant-0.2.46 → akquant-0.2.48}/examples/strategies/01_stock_dual_moving_average.py +0 -0
  235. {akquant-0.2.46 → akquant-0.2.48}/examples/strategies/02_stock_grid_trading.py +0 -0
  236. {akquant-0.2.46 → akquant-0.2.48}/examples/strategies/03_stock_atr_breakout.py +0 -0
  237. {akquant-0.2.46 → akquant-0.2.48}/examples/strategies/04_stock_momentum_rotation.py +0 -0
  238. {akquant-0.2.46 → akquant-0.2.48}/examples/strategies/05_stock_momentum_rotation_timer.py +0 -0
  239. {akquant-0.2.46 → akquant-0.2.48}/examples/strategies/06_stock_momentum_rotation_bucket.py +0 -0
  240. {akquant-0.2.46 → akquant-0.2.48}/examples/strategies/07_stock_momentum_rotation_on_timer.py +0 -0
  241. {akquant-0.2.46 → akquant-0.2.48}/examples/strategies/08_target_positions_long_short.py +0 -0
  242. {akquant-0.2.46 → akquant-0.2.48}/examples/strategies/09_stock_momentum_rotation_after_bar.py +0 -0
  243. {akquant-0.2.46 → akquant-0.2.48}/examples/strategies/README.md +0 -0
  244. {akquant-0.2.46 → akquant-0.2.48}/examples/textbook/ch01_quickstart.py +0 -0
  245. {akquant-0.2.46 → akquant-0.2.48}/examples/textbook/ch02_programming.py +0 -0
  246. {akquant-0.2.46 → akquant-0.2.48}/examples/textbook/ch03_data.py +0 -0
  247. {akquant-0.2.46 → akquant-0.2.48}/examples/textbook/ch04_comparison.py +0 -0
  248. {akquant-0.2.46 → akquant-0.2.48}/examples/textbook/ch05_strategy.py +0 -0
  249. {akquant-0.2.46 → akquant-0.2.48}/examples/textbook/ch06_stock_a.py +0 -0
  250. {akquant-0.2.46 → akquant-0.2.48}/examples/textbook/ch07_futures.py +0 -0
  251. {akquant-0.2.46 → akquant-0.2.48}/examples/textbook/ch08_options.py +0 -0
  252. {akquant-0.2.46 → akquant-0.2.48}/examples/textbook/ch09_funds.py +0 -0
  253. {akquant-0.2.46 → akquant-0.2.48}/examples/textbook/ch09_portfolio.py +0 -0
  254. {akquant-0.2.46 → akquant-0.2.48}/examples/textbook/ch10_analysis.py +0 -0
  255. {akquant-0.2.46 → akquant-0.2.48}/examples/textbook/ch11_optimization.py +0 -0
  256. {akquant-0.2.46 → akquant-0.2.48}/examples/textbook/ch12_ml.py +0 -0
  257. {akquant-0.2.46 → akquant-0.2.48}/examples/textbook/ch13_visualization.py +0 -0
  258. {akquant-0.2.46 → akquant-0.2.48}/examples/textbook/ch14_factor.py +0 -0
  259. {akquant-0.2.46 → akquant-0.2.48}/examples/textbook/ch15_live_trading.py +0 -0
  260. {akquant-0.2.46 → akquant-0.2.48}/examples/textbook/ch15_strategy_loader.py +0 -0
  261. {akquant-0.2.46 → akquant-0.2.48}/examples/textbook/ch16_indicators.py +0 -0
  262. {akquant-0.2.46 → akquant-0.2.48}/mkdocs.yml +0 -0
  263. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/akquant.pyi +0 -0
  264. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/analysis/__init__.py +0 -0
  265. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/analysis/benchmark.py +0 -0
  266. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/analyzer_plugin.py +0 -0
  267. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/backtest/__init__.py +0 -0
  268. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/backtest/__init__.pyi +0 -0
  269. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/backtest/result.py +0 -0
  270. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/checkpoint.py +0 -0
  271. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/data.py +0 -0
  272. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/factor/__init__.py +0 -0
  273. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/factor/engine.py +0 -0
  274. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/factor/ops.py +0 -0
  275. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/factor/parser.py +0 -0
  276. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/feed_adapter.py +0 -0
  277. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/fund/__init__.py +0 -0
  278. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/futures/__init__.py +0 -0
  279. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/gateway/__init__.py +0 -0
  280. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/gateway/base.py +0 -0
  281. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/gateway/broker_event_bridge.py +0 -0
  282. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/gateway/broker_event_mapper.py +0 -0
  283. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/gateway/broker_models.py +0 -0
  284. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/gateway/broker_recovery.py +0 -0
  285. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/gateway/broker_runtime.py +0 -0
  286. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/gateway/brokers/__init__.py +0 -0
  287. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/gateway/brokers/ctp/__init__.py +0 -0
  288. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/gateway/brokers/ctp/adapter.py +0 -0
  289. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/gateway/brokers/ctp/native.py +0 -0
  290. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/gateway/brokers/miniqmt/__init__.py +0 -0
  291. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/gateway/brokers/miniqmt/stub.py +0 -0
  292. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/gateway/brokers/ptrade/__init__.py +0 -0
  293. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/gateway/brokers/ptrade/stub.py +0 -0
  294. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/gateway/ctp_adapter.py +0 -0
  295. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/gateway/ctp_native.py +0 -0
  296. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/gateway/factory.py +0 -0
  297. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/gateway/mapper.py +0 -0
  298. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/gateway/miniqmt.py +0 -0
  299. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/gateway/models.py +0 -0
  300. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/gateway/order_submitter.py +0 -0
  301. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/gateway/protocols.py +0 -0
  302. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/gateway/ptrade.py +0 -0
  303. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/gateway/registry.py +0 -0
  304. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/indicator.py +0 -0
  305. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/indicator_recording.py +0 -0
  306. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/indicator_stream.py +0 -0
  307. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/live.py +0 -0
  308. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/log.py +0 -0
  309. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/ml/__init__.py +0 -0
  310. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/ml/model.py +0 -0
  311. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/optimize.py +0 -0
  312. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/option/__init__.py +0 -0
  313. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/params.py +0 -0
  314. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/params_adapter.py +0 -0
  315. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/plot/__init__.py +0 -0
  316. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/plot/analysis.py +0 -0
  317. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/plot/dashboard.py +0 -0
  318. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/plot/indicator.py +0 -0
  319. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/plot/strategy.py +0 -0
  320. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/plot/utils.py +0 -0
  321. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/py.typed +0 -0
  322. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/risk.py +0 -0
  323. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/sizer.py +0 -0
  324. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/stock/__init__.py +0 -0
  325. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/strategy.py +0 -0
  326. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/strategy_events.py +0 -0
  327. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/strategy_framework_hooks.py +0 -0
  328. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/strategy_history.py +0 -0
  329. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/strategy_loader.py +0 -0
  330. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/strategy_logging.py +0 -0
  331. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/strategy_ml.py +0 -0
  332. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/strategy_order_events.py +0 -0
  333. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/strategy_position.py +0 -0
  334. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/strategy_scheduler.py +0 -0
  335. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/strategy_time.py +0 -0
  336. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/strategy_trading_api.py +0 -0
  337. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/talib/__init__.py +0 -0
  338. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/talib/backend.py +0 -0
  339. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/talib/core.py +0 -0
  340. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/talib/funcs.py +0 -0
  341. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/utils/__init__.py +0 -0
  342. {akquant-0.2.46 → akquant-0.2.48}/python/akquant/utils/inspector.py +0 -0
  343. {akquant-0.2.46 → akquant-0.2.48}/requirements-dev.txt +0 -0
  344. {akquant-0.2.46 → akquant-0.2.48}/scripts/cargo-test.sh +0 -0
  345. {akquant-0.2.46 → akquant-0.2.48}/scripts/check_docs_api_examples.py +0 -0
  346. {akquant-0.2.46 → akquant-0.2.48}/scripts/check_docs_links.py +0 -0
  347. {akquant-0.2.46 → akquant-0.2.48}/scripts/dev-check.sh +0 -0
  348. {akquant-0.2.46 → akquant-0.2.48}/scripts/find_legacy_execution_policy_calls.py +0 -0
  349. {akquant-0.2.46 → akquant-0.2.48}/scripts/golden_baseline_report.py +0 -0
  350. {akquant-0.2.46 → akquant-0.2.48}/src/account.rs +0 -0
  351. {akquant-0.2.46 → akquant-0.2.48}/src/analysis/mod.rs +0 -0
  352. {akquant-0.2.46 → akquant-0.2.48}/src/analysis/tracker.rs +0 -0
  353. {akquant-0.2.46 → akquant-0.2.48}/src/analysis/types.rs +0 -0
  354. {akquant-0.2.46 → akquant-0.2.48}/src/bin/stub_gen.rs +0 -0
  355. {akquant-0.2.46 → akquant-0.2.48}/src/clock.rs +0 -0
  356. {akquant-0.2.46 → akquant-0.2.48}/src/context.rs +0 -0
  357. {akquant-0.2.46 → akquant-0.2.48}/src/data/aggregator.rs +0 -0
  358. {akquant-0.2.46 → akquant-0.2.48}/src/data/batch.rs +0 -0
  359. {akquant-0.2.46 → akquant-0.2.48}/src/data/client.rs +0 -0
  360. {akquant-0.2.46 → akquant-0.2.48}/src/data/feed.rs +0 -0
  361. {akquant-0.2.46 → akquant-0.2.48}/src/data/mod.rs +0 -0
  362. {akquant-0.2.46 → akquant-0.2.48}/src/engine/mod.rs +0 -0
  363. {akquant-0.2.46 → akquant-0.2.48}/src/engine/state.rs +0 -0
  364. {akquant-0.2.46 → akquant-0.2.48}/src/error.rs +0 -0
  365. {akquant-0.2.46 → akquant-0.2.48}/src/event.rs +0 -0
  366. {akquant-0.2.46 → akquant-0.2.48}/src/event_manager.rs +0 -0
  367. {akquant-0.2.46 → akquant-0.2.48}/src/execution/common.rs +0 -0
  368. {akquant-0.2.46 → akquant-0.2.48}/src/execution/crypto.rs +0 -0
  369. {akquant-0.2.46 → akquant-0.2.48}/src/execution/forex.rs +0 -0
  370. {akquant-0.2.46 → akquant-0.2.48}/src/execution/futures.rs +0 -0
  371. {akquant-0.2.46 → akquant-0.2.48}/src/execution/matcher.rs +0 -0
  372. {akquant-0.2.46 → akquant-0.2.48}/src/execution/mod.rs +0 -0
  373. {akquant-0.2.46 → akquant-0.2.48}/src/execution/option.rs +0 -0
  374. {akquant-0.2.46 → akquant-0.2.48}/src/execution/python.rs +0 -0
  375. {akquant-0.2.46 → akquant-0.2.48}/src/execution/realtime.rs +0 -0
  376. {akquant-0.2.46 → akquant-0.2.48}/src/execution/simulated.rs +0 -0
  377. {akquant-0.2.46 → akquant-0.2.48}/src/execution/slippage.rs +0 -0
  378. {akquant-0.2.46 → akquant-0.2.48}/src/execution/stock.rs +0 -0
  379. {akquant-0.2.46 → akquant-0.2.48}/src/history.rs +0 -0
  380. {akquant-0.2.46 → akquant-0.2.48}/src/indicators/momentum.rs +0 -0
  381. {akquant-0.2.46 → akquant-0.2.48}/src/indicators/momentum_oscillators.rs +0 -0
  382. {akquant-0.2.46 → akquant-0.2.48}/src/indicators/momentum_rates.rs +0 -0
  383. {akquant-0.2.46 → akquant-0.2.48}/src/indicators/moving_average.rs +0 -0
  384. {akquant-0.2.46 → akquant-0.2.48}/src/indicators/moving_average_compound.rs +0 -0
  385. {akquant-0.2.46 → akquant-0.2.48}/src/indicators/moving_average_core.rs +0 -0
  386. {akquant-0.2.46 → akquant-0.2.48}/src/indicators/moving_average_transforms.rs +0 -0
  387. {akquant-0.2.46 → akquant-0.2.48}/src/indicators/moving_average_windowed.rs +0 -0
  388. {akquant-0.2.46 → akquant-0.2.48}/src/indicators/trend.rs +0 -0
  389. {akquant-0.2.46 → akquant-0.2.48}/src/indicators/trend_directional.rs +0 -0
  390. {akquant-0.2.46 → akquant-0.2.48}/src/indicators/trend_oscillators.rs +0 -0
  391. {akquant-0.2.46 → akquant-0.2.48}/src/indicators/trend_regression.rs +0 -0
  392. {akquant-0.2.46 → akquant-0.2.48}/src/indicators/volatility.rs +0 -0
  393. {akquant-0.2.46 → akquant-0.2.48}/src/indicators/volatility_price.rs +0 -0
  394. {akquant-0.2.46 → akquant-0.2.48}/src/indicators/volatility_stats.rs +0 -0
  395. {akquant-0.2.46 → akquant-0.2.48}/src/indicators/volume.rs +0 -0
  396. {akquant-0.2.46 → akquant-0.2.48}/src/indicators.rs +0 -0
  397. {akquant-0.2.46 → akquant-0.2.48}/src/lib.rs +0 -0
  398. {akquant-0.2.46 → akquant-0.2.48}/src/log_context.rs +0 -0
  399. {akquant-0.2.46 → akquant-0.2.48}/src/margin/calculator.rs +0 -0
  400. {akquant-0.2.46 → akquant-0.2.48}/src/margin/engine.rs +0 -0
  401. {akquant-0.2.46 → akquant-0.2.48}/src/margin/mod.rs +0 -0
  402. {akquant-0.2.46 → akquant-0.2.48}/src/market/china.rs +0 -0
  403. {akquant-0.2.46 → akquant-0.2.48}/src/market/core.rs +0 -0
  404. {akquant-0.2.46 → akquant-0.2.48}/src/market/corporate_action.rs +0 -0
  405. {akquant-0.2.46 → akquant-0.2.48}/src/market/fund.rs +0 -0
  406. {akquant-0.2.46 → akquant-0.2.48}/src/market/futures.rs +0 -0
  407. {akquant-0.2.46 → akquant-0.2.48}/src/market/manager.rs +0 -0
  408. {akquant-0.2.46 → akquant-0.2.48}/src/market/mod.rs +0 -0
  409. {akquant-0.2.46 → akquant-0.2.48}/src/market/option.rs +0 -0
  410. {akquant-0.2.46 → akquant-0.2.48}/src/market/simple.rs +0 -0
  411. {akquant-0.2.46 → akquant-0.2.48}/src/market/stock.rs +0 -0
  412. {akquant-0.2.46 → akquant-0.2.48}/src/model/corporate_action.rs +0 -0
  413. {akquant-0.2.46 → akquant-0.2.48}/src/model/instrument.rs +0 -0
  414. {akquant-0.2.46 → akquant-0.2.48}/src/model/market_data.rs +0 -0
  415. {akquant-0.2.46 → akquant-0.2.48}/src/model/mod.rs +0 -0
  416. {akquant-0.2.46 → akquant-0.2.48}/src/model/order.rs +0 -0
  417. {akquant-0.2.46 → akquant-0.2.48}/src/model/timer.rs +0 -0
  418. {akquant-0.2.46 → akquant-0.2.48}/src/model/types.rs +0 -0
  419. {akquant-0.2.46 → akquant-0.2.48}/src/order_manager.rs +0 -0
  420. {akquant-0.2.46 → akquant-0.2.48}/src/pipeline/mod.rs +0 -0
  421. {akquant-0.2.46 → akquant-0.2.48}/src/pipeline/processor.rs +0 -0
  422. {akquant-0.2.46 → akquant-0.2.48}/src/pipeline/runner.rs +0 -0
  423. {akquant-0.2.46 → akquant-0.2.48}/src/portfolio.rs +0 -0
  424. {akquant-0.2.46 → akquant-0.2.48}/src/risk/common.rs +0 -0
  425. {akquant-0.2.46 → akquant-0.2.48}/src/risk/config.rs +0 -0
  426. {akquant-0.2.46 → akquant-0.2.48}/src/risk/futures.rs +0 -0
  427. {akquant-0.2.46 → akquant-0.2.48}/src/risk/manager.rs +0 -0
  428. {akquant-0.2.46 → akquant-0.2.48}/src/risk/mod.rs +0 -0
  429. {akquant-0.2.46 → akquant-0.2.48}/src/risk/option.rs +0 -0
  430. {akquant-0.2.46 → akquant-0.2.48}/src/risk/portfolio.rs +0 -0
  431. {akquant-0.2.46 → akquant-0.2.48}/src/risk/rule.rs +0 -0
  432. {akquant-0.2.46 → akquant-0.2.48}/src/risk/stock.rs +0 -0
  433. {akquant-0.2.46 → akquant-0.2.48}/src/settlement/expiry.rs +0 -0
  434. {akquant-0.2.46 → akquant-0.2.48}/src/settlement/handler.rs +0 -0
  435. {akquant-0.2.46 → akquant-0.2.48}/src/settlement/manager.rs +0 -0
  436. {akquant-0.2.46 → akquant-0.2.48}/src/settlement/mod.rs +0 -0
  437. {akquant-0.2.46 → akquant-0.2.48}/src/settlement/option.rs +0 -0
  438. {akquant-0.2.46 → akquant-0.2.48}/tests/gateway_contract/test_event_bridge_contract.py +0 -0
  439. {akquant-0.2.46 → akquant-0.2.48}/tests/gateway_contract/test_recovery_contract.py +0 -0
  440. {akquant-0.2.46 → akquant-0.2.48}/tests/gateway_contract/test_submitter_contract.py +0 -0
  441. {akquant-0.2.46 → akquant-0.2.48}/tests/golden/BASELINE_REPORT.md +0 -0
  442. {akquant-0.2.46 → akquant-0.2.48}/tests/golden/LEGACY_EXECUTION_CALLS.md +0 -0
  443. {akquant-0.2.46 → akquant-0.2.48}/tests/golden/baselines/futures_margin/equity_curve.parquet +0 -0
  444. {akquant-0.2.46 → akquant-0.2.48}/tests/golden/baselines/futures_margin/orders.parquet +0 -0
  445. {akquant-0.2.46 → akquant-0.2.48}/tests/golden/baselines/option_basic/equity_curve.parquet +0 -0
  446. {akquant-0.2.46 → akquant-0.2.48}/tests/golden/baselines/option_basic/orders.parquet +0 -0
  447. {akquant-0.2.46 → akquant-0.2.48}/tests/golden/baselines/stock_t1/equity_curve.parquet +0 -0
  448. {akquant-0.2.46 → akquant-0.2.48}/tests/golden/baselines/stock_t1/orders.parquet +0 -0
  449. {akquant-0.2.46 → akquant-0.2.48}/tests/golden/baselines/stock_t1/trades.parquet +0 -0
  450. {akquant-0.2.46 → akquant-0.2.48}/tests/golden/current/futures_margin/equity_curve.parquet +0 -0
  451. {akquant-0.2.46 → akquant-0.2.48}/tests/golden/current/futures_margin/orders.parquet +0 -0
  452. {akquant-0.2.46 → akquant-0.2.48}/tests/golden/current/option_basic/equity_curve.parquet +0 -0
  453. {akquant-0.2.46 → akquant-0.2.48}/tests/golden/current/option_basic/orders.parquet +0 -0
  454. {akquant-0.2.46 → akquant-0.2.48}/tests/golden/current/stock_t1/equity_curve.parquet +0 -0
  455. {akquant-0.2.46 → akquant-0.2.48}/tests/golden/current/stock_t1/orders.parquet +0 -0
  456. {akquant-0.2.46 → akquant-0.2.48}/tests/golden/current/stock_t1/trades.parquet +0 -0
  457. {akquant-0.2.46 → akquant-0.2.48}/tests/golden/data/future_margin.parquet +0 -0
  458. {akquant-0.2.46 → akquant-0.2.48}/tests/golden/data/future_margin.parquet.sha256 +0 -0
  459. {akquant-0.2.46 → akquant-0.2.48}/tests/golden/data/option_basic.parquet +0 -0
  460. {akquant-0.2.46 → akquant-0.2.48}/tests/golden/data/option_basic.parquet.sha256 +0 -0
  461. {akquant-0.2.46 → akquant-0.2.48}/tests/golden/data/stock_t1.parquet +0 -0
  462. {akquant-0.2.46 → akquant-0.2.48}/tests/golden/data/stock_t1.parquet.sha256 +0 -0
  463. {akquant-0.2.46 → akquant-0.2.48}/tests/golden/gen_data.py +0 -0
  464. {akquant-0.2.46 → akquant-0.2.48}/tests/golden/runner.py +0 -0
  465. {akquant-0.2.46 → akquant-0.2.48}/tests/golden/strategies/futures_margin.py +0 -0
  466. {akquant-0.2.46 → akquant-0.2.48}/tests/golden/strategies/option_basic.py +0 -0
  467. {akquant-0.2.46 → akquant-0.2.48}/tests/golden/strategies/stock_t1.py +0 -0
  468. {akquant-0.2.46 → akquant-0.2.48}/tests/golden/test_golden.py +0 -0
  469. {akquant-0.2.46 → akquant-0.2.48}/tests/test_account_risk_rules.py +0 -0
  470. {akquant-0.2.46 → akquant-0.2.48}/tests/test_custom_matcher.py +0 -0
  471. {akquant-0.2.46 → akquant-0.2.48}/tests/test_data_catalog.py +0 -0
  472. {akquant-0.2.46 → akquant-0.2.48}/tests/test_docs_api_examples.py +0 -0
  473. {akquant-0.2.46 → akquant-0.2.48}/tests/test_docs_links.py +0 -0
  474. {akquant-0.2.46 → akquant-0.2.48}/tests/test_engine.py +0 -0
  475. {akquant-0.2.46 → akquant-0.2.48}/tests/test_examples_regression.py +0 -0
  476. {akquant-0.2.46 → akquant-0.2.48}/tests/test_factor_engine.py +0 -0
  477. {akquant-0.2.46 → akquant-0.2.48}/tests/test_factor_ops.py +0 -0
  478. {akquant-0.2.46 → akquant-0.2.48}/tests/test_feed_adapter.py +0 -0
  479. {akquant-0.2.46 → akquant-0.2.48}/tests/test_gateway_brokers_paths.py +0 -0
  480. {akquant-0.2.46 → akquant-0.2.48}/tests/test_gateway_callbacks.py +0 -0
  481. {akquant-0.2.46 → akquant-0.2.48}/tests/test_gateway_ctp_adapter.py +0 -0
  482. {akquant-0.2.46 → akquant-0.2.48}/tests/test_gateway_ctp_native.py +0 -0
  483. {akquant-0.2.46 → akquant-0.2.48}/tests/test_gateway_factory.py +0 -0
  484. {akquant-0.2.46 → akquant-0.2.48}/tests/test_gateway_mapper.py +0 -0
  485. {akquant-0.2.46 → akquant-0.2.48}/tests/test_gateway_registry.py +0 -0
  486. {akquant-0.2.46 → akquant-0.2.48}/tests/test_indicator_live_web_example.py +0 -0
  487. {akquant-0.2.46 → akquant-0.2.48}/tests/test_indicator_recording.py +0 -0
  488. {akquant-0.2.46 → akquant-0.2.48}/tests/test_inspector.py +0 -0
  489. {akquant-0.2.46 → akquant-0.2.48}/tests/test_live_runner.py +0 -0
  490. {akquant-0.2.46 → akquant-0.2.48}/tests/test_live_runner_broker_bridge.py +0 -0
  491. {akquant-0.2.46 → akquant-0.2.48}/tests/test_live_runner_broker_recovery.py +0 -0
  492. {akquant-0.2.46 → akquant-0.2.48}/tests/test_live_runner_broker_submitter.py +0 -0
  493. {akquant-0.2.46 → akquant-0.2.48}/tests/test_multisymbol_cross_section_consistency.py +0 -0
  494. {akquant-0.2.46 → akquant-0.2.48}/tests/test_orders_df.py +0 -0
  495. {akquant-0.2.46 → akquant-0.2.48}/tests/test_params_adapter.py +0 -0
  496. {akquant-0.2.46 → akquant-0.2.48}/tests/test_partial_filled_status.py +0 -0
  497. {akquant-0.2.46 → akquant-0.2.48}/tests/test_portfolio.py +0 -0
  498. {akquant-0.2.46 → akquant-0.2.48}/tests/test_quickstart_stream_consistency.py +0 -0
  499. {akquant-0.2.46 → akquant-0.2.48}/tests/test_report_helpers.py +0 -0
  500. {akquant-0.2.46 → akquant-0.2.48}/tests/test_report_plot_extensions.py +0 -0
  501. {akquant-0.2.46 → akquant-0.2.48}/tests/test_result_analysis_extensions.py +0 -0
  502. {akquant-0.2.46 → akquant-0.2.48}/tests/test_stop_orders.py +0 -0
  503. {akquant-0.2.46 → akquant-0.2.48}/tests/test_strategy_extras.py +0 -0
  504. {akquant-0.2.46 → akquant-0.2.48}/tests/test_strategy_timers_indicators.py +0 -0
  505. {akquant-0.2.46 → akquant-0.2.48}/tests/test_t_plus_one.py +0 -0
  506. {akquant-0.2.46 → akquant-0.2.48}/tests/test_talib_backend.py +0 -0
  507. {akquant-0.2.46 → akquant-0.2.48}/tests/test_talib_compat.py +0 -0
  508. {akquant-0.2.46 → akquant-0.2.48}/tests/test_trades_df.py +0 -0
  509. {akquant-0.2.46 → akquant-0.2.48}/tests/test_version.py +0 -0
@@ -7,7 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ### Added
11
+ - `BacktestConfig` 新增 `days_per_year`(年化天数因子,默认 252;数字货币 24/7 市场可设 365)与 `risk_free_rate`(年化无风险利率,默认 0.0)两个字段,用于参数化 Sharpe/Sortino/波动率等风险指标的年化口径。`risk_free_rate` 默认 0 不改变任何现有数值。
12
+
10
13
  ### Changed
14
+ - **回测指标口径变更(破坏性)**:Sharpe / Sortino 比率的分子改为「日收益算术均值 × `days_per_year`」做年化,替代原先的 CAGR(复合年化),与 pyfolio/empyrical/quantstats 等主流实现一致,并与分母 `√days_per_year` 的年化口径匹配。升级后历史报告的 Sharpe/Sortino 数值会变化,不可直接与旧版逐值对比;UPI 与 Calmar 仍沿用 CAGR 口径。
11
15
  - 策略交易日边界回调已硬切改名:`before_trading(trading_date, timestamp)` 更名为 `on_before_trading(trading_date, timestamp)`,`after_trading(trading_date, timestamp)` 更名为 `on_after_trading(trading_date, timestamp)`。
12
16
  - 旧回调名不再保留兼容别名;升级到当前版本后,若策略仍实现 `before_trading` / `after_trading`,将不会再被框架触发,请同步迁移到新名称。
13
17
  - 新增 `on_pre_open(event)` 框架回调,用于表达“盘前决策,本次 open 成交”;该回调默认下单语义会自动解析为 `price_basis=open, bar_offset=1, temporal=same_cycle`,不再要求用户自行用 `on_timer` 拼装开盘成交时序。
@@ -17,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
17
21
  - 示例体系新增 `examples/52_pre_open_demo.py`,用于演示 `on_pre_open -> on_order/on_trade -> on_bar` 的触发顺序与当日 open 成交语义。
18
22
 
19
23
  ### Fixed
24
+ - 修复短回测区间下 Sharpe/Sortino 因「分子用 CAGR、分母用日波动 × √252」口径不一致而出现异常巨大值(如期货场景 Sharpe 高达数千万)的问题;改用日收益算术均值年化后数值回归合理量级。
20
25
  - 修复 `on_timer` / `add_daily_timer` 场景下,订单级 `fill_policy={"price_basis":"close","bar_offset":0,"temporal":"same_cycle"}` 未在当日 timer 事件内生效的问题;相关卖单现在会按当日 timer 时间与当日 close 成交,不再延后到下一交易日。
21
26
  - 修复 framework 内部 `__framework_rebalance__` / `__framework_boundary__` timer 被误参与 same-cycle 撮合与终态统计的问题,避免出现 `+1ns` 的伪成交、partial fill 被过早补满,以及权益曲线尾部多出额外采样点。
22
27
  - 修复 `on_pre_open` 首个交易日可能因延迟注册 timer 而不触发的问题;相关 framework timer 现会在回测事件循环开始前直接注入引擎,从而保证首日也能按预期开盘语义执行。
@@ -30,7 +30,7 @@ dependencies = [
30
30
 
31
31
  [[package]]
32
32
  name = "akquant"
33
- version = "0.2.46"
33
+ version = "0.2.48"
34
34
  dependencies = [
35
35
  "anyhow",
36
36
  "chrono",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "akquant"
3
- version = "0.2.46"
3
+ version = "0.2.48"
4
4
  edition = "2024"
5
5
  description = "High-performance quantitative trading framework based on Rust and Python"
6
6
  license = "MIT"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: akquant
3
- Version: 0.2.46
3
+ Version: 0.2.48
4
4
  Classifier: Programming Language :: Rust
5
5
  Classifier: Programming Language :: Python :: Implementation :: CPython
6
6
  Classifier: Programming Language :: Python :: Implementation :: PyPy
@@ -4,7 +4,7 @@ build-backend = "maturin"
4
4
 
5
5
  [project]
6
6
  name = "akquant"
7
- version = "0.2.46"
7
+ version = "0.2.48"
8
8
  description = "High-performance quantitative trading framework based on Rust and Python"
9
9
  readme = "README.md"
10
10
  license = {text = "MIT License"}
@@ -8,7 +8,7 @@ try:
8
8
  except metadata.PackageNotFoundError:
9
9
  __version__ = "0.0.0+unknown"
10
10
 
11
- __engine_rule_version__ = "1.0.0" # Increment on behavior-changing updates
11
+ __engine_rule_version__ = "1.1.0" # Increment on behavior-changing updates
12
12
 
13
13
  from . import akquant as _akquant
14
14
  from . import log as _log
@@ -3313,6 +3313,14 @@ def run_backtest(
3313
3313
  raise ValueError(f"Invalid timezone: {timezone}")
3314
3314
  offset = int(offset_delta.total_seconds())
3315
3315
  engine.set_timezone(offset)
3316
+ if hasattr(engine, "set_days_per_year"):
3317
+ cast(Any, engine).set_days_per_year(
3318
+ getattr(config, "days_per_year", 252.0) if config else 252.0
3319
+ )
3320
+ if hasattr(engine, "set_risk_free_rate"):
3321
+ cast(Any, engine).set_risk_free_rate(
3322
+ getattr(config, "risk_free_rate", 0.0) if config else 0.0
3323
+ )
3316
3324
  engine.set_cash(initial_cash)
3317
3325
  if hasattr(engine, "set_default_strategy_id"):
3318
3326
  cast(Any, engine).set_default_strategy_id(effective_strategy_id)
@@ -631,6 +631,11 @@ class BacktestConfig:
631
631
  **Environment:**
632
632
  :param benchmark: Benchmark symbol for performance comparison.
633
633
  :param timezone: Exchange timezone. Default "Asia/Shanghai".
634
+ :param days_per_year: Annualization factor for risk metrics (Sharpe/Sortino/
635
+ volatility). Traditional markets use 252; crypto 24/7
636
+ markets use 365. Default 252.0.
637
+ :param risk_free_rate: Annualized risk-free rate subtracted from annualized
638
+ return in Sharpe/Sortino/UPI. Default 0.0.
634
639
  :param show_progress: Show progress bar. Default True.
635
640
  :param history_depth: Auto-load N bars of history before strategy starts.
636
641
 
@@ -654,6 +659,8 @@ class BacktestConfig:
654
659
 
655
660
  benchmark: Optional[str] = None
656
661
  timezone: str = "Asia/Shanghai"
662
+ days_per_year: float = 252.0
663
+ risk_free_rate: float = 0.0
657
664
  show_progress: bool = True
658
665
  history_depth: int = 0
659
666
 
@@ -793,6 +793,12 @@ def _build_metrics_html(result: Any) -> str:
793
793
  format_metric_value("max_drawdown_pct", metrics.max_drawdown_pct),
794
794
  "negative",
795
795
  ),
796
+ (
797
+ "最大回撤金额 (Max DD Amount)",
798
+ _get_metric_value(result, metrics, "max_drawdown_value"),
799
+ _format_currency(_get_metric_value(result, metrics, "max_drawdown_value")),
800
+ "negative",
801
+ ),
796
802
  (
797
803
  "波动率 (Volatility)",
798
804
  metrics.volatility,
@@ -474,6 +474,7 @@ impl BacktestResult {
474
474
  "total_loss",
475
475
  "total_commission",
476
476
  "max_drawdown",
477
+ "max_drawdown_value",
477
478
  "max_drawdown_pct",
478
479
  "win_rate",
479
480
  "loss_rate",
@@ -562,6 +563,7 @@ impl BacktestResult {
562
563
  push_f64(t_metrics.total_profit);
563
564
  push_f64(t_metrics.total_loss);
564
565
  push_f64(t_metrics.total_commission);
566
+ push_f64(metrics.max_drawdown);
565
567
  push_f64(metrics.max_drawdown_value);
566
568
  push_f64(metrics.max_drawdown_pct);
567
569
  push_f64(t_metrics.win_rate);
@@ -22,6 +22,10 @@ pub struct CalculatorInput {
22
22
  pub orders: Vec<Order>,
23
23
  pub executions: Vec<Trade>,
24
24
  pub liquidation_audits: Vec<LiquidationAudit>,
25
+ /// 年化天数因子。A 股等传统市场用 252,数字货币 24/7 市场用 365。
26
+ pub days_per_year: f64,
27
+ /// 年化无风险利率,默认 0.0。直接从年化收益分子里扣除。
28
+ pub risk_free_rate: f64,
25
29
  }
26
30
 
27
31
  #[gen_stub_pyclass]
@@ -250,12 +254,18 @@ impl BacktestResult {
250
254
  };
251
255
 
252
256
  let std_dev = variance.sqrt();
253
- let annualized_volatility = std_dev * (252.0f64).sqrt();
257
+ let dpy = input.days_per_year;
258
+ let annualized_volatility = std_dev * dpy.sqrt();
259
+
260
+ // 口径说明:Sharpe / Sortino 的分子使用「日收益算术均值 × days_per_year」做年化,
261
+ // 与 pyfolio/empyrical/quantstats 等行业实现一致;与分母 √days_per_year 的算术年化口径匹配。
262
+ // 注意:UPI 与 Calmar 刻意沿用 CAGR(annualized_return),不要改成算术均值。
263
+ let annualized_mean_return = mean_return * dpy;
264
+ let risk_free_rate = input.risk_free_rate; // 年化无风险利率,默认 0.0
254
265
 
255
266
  // 5. Sharpe Ratio
256
- let risk_free_rate = 0.0; // Assume 0 for simplicity or pass config
257
267
  let sharpe_ratio = if annualized_volatility != 0.0 {
258
- (annualized_return - risk_free_rate) / annualized_volatility
268
+ (annualized_mean_return - risk_free_rate) / annualized_volatility
259
269
  } else {
260
270
  0.0
261
271
  };
@@ -268,15 +278,15 @@ impl BacktestResult {
268
278
  0.0
269
279
  };
270
280
  let downside_std_dev = downside_variance.sqrt();
271
- let annualized_downside_volatility = downside_std_dev * (252.0f64).sqrt();
281
+ let annualized_downside_volatility = downside_std_dev * dpy.sqrt();
272
282
 
273
283
  let sortino_ratio = if annualized_downside_volatility != 0.0 {
274
- (annualized_return - risk_free_rate) / annualized_downside_volatility
284
+ (annualized_mean_return - risk_free_rate) / annualized_downside_volatility
275
285
  } else {
276
286
  0.0
277
287
  };
278
288
 
279
- // 7. UPI
289
+ // 7. UPI (Ulcer Performance Index) —— 沿用 CAGR 口径(与 quantstats 一致)
280
290
  let upi = if ulcer_index != 0.0 {
281
291
  (annualized_return - risk_free_rate) / ulcer_index
282
292
  } else {
@@ -229,6 +229,8 @@ fn test_max_drawdown_logic() {
229
229
  orders: vec![],
230
230
  executions: vec![],
231
231
  liquidation_audits: vec![],
232
+ days_per_year: 252.0,
233
+ risk_free_rate: 0.0,
232
234
  });
233
235
  assert_eq!(result.metrics.max_drawdown, 0.25);
234
236
 
@@ -252,6 +254,8 @@ fn test_max_drawdown_logic() {
252
254
  orders: vec![],
253
255
  executions: vec![],
254
256
  liquidation_audits: vec![],
257
+ days_per_year: 252.0,
258
+ risk_free_rate: 0.0,
255
259
  });
256
260
  assert_eq!(result_2.metrics.max_drawdown, 0.0);
257
261
 
@@ -275,6 +279,8 @@ fn test_max_drawdown_logic() {
275
279
  orders: vec![],
276
280
  executions: vec![],
277
281
  liquidation_audits: vec![],
282
+ days_per_year: 252.0,
283
+ risk_free_rate: 0.0,
278
284
  });
279
285
  assert_eq!(result_3.metrics.max_drawdown, 0.2);
280
286
 
@@ -301,6 +307,8 @@ fn test_max_drawdown_logic() {
301
307
  orders: vec![],
302
308
  executions: vec![],
303
309
  liquidation_audits: vec![],
310
+ days_per_year: 252.0,
311
+ risk_free_rate: 0.0,
304
312
  });
305
313
  assert_eq!(result_4.metrics.max_drawdown, 0.5);
306
314
  }
@@ -337,6 +345,8 @@ fn test_ulcer_index_logic() {
337
345
  orders: vec![],
338
346
  executions: vec![],
339
347
  liquidation_audits: vec![],
348
+ days_per_year: 252.0,
349
+ risk_free_rate: 0.0,
340
350
  });
341
351
  let expected_ui = 0.004f64.sqrt();
342
352
  assert!((result.metrics.ulcer_index - expected_ui).abs() < 1e-9);
@@ -364,6 +374,8 @@ fn test_calmar_uses_raw_drawdown_ratio_not_pct() {
364
374
  orders: vec![],
365
375
  executions: vec![],
366
376
  liquidation_audits: vec![],
377
+ days_per_year: 252.0,
378
+ risk_free_rate: 0.0,
367
379
  });
368
380
 
369
381
  let expected_raw_calmar = result.metrics.annualized_return / result.metrics.max_drawdown;
@@ -409,6 +421,8 @@ fn test_daily_metrics_resample_by_local_timezone_day() {
409
421
  orders: vec![],
410
422
  executions: vec![],
411
423
  liquidation_audits: vec![],
424
+ days_per_year: 252.0,
425
+ risk_free_rate: 0.0,
412
426
  });
413
427
 
414
428
  let expected_returns = [0.21_f64, 0.10_f64];
@@ -422,3 +436,83 @@ fn test_daily_metrics_resample_by_local_timezone_day() {
422
436
 
423
437
  assert!((result.metrics.volatility - expected_volatility).abs() < 1e-12);
424
438
  }
439
+
440
+ /// 构造一段每日权益曲线(日收益 [0.21, 0.10]),用指定的 days_per_year / risk_free_rate 计算结果。
441
+ fn build_sharpe_fixture(days_per_year: f64, risk_free_rate: f64) -> BacktestResult {
442
+ let empty_pnl = TradeTracker::new().calculate_pnl(None);
443
+ let day_ns = 86_400_000_000_000_i64;
444
+ let equity_curve = vec![
445
+ (0, Decimal::from(100)),
446
+ (day_ns, Decimal::from(121)),
447
+ (2 * day_ns, Decimal::from_str_exact("133.1").unwrap()),
448
+ ];
449
+
450
+ BacktestResult::calculate(crate::analysis::CalculatorInput {
451
+ equity_curve_decimal: equity_curve,
452
+ cash_curve_decimal: vec![],
453
+ margin_curve_decimal: vec![],
454
+ snapshots: vec![],
455
+ timezone_name: Some("UTC".to_string()),
456
+ timezone_offset: 0,
457
+ trade_pnl: empty_pnl,
458
+ trades: vec![],
459
+ initial_cash: Decimal::from(100),
460
+ orders: vec![],
461
+ executions: vec![],
462
+ liquidation_audits: vec![],
463
+ days_per_year,
464
+ risk_free_rate,
465
+ })
466
+ }
467
+
468
+ #[test]
469
+ fn test_sharpe_uses_arithmetic_mean_not_cagr() {
470
+ let dpy = 252.0_f64;
471
+ let result = build_sharpe_fixture(dpy, 0.0);
472
+
473
+ // 期望:分子为日收益算术均值年化 (mean * dpy),与分母 std * sqrt(dpy) 口径匹配。
474
+ let returns = [0.21_f64, 0.10_f64];
475
+ let mean = returns.iter().sum::<f64>() / returns.len() as f64;
476
+ let variance =
477
+ returns.iter().map(|r| (r - mean).powi(2)).sum::<f64>() / (returns.len() - 1) as f64;
478
+ let std = variance.sqrt();
479
+ let expected_sharpe = (mean * dpy) / (std * dpy.sqrt());
480
+
481
+ assert!((result.metrics.sharpe_ratio - expected_sharpe).abs() < 1e-9);
482
+
483
+ // 反向断言:旧的 CAGR 口径 (annualized_return / volatility) 在这种短回测下会爆炸,
484
+ // 新口径必须显著不同于它,以防回归到 PR #305 修复前的行为。
485
+ let old_cagr_sharpe = result.metrics.annualized_return / result.metrics.volatility;
486
+ assert!((result.metrics.sharpe_ratio - old_cagr_sharpe).abs() > 1.0);
487
+ }
488
+
489
+ #[test]
490
+ fn test_days_per_year_scales_annualization() {
491
+ let sharpe_252 = build_sharpe_fixture(252.0, 0.0).metrics.sharpe_ratio;
492
+ let sharpe_365 = build_sharpe_fixture(365.0, 0.0).metrics.sharpe_ratio;
493
+
494
+ // sharpe = mean * sqrt(dpy) / std,故比值应为 sqrt(365/252)。
495
+ let expected_ratio = (365.0_f64 / 252.0_f64).sqrt();
496
+ assert!((sharpe_365 / sharpe_252 - expected_ratio).abs() < 1e-9);
497
+ }
498
+
499
+ #[test]
500
+ fn test_risk_free_rate_lowers_sharpe() {
501
+ let dpy = 252.0_f64;
502
+ let rf = 0.5_f64; // 年化无风险利率
503
+ let base = build_sharpe_fixture(dpy, 0.0);
504
+ let with_rf = build_sharpe_fixture(dpy, rf);
505
+
506
+ // rf=0 与默认行为一致,保证基线不动。
507
+ let returns = [0.21_f64, 0.10_f64];
508
+ let mean = returns.iter().sum::<f64>() / returns.len() as f64;
509
+ let variance =
510
+ returns.iter().map(|r| (r - mean).powi(2)).sum::<f64>() / (returns.len() - 1) as f64;
511
+ let annualized_vol = variance.sqrt() * dpy.sqrt();
512
+
513
+ // Sharpe 下降量应恰为 rf / annualized_volatility。
514
+ let expected_drop = rf / annualized_vol;
515
+ assert!(
516
+ (base.metrics.sharpe_ratio - with_rf.metrics.sharpe_ratio - expected_drop).abs() < 1e-9
517
+ );
518
+ }
@@ -63,6 +63,10 @@ pub struct Engine {
63
63
  pub risk_manager: RiskManager,
64
64
  pub(crate) timezone_offset: i32,
65
65
  pub(crate) timezone_name: Option<String>,
66
+ /// 年化天数因子。传统市场 252,数字货币 24/7 用 365。
67
+ pub(crate) days_per_year: f64,
68
+ /// 年化无风险利率,默认 0.0。
69
+ pub(crate) risk_free_rate: f64,
66
70
  pub(crate) history_buffer: Arc<RwLock<HistoryBuffer>>,
67
71
  pub(crate) initial_cash: Decimal,
68
72
  #[pyo3(get, set)]
@@ -519,6 +519,8 @@ impl Engine {
519
519
  risk_manager: RiskManager::new(),
520
520
  timezone_offset: 28800, // Default UTC+8
521
521
  timezone_name: Some("Asia/Shanghai".to_string()),
522
+ days_per_year: 252.0,
523
+ risk_free_rate: 0.0,
522
524
  history_buffer: Arc::new(RwLock::new(HistoryBuffer::new(10000))), // Default large capacity for MAE/MFE
523
525
  initial_cash,
524
526
  active_start_time_ns: None,
@@ -742,6 +744,20 @@ impl Engine {
742
744
  Ok(())
743
745
  }
744
746
 
747
+ /// 设置年化天数因子.
748
+ ///
749
+ /// :param days: 年化使用的天数。A 股等传统市场用 252,数字货币 24/7 市场用 365。
750
+ pub fn set_days_per_year(&mut self, days: f64) {
751
+ self.days_per_year = days;
752
+ }
753
+
754
+ /// 设置年化无风险利率.
755
+ ///
756
+ /// :param rate: 年化无风险利率 (例如 0.02 表示 2%)。默认 0.0。
757
+ pub fn set_risk_free_rate(&mut self, rate: f64) {
758
+ self.risk_free_rate = rate;
759
+ }
760
+
745
761
  /// 启用模拟执行 (回测模式)
746
762
  ///
747
763
  /// 默认模式。在内存中撮合订单。
@@ -1235,6 +1251,8 @@ impl Engine {
1235
1251
  self.terminal_result_timestamp(),
1236
1252
  self.timezone_name.clone(),
1237
1253
  self.timezone_offset,
1254
+ self.days_per_year,
1255
+ self.risk_free_rate,
1238
1256
  )
1239
1257
  }
1240
1258
  }
@@ -0,0 +1,261 @@
1
+ use super::shared::{
2
+ ExecutionPhase, apply_execution_report, flush_accumulated_trades, has_orders_matching_phase,
3
+ };
4
+ use crate::context::EngineContext;
5
+ use crate::engine::Engine;
6
+ use crate::event::Event;
7
+ use crate::model::{Order, OrderStatus};
8
+ use crate::pipeline::processor::{Processor, ProcessorResult};
9
+ use pyo3::prelude::*;
10
+ use std::collections::{HashMap, HashSet};
11
+
12
+ pub struct ChannelProcessor;
13
+ fn process_order_request(engine: &mut Engine, py: Python<'_>, mut order: Order) {
14
+ let current_time = engine.context_timestamp();
15
+ engine.maybe_reset_risk_budget_usage(current_time);
16
+ let mut strategy_limit_err = engine.check_strategy_risk_cooldown_mode(&order);
17
+ let mut triggers_risk_fallback = false;
18
+ if strategy_limit_err.is_none() {
19
+ strategy_limit_err = engine.check_strategy_reduce_only_mode(&order);
20
+ }
21
+ if strategy_limit_err.is_none() {
22
+ strategy_limit_err = engine
23
+ .check_strategy_order_size_limit(&order)
24
+ .or_else(|| engine.check_strategy_position_size_limit(&order))
25
+ .or_else(|| engine.check_strategy_order_value_limit(&order));
26
+ triggers_risk_fallback = strategy_limit_err.is_some();
27
+ }
28
+ if strategy_limit_err.is_none() {
29
+ strategy_limit_err = engine.check_strategy_daily_loss_limit(&order, current_time);
30
+ triggers_risk_fallback = strategy_limit_err.is_some();
31
+ }
32
+ if strategy_limit_err.is_none() {
33
+ strategy_limit_err = engine.check_strategy_drawdown_limit(&order);
34
+ triggers_risk_fallback = strategy_limit_err.is_some();
35
+ }
36
+ if strategy_limit_err.is_none() {
37
+ strategy_limit_err = engine.check_strategy_risk_budget_limit(&order);
38
+ }
39
+ if strategy_limit_err.is_none() {
40
+ strategy_limit_err = engine.check_portfolio_risk_budget_limit(&order);
41
+ }
42
+ if triggers_risk_fallback {
43
+ engine.activate_strategy_reduce_only_if_configured(&order);
44
+ engine.activate_strategy_risk_cooldown_if_configured(&order);
45
+ }
46
+ let strategy_limit_err = strategy_limit_err.map(crate::error::AkQuantError::OrderError);
47
+ let check_result = if let Some(err) = strategy_limit_err {
48
+ Err(err)
49
+ } else {
50
+ let ctx = EngineContext {
51
+ instruments: &engine.instruments,
52
+ portfolio: &engine.state.portfolio,
53
+ last_prices: &engine.last_prices,
54
+ trade_tracker: &engine.state.order_manager.trade_tracker,
55
+ market_model: engine.market_manager.model.as_ref(),
56
+ execution_policy_core: engine.execution_policy_core(),
57
+ bar_index: engine.bar_count,
58
+ current_time,
59
+ session: engine.clock.session,
60
+ active_orders: &engine.state.order_manager.active_orders,
61
+ risk_config: &engine.risk_manager.config,
62
+ timezone_name: engine.timezone_name.as_deref(),
63
+ timezone_offset: engine.timezone_offset,
64
+ };
65
+ engine.risk_manager.check_and_adjust(&mut order, &ctx)
66
+ };
67
+ if let Err(err) = check_result {
68
+ order.status = OrderStatus::Rejected;
69
+ order.reject_reason = err.to_string();
70
+ order.updated_at = current_time;
71
+
72
+ let mut risk_payload = HashMap::new();
73
+ risk_payload.insert("order_id", order.id.clone());
74
+ risk_payload.insert("symbol", order.symbol.clone());
75
+ risk_payload.insert("reason", order.reject_reason.clone());
76
+ risk_payload.insert(
77
+ "owner_strategy_id",
78
+ order.owner_strategy_id.clone().unwrap_or_default(),
79
+ );
80
+ engine.emit_stream_event(
81
+ py,
82
+ "risk",
83
+ Some(order.symbol.as_str()),
84
+ "warn",
85
+ risk_payload,
86
+ );
87
+ let _ = engine
88
+ .event_manager
89
+ .send(Event::ExecutionReport(order, None));
90
+ } else {
91
+ let mut order_payload = HashMap::new();
92
+ order_payload.insert("order_id", order.id.clone());
93
+ order_payload.insert("status", format!("{:?}", OrderStatus::New));
94
+ order_payload.insert("symbol", order.symbol.clone());
95
+ order_payload.insert(
96
+ "owner_strategy_id",
97
+ order.owner_strategy_id.clone().unwrap_or_default(),
98
+ );
99
+ engine.emit_stream_event(
100
+ py,
101
+ "order",
102
+ Some(order.symbol.as_str()),
103
+ "info",
104
+ order_payload,
105
+ );
106
+ if !engine.risk_budget_use_trade_mode() {
107
+ engine.apply_risk_budget_usage(&order);
108
+ }
109
+ let _ = engine.event_manager.send(Event::OrderValidated(order));
110
+ }
111
+ }
112
+ fn should_run_post_strategy_match_now(engine: &Engine, orders: &[Order]) -> bool {
113
+ has_orders_matching_phase(engine, &ExecutionPhase::PostStrategy, orders)
114
+ }
115
+
116
+ fn is_reduce_first_order(order: &Order) -> bool {
117
+ crate::model::is_reduce_first_order(order.side, order.position_effect)
118
+ }
119
+
120
+ fn emit_execution_reports_for_current_event(engine: &mut Engine) {
121
+ let Some(event) = engine.current_event.clone() else {
122
+ return;
123
+ };
124
+
125
+ if !matches!(event, Event::Bar(_) | Event::Tick(_) | Event::Timer(_)) {
126
+ return;
127
+ }
128
+
129
+ let ctx = EngineContext {
130
+ instruments: &engine.instruments,
131
+ portfolio: &engine.state.portfolio,
132
+ last_prices: &engine.last_prices,
133
+ trade_tracker: &engine.state.order_manager.trade_tracker,
134
+ market_model: engine.market_manager.model.as_ref(),
135
+ execution_policy_core: engine.execution_policy_core(),
136
+ bar_index: engine.bar_count,
137
+ current_time: engine.context_timestamp(),
138
+ session: engine.clock.session,
139
+ active_orders: &engine.state.order_manager.active_orders,
140
+ risk_config: &engine.risk_manager.config,
141
+ timezone_name: engine.timezone_name.as_deref(),
142
+ timezone_offset: engine.timezone_offset,
143
+ };
144
+
145
+ let reports = engine.execution_model.on_event(&event, &ctx);
146
+ for report in reports {
147
+ let _ = engine.event_manager.send(report);
148
+ }
149
+ }
150
+ impl Processor for ChannelProcessor {
151
+ fn process(
152
+ &mut self,
153
+ engine: &mut Engine,
154
+ py: Python<'_>,
155
+ _strategy: &Bound<'_, PyAny>,
156
+ ) -> PyResult<ProcessorResult> {
157
+ let mut trades_to_process = Vec::new();
158
+ let mut pending_order_requests = Vec::new();
159
+ let mut oco_suppressed_fill_order_ids: HashSet<String> = HashSet::new();
160
+ let mut settle_reductions_before_increases = false;
161
+ let mut run_intermediate_reduce_match = false;
162
+ loop {
163
+ let mut drained_event = false;
164
+ while let Some(event) = engine.event_manager.try_recv() {
165
+ drained_event = true;
166
+ match event {
167
+ Event::OrderRequest(order) => pending_order_requests.push(order),
168
+ Event::OrderValidated(order) => {
169
+ engine.execution_model.on_order(order.clone());
170
+ engine.state.order_manager.add_active_order(order);
171
+ }
172
+ Event::ExecutionReport(order, trade) => {
173
+ apply_execution_report(
174
+ engine,
175
+ py,
176
+ order,
177
+ trade,
178
+ &mut oco_suppressed_fill_order_ids,
179
+ &mut trades_to_process,
180
+ );
181
+ }
182
+ _ => {}
183
+ }
184
+ }
185
+
186
+ if !pending_order_requests.is_empty() {
187
+ if settle_reductions_before_increases {
188
+ flush_accumulated_trades(engine, &mut trades_to_process);
189
+ settle_reductions_before_increases = false;
190
+ }
191
+ if run_intermediate_reduce_match {
192
+ emit_execution_reports_for_current_event(engine);
193
+ run_intermediate_reduce_match = false;
194
+ settle_reductions_before_increases = true;
195
+ continue;
196
+ }
197
+
198
+ pending_order_requests.sort_by(|left, right| {
199
+ let left_phase_rank =
200
+ crate::model::reduction_priority_rank(left.side, left.position_effect);
201
+ let right_phase_rank =
202
+ crate::model::reduction_priority_rank(right.side, right.position_effect);
203
+ let left_priority = engine.strategy_priority_for_order(left);
204
+ let right_priority = engine.strategy_priority_for_order(right);
205
+ left_phase_rank
206
+ .cmp(&right_phase_rank)
207
+ .then_with(|| right_priority.cmp(&left_priority))
208
+ .then_with(|| {
209
+ let left_id =
210
+ Engine::normalized_order_strategy_id(left).unwrap_or_default();
211
+ let right_id =
212
+ Engine::normalized_order_strategy_id(right).unwrap_or_default();
213
+ left_id.cmp(&right_id)
214
+ })
215
+ });
216
+
217
+ let has_reduce = pending_order_requests.iter().any(is_reduce_first_order);
218
+ let has_increase = pending_order_requests
219
+ .iter()
220
+ .any(|order| !is_reduce_first_order(order));
221
+ let can_do_two_phase = has_reduce
222
+ && has_increase
223
+ && should_run_post_strategy_match_now(engine, &pending_order_requests);
224
+
225
+ if can_do_two_phase {
226
+ let mut reduce_orders = Vec::new();
227
+ let mut increase_orders = Vec::new();
228
+ for order in pending_order_requests.drain(..) {
229
+ if is_reduce_first_order(&order) {
230
+ reduce_orders.push(order);
231
+ } else {
232
+ increase_orders.push(order);
233
+ }
234
+ }
235
+
236
+ for order in reduce_orders {
237
+ process_order_request(engine, py, order);
238
+ }
239
+
240
+ if !increase_orders.is_empty() {
241
+ pending_order_requests.extend(increase_orders);
242
+ run_intermediate_reduce_match = true;
243
+ }
244
+ } else {
245
+ for order in pending_order_requests.drain(..) {
246
+ process_order_request(engine, py, order);
247
+ }
248
+ }
249
+ continue;
250
+ }
251
+
252
+ if !drained_event {
253
+ break;
254
+ }
255
+ }
256
+
257
+ flush_accumulated_trades(engine, &mut trades_to_process);
258
+
259
+ Ok(ProcessorResult::Next)
260
+ }
261
+ }
@@ -0,0 +1,17 @@
1
+ use crate::engine::Engine;
2
+ use crate::pipeline::processor::{Processor, ProcessorResult};
3
+ use pyo3::prelude::*;
4
+
5
+ pub struct CleanupProcessor;
6
+
7
+ impl Processor for CleanupProcessor {
8
+ fn process(
9
+ &mut self,
10
+ engine: &mut Engine,
11
+ _py: Python<'_>,
12
+ _strategy: &Bound<'_, PyAny>,
13
+ ) -> PyResult<ProcessorResult> {
14
+ engine.state.order_manager.cleanup_finished_orders();
15
+ Ok(ProcessorResult::Next)
16
+ }
17
+ }