Skip to content

Commit 67fb90e

Browse files
committed
feat: trade journal analyzer — profile + behavior diagnostics
Users upload broker exports (CSV/Excel) and get a trading portrait plus 4 bias diagnostics. Parses 同花顺/东方财富/富途/generic formats with encoding fallback (utf-8/gbk/gb2312/big5). Profile: holding days, win rate, PnL ratio, max drawdown, top symbols, market/hourly distribution via FIFO lot matching. Behavior: disposition effect, overtrading, chasing momentum, anchoring — each with severity + numeric evidence derived from the journal itself, no external market data pulls. Also: gitignore agent/uploads/ so runtime user uploads never leak into the repo; inline a tiny csv in test_doc_reader instead of depending on a fixture file.
1 parent 1717ad1 commit 67fb90e

6 files changed

Lines changed: 1103 additions & 5 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ agent/sessions/
1616
agent/runs/
1717
agent/.ui_runtime/
1818
agent/.tasks/
19+
agent/uploads/
1920
agent/tests/*_result.json
2021
.ruff_cache/
2122
frontend/dist/

agent/src/agent/context.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,12 @@
5252
**Document / web** — user provides a PDF or URL:
5353
- `read_document(path=...)` for PDFs, `read_url(url=...)` for web pages.
5454
55+
**Trade journal** — user uploads a CSV/Excel broker export (交割单) or asks to analyze their own trading history:
56+
1. `load_skill("trade-journal")` — read analysis methodology and report templates
57+
2. `analyze_trade_journal(file_path=..., analysis_type="full")` — parse + profile
58+
3. Present results as the markdown report in the skill. Offer follow-ups: time-slice, symbol deep-dive, market split.
59+
4. Behavior diagnostics + strategy extraction are Phase 4b — tell the user they're coming, don't fabricate.
60+
5561
## Guidelines
5662
5763
- Load the relevant skill BEFORE starting any task. Skills contain the exact API contracts and examples.
Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
---
2+
name: trade-journal
3+
description: Analyze a user's trade journal (CSV/Excel broker export). Parses 同花顺/东方财富/富途/generic formats, produces a trading profile and 4 behavior diagnostics (disposition effect, overtrading, chasing, anchoring). Use the `analyze_trade_journal` tool.
4+
category: tool
5+
---
6+
# Trade Journal Analysis
7+
8+
## Purpose
9+
10+
Users upload broker exports (交割单) and get an honest, data-grounded portrait
11+
of their own trading. Two layers are live:
12+
13+
- **Profile** — holding days, frequency, win rate, PnL ratio, cumulative PnL,
14+
max drawdown, top symbols, market/hourly distribution.
15+
- **Behavior diagnostics** — 4 biases, each with severity (low/medium/high)
16+
and numeric evidence: disposition effect, overtrading, chasing momentum,
17+
anchoring.
18+
19+
Strategy extraction → backtest bridge lands in Phase 4c.
20+
21+
Supported formats (auto-detected):
22+
- **同花顺** (Tonghuashun) — A-share CSV, typically GBK-encoded
23+
- **东方财富** (Eastmoney) — A-share CSV, typically GBK-encoded
24+
- **富途** (Futu) — HK/US CSV, UTF-8
25+
- **Generic** — any CSV with columns like `datetime/symbol/side/qty/price`
26+
27+
## Usage
28+
29+
**Call the `analyze_trade_journal` tool directly. Never run Python from bash.**
30+
31+
```
32+
analyze_trade_journal(file_path="uploads/xxx.csv")
33+
analyze_trade_journal(file_path="uploads/xxx.csv", analysis_type="profile")
34+
analyze_trade_journal(file_path="uploads/xxx.csv", filter_expr="2026-01 to 2026-03")
35+
analyze_trade_journal(file_path="uploads/xxx.csv", filter_expr="symbol=600519.SH")
36+
analyze_trade_journal(file_path="uploads/xxx.csv", filter_expr="market=china_a")
37+
```
38+
39+
`analysis_type`:
40+
- `full` (default) — profile + behavior (strategy still placeholder)
41+
- `profile` — profile metrics only (fastest)
42+
- `behavior` — 4 behavior diagnostics only
43+
- `strategy` — Phase 4c placeholder
44+
45+
`filter_expr` (optional):
46+
- Date range: `"YYYY-MM to YYYY-MM"` or `"YYYY-MM-DD to YYYY-MM-DD"`
47+
- Symbol: `"symbol=600519.SH"` (exact match on qualified symbol)
48+
- Market: `"market=china_a|us|hk|crypto"`
49+
50+
## Return shape (profile subset)
51+
52+
```json
53+
{
54+
"status": "ok",
55+
"file": "xxx.csv",
56+
"format_detected": "tonghuashun",
57+
"total_records": 326,
58+
"date_range": "2026-01-06 ~ 2026-03-28",
59+
"symbols_count": 42,
60+
"market": "china_a",
61+
"profile": {
62+
"total_trades": 326,
63+
"total_roundtrips": 118,
64+
"avg_holding_days": 3.2,
65+
"trade_frequency_per_week": 4.1,
66+
"win_rate": 0.48,
67+
"profit_loss_ratio": 1.35,
68+
"total_pnl": 18240.55,
69+
"max_drawdown": -9820.10,
70+
"top_symbols": [{"symbol": "600519.SH", "trades": 14, "total_amount": 1.02e6}, ...],
71+
"market_distribution": {"china_a": 326},
72+
"hourly_distribution": {9: 52, 10: 84, ...},
73+
"roundtrips_sample": [{"symbol": "600519.SH", "buy_dt": "...", "sell_dt": "...", "pnl": 3400.1, "pnl_pct": 0.021, "hold_days": 2.5}, ...]
74+
}
75+
}
76+
```
77+
78+
Note: PnL uses FIFO lot matching; unmatched open positions are excluded from
79+
win rate / PnL ratio (only closed round-trips count).
80+
81+
## Presenting results to the user
82+
83+
Produce a **single markdown report** in the user's language. Lead with the
84+
top-line numbers, then section-by-section. Keep it dense — this is retail
85+
readers skimming on a phone.
86+
87+
### Report template
88+
89+
```
90+
## 你的交易画像 — {date_range}
91+
92+
**总体**
93+
- 交易笔数:{total_trades}(完整来回 {total_roundtrips} 次)
94+
- 平均持仓:{avg_holding_days} 天
95+
- 交易频率:{trade_frequency_per_week} 次/周
96+
- 胜率:{win_rate:.0%}
97+
- 盈亏比:{profit_loss_ratio}
98+
- 累计盈亏:{total_pnl}
99+
- 最大回撤:{max_drawdown}
100+
101+
**最常交易的标的**(前 5 名)
102+
| 标的 | 笔数 | 成交额 |
103+
|------|------|--------|
104+
| ... | ... | ... |
105+
106+
**市场分布**
107+
{market_distribution}
108+
109+
**交易时段**
110+
{hourly_distribution — highlight peak hours}
111+
112+
**一句话观察**
113+
(根据数据写 1-2 句:过度交易?只做窄范围标的?集中在某时段?)
114+
```
115+
116+
Guidance:
117+
- If `win_rate < 0.4` AND `profit_loss_ratio < 1.0` → explicit warning: losing
118+
on both win rate and payoff. Ask whether they want behavior diagnostics
119+
(Phase 4b) or a cooling-off reality check.
120+
- If `avg_holding_days < 1` AND `trade_frequency_per_week > 15` → flag
121+
intraday-heavy pattern, note that minute-level backtest would be better.
122+
- If `symbols_count <= 3` → concentration risk; ask if they want a sector-
123+
diversification check.
124+
125+
## Follow-up dialogue
126+
127+
After the initial report, users typically ask:
128+
- **Time-slice**: "3 月份表现怎么样" → re-call with `filter_expr="2026-03-01 to 2026-03-31"`.
129+
- **Symbol deep-dive**: "茅台这只赚了多少" → `filter_expr="symbol=600519.SH"`.
130+
- **Market split**: "港股和美股分开看" → two calls, `market=hk` and `market=us`.
131+
- **Hypothetical** ("如果我严格止损 -5%") → Phase 4b feature; for now tell the
132+
user this is on the roadmap.
133+
134+
Do NOT re-upload — the file path is still valid for subsequent tool calls
135+
in the same session.
136+
137+
## Error handling
138+
139+
- `File not found` / `Unsupported extension` — ask user to re-upload.
140+
- `Unrecognized trade journal format` — share the detected columns back to
141+
the user and ask them to rename the key columns to: `datetime, symbol,
142+
side, quantity, price, amount, fee` (generic fallback).
143+
- `No trade records parsed` — likely empty file or header-only; ask user to
144+
confirm the export contains actual fills.
145+
146+
## Behavior diagnostics (shape)
147+
148+
Under `result["behavior"]`:
149+
150+
```json
151+
{
152+
"disposition_effect": {
153+
"severity": "high",
154+
"ratio_loss_to_win_hold": 1.69,
155+
"avg_winner_hold_days": 7.4,
156+
"avg_loser_hold_days": 12.5,
157+
"evidence": "Losing roundtrips held 12.5d vs winning 7.4d (ratio 1.69). Classic disposition pattern."
158+
},
159+
"overtrading": {
160+
"severity": "high",
161+
"busy_day_avg_pnl": -2632,
162+
"quiet_day_avg_pnl": 759,
163+
"evidence": "On busy days (≥3 trades) avg PnL -2632; on quiet days (≤1) avg PnL +759. High activity hurts returns."
164+
},
165+
"chasing_momentum": {
166+
"severity": "medium",
167+
"chase_ratio": 0.5,
168+
"buys_evaluated": 4,
169+
"evidence": "2/4 buys (50%) came after a >3% price run-up in the same symbol. Some chasing tendency."
170+
},
171+
"anchoring": {
172+
"severity": "high",
173+
"anchored_symbol_ratio": 0.83,
174+
"symbols_evaluated": 6,
175+
"anchored_symbols": [...],
176+
"evidence": "5/6 frequently-traded symbols stayed in a narrow price band (CV<5%). Strong anchoring."
177+
}
178+
}
179+
```
180+
181+
### Detection logic (for user-facing explanation)
182+
183+
| Bias | Metric | Medium | High |
184+
|------|--------|--------|------|
185+
| **Disposition effect** | avg_loser_hold / avg_winner_hold | ≥ 1.2 | ≥ 1.5 |
186+
| **Overtrading** | (quiet − busy) / \|quiet\| day-PnL gap | ≥ 0.3 | ≥ 1.0 |
187+
| **Chasing** | fraction of buys after 3-trade rolling +3% move | ≥ 40% | ≥ 60% |
188+
| **Anchoring** | fraction of ≥5-trade symbols with price CV < 5% | ≥ 33% | ≥ 66% |
189+
190+
### Report section (Chinese)
191+
192+
```
193+
## 行为偏差诊断
194+
195+
| 偏差 | 严重程度 | 核心证据 |
196+
|------|----------|----------|
197+
| 处置效应 | {high/medium/low} | {evidence} |
198+
| 过度交易 | {...} | {...} |
199+
| 追涨杀跌 | {...} | {...} |
200+
| 锚定效应 | {...} | {...} |
201+
202+
**改进建议**(根据检测到的 high/medium 项生成):
203+
- 处置效应 high → 写死止损(例如 -8%),盈利持仓不要过早兑现
204+
- 过度交易 high → 每日交易次数 <= N 的硬约束
205+
- 追涨杀跌 high → 改买回调而不是新高,设置"涨幅 X% 以上当日不追"规则
206+
- 锚定效应 high → 扩宽价格带,不要死守某个"心理价"
207+
```
208+
209+
## Phase 4c preview (not yet implemented)
210+
211+
Strategy extraction → SignalEngine code gen → auto-backtest lands in Phase 4c.
212+
When the user asks for it, respond honestly and offer the behavior diagnostics
213+
instead (they're live).

0 commit comments

Comments
 (0)