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,14 @@
1
+ """Order view consumed by strategy context."""
2
+
3
+ from typing import Protocol, runtime_checkable
4
+
5
+ from quant_core.app.order_execution.domain.order_value import OrderSnapshot
6
+
7
+
8
+ @runtime_checkable
9
+ class IStrategyOrderReader(Protocol):
10
+ def get_order(self, order_id: str) -> OrderSnapshot | None: ...
11
+
12
+ def open_order_snapshots(
13
+ self, instrument_id: str | None = None
14
+ ) -> tuple[OrderSnapshot, ...]: ...
File without changes
@@ -0,0 +1,28 @@
1
+ """
2
+ Portfolio Schema - Strategy 可读的持仓/资金视图(只读快照)
3
+
4
+ 边界(硬规则):Strategy 可以读取持仓,但不能拥有或修改持仓。
5
+ cash/position 的真实状态归 Portfolio / Backtest;资产变化只发生在
6
+ Signal → Backtest/Execution → Order → Fill → Portfolio Update 链路上。
7
+ """
8
+
9
+ from decimal import Decimal
10
+
11
+ from pydantic import BaseModel, ConfigDict, Field
12
+
13
+
14
+ class PositionView(BaseModel):
15
+ """单个标的的持仓只读视图(不暴露 cost basis 之外的可变语义)"""
16
+
17
+ model_config = ConfigDict(validate_assignment=True)
18
+
19
+ instrument_id: str = Field(..., description="Canonical 标的 ID")
20
+ quantity: int = Field(..., description="持仓股数(0 = 未持仓)")
21
+
22
+
23
+ class CashView(BaseModel):
24
+ """资金只读视图"""
25
+
26
+ model_config = ConfigDict(validate_assignment=True)
27
+
28
+ cash: Decimal = Field(..., ge=0, description="可用资金(Decimal,与资金类字段精度规范一致)")
@@ -0,0 +1,70 @@
1
+ """选股分数到目标组合的约束与可解释输出。"""
2
+
3
+ from dataclasses import dataclass
4
+ from decimal import Decimal
5
+
6
+ from quant_core.app.factor.schema.selection_types import SelectionResult
7
+
8
+
9
+ @dataclass(frozen=True)
10
+ class PortfolioConstraints:
11
+ max_positions: int = 10
12
+ max_weight: Decimal = Decimal("1")
13
+ cash_buffer: Decimal = Decimal("0.02")
14
+ industry_caps: tuple[tuple[str, Decimal], ...] = ()
15
+ max_turnover: Decimal | None = None
16
+ retention_rank: int | None = None
17
+ max_replacements: int | None = None
18
+ min_holding_sessions: int = 0
19
+
20
+ def __post_init__(self) -> None:
21
+ if self.retention_rank is not None and self.retention_rank < self.max_positions:
22
+ raise ValueError("保留排名阈值不得小于目标持仓数")
23
+ if self.max_replacements is not None and self.max_replacements < 0:
24
+ raise ValueError("最大替换数量不得为负")
25
+ if self.min_holding_sessions < 0:
26
+ raise ValueError("最低持有交易日不得为负")
27
+ if (
28
+ self.max_positions < 1
29
+ or not self.max_weight.is_finite()
30
+ or not Decimal(0) < self.max_weight <= 1
31
+ ):
32
+ raise ValueError("持仓数和单股权重上限非法")
33
+ if not self.cash_buffer.is_finite() or not Decimal(0) <= self.cash_buffer < 1:
34
+ raise ValueError("现金缓冲必须在 [0,1)")
35
+ if len(dict(self.industry_caps)) != len(self.industry_caps):
36
+ raise ValueError("行业权重上限重复")
37
+ if any(not cap.is_finite() or not Decimal(0) <= cap <= 1 for _, cap in self.industry_caps):
38
+ raise ValueError("行业权重上限非法")
39
+ if self.max_turnover is not None and (
40
+ not self.max_turnover.is_finite() or not Decimal(0) <= self.max_turnover <= 1
41
+ ):
42
+ raise ValueError("换手上限必须在 [0,1]")
43
+
44
+
45
+ @dataclass(frozen=True)
46
+ class TargetPortfolio:
47
+ weights: tuple[tuple[str, Decimal], ...]
48
+ cash_weight: Decimal
49
+ reasons: tuple[str, ...] = ()
50
+
51
+ def __post_init__(self) -> None:
52
+ if len(dict(self.weights)) != len(self.weights):
53
+ raise ValueError("组合目标证券重复")
54
+ if any(not weight.is_finite() or weight < 0 for _, weight in self.weights):
55
+ raise ValueError("组合权重非法")
56
+ if (
57
+ not self.cash_weight.is_finite()
58
+ or self.cash_weight < 0
59
+ or sum((weight for _, weight in self.weights), Decimal(0)) + self.cash_weight != 1
60
+ ):
61
+ raise ValueError("持仓及现金权重必须合计为 1")
62
+
63
+
64
+ @dataclass(frozen=True)
65
+ class PortfolioDecision:
66
+ """评分贡献与目标组合的同次决策证据,随回测结果持久化,不驱动交易状态。"""
67
+
68
+ selection: SelectionResult
69
+ target: TargetPortfolio
70
+ constraints: PortfolioConstraints
@@ -0,0 +1,94 @@
1
+ """Immutable Python strategy publication and run input."""
2
+
3
+ import ast
4
+ import hashlib
5
+ import json
6
+ from typing import Any
7
+
8
+ from pydantic import BaseModel, ConfigDict, Field
9
+
10
+ from quant_core.app.factor.schema.factor_contract import FactorIdentity
11
+
12
+
13
+ class PythonStrategySpec(BaseModel):
14
+ model_config = ConfigDict(frozen=True, extra="forbid")
15
+
16
+ strategy_id: str = Field(pattern=r"^[a-z][a-z0-9_]*$", max_length=64)
17
+ version: str = Field(min_length=1, max_length=32)
18
+ name: str = Field(min_length=1, max_length=128)
19
+ description: str = ""
20
+ python_source: str = Field(min_length=1, max_length=65536)
21
+ factor_refs: tuple[FactorIdentity, ...] = ()
22
+ factor_aliases: dict[str, FactorIdentity] = Field(default_factory=dict)
23
+ parameters: dict[str, Any] = Field(default_factory=dict)
24
+ universe: tuple[str, ...] = ()
25
+ submitted_by: str | None = None
26
+
27
+ @property
28
+ def source_digest(self) -> str:
29
+ return hashlib.sha256(self.python_source.encode("utf-8")).hexdigest()
30
+
31
+ @property
32
+ def content_digest(self) -> str:
33
+ payload = self.model_dump(mode="json", exclude={"submitted_by"})
34
+ if not self.factor_aliases:
35
+ payload.pop("factor_aliases") # 保留既有不可变版本摘要
36
+ return hashlib.sha256(
37
+ json.dumps(payload, sort_keys=True, ensure_ascii=False, separators=(",", ":")).encode(
38
+ "utf-8"
39
+ )
40
+ ).hexdigest()
41
+
42
+ def resolved_factors(self) -> dict[str, FactorIdentity]:
43
+ """将编写别名绑定完整身份;同名多版本必须显式绑定。"""
44
+ refs = {item.logical_key: item for item in self.factor_refs}
45
+ if len(refs) != len(self.factor_refs):
46
+ raise ValueError("策略因子依赖重复")
47
+ bindings = dict(self.factor_aliases)
48
+ for alias, identity in bindings.items():
49
+ if not alias.isidentifier() or identity.logical_key not in refs:
50
+ raise ValueError("因子别名必须合法且引用已声明的完整身份")
51
+ bound = {item.logical_key for item in bindings.values()}
52
+ for identity in self.factor_refs:
53
+ if identity.logical_key in bound:
54
+ continue
55
+ if identity.name in bindings:
56
+ raise ValueError("同名因子依赖存在歧义,请为每个身份声明不同别名")
57
+ bindings[identity.name] = identity
58
+ return bindings
59
+
60
+ def validate_source(self) -> None:
61
+ self.resolved_factors()
62
+ module = ast.parse(self.python_source)
63
+ callbacks = {
64
+ node.name: node
65
+ for node in module.body
66
+ if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef))
67
+ }
68
+ if not callbacks.keys() & {"handle_bar", "on_bar", "on_session", "on_slice"}:
69
+ raise ValueError("策略源码至少定义 handle_bar、on_bar、on_session 或 on_slice 回调")
70
+ for name, expected in {
71
+ "init": 1,
72
+ "handle_bar": 2,
73
+ "on_bar": 2,
74
+ "on_slice": 2,
75
+ "on_session": 2,
76
+ "before_trading": 1,
77
+ "after_trading": 1,
78
+ }.items():
79
+ node = callbacks.get(name)
80
+ if node is None:
81
+ continue
82
+ positional = len(node.args.posonlyargs) + len(node.args.args)
83
+ if (
84
+ positional != expected
85
+ or node.args.vararg is not None
86
+ or node.args.kwarg is not None
87
+ or node.args.kwonlyargs
88
+ ):
89
+ raise ValueError(f"{name} 回调必须声明 {expected} 个位置参数")
90
+ if len(self.python_source.encode("utf-8")) > 65536:
91
+ raise ValueError("策略源码超过 64 KiB")
92
+ identities = [item.logical_key for item in self.factor_refs]
93
+ if len(set(identities)) != len(identities):
94
+ raise ValueError("策略因子依赖重复")
@@ -0,0 +1,51 @@
1
+ """
2
+ Screener Schema - 候选标的筛选/排名契约
3
+
4
+ Universe → Factor Values → Filter → Rank → Top N → ScreenerResult。
5
+ """
6
+
7
+ from datetime import datetime
8
+ from decimal import Decimal
9
+ from typing import Any
10
+
11
+ from pydantic import BaseModel, ConfigDict, Field
12
+
13
+ from quant_core.app.factor.schema.factor_contract import FactorValue
14
+ from quant_core.app.strategy.schema.strategy_schema import ScreenerSpec
15
+
16
+
17
+ class ScreenerCandidate(BaseModel):
18
+ """筛选后的单个候选标的"""
19
+
20
+ model_config = ConfigDict(validate_assignment=True)
21
+
22
+ instrument_id: str = Field(..., description="Canonical 标的 ID")
23
+ score: Decimal = Field(..., description="排名得分(rank_by 的值)")
24
+ factor_values: dict[str, Decimal | None] = Field(
25
+ default_factory=dict, description="各因子值快照(血缘/可解释性)"
26
+ )
27
+
28
+
29
+ class ScreenerRequest(BaseModel):
30
+ """筛选请求"""
31
+
32
+ model_config = ConfigDict(validate_assignment=True)
33
+
34
+ universe: list[str] = Field(..., description="候选标的池(Canonical ID)")
35
+ event_time: datetime = Field(..., description="市场事件时间")
36
+ spec: ScreenerSpec = Field(
37
+ ..., description="筛选/排名规则(Canonical,来自 StrategyDefinition.screening_rules)"
38
+ )
39
+ factor_values: dict[str, list[FactorValue]] = Field(
40
+ default_factory=dict, description="factor_name → 各标的 Canonical FactorValue(R2-1)"
41
+ )
42
+
43
+
44
+ class ScreenerResult(BaseModel):
45
+ """筛选结果"""
46
+
47
+ model_config = ConfigDict(validate_assignment=True)
48
+
49
+ event_time: datetime = Field(..., description="市场事件时间")
50
+ candidates: list[ScreenerCandidate] = Field(default_factory=list, description="按得分降序")
51
+ metadata: dict[str, Any] = Field(default_factory=dict, description="扩展元数据")
@@ -0,0 +1,33 @@
1
+ """
2
+ Signal Schema - Strategy 的 Canonical Decision Result
3
+
4
+ 边界(硬规则):Strategy 只产生 Signal,不产生 Broker Order。
5
+ Strategy 回答「应该做什么」,Execution / Backtest 回答「怎么成交」。
6
+ """
7
+
8
+ from datetime import datetime
9
+ from decimal import Decimal
10
+ from typing import Any
11
+ from uuid import uuid4
12
+
13
+ from pydantic import BaseModel, ConfigDict, Field
14
+
15
+ from quant_core.app.strategy.constants import SignalSide, SignalType
16
+
17
+
18
+ class Signal(BaseModel):
19
+ """策略信号——Strategy 某时点产生的结构化决策结果"""
20
+
21
+ model_config = ConfigDict(validate_assignment=True)
22
+
23
+ signal_id: str = Field(default_factory=lambda: str(uuid4()), description="信号唯一标识")
24
+ strategy_id: str = Field(..., description="策略标识(StrategyDefinition.strategy_id)")
25
+ strategy_version: str = Field("1", description="策略版本")
26
+ instrument_id: str = Field(..., description="Canonical 标的 ID")
27
+ event_time: datetime = Field(..., description="市场事件时间(Strategy 只认这个)")
28
+ signal_type: SignalType = Field(..., description="信号类型")
29
+ side: SignalSide = Field(SignalSide.LONG, description="方向(A股 V1:LONG/NONE)")
30
+ score: Decimal | None = Field(None, description="信号强度评分")
31
+ reason: str = Field("", description="人类可读的决策原因(可审计)")
32
+ factor_refs: list[str] = Field(default_factory=list, description="引用的因子名(血缘)")
33
+ metadata: dict[str, Any] = Field(default_factory=dict, description="扩展元数据")
@@ -0,0 +1,32 @@
1
+ """离线策略包固定定义及全部因子依赖,不包含计算结果。"""
2
+
3
+ from typing import Literal
4
+
5
+ from pydantic import BaseModel, ConfigDict, model_validator
6
+
7
+ from quant_core.app.factor.schema.factor_publication_schema import PublishedFactor
8
+ from quant_core.app.strategy.schema.python_strategy_schema import PythonStrategySpec
9
+
10
+
11
+ class PublishedStrategy(BaseModel):
12
+ model_config = ConfigDict(extra="forbid", frozen=True)
13
+ format_version: Literal[1] = 1
14
+ definition: PythonStrategySpec
15
+ content_digest: str
16
+ factors: tuple[PublishedFactor, ...] = ()
17
+
18
+ @model_validator(mode="after")
19
+ def verify(self) -> "PublishedStrategy":
20
+ self.definition.validate_source()
21
+ if (
22
+ self.definition.version == "latest"
23
+ or self.definition.content_digest != self.content_digest
24
+ ):
25
+ raise ValueError("策略版本未固定或摘要不匹配")
26
+ expected = {ref.logical_key for ref in self.definition.factor_refs}
27
+ actual = [factor.definition.identity.logical_key for factor in self.factors]
28
+ if len(actual) != len(set(actual)) or set(actual) != expected:
29
+ raise ValueError("策略包必须包含准确且完整的固定因子依赖")
30
+ if any(ref.definition_version == "latest" for ref in self.definition.factor_refs):
31
+ raise ValueError("策略因子依赖不能使用 latest")
32
+ return self
@@ -0,0 +1,96 @@
1
+ """
2
+ Strategy Schema - StrategyDefinition(Canonical 契约)
3
+
4
+ StrategyDefinition 描述完整交易决策规则(Artifact),不是运行中的实例,
5
+ 也不是 Signal。概念结构:Identity / Universe / Factors / Screener /
6
+ Entry / Exit / Holding / PositionIntent / Parameters。
7
+
8
+ 生命周期(StrategyStatus)描述 Artifact 状态;运行环境(StrategyRunMode)
9
+ 单独定义——同一 Definition 在 Backtest / Paper / Live 下业务逻辑不变。
10
+ """
11
+
12
+ from typing import Any, Literal
13
+
14
+ from pydantic import BaseModel, ConfigDict, Field
15
+
16
+ from quant_core.app.strategy.constants import (
17
+ AuthoringSource,
18
+ StrategyStatus,
19
+ StrategyStyle,
20
+ StrategyType,
21
+ )
22
+
23
+ # V1 规则操作符(受限谓词,由 Runtime 解释;不引入通用表达式引擎)
24
+ RuleOperator = Literal["gt", "gte", "lt", "lte", "cross_below", "cross_above"]
25
+
26
+
27
+ class RuleCondition(BaseModel):
28
+ """单条规则条件:factor 因子值(或 close/volume 等价格字段)与阈值/参照比较"""
29
+
30
+ model_config = ConfigDict(validate_assignment=True)
31
+
32
+ left: str = Field(..., description="左值:因子名或价格字段(close/volume/ma_10...)")
33
+ operator: RuleOperator = Field(..., description="比较操作")
34
+ right: str | float = Field(..., description="右值:数值或另一因子/字段名")
35
+
36
+
37
+ class ScreenerSpec(BaseModel):
38
+ """策略内嵌筛选说明(声明式;执行归属 Screener 能力)"""
39
+
40
+ model_config = ConfigDict(validate_assignment=True)
41
+
42
+ filters: list[RuleCondition] = Field(default_factory=list, description="过滤条件(全部满足)")
43
+ rank_by: str | None = Field(default=None, description="排名因子/字段(降序)")
44
+ top_n: int = Field(default=10, ge=1, description="取前 N")
45
+
46
+
47
+ class StrategyDefinition(BaseModel):
48
+ """策略定义(Artifact)——Human / Agent / Python 都生产同一个 Canonical Definition"""
49
+
50
+ model_config = ConfigDict(validate_assignment=True)
51
+
52
+ strategy_id: str = Field(..., pattern=r"^[a-z][a-z0-9_]*$", description="策略标识(唯一键)")
53
+ version: str = Field(default="1", description="策略版本")
54
+ name: str = Field(..., description="策略名称")
55
+ description: str = Field(default="", description="策略说明")
56
+ strategy_type: StrategyType = Field(..., description="决策范式")
57
+ styles: list[str] = Field(default_factory=list, description="风格 Tag(StrategyStyle 值)")
58
+ universe: list[str] = Field(default_factory=list, description="标的池(Canonical ID)")
59
+ required_factors: list[str] = Field(default_factory=list, description="依赖因子名")
60
+ screening_rules: ScreenerSpec | None = Field(default=None, description="筛选/排名说明")
61
+ entry_rules: list[RuleCondition] = Field(default_factory=list, description="入场条件,全部满足")
62
+ exit_rules: list[RuleCondition] = Field(default_factory=list, description="出场条件,任一满足")
63
+ holding_rules: list[str] = Field(default_factory=list, description="持仓管理规则,V1 描述性")
64
+ position_intent: Literal["long_only", "long_short", "target_weight"] = Field(
65
+ default="long_only", description="持仓意图(A股 V1:long_only)"
66
+ )
67
+ parameters: dict[str, Any] = Field(default_factory=dict, description="策略参数")
68
+ status: StrategyStatus = Field(default=StrategyStatus.DRAFT, description="Artifact 生命周期")
69
+ authoring_source: AuthoringSource = Field(default=AuthoringSource.HUMAN, description="生产入口")
70
+ created_by: str | None = Field(default=None, description="生产者标识(可追溯)")
71
+ parent_version: str | None = Field(default=None, description="迭代自哪个版本(Agent 迭代链)")
72
+ implementation_ref: str | None = Field(
73
+ default=None, description="Python Native 实现引用(dotted path,显式关联不靠文件名)"
74
+ )
75
+ metadata: dict[str, Any] = Field(default_factory=dict, description="扩展元数据")
76
+
77
+ def validate_definition(self, registered_factors: set[str] | None = None) -> list[str]:
78
+ """Definition Validation(静态层),返回问题列表
79
+
80
+ registered_factors:可选传入已注册因子名集合,校验 required_factors 引用合法。
81
+ """
82
+ problems: list[str] = []
83
+ if not self.entry_rules and not self.exit_rules:
84
+ problems.append("entry_rules 与 exit_rules 至少定义一组")
85
+ valid_styles = {s.value for s in StrategyStyle}
86
+ for style in self.styles:
87
+ if style not in valid_styles:
88
+ problems.append(f"非法 style: {style}")
89
+ for cond in [*self.entry_rules, *self.exit_rules]:
90
+ if not cond.left.strip():
91
+ problems.append("规则条件 left 不能为空")
92
+ if registered_factors is not None:
93
+ for name in self.required_factors:
94
+ if name not in registered_factors:
95
+ problems.append(f"required_factors 引用未注册因子: {name}")
96
+ return problems
File without changes
@@ -0,0 +1,103 @@
1
+ """内置多因子链的单因子 TopN 配置:共享评分、目标组合与调仓。"""
2
+
3
+ from datetime import date
4
+ from decimal import Decimal
5
+ from typing import Literal
6
+
7
+ from quant_core.app.data.schema.event_schema import BarEvent, MarketTimeSlice, TradingSessionEvent
8
+ from quant_core.app.factor.schema.factor_contract import FactorIdentity
9
+ from quant_core.app.factor.schema.selection_types import FactorWeight, SelectionDefinition
10
+ from quant_core.app.strategy.schema.portfolio_target import PortfolioConstraints, PortfolioDecision
11
+ from quant_core.app.strategy.schema.signal_schema import Signal
12
+ from quant_core.app.strategy.schema.strategy_schema import StrategyDefinition, StrategyType
13
+ from quant_core.app.strategy.strategy_context import StrategyContext
14
+
15
+
16
+ class FactorTopNStrategy:
17
+ """每个交易日首个可观察 Bar 时间片构造一次目标;持仓事实只从账户读取。"""
18
+
19
+ def __init__(
20
+ self,
21
+ universe: tuple[str, ...],
22
+ identity: FactorIdentity,
23
+ top_n: int,
24
+ constraints: PortfolioConstraints,
25
+ strategy_id: str = "factor_top_n",
26
+ rebalance_period: Literal["daily", "weekly"] = "daily",
27
+ ) -> None:
28
+ self._universe = universe
29
+ self._identity = identity
30
+ self._selection = SelectionDefinition((FactorWeight(identity, Decimal(1)),), top_n)
31
+ self._constraints = constraints
32
+ self._strategy_id = strategy_id
33
+ self._last_day: date | None = None
34
+ self._rebalance_period = rebalance_period
35
+
36
+ def definition(self) -> StrategyDefinition:
37
+ return StrategyDefinition(
38
+ strategy_id=self._strategy_id,
39
+ name="Factor Top-N Target Portfolio",
40
+ strategy_type=StrategyType.EVENT_DRIVEN,
41
+ universe=list(self._universe),
42
+ required_factors=[self._identity.name],
43
+ parameters={
44
+ "top_n": self._selection.top_n,
45
+ "cash_buffer": str(self._constraints.cash_buffer),
46
+ "max_weight": str(self._constraints.max_weight),
47
+ },
48
+ )
49
+
50
+ async def on_bar(self, event: BarEvent, context: StrategyContext) -> list[Signal]:
51
+ return []
52
+
53
+ async def on_session(
54
+ self, event: TradingSessionEvent, context: StrategyContext
55
+ ) -> list[Signal]:
56
+ return []
57
+
58
+ async def on_slice(self, time_slice: MarketTimeSlice, context: StrategyContext) -> list[Signal]:
59
+ day = time_slice.event_time.date()
60
+ if self._last_day == day or not any(
61
+ isinstance(event, BarEvent) for event in time_slice.events
62
+ ):
63
+ return []
64
+ if (
65
+ self._rebalance_period == "weekly"
66
+ and self._last_day is not None
67
+ and self._last_day.isocalendar()[:2] == day.isocalendar()[:2]
68
+ ):
69
+ return []
70
+ if context.portfolio is None:
71
+ raise RuntimeError("组合策略必须提供真实账户读取能力")
72
+ account = await context.portfolio.get_snapshot()
73
+ current_weights = {
74
+ position.instrument_id: position.market_value / account.total_value
75
+ for position in account.positions
76
+ if account.total_value > 0
77
+ }
78
+ holding_sessions = {}
79
+ if self._constraints.min_holding_sessions:
80
+ acquired = {}
81
+ for lot in await context.portfolio.get_position_lots():
82
+ if lot.remaining_quantity:
83
+ acquired[lot.instrument_id] = max(
84
+ acquired.get(lot.instrument_id, lot.acquired_at.date()),
85
+ lot.acquired_at.date(),
86
+ )
87
+ for instrument_id, acquired_on in acquired.items():
88
+ calendar = await context.market.list_trading_days("XSHG", acquired_on, day)
89
+ holding_sessions[instrument_id] = sum(
90
+ item.is_open and acquired_on < item.trade_date <= day for item in calendar
91
+ )
92
+ selection = context.select_factors(self._selection)
93
+ target = context.construct_portfolio(
94
+ selection,
95
+ self._constraints,
96
+ current_weights=current_weights,
97
+ holding_sessions=holding_sessions,
98
+ )
99
+ context.order_target_portfolio(
100
+ dict(target.weights), decision=PortfolioDecision(selection, target, self._constraints)
101
+ )
102
+ self._last_day = day
103
+ return []