powerquant-cli 0.2.6__py3-none-any.whl

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 (355) hide show
  1. powerquant_cli-0.2.6.dist-info/METADATA +62 -0
  2. powerquant_cli-0.2.6.dist-info/RECORD +355 -0
  3. powerquant_cli-0.2.6.dist-info/WHEEL +4 -0
  4. powerquant_cli-0.2.6.dist-info/entry_points.txt +2 -0
  5. powerquant_cli-0.2.6.dist-info/licenses/LICENSE +36 -0
  6. powerquant_cli-0.2.6.dist-info/licenses/licenses/LICENSE.vnpy +21 -0
  7. pqdata/__init__.py +6 -0
  8. pqdata/client.py +389 -0
  9. pqdata/download.py +60 -0
  10. pqdata/exceptions.py +14 -0
  11. pqdata/schema/__init__.py +1 -0
  12. pqdata/schema/adjustment_schema.py +67 -0
  13. pqdata/schema/bar_content_schema.py +24 -0
  14. pqdata/schema/bar_schema.py +56 -0
  15. pqdata/schema/corporate_action_schema.py +46 -0
  16. pqdata/schema/history_schema.py +61 -0
  17. pqdata/schema/pit_daily_window.py +16 -0
  18. pqdata/schema/query_schema.py +53 -0
  19. pqdata/schema/quote_schema.py +73 -0
  20. pqdata/schema/reference_schema.py +48 -0
  21. pqdata/schema/reliability_schema.py +152 -0
  22. quant_cli/__init__.py +0 -0
  23. quant_cli/api.py +39 -0
  24. quant_cli/app/__init__.py +0 -0
  25. quant_cli/app/backtest/__init__.py +0 -0
  26. quant_cli/app/backtest/cli/__init__.py +0 -0
  27. quant_cli/app/backtest/cli/local_backtest_cli.py +72 -0
  28. quant_cli/app/backtest/local_backtest_dependency.py +10 -0
  29. quant_cli/app/backtest/local_backtest_schema.py +49 -0
  30. quant_cli/app/backtest/repository/__init__.py +0 -0
  31. quant_cli/app/backtest/repository/backtest_local_repository.py +67 -0
  32. quant_cli/app/backtest/service/__init__.py +0 -0
  33. quant_cli/app/backtest/service/local_backtest_service.py +271 -0
  34. quant_cli/app/data/__init__.py +0 -0
  35. quant_cli/app/data/cli/__init__.py +0 -0
  36. quant_cli/app/data/cli/data_cli.py +78 -0
  37. quant_cli/app/data/data_dependency.py +35 -0
  38. quant_cli/app/data/repository/__init__.py +0 -0
  39. quant_cli/app/data/repository/parquet_repository.py +124 -0
  40. quant_cli/app/data/schema/__init__.py +0 -0
  41. quant_cli/app/data/schema/dataset_audit_schema.py +37 -0
  42. quant_cli/app/data/schema/dataset_request_schema.py +28 -0
  43. quant_cli/app/data/schema/parquet_file_schema.py +17 -0
  44. quant_cli/app/data/service/__init__.py +0 -0
  45. quant_cli/app/data/service/bar_cache_service.py +287 -0
  46. quant_cli/app/data/service/dataset_audit_service.py +137 -0
  47. quant_cli/app/data/service/dataset_load_service.py +115 -0
  48. quant_cli/app/data/service/dataset_prepare_service.py +187 -0
  49. quant_cli/app/factor/__init__.py +0 -0
  50. quant_cli/app/factor/cli/__init__.py +0 -0
  51. quant_cli/app/factor/cli/factor_definition_cli.py +81 -0
  52. quant_cli/app/factor/cli/local_factor_cli.py +102 -0
  53. quant_cli/app/factor/cli/local_selection_cli.py +58 -0
  54. quant_cli/app/factor/interface/__init__.py +0 -0
  55. quant_cli/app/factor/interface/local_selection_store.py +12 -0
  56. quant_cli/app/factor/local_factor_dependency.py +38 -0
  57. quant_cli/app/factor/repository/__init__.py +0 -0
  58. quant_cli/app/factor/repository/factor_local_repository.py +154 -0
  59. quant_cli/app/factor/repository/selection_local_repository.py +67 -0
  60. quant_cli/app/factor/schema/local_selection_schema.py +56 -0
  61. quant_cli/app/factor/service/__init__.py +0 -0
  62. quant_cli/app/factor/service/factor_data_prepare_service.py +181 -0
  63. quant_cli/app/factor/service/local_factor_query_service.py +146 -0
  64. quant_cli/app/factor/service/local_factor_service.py +120 -0
  65. quant_cli/app/factor/service/local_selection_service.py +139 -0
  66. quant_cli/app/job/__init__.py +0 -0
  67. quant_cli/app/job/cli/__init__.py +0 -0
  68. quant_cli/app/job/cli/local_job_cli.py +69 -0
  69. quant_cli/app/job/schema/__init__.py +0 -0
  70. quant_cli/app/job/schema/local_job_schema.py +11 -0
  71. quant_cli/app/job/service/__init__.py +0 -0
  72. quant_cli/app/job/service/local_job_service.py +60 -0
  73. quant_cli/app/quant_cli.py +49 -0
  74. quant_cli/app/research/__init__.py +0 -0
  75. quant_cli/app/research/cli/__init__.py +0 -0
  76. quant_cli/app/research/cli/local_research_cli.py +93 -0
  77. quant_cli/app/research/local_research_dependency.py +10 -0
  78. quant_cli/app/research/repository/__init__.py +0 -0
  79. quant_cli/app/research/repository/research_local_repository.py +67 -0
  80. quant_cli/app/research/schema/__init__.py +0 -0
  81. quant_cli/app/research/schema/local_research_schema.py +26 -0
  82. quant_cli/app/research/service/__init__.py +0 -0
  83. quant_cli/app/research/service/local_research_execution_service.py +105 -0
  84. quant_cli/app/research/service/local_research_service.py +101 -0
  85. quant_cli/app/strategy/__init__.py +0 -0
  86. quant_cli/app/strategy/cli/__init__.py +0 -0
  87. quant_cli/app/strategy/cli/strategy_cli.py +104 -0
  88. quant_cli/common/__init__.py +0 -0
  89. quant_cli/common/constants.py +7 -0
  90. quant_cli/common/database/__init__.py +0 -0
  91. quant_cli/common/database/duckdb_connection.py +30 -0
  92. quant_cli/common/http/__init__.py +0 -0
  93. quant_cli/common/http/definition_client.py +54 -0
  94. quant_cli/common/utils/__init__.py +0 -0
  95. quant_cli/common/utils/cli_utils.py +37 -0
  96. quant_cli/common/utils/platform_utils.py +11 -0
  97. quant_cli/common/utils/runtime_environment.py +12 -0
  98. quant_cli/examples/cached_factor_screen.py +165 -0
  99. quant_cli/examples/factors/ma_close_10.py +10 -0
  100. quant_cli/examples/factors/max_drawdown_20d.py +18 -0
  101. quant_cli/examples/factors/momentum_20d.py +13 -0
  102. quant_cli/examples/factors/multi_intraday_screen.py +16 -0
  103. quant_cli/examples/factors/risk_adjusted_momentum_20d.py +18 -0
  104. quant_cli/examples/factors/volume_ratio_5d.py +14 -0
  105. quant_cli/examples/factors/volume_weighted_close_pressure.py +14 -0
  106. quant_cli/examples/full_market_screen.py +189 -0
  107. quant_cli/examples/local_quant_validation.py +196 -0
  108. quant_cli/examples/published_definition_validation.py +219 -0
  109. quant_cli/examples/research/factor_pit_coverage.py +87 -0
  110. quant_cli/examples/research/ipo_first_day.py +348 -0
  111. quant_cli/examples/selection/README.md +59 -0
  112. quant_cli/examples/selection/backtest.json +18 -0
  113. quant_cli/examples/selection/buy_hold.py +7 -0
  114. quant_cli/examples/selection/compute.json +15 -0
  115. quant_cli/examples/selection/prepare.json +13 -0
  116. quant_cli/examples/selection/select.json +44 -0
  117. quant_cli/examples/strategies/bar_roundtrip.py +12 -0
  118. quant_cli/examples/strategies/buy_and_hold.py +11 -0
  119. quant_cli/examples/strategies/contrarian_timing.py +14 -0
  120. quant_cli/examples/strategies/factor_gate_vwap.py +22 -0
  121. quant_cli/examples/strategies/ipo_next_day_vwap.py +31 -0
  122. quant_cli/examples/strategies/momentum_timing.py +14 -0
  123. quant_cli/examples/strategies/monthly_equal_weight.py +19 -0
  124. quant_cli/examples/strategies/session_roundtrip.py +16 -0
  125. quant_cli/examples/strategies/trend_drawdown_guard.py +22 -0
  126. quant_cli/examples/strategies/weekly_risk_adjusted_rotation.py +61 -0
  127. quant_cli/examples/unified_snapshot_validation.py +260 -0
  128. quant_core/__init__.py +0 -0
  129. quant_core/app/SPEC.md +18 -0
  130. quant_core/app/__init__.py +0 -0
  131. quant_core/app/backtest/SPEC.md +65 -0
  132. quant_core/app/backtest/__init__.py +1 -0
  133. quant_core/app/backtest/backtest_input_schema.py +20 -0
  134. quant_core/app/backtest/backtest_run.py +393 -0
  135. quant_core/app/backtest/data_guard.py +77 -0
  136. quant_core/app/backtest/historical_clock.py +26 -0
  137. quant_core/app/backtest/legacy_result_schema.py +34 -0
  138. quant_core/app/backtest/metrics.py +194 -0
  139. quant_core/app/backtest/performance_report.py +61 -0
  140. quant_core/app/backtest/report_schema.py +64 -0
  141. quant_core/app/backtest/schema.py +477 -0
  142. quant_core/app/backtest/service/__init__.py +0 -0
  143. quant_core/app/backtest/service/backtest_execution_service.py +114 -0
  144. quant_core/app/data/__init__.py +0 -0
  145. quant_core/app/data/constants.py +284 -0
  146. quant_core/app/data/domain/__init__.py +0 -0
  147. quant_core/app/data/domain/trading_quantity_value.py +57 -0
  148. quant_core/app/data/exceptions.py +95 -0
  149. quant_core/app/data/interface/__init__.py +0 -0
  150. quant_core/app/data/interface/adjustment_factor_query.py +21 -0
  151. quant_core/app/data/interface/market_data_query.py +71 -0
  152. quant_core/app/data/interface/market_data_verification.py +20 -0
  153. quant_core/app/data/interface/market_event_source.py +11 -0
  154. quant_core/app/data/interface/market_revision_query.py +8 -0
  155. quant_core/app/data/interface/market_runtime_repository.py +44 -0
  156. quant_core/app/data/interface/market_time_slice_source.py +11 -0
  157. quant_core/app/data/repository/__init__.py +0 -0
  158. quant_core/app/data/schema/__init__.py +1 -0
  159. quant_core/app/data/schema/adjustment_schema.py +67 -0
  160. quant_core/app/data/schema/corporate_action_schema.py +46 -0
  161. quant_core/app/data/schema/event_schema.py +506 -0
  162. quant_core/app/data/schema/exchange_operating_state.py +37 -0
  163. quant_core/app/data/schema/exchange_state_schema.py +106 -0
  164. quant_core/app/data/schema/history_schema.py +48 -0
  165. quant_core/app/data/schema/instrument_identity.py +69 -0
  166. quant_core/app/data/schema/market_schema.py +128 -0
  167. quant_core/app/data/schema/pit_daily_window.py +16 -0
  168. quant_core/app/data/schema/quote_schema.py +73 -0
  169. quant_core/app/data/schema/reliability_schema.py +152 -0
  170. quant_core/app/data/schema/snapshot_schema.py +82 -0
  171. quant_core/app/data/schema/trading_state_schema.py +211 -0
  172. quant_core/app/data/service/__init__.py +0 -0
  173. quant_core/app/data/service/calendar_coverage_service.py +63 -0
  174. quant_core/app/data/service/prepared_data_verification_service.py +40 -0
  175. quant_core/app/data/service/prepared_market_service.py +189 -0
  176. quant_core/app/data/service/price_adjustment_service.py +235 -0
  177. quant_core/app/factor/LICENSE.vnpy +21 -0
  178. quant_core/app/factor/__init__.py +13 -0
  179. quant_core/app/factor/builtin/__init__.py +0 -0
  180. quant_core/app/factor/builtin/alpha101_recipe.py +1095 -0
  181. quant_core/app/factor/builtin/alpha158_recipe.py +1284 -0
  182. quant_core/app/factor/builtin/factor_recipe_catalog.py +100 -0
  183. quant_core/app/factor/computation/__init__.py +0 -0
  184. quant_core/app/factor/computation/factor_batch_evaluator.py +141 -0
  185. quant_core/app/factor/computation/factor_expression.py +106 -0
  186. quant_core/app/factor/computation/factor_source_loader.py +24 -0
  187. quant_core/app/factor/computation/operators/__init__.py +0 -0
  188. quant_core/app/factor/computation/operators/arithmetic_operator.py +182 -0
  189. quant_core/app/factor/computation/operators/cross_section_operator.py +66 -0
  190. quant_core/app/factor/computation/operators/time_series_operator.py +342 -0
  191. quant_core/app/factor/computation/panel_evaluator.py +59 -0
  192. quant_core/app/factor/computation/panel_expression.py +193 -0
  193. quant_core/app/factor/computation/python_factor_executor.py +87 -0
  194. quant_core/app/factor/computation/universe_filter.py +25 -0
  195. quant_core/app/factor/constants.py +114 -0
  196. quant_core/app/factor/exceptions.py +35 -0
  197. quant_core/app/factor/factor_dependency.py +65 -0
  198. quant_core/app/factor/interface/__init__.py +0 -0
  199. quant_core/app/factor/interface/factor_compute.py +12 -0
  200. quant_core/app/factor/interface/factor_execution.py +15 -0
  201. quant_core/app/factor/interface/factor_local_data.py +42 -0
  202. quant_core/app/factor/interface/factor_panel_compute.py +18 -0
  203. quant_core/app/factor/interface/factor_panel_input.py +12 -0
  204. quant_core/app/factor/interface/factor_query_store.py +39 -0
  205. quant_core/app/factor/interface/public/__init__.py +0 -0
  206. quant_core/app/factor/interface/public/factor_diagnostics.py +18 -0
  207. quant_core/app/factor/interface/public/factor_exposure_query.py +12 -0
  208. quant_core/app/factor/interface/public/factor_local.py +15 -0
  209. quant_core/app/factor/interface/public/factor_query.py +57 -0
  210. quant_core/app/factor/interface/public/factor_selection.py +20 -0
  211. quant_core/app/factor/model/__init__.py +0 -0
  212. quant_core/app/factor/repository/__init__.py +0 -0
  213. quant_core/app/factor/schema/__init__.py +1 -0
  214. quant_core/app/factor/schema/diagnostic_types.py +42 -0
  215. quant_core/app/factor/schema/factor_artifact_schema.py +36 -0
  216. quant_core/app/factor/schema/factor_computation_schema.py +102 -0
  217. quant_core/app/factor/schema/factor_compute_request.py +24 -0
  218. quant_core/app/factor/schema/factor_contract.py +257 -0
  219. quant_core/app/factor/schema/factor_input_schema.py +32 -0
  220. quant_core/app/factor/schema/factor_panel.py +65 -0
  221. quant_core/app/factor/schema/factor_publication_schema.py +41 -0
  222. quant_core/app/factor/schema/factor_query_result_schema.py +12 -0
  223. quant_core/app/factor/schema/factor_result_schema.py +6 -0
  224. quant_core/app/factor/schema/local_factor_schema.py +42 -0
  225. quant_core/app/factor/schema/panel_policy.py +13 -0
  226. quant_core/app/factor/schema/selection_report.py +116 -0
  227. quant_core/app/factor/schema/selection_types.py +135 -0
  228. quant_core/app/factor/service/__init__.py +1 -0
  229. quant_core/app/factor/service/factor_compute_service.py +140 -0
  230. quant_core/app/factor/service/factor_diagnostic_runner.py +134 -0
  231. quant_core/app/factor/service/factor_diagnostics_service.py +140 -0
  232. quant_core/app/factor/service/factor_exposure_service.py +24 -0
  233. quant_core/app/factor/service/factor_input_service.py +258 -0
  234. quant_core/app/factor/service/factor_local_execution_service.py +51 -0
  235. quant_core/app/factor/service/factor_panel_service.py +86 -0
  236. quant_core/app/factor/service/factor_runtime_service.py +306 -0
  237. quant_core/app/factor/service/factor_selection_runner.py +88 -0
  238. quant_core/app/factor/service/published_factor_execution_service.py +160 -0
  239. quant_core/app/factor/service/selection_service.py +234 -0
  240. quant_core/app/factor/visibility.py +22 -0
  241. quant_core/app/opportunity/SPEC.md +45 -0
  242. quant_core/app/opportunity/__init__.py +1 -0
  243. quant_core/app/order_execution/SPEC.md +64 -0
  244. quant_core/app/order_execution/__init__.py +1 -0
  245. quant_core/app/order_execution/bar_matcher.py +55 -0
  246. quant_core/app/order_execution/constants.py +142 -0
  247. quant_core/app/order_execution/domain/__init__.py +45 -0
  248. quant_core/app/order_execution/domain/execution_batch.py +108 -0
  249. quant_core/app/order_execution/domain/order_entity.py +197 -0
  250. quant_core/app/order_execution/domain/order_event.py +87 -0
  251. quant_core/app/order_execution/domain/order_policy.py +29 -0
  252. quant_core/app/order_execution/domain/order_value.py +142 -0
  253. quant_core/app/order_execution/fee_model.py +60 -0
  254. quant_core/app/order_execution/interface/__init__.py +0 -0
  255. quant_core/app/order_execution/interface/order_account.py +10 -0
  256. quant_core/app/order_execution/interface/order_trading_rules.py +10 -0
  257. quant_core/app/order_execution/interface/public/__init__.py +0 -0
  258. quant_core/app/order_execution/interface/public/execution_account.py +53 -0
  259. quant_core/app/order_execution/interface/public/order_events.py +19 -0
  260. quant_core/app/order_execution/interface/public/order_execution.py +107 -0
  261. quant_core/app/order_execution/interface/public/order_runtime.py +30 -0
  262. quant_core/app/order_execution/interface/public/order_submission.py +17 -0
  263. quant_core/app/order_execution/order_event_bus.py +28 -0
  264. quant_core/app/order_execution/order_execution_dependency.py +48 -0
  265. quant_core/app/order_execution/order_submission_service.py +301 -0
  266. quant_core/app/order_execution/schema/__init__.py +0 -0
  267. quant_core/app/order_execution/schema/order_request_schema.py +88 -0
  268. quant_core/app/order_execution/simulation_broker.py +883 -0
  269. quant_core/app/order_execution/slippage_model.py +40 -0
  270. quant_core/app/order_execution/tradability_policy.py +174 -0
  271. quant_core/app/order_execution/validators.py +40 -0
  272. quant_core/app/portfolio/SPEC.md +69 -0
  273. quant_core/app/portfolio/__init__.py +0 -0
  274. quant_core/app/portfolio/domain/__init__.py +32 -0
  275. quant_core/app/portfolio/domain/entitlement_entity.py +45 -0
  276. quant_core/app/portfolio/domain/portfolio_entity.py +511 -0
  277. quant_core/app/portfolio/domain/portfolio_event.py +59 -0
  278. quant_core/app/portfolio/domain/portfolio_value.py +21 -0
  279. quant_core/app/portfolio/domain/position_entity.py +165 -0
  280. quant_core/app/portfolio/domain/reservation_entity.py +69 -0
  281. quant_core/app/portfolio/interface/__init__.py +0 -0
  282. quant_core/app/portfolio/interface/public/__init__.py +0 -0
  283. quant_core/app/portfolio/interface/public/portfolio.py +77 -0
  284. quant_core/app/portfolio/interface/public/simulation_account.py +16 -0
  285. quant_core/app/portfolio/ledger_schema.py +130 -0
  286. quant_core/app/portfolio/order_account_listener.py +36 -0
  287. quant_core/app/portfolio/portfolio_dependency.py +21 -0
  288. quant_core/app/portfolio/portfolio_schema.py +204 -0
  289. quant_core/app/portfolio/simulated_portfolio.py +427 -0
  290. quant_core/app/research/SPEC.md +57 -0
  291. quant_core/app/research/__init__.py +0 -0
  292. quant_core/app/research/adapter/__init__.py +0 -0
  293. quant_core/app/research/adapter/market_label_query.py +84 -0
  294. quant_core/app/research/interface/__init__.py +0 -0
  295. quant_core/app/research/interface/research_dataset.py +47 -0
  296. quant_core/app/research/interface/research_evaluation.py +19 -0
  297. quant_core/app/research/schema/__init__.py +0 -0
  298. quant_core/app/research/schema/comparison_schema.py +12 -0
  299. quant_core/app/research/schema/dataset_schema.py +112 -0
  300. quant_core/app/research/schema/research_schema.py +120 -0
  301. quant_core/app/research/service/__init__.py +0 -0
  302. quant_core/app/research/service/research_dataset_service.py +80 -0
  303. quant_core/app/research/service/research_evaluation_service.py +56 -0
  304. quant_core/app/research/service/research_service.py +171 -0
  305. quant_core/app/strategy/SPEC.md +215 -0
  306. quant_core/app/strategy/__init__.py +1 -0
  307. quant_core/app/strategy/constants.py +87 -0
  308. quant_core/app/strategy/exceptions.py +32 -0
  309. quant_core/app/strategy/interface/__init__.py +0 -0
  310. quant_core/app/strategy/interface/public/__init__.py +0 -0
  311. quant_core/app/strategy/interface/public/portfolio_construction.py +19 -0
  312. quant_core/app/strategy/interface/public/strategy.py +30 -0
  313. quant_core/app/strategy/interface/public/strategy_market_query.py +53 -0
  314. quant_core/app/strategy/interface/public/strategy_portfolio_reader.py +33 -0
  315. quant_core/app/strategy/interface/strategy_factor_reader.py +19 -0
  316. quant_core/app/strategy/interface/strategy_order_reader.py +14 -0
  317. quant_core/app/strategy/schema/__init__.py +0 -0
  318. quant_core/app/strategy/schema/portfolio_schema.py +28 -0
  319. quant_core/app/strategy/schema/portfolio_target.py +70 -0
  320. quant_core/app/strategy/schema/python_strategy_schema.py +94 -0
  321. quant_core/app/strategy/schema/screener_schema.py +51 -0
  322. quant_core/app/strategy/schema/signal_schema.py +33 -0
  323. quant_core/app/strategy/schema/strategy_publication_schema.py +32 -0
  324. quant_core/app/strategy/schema/strategy_schema.py +96 -0
  325. quant_core/app/strategy/service/__init__.py +0 -0
  326. quant_core/app/strategy/service/factor_top_n_strategy.py +103 -0
  327. quant_core/app/strategy/service/portfolio_construction_service.py +142 -0
  328. quant_core/app/strategy/service/python_strategy_service.py +65 -0
  329. quant_core/app/strategy/service/screener_service.py +77 -0
  330. quant_core/app/strategy/service/store_factor_reader.py +120 -0
  331. quant_core/app/strategy/service/strategy_market_view.py +120 -0
  332. quant_core/app/strategy/service/strategy_runtime_service.py +319 -0
  333. quant_core/app/strategy/service/strategy_scheduler.py +50 -0
  334. quant_core/app/strategy/strategies/__init__.py +7 -0
  335. quant_core/app/strategy/strategies/momentum_volume_breakout.py +157 -0
  336. quant_core/app/strategy/strategy_context.py +316 -0
  337. quant_core/app/strategy/strategy_dependency.py +101 -0
  338. quant_core/app/trading_runtime/SPEC.md +70 -0
  339. quant_core/app/trading_runtime/__init__.py +1 -0
  340. quant_core/app/trading_runtime/domain/__init__.py +0 -0
  341. quant_core/app/trading_runtime/replay/__init__.py +0 -0
  342. quant_core/app/trading_runtime/replay/daily_state_expand.py +49 -0
  343. quant_core/app/trading_runtime/replay/exchange_state_resolver.py +90 -0
  344. quant_core/app/trading_runtime/replay/market_runtime_service.py +573 -0
  345. quant_core/app/trading_runtime/replay/market_state_resolver.py +150 -0
  346. quant_core/app/trading_runtime/runtime_ports.py +64 -0
  347. quant_core/app/trading_runtime/runtime_schema.py +129 -0
  348. quant_core/app/trading_runtime/runtime_session.py +361 -0
  349. quant_core/app/trading_runtime/strategy_portfolio_adapter.py +58 -0
  350. quant_core/app/trading_runtime/trading_rule_book.py +37 -0
  351. quant_core/app/trading_runtime/trading_rule_query.py +12 -0
  352. quant_core/common/__init__.py +0 -0
  353. quant_core/common/exceptions/__init__.py +0 -0
  354. quant_core/common/exceptions/base.py +89 -0
  355. quant_core/common/local_run.py +45 -0
@@ -0,0 +1,22 @@
1
+ """多标的趋势策略:正动量且近期回撤受控时持有,失效则退出。"""
2
+
3
+ from decimal import Decimal
4
+
5
+
6
+ async def on_bar(event, context):
7
+ instrument_id = event.payload.instrument_id
8
+ momentum = context.factor_value("momentum_20d", instrument_id)
9
+ drawdown = context.factor_value("max_drawdown_20d", instrument_id)
10
+ volume_ratio = context.factor_value("volume_ratio_5d", instrument_id)
11
+ if None in (momentum, drawdown, volume_ratio):
12
+ return []
13
+ position = await context.portfolio.get_position(instrument_id)
14
+ held = position.quantity if position else 0
15
+ if context.get_open_orders(instrument_id):
16
+ return []
17
+ allowed = momentum > 0 and drawdown >= Decimal("-0.12") and volume_ratio >= Decimal("0.7")
18
+ if allowed and not held:
19
+ context.order_target_percent(instrument_id, Decimal("0.2"))
20
+ elif not allowed and held:
21
+ context.order_target(instrument_id, 0)
22
+ return []
@@ -0,0 +1,61 @@
1
+ """每周三因子轮动:与研究入口共用因子排名和缺值规则。"""
2
+
3
+ from decimal import Decimal
4
+
5
+ from quant_core.app.factor.schema.factor_contract import FactorIdentity
6
+ from quant_core.app.factor.schema.selection_types import (
7
+ FactorFilter,
8
+ FactorTransform,
9
+ FactorWeight,
10
+ NormalizationMethod,
11
+ SelectionDefinition,
12
+ )
13
+
14
+
15
+ def _identity(name):
16
+ return FactorIdentity(name=name, source="local-python", definition_version="3")
17
+
18
+
19
+ SELECTION = SelectionDefinition(
20
+ factors=(
21
+ FactorWeight(
22
+ _identity("momentum_20d"),
23
+ Decimal(1),
24
+ transform=FactorTransform(normalization=NormalizationMethod.RANK),
25
+ ),
26
+ FactorWeight(
27
+ FactorIdentity(
28
+ name="risk_adjusted_momentum_20d", source="local-python", definition_version="1"
29
+ ),
30
+ Decimal(1),
31
+ transform=FactorTransform(normalization=NormalizationMethod.RANK),
32
+ ),
33
+ ),
34
+ filters=(
35
+ FactorFilter(_identity("momentum_20d"), minimum=Decimal(0)),
36
+ FactorFilter(_identity("volume_ratio_5d"), minimum=Decimal("0.6")),
37
+ ),
38
+ top_n=2,
39
+ )
40
+
41
+
42
+ async def on_bar(event, context):
43
+ week = context.event_time.isocalendar()[:2]
44
+ if context.state.get("rebalance_week") == week:
45
+ return []
46
+ if any(
47
+ (bar := context.current(instrument_id)) is None or bar.timestamp != context.event_time
48
+ for instrument_id in context.universe
49
+ ):
50
+ return []
51
+
52
+ selected = {row.instrument_id for row in context.select_factors(SELECTION).selected}
53
+ weight = Decimal("0.9") / len(selected) if selected else Decimal("0")
54
+ context.rebalance(
55
+ {
56
+ instrument_id: weight if instrument_id in selected else Decimal("0")
57
+ for instrument_id in context.universe
58
+ }
59
+ )
60
+ context.state["rebalance_week"] = week
61
+ return []
@@ -0,0 +1,260 @@
1
+ """同一快照的真实 CLI 验收;日期固定为 2026-09 样本,不评价因子投资价值。"""
2
+
3
+ import argparse
4
+ import json
5
+ import os
6
+ import subprocess
7
+ import sys
8
+ from datetime import date, datetime, time
9
+ from decimal import Decimal
10
+ from pathlib import Path
11
+ from zoneinfo import ZoneInfo
12
+
13
+ from quant_core.app.backtest.schema import BacktestResult
14
+
15
+ from quant_cli.app.data.service.dataset_load_service import load_dataset
16
+
17
+ CN = ZoneInfo("Asia/Shanghai")
18
+ SOURCE = (
19
+ "from quant_core.app.factor.computation.factor_expression import MA, Factor\n"
20
+ "factors = {'reversal5': -MA(Factor('close') / Factor('open') - 1, 5)}"
21
+ )
22
+ STRATEGY = """def handle_bar(context, bars):
23
+ if context.event_time.date().isoformat() == "2026-09-28":
24
+ rows = context.factors.get("reversal5", {})
25
+ scores = [(key, context.factor_value("reversal5", key)) for key in context.universe]
26
+ assert all(value is not None for key, value in scores), "missing visible factor"
27
+ assert all(row.available_at <= context.event_time for row in rows.values())
28
+ assert all(row.as_of.date().isoformat() == "2026-09-24" for row in rows.values())
29
+ selected = [key for key, value in sorted(scores, key=lambda row: (-row[1], row[0]))[:3]]
30
+ context.state["selected"] = selected
31
+ for key in selected:
32
+ context.order_shares(key, 200)
33
+ elif context.event_time.date().isoformat() == "2026-09-30":
34
+ for key in context.state.get("selected", []):
35
+ context.order_target(key, 0)
36
+ """
37
+ BOOTSTRAP = """
38
+ import asyncio, sys
39
+ from importlib.abc import MetaPathFinder
40
+ from unittest.mock import patch
41
+ class Guard(MetaPathFinder):
42
+ def find_spec(self, fullname, path=None, target=None):
43
+ if fullname.startswith('quant_backend'):
44
+ raise AssertionError('Remote infrastructure import: ' + fullname)
45
+ sys.meta_path.insert(0, Guard())
46
+ from quant_cli.app.quant_cli import main
47
+ with asyncio.Runner() as runner:
48
+ runner.get_loop()
49
+ with (
50
+ patch('socket.socket', side_effect=AssertionError('Offline network access')),
51
+ patch('asyncio.run', runner.run),
52
+ ):
53
+ main()
54
+ """
55
+
56
+
57
+ def command(*args: str, expected: int = 0) -> dict | list:
58
+ result = subprocess.run(
59
+ [sys.executable, "-c", BOOTSTRAP, *args],
60
+ cwd=Path(__file__).parents[1],
61
+ capture_output=True,
62
+ text=True,
63
+ encoding="utf-8",
64
+ env={**os.environ, "PYTHONIOENCODING": "utf-8"},
65
+ timeout=180,
66
+ )
67
+ if result.returncode != expected:
68
+ raise AssertionError(f"CLI failed {args}: {result.stdout}\n{result.stderr}")
69
+ return json.loads(result.stdout)
70
+
71
+
72
+ def validate(manifest: Path, output: Path) -> dict:
73
+ output.mkdir(parents=True, exist_ok=True)
74
+ database = str(output / "research.duckdb")
75
+ market, _ = load_dataset(manifest, 1000000)
76
+ ids = sorted(market.facts)
77
+
78
+ def config(name: str, payload: dict) -> str:
79
+ path = output / f"{name}.json"
80
+ path.write_text(
81
+ json.dumps(payload, default=str, ensure_ascii=False, indent=2), encoding="utf-8"
82
+ )
83
+ return str(path)
84
+
85
+ audit = command(
86
+ "data",
87
+ "audit",
88
+ "--manifest",
89
+ str(manifest),
90
+ "--config",
91
+ config(
92
+ "audit-request",
93
+ {
94
+ "instrument_ids": ids,
95
+ "start": "2026-09-02",
96
+ "end": "2026-09-30",
97
+ },
98
+ ),
99
+ )
100
+ assert audit["ready"]
101
+ config("audit-report", audit)
102
+ factor = command(
103
+ "factor",
104
+ "compute",
105
+ "--database",
106
+ database,
107
+ "--config",
108
+ config(
109
+ "factor-request",
110
+ {
111
+ "source": SOURCE,
112
+ "manifest_path": str(manifest),
113
+ "instrument_ids": ids,
114
+ "start": "2026-09-08",
115
+ "end": "2026-09-29",
116
+ },
117
+ ),
118
+ )
119
+ cells = command(
120
+ "factor", "values", "--run", factor["uuid"], "--limit", "10000", "--database", database
121
+ )
122
+ assert len(cells) == factor["result"]["cells"]
123
+ error = Decimal(0)
124
+ labels, selection = [], []
125
+ for instrument in ids:
126
+ facts = market.facts[instrument]
127
+ days = sorted(row.trade_date for row in facts.calendar if row.is_open)
128
+ bars = {bar.timestamp.astimezone(CN).date(): bar for bar in market.bars[(instrument, "1d")]}
129
+ for cell in (row for row in cells if row["instrument_id"] == instrument):
130
+ ts = datetime.fromisoformat(cell["as_of"])
131
+ day = ts.astimezone(CN).date()
132
+ index = days.index(day)
133
+ window = [bars.get(d) for d in days[max(0, index - 4) : index + 1]]
134
+ expected = None
135
+ if len(window) == 5 and all(
136
+ bar and not bar.suspended and bar.open > 0 for bar in window
137
+ ):
138
+ expected = -sum((bar.close / bar.open - 1 for bar in window), Decimal(0)) / 5
139
+ actual = None if cell["value"] is None else Decimal(cell["value"])
140
+ assert (expected is None) == (actual is None)
141
+ if expected is not None:
142
+ error = max(error, abs(expected - actual))
143
+ assert abs(expected - actual) < Decimal("1e-12")
144
+ if day == date(2026, 9, 24) and expected is not None:
145
+ selection.append((instrument, expected))
146
+ if index + 2 < len(days) and days[index + 2] <= date(2026, 9, 24):
147
+ buy, sell = bars.get(days[index + 1]), bars.get(days[index + 2])
148
+ if not buy or not sell or buy.suspended or sell.suspended:
149
+ continue
150
+ labels.append(
151
+ {
152
+ "decision_time": ts.isoformat(),
153
+ "instrument_id": instrument,
154
+ "end_time": datetime.combine(days[index + 2], time(15), CN).isoformat(),
155
+ "value": str(sell.close / buy.close - 1),
156
+ "price_basis": "raw-next-close-to-following-close",
157
+ "data_revision": market.revision,
158
+ }
159
+ )
160
+ research = command(
161
+ "research",
162
+ "evaluate",
163
+ "--database",
164
+ database,
165
+ "--config",
166
+ config(
167
+ "research-request",
168
+ {
169
+ "factor_run_uuid": factor["uuid"],
170
+ "factor_name": "reversal5",
171
+ "labels": labels,
172
+ "quantiles": 2,
173
+ },
174
+ ),
175
+ )
176
+ backtest_request = {
177
+ "source": STRATEGY,
178
+ "manifest_path": str(manifest),
179
+ "factor_run_uuid": factor["uuid"],
180
+ "config": {
181
+ "strategy_id": "unified_snapshot",
182
+ "instrument_ids": ids,
183
+ "frequency": "1d",
184
+ "start": "2026-09-28T09:30:00+08:00",
185
+ "end": "2026-09-30T15:00:00+08:00",
186
+ "initial_cash": "1000000",
187
+ "strategy_adjustment_type": "none",
188
+ },
189
+ }
190
+ backtest = command(
191
+ "backtest",
192
+ "run",
193
+ "--database",
194
+ database,
195
+ "--config",
196
+ config("backtest-request", backtest_request),
197
+ )
198
+ runs = {"factor": factor, "research": research, "backtest": backtest}
199
+ for kind, record in runs.items():
200
+ assert record["provenance"]["market_revision"] == market.revision
201
+ assert record["provenance"]["manifest_digest"] == factor["provenance"]["manifest_digest"]
202
+ assert command(kind, "get", "--run", record["uuid"], "--database", database) == record
203
+ result = BacktestResult.model_validate(backtest["result"])
204
+ top = [key for key, score in sorted(selection, key=lambda row: (-row[1], row[0]))[:3]]
205
+ assert len(result.fills) == 6
206
+ assert sorted(
207
+ fill.instrument_id for fill in result.fills if fill.side.value == "buy"
208
+ ) == sorted(top)
209
+ cash = Decimal(1000000) + sum(
210
+ (
211
+ (1 if fill.side.value == "sell" else -1) * fill.price * fill.quantity - fill.fees.total
212
+ for fill in result.fills
213
+ ),
214
+ Decimal(0),
215
+ )
216
+ assert result.final_portfolio_snapshot.cash.total_cash == cash
217
+ # 新进程再次回放:业务结果稳定,运行 UUID 可以变化。
218
+ repeat = command(
219
+ "backtest", "run", "--database", database, "--config", str(output / "backtest-request.json")
220
+ )
221
+ repeated = BacktestResult.model_validate(repeat["result"])
222
+ assert repeated.final_portfolio_snapshot.cash.total_cash == cash
223
+ assert [(f.instrument_id, f.side, f.quantity, f.price, f.fees.total) for f in result.fills] == [
224
+ (f.instrument_id, f.side, f.quantity, f.price, f.fees.total) for f in repeated.fills
225
+ ]
226
+ report = {
227
+ "status": "passed",
228
+ "manifest": str(manifest),
229
+ "market_revision": market.revision,
230
+ "manifest_digest": factor["provenance"]["manifest_digest"],
231
+ "runs": {k: v["uuid"] for k, v in runs.items()},
232
+ "repeat_backtest": repeat["uuid"],
233
+ "instruments": ids,
234
+ "selected": top,
235
+ "factor_cells": len(cells),
236
+ "maximum_error": str(error),
237
+ "fills": len(result.fills),
238
+ "final_cash": str(cash),
239
+ "limitations": [
240
+ "固定五股工程样本,非全市场验收",
241
+ "原始价格标签仅用于流程检查,未评价投资有效性",
242
+ "当前修订快照不代表完整历史知识时间 PIT",
243
+ ],
244
+ }
245
+ config("validation-report", report)
246
+ (output / "report.md").write_text(
247
+ "# 同一快照端到端验证\n\n```json\n"
248
+ + json.dumps(report, ensure_ascii=False, indent=2)
249
+ + "\n```\n",
250
+ encoding="utf-8",
251
+ )
252
+ return report
253
+
254
+
255
+ if __name__ == "__main__":
256
+ parser = argparse.ArgumentParser()
257
+ parser.add_argument("--manifest", type=Path, required=True)
258
+ parser.add_argument("--output", type=Path, required=True)
259
+ args = parser.parse_args()
260
+ print(json.dumps(validate(args.manifest.resolve(), args.output.resolve()), ensure_ascii=False))
quant_core/__init__.py ADDED
File without changes
quant_core/app/SPEC.md ADDED
@@ -0,0 +1,18 @@
1
+ # 计算核心边界
2
+
3
+ 本目录承载数据契约、因子、研究、策略、交易运行、订单、组合与回测计算。通用工程规则见 [SPEC.md](../../../../../SPEC.md)。
4
+
5
+ ```text
6
+ 调用方提供输入和协议实现
7
+ |
8
+ v
9
+ schema / interface -> service / computation -> 结构化结果
10
+ ```
11
+
12
+ - 核心不访问 HTTP、PostgreSQL、DuckDB、Redis,不读取应用配置,不导入应用或 SDK。
13
+ - Service 依赖消费方拥有的最小 Protocol;具体供数及持久化在应用中组装。
14
+ - CLI 负责参数、批量输入准备和本地归档;后端负责身份认证及发布定义管理。
15
+ - 纯计算使用函数;有运行状态或明确生命周期的对象使用类,不为目录完整创建空层。
16
+ - Strategy 保持环境无关。Backtest 单向依赖 Trading Runtime;Runtime 不依赖 Backtest。
17
+ - Order Execution 产生 Fill,Portfolio 应用 Fill;订单被接受不会改变资金持仓。
18
+ - 不同运行共享核算规则,不共享可变账户状态。
File without changes
@@ -0,0 +1,65 @@
1
+ # Backtest SPEC
2
+
3
+ ## 1. Domain Purpose
4
+
5
+ `backtest` 负责定义、组装和运行一次历史策略回测,并产出可复现的结果、指标和决策轨迹。它使用 Trading Runtime 的通用事件循环,不拥有另一套 Runtime。
6
+
7
+ ## 2. Responsibilities
8
+
9
+ - 管理 BacktestRun、BacktestConfig 和运行生命周期。
10
+ - 组装 HistoricalClock、Historical Market Replay、Simulated Order Execution 与 Simulated Portfolio。
11
+ - 回测启动前验证所需 Market 数据。
12
+ - 调用 Trading Runtime 执行历史事件循环。
13
+ - 生成 BacktestResult、交易记录、资金曲线、绩效指标和 trace。
14
+
15
+ ## 3. Non-Responsibilities
16
+
17
+ - 不定义或复制 Strategy Logic。
18
+ - 不实现通用时间片事件循环。
19
+ - 不拥有 Canonical Order、Fill 或 Portfolio 规则。
20
+ - 不修改 Paper 或 Live Portfolio。
21
+ - 不生成研究结论、机会状态或通知。
22
+
23
+ ## 4. Main Concepts
24
+
25
+ ```text
26
+ BacktestRun
27
+ ├── BacktestConfig
28
+ ├── HistoricalClock
29
+ ├── HistoricalReplay
30
+ ├── Simulated Execution Configuration
31
+ ├── Simulated Portfolio Initialization
32
+ └── BacktestResult
33
+ ├── Trades
34
+ ├── Equity Curve
35
+ ├── Metrics
36
+ └── Decision Trace
37
+ ```
38
+
39
+ ## 5. Dependency Direction
40
+
41
+ ```text
42
+ Backtest
43
+ ├──> Trading Runtime
44
+ ├──> Market
45
+ ├──> Strategy
46
+ ├──> Order Execution
47
+ └──> Portfolio
48
+
49
+ Trading Runtime -X-> Backtest
50
+ ```
51
+
52
+ Backtest 负责选择并组装历史环境实现;Trading Runtime 只运行被注入的契约,不得通过 `if mode == BACKTEST` 依赖本域。
53
+
54
+ ## 6. Important Business Rules
55
+
56
+ - PowerQuant 自有事件驱动回测是唯一权威回测路径,第三方项目只作为设计参考。
57
+ - 数据缺失或无效时拒绝启动,运行中禁止隐式同步或修复 Market 数据。
58
+ - Replay 使用精确相等 `event_time` 的 `MarketTimeSlice`。
59
+ - 新订单不得使用策略作出决策之前的市场事实成交。
60
+ - 相同输入、配置和策略版本必须产生相同业务结果。
61
+ - 回测状态与 Paper、Live 状态严格隔离。
62
+
63
+ ## 7. Application Entry
64
+
65
+ `execute_backtest` 执行共享回测用例,接收已组装的行情组件与计算依赖。CLI 负责本地快照准备和 DuckDB 归档;核心不创建数据库会话,不访问远端服务。
@@ -0,0 +1 @@
1
+ """Backtest Domain - 历史回测运行与结果。"""
@@ -0,0 +1,20 @@
1
+ """历史运行的显式输入组件,无文件加载副作用。"""
2
+
3
+ from dataclasses import dataclass
4
+ from pathlib import Path
5
+
6
+ from quant_core.app.data.service.prepared_data_verification_service import PreparedDataVerification
7
+ from quant_core.app.data.service.prepared_market_service import PreparedMarketData
8
+ from quant_core.app.trading_runtime.replay.exchange_state_resolver import ExchangeStateResolver
9
+ from quant_core.app.trading_runtime.replay.market_runtime_service import ReplayMarketEventSource
10
+ from quant_core.app.trading_runtime.replay.market_state_resolver import MarketStateResolver
11
+
12
+
13
+ @dataclass(frozen=True)
14
+ class BacktestMarketComponents:
15
+ query: PreparedMarketData
16
+ verification: PreparedDataVerification
17
+ slice_source: ReplayMarketEventSource
18
+ market_state_resolver: MarketStateResolver
19
+ exchange_state_resolver: ExchangeStateResolver
20
+ manifest_path: Path