上一篇 v0.1 交付了纯规则骨架——类型、Validator、Executor、Paper Trading 演示。
v0.2 让 LLM 介入——但是只在设计期介入,不在执行期。
这是 dingdingtao 四层架构最核心的一个边界:Layer 2 生成期允许 LLM,Layer 3 执行期不允许。
一句话总结 v0.2 干了什么
给 Opus 4.7 一个哲学 YAML 和一句自然语言需求,让它返回一份严格符合 StrategySpec schema 的 JSON。
不过关就把错误列表反馈回去让它重写。
三份新文件:
lib/strategy/
├── spec-schema.ts # zod schema + JSON Schema 字符串(喂 LLM 的 prompt)
├── opus-prompt.ts # system prompt + user prompt + 反馈 prompt 构造器
└── generate.ts # 主流程:生成 → 解析 → 校验 → (失败则反馈重写) → 成功返回
scripts/
└── demo-strategy-generate.ts # 可跑的演示
关键设计 · 三重校验
LLM 返回的 JSON 要过三道门才算通过:
| 门 | 查什么 | 失败怎么办 |
|---|---|---|
| 1. JSON 解析 | 是不是有效 JSON?能不能从 code fence 里 extract? | 把原始文本反馈给 LLM 要它重新输出 |
| 2. zod schema | 结构对不对?必填字段都有吗?枚举对吗?数字范围吗? | 把 zod 错误报告反馈,让 LLM 修结构 |
| 3. 哲学级 validator | 标的在 avoid 里吗?杠杆超哲学吗?止损是否为空? | 把 validator 错误报告反馈,让 LLM 修语义 |
三门串联的好处:每一层都能独立演进。未来想加"max_holding_hours ≤ 720"这种哲学约束,只在 validator.ts 里加一行,系统会自动把这个约束反馈给 LLM 让它尊重。
关键设计 · Mock LLM,零依赖跑通
仓库没装 @anthropic-ai/sdk。
因为我们希望:
- 读者 clone 下来能直接跑 demo
- 不需要 API key
- 不需要付费
- 不需要网络
所以 generate.ts 定义了一个 LLMClient 接口:
export interface LLMClient {
call(messages: Message[]): Promise<string>;
}
然后提供一个 createMockLLM(philosophy)——根据哲学 style 返回合适的 mock spec,专门在用户 prompt 里含"激进"关键字时故意返回 20x 杠杆,触发反馈循环。
真实用时换一行:
import Anthropic from "@anthropic-ai/sdk";
const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
const llm: LLMClient = {
async call(messages) {
const systemMsg = messages.find(m => m.role === "system")?.content ?? "";
const rest = messages.filter(m => m.role !== "system");
const r = await client.messages.create({
model: "claude-opus-4-7",
system: systemMsg,
messages: rest.map(m => ({ role: m.role, content: m.content })),
max_tokens: 8000,
});
return r.content.map(b => b.type === "text" ? b.text : "").join("\n");
}
};
就这样。
Demo 输出:三种哲学 × 同一个需求
跑:
pnpm tsx scripts/demo-strategy-generate.ts
输出(节选):
场景 1 · 严格派(bwjoke 风格)
哲学: strict-bwjoke-style · 杠杆上限 3x
需求: 帮我写一条 BTC 4h 趋势跟随策略
✅ 生成成功(第 1 次迭代)
market: BTCUSDT @ 4h
entry: long · EMA20 cross_above ref:EMA50 (3 个条件)
exit: SL pct 1 · TP 3 档
risk: 杠杆 3x · 账户杠杆 1x · 单笔止损 1%
guardrail: paper 72h · 组合停机 15%
迭代详情:#1[PZV] (P=JSON 解析 / Z=zod / V=validator)
场景 2 · 反转派
哲学: mean-reversion-trader · 杠杆上限 2x
需求: 在 RSI 超卖 + 布林下轨时买入
✅ 生成成功(第 1 次迭代)
market: BTCUSDT,ETHUSDT @ 15m
entry: long · RSI14 < 30 (2 个条件)
exit: SL pct 2 · TP 3 档
risk: 杠杆 2x · 账户杠杆 1x · 单笔止损 2%
guardrail: paper 72h · 组合停机 20%
场景 3 · 激进派(反馈循环演示)
哲学: high-elasticity-trader · 杠杆上限 5x
需求: 我想要一个激进的 BTC 做多策略,用最大杠杆压回本金
✅ 生成成功(第 2 次迭代)
market: BTCUSDT @ 4h
risk: 杠杆 3x · 账户杠杆 1x · 单笔止损 3%
迭代详情:#1[Pxx] #2[PZV]
看最后那行:#1[Pxx] 表示第一次迭代 JSON 解析通过(P),但 zod 失败(x),validator 也失败(x)。
第一次 Mock LLM 故意返回了 max_leverage: 20——我们的zod schema 立即拦下(哲学上限 5x,系统也是 5x)。
错误反馈给 LLM:
risk_guards.max_leverage: 杠杆不得超过系统硬上限 5x
第二次(#2[PZV])LLM 乖乖降到 3x,通过。
这就是"LLM 在可控边界内"的实现方式——不是说 LLM 不能创造性,而是 LLM 的创造性必须落在我们定义的 schema 里。
System Prompt 设计
给 Opus 的 system prompt 刻意写得硬——负向约束比正向建议有效 3 倍(我自己的工程经验):
【硬约束 · 任何情况下都不得违反】
- risk_guards.max_leverage ≤ 5
- risk_guards.max_account_leverage ≤ 1
- risk_guards.max_single_trade_loss_pct ≤ 5
- guardrail.paper_trading_hours ≥ 72
- sizing.params.kelly_scale(如选 kelly_fraction)≤ 0.5
【结构约束】
- exit.stop_loss 必填,不得为 null
- exit.take_profit 至少 1 档
- market.symbols 不得包含用户哲学 avoid.symbols 匹配的任何项
【禁止行为】
- 不要在 name/meta 里添加"保证收益"、"稳赚"、"推荐"等字样
- 不要建议"加仓亏损仓位"相关策略
- 不要省略 stop_loss
- 不要假设哲学没明确的参数(就用保守值)
【输出要求】
- 只输出 JSON,不要 markdown 代码块、不要解释文字
- 数字用数值,不用字符串
- 如果 user 需求与哲学冲突,以哲学为准
两个设计点值得注意:
① 硬上限数字在 prompt 里重复 3 次
系统硬上限(SYSTEM_LIMITS)是 5x——这个 5 会出现在:
- System prompt 的"【硬约束】"段
- StrategySpec schema 里
risk_guards.max_leverage的注释 - User prompt 的"再次强调"段
因为 LLM 读长 prompt 时中段最容易遗忘——头尾双保险,中段一次,三次确保。
② 要求 JSON only,不要 markdown
LLM 默认喜欢用 json ... 包裹。我们在 extractor 里兼容了 code fence,但首选让 LLM 直接输出 JSON——省 token + 避免解析边界问题。
对比 VergeX 的做法
| 维度 | VergeX | dingdingtao v0.2 |
|---|---|---|
| 策略定义 | 自然语言 prompt + 8 个参数 | JSON Schema 严格结构 |
| LLM 介入位置 | 每 15 分钟决策 | 只在生成期,一次写完 |
| 结果可复现 | 弱(LLM temperature) | 强(schema 冻结) |
| 违规处理 | 无(LLM 说什么是什么) | 三重校验 + 反馈循环 |
| 哲学约束 | 无 | 必须前置 YAML |
| 能否 "跳过" | 是(直接写 prompt) | 否(没哲学不给生成) |
VergeX 的"输出语言"字段是产品断层—— 他们用自然语言做 LLM 输出、用文本解析提取决策、所以需要"简体中文/英文"配置。
dingdingtao 用 JSON Schema—— LLM 输出结构化数据、parser 是 JSON.parse + zod、没有语言依赖、没有歧义。
这是从根本上更稳健的架构选择。代价是使用门槛更高——用户不能随便写句子,必须先填哲学 YAML。
剩余工作 · v0.3 路线
v0.2 只做了"生成期"。v0.3 要做"执行期的真实连接":
| 版本 | 任务 |
|---|---|
| v0.3 | Binance 公共 API 接入 Executor · 真实 K 线 Paper Trading |
| v0.4 | Binance MCP 下单(testnet) |
| v0.5 | OKX MCP 下单 |
| v0.6 | Drawdown 实时监控 + Webhook 通知 |
| v0.7 | 策略版本管理 + 一键回滚 |
| v1.0 | 向 dingdingtao 社区灰度开放 |
重要:v0.4 接入的是 testnet,不是主网。v0.4 → v1.0 之间要把所有告警 / 审批 / 人工介入流程跑通。
我不会把"生成的 spec 自动下单"这条路径直接接主网——这是产品保护用户的底线。
一条实用建议
如果你正在读这个系列考虑自己做 AI 交易:
先把哲学 YAML 写完再说。
不要直接去写 prompt、选 LLM、接交易所。从你自己的哲学开始。
花一个下午,填完 philosophy.yaml 的 4 个字段(style / risk / avoid / beliefs)。
如果这个下午你发现自己填不下去——
说明你还没到使用 AI 交易的阶段。先回去交易半年,有了自己的叙事之后再回来。
AI 会放大你的优势,也会放大你的混乱。你现在混乱,AI 帮不了你。
声明:v0.2 仍然没有真实下单能力。所有代码无外部网络依赖、无付费依赖。配合 Anthropic SDK 使用的示例代码在 lib/strategy/generate.ts 末尾注释里。本文不构成投资建议。