mytrashcan/MarketGuard

GitHub: mytrashcan/MarketGuard

一款基于规则的韩国股票市场异常检测系统,通过 Toss 证券公开 API 实时监测行情异动并提供运维告警仪表盘。

Stars: 1 | Forks: 0

# MarketGuard [![CI](https://static.pigsec.cn/wp-content/uploads/repos/cas/ad/ad5834178f7599af9fdda11629d49cae07f2997beec49821b2920eff5bfd50e7.svg)](https://github.com/mytrashcan/MarketGuard/actions/workflows/ci.yml) MarketGuard 是一个组合项目,它**仅读取** Toss 证券 Open API 的**公开市场数据**,通过基于规则的模型检测异常迹象,并在运维仪表盘中进行记录和告警。 ## 核心特性 - 仅查询当前价、报价、K线、涨跌停限制、投资警告、交易日历以及 KRX 各市场投资者交易额 - 基于 `BigDecimal` 的价格/比例计算,并保留官方市场时间戳 - 包含 8 种检测规则、结构化的观测依据、按规则进行故障隔离、持久化的原子冷却机制 - 对同一股票信号进行事件分组,提供可解释的 0~100 关注度评分,支持审查状态、备忘录及历史记录 - 具备超时控制、按官方调用分组的 rate limit、选择性重试、`Retry-After` 以及 circuit breaker - 运维 HTTP Basic 认证,loopback 写入 token,严格的 WebSocket Origin 校验,输入限制,分 peer 的 API rate limit,以及 CSP/SRI 防护 - 使用 PostgreSQL + Flyway + Hibernate schema validation - 支持 Prometheus 指标、liveness/readiness 探针及安全的审计日志 - 采用真实的 PostgreSQL Testcontainers 测试与 Docker Compose 冒烟测试 ## 架构 ``` flowchart LR Browser["Operator browser"] -->|"Basic auth / HTTPS"| Web["Dashboard + read API"] Web --> App["Application services"] Scheduler["Bounded schedulers"] --> App App --> Core["Framework-free detection core"] App --> DB[("PostgreSQL / Flyway")] App -->|"OAuth2 + bounded REST"| Toss["Toss Open API"] Core --> App App -->|"STOMP alerts"| Browser ``` 源代码依赖方向为 `config/collector/dashboard/domain -> application/detection`。`detection` 核心模块没有引入 Spring/JPA/外部层,这一点由架构测试强制保证。详情请参阅 [architecture.md](docs/architecture.md)。 ## 检测规则 | 规则 | 判定基准 | |---|---| | `PRICE_SPIKE` | 相对于近期平均价格的波动达到设定比例以上 | | `PRICE_LIMIT` | 达到涨停/跌停价或接近至设定比例以内 | | `ORDERBOOK_IMBALANCE` | 买/卖总订单量比例超过阈值 | | `VOLUME_SURGE` | 最新一分钟成交量达到前一平均值的阈值倍数以上 | | `INVESTMENT_WARNING` | 当前存在有效的投资警告、风险、短期过热或清算交易指定 | | `PRICE_VOLUME_SURGE` | 价格波动与 1 分钟成交量同时超过各自阈值 | | `INSTITUTIONAL_NET_BUY_SURGE` | KOSPI/KOSDAQ 机构净买入同时超过近期中位数的指定倍数与最低金额 | | `INSTITUTIONAL_NET_SELL_SURGE` | KOSPI/KOSDAQ 机构净卖出同时超过近期中位数的指定倍数与最低金额 | 每个信号都会同时记录标题、摘要、通俗说明、观测值、基准值、阈值、比较范围、上下文标签以及补充确认项。各规则独立运行,某一个规则的异常不会中断对其他规则的评估。如果市场日历获取失败,采集器会将其视为收盘状态,以避免用过期数据产生误报。计算公式与边界条件请参阅 [检测规则文档](docs/detection-rules.md)。 ## 界面数据的含义 - 主界面展示 Toss 市场的整体交易额、交易量、上涨和下跌排行。排名及 `lastPrice`、`basePrice`、`changeRate`、交易量、交易额均使用同一响应中的数据。 - 兼容 API `/api/prices/live` 中的“买/卖”并非实际成交比例或各投资者净买入,而是公开买卖盘中的**未成交买/卖订单量比例**。系统会区分场外、未提供数据与 upstream 错误,将其与 0 区分开来。 - 机构资金流向信号是 KOSPI/KOSDAQ **市场整体合计**数据。它不代表个别股票或特定机构的交易,收盘前的当日数据均为不断更新的暂定值。 - 检测事件会同时保存股票或市场名称及其代码。历史数据在迁移时会将股票代码作为安全的名称回退。 ## 快速开始 环境要求为 JDK 17 和 Docker。 ``` ./gradlew clean test ./gradlew bootRun ``` 默认配置为安全的本地开发模式。 - 仅绑定至 `127.0.0.1:5050` - 禁用采集器 - 禁用身份验证 - 使用内存 H2 及本地 H2 控制台 仪表盘地址:`http://127.0.0.1:5050/` 开启实际采集需要 Toss 的凭证,若缺失任何一项,程序将启动失败。 ``` export TOSS_CLIENT_ID='...' export TOSS_CLIENT_SECRET='...' export COLLECTOR_ENABLED=true ./gradlew bootRun ``` ## Docker Compose ``` cp .env.example .env # 编辑 .env 的必填值 docker compose up --build --wait ``` 若设置 `collector=false`,则无需 Toss 凭证即可验证安全的运行环境包。仅在需要实际采集时,才设置 `TOSS_CLIENT_ID`、`TOSS_CLIENT_SECRET` 和 `COLLECTOR_ENABLED=true`。 Compose 强制执行以下限制: - 数据库密码无默认值;若开启身份验证,缺少运维密码将导致启动失败 - PostgreSQL 主机端口不对外公开,使用 named volume - 应用端口仅对主机 loopback 公开 - 采用 non-root 应用、只读 root filesystem 并移除多余 capability - 只有在 readiness 探针通过后才标记为 healthy 状态 完整且可复现的冒烟测试: ``` ./scripts/compose-smoke.sh ``` ## API 与运维端点 在生产配置下,除探针外,所有路径均默认需要身份验证。仅在主机 loopback 使用的个人部署环境,可以通过 `.env` 中的 `MARKETGUARD_SECURITY_ENABLED=false` 关闭登录界面。即使在该设置关闭的情况下,若缺少 `MARKETGUARD_OPERATOR_TOKEN`,更改事件状态或添加备忘录的写操作也会被拒绝。设置足够长的随机 token 后,仪表盘在首次写入时会要求输入 token,并将其存储在浏览器的 `localStorage` 中,随后作为 `X-Operator-Token` 请求头发送。 ``` MARKETGUARD_SECURITY_ENABLED=false MARKETGUARD_OPERATOR_TOKEN="$(openssl rand -hex 32)" ``` CSRF token 依然必需,并且审查者和作者由服务器根据已认证的 principal `operator` 来判定,而非由客户端输入决定。此模式下的查询 API 和 `/api/audit` 是有意允许匿名访问的,因此请勿将端口公开至外网,也不要连接到无需认证的 reverse proxy 或隧道中。该 token 仅用于保护写入完整性,并不提供查询数据的机密性保障。 下方的匿名访问表格基于默认开启身份验证的模式。 | 路径 | 说明 | 匿名访问 | |---|---|---| | `GET /api/prices/live` | 关注列表实时看板 | 否 | | `GET /api/rankings?type=MARKET_TRADING_AMOUNT&limit=50` | 服务器缓存的 Toss 市场排行,`limit=1..100` | 否 | | `GET /api/stocks/{code}/candles?interval=1m&count=60` | 1分钟/日 K 线,`count=1..200` | 否 | | `GET /api/anomalies?limit=50` | 近期异常记录,`limit=1..200` | 否 | | `GET /api/anomalies/{id}` | 结构化的单一信号依据 | 否 | | `GET /api/cases?...` | 事件列表,支持 pagination 及状态/规则/严重程度/股票/时间/评分过滤 | 否 | | `GET /api/cases/{id}` | 评分构成、时间线、备忘录及状态历史 | 否 | | `PATCH /api/cases/{id}/status` | 基于版本的状态变更 | 否 | | `POST /api/cases/{id}/notes` | 添加审查备忘录 | 否 | | `GET /api/stocks/{code}/context` | 价格/成交量图表及当前可用的市场背景 | 否 | | `GET /api/analytics/rules` | 各规则的触发、驳回、审查前列及平均审查时间 | 否 | | `GET /api/csrf` | 浏览器写入请求所需的 CSRF token | 否 | | `GET /api/audit?limit=100` | 近期审计记录 | 否 | | `GET /api/stocks/{code}/snapshots?limit=50` | 近期行情快照 | 否 | | `GET /actuator/health/liveness` | 进程存活状态 | 是 | | `GET /actuator/health/readiness` | 应用/数据库准备状态 | 是 | | `GET /actuator/prometheus` | Prometheus 指标 | 否 | 错误的输入将返回稳定的 `400` JSON,永久性 upstream 错误返回 `502`,临时性错误或 circuit breaker 触发返回 `503`。响应中不包含内部异常、upstream 响应体或任何凭证信息。 ## 验证 ``` # 如果没有 Docker socket,PostgreSQL 集成测试将不会作为成功跳过,而是直接失败。 ./gradlew clean check bootJar docker build -t marketguard:local . ./scripts/compose-smoke.sh ``` CI 包含 Gradle wrapper 校验、全量测试、65% 行覆盖率卡点、CodeQL/依赖审查、强化镜像构建与扫描,以及 Compose 冒烟测试。GitHub Actions 与 Docker base image 均通过 commit/digest 锁定。 ## 运维与安全 - 生产环境部署、TLS reverse proxy、备份与恢复、指标与告警:[operations.md](docs/operations.md) - 信任边界与安全控制:[threat-model.md](docs/threat-model.md) - 设计与分层规则:[architecture.md](docs/architecture.md) - 事件 API 契约:[api.md](docs/api.md) - 综合评分:[composite-score.md](docs/composite-score.md) - 审查状态流转:[case-workflow.md](docs/case-workflow.md) - 数据模型:[data-model.md](docs/data-model.md) - 误报与局限性:[false-positives-and-limitations.md](docs/false-positives-and-limitations.md) - 漏洞报告与机密信息处理准则:[SECURITY.md](SECURITY.md) - 初步审计结果及解决状态:[production-readiness.md](docs/review/production-readiness.md) ## 限制说明 - Toss 每个 client 仅允许保留一个有效 token,因此对于共享同一 client credential 的应用,仅支持 **1 个** replica。 - 内置的 STOMP broker 与 API rate limiter 仅在单进程范围内有效。 - 告警发送在数据库 commit 之后以 best-effort 方式进行。断开连接的浏览器可通过查询数据库中的近期 anomaly API 进行恢复。 - anomaly/audit 的长期保留策略需根据生产环境的合规与成本需求另行制定。 - 公开部署必须置于 TLS reverse proxy 及网络 ACL 保护之下。 - 由于缺乏行业/市场指数时间序列、公告、新闻及 corporate action 数据,相对收益率与事件背景不会被凭空捏造。当前 API 会将此类背景信息明确标记为 `unavailable`。
标签:JS文件枚举, PostgreSQL, Spring, 后台面板检测, 域名枚举, 异常检测, 测试用例, 版权保护, 监控大屏, 股票市场, 自定义请求头, 请求拦截, 金融科技