powerquant-cli 0.2.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (414) hide show
  1. powerquant_cli-0.2.6/.env.example +7 -0
  2. powerquant_cli-0.2.6/.gitignore +22 -0
  3. powerquant_cli-0.2.6/LICENSE +36 -0
  4. powerquant_cli-0.2.6/PKG-INFO +62 -0
  5. powerquant_cli-0.2.6/README.md +41 -0
  6. powerquant_cli-0.2.6/bundled/pqdata/__init__.py +6 -0
  7. powerquant_cli-0.2.6/bundled/pqdata/client.py +389 -0
  8. powerquant_cli-0.2.6/bundled/pqdata/download.py +60 -0
  9. powerquant_cli-0.2.6/bundled/pqdata/exceptions.py +14 -0
  10. powerquant_cli-0.2.6/bundled/pqdata/schema/__init__.py +1 -0
  11. powerquant_cli-0.2.6/bundled/pqdata/schema/adjustment_schema.py +67 -0
  12. powerquant_cli-0.2.6/bundled/pqdata/schema/bar_content_schema.py +24 -0
  13. powerquant_cli-0.2.6/bundled/pqdata/schema/bar_schema.py +56 -0
  14. powerquant_cli-0.2.6/bundled/pqdata/schema/corporate_action_schema.py +46 -0
  15. powerquant_cli-0.2.6/bundled/pqdata/schema/history_schema.py +61 -0
  16. powerquant_cli-0.2.6/bundled/pqdata/schema/pit_daily_window.py +16 -0
  17. powerquant_cli-0.2.6/bundled/pqdata/schema/query_schema.py +53 -0
  18. powerquant_cli-0.2.6/bundled/pqdata/schema/quote_schema.py +73 -0
  19. powerquant_cli-0.2.6/bundled/pqdata/schema/reference_schema.py +48 -0
  20. powerquant_cli-0.2.6/bundled/pqdata/schema/reliability_schema.py +152 -0
  21. powerquant_cli-0.2.6/bundled/quant_core/__init__.py +0 -0
  22. powerquant_cli-0.2.6/bundled/quant_core/app/SPEC.md +18 -0
  23. powerquant_cli-0.2.6/bundled/quant_core/app/__init__.py +0 -0
  24. powerquant_cli-0.2.6/bundled/quant_core/app/backtest/SPEC.md +65 -0
  25. powerquant_cli-0.2.6/bundled/quant_core/app/backtest/__init__.py +1 -0
  26. powerquant_cli-0.2.6/bundled/quant_core/app/backtest/backtest_input_schema.py +20 -0
  27. powerquant_cli-0.2.6/bundled/quant_core/app/backtest/backtest_run.py +393 -0
  28. powerquant_cli-0.2.6/bundled/quant_core/app/backtest/data_guard.py +77 -0
  29. powerquant_cli-0.2.6/bundled/quant_core/app/backtest/historical_clock.py +26 -0
  30. powerquant_cli-0.2.6/bundled/quant_core/app/backtest/legacy_result_schema.py +34 -0
  31. powerquant_cli-0.2.6/bundled/quant_core/app/backtest/metrics.py +194 -0
  32. powerquant_cli-0.2.6/bundled/quant_core/app/backtest/performance_report.py +61 -0
  33. powerquant_cli-0.2.6/bundled/quant_core/app/backtest/report_schema.py +64 -0
  34. powerquant_cli-0.2.6/bundled/quant_core/app/backtest/schema.py +477 -0
  35. powerquant_cli-0.2.6/bundled/quant_core/app/backtest/service/__init__.py +0 -0
  36. powerquant_cli-0.2.6/bundled/quant_core/app/backtest/service/backtest_execution_service.py +114 -0
  37. powerquant_cli-0.2.6/bundled/quant_core/app/data/__init__.py +0 -0
  38. powerquant_cli-0.2.6/bundled/quant_core/app/data/constants.py +284 -0
  39. powerquant_cli-0.2.6/bundled/quant_core/app/data/domain/__init__.py +0 -0
  40. powerquant_cli-0.2.6/bundled/quant_core/app/data/domain/trading_quantity_value.py +57 -0
  41. powerquant_cli-0.2.6/bundled/quant_core/app/data/exceptions.py +95 -0
  42. powerquant_cli-0.2.6/bundled/quant_core/app/data/interface/__init__.py +0 -0
  43. powerquant_cli-0.2.6/bundled/quant_core/app/data/interface/adjustment_factor_query.py +21 -0
  44. powerquant_cli-0.2.6/bundled/quant_core/app/data/interface/market_data_query.py +71 -0
  45. powerquant_cli-0.2.6/bundled/quant_core/app/data/interface/market_data_verification.py +20 -0
  46. powerquant_cli-0.2.6/bundled/quant_core/app/data/interface/market_event_source.py +11 -0
  47. powerquant_cli-0.2.6/bundled/quant_core/app/data/interface/market_revision_query.py +8 -0
  48. powerquant_cli-0.2.6/bundled/quant_core/app/data/interface/market_runtime_repository.py +44 -0
  49. powerquant_cli-0.2.6/bundled/quant_core/app/data/interface/market_time_slice_source.py +11 -0
  50. powerquant_cli-0.2.6/bundled/quant_core/app/data/repository/__init__.py +0 -0
  51. powerquant_cli-0.2.6/bundled/quant_core/app/data/schema/__init__.py +1 -0
  52. powerquant_cli-0.2.6/bundled/quant_core/app/data/schema/adjustment_schema.py +67 -0
  53. powerquant_cli-0.2.6/bundled/quant_core/app/data/schema/corporate_action_schema.py +46 -0
  54. powerquant_cli-0.2.6/bundled/quant_core/app/data/schema/event_schema.py +506 -0
  55. powerquant_cli-0.2.6/bundled/quant_core/app/data/schema/exchange_operating_state.py +37 -0
  56. powerquant_cli-0.2.6/bundled/quant_core/app/data/schema/exchange_state_schema.py +106 -0
  57. powerquant_cli-0.2.6/bundled/quant_core/app/data/schema/history_schema.py +48 -0
  58. powerquant_cli-0.2.6/bundled/quant_core/app/data/schema/instrument_identity.py +69 -0
  59. powerquant_cli-0.2.6/bundled/quant_core/app/data/schema/market_schema.py +128 -0
  60. powerquant_cli-0.2.6/bundled/quant_core/app/data/schema/pit_daily_window.py +16 -0
  61. powerquant_cli-0.2.6/bundled/quant_core/app/data/schema/quote_schema.py +73 -0
  62. powerquant_cli-0.2.6/bundled/quant_core/app/data/schema/reliability_schema.py +152 -0
  63. powerquant_cli-0.2.6/bundled/quant_core/app/data/schema/snapshot_schema.py +82 -0
  64. powerquant_cli-0.2.6/bundled/quant_core/app/data/schema/trading_state_schema.py +211 -0
  65. powerquant_cli-0.2.6/bundled/quant_core/app/data/service/__init__.py +0 -0
  66. powerquant_cli-0.2.6/bundled/quant_core/app/data/service/calendar_coverage_service.py +63 -0
  67. powerquant_cli-0.2.6/bundled/quant_core/app/data/service/prepared_data_verification_service.py +40 -0
  68. powerquant_cli-0.2.6/bundled/quant_core/app/data/service/prepared_market_service.py +189 -0
  69. powerquant_cli-0.2.6/bundled/quant_core/app/data/service/price_adjustment_service.py +235 -0
  70. powerquant_cli-0.2.6/bundled/quant_core/app/factor/LICENSE.vnpy +21 -0
  71. powerquant_cli-0.2.6/bundled/quant_core/app/factor/__init__.py +13 -0
  72. powerquant_cli-0.2.6/bundled/quant_core/app/factor/builtin/__init__.py +0 -0
  73. powerquant_cli-0.2.6/bundled/quant_core/app/factor/builtin/alpha101_recipe.py +1095 -0
  74. powerquant_cli-0.2.6/bundled/quant_core/app/factor/builtin/alpha158_recipe.py +1284 -0
  75. powerquant_cli-0.2.6/bundled/quant_core/app/factor/builtin/factor_recipe_catalog.py +100 -0
  76. powerquant_cli-0.2.6/bundled/quant_core/app/factor/computation/__init__.py +0 -0
  77. powerquant_cli-0.2.6/bundled/quant_core/app/factor/computation/factor_batch_evaluator.py +141 -0
  78. powerquant_cli-0.2.6/bundled/quant_core/app/factor/computation/factor_expression.py +106 -0
  79. powerquant_cli-0.2.6/bundled/quant_core/app/factor/computation/factor_source_loader.py +24 -0
  80. powerquant_cli-0.2.6/bundled/quant_core/app/factor/computation/operators/__init__.py +0 -0
  81. powerquant_cli-0.2.6/bundled/quant_core/app/factor/computation/operators/arithmetic_operator.py +182 -0
  82. powerquant_cli-0.2.6/bundled/quant_core/app/factor/computation/operators/cross_section_operator.py +66 -0
  83. powerquant_cli-0.2.6/bundled/quant_core/app/factor/computation/operators/time_series_operator.py +342 -0
  84. powerquant_cli-0.2.6/bundled/quant_core/app/factor/computation/panel_evaluator.py +59 -0
  85. powerquant_cli-0.2.6/bundled/quant_core/app/factor/computation/panel_expression.py +193 -0
  86. powerquant_cli-0.2.6/bundled/quant_core/app/factor/computation/python_factor_executor.py +87 -0
  87. powerquant_cli-0.2.6/bundled/quant_core/app/factor/computation/universe_filter.py +25 -0
  88. powerquant_cli-0.2.6/bundled/quant_core/app/factor/constants.py +114 -0
  89. powerquant_cli-0.2.6/bundled/quant_core/app/factor/exceptions.py +35 -0
  90. powerquant_cli-0.2.6/bundled/quant_core/app/factor/factor_dependency.py +65 -0
  91. powerquant_cli-0.2.6/bundled/quant_core/app/factor/interface/__init__.py +0 -0
  92. powerquant_cli-0.2.6/bundled/quant_core/app/factor/interface/factor_compute.py +12 -0
  93. powerquant_cli-0.2.6/bundled/quant_core/app/factor/interface/factor_execution.py +15 -0
  94. powerquant_cli-0.2.6/bundled/quant_core/app/factor/interface/factor_local_data.py +42 -0
  95. powerquant_cli-0.2.6/bundled/quant_core/app/factor/interface/factor_panel_compute.py +18 -0
  96. powerquant_cli-0.2.6/bundled/quant_core/app/factor/interface/factor_panel_input.py +12 -0
  97. powerquant_cli-0.2.6/bundled/quant_core/app/factor/interface/factor_query_store.py +39 -0
  98. powerquant_cli-0.2.6/bundled/quant_core/app/factor/interface/public/__init__.py +0 -0
  99. powerquant_cli-0.2.6/bundled/quant_core/app/factor/interface/public/factor_diagnostics.py +18 -0
  100. powerquant_cli-0.2.6/bundled/quant_core/app/factor/interface/public/factor_exposure_query.py +12 -0
  101. powerquant_cli-0.2.6/bundled/quant_core/app/factor/interface/public/factor_local.py +15 -0
  102. powerquant_cli-0.2.6/bundled/quant_core/app/factor/interface/public/factor_query.py +57 -0
  103. powerquant_cli-0.2.6/bundled/quant_core/app/factor/interface/public/factor_selection.py +20 -0
  104. powerquant_cli-0.2.6/bundled/quant_core/app/factor/model/__init__.py +0 -0
  105. powerquant_cli-0.2.6/bundled/quant_core/app/factor/repository/__init__.py +0 -0
  106. powerquant_cli-0.2.6/bundled/quant_core/app/factor/schema/__init__.py +1 -0
  107. powerquant_cli-0.2.6/bundled/quant_core/app/factor/schema/diagnostic_types.py +42 -0
  108. powerquant_cli-0.2.6/bundled/quant_core/app/factor/schema/factor_artifact_schema.py +36 -0
  109. powerquant_cli-0.2.6/bundled/quant_core/app/factor/schema/factor_computation_schema.py +102 -0
  110. powerquant_cli-0.2.6/bundled/quant_core/app/factor/schema/factor_compute_request.py +24 -0
  111. powerquant_cli-0.2.6/bundled/quant_core/app/factor/schema/factor_contract.py +257 -0
  112. powerquant_cli-0.2.6/bundled/quant_core/app/factor/schema/factor_input_schema.py +32 -0
  113. powerquant_cli-0.2.6/bundled/quant_core/app/factor/schema/factor_panel.py +65 -0
  114. powerquant_cli-0.2.6/bundled/quant_core/app/factor/schema/factor_publication_schema.py +41 -0
  115. powerquant_cli-0.2.6/bundled/quant_core/app/factor/schema/factor_query_result_schema.py +12 -0
  116. powerquant_cli-0.2.6/bundled/quant_core/app/factor/schema/factor_result_schema.py +6 -0
  117. powerquant_cli-0.2.6/bundled/quant_core/app/factor/schema/local_factor_schema.py +42 -0
  118. powerquant_cli-0.2.6/bundled/quant_core/app/factor/schema/panel_policy.py +13 -0
  119. powerquant_cli-0.2.6/bundled/quant_core/app/factor/schema/selection_report.py +116 -0
  120. powerquant_cli-0.2.6/bundled/quant_core/app/factor/schema/selection_types.py +135 -0
  121. powerquant_cli-0.2.6/bundled/quant_core/app/factor/service/__init__.py +1 -0
  122. powerquant_cli-0.2.6/bundled/quant_core/app/factor/service/factor_compute_service.py +140 -0
  123. powerquant_cli-0.2.6/bundled/quant_core/app/factor/service/factor_diagnostic_runner.py +134 -0
  124. powerquant_cli-0.2.6/bundled/quant_core/app/factor/service/factor_diagnostics_service.py +140 -0
  125. powerquant_cli-0.2.6/bundled/quant_core/app/factor/service/factor_exposure_service.py +24 -0
  126. powerquant_cli-0.2.6/bundled/quant_core/app/factor/service/factor_input_service.py +258 -0
  127. powerquant_cli-0.2.6/bundled/quant_core/app/factor/service/factor_local_execution_service.py +51 -0
  128. powerquant_cli-0.2.6/bundled/quant_core/app/factor/service/factor_panel_service.py +86 -0
  129. powerquant_cli-0.2.6/bundled/quant_core/app/factor/service/factor_runtime_service.py +306 -0
  130. powerquant_cli-0.2.6/bundled/quant_core/app/factor/service/factor_selection_runner.py +88 -0
  131. powerquant_cli-0.2.6/bundled/quant_core/app/factor/service/published_factor_execution_service.py +160 -0
  132. powerquant_cli-0.2.6/bundled/quant_core/app/factor/service/selection_service.py +234 -0
  133. powerquant_cli-0.2.6/bundled/quant_core/app/factor/visibility.py +22 -0
  134. powerquant_cli-0.2.6/bundled/quant_core/app/opportunity/SPEC.md +45 -0
  135. powerquant_cli-0.2.6/bundled/quant_core/app/opportunity/__init__.py +1 -0
  136. powerquant_cli-0.2.6/bundled/quant_core/app/order_execution/SPEC.md +64 -0
  137. powerquant_cli-0.2.6/bundled/quant_core/app/order_execution/__init__.py +1 -0
  138. powerquant_cli-0.2.6/bundled/quant_core/app/order_execution/bar_matcher.py +55 -0
  139. powerquant_cli-0.2.6/bundled/quant_core/app/order_execution/constants.py +142 -0
  140. powerquant_cli-0.2.6/bundled/quant_core/app/order_execution/domain/__init__.py +45 -0
  141. powerquant_cli-0.2.6/bundled/quant_core/app/order_execution/domain/execution_batch.py +108 -0
  142. powerquant_cli-0.2.6/bundled/quant_core/app/order_execution/domain/order_entity.py +197 -0
  143. powerquant_cli-0.2.6/bundled/quant_core/app/order_execution/domain/order_event.py +87 -0
  144. powerquant_cli-0.2.6/bundled/quant_core/app/order_execution/domain/order_policy.py +29 -0
  145. powerquant_cli-0.2.6/bundled/quant_core/app/order_execution/domain/order_value.py +142 -0
  146. powerquant_cli-0.2.6/bundled/quant_core/app/order_execution/fee_model.py +60 -0
  147. powerquant_cli-0.2.6/bundled/quant_core/app/order_execution/interface/__init__.py +0 -0
  148. powerquant_cli-0.2.6/bundled/quant_core/app/order_execution/interface/order_account.py +10 -0
  149. powerquant_cli-0.2.6/bundled/quant_core/app/order_execution/interface/order_trading_rules.py +10 -0
  150. powerquant_cli-0.2.6/bundled/quant_core/app/order_execution/interface/public/__init__.py +0 -0
  151. powerquant_cli-0.2.6/bundled/quant_core/app/order_execution/interface/public/execution_account.py +53 -0
  152. powerquant_cli-0.2.6/bundled/quant_core/app/order_execution/interface/public/order_events.py +19 -0
  153. powerquant_cli-0.2.6/bundled/quant_core/app/order_execution/interface/public/order_execution.py +107 -0
  154. powerquant_cli-0.2.6/bundled/quant_core/app/order_execution/interface/public/order_runtime.py +30 -0
  155. powerquant_cli-0.2.6/bundled/quant_core/app/order_execution/interface/public/order_submission.py +17 -0
  156. powerquant_cli-0.2.6/bundled/quant_core/app/order_execution/order_event_bus.py +28 -0
  157. powerquant_cli-0.2.6/bundled/quant_core/app/order_execution/order_execution_dependency.py +48 -0
  158. powerquant_cli-0.2.6/bundled/quant_core/app/order_execution/order_submission_service.py +301 -0
  159. powerquant_cli-0.2.6/bundled/quant_core/app/order_execution/schema/__init__.py +0 -0
  160. powerquant_cli-0.2.6/bundled/quant_core/app/order_execution/schema/order_request_schema.py +88 -0
  161. powerquant_cli-0.2.6/bundled/quant_core/app/order_execution/simulation_broker.py +883 -0
  162. powerquant_cli-0.2.6/bundled/quant_core/app/order_execution/slippage_model.py +40 -0
  163. powerquant_cli-0.2.6/bundled/quant_core/app/order_execution/tradability_policy.py +174 -0
  164. powerquant_cli-0.2.6/bundled/quant_core/app/order_execution/validators.py +40 -0
  165. powerquant_cli-0.2.6/bundled/quant_core/app/portfolio/SPEC.md +69 -0
  166. powerquant_cli-0.2.6/bundled/quant_core/app/portfolio/__init__.py +0 -0
  167. powerquant_cli-0.2.6/bundled/quant_core/app/portfolio/domain/__init__.py +32 -0
  168. powerquant_cli-0.2.6/bundled/quant_core/app/portfolio/domain/entitlement_entity.py +45 -0
  169. powerquant_cli-0.2.6/bundled/quant_core/app/portfolio/domain/portfolio_entity.py +511 -0
  170. powerquant_cli-0.2.6/bundled/quant_core/app/portfolio/domain/portfolio_event.py +59 -0
  171. powerquant_cli-0.2.6/bundled/quant_core/app/portfolio/domain/portfolio_value.py +21 -0
  172. powerquant_cli-0.2.6/bundled/quant_core/app/portfolio/domain/position_entity.py +165 -0
  173. powerquant_cli-0.2.6/bundled/quant_core/app/portfolio/domain/reservation_entity.py +69 -0
  174. powerquant_cli-0.2.6/bundled/quant_core/app/portfolio/interface/__init__.py +0 -0
  175. powerquant_cli-0.2.6/bundled/quant_core/app/portfolio/interface/public/__init__.py +0 -0
  176. powerquant_cli-0.2.6/bundled/quant_core/app/portfolio/interface/public/portfolio.py +77 -0
  177. powerquant_cli-0.2.6/bundled/quant_core/app/portfolio/interface/public/simulation_account.py +16 -0
  178. powerquant_cli-0.2.6/bundled/quant_core/app/portfolio/ledger_schema.py +130 -0
  179. powerquant_cli-0.2.6/bundled/quant_core/app/portfolio/order_account_listener.py +36 -0
  180. powerquant_cli-0.2.6/bundled/quant_core/app/portfolio/portfolio_dependency.py +21 -0
  181. powerquant_cli-0.2.6/bundled/quant_core/app/portfolio/portfolio_schema.py +204 -0
  182. powerquant_cli-0.2.6/bundled/quant_core/app/portfolio/simulated_portfolio.py +427 -0
  183. powerquant_cli-0.2.6/bundled/quant_core/app/research/SPEC.md +57 -0
  184. powerquant_cli-0.2.6/bundled/quant_core/app/research/__init__.py +0 -0
  185. powerquant_cli-0.2.6/bundled/quant_core/app/research/adapter/__init__.py +0 -0
  186. powerquant_cli-0.2.6/bundled/quant_core/app/research/adapter/market_label_query.py +84 -0
  187. powerquant_cli-0.2.6/bundled/quant_core/app/research/interface/__init__.py +0 -0
  188. powerquant_cli-0.2.6/bundled/quant_core/app/research/interface/research_dataset.py +47 -0
  189. powerquant_cli-0.2.6/bundled/quant_core/app/research/interface/research_evaluation.py +19 -0
  190. powerquant_cli-0.2.6/bundled/quant_core/app/research/schema/__init__.py +0 -0
  191. powerquant_cli-0.2.6/bundled/quant_core/app/research/schema/comparison_schema.py +12 -0
  192. powerquant_cli-0.2.6/bundled/quant_core/app/research/schema/dataset_schema.py +112 -0
  193. powerquant_cli-0.2.6/bundled/quant_core/app/research/schema/research_schema.py +120 -0
  194. powerquant_cli-0.2.6/bundled/quant_core/app/research/service/__init__.py +0 -0
  195. powerquant_cli-0.2.6/bundled/quant_core/app/research/service/research_dataset_service.py +80 -0
  196. powerquant_cli-0.2.6/bundled/quant_core/app/research/service/research_evaluation_service.py +56 -0
  197. powerquant_cli-0.2.6/bundled/quant_core/app/research/service/research_service.py +171 -0
  198. powerquant_cli-0.2.6/bundled/quant_core/app/strategy/SPEC.md +215 -0
  199. powerquant_cli-0.2.6/bundled/quant_core/app/strategy/__init__.py +1 -0
  200. powerquant_cli-0.2.6/bundled/quant_core/app/strategy/constants.py +87 -0
  201. powerquant_cli-0.2.6/bundled/quant_core/app/strategy/exceptions.py +32 -0
  202. powerquant_cli-0.2.6/bundled/quant_core/app/strategy/interface/__init__.py +0 -0
  203. powerquant_cli-0.2.6/bundled/quant_core/app/strategy/interface/public/__init__.py +0 -0
  204. powerquant_cli-0.2.6/bundled/quant_core/app/strategy/interface/public/portfolio_construction.py +19 -0
  205. powerquant_cli-0.2.6/bundled/quant_core/app/strategy/interface/public/strategy.py +30 -0
  206. powerquant_cli-0.2.6/bundled/quant_core/app/strategy/interface/public/strategy_market_query.py +53 -0
  207. powerquant_cli-0.2.6/bundled/quant_core/app/strategy/interface/public/strategy_portfolio_reader.py +33 -0
  208. powerquant_cli-0.2.6/bundled/quant_core/app/strategy/interface/strategy_factor_reader.py +19 -0
  209. powerquant_cli-0.2.6/bundled/quant_core/app/strategy/interface/strategy_order_reader.py +14 -0
  210. powerquant_cli-0.2.6/bundled/quant_core/app/strategy/schema/__init__.py +0 -0
  211. powerquant_cli-0.2.6/bundled/quant_core/app/strategy/schema/portfolio_schema.py +28 -0
  212. powerquant_cli-0.2.6/bundled/quant_core/app/strategy/schema/portfolio_target.py +70 -0
  213. powerquant_cli-0.2.6/bundled/quant_core/app/strategy/schema/python_strategy_schema.py +94 -0
  214. powerquant_cli-0.2.6/bundled/quant_core/app/strategy/schema/screener_schema.py +51 -0
  215. powerquant_cli-0.2.6/bundled/quant_core/app/strategy/schema/signal_schema.py +33 -0
  216. powerquant_cli-0.2.6/bundled/quant_core/app/strategy/schema/strategy_publication_schema.py +32 -0
  217. powerquant_cli-0.2.6/bundled/quant_core/app/strategy/schema/strategy_schema.py +96 -0
  218. powerquant_cli-0.2.6/bundled/quant_core/app/strategy/service/__init__.py +0 -0
  219. powerquant_cli-0.2.6/bundled/quant_core/app/strategy/service/factor_top_n_strategy.py +103 -0
  220. powerquant_cli-0.2.6/bundled/quant_core/app/strategy/service/portfolio_construction_service.py +142 -0
  221. powerquant_cli-0.2.6/bundled/quant_core/app/strategy/service/python_strategy_service.py +65 -0
  222. powerquant_cli-0.2.6/bundled/quant_core/app/strategy/service/screener_service.py +77 -0
  223. powerquant_cli-0.2.6/bundled/quant_core/app/strategy/service/store_factor_reader.py +120 -0
  224. powerquant_cli-0.2.6/bundled/quant_core/app/strategy/service/strategy_market_view.py +120 -0
  225. powerquant_cli-0.2.6/bundled/quant_core/app/strategy/service/strategy_runtime_service.py +319 -0
  226. powerquant_cli-0.2.6/bundled/quant_core/app/strategy/service/strategy_scheduler.py +50 -0
  227. powerquant_cli-0.2.6/bundled/quant_core/app/strategy/strategies/__init__.py +7 -0
  228. powerquant_cli-0.2.6/bundled/quant_core/app/strategy/strategies/momentum_volume_breakout.py +157 -0
  229. powerquant_cli-0.2.6/bundled/quant_core/app/strategy/strategy_context.py +316 -0
  230. powerquant_cli-0.2.6/bundled/quant_core/app/strategy/strategy_dependency.py +101 -0
  231. powerquant_cli-0.2.6/bundled/quant_core/app/trading_runtime/SPEC.md +70 -0
  232. powerquant_cli-0.2.6/bundled/quant_core/app/trading_runtime/__init__.py +1 -0
  233. powerquant_cli-0.2.6/bundled/quant_core/app/trading_runtime/domain/__init__.py +0 -0
  234. powerquant_cli-0.2.6/bundled/quant_core/app/trading_runtime/replay/__init__.py +0 -0
  235. powerquant_cli-0.2.6/bundled/quant_core/app/trading_runtime/replay/daily_state_expand.py +49 -0
  236. powerquant_cli-0.2.6/bundled/quant_core/app/trading_runtime/replay/exchange_state_resolver.py +90 -0
  237. powerquant_cli-0.2.6/bundled/quant_core/app/trading_runtime/replay/market_runtime_service.py +573 -0
  238. powerquant_cli-0.2.6/bundled/quant_core/app/trading_runtime/replay/market_state_resolver.py +150 -0
  239. powerquant_cli-0.2.6/bundled/quant_core/app/trading_runtime/runtime_ports.py +64 -0
  240. powerquant_cli-0.2.6/bundled/quant_core/app/trading_runtime/runtime_schema.py +129 -0
  241. powerquant_cli-0.2.6/bundled/quant_core/app/trading_runtime/runtime_session.py +361 -0
  242. powerquant_cli-0.2.6/bundled/quant_core/app/trading_runtime/strategy_portfolio_adapter.py +58 -0
  243. powerquant_cli-0.2.6/bundled/quant_core/app/trading_runtime/trading_rule_book.py +37 -0
  244. powerquant_cli-0.2.6/bundled/quant_core/app/trading_runtime/trading_rule_query.py +12 -0
  245. powerquant_cli-0.2.6/bundled/quant_core/common/__init__.py +0 -0
  246. powerquant_cli-0.2.6/bundled/quant_core/common/exceptions/__init__.py +0 -0
  247. powerquant_cli-0.2.6/bundled/quant_core/common/exceptions/base.py +89 -0
  248. powerquant_cli-0.2.6/bundled/quant_core/common/local_run.py +45 -0
  249. powerquant_cli-0.2.6/examples/cached_factor_screen.py +165 -0
  250. powerquant_cli-0.2.6/examples/factors/ma_close_10.py +10 -0
  251. powerquant_cli-0.2.6/examples/factors/max_drawdown_20d.py +18 -0
  252. powerquant_cli-0.2.6/examples/factors/momentum_20d.py +13 -0
  253. powerquant_cli-0.2.6/examples/factors/multi_intraday_screen.py +16 -0
  254. powerquant_cli-0.2.6/examples/factors/risk_adjusted_momentum_20d.py +18 -0
  255. powerquant_cli-0.2.6/examples/factors/volume_ratio_5d.py +14 -0
  256. powerquant_cli-0.2.6/examples/factors/volume_weighted_close_pressure.py +14 -0
  257. powerquant_cli-0.2.6/examples/full_market_screen.py +189 -0
  258. powerquant_cli-0.2.6/examples/local_quant_validation.py +196 -0
  259. powerquant_cli-0.2.6/examples/published_definition_validation.py +219 -0
  260. powerquant_cli-0.2.6/examples/research/factor_pit_coverage.py +87 -0
  261. powerquant_cli-0.2.6/examples/research/ipo_first_day.py +348 -0
  262. powerquant_cli-0.2.6/examples/selection/README.md +59 -0
  263. powerquant_cli-0.2.6/examples/selection/backtest.json +18 -0
  264. powerquant_cli-0.2.6/examples/selection/buy_hold.py +7 -0
  265. powerquant_cli-0.2.6/examples/selection/compute.json +15 -0
  266. powerquant_cli-0.2.6/examples/selection/prepare.json +13 -0
  267. powerquant_cli-0.2.6/examples/selection/select.json +44 -0
  268. powerquant_cli-0.2.6/examples/strategies/bar_roundtrip.py +12 -0
  269. powerquant_cli-0.2.6/examples/strategies/buy_and_hold.py +11 -0
  270. powerquant_cli-0.2.6/examples/strategies/contrarian_timing.py +14 -0
  271. powerquant_cli-0.2.6/examples/strategies/factor_gate_vwap.py +22 -0
  272. powerquant_cli-0.2.6/examples/strategies/ipo_next_day_vwap.py +31 -0
  273. powerquant_cli-0.2.6/examples/strategies/momentum_timing.py +14 -0
  274. powerquant_cli-0.2.6/examples/strategies/monthly_equal_weight.py +19 -0
  275. powerquant_cli-0.2.6/examples/strategies/session_roundtrip.py +16 -0
  276. powerquant_cli-0.2.6/examples/strategies/trend_drawdown_guard.py +22 -0
  277. powerquant_cli-0.2.6/examples/strategies/weekly_risk_adjusted_rotation.py +61 -0
  278. powerquant_cli-0.2.6/examples/unified_snapshot_validation.py +260 -0
  279. powerquant_cli-0.2.6/hatch_build.py +22 -0
  280. powerquant_cli-0.2.6/licenses/LICENSE.vnpy +21 -0
  281. powerquant_cli-0.2.6/pyproject.toml +84 -0
  282. powerquant_cli-0.2.6/src/quant_cli/__init__.py +0 -0
  283. powerquant_cli-0.2.6/src/quant_cli/api.py +39 -0
  284. powerquant_cli-0.2.6/src/quant_cli/app/__init__.py +0 -0
  285. powerquant_cli-0.2.6/src/quant_cli/app/backtest/__init__.py +0 -0
  286. powerquant_cli-0.2.6/src/quant_cli/app/backtest/cli/__init__.py +0 -0
  287. powerquant_cli-0.2.6/src/quant_cli/app/backtest/cli/local_backtest_cli.py +72 -0
  288. powerquant_cli-0.2.6/src/quant_cli/app/backtest/local_backtest_dependency.py +10 -0
  289. powerquant_cli-0.2.6/src/quant_cli/app/backtest/local_backtest_schema.py +49 -0
  290. powerquant_cli-0.2.6/src/quant_cli/app/backtest/repository/__init__.py +0 -0
  291. powerquant_cli-0.2.6/src/quant_cli/app/backtest/repository/backtest_local_repository.py +67 -0
  292. powerquant_cli-0.2.6/src/quant_cli/app/backtest/service/__init__.py +0 -0
  293. powerquant_cli-0.2.6/src/quant_cli/app/backtest/service/local_backtest_service.py +271 -0
  294. powerquant_cli-0.2.6/src/quant_cli/app/data/__init__.py +0 -0
  295. powerquant_cli-0.2.6/src/quant_cli/app/data/cli/__init__.py +0 -0
  296. powerquant_cli-0.2.6/src/quant_cli/app/data/cli/data_cli.py +78 -0
  297. powerquant_cli-0.2.6/src/quant_cli/app/data/data_dependency.py +35 -0
  298. powerquant_cli-0.2.6/src/quant_cli/app/data/repository/__init__.py +0 -0
  299. powerquant_cli-0.2.6/src/quant_cli/app/data/repository/parquet_repository.py +124 -0
  300. powerquant_cli-0.2.6/src/quant_cli/app/data/schema/__init__.py +0 -0
  301. powerquant_cli-0.2.6/src/quant_cli/app/data/schema/dataset_audit_schema.py +37 -0
  302. powerquant_cli-0.2.6/src/quant_cli/app/data/schema/dataset_request_schema.py +28 -0
  303. powerquant_cli-0.2.6/src/quant_cli/app/data/schema/parquet_file_schema.py +17 -0
  304. powerquant_cli-0.2.6/src/quant_cli/app/data/service/__init__.py +0 -0
  305. powerquant_cli-0.2.6/src/quant_cli/app/data/service/bar_cache_service.py +287 -0
  306. powerquant_cli-0.2.6/src/quant_cli/app/data/service/dataset_audit_service.py +137 -0
  307. powerquant_cli-0.2.6/src/quant_cli/app/data/service/dataset_load_service.py +115 -0
  308. powerquant_cli-0.2.6/src/quant_cli/app/data/service/dataset_prepare_service.py +187 -0
  309. powerquant_cli-0.2.6/src/quant_cli/app/factor/__init__.py +0 -0
  310. powerquant_cli-0.2.6/src/quant_cli/app/factor/cli/__init__.py +0 -0
  311. powerquant_cli-0.2.6/src/quant_cli/app/factor/cli/factor_definition_cli.py +81 -0
  312. powerquant_cli-0.2.6/src/quant_cli/app/factor/cli/local_factor_cli.py +102 -0
  313. powerquant_cli-0.2.6/src/quant_cli/app/factor/cli/local_selection_cli.py +58 -0
  314. powerquant_cli-0.2.6/src/quant_cli/app/factor/interface/__init__.py +0 -0
  315. powerquant_cli-0.2.6/src/quant_cli/app/factor/interface/local_selection_store.py +12 -0
  316. powerquant_cli-0.2.6/src/quant_cli/app/factor/local_factor_dependency.py +38 -0
  317. powerquant_cli-0.2.6/src/quant_cli/app/factor/repository/__init__.py +0 -0
  318. powerquant_cli-0.2.6/src/quant_cli/app/factor/repository/factor_local_repository.py +154 -0
  319. powerquant_cli-0.2.6/src/quant_cli/app/factor/repository/selection_local_repository.py +67 -0
  320. powerquant_cli-0.2.6/src/quant_cli/app/factor/schema/local_selection_schema.py +56 -0
  321. powerquant_cli-0.2.6/src/quant_cli/app/factor/service/__init__.py +0 -0
  322. powerquant_cli-0.2.6/src/quant_cli/app/factor/service/factor_data_prepare_service.py +181 -0
  323. powerquant_cli-0.2.6/src/quant_cli/app/factor/service/local_factor_query_service.py +146 -0
  324. powerquant_cli-0.2.6/src/quant_cli/app/factor/service/local_factor_service.py +120 -0
  325. powerquant_cli-0.2.6/src/quant_cli/app/factor/service/local_selection_service.py +139 -0
  326. powerquant_cli-0.2.6/src/quant_cli/app/job/__init__.py +0 -0
  327. powerquant_cli-0.2.6/src/quant_cli/app/job/cli/__init__.py +0 -0
  328. powerquant_cli-0.2.6/src/quant_cli/app/job/cli/local_job_cli.py +69 -0
  329. powerquant_cli-0.2.6/src/quant_cli/app/job/schema/__init__.py +0 -0
  330. powerquant_cli-0.2.6/src/quant_cli/app/job/schema/local_job_schema.py +11 -0
  331. powerquant_cli-0.2.6/src/quant_cli/app/job/service/__init__.py +0 -0
  332. powerquant_cli-0.2.6/src/quant_cli/app/job/service/local_job_service.py +60 -0
  333. powerquant_cli-0.2.6/src/quant_cli/app/quant_cli.py +49 -0
  334. powerquant_cli-0.2.6/src/quant_cli/app/research/__init__.py +0 -0
  335. powerquant_cli-0.2.6/src/quant_cli/app/research/cli/__init__.py +0 -0
  336. powerquant_cli-0.2.6/src/quant_cli/app/research/cli/local_research_cli.py +93 -0
  337. powerquant_cli-0.2.6/src/quant_cli/app/research/local_research_dependency.py +10 -0
  338. powerquant_cli-0.2.6/src/quant_cli/app/research/repository/__init__.py +0 -0
  339. powerquant_cli-0.2.6/src/quant_cli/app/research/repository/research_local_repository.py +67 -0
  340. powerquant_cli-0.2.6/src/quant_cli/app/research/schema/__init__.py +0 -0
  341. powerquant_cli-0.2.6/src/quant_cli/app/research/schema/local_research_schema.py +26 -0
  342. powerquant_cli-0.2.6/src/quant_cli/app/research/service/__init__.py +0 -0
  343. powerquant_cli-0.2.6/src/quant_cli/app/research/service/local_research_execution_service.py +105 -0
  344. powerquant_cli-0.2.6/src/quant_cli/app/research/service/local_research_service.py +101 -0
  345. powerquant_cli-0.2.6/src/quant_cli/app/strategy/__init__.py +0 -0
  346. powerquant_cli-0.2.6/src/quant_cli/app/strategy/cli/__init__.py +0 -0
  347. powerquant_cli-0.2.6/src/quant_cli/app/strategy/cli/strategy_cli.py +104 -0
  348. powerquant_cli-0.2.6/src/quant_cli/common/__init__.py +0 -0
  349. powerquant_cli-0.2.6/src/quant_cli/common/constants.py +7 -0
  350. powerquant_cli-0.2.6/src/quant_cli/common/database/__init__.py +0 -0
  351. powerquant_cli-0.2.6/src/quant_cli/common/database/duckdb_connection.py +30 -0
  352. powerquant_cli-0.2.6/src/quant_cli/common/http/__init__.py +0 -0
  353. powerquant_cli-0.2.6/src/quant_cli/common/http/definition_client.py +54 -0
  354. powerquant_cli-0.2.6/src/quant_cli/common/utils/__init__.py +0 -0
  355. powerquant_cli-0.2.6/src/quant_cli/common/utils/cli_utils.py +37 -0
  356. powerquant_cli-0.2.6/src/quant_cli/common/utils/platform_utils.py +11 -0
  357. powerquant_cli-0.2.6/src/quant_cli/common/utils/runtime_environment.py +12 -0
  358. powerquant_cli-0.2.6/tests/__init__.py +0 -0
  359. powerquant_cli-0.2.6/tests/conftest.py +10 -0
  360. powerquant_cli-0.2.6/tests/factor_fixtures.py +51 -0
  361. powerquant_cli-0.2.6/tests/integration/__init__.py +0 -0
  362. powerquant_cli-0.2.6/tests/integration/test_local_quant_storage.py +176 -0
  363. powerquant_cli-0.2.6/tests/integration/test_local_selection.py +234 -0
  364. powerquant_cli-0.2.6/tests/unit/__init__.py +1 -0
  365. powerquant_cli-0.2.6/tests/unit/test_alpha_backtest_reports.py +59 -0
  366. powerquant_cli-0.2.6/tests/unit/test_alpha_identity.py +47 -0
  367. powerquant_cli-0.2.6/tests/unit/test_alpha_market_input.py +65 -0
  368. powerquant_cli-0.2.6/tests/unit/test_alpha_research.py +226 -0
  369. powerquant_cli-0.2.6/tests/unit/test_alpha_top_n.py +42 -0
  370. powerquant_cli-0.2.6/tests/unit/test_backtest_run_result.py +1160 -0
  371. powerquant_cli-0.2.6/tests/unit/test_bar_cache.py +203 -0
  372. powerquant_cli-0.2.6/tests/unit/test_coarse_execution_capability.py +409 -0
  373. powerquant_cli-0.2.6/tests/unit/test_corporate_action_replay.py +62 -0
  374. powerquant_cli-0.2.6/tests/unit/test_d1_replay_execution_chain.py +244 -0
  375. powerquant_cli-0.2.6/tests/unit/test_daily_state_expand.py +313 -0
  376. powerquant_cli-0.2.6/tests/unit/test_daily_state_snapshot.py +220 -0
  377. powerquant_cli-0.2.6/tests/unit/test_daily_vwap_execution.py +57 -0
  378. powerquant_cli-0.2.6/tests/unit/test_data_guard.py +72 -0
  379. powerquant_cli-0.2.6/tests/unit/test_dataset_audit.py +246 -0
  380. powerquant_cli-0.2.6/tests/unit/test_domain_dependency_boundaries.py +39 -0
  381. powerquant_cli-0.2.6/tests/unit/test_domain_interface_boundary.py +66 -0
  382. powerquant_cli-0.2.6/tests/unit/test_exchange_state_execution.py +211 -0
  383. powerquant_cli-0.2.6/tests/unit/test_exchange_state_replay.py +413 -0
  384. powerquant_cli-0.2.6/tests/unit/test_exchange_state_resolver.py +144 -0
  385. powerquant_cli-0.2.6/tests/unit/test_exchange_state_schema.py +151 -0
  386. powerquant_cli-0.2.6/tests/unit/test_factor_contract.py +449 -0
  387. powerquant_cli-0.2.6/tests/unit/test_factor_diagnostic_runner.py +111 -0
  388. powerquant_cli-0.2.6/tests/unit/test_factor_diagnostics.py +30 -0
  389. powerquant_cli-0.2.6/tests/unit/test_factor_expression.py +107 -0
  390. powerquant_cli-0.2.6/tests/unit/test_factor_local_runner.py +45 -0
  391. powerquant_cli-0.2.6/tests/unit/test_factor_runtime_service.py +498 -0
  392. powerquant_cli-0.2.6/tests/unit/test_factor_selection.py +177 -0
  393. powerquant_cli-0.2.6/tests/unit/test_factor_selection_runner.py +219 -0
  394. powerquant_cli-0.2.6/tests/unit/test_ipo_vwap_study.py +149 -0
  395. powerquant_cli-0.2.6/tests/unit/test_local_quant_boundary.py +133 -0
  396. powerquant_cli-0.2.6/tests/unit/test_market_replay.py +586 -0
  397. powerquant_cli-0.2.6/tests/unit/test_market_trading_state.py +773 -0
  398. powerquant_cli-0.2.6/tests/unit/test_order_domain.py +140 -0
  399. powerquant_cli-0.2.6/tests/unit/test_order_submission.py +185 -0
  400. powerquant_cli-0.2.6/tests/unit/test_portfolio_construction.py +83 -0
  401. powerquant_cli-0.2.6/tests/unit/test_portfolio_contracts.py +325 -0
  402. powerquant_cli-0.2.6/tests/unit/test_python_strategy_runtime.py +80 -0
  403. powerquant_cli-0.2.6/tests/unit/test_replay_state_boundaries.py +592 -0
  404. powerquant_cli-0.2.6/tests/unit/test_risk_adjusted_strategy_example.py +100 -0
  405. powerquant_cli-0.2.6/tests/unit/test_simulated_order_execution.py +1603 -0
  406. powerquant_cli-0.2.6/tests/unit/test_simulated_portfolio.py +961 -0
  407. powerquant_cli-0.2.6/tests/unit/test_store_factor_reader.py +209 -0
  408. powerquant_cli-0.2.6/tests/unit/test_strategy_adjusted_view.py +122 -0
  409. powerquant_cli-0.2.6/tests/unit/test_strategy_domain.py +946 -0
  410. powerquant_cli-0.2.6/tests/unit/test_strategy_lifecycle.py +98 -0
  411. powerquant_cli-0.2.6/tests/unit/test_tradability_policy.py +539 -0
  412. powerquant_cli-0.2.6/tests/unit/test_trading_runtime_contracts.py +262 -0
  413. powerquant_cli-0.2.6/tests/unit/test_trading_runtime_session.py +294 -0
  414. powerquant_cli-0.2.6/uv.lock +793 -0
@@ -0,0 +1,7 @@
1
+ # CLI uses HTTP endpoints and local workspace; no database credentials.
2
+ PQ_QUANT_URL=http://localhost:8001/api/v1
3
+ PQ_QUANT_TOKEN=
4
+ # 默认 ~/.powerquant/workspace.duckdb;需要覆盖时设置 PQ_LOCAL_DATABASE
5
+ # PQ_LOCAL_DATABASE=/path/to/workspace.duckdb
6
+ MARKET_BASE_URL=http://localhost:8002/api/v1
7
+ MARKET_API_TOKEN=
@@ -0,0 +1,22 @@
1
+ # 运行时日志与文件锁(应用启动时自动创建 logs/ 目录)
2
+ logs/
3
+
4
+ # 本地环境配置(含数据库连接串、密钥等),只提交 .env.example
5
+ .env
6
+ .env.*
7
+ !.env.example
8
+
9
+ # Python 运行时产物
10
+ __pycache__/
11
+ *.py[cod]
12
+ .pytest_cache/
13
+ .ruff_cache/
14
+ .cache/
15
+ .venv/
16
+ dist/
17
+ build/
18
+ tasks/draft/TASK-20260906-1704-akshare-minute-ingestion/samples/
19
+ tasks/approved/TASK-20260906-2119-real-market-integration-review-fixes/samples/
20
+
21
+ node_modules/
22
+ apps/quant_frontend/public/data-dictionary.md
@@ -0,0 +1,36 @@
1
+ Power Quant Proprietary License
2
+ Copyright (c) 2026 Power Quant contributors. All rights reserved.
3
+
4
+ 1. Permission to use
5
+ You may install and use this software, inspect its source code, and make
6
+ local copies and modifications for your own use. Permitted uses include
7
+ connecting to Power Quant services and performing research and backtesting.
8
+ Commercial use of the software is permitted under these same conditions.
9
+
10
+ 2. Redistribution and resale
11
+ Unless you have prior written authorization from the copyright holders,
12
+ you may not redistribute, sublicense, publish, or resell this software or
13
+ its modified versions, including as part of another software product.
14
+ The copyright holders may distribute the software through their designated
15
+ release channels, including PyPI. This does not authorize recipients to
16
+ redistribute it.
17
+
18
+ 3. Service access and results
19
+ This license does not grant access to Power Quant services or market data.
20
+ Service access requires a valid access token and remains subject to the
21
+ applicable service and data terms. These redistribution restrictions apply
22
+ to the software, not to your own research code or results; rights in market
23
+ data remain governed by the applicable data terms.
24
+
25
+ 4. Third-party components
26
+ Third-party components retain their own licenses and copyright notices.
27
+ The restrictions above do not override rights granted by those licenses.
28
+ In particular, components identified as derived from vn.py remain subject
29
+ to the accompanying MIT license. Separately installed dependencies are
30
+ subject to their own licenses.
31
+
32
+ 5. Disclaimer
33
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
34
+ OR IMPLIED, INCLUDING MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
35
+ AND NON-INFRINGEMENT. TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE
36
+ COPYRIGHT HOLDERS ARE NOT LIABLE FOR DAMAGES ARISING FROM ITS USE.
@@ -0,0 +1,62 @@
1
+ Metadata-Version: 2.5
2
+ Name: powerquant-cli
3
+ Version: 0.2.6
4
+ Summary: Power Quant local research CLI
5
+ Project-URL: Repository, https://github.com/Kepler452Byte/power-quant
6
+ License-Expression: LicenseRef-PowerQuant-Proprietary AND MIT
7
+ License-File: LICENSE
8
+ License-File: licenses/LICENSE.vnpy
9
+ Requires-Python: >=3.12
10
+ Requires-Dist: duckdb<2,>=1.4
11
+ Requires-Dist: filelock<4,>=3.20
12
+ Requires-Dist: httpx<1,>=0.28
13
+ Requires-Dist: numpy<3,>=2
14
+ Requires-Dist: pandas<4,>=3
15
+ Requires-Dist: polars<2,>=1.36
16
+ Requires-Dist: pydantic<3,>=2.5
17
+ Requires-Dist: scipy<2,>=1.17
18
+ Requires-Dist: typer>=0.27.2
19
+ Requires-Dist: tzdata>=2026.3
20
+ Description-Content-Type: text/markdown
21
+
22
+ # Power Quant CLI
23
+
24
+ 本地行情准备、因子计算、选股、研究及策略回测;结果保存在本地 DuckDB 和文件。
25
+
26
+ ## 安装
27
+
28
+ 推荐通过 uv 安装独立工具环境:
29
+
30
+ ```sh
31
+ uv tool install --python 3.12 powerquant-cli
32
+ pq --help
33
+ uv tool upgrade powerquant-cli
34
+ ```
35
+
36
+ 也支持在 Python 3.12+ 环境通过 `pip install powerquant-cli` 安装。
37
+
38
+ ## 接入行情
39
+
40
+ 向管理员获取行情读取密钥,无需注册账户。服务当前仅在 LAN/WireGuard 内可达。
41
+
42
+ ```sh
43
+ export MARKET_BASE_URL='http://服务器地址/market/api/v1'
44
+ export MARKET_API_TOKEN='管理员提供的行情密钥'
45
+ pq examples --directory powerquant-examples
46
+ cd powerquant-examples/selection
47
+ pq data prepare --config prepare.json
48
+ ```
49
+
50
+ PowerShell 使用 `$env:MARKET_BASE_URL="..."` 与 `$env:MARKET_API_TOKEN="..."`。
51
+
52
+ 按实际覆盖调整 prepare.json 的股票和日期,将返回 manifest_path 写入 compute.json、select.json;按示例 README 执行 compute、select 和 backtest。不同使用目的的快照须分别通过完整性检查。
53
+
54
+ 密钥到期或撤销后在线准备会失败,已下载快照仍可离线使用。行情密钥不用于定义发布;发布定义另使用 PQ_QUANT_URL/PQ_QUANT_TOKEN。默认本地目录为 ~/.powerquant。
55
+
56
+ ## Python
57
+
58
+ 公开请求模型与调用入口位于 quant_cli.api,与 CLI 使用同一应用服务。
59
+
60
+ ## 使用许可
61
+
62
+ 自有代码采用随包附带的专有许可证:允许安装、查看源码、为自用进行修改,以及商业使用;未经书面授权不得再分发或转售本软件及其修改版本。第三方组件保留各自许可证。行情访问仍需要有效 Token;本许可证不授予行情数据的使用或再分发权。
@@ -0,0 +1,41 @@
1
+ # Power Quant CLI
2
+
3
+ 本地行情准备、因子计算、选股、研究及策略回测;结果保存在本地 DuckDB 和文件。
4
+
5
+ ## 安装
6
+
7
+ 推荐通过 uv 安装独立工具环境:
8
+
9
+ ```sh
10
+ uv tool install --python 3.12 powerquant-cli
11
+ pq --help
12
+ uv tool upgrade powerquant-cli
13
+ ```
14
+
15
+ 也支持在 Python 3.12+ 环境通过 `pip install powerquant-cli` 安装。
16
+
17
+ ## 接入行情
18
+
19
+ 向管理员获取行情读取密钥,无需注册账户。服务当前仅在 LAN/WireGuard 内可达。
20
+
21
+ ```sh
22
+ export MARKET_BASE_URL='http://服务器地址/market/api/v1'
23
+ export MARKET_API_TOKEN='管理员提供的行情密钥'
24
+ pq examples --directory powerquant-examples
25
+ cd powerquant-examples/selection
26
+ pq data prepare --config prepare.json
27
+ ```
28
+
29
+ PowerShell 使用 `$env:MARKET_BASE_URL="..."` 与 `$env:MARKET_API_TOKEN="..."`。
30
+
31
+ 按实际覆盖调整 prepare.json 的股票和日期,将返回 manifest_path 写入 compute.json、select.json;按示例 README 执行 compute、select 和 backtest。不同使用目的的快照须分别通过完整性检查。
32
+
33
+ 密钥到期或撤销后在线准备会失败,已下载快照仍可离线使用。行情密钥不用于定义发布;发布定义另使用 PQ_QUANT_URL/PQ_QUANT_TOKEN。默认本地目录为 ~/.powerquant。
34
+
35
+ ## Python
36
+
37
+ 公开请求模型与调用入口位于 quant_cli.api,与 CLI 使用同一应用服务。
38
+
39
+ ## 使用许可
40
+
41
+ 自有代码采用随包附带的专有许可证:允许安装、查看源码、为自用进行修改,以及商业使用;未经书面授权不得再分发或转售本软件及其修改版本。第三方组件保留各自许可证。行情访问仍需要有效 Token;本许可证不授予行情数据的使用或再分发权。
@@ -0,0 +1,6 @@
1
+ """Power Quant 行情服务的独立 Python 客户端。"""
2
+
3
+ from pqdata.client import MarketClient
4
+ from pqdata.exceptions import MarketAPIError, MarketProtocolError
5
+
6
+ __all__ = ["MarketAPIError", "MarketClient", "MarketProtocolError"]
@@ -0,0 +1,389 @@
1
+ """有界、强类型行情客户端;不引入 ORM、供应商 SDK 或应用配置。"""
2
+
3
+ import asyncio
4
+ from collections.abc import AsyncIterator
5
+ from datetime import date, datetime
6
+ from hashlib import sha256
7
+ from types import TracebackType
8
+ from typing import Any, Self
9
+ from urllib.parse import quote
10
+
11
+ import httpx
12
+ from pydantic import TypeAdapter, ValidationError
13
+
14
+ from pqdata.exceptions import MarketAPIError, MarketProtocolError
15
+ from pqdata.schema.adjustment_schema import AdjustedBarSeries, BarViewRequest
16
+ from pqdata.schema.bar_content_schema import BarContent
17
+ from pqdata.schema.bar_schema import BarFrequency, BarPage, BarQuery, BarResponse
18
+ from pqdata.schema.history_schema import HistoryFacts, HistoryFactsQuery
19
+ from pqdata.schema.pit_daily_window import PITDailyWindow
20
+ from pqdata.schema.query_schema import (
21
+ BarWindowQuery,
22
+ DataHealthQuery,
23
+ InstrumentRangeQuery,
24
+ PITWindowQuery,
25
+ )
26
+ from pqdata.schema.quote_schema import DailyBasicResponse
27
+ from pqdata.schema.reference_schema import (
28
+ InstrumentResponse,
29
+ TradingCalendarDayResponse,
30
+ TradingRuleResponse,
31
+ )
32
+ from pqdata.schema.reliability_schema import DatasetHealthResult
33
+
34
+
35
+ class MarketClient:
36
+ """持有一个连接池;通过 async with 或 aclose 显式释放连接。"""
37
+
38
+ def __init__(
39
+ self,
40
+ base_url: str,
41
+ *,
42
+ token: str | None = None,
43
+ timeout: float = 60.0,
44
+ max_retries: int = 2,
45
+ transport: httpx.AsyncBaseTransport | None = None,
46
+ ) -> None:
47
+ if not 0 <= max_retries <= 5:
48
+ raise ValueError("max_retries must be between 0 and 5")
49
+ self._max_retries = max_retries
50
+ self._http = httpx.AsyncClient(
51
+ base_url=base_url.rstrip("/") + "/",
52
+ headers={"Authorization": f"Bearer {token}"} if token else {},
53
+ timeout=timeout,
54
+ transport=transport,
55
+ follow_redirects=False,
56
+ )
57
+
58
+ async def __aenter__(self) -> Self:
59
+ return self
60
+
61
+ async def __aexit__(
62
+ self,
63
+ exc_type: type[BaseException] | None,
64
+ exc: BaseException | None,
65
+ traceback: TracebackType | None,
66
+ ) -> None:
67
+ await self.aclose()
68
+
69
+ async def aclose(self) -> None:
70
+ await self._http.aclose()
71
+
72
+ @property
73
+ def source_id(self) -> str:
74
+ """服务地址的稳定摘要,用于隔离不同数据源的本地缓存。"""
75
+ return sha256(str(self._http.base_url).encode("utf-8")).hexdigest()
76
+
77
+ async def _request(
78
+ self,
79
+ method: str,
80
+ path: str,
81
+ response_type: Any,
82
+ *,
83
+ params: dict[str, Any] | None = None,
84
+ body: Any = None,
85
+ ) -> Any:
86
+ # 所有当前端点均为只读查询;未来写入端点不能复用此重试路径。
87
+ for attempt in range(self._max_retries + 1):
88
+ try:
89
+ response = await self._http.request(method, path, params=params, json=body)
90
+ except httpx.TransportError:
91
+ if attempt == self._max_retries:
92
+ raise
93
+ await asyncio.sleep(min(0.25 * 2**attempt, 2.0))
94
+ continue
95
+ if response.status_code not in {429, 502, 503, 504} or attempt == self._max_retries:
96
+ break
97
+ delay = min(0.25 * 2**attempt, 2.0)
98
+ retry_after = response.headers.get("retry-after", "")
99
+ if retry_after.isdecimal():
100
+ delay = min(max(delay, int(retry_after)), 5.0)
101
+ await response.aclose()
102
+ await asyncio.sleep(delay)
103
+ if not response.is_success:
104
+ raise MarketAPIError(response.status_code, response.headers.get("x-request-id"))
105
+ try:
106
+ return TypeAdapter(response_type).validate_json(response.content)
107
+ except ValidationError as error:
108
+ raise MarketProtocolError("Invalid market response") from error
109
+
110
+ async def get_instrument(self, instrument_id: str) -> InstrumentResponse:
111
+ return await self._request(
112
+ "GET",
113
+ f"instruments/{quote(instrument_id, safe='')}",
114
+ InstrumentResponse,
115
+ )
116
+
117
+ async def get_history_facts(self, query: HistoryFactsQuery) -> HistoryFacts:
118
+ result = await self._request(
119
+ "POST", "history/facts", HistoryFacts, body=query.model_dump(mode="json")
120
+ )
121
+ if query.revision is not None and result.revision != query.revision:
122
+ raise MarketProtocolError("History facts revision changed")
123
+ if result.instrument.instrument_id != query.instrument_id:
124
+ raise MarketProtocolError("History facts instrument differs from query")
125
+ return result
126
+
127
+ async def get_bars(
128
+ self,
129
+ instrument_id: str,
130
+ frequency: str,
131
+ start: datetime | None = None,
132
+ end: datetime | None = None,
133
+ limit: int = 1000,
134
+ ) -> list[BarResponse]:
135
+ query = BarWindowQuery(
136
+ instrument_id=instrument_id,
137
+ frequency=TypeAdapter(BarFrequency).validate_python(frequency),
138
+ start=start,
139
+ end=end,
140
+ limit=limit,
141
+ )
142
+ return await self._request(
143
+ "POST", "bars/window", list[BarResponse], body=query.model_dump(mode="json")
144
+ )
145
+
146
+ async def get_adjusted_bars(
147
+ self,
148
+ instrument_id: str,
149
+ frequency: str,
150
+ view: BarViewRequest,
151
+ start: datetime | None = None,
152
+ end: datetime | None = None,
153
+ limit: int = 1000,
154
+ ) -> AdjustedBarSeries:
155
+ query = BarWindowQuery(
156
+ instrument_id=instrument_id,
157
+ frequency=TypeAdapter(BarFrequency).validate_python(frequency),
158
+ start=start,
159
+ end=end,
160
+ limit=limit,
161
+ view=BarViewRequest.model_validate(view.model_dump(mode="json")),
162
+ )
163
+ return await self._request(
164
+ "POST", "bars/view", AdjustedBarSeries, body=query.model_dump(mode="json")
165
+ )
166
+
167
+ async def verify_bars(
168
+ self,
169
+ instrument_id: str,
170
+ frequency: str,
171
+ start: datetime,
172
+ end: datetime,
173
+ ) -> DatasetHealthResult:
174
+ query = DataHealthQuery(
175
+ instrument_id=instrument_id,
176
+ frequency=TypeAdapter(BarFrequency).validate_python(frequency),
177
+ start=start,
178
+ end=end,
179
+ )
180
+ return await self._request(
181
+ "POST", "verify/bars", DatasetHealthResult, body=query.model_dump(mode="json")
182
+ )
183
+
184
+ async def verify_adjustment_factors(
185
+ self,
186
+ instrument_id: str,
187
+ frequency: str,
188
+ start: datetime,
189
+ end: datetime,
190
+ ) -> DatasetHealthResult:
191
+ query = DataHealthQuery(
192
+ instrument_id=instrument_id,
193
+ frequency=TypeAdapter(BarFrequency).validate_python(frequency),
194
+ start=start,
195
+ end=end,
196
+ )
197
+ return await self._request(
198
+ "POST",
199
+ "verify/adjustment-factors",
200
+ DatasetHealthResult,
201
+ body=query.model_dump(mode="json"),
202
+ )
203
+
204
+ async def verify_corporate_actions(
205
+ self,
206
+ instrument_id: str,
207
+ start: datetime,
208
+ end: datetime,
209
+ ) -> DatasetHealthResult:
210
+ query = DataHealthQuery(instrument_id=instrument_id, start=start, end=end)
211
+ return await self._request(
212
+ "POST",
213
+ "verify/corporate-actions",
214
+ DatasetHealthResult,
215
+ body=query.model_dump(mode="json"),
216
+ )
217
+
218
+ async def snapshot(self, start: date, end: date) -> dict[str, str]:
219
+ return await self._request(
220
+ "GET",
221
+ "revisions",
222
+ dict[str, str],
223
+ params={
224
+ "start": start.isoformat(),
225
+ "end": end.isoformat(),
226
+ },
227
+ )
228
+
229
+ async def search_instruments(
230
+ self,
231
+ keyword: str | None = None,
232
+ instrument_type: str | None = None,
233
+ exchange: str | None = None,
234
+ limit: int = 100,
235
+ ) -> list[InstrumentResponse]:
236
+ params = {
237
+ key: value
238
+ for key, value in {
239
+ "keyword": keyword,
240
+ "instrument_type": instrument_type,
241
+ "exchange": exchange,
242
+ "limit": limit,
243
+ }.items()
244
+ if value is not None
245
+ }
246
+ return await self._request("GET", "instruments", list[InstrumentResponse], params=params)
247
+
248
+ async def list_instruments_on(
249
+ self, on_date: date, limit: int = 10000
250
+ ) -> list[InstrumentResponse]:
251
+ return await self._request(
252
+ "GET",
253
+ "universe",
254
+ list[InstrumentResponse],
255
+ params={
256
+ "on_date": on_date.isoformat(),
257
+ "limit": limit,
258
+ },
259
+ )
260
+
261
+ async def list_trading_rules(
262
+ self,
263
+ instrument_ids: list[str],
264
+ start: date,
265
+ end: date,
266
+ ) -> list[TradingRuleResponse]:
267
+ query = InstrumentRangeQuery(instrument_ids=instrument_ids, start=start, end=end)
268
+ return await self._request(
269
+ "POST",
270
+ "trading-rules/query",
271
+ list[TradingRuleResponse],
272
+ body=query.model_dump(mode="json"),
273
+ )
274
+
275
+ async def list_trading_days(
276
+ self,
277
+ exchange: str,
278
+ start: date,
279
+ end: date,
280
+ ) -> list[TradingCalendarDayResponse]:
281
+ return await self._request(
282
+ "GET",
283
+ "calendar",
284
+ list[TradingCalendarDayResponse],
285
+ params={
286
+ "exchange": exchange,
287
+ "start": start.isoformat(),
288
+ "end": end.isoformat(),
289
+ },
290
+ )
291
+
292
+ async def is_trading_day(self, exchange: str, trade_date: date) -> bool:
293
+ return await self._request(
294
+ "GET",
295
+ "calendar/is-open",
296
+ bool,
297
+ params={
298
+ "exchange": exchange,
299
+ "trade_date": trade_date.isoformat(),
300
+ },
301
+ )
302
+
303
+ async def list_daily_basic_at(
304
+ self,
305
+ decision_time: datetime,
306
+ limit: int = 10000,
307
+ ) -> list[DailyBasicResponse]:
308
+ return await self._request(
309
+ "GET",
310
+ "daily-basic/visible",
311
+ list[DailyBasicResponse],
312
+ params={
313
+ "decision_time": decision_time.isoformat(),
314
+ "limit": limit,
315
+ },
316
+ )
317
+
318
+ async def get_pit_daily_windows(
319
+ self,
320
+ instrument_id: str,
321
+ days: list[date],
322
+ next_days: list[date],
323
+ lookback: int,
324
+ ) -> tuple[PITDailyWindow, ...]:
325
+ query = PITWindowQuery(
326
+ instrument_id=instrument_id,
327
+ days=days,
328
+ next_days=next_days,
329
+ lookback=lookback,
330
+ )
331
+ return await self._request(
332
+ "POST",
333
+ "bars/pit-windows",
334
+ tuple[PITDailyWindow, ...],
335
+ body=query.model_dump(mode="json"),
336
+ )
337
+
338
+ async def query_bars(self, query: BarQuery) -> BarPage:
339
+ """读取一页数据;错误内容不混入行情结果。"""
340
+ page = await self._request(
341
+ "POST", "bars/query", BarPage, body=query.model_dump(mode="json")
342
+ )
343
+ if query.revision is not None and page.revision != query.revision:
344
+ raise MarketProtocolError("Market revision changed during pagination")
345
+ if len(page.items) > query.page_size:
346
+ raise MarketProtocolError("Server exceeded requested page size")
347
+ previous: tuple | None = None
348
+ for bar in page.items:
349
+ key = (bar.timestamp, bar.instrument_id)
350
+ if (
351
+ bar.instrument_id not in query.instrument_ids
352
+ or bar.frequency != query.frequency
353
+ or not query.start <= bar.timestamp <= query.end
354
+ or (previous is not None and key <= previous)
355
+ ):
356
+ raise MarketProtocolError("Bar page violates query range or ordering")
357
+ previous = key
358
+ return page
359
+
360
+ async def describe_bars(self, query: BarQuery) -> BarContent:
361
+ """只传输范围摘要;服务端仍需扫描数据,不能视为常数成本的查询。"""
362
+ if query.cursor is not None:
363
+ raise ValueError("Content queries must start at the first page")
364
+ result = await self._request(
365
+ "POST", "bars/content", BarContent, body=query.model_dump(mode="json")
366
+ )
367
+ if query.revision is not None and result.revision != query.revision:
368
+ raise MarketProtocolError("Content revision differs from query")
369
+ return result
370
+
371
+ async def iter_bar_pages(self, query: BarQuery) -> AsyncIterator[BarPage]:
372
+ """逐页消费,固定 revision,检测重复游标与跨页重复数据。"""
373
+ seen: set[str] = {query.cursor} if query.cursor else set()
374
+ previous: tuple | None = None
375
+ while True:
376
+ page = await self.query_bars(query)
377
+ if previous is not None and page.items:
378
+ first = page.items[0]
379
+ if (first.timestamp, first.instrument_id) <= previous:
380
+ raise MarketProtocolError("Bar pages overlap or are out of order")
381
+ if page.next_cursor and (page.next_cursor in seen or not page.items):
382
+ raise MarketProtocolError("Pagination did not advance")
383
+ yield page
384
+ if page.next_cursor is None:
385
+ return
386
+ last = page.items[-1]
387
+ previous = (last.timestamp, last.instrument_id)
388
+ seen.add(page.next_cursor)
389
+ query = query.model_copy(update={"cursor": page.next_cursor, "revision": page.revision})
@@ -0,0 +1,60 @@
1
+ """流式落盘;完整下载后才替换目标文件,失败保留原文件。"""
2
+
3
+ import hashlib
4
+ import os
5
+ import tempfile
6
+ from pathlib import Path
7
+
8
+ from pydantic import BaseModel, Field
9
+
10
+ from pqdata.client import MarketClient
11
+ from pqdata.schema.bar_schema import BarQuery
12
+
13
+
14
+ class DownloadReceipt(BaseModel):
15
+ """供本地缓存记录查询条件、版本及内容校验和。"""
16
+
17
+ query: BarQuery
18
+ revision: str
19
+ row_count: int = Field(ge=0)
20
+ byte_count: int = Field(ge=0)
21
+ sha256: str = Field(pattern=r"^[a-f0-9]{64}$")
22
+
23
+
24
+ async def download_bars(
25
+ client: MarketClient, query: BarQuery, destination: Path
26
+ ) -> DownloadReceipt:
27
+ """完整范围以 JSON Lines 落盘;内存上限为一页数据。"""
28
+ if query.cursor is not None:
29
+ raise ValueError("Downloads must start at the first page")
30
+ destination.parent.mkdir(parents=True, exist_ok=True)
31
+ temporary: Path | None = None
32
+ digest = hashlib.sha256()
33
+ row_count = byte_count = 0
34
+ revision = ""
35
+ try:
36
+ with tempfile.NamedTemporaryFile(
37
+ mode="wb", dir=destination.parent, prefix=".market-", suffix=".part", delete=False
38
+ ) as output:
39
+ temporary = Path(output.name)
40
+ async for page in client.iter_bar_pages(query):
41
+ revision = page.revision
42
+ for bar in page.items:
43
+ line = (bar.model_dump_json() + "\n").encode("utf-8")
44
+ output.write(line)
45
+ digest.update(line)
46
+ row_count += 1
47
+ byte_count += len(line)
48
+ output.flush()
49
+ os.fsync(output.fileno())
50
+ os.replace(temporary, destination)
51
+ finally:
52
+ if temporary is not None:
53
+ temporary.unlink(missing_ok=True)
54
+ return DownloadReceipt(
55
+ query=query,
56
+ revision=revision,
57
+ row_count=row_count,
58
+ byte_count=byte_count,
59
+ sha256=digest.hexdigest(),
60
+ )
@@ -0,0 +1,14 @@
1
+ """客户端错误不依赖服务端异常实现。"""
2
+
3
+
4
+ class MarketAPIError(Exception):
5
+ """服务拒绝请求;保留状态码和可追踪的请求 ID,不暴露凭据。"""
6
+
7
+ def __init__(self, status_code: int, request_id: str | None = None) -> None:
8
+ self.status_code = status_code
9
+ self.request_id = request_id
10
+ super().__init__(f"Market request failed (HTTP {status_code}, request_id={request_id})")
11
+
12
+
13
+ class MarketProtocolError(Exception):
14
+ """响应不符合契约或下载内容不一致,禁止继续消费。"""
@@ -0,0 +1 @@
1
+ """客户端公开数据契约。"""