跳至主要内容
小龙虾小龙虾AI
🤖

Privora · A股/港股/黄金/基金 多资产 量化分析 · 量化回测 · 模拟盘 · 实时告警 · 风险监控 · Python 策略 · AI Agent

Privora · AI Agent 投资工作流平台 — Bearer Token 即接入 /agent/skills/execute 通用 dispatcher,覆盖 A 股/港股/黄金/基金/财报数据 + Python 回测(含 sandbox)+ 模拟交易 + 组合归因(α/β TWR)+ 云端告警 + 流...

下载2.1k
星标1
版本1.0.44
AI 智能体
安全通过

技能说明


name: Privora · 数据驱动投资工作流平台 for AI Agents title: 🔬 Privora · AI Agent 投资工作流平台(A股/港股/黄金/基金/财报数据 + Python 回测 + 模拟交易 + 组合归因 + 云端告警 + 流程编排) version: 1.0.44 updatedAt: 2026-07-11 keywords:

  • A股
  • 港股
  • 基金
  • 黄金
  • 财报数据
  • 业绩预告
  • 现金分红
  • 分钟K线
  • 实时行情
  • 量化回测
  • 策略沙盒
  • Python回测
  • A股模拟盘
  • 模拟交易
  • 持仓监控
  • 组合归因
  • 净值曲线
  • AI Agent
  • 数据后端
  • 股票
  • 告警
  • 数据新鲜度 description: Privora · AI Agent 投资工作流平台 — Bearer Token 即接入 /agent/skills/execute 通用 dispatcher,覆盖 A 股/港股/黄金/基金/财报数据 + Python 回测(含 sandbox)+ 模拟交易 + 组合归因(α/β TWR)+ 云端告警 + 流程编排。Hermes / Claude / GPT / OpenClaw 全兼容。 license: MIT-0 metadata: { "openclaw": { "emoji": "📈", "requires": { "env": ["LG_AGENT_BASE_URL", "LG_AGENT_TOKEN"] } } }

Privora · AI Agent 投资工作流平台(Bearer Token 即接入 · A股/港股/黄金/基金 数据 + 回测 + 模拟交易 + 告警 + 流程编排)

给你的 AI Agent 一个统一的投资研究工作流后端 —— 数据查询 + 策略回测 + 组合归因 + 云端告警 + 流程编排一个 Token 全覆盖。

Hermes / Claude / GPT / OpenClaw 任何 Agent,通过一个 Bearer Token 即可访问:

  • 📊 多资产统一数据:A 股 + 港股 8500+ 股票统一日线stock_day,含沪深300 / 上证综指 / 中证A500 / 深证成指 4 主要指数 + 港股 3000+ 全谱)和统一分钟 K 线stock_minutes,A 股 + 港股一张表,按 ticker 路由 + 实时刷新)、持仓黄金基金财报事件(业绩预告 / 快报)——一个 API 全覆盖。
  • 🔔 7×24 云端监控:Serverless 策略托管,飞书 / 微信毫秒级预警,零服务器运维
  • 🧪 Python 策略回测:用同一份平台数据跑回测,输出 Sharpe / 最大回撤 / 交易明细
  • 🔒 加密静态、认证边界返明文:持仓数据在数据库中以 per-account 独立密钥密文存储(防 DB 层泄露 + 平台 admin 跨账户读取);持有你 Bearer Token 的 Agent 通过 API 认证后,平台按调用者身份解密并返回明文 —— 这不是 E2E 加密,Token 授权即数据访问权。
  • 🎯 1-click subscribe→alert:Agent 帮用户从"订阅 dashboard"到"配置 alert 上线"降到 1 step (2026-06-05 新增)
  • 🧾 模拟交易 (Paper Trading):MARKET / LIMIT 委托类型 + 调度器驱动 + 真实涨跌停 / 停牌信号,账户 + 订单 DB-level 幂等。

让普通人也能拥有私募级别的工作流——不需要私募的预算,就能像私募研究员一样在同一条流水线里跑数据 + 分析 + Agent + 告警。

🆕 最新版本 v1.0.44(2026-07-11)· 文档结构重组 + Quick Start §0 30 秒试用(marketplace 匿名浏览)。历史版本变更见文末 §最近更新

🎯 最适合:想把整个投研工作流(数据查询 + 回测 + 模拟交易 + 告警 + 流程编排)交给 AI Agent 自动化的散户 / 小工作室;把 Hermes / Claude / GPT / OpenClaw 当量化助手用的开发者。注意:Bearer Token 是"工作流授权凭证",不只是"数据 API key" —— 授予前先按 §Scope 分类明白要给 Agent 哪些能力

🌐 产品主页https://privora.cn · 注册即拿 Token

演示


🌟 核心亮点

1. 🤖 兼容所有主流通用 AI Agent

打破生态壁垒,本技能不仅专供某一平台,而是完美兼容 Hermes、OpenClaw、Claude Code、GitHub Copilot 等所有支持外挂工具/技能的通用大模型 Agent。只需简单配置环境变量,您的通用 AI 助手瞬间化身专业量化分析师。

2. 🔒 加密静态 · 认证边界返明文(Encryption-at-rest, not E2E)

每个账户的持仓数据在数据库中以 per-account 独立密钥密文存储。这是防"库泄露 / 平台 admin 跨账户读取"的加密,不是 E2E 加密

  • 持有你 Bearer Token 的 Agent 通过 API 认证后,平台按调用者身份解密并返回明文 —— Token 是解密权的钥匙,保管好 Token 就是保管加密防线
  • 每个账户的加密密钥独立,平台管理员账号无法跨账户读取持仓明细(DB 层保证)
  • 订阅他人发布资产时,发布方看不到你的查询内容或账户信息(widget config 对订阅方 sanitize)
  • 给不可信 Agent 的 Token = 给它明文数据。按 §Scope 授予最小 scope,不要 bundle 无关能力

3. ⚡ Serverless 极速预警与零部署

策略云端托管运行,无需您购买第三方行情 API,无需自建服务器维护 Cron 任务,无 Token 消耗税。策略触发后,毫秒级推送到您的飞书机器人或微信 Webhook。


🛠️ 能做什么

核心功能详细说明
资产盈亏巡航一键查询持仓明细、当日盈亏、历史收益率,数据由 privora.cn 闭环处理。
云端自动盯盘设置预警条件(突破均线、涨跌幅、换手率等),触发即通知,7x24小时云端值守。
多终端实时推送策略触发毫秒级推送到飞书、微信 Webhook,不错过任何交易信号。
行情数据stock_day 日线 + stock_minutes 分钟 K 线统一覆盖 A 股 + 港股 8500+ 股票(含沪深300/上证综指/中证A500/深证成指 4 指数 + 港股 3000+ 全谱,按 ticker 路由);基金日 NAV(fund_day);SGE 黄金日线(metal_day);财报事件(stock_forecast 业绩预告 + stock_express 业绩快报,11 年历史已回填)。详见下方「数据资产可用性」表。
Python 策略回测用平台日线数据跑单股 / 多股组合回测,输出 Sharpe / 最大回撤 / 交易明细 / equity curve;结果持久化到 process_backtest_result,可通过 investment.stock.backtest.list 检索历史审计记录(平台已积累 44+ 次持久化回测)。
模拟交易 (Paper Trading)MARKET / LIMIT 两种委托类型,调度器驱动,模拟完整委托 → 成交 → 盈亏核算链路;账户按 user_name 唯一(DB-level UNIQUE),订单按 (user_name, client_order_id) 幂等,Agent 重复调用不重建。适合策略 6 阶段验证的最终纸面交易关卡。
用户声音收集支持 Agent 代客户提交 Bug 和需求,无缝对接后台反馈系统。

数据资产可用性(2026-06-22 audit / Triage T-1)

DataAsset状态覆盖频率备注
stock_day🟢 生产可用 (v1.0.31 含 HK 全量)A 股 5500+ + 港股 3000+ 股票统一日线 + 4 主要指数(沪深300 1B0300 / 上证综指 1A0001 / 中证A500 1B0510 / 深证成指 399001按 ticker 自动路由(A 股 000001 / 港股 00700.HK);399001 自 2026-03-17 起停更,请求会返回 meta.benchmarkWarning
stock_minutes🟢 生产可用 (NEW v1.0.31)A 股 + 港股 8500+ 股票统一的 1/5/15/30/60 分钟 K 线分钟stock_day 一样按 ticker 路由
fund_day🟢 生产可用公募基金日 NAV日 (T+1)数据延迟约 1 个工作日
metal_day🟢 生产可用SGE 黄金 / 白银日线上海黄金交易所
stock_forecast🟢 生产可用 (NEW 2026-06-22)A 股上市公司业绩预告;11 年历史 82,457 行已回填财报季 (1/4/7/10 月底前后) 集中发布
stock_express🟢 生产可用 (NEW 2026-06-22)A 股上市公司业绩快报;11 年历史 19,945 行已回填比业绩预告更精确但发布更稀疏
stock_dividend🟢 生产可用 (NEW v1.0.32)A 股上市公司现金分红事件;进入 portfolio.attribution 归因事件驱动除权除息日发布
stock_us未实现美股日线产品 / 预算待定

对 Agent 的指导:调用 dataasset.list 看完整列表;标 🔴 / ⚫ 的资产请避免在策略里硬编码依赖。dataasset.metadata.get (2026-06-22 新上) 可查每张表的 lastUpdated / expectedUpdateCadence / cronExpression 来判断当前状态。


🛡️ Scope 与操作者责任

本 skill 是通过 Bearer Token 对接 Privora 平台的能力。操作类别的副作用不同,操作者负责按类别 scope token 并为需要的类别加入确认门槛

  • 只读(Read-only)—— 数据 API、回测结果查询、流程/调度/数据源/仪表盘/市场的 list/get。平台状态零副作用
  • 幂等写(Idempotent write)—— 模拟交易下单(DB 层 UNIQUE on user_name + client_order_id,同 key 重试返回同一记录)、marketplace subscribe(ON CONFLICT 返回已有订阅)、告警配置更新。同输入多次调用只产生一次逻辑效果,可安全重试
  • 流程状态转移(Workflow state transition)—— process.ingestion.execute 触发已授权 python_script 运行并写入 process_backtest_result 表;scheduler-instance 的 redo / hold / resume / reset-priority 转移 trigger row 状态。每次调用创建或修改持久化记录
  • 外发 webhook(Outbound webhook)—— schedule.job.plugin.webhook.trigger 与告警评估路径向操作者配置的外部端点(飞书 / 微信 / 通用 webhook)发送通知。副作用在 Privora 之外,平台不可撤销

本 skill 不暴露(需要人在 platform UI 手动完成):

  • 持久化记录的删除 / 撤销 / reset 操作
  • 调度器 online / offline 状态转移
  • Webhook 插件生命周期变更(删除 / 禁用)
  • 管理员级账户操作

本 skill 不预先声明任何操作是"agent-safe" —— 这个分类取决于操作者的风险偏好、agent 的可靠性、以及具体用例。推荐姿势:只读 + 幂等写允许 agent 自主调用,流程状态转移和外发 webhook 必须先经过用户确认门槛。

Token 使用建议

  1. privora.cn/profile/tokens 创建专用 Bearer Token
  2. 最小 scope 原则 —— 只授予当前 use case 需要的 scope(如:只读分析用 data.read;策略研究加 backtest.run;agent 真的要下模拟单才加 paper.trade)。不要为"以防万一"打包无关 scope
  3. 明确设置 LG_AGENT_BASE_URL=https://privora.cn
  4. Token 泄露立即 rotate —— Token Management 页面列出所有活跃 token 及最后使用时间戳和 revoke 按钮

🛑 绝对不要让你的 agent 代替你 mint token。Token 创建是 operator 动作,不是 agent 动作。Agent 应该消费 operator 签发的 Bearer Token,不应该自己调 POST /api/subscription/tokens

📑 输出仅供分析参考,不构成投资建议

本 skill 的输出(行情数据 / 组合分析 / 回测报告 / 模拟交易 / 告警评估)是供操作者审查的分析结果,不是投资建议、不是交易指令、也不能替代持牌财务咨询。

  • 把结果作为你自己决策过程的输入 —— 使用前请自行验证数据新鲜度、假设、边界情况
  • 实盘交易和不可逆的财务决策不应放在 agent 自动执行链路里 —— 模拟交易只是模拟;真钱交易必须走由操作者控制的券商链路并显式确认
  • 回测反映的是历史条件 —— 过去表现不预测未来结果。使用前请确认数据窗口、策略逻辑、以及生存偏差 / look-ahead 假设
  • 无监管咨询声明 —— 本平台是数据基础设施;下游任何投资决策由操作者本人(你)负责

🚀 快速接入 (Quick Start)

0) ⚡ 30 秒试一下(不需要注册 / 不需要 Token)

装完 skill 想立刻看看能干什么?打开 privora.cn/marketplace

  • 无需登录,直接浏览公开挂牌的 A 股 / 港股 / 黄金 / 基金 / 财报事件等数据资产
  • 每个资产可以点进去看 25 行样本数据 + 20 字段元信息(lastUpdated / 数据源 / cron 表达式等)
  • 看到有价值的资产?记下 numeric asset id → 回到终端跑下面的 §4 First Call Recipe(3 步 / 约 2 分钟)立刻验证 Bearer Token 对同一资产能跑通 —— 无需额外配置,安装 skill 时的环境变量对同一资产完全生效
  • 觉得样本还不够?往下走 §1 - §4 注册生成 Bearer Token 拿完整访问权(分页 / 过滤 / 更高 rate limit / 写操作 / Agent 集成)

为什么先看再注册:Privora 是投研工作流平台,"你的数据是否值得订阅"应该 30 秒能判断 —— 不需要注册墙。marketplace UI 是发现工具,Bearer Token 是同一批数据的程序化访问入口,两者对应关系明确。

1) 获取您的专属 Token

  1. 注册并登录 privora.cn
  2. 在侧边栏点击你的用户名 → API Token Management,或直接访问 https://privora.cn/profile/tokens
  3. 创建一个仅包含所需 scopes 的专用 Token(建议先用只读或低权限 Token)
  4. 复制您的专属 LG_AGENT_TOKEN

2) 为您的 Agent 配置环境变量

在您使用的 Agent 终端(如 Hermes、Claude Code、GitHub Copilot 或 OpenClaw)中注入以下环境变量:

export LG_AGENT_BASE_URL="https://privora.cn"
export LG_AGENT_TOKEN="***"

公开版主要走以上 Bearer Token 方式;如果 Agent 只是浏览 marketplace / 预览已发布看板 & 资产,也可以走匿名模式(不需要 token,见下方 §🌐 匿名预览)。session cookie / CSRF 兼容调用不支持。

3) 唤醒 Agent,开始对话

现在,您可以直接用自然语言向您的 Agent 下达指令了!

4) ⚠️ 做出你的第一次成功 API 调用(3 步走 / 避免最常见的 500)

最容易踩的坑:URL 里的 {id} 必须是数字型 asset ID(如 42),不是 asset 名字(如 fund_day / stock_day)。传成名字后端 Spring 转 Long 失败会返回 500——错误信息不会明确告诉你原因。

正确的 3 步 recipe

# Step 1: 先 list 拿数字 id ← 别跳过这步
curl -H "Authorization: Bearer $LG_AGENT_TOKEN" \
  https://privora.cn/api/data-assets | jq '.data[] | {id, assetName}'
# 输出示例:
# {"id": 42, "assetName": "fund_day"}
# {"id": 8,  "assetName": "stock_day"}
# {"id": 15, "assetName": "stock_dividend"}

# Step 2: 用数字 id (不是 assetName!) 查元数据
curl -H "Authorization: Bearer $LG_AGENT_TOKEN" \
  https://privora.cn/api/data-assets/42/metadata

# Step 3: 用数字 id 查实际数据
curl -H "Authorization: Bearer $LG_AGENT_TOKEN" \
  "https://privora.cn/api/data-assets/42/data?page=1&size=10"

Agent 侧调用同理dataasset.data.get / dataasset.metadata.getpathParams.id 必须是数字,先 dataasset.list 拿到再传。

如果你已经踩到 500:不用改代码逻辑,只需把 {name} 换成对应的数字 id 即可。数据资产的可用列表见下方「数据资产可用性」表 —— 那里的名字对应 dataasset.list 返回的 assetName 字段,需要先 list 拿到本 team 里对应的数字 id。


<a name="anonymous-preview"></a>

🌐 匿名预览(无 token)

如果 Agent 只是想浏览 marketplace 或预览已发布的看板/数据资产/流程——比如帮用户看看 Privora 有什么数据源、有哪些现成看板可订阅、某个流程做什么用——不需要 Bearer Token 也能直接跑。这条通路和 privora.cn/marketplace 页面上未登录访客看到的内容是同一套数据,只是把它变成 machine-readable 的 skill 调用。

什么时候用

  • 用户还没注册,Agent 想先展示"这平台上有啥"
  • 用户已注册但当前 session 没配 token,你想让 Agent 先给个 marketplace 摘要
  • Agent 在做 discovery / recommendation,不需要写权限、也不涉及用户私有数据

怎么用

留空 LG_AGENT_TOKEN 或直接不传 Authorization header 即可:

# 无 token 调用 —— 直接返回 mode:"anonymous" + 10 个可用 skill
curl https://privora.cn/agent/skills

# 无 token 拿 marketplace 列表
curl -X POST https://privora.cn/agent/skills/execute \
  -H "Content-Type: application/json" \
  -d '{"skillId":"marketplace.item.list"}'

# 无 token 拿某个已发布看板的 widget 数据
curl -X POST https://privora.cn/agent/skills/execute \
  -H "Content-Type: application/json" \
  -d '{"skillId":"dashboard.data.get","params":{"pathParams":{"id":"<published-dashboard-uuid>"}}}'

响应体的 mode 字段会明确标 "anonymous"grantedScopes 列出下方 10 个允许的 skill。

匿名模式可用的 skill(全部只读)

skillId用途
marketplace.item.list列出所有可订阅的 marketplace 条目(看板 / 资产 / 流程)—— discovery 入口
dashboard.get按 id 拿某个已发布看板的元数据 + widget 定义
dashboard.data.get一次拿某个已发布看板所有 widget 的数据
dataasset.get拿某个 allowSubscription=true 资产的详情
dataasset.schema.get拿该资产的列 schema
dataasset.metadata.get拿该资产的富元数据(lastUpdated / expectedUpdateCadence / cron / 数据源描述等 20 字段)
dataasset.data.get拿该资产的历史数据(预览有效范围内)
dataasset.data.getRealtime拿该资产的实时镜像数据(若配置了 realtime mirror)
process.ingestion.get拿某个 allowSubscription=true 流程的结构(不含 stepCfg 源码
process.component.list列平台可用的步骤组件类型(rendering diagram preview 用)

硬性约束

  • 只读白名单仅上表 10 个 skill 可调,其余 skill(包括其它只读 GET,如 investment.stock.portfolio.list / dashboard.list / dataasset.list)无论是否存在都返回 403 missing-scope。任何写操作(subscribe / create / update / delete)同样 403;哪怕手工构造匿名 PUT/POST 到底层 /api/**,Node 代理层也会先 401 拦截,不会到 Spring。

  • 每 IP 限流(三桶,任一超限即 429)

    范围限额429 响应体
    通用桶所有匿名 skill60 / IP / 分钟{"success":false, "message":"Too many anonymous agent requests, ..."} (无 bucket 字段)
    数据爆发桶dataasset.data.get + dataasset.data.getRealtime10 / IP / 分钟{"success":false, "traceId":"...", "message":"Anonymous data-fetch burst-limit exhausted (10/min per IP). ...", "bucket":"burst"}
    数据日总桶同上100 / IP / 天{"success":false, "traceId":"...", "message":"Anonymous data-fetch daily budget exhausted (100/day per IP). ...", "bucket":"daily"}

    收到 429 时读 response.body.bucket 判断:"burst" 等 60 秒重试;"daily" 今日不再放行该 skill,建议引导用户注册 Bearer Token;无 bucket 字段则是通用 60/min 桶命中。

    Rate-limit 存储: v1.0.37 起三桶都存 Redis sorted sets (anonratelimit:{burst,daily,general}:<ip>),通过 Lua 原子脚本实现 sliding window。fleet 内所有 Node worker + 所有 host 共享一份 counter,反爬承诺真的成立。Redis 不可用时 fail-open:静默放行 + ERROR 日志 event:"rate-limit-redis-fail",反爬承诺仅在 Redis 健康时有效(运维监控 fail-open 日志识别异常)。

  • preview token 服务端自动签:不用你手工去 /preview-token 拿;Node 按 skill 类型选:dashboard 类(dashboard.get / dashboard.data.get要求调用方在 params.pathParams.id 里传目标看板 id,Node 会用该 id 签 dashboardId-bound token;未传 id 或其它 skill 一律降级为 standalone sentinel(等同 dataasset 类行为)。

  • 无效 Bearer 不降级:如果传了 Authorization: Bearer <bogus>,返回 401 而不会悄悄退回匿名模式给你部分数据。要匿名就别传 header。

  • 无跨租户 leak:所有资产读都走 canReadAsset gate;只有 allowSubscription=true 的资产/看板/流程会被返回,其它一律 404。跟浏览器 /marketplace 未登录访客看到的是同一套子集。

  • Dashboard-scoped token 不能跨团枚举:dashboard-A(发布者 = team-A)签的 preview token 无法通过 dataasset.metadata.get 读到 team-B 的资产元信息,哪怕 team-B 资产 allowSubscription=true。仅 standalone sentinel token 可读所有公开挂牌资产。

匿名模式下的能力受限(订阅后才解锁)

数据获取类 skill(dataasset.data.get / dataasset.data.getRealtime / dataasset.get / dataasset.metadata.get / process.ingestion.get)在匿名模式下服务端会自动应用以下约束,参数会被静默改写或剥离——不是 400,你的 curl 依然能正常拿到响应,但拿到的不是你请求的形状:

  • 分页强制固定 page=1, size=25:传任何其他值都被服务端硬覆盖。响应 pageSize=25, currentPage=1。想拿更多请订阅后用 Bearer Token 调。
  • 过滤 / 排序参数被静默清空filterColumn / filterValue / filterOp / orderBy / orderDirection 一律置 null 再进服务层。想按条件过滤请订阅后再来。
  • 发布者身份字段被剥离dataasset.get):dataSource / realtimeDataSource / businessOwner / technicalOwner / teamName / jobCode / createdBy / createdDate / updatedBy / updatedDate 一律返回 null。Metadata map 中 teamName / dataSource / jobCode / createdBy / createdDate / sourceDescription 也被剥离。
  • process.ingestion.getstepCfg 被剥离:匿名调用者拿不到 process step 的源码 / SQL / Python 内容。
  • totalElements 是哨兵值 0,不是真实总数:匿名调用不消耗后端 COUNT(*) 查询(防止 JDBC 池 DoS)。分页导航请以 data.length 为准。

配合分页固定 + rate limit,匿名调用者理论上每 IP 每天最多拿到 2,500 行数据(100 次 × 25 行)——真的想跑分析请注册。

匿名模式下常见的误用

  • ❌ 不要基于匿名 preview 数据做投资决策 —— 25 行不是完整数据集,这只是"试读",不是"取样"。
  • ❌ 不要用多 IP 池绕 rate limit —— 我们记录并封 IP 池行为,正确路径是注册 Bearer Token。
  • ❌ 不要期望 totalElements 反映真实行数 —— 匿名调用永远返回 0,这是设计意图,不是 bug。
  • ❌ 不要在匿名模式下尝试 filterColumn=... —— 参数会被静默丢弃,返回的是无过滤的前 25 行,不是过滤后的结果。

技术契约锚点(review 用):匿名 skill 白名单 = Node app.jsANONYMOUS_SKILL_SCOPES 常量;匿名 rate limit = canAnonymousAgentCall (60/min) + canAnonymousDataFetchCall (10/min burst + 100/day daily);preview + HMAC 验证 = docs/auth-flow-invariants.md §1 + §2.6 + §2.6.1;能力锁定实现与残留 test 缺口 = docs/plans/2026-07-07-anon-preview-dataasset-lockdown.md

从匿名 → 注册的漏斗

匿名浏览完,如果用户想真正订阅一个看板 / 用私有数据 / 跑回测,需要注册并领 token

  • 引导用户去 https://privora.cn/registermarketplace.item.subscribe 是 🟡 写操作,不在匿名 scope 里)
  • 或直接调 auth.user.register skill(也在匿名 scope 之外 —— 需要一层 signup 意图确认,见 §用户注册 & 反馈)

💬 典型应用场景

场景 1:查询账户今日盈亏(个人数据,仅自己可见)

您: “帮我查下今天的账户盈亏情况。”

Agent(调用 dataasset.data.get): “为您同步 privora.cn 的最新分析结果: 💰 当日盈亏: +319 元 | 累计浮动: -19,135 元 📊 持仓明细:

  • 中国核电:+2.06%
  • 永和股份:-32.45%
  • 中国联通:-16.25%”

场景 2:设定云端智能监控

您: “帮我监控贵州茅台,只要突破MA20均线就通知我。”

Agent(调用监控接口): “✅ 已在云端成功创建监控任务:

  • 标的:贵州茅台 (SH600519)
  • 条件:价格突破 MA20
  • 通知:飞书/微信推送 任务将在 Serverless 云端静默运行,触发时您将立刻收到推送。

场景 3:测试流程并抓取执行日志

# 触发执行(异步),记下返回的 executionId
# 自定义参数放在 body 里:key=参数名(以 - / -- 开头),value=参数值
# 后端会自动注入 `-f <procName>` —— body 里不用传 -f(传了也会被忽略)
RESP=$(scripts/lg_agent_exec.sh '{
  "skillId": "process.ingestion.execute",
  "params": {
    "pathParams": {"id": "123"},
    "body": {
      "-start_date": "20260419",
      "-end_date":   "20260420",
      "--env":       "dev"
    }
  }
}')
EXEC_ID=$(echo "$RESP" | jq -r '.executionId')

# 轮询日志,直到 completed=true
OFFSET=0
while :; do
  LOG=$(scripts/lg_agent_exec.sh "{
    \"skillId\": \"process.ingestion.execute.log.get\",
    \"params\": {
      \"pathParams\": {\"id\": \"123\", \"executionId\": \"$EXEC_ID\"},
      \"query\": {\"offset\": \"$OFFSET\"}
    }
  }")
  echo "$LOG" | jq -r '.logLines[]'
  [ "$(echo "$LOG" | jq -r '.completed')" = "true" ] && break
  OFFSET=$(echo "$LOG" | jq -r '.nextOffset')
  sleep 1
done
echo "exitCode=$(echo "$LOG" | jq -r '.exitCode')"

返回:statusrunning 过渡到 completedfailedexitCode 为脚本退出码,logLines 为增量日志行。

场景 4:策略回测(双均线跑茅台)

您: “用双均线(5日/20日)对茅台 SH600519 过去三年跑个回测”

在平台新建一个 python_script 流程节点,脚本如下(lg_utils 已预装):

💡 stock_day 回测用现成的 run_stock_day_backtest 就好——它已经把列名大小写(STOCK_NUM / OPEN_PRICE / CLOSE_PRICE)和日期格式(day_idYYYYMMDD)配好了,别再手动传 price_columns={“open”:”open_price”,...} 或 ISO 日期,那些是 2026-04-21 踩过的坑。

from lg_utils import get_variable
from lg_utils.backtest_examples.dual_ma import DualMA
from lg_utils.backtest_examples.stock_day import run_stock_day_backtest

result = run_stock_day_backtest(
    strategy=DualMA(fast=5, slow=20),
    stock_num=”600519”,
    start=”20220101”,
    end=”20241231”,
    initial_cash=1_000_000,
    commission_bps=3, slippage_bps=1,
    benchmark_asset=”stock_day”,            # 可选:跟某只指数/股票对比
    benchmark_filter_column=”STOCK_NUM”,
    benchmark_filter_value=”000001”,
)
print(result.summary())
result.export_to_context(“maotai_ma520”)   # stdout 日志快照
result.persist(name=”maotai_ma520”)         # 持久化到 process_backtest_result 表

组合回测(共享现金池、多标的同时跑):

from lg_utils.backtest_examples.stock_day import run_stock_day_portfolio_backtest
from lg_utils.backtest_examples.dual_ma import DualMA

result = run_stock_day_portfolio_backtest(
    strategies={“600519”: DualMA(5, 20), “000001”: DualMA(10, 30)},
    stock_nums=[“600519”, “000001”],   # 决定 size='all' 结算先后
    start=”20240101”, end=”20241231”,
    initial_cash=1_000_000,
)
# result.metrics[“per_asset”] 给出每只股票的贡献度/回撤/交易数

任务日志里会出现:

=== Backtest Summary ===
asset           : stock_day
period          : 20220101 ~ 20241231  (bars=725)
total_return    : 23.1500%
sharpe          : 0.8412
max_drawdown    : 18.2300%
num_trades      : 14
win_rate        : 57.1429%
__LG_BACKTEST_RESULT__:maotai_ma520:{"metrics":...,"trades":...}

完整 JSON(含 trades / equity_curve)会被下游节点或监控面板消费。

场景 5:一键 subscribe→alert deeplink (NEW v1.0.13)

您: "帮我配个告警,招商银行股价跌破 30 通知我。"

Agent 调用流程(之前 6 步深埋,2026-06-05 起 1 步):

# 1) Agent 帮用户订阅相关 dashboard
RESP=$(scripts/lg_agent_exec.sh '{
  "skillId": "marketplace.item.subscribe",
  "params": { "pathParams": { "itemId": "dashboard-china-merchants-bank-watch" } }
}')

# 2) 从 response 拿到本租户的 cloned dashboard ID
DASH_ID=$(echo "$RESP" | jq -r '.clonedDashboardId')

# 3) 构造 1-click deeplink — Agent 把这个 URL 给用户
DEEPLINK="https://privora.cn/dashboards?selectId=${DASH_ID}&openAlerts=true"
echo "请打开此链接配置告警:${DEEPLINK}"

用户点链接进去,metric alert modal 自动打开——已经对准刚订阅的 dashboard,剩下用户填阈值 + 选 webhook 渠道 finalize 就完。user-in-the-loop 边界保留(敏感操作仍需用户在 web 上确认),但 5 步导航 + 选 dashboard + 翻 toolbar 找 "Alerts" button 这些都省了。

这是平台活跃用户反馈最集中的需求——以前的路径是:订阅 → 跳到 dashboard 列表 → 找到目标 dashboard → 打开 toolbar → 找 "Alerts" button → (第一次还要去 /datasources 配 webhook,回来再继续)→ 配置 → 保存。这次更新把这条路径压到 1 步

场景 6:模拟交易(paper trading happy path)

您: "用模拟账户跑一笔 600519 的市价买单 100 股,看看现在能不能成交。"

Agent 调用:

# 1) Agent 确认 / 创建模拟账户(账户 UNIQUE on user_name,重复调用幂等)
ACCT=$(scripts/lg_agent_exec.sh '{
  "skillId": "paper.account.create",
  "params": {"body": {"initialCashCny": 1000000}}
}')

# 2) 提交 MARKET 买单(client_order_id 是幂等键 — Agent 自己生成 UUID)
ORDER_ID="$(uuidgen)"
RESP=$(scripts/lg_agent_exec.sh "{
  \"skillId\": \"paper.order.place\",
  \"params\": {\"body\": {
    \"clientOrderId\": \"$ORDER_ID\",
    \"symbol\": \"600519\",
    \"side\": \"BUY\",
    \"orderType\": \"MARKET\",
    \"quantity\": 100
  }}
}")

# 3) 查订单状态(同 clientOrderId 重试拿同一订单,不会重复下单)
scripts/lg_agent_exec.sh "{
  \"skillId\": \"paper.order.get\",
  \"params\": {\"pathParams\": {\"clientOrderId\": \"$ORDER_ID\"}}
}"

支持涨跌停 / 停牌 / suspended-stocks 信号、scheduler-driven 撮合。Bearer Token 必须挂 paper.* scope。

适合的 use case:策略上真实交易前 12 个月 paper trade 验证(per 6 阶段量化研究流水线最终关卡)。

技能列表

REST 技能(scripts/lg_agent_exec.sh 调用)

公开版 skill 覆盖 4 类操作,见 §🛡️ Scope & Operator Responsibility 完整的 read / idempotent-write / workflow-transition / outbound-webhook 分类。删除、撤销、系统级审批等破坏性/管理类操作不在本 skill 范围内,需通过 platform UI 或 admin 工具完成。 风险标记:🟢 low / 🟡 medium / 🔴 high。所有 GET 技能默认对会话用户开放;写操作需显式授予 scope。

📦 Request shape: canonical envelope —— 推荐 params 字段下嵌套 pathParams / query / body向后兼容(自 2026-06-29 起):顶层 pathParams / query / body 会被网关自动折叠进 params(仅当 params.<field> 缺失时;非对象值跳过)。嵌套和顶层字段同时出现时,嵌套字段优先生效。所有调用都应优先用 envelope 形式:

{"skillId": "...", "params": {"pathParams": {...}, "query": {...}, "body": {...}}}

历史踩坑:2026-05-07 一次 backfill 因为漏写 params: 包裹(且当时 body 字段没有 fallback),-target_day_id 20260506 没到 broker,python_script 拿到 target_day_id=None 跑了一轮空 SELECT。2026-06-29 把 query/body 的 fallback 加齐;同期发现的 dataasset.data.get filter 被静默丢弃就是同源问题(用旧顶层 shape 时 filter 没注入 URL,看着 SUCCESS 但实际未过滤)。

Update 2026-05-21: The gateway now supports per-skill paramAliases for snake_case ↔ camelCase query-param translation. dataasset.data.get was the first skill to opt in (filter_column / filter_value / filter_op accepted). If a future skill exposes the same footgun, add a paramAliases entry rather than relying on agents to remember the casing rule. LE incident 2026-05-21 evening (fund_day filter on 007722 silently returning unfiltered data) was the forcing function.

流程 (Process / Ingestion)

skillIdmethod功能风险
process.ingestion.listGET列出所有流程🟢
process.ingestion.getGET根据 id 获取流程详情🟢
process.ingestion.executePOST异步触发流程执行(返回 executionId)。body 接收自定义 CLI 参数,如 {"-start_date":"20260419","--env":"dev"}。后端自动注入 -f <procName>,不要自己传 -f🟡
process.ingestion.execute.log.getGETexecutionId 拉取日志+状态,支持 offset 增量轮询。记录持久化在 process_execution 表 + 磁盘文件,重启不丢。🟢
process.component.listGET列出当前团队可用的步骤组件(含 Markdown 使用说明)🟢
process.pipeline.buildPOST一次性创建完整 pipeline(节点+组件+边)。python_script 节点的 stepCfg 必须使用执行器字段名 "script"(不是 "pythonScript",例:{"script":"import sys\nprint(sys.version)","requirements":"pandas"}process.component.list 返回的 form-schema 中的显示名 pythonScript 是 UI 表单专用名,不等于执行器运行时 JSON key——混用会导致执行时报 "Python script is empty or NULL"(2026-07-10 incident,已在后端加翻译层向前兼容,但规范写法仍推荐用 script)。🟡
process.pipeline.updatePUT全量更新已有 pipelinePUT /api/ingestions/{id},同形 BuildPipelineRequest)。nodes 省略=仅改名/描述,保留现有步骤;nodes=[] 显式清空;nodes=[...] 全量替换。每次 PUT 自动写一条 dacp_meta_proc_version,可 /versions/{n}/restore 回滚。legacy team_name IS NULL 的流程会直接 403,需先 backfill。想只改一个步骤的 label/conf/remark 而保留 DAG?用 process.pipeline.update_node PATCH,避免 aftId 等拓扑字段被默认值 "-1" 误清空。想只改流程名称/描述?用 process.pipeline.patch_meta🟡
process.pipeline.update_nodePATCH部分更新单个步骤(PATCH /api/processes/{procId}/steps/{stepId})。字段掩码语义:仅 stepLabel / stepConf / remark 三个安全字段可被更新;缺失字段、显式 null以及空字符串 "" 都视为"跳过"(清空)。严格拒绝aftId / sStep / nStep / fStep(DAG 拓扑)出现在 body 即返回 HTTP 200 success:false, code:"TOPOLOGY_FIELD_REJECTED" —— 要改变 DAG 拓扑请用 PUT process.pipeline.update 全量替换所有节点。stepName / stepSeq / parentId 静默丢弃。要清空 stepLabel/stepConf/remark 字段也必须走 PUT 全量替换 —— PATCH 设计为"只增改、不清空"。此 skill 需要 process.pipeline.update_node scope(独立于 process.pipeline.update),现有 token 须重新签发方可使用。🟢
process.pipeline.patch_metaPATCH部分更新流程级别元数据(PATCH /api/ingestions/{id}/meta)。字段掩码语义:仅 procLabel / procDescr 两个安全字段可被更新;缺失字段、显式 null以及空字符串 "" 都视为"跳过"(清空)。严格拒绝nodes(拓扑变更)/ procName(标识符)/ creater(归属)/ teamName 出现在 body 即返回 HTTP 200 success:false, code:"FIELD_NOT_PATCHABLE" —— 要改名称/DAG 结构请用 PUT process.pipeline.update此 skill 需要 process.pipeline.patch_meta scope(独立于 process.pipeline.update),现有 token 须重新签发方可使用。🟢

调度 (Schedule)

skillIdmethod功能风险
schedule.job.listGET列出调度作业🟢
schedule.job.getGET获取调度作业详情🟢
schedule.workgroup.listGET发现 当前平台注册的 workgroup / namespace(从已注册 broker 聚合),是 schedule.job.create 两个必填字段的唯一合法来源🟢
schedule.scripts.getGET发现 平台配置的 jobScript 默认模板({dp, sh, py}),给 schedule.job.createjobScript 字段用🟢
schedule.job.createPOST创建调度作业(POST /api/schedule/jobs)。新建后 state="0",上线操作需通过平台 UI 执行。🟡
schedule.job.updatePUT全量替换作业配置(PUT /api/schedule/job/{jobId})。全部字段都会被覆盖——缺失字段会被写成 null,可能清空 cronExp 等关键字段。推荐用 schedule.job.patch 做部分更新;PUT 只在需要显式清空某字段时使用。state 字段静默丢弃。🟡
schedule.job.patchPATCH部分更新作业配置(PATCH /api/schedule/job/{jobId})。字段掩码语义:只有非 null 字段会被覆盖到已存在的行,缺失字段和显式 null 都视为"跳过"。要清空字段请用 PUT schedule.job.updatestate / teamName 同样静默丢弃(与 PUT 一致)。此 skill 需要 schedule.job.patch scope(独立于 schedule.job.update),现有 token 须重新签发方可使用。🟢
schedule.job.depends.listGET列出作业依赖(按 jobCode)🟢
schedule.job.depends.savePOST全量替换作业依赖列表(旧的先删再写)🟡
schedule.job.plugins.listGET列出作业绑定的插件(按 jobCode)🟢
schedule.job.plugins.savePOST全量替换作业插件列表(旧的先删再写)🟡
schedule.instance.listGET列出作业实例(一次运行=一条 trigger 行);ops 操作所需的 jobTriggerId 都从这里拿🟢
schedule.instance.status.getGET(jobCode, batchNo) 查单条最新状态,用于轮询🟢
schedule.instance.log.getGETjobTriggerId 拉取执行日志🟢
schedule.instance.redoPOST重跑失败/已完成实例(保留依赖链语义)🟡
schedule.instance.holdPOST暂停运行中的实例(不杀进程,可恢复)🟡
schedule.instance.resumePOST恢复之前 hold 住的实例🟡
schedule.instance.reset_priorityPOST调等待队列里实例的优先级(priority 1-9,越小越先跑)🟡
schedule.job.lineageGET作业的上下游依赖图(includeAssets=true 时附带每个节点的输出资产)🟢
schedule.job.by_processGET用 process 名反查 jobCode(拿到后才能调 ops skill)🟢
schedule.broker.listGET列当前注册的 broker(排"无人认领 workgroup"类问题时用)🟢
schedule.broker.latencyGETBroker 队列长度 + 消费速率 + 推算的等待延迟(诊断"上线但跑得慢"类问题)🟢
schedule.job.plugin.webhook.triggerPOST手动触发作业绑定的 webhook 插件🟡

调度作业字段契约

外部 agent 在调 schedule.job.create 之前,先走一遍"发现"(这几个字段没有硬编码枚举,值取决于当前部署):

  1. schedule.workgroup.list → 拿到 {workgroups, namespaces},从中各选一个赋给 workgroup / namespace传一个没人认领的 workgroup 不会报错,但没 broker 会去跑——这是最典型的"创建完成但永远不执行"陷阱。
  2. schedule.scripts.get → 拿到 {dp, sh, py},按 jobType 选对应字段赋给 jobScriptdp 作业用 dppython 作业用 pyshell 作业用 sh;空字符串表示该类型没有在这套部署上配好)。
  3. 如需参考现有同类 job:schedule.job.list + schedule.job.get 挑一个已上线的作业 clone 一份。

schedule.job.create / schedule.job.update 的 body(DataflowJob 形):

字段必填说明
jobCode后端强制团队内唯一业务编码。已存在时 create 幂等返回旧 jobId。
jobLabelUI 强制展示名
jobTypeUI 强制枚举:dp / datastash / python / shell
workgroupUI 强制集群组名。合法值来自 schedule.workgroup.list,不要自己编
namespaceUI 强制命名空间。合法值来自 schedule.workgroup.list
jobScriptUI 强制执行命令行。默认模板来自 schedule.scripts.get(按 jobType 取对应字段)
batchTypeUI 强制枚举:monthly / daily / hourly / minutely / once / daemon
cronExp条件Quartz 6 段式(秒起头),如 0 5 15 * * ?
jobParam条件JSON 字符串 数组"[{\"paramName\":\"-f\",\"paramVal\":\"my_proc\"}, ...]"jobType=dp 时后端按 paramName="-f" 自动回写 procName
procName可选dp 作业通常交给后端从 jobParam 反推;其他 type 可显式传
runConstraint可选"1"=顺序执行(默认),"2"=并发执行
batchNo / batchOffset / batchStep可选批次计算相关
jobPriority可选1–9,数字越小越高(默认 5)
redoNum可选失败重试次数
lastdtOffset可选最晚启动偏移(秒),0 为不宽限
maxElapsed可选最长运行时间(秒)
jobExtCfg可选≤1024 字符的扩展配置 JSON
tag可选自由标签
jobDescr可选描述
stateupdate 时静默丢弃,上下线状态变更需通过平台 UI 操作
服务端自动填充jobId(UUID)、state="0"version=1teamName / memberName / createUser(取自会话)

schedule.job.depends.save 的 body(JSON 数组,全量替换):

[
  { "dependCode": "upstream_job_code", "dependType": "10", "isDefault": "1" },
  { "dependCode": "20260424",          "dependType": "20",
    "batchCalExp": "${batchNo?calDate(-1,'d','yyyyMMdd')}" }
]
  • dependType="10" — 任务依赖,dependCode另一个 jobCode(同团队内可见)
  • dependType="20" — 时间/批次依赖,dependCode 是时间字符串,batchCalExp 是批次偏移表达式(${batchNo?calDate(...)}
  • 其他字段:procNameoutputisDefault"1" 标默认)都可选
  • dependId 服务端生成(UUID16),不用自己传

schedule.job.plugins.save 的 body(JSON 数组,全量替换):

[
  {
    "pluginCode": "webhook",
    "state": "1",
    "pluginCfg": "{\"webhookDsName\":\"feishu_ds\",\"dataSourceName\":\"feishu_ds\",\"triggerStates\":[\"1\",\"-2\"]}",
    "isBlock": "0",
    "isDefault": "1"
  }
]
  • pluginCode + pluginCfg(JSON 字符串)为必填
  • state 为要监听的任务状态:"1" 成功 / "-2" 失败 / "2" 结束 / "0" 启动 / "-1" 中止(dacp_dataflow_job_trigger.state 的子集)
  • webhook 插件pluginCfg必须webhookDsName,否则返回 {"success":false, "message":"Webhook plugin requires pluginCfg.webhookDsName"}
  • jobPluginId 服务端生成

典型的"从零到调度可跑"流程(外部 agent 视角):

  1. schedule.workgroup.list + schedule.scripts.get → 发现合法的 workgroup / namespace / jobScript
  2. schedule.job.create → 拿到 jobId
  3. schedule.job.depends.save(至少一条依赖,否则上线后不会产生 instance)
  4. (可选)schedule.job.plugins.save → 绑 webhook 等插件
  5. 通过平台 UI 上线作业(state: 0 → 1)→ 让 broker 把它纳入触发域

作业运维决策手册

Ops 流程几乎总是先 schedule.instance.list(或 schedule.job.by_processschedule.instance.list)拿到目标 jobTriggerId,再按下面这张表选动作:

场景推荐 skill备注
失败了想重跑一次schedule.instance.redo保留依赖链;默认 opType="3",带依赖重跑
运行中但想先停住等数据就绪schedule.instance.hold不杀进程,可 schedule.instance.resume 恢复
等待太久想插队schedule.instance.reset_priority只对"在队列等待"的实例有效
查上下游会被哪些 job 影响schedule.job.lineage在平台 UI 操作前先看一下上下游影响
已知 process 名找对应 jobCodeschedule.job.by_process常用于从 Process 页面反向调 ops
排查"作业没有 instance"schedule.broker.list → 看 workgroup 有没有 broker;schedule.job.lineage → 看 depend 是否还卡着第二常见的"不跑"陷阱
排查"在跑但很慢 / 积压"schedule.broker.latencystalled / 队列长度;若是 broker 瓶颈就不是 job 的问题
想看这次跑得怎么样schedule.instance.status.get(单点)或 schedule.instance.log.get(看日志)轮询建议用 status.get,日志用 log.get

数据源 & 数据资产 (Datasource / Data Asset)

skillIdmethod功能风险
datasource.listGET列出数据源🟢
datasource.getGET获取数据源详情🟢
datasource.list.activeGET列出活跃数据源🟢
datasource.connection.testPOST测试数据源连接🟡
datasource.updatePUT全量替换数据源(含连接配置 dsConf)。想只改描述或显示标签?用 datasource.patch,避免 dsConf/dsAuth 被覆盖。🟡
datasource.patchPATCH部分更新数据源元数据(PATCH /api/datasources/{dsId})。字段掩码语义:仅 dsDescr(描述)/ dsLabel(显示标签)两个安全字段可被更新;缺失字段、显式 null以及空字符串 "" 都视为"跳过"(清空)。严格拒绝dsConf(更改连接目标会破坏运行中的 ETL)/ dsType(标识符)/ dsAuth / dsId / dsName / teamName 出现在 body 即返回 HTTP 200 success:false, code:"FIELD_NOT_PATCHABLE"此 skill 需要 datasource.patch scope(独立于 datasource.update),现有 token 须重新签发方可使用。🟢
dataasset.listGET列出数据资产🟢
dataasset.getGET获取资产详情🟢
dataasset.schema.getGET获取资产 schema(列名/类型);支持 ?refresh=true 绕过缓存实时重新采集🟢
dataasset.metadata.getGET获取资产富元数据:20 个字段,包含 lastUpdated(最近刷新时间)、expectedUpdateCadence(调度批次类型,来自关联 Job)、cronExpression(cron 表达式)、sourceDescription(数据源描述)。Bug #50 后续,用于程序化判断资产新鲜度。🟢
dataasset.data.getGET查询资产数据(盈亏、行情等,全量历史);支持 filter_op 过滤运算符(见下方详情)。MC 数据源资产速度较慢(2-5s)但包含完整历史🟢
dataasset.data.getRealtimeGET查询资产的 实时镜像数据源(PG 热窗口,最近 365 天);低延迟,适合仪表盘 / 实时 P&L。资产未配置 realtimeDataSource 时返回 {success:false, message:"This asset has no realtime mirror"},不抛异常。路径:GET /api/data-assets/{id}/data-realtime🟢
dataasset.history.field-as-ofGET点状时间(PIT)股票状态字段查询:is_st(ST/暂停挂牌)、market_board(板块)、delisted(退市)、industry(申万行业分类)。返回指定股票在某日期上的历史状态快照。依赖 PR-B ETL 完成首次回填后方可返回实际数据;回填未完成前返回 data:null。详见下方「dataasset.history.field-as-of」节。🟢
dataasset.updatePUT全量替换数据资产元数据(PUT /api/data-assets/{id})。想只改 description/tags/allowSubscription?用 dataasset.patch PATCH,避免误改 sensitivityLevel 等不可改字段。🟢
dataasset.patchPATCH部分更新数据资产元数据(PATCH /api/data-assets/{id})。字段掩码语义:仅 description / tags / allowSubscription 三个安全字段可被更新;缺失字段、显式 null以及空字符串 "" 都视为"跳过"。严格拒绝sensitivityLevel(含别名 sensitivity_level / securityLevel / level)— 敏感级是 MONOTONIC,调整必须走专用 sensitivity-change 路径;assetName / assetType / tableName / teamName 等标识符也拒绝。allowSubscription=true 在 INTERNAL 资产上要求 tags 包含 permission_field:<col>,否则返回 code:"PERMISSION_FIELD_REQUIRED"(INLINE 复刻 PUT 路径的 publish-validation guard)。此 skill 需要 dataasset.patch scope(独立于 dataasset.update),现有 token 须重新签发方可使用。🟢

数据资产 API 详情

dataasset.schema.get?refresh 参数

路径:GET /api/data-assets/{id}/schema

参数必填默认说明
refreshfalsefalse:返回注册时缓存的 schemaInfo JSON(快速,无 I/O)。true:绕过缓存,对底层表做实时列采集,结果持久化回 schemaInfo 并写 last_schema_sync_at

refresh=true 时响应的 meta 字段会包含 lastSchemaSyncAt(ISO-8601 UTC 时间戳),标示本次同步完成时间。

示例(refresh=true):

{
  "success": true,
  "data": {
    "columns": [
      {"name": "stock_num", "type": "varchar"},
      {"name": "day_id",    "type": "int4"},
      {"name": "close_price","type": "numeric"},
      "..."
    ]
  },
  "message": "success",
  "meta": {
    "lastSchemaSyncAt": "2026-06-17T03:12:45Z"
  }
}

业务错误(底层数据源不可达 / 序列化失败 / 保存失败):返回 HTTP 200,{"success": false, "message": "Schema refresh failed: <原因>"} —— 符合平台 {success, data, message, meta?} 合约,不抛 HTTP 500。


dataasset.metadata.get — 资产富元数据

路径:GET /api/data-assets/{id}/metadata

参数必填说明
id数据资产 PK(Long)

无分页,无请求体。返回 data 对象包含 20 个字段:

字段类型来源备注
assetIdLong直接资产 PK
assetNameString直接资产名称
assetTypeString直接"table"
assetCategoryString直接"market-data"
descriptionString直接可为 null
tagsString直接CSV blob,调用方自行拆分
sensitivityLevelString直接PUBLIC / INTERNAL / CONFIDENTIAL / RESTRICTED
allowSubscriptionBoolean直接是否可订阅
teamNameString直接所属团队
createdDateLocalDateTime直接创建时间
createdByString直接创建人
recordCountLong直接行数(去规范化,可能滞后)
sizeBytesLong直接字节数(去规范化)
lastUpdatedLocalDateTime直接最近数据刷新时间(与 dataasset.list 命名一致)
lastSchemaSyncAtOffsetDateTime直接最近 schema 同步时间(UTC)
jobCodeString直接关联 Job 代码(标量,可为 null)
expectedUpdateCadenceString派生来自 DataflowJob.batchType;多 Job 时取第一个有 cronExp 的 Job;无匹配 → null
cronExpressionString派生来自 DataflowJob.cronExp;同上选取逻辑
dataSourceString直接数据源名称(文本)
sourceDescriptionString派生来自 DataSource.dsDescr;team-scoped join,订阅方通常为 null

响应示例(成功):

{
  "success": true,
  "data": {
    "assetId": 46,
    "assetName": "stock_day",
    "assetType": "table",
    "assetCategory": "market-data",
    "description": "A-share daily OHLCV bars",
    "tags": "PUBLIC,equity,daily",
    "sensitivityLevel": "PUBLIC",
    "allowSubscription": true,
    "teamName": "tenant_lg_data",
    "createdDate": "2025-12-01T08:00:00",
    "createdBy": "admin",
    "recordCount": 5234567,
    "sizeBytes": 8923847234,
    "lastUpdated": "2026-06-17T15:42:18",
    "lastSchemaSyncAt": "2026-06-16T03:00:00Z",
    "jobCode": "stock_day_sync",
    "expectedUpdateCadence": "daily",
    "cronExpression": "0 0 18 * * ?",
    "dataSource": "lg_data_pro_pg",
    "sourceDescription": "Primary LG Postgres lakehouse (read-only mirror)"
  },
  "message": "OK"
}

业务错误(资产不存在 / 跨团队访问):返回 HTTP 200,{"success": false, "code": "ASSET_NOT_FOUND", "message": "<原因>"} —— 符合平台 {success, data, message} 合约,不抛 HTTP 4xx。


dataasset.data.getfilter_op 支持的运算符集合

路径:GET /api/data-assets/{id}/data

Gateway 支持 snake_case 别名(filter_column / filter_value / filter_op / order_by / order_direction)和 camelCase 两种写法,两者等价。

filter_op / filterOp 完整合法值:

运算符SQL 语义示例
eq= 'val'filterOp=eq&filterValue=600519
ne<> 'val'filterOp=ne&filterValue=600519
gt> 'val'filterOp=gt&filterValue=10.5
gte>= 'val'filterOp=gte&filterValue=10.5
lt< 'val'filterOp=lt&filterValue=10.5
lte<= 'val'filterOp=lte&filterValue=10.5
likeLIKE '%val%'(平台自动加两侧 %,与 contains 等价)filterOp=like&filterValue=浦发
containsLIKE '%val%'(平台自动加两侧 %,与 like 等价)filterOp=contains&filterValue=浦发
inIN ('a','b','c')filterOp=in&filterValue=a,b,c
not_inNOT IN ('a','b','c')filterOp=not_in&filterValue=a,b,c
  • 多值运算符in / not_in):filter_value 用英文逗号分隔,如 filter_value=600519,000001,601398 → 生成 IN ('600519','000001','601398')
  • likecontains 行为相同:平台都生成 LIKE '%val%' 子串匹配。如需自定义 LIKE 模式(前缀 / 后缀 / 通配符位置),目前需在调用方 SQL 端处理或走 POST /api/data-assets/{id}/query
  • 未知运算符拒绝(不静默降为 eq):传入不在上表中的值(如 filterOp=between)→ HTTP 200 {"success": false, "message": "Validation Failed: Unsupported filter operator: between"}
  • order_direction(snake_case;camelCase 写法为 orderDirection)接受 asc / desc(大小写不敏感),默认 asc

dataasset.data.get vs dataasset.data.getRealtime — 路由选择指南

用哪个?

  • dataasset.data.get / lg.get_asset_data — 分析型 / 历史全量 / 完整数据。底层存储是资产的主数据源(Step 4 切换后为 MC;速度较慢 ~2-5s,但包含完整历史和全列)。
  • dataasset.data.getRealtime / lg.get_asset_data_realtime — 实时型 / 低延迟 / 热窗口(最近 365 天)。底层存储是资产的 realtimeDataSource(PG 镜像)。对于未配置 realtimeDataSource 的资产返回 {success:false, message:"This asset has no realtime mirror"} — 这是正常业务返回,不是异常,调用方应处理而非视为错误。

两个函数接受完全相同的参数(asset_identifier, page, size, order_by, filter_column, filter_value, filter_operator),返回 shape 相同,差异仅在于:

  1. 路由目标不同(/data vs /data-realtime)。
  2. get_asset_data_realtime 永不抛出异常;所有错误(无镜像、类型不符、连接失败、认证失败)均以 {success:false, message:...} 返回,调用方 必须 检查 result["success"]

Prerequisite: .getRealtime requires the asset to have realtimeDataSource populated (via PUT /api/data-assets/{id}, an admin API). If not configured, .getRealtime returns {success:false, message:"This asset has no realtime mirror"}. Currently stock_day and fund_day are the only assets with realtime mirrors configured; other assets will return the "no mirror" response until an admin populates their realtimeDataSource.

Note for existing bearer token holders: if you receive 403 Forbidden: missing required scope 'dataasset.data.getRealtime' when calling this skill, your token was minted before this skill was added. Go to lg-data.cc → Token Management, edit your token, and add the dataasset.data.getRealtime scope. New tokens minted after 2026-07-02 automatically include all currently-registered scopes.


dataasset.history.field-as-of — 股票 PIT 状态字段查询 (#13 PR-A/C)

路径:GET /api/data-assets/history/field-as-of

Auth: 标准 Spring Security session 或 Bearer token(lgatk_…)。GET 请求无需 CSRF token。Bearer token 需要 scope dataasset.history.field-as-of(PR-A 新增;现有 token 须重新签发方可使用)。

依赖: 底层表(stock_st_history / stock_board_history / stock_delist_history / stock_industry_history)由 PR-B ETL 进程回填。ETL 未运行前所有查询均返回 data:null,不会报错。

参数类型必填说明
fieldString字段名,固定四选一:is_st / market_board / delisted / industry
stockNumString股票代码,如 600519.SH / 00700.HK
asOfDateString查询日期,ISO-8601 格式 YYYY-MM-DD
swVintageStringfield=industry 时有效:SW2014SW2021。缺省时自动按 asOfDate 选择(asOfDate < 2021-10-01 → SW2014;否则 SW2021)

field 语义说明:

field返回类型说明
is_stBooleantrue = ST/特别处理/暂停挂牌;false = 正常;null = 无历史记录(ETL 未回填或日期早于数据)
market_boardString主板 / 创业板 / 科创板 / 北交所 / 中小板(v1 每日刷新,非事件驱动,精度至天)
delistedBooleantrue = delist_date <= asOfDatefalse = 未退市或无记录(默认 False,不返回 null)
industryObject申万行业分类对象(见下方结构),null = 无记录

industry 值对象结构:

{
  "l1_code":    "110000",
  "l1_name":    "食品饮料",
  "l2_code":    "110201",
  "l2_name":    "白酒",
  "sw_vintage": "SW2014"
}

响应示例(field=is_st,股票在 ST 状态):

{
  "success": true,
  "data": {
    "field":        "is_st",
    "stockNum":     "000666.SZ",
    "asOfDate":     "2022-06-01",
    "value":        true,
    "validFrom":    "2021-03-15",
    "validTo":      null,
    "source":       "tushare-namechange",
    "changeReason": "特别处理"
  },
  "message": "OK"
}

响应示例(field=industry,自动选择 SW2014):

{
  "success": true,
  "data": {
    "field":    "industry",
    "stockNum": "600519.SH",
    "asOfDate": "2020-06-15",
    "value": {
      "l1_code":    "110000",
      "l1_name":    "食品饮料",
      "l2_code":    "110201",
      "l2_name":    "白酒",
      "sw_vintage": "SW2014"
    },
    "validFrom": "2014-01-01",
    "validTo":   null,
    "source":    "tushare-index-member"
  },
  "message": "OK"
}

无历史记录(ETL 未回填 / 日期早于数据):

{
  "success": true,
  "data": null,
  "message": "No history record found for stockNum=600519.SH, field=is_st, asOfDate=2020-06-15. ETL may not yet have populated this field."
}

业务错误(非法 field / 非法日期格式):

{
  "success": false,
  "message": "Unsupported history field: 'foo'. Supported: [is_st, market_board, delisted, industry]"
}

Python 快捷用法(via lg_utils.history):

from datetime import date
from lg_utils.history import is_st_at, market_board_at, is_delisted_at, industry_at, load_st_history

# 单日点查
st      = is_st_at("000666.SZ", date(2022, 6, 1))           # True / False / None
board   = market_board_at("688001.SH", date(2022, 6, 1))    # "科创板" / None
delist  = is_delisted_at("000001.SZ", date(2023, 1, 1))     # True / False (never None)
ind     = industry_at("600519.SH", date(2020, 6, 15))       # dict with 5 keys / None

# 获取某股票全部 ST 历史事件列表(按 valid_from 升序)
events = load_st_history("000666.SZ")
# [{'valid_from': '2001-06-04', 'valid_to': '2001-09-18', 'is_st_value': True, ...}, ...]

lg_utils.history 模块通过 executor 内部 HTTP 调用同一个 REST endpoint,fail-soft 语义与 REST API 一致(无记录返回 None/[],不抛异常)。


看板 (Dashboard)

skillIdmethod功能风险
dashboard.listGET列出看板🟢
dashboard.getGET获取看板详情🟢
dashboard.data.getGET一次拿看板所有组件的数据(支持 maxRows,默认 100,上限 500)🟢
dashboard.updatePUT全量替换看板(含 widgets/variables)。想只改名称或描述?用 dashboard.patch,避免 widgetsJson 被覆盖。🟡
dashboard.patchPATCH部分更新看板元数据(PATCH /api/dashboards/{id})。字段掩码语义:仅 name / description 两个安全字段可被更新;缺失字段、显式 null以及空字符串 "" 都视为"跳过"(清空)。严格拒绝widgetsJson / variablesJson / widgets / teamName / dashboardId / allowSubscription 出现在 body 即返回 HTTP 200 success:false, code:"FIELD_NOT_PATCHABLE"此 skill 需要 dashboard.patch scope(独立于 dashboard.update),现有 token 须重新签发方可使用。🟢

订阅 & Marketplace

skillIdmethod功能风险
subscription.token.listGET列出订阅 token🟢
marketplace.item.listGET列出可订阅的看板/资产🟢
marketplace.item.subscribePOST订阅市场条目🟡
marketplace.item.unsubscribePOST取消订阅🟡

指标告警 (Metric Alert)

skillIdmethod功能风险
metric.alert.listGETdashboardId 列出告警规则🟢
metric.alert.getGETruleCode 获取规则🟢
metric.alert.createPOST创建告警规则🟡
metric.alert.updatePUT全量替换告警规则。想只改阈值/webhook/消息模板?用 metric.alert.patch🟡
metric.alert.togglePUT启用/停用规则🟡
metric.alert.testPOST仅测试(无副作用)🟡
metric.alert.evaluatePOST执行评估并按规则触发 webhook🟡
metric.alert.patchPATCH部分更新告警规则元数据(PATCH /api/metric-alerts/{ruleCode})。字段掩码语义threshold(阈值字符串)/ webhookDsName(webhook 数据源名)/ messageTemplate(消息模板)/ templateEngine(模板引擎,'legacy''freemarker')四个安全字段可被更新;缺失字段、显式 null以及空字符串 "" 都视为"跳过"(清空)。严格拒绝enabled(用 metric.alert.toggle)/ ruleCode(标识符)/ metricKey(更改后测量对象变化 — 破坏性)/ teamName 出现在 body 即返回 HTTP 200 success:false, code:"FIELD_NOT_PATCHABLE"templateEngine 传入非 legacy/freemarker 值返回 HTTP 200 success:false, code:"INVALID_TEMPLATE_ENGINE"此 skill 需要 metric.alert.patch scope(独立于 metric.alert.update),现有 token 须重新签发方可使用。🟢
metric.alert.snoozePUT暂停指定 alert rule 直到 until (ISO 8601 本地时间戳);body: {"until": "2026-06-22T18:00:00"}. Snooze 优先级高于 silence + rate-limit;snooze 到期后自动恢复正常 evaluate()。until 缺失或非未来时间 → HTTP 200 success:false code:INVALID_UNTIL🟢
metric.alert.unsnoozePUT取消活跃的 snooze (snoozed_until → null);与 snooze 是独立 scope — 设置 snooze 的 agent 不会自动获得清除权限。幂等,已无 snooze 时也返回 success。🟢
metric.alert.acknowledgePUT标记 alert 已知 (acknowledged_at = now());v1 仅 info-only — 不影响下次 evaluate()(UI 显示"已知"状态);v2 可能扩展为"clears next trigger window"。🟢

metric.alert.evaluate — 数据新鲜度门控(freshness gate,2026-06-22 重写)

freshnessConfig.sourceType = 'asset' 模式下,freshness gate 不再执行 SELECT MAX(field) 实时查询,而是直接读取 data_asset.last_data_refresh_at 列——该列由 registerAsset executor step 在每次 ETL 成功执行后自动写入。行为变化:

  • field 字段已废弃但向下兼容:现有配置中的 field 会被忽略(不校验、不查询),无需修改已有 freshnessConfig
  • 跨租户订阅规则publisherTeam != null):subscriber 侧直接通过 getAssetByIdInTeam 读取 publisher 资产的 last_data_refresh_at,无需 publisher 数据源凭证;跨租户 freshness gate 现已生效(此前 custom-sql 跨租户路径仍受 MVP 限制,保持 fail-open)。
  • NULL 值 = fail-open:资产首次注册后 executor 未重新部署前(或从未经过 registerAsset step),last_data_refresh_at 为 NULL → probe 返回 Optional.empty() → 评估照常进行,不触发 gate。
  • sourceType = 'custom-sql':行为不变(own-team 执行自定义 SQL;跨租户仍为 MVP 限制)。

已知限制:ETL job 执行成功但写入 0 行时,RegisterAssetStep 仍然会 bump last_data_refresh_at,导致 gate 不会跳过该次评估。若 webhook 频繁误触发,建议使用 metric.alert.snooze 或调高阈值;v2 会在 rowsWritten > 0 条件下才 bump(目前为规划中的 follow-up)。

metric.alert.snooze — evaluate() 跳过优先级(Triage #11)

evaluate() 内部跳过检查的优先级(先匹配先返回):

  1. snoozedUntilsnoozed_until > now() → SKIPPED "snoozed until <timestamp>"
  2. silenceMinuteslastTriggeredAt + silenceMinutes > now() → SKIPPED "in silence period"
  3. maxFiresPerDayfiresToday >= maxFiresPerDay → SKIPPED "daily limit reached"
  4. → 正常执行评估 + webhook

Snooze 到期后(snoozed_until < now())自动恢复,无需再调 unsnooze。

metric.alert.patch — templateEngine 字段(Triage #10)

templateEngine 控制 messageTemplate 的渲染方式:

行为适用场景
"legacy"(默认)${varName} 正则替换,与历史版本完全兼容现有规则无需改动
"freemarker"FreeMarker 模板引擎:支持条件指令(<#if var??>...</#if>)、<#list> 迭代、null 安全访问(${var!})、rowFields 嵌套访问(${rowFields.stock_num!}需要条件内容或丰富格式的飞书/微信消息

freemarker 模式下可用变量:

变量类型说明
${ruleName}String规则名称
${value}String当前触发值
${threshold}String阈值
${fieldName}String监控字段名
${assetName}String资产名称
${operator}String运算符(GT / LT / EQ 等)
${tenantName}String告警规则所属团队
${publisherTeam}String订阅资产发布方团队(自有资产为 null)
${timestamp}StringISO-8601 UTC 触发时间(freemarker 独有,legacy 不含
${rowFields.col_name!}StringGROUP BY 分组字段值(如 ${rowFields.stock_num!}! 表示缺失时默认为空字符串)
${col_name}StringrowFields 键同时提升为顶级变量,方便直接访问

freemarker 模板示例(飞书 Markdown):

<#if value??>
**告警 ${ruleName}** 触发
- 字段:`${fieldName}`
- 当前值:${value}
- 阈值:${threshold}(${operator})
<#if rowFields.stock_num??>- 股票:${rowFields.stock_num!}</#if>
<#if rowFields.region??>- 区域:${rowFields.region!}</#if>
触发时间:${timestamp}
</#if>

<#if value??>...</#if> 是 FreeMarker 空值检查指令:当 value 非 null 时渲染,等价于 if value is not None:${rowFields.stock_num!} 中的 ! 是默认值操作符 — 缺失时渲染为空字符串,避免 undefined variable 错误。<#if rowFields.stock_num??> 同理 — 仅当 GROUP BY 包含该列时渲染该行,避免多余的空行。

向下兼容说明: FreeMarker 的 ${var} 语法与 legacy 正则语法完全相同 — 将 templateEngine"legacy" 切换为 "freemarker" 后,现有的 ${ruleName} / ${value} 等模板无需任何改动即可继续工作。"freemarker" 模式额外支持 <#if> / <#list> 等高级指令。

切换引擎示例:

PATCH /api/metric-alerts/{ruleCode}
{
  "templateEngine": "freemarker",
  "messageTemplate": "${ruleName}: ${fieldName}=${value} > ${threshold}"
}

成功响应:{"success":true,"message":"Alert rule patched","ruleCode":"<ruleCode>"}

Webhook 插件

skillIdmethod功能风险
plugin.webhook.sendPOST通过数据源发送 webhook🟡

用户注册 & 反馈

skillIdmethod功能风险
auth.user.registerPOST注册新账号(teamName 自动生成为 tenant_${username}🟢
auth.token.introspectGET查询当前 Bearer token 的身份、scope、状态与过期时间(无参数,仅限自身 token)🟢
feedback.submitPOST提交反馈/Bug/需求🟢
feedback.listGET查看历史反馈与官方回复🟢

反馈详情

feedback.submit — 提交反馈 / Bug 报告 / 功能需求

路径:POST /api/feedback

必填字段:

字段类型说明
categorystring反馈分类,严格枚举,见下方
subjectstring主题(最多 256 字符)
messagestring详细描述

category 合法值(严格匹配,不接受缩写):

bug_report | feature_request | general | other

注意:"bug""feature" 等缩写形式均为非法 category,接口将返回 HTTP 200 + success:false

可选字段:

字段类型说明
contactEmailstring联系邮箱(可选,最多 180 字符)

请求示例:

{
  "category": "bug_report",
  "subject": "数据资产列表加载失败",
  "message": "在工作区 A 下点击「数据资产」后页面空白,控制台显示 JSON.parse 报错。浏览器 Chrome 125,复现率 100%。",
  "contactEmail": "user@example.com"
}

成功响应(HTTP 200):

{
  "success": true,
  "message": "Feedback submitted"
}

业务错误响应(HTTP 200 + success:false,category 非法示例):

{
  "success": false,
  "message": "Invalid category"
}

认证错误响应(HTTP 401,未登录):

{
  "success": false,
  "message": "Authentication required"
}

身份 & Token 详情

auth.token.introspect — 查询当前 Bearer token 的身份 + scope + 生命周期

路径:GET /api/public/agent/token-introspect

调用此接口可在不触发任何业务 401/403 的前提下,查看当前 Bearer token 的身份、所属团队、token id、用户 id、有效 scope 列表,以及 token 名称 / 状态 / 过期时间。自省范围限定为当前 token——接口不接受 tokenId 参数,永远只描述请求 header 中提供的那一个 Bearer。

输入:无 query 参数。Authorization: Bearer lgatk_... header 是唯一身份来源。

响应示例(200):

{
  "success": true,
  "username": "alice",
  "teamName": "team-alpha",
  "userId": 7,
  "tokenId": 42,
  "tokenName": "alice-clawhub-readonly",
  "status": "active",
  "expiresAt": "2026-09-17T10:30:45Z",
  "scopes": [
    "dataasset.list",
    "dataasset.get",
    "dataasset.schema.get",
    "dataasset.data.get",
    "process.ingestion.list"
  ]
}

expiresAt 在 token 无过期时为字面量 null(key 始终存在)。

响应示例(401,missing/invalid token):

{
  "success": false,
  "message": "Invalid token"
}

典型用法:在调用任何 /api/** 业务接口前先 introspect 一次,把当前 token 的 scopes 与计划调用的 skill 所需 scope 做集合对比;缺失则在客户端给出友好提示,避免逐个 skill 403 试探。


Backtest API

stock_day 日线回测请使用 run_stock_day_backtest(单股)或 run_stock_day_portfolio_backtest(多股组合),均在 lg_utils.backtest_examples.stock_day 模块。基金 / 黄金 / 港股日线回测也已 ship — lg_utils.backtest_examples.{fund_day,metal_day,hk_day}.run_*_backtest,shape 与 stock_day 一致(详见下方 Python 工具库表格)。完整参数说明见场景 4 示例

结果可通过 investment.stock.backtest.* REST skill 检索:list(摘要)、get(全量 JSON)、compare(两次 metrics diff)。REST skill 当前以 investment.stock.backtest.* 命名但是 asset-class-agnostic — 通过 Python 调用 lg_utils.backtest_examples.fund_day / metal_day / hk_day 持久化的结果同样可由这 3 个 REST skill 检索(persist(name=...) 调用统一走 process_backtest_result 表,按 name 索引)。专用的 investment.fund.backtest.* / investment.gold.backtest.* REST skill 命名空间是 v2 follow-up(目前 stock-class skill 已能 cover 所有资产类的回测结果检索)。

Wealth Studio(需开通 investment_studio 解决方案权限)

Phase 4 重命名说明:skill id 前缀由 stockstudio.* 改为 investment.stock.*(同时新增 investment.fund.* / investment.gold.*)。旧 stockstudio.* id 通过 Express alias 表自动转发,新代码优先使用 investment.stock.*

investment.stock.* — 股票持仓 / 交易 / 回测

skillIdmethod功能风险
investment.stock.portfolio.listGET查股票持仓(query 传 asset_class=stock)。每行附带 recommendation 字段:最新一条 per-stock 推荐;完整历史走 /api/profile/portfolio-positions/recommendations?stock_num=...。新增 ?accountType=real|paper|all 参数,默认 real;显式传 paperall 才会包含模拟交易数据🟢
investment.stock.portfolio.createPOST新增股票持仓条目(body 含 asset_class: "stock"🟡
investment.stock.portfolio.updatePUT更新股票持仓🟡
investment.stock.trading.listGET查股票交易记录(query 传 asset_class=stock🟢
investment.stock.trading.createPOST录入新股票交易(自动更新持仓;body 含 asset_class: "stock"🟡
investment.stock.backtest.listGET列当前团队的回测结果,支持 ?name= / ?tag= / ?programGroup= 过滤。Summary 视图——返回 totalReturn/sharpe/maxDrawdown/... 数值列 + tags / programGroup 元数据,不含大 JSON🟢
investment.stock.backtest.getGET单条回测结果详情(按 id),包含 paramsJson/metricsJson/tradesJson/equityCurveJson 等全量 JSON 负载🟢
investment.stock.backtest.compareGET两次回测结果 metrics 逐项 diff(totalReturn/sharpe/maxDrawdown/...🟢
investment.stock.portfolio.nav_history.getGET组合 NAV 日历史 + 日度时间加权收益率(TWR) + 基准对比 α;详见下方「理财组合 NAV 历史」🟢
investment.stock.portfolio.nav_history.benchmarks.listGET返回平台支持的基准指数目录(4 条),附带每个指数最新可用日期与 active/stale 状态;可用于 UI 下拉选择器🟢
investment.stock.portfolio.attribution.getGET组合 α/β v1 归因:一次调用返回 alpha(日度 + 年化)、beta、R²、波动率、跟踪误差、信息比率;详见下方「理财组合归因」🟢

investment.stock.backtest.list — 过滤参数 (#15)

路径:GET /api/profile/backtest-results

参数必填默认说明
name精确匹配 name 字段
tag子串包含匹配:返回 tags 数组包含此 tag 的所有结果
programGroup精确匹配 program_group 字段

多个过滤条件 AND 组合。无过滤参数时返回当前团队最近 50 条。

响应示例(截取):

{
  "success": true,
  "total": 2,
  "items": [
    {
      "id": 123,
      "name": "dual_ma_5_20",
      "totalReturn": 0.1842,
      "sharpe": 1.34,
      "tags": ["live-mock-2026Q3", "tutorial-walkthrough"],
      "programGroup": "dual-ma-series"
    },
    {
      "id": 122,
      "name": "dual_ma_5_30",
      "totalReturn": 0.1751,
      "sharpe": 1.21,
      "tags": [],
      "programGroup": "dual-ma-series"
    }
  ]
}

tags 是 JSON 数组(无 tag 时为 [],不是 null 也不省略 key)。programGroup 是字符串(未设置时为 null,但 key 始终存在)。

写入 tags / programGroup:调用 POST /api/internal/backtest-results 时在 body 里加 "tags": ["...","..."]"programGroup": "..." 字段。约束:tag 单条最长 64 字符;最多 20 条;空白 tag 自动剔除;programGroup 最长 128 字符。


理财组合 NAV 历史

skillId: investment.stock.portfolio.nav_history.get
路径: GET /api/profile/nav-history
权限: 需开通 investment_studio 解决方案权限(与持仓 / 交易系列相同)

查询参数:

参数必填默认说明
start组合最早快照日期起始日期,格式 YYYY-MM-DD
end今天结束日期,格式 YYYY-MM-DD
asset_classstock持仓资产类别(stock / fund / gold
benchmarkTeamVariable benchmark_index → 默认 1B0300基准指数代码(见下方指数目录)。优先级:请求参数 > 团队变量 > 默认 1B0300
accountTypereal账户类型筛选:real(仅真实账户,默认)/ paper(仅模拟账户)/ all(全部)。默认 real;显式传 paperall 才会包含模拟交易数据。

基准指数解析优先级:

  1. ?benchmark=<code> 请求参数(最高优先级)
  2. 团队变量 benchmark_index(团队管理员在 TeamVariable 里配置的默认基准)
  3. 硬编码默认值:1B0300(沪深300)

支持的基准指数(stock_day 内的 legacy 编码):

代码名称市场
1B0300沪深300(默认)SH
1A0001上证综指SH
1B0510中证A500SH
399001深证成指(数据停更于 2026-03-17,请求时会降级并返回 meta.benchmarkWarningSZ

注意:这些是平台 stock_day 表里使用的 legacy 编码,与第三方常见的 xxxxxx.SH / xxxxxx.SZ 格式不同,不要混用。

EOD 快照与 accountType(v1.0.30 修复):

每日 EOD 快照作业(daily_pnl_snapshot)写入 daily_position_pnl 时,从 portfolio_positions.account_type 读取 accountType 并原样写入快照行。v1.0.30 之前,快照行始终以实体默认值 REAL 写入,导致模拟账户头寸被误标记为 REAL,使真实账户 NAV 曲线出现虚高。v1.0.30 通过同步写路径修复了此问题,并通过 Flyway 迁移回填了历史数据。如在 v1.0.30 发布前存入的快照行出现异常 NAV 尖峰,请在后端重启后检查 daily_position_pnlaccount_id LIKE 'paper-%' 的行是否均为 account_type = 'PAPER'

收益率计算方法(日度时间加权收益率 TWR):

平台以每日 SUM(market_value) 作为组合净值 V_t,从 trading_records 提取当日净流量 F_t = Σ(BUY 金额) − Σ(SELL 金额),按 r_t = (V_t − F_t) / V_{t−1} − 1 计算日收益率,再链乘为累积收益率 R_t = Π(1 + r_i) − 1。通过减去当日净流入,"增仓 / 减仓"带来的资金变动不计入收益,只保留纯粹的价格涨跌信号。完整数学推导见设计文档 docs/plans/2026-06-16-portfolio-nav-history-api-design.md §3a。

响应示例(含基准):

{
  "success": true,
  "data": {
    "dates": ["2026-01-02", "2026-01-03", "2026-01-06"],
    "portfolio_nav": [100000.00, 102000.00, 101500.00],
    "portfolio_cumulative_return": [0.00000000, 0.02000000, 0.01500000],
    "benchmark_code": "1B0300",
    "benchmark_cumulative_return": [0.00000000, 0.01000000, 0.00800000],
    "alpha_spread": [0.00000000, 0.01000000, 0.00700000]
  },
  "message": "ok",
  "meta": {
    "filledDates": [],
    "benchmarkSource": "default",
    "benchmarkWarning": null,
    "benchmarkStaleAt": null
  }
}

meta 字段说明:

字段说明
filledDatesmarket_value=null(当日无行情)而向前填充的日期列表;不影响收益率计算,仅作信息提示
benchmarkSource"request" / "team_variable" / "default" — 说明基准来源
benchmarkWarning基准数据停更时的告警文案(如"Benchmark 399001 series stale; last available 2026-03-17");为 null 时省略
benchmarkStaleAt基准最后有效日期(ISO);基准截断时此字段告知截断位置,alpha_spread 此后各元素为 null

业务错误合约:

  • ?benchmark=BADCODE → HTTP 200,{"success": false, "message": "Unknown benchmark index: BADCODE (...)"}(不抛 500,也不返 HTML)
  • 基准数据停更(如 399001)→ 仍返回完整 NAV,meta.benchmarkWarning 描述停更,alpha_spread 截断到最后有效日;不失败整个调用
  • 无持仓快照记录 → HTTP 200,{"success": true, "data": {"dates": [], ...}, "message": "No snapshot history found for this portfolio"}

理财组合归因

skillId: investment.stock.portfolio.attribution.get
路径: GET /api/profile/attribution
权限: 需开通 investment_studio 解决方案权限(与持仓 / 交易系列相同)

一次调用即可得到"我的策略跑赢市场了吗?"的核心数字:年化 alpha(超额收益)、beta(市场敏感度)、(拟合度)、波动率、跟踪误差和信息比率。底层数据来自已有 nav_history.get 的日度 TWR 累积收益序列,无额外 DB 查询。

查询参数:

参数必填默认说明
start组合最早快照日期起始日期,格式 YYYY-MM-DD
end今天结束日期,格式 YYYY-MM-DD
asset_classstock持仓资产类别(stock / fund / gold
benchmarkTeamVariable benchmark_index → 默认 1B0300基准指数代码,优先级与 nav_history.get 相同
accountTyperealreal(仅真实账户)/ paper(仅模拟)/ all(全部)

计算方法(α/β v1):

nav_history.get 返回的日度累积收益序列,经链式反推 R_p[i] = (1+CR[i])/(1+CR[i-1]) - 1 得到日度收益率序列,再以 OLS 回归计算:

指标公式
betaCov(R_p, R_b) / Var(R_b)(样本方差,N-1 分母)
alphamean(R_p) - beta × mean(R_b)(日度)
alphaAnnualizedalpha × 252
rSquaredCov(R_p, R_b)² / (Var(R_p) × Var(R_b))
portfolioVolatilitystddev(R_p) × √252(年化)
benchmarkVolatilitystddev(R_b) × √252(年化)
trackingErrorstddev(R_p − R_b) × √252(年化主动风险)
informationRatio(mean(R_p) − mean(R_b)) × 252 / trackingError

所有数值字段精度为 4 位小数(BigDecimal scale 4)。

响应示例(含基准,合成数字):

{
  "success": true,
  "data": {
    "start": "2026-01-01",
    "end": "2026-06-17",
    "tradingDays": 115,
    "benchmarkCode": "1B0300",
    "benchmarkSource": "team_variable",
    "portfolioCumulativeReturn": "-0.1319",
    "benchmarkCumulativeReturn": "0.0528",
    "alpha": "-0.1847",
    "alphaAnnualized": "-0.4123",
    "beta": "0.8742",
    "rSquared": "0.6418",
    "portfolioVolatility": "0.2156",
    "benchmarkVolatility": "0.1834",
    "trackingError": "0.0941",
    "informationRatio": "-1.9628"
  },
  "meta": {
    "benchmarkWarning": null,
    "benchmarkStaleAt": null,
    "sampleSize": 115,
    "minSampleWarning": null,
    "computationWarning": null
  },
  "message": "OK"
}

22 个字段总览(D12 文档锁):

15 data 字段: start, end, tradingDays, benchmarkCode, benchmarkSource, portfolioCumulativeReturn, benchmarkCumulativeReturn, alpha, alphaAnnualized, beta, rSquared, portfolioVolatility, benchmarkVolatility, trackingError, informationRatio.

5 meta 字段: benchmarkWarning, benchmarkStaleAt, sampleSize, minSampleWarning, computationWarning.

2 envelope 字段: success, message.

meta 字段说明:

字段说明
benchmarkWarning基准缺失 / 对齐窗口过短时的告警文案;null 表示正常
benchmarkStaleAt基准最后有效日期(ISO);基准截断时告知截断位置
sampleSize对齐后的原始序列天数(等于 tradingDays
minSampleWarning对齐后日度收益点数 < 5 时的告警 (OLS 最小有效样本)(含实际点数);null 表示正常;fires when aligned daily returns < 5 (OLS minimum)
computationWarning基准方差接近零(β 无法定义)时的告警;null 表示正常

边界行为合约:

  • 对齐窗口 ≤ 1 天(基准数据只有首日)→ beta/rSquared/trackingError/informationRatio 全为 null,meta.benchmarkWarning = "Benchmark series too short for α/β computation after alignment"
  • 日度收益点数 < 5(OLS 最小样本,tradingDays 太短)→ 同样置 null,meta.minSampleWarning 含实际点数
  • 基准方差近零(常数基准)→ β 无意义,置 null,meta.computationWarning = "Benchmark variance near zero; β undefined"
  • 无持仓数据 → 与 nav_history.get 行为一致,返回空序列 + success:true
  • ?benchmark=BADCODE → HTTP 200 {"success": false, "message": "..."}

investment.fund.* — 基金持仓 / 交易 (回测通过 lg_utils.backtest_examples.fund_day Python 入口,REST skill investment.stock.backtest.* 可检索结果)

skillIdmethod功能风险
investment.fund.portfolio.listGET查基金持仓(query 传 asset_class=fundmarket 取值 OF/ETF/LOF)🟢
investment.fund.portfolio.createPOST新增基金持仓(body 含 asset_class: "fund"🟡
investment.fund.portfolio.updatePUT更新基金持仓🟡
investment.fund.trading.listGET查基金交易记录(query 传 asset_class=fund🟢
investment.fund.trading.createPOST录入新基金交易(body 含 asset_class: "fund"🟡

investment.gold.* — 黄金持仓 / 交易 (回测通过 lg_utils.backtest_examples.metal_day Python 入口,REST skill investment.stock.backtest.* 可检索结果)

skillIdmethod功能风险
investment.gold.portfolio.listGET查黄金持仓(query 传 asset_class=goldmarket 取值 SGE/BANK;stock_num 取值 Au99.99/Au100g/AuTD)🟢
investment.gold.portfolio.createPOST新增黄金持仓(body 含 asset_class: "gold"🟡
investment.gold.portfolio.updatePUT更新黄金持仓🟡
investment.gold.trading.listGET查黄金交易记录(query 传 asset_class=gold🟢
investment.gold.trading.createPOST录入新黄金交易(body 含 asset_class: "gold"🟡

investment.paper.* — 模拟盘交易(Paper Trading,单户 ¥1,000,000 沙盘)

⚠️ Paper-trading tokens are platform-issued, NOT self-mintable. The paper.* scope namespace is reserved — calling POST /api/subscription/tokens with scopes: "paper.account.read" (etc.) returns HTTP 400 + {"code":"RESERVED_SCOPE"}. Paper-execution tokens are auto-minted by the platform when a strategy is bound to a paper account via the UI (PaperExecutionTokenService.mintForExecution). If you want to drive paper trading from an external agent, mint the token via the strategy-binding flow first, then call the endpoints below with that token.

新 v1.1:单户模拟盘,行情打通 stock_day 实时价;MARKET 同步成交,LIMIT 走每 60s 撮合 + 15:00 自动 EXPIRED;T+1、一手 100 股、A 股标准手续费全部强制。与真金账户完全隔离——真金交易记录走 investment.stock.trading.*,二者数据库行级互不影响。

skillIdmethod功能风险
investment.paper.account.getGET查模拟账户当前状态:现金 / 初始资金 / 累计重置次数 / 总市值。首次调用 lazy-create🟢
investment.paper.orders.submitPOST下单:{stock_num, market, side: BUY/SELL, order_type: MARKET/LIMIT, qty, limit_price?, client_order_id}client_order_id 必传(同 user + 同 client_order_id 幂等)。MARKET 同步返回成交价;LIMIT 返回 SUBMITTED,撮合后由 scheduler 推到 FILLED/EXPIRED🟡
investment.paper.orders.listGET查订单(支持 status= / from= / to= 过滤);返回字段含 status / source / filledPrice / filledQty / fees / rejectReason🟢
investment.paper.positions.listGET查模拟持仓(只返 account_id LIKE 'paper-%' 的行,不会和真金混在一起)🟢

Scope 集(process-execution 默认)paper.orders.write paper.account.read dataasset.read。这是后端在 process 起跑时自动注入的 scope-limited 短期 Bearer。

Scope guard 边界:带 paper scope 的 Bearer 只能访问 /api/wealth/paper/**/api/data-assets/**/api/auth/current/api/public/agent/token-introspect;其他 namespace(如 /api/trading-records 真金路径)一律 403 scope_insufficient。如需在自己的 PAT 上启用 paper 调用,去 个人设置 → Token 管理 创建一个带这套 scope 的长期 PAT。

典型用法(在 Process Python 节点里跑量化策略)

import lg

execution_id = lg.get_variable("execution_id")
quote_resp = lg.get_asset_data_realtime("stock_day",
    filter_column="stock_num", filter_value="600000",
    filter_operator="eq", size=1)
if not quote_resp["success"]:
    print(quote_resp["message"])
    return
quote_rows = quote_resp["data"]
positions = {p["stockNum"] for p in lg.paper.get_positions()["items"]}

if quote_rows and quote_rows[0]["close_price"] < 9.50 and "600000" not in positions:
    # Preferred form: bare stock code + explicit market.
    # The dotted form ("600000.SH") is also accepted — the SDK and backend both
    # auto-strip it via PaperOrderService.normalizeStockNum (Bug #51-A).
    lg.paper.submit_order(
        symbol="600000", market="SH", side="BUY", qty=100, order_type="MARKET",
        client_order_id=f"{execution_id}-pufa-entry",  # 用 execution_id 做幂等键
    )

资源市场已上线 starter_paper_trade_strategy 模板,「免费接入」一键复制到你的 tenant 即可改写。

实时热窗口查询(lg.get_asset_data_realtime

# Real-time hot-window query (last 365 days, PG mirror, low-latency)
result = lg.get_asset_data_realtime(
    "stock_day",
    page=1, size=50,
    filter_column="day_id",
    filter_value="20260701",
    filter_op="eq",
    order_by="day_id",
    order_direction="desc",
)

if not result.get("success"):
    print(f"Realtime query failed: {result.get('message')}")
    # Common message: "This asset has no realtime mirror"
    # → Ask an admin to populate `realtimeDataSource` on the asset via
    # PUT /api/data-assets/{id}
else:
    rows = result["data"]
    total = result["totalElements"]
    print(f"Got {len(rows)} rows of {total} total")

回测模式(Ship 5-H,同一份脚本两种跑法):在 Process 执行环境上额外设置 LG_PAPER_MODE=backtest + LG_PAPER_BACKTEST_FROM=YYYY-MM-DD + LG_PAPER_BACKTEST_TO=YYYY-MM-DD同一脚本不改一行代码即对历史 stock_day 回放。此模式下:

  • 不发 HTTP —— submit_order 等调用走 lg_utils.paper_sim 进程内模拟器;后端 /api/wealth/paper/** 不会收到任何请求;paper_order 表不会写入新行。PaperScopeGuard / process_execution_secret / __LG_PAPER_TOKEN__ 全部不被消耗(token 若存在则 DEBUG 日志一行后忽略)。
  • 规则完全一致 —— Java 后端 FeeCalculator / PriceLimitValidator / PaperOrderService (T+1 + 一手) 为规则正本;Python 模拟器有 fixture 平价测试 (paper_sim_fixtures.yml + PaperSimRuleParityTest) 守护,任何规则方法 PR 必须同步重新生成 fixture + 更新 Python,否则 CI 红。
  • 不消费 scope —— 因为根本不发出 HTTP,所以 paper.orders.write 等 scope 在 backtest 模式下完全用不到;agent / PAT / process-execution token 的 scope 集合不需要任何调整。
  • 持久化 —— 脚本末尾必须显式调 lg.paper.persist_backtest(name=...)(无 atexit 隐式持久化;防止脚本异常中断时写入「半拉子结果」造成数据污染)。结果写入 process_backtest_result,UI 走 /profile/backtest-results
  • suspension 不查 —— dacp_suspended_stocks 是前向运维表,回测刻意不查;这是文档化的 KNOWN_DIVERGENCE,不是 bug。

资源市场已上线 starter_paper_trade_strategy_backtest 模板。设计文档:docs/plans/2026-06-11-paper-trading-ship-5-h.md(LA-APPROVED v3)。

lg.paper 完整 API 速查表(live + backtest 两种模式共用同一函数签名;运行时由 LG_PAPER_MODE env var 决定走 HTTP 还是走进程内模拟器):

函数模式签名返回备注
lg.paper.submit_order(symbol, side, qty, order_type="MARKET", limit_price=None, client_order_id=None, market=None, asset_class=None)live + backtest见左dict两端共有id主键 — 不是 orderId,两端一致)、clientOrderIdstockNummarketsideqtyorderTypelimitPriceassetClassstatusrejectReasonfeesfilledQty已成交价键名两端不一致:backtest 用 fillPrice,live 用 filledPrice(pre-existing 命名分歧;agent 代码需做 r.get("fillPrice") or r.get("filledPrice") 兼容写法)。时间戳键两端不同:backtest 仅返回 tradeDate(推进到的模拟交易日);live 额外返回 submittedAt/filledAt(PaperOrder 实体时间戳)但不返回 tradeDatelive 独有键accountIdsubmittedAtfilledAtsource — backtest 路径无此四键。symbol 推荐裸代码 + 显式 market="SH"/"SZ"/"BJ"/"HK";带 .SH/.SZ/.BJ/.HK 后缀也接受(SDK 客户端 + 后端 PaperOrderService.normalizeStockNum 都会自动剥离 — Bug #51-A)。client_order_id 用作幂等键。order_type="LIMIT"limit_price 必填。
lg.paper.get_account()live + backtest()dict回测{"initialCapital", "cashBalance", "positionValue", "totalEquity", "unrealizedPnl"}live{"id", "accountId", "initialCapital", "cashBalance", "baseCurrency", "status", ...} — 注意 live 路径不返回 positionValue / totalEquity / unrealizedPnl(这些是模拟器进程内汇总,不是后端持久化字段)。回测模式下来自模拟器持仓估值;live 模式查询后端 /api/wealth/paper/accountPaperTradingController.toAccountMap()
lg.paper.get_positions()live + backtest()dict{"items": [...], "total": N}每条 item 关键键名因模式不完全统一回测返回 {stockNum, qty, avgCost, lastBuyDate}(精简);live 返回 {id, stockNum, market, stockName, assetClass, positionQty, avgCost, lastBuyDate, currency}(包含 positionQty 而非 qty)。marketValue / unrealizedPnl 两端都不返回 — 如需 market value / P&L 请自行用 get_asset_data("stock_day", ...)close_price × qty/positionQty 算。两端通用 stockNum(camelCase)— 不是 stock_num
lg.paper.get_orders(status=None)live + backtest(status=None)dict{"items": [...], "total": N}status 可选过滤值:SUBMITTED / FILLED / CANCELLED / REJECTED / EXPIRED(5 个;EXPIRED 覆盖 DAY-TIF 限价单未触发收盘自动失效)。不支持 PARTIALLY_FILLED(此后端未实现 partial fill 语义)。
lg.paper.cancel_order(order_id)live + backtest(order_id)dict(更新后的订单对象)已成交订单 422 拒绝。
lg.paper.advance_day(day)backtest only(day: str "YYYY-MM-DD")None推进模拟时钟到指定交易日;触发挂单成交评估、价格限制检查、T+1 规则。live 模式调用此函数会抛 RuntimeError(函数级文案,例如 "advance_day() is only available in backtest mode (LG_PAPER_MODE=backtest). In live mode, the current day is inferred from the market calendar.")。
lg.paper.record_eod_equity()backtest only()None在当前模拟日的收盘后调用,记录该日的 equity curve 点(用于 Sharpe / MaxDD 等指标聚合)。
lg.paper.persist_backtest(name=None, force=False)backtest only(name=None, force=False)dict{"id": int, "name": str} — 仅 2 个 key必须显式调用 — 没有 atexit 隐式持久化(防异常中断写入半拉子结果)。name=None 时使用 LG_PAPER_BACKTEST_PERSIST_NAME env 或自动生成时间戳名。force=True 允许覆盖同名 result。metrics 不在返回里 — 需要 metrics 摘要请查 process_backtest_result 表或 /profile/backtest-results UI。

回测模式完整脚本示范(同一脚本与 live 模式只差三个 env var):

Pattern note: use lg.get_asset_data for backtest / historical-window loops (full history via MC after Step 4 flip); use lg.get_asset_data_realtime for live / paper-trade single-row lookups (hot 365-day window on PG). .getRealtime returns success:false on data older than 365 days — never use it inside backtest loops that may extend beyond a year.

import lg
from datetime import date, timedelta

execution_id = lg.get_variable("execution_id")

# 走历史 stock_day(回放);不发任何 HTTP 给后端。
start = date(2026, 1, 1)
end   = date(2026, 1, 31)
day   = start
while day <= end:
    lg.paper.advance_day(day.isoformat())

    # 策略示例:每日抓 600000 的 stock_day 当日收盘价;< 9.50 且无持仓时买 100 股。
    # 注意:lg.get_asset_data 是单列过滤;如需 (stock_num, day_id) 联合过滤,
    # 在 SQL 化的策略里把 day_id 作为 filter_column、由 advance_day 控制时间游标。
    quote_resp = lg.get_asset_data("stock_day",
        filter_column="day_id", filter_value=int(day.strftime("%Y%m%d")),
        filter_operator="eq", size=500)
    quote_rows = [r for r in quote_resp["data"] if r.get("stock_num") == "600000"]
    positions = {p["stockNum"] for p in lg.paper.get_positions()["items"]}

    if quote_rows and quote_rows[0]["close_price"] < 9.50 and "600000" not in positions:
        lg.paper.submit_order(
            symbol="600000", market="SH", side="BUY", qty=100, order_type="MARKET",
            client_order_id=f"{execution_id}-day{day.isoformat()}",
        )

    lg.paper.record_eod_equity()    # 收盘记账
    day += timedelta(days=1)

# 显式持久化 — 没有这一行结果不会写 process_backtest_result
result = lg.paper.persist_backtest(name="600000_low_entry_jan2026")
print(f"backtest persisted: id={result['id']}, name={result['name']}")

关键差异 vs live 模式(同一份用户代码, env var 区别):

  • live 模式不需要 advance_day / record_eod_equity / persist_backtest(实时撮合 + 真实交易日时钟)— 这三个函数在 live 模式下会抛 RuntimeError(错误文案按函数定制,例如 advance_day 文案明确指出"live mode 由 market calendar 推进当前日"),便于 fail-fast 发现脚本误配。
  • backtest 模式下 submit_order 不会写 paper_order 表(进程内模拟器 only);live 模式下每次成功调用都对应后端一行 paper_order 记录。
  • backtest 模式 fixture 平价测试(paper_sim_fixtures.yml + PaperSimRuleParityTest Python 端 + PaperOrderServiceSymbolFormatTest Java 端)守护规则一致性 — 任何 fee / price-limit / T+1 / 一手规则的 PR 必须同步重新生成 fixture,否则 CI 红。

技能源在 app.jsSKILL_CATALOG,运行时可通过 GET /agent/skills 查询当前 token 实际可用的列表(会过滤 scope)。

Python 工具库 lg_utils(在平台 python_script 流程节点里 import 使用)

平台的 python_script 执行器会自动把 lg_utils 注入到用户脚本的 PYTHONPATH,无需安装。

模块 / 函数功能
lg_utils.get_variable(key, default) / lg_utils.get_variables()读取流程上下文变量(由前端/调度器传入;get_variables() 返回全集 dict)
lg_utils.put_variable(key, value) ✨ NEW把变量回写到当前 session 的 JobPool,下游 step 的 ${key} 替换能解析到。value 必须 JSON-serializable,单个值 ≤ 64 KB。key 不能以 _lg_ 开头(保留给系统)。同 step 内多次调用累积;用于把 Python 脚本计算出的字符串/数字/小型 dict 传给后续 step(webhook messageTemplate / SQL where 子句等)
lg_utils.get_context()当前团队快照:assets / datasources / dashboards / processes
lg_utils.get_asset_data(asset_identifier, page, size, order_by, filter_column, filter_value, filter_operator=None)分页拉团队有权限的资产数据(历史全量,步骤 4 切换后走 MC);返回 {success, data, totalElements, totalPages, ...}filter_value 支持 list/tuple → IN 查询;filter_operator 支持 eq / ne / in / not_in / like / gt / gte / lt / lte / contains,默认 contains。失败时 抛出 RuntimeError
lg_utils.get_asset_data_realtime(asset_identifier, page, size, order_by, filter_column, filter_value, filter_operator=None)get_asset_data 参数完全相同;内部走 GET /api/data-assets/{id}/data-realtime,查询资产的 实时镜像数据源(PG 热窗口,最近 365 天)。永不抛出异常——所有错误以 {success:false, message:...} 返回(无镜像 / MC 类型误配 / 连接失败 / 认证失败);调用方须检查 result["success"]。适合仪表盘、实时 P&L 等低延迟场景。与 dataasset.data.getRealtime skill 对应。
lg_utils.get_portfolio_positions(stock_num=None, page=1, size=500)当前团队持仓(每行附带最新的一条 per-stock 推荐 recommendation,由内部 API 按 update_time 取最近)
lg_utils.get_trading_records(account_id=None, market=None, stock_num=None, trade_type=None, page=1, size=50) ✨ NEW拉团队的交易记录(分页 dict,字段 Jackson camelCase 如 tradeDate / stockNum / tradeType
lg_utils.write_recommendations(items, process_id=None, execution_id=None)Python 脚本把 per-stock 推荐(action/priority/add1/add2/reduce1/reduce2/noMoreAdd/market追加process_stock_recommendation(历史保留,不 upsert);前端持仓页"推荐"按时间倒序展示历史
lg_utils.get_connection(ds_name) / get_db_config(ds_name)按团队数据源名取 JDBC 连接
lg_utils.backtest(strategy, asset, ...)单资产回测引擎:long-only、整数股;输出 Sharpe / Sortino / MaxDD / 胜率 / profit_factor / 交易明细 / equity_curve / 年度拆分;可选 benchmark_asset= 对比并输出 alpha/beta;可选 persist=True 持久化到 process_backtest_result
lg_utils.backtest_portfolio(strategies, assets, ...) ✨ NEW组合回测:多标的共享现金池;额外输出 per_asset 贡献度/回撤;同样支持 benchmark / persist
lg_utils.write_backtest_result(result, name=...) ✨ NEWBacktestResult 持久化到 process_backtest_result 表(append-only,按团队隔离)。BacktestResult.persist(name=...) 是同义糖
lg_utils.log 子模块 (from lg_utils.log import info, warn, error)标准化日志 helper:info → stdout,warn / error → stderr,自动加 [INFO]/[WARN]/[ERROR] 前缀。注意是子模块,不在 lg_utils.__all__ 里,必须按子模块路径 import
lg_utils.backtest_examples.dual_ma.DualMA内置双均线参考策略
lg_utils.backtest_examples.stock_day.run_stock_day_backtest针对平台 stock_day 日线表(OPEN_PRICE/CLOSE_PRICE/day_id/STOCK_NUM)的单股快捷封装
lg_utils.backtest_examples.stock_day.run_stock_day_portfolio_backtest ✨ NEW多只股票组合的快捷封装
lg_utils.backtest_examples.fund_day.run_fund_day_backtest ✨ NEW基金日线回测快捷封装。fund_day 表的 unit_nav 同时映射到 open/close(基金无 OHLC,只有单日净值);fund_code 作为 filter;nav_date (YYYY-MM-DD) 作为日期列。其余参数 (strategy / start / end / capital / benchmark_asset) 与 stock_day 同形。
lg_utils.backtest_examples.metal_day.run_metal_day_backtest ✨ NEWSGE 黄金/白银日线回测快捷封装。metal_day.close_price 同时映射到 open/close;metal_code 作为 filter。常用 metal_code: Au99.99 (黄金 9999)、Au100g (黄金 100g)、Ag(T+D) (白银 T+D)。
lg_utils.backtest_examples.hk_day.run_hk_day_backtest ✨ (v1.0.31 起 港股数据已合并入 stock_day)港股日线回测快捷封装。底层就是 stock_day 表过滤 HK ticker(v1.0.31 起 stock_day 统一覆盖 A 股 + 港股)。结构同 stock_day 模板。

环境要求

必需

  • LG_AGENT_BASE_URL - 平台地址(默认 https://privora.cn
  • LG_AGENT_TOKEN - Bearer Token(公开版唯一认证方式;建议使用最小权限、专用 Token)

Security Notes

  • 公开版 skill 仅支持 Bearer Token 模式,不接受会话 Cookie / CSRF。
  • 首次安装建议使用测试账号或低权限 Token 验证读取类能力。
  • 公开版 skill 覆盖数据查询 + 回测 + 模拟交易 + 告警 + 流程编排四大类,完整 per-category 分类(read / idempotent-write / workflow-transition / outbound-webhook)见 §Scope。删除、撤销、审批等破坏性/管理类操作不在 skill 范围内,需通过 platform UI 完成。
  • 写操作应只授予明确需要的 scopes。
  • 不要在脚本里硬编码 Token 凭据。

注意事项

  • 公开版 skill 覆盖数据 + 回测 + 模拟交易 + 告警 + 流程编排;破坏性/管理类操作(删除 / 撤销 / 审批)不在 skill 范围内,走 platform UI。
  • 公开版 helper scripts 只支持 Token 调用,不支持 session/cookie 兼容模式。
  • idempotencyKey 用于幂等控制,写操作请保持稳定。
  • Token 从平台获取,不要硬编码在脚本中。

最近更新

v1.0.44 (2026-07-11)

  • 🔄 内容 identical to v1.0.42(同 UX),v1.0.43 短暂回滚到 v1.0.40 SAFE 内容后发现 SkillSpector v2.3.5 在今天有 non-determinism,同 content 不同 scan 结果。既然 rollback 无法回 SAFE badge,选择保留 v1.0.42 的可读性改进(Chinese-fy Scope + What'''s-New 移底部 + marketplace §0)。

v1.0.43 (2026-07-11)

  • ⏪ 短暂 rollback 到 v1.0.40 内容尝试回 SAFE badge —— 失败(scanner non-determinism)。v1.0.44 恢复 v1.0.42 的可读性改进。

v1.0.42 (2026-07-11)

  • 📐 文档结构重组:4 个 What's-New 顶部块合并到本节;§Scope 与 §投资建议免责改为中文首要(不再中英双语介绍)。
  • 🚀 Quick Start §0 30 秒试用改为指向 marketplace UI(无需注册直接浏览公开挂牌资产),移除 top-of-doc dispatcher endpoint 示例。

v1.0.41 (2026-07-11)

  • 🚀 首版 Quick Start §0 匿名试用(后在 v1.0.42 改为 marketplace UI 路径)。

v1.0.40 (2026-07-10)

  • 🔒 SkillSpector 响应:primary framing 从"金融数据后端"翻转到"AI Agent 投资工作流平台";§2 加密段重写为"encryption at rest / 认证边界返明文";删除 3 处矛盾的"只读能力与常规非破坏性写操作"声明。

v1.0.38 (2026-07-10)

  • 📖 Quick Start §4 加 First Call Recipe(3 步 curl:list → 拿 numeric id → data.get)说明 URL {id} 必须是数字型资产 ID(如 42),不是资产名字(如 fund_day)。

v1.0.37 (2026-07-08)

  • 🛡️ 匿名 rate limit 迁移到 Redis 集群共享:10/IP/分钟 burst + 100/IP/天 daily 承诺现在真的成立(v1.0.36 时因 PM2 cluster 被放大 ~4×)。Redis 断连时 fail-open。

v1.0.36 (2026-07-08)

  • 🌐 匿名 marketplace preview 模式全接入:/agent/skills*Authorization header 自动进入 anonymous 模式,10 个只读 skill 白名单。三桶 rate limit(60/IP/分 general + 10/IP/分 data burst + 100/IP/天 data daily),任一超限 429 with bucket field。匿名调用强制 page=1, size=25,剥离发布者身份字段。

v1.0.32 (2026-07-02)

  • 📊 组合归因 (α/β):新增 investment.stock.portfolio.attribution.get,跟基准做 TWR 对比。
  • 📈 组合净值曲线:portfolio.nav_history.get 输出 NAV + 基准 overlay。
  • 💰 现金分红归因:新增 stock_dividend 数据资产。
  • 🧪 策略回测 sandbox 模式:process.ingestion.execute --mode=backtest
  • 🔔 告警治理 v1:alert.snooze / alert.acknowledge + Freemarker webhook 自定义 payload + 基于新鲜度的 stale 屏蔽。
  • ⚡ 实时行情双路由:dataasset.data.getRealtime 新 skill。

v1.0.31 (2026-06-25)

  • 🌏 stock_daystock_minutes 统一覆盖 A 股 + 港股(8500+ 股票:A 股 5500+ + 港股 3000+ 全谱),Bearer Token 即用 dataasset.data.get 按 ticker 自动路由(A 股 000001 / 港股 00700.HK 同一接口同一 schema)。

v1.0.35 (2026-07-03)

  • 📝 dataasset.metadata.get skill 注册:Node SKILL_CATALOG 补录 dataasset.metadata.get 条目,覆盖 GET /api/data-assets/{id}/metadata(20 字段富元数据 + 新鲜度字段)。此前该 skill 在 @RequireScope 和 SKILL.md 均已存在,但 Node catalog 缺失导致 Agent 调用返回 HTTP 400 "Skill not found"。(feedback ID=61 gap 4)

v1.0.30 (2026-06-23)

  • 📑 Added explicit analysis-not-investment-advice disclaimer in the Token Recommendation section: outputs are analytical inputs for operator review, not regulated advice; live trading and irreversible decisions stay outside autonomous execution.
  • 🔒 Streamlined the agent skill catalog: token-management write operations are documented as operator-only actions performed via the Privora token-management UI, consistent with the existing "operator-issued Bearer Token" guidance.

v1.0.29 (2026-06-23)

  • 🔒 Documentation cleanup based on ClawHub security audit feedback. Internal token-management surfaces are now exposed only through the operator UI rather than documented in the agent manifest; runtime behavior unchanged. (Note: this cleanup was queued for v1.0.28 but missed in the squash-merge; v1.0.29 ships the queued change.)

v1.0.28 (2026-06-23)

  • 📌 文案修正:实时 skill manifest 三个 public 端点的域名从 lg-data.cc 改为 privora.cn(与平台主域名一致)。旧域名仍 302 重定向到 privora.cn,已 in-flight 的调用不受影响,但建议把 /skill-version / /skill-manifest / /capabilities 的 baseURL 切到 https://privora.cn
  • 🔒 Token 推荐章节加强:显式强调 minimum-scope 原则 + "Do NOT have your agent create tokens on your behalf" 顶级规则(token mint 是 operator 动作,不是 agent 动作)。

v1.0.26 (2026-06-23)

  • 🔧 schedule.job.online / schedule.job.offline 响应清理:retired legacy code / msg alias 字段;统一使用 {success, message, data} 标准包络(PR #385 加的临时 alias 至此完成迁移)。
  • ✅ Frontend public/js/jobs.js 切到 result.success

v1.0.27 (2026-06-23)

  • 🔒 内部 token 校验加强:POST /api/subscription/tokens mint 路径增加额外服务端校验(最长有效期上限 + WARN 级别 audit log),由 operator 在 Token Management UI 上感知;不影响 agent-facing API 行为。

v1.0.26 (2026-06-23)

  • (internal) alertness freshness gate reads data_asset.last_data_refresh_at (no SQL probe). No user-facing behavior change.

v1.0.25 (2026-06-23)

  • 📝 Paper trading: clarified that paper.* tokens are platform-issued only.
  • ✅ Skill discovery: /api/public/agent/capabilities now enumerates 7 paper-trading endpoints (previously omitted because yml didn't register them).

v1.0.24 (2026-06-22)

  • 🔄 Live skill discovery:新增三个 public 端点(/api/public/agent/skill-version/api/public/agent/skill-manifest/api/public/agent/capabilities),已安装的 agent 可直接从 privora.cn 获取最新 manifest 和 endpoint catalog,不再依赖 ClawHub 缓存。/skill-version 返回 {version, updatedAt} 轻量级探针,用于判断本地缓存是否过期。
  • 📋 Capabilities catalog/capabilities 返回从 agent-scope-mapping.yml 解析的完整 {method, path, scope} 映射表(含 PR #382 snooze/ack/unsnooze 新 scope),agent 可程序化发现所有 endpoint 而无需手动翻 SKILL.md。
  • 📌 Frontmatter updatedAt 字段:从本版本起,每次 SKILL.md 发布都同步更新 frontmatter updatedAt 字段(ISO 日期字符串)。/skill-version 端点以此字段为权威版本时间,不受 Node 重部署 mtime 漂移影响。

v1.0.23 (2026-06-22)

  • 📊 新数据资产stock_forecast (业绩预告) + stock_express (业绩快报) 接入,11 年历史已回填 (82,457 + 19,945 行),每日 19:30 增量更新。
  • 🧹 覆盖声明收紧 (T-1 audit):原 headline 多资产统一数据:A 股 / 港股日线行情 改为按状态分类:stock_day / fund_day / metal_day / stock_forecast / stock_express 标 🟢 生产可用;stock_hk / stock_minutes 标 🔴 建设中(预计 Q3 开放);stock_us 标 ⚫ 未实现。新增「数据资产可用性」表 + 同步更新 frontmatter title / description / keywords 移除未交付的 港股 顶层标签。
  • 🔬 新 skilldataasset.metadata.get (Triage #14) — 单资产元数据查询,含 lastUpdated / expectedUpdateCadence / cronExpression / sourceDescription,可程序化判断数据新鲜度。
  • 📈 新 skillinvestment.stock.portfolio.attribution.get (Triage #9) — α/β 归因分析,复用 nav_history 时序,输出 alpha / beta / R² / 波动率 / tracking error / information ratio。配套前端 dashboard widget (#9 PR-B 已 ship)。
  • 🎯 新 skillmetric.alert.patch 支持 templateEngine 字段 — 选 freemarker 后 webhook 模板可用 <#if> 条件分支 + <#list> 循环 + 内置 ?string 格式化等。原 ${var} 字面替换仍是默认 templateEngine='legacy',向后兼容。
  • 🧰 执行器修复 (PR-D)StepMeta.parseParam 移除破坏性 replaceAll("[\\t\\n]","") strip — Python 多行脚本、pip 多行 requirements、SQL 多行子句存在 JSON 字符串里现在保留真实换行字符。修复 2026-05-26 / 2026-06-09 / 2026-06-15 三起相关事故。

v1.0.21 (2026-06-12)

  • 🔍 Display title expansion — adds 3 more 0-competitor empty-market terms to title: 量化分析 / 多资产 / 风险监控. Cumulative #1 唯一 long-tail wins after this release: 9 queries.

v1.0.20 (2026-06-12)

  • 🔍 Display title expansion — adds "模拟盘 + 实时告警" to surface the paper-trading and alert capabilities in clawhub search (both query terms were 0-competitor empty markets pre-v1.0.20).

v1.0.19 (2026-06-12)

  • 🔍 Display title experiment — pass --name on publish to test if the CLI flag can set a Chinese-keyword-rich display title (auto-generated slug-based titles miss Chinese query intent like "A股 / 量化回测" / "Python 策略").
  • No content change vs v1.0.18.

v1.0.22 (2026-06-12)

  • 📝 Documentation language tightened — replaced absolute "agent-safe" framing with precise per-category descriptions (read, idempotent write, workflow state transition, outbound webhook). Operators should configure least-privilege tokens and apply their own confirmation gates for state-changing operations.

v1.0.18 (2026-06-12)

  • 📝 Documented operation surface refined — read, idempotent write (subscribe / portfolio entries / token rotation), workflow state transition (redo / hold / resume / reset-priority), and outbound webhook trigger. Operators should evaluate risk per category and scope tokens accordingly.

v1.0.17 (2026-06-12)

  • 📝 Scope & Operator Responsibility section added — token recommendation + per-category side-effect description.
  • No new capabilities. No API surface change.

v1.0.16 (2026-06-12)

  • 🔍 文档元数据补全 — 加 title: + keywords: frontmatter 字段,提升 clawhub vector search 对中文量化 / 股票 / A股 / 港股 / 回测关键词的命中率。功能无变化(v1.0.15 已 ship 模拟交易 + 回测升级)。

v1.0.15 (2026-06-12)

  • 🧾 模拟交易 (Paper Trading) ✨ — MARKET / LIMIT 委托 + scheduler-driven 撮合 + 真实涨跌停 / 停牌信号;账户 UNIQUE on user_name,订单按 client_order_id 幂等。适合策略 12 月 paper trade 验证。
  • 📊 策略回测升级 — 平台已积累 44+ 次持久化回测,可通过 investment.stock.backtest.list 检索历史审计记录;新增 lg_utils.backtest_examples.stock_day.run_stock_day_portfolio_backtest 多股组合回测。
  • 📝 description + 文档清理 — 移除外链 + 简化加密叙事 + 强化产品能力描述,跟最新平台状态对齐。

v1.0.14 (2026-06-10)

  • 📝 Description 重排:把"字段级加密 GA"前置到前 100 字,Hermes / clawhub 列表视图能立刻看到核心差异化。功能本身无变化(v1.0.13 已 ship)。

v1.0.13 (2026-06-05)

两件 headline 更新:

  • 🔒 字段级加密 GA — 持仓量、成本价、交易价格等字段密文化存储,per-account 密钥隔离 (Ship 5, 2026-06-04)。
  • 🎯 1-click subscribe→alert deeplinkmarketplace.item.subscribedashboard-* item 现在返回 clonedDashboardId。Agent 用这个 ID 构造 /dashboards?selectId=<id>&openAlerts=true,把用户从订阅一步带到 alert 配置 modal。详见上方场景 5

📌 环境变更:平台主域名 lg-data.cc → privora.cnLG_AGENT_BASE_URL 默认值已更新。旧域名仍重定向工作,无需立即修改你的环境变量。

v1.0.12 (2026-05-24)

  • 多资产数据 + Python 策略回测能力 + 监控告警 webhook plugin 模板(飞书 / 微信 / 任意 HTTP 端点)。
  • 60+ REST skills 跨 Process / Schedule / Datasource / Dashboard / Marketplace / MetricAlert / Webhook plugins / Investment studio。

**量化回测 / 模拟交易 / 告警仅需此三项能力?**请见窄范围版:privora-cn-quant(无管理操作)。


⭐ 觉得这个 skill 有用?

如果它帮你的 AI Agent 少踩了几个坑、省了几天工程时间——欢迎去 ClawHub 给个 star,让更多散户找到它:

👉 clawhub.ai/guangfuwu/skills/privora-cn-quant (右上角 ⭐)

每个 star 都让 ClawHub 算法把这个 skill 排到更多寻找量化数据后端的开发者面前。3 秒钟的事,对维护者帮助巨大。

也欢迎在 issue 区反馈用得不爽的地方 / 想要的新能力——我会逐条跟进。


附录 · 已安装 agent 的实时 manifest 探测

此段是给已安装 agent 的元数据接入指南,不是 first-time discovery 内容。新读者可跳过。

已安装的 agent 可以直接从 privora.cn 拉最新版本(不经 ClawHub 缓存):

端点用途
GET https://privora.cn/api/public/agent/skill-version轻量级版本探测,返回 {version, name, description, updatedAt}
GET https://privora.cn/api/public/agent/skill-manifest拿 SKILL.md 全文 markdown
GET https://privora.cn/api/public/agent/capabilities结构化 endpoint catalog(scope ↔ HTTP path 映射)

三个端点都是 public,no auth。建议每次会话开始时 hit /skill-version 一次,如果版本号高于本地缓存就重新拉 manifest。

产品主页: https://privora.cn

如何使用「Privora · A股/港股/黄金/基金 多资产 量化分析 · 量化回测 · 模拟盘 · 实时告警 · 风险监控 · Python 策略 · AI Agent」?

  1. 打开小龙虾AI(Web 或 iOS App)
  2. 点击上方「立即使用」按钮,或在对话框中输入任务描述
  3. 小龙虾AI 会自动匹配并调用「Privora · A股/港股/黄金/基金 多资产 量化分析 · 量化回测 · 模拟盘 · 实时告警 · 风险监控 · Python 策略 · AI Agent技能完成任务
  4. 结果即时呈现,支持继续对话优化

相关技能