使用专业级风险指标分析您的 NSE 投资组合 —— 包括 Value at Risk、蒙特卡洛模拟、因子分解、市场状态检测、HRP 优化、压力测试、Altman Z-Score、VaR 回测、GARCH 波动率建模、PELVE 比率以及高级投资组合优化。零付费 API。包含 355 项测试。
## 功能
| 类别 | 您将获得 |
|----------|-------------|
| **风险指标** | 年化波动率、VaR (95%/99%)、CVaR、Sharpe/Sortino 比率、Calmar 比率、Treynor 比率、beta、最大回撤(包含峰谷日期)。日收益率的偏度和超额峰度 |
| **蒙特卡洛模拟** | 10,000 条路径的 GBM 模拟 —— 盈利概率、置信区间、区间 VaR |
| **优化** | 层次风险平价 (HRP)、最小波动率、最大 Sharpe —— 支持权重上限、现金工具排除和交易成本估算 |
| **市场状态检测** | 基于 HMM 的牛市/中性/熊市分类,包含状态转移矩阵和各状态统计信息。未安装 hmmlearn 时回退到分位数启发式算法 |
| **因子分解** | 市场、规模、动量、波动率、流动性和集中度因子暴露。针对原油、利率、INR/USD 和风险情绪的宏观敏感度估算 |
| **行业分析** | 预先映射了涵盖 18 个行业的 160 多只 NSE 股票。集中度检测 (HHI)、多元化评分 |
| **基准比较** | Nifty 50 / Bank Nifty / Sensex / 行业指数。Alpha、跟踪误差、信息比率、月度跑赢基准表现 |
| **相关性去噪** | Marchenko-Pastur 特征值裁剪,以获得更清晰协方差估算 |
| **压力测试** | 5 个基本场景 + 7 个宏观场景,带有特定行业乘数和因果推理 |
| **机构评分** | P×I×C 框架 —— 总体风险、信念度、压力、隐藏相关性和尾部风险评分 |
| **预警** | MA 交叉、RSI 极值、波动率状态切换、相关性破裂、动量背离 |
| **建议** | 可执行的建议(减持/对冲/分散/建仓),包含预期风险降低和权衡分析 |
| **AI 叙述** | 基于规则的通俗英文解释 —— 波动率、VaR、Sharpe、回撤、集中度、基准 alpha/beta、关键问题、总体结论。无需 LLM,无 API 调用 |
|| **风险配置** | 保守 / 稳健 / 激进 —— 控制优化方法、单股上限及 6 项建议阈值。更改配置会自动重新计算所有指标 |
|| **投资组合健康度仪表** | 每个报告顶部的单一 0-100 健康度评分 —— 带有绿/黄/红颜色编码。即时回答“我的投资组合状况如何?” |
|| **无风险利率** | 侧边栏中的可调滑块 (3-10%) —— Sharpe、Sortino 和 alpha 动态更新。默认为 6.5%(印度 10 年期债券收益率) |
|| **可分享链接** | 在 `?p=` 查询参数中使用 Base64 编码的投资组合 —— 将您的风险报告作为单个 URL 分享。无需服务器存储 |
|||| **导出** | 包含仓位级风险数据的 CSV + 4 页 PDF 报告(封面、风险分析、持仓明细) |
|||| **10+ 券商格式** | Zerodha、Groww、Upstox、Angel One、ICICI Direct、Kotak、HDFC —— 印度数字格式,自动列检测 |
|||| **投资组合构成** | ETF / 被动配置占比 %、美国市场敞口 % (MAFANG, MASPTOP50)、前 3 大集中度 %、盈亏计数 |
|||| **尝试示例投资组合** | 一键按钮加载包含股票、行业 ETF 和商品 ETF 的多元化 7 只持仓投资组合。无需任何设置即可进行即时分析 —— 无需上传 CSV |
## 演示
## 快速开始
```
# 克隆并进入
git clone https://github.com/AshayK003/nse-portfolio-risk-scanner.git
cd nse-portfolio-risk-scanner
# 创建虚拟环境
python -m venv .venv
# Windows: .venv\Scripts\activate
# macOS/Linux: source .venv/bin/activate
# 安装(按需选择 extras)
pip install -e ".[dev,pdf]" # Minimal — yfinance only
pip install -e ".[dev,pdf,nse]" # + nselib for official NSE data
pip install -e ".[dev,pdf,nse,ml]" # + hmmlearn for ML regime detection
# 启动
streamlit run app.py
```
### 依赖矩阵
| 包名 | 必需 | 用途 |
|---------|----------|---------|
| streamlit | 是 | Web UI 框架 |
| plotly | 是 | 交互式图表 |
| yfinance | 是 | 价格数据(后备) |
| numpy / pandas / scipy | 是 | 计算 |
| loguru | 是 | 结构化日志 |
| diskcache | 是 | 价格缓存 |
| nselib | `[nse]` | 官方 NSE 数据 |
| hmmlearn | `[ml]` | HMM 市场状态检测 |
| pdf-studio (ReportLab) + matplotlib | — | PDF 报告导出(默认包含) |
| ruff / pre-commit | dev | 代码检查 / 格式化 |
| pytest / vcrpy | dev | 测试 |
### 环境变量
无需任何配置。零配置即可运行。
| 变量 | 用途 | 默认值 |
|----------|---------|---------|
| `NSE_RISK_SCANNER_CACHE_DIR` | 覆盖 diskcache 目录 | `./data/__cache__` |
| `NSE_RISK_SCANNER_DB_PATH` | 覆盖 SQLite 数据库路径 | `./data/nse_risk_scanner.db` |
## 架构
```
┌─────────────────────────────────────────────────────┐
│ app.py (thin orchestration, ~200 imperative lines) │
├─────────────────────────────────────────────────────┤
│ ┌──────────────────┐ │
│ │ engine/ │ │
│ │ pure functions │ │
│ │ zero Streamlit │ │
│ │ zero I/O │ │
│ └──────────────────┘ │
│ ┌──────────────────┐ │
│ │ data/ │ │
│ │ price fetching │ │
│ │ 3-tier cache │ │
│ └──────────────────┘ │
│ ┌──────────────────┐ │
│ │ ui/ │ │
│ │ Streamlit only │ │
│ │ no business logic│ │
│ └──────────────────┘ │
│ ┌──────────────────┐ │
│ │ storage/ │ │
│ │ SQLite history │ │
│ └──────────────────┘ │
└─────────────────────────────────────────────────────┘
```
**规则非常严格:**
- `engine/` 永远不导入 Streamlit。每个函数都是纯函数 —— 输入数据,输出 dataclass。
- `ui/` 从不进行数学计算。它调用 engine 函数并渲染结果。
- `app.py` 是一个轻量级的编排器:读取 CSV → 计算 → 渲染。
- 每个智能模块都受到独立保护 —— 一个模块出现故障不会影响其他模块。
### 目录结构
```
├── app.py # Entry point
├── engine/
│ ├── __init__.py # Data models (dataclasses only)
│ ├── portfolio.py # CSV parsing, validation, ticker normalization
│ ├── risk.py # VaR, CVaR, volatility, beta, drawdown, Monte Carlo, denoising
│ ├── performance.py # Returns, CAGR, win rate, holding P&L
│ ├── benchmark.py # Benchmark comparison (alpha, tracking error, IR)
│ ├── sector.py # Sector classification + concentration (HHI)
│ ├── optimization.py # HRP, min-vol, max-Sharpe, rebalancing
│ ├── regime.py # HMM regime detection (optional hmmlearn)
│ ├── scenario.py # Basic + macro stress tests
│ ├── backtesting.py # VaR backtesting (Kupiec POF)
│ ├── fundamentals.py # Altman Z-Score
│ ├── garch_var.py # GARCH(1,1)-t VaR
│ ├── optimization_advanced.py # Riskfolio-Lib wrapper
│ ├── pelve.py # PELVE ratio
│ ├── factors.py # Factor decomposition + macro sensitivities
│ ├── scoring.py # Institutional risk scoring (P×I×C)
│ ├── narrative.py # Rule-based narrative generation (zero LLM)
│ ├── recommendations.py # Portfolio recommendations engine
│ ├── warnings.py # Early warning signal detection
│ └── delivery.py # NSE delivery analysis (optional nselib)
├── ui/
│ ├── dashboard.py # Metric cards, tabs, layout
│ ├── charts.py # Plotly chart builders
│ ├── upload.py # CSV upload + manual entry
│ ├── export.py # CSV/PDF export
│ ├── styles.py # Dark theme CSS
│ └── icons.py # SVG icon helpers
├── data/
│ ├── prices.py # Price fetching (nselib + yfinance, 3-tier cache)
│ └── cache.py # diskcache-backed L2
├── storage/
│ ├── db.py # SQLite CRUD
│ └── models.py # Serialization
├── tests/ # 355 tests
└── .pre-commit-config.yaml # Ruff + pre-commit hooks
```
## 本地开发
```
# Lint 与格式化
ruff check .
ruff format .
# 运行所有测试
pytest tests/ -v
# 单个测试文件
pytest tests/test_risk.py -v
# 附带 coverage
pytest tests/ --cov=engine --cov=data
# Pre-commit hooks(安装一次)
pre-commit install
```
### 代码风格
| 规则 | 标准 |
|------|----------|
| **目标** | Python 3.10+ |
| **行长** | 110 |
| **代码检查工具** | ruff (E, F, I, N, W, UP, B, SIM) |
| **格式化工具** | ruff-format |
| **不可变性** | 每次修改时使用 spread/copy,共享状态绝不使用 `.append()` 或直接赋值 |
| **注释** | 解释*为什么*,而不是*是什么* |
### 提交规范
```
:
Types: fix, feat, docs, refactor, test, chore
```
## 测试
跨 26 个测试文件的 355 项测试。`engine/` 中的每个模块都有专门的单元测试。
```
pytest tests/ # Full suite
pytest tests/ -v --tb=short # Verbose, short tracebacks
pytest tests/test_risk.py -k "monte" # Filter by test name
pytest tests/ --cov=engine --cov-report=term-missing
```
| 领域 | 文件 | 覆盖范围 |
|------|------|----------|
| CSV 解析 + 券商格式 | `test_portfolio.py` | 所有 10+ 券商格式、印度数字、BOM、分隔符 |
| 风险指标 | `test_risk.py` | VaR、波动率、回撤、蒙特卡洛、去噪、归因 |
| 行业分类 | `test_sector.py` | 映射、HHI、多元化评分、未知代码 |
| 性能 | `test_performance.py` | 收益率、最大回撤 |
| 基准 | `test_benchmark.py` | Alpha、beta、跟踪误差、IR、跑赢基准表现 |
| 优化 | `test_optimization.py` | HRP、最小波动率、最大 Sharpe、再平衡 |
| 市场状态检测 | `test_regime.py` | HMM、统计后备、状态转移矩阵 |
| 场景分析 | `test_scenario.py` | 基本、宏观场景、行业影响 |
| 因子 | `test_factors.py` | 因子暴露、宏观驱动因素 |
| 评分 | `test_scoring.py` | P×I×C 评分、风险因素、解释 |
| 建议 | `test_recommendations.py` | 操作生成、权衡、优先级 |
| 叙述 | `test_narrative.py` | 阈值边界、边缘情况、无基准情况 |
| 基本面 | `test_fundamentals.py` | Altman Z-Score、区域分类、缺失数据 |
| 回测 | `test_backtesting.py` | Kupiec POF、突破率、多重置信度 |
| GARCH VaR | `test_garch_var.py` | GARCH(1,1)-t VaR、数据不足、arch 后备 |
| PELVE | `test_pelve.py` | PELVE 比率、epsilon 参数、零波动率、肥尾 |
| 预警 | `test_warnings.py` | MA 交叉、RSI、波动率切换、相关性破裂 |
| 价格获取 | `test_prices.py` | 重试、退避、并行获取、错误处理 |
| 缓存 | `test_cache.py` | TTL、驱逐、清除、往返 |
| 存储 | `test_db.py`, `test_models.py` | CRUD、序列化、历史记录 |
| 集成 | `test_integration.py` | 完整 pipeline、边缘情况 |
| PDF 导出 | `test_pdf_export.py` | 图表生成、报告组装 |
## 部署
### Streamlit Cloud(推荐)
```
# .streamlit/config.toml(已包含)
[server]
headless = true
[theme]
base = "dark"
```
1. 推送到 GitHub
2. 访问 [share.streamlit.io](https://share.streamlit.io)
3. 连接仓库
4. 将主文件设置为 `app.py`
5. 部署 —— 不需要任何密钥或环境变量
### 手动 / Docker
```
streamlit run app.py --server.port 8501 --server.headless true
```
应用默认绑定到 `0.0.0.0:8501`。在生产环境中请使用反向代理(nginx、Caddy)。
## Pre-commit
本项目在每次提交时使用 [pre-commit](https://pre-commit.com) 和 [Ruff](https://docs.astral.sh/ruff/) 进行代码检查和格式化。设置方法:
## 常见问题排查
### “未能获取任何持仓的数据”
**原因:** yfinance 速率限制或网络问题。印度股票需要 `.NS` 后缀 —— `RELIANCE` 会自动变为 `RELIANCE.NS`,但如果 yfinance 失败,则不会返回任何数据。
**修复:** 安装 nselib (`pip install -e ".[nse]"`) 以获取官方 NSE 数据。或者等待并重试 —— yfinance 具有滚动速率限制。
### “找不到列:ticker, quantity, price”
**原因:** CSV 不匹配任何已知券商格式。无法识别列名。
**修复:** 将列名重命名为受支持的别名之一(参见 `engine/portfolio.py:_COLUMN_ALIASES`)。至少需要:`symbol`、`qty`、`avg price`。
### CSV 解析成功但显示数量为零
**原因:** 带有逗号的印度数字格式(例如 `1,000`)。解析器会全局去除逗号,但如果您的区域设置使用逗号作为小数点分隔符,解析将会中断。
**修复:** 从您的券商导出时不要使用千位分隔符,或者使用纯文本格式。
### 蒙特卡洛显示 NaN 或 0
**原因:** 价格历史记录不足(<10 个交易日)。模拟需要足够的数据来估算漂移和波动率。
**修复:** 使用 1 年的价格区间。如果某只股票最近才上市,其历史数据将会很有限。
### 市场状态检测总是返回 None
**原因:** 默认的统计后备算法需要 50+ 个数据点。HMM 模式 (hmmlearn) 需要 100+ 个。
**修复:** 安装 hmmlearn (`pip install -e ".[ml]"`) 或确保有 6 个月以上的价格历史记录。
### “投资组合超过最大持仓数 (200)”
**原因:** 为防止资源耗尽而设置的硬性限制。您的 CSV 包含超过 200 行。
**修复:** 拆分为子投资组合,或在 `engine/portfolio.py:_MAX_HOLDINGS` 中降低该限制。
### 良好的首个issue
寻找带有 [`good-first-issue`](https://github.com/AshayK003/nse-portfolio-risk-scanner/labels/good-first-issue) 标签的 issue。这些 issue 范围明确、文档齐全,并且有现成的测试作为参考。
Built by Ashay Kushwaha ·
GNU AGPL v3 ·