tmdx 3.0.0__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 (554) hide show
  1. tmdx-3.0.0/.gitattributes +6 -0
  2. tmdx-3.0.0/.github/workflows/ci.yml +72 -0
  3. tmdx-3.0.0/.github/workflows/publish.yml +35 -0
  4. tmdx-3.0.0/.github/workflows/release.yml +94 -0
  5. tmdx-3.0.0/.gitignore +47 -0
  6. tmdx-3.0.0/.readthedocs.yaml +18 -0
  7. tmdx-3.0.0/API_QUICKSTART.md +254 -0
  8. tmdx-3.0.0/CHANGELOG.md +1545 -0
  9. tmdx-3.0.0/Dockerfile +16 -0
  10. tmdx-3.0.0/LICENSE +34 -0
  11. tmdx-3.0.0/NOTICE +24 -0
  12. tmdx-3.0.0/PKG-INFO +1917 -0
  13. tmdx-3.0.0/README.md +1867 -0
  14. tmdx-3.0.0/RELEASING.md +408 -0
  15. tmdx-3.0.0/_analyze.py +29 -0
  16. tmdx-3.0.0/docker-compose.yml +35 -0
  17. tmdx-3.0.0/docs/api_reference.md +704 -0
  18. tmdx-3.0.0/docs/architecture.html +682 -0
  19. tmdx-3.0.0/docs/backtest_usage.md +680 -0
  20. tmdx-3.0.0/docs/conf.py +41 -0
  21. tmdx-3.0.0/docs/field_mapping.md +315 -0
  22. tmdx-3.0.0/docs/free-a-share-quote-sdk-survey.md +275 -0
  23. tmdx-3.0.0/docs/index.md +16 -0
  24. tmdx-3.0.0/docs/indicator-bias-signal.md +167 -0
  25. tmdx-3.0.0/docs/indicator-zhuoyao.md +84 -0
  26. tmdx-3.0.0/docs/packaging.md +123 -0
  27. tmdx-3.0.0/docs/protocol-reverse-engineering.md +144 -0
  28. tmdx-3.0.0/docs/protocol-unknown-fields.md +437 -0
  29. tmdx-3.0.0/docs/pullup-detection-criteria.md +323 -0
  30. tmdx-3.0.0/docs/pullup-detection.md +142 -0
  31. tmdx-3.0.0/docs/pullup-open-volume-redesign.md +225 -0
  32. tmdx-3.0.0/docs/quantitative-guide.md +628 -0
  33. tmdx-3.0.0/docs/readme.md +5 -0
  34. tmdx-3.0.0/docs/report-snapshot-threshold-usage.md +51 -0
  35. tmdx-3.0.0/docs/requirements.txt +4 -0
  36. tmdx-3.0.0/docs/superpowers/plans/2026-06-09-backtest-engine.md +2361 -0
  37. tmdx-3.0.0/docs/superpowers/plans/2026-06-12-v1.11.0-factor-engine.md +1790 -0
  38. tmdx-3.0.0/docs/superpowers/plans/2026-06-12-v1.12.0-factor-analysis.md +877 -0
  39. tmdx-3.0.0/docs/superpowers/plans/2026-06-12-v1.13.0-portfolio.md +956 -0
  40. tmdx-3.0.0/docs/superpowers/plans/2026-06-12-v1.14.0-slippage-execution.md +1724 -0
  41. tmdx-3.0.0/docs/superpowers/plans/2026-06-12-v1.15.0-attribution.md +540 -0
  42. tmdx-3.0.0/docs/superpowers/specs/2026-06-09-backtest-engine-design.md +549 -0
  43. tmdx-3.0.0/docs/superpowers/specs/2026-06-12-advanced-backtest-design.md +355 -0
  44. tmdx-3.0.0/docs/superpowers/specs/2026-06-12-quantitative-factor-engine-design.md +437 -0
  45. tmdx-3.0.0/docs/tdx-quant-replacement-analysis.md +159 -0
  46. tmdx-3.0.0/docs/upgrade-plan-2026H2.md +135 -0
  47. tmdx-3.0.0/docs//345/233/236/346/265/213/347/263/273/347/273/237/345/256/214/345/205/250/344/270/212/346/211/213/346/211/213/345/206/214.html +1568 -0
  48. tmdx-3.0.0/easy_tdx.spec +114 -0
  49. tmdx-3.0.0/examples/01_connection/async_connect.py +67 -0
  50. tmdx-3.0.0/examples/01_connection/connect_best_host.py +38 -0
  51. tmdx-3.0.0/examples/01_connection/ping_servers.py +51 -0
  52. tmdx-3.0.0/examples/02_market_info/market_stat.py +38 -0
  53. tmdx-3.0.0/examples/02_market_info/security_count.py +30 -0
  54. tmdx-3.0.0/examples/02_market_info/security_list.py +124 -0
  55. tmdx-3.0.0/examples/02_market_info/security_list_all.py +148 -0
  56. tmdx-3.0.0/examples/02_market_info/security_quotes.py +66 -0
  57. tmdx-3.0.0/examples/03_kline/index_bars.py +60 -0
  58. tmdx-3.0.0/examples/03_kline/security_bars.py +85 -0
  59. tmdx-3.0.0/examples/04_minute/history_minute_data.py +47 -0
  60. tmdx-3.0.0/examples/04_minute/minute_time_data.py +46 -0
  61. tmdx-3.0.0/examples/05_transaction/history_transaction.py +50 -0
  62. tmdx-3.0.0/examples/05_transaction/transaction_data.py +49 -0
  63. tmdx-3.0.0/examples/06_finance/README.md +135 -0
  64. tmdx-3.0.0/examples/06_finance/company_cli.sh +69 -0
  65. tmdx-3.0.0/examples/06_finance/company_info.py +103 -0
  66. tmdx-3.0.0/examples/06_finance/company_web_api.py +134 -0
  67. tmdx-3.0.0/examples/06_finance/finance_info.py +97 -0
  68. tmdx-3.0.0/examples/06_finance/price_limits.py +38 -0
  69. tmdx-3.0.0/examples/06_finance/xdxr_info.py +58 -0
  70. tmdx-3.0.0/examples/07_block/block_info.py +52 -0
  71. tmdx-3.0.0/examples/08_fund_flow/fund_flow.py +61 -0
  72. tmdx-3.0.0/examples/08_fund_flow/history_fund_flow.py +57 -0
  73. tmdx-3.0.0/examples/09_file_download/report_file.py +178 -0
  74. tmdx-3.0.0/examples/10_offline/block_data.py +139 -0
  75. tmdx-3.0.0/examples/10_offline/daily_bars.py +83 -0
  76. tmdx-3.0.0/examples/10_offline/detect_home.py +157 -0
  77. tmdx-3.0.0/examples/10_offline/ex_daily_bars.py +89 -0
  78. tmdx-3.0.0/examples/10_offline/gbbq.py +128 -0
  79. tmdx-3.0.0/examples/10_offline/history_financial.py +135 -0
  80. tmdx-3.0.0/examples/10_offline/min_bars.py +150 -0
  81. tmdx-3.0.0/examples/11_mac_quotes/quotes_list.py +81 -0
  82. tmdx-3.0.0/examples/11_mac_quotes/stock_quotes.py +39 -0
  83. tmdx-3.0.0/examples/11_realtime/realtime_feed_async.py +73 -0
  84. tmdx-3.0.0/examples/12_mac_kline/kline_offset.py +23 -0
  85. tmdx-3.0.0/examples/12_mac_kline/stock_kline.py +96 -0
  86. tmdx-3.0.0/examples/13_mac_tick/chart_sampling.py +49 -0
  87. tmdx-3.0.0/examples/13_mac_tick/multi_day_tick.py +46 -0
  88. tmdx-3.0.0/examples/13_mac_tick/tick_chart.py +59 -0
  89. tmdx-3.0.0/examples/14_mac_transaction/transaction.py +80 -0
  90. tmdx-3.0.0/examples/15_mac_board/belong_board.py +42 -0
  91. tmdx-3.0.0/examples/15_mac_board/board_list.py +70 -0
  92. tmdx-3.0.0/examples/15_mac_board/board_members.py +61 -0
  93. tmdx-3.0.0/examples/15_mac_board/board_ranking.py +48 -0
  94. tmdx-3.0.0/examples/15_mac_board/board_summary.py +57 -0
  95. tmdx-3.0.0/examples/16_mac_capital/capital_flow.py +53 -0
  96. tmdx-3.0.0/examples/17_mac_monitor/auction.py +41 -0
  97. tmdx-3.0.0/examples/17_mac_monitor/server_info.py +29 -0
  98. tmdx-3.0.0/examples/17_mac_monitor/symbol_info.py +41 -0
  99. tmdx-3.0.0/examples/17_mac_monitor/unusual.py +80 -0
  100. tmdx-3.0.0/examples/18_mac_ex/ex_goods_list.py +65 -0
  101. tmdx-3.0.0/examples/18_mac_ex/ex_kline.py +72 -0
  102. tmdx-3.0.0/examples/18_mac_ex/ex_quotes.py +42 -0
  103. tmdx-3.0.0/examples/18_mac_ex/ex_tick_chart.py +61 -0
  104. tmdx-3.0.0/examples/19_unified/unified_client.py +58 -0
  105. tmdx-3.0.0/examples/20_cli/cli_examples.sh +456 -0
  106. tmdx-3.0.0/examples/21_indicator/basic_indicators.py +142 -0
  107. tmdx-3.0.0/examples/21_indicator/list_indicators.py +50 -0
  108. tmdx-3.0.0/examples/22_backtest_advanced/backtest_601088_advanced.py +138 -0
  109. tmdx-3.0.0/examples/23_screen_strength/README.md +96 -0
  110. tmdx-3.0.0/examples/23_screen_strength/strength_api.py +104 -0
  111. tmdx-3.0.0/examples/23_screen_strength/strength_cli.sh +78 -0
  112. tmdx-3.0.0/examples/23_screen_strength/strength_web_api.py +112 -0
  113. tmdx-3.0.0/pullup_sse_demo.html +271 -0
  114. tmdx-3.0.0/pyproject.toml +119 -0
  115. tmdx-3.0.0/requirements-dev.txt +33 -0
  116. tmdx-3.0.0/run_all_strategies.py +550 -0
  117. tmdx-3.0.0/scripts/__init__.py +0 -0
  118. tmdx-3.0.0/scripts/bench_engine.py +129 -0
  119. tmdx-3.0.0/scripts/export_limit_events.py +617 -0
  120. tmdx-3.0.0/scripts/probe_unknowns.py +107 -0
  121. tmdx-3.0.0/scripts/release/release.bat +58 -0
  122. tmdx-3.0.0/scripts/release/release.py +682 -0
  123. tmdx-3.0.0/scripts/report_snapshot_threshold.py +251 -0
  124. tmdx-3.0.0/scripts/ruff_hook.py +43 -0
  125. tmdx-3.0.0/scripts/verify-token/token1.txt +1 -0
  126. tmdx-3.0.0/scripts/verify-token/verify-token.bat +58 -0
  127. tmdx-3.0.0/scripts/verify-token/verify_token.py +570 -0
  128. tmdx-3.0.0/scripts/verify_ci.sh +60 -0
  129. tmdx-3.0.0/scripts/verify_fixes_20260415.py +128 -0
  130. tmdx-3.0.0/scripts/verify_issue64.py +69 -0
  131. tmdx-3.0.0/scripts/ws_smoke.py +86 -0
  132. tmdx-3.0.0/src/easy_tdx/MyTT.py +687 -0
  133. tmdx-3.0.0/src/easy_tdx/MyTT.pyi +123 -0
  134. tmdx-3.0.0/src/easy_tdx/__init__.py +133 -0
  135. tmdx-3.0.0/src/easy_tdx/__main__.py +145 -0
  136. tmdx-3.0.0/src/easy_tdx/_binary.py +48 -0
  137. tmdx-3.0.0/src/easy_tdx/_df.py +238 -0
  138. tmdx-3.0.0/src/easy_tdx/_dist.py +18 -0
  139. tmdx-3.0.0/src/easy_tdx/_health.py +163 -0
  140. tmdx-3.0.0/src/easy_tdx/_reconnect.py +329 -0
  141. tmdx-3.0.0/src/easy_tdx/ai/__init__.py +35 -0
  142. tmdx-3.0.0/src/easy_tdx/ai/llm.py +401 -0
  143. tmdx-3.0.0/src/easy_tdx/backtest/__init__.py +63 -0
  144. tmdx-3.0.0/src/easy_tdx/backtest/attribution.py +217 -0
  145. tmdx-3.0.0/src/easy_tdx/backtest/benchmark.py +282 -0
  146. tmdx-3.0.0/src/easy_tdx/backtest/cli.py +510 -0
  147. tmdx-3.0.0/src/easy_tdx/backtest/combo.py +458 -0
  148. tmdx-3.0.0/src/easy_tdx/backtest/dsl.py +55 -0
  149. tmdx-3.0.0/src/easy_tdx/backtest/engine.py +720 -0
  150. tmdx-3.0.0/src/easy_tdx/backtest/execution.py +655 -0
  151. tmdx-3.0.0/src/easy_tdx/backtest/fees.py +182 -0
  152. tmdx-3.0.0/src/easy_tdx/backtest/fitness.py +366 -0
  153. tmdx-3.0.0/src/easy_tdx/backtest/formula_strategy.py +187 -0
  154. tmdx-3.0.0/src/easy_tdx/backtest/grading.py +610 -0
  155. tmdx-3.0.0/src/easy_tdx/backtest/indicator_cache.py +94 -0
  156. tmdx-3.0.0/src/easy_tdx/backtest/multi_strategy_engine.py +249 -0
  157. tmdx-3.0.0/src/easy_tdx/backtest/optimizer.py +365 -0
  158. tmdx-3.0.0/src/easy_tdx/backtest/orders.py +544 -0
  159. tmdx-3.0.0/src/easy_tdx/backtest/performance.py +444 -0
  160. tmdx-3.0.0/src/easy_tdx/backtest/portfolio.py +200 -0
  161. tmdx-3.0.0/src/easy_tdx/backtest/portfolio_engine.py +282 -0
  162. tmdx-3.0.0/src/easy_tdx/backtest/rotation.py +429 -0
  163. tmdx-3.0.0/src/easy_tdx/backtest/scoring.py +130 -0
  164. tmdx-3.0.0/src/easy_tdx/backtest/slippage.py +129 -0
  165. tmdx-3.0.0/src/easy_tdx/backtest/strategies/__init__.py +41 -0
  166. tmdx-3.0.0/src/easy_tdx/backtest/strategies/builtin.py +805 -0
  167. tmdx-3.0.0/src/easy_tdx/backtest/strategies/presets.py +112 -0
  168. tmdx-3.0.0/src/easy_tdx/backtest/strategies/registry.py +340 -0
  169. tmdx-3.0.0/src/easy_tdx/backtest/strategy.py +537 -0
  170. tmdx-3.0.0/src/easy_tdx/backtest/types.py +199 -0
  171. tmdx-3.0.0/src/easy_tdx/backtest/validation.py +274 -0
  172. tmdx-3.0.0/src/easy_tdx/backtest/walkforward.py +268 -0
  173. tmdx-3.0.0/src/easy_tdx/ccpm/__init__.py +37 -0
  174. tmdx-3.0.0/src/easy_tdx/ccpm/client.py +314 -0
  175. tmdx-3.0.0/src/easy_tdx/ccpm/models.py +130 -0
  176. tmdx-3.0.0/src/easy_tdx/chanlun/__init__.py +47 -0
  177. tmdx-3.0.0/src/easy_tdx/chanlun/analyser.py +285 -0
  178. tmdx-3.0.0/src/easy_tdx/chanlun/beichi.py +181 -0
  179. tmdx-3.0.0/src/easy_tdx/chanlun/bi.py +118 -0
  180. tmdx-3.0.0/src/easy_tdx/chanlun/config.py +56 -0
  181. tmdx-3.0.0/src/easy_tdx/chanlun/fractal.py +76 -0
  182. tmdx-3.0.0/src/easy_tdx/chanlun/kline_merge.py +101 -0
  183. tmdx-3.0.0/src/easy_tdx/chanlun/macd.py +104 -0
  184. tmdx-3.0.0/src/easy_tdx/chanlun/mmd.py +160 -0
  185. tmdx-3.0.0/src/easy_tdx/chanlun/multi_level.py +259 -0
  186. tmdx-3.0.0/src/easy_tdx/chanlun/types.py +213 -0
  187. tmdx-3.0.0/src/easy_tdx/chanlun/xd.py +128 -0
  188. tmdx-3.0.0/src/easy_tdx/chanlun/zs.py +103 -0
  189. tmdx-3.0.0/src/easy_tdx/chanlun/zsd.py +143 -0
  190. tmdx-3.0.0/src/easy_tdx/cli/__init__.py +105 -0
  191. tmdx-3.0.0/src/easy_tdx/cli/cmd_admin.py +49 -0
  192. tmdx-3.0.0/src/easy_tdx/cli/cmd_announcement.py +101 -0
  193. tmdx-3.0.0/src/easy_tdx/cli/cmd_auction.py +30 -0
  194. tmdx-3.0.0/src/easy_tdx/cli/cmd_board.py +267 -0
  195. tmdx-3.0.0/src/easy_tdx/cli/cmd_capital.py +30 -0
  196. tmdx-3.0.0/src/easy_tdx/cli/cmd_ccpm.py +98 -0
  197. tmdx-3.0.0/src/easy_tdx/cli/cmd_chanlun.py +238 -0
  198. tmdx-3.0.0/src/easy_tdx/cli/cmd_company.py +253 -0
  199. tmdx-3.0.0/src/easy_tdx/cli/cmd_ex.py +180 -0
  200. tmdx-3.0.0/src/easy_tdx/cli/cmd_factor.py +91 -0
  201. tmdx-3.0.0/src/easy_tdx/cli/cmd_finance.py +63 -0
  202. tmdx-3.0.0/src/easy_tdx/cli/cmd_formula.py +208 -0
  203. tmdx-3.0.0/src/easy_tdx/cli/cmd_indicator.py +131 -0
  204. tmdx-3.0.0/src/easy_tdx/cli/cmd_info.py +51 -0
  205. tmdx-3.0.0/src/easy_tdx/cli/cmd_kline.py +67 -0
  206. tmdx-3.0.0/src/easy_tdx/cli/cmd_monitor.py +58 -0
  207. tmdx-3.0.0/src/easy_tdx/cli/cmd_offline.py +702 -0
  208. tmdx-3.0.0/src/easy_tdx/cli/cmd_pfactor.py +58 -0
  209. tmdx-3.0.0/src/easy_tdx/cli/cmd_quote.py +81 -0
  210. tmdx-3.0.0/src/easy_tdx/cli/cmd_run_all.py +562 -0
  211. tmdx-3.0.0/src/easy_tdx/cli/cmd_tick.py +44 -0
  212. tmdx-3.0.0/src/easy_tdx/cli/cmd_transaction.py +43 -0
  213. tmdx-3.0.0/src/easy_tdx/cli/cmd_warehouse.py +172 -0
  214. tmdx-3.0.0/src/easy_tdx/cli/cmd_web.py +79 -0
  215. tmdx-3.0.0/src/easy_tdx/cli/conn.py +58 -0
  216. tmdx-3.0.0/src/easy_tdx/cli/output.py +71 -0
  217. tmdx-3.0.0/src/easy_tdx/cli/parsers.py +188 -0
  218. tmdx-3.0.0/src/easy_tdx/client.py +1775 -0
  219. tmdx-3.0.0/src/easy_tdx/cninfo/__init__.py +18 -0
  220. tmdx-3.0.0/src/easy_tdx/cninfo/client.py +274 -0
  221. tmdx-3.0.0/src/easy_tdx/cninfo/models.py +64 -0
  222. tmdx-3.0.0/src/easy_tdx/codec/__init__.py +18 -0
  223. tmdx-3.0.0/src/easy_tdx/codec/bitmap.py +504 -0
  224. tmdx-3.0.0/src/easy_tdx/codec/block.py +71 -0
  225. tmdx-3.0.0/src/easy_tdx/codec/datetime_.py +64 -0
  226. tmdx-3.0.0/src/easy_tdx/codec/financial.py +101 -0
  227. tmdx-3.0.0/src/easy_tdx/codec/frame.py +67 -0
  228. tmdx-3.0.0/src/easy_tdx/codec/industry.py +24 -0
  229. tmdx-3.0.0/src/easy_tdx/codec/mac_frame.py +50 -0
  230. tmdx-3.0.0/src/easy_tdx/codec/price.py +64 -0
  231. tmdx-3.0.0/src/easy_tdx/codec/price_rules.py +94 -0
  232. tmdx-3.0.0/src/easy_tdx/codec/volume.py +56 -0
  233. tmdx-3.0.0/src/easy_tdx/commands/__init__.py +10 -0
  234. tmdx-3.0.0/src/easy_tdx/commands/base.py +29 -0
  235. tmdx-3.0.0/src/easy_tdx/commands/block_info.py +61 -0
  236. tmdx-3.0.0/src/easy_tdx/commands/company_info.py +84 -0
  237. tmdx-3.0.0/src/easy_tdx/commands/finance_info.py +116 -0
  238. tmdx-3.0.0/src/easy_tdx/commands/minute_time.py +70 -0
  239. tmdx-3.0.0/src/easy_tdx/commands/report_file.py +32 -0
  240. tmdx-3.0.0/src/easy_tdx/commands/security_bars.py +271 -0
  241. tmdx-3.0.0/src/easy_tdx/commands/security_count.py +25 -0
  242. tmdx-3.0.0/src/easy_tdx/commands/security_list.py +68 -0
  243. tmdx-3.0.0/src/easy_tdx/commands/security_quotes.py +262 -0
  244. tmdx-3.0.0/src/easy_tdx/commands/setup.py +15 -0
  245. tmdx-3.0.0/src/easy_tdx/commands/transaction.py +113 -0
  246. tmdx-3.0.0/src/easy_tdx/commands/xdxr_info.py +107 -0
  247. tmdx-3.0.0/src/easy_tdx/config.py +330 -0
  248. tmdx-3.0.0/src/easy_tdx/ex/__init__.py +15 -0
  249. tmdx-3.0.0/src/easy_tdx/ex/_hk_transaction.py +215 -0
  250. tmdx-3.0.0/src/easy_tdx/ex/client.py +575 -0
  251. tmdx-3.0.0/src/easy_tdx/ex/commands/__init__.py +1 -0
  252. tmdx-3.0.0/src/easy_tdx/ex/commands/get_history_bars_range.py +76 -0
  253. tmdx-3.0.0/src/easy_tdx/ex/commands/get_instrument_bars.py +74 -0
  254. tmdx-3.0.0/src/easy_tdx/ex/commands/get_instrument_count.py +17 -0
  255. tmdx-3.0.0/src/easy_tdx/ex/commands/get_instrument_info.py +50 -0
  256. tmdx-3.0.0/src/easy_tdx/ex/commands/get_instrument_quote.py +103 -0
  257. tmdx-3.0.0/src/easy_tdx/ex/commands/get_instrument_quote_list.py +218 -0
  258. tmdx-3.0.0/src/easy_tdx/ex/commands/get_markets.py +41 -0
  259. tmdx-3.0.0/src/easy_tdx/ex/commands/get_minute_time.py +74 -0
  260. tmdx-3.0.0/src/easy_tdx/ex/commands/get_transaction.py +97 -0
  261. tmdx-3.0.0/src/easy_tdx/ex/commands/login.py +51 -0
  262. tmdx-3.0.0/src/easy_tdx/ex/commands/setup.py +16 -0
  263. tmdx-3.0.0/src/easy_tdx/ex/mac_client.py +903 -0
  264. tmdx-3.0.0/src/easy_tdx/ex/models.py +137 -0
  265. tmdx-3.0.0/src/easy_tdx/ex/transport/__init__.py +1 -0
  266. tmdx-3.0.0/src/easy_tdx/ex/transport/async_.py +117 -0
  267. tmdx-3.0.0/src/easy_tdx/ex/transport/sync.py +158 -0
  268. tmdx-3.0.0/src/easy_tdx/exceptions.py +25 -0
  269. tmdx-3.0.0/src/easy_tdx/factor/__init__.py +36 -0
  270. tmdx-3.0.0/src/easy_tdx/factor/analysis.py +156 -0
  271. tmdx-3.0.0/src/easy_tdx/factor/base.py +51 -0
  272. tmdx-3.0.0/src/easy_tdx/factor/builtin/__init__.py +40 -0
  273. tmdx-3.0.0/src/easy_tdx/factor/builtin/chanlun.py +90 -0
  274. tmdx-3.0.0/src/easy_tdx/factor/builtin/momentum.py +40 -0
  275. tmdx-3.0.0/src/easy_tdx/factor/builtin/quality.py +55 -0
  276. tmdx-3.0.0/src/easy_tdx/factor/builtin/technical.py +55 -0
  277. tmdx-3.0.0/src/easy_tdx/factor/builtin/value.py +29 -0
  278. tmdx-3.0.0/src/easy_tdx/factor/builtin/volatility.py +58 -0
  279. tmdx-3.0.0/src/easy_tdx/factor/builtin/volume.py +63 -0
  280. tmdx-3.0.0/src/easy_tdx/factor/engine.py +136 -0
  281. tmdx-3.0.0/src/easy_tdx/factor/transform.py +197 -0
  282. tmdx-3.0.0/src/easy_tdx/formula.py +519 -0
  283. tmdx-3.0.0/src/easy_tdx/indicator.py +334 -0
  284. tmdx-3.0.0/src/easy_tdx/mac/__init__.py +1 -0
  285. tmdx-3.0.0/src/easy_tdx/mac/adjust.py +178 -0
  286. tmdx-3.0.0/src/easy_tdx/mac/client.py +2160 -0
  287. tmdx-3.0.0/src/easy_tdx/mac/commands/__init__.py +34 -0
  288. tmdx-3.0.0/src/easy_tdx/mac/commands/board_list.py +109 -0
  289. tmdx-3.0.0/src/easy_tdx/mac/commands/board_members_quotes.py +121 -0
  290. tmdx-3.0.0/src/easy_tdx/mac/commands/chart_sampling.py +47 -0
  291. tmdx-3.0.0/src/easy_tdx/mac/commands/file_query.py +90 -0
  292. tmdx-3.0.0/src/easy_tdx/mac/commands/goods_list.py +80 -0
  293. tmdx-3.0.0/src/easy_tdx/mac/commands/kline_offset.py +38 -0
  294. tmdx-3.0.0/src/easy_tdx/mac/commands/server_info.py +74 -0
  295. tmdx-3.0.0/src/easy_tdx/mac/commands/symbol_auction.py +66 -0
  296. tmdx-3.0.0/src/easy_tdx/mac/commands/symbol_bar.py +122 -0
  297. tmdx-3.0.0/src/easy_tdx/mac/commands/symbol_belong_board.py +90 -0
  298. tmdx-3.0.0/src/easy_tdx/mac/commands/symbol_capital_flow.py +85 -0
  299. tmdx-3.0.0/src/easy_tdx/mac/commands/symbol_info.py +88 -0
  300. tmdx-3.0.0/src/easy_tdx/mac/commands/symbol_quotes.py +103 -0
  301. tmdx-3.0.0/src/easy_tdx/mac/commands/symbol_tick_chart.py +112 -0
  302. tmdx-3.0.0/src/easy_tdx/mac/commands/symbol_transaction.py +76 -0
  303. tmdx-3.0.0/src/easy_tdx/mac/commands/tick_charts.py +149 -0
  304. tmdx-3.0.0/src/easy_tdx/mac/commands/unusual.py +227 -0
  305. tmdx-3.0.0/src/easy_tdx/mac/enums.py +238 -0
  306. tmdx-3.0.0/src/easy_tdx/mac/models.py +222 -0
  307. tmdx-3.0.0/src/easy_tdx/mac/qfq_check.py +312 -0
  308. tmdx-3.0.0/src/easy_tdx/models/__init__.py +35 -0
  309. tmdx-3.0.0/src/easy_tdx/models/bar.py +28 -0
  310. tmdx-3.0.0/src/easy_tdx/models/enums.py +88 -0
  311. tmdx-3.0.0/src/easy_tdx/models/finance.py +157 -0
  312. tmdx-3.0.0/src/easy_tdx/models/quote.py +90 -0
  313. tmdx-3.0.0/src/easy_tdx/models/security.py +23 -0
  314. tmdx-3.0.0/src/easy_tdx/models/stats.py +81 -0
  315. tmdx-3.0.0/src/easy_tdx/models/timeseries.py +39 -0
  316. tmdx-3.0.0/src/easy_tdx/offline/__init__.py +74 -0
  317. tmdx-3.0.0/src/easy_tdx/offline/block.py +92 -0
  318. tmdx-3.0.0/src/easy_tdx/offline/daily_bar.py +142 -0
  319. tmdx-3.0.0/src/easy_tdx/offline/ex_daily_bar.py +81 -0
  320. tmdx-3.0.0/src/easy_tdx/offline/finders.py +38 -0
  321. tmdx-3.0.0/src/easy_tdx/offline/gbbq.py +366 -0
  322. tmdx-3.0.0/src/easy_tdx/offline/history_financial.py +59 -0
  323. tmdx-3.0.0/src/easy_tdx/offline/min_bar.py +122 -0
  324. tmdx-3.0.0/src/easy_tdx/offline/paths.py +74 -0
  325. tmdx-3.0.0/src/easy_tdx/offline/write_daily.py +194 -0
  326. tmdx-3.0.0/src/easy_tdx/offline/write_ex_daily.py +93 -0
  327. tmdx-3.0.0/src/easy_tdx/offline/write_min_bar.py +186 -0
  328. tmdx-3.0.0/src/easy_tdx/portfolio/__init__.py +26 -0
  329. tmdx-3.0.0/src/easy_tdx/portfolio/optimizer.py +216 -0
  330. tmdx-3.0.0/src/easy_tdx/portfolio/rebalance.py +266 -0
  331. tmdx-3.0.0/src/easy_tdx/portfolio/risk.py +56 -0
  332. tmdx-3.0.0/src/easy_tdx/portfolio/types.py +30 -0
  333. tmdx-3.0.0/src/easy_tdx/py.typed +1 -0
  334. tmdx-3.0.0/src/easy_tdx/realtime/__init__.py +38 -0
  335. tmdx-3.0.0/src/easy_tdx/realtime/engine.py +217 -0
  336. tmdx-3.0.0/src/easy_tdx/realtime/feed.py +337 -0
  337. tmdx-3.0.0/src/easy_tdx/realtime/session.py +74 -0
  338. tmdx-3.0.0/src/easy_tdx/screen/__init__.py +43 -0
  339. tmdx-3.0.0/src/easy_tdx/screen/alert_persister.py +135 -0
  340. tmdx-3.0.0/src/easy_tdx/screen/alert_store.py +284 -0
  341. tmdx-3.0.0/src/easy_tdx/screen/board_snapshot.py +783 -0
  342. tmdx-3.0.0/src/easy_tdx/screen/board_snapshot_log.py +189 -0
  343. tmdx-3.0.0/src/easy_tdx/screen/cli.py +431 -0
  344. tmdx-3.0.0/src/easy_tdx/screen/defaults.py +109 -0
  345. tmdx-3.0.0/src/easy_tdx/screen/minute_hub.py +377 -0
  346. tmdx-3.0.0/src/easy_tdx/screen/pullup.py +292 -0
  347. tmdx-3.0.0/src/easy_tdx/screen/ranker.py +364 -0
  348. tmdx-3.0.0/src/easy_tdx/screen/scanner.py +711 -0
  349. tmdx-3.0.0/src/easy_tdx/screen/stock_pullup_scanner.py +99 -0
  350. tmdx-3.0.0/src/easy_tdx/screen/strength.py +498 -0
  351. tmdx-3.0.0/src/easy_tdx/screen/universe.py +190 -0
  352. tmdx-3.0.0/src/easy_tdx/sina/__init__.py +31 -0
  353. tmdx-3.0.0/src/easy_tdx/sina/client.py +165 -0
  354. tmdx-3.0.0/src/easy_tdx/sina/models.py +53 -0
  355. tmdx-3.0.0/src/easy_tdx/transport/__init__.py +4 -0
  356. tmdx-3.0.0/src/easy_tdx/transport/async_.py +151 -0
  357. tmdx-3.0.0/src/easy_tdx/transport/sync.py +296 -0
  358. tmdx-3.0.0/src/easy_tdx/tray.py +127 -0
  359. tmdx-3.0.0/src/easy_tdx/unified.py +648 -0
  360. tmdx-3.0.0/src/easy_tdx/warehouse/__init__.py +21 -0
  361. tmdx-3.0.0/src/easy_tdx/warehouse/store.py +423 -0
  362. tmdx-3.0.0/src/easy_tdx/warehouse/sync.py +155 -0
  363. tmdx-3.0.0/src/easy_tdx/web/__init__.py +53 -0
  364. tmdx-3.0.0/src/easy_tdx/web/app.py +563 -0
  365. tmdx-3.0.0/src/easy_tdx/web/backtest_schemas.py +559 -0
  366. tmdx-3.0.0/src/easy_tdx/web/convert.py +234 -0
  367. tmdx-3.0.0/src/easy_tdx/web/datasource.py +364 -0
  368. tmdx-3.0.0/src/easy_tdx/web/deps.py +44 -0
  369. tmdx-3.0.0/src/easy_tdx/web/e2e_mock.py +498 -0
  370. tmdx-3.0.0/src/easy_tdx/web/errors.py +46 -0
  371. tmdx-3.0.0/src/easy_tdx/web/llm_history_store.py +194 -0
  372. tmdx-3.0.0/src/easy_tdx/web/quote_streamer.py +234 -0
  373. tmdx-3.0.0/src/easy_tdx/web/realtime_hub.py +247 -0
  374. tmdx-3.0.0/src/easy_tdx/web/routers/__init__.py +1 -0
  375. tmdx-3.0.0/src/easy_tdx/web/routers/announcement.py +37 -0
  376. tmdx-3.0.0/src/easy_tdx/web/routers/backtest.py +1122 -0
  377. tmdx-3.0.0/src/easy_tdx/web/routers/bars.py +688 -0
  378. tmdx-3.0.0/src/easy_tdx/web/routers/block.py +29 -0
  379. tmdx-3.0.0/src/easy_tdx/web/routers/board_mac.py +129 -0
  380. tmdx-3.0.0/src/easy_tdx/web/routers/ccpm.py +88 -0
  381. tmdx-3.0.0/src/easy_tdx/web/routers/chanlun.py +58 -0
  382. tmdx-3.0.0/src/easy_tdx/web/routers/ex_market.py +84 -0
  383. tmdx-3.0.0/src/easy_tdx/web/routers/finance.py +85 -0
  384. tmdx-3.0.0/src/easy_tdx/web/routers/formula.py +260 -0
  385. tmdx-3.0.0/src/easy_tdx/web/routers/indicator.py +45 -0
  386. tmdx-3.0.0/src/easy_tdx/web/routers/llm.py +277 -0
  387. tmdx-3.0.0/src/easy_tdx/web/routers/mac_data.py +48 -0
  388. tmdx-3.0.0/src/easy_tdx/web/routers/mac_quotes.py +69 -0
  389. tmdx-3.0.0/src/easy_tdx/web/routers/market.py +199 -0
  390. tmdx-3.0.0/src/easy_tdx/web/routers/realtime.py +122 -0
  391. tmdx-3.0.0/src/easy_tdx/web/routers/screen.py +1111 -0
  392. tmdx-3.0.0/src/easy_tdx/web/routers/server.py +162 -0
  393. tmdx-3.0.0/src/easy_tdx/web/routers/sina.py +39 -0
  394. tmdx-3.0.0/src/easy_tdx/web/routers/strategies.py +96 -0
  395. tmdx-3.0.0/src/easy_tdx/web/routers/stream.py +63 -0
  396. tmdx-3.0.0/src/easy_tdx/web/routers/watchlist.py +51 -0
  397. tmdx-3.0.0/src/easy_tdx/web/schemas.py +153 -0
  398. tmdx-3.0.0/src/easy_tdx/web/signal_scan.py +331 -0
  399. tmdx-3.0.0/src/easy_tdx/web/strategy_store.py +232 -0
  400. tmdx-3.0.0/src/easy_tdx/web/task_runner.py +327 -0
  401. tmdx-3.0.0/src/easy_tdx/web/task_store.py +321 -0
  402. tmdx-3.0.0/src/easy_tdx/web/watchlist_store.py +183 -0
  403. tmdx-3.0.0/sse_demo.html +143 -0
  404. tmdx-3.0.0/strategies/README.md +51 -0
  405. tmdx-3.0.0/strategies/bias_reversal.py +28 -0
  406. tmdx-3.0.0/strategies/bollinger_breakout.py +28 -0
  407. tmdx-3.0.0/strategies/cci_breakout.py +37 -0
  408. tmdx-3.0.0/strategies/demo/1.png +0 -0
  409. tmdx-3.0.0/strategies/demo/2.png +0 -0
  410. tmdx-3.0.0/strategies/demo/3.png +0 -0
  411. tmdx-3.0.0/strategies/demo/4.png +0 -0
  412. tmdx-3.0.0/strategies/dmi_trend.py +40 -0
  413. tmdx-3.0.0/strategies/expma_cross.py +27 -0
  414. tmdx-3.0.0/strategies/kdj_golden.py +30 -0
  415. tmdx-3.0.0/strategies/ma_cross.py +27 -0
  416. tmdx-3.0.0/strategies/macd_cross.py +26 -0
  417. tmdx-3.0.0/strategies/mfi_volume.py +37 -0
  418. tmdx-3.0.0/strategies/mtm_momentum.py +33 -0
  419. tmdx-3.0.0/strategies/obv_trend.py +48 -0
  420. tmdx-3.0.0/strategies/rsi_reversal.py +26 -0
  421. tmdx-3.0.0/strategies/trix_cross.py +31 -0
  422. tmdx-3.0.0/strategies/turtle_breakout.py +29 -0
  423. tmdx-3.0.0/strategies/volume_price.py +35 -0
  424. tmdx-3.0.0/strategies/zhuoyao_momentum.py +47 -0
  425. tmdx-3.0.0/strategies/zig_breakout.py +83 -0
  426. tmdx-3.0.0/tests/__init__.py +0 -0
  427. tmdx-3.0.0/tests/conftest.py +16 -0
  428. tmdx-3.0.0/tests/fixtures/company_info_category.hex +1 -0
  429. tmdx-3.0.0/tests/fixtures/company_info_category.json +8 -0
  430. tmdx-3.0.0/tests/fixtures/company_info_content.hex +1 -0
  431. tmdx-3.0.0/tests/fixtures/company_info_content.json +4 -0
  432. tmdx-3.0.0/tests/fixtures/ex_history_transaction.hex +1 -0
  433. tmdx-3.0.0/tests/fixtures/ex_history_transaction.json +22 -0
  434. tmdx-3.0.0/tests/fixtures/finance_info.hex +1 -0
  435. tmdx-3.0.0/tests/fixtures/finance_info.json +5 -0
  436. tmdx-3.0.0/tests/fixtures/history_minute_time.hex +1 -0
  437. tmdx-3.0.0/tests/fixtures/history_minute_time.json +7 -0
  438. tmdx-3.0.0/tests/fixtures/history_transaction.hex +1 -0
  439. tmdx-3.0.0/tests/fixtures/history_transaction.json +9 -0
  440. tmdx-3.0.0/tests/fixtures/minute_time.hex +1 -0
  441. tmdx-3.0.0/tests/fixtures/minute_time.json +8 -0
  442. tmdx-3.0.0/tests/fixtures/security_bars.hex +1 -0
  443. tmdx-3.0.0/tests/fixtures/security_bars.json +10 -0
  444. tmdx-3.0.0/tests/fixtures/security_count.hex +1 -0
  445. tmdx-3.0.0/tests/fixtures/security_count.json +3 -0
  446. tmdx-3.0.0/tests/fixtures/security_list.hex +1 -0
  447. tmdx-3.0.0/tests/fixtures/security_list.json +8 -0
  448. tmdx-3.0.0/tests/fixtures/security_quotes.hex +1 -0
  449. tmdx-3.0.0/tests/fixtures/security_quotes.json +9 -0
  450. tmdx-3.0.0/tests/fixtures/transaction.hex +1 -0
  451. tmdx-3.0.0/tests/fixtures/transaction.json +9 -0
  452. tmdx-3.0.0/tests/fixtures/xdxr_info.hex +1 -0
  453. tmdx-3.0.0/tests/fixtures/xdxr_info.json +9 -0
  454. tmdx-3.0.0/tests/golden/backtest_metrics.json +354 -0
  455. tmdx-3.0.0/tests/integration/__init__.py +0 -0
  456. tmdx-3.0.0/tests/integration/test_live_client.py +36 -0
  457. tmdx-3.0.0/tests/unit/__init__.py +0 -0
  458. tmdx-3.0.0/tests/unit/test_a_share_extensions.py +369 -0
  459. tmdx-3.0.0/tests/unit/test_ai_llm.py +434 -0
  460. tmdx-3.0.0/tests/unit/test_async_transport.py +139 -0
  461. tmdx-3.0.0/tests/unit/test_backtest_attribution.py +211 -0
  462. tmdx-3.0.0/tests/unit/test_backtest_cli.py +30 -0
  463. tmdx-3.0.0/tests/unit/test_backtest_combo.py +397 -0
  464. tmdx-3.0.0/tests/unit/test_backtest_engine.py +728 -0
  465. tmdx-3.0.0/tests/unit/test_backtest_engine_vector.py +320 -0
  466. tmdx-3.0.0/tests/unit/test_backtest_execution.py +420 -0
  467. tmdx-3.0.0/tests/unit/test_backtest_fees.py +206 -0
  468. tmdx-3.0.0/tests/unit/test_backtest_fitness_benchmark.py +196 -0
  469. tmdx-3.0.0/tests/unit/test_backtest_orders.py +423 -0
  470. tmdx-3.0.0/tests/unit/test_backtest_performance.py +612 -0
  471. tmdx-3.0.0/tests/unit/test_backtest_portfolio.py +360 -0
  472. tmdx-3.0.0/tests/unit/test_backtest_rotation.py +159 -0
  473. tmdx-3.0.0/tests/unit/test_backtest_slippage.py +161 -0
  474. tmdx-3.0.0/tests/unit/test_backtest_strategy.py +541 -0
  475. tmdx-3.0.0/tests/unit/test_backtest_types.py +267 -0
  476. tmdx-3.0.0/tests/unit/test_backtest_walkforward.py +176 -0
  477. tmdx-3.0.0/tests/unit/test_bars_min120_derived.py +84 -0
  478. tmdx-3.0.0/tests/unit/test_bars_vol_semantics.py +112 -0
  479. tmdx-3.0.0/tests/unit/test_block_info.py +109 -0
  480. tmdx-3.0.0/tests/unit/test_board_change_ranking.py +337 -0
  481. tmdx-3.0.0/tests/unit/test_board_list.py +180 -0
  482. tmdx-3.0.0/tests/unit/test_ccpm.py +392 -0
  483. tmdx-3.0.0/tests/unit/test_chanlun.py +725 -0
  484. tmdx-3.0.0/tests/unit/test_chanlun_phase2.py +265 -0
  485. tmdx-3.0.0/tests/unit/test_chanlun_phase3.py +242 -0
  486. tmdx-3.0.0/tests/unit/test_client_reconnect.py +158 -0
  487. tmdx-3.0.0/tests/unit/test_cninfo.py +730 -0
  488. tmdx-3.0.0/tests/unit/test_codec_bitmap.py +126 -0
  489. tmdx-3.0.0/tests/unit/test_codec_datetime.py +110 -0
  490. tmdx-3.0.0/tests/unit/test_codec_frame.py +39 -0
  491. tmdx-3.0.0/tests/unit/test_codec_price.py +62 -0
  492. tmdx-3.0.0/tests/unit/test_codec_volume.py +36 -0
  493. tmdx-3.0.0/tests/unit/test_commands_offline.py +552 -0
  494. tmdx-3.0.0/tests/unit/test_config.py +201 -0
  495. tmdx-3.0.0/tests/unit/test_decode_errors.py +113 -0
  496. tmdx-3.0.0/tests/unit/test_e2e_mock.py +166 -0
  497. tmdx-3.0.0/tests/unit/test_ex_reconnect.py +318 -0
  498. tmdx-3.0.0/tests/unit/test_ex_tick_chart_date.py +259 -0
  499. tmdx-3.0.0/tests/unit/test_factor_analysis.py +117 -0
  500. tmdx-3.0.0/tests/unit/test_factor_base.py +119 -0
  501. tmdx-3.0.0/tests/unit/test_factor_builtin.py +211 -0
  502. tmdx-3.0.0/tests/unit/test_factor_engine.py +206 -0
  503. tmdx-3.0.0/tests/unit/test_factor_transform.py +119 -0
  504. tmdx-3.0.0/tests/unit/test_failover.py +779 -0
  505. tmdx-3.0.0/tests/unit/test_financial_data.py +116 -0
  506. tmdx-3.0.0/tests/unit/test_formula.py +214 -0
  507. tmdx-3.0.0/tests/unit/test_formula_integration.py +193 -0
  508. tmdx-3.0.0/tests/unit/test_golden_backtest.py +292 -0
  509. tmdx-3.0.0/tests/unit/test_grading_scoring.py +285 -0
  510. tmdx-3.0.0/tests/unit/test_health.py +149 -0
  511. tmdx-3.0.0/tests/unit/test_heartbeat.py +70 -0
  512. tmdx-3.0.0/tests/unit/test_hk_transaction.py +433 -0
  513. tmdx-3.0.0/tests/unit/test_indicator.py +160 -0
  514. tmdx-3.0.0/tests/unit/test_kline_bar_time.py +213 -0
  515. tmdx-3.0.0/tests/unit/test_llm_history_store.py +69 -0
  516. tmdx-3.0.0/tests/unit/test_mac_qfq_adjust.py +209 -0
  517. tmdx-3.0.0/tests/unit/test_mac_qfq_integration.py +184 -0
  518. tmdx-3.0.0/tests/unit/test_mac_tick_charts.py +188 -0
  519. tmdx-3.0.0/tests/unit/test_multi_strategy.py +258 -0
  520. tmdx-3.0.0/tests/unit/test_mytt.py +216 -0
  521. tmdx-3.0.0/tests/unit/test_mytt_zig.py +66 -0
  522. tmdx-3.0.0/tests/unit/test_optimizer.py +168 -0
  523. tmdx-3.0.0/tests/unit/test_optimizer_cache_multiseed.py +218 -0
  524. tmdx-3.0.0/tests/unit/test_portfolio_engine.py +197 -0
  525. tmdx-3.0.0/tests/unit/test_portfolio_optimizer.py +95 -0
  526. tmdx-3.0.0/tests/unit/test_portfolio_rebalance.py +131 -0
  527. tmdx-3.0.0/tests/unit/test_portfolio_risk.py +45 -0
  528. tmdx-3.0.0/tests/unit/test_protocol_fixes.py +130 -0
  529. tmdx-3.0.0/tests/unit/test_public_api.py +139 -0
  530. tmdx-3.0.0/tests/unit/test_qfq_crosscheck.py +278 -0
  531. tmdx-3.0.0/tests/unit/test_quote_decimal_digits.py +41 -0
  532. tmdx-3.0.0/tests/unit/test_realtime.py +164 -0
  533. tmdx-3.0.0/tests/unit/test_realtime_feed.py +334 -0
  534. tmdx-3.0.0/tests/unit/test_realtime_session.py +58 -0
  535. tmdx-3.0.0/tests/unit/test_realtime_ws.py +284 -0
  536. tmdx-3.0.0/tests/unit/test_scanner.py +266 -0
  537. tmdx-3.0.0/tests/unit/test_screen.py +695 -0
  538. tmdx-3.0.0/tests/unit/test_screen_universe_core.py +70 -0
  539. tmdx-3.0.0/tests/unit/test_signal_scan.py +386 -0
  540. tmdx-3.0.0/tests/unit/test_sina.py +526 -0
  541. tmdx-3.0.0/tests/unit/test_strategy_store.py +257 -0
  542. tmdx-3.0.0/tests/unit/test_sync_transport.py +88 -0
  543. tmdx-3.0.0/tests/unit/test_task_store.py +259 -0
  544. tmdx-3.0.0/tests/unit/test_unusual.py +221 -0
  545. tmdx-3.0.0/tests/unit/test_warehouse.py +307 -0
  546. tmdx-3.0.0/tests/unit/test_watchlist_and_streamer.py +155 -0
  547. tmdx-3.0.0/tests/unit/test_web_api.py +578 -0
  548. tmdx-3.0.0/tests/unit/test_web_backtest.py +1050 -0
  549. tmdx-3.0.0/tests/unit/test_web_spa_api_guard.py +84 -0
  550. tmdx-3.0.0/tests/unit/test_write_daily.py +345 -0
  551. tmdx-3.0.0/tests/unit/test_write_ex_daily.py +228 -0
  552. tmdx-3.0.0/tests/unit/test_write_min_bar.py +276 -0
  553. tmdx-3.0.0/tests/unit/test_zig_strategy.py +97 -0
  554. tmdx-3.0.0/welcome-discussion.md +46 -0
@@ -0,0 +1,6 @@
1
+ # Windows 批处理脚本必须用 CRLF 行尾。
2
+ # LF 行尾的 .bat 在含多行 if (...) 块时可能被 cmd 解析异常。
3
+ # 另外 scripts/verify-token/verify-token.bat 必须保持纯 ASCII
4
+ # (cmd 解析含 GBK 中文的批处理会字节错位,实测复现),
5
+ # 中文提示一律放在同目录的 verify_token.py 里输出。
6
+ *.bat text eol=crlf
@@ -0,0 +1,72 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ${{ matrix.os }}
12
+ strategy:
13
+ fail-fast: false
14
+ matrix:
15
+ # 项目核心场景是读取 Windows 通达信安装目录下的 .day 文件,
16
+ # 路径分隔符 / GBK 文件名 / tzdata 时区行为在 Windows 上与 Linux 不同,
17
+ # 故补充 Windows 矩阵覆盖这些平台特有路径。
18
+ os: [ubuntu-latest, windows-latest]
19
+ python-version: ["3.10", "3.12", "3.13"]
20
+ steps:
21
+ - uses: actions/checkout@v4
22
+ - uses: actions/setup-python@v5
23
+ with:
24
+ python-version: ${{ matrix.python-version }}
25
+ # 纯 Python 包(pyproject 已移除 force-include),无需 Node / 前端产物。
26
+ - run: pip install -e ".[dev]"
27
+ - run: pip install -r requirements-dev.txt
28
+ - run: python -m pytest tests/unit/ -v --tb=short --cov src/easy_tdx --cov-fail-under=60
29
+ - run: ruff check src/ tests/
30
+ - run: ruff format --check src/ tests/
31
+
32
+ mypy:
33
+ runs-on: ubuntu-latest
34
+ steps:
35
+ - uses: actions/checkout@v4
36
+ - uses: actions/setup-python@v5
37
+ with:
38
+ python-version: "3.13"
39
+ # 同 test job:纯 Python 包,无需 Node。
40
+ - run: pip install -e ".[dev,web]"
41
+ - run: mypy src/
42
+
43
+ # 前端构建检查。web-ui 此前只有本地 vue-tsc,CI 不覆盖,
44
+ # 类型错误 / 破坏性 import 只有发布后用户才发觉(v1.17.9 的
45
+ # 65409694% 离谱数值 bug 即前端无回归守卫的典型后果)。
46
+ # 与 Python 版本无关,故独立 job,避免在 test matrix 里跑 6 遍。
47
+ frontend:
48
+ runs-on: ubuntu-latest
49
+ steps:
50
+ - uses: actions/checkout@v4
51
+ - uses: actions/setup-node@v4
52
+ with:
53
+ node-version: "20"
54
+ cache: "npm"
55
+ cache-dependency-path: web-ui/package-lock.json
56
+ - run: npm ci
57
+ working-directory: web-ui
58
+ - run: npm run build
59
+ working-directory: web-ui
60
+ # Playwright E2E(mock 模式):serve 托管刚构建的 dist,行情全部来自
61
+ # EASY_TDX_E2E_MOCK=1 合成数据,不连真实通达信服务器、不受交易时段限制。
62
+ # 需要 Python 侧 tmdx[web](webServer 用系统 python 启动 serve)。
63
+ - uses: actions/setup-python@v5
64
+ with:
65
+ python-version: "3.13"
66
+ - run: pip install -e ".[web]"
67
+ - run: npx playwright install --with-deps chromium
68
+ working-directory: web-ui
69
+ - run: npm run test:e2e
70
+ working-directory: web-ui
71
+ env:
72
+ EASY_TDX_PYTHON: python
@@ -0,0 +1,35 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ jobs:
9
+ build-and-publish:
10
+ name: Build and publish to PyPI
11
+ runs-on: ubuntu-latest
12
+ environment: release
13
+ permissions:
14
+ id-token: write
15
+
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+
19
+ - uses: actions/setup-python@v5
20
+ with:
21
+ python-version: "3.13"
22
+
23
+ # 纯 Python wheel:pyproject.toml 已移除 hatchling force-include,
24
+ # 不再需要 Node / 前端产物,干净 checkout 即可构建(不会出现
25
+ # FileNotFoundError 硬崩)。前端 UI 走 Docker / EXE(release.yml)分发。
26
+ - run: pip install build
27
+
28
+ - run: python -m build
29
+
30
+ - uses: pypa/gh-action-pypi-publish@release/v1
31
+ with:
32
+ # sigstore provenance 签名认证。OIDC 服务(GitHub Actions → sigstore)
33
+ # 偶发 503 overflow 故障时会让整个 publish 失败,故暂时关闭;
34
+ # 待服务稳定后再改回 true。上传本身用 OIDC trusted publishing,无需 API token。
35
+ attestations: false
@@ -0,0 +1,94 @@
1
+ name: Release EXE
2
+
3
+ # 打 tag 时构建 Windows 单 EXE 并发布到 GitHub Release。
4
+ # 与 publish.yml(PyPI)并行独立:即使 PyPI 发布失败,EXE 仍可发布,
5
+ # 提高发布韧性。Phase 1 产物未签名,老人首次运行会被 SmartScreen 拦截,
6
+ # 需手动"更多信息 → 仍要运行"——Phase 2 引入代码签名后解决。
7
+ on:
8
+ push:
9
+ tags:
10
+ - "v*"
11
+
12
+ permissions:
13
+ contents: write # softprops/action-gh-release 创建 Release 需要
14
+
15
+ jobs:
16
+ build-windows:
17
+ name: Build Windows EXE
18
+ runs-on: windows-latest
19
+ steps:
20
+ - uses: actions/checkout@v4
21
+
22
+ - name: Set up Python
23
+ uses: actions/setup-python@v5
24
+ with:
25
+ python-version: "3.12"
26
+
27
+ - name: Set up Node
28
+ uses: actions/setup-node@v4
29
+ with:
30
+ node-version: "20"
31
+ cache: "npm"
32
+ cache-dependency-path: web-ui/package-lock.json
33
+
34
+ # 构建前端 → web-ui/dist/。必须在 pip install 前:easy_tdx.spec 要把
35
+ # web-ui/dist 收进 EXE 的 easy_tdx/web/dist/,缺失则打出的 EXE 没有 UI。
36
+ - name: Build frontend
37
+ run: |
38
+ npm ci
39
+ npm run build
40
+ working-directory: web-ui
41
+
42
+ # 安装后端(含 [web,packaging] extras:fastapi/uvicorn + pystray/Pillow)
43
+ - name: Install Python deps
44
+ run: |
45
+ pip install -e ".[web,packaging]"
46
+ pip install pyinstaller
47
+
48
+ - name: Build EXE
49
+ run: pyinstaller easy_tdx.spec --noconfirm
50
+
51
+ # 重命名为带版本号的文件名,方便老人下载时识别
52
+ - name: Rename EXE with version
53
+ shell: bash
54
+ run: |
55
+ VERSION="${GITHUB_REF_NAME#v}"
56
+ mv dist/tmdx.exe "dist/tmdx-${VERSION}-windows.exe"
57
+ ls -lh dist/
58
+
59
+ - uses: actions/upload-artifact@v4
60
+ with:
61
+ name: tmdx-windows-exe
62
+ path: dist/tmdx-*-windows.exe
63
+ if-no-files-found: error
64
+
65
+ release:
66
+ name: Publish GitHub Release
67
+ needs: build-windows
68
+ runs-on: ubuntu-latest
69
+ steps:
70
+ - uses: actions/download-artifact@v4
71
+ with:
72
+ name: tmdx-windows-exe
73
+ path: dist
74
+
75
+ - name: Create release
76
+ uses: softprops/action-gh-release@v2
77
+ with:
78
+ files: dist/tmdx-*-windows.exe
79
+ generate_release_notes: true
80
+ body: |
81
+ ## 下载使用
82
+
83
+ 1. 下载下方 `tmdx-*-windows.exe`(约 80-150MB)
84
+ 2. 双击运行
85
+ 3. 浏览器会自动打开回测界面(地址 `http://localhost:8000`)
86
+
87
+ ## ⚠️ SmartScreen 提示
88
+
89
+ 本版本未做代码签名,首次运行 Windows 会弹出"已保护你的电脑":
90
+
91
+ 1. 点击 **更多信息**
92
+ 2. 点击 **仍要运行**
93
+
94
+ Phase 2 将引入代码签名消除此提示。
tmdx-3.0.0/.gitignore ADDED
@@ -0,0 +1,47 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ dist/
5
+ build/
6
+ .eggs/
7
+ .mypy_cache/
8
+ .ruff_cache/
9
+ .pytest_cache/
10
+ *.egg
11
+ .venv/
12
+ venv/
13
+ .omc/
14
+ .claude/
15
+ .playwright-mcp/
16
+ docs/_build/
17
+ FK/
18
+
19
+ # 测试覆盖率与运行时产物
20
+ .coverage
21
+ signals.json
22
+
23
+ # 本地独立脚本(不属于 easy_tdx 库,依赖外部 unified_config / sqlalchemy)
24
+ src/easy_tdx/exchange_margin.py
25
+
26
+ # 本地文件,不递交到 GitHub
27
+ CLAUDE.md
28
+
29
+ # 本机包管理缓存(沙箱环境下避开 AppData 路径,落在项目内)
30
+ .npm-cache/
31
+ .uv-cache/
32
+
33
+ # 代码审计报告(本地产物,不入库)
34
+ audit-report-*.html
35
+
36
+ *.log
37
+ .workbuddy/
38
+ # Playwright E2E 运行产物(trace/截图/视频)与临时配置目录
39
+ web-ui/e2e/.results/
40
+ web-ui/test-results/
41
+ web-ui/playwright-report/
42
+
43
+ # ZCode agent 会话目录(本地,不入库)
44
+ .zcode/
45
+
46
+ # PyPI token 本地暂存(scripts/verify-token 用)—— token 一旦入库即等于泄漏
47
+ scripts/verify-token/token.txt
@@ -0,0 +1,18 @@
1
+ # .readthedocs.yaml
2
+ version: 2
3
+
4
+ build:
5
+ os: ubuntu-22.04
6
+ tools:
7
+ python: "3.11"
8
+
9
+ sphinx:
10
+ configuration: docs/conf.py
11
+
12
+ formats:
13
+ - pdf
14
+ - epub
15
+
16
+ python:
17
+ install:
18
+ - requirements: docs/requirements.txt
@@ -0,0 +1,254 @@
1
+ # tmdx API 快速试用指南
2
+
3
+ 服务已在本地启动:**http://127.0.0.1:8000**
4
+ 在线调试(Swagger UI,可直接点):**http://127.0.0.1:8000/docs**
5
+
6
+ > 所有路由挂在 `/api/v1` 前缀下。
7
+ > 当前为非交易时段,**今日分时(09:30 开盘后才有)现在为空**;
8
+ > 想立刻看到数据请用历史日期参数,例如 `date=20260814`(该日各市场均返回 240 行)。
9
+ >
10
+ > **板块异动检测(第 8、9 节)新增「快照记录器」主线**:服务启动时后台常驻循环,每 15s 拉一次**全量**板块列表并缓存 `change_pct` 时序(环形缓冲,默认 3 分钟 = 12 点窗口)。
11
+ > 交易时段(09:30–15:00 工作日)内,`board-pullup` 优先读这条快照序列算板块涨速(全量不截断 → **水下拉升也能报警**);非交易时段或快照为空时自动回退到「板块列表 + 批量分时」路径。
12
+ > 可用第 9 节 `/screen/board-snapshot/status` 确认记录器是否在采、已采点数。
13
+
14
+ ---
15
+
16
+ ## 1. 批量分时(本项目新增)`GET /api/v1/minute/batch`
17
+ 连接池并发直连通达信,N 只耗时约为逐只串行的 `1/pool_size`。
18
+
19
+ - `codes`:逗号分隔。**可纯代码(按前缀自动识别市场)** 或显式 `市场:代码`
20
+ - `600000`→SH、`000001`/`300750`→SZ、`880550`→SH 板块指数、`830799`/`920002`→BJ
21
+ - 非法代码(非 6 位)→ HTTP 400 清晰提示
22
+ - `date`:可选 `YYYYMMDD`,省略=今日分时
23
+ - `pool_size` / `max_concurrency`:并发池大小(默认 6 / 8)
24
+
25
+ ```bash
26
+ # 历史批量(自动识别 4 个市场)
27
+ curl "http://127.0.0.1:8000/api/v1/minute/batch?codes=600000,000001,880550,300750,920002&date=20260814"
28
+
29
+ # 今日批量(开盘后才有数据)
30
+ curl "http://127.0.0.1:8000/api/v1/minute/batch?codes=600000,000001,880550"
31
+
32
+ # 混合显式指定市场
33
+ curl "http://127.0.0.1:8000/api/v1/minute/batch?codes=SH:600000,SZ:000001"
34
+ ```
35
+ 返回:`{ "data": { "<code>": [ {datetime, price, vol}, ... ] } }`,失败的 code 对应空列表。
36
+
37
+ ## 2. 单只今日分时 `GET /api/v1/minute`
38
+ ```bash
39
+ curl "http://127.0.0.1:8000/api/v1/minute?market=SH&code=600000"
40
+ ```
41
+
42
+ ## 3. 单只历史分时 `GET /api/v1/minute/history`
43
+ ```bash
44
+ curl "http://127.0.0.1:8000/api/v1/minute/history?market=SH&code=600000&date=20260814"
45
+ ```
46
+
47
+ ## 4. 涨速排行(真·涨速 SPEED_PCT)`GET /api/v1/mac/quote-list`
48
+ ```bash
49
+ # 主板涨速前 5(排除科创/创业/北交/ST)
50
+ curl "http://127.0.0.1:8000/api/v1/mac/quote-list?sort_type=SPEED_PCT&category=A&exclude=KC,CY,BJ,ST&count=5"
51
+
52
+ # 全市场涨速前 20
53
+ curl "http://127.0.0.1:8000/api/v1/mac/quote-list?sort_type=SPEED_PCT&count=20"
54
+ ```
55
+ > 单看涨速排序前几名常混入「深跌反弹 / 无量脉冲」假阳性,建议组合:
56
+ > `change_pct>0`(本地算) + `vol_ratio>=1.5` + `amount` 阈值 + `exclude`,
57
+ > 或先用本接口取候选池 → 批量拉分钟 → 用 `detect_minute_pullup` 算斜率+R²+量能。
58
+
59
+ ## 5. K 线(支持复权)`GET /api/v1/bars`
60
+ ```bash
61
+ curl "http://127.0.0.1:8000/api/v1/bars?market=SH&code=600000&category=DAY&count=100"
62
+ # category: MIN_1/MIN_5/DAY/WEEK/MONTH ... adjust: NONE/QFQ(默认)/HFQ
63
+ ```
64
+
65
+ ## 6. 昨日板块排行 `GET /api/v1/board-mac/change-ranking`
66
+ ```bash
67
+ curl "http://127.0.0.1:8000/api/v1/board-mac/change-ranking?target_date=20260814&days=1&count=5"
68
+ ```
69
+
70
+ ---
71
+
72
+ ### 7. 盘中拉升扫描(一步到位)`GET /api/v1/screen/pullup`
73
+ 把「涨速候选池 → 批量拉分钟 → 斜率+R²+量能判定」串成一步,看盘页直接轮询即可。
74
+
75
+ - `window`:**分钟级别(3 或 5 最常用)**,取最近 N 分钟判定
76
+ - `candidates`:涨速候选池大小(取涨速前 N 只)
77
+ - `min_speed_pct`:候选池涨速下限过滤(可选,单位 %/分钟)
78
+ - `date`:省略=**今日分时(盘中实时)**;传 `YYYYMMDD` 测历史
79
+ - `only_alerts`:默认 true,只返回命中 `is_pullup`/`is_rising_alert` 的标的
80
+ - 阈值:`slope_pct_thresh`/`r2_thresh`/`vol_spike_thresh`/`peak_slope_thresh`/`pullback_tol_pct`/`jump_ratio_thresh`
81
+
82
+ ```bash
83
+ # 盘中实时(最简:5 分钟级,只返回报警标的)
84
+ curl "http://127.0.0.1:8000/api/v1/screen/pullup?window=5"
85
+
86
+ # 3 分钟级别 + 涨速前 20 候选 + 看全部(不只报警)
87
+ curl "http://127.0.0.1:8000/api/v1/screen/pullup?window=3&candidates=20&only_alerts=false"
88
+
89
+ # 历史回测验证(非交易时段今日分时空,用历史日)
90
+ curl "http://127.0.0.1:8000/api/v1/screen/pullup?window=5&candidates=10&date=20260814&only_alerts=false"
91
+ ```
92
+ 返回列:`code/name/speed_pct/slope/slope_pct/window_pct/r2/vol_spike/peak_slope_pct/peak_r2/pullback_from_peak_pct/jump_full/jump_rise/is_pullup/is_rising_alert/reason`,按 `peak_slope_pct` 降序。
93
+
94
+ ### 8. 板块/概念拉升扫描(一步到位)`GET /api/v1/screen/board-pullup`
95
+ 逻辑同 `screen/pullup`,但针对**板块/概念指数**。默认扫**概念(GN)**。
96
+
97
+ **新版数据路径(快照记录器主线)**:交易时段内优先读后台每 15s 采集的**全量**板块 `change_pct` 时序,对每只板块用最近 `window` 分钟算板块涨速(= change_pct 序列斜率,%/min,天然绕开协议 `rise_speed` 永远为 0 的盲区),**全量不截断** → 水下翻红拉升的板块也能报警。非交易时段或快照为空时自动回退「板块列表 + 批量分时」路径。
98
+
99
+ - `board_type`:板块类型,`GN`=概念(默认) / `HY`=行业 / `ALL`=全部
100
+ - `window`:**判定窗口(分钟级),默认 3**(=12 个采集点 @ 15s)。`window=1` 更快更噪,`window=5` 更稳更慢
101
+ - `candidates`:回退路径下扫描板块数(取当日涨幅前 N 只板块作候选)
102
+ - `min_change_pct`:候选板块当日涨幅下限过滤(可选,%)
103
+ - `date`:省略=今日分时(盘中实时,走快照路径);传 `YYYYMMDD` 测历史(走回退路径)
104
+ - `only_alerts`:默认 true,只返回命中报警的板块
105
+ - 阈值参数同 `screen/pullup`
106
+
107
+ ```bash
108
+ # 概念板块实时(默认 GN,3 分钟级,只报警)—— 水下拉升也能抓
109
+ curl "http://127.0.0.1:8000/api/v1/screen/board-pullup?board_type=GN&window=3"
110
+
111
+ # 看全部候选(不只报警)
112
+ curl "http://127.0.0.1:8000/api/v1/screen/board-pullup?board_type=GN&window=3&only_alerts=false"
113
+
114
+ # 行业板块 + 3 分钟级 + 看全部候选
115
+ curl "http://127.0.0.1:8000/api/v1/screen/board-pullup?board_type=HY&window=3&candidates=20&only_alerts=false"
116
+
117
+ # 历史回测(概念,20260814)
118
+ curl "http://127.0.0.1:8000/api/v1/screen/board-pullup?board_type=GN&window=3&candidates=20&date=20260814&only_alerts=false"
119
+ ```
120
+ 返回列:在个股版基础上增加 `rise_speed`/`change_pct`(板块列表快照字段)。
121
+ > 注:板块列表 `rise_speed` 字段实测恒为 0,候选排序统一按 `change_pct`(当日涨幅),实时/历史均稳定。
122
+ > 注:本端点阈值**默认按板块标定**(slope/peak=0.03、r2=0.4、pullback=1.5、jump=1.5),比个股端点松约 10 倍——板块指数不可能像个股那样 5 分钟拉 2.5%。个股端点仍是原默认(0.5/0.7/1.5…)。
123
+ > 注:**板块检测默认不要求量能**(快照路径无量能字段、回退路径 `require_vol=false`,两条路径口径一致),量能不参与决策;如需重新启用量能门槛传 `require_vol=true`。个股 `/screen/pullup` 仍保留"带量"硬门槛。
124
+ > 注:`only_alerts` 默认 true(只返报警),想看全部候选加 `only_alerts=false`。极个别板块因子TDX多主机数据覆盖差异可能取不到分时(非接口bug)。
125
+
126
+ ---
127
+
128
+ ### 9. 板块快照记录器调试 `GET /api/v1/screen/board-snapshot/status` & `/board-series`
129
+ 配合第 8 节的快照记录器主线使用,便于确认采集状态与排查空数据。
130
+
131
+ ```bash
132
+ # 记录器运行状态:是否 running、采集间隔、各类型已采板块数/总点数
133
+ curl "http://127.0.0.1:8000/api/v1/screen/board-snapshot/status"
134
+
135
+ # 某板块的 change_pct 时序(调试用,code 换成关心的板块代码)
136
+ curl "http://127.0.0.1:8000/api/v1/screen/board-series?board_type=GN&code=880520"
137
+ ```
138
+ - `status` 返回示例:`{"running": true, "interval_sec": 15, "board_counts": {"GN": 269, "HY": 124, "DQ": ...}, "last_update": 1692...}`
139
+ - 非交易时段 `running` 仍为真,但 `board_counts` 为空(按设计不采集,不是 bug)。
140
+ - `board-series` 返回该板块按时间排列的 `{ts, change_pct}` 序列;无数据时返回空 `data:[]`(不报错)。
141
+
142
+ ---
143
+
144
+ ### 10. 实时分时 SSE 推送 `GET /api/v1/minute/sse`
145
+
146
+ 把「前端每次现场拉 TDX」改为「后台采集器按默认 5s(且可被连接的 `interval` 参数动态拉低)拉并集存内存,SSE 只推增量」。
147
+ 与 `/minute/batch` 对称,但无前端等待、带宽极小、支持前端动态增删。
148
+
149
+ ```bash
150
+ # 基本用法(混传个股+板块/概念指数;前端用 EventSource 长连,curl 用 -N 看流)
151
+ curl -N "http://127.0.0.1:8000/api/v1/minute/sse?codes=600000,000001,880520"
152
+
153
+ # 单连接 codes 上限(默认 150,超出请分批/重连)
154
+ curl -N "http://127.0.0.1:8000/api/v1/minute/sse?codes=600000,880520&max_per_conn=150"
155
+ ```
156
+
157
+ - 事件流:`snapshot`(首连全量分时)→ 每轮 `tick`(按「当日分钟序号 `idx`」推送新增点;**正在形成的分钟 bar 价量变化也会补推**,故盘中持续有增量,不只在整分钟切换时)或 `heartbeat`(无更新保活)。
158
+ - 动态增删:前端改集合后**重连**并带新 `codes`(旧连接自动退订),标准 `EventSource` 原生支持,无需 POST 控制通道。
159
+ - 兼容个股 / 板块指数 / 概念指数(`880xxx`/`881xxx` 协议层自动识别为 SH);**不展开成分股**——传什么推什么。
160
+ - 后台 `MinuteBroadcastHub` 每次采集用**临时连接池**(与 `/minute/batch` 同款,默认 16 并发、上限 24)现场拉「所有连接订阅并集」存内存;
161
+ 多连接共享采集、各自独立游标,互不干扰;并集全局上限 500。
162
+ - 演示页:项目根 `sse_demo.html`,浏览器打开输入 codes 即可实时看分时/涨速,改集合点按钮即重连。
163
+
164
+ > 注:非交易时段也会拉取当日收盘分时(静态),演示与盘中逻辑一致;盘中自动变实时更新。
165
+
166
+ ---
167
+
168
+ ### 11. 异动拉升 SSE 推送(个股 + 板块)`GET /api/v1/screen/sse/pullup` & `/screen/sse/board-pullup`
169
+
170
+ 把「前端轮询 `screen/pullup`/`board-pullup`」升级为**后台持续扫描 + 增量事件推送**。新增内容(snapshot 全量 / alert 新触发 / 失败 heartbeat)与分时 SSE(snapshot / tick / heartbeat)同构,下游消费逻辑一致。
171
+
172
+ | 接口 | 默认扫描频率 | 检测数据源 |
173
+ |---|---|---|
174
+ | `GET /api/v1/screen/sse/pullup`(个股) | `interval=10`s | 每轮现拉涨速前 N 候选股批量分时、逐只算 |
175
+ | `GET /api/v1/screen/sse/board-pullup`(板块/概念) | `interval=15`s | 默认读后台快照记录器(15s 一刷,零成本) |
176
+
177
+ > **频率关键差异**:个股端点每轮都**现拉现算**,`interval` 直接决定新鲜度(可设 3–60s);板块端点默认读「快照记录器」(每 15s 采一次的全量板块 `change_pct` 时序),底层粒度固定 15s,**把 SSE `interval` 调更低只会重复推同一份快照**——建议保持 15s。仅当 `use_snapshot=false`(历史回放/刚启动/强制实时)才走现拉回退路径,那时 `interval` 才控制新鲜度。
178
+
179
+ **事件流(每轮 push)**
180
+ - `snapshot`:当前命中标的**全集**,前端整体替换展示。
181
+ `{"ts": <epoch>, "interval": <int>, "data": [ {记录}, ... ]}`
182
+ - `alert`:仅**状态翻转**(非异动→异动)的新触发标的才推,自动去重防刷屏;**首次连接只建基线、不补历史 alert**。
183
+ `{"ts": <epoch>, "data": {单条记录}}`
184
+ - `history`:**仅当 `receive_all_at_first=true` 且为首次事件**时推一条:今日已存储的全部 alert 历史(按 `code` 去重、每标保留最新记录)。
185
+ `{"ts": <epoch>, "count": <int>, "data": [ {记录含 stored_at}, ... ]}`
186
+ - 所有 SSE 连接实际推送出去的 `alert` 都会**同步存入服务端内存日报**(跨连接共享);
187
+ - 数据**次日 09:00 自动过期清空**(对齐交易日开盘前);
188
+ - 传 `receive_all_at_first=true` 重连/换设备时即可拿到「今天到目前为止所有触发过异动的标的」。
189
+ - `heartbeat`:仅**单轮扫描异常**时发 `{"ts":.., "kind":"scan_failed", "error":"...", "note":"本轮…失败,下轮自动重试(连接未断开)"}`。正常轮不发 heartbeat,连接持续不断。
190
+
191
+ > 下游**必须按 `event:` 字段路由**(用 `addEventListener('snapshot'/'alert'/'heartbeat')`),用 `EventSource.onmessage` 会静默丢增量。
192
+ >
193
+ > **避免 N 分钟内重复提示**:`alert` 事件已按「状态翻转」去重(持续报警期间每轮不再重推);但若个股/板块在阈值边缘反复横跳(滑动窗口导致 `is_pullup`/`is_rising_alert` 退→进),会再次触发。可传 `alert_cooldown`(秒,如 300=5 分钟)抑制冷却期内同标的重触发。最佳实践:**通知只消费 `alert` 事件、`snapshot` 仅用于列表展示**,且前端再叠加一个按 `code` 的本地冷却字典最稳。
194
+ > 注:早期版本 `alert` 事件因 `prev_hit` 在更新 `active_since` 之后才快照,导致 `alert` 永不触发(实际只有 `snapshot` 每轮推送)——该 bug 已修复,现在 `alert` 仅在状态翻转时正确推送。
195
+
196
+ ```bash
197
+ # 个股异动 SSE(盘中实时,默认 10s 扫一次)
198
+ curl -N "http://127.0.0.1:8000/api/v1/screen/sse/pullup?window=5"
199
+
200
+ # 个股异动 SSE,3 分钟级 + 5s 高频
201
+ curl -N "http://127.0.0.1:8000/api/v1/screen/sse/pullup?window=3&interval=5"
202
+
203
+ # 板块/概念异动 SSE(默认 GN 概念,15s,读快照)
204
+ curl -N "http://127.0.0.1:8000/api/v1/screen/sse/board-pullup?board_type=GN&window=3"
205
+
206
+ # 收盘/联调用 mock 模拟数据(不连真实行情)
207
+ curl -N "http://127.0.0.1:8000/api/v1/screen/sse/pullup?mock=true"
208
+ curl -N "http://127.0.0.1:8000/api/v1/screen/sse/board-pullup?mock=true"
209
+
210
+ # 抑制闪烁重触发:同一标的 5 分钟内只提示一次(alert_cooldown 单位秒)
211
+ curl -N "http://127.0.0.1:8000/api/v1/screen/sse/pullup?window=5&alert_cooldown=300"
212
+
213
+ # 开局补全:首连推 history 事件(今日已存储的全量 alert 历史),后续只发增量
214
+ curl -N "http://127.0.0.1:8000/api/v1/screen/sse/pullup?receive_all_at_first=true"
215
+ curl -N "http://127.0.0.1:8000/api/v1/screen/sse/board-pullup?receive_all_at_first=true"
216
+
217
+ # 查看 alert 日报存储状态(过期时间/事件数/去重标的数)
218
+ curl "http://127.0.0.1:8000/api/v1/screen/alert-store/status"
219
+ ```
220
+
221
+ **记录字段(两种端点通用)**
222
+
223
+ | 字段 | 含义 |
224
+ |---|---|
225
+ | `code` / `name` | 代码 / 名称 |
226
+ | `slope` / `slope_pct` | 绝对斜率(元/分钟) / **相对涨速**(%/分钟),驱动 `is_pullup` |
227
+ | `window_pct` | 窗口累计涨幅 % |
228
+ | `r2` | 价格回归**拟合优度**(越高越「线性陡峭」,排除一笔脉冲后走平) |
229
+ | `vol_spike` | 量能脉冲 = 近窗均量/基准均量(个股端点要求 >1.5 才算带量) |
230
+ | `peak_idx` | 窗口内最高价位置 |
231
+ | `peak_slope_pct` / `peak_r2` | 峰值段涨速(%/分钟) / 拟合优度,驱动 `is_rising_alert` |
232
+ | `pullback_from_peak_pct` | 距峰值回撤 %(负=已回落) |
233
+ | `jump_full` / `jump_rise` | 单步脉冲护栏(单分钟涨幅占整体比例,识别「一笔脉冲」假拉升) |
234
+ | `is_pullup` | 持续强势:斜率+R²+量能三者同时达标,此刻仍在持续陡峭拉升 |
235
+ | `is_rising_alert` | 正在猛拉/刚拉完报警:窗口内出现过陡峭线性段且回撤可控,**更早更宽容** |
236
+ | `reason` | `is_pullup` 未达标说明(如「量能未放大」);达标则「满足全部条件」 |
237
+ | `started_at` / `started_at_ts` | SSE 层附加:本连接**首次**命中时间(用于显示「已拉升 N 秒」) |
238
+
239
+ **板块记录额外字段**:`change_pct`(板块涨跌幅)、`rise_speed`/`speed_per_min`(板块涨速 %/分钟)。板块端点默认 `require_vol=false`——板块分时无量能字段、量能噪声大,**量能不参与判定**,`reason` 不会报「量能未放大」。
240
+
241
+ **判定逻辑意义**:`is_pullup`=真金白银在推(持续涨 + 线性 + 放量);`is_rising_alert`=只要在窗口内出现过一段陡峭线性拉升、且之后回撤 ≤ `pullback_tol_pct` 就报警,比 `is_pullup` 更早触发,适合「正在猛拉的那几秒就提醒」。二者可同时真(持续拉升中),也可仅 `is_rising_alert` 真(拉过一波后小幅回落)。
242
+
243
+ > 板块端点阈值默认按板块量级标定得更宽松:涨速 `0.03`%/min、`r2` 下限 `0.4`、回撤容忍 `1.5`%、`jump=1.5`,比个股端点(0.5/0.7/1.5/0.9)松约 10 倍——板块指数不可能像个股那样 5 分钟拉 2.5%。
244
+ > 别名 `/screen/pullup/stream`、`/screen/board-pullup/stream` 同上。演示页见项目根 `pullup_sse_demo.html`。
245
+
246
+ ---
247
+
248
+ ## 启动 / 停止
249
+ ```bash
250
+ # 启动(需要 tmdx[web],已装在系统 Python 3.12)
251
+ python -m easy_tdx.cli serve --host 127.0.0.1 --port 8000 --no-open-browser
252
+ # 等价:tmdx serve
253
+ ```
254
+ 当前实例由后台任务托管,Ctrl+C 或结束进程即可停止;如需重启按上面命令即可。