feat: 将参考 AlphaSift 的选股实现纳入主项目 (#2136)

* feat: integrate built-in stock screening engine

* test: verify screening routes via public contract

* refactor: make screening integration fully native

* fix(review-feedback-2136): [Verification blocker] 当前 Head 的阻断型 CI 已最终失败:backend-gate。请在本地运行对应

* feat: persist screening runs and reuse DSA analysis

* fix(review-feedback-2136): 对选股重排应用统一的模型参数兼容逻辑

* fix(review-feedback-2136): 将字段缺失计入数据源失败

* fix(review-feedback-2136): 评审结论 - 代码检查 :当前整个 PR 仍有 3 个未关闭的高置信度代码 blocker。最新复核摘要:当前整个 PR 仍有 3

* fix(review-feedback-2136): 延迟写入候选直到覆盖率通过校验 and 将 DSA 日线成功结果写入 last-good 缓存

* fix(review-feedback-2136): 将直连日线源纳入统一调用超时 and 将新增的选股缓存目录加入桌面更新备份

* fix(review-feedback-2136): 保留完整热点缓存而非请求切片 and 遵守 DSA 上下文声明的候选上限

* fix(review-feedback-2136): 补一条端到端回归:把真实 ScreenResult 经 ScreeningService.screen 落到 /history 和

* fix(review-feedback-2136): 补一个 refresh=true 且 live fetch 失败时返回 stale cache 的回归测试,避免当前只更新 stale
This commit is contained in:
zhulinsen
2026-08-01 14:13:23 +08:00
committed by GitHub
parent 831ada5370
commit bcb7ae4e13
86 changed files with 16486 additions and 2510 deletions

View File

@@ -44,8 +44,8 @@ log "Installing backend dependencies..."
log "Checking python-multipart availability..."
"${PYTHON_BIN}" -c "import multipart, multipart.multipart"
log "Checking AlphaSift adapter availability..."
"${PYTHON_BIN}" -c "import alphasift.dsa_adapter"
log "Checking built-in screening engine availability..."
"${PYTHON_BIN}" -c "import src.services.screening.pipeline"
log "Checking Futu SDK availability..."
"${PYTHON_BIN}" -c "import futu"
@@ -83,7 +83,7 @@ hidden_imports=(
"api.v1.endpoints.history"
"api.v1.endpoints.stocks"
"api.v1.endpoints.health"
"api.v1.endpoints.alphasift"
"api.v1.endpoints.screening"
"api.v1.schemas"
"api.v1.schemas.analysis"
"api.v1.schemas.history"
@@ -95,9 +95,9 @@ hidden_imports=(
"src.services.task_queue"
"src.services.analysis_service"
"src.services.history_service"
"src.services.alphasift_service"
"alphasift"
"alphasift.dsa_adapter"
"src.services.screening_service"
"src.services.screening"
"src.services.screening.pipeline"
"orjson"
"uvicorn.logging"
"uvicorn.loops"
@@ -118,7 +118,7 @@ done
pushd "${ROOT_DIR}" >/dev/null
cmd=("${PYTHON_BIN}" -m PyInstaller --name stock_analysis --onedir --noconfirm --noconsole --add-data "static:static" --add-data "strategies:strategies" --collect-data litellm --collect-data tiktoken --collect-data akshare)
cmd+=("--collect-all" "alphasift")
cmd+=("--collect-all" "src.services.screening")
cmd+=("--collect-all" "futu")
cmd+=("${hidden_import_args[@]}" "main.py")
@@ -147,7 +147,7 @@ if ! "${packaged_entry}" --help >/tmp/dsa-packaged-help.log 2>&1; then
exit 1
fi
for module in alphasift.dsa_adapter futu orjson; do
for module in src.services.screening.pipeline futu orjson; do
if DSA_PACKAGED_IMPORT_PROBE="${module}" "${packaged_entry}" >/tmp/dsa-packaged-import.log 2>&1; then
cat /tmp/dsa-packaged-import.log
else
@@ -194,4 +194,20 @@ if [[ "${packaged_strategy_count}" != "${source_strategy_count}" ]]; then
exit 1
fi
log "Verifying packaged screening strategies..."
source_screening_strategy_count="$(find "${ROOT_DIR}/src/services/screening/strategies" -maxdepth 1 -type f -name '*.yaml' | wc -l | tr -d '[:space:]')"
packaged_screening_strategies="${ROOT_DIR}/dist/backend/stock_analysis/_internal/src/services/screening/strategies"
if [[ ! -d "${packaged_screening_strategies}" ]]; then
packaged_screening_strategies="${ROOT_DIR}/dist/backend/stock_analysis/src/services/screening/strategies"
fi
if [[ ! -d "${packaged_screening_strategies}" ]]; then
echo "ERROR: packaged screening strategies directory not found under dist/backend/stock_analysis."
exit 1
fi
packaged_screening_strategy_count="$(find "${packaged_screening_strategies}" -maxdepth 1 -type f -name '*.yaml' | wc -l | tr -d '[:space:]')"
if [[ "${packaged_screening_strategy_count}" != "${source_screening_strategy_count}" ]]; then
echo "ERROR: packaged screening strategies count mismatch: expected ${source_screening_strategy_count}, got ${packaged_screening_strategy_count}."
exit 1
fi
log "Backend build completed."

View File

@@ -51,9 +51,9 @@ if (-not (Test-PythonCode -Python $pythonBin -Code "import multipart, multipart.
throw 'python-multipart is not importable in the selected Python environment.'
}
Write-Host 'Checking AlphaSift adapter availability...'
if (-not (Test-PythonCode -Python $pythonBin -Code "import alphasift.dsa_adapter")) {
throw 'alphasift.dsa_adapter is not importable after installing requirements.'
Write-Host 'Checking built-in screening engine availability...'
if (-not (Test-PythonCode -Python $pythonBin -Code "import src.services.screening.pipeline")) {
throw 'src.services.screening.pipeline is not importable.'
}
Write-Host 'Checking Futu SDK availability...'
@@ -97,9 +97,9 @@ $hiddenImports = @(
'api.v1.endpoints.history',
'api.v1.endpoints.stocks',
'api.v1.endpoints.health',
'api.v1.endpoints.alphasift',
'alphasift',
'alphasift.dsa_adapter',
'api.v1.endpoints.screening',
'src.services.screening',
'src.services.screening.pipeline',
'api.v1.schemas',
'api.v1.schemas.analysis',
'api.v1.schemas.history',
@@ -111,7 +111,7 @@ $hiddenImports = @(
'src.services.task_queue',
'src.services.analysis_service',
'src.services.history_service',
'src.services.alphasift_service',
'src.services.screening_service',
'uvicorn.logging',
'uvicorn.loops',
'uvicorn.loops.auto',
@@ -136,7 +136,7 @@ $pyInstallerArgs = @(
'--collect-data', 'litellm',
'--collect-data', 'tiktoken',
'--collect-data', 'akshare',
'--collect-all', 'alphasift',
'--collect-all', 'src.services.screening',
'--collect-all', 'futu'
)
$pyInstallerArgs += $hiddenImportArgs
@@ -161,7 +161,7 @@ if (-not (Test-Path $packagedEntry)) {
}
$previousProbe = $env:DSA_PACKAGED_IMPORT_PROBE
try {
foreach ($module in @('alphasift.dsa_adapter', 'futu', 'orjson')) {
foreach ($module in @('src.services.screening.pipeline', 'futu', 'orjson')) {
$env:DSA_PACKAGED_IMPORT_PROBE = $module
$probeProcess = Start-Process -FilePath $packagedEntry -Wait -PassThru
if ($probeProcess.ExitCode -ne 0) {
@@ -213,4 +213,19 @@ if ($packagedStrategyCount -ne $sourceStrategyCount) {
throw "Packaged strategies count mismatch: expected $sourceStrategyCount, got $packagedStrategyCount."
}
Write-Host 'Verifying packaged screening strategies...'
$sourceScreeningStrategies = Join-Path 'src\services\screening' 'strategies'
$sourceScreeningStrategyCount = @(Get-ChildItem -Path $sourceScreeningStrategies -Filter '*.yaml' -File).Count
$packagedScreeningStrategies = Join-Path 'dist\backend\stock_analysis' '_internal\src\services\screening\strategies'
if (-not (Test-Path $packagedScreeningStrategies)) {
$packagedScreeningStrategies = Join-Path 'dist\backend\stock_analysis' 'src\services\screening\strategies'
}
if (-not (Test-Path $packagedScreeningStrategies)) {
throw 'Packaged screening strategies directory not found under dist\backend\stock_analysis.'
}
$packagedScreeningStrategyCount = @(Get-ChildItem -Path $packagedScreeningStrategies -Filter '*.yaml' -File).Count
if ($packagedScreeningStrategyCount -ne $sourceScreeningStrategyCount) {
throw "Packaged screening strategies count mismatch: expected $sourceScreeningStrategyCount, got $packagedScreeningStrategyCount."
}
Write-Host 'Backend build completed.'