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,171 @@
1
+ """可信本机研究代码的唯一运行入口。"""
2
+
3
+ import hashlib
4
+ import inspect
5
+ from dataclasses import dataclass
6
+ from datetime import date, datetime
7
+ from typing import Any
8
+ from uuid import uuid4
9
+
10
+ from quant_core.app.data.interface.market_data_query import IMarketDataQuery
11
+ from quant_core.app.data.interface.market_revision_query import IMarketRevisionQuery
12
+ from quant_core.app.factor.constants import FactorFreshness
13
+ from quant_core.app.factor.interface.public.factor_query import IFactorQuery
14
+ from quant_core.app.factor.schema.factor_contract import FactorIdentity, FactorSnapshot, FactorValue
15
+ from quant_core.app.research.interface.research_dataset import (
16
+ IResearchDatasetBuilder,
17
+ IResearchDatasetFactory,
18
+ )
19
+ from quant_core.app.research.interface.research_evaluation import IResearchEvaluation
20
+ from quant_core.app.research.schema.research_schema import (
21
+ ResearchEvidence,
22
+ ResearchRunRecord,
23
+ ResearchRunRequest,
24
+ ResearchWorkspace,
25
+ )
26
+
27
+
28
+ @dataclass(frozen=True, slots=True)
29
+ class ResearchContext:
30
+ """研究代码只通过公开查询契约读取历史事实。"""
31
+
32
+ market: IMarketDataQuery
33
+ factors: IFactorQuery | None
34
+ start: date
35
+ end: date
36
+ datasets: IResearchDatasetBuilder | None = None
37
+ evaluation: IResearchEvaluation | None = None
38
+
39
+
40
+ class _TrackedFactorQuery:
41
+ """仅在研究代码实际读取因子时将其版本记入运行证据。"""
42
+
43
+ def __init__(self, query: IFactorQuery) -> None:
44
+ self._query = query
45
+ self.used = False
46
+ self.revisions: set[str] = set()
47
+
48
+ def _track(self, values: tuple[FactorValue, ...], requested_revision: str | None) -> None:
49
+ if requested_revision is not None:
50
+ self.revisions.add(requested_revision)
51
+ self.revisions.update(value.provenance.dataset_revision or "0-legacy" for value in values)
52
+
53
+ async def get_snapshot(
54
+ self,
55
+ identities: list[FactorIdentity],
56
+ instrument_ids: list[str],
57
+ decision_time: datetime,
58
+ *,
59
+ target_as_of: date | None = None,
60
+ freshness: FactorFreshness = FactorFreshness.EXACT,
61
+ dataset_revision: str | None = None,
62
+ ) -> FactorSnapshot:
63
+ self.used = True
64
+ snapshot = await self._query.get_snapshot(
65
+ identities,
66
+ instrument_ids,
67
+ decision_time,
68
+ target_as_of=target_as_of,
69
+ freshness=freshness,
70
+ dataset_revision=dataset_revision,
71
+ )
72
+ self._track(snapshot.values, dataset_revision)
73
+ return snapshot
74
+
75
+ async def get_series(
76
+ self,
77
+ identity: FactorIdentity,
78
+ instrument_id: str,
79
+ start: datetime,
80
+ end: datetime,
81
+ decision_time: datetime,
82
+ *,
83
+ dataset_revision: str | None = None,
84
+ ) -> tuple[FactorValue, ...]:
85
+ self.used = True
86
+ values = await self._query.get_series(
87
+ identity,
88
+ instrument_id,
89
+ start,
90
+ end,
91
+ decision_time,
92
+ dataset_revision=dataset_revision,
93
+ )
94
+ self._track(values, dataset_revision)
95
+ return values
96
+
97
+
98
+ async def execute_research(
99
+ request: ResearchRunRequest,
100
+ market: IMarketDataQuery,
101
+ revisions: IMarketRevisionQuery,
102
+ *,
103
+ workspace: ResearchWorkspace | None = None,
104
+ factors_query: IFactorQuery | None = None,
105
+ dataset_factory: IResearchDatasetFactory | None = None,
106
+ evaluation: IResearchEvaluation | None = None,
107
+ ) -> ResearchRunRecord:
108
+ """执行显式研究定义并返回记录,不读取或保存管理数据库。"""
109
+ if request.research_id and workspace is None:
110
+ raise ValueError("已注册研究需先显式加载定义")
111
+ source = workspace.python_source if workspace else request.python_source
112
+ assert source is not None
113
+ parameters = {**(workspace.default_parameters if workspace else {}), **request.parameters}
114
+ config = {
115
+ "start": request.start.isoformat(),
116
+ "end": request.end.isoformat(),
117
+ "parameters": parameters,
118
+ }
119
+ data_revisions: dict[str, str] = {}
120
+ result: ResearchEvidence | None = None
121
+ error: str | None = None
122
+ factors = _TrackedFactorQuery(factors_query) if factors_query is not None else None
123
+ datasets = dataset_factory(factors) if dataset_factory and factors else None
124
+ try:
125
+ spec = ResearchWorkspace(
126
+ research_id=workspace.research_id if workspace else "temporary",
127
+ name=workspace.name if workspace else "临时研究",
128
+ python_source=source,
129
+ )
130
+ spec.validate_source()
131
+ data_revisions = await revisions.snapshot(request.start, request.end)
132
+ namespace: dict[str, Any] = {"__name__": "research_user_code"}
133
+ exec(compile(source, "<research>", "exec"), namespace)
134
+ callback = namespace["run"]
135
+ value = callback(
136
+ ResearchContext(
137
+ market,
138
+ factors,
139
+ request.start,
140
+ request.end,
141
+ datasets,
142
+ evaluation,
143
+ ),
144
+ parameters,
145
+ )
146
+ if inspect.isawaitable(value):
147
+ value = await value
148
+ result = ResearchEvidence.model_validate(value)
149
+ if result.sample_start != request.start or result.sample_end != request.end:
150
+ raise ValueError("研究输出的样本区间与运行请求不一致")
151
+ result.model_dump(mode="json")
152
+ except Exception as exc:
153
+ result = None
154
+ error = f"{type(exc).__name__}: {exc}"
155
+ if factors is not None and factors.used:
156
+ data_revisions["factor"] = ",".join(sorted(factors.revisions)) or "no_values"
157
+ if datasets is not None:
158
+ data_revisions.update(datasets.revisions)
159
+ record = ResearchRunRecord(
160
+ uuid=str(uuid4()),
161
+ status="completed" if result is not None else "failed",
162
+ research_id=request.research_id,
163
+ source_kind="workspace" if workspace else "temporary",
164
+ source_snapshot=source,
165
+ source_digest=hashlib.sha256(source.encode("utf-8")).hexdigest(),
166
+ config_snapshot=config,
167
+ data_revisions=data_revisions,
168
+ result=result,
169
+ error=error,
170
+ )
171
+ return record
@@ -0,0 +1,215 @@
1
+ # Strategy Domain SPEC
2
+
3
+ > Strategy 域的架构 SSOT:领域模型、边界、契约与演进范围以本文件为准。
4
+ > 字段级语义见各 schema 模块 docstring;决策背景见根 [DECISIONS.md](../../DECISIONS.md)。
5
+ > **P1 已 Freeze(2026-09-03)**:本域达到 Backtest 准入状态;后续只做增量扩展,不阻塞 Backtest。
6
+
7
+ ## 1. 领域定位与公式
8
+
9
+ Strategy 域建立 **统一策略生产体系**:Human / Agent / Python(未来 MyLanguage / UI DSL)
10
+ 都生产同一个 Canonical Definition,Runtime 稳定执行它们。
11
+
12
+ ```text
13
+ Strategy Domain =
14
+ Authoring(多入口 → 同一 Canonical Definition)
15
+ + Factor Definition / Runtime / Result
16
+ + Screener
17
+ + Strategy Definition / Runtime
18
+ + Signal
19
+ + Validation(Definition / Runtime 两层)
20
+ + Evaluation(契约;绩效计算归 Backtest)
21
+ + Human Review(Agent Artifact 进 Paper/Live 的硬门槛)
22
+ ```
23
+
24
+ 核心定位:**Agent First ≠ Agent Direct Execution**——Agent 负责 Generate / Analyze /
25
+ Iterate,但任何 Quant Artifact 都不得绕过 Canonical Contract → Validation → Backtest →
26
+ Human Approval 进入 Live。
27
+
28
+ ## 2. 领域模型:Definition / Runtime / Result
29
+
30
+ Factor 与 Strategy 都遵循同一模式:
31
+
32
+ ```text
33
+ Definition(是什么、怎么算) Runtime(执行) Result
34
+ FactorDefinition → FactorService(FactorRuntime) → FactorResult
35
+ StrategyDefinition → StrategyRuntimeService → Signal
36
+ ```
37
+
38
+ **Screener 不是 Strategy 的必经阶段**——它是域内候选集生成能力,只有横截面类策略经过它:
39
+
40
+ ```text
41
+ Cross-sectional:Market → Factor → Screener → Strategy → Signal
42
+ Single Instrument Timing:BarEvent → Strategy → Signal(完全不经过 Screener)
43
+ ```
44
+
45
+ 禁止把 Domain 硬编码成 Factor → Screener → Strategy → Signal 的单一路径。
46
+
47
+ ## 3. P1 真实执行模型(重要声明)
48
+
49
+ ```text
50
+ StrategyDefinition(Canonical Artifact / Metadata / 声明式 Contract)
51
+ +
52
+ Python Native Strategy Implementation(StrategyProtocol 实现)
53
+ ↓
54
+ StrategyRuntimeService
55
+ ↓
56
+ Signal
57
+ ```
58
+
59
+ > **P1 的 `StrategyDefinition` 是 Canonical Artifact、Metadata 与声明式 Contract;
60
+ > 实际执行由 Python Native Strategy implementation 承载。P1 不宣称已经实现完整的
61
+ > Definition-driven execution。**
62
+
63
+ Definition 回答「这个策略是什么」,Implementation 回答「P1 当前如何执行它」;
64
+ 二者通过 `StrategyDefinition.implementation_ref`(dotted path)显式关联,不靠文件名猜测。
65
+ Definition → Compiler/Interpreter → Executable Strategy 的演进属 Agent DSL / MyLanguage /
66
+ UI DSL 阶段(P2+),届时新增编译入口而非第二套 Runtime。
67
+
68
+ ## 4. 硬规则
69
+
70
+ 1. **Definition / Runtime / Result 概念分离**:Definition 是 Artifact(非运行实例、非 Signal)。
71
+ 2. **所有 Authoring 入口收敛到 Canonical Definition**:不建立 Agent 专属策略模型、
72
+ 不为 DSL 建第二套 Runtime。MyLanguage 是 Authoring Adapter(Parser → AST → Definition),
73
+ 禁止直接驱动 Backtest Engine。
74
+ 3. **Strategy 只产生 Signal,不产生 Broker Order**:Strategy 回答「应该做什么」,
75
+ Execution / Backtest 回答「怎么成交」。资产变化只发生在
76
+ `Signal → Backtest/Execution → Order → Fill → Portfolio Update` 链路上。
77
+ 4. **Strategy 环境无关**:市场时间只认 `event_time`(业务代码禁止
78
+ `datetime.now()`/`datetime.utcnow()`/`time.time()`);
79
+ 数据只经 `StrategyContext` 注入的 Port(禁止直连 AKShare / Repository / DB / Broker);
80
+ 同一 Definition 在 Backtest / Paper / Live 下业务逻辑不变(环境由 `StrategyRunMode` 表达,
81
+ 不混入 StrategyStatus)。
82
+ 5. **非结构化输入必须先转 Canonical FactorResult**:禁止 Strategy 内部直接 prompt LLM、
83
+ 自由文本决定买卖;FactorResult 的 NULL = 不可计算(禁止 0 填充,与 Market NULL 语义一致)。
84
+ Strategy 不得重复计算 FactorService 已有的因子(含 MA 等参考线)。
85
+ 6. **状态与环境分离**:`StrategyStatus` / `FactorStatus` 描述 Artifact 生命周期;
86
+ `StrategyRunMode` 描述运行环境。
87
+ 7. **Agent Artifact 可追溯**:Definition 携带 authoring_source / created_by /
88
+ parent_version / implementation_ref。
89
+ 8. **Human Approval 是 Paper/Live 的前置条件**(领域规则,不是 UI 细节)。
90
+ 9. **Screener 契约唯一**:`ScreenerService` 的运行输入就是 Canonical `ScreenerSpec`
91
+ (来自 `StrategyDefinition.screening_rules`),不存在 Definition 一套声明模型、
92
+ Runtime 另一套临时 dict 模型。
93
+
94
+ ## 5. StrategyContext 状态职责表
95
+
96
+ | 载体 | 归属 | 允许 | 禁止 |
97
+ |---|---|---|---|
98
+ | `StrategyContext.state` | Strategy 自己 | last_signal_time / consecutive_count / trailing_state / 自定义计数器 | **cash / position quantity / available quantity / cost basis / equity** |
99
+ | `StrategyContext.portfolio` | Portfolio / Backtest(只读注入) | 经 `PortfolioReaderProtocol` 读取持仓与资金 | 任何修改(无写方法) |
100
+
101
+ ```python
102
+ class PortfolioReaderProtocol(Protocol):
103
+ async def get_position(self, instrument_id: str) -> PositionView | None: ...
104
+ async def get_cash(self) -> CashView: ...
105
+ ```
106
+
107
+ 只读、最小化。禁止提前设计完整 Portfolio Domain / 多账户 / Broker account / 对账。
108
+
109
+ ## 6. 目录与职责
110
+
111
+ ```text
112
+ app/strategy/
113
+ ├── constants.py # 枚举(Type/Style/Status/RunMode/Signal…)
114
+ ├── exceptions.py # 域内异常
115
+ ├── schema/
116
+ │ ├── factor_schema.py # FactorDefinition / FactorResult
117
+ │ ├── strategy_schema.py # StrategyDefinition / RuleCondition / ScreenerSpec
118
+ │ ├── screener_schema.py # ScreenerRequest(spec: ScreenerSpec)/ ScreenerResult
119
+ │ ├── signal_schema.py # Signal
120
+ │ └── portfolio_schema.py # PositionView / CashView(只读视图)
121
+ ├── service/
122
+ │ ├── expression.py # 受限公式解释器(ref/ma + 算术,ast 白名单)
123
+ │ ├── factor_service.py # Factor Runtime:注册 Definition → 计算 FactorResult
124
+ │ ├── screener_service.py # ScreenerSpec 驱动:Universe → Filter → Rank → Top N
125
+ │ ├── strategy_protocol.py # StrategyProtocol / StrategyContext / PortfolioReader
126
+ │ └── strategy_runtime_service.py
127
+ └── strategies/
128
+ └── momentum_volume_breakout.py # P1 Demo(Python Native,公式移植自 FactorHub)
129
+ ```
130
+
131
+ **StrategyRuntimeService 职责**(收口):
132
+
133
+ ```text
134
+ ├── 接收 MarketEvent 或完整 MarketTimeSlice(不做 Bar-centric 假设)
135
+ ├── 构造 StrategyContext
136
+ ├── 调度 required Factor(经 StrategyFactorProviderProtocol)
137
+ ├── 调用 Strategy implementation(on_bar / on_session)
138
+ ├── 收集 Signal
139
+ └── 维护 strategy-local runtime state
140
+ ```
141
+
142
+ Backtest 的稳定调度边界是单时间片入口:
143
+
144
+ ```text
145
+ MarketTimeSlice@T
146
+ |
147
+ v
148
+ run_slice(time_slice) → list[Signal]
149
+ |
150
+ v
151
+ Backtest 执行 Fill / 更新 Portfolio
152
+ |
153
+ v
154
+ MarketTimeSlice@T+1 读取更新后的只读 PortfolioView
155
+ ```
156
+
157
+ `run_slice()` 在该片回调全部完成后立即返回 Signal Batch,不产生 Order/Fill、
158
+ 不修改 Portfolio。`run_slices()` 只是复用 `run_slice()` 的整段回放便利包装;需要在
159
+ 片间形成 Portfolio 反馈的 Backtest 必须逐片调用 `run_slice()`。
160
+
161
+ 它不是「加载窗口 Bar 的服务」——拉窗口只是 Context 装配的实现细节。
162
+ 未知事件按契约忽略;默认消费**已通过运行环境前置验证的数据**(Backtest 启动前经
163
+ `IMarketDataVerification` 检查),不在 `on_bar()` 内重复 `verify_bars()`。
164
+
165
+ ## 7. 对 Market 的依赖边界
166
+
167
+ 只允许依赖 Market 跨域稳定 Port(见 [market/SPEC.md](../market/SPEC.md) §4):
168
+
169
+ - `IMarketDataQuery`(Pull 行情窗口;经 StrategyContext.market)
170
+ - `IMarketEventSource`(事件流输入)
171
+ - `MarketTimeSlice`(Backtest 注入的完整同市场时刻信息集;时间片 Source 由 Backtest 编排)
172
+ - `IMarketDataVerification`(由 Backtest 前置调用,Strategy 不逐 bar 调用)
173
+
174
+ 禁止依赖:Market Repository / Adapter / AKShare / MarketSyncService / Database / Broker。
175
+
176
+ ## 8. 与 Backtest / Research 的边界
177
+
178
+ - Backtest 消费 Strategy Runtime 的 Signal 流,负责 Order / Fill / Commission /
179
+ Slippage / SimulatedPortfolio / Metrics;禁止复制 Strategy 逻辑。
180
+ Signal 契约禁止包含 broker_order_id / fill_price / commission / slippage / account_id。
181
+ Backtest 准入链:`BacktestRequest → ensure_backtest_data_ready → MarketTimeSlice Replay →
182
+ StrategyRuntime.run_slice → Signal Batch → Order → Simulated Fill → SimulatedPortfolio → Metrics`;
183
+ 每片完成 Portfolio 更新后才能处理下一片。
184
+ - Research 可消费 Definition / FactorResult / Signal / 回测结果做解释;
185
+ 自然语言结论不得污染 FactorResult / Signal 契约。
186
+
187
+ ## 9. Validation 两层
188
+
189
+ | 层 | 检查内容 | 现状 |
190
+ |---|---|---|
191
+ | Definition Validation(静态) | identity/version 格式(Field pattern)、styles 合法、expression/implementation_ref 互斥、规则组非空且 left 非空、required_factors 引用合法(传入已注册因子集校验) | 已实现(`validate_definition()`) |
192
+ | Runtime/Data Validation | 因子窗口是否足够(→ FactorResult NULL);Market Data 健康检查归 Backtest 前置 Guard | 已实现 / 前置 Guard 已具备 |
193
+
194
+ 不引入通用 Rule Engine。
195
+
196
+ ## 10. 实现范围
197
+
198
+ - **P1(已实现,FROZEN)**:六契约(StrategyDefinition / FactorDefinition / FactorResult /
199
+ Signal / StrategyContext / StrategyProtocol)、ScreenerSpec 统一驱动 ScreenerService、
200
+ PortfolioReader 最小契约、受限 expression 解释器、FactorService(momentum_20d /
201
+ volume_ratio_5d / ma_close_10,公式移植自 FactorHub)、StrategyRuntimeService(事件调度 +
202
+ portfolio 注入 + strategy-local state + 单时间片 `run_slice()` 反馈边界)、MomentumVolumeBreakout Demo、
203
+ Deterministic Replay E2E(两次运行业务结果一致)。
204
+ - **P2+(不阻塞 Backtest)**:Agent Authoring 入口、Strategy/Factor 持久化与生命周期 API、
205
+ Backtest 调用入口与 Evaluation Contract、Human Review lifecycle、MyLanguage Adapter、
206
+ 非结构化 Factor(TEXT + confidence)、on_quote/on_news 回调、Paper/Live。
207
+ - **明确不做**:通用 Strategy IR、Visual Builder、AutoML、遗传规划、Multi-Agent 策略委员会、
208
+ 复杂 Workflow Engine、通用 Event Bus。
209
+
210
+ ## 11. 边界检查
211
+
212
+ ```bash
213
+ # app/strategy/ 业务代码中 grep akshare|ts_code|sqlalchemy|datetime.now|time.time
214
+ # 必须为 0 命中(文档性注释提及"禁止…"除外)
215
+ ```
@@ -0,0 +1 @@
1
+ """Strategy Domain:Authoring + Factor + Screener + Strategy + Signal(见 SPEC.md)"""
@@ -0,0 +1,87 @@
1
+ """
2
+ Strategy 域常量与枚举
3
+
4
+ 概念分层(SSOT 见 SPEC):
5
+ - Definition / Runtime / Result 三层分离:Factor 与 Strategy 都遵循
6
+ Definition(是什么、怎么算)→ Runtime(执行)→ Result(Canonical
7
+ FactorValue / Signal,R2-1 起因子值唯一形态是 quant_core.app.factor.FactorValue)。
8
+ - 状态(StrategyStatus / FactorStatus)描述 Artifact 生命周期;
9
+ 运行环境(StrategyRunMode)单独定义——同一 Definition 在不同环境下业务逻辑不变。
10
+ """
11
+
12
+ from enum import Enum
13
+
14
+
15
+ class StrategyType(str, Enum):
16
+ """策略决策范式(不是风格:Momentum/Value 等属 StrategyStyle)"""
17
+
18
+ SELECTION = "selection" # 选股型
19
+ TIMING = "timing" # 择时型
20
+ CROSS_SECTIONAL = "cross_sectional" # 横截面因子排名 / 多因子
21
+ EVENT_DRIVEN = "event_driven" # 新闻 / 公告 / 市场事件驱动
22
+ PORTFOLIO = "portfolio" # 组合权重 / 再平衡
23
+
24
+
25
+ class StrategyStyle(str, Enum):
26
+ """策略风格 Tag(一个策略可含多个 Style)"""
27
+
28
+ MOMENTUM = "momentum"
29
+ TREND = "trend"
30
+ MEAN_REVERSION = "mean_reversion"
31
+ BREAKOUT = "breakout"
32
+ VALUE = "value"
33
+ QUALITY = "quality"
34
+ GROWTH = "growth"
35
+ VOLATILITY = "volatility"
36
+ EVENT = "event"
37
+ MULTI_FACTOR = "multi_factor"
38
+
39
+
40
+ class StrategyStatus(str, Enum):
41
+ """Strategy Artifact 生命周期(运行环境见 StrategyRunMode,不得混入)"""
42
+
43
+ DRAFT = "draft"
44
+ VALIDATING = "validating"
45
+ BACKTESTED = "backtested"
46
+ REVIEW_REQUIRED = "review_required"
47
+ APPROVED = "approved"
48
+ REJECTED = "rejected"
49
+ PAPER = "paper"
50
+ LIVE = "live"
51
+ ARCHIVED = "archived"
52
+
53
+
54
+ class StrategyRunMode(str, Enum):
55
+ """Runtime 运行环境:Definition 不变,只换运行环境与 EventSource"""
56
+
57
+ RESEARCH = "research"
58
+ BACKTEST = "backtest"
59
+ PAPER = "paper"
60
+ LIVE = "live"
61
+
62
+
63
+ class AuthoringSource(str, Enum):
64
+ """Artifact 生产入口(Agent First:Agent 与 Human 走同一 Canonical Definition)"""
65
+
66
+ HUMAN = "human"
67
+ AGENT = "agent"
68
+ PYTHON = "python"
69
+ MYLANGUAGE = "mylanguage" # Authoring Language Adapter,reserved
70
+ DSL = "dsl"
71
+
72
+
73
+ class SignalType(str, Enum):
74
+ """信号类型(Strategy 产生的 Canonical Decision Result)"""
75
+
76
+ ENTER = "enter"
77
+ EXIT = "exit"
78
+ HOLD = "hold"
79
+ WATCH = "watch"
80
+
81
+
82
+ class SignalSide(str, Enum):
83
+ """信号方向(A股 V1:LONG / NONE;SHORT reserved)"""
84
+
85
+ LONG = "long"
86
+ NONE = "none"
87
+ SHORT = "short" # reserved
@@ -0,0 +1,32 @@
1
+ """Strategy 域业务异常(Common 拥有机制,Domain 拥有语义)"""
2
+
3
+ from quant_core.common.exceptions.base import AppException, ConflictException, ValidationException
4
+
5
+
6
+ class StrategyPublicationConflict(ConflictException):
7
+ error_code = "STRATEGY_PUBLICATION_CONFLICT"
8
+
9
+
10
+ class PublishedStrategyNotFound(AppException):
11
+ error_code = "PUBLISHED_STRATEGY_NOT_FOUND"
12
+ http_status = 404
13
+
14
+
15
+ class StrategyValidationException(ValidationException):
16
+ """Strategy/Factor Definition 校验失败(Definition Validation 层)"""
17
+
18
+ error_code = "STRATEGY_VALIDATION_ERROR"
19
+ http_status = 422
20
+
21
+ def __init__(self, message: str = "策略定义校验失败"):
22
+ super().__init__(message)
23
+
24
+
25
+ class StrategyNotRegisteredException(AppException):
26
+ """指定策略未注册"""
27
+
28
+ error_code = "STRATEGY_NOT_REGISTERED"
29
+ http_status = 404
30
+
31
+ def __init__(self, strategy_id: str):
32
+ super().__init__(f"strategy 未注册: {strategy_id}")
File without changes
File without changes
@@ -0,0 +1,19 @@
1
+ """策略组合构建公共接口;不产生订单或修改资产。"""
2
+
3
+ from decimal import Decimal
4
+ from typing import Protocol
5
+
6
+ from quant_core.app.factor.schema.selection_types import SelectionResult
7
+ from quant_core.app.strategy.schema.portfolio_target import PortfolioConstraints, TargetPortfolio
8
+
9
+
10
+ class IPortfolioConstruction(Protocol):
11
+ def build(
12
+ self,
13
+ selection: SelectionResult,
14
+ constraints: PortfolioConstraints,
15
+ *,
16
+ current_weights: dict[str, Decimal] | None = None,
17
+ industries: dict[str, str] | None = None,
18
+ holding_sessions: dict[str, int] | None = None,
19
+ ) -> TargetPortfolio: ...
@@ -0,0 +1,30 @@
1
+ """Public Python strategy callback contract."""
2
+
3
+ from typing import Any, Protocol, runtime_checkable
4
+
5
+ from quant_core.app.data.schema.event_schema import BarEvent, TradingSessionEvent
6
+ from quant_core.app.strategy.schema.signal_schema import Signal
7
+ from quant_core.app.strategy.strategy_context import StrategyContext
8
+
9
+
10
+ @runtime_checkable
11
+ class IStrategy(Protocol):
12
+ """策略实现契约(Python Native 策略的第一入口)
13
+
14
+ 所有入口(Human / Agent / MyLanguage)最终编译为同一个 Canonical
15
+ StrategyDefinition;Python Native 策略额外实现本 Protocol 以被 Runtime 执行。
16
+ """
17
+
18
+ def definition(self) -> Any:
19
+ """返回策略的 Canonical StrategyDefinition"""
20
+ ...
21
+
22
+ async def on_bar(self, event: BarEvent, context: "StrategyContext") -> list[Signal]:
23
+ """Bar 事件驱动的决策入口"""
24
+ ...
25
+
26
+ async def on_session(
27
+ self, event: TradingSessionEvent, context: "StrategyContext"
28
+ ) -> list[Signal]:
29
+ """Session 事件驱动的决策入口"""
30
+ ...
@@ -0,0 +1,53 @@
1
+ """策略已绑定决策时点的行情读取面,不暴露完整 Market 生产能力。"""
2
+
3
+ from collections.abc import Sequence
4
+ from datetime import date, datetime
5
+ from typing import Protocol
6
+
7
+ from quant_core.app.data.schema.adjustment_schema import AdjustedBarSeries, BarViewRequest
8
+ from quant_core.app.data.schema.market_schema import (
9
+ BarResponse,
10
+ InstrumentResponse,
11
+ TradingCalendarDayResponse,
12
+ )
13
+
14
+
15
+ class IStrategyMarketQuery(Protocol):
16
+ async def get_bars(
17
+ self,
18
+ instrument_id: str,
19
+ frequency: str,
20
+ start: datetime | None = None,
21
+ end: datetime | None = None,
22
+ limit: int = 1000,
23
+ ) -> Sequence[BarResponse]: ...
24
+
25
+ async def get_adjusted_bars(
26
+ self,
27
+ instrument_id: str,
28
+ frequency: str,
29
+ view: BarViewRequest,
30
+ start: datetime | None = None,
31
+ end: datetime | None = None,
32
+ limit: int = 1000,
33
+ ) -> AdjustedBarSeries: ...
34
+
35
+ async def get_instrument(self, instrument_id: str) -> InstrumentResponse: ...
36
+
37
+ async def list_instruments_on(
38
+ self, on_date: date, limit: int = 10000
39
+ ) -> list[InstrumentResponse]: ...
40
+
41
+ async def search_instruments(
42
+ self,
43
+ keyword: str | None = None,
44
+ instrument_type: str | None = None,
45
+ exchange: str | None = None,
46
+ limit: int = 100,
47
+ ) -> list[InstrumentResponse]: ...
48
+
49
+ async def is_trading_day(self, exchange: str, trade_date: date) -> bool: ...
50
+
51
+ async def list_trading_days(
52
+ self, exchange: str, start: date, end: date
53
+ ) -> list[TradingCalendarDayResponse]: ...
@@ -0,0 +1,33 @@
1
+ """Async portfolio view consumed by strategies."""
2
+
3
+ from typing import Protocol, runtime_checkable
4
+
5
+ from quant_core.app.portfolio.ledger_schema import PositionLotSnapshot
6
+ from quant_core.app.portfolio.portfolio_schema import PortfolioSnapshot
7
+ from quant_core.app.strategy.schema.portfolio_schema import CashView, PositionView
8
+
9
+
10
+ @runtime_checkable
11
+ class IStrategyPortfolioReader(Protocol):
12
+ """持仓/资金只读能力(V1 由 Backtest 模拟持仓实现;可选依赖)
13
+
14
+ 最小读取边界:Strategy 可以读取持仓与资金,但不能拥有或修改——
15
+ 资产变化只发生在 Signal → Backtest/Execution → Order → Fill →
16
+ Portfolio Update 链路上。禁止在此扩展多账户/Broker account/对账。
17
+ """
18
+
19
+ async def get_position(self, instrument_id: str) -> PositionView | None:
20
+ """单个标的持仓视图(未持仓返回 None)"""
21
+ ...
22
+
23
+ async def get_cash(self) -> CashView:
24
+ """可用资金视图"""
25
+ ...
26
+
27
+ async def get_snapshot(self) -> PortfolioSnapshot:
28
+ """当前账户总值和持仓市值的公开只读快照。"""
29
+ ...
30
+
31
+ async def get_position_lots(self) -> tuple[PositionLotSnapshot, ...]:
32
+ """当前持仓的真实成交批次,包含取得时间和剩余数量。"""
33
+ ...
@@ -0,0 +1,19 @@
1
+ """Strategy consumes already-published PIT factor values; it never computes them."""
2
+
3
+ from datetime import datetime
4
+ from typing import Protocol
5
+
6
+ from quant_core.app.factor.schema.factor_contract import FactorValue
7
+
8
+
9
+ class IStrategyFactorReader(Protocol):
10
+ async def read_many(
11
+ self,
12
+ factor_names: tuple[str, ...],
13
+ instrument_ids: tuple[str, ...],
14
+ decision_time: datetime,
15
+ ) -> dict[str, dict[str, FactorValue]]: ...
16
+
17
+ async def read(
18
+ self, factor_name: str, instrument_id: str, decision_time: datetime
19
+ ) -> FactorValue | None: ...