Aekor

Aekor
专注于用户阅读体验的响应式博客主题
  1. 首页
  2. TradingAgents-CN
  3. 正文

TradingAgents-CN 项目优化日志:数据源统一、实时行情优化与基本面分析增强

2025-10-24 8895点热度 0人点赞 0条评论

📋 概述

2025年10月24日是项目开发的高产日,完成了 31 次提交,涵盖数据源管理、实时行情优化、基本面分析增强、Docker 构建优化等多个方面。主要亮点包括:

  1. 创建统一数据源编码管理系统,解决数据源标识混乱问题
  2. 优化实时行情入库服务,实现智能频率控制和接口轮换机制
  3. 增强基本面分析功能,优化 PE/PB 计算策略,同时提供 PE 和 PE_TTM 两个指标
  4. 完善定时任务管理界面,新增搜索和筛选功能
  5. 优化 Docker 构建策略,采用分架构独立仓库提高发布效率
  6. 修复多个数据同步和索引冲突问题

🎯 核心改进

一、数据源管理系统重构(早上 8:00-10:00)

1.1 创建统一数据源编码管理系统

提交记录:

  • bc4d0b4 - feat: 创建统一数据源编码管理系统
  • a0a4840 - refactor: 后端代码使用统一数据源编码
  • 650b22a - refactor: 前端代码使用统一数据源编码

问题背景:

原有代码中数据源标识混乱:

  • 后端使用:"tushare", "akshare", "baostock"
  • 前端使用:"Tushare", "AKShare", "BaoStock"
  • 数据库存储:"tushare", "akshare", "baostock"
  • 配置文件:DATA_SOURCE_PRIORITY = ["tushare", "akshare", "baostock"]

导致问题:

  • ❌ 前后端数据源标识不一致
  • ❌ 数据源优先级配置不生效
  • ❌ 代码中硬编码数据源名称
  • ❌ 难以维护和扩展

解决方案:

创建 tradingagents/core/data_source_codes.py 统一管理:

classDataSourceCode:

"""统一数据源编码"""

    TUSHARE ="tushare"

    AKSHARE ="akshare"

    BAOSTOCK ="baostock"

    MANUAL ="manual"

    SYSTEM ="system"

# 显示名称映射

    DISPLAY_NAMES ={

        TUSHARE:"Tushare",

        AKSHARE:"AKShare",

        BAOSTOCK:"BaoStock",

        MANUAL:"手动",

        SYSTEM:"系统",

}

@classmethod

defget_display_name(cls, code:str)->str:

"""获取数据源显示名称"""

return cls.DISPLAY_NAMES.get(code, code)

@classmethod

defnormalize(cls, code:str)->str:

"""标准化数据源编码(大小写不敏感)"""

        code_lower = code.lower()

for attr_name indir(cls):

ifnot attr_name.startswith('_'):

                attr_value =getattr(cls, attr_name)

ifisinstance(attr_value,str)and attr_value.lower()== code_lower:

return attr_value

return code

效果:

  • ✅ 统一数据源编码标准
  • ✅ 前后端使用相同编码
  • ✅ 支持大小写不敏感转换
  • ✅ 便于维护和扩展

1.2 修复数据源优先级配置不生效问题

提交记录:

  • e994035 - fix: 修复数据源优先级配置不生效的问题
  • 9d1a5c5 - fix: 修复分析时数据源降级优先级硬编码问题
  • 3e6998c - fix: 数据源降级优先级支持市场分类(A股/美股/港股)

问题背景:

  1. MongoDB 查询返回多个数据源的重复数据
  2. 代码中硬编码数据源优先级:["tushare", "akshare", "baostock"]
  3. 不使用配置文件中的 DATA_SOURCE_PRIORITY
  4. 不同市场(A股/美股/港股)应该有不同的数据源优先级

解决方案:

# app/services/data_query_service.py

def_apply_source_priority(self, records: List[Dict], market:str="A")-> List[Dict]:

"""应用数据源优先级,去重并选择最优数据源"""

# 根据市场选择优先级

if market =="A":

        priority = settings.DATA_SOURCE_PRIORITY  # ["tushare", "akshare", "baostock"]

elif market =="US":

        priority =["yahoo","alphavantage"]

elif market =="HK":

        priority =["yahoo","tushare"]

else:

        priority = settings.DATA_SOURCE_PRIORITY

# 按 code 分组

    grouped ={}

for record in records:

        code = record.get("code")

if code notin grouped:

            grouped[code]=[]

        grouped[code].append(record)

# 选择最优数据源

    result =[]

for code, records_list in grouped.items():

        best_record =None

        best_priority =len(priority)

for record in records_list:

            source = record.get("source","")

try:

                idx = priority.index(source)

if idx < best_priority:

                    best_priority = idx

                    best_record = record

except ValueError:

continue

if best_record:

            result.append(best_record)

return result

效果:

  • ✅ 使用配置文件中的数据源优先级
  • ✅ 支持不同市场的数据源优先级
  • ✅ 自动去重,选择最优数据源
  • ✅ 提高数据质量

二、实时行情入库服务优化(中午 12:00-14:00)

2.1 早期优化:降低 AkShare 实时行情同步频率

提交记录:

  • 3f009da - opt: 优化 AkShare 批量获取实时行情,避免频率限制
  • 3915f5e - fix: 支持带前缀的股票代码匹配,增强批量获取兼容性
  • 3193107 - opt: 降低 AkShare 实时行情同步频率,避免被封

问题背景:

  1. AkShare 接口频繁调用被封 IP
    • 原有代码每次获取全市场行情
    • 单次请求数据量大,容易触发限流
  2. 股票代码匹配问题
    • 部分代码带前缀(如 SH600000)
    • 部分代码不带前缀(如 600000)
    • 导致批量获取失败

解决方案:

# app/services/data_sources/akshare_adapter.py

defget_realtime_quotes_batch(self, codes: List[str])-> Dict[str, Dict]:

"""批量获取实时行情(支持带前缀的代码)"""

# 标准化代码(去除前缀)

    normalized_codes =[]

for code in codes:

if'.'in code:

            code = code.split('.')[0]# 去除后缀

iflen(code)>6:

            code = code[-6:]# 去除前缀,保留6位数字

        normalized_codes.append(code)

# 获取全市场行情

    all_quotes = self.get_realtime_quotes()

# 筛选指定股票

    result ={}

for code in normalized_codes:

if code in all_quotes:

            result[code]= all_quotes[code]

return result

效果:

  • ✅ 支持带前缀的股票代码
  • ✅ 提高批量获取成功率
  • ✅ 降低被封 IP 风险

2.2 核心优化:智能频率控制和接口轮换

提交记录:

  • ebb9197 - feat: 优化实时行情入库服务 - 智能频率控制和接口轮换
  • bd4c976 - docs: 添加实时行情入库服务配置文档和优化总结

问题背景:

  1. 默认30秒采集频率过高
    • Tushare 免费用户每小时只能调用 2 次 rt_k 接口
    • 30秒采集 = 每小时 120 次,立即超限
    • 导致免费用户服务完全不可用
  2. AKShare 只使用单一接口
    • 只使用东方财富接口(stock_zh_a_spot_em)
    • 未使用新浪财经接口(stock_zh_a_spot)
    • 频繁调用单一接口容易被封 IP
  3. 无智能频率控制
    • 付费用户和免费用户使用相同配置
    • 付费用户无法充分利用权限
    • 免费用户容易超限

解决方案

方案 1:调整默认采集频率为 6 分钟

# app/core/config.py

QUOTES_INGEST_INTERVAL_SECONDS:int= Field(

    default=360,# 从 30 秒改为 360 秒(6 分钟)

    description="实时行情采集间隔(秒)。默认360秒(6分钟),免费用户建议>=300秒,付费用户可设置5-60秒"

)

效果:

  • ✅ 每小时采集 10 次,Tushare 最多调用 2 次(不超限)
  • ✅ 免费用户可正常使用
  • ✅ 满足大多数场景需求

方案 2:为 AKShare 添加新浪财经备用接口

# app/services/data_sources/akshare_adapter.py

defget_realtime_quotes(self, source:str="eastmoney"):

"""

    获取全市场实时快照

    Args:

        source: "eastmoney"(东方财富)或 "sina"(新浪财经)

    """

if source =="sina":

        df = ak.stock_zh_a_spot()# 新浪财经接口

        logger.info("使用 AKShare 新浪财经接口获取实时行情")

else:

        df = ak.stock_zh_a_spot_em()# 东方财富接口

        logger.info("使用 AKShare 东方财富接口获取实时行情")

效果:

  • ✅ 支持两个 AKShare 接口
  • ✅ 可轮换使用,降低被封 IP 风险
  • ✅ 提高服务可靠性

方案 3:实现三种接口轮换机制

轮换顺序:Tushare rt_k → AKShare 东方财富 → AKShare 新浪财经

# app/services/quotes_ingestion_service.py

def_get_next_source(self)-> Tuple[str, Optional[str]]:

"""获取下一个数据源(轮换机制)"""

    current_source = self._rotation_sources[self._rotation_index]

    self._rotation_index =(self._rotation_index +1)%len(self._rotation_sources)

if current_source =="tushare":

return"tushare",None

elif current_source =="akshare_eastmoney":

return"akshare","eastmoney"

else:# akshare_sina

return"akshare","sina"

工作流程(免费用户,6分钟采集一次):

时间轴:

00:00 → Tushare rt_k(第1次调用)✅

06:00 → AKShare 东方财富

12:00 → AKShare 新浪财经

18:00 → Tushare rt_k(第2次调用)✅

24:00 → AKShare 东方财富

30:00 → AKShare 新浪财经

36:00 → Tushare rt_k(超限,跳过)❌ → AKShare 东方财富(自动降级)✅

42:00 → AKShare 新浪财经

48:00 → Tushare rt_k(超限,跳过)❌ → AKShare 东方财富(自动降级)✅

54:00 → AKShare 新浪财经

60:00 → 新的一小时开始,Tushare 限制重置

效果:

  • ✅ 三种接口轮流使用
  • ✅ 避免单一接口被限流
  • ✅ 提高服务稳定性

方案 4:添加 Tushare 调用次数限制

def_can_call_tushare(self)->bool:

"""判断是否可以调用 Tushare rt_k 接口"""

if self._tushare_has_premium:

returnTrue# 付费用户不限制

# 免费用户:检查每小时调用次数

    now = datetime.now(self.tz)

    one_hour_ago = now - timedelta(hours=1)

# 清理1小时前的记录

while self._tushare_call_times and self._tushare_call_times[0]< one_hour_ago:

        self._tushare_call_times.popleft()

# 检查是否超过限制

iflen(self._tushare_call_times)>= self._tushare_hourly_limit:

        logger.warning("⚠️ Tushare rt_k 接口已达到每小时调用限制,跳过本次调用")

returnFalse

returnTrue

效果:

  • ✅ 免费用户每小时最多调用 2 次
  • ✅ 超过限制自动跳过,使用 AKShare
  • ✅ 不影响服务正常运行

方案 5:自动检测 Tushare 付费权限

def_check_tushare_permission(self)->bool:

"""检测 Tushare rt_k 接口权限"""

try:

        adapter = TushareAdapter()

        df = adapter._provider.api.rt_k(ts_code='000001.SZ')

if df isnotNoneandnotgetattr(df,'empty',True):

            logger.info("✅ 检测到 Tushare rt_k 接口权限(付费用户)")

            self._tushare_has_premium =True

else:

            logger.info("⚠️ Tushare rt_k 接口无权限(免费用户)")

            self._tushare_has_premium =False

except Exception as e:

if"权限"instr(e)or"permission"instr(e):

            self._tushare_has_premium =False

return self._tushare_has_premium

首次运行日志:

免费用户:

🔍 首次运行,检测 Tushare rt_k 接口权限...

⚠️ Tushare rt_k 接口无权限(免费用户)

ℹ️ Tushare 免费用户,每小时最多调用 2 次 rt_k 接口。当前采集间隔: 360 秒

付费用户:

🔍 首次运行,检测 Tushare rt_k 接口权限...

✅ 检测到 Tushare rt_k 接口权限(付费用户)

✅ 检测到 Tushare 付费权限!建议将 QUOTES_INGEST_INTERVAL_SECONDS 设置为 5-60 秒以充分利用权限

效果:

  • ✅ 首次运行自动检测权限
  • ✅ 付费用户:提示可设置高频采集
  • ✅ 免费用户:提示当前限制

新增配置项

.env.example 中新增:

# ==================== 实时行情入库服务配置 ====================

# 📈实时行情入库服务

# 启用/禁用实时行情入库服务

QUOTES_INGEST_ENABLED=true

# 行情采集间隔(秒)

# - 免费用户建议: 300-600 秒(5-10分钟)

# - 付费用户建议: 5-60 秒

# - 默认: 360 秒(6分钟)

QUOTES_INGEST_INTERVAL_SECONDS=360

# 启用接口轮换机制

# - true: 轮流使用 Tushare rt_k → AKShare东方财富 → AKShare新浪财经

# - false: 按默认优先级使用(Tushare > AKShare)

QUOTES_ROTATION_ENABLED=true

# Tushare rt_k 接口每小时调用次数限制

# - 免费用户: 2 次(Tushare 官方限制)

# - 付费用户: 可设置更高(如 1000)

QUOTES_TUSHARE_HOURLY_LIMIT=2

# 自动检测 Tushare rt_k 接口权限

# - true: 首次运行自动检测,付费用户会收到提示

# - false: 不检测,按配置运行

QUOTES_AUTO_DETECT_TUSHARE_PERMISSION=true

# 休市期/启动兜底补数

QUOTES_BACKFILL_ON_STARTUP=true

QUOTES_BACKFILL_ON_OFFHOURS=true

性能对比

指标优化前(免费用户)优化后(免费用户)
采集频率30 秒6 分钟
每小时采集次数120 次10 次
Tushare 调用次数120 次(超限❌)2 次(不超限✅)
服务可用性❌ 不可用✅ 可用
被封 IP 风险⚠️ 高✅ 低

三、基本面分析功能增强(下午 14:00-18:00)

3.1 优化基本面分析数据获取策略

提交记录:

  • 7b723b6 - refactor: 优化基本面分析数据获取策略
  • bec86db - feat: 将分析师数据获取范围改为可配置参数

问题背景:

  1. 数据获取策略不合理
    • 每次分析都重新获取所有数据
    • 没有利用缓存机制
    • 数据获取效率低
  2. 分析师数据获取范围固定
    • 硬编码获取最近 30 天数据
    • 无法根据需求调整

解决方案:

# app/core/config.py

ANALYST_RATING_DAYS:int= Field(

    default=30,

    description="分析师评级数据获取天数范围"

)

# app/services/fundamental_analysis_service.py

asyncdefget_fundamental_data(self, code:str)-> Dict:

"""获取基本面数据(优化缓存策略)"""

# 1. 尝试从缓存获取

    cached =await self._get_from_cache(code)

if cached and self._is_cache_valid(cached):

return cached

# 2. 从数据库获取

    data =await self._get_from_database(code)

# 3. 缓存数据

await self._save_to_cache(code, data)

return data

效果:

  • ✅ 提高数据获取效率
  • ✅ 减少数据库查询次数
  • ✅ 支持配置分析师数据范围

3.2 优化 PE/PB 计算策略

提交记录:

  • 7724255 - feat: 优化PE/PB计算策略 - 优先使用动态PE(基于实时股价+Tushare TTM)
  • 2baa89f - fix: 修复PE计算日志中变量引用错误

问题背景:

  1. PE 计算不准确
    • 只使用静态 PE(基于财报数据)
    • 不考虑实时股价变化
    • 数据滞后
  2. 缺少 TTM(Trailing Twelve Months)指标
    • TTM 是更准确的 PE 计算方式
    • 考虑最近 12 个月的盈利

解决方案:

计算策略:

  1. 优先使用动态 PE(实时股价 + Tushare TTM 数据)
    # 获取实时股价
  2. current_price =await self._get_realtime_price(code)
  3. # 获取 Tushare TTM 数据
  4. ttm_data =await self._get_tushare_ttm(code)
  5. # 计算动态 PE
  6. if ttm_data and ttm_data.get("eps_ttm"):
  7.     pe_dynamic = current_price / ttm_data["eps_ttm"]

  8. 降级使用静态 PE(财报数据)
    # 如果没有 TTM 数据,使用最新财报
  9. ifnot pe_dynamic:
  10.     latest_report =await self._get_latest_financial_report(code)
  11. if latest_report and latest_report.get("eps"):
  12.         pe_static = current_price / latest_report["eps"]

  13. 最终降级使用数据源提供的 PE
    # 如果都没有,使用数据源提供的 PE
  14. ifnot pe_dynamic andnot pe_static:
  15.     pe = stock_info.get("pe")

效果:

  • ✅ PE 计算更准确
  • ✅ 考虑实时股价变化
  • ✅ 支持多级降级策略

3.3 同时提供 PE 和 PE_TTM 两个指标

提交记录:

  • 410fd21 - feat: 基本面分析同时提供PE和PE_TTM两个指标

问题背景:

用户需要同时查看:

  • PE(静态市盈率):基于最新财报的 EPS
  • PE_TTM(动态市盈率):基于最近 12 个月的 EPS

解决方案:

# app/schemas/analysis.py

classFundamentalAnalysisResponse(BaseModel):

"""基本面分析响应"""

    code:str

    name:str

# 估值指标

    pe: Optional[float]= Field(None, description="静态市盈率(基于最新财报)")

    pe_ttm: Optional[float]= Field(None, description="动态市盈率(TTM)")

    pb: Optional[float]= Field(None, description="市净率")

    ps: Optional[float]= Field(None, description="市销率")

# ... 其他字段

前端显示:

<el-descriptions-item label="市盈率(PE)">

  {{ data.pe?.toFixed(2) || 'N/A' }}

</el-descriptions-item>

<el-descriptions-item label="市盈率(PE_TTM)">

  {{ data.pe_ttm?.toFixed(2) || 'N/A' }}

  <el-tag v-if="data.pe_ttm" type="info" size="small">动态</el-tag>

</el-descriptions-item>

效果:

  • ✅ 同时提供两个指标
  • ✅ 用户可对比静态和动态 PE
  • ✅ 提高分析准确性

四、数据同步和索引问题修复(上午 10:00-12:00)

4.1 修复 market_quotes 集合索引冲突

提交记录:

  • 6bab35b - fix: 修复 market_quotes 集合 code 字段为 null 导致的唯一索引冲突
  • 2d993c5 - docs: 添加 market_quotes code 字段 null 值修复指南
  • 3741ab7 - fix: 修复脚本日志配置问题
  • 071fd4e - fix: 脚本添加数据库初始化
  • 28e1579 - fix: 使用正确的数据库初始化函数名
  • f46f952 - test: 添加测试脚本并验证修复效果

问题背景:

MongoDB market_quotes 集合存在 code 字段为 null 的记录,导致唯一索引冲突:

E11000 duplicate key error collection: tradingagents.market_quotes

index: code_1 dup key: { code: null }

原因分析:

  1. 早期代码没有验证 code 字段
  2. 部分数据源返回的数据缺少 code 字段
  3. 插入时没有检查必填字段

解决方案:

步骤 1:创建修复脚本

# scripts/fix_market_quotes_null_code.py

asyncdeffix_null_code_records():

"""修复 code 字段为 null 的记录"""

    db = get_database()

    collection = db[settings.MARKET_QUOTES_COLLECTION]

# 查找 code 为 null 的记录

    null_records =await collection.find({"code":None}).to_list(None)

    logger.info(f"找到 {len(null_records)} 条 code 为 null 的记录")

# 删除这些记录

if null_records:

        result =await collection.delete_many({"code":None})

        logger.info(f"已删除 {result.deleted_count} 条记录")

步骤 2:添加数据验证

# app/services/quotes_ingestion_service.py

asyncdef_save_quotes(self, quotes: Dict[str, Dict]):

"""保存行情数据(添加验证)"""

    valid_quotes =[]

for code, quote in quotes.items():

# 验证必填字段

ifnot code ornot quote.get("name"):

            logger.warning(f"跳过无效记录:code={code}, quote={quote}")

continue

        valid_quotes.append({

"code": code,

"name": quote["name"],

"price": quote.get("price"),

# ... 其他字段

})

# 批量插入

if valid_quotes:

await collection.insert_many(valid_quotes)

效果:

  • ✅ 修复历史数据
  • ✅ 防止新数据出现问题
  • ✅ 提供修复指南文档

4.2 修复 Tushare 同步服务 Pydantic 模型错误

提交记录:

  • bcd9d09 - fix: 修复 Tushare 同步服务中 Pydantic 模型调用字典方法的错误

问题背景:

Pydantic v2 模型不再支持字典方法(如 .get()),导致代码报错:

# 错误代码

stock_info = StockBasicInfo(...)

name = stock_info.get("name")# AttributeError: 'StockBasicInfo' object has no attribute 'get'

解决方案:

# 修复后

stock_info = StockBasicInfo(...)

name = stock_info.name  # 直接访问属性

# 或者转换为字典

stock_dict = stock_info.model_dump()

name = stock_dict.get("name")

效果:

  • ✅ 兼容 Pydantic v2
  • ✅ 修复同步服务错误
  • ✅ 提高代码质量

4.3 修复数据同步和数据源优先级问题

提交记录:

  • 7fd534c - fix: 修复数据同步和数据源优先级问题

问题背景:

  1. 任务手动触发功能不支持暂停任务
  2. 历史数据同步存在问题
    • 股票列表查询条件不正确
    • 每次全量同步,导致数据重复
  3. 财务数据同步问题
    • 只同步季报,缺少年报
    • 只获取最近 4 期(约 1 年)

解决方案:

详见之前的提交记录。

效果:

  • ✅ 支持暂停任务的手动触发
  • ✅ 历史数据增量同步
  • ✅ 财务数据包含年报和季报

五、定时任务管理界面优化(下午 16:00-17:00)

5.1 为所有任务添加友好的中文名称

提交记录:

  • 8bb8e02 - fix: 为所有定时任务添加友好的中文名称

问题背景:

  1. 任务名称显示不友好
    • 部分任务显示为函数路径(如 lifespan.<locals>.run_news_sync)
    • 部分任务显示为函数名(如 run_akshare_basic_info_sync)
  2. 用户体验差
    • 无法快速识别任务功能
    • 需要查看代码才能理解

解决方案:

# app/main.py

scheduler.add_job(

    run_akshare_basic_info_sync,

    CronTrigger.from_crontab(settings.AKSHARE_BASIC_INFO_SYNC_CRON, timezone=settings.TIMEZONE),

id="akshare_basic_info_sync",

    name="股票基础信息同步(AKShare)",# ← 新增友好名称

    kwargs={"force_update":False}

)

命名格式:功能描述(数据源)

修改的任务:

  • ✅ 18 个定时任务全部添加中文名称
  • ✅ 统一命名格式
  • ✅ 提升用户体验

5.2 添加搜索和筛选功能

提交记录:

  • b349e89 - feat: 为定时任务管理页面添加搜索和筛选功能

问题背景:

  1. 任务查找困难
    • 10+ 个任务,没有搜索和筛选功能
    • 查找特定任务需要手动翻找
  2. 无法按条件筛选
    • 无法只查看某个数据源的任务
    • 无法只查看运行中或暂停的任务

解决方案:

<!-- frontend/src/views/System/SchedulerManagement.vue -->

<el-form :inline="true" class="filter-form">

  <!-- 任务名称搜索 -->

  <el-form-item label="任务名称">

    <el-input

      v-model="searchKeyword"

      placeholder="搜索任务名称"

      clearable

      :prefix-icon="Search"

      @input="handleSearch"

    />

  </el-form-item>

  <!-- 数据源筛选 -->

  <el-form-item label="数据源">

    <el-select v-model="filterDataSource" @change="handleSearch">

      <el-option label="全部数据源" value="" />

      <el-option label="Tushare" value="Tushare" />

      <el-option label="AKShare" value="AKShare" />

      <el-option label="BaoStock" value="BaoStock" />

      <el-option label="多数据源" value="多数据源" />

      <el-option label="其他" value="其他" />

    </el-select>

  </el-form-item>

  <!-- 状态筛选 -->

  <el-form-item label="状态">

    <el-select v-model="filterStatus" @change="handleSearch">

      <el-option label="全部状态" value="" />

      <el-option label="运行中" value="running" />

      <el-option label="已暂停" value="paused" />

    </el-select>

  </el-form-item>

  <el-form-item>

    <el-button :icon="Refresh" @click="handleReset">重置</el-button>

  </el-form-item>

</el-form>

筛选逻辑:

const filteredJobs =computed(()=>{

let result =[...jobs.value]

// 按任务名称搜索

if(searchKeyword.value){

const keyword = searchKeyword.value.toLowerCase()

    result = result.filter(job =>

      job.name.toLowerCase().includes(keyword)||

      job.id.toLowerCase().includes(keyword)

)

}

// 按数据源筛选

if(filterDataSource.value){

    result = result.filter(job => job.name.includes(filterDataSource.value))

}

// 按状态筛选

if(filterStatus.value){

if(filterStatus.value ==='running'){

      result = result.filter(job =>!job.paused)

}elseif(filterStatus.value ==='paused'){

      result = result.filter(job => job.paused)

}

}

// 默认排序:运行中的任务优先

  result.sort((a, b)=>{

if(a.paused !== b.paused){

return a.paused ?1:-1

}

return a.name.localeCompare(b.name,'zh-CN')

})

return result

})

效果:

  • ✅ 支持任务名称搜索
  • ✅ 支持数据源筛选
  • ✅ 支持状态筛选
  • ✅ 运行中的任务优先显示
  • ✅ 实时搜索,无需点击按钮

六、Docker 构建优化(上午 9:00-11:00)

6.1 采用分架构独立仓库策略

提交记录:

  • 76f24e0 - feat: 采用分架构独立仓库策略,提高发布效率
  • 2ac8dd5 - docs: 添加快速构建参考指南
  • c04fc53 - refactor: 删除 Apple Silicon 独立脚本,统一使用 ARM64

问题背景:

  1. 多架构构建耗时长
    • 同时构建 AMD64 和 ARM64 需要 30+ 分钟
    • 每次发布都要等待很久
  2. Apple Silicon 脚本冗余
    • 有独立的 build-apple-silicon.sh 脚本
    • 与 ARM64 脚本功能重复

解决方案:

策略 1:分架构独立仓库

# AMD64 架构(Linux/Windows)

docker build --platform linux/amd64 -t tradingagents-cn:amd64 .

docker tag tradingagents-cn:amd64 your-registry/tradingagents-cn:amd64

docker push your-registry/tradingagents-cn:amd64

# ARM64 架构(Apple Silicon/ARM服务器)

docker build --platform linux/arm64 -t tradingagents-cn:arm64 .

docker tag tradingagents-cn:arm64 your-registry/tradingagents-cn:arm64

docker push your-registry/tradingagents-cn:arm64

策略 2:用户根据架构选择镜像

# AMD64 用户

docker pull your-registry/tradingagents-cn:amd64

# ARM64 用户

docker pull your-registry/tradingagents-cn:arm64

效果:

  • ✅ 构建时间从 30+ 分钟降低到 10 分钟
  • ✅ 提高发布效率
  • ✅ 简化构建脚本

6.2 删除冗余脚本

删除的脚本:

  • scripts/build-apple-silicon.sh(与 ARM64 脚本重复)

保留的脚本:

  • scripts/build-amd64.sh(AMD64 架构)
  • scripts/build-arm64.sh(ARM64 架构,包括 Apple Silicon)

效果:

  • ✅ 减少维护成本
  • ✅ 避免脚本冗余
  • ✅ 统一构建流程

七、系统架构优化(早上 7:00-8:00)

7.1 完全移除 SSE + Redis PubSub 通知系统

提交记录:

  • 947a791 - refactor: 完全移除 SSE + Redis PubSub 通知系统,只保留 WebSocket

问题背景:

  1. 双通知系统冗余
    • 同时维护 SSE 和 WebSocket 两套系统
    • 增加维护成本
  2. Redis PubSub 连接泄漏
    • 之前已修复,但仍有潜在风险

解决方案:

完全移除 SSE + Redis PubSub,只保留 WebSocket:

# 删除的代码

# app/routers/sse.py

# app/services/notification_service.py (Redis PubSub 部分)

# 保留的代码

# app/routers/websocket.py

# app/services/websocket_manager.py

效果:

  • ✅ 简化系统架构
  • ✅ 减少维护成本
  • ✅ 避免连接泄漏风险

7.2 统一使用配置时区

提交记录:

  • a85c86c - fix: 统一使用配置时区(now_tz)替代 UTC 时间(datetime.utcnow)

问题背景:

  1. 时区混乱
    • 部分代码使用 datetime.utcnow()(UTC 时间)
    • 部分代码使用 datetime.now(tz)(配置时区)
    • 导致时间显示不一致
  2. 用户体验差
    • 日志时间显示为 UTC
    • 前端显示时间需要转换

解决方案:

统一使用配置时区:

# 错误写法

now = datetime.utcnow()# UTC 时间

# 正确写法

from app.core.config import settings

from zoneinfo import ZoneInfo

tz = ZoneInfo(settings.TIMEZONE)# 配置时区(如 "Asia/Shanghai")

now = datetime.now(tz)# 配置时区时间

效果:

  • ✅ 时间显示一致
  • ✅ 提高用户体验
  • ✅ 避免时区转换错误

🎉 总结

今日成果

提交统计:

  • ✅ 31 次提交
  • ✅ 80+ 个文件修改
  • ✅ 6,000+ 行新增代码
  • ✅ 1,000+ 行删除代码

核心价值:

  1. 数据源管理更规范
    • 统一数据源编码
    • 数据源优先级配置生效
    • 支持市场分类
  2. 实时行情服务更可靠
    • 免费用户可正常使用
    • 付费用户充分利用权限
    • 智能频率控制和接口轮换
    • 降低被限流和封 IP 风险
  3. 基本面分析更准确
    • 优化 PE/PB 计算策略
    • 同时提供 PE 和 PE_TTM
    • 优化数据获取策略
  4. 任务管理更友好
    • 友好的中文任务名称
    • 强大的搜索筛选功能
    • 提高用户体验
  5. 系统架构更简洁
    • 移除冗余的 SSE 通知系统
    • 统一使用配置时区
    • 优化 Docker 构建策略
  6. 数据质量更高
    • 修复索引冲突问题
    • 修复数据同步问题
    • 完善数据验证

代码质量:

  • ✅ 完善的文档支持
  • ✅ 详细的配置说明
  • ✅ 清晰的代码注释
  • ✅ 完整的测试脚本

用户体验:

  • ✅ 自动检测权限
  • ✅ 智能频率控制
  • ✅ 友好的任务名称
  • ✅ 强大的搜索筛选
  • ✅ 准确的基本面分析

✅ 官方唯一渠道:📦 GitHub 仓库:https://github.com/hsliuping/TradingAgents-CN

Aekor AI-API 中转站,让全球顶尖 AI 大模型“触手可及”!你是否曾为这些烦恼头疼?

🔹 人在国内,却总被海外官网 API 的高延迟、掉线、甚至无法访问困扰?

🔹 想用最强的 GPT、Claude 等模型,却卡在海外信用卡、支付审核等重重阻碍?

🔹 官方 API 太贵?Aekor 为你打通“网络-支付-成本”的任督二脉!

💡 Aekor 核心价值:好用、便宜、快 💡

🚀 高速稳定,告别掉线国内专线加速,API 响应低延迟,告别「转圈圈」的焦虑,开发效率瞬间拉满!

🧠 顶尖模型,随需而调涵盖 GPT 系列、Claude 系列等全球主流大厂模型,一次接入,轻松调用!

🎁 免费白嫖,诚意拉满!

注册即送 20 美元体验额度,够你狠狠测试一轮模型质量与线路稳定性了!

🚪 入口:https://api.aekor.com

⚠️ 温馨提示:API 中转市场虽多但良莠不齐(甚至有些会偷工减料换小模型糊弄事儿)。Aekor 坚持提供正版稳定的服务,但还是建议:先用免费的 20 刀测试是否契合自身需求,满意了再小额充值上车,理性消费不盲目。

本作品采用 知识共享署名 4.0 国际许可协议 进行许可
标签: AkShare Docker TradingAgents-CN
最后更新:2026-05-23

Aekor

这个人很懒,什么都没留下

点赞

文章评论

razz evil exclaim smile redface biggrin eek confused idea lol mad twisted rolleyes wink cool arrow neutral cry mrgreen drooling persevering
取消回复

使用AI教程

  • API报错解决方案
  • API 基础知识
  • API Key 获取

分类

  • Blog
  • TradingAgents-CN

COPYRIGHT © 2026 Aekor. ALL RIGHTS RESERVED.