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.
- tmdx-3.0.0/.gitattributes +6 -0
- tmdx-3.0.0/.github/workflows/ci.yml +72 -0
- tmdx-3.0.0/.github/workflows/publish.yml +35 -0
- tmdx-3.0.0/.github/workflows/release.yml +94 -0
- tmdx-3.0.0/.gitignore +47 -0
- tmdx-3.0.0/.readthedocs.yaml +18 -0
- tmdx-3.0.0/API_QUICKSTART.md +254 -0
- tmdx-3.0.0/CHANGELOG.md +1545 -0
- tmdx-3.0.0/Dockerfile +16 -0
- tmdx-3.0.0/LICENSE +34 -0
- tmdx-3.0.0/NOTICE +24 -0
- tmdx-3.0.0/PKG-INFO +1917 -0
- tmdx-3.0.0/README.md +1867 -0
- tmdx-3.0.0/RELEASING.md +408 -0
- tmdx-3.0.0/_analyze.py +29 -0
- tmdx-3.0.0/docker-compose.yml +35 -0
- tmdx-3.0.0/docs/api_reference.md +704 -0
- tmdx-3.0.0/docs/architecture.html +682 -0
- tmdx-3.0.0/docs/backtest_usage.md +680 -0
- tmdx-3.0.0/docs/conf.py +41 -0
- tmdx-3.0.0/docs/field_mapping.md +315 -0
- tmdx-3.0.0/docs/free-a-share-quote-sdk-survey.md +275 -0
- tmdx-3.0.0/docs/index.md +16 -0
- tmdx-3.0.0/docs/indicator-bias-signal.md +167 -0
- tmdx-3.0.0/docs/indicator-zhuoyao.md +84 -0
- tmdx-3.0.0/docs/packaging.md +123 -0
- tmdx-3.0.0/docs/protocol-reverse-engineering.md +144 -0
- tmdx-3.0.0/docs/protocol-unknown-fields.md +437 -0
- tmdx-3.0.0/docs/pullup-detection-criteria.md +323 -0
- tmdx-3.0.0/docs/pullup-detection.md +142 -0
- tmdx-3.0.0/docs/pullup-open-volume-redesign.md +225 -0
- tmdx-3.0.0/docs/quantitative-guide.md +628 -0
- tmdx-3.0.0/docs/readme.md +5 -0
- tmdx-3.0.0/docs/report-snapshot-threshold-usage.md +51 -0
- tmdx-3.0.0/docs/requirements.txt +4 -0
- tmdx-3.0.0/docs/superpowers/plans/2026-06-09-backtest-engine.md +2361 -0
- tmdx-3.0.0/docs/superpowers/plans/2026-06-12-v1.11.0-factor-engine.md +1790 -0
- tmdx-3.0.0/docs/superpowers/plans/2026-06-12-v1.12.0-factor-analysis.md +877 -0
- tmdx-3.0.0/docs/superpowers/plans/2026-06-12-v1.13.0-portfolio.md +956 -0
- tmdx-3.0.0/docs/superpowers/plans/2026-06-12-v1.14.0-slippage-execution.md +1724 -0
- tmdx-3.0.0/docs/superpowers/plans/2026-06-12-v1.15.0-attribution.md +540 -0
- tmdx-3.0.0/docs/superpowers/specs/2026-06-09-backtest-engine-design.md +549 -0
- tmdx-3.0.0/docs/superpowers/specs/2026-06-12-advanced-backtest-design.md +355 -0
- tmdx-3.0.0/docs/superpowers/specs/2026-06-12-quantitative-factor-engine-design.md +437 -0
- tmdx-3.0.0/docs/tdx-quant-replacement-analysis.md +159 -0
- tmdx-3.0.0/docs/upgrade-plan-2026H2.md +135 -0
- 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
- tmdx-3.0.0/easy_tdx.spec +114 -0
- tmdx-3.0.0/examples/01_connection/async_connect.py +67 -0
- tmdx-3.0.0/examples/01_connection/connect_best_host.py +38 -0
- tmdx-3.0.0/examples/01_connection/ping_servers.py +51 -0
- tmdx-3.0.0/examples/02_market_info/market_stat.py +38 -0
- tmdx-3.0.0/examples/02_market_info/security_count.py +30 -0
- tmdx-3.0.0/examples/02_market_info/security_list.py +124 -0
- tmdx-3.0.0/examples/02_market_info/security_list_all.py +148 -0
- tmdx-3.0.0/examples/02_market_info/security_quotes.py +66 -0
- tmdx-3.0.0/examples/03_kline/index_bars.py +60 -0
- tmdx-3.0.0/examples/03_kline/security_bars.py +85 -0
- tmdx-3.0.0/examples/04_minute/history_minute_data.py +47 -0
- tmdx-3.0.0/examples/04_minute/minute_time_data.py +46 -0
- tmdx-3.0.0/examples/05_transaction/history_transaction.py +50 -0
- tmdx-3.0.0/examples/05_transaction/transaction_data.py +49 -0
- tmdx-3.0.0/examples/06_finance/README.md +135 -0
- tmdx-3.0.0/examples/06_finance/company_cli.sh +69 -0
- tmdx-3.0.0/examples/06_finance/company_info.py +103 -0
- tmdx-3.0.0/examples/06_finance/company_web_api.py +134 -0
- tmdx-3.0.0/examples/06_finance/finance_info.py +97 -0
- tmdx-3.0.0/examples/06_finance/price_limits.py +38 -0
- tmdx-3.0.0/examples/06_finance/xdxr_info.py +58 -0
- tmdx-3.0.0/examples/07_block/block_info.py +52 -0
- tmdx-3.0.0/examples/08_fund_flow/fund_flow.py +61 -0
- tmdx-3.0.0/examples/08_fund_flow/history_fund_flow.py +57 -0
- tmdx-3.0.0/examples/09_file_download/report_file.py +178 -0
- tmdx-3.0.0/examples/10_offline/block_data.py +139 -0
- tmdx-3.0.0/examples/10_offline/daily_bars.py +83 -0
- tmdx-3.0.0/examples/10_offline/detect_home.py +157 -0
- tmdx-3.0.0/examples/10_offline/ex_daily_bars.py +89 -0
- tmdx-3.0.0/examples/10_offline/gbbq.py +128 -0
- tmdx-3.0.0/examples/10_offline/history_financial.py +135 -0
- tmdx-3.0.0/examples/10_offline/min_bars.py +150 -0
- tmdx-3.0.0/examples/11_mac_quotes/quotes_list.py +81 -0
- tmdx-3.0.0/examples/11_mac_quotes/stock_quotes.py +39 -0
- tmdx-3.0.0/examples/11_realtime/realtime_feed_async.py +73 -0
- tmdx-3.0.0/examples/12_mac_kline/kline_offset.py +23 -0
- tmdx-3.0.0/examples/12_mac_kline/stock_kline.py +96 -0
- tmdx-3.0.0/examples/13_mac_tick/chart_sampling.py +49 -0
- tmdx-3.0.0/examples/13_mac_tick/multi_day_tick.py +46 -0
- tmdx-3.0.0/examples/13_mac_tick/tick_chart.py +59 -0
- tmdx-3.0.0/examples/14_mac_transaction/transaction.py +80 -0
- tmdx-3.0.0/examples/15_mac_board/belong_board.py +42 -0
- tmdx-3.0.0/examples/15_mac_board/board_list.py +70 -0
- tmdx-3.0.0/examples/15_mac_board/board_members.py +61 -0
- tmdx-3.0.0/examples/15_mac_board/board_ranking.py +48 -0
- tmdx-3.0.0/examples/15_mac_board/board_summary.py +57 -0
- tmdx-3.0.0/examples/16_mac_capital/capital_flow.py +53 -0
- tmdx-3.0.0/examples/17_mac_monitor/auction.py +41 -0
- tmdx-3.0.0/examples/17_mac_monitor/server_info.py +29 -0
- tmdx-3.0.0/examples/17_mac_monitor/symbol_info.py +41 -0
- tmdx-3.0.0/examples/17_mac_monitor/unusual.py +80 -0
- tmdx-3.0.0/examples/18_mac_ex/ex_goods_list.py +65 -0
- tmdx-3.0.0/examples/18_mac_ex/ex_kline.py +72 -0
- tmdx-3.0.0/examples/18_mac_ex/ex_quotes.py +42 -0
- tmdx-3.0.0/examples/18_mac_ex/ex_tick_chart.py +61 -0
- tmdx-3.0.0/examples/19_unified/unified_client.py +58 -0
- tmdx-3.0.0/examples/20_cli/cli_examples.sh +456 -0
- tmdx-3.0.0/examples/21_indicator/basic_indicators.py +142 -0
- tmdx-3.0.0/examples/21_indicator/list_indicators.py +50 -0
- tmdx-3.0.0/examples/22_backtest_advanced/backtest_601088_advanced.py +138 -0
- tmdx-3.0.0/examples/23_screen_strength/README.md +96 -0
- tmdx-3.0.0/examples/23_screen_strength/strength_api.py +104 -0
- tmdx-3.0.0/examples/23_screen_strength/strength_cli.sh +78 -0
- tmdx-3.0.0/examples/23_screen_strength/strength_web_api.py +112 -0
- tmdx-3.0.0/pullup_sse_demo.html +271 -0
- tmdx-3.0.0/pyproject.toml +119 -0
- tmdx-3.0.0/requirements-dev.txt +33 -0
- tmdx-3.0.0/run_all_strategies.py +550 -0
- tmdx-3.0.0/scripts/__init__.py +0 -0
- tmdx-3.0.0/scripts/bench_engine.py +129 -0
- tmdx-3.0.0/scripts/export_limit_events.py +617 -0
- tmdx-3.0.0/scripts/probe_unknowns.py +107 -0
- tmdx-3.0.0/scripts/release/release.bat +58 -0
- tmdx-3.0.0/scripts/release/release.py +682 -0
- tmdx-3.0.0/scripts/report_snapshot_threshold.py +251 -0
- tmdx-3.0.0/scripts/ruff_hook.py +43 -0
- tmdx-3.0.0/scripts/verify-token/token1.txt +1 -0
- tmdx-3.0.0/scripts/verify-token/verify-token.bat +58 -0
- tmdx-3.0.0/scripts/verify-token/verify_token.py +570 -0
- tmdx-3.0.0/scripts/verify_ci.sh +60 -0
- tmdx-3.0.0/scripts/verify_fixes_20260415.py +128 -0
- tmdx-3.0.0/scripts/verify_issue64.py +69 -0
- tmdx-3.0.0/scripts/ws_smoke.py +86 -0
- tmdx-3.0.0/src/easy_tdx/MyTT.py +687 -0
- tmdx-3.0.0/src/easy_tdx/MyTT.pyi +123 -0
- tmdx-3.0.0/src/easy_tdx/__init__.py +133 -0
- tmdx-3.0.0/src/easy_tdx/__main__.py +145 -0
- tmdx-3.0.0/src/easy_tdx/_binary.py +48 -0
- tmdx-3.0.0/src/easy_tdx/_df.py +238 -0
- tmdx-3.0.0/src/easy_tdx/_dist.py +18 -0
- tmdx-3.0.0/src/easy_tdx/_health.py +163 -0
- tmdx-3.0.0/src/easy_tdx/_reconnect.py +329 -0
- tmdx-3.0.0/src/easy_tdx/ai/__init__.py +35 -0
- tmdx-3.0.0/src/easy_tdx/ai/llm.py +401 -0
- tmdx-3.0.0/src/easy_tdx/backtest/__init__.py +63 -0
- tmdx-3.0.0/src/easy_tdx/backtest/attribution.py +217 -0
- tmdx-3.0.0/src/easy_tdx/backtest/benchmark.py +282 -0
- tmdx-3.0.0/src/easy_tdx/backtest/cli.py +510 -0
- tmdx-3.0.0/src/easy_tdx/backtest/combo.py +458 -0
- tmdx-3.0.0/src/easy_tdx/backtest/dsl.py +55 -0
- tmdx-3.0.0/src/easy_tdx/backtest/engine.py +720 -0
- tmdx-3.0.0/src/easy_tdx/backtest/execution.py +655 -0
- tmdx-3.0.0/src/easy_tdx/backtest/fees.py +182 -0
- tmdx-3.0.0/src/easy_tdx/backtest/fitness.py +366 -0
- tmdx-3.0.0/src/easy_tdx/backtest/formula_strategy.py +187 -0
- tmdx-3.0.0/src/easy_tdx/backtest/grading.py +610 -0
- tmdx-3.0.0/src/easy_tdx/backtest/indicator_cache.py +94 -0
- tmdx-3.0.0/src/easy_tdx/backtest/multi_strategy_engine.py +249 -0
- tmdx-3.0.0/src/easy_tdx/backtest/optimizer.py +365 -0
- tmdx-3.0.0/src/easy_tdx/backtest/orders.py +544 -0
- tmdx-3.0.0/src/easy_tdx/backtest/performance.py +444 -0
- tmdx-3.0.0/src/easy_tdx/backtest/portfolio.py +200 -0
- tmdx-3.0.0/src/easy_tdx/backtest/portfolio_engine.py +282 -0
- tmdx-3.0.0/src/easy_tdx/backtest/rotation.py +429 -0
- tmdx-3.0.0/src/easy_tdx/backtest/scoring.py +130 -0
- tmdx-3.0.0/src/easy_tdx/backtest/slippage.py +129 -0
- tmdx-3.0.0/src/easy_tdx/backtest/strategies/__init__.py +41 -0
- tmdx-3.0.0/src/easy_tdx/backtest/strategies/builtin.py +805 -0
- tmdx-3.0.0/src/easy_tdx/backtest/strategies/presets.py +112 -0
- tmdx-3.0.0/src/easy_tdx/backtest/strategies/registry.py +340 -0
- tmdx-3.0.0/src/easy_tdx/backtest/strategy.py +537 -0
- tmdx-3.0.0/src/easy_tdx/backtest/types.py +199 -0
- tmdx-3.0.0/src/easy_tdx/backtest/validation.py +274 -0
- tmdx-3.0.0/src/easy_tdx/backtest/walkforward.py +268 -0
- tmdx-3.0.0/src/easy_tdx/ccpm/__init__.py +37 -0
- tmdx-3.0.0/src/easy_tdx/ccpm/client.py +314 -0
- tmdx-3.0.0/src/easy_tdx/ccpm/models.py +130 -0
- tmdx-3.0.0/src/easy_tdx/chanlun/__init__.py +47 -0
- tmdx-3.0.0/src/easy_tdx/chanlun/analyser.py +285 -0
- tmdx-3.0.0/src/easy_tdx/chanlun/beichi.py +181 -0
- tmdx-3.0.0/src/easy_tdx/chanlun/bi.py +118 -0
- tmdx-3.0.0/src/easy_tdx/chanlun/config.py +56 -0
- tmdx-3.0.0/src/easy_tdx/chanlun/fractal.py +76 -0
- tmdx-3.0.0/src/easy_tdx/chanlun/kline_merge.py +101 -0
- tmdx-3.0.0/src/easy_tdx/chanlun/macd.py +104 -0
- tmdx-3.0.0/src/easy_tdx/chanlun/mmd.py +160 -0
- tmdx-3.0.0/src/easy_tdx/chanlun/multi_level.py +259 -0
- tmdx-3.0.0/src/easy_tdx/chanlun/types.py +213 -0
- tmdx-3.0.0/src/easy_tdx/chanlun/xd.py +128 -0
- tmdx-3.0.0/src/easy_tdx/chanlun/zs.py +103 -0
- tmdx-3.0.0/src/easy_tdx/chanlun/zsd.py +143 -0
- tmdx-3.0.0/src/easy_tdx/cli/__init__.py +105 -0
- tmdx-3.0.0/src/easy_tdx/cli/cmd_admin.py +49 -0
- tmdx-3.0.0/src/easy_tdx/cli/cmd_announcement.py +101 -0
- tmdx-3.0.0/src/easy_tdx/cli/cmd_auction.py +30 -0
- tmdx-3.0.0/src/easy_tdx/cli/cmd_board.py +267 -0
- tmdx-3.0.0/src/easy_tdx/cli/cmd_capital.py +30 -0
- tmdx-3.0.0/src/easy_tdx/cli/cmd_ccpm.py +98 -0
- tmdx-3.0.0/src/easy_tdx/cli/cmd_chanlun.py +238 -0
- tmdx-3.0.0/src/easy_tdx/cli/cmd_company.py +253 -0
- tmdx-3.0.0/src/easy_tdx/cli/cmd_ex.py +180 -0
- tmdx-3.0.0/src/easy_tdx/cli/cmd_factor.py +91 -0
- tmdx-3.0.0/src/easy_tdx/cli/cmd_finance.py +63 -0
- tmdx-3.0.0/src/easy_tdx/cli/cmd_formula.py +208 -0
- tmdx-3.0.0/src/easy_tdx/cli/cmd_indicator.py +131 -0
- tmdx-3.0.0/src/easy_tdx/cli/cmd_info.py +51 -0
- tmdx-3.0.0/src/easy_tdx/cli/cmd_kline.py +67 -0
- tmdx-3.0.0/src/easy_tdx/cli/cmd_monitor.py +58 -0
- tmdx-3.0.0/src/easy_tdx/cli/cmd_offline.py +702 -0
- tmdx-3.0.0/src/easy_tdx/cli/cmd_pfactor.py +58 -0
- tmdx-3.0.0/src/easy_tdx/cli/cmd_quote.py +81 -0
- tmdx-3.0.0/src/easy_tdx/cli/cmd_run_all.py +562 -0
- tmdx-3.0.0/src/easy_tdx/cli/cmd_tick.py +44 -0
- tmdx-3.0.0/src/easy_tdx/cli/cmd_transaction.py +43 -0
- tmdx-3.0.0/src/easy_tdx/cli/cmd_warehouse.py +172 -0
- tmdx-3.0.0/src/easy_tdx/cli/cmd_web.py +79 -0
- tmdx-3.0.0/src/easy_tdx/cli/conn.py +58 -0
- tmdx-3.0.0/src/easy_tdx/cli/output.py +71 -0
- tmdx-3.0.0/src/easy_tdx/cli/parsers.py +188 -0
- tmdx-3.0.0/src/easy_tdx/client.py +1775 -0
- tmdx-3.0.0/src/easy_tdx/cninfo/__init__.py +18 -0
- tmdx-3.0.0/src/easy_tdx/cninfo/client.py +274 -0
- tmdx-3.0.0/src/easy_tdx/cninfo/models.py +64 -0
- tmdx-3.0.0/src/easy_tdx/codec/__init__.py +18 -0
- tmdx-3.0.0/src/easy_tdx/codec/bitmap.py +504 -0
- tmdx-3.0.0/src/easy_tdx/codec/block.py +71 -0
- tmdx-3.0.0/src/easy_tdx/codec/datetime_.py +64 -0
- tmdx-3.0.0/src/easy_tdx/codec/financial.py +101 -0
- tmdx-3.0.0/src/easy_tdx/codec/frame.py +67 -0
- tmdx-3.0.0/src/easy_tdx/codec/industry.py +24 -0
- tmdx-3.0.0/src/easy_tdx/codec/mac_frame.py +50 -0
- tmdx-3.0.0/src/easy_tdx/codec/price.py +64 -0
- tmdx-3.0.0/src/easy_tdx/codec/price_rules.py +94 -0
- tmdx-3.0.0/src/easy_tdx/codec/volume.py +56 -0
- tmdx-3.0.0/src/easy_tdx/commands/__init__.py +10 -0
- tmdx-3.0.0/src/easy_tdx/commands/base.py +29 -0
- tmdx-3.0.0/src/easy_tdx/commands/block_info.py +61 -0
- tmdx-3.0.0/src/easy_tdx/commands/company_info.py +84 -0
- tmdx-3.0.0/src/easy_tdx/commands/finance_info.py +116 -0
- tmdx-3.0.0/src/easy_tdx/commands/minute_time.py +70 -0
- tmdx-3.0.0/src/easy_tdx/commands/report_file.py +32 -0
- tmdx-3.0.0/src/easy_tdx/commands/security_bars.py +271 -0
- tmdx-3.0.0/src/easy_tdx/commands/security_count.py +25 -0
- tmdx-3.0.0/src/easy_tdx/commands/security_list.py +68 -0
- tmdx-3.0.0/src/easy_tdx/commands/security_quotes.py +262 -0
- tmdx-3.0.0/src/easy_tdx/commands/setup.py +15 -0
- tmdx-3.0.0/src/easy_tdx/commands/transaction.py +113 -0
- tmdx-3.0.0/src/easy_tdx/commands/xdxr_info.py +107 -0
- tmdx-3.0.0/src/easy_tdx/config.py +330 -0
- tmdx-3.0.0/src/easy_tdx/ex/__init__.py +15 -0
- tmdx-3.0.0/src/easy_tdx/ex/_hk_transaction.py +215 -0
- tmdx-3.0.0/src/easy_tdx/ex/client.py +575 -0
- tmdx-3.0.0/src/easy_tdx/ex/commands/__init__.py +1 -0
- tmdx-3.0.0/src/easy_tdx/ex/commands/get_history_bars_range.py +76 -0
- tmdx-3.0.0/src/easy_tdx/ex/commands/get_instrument_bars.py +74 -0
- tmdx-3.0.0/src/easy_tdx/ex/commands/get_instrument_count.py +17 -0
- tmdx-3.0.0/src/easy_tdx/ex/commands/get_instrument_info.py +50 -0
- tmdx-3.0.0/src/easy_tdx/ex/commands/get_instrument_quote.py +103 -0
- tmdx-3.0.0/src/easy_tdx/ex/commands/get_instrument_quote_list.py +218 -0
- tmdx-3.0.0/src/easy_tdx/ex/commands/get_markets.py +41 -0
- tmdx-3.0.0/src/easy_tdx/ex/commands/get_minute_time.py +74 -0
- tmdx-3.0.0/src/easy_tdx/ex/commands/get_transaction.py +97 -0
- tmdx-3.0.0/src/easy_tdx/ex/commands/login.py +51 -0
- tmdx-3.0.0/src/easy_tdx/ex/commands/setup.py +16 -0
- tmdx-3.0.0/src/easy_tdx/ex/mac_client.py +903 -0
- tmdx-3.0.0/src/easy_tdx/ex/models.py +137 -0
- tmdx-3.0.0/src/easy_tdx/ex/transport/__init__.py +1 -0
- tmdx-3.0.0/src/easy_tdx/ex/transport/async_.py +117 -0
- tmdx-3.0.0/src/easy_tdx/ex/transport/sync.py +158 -0
- tmdx-3.0.0/src/easy_tdx/exceptions.py +25 -0
- tmdx-3.0.0/src/easy_tdx/factor/__init__.py +36 -0
- tmdx-3.0.0/src/easy_tdx/factor/analysis.py +156 -0
- tmdx-3.0.0/src/easy_tdx/factor/base.py +51 -0
- tmdx-3.0.0/src/easy_tdx/factor/builtin/__init__.py +40 -0
- tmdx-3.0.0/src/easy_tdx/factor/builtin/chanlun.py +90 -0
- tmdx-3.0.0/src/easy_tdx/factor/builtin/momentum.py +40 -0
- tmdx-3.0.0/src/easy_tdx/factor/builtin/quality.py +55 -0
- tmdx-3.0.0/src/easy_tdx/factor/builtin/technical.py +55 -0
- tmdx-3.0.0/src/easy_tdx/factor/builtin/value.py +29 -0
- tmdx-3.0.0/src/easy_tdx/factor/builtin/volatility.py +58 -0
- tmdx-3.0.0/src/easy_tdx/factor/builtin/volume.py +63 -0
- tmdx-3.0.0/src/easy_tdx/factor/engine.py +136 -0
- tmdx-3.0.0/src/easy_tdx/factor/transform.py +197 -0
- tmdx-3.0.0/src/easy_tdx/formula.py +519 -0
- tmdx-3.0.0/src/easy_tdx/indicator.py +334 -0
- tmdx-3.0.0/src/easy_tdx/mac/__init__.py +1 -0
- tmdx-3.0.0/src/easy_tdx/mac/adjust.py +178 -0
- tmdx-3.0.0/src/easy_tdx/mac/client.py +2160 -0
- tmdx-3.0.0/src/easy_tdx/mac/commands/__init__.py +34 -0
- tmdx-3.0.0/src/easy_tdx/mac/commands/board_list.py +109 -0
- tmdx-3.0.0/src/easy_tdx/mac/commands/board_members_quotes.py +121 -0
- tmdx-3.0.0/src/easy_tdx/mac/commands/chart_sampling.py +47 -0
- tmdx-3.0.0/src/easy_tdx/mac/commands/file_query.py +90 -0
- tmdx-3.0.0/src/easy_tdx/mac/commands/goods_list.py +80 -0
- tmdx-3.0.0/src/easy_tdx/mac/commands/kline_offset.py +38 -0
- tmdx-3.0.0/src/easy_tdx/mac/commands/server_info.py +74 -0
- tmdx-3.0.0/src/easy_tdx/mac/commands/symbol_auction.py +66 -0
- tmdx-3.0.0/src/easy_tdx/mac/commands/symbol_bar.py +122 -0
- tmdx-3.0.0/src/easy_tdx/mac/commands/symbol_belong_board.py +90 -0
- tmdx-3.0.0/src/easy_tdx/mac/commands/symbol_capital_flow.py +85 -0
- tmdx-3.0.0/src/easy_tdx/mac/commands/symbol_info.py +88 -0
- tmdx-3.0.0/src/easy_tdx/mac/commands/symbol_quotes.py +103 -0
- tmdx-3.0.0/src/easy_tdx/mac/commands/symbol_tick_chart.py +112 -0
- tmdx-3.0.0/src/easy_tdx/mac/commands/symbol_transaction.py +76 -0
- tmdx-3.0.0/src/easy_tdx/mac/commands/tick_charts.py +149 -0
- tmdx-3.0.0/src/easy_tdx/mac/commands/unusual.py +227 -0
- tmdx-3.0.0/src/easy_tdx/mac/enums.py +238 -0
- tmdx-3.0.0/src/easy_tdx/mac/models.py +222 -0
- tmdx-3.0.0/src/easy_tdx/mac/qfq_check.py +312 -0
- tmdx-3.0.0/src/easy_tdx/models/__init__.py +35 -0
- tmdx-3.0.0/src/easy_tdx/models/bar.py +28 -0
- tmdx-3.0.0/src/easy_tdx/models/enums.py +88 -0
- tmdx-3.0.0/src/easy_tdx/models/finance.py +157 -0
- tmdx-3.0.0/src/easy_tdx/models/quote.py +90 -0
- tmdx-3.0.0/src/easy_tdx/models/security.py +23 -0
- tmdx-3.0.0/src/easy_tdx/models/stats.py +81 -0
- tmdx-3.0.0/src/easy_tdx/models/timeseries.py +39 -0
- tmdx-3.0.0/src/easy_tdx/offline/__init__.py +74 -0
- tmdx-3.0.0/src/easy_tdx/offline/block.py +92 -0
- tmdx-3.0.0/src/easy_tdx/offline/daily_bar.py +142 -0
- tmdx-3.0.0/src/easy_tdx/offline/ex_daily_bar.py +81 -0
- tmdx-3.0.0/src/easy_tdx/offline/finders.py +38 -0
- tmdx-3.0.0/src/easy_tdx/offline/gbbq.py +366 -0
- tmdx-3.0.0/src/easy_tdx/offline/history_financial.py +59 -0
- tmdx-3.0.0/src/easy_tdx/offline/min_bar.py +122 -0
- tmdx-3.0.0/src/easy_tdx/offline/paths.py +74 -0
- tmdx-3.0.0/src/easy_tdx/offline/write_daily.py +194 -0
- tmdx-3.0.0/src/easy_tdx/offline/write_ex_daily.py +93 -0
- tmdx-3.0.0/src/easy_tdx/offline/write_min_bar.py +186 -0
- tmdx-3.0.0/src/easy_tdx/portfolio/__init__.py +26 -0
- tmdx-3.0.0/src/easy_tdx/portfolio/optimizer.py +216 -0
- tmdx-3.0.0/src/easy_tdx/portfolio/rebalance.py +266 -0
- tmdx-3.0.0/src/easy_tdx/portfolio/risk.py +56 -0
- tmdx-3.0.0/src/easy_tdx/portfolio/types.py +30 -0
- tmdx-3.0.0/src/easy_tdx/py.typed +1 -0
- tmdx-3.0.0/src/easy_tdx/realtime/__init__.py +38 -0
- tmdx-3.0.0/src/easy_tdx/realtime/engine.py +217 -0
- tmdx-3.0.0/src/easy_tdx/realtime/feed.py +337 -0
- tmdx-3.0.0/src/easy_tdx/realtime/session.py +74 -0
- tmdx-3.0.0/src/easy_tdx/screen/__init__.py +43 -0
- tmdx-3.0.0/src/easy_tdx/screen/alert_persister.py +135 -0
- tmdx-3.0.0/src/easy_tdx/screen/alert_store.py +284 -0
- tmdx-3.0.0/src/easy_tdx/screen/board_snapshot.py +783 -0
- tmdx-3.0.0/src/easy_tdx/screen/board_snapshot_log.py +189 -0
- tmdx-3.0.0/src/easy_tdx/screen/cli.py +431 -0
- tmdx-3.0.0/src/easy_tdx/screen/defaults.py +109 -0
- tmdx-3.0.0/src/easy_tdx/screen/minute_hub.py +377 -0
- tmdx-3.0.0/src/easy_tdx/screen/pullup.py +292 -0
- tmdx-3.0.0/src/easy_tdx/screen/ranker.py +364 -0
- tmdx-3.0.0/src/easy_tdx/screen/scanner.py +711 -0
- tmdx-3.0.0/src/easy_tdx/screen/stock_pullup_scanner.py +99 -0
- tmdx-3.0.0/src/easy_tdx/screen/strength.py +498 -0
- tmdx-3.0.0/src/easy_tdx/screen/universe.py +190 -0
- tmdx-3.0.0/src/easy_tdx/sina/__init__.py +31 -0
- tmdx-3.0.0/src/easy_tdx/sina/client.py +165 -0
- tmdx-3.0.0/src/easy_tdx/sina/models.py +53 -0
- tmdx-3.0.0/src/easy_tdx/transport/__init__.py +4 -0
- tmdx-3.0.0/src/easy_tdx/transport/async_.py +151 -0
- tmdx-3.0.0/src/easy_tdx/transport/sync.py +296 -0
- tmdx-3.0.0/src/easy_tdx/tray.py +127 -0
- tmdx-3.0.0/src/easy_tdx/unified.py +648 -0
- tmdx-3.0.0/src/easy_tdx/warehouse/__init__.py +21 -0
- tmdx-3.0.0/src/easy_tdx/warehouse/store.py +423 -0
- tmdx-3.0.0/src/easy_tdx/warehouse/sync.py +155 -0
- tmdx-3.0.0/src/easy_tdx/web/__init__.py +53 -0
- tmdx-3.0.0/src/easy_tdx/web/app.py +563 -0
- tmdx-3.0.0/src/easy_tdx/web/backtest_schemas.py +559 -0
- tmdx-3.0.0/src/easy_tdx/web/convert.py +234 -0
- tmdx-3.0.0/src/easy_tdx/web/datasource.py +364 -0
- tmdx-3.0.0/src/easy_tdx/web/deps.py +44 -0
- tmdx-3.0.0/src/easy_tdx/web/e2e_mock.py +498 -0
- tmdx-3.0.0/src/easy_tdx/web/errors.py +46 -0
- tmdx-3.0.0/src/easy_tdx/web/llm_history_store.py +194 -0
- tmdx-3.0.0/src/easy_tdx/web/quote_streamer.py +234 -0
- tmdx-3.0.0/src/easy_tdx/web/realtime_hub.py +247 -0
- tmdx-3.0.0/src/easy_tdx/web/routers/__init__.py +1 -0
- tmdx-3.0.0/src/easy_tdx/web/routers/announcement.py +37 -0
- tmdx-3.0.0/src/easy_tdx/web/routers/backtest.py +1122 -0
- tmdx-3.0.0/src/easy_tdx/web/routers/bars.py +688 -0
- tmdx-3.0.0/src/easy_tdx/web/routers/block.py +29 -0
- tmdx-3.0.0/src/easy_tdx/web/routers/board_mac.py +129 -0
- tmdx-3.0.0/src/easy_tdx/web/routers/ccpm.py +88 -0
- tmdx-3.0.0/src/easy_tdx/web/routers/chanlun.py +58 -0
- tmdx-3.0.0/src/easy_tdx/web/routers/ex_market.py +84 -0
- tmdx-3.0.0/src/easy_tdx/web/routers/finance.py +85 -0
- tmdx-3.0.0/src/easy_tdx/web/routers/formula.py +260 -0
- tmdx-3.0.0/src/easy_tdx/web/routers/indicator.py +45 -0
- tmdx-3.0.0/src/easy_tdx/web/routers/llm.py +277 -0
- tmdx-3.0.0/src/easy_tdx/web/routers/mac_data.py +48 -0
- tmdx-3.0.0/src/easy_tdx/web/routers/mac_quotes.py +69 -0
- tmdx-3.0.0/src/easy_tdx/web/routers/market.py +199 -0
- tmdx-3.0.0/src/easy_tdx/web/routers/realtime.py +122 -0
- tmdx-3.0.0/src/easy_tdx/web/routers/screen.py +1111 -0
- tmdx-3.0.0/src/easy_tdx/web/routers/server.py +162 -0
- tmdx-3.0.0/src/easy_tdx/web/routers/sina.py +39 -0
- tmdx-3.0.0/src/easy_tdx/web/routers/strategies.py +96 -0
- tmdx-3.0.0/src/easy_tdx/web/routers/stream.py +63 -0
- tmdx-3.0.0/src/easy_tdx/web/routers/watchlist.py +51 -0
- tmdx-3.0.0/src/easy_tdx/web/schemas.py +153 -0
- tmdx-3.0.0/src/easy_tdx/web/signal_scan.py +331 -0
- tmdx-3.0.0/src/easy_tdx/web/strategy_store.py +232 -0
- tmdx-3.0.0/src/easy_tdx/web/task_runner.py +327 -0
- tmdx-3.0.0/src/easy_tdx/web/task_store.py +321 -0
- tmdx-3.0.0/src/easy_tdx/web/watchlist_store.py +183 -0
- tmdx-3.0.0/sse_demo.html +143 -0
- tmdx-3.0.0/strategies/README.md +51 -0
- tmdx-3.0.0/strategies/bias_reversal.py +28 -0
- tmdx-3.0.0/strategies/bollinger_breakout.py +28 -0
- tmdx-3.0.0/strategies/cci_breakout.py +37 -0
- tmdx-3.0.0/strategies/demo/1.png +0 -0
- tmdx-3.0.0/strategies/demo/2.png +0 -0
- tmdx-3.0.0/strategies/demo/3.png +0 -0
- tmdx-3.0.0/strategies/demo/4.png +0 -0
- tmdx-3.0.0/strategies/dmi_trend.py +40 -0
- tmdx-3.0.0/strategies/expma_cross.py +27 -0
- tmdx-3.0.0/strategies/kdj_golden.py +30 -0
- tmdx-3.0.0/strategies/ma_cross.py +27 -0
- tmdx-3.0.0/strategies/macd_cross.py +26 -0
- tmdx-3.0.0/strategies/mfi_volume.py +37 -0
- tmdx-3.0.0/strategies/mtm_momentum.py +33 -0
- tmdx-3.0.0/strategies/obv_trend.py +48 -0
- tmdx-3.0.0/strategies/rsi_reversal.py +26 -0
- tmdx-3.0.0/strategies/trix_cross.py +31 -0
- tmdx-3.0.0/strategies/turtle_breakout.py +29 -0
- tmdx-3.0.0/strategies/volume_price.py +35 -0
- tmdx-3.0.0/strategies/zhuoyao_momentum.py +47 -0
- tmdx-3.0.0/strategies/zig_breakout.py +83 -0
- tmdx-3.0.0/tests/__init__.py +0 -0
- tmdx-3.0.0/tests/conftest.py +16 -0
- tmdx-3.0.0/tests/fixtures/company_info_category.hex +1 -0
- tmdx-3.0.0/tests/fixtures/company_info_category.json +8 -0
- tmdx-3.0.0/tests/fixtures/company_info_content.hex +1 -0
- tmdx-3.0.0/tests/fixtures/company_info_content.json +4 -0
- tmdx-3.0.0/tests/fixtures/ex_history_transaction.hex +1 -0
- tmdx-3.0.0/tests/fixtures/ex_history_transaction.json +22 -0
- tmdx-3.0.0/tests/fixtures/finance_info.hex +1 -0
- tmdx-3.0.0/tests/fixtures/finance_info.json +5 -0
- tmdx-3.0.0/tests/fixtures/history_minute_time.hex +1 -0
- tmdx-3.0.0/tests/fixtures/history_minute_time.json +7 -0
- tmdx-3.0.0/tests/fixtures/history_transaction.hex +1 -0
- tmdx-3.0.0/tests/fixtures/history_transaction.json +9 -0
- tmdx-3.0.0/tests/fixtures/minute_time.hex +1 -0
- tmdx-3.0.0/tests/fixtures/minute_time.json +8 -0
- tmdx-3.0.0/tests/fixtures/security_bars.hex +1 -0
- tmdx-3.0.0/tests/fixtures/security_bars.json +10 -0
- tmdx-3.0.0/tests/fixtures/security_count.hex +1 -0
- tmdx-3.0.0/tests/fixtures/security_count.json +3 -0
- tmdx-3.0.0/tests/fixtures/security_list.hex +1 -0
- tmdx-3.0.0/tests/fixtures/security_list.json +8 -0
- tmdx-3.0.0/tests/fixtures/security_quotes.hex +1 -0
- tmdx-3.0.0/tests/fixtures/security_quotes.json +9 -0
- tmdx-3.0.0/tests/fixtures/transaction.hex +1 -0
- tmdx-3.0.0/tests/fixtures/transaction.json +9 -0
- tmdx-3.0.0/tests/fixtures/xdxr_info.hex +1 -0
- tmdx-3.0.0/tests/fixtures/xdxr_info.json +9 -0
- tmdx-3.0.0/tests/golden/backtest_metrics.json +354 -0
- tmdx-3.0.0/tests/integration/__init__.py +0 -0
- tmdx-3.0.0/tests/integration/test_live_client.py +36 -0
- tmdx-3.0.0/tests/unit/__init__.py +0 -0
- tmdx-3.0.0/tests/unit/test_a_share_extensions.py +369 -0
- tmdx-3.0.0/tests/unit/test_ai_llm.py +434 -0
- tmdx-3.0.0/tests/unit/test_async_transport.py +139 -0
- tmdx-3.0.0/tests/unit/test_backtest_attribution.py +211 -0
- tmdx-3.0.0/tests/unit/test_backtest_cli.py +30 -0
- tmdx-3.0.0/tests/unit/test_backtest_combo.py +397 -0
- tmdx-3.0.0/tests/unit/test_backtest_engine.py +728 -0
- tmdx-3.0.0/tests/unit/test_backtest_engine_vector.py +320 -0
- tmdx-3.0.0/tests/unit/test_backtest_execution.py +420 -0
- tmdx-3.0.0/tests/unit/test_backtest_fees.py +206 -0
- tmdx-3.0.0/tests/unit/test_backtest_fitness_benchmark.py +196 -0
- tmdx-3.0.0/tests/unit/test_backtest_orders.py +423 -0
- tmdx-3.0.0/tests/unit/test_backtest_performance.py +612 -0
- tmdx-3.0.0/tests/unit/test_backtest_portfolio.py +360 -0
- tmdx-3.0.0/tests/unit/test_backtest_rotation.py +159 -0
- tmdx-3.0.0/tests/unit/test_backtest_slippage.py +161 -0
- tmdx-3.0.0/tests/unit/test_backtest_strategy.py +541 -0
- tmdx-3.0.0/tests/unit/test_backtest_types.py +267 -0
- tmdx-3.0.0/tests/unit/test_backtest_walkforward.py +176 -0
- tmdx-3.0.0/tests/unit/test_bars_min120_derived.py +84 -0
- tmdx-3.0.0/tests/unit/test_bars_vol_semantics.py +112 -0
- tmdx-3.0.0/tests/unit/test_block_info.py +109 -0
- tmdx-3.0.0/tests/unit/test_board_change_ranking.py +337 -0
- tmdx-3.0.0/tests/unit/test_board_list.py +180 -0
- tmdx-3.0.0/tests/unit/test_ccpm.py +392 -0
- tmdx-3.0.0/tests/unit/test_chanlun.py +725 -0
- tmdx-3.0.0/tests/unit/test_chanlun_phase2.py +265 -0
- tmdx-3.0.0/tests/unit/test_chanlun_phase3.py +242 -0
- tmdx-3.0.0/tests/unit/test_client_reconnect.py +158 -0
- tmdx-3.0.0/tests/unit/test_cninfo.py +730 -0
- tmdx-3.0.0/tests/unit/test_codec_bitmap.py +126 -0
- tmdx-3.0.0/tests/unit/test_codec_datetime.py +110 -0
- tmdx-3.0.0/tests/unit/test_codec_frame.py +39 -0
- tmdx-3.0.0/tests/unit/test_codec_price.py +62 -0
- tmdx-3.0.0/tests/unit/test_codec_volume.py +36 -0
- tmdx-3.0.0/tests/unit/test_commands_offline.py +552 -0
- tmdx-3.0.0/tests/unit/test_config.py +201 -0
- tmdx-3.0.0/tests/unit/test_decode_errors.py +113 -0
- tmdx-3.0.0/tests/unit/test_e2e_mock.py +166 -0
- tmdx-3.0.0/tests/unit/test_ex_reconnect.py +318 -0
- tmdx-3.0.0/tests/unit/test_ex_tick_chart_date.py +259 -0
- tmdx-3.0.0/tests/unit/test_factor_analysis.py +117 -0
- tmdx-3.0.0/tests/unit/test_factor_base.py +119 -0
- tmdx-3.0.0/tests/unit/test_factor_builtin.py +211 -0
- tmdx-3.0.0/tests/unit/test_factor_engine.py +206 -0
- tmdx-3.0.0/tests/unit/test_factor_transform.py +119 -0
- tmdx-3.0.0/tests/unit/test_failover.py +779 -0
- tmdx-3.0.0/tests/unit/test_financial_data.py +116 -0
- tmdx-3.0.0/tests/unit/test_formula.py +214 -0
- tmdx-3.0.0/tests/unit/test_formula_integration.py +193 -0
- tmdx-3.0.0/tests/unit/test_golden_backtest.py +292 -0
- tmdx-3.0.0/tests/unit/test_grading_scoring.py +285 -0
- tmdx-3.0.0/tests/unit/test_health.py +149 -0
- tmdx-3.0.0/tests/unit/test_heartbeat.py +70 -0
- tmdx-3.0.0/tests/unit/test_hk_transaction.py +433 -0
- tmdx-3.0.0/tests/unit/test_indicator.py +160 -0
- tmdx-3.0.0/tests/unit/test_kline_bar_time.py +213 -0
- tmdx-3.0.0/tests/unit/test_llm_history_store.py +69 -0
- tmdx-3.0.0/tests/unit/test_mac_qfq_adjust.py +209 -0
- tmdx-3.0.0/tests/unit/test_mac_qfq_integration.py +184 -0
- tmdx-3.0.0/tests/unit/test_mac_tick_charts.py +188 -0
- tmdx-3.0.0/tests/unit/test_multi_strategy.py +258 -0
- tmdx-3.0.0/tests/unit/test_mytt.py +216 -0
- tmdx-3.0.0/tests/unit/test_mytt_zig.py +66 -0
- tmdx-3.0.0/tests/unit/test_optimizer.py +168 -0
- tmdx-3.0.0/tests/unit/test_optimizer_cache_multiseed.py +218 -0
- tmdx-3.0.0/tests/unit/test_portfolio_engine.py +197 -0
- tmdx-3.0.0/tests/unit/test_portfolio_optimizer.py +95 -0
- tmdx-3.0.0/tests/unit/test_portfolio_rebalance.py +131 -0
- tmdx-3.0.0/tests/unit/test_portfolio_risk.py +45 -0
- tmdx-3.0.0/tests/unit/test_protocol_fixes.py +130 -0
- tmdx-3.0.0/tests/unit/test_public_api.py +139 -0
- tmdx-3.0.0/tests/unit/test_qfq_crosscheck.py +278 -0
- tmdx-3.0.0/tests/unit/test_quote_decimal_digits.py +41 -0
- tmdx-3.0.0/tests/unit/test_realtime.py +164 -0
- tmdx-3.0.0/tests/unit/test_realtime_feed.py +334 -0
- tmdx-3.0.0/tests/unit/test_realtime_session.py +58 -0
- tmdx-3.0.0/tests/unit/test_realtime_ws.py +284 -0
- tmdx-3.0.0/tests/unit/test_scanner.py +266 -0
- tmdx-3.0.0/tests/unit/test_screen.py +695 -0
- tmdx-3.0.0/tests/unit/test_screen_universe_core.py +70 -0
- tmdx-3.0.0/tests/unit/test_signal_scan.py +386 -0
- tmdx-3.0.0/tests/unit/test_sina.py +526 -0
- tmdx-3.0.0/tests/unit/test_strategy_store.py +257 -0
- tmdx-3.0.0/tests/unit/test_sync_transport.py +88 -0
- tmdx-3.0.0/tests/unit/test_task_store.py +259 -0
- tmdx-3.0.0/tests/unit/test_unusual.py +221 -0
- tmdx-3.0.0/tests/unit/test_warehouse.py +307 -0
- tmdx-3.0.0/tests/unit/test_watchlist_and_streamer.py +155 -0
- tmdx-3.0.0/tests/unit/test_web_api.py +578 -0
- tmdx-3.0.0/tests/unit/test_web_backtest.py +1050 -0
- tmdx-3.0.0/tests/unit/test_web_spa_api_guard.py +84 -0
- tmdx-3.0.0/tests/unit/test_write_daily.py +345 -0
- tmdx-3.0.0/tests/unit/test_write_ex_daily.py +228 -0
- tmdx-3.0.0/tests/unit/test_write_min_bar.py +276 -0
- tmdx-3.0.0/tests/unit/test_zig_strategy.py +97 -0
- tmdx-3.0.0/welcome-discussion.md +46 -0
|
@@ -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,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 或结束进程即可停止;如需重启按上面命令即可。
|