feat(market): 添加市场分析数据基础架构和功能模块

- 新增市场分析相关数据库表结构,包括市场工具、每日报价、财务指标、
  宏观指标和公告等数据模型
- 创建市场分析相关的Alembic迁移脚本,包含完整的up和downgrade逻辑
- 集成市场分析路由到主API路由器中
- 添加市场数据定时任务调度,支持盘前、收盘和周度市场分析报告
- 实现市场数据后台任务队列,包含报告生成和收盘分析功能
- 扩展系统配置设置,添加市场分析启用开关和相关参数配置
- 增加AI智能技能支持,包含市场概览分析和股票分析功能
- 添加审计日志记录,支持市场数据同步和自选股更新操作追踪
- 实现飞书命令集成,支持股票分析、自选股管理、公告查询等交互
- 提供市场数据服务层,包含行业分析、股票对比、市场概览等功能
```
This commit is contained in:
2026-07-12 22:05:23 +08:00
parent 9cf7c44393
commit f8020cab56
28 changed files with 2704 additions and 60 deletions

View File

@@ -1,3 +1,4 @@
from datetime import date
from socket import gethostname
from typing import Any
@@ -39,6 +40,7 @@ def create_scheduler(app: FastAPI | None = None) -> Any:
enqueue_legacy_project_sync,
enqueue_legacy_task_sync,
enqueue_lifecycle_report,
enqueue_market_report,
enqueue_project_weekly_push,
)
from app.modules.observability.service import ObservabilityService
@@ -127,6 +129,18 @@ def create_scheduler(app: FastAPI | None = None) -> Any:
)
_set_state(app, "last_weekly_lifecycle_dispatch", dispatch)
def run_market_premarket() -> None:
dispatch = enqueue_market_report("premarket")
_set_state(app, "last_market_premarket_dispatch", dispatch)
def run_market_close() -> None:
dispatch = enqueue_market_report("close")
_set_state(app, "last_market_close_dispatch", dispatch)
def run_market_weekly() -> None:
dispatch = enqueue_market_report("weekly", date.today())
_set_state(app, "last_market_weekly_dispatch", dispatch)
def run_event_dispatch() -> None:
dispatch = enqueue_event_dispatch(
limit=settings.event_dispatch_batch_size,
@@ -190,6 +204,34 @@ def create_scheduler(app: FastAPI | None = None) -> Any:
id="event_dispatch",
replace_existing=True,
)
if settings.market_analysis_enabled:
scheduler.add_job(
run_market_premarket,
trigger="cron",
day_of_week="mon-fri",
hour=settings.market_premarket_cron_hour,
minute=settings.market_premarket_cron_minute,
id="market_premarket_analysis",
replace_existing=True,
)
scheduler.add_job(
run_market_close,
trigger="cron",
day_of_week="mon-fri",
hour=settings.market_close_cron_hour,
minute=settings.market_close_cron_minute,
id="market_close_analysis",
replace_existing=True,
)
scheduler.add_job(
run_market_weekly,
trigger="cron",
day_of_week=settings.market_weekly_day_of_week,
hour=settings.market_weekly_cron_hour,
minute=settings.market_weekly_cron_minute,
id="market_weekly_analysis",
replace_existing=True,
)
scheduler.add_job(
record_scheduler_heartbeat,
trigger="interval",

View File

@@ -6,12 +6,21 @@ from app.core.background.task_queue.constants import (
TASK_SYNC_LEGACY_PROJECTS,
TASK_SYNC_LEGACY_TASKS,
TASK_RUN_LIFECYCLE,
TASK_RUN_MARKET_CLOSE,
TASK_RUN_MARKET_REPORT,
)
from app.core.background.task_queue.dispatcher import dispatch_task
from app.core.background.task_queue.events import enqueue_event_dispatch
from app.core.background.task_queue.legacy import enqueue_legacy_project_sync, enqueue_legacy_task_sync
from app.core.background.task_queue.legacy import (
enqueue_legacy_project_sync,
enqueue_legacy_task_sync,
)
from app.core.background.task_queue.lifecycle import enqueue_lifecycle_report
from app.core.background.task_queue.reports import enqueue_daily_brief_push, enqueue_project_weekly_push
from app.core.background.task_queue.market import enqueue_market_close, enqueue_market_report
from app.core.background.task_queue.reports import (
enqueue_daily_brief_push,
enqueue_project_weekly_push,
)
from app.core.background.task_queue.risk import enqueue_risk_event_generation
@@ -23,12 +32,16 @@ __all__ = [
"TASK_SYNC_LEGACY_PROJECTS",
"TASK_SYNC_LEGACY_TASKS",
"TASK_RUN_LIFECYCLE",
"TASK_RUN_MARKET_CLOSE",
"TASK_RUN_MARKET_REPORT",
"dispatch_task",
"enqueue_daily_brief_push",
"enqueue_event_dispatch",
"enqueue_legacy_project_sync",
"enqueue_legacy_task_sync",
"enqueue_lifecycle_report",
"enqueue_market_close",
"enqueue_market_report",
"enqueue_project_weekly_push",
"enqueue_risk_event_generation",
]

View File

@@ -5,3 +5,5 @@ TASK_SYNC_LEGACY_PROJECTS = "legacy.sync_projects"
TASK_SYNC_LEGACY_TASKS = "legacy.sync_tasks"
TASK_DISPATCH_PENDING_EVENTS = "events.dispatch_pending"
TASK_RUN_LIFECYCLE = "reports.run_lifecycle"
TASK_RUN_MARKET_REPORT = "market.report.run"
TASK_RUN_MARKET_CLOSE = TASK_RUN_MARKET_REPORT

View File

@@ -45,6 +45,7 @@ def enqueue_legacy_project_sync(
inline,
)
def enqueue_legacy_task_sync(
source_query: str | None = None,
source_query_name: str | None = None,

View File

@@ -0,0 +1,26 @@
from datetime import date
from typing import Any
from app.core.background.task_queue.constants import TASK_RUN_MARKET_REPORT
from app.core.config import get_settings
def enqueue_market_report(
report_type: str, trade_date: date | None = None, force: bool = False
) -> dict[str, Any]:
value = trade_date.isoformat() if trade_date else None
if not get_settings().task_queue_enabled:
from app.tasks.market import run_market_report
return run_market_report.run(report_type, value, force)
from app.tasks import celery_app
result = celery_app.signature(
TASK_RUN_MARKET_REPORT,
kwargs={"report_type": report_type, "trade_date": value, "force": force},
).apply_async()
return {"task_id": result.id, "status": "queued"}
def enqueue_market_close(trade_date: date | None = None) -> dict[str, Any]:
return enqueue_market_report("close", trade_date)

View File

@@ -76,6 +76,7 @@ def enqueue_daily_brief_push(
inline,
)
def enqueue_project_weekly_push(
receive_id: str | None = None,
receive_id_type: str = FeishuReceiveIdType.CHAT_ID,

View File

@@ -71,6 +71,17 @@ class Settings(BaseSettings):
task_queue_always_eager: bool = False
lifecycle_pipeline_enabled: bool = False
finance_needs_enabled: bool = False
market_analysis_enabled: bool = False
market_data_provider: str = "tushare"
market_data_base_url: str = "https://api.tushare.pro"
market_data_token: str | None = None
market_premarket_cron_hour: int = 8
market_premarket_cron_minute: int = 30
market_close_cron_hour: int = 15
market_close_cron_minute: int = 30
market_weekly_day_of_week: str = "sun"
market_weekly_cron_hour: int = 20
market_weekly_cron_minute: int = 0
legacy_sync_enabled: bool = False
celery_result_backend_url: str | None = None
daily_brief_cron_hour: int = 9
@@ -106,6 +117,7 @@ class Settings(BaseSettings):
"openclaw_gateway_token",
"hermes_api_key",
"direct_llm_api_key",
"market_data_token",
"feishu_app_secret",
"feishu_verification_token",
]