运行模拟技能Skill run-simulations

这个技能用于设置、运行和分析股票量化交易的模拟回测,包括配置选择、批次模拟执行和综合诊断分析。关键词:量化交易、模拟回测、算法交易、数据分析、股票评估、回测系统。

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

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

运行模拟技能

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

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

系统置信度:约 85% 准确。 模拟系统大部分可信,但差异仍然存在且至关重要。

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

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

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

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

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

在可信的系统下,我们现在主动研究:

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

突出显示有趣的利润模式和准确性问题。两者现在都很重要。


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

推荐批次大小

当前推荐:每批次 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、布林带、组合)
  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 主批次进程的 DB 池大小(默认:10)
--db-pool-max-worker=N 工作器进程的 DB 池大小(默认: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 与 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 与 NO 性能 相似(市场对称) ? ? 价格转换?边选择?
仓位大小 匹配配置 maxTradeSize ? ? 填充逻辑?深度解析?
持仓持续时间 分布在各桶中 ? ? 出场条件触发?
交易序列 后期交易 ≈ 早期 ? ? 状态累积错误?
布林带/其他指标 应有时触发 ? ? 指标实现?

关键验证检查

  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. 优化机会 - 参数调整建议

下一步

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

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