riskguard
Health Pass
- License — License: MIT
- Description — Repository has a description
- Active repo — Last push 0 days ago
- Community trust — 20 GitHub stars
Code Pass
- Code scan — Scanned 12 files during light audit, no dangerous patterns found
Permissions Pass
- Permissions — No dangerous permissions requested
No AI report is available for this listing yet.
券商无关的开源交易风控层:仓位上限 · 回撤熔断 · 动态仓位(Kelly/波动率) · 防篡改审计 · 实时监控守护。补上量化五层积木唯一缺的一层,核心零依赖。
🛡️ RiskGuard
给你的交易系统,配一个 7×24、永不情绪化的风控官。
"Rule No.1:永远不要亏钱。Rule No.2:永远不要忘记第一条。" —— 沃伦·巴菲特
简体中文 · English
量化交易的五层积木——数据 → 研究 → 回测 → 风控 → 执行——数据有 OpenBB、研究有 Qlib、回测有 backtesting.py/vectorbt、执行有 Alpaca,唯独风控层没有一个成熟的开源标准件。RiskGuard 就是来补这块空白的。
风控的本质不是技术,而是提前写死的纪律。RiskGuard 把那几条能挡住绝大多数爆仓惨剧的规则,做成一个券商无关、核心零依赖、可组合的库,让系统自动执行——因为等你真的亏钱上头那一刻,意志力是这世界上最靠不住的东西。
📖 目录- 看它拦下一次爆仓(30 秒)
- 为什么不自己写几个 if 判断就好?
- 一套风控,这些能力
- 60 秒上手
- 动态下注
- 实时哨兵(kill-switch)
- 防篡改审计
- 状态持久化
- 每日体检 + 压力测试
- 接进你的回测框架
- 三档预设 + 命令行
- 架构
- 适合谁 / 设计原则 / 边界 / 路线图
🔥 看它拦下一次爆仓(30 秒)
同一个"满仓做多"信号,喂给同一段 −45% 的崩盘行情。一个不设风控,一个套上 RiskGuard(单笔 10% 上限 + 15% 回撤熔断)。看最大回撤的差距:
| 裸奔账户 | 🛡️ RiskGuard 账户 | |
|---|---|---|
| 单笔仓位占比 | 100% | 10%(自动缩单) |
| 期末权益(起始 10 万) | 55,000 | 94,377 |
| 最大回撤 | −45.0% | −5.6% |
同一段崩盘,风控把最大回撤从 45% 压到 5.6%。
纪律不是让你多赚,是让你别在最坏的一天把本金亏光。
这段数字是
examples/06_with_vs_without.py的真实输出,是"仓位封顶"这条纪律的机械结果、可一键复现——不是收益承诺。git clone下来自己跑一遍就知道。
但纪律不是免费的
同一档配置,换成 +200% 的单边牛市,风控账户只吃到裸奔涨幅的 10%——120,000 vs 300,000。仓位上限截断的是分布的两端:既拦暴跌,也拦暴涨。这是真实的保费,不是隐藏条款(见 examples/09)。值不值,取决于你更怕错过暴涨,还是更怕活不到下一次机会。
🤔 为什么不自己写几个 if 判断就好?
你当然可以写。但风控真正难的不是"能不能判断",而是分析质量和决策纪律——尤其在你亏钱上头、最不该动手的那一刻。
| 你以为够了 | 现实会发生什么 | RiskGuard 怎么做 |
|---|---|---|
| 记在脑子里"别超过 10%" | 上头时,脑子里的规则第一个失效 | 写进配置、系统强制,越界自动缩单 |
| "跌多了我就手动止损" | 你会犹豫、舍不得、甚至加仓摊平 | 回撤触线自动熔断,拉闸停手 |
| 回测年化 30% 很香 | 滑点/手续费一扣,实盘常年亏 | 模拟盘内建滑点 + 手续费,先扣摩擦再看 |
| 拿不到价就用上一个价 | 用错价 = 敞口算错 = 风控失效 | 拿不到价直接拒单(fail-closed) |
| 平仓单和开仓单一视同仁 | 熔断时把平仓也拦了,风险无法收敛 | 减仓单永远放行 |
| 日志记个 txt | 能被随手改,出事查无对证 | 哈希链留痕,verify() 一键验真伪 |
| 重启一下继续交易 | 高水位/熔断状态全部归零,纪律形同虚设 | 状态可持久化,重启也扛得住熔断 |
这不是"能不能判断"的问题,而是"纪律能不能被系统强制执行"的问题。 RiskGuard 把这七件事全部焊死进代码。
🧱 一套风控,这些能力
| 能力 | 类 | 一句话 |
|---|---|---|
| 🚫 单笔仓位闸 | MaxPositionLimit |
任一标的名义敞口 ≤ 权益的 max_position_pct(默认 10%)—— 别把身家压一注 |
| 🧯 回撤熔断 | DrawdownCircuitBreaker |
回撤触及 max_drawdown_pct(默认 15%)即停新仓,减仓永远放行 |
| 🐣 新兵隔离区 | StrategyQuarantine |
新策略先用小钱蹲观察期(默认 90 天),活过了再加仓 |
| ⚖️ 组合敞口闸 | GrossExposureLimit / NetExposureLimit |
总敞口管杠杆、净敞口管方向;一堆各自合规的小仓也不许叠成大风险 |
| 🎲 动态下注 | KellySizer / VolatilityTargetSizer / FixedFractionalSizer |
下多大注让公式算,不让情绪算;无正期望时自动不下注 |
| 🚨 实时哨兵 | RiskMonitor |
后台线程盯盘,触线自动踩刹车(撤单 + 平仓)——情绪失控时替你拉闸 |
| 🌙 每日体检 / 压力测试 | build_digest / run_stress_test |
结构化摘要给 AI agent 叙述;"如果集体跌 20%"的一次性推演,零副作用 |
| 📿 防篡改案底 | JsonlAuditSink / SqliteAuditSink |
每次裁决/熔断/成交都留哈希链记录,可选 HMAC 防伪 |
| 🔒 状态持久化 | SqliteStateStore |
高水位/熔断状态写透磁盘,重启不再是绕过风控的后门 |
| 🔌 券商无关 | Broker / PaperBroker / AlpacaBroker |
纸面模拟盘、Alpaca、或你自研后端,实现一个接口即可接入 |
两层严格分离:
Sizer只决定"下多大注",Rule决定"能不能下、要不要缩、该不该全停"。AI 可以做研究、写代码、挑毛病,但每一笔真实指令都必须先过这几条写死的风控规则。
⚡ 60 秒上手
⚠️ 尚未发布到 PyPI——
pip install riskguard现在装不上,请用下面的 git 安装方式。
发布到 PyPI 后这里会第一时间更新(进度见路线图)。
pip install git+https://github.com/SilentFleetKK/riskguard.git # 核心,零依赖
pip install "riskguard[alpaca] @ git+https://github.com/SilentFleetKK/riskguard.git" # 需要 Alpaca 适配器时
from riskguard import RiskEngine, RiskConfig, Order, Side, PaperBroker
# 1) 一个带滑点 + 手续费的纸面模拟盘(呼应"回测和实盘是两个世界")
broker = PaperBroker(cash=100_000, slippage_bps=2, commission_bps=1,
marks={"AAPL": 200.0})
# 2) 把纪律写进配置:单笔 ≤ 10%、回撤 15% 熔断
engine = RiskEngine(RiskConfig(max_position_pct=0.10, max_drawdown_pct=0.15),
broker=broker)
# 3) 想买 1000 股 = 20 万 = 占比 200%,风控自动缩到上限
decision = engine.check(Order("AAPL", Side.BUY, 1000), broker.get_portfolio())
print(decision.decision) # Decision.RESIZE
print(decision.order.quantity) # 50.0 ← 缩到 10% 权益
print(decision.reasons())
# AAPL capped to 10.00% of equity (qty 1000 -> 50); gross exposure capped ... (qty 1000 -> 500)
# ↑ 多条规则可能同时报告,引擎取最保守的那个(min = 50)
# 4) 放行则真正下单(这里进模拟盘)
engine.submit(Order("AAPL", Side.BUY, 40), broker.get_portfolio())
熔断触发后必须人工复盘再 engine.reset_breaker()——不把原因想明白,不许重启。
🎲 动态下注:让公式算,不让情绪算
from riskguard import KellySizer, Signal, Side
engine = RiskEngine(RiskConfig(kelly_fraction=0.5), sizer=KellySizer(), broker=broker)
# Kelly 需要胜率与盈亏比;f = kelly_fraction × (p − q/b)
sig = Signal("AAPL", Side.BUY, price=200.0, win_probability=0.55, payoff_ratio=1.5)
engine.size_and_submit(sig, broker.get_portfolio()) # 无正期望时返回 None,自动不下注
FixedFractionalSizer—— 固定比例,最难自欺。KellySizer—— 分数 Kelly,满 Kelly 太颠簸,默认取半。VolatilityTargetSizer—— 目标波动率 / 实现波动率,给每个头寸等量风险预算。
🚨 实时哨兵(kill-switch)
光把纪律写进配置还不够——你睡觉、开会、没信号的时候,市场不会为了配合你的作息暂停。RiskMonitor 是一条永不闭眼的后台线程,周期性盯着账户,触线自动踩刹车:
from riskguard import RiskMonitor
with RiskMonitor(engine, broker, interval=5.0, auto_liquidate=True):
... # 后台线程:周期观测权益 → 触及回撤红线自动熔断并平仓
📿 防篡改审计
from riskguard import RiskEngine, JsonlAuditSink
with JsonlAuditSink("audit.jsonl", hmac_key="放在日志之外的密钥") as audit:
engine = RiskEngine(broker=broker, audit=audit)
...
JsonlAuditSink.verify("audit.jsonl", hmac_key="...", expected_count=42) # 验真伪
诚实说清边界:不带密钥的纯哈希链只能防"改/重排中间记录",挡不住尾部截断或整体重写。要真正防伪,传
hmac_key(密钥存在日志之外)+verify(expected_count=N)用外部锚点核对条数。
🔒 状态持久化:堵住"重启绕过熔断"的后门
RiskState 默认纯内存——进程一重启,高水位和熔断标记全部归零,等于给亏红眼的操作者留了一条"重启一下继续交易"的隐藏后路。这与本库的立库初衷直接矛盾。接一个 state_store,熔断就真的扛得住重启(examples/10):
from riskguard import RiskEngine, SqliteStateStore
store = SqliteStateStore("risk_state.db")
engine = RiskEngine(config, broker=broker, state_store=store)
# 构造时自动从存档恢复高水位/熔断/策略入役时间;此后每次状态变更自动写透。
# 存档读取失败会直接抛出——宁可拒绝启动,也不能悄悄当成"一切正常"重新开始。
CLI 同理支持跨调用持久化(否则脚本反复调用本身就是一种"重启绕过"),多个策略/标的共用一个数据库文件时用 --state-key 隔离:
$ riskguard check --state-db risk_state.db --state-key strategy_a \
--equity 80000 --side buy --qty 10 --price 100
诚实说清边界:这堵住的是"重启进程"这条最常见的绕过路径,不是万能防绕过——
一个决心要绕的人仍可以删掉存档文件、改配置、或者干脆改代码。它提高的是绕过成本,
不是消灭绕过本身;个人量化的风控终究是尤利西斯之约,不是机构级的权责分离。一个
(存档文件, key)只能有一个活跃写者:两个引擎共用同一个 key 会被乐观锁
(版本号 CAS)检测到并报错,而不是静默互相覆盖——多策略/多账户请务必用不同 key。
🌙 每日体检 + 压力测试:AI 帮你盯盘的那一层
"AI 帮你 24 小时盯盘"这个刚需,拆开看是四层:实时监控、异常检测、硬编码熔断线、压力测试。硬编码熔断线是 RiskGuard 从第一行代码起就在做的事(上面几节都是);这里补上另外两层里属于本库使命范围的部分——每日体检(把"实时监控"摊开成一份可复算的摘要)和压力测试(把"如果……会怎样"摊开成一份推演)。
刻意不做的事:统计/AI 判定"这次不一样"式的异常检测,不属于这个库——那是数据科学能力(需要历史基线、新闻/情绪输入、模型打分),和 RiskGuard "确定性、fail-closed、绝不模糊判断" 的设计哲学是两种不同的产品。这一层该由外部的 AI agent 来做:agent 负责观察、判断、叙述,RiskGuard 只负责保证它拿到的事实是真的、以及在规则被触发时毫不犹豫地执行——这条线不能由 AI 决定。
from riskguard.reporting import build_digest, render_digest_text, run_stress_test, render_stress_text
# 每日体检:把 engine 当前状态(高水位/熔断/隔离策略)+ 一份持仓快照,组装成结构化摘要
report = build_digest(engine, portfolio)
print(render_digest_text(report))
report.to_dict() # 结构化数据,交给 AI agent 去叙述、去推送提醒
# 压力测试:"如果我的持仓明天集体跌 20%,我扛得住吗"——绝对只读,零副作用
result = run_stress_test(engine, portfolio, shock_pct=-0.20)
print(render_stress_text(result))
CLI 同样支持,适合脚本化的"每天 check-in"和"随手问一句"(examples/11、examples/12):
$ riskguard digest --equity 95000 --position AAPL:80:190 --position TSLA:-40:250 --state-db risk.db
$ riskguard stress --equity 95000 --shock -0.20 --position AAPL:80:190 --position TSLA:-40:250
压力测试的只读承诺:不触发熔断、不写审计、不碰持久化——即便
--state-db指向一个还不存在的文件,也不会在磁盘上留下任何新文件。它是纯粹的"假设推演",算完就完。
🔌 接进你的回测框架
把 RiskGuard 作为"风险叠加层"接进 backtesting.py / vectorbt,让策略研究和爆仓防护第一次在同一条流水线上(examples/07)。
# 框架无关的叠加层:目标持仓 → 风控批准的订单;还能一键跑"套风控 vs 不套"对比
from riskguard.backtest import RiskOverlay, compare
res = compare(prices, my_strategy, config=RiskConfig(max_position_pct=0.10))
print(res["naive"].max_drawdown, res["guarded"].max_drawdown) # 例:−45% vs −5.6%
# backtesting.py:子类只写 signal() 返回目标权重,下单自动过风控
from riskguard.backtest import make_riskguard_strategy
class MyStrat(make_riskguard_strategy(RiskConfig(max_position_pct=0.10))):
def signal(self): return 1.0 if bullish else 0.0
# vectorbt:把仓位按上限封顶(纯函数,不装 vectorbt 也能用)
from riskguard.backtest import risk_capped_weights, kelly_weights
size = risk_capped_weights(target_weights, RiskConfig(max_position_pct=0.10))
🎚️ 三档预设 + 命令行
不想逐个调参?挑一档起步——保守 / 均衡 / 激进:
from riskguard import get_preset, RiskEngine
engine = RiskEngine(get_preset("conservative")) # 或 "balanced" / "aggressive"
# 再用 config.replace(...) 微调成你自己的口径
装好后还带一个零依赖命令行,一键验单、看预设、跑对比:
$ riskguard check --preset balanced --equity 100000 --side buy --qty 1000 --price 200
裁决: RESIZE
放行: BUY 50 ASSET (占权益 10.0%)
$ riskguard presets # 三档参数对照表
$ riskguard replay --prices 100,96,90,82,75,70 # 套风控 vs 不套 的回撤对比
🗺️ 架构
Signal ──Sizer──┐ RiskOverlay(回测叠加层) / reporting(digest · stress)
Kelly·波动率·固定 │ 都是同一个 RiskEngine 的不同调用方,只读或推演
▼
Order
│
▼
┌──────────────────────────────────┐
│ RiskEngine │
│ Rules 能不能下、要不要缩 │
│ Breaker 该不该全停 │
│ State ⇄ StateStore(持久化) │
└───────────────┬──────────────────┘
│ 放行的订单(线程安全、加锁)
▼
Broker 抽象层 ── PaperBroker / AlpacaBroker / 自研
📿 审计(JSONL/SQLite,可选 HMAC) · 🚨 RiskMonitor(kill-switch,直连 Broker)
🙋 适合谁
- 已经在用 backtesting.py / vectorbt / 自研执行层,缺一个不绑定任何框架的风控中间件的
- 想让 AI agent 帮你研究、下单,但不敢让它直接碰真实资金的——RiskGuard 就是那道 AI 绕不过去的闸门
- 爆过一次仓、亏过一次意气之战,想把"纪律"从脑子里搬进代码、不再靠意志力硬扛的
- 需要一个券商无关、能接 Alpaca / 自研后端的风控中间层的
- 想要一份"从零搭起、还有测试和模拟盘实战"的硬核作品的
🧭 设计原则
- 纪律成文、系统强制:阈值全在
RiskConfig,不可变、启动即校验。 - 绝不静默失败:拿不到价宁可抛异常、丢弃坏价,也绝不用猜测价放行(fail-closed)。
- 减仓永远放行:任何时候都不阻止风险收敛,哪怕熔断中、哪怕反手也会被夹到平仓为止。
- 一切不可变:数据对象只读,状态变更返回新快照,历史永远可回放;监控线程可安全共享。
- AI 动脑,系统守纪律:AI 可研究/写码/挑毛病,但每笔真实指令必须先过写死的风控规则。
- 纪律扛得住重启:熔断状态可持久化,进程重启不是绕过风控的合法后门。
⚠️ 诚实的边界与免责声明
- 本库是风险控制工具,不是投资建议,也不保证盈利或防止亏损。它约束的是敞口和纪律,不预测行情。
- 任何策略都应先在模拟盘养满三个月,真钱只用"亏光也不影响生活、不影响睡眠的闲钱"。
- 上面的对比数字来自确定性模拟脚本,是仓位纪律的机械结果,不代表任何未来收益。本项目不附带、也不宣称任何实盘战绩。
- 审计防篡改有明确边界(见上),别当成万能防伪。
- 本库不做异常检测/行情判断——没有新闻源、没有情绪分析、没有"这次不一样"式的模糊判断。它只做一件事:把你自己定好的硬规则,毫不犹豫地执行。
🚧 路线图
- 发布到 PyPI(
pip install riskguard真正能装) - Alpaca 适配器实盘打通(下单/持仓/撤单端到端)
- 更多券商:盈透(IBKR)、加密交易所(ccxt)
- 每日体检 + 压力测试
- 状态持久化:堵住"重启绕过熔断"的后门
- CI(pytest 矩阵 + ruff)
- 配置预设 + CLI:保守/均衡/激进三档 + 命令行
- 与 backtesting.py / vectorbt 的一键接线
- 审计外部锚定(WORM / 公证)
- AI 代理闸门三件套:日内亏损线、fat-finger 价格保护带、下单频率节流
- CLI 输出国际化(目前命令行打印内容仅支持中文;库本身的输出已是英文)
已完成的版本历史见 CHANGELOG。
🛠️ 开发
pip install -e ".[dev]"
pytest # 774 passed,CI 跑 3.10–3.13
ruff check src # 干净
mypy src # strict 模式,当前仍有已知缺口(类型标注在路上,尚未接入 CI 门禁)
📄 License
MIT。欢迎 issue / PR。
Reviews (0)
Sign in to leave a review.
Leave a reviewNo results found