run-simulations

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Run Simulations Skill

交易模拟运行Skill

You are helping set up and run trading engine simulations, then performing comprehensive diagnostic analysis.
你将协助完成交易引擎模拟的配置与运行,并执行全面的诊断分析。

Current Phase: DUAL-FOCUS (Validation + Optimization)

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

System confidence: ~85% accurate. The simulation system is largely trustworthy, but discrepancies still exist and are critical to find.
系统置信度:约85%准确。模拟系统整体可靠,但仍存在差异,且这些差异的排查至关重要。

Priority #1: ACCURACY DISCREPANCIES (Most Important)

优先级1:准确性差异(最关键)

Any gap between expected vs actual behavior is a potential bug or misunderstanding. These must be surfaced prominently:
  • Does behavior match config parameters?
  • Are there logical inconsistencies in the data?
  • Do results align with what the strategy SHOULD produce?
When you spot a discrepancy, flag it prominently. Even small accuracy issues compound over thousands of trades.
预期行为与实际行为之间的任何差距都可能是潜在bug或理解偏差。必须重点突出这些问题:
  • 行为是否与配置参数匹配?
  • 数据中是否存在逻辑不一致?
  • 结果是否符合策略应产生的预期?
发现差异时,请显著标记。即使是微小的准确性问题,在数千次交易中也会被放大。

Priority #2: PROFIT OPTIMIZATION (Active Research)

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

With a trustworthy system, we're now actively researching:
  • Which configs perform best?
  • What market conditions favor which strategies?
  • How do parameters affect PnL?
Surface interesting profit patterns AND accuracy concerns. Both matter now.

IMPORTANT: This skill should be invoked BEFORE running simulations to help with setup decisions (config selection, segment selection via collections, batch sizing).
在系统可信的基础上,我们正在积极研究:
  • 哪些配置表现最佳?
  • 哪些市场条件更适合特定策略?
  • 参数如何影响盈亏(PnL)?
既要呈现有趣的利润模式,也要关注准确性问题。两者当前都很重要。

重要提示:此Skill应在模拟运行前调用,以协助完成配置决策(配置选择、通过集合选择片段、批量规模设置)。

Recommended Batch Size

推荐批量运行规模

Current recommendation: 2,000-3,000 simulations per batch.
  • Runs quickly (~30-60 seconds with tick caching)
  • Provides statistically meaningful results
  • More than 3,000 doesn't add much value at current stage
  • Formula: configs × segments = total runs (e.g., 20 configs × 100 segments = 2,000 runs)
当前推荐:每批2000-3000次模拟
  • 运行速度快(启用tick缓存后约30-60秒)
  • 提供具有统计意义的结果
  • 当前阶段超过3000次运行不会带来太多额外价值
  • 计算公式:配置数量 × 片段数量 = 总运行次数(例如:20个配置 × 100个片段 = 2000次运行)

Phase 1: Setup & Run

阶段1:配置与运行

⚠️ USER INPUT TAKES PRIORITY

⚠️ 用户输入优先

If the user provides ANY arguments, instructions, or context when invoking this skill, those take absolute priority over all defaults below.
  • User says "run config X" → run config X, ignore default config selection
  • User says "test these 5 segments" → test those 5 segments, ignore default segment query
  • User asks a specific question → answer that question, don't run the full default workflow
  • User provides partial instructions → fill in gaps with defaults, but honor what they specified
The defaults below are ONLY for when the skill is invoked with no arguments at all.

如果用户在调用此Skill时提供了任何参数、指令或上下文,这些内容将绝对优先于以下所有默认设置
  • 用户说"run config X" → 运行配置X,忽略默认配置选择
  • 用户说"test these 5 segments" → 测试这5个片段,忽略默认片段查询
  • 用户提出具体问题 → 回答该问题,不执行完整的默认工作流
  • 用户提供部分指令 → 使用默认值填补空白,但优先遵循用户指定的内容
以下默认设置仅适用于调用Skill时未提供任何参数的情况

Default Behavior (no args only)

默认行为(仅无参数时)

Pick a diverse spread of configs and segments using segment collections (legacy filters are deprecated):
  1. Query available configs:
    SELECT name, config FROM strategy_configs ORDER BY name
  2. Select 10-20 configs covering different indicator types (EMA, RSI, Bollinger, combined)
  3. Choose or create a segment collection (AND-only annotations in v1):
    bash
    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. Run batch simulation:
    bash
    bun src/systems/trading-engine/batch-runner.ts --config-pattern="<pattern>" --collection=high-activity --save
使用片段集合(旧版过滤器已弃用)选择多样化的配置和片段:
  1. 查询可用配置:
    SELECT name, config FROM strategy_configs ORDER BY name
  2. 选择10-20个覆盖不同指标类型的配置(EMA、RSI、布林带、组合指标)
  3. 选择或创建片段集合(v1版本仅支持AND类型的注解):
    bash
    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. 运行批量模拟:
    bash
    bun src/systems/trading-engine/batch-runner.ts --config-pattern="<pattern>" --collection=high-activity --save

Batch Runner CLI Reference

Batch Runner CLI参考

Location:
src/systems/trading-engine/batch-runner.ts
Usage:
bash
bun src/systems/trading-engine/batch-runner.ts [options]
Options:
OptionShortDescription
--config=ID
-c
Strategy config ID (can specify multiple)
--all-configs
-a
Run all available strategy configs
--config-pattern=PAT
-p
Run configs matching name pattern (SQL LIKE)
--strategy-type=TYPE
Filter configs by strategy type (volatility, arb, grid)
`--collection=NAMEID`
-C
--show-selection
Preview collection selection and exit
--capital=CENTS
Initial capital in cents (default: 10000 = $100)
--workers=N
-w
Number of parallel workers (default: 8)
--save
-S
Save results to database
--dry-run
Show what would run without executing
--warmup-candles=N
Pre-load N candles before segment for indicator warmup (default: 50)
--db-pool-max=N
DB pool size for main batch process (default: 10)
--db-pool-max-worker=N
DB pool size for worker processes (default: 3)
Examples:
bash
undefined
位置:
src/systems/trading-engine/batch-runner.ts
用法:
bash
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=NAMEID`
-C
--show-selection
预览集合选择结果后退出
--capital=CENTS
初始资金(单位:分,默认值:10000 = 100美元)
--workers=N
-w
并行工作进程数量(默认值:8)
--save
-S
将结果保存到数据库
--dry-run
显示将要执行的操作但不实际运行
--warmup-candles=N
为指标预热,在片段前预加载N根K线(默认值:50)
--db-pool-max=N
主批量进程的数据库连接池大小(默认值:10)
--db-pool-max-worker=N
工作进程的数据库连接池大小(默认值:3)
示例:
bash
undefined

Run one config on a segment collection

在片段集合上运行单个配置

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/bd-segments.ts create --name=swings100 --annotations=high_activity bun src/systems/trading-engine/batch-runner.ts --config=abc123 --collection=swings100 --save

Run all configs on a collection

在集合上运行所有配置

bun src/systems/trading-engine/batch-runner.ts --all-configs --collection=swings50 --save
bun src/systems/trading-engine/batch-runner.ts --all-configs --collection=swings50 --save

Run all configs on specific segment IDs (snapshot collection)

在特定片段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
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

Run configs matching "RSI-*" on high_activity segments

在高活跃度片段上运行名称匹配"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/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

Dry run to see what would execute

空运行以查看将要执行的操作

bun src/systems/trading-engine/batch-runner.ts --all-configs --collection=swings100 --dry-run
undefined
bun src/systems/trading-engine/batch-runner.ts --all-configs --collection=swings100 --dry-run
undefined

Key CLI Flags (Quick Reference)

关键CLI标记(快速参考)

  • --collection=NAME|ID
    or
    -C
    : REQUIRED segment collection selector
  • --show-selection
    : Preview resolved segment list and exit
  • --config-pattern="TEST-%"
    or
    -p
    : Filter configs by name pattern
  • --strategy-type=grid
    : Filter configs by strategy type
  • --collection=NAME|ID
    -C
    :必填的片段集合选择器
  • --show-selection
    :预览解析后的片段列表后退出
  • --config-pattern="TEST-%"
    -p
    :按名称模式过滤配置
  • --strategy-type=grid
    :按策略类型过滤配置

Phase 2: Comprehensive Analysis (ALWAYS RUN)

阶段2:全面分析(必须执行)

After simulations complete, run ALL of the following diagnostic queries. Present results in tables.
模拟完成后,运行以下所有诊断查询。以表格形式呈现结果。

2.1 Core Stats

2.1 核心统计数据

sql
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;
sql
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 Entry Signal Frequency (RED FLAG: median < 60s is suspicious)

2.2 入场信号频率(红色预警:中位数<60秒则可疑)

sql
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;
Interpretation: Median gaps < 60s suggest overtrading. Healthy configs should have gaps in minutes (20-30min typical). Gaps < 1s are a MAJOR RED FLAG - indicates indicator firing constantly.
sql
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 Position Overlap

2.3 仓位重叠

sql
-- How many concurrent positions on average?
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;
sql
-- 平均并发仓位数量?
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 Holding Duration Distribution

2.4 持仓时长分布

sql
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);
sql
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 Exit Reason Analysis

2.5 离场原因分析

sql
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;
sql
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 Side Analysis (IMPORTANT - check for asymmetries)

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

sql
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;
Interpretation: Significant asymmetry between YES/NO performance suggests bugs in price conversion or side selection logic. Both sides should have similar characteristics unless the strategy explicitly favors one.
sql
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方向的表现存在显著不对称性,表明价格转换或方向选择逻辑存在bug。除非策略明确偏向某一方向,否则两者应具有相似特征。

2.7 Position Sizing Verification

2.7 仓位规模验证

sql
-- Check if fills match expected sizes and align with order book
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;
Check: Does quantity match config's maxTradeSize? Are partial fills happening? Compare with maxPositionPerMarket.
sql
-- 检查成交规模是否符合预期,且与订单簿一致
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 Trade Sequence Performance

2.8 交易序列表现

sql
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);
sql
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 Market Segment Analysis

2.9 市场片段分析

sql
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;
sql
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 Run Health Check

2.10 运行健康检查

sql
SELECT status, COUNT(*) as count
FROM sim_runs
WHERE created_at > NOW() - INTERVAL '1 hour'
GROUP BY status;
RED FLAG: Any "running" status after batch completes = crashed simulations.
sql
SELECT status, COUNT(*) as count
FROM sim_runs
WHERE created_at > NOW() - INTERVAL '1 hour'
GROUP BY status;
红色预警:批量运行完成后仍存在"running"状态的记录,说明模拟崩溃。

Phase 3: Log Analysis (ALWAYS RUN)

阶段3:日志分析(必须执行)

Check recent simulation logs for anomalies:
bash
undefined
检查近期模拟日志中的异常:
bash
undefined

Find most recent sim log

查找最新的模拟日志

ls -lt logs/sim/ | head -5
ls -lt logs/sim/ | head -5

Sample entries from most recent log with content

从最新日志中提取样本内容

LOG=$(ls -ltS logs/sim/*.log | head -1 | awk '{print $NF}') echo "Analyzing: $LOG ($(du -h "$LOG" | cut -f1))"
LOG=$(ls -ltS logs/sim/*.log | head -1 | awk '{print $NF}') echo "Analyzing: $LOG ($(du -h "$LOG" | cut -f1))"

Check log volume by category (PERFORMANCE RED FLAG: >1M indicator logs)

按类别检查日志量(性能红色预警:指标日志超过100万条)

echo "Log volume by category:" rg '"category"' "$LOG" | jq -r '.category' 2>/dev/null | sort | uniq -c | sort -rn
echo "Log volume by category:" rg '"category"' "$LOG" | jq -r '.category' 2>/dev/null | sort | uniq -c | sort -rn

Check run duration from timestamps

根据时间戳检查运行时长

echo "Run duration:" 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 "Duration: $(( (LAST_TS - FIRST_TS) / 1000 )) seconds" fi
echo "Run duration:" 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 "Duration: $(( (LAST_TS - FIRST_TS) / 1000 )) seconds" fi

Count exit reasons

统计离场原因分布

echo "Exit reason distribution:" rg '"reason"' "$LOG" | jq -r '.reason' 2>/dev/null | sort | uniq -c | sort -rn | head -20
echo "Exit reason distribution:" rg '"reason"' "$LOG" | jq -r '.reason' 2>/dev/null | sort | uniq -c | sort -rn | head -20

Check for errors

检查错误

echo "Errors in log:" rg -i '"level":50' "$LOG" | head -10
echo "Errors in log:" rg -i '"level":50' "$LOG" | head -10

Check for warnings

检查警告

echo "Warnings in log:" rg -i '"level":40' "$LOG" | head -10

**Look for**:
- Unusual exit reasons (not profit_target, time_limit, collapse, end_of_data)
- Error level (50) entries
- Patterns suggesting bugs (repeated identical entries, missing fields)
- **PERFORMANCE**: Log file > 500MB = excessive logging
- **PERFORMANCE**: >1M indicator category logs = debug logging bottleneck
echo "Warnings in log:" rg -i '"level":40' "$LOG" | head -10

**需要关注的内容**:
- 异常离场原因(非profit_target、time_limit、collapse、end_of_data)
- 错误级别(50)的日志条目
- 表明bug的模式(重复的相同条目、缺失字段)
- **性能问题**:日志文件>500MB = 日志记录过多
- **性能问题**:指标类别日志>100万条 = 调试日志成为瓶颈

Phase 3.5: Performance Analysis

阶段3.5:性能分析

Check simulation throughput:
sql
-- Runs per second over recent batch
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;
Expected throughput: 20-40 runs/sec with tick caching. If < 10 runs/sec, investigate:
  • Is tick cache being used? (check for "Pre-loaded" in batch script output)
  • Is indicator debug logging enabled? (check log size)
  • Are workers I/O bound? (check worker count vs CPU cores)
检查模拟吞吐量:
sql
-- 近期批量运行的每秒运行次数
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核心数的匹配情况)

Phase 4: Analysis Summary

阶段4:分析总结

DUAL FOCUS: Surface both accuracy concerns AND profit insights.
双重聚焦:既要突出准确性问题,也要呈现利润洞察。

First: Accuracy Check (Priority #1)

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

Flag any discrepancies between expected vs actual behavior:
  1. Does config behavior match parameters?
  2. Are there unexplained patterns or anomalies?
  3. Do metrics make logical sense?
Accuracy issues take precedence. A 1% accuracy bug can invalidate profit analysis.
标记预期行为与实际行为之间的任何差异:
  1. 配置行为是否与参数匹配?
  2. 是否存在无法解释的模式或异常?
  3. 指标是否符合逻辑?
准确性问题优先。1%的准确性bug可能会使利润分析完全失效。

Then: Profit Insights (Priority #2)

其次:利润洞察(优先级2)

With accuracy validated, analyze:
  1. Which configs are most profitable?
  2. What patterns emerge across market conditions?
  3. Are there optimization opportunities?
在准确性验证通过后,分析:
  1. 哪些配置盈利能力最强?
  2. 不同市场条件下呈现出哪些模式?
  3. 是否存在优化机会?

Validation Questions to Answer

需要回答的验证问题

For each finding, ask: "Does this match what we'd expect? If not, why?"
ObservationExpected BehaviorActualMatch?If No, Investigate
Entry gapsBased on indicator params??Indicator calculation? Candle aggregation?
Exit reasonsBalanced mix??Exit logic? Threshold bugs?
YES vs NO performanceSimilar (market is symmetric)??Price conversion? Side selection?
Position sizesMatch config maxTradeSize??Fill logic? Depth parsing?
Holding durationsSpread across buckets??Exit conditions firing?
Trade sequenceLater trades ≈ earlier??State accumulation bugs?
Bollinger/other indicatorsShould trigger sometimes??Indicator implementation?
对于每个发现,思考:"这符合我们的预期吗?如果不符合,原因是什么?"
观察结果预期行为实际情况是否匹配?若不匹配,需排查
入场间隔基于指标参数??指标计算?K线聚合?
离场原因均衡分布??离场逻辑?阈值bug?
YES vs NO表现相似(市场对称)??价格转换?方向选择?
仓位规模与配置的maxTradeSize匹配??成交逻辑?深度解析?
持仓时长分布在多个区间??离场条件触发?
交易序列后期交易与前期近似??状态累积bug?
布林带/其他指标应偶尔触发??指标实现?

Key Validation Checks

关键验证检查

  1. Indicator Firing Frequency
    • Does the gap between entries match what the indicator period implies?
    • EMA(20) with 60s candles = 20 min of data needed. Are we seeing entries faster than that makes sense?
  2. Price/Side Consistency
    • YES and NO should be mirror images. Major asymmetry = likely bug in price handling.
    • Check: Are we converting bid/ask correctly for each side?
  3. Config Parameters Actually Applied
    • Is stopLossCents actually triggering at that price?
    • Is profitTargetCents actually triggering?
    • Is maxSpreadToEnter being checked?
  4. Data Flow Integrity
    • Are ticks reaching the strategy in order?
    • Is depth data being parsed correctly?
    • Are fills being recorded accurately?
  1. 指标触发频率
    • 入场间隔是否与指标周期的预期一致?
    • 20周期EMA(60秒K线)需要20分钟的数据。是否出现了比预期更快的入场信号?
  2. 价格/方向一致性
    • YES和NO方向应互为镜像。显著不对称性表明价格处理存在bug。
    • 检查:是否为每个方向正确转换了买卖价?
  3. 配置参数是否实际生效
    • stopLossCents是否确实在指定价格触发?
    • profitTargetCents是否确实触发?
    • 是否检查了maxSpreadToEnter?
  4. 数据流完整性
    • 行情tick是否按顺序传递给策略?
    • 深度数据是否解析正确?
    • 成交记录是否准确?

Phase 5: Findings & Next Steps

阶段5:发现与下一步计划

Based on analysis, organize findings into:
基于分析结果,将发现整理为以下类别:

Accuracy Concerns (Surface First)

准确性问题(优先呈现)

  1. Likely Bugs - Discrepancies that suggest code errors
    • "X should be Y but we see Z" → investigate code path
  2. Unclear Behavior - Things we don't understand yet
    • "We expected X, got Y, not sure why" → needs deeper investigation
  3. Confirmed Working - Things that match expectations
    • "This behaves as expected" → increased confidence
  1. 疑似Bug - 表明代码错误的差异
    • "X应该是Y,但实际是Z" → 排查代码路径
  2. 行为不明确 - 我们尚未理解的现象
    • "我们预期是X,实际是Y,原因不明" → 需要深入排查
  3. 确认正常工作 - 符合预期的现象
    • "此行为符合预期" → 提升置信度

Profit Insights (Surface Second)

利润洞察(其次呈现)

  1. Top Performers - Which configs show best results?
  2. Market Patterns - What conditions favor which strategies?
  3. Optimization Opportunities - Parameter tuning suggestions
  1. 表现最佳的配置 - 哪些配置结果最好?
  2. 市场模式 - 哪些条件更适合特定策略?
  3. 优化机会 - 参数调优建议

Next Steps

下一步计划

  • Investigation priorities for accuracy concerns
  • Experiments to validate profit hypotheses
  • Config variations to test
Remember: Accuracy comes first. Surface discrepancies prominently, then provide profit optimization insights. Both matter, but a small accuracy bug can invalidate all profit analysis.
  • 准确性问题的排查优先级
  • 验证利润假设的实验
  • 需测试的配置变体
记住:准确性优先。先突出差异,再提供利润优化洞察。两者都很重要,但微小的准确性bug可能会使所有利润分析失效。