commands: m2d-log + backtest + batch + stats slash commands (124 tests pass)
This commit is contained in:
73
.claude/commands/backtest.md
Normal file
73
.claude/commands/backtest.md
Normal file
@@ -0,0 +1,73 @@
|
||||
---
|
||||
description: Run vision extraction on a single TradeStation screenshot, then append to jurnal CSV + regenerate MD.
|
||||
argument-hint: "<screenshot_path_or_basename> [--calibration]"
|
||||
---
|
||||
|
||||
# /backtest — single screenshot vision extraction
|
||||
|
||||
Lansează subagentul `m2d-extractor` pe un screenshot, primește JSON-ul, append la `data/jurnal.csv`, regenerează `data/jurnal.md`.
|
||||
|
||||
## Arguments
|
||||
|
||||
- `$1` (obligatoriu) — path la screenshot. Acceptă:
|
||||
- basename (`2026-05-13-dia-1645.png`) — caută în `screenshots/inbox/`, fallback `screenshots/processed/`
|
||||
- path relativ sau absolut explicit
|
||||
- `--calibration` (flag) — `source=vision_calibration` în loc de `source=vision`. Folosit împreună cu `/m2d-log --calibration` pe același screenshot pentru P4 mismatch report.
|
||||
|
||||
## Workflow
|
||||
|
||||
1. **Rezolvă path-ul** screenshot-ului. Dacă `$1` e doar basename, încearcă `screenshots/inbox/<basename>` apoi `screenshots/processed/<basename>`. Dacă nu există nicăieri, raportezi eroare și te oprești.
|
||||
|
||||
2. **Invocă subagentul `m2d-extractor`** (definit în `.claude/agents/m2d-extractor.md`) prin Task tool cu `subagent_type: "m2d-extractor"`. Prompt-ul către agent:
|
||||
|
||||
```
|
||||
screenshot_path: <absolute_path>
|
||||
screenshot_file: <basename>
|
||||
```
|
||||
|
||||
Agentul scrie `data/extractions/<basename_no_ext>.json` + `.log` și returnează status-line scurt.
|
||||
|
||||
3. **Verifică output-ul**:
|
||||
- Dacă fișierul `data/extractions/<basename_no_ext>.json` nu există după ce agentul revine → eroare; raportezi și muți screenshot-ul la `screenshots/needs_review/`.
|
||||
- Citește JSON-ul. Dacă `confidence == "low"` SAU `ambiguities` non-empty cu `image_unreadable` → muți screenshot-ul la `screenshots/needs_review/`, raportezi, nu apelezi append.
|
||||
|
||||
4. **Append la CSV**:
|
||||
|
||||
```bash
|
||||
python -c "from pathlib import Path; from scripts.append_row import append_extraction; import json; r = append_extraction(Path('data/extractions/<basename_no_ext>.json'), source='<source>'); print(json.dumps(r, default=str))"
|
||||
```
|
||||
|
||||
`<source>` = `vision_calibration` dacă `--calibration`, altfel `vision`.
|
||||
|
||||
Parsezi răspunsul. Dacă `status == "rejected"`:
|
||||
- `reason` conține "duplicate" → screenshot deja procesat cu acest source; raportezi și NU îl muți.
|
||||
- `reason` conține "validation error" → JSON-ul agentului a fost respins; muți screenshot la `screenshots/needs_review/` și raportezi.
|
||||
- Alte erori → raportezi și lași screenshot-ul unde e.
|
||||
|
||||
5. **Mută screenshot-ul** la `screenshots/processed/<basename>` dacă append-ul a reușit și fișierul originar a fost în `inbox/`. Dacă era deja în `processed/`, nu-l muta.
|
||||
|
||||
6. **Regenerează MD**:
|
||||
|
||||
```bash
|
||||
python scripts/regenerate_md.py
|
||||
```
|
||||
|
||||
7. **Raport final** (în română):
|
||||
|
||||
```
|
||||
/backtest <basename> → trade #<id> adăugat (source=<source>, set=<set>, pl_marius=<pl>, confidence=<conf>).
|
||||
Regenerat data/jurnal.md (<total> rânduri).
|
||||
```
|
||||
|
||||
Dacă screenshot-ul a fost mutat la `needs_review`:
|
||||
|
||||
```
|
||||
/backtest <basename> → NEEDS REVIEW: <motiv>. Mutat la screenshots/needs_review/<basename>.
|
||||
```
|
||||
|
||||
## Reguli
|
||||
|
||||
- O singură invocare per screenshot. Nu reapelezi agentul dacă output-ul e dubios — îl muți la `needs_review` și raportezi.
|
||||
- NU edita CSV direct.
|
||||
- NU regenera MD dacă append-ul a fost respins.
|
||||
- Path discipline: subagentul scrie doar la `data/extractions/`; tu (slash command) muți screenshot-uri și apelezi scripts/.
|
||||
95
.claude/commands/batch.md
Normal file
95
.claude/commands/batch.md
Normal file
@@ -0,0 +1,95 @@
|
||||
---
|
||||
description: Run vision extraction in parallel on multiple screenshots (default screenshots/inbox/), then serial-append the results with partial-failure handling.
|
||||
argument-hint: "[dir_or_glob] [--limit N] [--calibration]"
|
||||
---
|
||||
|
||||
# /batch — parallel vision extraction over multiple screenshots
|
||||
|
||||
Procesează screenshot-uri multiple. Lansează până la **5 subagenți `m2d-extractor` în paralel** (cap rigid — protejează context window și rate limits). După ce toți revin, append-ezi rezultatele **serial** (`append_row` citește/scrie CSV — paralelism la write = corupție garantată).
|
||||
|
||||
## Arguments
|
||||
|
||||
- `$1` (opțional) — director sau glob. Default `screenshots/inbox/`. Exemplu: `screenshots/inbox/2025-09-*.png`.
|
||||
- `--limit N` (opțional) — procesează doar primele N screenshot-uri (în ordine alfabetică). Default: toate.
|
||||
- `--calibration` (flag) — `source=vision_calibration` în loc de `vision`.
|
||||
|
||||
## Workflow
|
||||
|
||||
### Fază 1 — Colectează lista
|
||||
|
||||
1. Enumeră fișierele PNG/JPG match-uind argumentul. Sortează alfabetic. Aplică `--limit` dacă există.
|
||||
2. Dacă lista e goală → raportezi "Nimic de procesat în <path>" și te oprești.
|
||||
3. Dacă lista are 1 element → sugerează `/backtest` în loc și continuă cu batch.
|
||||
|
||||
### Fază 2 — Extracție paralelă (max 5 concurent)
|
||||
|
||||
Procesezi în **batch-uri de 5**. Pentru fiecare batch:
|
||||
|
||||
- Lansezi câte un Task tool call cu `subagent_type: "m2d-extractor"` pentru fiecare screenshot, ÎN ACELAȘI MESAJ (tool calls paralele). Prompt-ul per agent:
|
||||
|
||||
```
|
||||
screenshot_path: <absolute_path>
|
||||
screenshot_file: <basename>
|
||||
```
|
||||
|
||||
- Aștepți să se întoarcă toți cinci. Pentru fiecare, verifici că `data/extractions/<basename_no_ext>.json` a fost scris.
|
||||
- Treci la următorul batch de 5.
|
||||
|
||||
**De ce 5**: peste 5 sub-agenți paraleli începi să saturezi context window-ul orchestratorului cu output-urile lor și rate limits-urile API-ului. Cap rigid.
|
||||
|
||||
### Fază 3 — Append serial cu partial-failure
|
||||
|
||||
Pentru fiecare screenshot din lista originală, **în ordine**:
|
||||
|
||||
1. Verifică `data/extractions/<basename_no_ext>.json`:
|
||||
- Lipsă → log "missing JSON, agent abort", mută screenshot-ul la `screenshots/needs_review/`, continuă cu următorul.
|
||||
- Citește JSON. Dacă `confidence == "low"` SAU `"image_unreadable" in ambiguities` → mută la `needs_review/`, continuă.
|
||||
|
||||
2. Apelează append:
|
||||
|
||||
```bash
|
||||
python -c "from pathlib import Path; from scripts.append_row import append_extraction; import json; r = append_extraction(Path('data/extractions/<basename_no_ext>.json'), source='<source>'); print(json.dumps(r, default=str))"
|
||||
```
|
||||
|
||||
`<source>` = `vision_calibration` dacă `--calibration`, altfel `vision`.
|
||||
|
||||
3. Reacționezi la rezultat:
|
||||
- `status == "ok"` → ține minte ID-ul, mută screenshot la `screenshots/processed/<basename>` dacă era în inbox.
|
||||
- `status == "rejected"`, `reason` conține "duplicate" → ține minte ca skip; NU muta screenshot-ul (deja procesat).
|
||||
- `status == "rejected"`, alt reason → log motivul, mută la `needs_review/`.
|
||||
|
||||
4. NU oprești batch-ul la primul fail. Continuă până la capăt.
|
||||
|
||||
### Fază 4 — Regenerează MD o singură dată
|
||||
|
||||
După ce toate append-urile s-au terminat (chiar și parțial), rulezi UNA SINGURĂ DATĂ:
|
||||
|
||||
```bash
|
||||
python scripts/regenerate_md.py
|
||||
```
|
||||
|
||||
(Regenerarea după fiecare append e wasteful; CSV-ul e sursa de adevăr, MD-ul e mirror.)
|
||||
|
||||
### Fază 5 — Raport final
|
||||
|
||||
Format:
|
||||
|
||||
```
|
||||
/batch terminat. Procesat <total> screenshot-uri.
|
||||
OK: <n_ok> (trade-uri #<id1>, #<id2>, ...)
|
||||
Duplicate: <n_dup> (skipped — deja în CSV)
|
||||
Needs review: <n_nr> (mutate la screenshots/needs_review/)
|
||||
- <basename1>: <motiv>
|
||||
- <basename2>: <motiv>
|
||||
Erori: <n_err>
|
||||
- <basename>: <reason>
|
||||
Regenerat data/jurnal.md (<total_rows> rânduri).
|
||||
```
|
||||
|
||||
## Reguli
|
||||
|
||||
- **Cap concurrency la 5**. Niciodată mai mulți subagenți paraleli — chiar și pentru un batch mare. Procesezi în secvențe de batch-uri de 5.
|
||||
- **Append serial obligatoriu**. `append_extraction` citește CSV-ul, computează `next_id` și scrie atomic; rulat în paralel ar duce la ID-uri duplicat sau pierderi.
|
||||
- **Partial failure = continuă**. Un screenshot prost nu blochează restul batch-ului.
|
||||
- **MD regen o singură dată** la final.
|
||||
- **Path discipline pentru subagent neschimbată**: agentul scrie doar la `data/extractions/`. Tu, ca orchestrator, muți screenshot-uri.
|
||||
99
.claude/commands/m2d-log.md
Normal file
99
.claude/commands/m2d-log.md
Normal file
@@ -0,0 +1,99 @@
|
||||
---
|
||||
description: Adaugă manual un rând în jurnal.csv (source=manual sau manual_calibration). Pentru calibrare P4 sau forward paper.
|
||||
argument-hint: "[--calibration] <screenshot_path>"
|
||||
---
|
||||
|
||||
# /m2d-log — manual M2D trade entry
|
||||
|
||||
Marius extrage manual TOATE câmpurile trade-ului. Folosit pentru calibration P4 (împreună cu `/backtest --calibration` pe același screenshot) sau ca log direct fără vision.
|
||||
|
||||
## Workflow
|
||||
|
||||
1. **Parse `$ARGUMENTS`** — detectează flag `--calibration` și `<screenshot_path>`. Dacă `<screenshot_path>` lipsește, întreabă user-ul. Calculează `basename = basename(<screenshot_path>)` și `basename_no_ext = basename` minus ultima extensie.
|
||||
|
||||
2. **Promptează user-ul în română**, pe rând, pentru fiecare câmp din schema `M2DExtraction` (vezi `scripts/vision_schema.py`). Ordinea + opțiuni valide:
|
||||
|
||||
- `data` — `YYYY-MM-DD`
|
||||
- `ora_utc` — `HH:MM` (conversie din RO local: EEST=UTC+3 vară, EET=UTC+2 iarnă; întreabă user-ul direct dacă nu e clar)
|
||||
- `instrument` — `DIA` / `US30` / `other`
|
||||
- `directie` — `Buy` / `Sell`
|
||||
- `tf_mare` — `5min` / `15min`
|
||||
- `tf_mic` — `1min` / `3min`
|
||||
- `calitate` — `Clară` / `Mai mare ca impuls` / `Slabă` / `n/a`
|
||||
- `entry`, `sl`, `tp0`, `tp1`, `tp2` — float-uri
|
||||
- `risc_pct` — float (ex: `0.12` pentru 0.12%)
|
||||
- `outcome_path` — `SL` / `TP0→SL` / `TP0→TP1` / `TP0→TP2` / `TP0→pending` / `pending` (UNICODE `→`)
|
||||
- `max_reached` — `SL_first` / `TP0` / `TP1` / `TP2`
|
||||
- `be_moved` — `true` / `false`
|
||||
- `confidence` — default `high` (manual e by definition high)
|
||||
- `note` — string opțional, default `""`
|
||||
|
||||
`screenshot_file` se setează automat la `basename`; `ambiguities` se setează automat la `[]`. Dacă user-ul dă valoare invalidă, repetă întrebarea.
|
||||
|
||||
3. **Construiește JSON-ul** complet, valid contra `M2DExtraction`.
|
||||
|
||||
4. **Scrie JSON-ul** la `data/extractions/<basename_no_ext>.manual.json` — pretty-print indent 2, UTF-8, newline final. Sufixul `.manual` previne coliziunea cu output-ul vision (`<basename_no_ext>.json`).
|
||||
|
||||
5. **Determină source**: `manual_calibration` dacă `--calibration` e prezent, altfel `manual`.
|
||||
|
||||
6. **Append la CSV**:
|
||||
|
||||
```bash
|
||||
python -c "from pathlib import Path; from scripts.append_row import append_extraction; import json; r = append_extraction(Path('data/extractions/<basename_no_ext>.manual.json'), source='<source>'); print(json.dumps(r, default=str))"
|
||||
```
|
||||
|
||||
Parsezi răspunsul JSON.
|
||||
|
||||
7. **Dacă `status == "ok"`**:
|
||||
|
||||
```bash
|
||||
python -m scripts.regenerate_md
|
||||
```
|
||||
|
||||
Apoi afișezi:
|
||||
|
||||
```
|
||||
✅ Trade adăugat la jurnal. ID: <id>. Set: <set>. P/L Marius: <pl_marius>. outcome_path: <outcome_path>.
|
||||
```
|
||||
|
||||
8. **Dacă `status == "rejected"`**:
|
||||
|
||||
```
|
||||
❌ Trade respins: <reason>
|
||||
```
|
||||
|
||||
NU regenera MD. Dacă `reason` conține "duplicate":
|
||||
- pentru `--calibration`: spui user-ului că există deja rând `manual_calibration` pentru acest screenshot; nu poți avea două leg-uri manual de calibrare pe același screenshot.
|
||||
- pentru `source=manual` simplu: user-ul decide dacă suprascrie (atunci șterge manual rândul din `data/jurnal.csv` și re-rulează).
|
||||
|
||||
## Reguli
|
||||
|
||||
- NU edita CSV direct — folosește `append_extraction`.
|
||||
- NU regenera MD dacă append-ul a fost respins.
|
||||
|
||||
## Output skeleton JSON
|
||||
|
||||
```json
|
||||
{
|
||||
"screenshot_file": "2026-05-13-dia-1645.png",
|
||||
"data": "2026-05-13",
|
||||
"ora_utc": "14:45",
|
||||
"instrument": "DIA",
|
||||
"directie": "Buy",
|
||||
"tf_mare": "5min",
|
||||
"tf_mic": "1min",
|
||||
"calitate": "Clară",
|
||||
"entry": 497.42,
|
||||
"sl": 496.80,
|
||||
"tp0": 497.67,
|
||||
"tp1": 497.79,
|
||||
"tp2": 498.04,
|
||||
"risc_pct": 0.12,
|
||||
"outcome_path": "TP0→TP1",
|
||||
"max_reached": "TP1",
|
||||
"be_moved": true,
|
||||
"confidence": "high",
|
||||
"ambiguities": [],
|
||||
"note": ""
|
||||
}
|
||||
```
|
||||
42
.claude/commands/stats.md
Normal file
42
.claude/commands/stats.md
Normal file
@@ -0,0 +1,42 @@
|
||||
---
|
||||
description: Show backtest statistics for data/jurnal.csv (overall, per-Set, per-calitate, per-instrument with Wilson + bootstrap CIs). --calibration shows P4 mismatch report.
|
||||
argument-hint: "[--calibration] [--seed N]"
|
||||
---
|
||||
|
||||
# /stats — backtest statistics
|
||||
|
||||
Rulează `scripts/stats.py` și afișează raportul.
|
||||
|
||||
## Arguments
|
||||
|
||||
- `--calibration` (flag) — afișează raportul P4 (mismatch field-by-field pe perechi `manual_calibration` ↔ `vision_calibration` join-uite pe `screenshot_file`).
|
||||
- `--seed N` (opțional) — seed pentru bootstrap RNG (default fără seed → output ne-determinist între run-uri). Folosește când vrei reproducibilitate.
|
||||
|
||||
Default (fără flag-uri): backtest stats — overall + per-Set + per-calitate + per-instrument WR, expectancy, Wilson 95% CI pe WR, bootstrap 95% CI pe expectancy.
|
||||
|
||||
## Workflow
|
||||
|
||||
1. Construiește comanda:
|
||||
|
||||
```bash
|
||||
python scripts/stats.py [--calibration] [--seed N]
|
||||
```
|
||||
|
||||
`--csv data/jurnal.csv` e default-ul scriptului — nu îl pasezi.
|
||||
|
||||
2. Rulează prin Bash tool. Output-ul vine pe stdout în UTF-8.
|
||||
|
||||
3. Afișează output-ul **as-is** către user. Nu reformata, nu re-rezuma, nu interpreta. Scriptul are deja format ales (tabel + secțiuni text).
|
||||
|
||||
4. **Interpretare** scurtă (max 3 propoziții) DACĂ user-ul cere explicit sau dacă observi ceva ce merită menționat:
|
||||
- În modul backtest: Set-uri cu N ≥ 40 și Wilson lower bound > 50% → candidat pentru GO LIVE (vezi `STOPPING_RULE.md`).
|
||||
- În modul `--calibration`: dacă există ≥10 perechi și mismatch rate > 10% pe câmpuri core (`entry/sl/tp0/1/2/outcome_path/max_reached/directie`) → P4 FAIL, vision agent are nevoie de fix (`.claude/agents/m2d-extractor.md`).
|
||||
|
||||
5. NU edita CSV. NU regenera MD (citire pură).
|
||||
|
||||
## Reguli
|
||||
|
||||
- Read-only. Această comandă nu scrie nimic.
|
||||
- Output-ul scriptului e ground truth — nu inventezi numere.
|
||||
- `calitate` e descriptor biased (post-outcome) — vezi `STOPPING_RULE.md` §3 — raportul îl afișează informational only. NU sugerezi user-ului să folosească `calitate` ca filtru pentru GO LIVE.
|
||||
- Pentru calibration P4: minimum 10 perechi pentru ca verdictul să aibă sens. Sub 10 perechi → raportezi "insuficient pentru P4 — continuă să acumulezi calibrare".
|
||||
Reference in New Issue
Block a user