集中管理项目所有「预先准备好、运行时只读」的数据资源:
- 行业清单(catalog markdown)
- 图标字体(cos-icon webfont)
- 公司 logo(按 ticker 命名的 PNG)
所有调用方只通过 AssetManager 类访问这些资源,禁止硬编码路径。
| 文件 | 行数 | 职责 |
|---|---|---|
stocklens/assets/__init__.py |
~10 | 导出 AssetManager 与单例 assets |
stocklens/assets/manager.py |
~190 | AssetManager 类实现 |
assets/ # 受 AssetManager 管理
├── catalog/
│ └── us_stocks_by_sector.md # 112 细分行业 / 6705 只股票(NYSE+NASDAQ+AMEX 全量)
├── icons/
│ ├── font/
│ │ ├── cos-icon.css # 389 个 cos-icon
│ │ ├── cos-icon_f8a2aa1.woff2
│ │ ├── cos-icon_a9f9ae5.woff
│ │ └── cos-icon_3e840d7.ttf
│ └── logos/
│ ├── AAPL.png # 5126 只本地透明 logo
│ └── ...
└── README.md # 用户文档
| 资产 | 来源 | 备注 |
|---|---|---|
catalog/us_stocks_by_sector.md |
nvstly/symbols(ticker 列表)+ NASDAQ Screener API(行业/国家/公司名/市值) | 6705 只股票 / 112 细分行业(百度财经风格 sub-sector 分类),含 ADR |
icons/logos/*.png |
nvstly/icons(fork: davidepalazzo/ticker-logos) | 透明背景 PNG,专为深色主题设计 |
icons/font/* |
Baidu Finance cos-icon webfont | 389 个图标,本地化 |
catalog 字段说明:仅保留 代码 / 公司中文名 / 国家 / 交易所——这是一个纯行业分类目录,不再追踪市值变化(市值波动太频繁,且 heatmap 已通过雪球实时获取)。
重新生成 catalog:
python scripts/fetch_us_stocks.py
# 输出:assets/catalog/us_stocks_by_sector.mdfrom stocklens.assets import assets| 成员 | 类型 | 说明 |
|---|---|---|
root |
Path |
assets/ 顶层目录 |
catalog_dir |
Path |
assets/catalog/ |
catalog_path |
Path |
assets/catalog/us_stocks_by_sector.md,含 data_assets/ 兼容回退 |
icon_font_dir |
Path |
assets/icons/font/,含 web/static/icons/ 兼容回退 |
logo_dir |
Path |
assets/icons/logos/ |
logo_path(code) |
Path |
单只股票 logo 物理路径,自动 normalize |
has_logo(code) |
bool |
本地是否已有 |
list_local_logos() |
set[str] |
已有 logo 的 ticker 集合(缓存,需 invalidate_logo_cache() 刷新) |
normalize_ticker(code) |
str |
静态方法。'GOOGL/GOOG' → 'GOOGL'、'BRK.B' → 'BRK-B' |
all_tickers_from_catalog() |
Iterable[str] |
遍历 catalog 中所有 ticker(已规范化、去重) |
mgr = AssetManager(root=tmp_path) # 测试夹具用| 调用方 | 调用 | 用途 |
|---|---|---|
api/app.py |
assets.icon_font_dir / assets.logo_dir |
启动时 app.mount('/icons/font', ...)、app.mount('/logos', ...) |
api/v1/endpoints/stocks.py |
assets.normalize_ticker / assets.has_logo |
GET /api/v1/stocks/{code}/logo 端点的本地优先 + FMP 回退 |
stocklens/services/sector_catalog_service.py |
assets.catalog_path |
解析 markdown 清单 |
scripts/download_stock_logos.py |
assets.logo_dir / assets.all_tickers_from_catalog / assets.invalidate_logo_cache |
批量下载 + 后处理 logos |
| URL | 物理目录 |
|---|---|
/icons/font/* |
assets/icons/font/ |
/logos/* |
assets/icons/logos/ |
GET /api/v1/stocks/{code}/logo |
302 到 /logos/{NORMALIZED}.png,本地未命中则 302 到 FMP CDN |
tests/unit/test_asset_manager.py 覆盖:
- 默认/自定义 root 解析
- catalog path(存在 / 不存在均正常返回)
- ticker 规范化(5 个常见用例)
logo_path/has_logo/list_local_logos- 缓存行为(首次 = 二次同对象;invalidate 后能看到新文件)
- 单例完整性
13 个用例,全部通过。
| 目录 | 内容 | 谁管 | 是否入 git |
|---|---|---|---|
assets/ |
预制数据 | AssetManager |
✅ |
data/ |
SQLite DB | DatabaseManager |
❌ |
reports/ |
LLM 生成报告 | report_storage |
❌ |
logs/ |
运行日志 | logging | ❌ |
# 重新生成全量股票 catalog(NASDAQ Screener + nvstly/symbols)
python scripts/fetch_us_stocks.py
# 重下 logo(缺失的)
python scripts/download_stock_logos.py
# 强制全量重下
python scripts/download_stock_logos.py --force
# 仅下载指定股票
python scripts/download_stock_logos.py --tickers AAPL NVDA MSFT编辑 assets/catalog/us_stocks_by_sector.md 后无需重启服务,按 mtime 缓存自动重载。
SectorCatalogService 同时识别:
- 新格式(4 列,2026-05+):
| 代码 | 公司 | 国家 | 交易所 | - 旧格式(5 列,legacy):
| 代码 | 公司 | 国家 | 市值(B) | 交易所 |
新格式中 market_cap_b 字段会被解析为 None,由 heatmap API 在运行时通过雪球补齐。