运行模拟Skill run-simulations

这个技能用于帮助用户在量化交易中进行交易引擎模拟的设置、运行和分析。它支持配置选择、段集合管理、批处理模拟运行,并提供全面的诊断分析,包括准确性验证和利润优化。关键词包括:交易模拟、量化策略、回测系统、数据分析、配置优化、市场条件、准确性检查。

回测系统 0 次安装 0 次浏览 更新于 3/9/2026

name: 运行模拟 description: “在运行交易引擎模拟之前和之后使用。帮助:(1) 设置 - 选择配置,通过段集合选择段,批处理大小(推荐2,000-3,000次运行);(2) 执行 - 使用 --collection 运行批处理模拟;(3) 分析 - 运行后进行全面诊断。触发条件:‘运行模拟’、‘测试配置’、‘批处理模拟’、‘分析模拟结果’、‘测试哪些配置’、‘多少段’、‘模拟设置’。”

运行模拟技能

您正在帮助设置和运行交易引擎模拟,然后执行全面的诊断分析。

当前阶段: 双焦点(验证 + 优化)

系统置信度: ~85% 准确。 模拟系统基本可信,但仍存在差异,找出这些差异至关重要。

优先级 #1: 准确性差异(最重要)

预期与实际行为之间的任何差距都是潜在的错误或误解。这些必须显著突出:

  • 行为是否匹配配置参数?
  • 数据中是否存在逻辑不一致?
  • 结果是否与策略应该产生的一致?

当发现差异时,请显著标记。 即使是小的准确性问题在数千次交易中也会累积。

优先级 #2: 利润优化(主动研究)

拥有可信系统后,我们现在主动研究:

  • 哪些配置表现最佳?
  • 什么市场条件有利于哪些策略?
  • 参数如何影响盈亏?

突出有趣的利润模式 AND 准确性担忧。现在两者都很重要。


重要: 此技能应在运行模拟之前调用,以帮助设置决策(配置选择、通过集合选择段、批处理大小)。

推荐批量大小

当前推荐: 每批2,000-3,000次模拟。

  • 运行速度快(使用tick缓存约30-60秒)
  • 提供统计上有意义的结果
  • 在当前阶段,超过3,000次添加的价值不大
  • 公式: 配置数 × 段数 = 总运行次数(例如,20个配置 × 100个段 = 2,000次运行)

阶段 1: 设置与运行

⚠️ 用户输入优先

如果用户在调用此技能时提供任何参数、指令或上下文,这些绝对优先于以下所有默认设置。

  • 用户说“运行配置 X” → 运行配置 X,忽略默认配置选择
  • 用户说“测试这5个段” → 测试那5个段,忽略默认段查询
  • 用户提出特定问题 → 回答该问题,不要运行完整的默认工作流程
  • 用户提供部分指令 → 用默认填充空白,但尊重用户指定的内容

以下默认行为仅在技能调用时没有任何参数时适用。


默认行为(仅无参数时)

使用段集合选择多样化的配置和段(已弃用旧过滤器):

  1. 查询可用配置: SELECT name, config FROM strategy_configs ORDER BY name
  2. 选择10-20个配置,覆盖不同的指标类型(EMA, RSI, Bollinger, 组合)
  3. 选择或创建一个段集合(v1中仅AND注释):
    bun src/systems/trading-engine/bd-segments.ts list
    bun src/systems/trading-engine/bd-segments.ts create --name=high-activity --annotations=high_activity --limit=100
    bun src/systems/trading-engine/bd-segments.ts preview --name=high-activity
    
  4. 运行批处理模拟:
    bun src/systems/trading-engine/batch-runner.ts --config-pattern="<pattern>" --collection=high-activity --save
    

批处理运行器 CLI 参考

位置: src/systems/trading-engine/batch-runner.ts

用法:

bun src/systems/trading-engine/batch-runner.ts [options]

选项:

选项 短选项 描述
--config=ID -c 策略配置 ID(可指定多个)
--all-configs -a 运行所有可用策略配置
--config-pattern=PAT -p 运行名称匹配模式的配置(SQL LIKE)
--strategy-type=TYPE 按策略类型过滤配置(波动性、套利、网格)
`–collection=NAME ID` -C
--show-selection 预览集合选择并退出
--capital=CENTS 初始资本(美分)(默认: 10000 = $100)
--workers=N -w 并行工作线程数(默认: 8)
--save -S 将结果保存到数据库
--dry-run 显示将要运行的内容而不执行
--warmup-candles=N 在段之前预加载 N 个蜡烛图用于指标预热(默认: 50)
--db-pool-max=N 主批处理过程的数据库池大小(默认: 10)
--db-pool-max-worker=N 工作线程的数据库池大小(默认: 3)

示例:

# 在一个段集合上运行一个配置
bun src/systems/trading-engine/bd-segments.ts create --name=swings100 --annotations=high_activity
bun src/systems/trading-engine/batch-runner.ts --config=abc123 --collection=swings100 --save

# 在一个集合上运行所有配置
bun src/systems/trading-engine/batch-runner.ts --all-configs --collection=swings50 --save

# 在特定段 ID 上运行所有配置(快照集合)
bun src/systems/trading-engine/bd-segments.ts create --name=segment-set --segment-ids=949,950,951,952 --mode=snapshot
bun src/systems/trading-engine/batch-runner.ts --all-configs --collection=segment-set --save

# 在 high_activity 段上运行匹配 "RSI-*" 的配置
bun src/systems/trading-engine/bd-segments.ts create --name=rsi-activity --annotations=high_activity
bun src/systems/trading-engine/batch-runner.ts --config-pattern="RSI-%" --collection=rsi-activity --save

# 干运行以查看将要执行的内容
bun src/systems/trading-engine/batch-runner.ts --all-configs --collection=swings100 --dry-run

关键 CLI 标志(快速参考)

  • --collection=NAME|ID-C: 必需的段集合选择器
  • --show-selection: 预览解析后的段列表并退出
  • --config-pattern="TEST-%"-p: 按名称模式过滤配置
  • --strategy-type=grid: 按策略类型过滤配置

阶段 2: 全面分析(始终运行)

模拟完成后,运行所有以下诊断查询。以表格形式呈现结果。

2.1 核心统计

SELECT
  sc.name,
  COUNT(*) as runs,
  ROUND(AVG(trade_count)::numeric, 1) as avg_trades,
  ROUND(STDDEV(trade_count)::numeric, 1) as stddev_trades,
  MIN(trade_count) as min_trades,
  MAX(trade_count) as max_trades
FROM (
  SELECT r.id, sc.name, COUNT(t.id) as trade_count
  FROM sim_runs r
  JOIN strategy_configs sc ON r.config_id = sc.id
  LEFT JOIN sim_trades t ON t.run_id = r.id
  WHERE r.created_at > NOW() - INTERVAL '1 hour'
  GROUP BY r.id, sc.name
) sub
JOIN strategy_configs sc ON sc.name = sub.name
GROUP BY sc.name
ORDER BY avg_trades;

2.2 入场信号频率(红旗: 中位数 < 60秒可疑)

WITH entries AS (
  SELECT
    t.run_id, sc.name as config, t.entry_at,
    LAG(t.entry_at) OVER (PARTITION BY t.run_id ORDER BY t.entry_at) as prev_entry
  FROM sim_trades t
  JOIN sim_runs r ON t.run_id = r.id
  JOIN strategy_configs sc ON r.config_id = sc.id
  WHERE r.created_at > NOW() - INTERVAL '1 hour'
)
SELECT
  config,
  COUNT(*) as entry_gaps,
  ROUND(AVG((entry_at - prev_entry)/1000.0)::numeric, 1) as avg_gap_sec,
  ROUND(PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY (entry_at - prev_entry)/1000.0)::numeric, 1) as median_gap_sec,
  COUNT(*) FILTER (WHERE entry_at - prev_entry < 1000) as gaps_under_1s,
  COUNT(*) FILTER (WHERE entry_at - prev_entry < 5000) as gaps_under_5s,
  COUNT(*) FILTER (WHERE entry_at - prev_entry < 60000) as gaps_under_1min
FROM entries
WHERE prev_entry IS NOT NULL
GROUP BY config
ORDER BY median_gap_sec;

解释: 中位数间隙 < 60秒表明过度交易。健康的配置应该有分钟级的间隙(典型20-30分钟)。间隙 < 1秒是主要红旗 - 表明指标不断触发。

2.3 仓位重叠

-- 平均有多少并发仓位?
WITH trade_events AS (
  SELECT t.run_id, sc.name as config, t.entry_at as ts, 1 as delta
  FROM sim_trades t
  JOIN sim_runs r ON t.run_id = r.id
  JOIN strategy_configs sc ON r.config_id = sc.id
  WHERE r.created_at > NOW() - INTERVAL '1 hour'
  UNION ALL
  SELECT t.run_id, sc.name as config, t.exit_at as ts, -1 as delta
  FROM sim_trades t
  JOIN sim_runs r ON t.run_id = r.id
  JOIN strategy_configs sc ON r.config_id = sc.id
  WHERE r.created_at > NOW() - INTERVAL '1 hour' AND t.exit_at IS NOT NULL
),
running_pos AS (
  SELECT run_id, config, ts,
    SUM(delta) OVER (PARTITION BY run_id ORDER BY ts, delta DESC) as open_positions
  FROM trade_events
)
SELECT
  config,
  MAX(open_positions) as max_concurrent,
  ROUND(AVG(open_positions)::numeric, 2) as avg_concurrent,
  PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY open_positions) as median_concurrent
FROM running_pos
GROUP BY config
ORDER BY avg_concurrent DESC;

2.4 持有期限分布

SELECT
  sc.name as config,
  CASE
    WHEN t.holding_duration_ms < 30000 THEN '<30s'
    WHEN t.holding_duration_ms < 60000 THEN '30-60s'
    WHEN t.holding_duration_ms < 120000 THEN '1-2min'
    WHEN t.holding_duration_ms < 180000 THEN '2-3min'
    WHEN t.holding_duration_ms < 240000 THEN '3-4min'
    WHEN t.holding_duration_ms < 300000 THEN '4-5min'
    ELSE '5min+'
  END as hold_bucket,
  COUNT(*) as trades,
  ROUND(AVG(t.net_pnl_cents)::numeric, 1) as avg_pnl,
  ROUND(COUNT(*)::numeric / SUM(COUNT(*)) OVER (PARTITION BY sc.name) * 100, 1) as pct
FROM sim_trades t
JOIN sim_runs r ON t.run_id = r.id
JOIN strategy_configs sc ON r.config_id = sc.id
WHERE r.created_at > NOW() - INTERVAL '1 hour' AND t.holding_duration_ms IS NOT NULL
GROUP BY sc.name, hold_bucket
ORDER BY sc.name, MIN(t.holding_duration_ms);

2.5 出场原因分析

SELECT
  sc.name as config,
  t.exit_reason,
  COUNT(*) as trades,
  ROUND(AVG(t.net_pnl_cents)::numeric, 1) as avg_pnl,
  ROUND(COUNT(*)::numeric / SUM(COUNT(*)) OVER (PARTITION BY sc.name) * 100, 1) as pct
FROM sim_trades t
JOIN sim_runs r ON t.run_id = r.id
JOIN strategy_configs sc ON r.config_id = sc.id
WHERE r.created_at > NOW() - INTERVAL '1 hour'
GROUP BY sc.name, t.exit_reason
ORDER BY sc.name, trades DESC;

2.6 YES vs NO 边分析(重要 - 检查不对称性)

SELECT
  sc.name as config,
  t.side,
  COUNT(*) as trades,
  ROUND(AVG(t.net_pnl_cents)::numeric, 1) as avg_pnl,
  ROUND(AVG(CASE WHEN t.net_pnl_cents > 0 THEN 1 ELSE 0 END)::numeric * 100, 1) as win_pct,
  ROUND(AVG(t.entry_price)::numeric, 1) as avg_entry_price,
  ROUND(AVG(t.exit_price)::numeric, 1) as avg_exit_price,
  ROUND(AVG(t.quantity)::numeric, 1) as avg_qty
FROM sim_trades t
JOIN sim_runs r ON t.run_id = r.id
JOIN strategy_configs sc ON r.config_id = sc.id
WHERE r.created_at > NOW() - INTERVAL '1 hour'
GROUP BY sc.name, t.side
ORDER BY sc.name, t.side;

解释: YES和NO之间显著的不对称性表明价格转换或边选择逻辑存在错误。除非策略明确偏好某一方,否则双方应有相似特征。

2.7 仓位大小验证

-- 检查填充是否匹配预期大小并与订单簿对齐
SELECT
  sc.name as config,
  t.quantity as fill_qty,
  COUNT(*) as occurrences,
  ROUND(AVG(t.entry_price)::numeric, 1) as avg_entry_price,
  ROUND(AVG(t.net_pnl_cents)::numeric, 1) as avg_pnl
FROM sim_trades t
JOIN sim_runs r ON t.run_id = r.id
JOIN strategy_configs sc ON r.config_id = sc.id
WHERE r.created_at > NOW() - INTERVAL '1 hour'
GROUP BY sc.name, t.quantity
ORDER BY sc.name, t.quantity;

检查: 数量是否匹配配置的maxTradeSize?是否发生部分填充?与maxPositionPerMarket比较。

2.8 交易序列表现

WITH numbered_trades AS (
  SELECT t.*, sc.name as config,
    ROW_NUMBER() OVER (PARTITION BY t.run_id ORDER BY t.entry_at) as trade_num
  FROM sim_trades t
  JOIN sim_runs r ON t.run_id = r.id
  JOIN strategy_configs sc ON r.config_id = sc.id
  WHERE r.created_at > NOW() - INTERVAL '1 hour'
)
SELECT
  config,
  CASE
    WHEN trade_num = 1 THEN '1st'
    WHEN trade_num BETWEEN 2 AND 5 THEN '2-5th'
    WHEN trade_num BETWEEN 6 AND 10 THEN '6-10th'
    WHEN trade_num BETWEEN 11 AND 20 THEN '11-20th'
    ELSE '21st+'
  END as position,
  COUNT(*) as trades,
  ROUND(AVG(net_pnl_cents)::numeric, 1) as avg_pnl,
  ROUND(AVG(CASE WHEN net_pnl_cents > 0 THEN 1 ELSE 0 END)::numeric * 100, 1) as win_pct
FROM numbered_trades
GROUP BY config, position
ORDER BY config, MIN(trade_num);

2.9 市场段分析

SELECT
  ms.definition_name,
  COUNT(DISTINCT r.id) as runs,
  COUNT(t.id) as trades,
  ROUND(AVG(r.total_pnl_cents)::numeric, 1) as avg_run_pnl,
  ROUND(AVG((ms.metrics->>'swing_count')::int)::numeric, 1) as avg_swings,
  ROUND(AVG((ms.metrics->>'reversion_rate')::float)::numeric, 2) as avg_reversion_rate
FROM sim_runs r
JOIN market_segments ms ON ms.source_meta->>'legacy_test_case_id' = r.test_case_id::text
LEFT JOIN sim_trades t ON t.run_id = r.id
JOIN strategy_configs sc ON r.config_id = sc.id
WHERE r.created_at > NOW() - INTERVAL '1 hour'
GROUP BY ms.definition_name
ORDER BY runs DESC;

2.10 运行健康检查

SELECT status, COUNT(*) as count
FROM sim_runs
WHERE created_at > NOW() - INTERVAL '1 hour'
GROUP BY status;

红旗: 批处理完成后任何"running"状态 = 崩溃的模拟。

阶段 3: 日志分析(始终运行)

检查最近的模拟日志以查找异常:

# 查找最近的模拟日志
ls -lt logs/sim/ | head -5

# 从带内容的最新日志中采样条目
LOG=$(ls -ltS logs/sim/*.log | head -1 | awk '{print $NF}')
echo "分析: $LOG ($(du -h "$LOG" | cut -f1))"

# 按类别检查日志量(性能红旗: >1M 指标日志)
echo "按类别日志量:"
rg '\"category\"' "$LOG" | jq -r '.category' 2>/dev/null | sort | uniq -c | sort -rn

# 从时间戳检查运行持续时间
echo "运行持续时间:"
FIRST_TS=$(head -1 "$LOG" | jq -r '.time' 2>/dev/null)
LAST_TS=$(tail -1 "$LOG" | jq -r '.time' 2>/dev/null)
if [ "$FIRST_TS" != "null" ] && [ "$LAST_TS" != "null" ]; then
  echo "持续时间: $(( (LAST_TS - FIRST_TS) / 1000 )) 秒"
fi

# 统计出场原因
echo "出场原因分布:"
rg '\"reason\"' "$LOG" | jq -r '.reason' 2>/dev/null | sort | uniq -c | sort -rn | head -20

# 检查错误
echo "日志中的错误:"
rg -i '\"level\":50' "$LOG" | head -10

# 检查警告
echo "日志中的警告:"
rg -i '\"level\":40' "$LOG" | head -10

寻找:

  • 异常的出场原因(不是profit_target, time_limit, collapse, end_of_data)
  • 错误级别(50)条目
  • 暗示错误的模式(重复相同条目,缺少字段)
  • 性能: 日志文件 > 500MB = 过度日志记录
  • 性能: >1M 指标类别日志 = 调试日志瓶颈

阶段 3.5: 性能分析

检查模拟吞吐量:

-- 最近批处理中的每秒运行次数
SELECT
  DATE_TRUNC('minute', created_at) as minute,
  COUNT(*) as runs,
  ROUND(COUNT(*)::numeric / 60, 1) as runs_per_sec
FROM sim_runs
WHERE created_at > NOW() - INTERVAL '1 hour'
GROUP BY minute
ORDER BY minute DESC
LIMIT 10;

预期吞吐量: 使用tick缓存时为20-40次运行/秒。如果 < 10次运行/秒,调查:

  • 是否使用tick缓存?(检查批处理脚本输出中的"Pre-loaded")
  • 是否启用指标调试日志记录?(检查日志大小)
  • 工作线程是否I/O受限?(检查工作线程数与CPU核心数)

阶段 4: 分析总结

双焦点: 突出准确性担忧和利润见解。

首先: 准确性检查(优先级 #1)

标记预期与实际行为之间的任何差异:

  1. 配置行为是否匹配参数?
  2. 是否存在无法解释的模式或异常?
  3. 指标是否有逻辑意义?

准确性问题优先。 1%的准确性错误可能使利润分析无效。

然后: 利润见解(优先级 #2)

准确性验证后,分析:

  1. 哪些配置最有利可图?
  2. 跨市场条件出现什么模式?
  3. 是否存在优化机会?

验证问题以回答

对于每个发现,问:“这符合我们预期吗?如果不,为什么?”

观察 预期行为 实际 匹配? 如果不,调查
入场间隙 基于指标参数 ? ? 指标计算?蜡烛聚合?
出场原因 平衡组合 ? ? 出场逻辑?阈值错误?
YES vs NO 表现 相似(市场对称) ? ? 价格转换?边选择?
仓位大小 匹配配置 maxTradeSize ? ? 填充逻辑?深度解析?
持有期限 跨桶分布 ? ? 出场条件触发?
交易序列 后期交易 ≈ 早期 ? ? 状态累积错误?
Bollinger/其他指标 应有时触发 ? ? 指标实现?

关键验证检查

  1. 指标触发频率
    • 入场之间的间隙是否与指标周期暗示的一致?
    • EMA(20) 配60秒蜡烛 = 需要20分钟数据。我们看到入场比这快吗?
  2. 价格/边一致性
    • YES和NO应该是镜像。主要不对称 = 价格处理可能错误。
    • 检查: 我们是否为每边正确转换bid/ask?
  3. 配置参数实际应用
    • stopLossCents 是否真的在该价格触发?
    • profitTargetCents 是否真的触发?
    • 是否检查 maxSpreadToEnter?
  4. 数据流完整性
    • ticks 是否按顺序到达策略?
    • 深度数据是否被正确解析?
    • 填充是否被准确记录?

阶段 5: 发现与下一步

基于分析,组织发现:

准确性担忧(首先突出)

  1. 可能错误 - 暗示代码错误的差异
    • “X 应该是 Y,但我们看到 Z” → 调查代码路径
  2. 不清楚行为 - 我们尚未理解的事情
    • “我们预期 X,得到 Y,不确定原因” → 需要更深调查
  3. 确认工作正常 - 符合预期的事情
    • “这行为符合预期” → 增加置信度

利润见解(其次突出)

  1. 顶级表现者 - 哪些配置显示最佳结果?
  2. 市场模式 - 什么条件有利于哪些策略?
  3. 优化机会 - 参数调整建议

下一步

  • 准确性担忧的调查优先级
  • 验证利润假设的实验
  • 要测试的配置变体

记住: 准确性优先。显著突出差异,然后提供利润优化见解。两者都重要,但小的准确性错误可能使所有利润分析无效。