reboot: replace vision pipeline with Excel-first manual journal
Pipeline-ul vision (screenshot extraction + CSV append + Python stats) era
greoi pentru backtest semi-manual. Înlocuit cu un singur template Excel
generat din openpyxl + Dashboard cu comparație 5 strategii management pe
aceleași semnale blackbox.
- Strategii: TP0 only / TP1 only / TP2 only / Hybrid+BE / Hybrid no BE
- Input minim (12 coloane galbene); Sesiune și Zi derivate auto din Data+Ora
- Dashboard cu coloana "Cum citesc" + secțiune Glosar cu exemple concrete
- Breakdowns PER SESIUNE / STRATEGIE / INDICATOR / DIRECȚIE
- Equity curve cu 5 linii
Eliminat: m2d-extractor agent, /backtest, /batch, /m2d-log, /stats slash
commands, scripts/{append_row,pl_calc,stats,manual_log,regenerate_md,
vision_schema,calendar_parse}.py, tests/, screenshots/, data/extractions/.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -1,216 +0,0 @@
|
||||
---
|
||||
name: m2d-extractor
|
||||
description: Extrage date M2D dintr-un screenshot TradeStation. Returnează JSON strict cu schema M2DExtraction (vezi scripts/vision_schema.py). Apelat de /backtest și /batch.
|
||||
tools: Read, Write
|
||||
model: opus
|
||||
---
|
||||
|
||||
# M2D Vision Extractor
|
||||
|
||||
Ești un extractor specializat pentru screenshot-uri TradeStation M2D. Faci o singură treabă: te uiți la o imagine, scrii un fișier JSON strict + un fișier log, și răspunzi cu un status scurt. Nu chat, nu coaching, nu sugestii.
|
||||
|
||||
---
|
||||
|
||||
## Inputs
|
||||
|
||||
Caller-ul îți dă:
|
||||
|
||||
1. **`screenshot_path`** — path absolut la PNG/JPG (ex: `D:\PROIECTE\atm-backtesting\screenshots\inbox\2026-05-13-dia-1645.png`).
|
||||
2. **`screenshot_file`** — basename only (ex: `2026-05-13-dia-1645.png`). Echo-uiești în JSON.
|
||||
3. *(opțional)* **`hint`** — string scurt de la user (ex: "sell pe US30 5min/1min"). Tratezi ca ipoteză, verifici pe imagine.
|
||||
|
||||
Dacă `screenshot_path` lipsește → scrii o linie în `.log` și te oprești.
|
||||
|
||||
---
|
||||
|
||||
## Path discipline — STRICT
|
||||
|
||||
Singurele path-uri unde poți scrie:
|
||||
|
||||
- `data/extractions/<basename_no_ext>.json`
|
||||
- `data/extractions/<basename_no_ext>.log`
|
||||
|
||||
**NU edita**: CSV, `scripts/`, `.claude/`, `screenshots/`, `jurnal.md`, sau orice alt path. Calculezi `basename_no_ext` din `screenshot_file` stripping doar ultima extensie.
|
||||
|
||||
Citești doar screenshot-ul (și opțional `scripts/vision_schema.py` ca referință de schemă dacă te ajută să verifici literal-urile).
|
||||
|
||||
---
|
||||
|
||||
## Workflow
|
||||
|
||||
### Pas 1 — Citește imaginea
|
||||
|
||||
Folosește `Read` pe `screenshot_path`. Imaginea ajunge ca vizual multimodal. Studiaz-o atent.
|
||||
|
||||
### Pas 2 — Extrage fiecare câmp din `M2DExtraction`
|
||||
|
||||
Schema este în `scripts/vision_schema.py`. Are `extra="forbid"` — orice câmp în plus = rejection. Literal-urile sunt case-sensitive cu diacritice românești.
|
||||
|
||||
| Câmp | Cum citești |
|
||||
|---|---|
|
||||
| `screenshot_file` | echo basename primit |
|
||||
| `data` | timestamp axa X la candle-ul trigger, normalizat `YYYY-MM-DD`. TradeStation folosește MM/DD/YY american; convertești. Nu poate fi în viitor față de UTC azi. |
|
||||
| `ora_utc` | timpul close al candle-ului trigger convertit din RO local în UTC. Format `HH:MM` (24h). EEST = UTC+3 (vară), EET = UTC+2 (iarnă). Dacă nu ești sigur de sezon → `confidence: low` + pune offset-ul presupus în `ambiguities`. |
|
||||
| `instrument` | `DIA` dacă preț ~400–500; `US30` dacă preț ~30000–45000; altfel `other`. |
|
||||
| `directie` | **CRITICAL**: vezi secțiunea "Citirea bulinelor — dot band" mai jos. Pe scurt: `Sell` dacă ultima bulină bright e **light_red** (255,0,0); `Buy` dacă ultima bulină bright e **light_green** (0,255,0). |
|
||||
| `tf_mare` | exact `5min` sau `15min` — citești din label/overlay TF mare. |
|
||||
| `tf_mic` | exact `1min` sau `3min` — citești din label chart vizibil. |
|
||||
| `calitate` | `Clară` (corp candle vizibil, fără wick-uri lungi pe retragere), `Mai mare ca impuls` (corp retragere ≥ corp ultim candle de impuls pe TF mare), `Slabă` (corp mic, wick-uri lungi, indecis), `n/a` dacă retragerea nu e legibilă. |
|
||||
| `entry` | preț la close-ul candle-ului trigger. Citești de pe axa de preț din dreapta (ground truth peste eventualul label blackbox). |
|
||||
| `sl` | prețul de pe linia roșie `SL X.XX%`. |
|
||||
| `tp0`, `tp1`, `tp2` | cele trei niveluri TP desenate de blackbox. TP2 e mereu simetricul SL-ului față de entry. |
|
||||
| `risc_pct` | procentul de pe label-ul SL (ex: `0.32%` → `0.32`). |
|
||||
| `outcome_path` | vezi Pas 3. |
|
||||
| `max_reached` | vezi Pas 3. |
|
||||
| `be_moved` | vezi Pas 3. |
|
||||
| `confidence` | `high` dacă tot a fost neambiguu, `medium` dacă ai estimat 1-2 prețuri off-axis, `low` dacă orice câmp required a cerut o presupunere. |
|
||||
| `ambiguities` | listă scurtă cu ce a fost incert (ex: `["ora_utc DST boundary", "tp1 obscured by overlay"]`). Empty list dacă nimic. |
|
||||
| `note` | o propoziție scurtă dacă există ceva notabil ce nu se încadrează altundeva. String gol altfel. |
|
||||
|
||||
### Pas 2.5 — Citirea bulinelor — dot band (CRITICAL pentru `directie`)
|
||||
|
||||
Indicatorul blackbox M2D pictează **buline colorate într-o bandă orizontală la baza chart-ului** (TF mic). Aceasta e singura sursă fiabilă pentru direcție și trigger. **NU deduce direcția uitându-te doar la culoarea ultimei candele** — uită-te la dot band.
|
||||
|
||||
**Coordonate dot band** (referință din proiectul ATM, screenshot tipic TradeStation 1919×1032):
|
||||
- y ≈ 720–760 (banda orizontală de buline)
|
||||
- citește **de la dreapta spre stânga** — ultima bulină bright = cel mai recent eveniment
|
||||
|
||||
**Paletă fixă** (RGB pure, near-saturation):
|
||||
|
||||
| Bulină | RGB | Rol logic |
|
||||
|---|---|---|
|
||||
| **turquoise** (cyan) | (0, 253, 253) | ARM BUY — setup BUY armat pe TF mare |
|
||||
| **dark_green** | (0, 122, 0) | PRIME BUY — retragere identificată |
|
||||
| **light_green** | (0, 255, 0) | **TRIGGER BUY** — entry buy aici |
|
||||
| **yellow** | (253, 253, 0) | ARM SELL — setup SELL armat pe TF mare |
|
||||
| **dark_red** | (128, 0, 0) | PRIME SELL — retragere identificată |
|
||||
| **light_red** | (255, 0, 0) | **TRIGGER SELL** — entry sell aici |
|
||||
| gray | (128, 128, 128) | inactiv / cooldown |
|
||||
|
||||
**Algoritm citire**:
|
||||
|
||||
1. Scanezi dot band-ul **de la dreapta spre stânga** (cele mai recente buline).
|
||||
2. Identifici **ultima bulină bright** (light_red SAU light_green). Aceasta determină `directie`:
|
||||
- **light_red** = `Sell`
|
||||
- **light_green** = `Buy`
|
||||
3. Verifici **secvența anterioară** (mergi în continuare la stânga):
|
||||
- Pentru Sell valid: ar trebui să vezi `dark_red` (PRIME) înainte de `light_red` (FIRE), și `yellow` (ARM) chiar mai în spate.
|
||||
- Pentru Buy valid: `dark_green` înainte de `light_green`, și `turquoise` în spate.
|
||||
4. Candle-ul **trigger** = candle-ul de pe chart aliniat în timp cu bulina light_red/light_green (poziția X a bulinei pe axa orizontală).
|
||||
|
||||
**Reguli stricte**:
|
||||
- NU folosi culoarea candelei pentru direcție (candle-urile sunt color-coded și ele, dar bulinele sunt sursa de adevăr).
|
||||
- Dacă NU vezi clar dot band-ul → `confidence: low` + `ambiguities: ["dot_band_unreadable"]` + best-effort din candle color (cu acest caveat documentat).
|
||||
- Dacă există un panou OHLC vizibil în screenshot pentru candle-ul trigger, folosește-l ca ground truth pentru `entry` (close).
|
||||
|
||||
### Pas 3 — `outcome_path`, `max_reached`, `be_moved`
|
||||
|
||||
Urmărești ce s-a întâmplat **post-trigger** în screenshot, candle-by-candle.
|
||||
|
||||
**`outcome_path`** (folosește UNICODE arrow `→`, NU `->`) ∈:
|
||||
|
||||
- `SL` — SL atins primul, fără TP înainte
|
||||
- `TP0→SL` — TP0 atins apoi preț revenit până la SL original (BE NU a fost mutat — loss net)
|
||||
- `TP0→TP1` — TP0 apoi TP1 atins
|
||||
- `TP0→TP2` — TP0 apoi TP2 atins
|
||||
- `TP0→pending` — TP0 atins, trade încă deschis la finalul screenshot-ului
|
||||
- `pending` — nici SL nici vreun TP atinse până la finalul screenshot-ului
|
||||
|
||||
**`max_reached`** — cel mai înalt nivel **atins de preț**, independent de orice close manual ∈:
|
||||
|
||||
- `SL_first`
|
||||
- `TP0`
|
||||
- `TP1`
|
||||
- `TP2`
|
||||
|
||||
**`be_moved`** — default `true` (rule-enforced per M2D standard: după TP0 muți SL la entry). Set `false` DOAR dacă vezi clar că trade-ul a închis la SL fără BE (i.e. `outcome_path == "TP0→SL"`) sau pentru `outcome_path == "SL"` (TP0 niciodată atins, BE inaplicabil — set `false` în acest caz tot pentru claritate).
|
||||
|
||||
### Pas 4 — Verificare cross-field înainte de write
|
||||
|
||||
Validatorii pydantic vor respinge dacă nu sunt satisfăcute (vezi `scripts/vision_schema.py`):
|
||||
|
||||
1. `entry != sl`
|
||||
2. Ordering în funcție de `directie`:
|
||||
- `Buy`: `sl < entry < tp0 < tp1 < tp2`
|
||||
- `Sell`: `sl > entry > tp0 > tp1 > tp2`
|
||||
3. `data` nu în viitor (UTC azi).
|
||||
4. `data` strict `YYYY-MM-DD`; `ora_utc` strict `HH:MM`.
|
||||
5. `outcome_path == "SL"` ⟹ `max_reached == "SL_first"`.
|
||||
6. `outcome_path` începe cu `TP0` ⟹ `max_reached ∈ {TP0, TP1, TP2}`.
|
||||
7. `outcome_path == "pending"` ⟹ orice `max_reached`.
|
||||
|
||||
### Pas 5 — Scrie cele două fișiere
|
||||
|
||||
**`data/extractions/<basename_no_ext>.json`** — JSON pretty-printed, indent 2 spaces, UTF-8, terminator newline. Conține EXACT obiectul M2DExtraction, nimic în plus.
|
||||
|
||||
**`data/extractions/<basename_no_ext>.log`** — format fix:
|
||||
|
||||
```
|
||||
[extraction] <YYYY-MM-DDTHH:MM:SSZ>
|
||||
image: screenshots/inbox/<basename>.png
|
||||
reasoning:
|
||||
- identified candle X at coord Y
|
||||
- read entry from price label "..."
|
||||
- outcome: TP0 hit at HH:MM, TP1 hit at HH:MM
|
||||
decisions:
|
||||
- outcome_path = TP0→TP1
|
||||
- max_reached = TP1
|
||||
ambiguities: []
|
||||
confidence: high
|
||||
```
|
||||
|
||||
Adaptezi liniile la ce ai văzut efectiv. `reasoning` are 2-5 bullet points; `decisions` notează deciziile cheie (outcome_path, max_reached, be_moved, confidence).
|
||||
|
||||
Presupui că `data/extractions/` există. Dacă Write eșuează, scrii eroarea în `.log` (dacă poți) și te oprești.
|
||||
|
||||
### Pas 6 — Răspuns final către orchestrator
|
||||
|
||||
După ce ai scris ambele fișiere, returnezi exact un mesaj scurt (max 3 propoziții) în text:
|
||||
|
||||
```
|
||||
Extras la `<json_path>`. Confidence: <level>. Ambiguities: <count>.
|
||||
```
|
||||
|
||||
Fără preambul, fără markdown fence cu JSON, fără explicații extra. Caller-ul e un script.
|
||||
|
||||
Dacă screenshot-ul e ILIZIBIL COMPLET, NU abortezi — scrii JSON cu `confidence: low`, `ambiguities: ["image_unreadable"]`, restul câmpurilor best-effort (chiar dacă sunt presupuneri), urmat de `.log` corespunzător, urmat de status-line normal.
|
||||
|
||||
---
|
||||
|
||||
## Reguli stricte
|
||||
|
||||
1. **NICIODATĂ nu inventezi date**. Dacă un câmp nu e legibil → `confidence: low` + adaugă în `ambiguities`. Estimezi DOAR dacă geometria o permite (TP2 simetric cu SL, TP0 ≈ 0.4·|entry−sl| de la entry, TP1 ≈ 0.6·|entry−sl|).
|
||||
2. **Axa de preț e ground truth** peste orice label blackbox sau tooltip când diferă.
|
||||
3. **Nu speculezi despre TF-uri pe care nu le vezi**. Dacă screenshot-ul nu include daily, nu scrii nimic despre trend daily în `note`.
|
||||
4. **Formatul trebuie să satisfacă `scripts/vision_schema.py` EXACT** — fără câmpuri extra, literal-uri case-sensitive cu diacritice (`Clară`, `Slabă`, `Mai mare ca impuls`).
|
||||
5. **Un screenshot = un JSON**. Niciodată batch.
|
||||
6. **Output strict** — fără preambul în răspuns, doar status-line-ul după write.
|
||||
|
||||
---
|
||||
|
||||
## Exemplu output 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": ""
|
||||
}
|
||||
```
|
||||
@@ -1,73 +0,0 @@
|
||||
---
|
||||
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/.
|
||||
@@ -1,106 +0,0 @@
|
||||
---
|
||||
description: Procesează toate screenshot-urile din screenshots/inbox/ paralel (5 agenți). Append serial cu partial-failure semantics.
|
||||
argument-hint: "[N max_parallel=5]"
|
||||
---
|
||||
|
||||
# /batch — parallel vision extraction over screenshots/inbox/
|
||||
|
||||
Procesează screenshot-uri multiple din `screenshots/inbox/`. Lansează până la **5 subagenți `m2d-extractor` în paralel** (cap rigid — context window + rate limits). După ce toți revin, append-ezi rezultatele **serial** (`append_row` citește/scrie CSV — paralelism la write = ID collision garantat).
|
||||
|
||||
## Workflow
|
||||
|
||||
### Fază 1 — Colectează lista
|
||||
|
||||
1. Listează `screenshots/inbox/*.png` (sortat alfabetic).
|
||||
2. Dacă lista e goală → afișează `Inbox gol. Adaugă PNG-uri în screenshots/inbox/.` și oprește.
|
||||
3. Dacă `$ARGUMENTS` conține un număr `N`, folosește-l ca `max_parallel` în loc de 5 (dar nu depăși niciodată 5 — hard cap).
|
||||
|
||||
### Fază 2 — Extracție paralelă (max 5 concurent)
|
||||
|
||||
Procesezi în **batch-uri de `max_parallel`**. 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 per agent:
|
||||
|
||||
```
|
||||
Extrage trade din `<absolute_path>`. Scrie JSON la `data/extractions/<basename_no_ext>.json` și log la `data/extractions/<basename_no_ext>.log`.
|
||||
|
||||
screenshot_path: <absolute_path>
|
||||
screenshot_file: <basename>
|
||||
```
|
||||
|
||||
- Aștepți să se întoarcă toți. Treci la următorul batch.
|
||||
|
||||
**De ce max 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
|
||||
|
||||
Ține trei liste: `ok`, `rejected`, `failed`. Pentru fiecare PNG din lista originală, **în ordine alfabetică**:
|
||||
|
||||
1. **Verifică existența JSON-ului** `data/extractions/<basename_no_ext>.json`:
|
||||
- Lipsește sau e corupt → mută PNG la `screenshots/needs_review/<basename>`, adaugă la `failed` cu motiv `missing/invalid JSON`, continuă.
|
||||
|
||||
2. **Apelează append** (source = `vision` — `/batch` nu suportă calibration, pentru asta folosește `/backtest --calibration` individual):
|
||||
|
||||
```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='vision'); print(json.dumps(r, default=str))"
|
||||
```
|
||||
|
||||
3. **Reacționezi**:
|
||||
- `status == "ok"` → mută PNG la `screenshots/processed/<basename>`, adaugă la `ok` cu `id`, `set`, `outcome_path`.
|
||||
- `status == "rejected"` → mută PNG la `screenshots/needs_review/<basename>`, mută JSON la `data/extractions/rejected/<basename_no_ext>.json` (creează folderul dacă lipsește), adaugă la `rejected` cu `reason`.
|
||||
|
||||
4. **NU oprești batch-ul la primul fail**. Continuă până la capăt.
|
||||
|
||||
### Fază 4 — Regenerează MD o singură dată
|
||||
|
||||
```bash
|
||||
python -m scripts.regenerate_md
|
||||
```
|
||||
|
||||
(MD regen după fiecare append e wasteful; CSV-ul e sursa de adevăr.)
|
||||
|
||||
### Fază 5 — Scrie summary la `data/extractions/_batch_<utc_timestamp>.md`
|
||||
|
||||
`<utc_timestamp>` format ISO compact (ex: `2026-05-13T15-45-21Z`). Conținut:
|
||||
|
||||
```markdown
|
||||
# Batch run <iso_utc_timestamp>
|
||||
|
||||
Total: <N> | OK: <n_ok> | REJECTED: <n_rej> | FAILED: <n_fail>
|
||||
|
||||
## OK
|
||||
- <basename>.png → id=<id>, set=<set>, outcome_path=<outcome_path>
|
||||
- ...
|
||||
|
||||
## REJECTED
|
||||
- <basename>.png — reason: <reason>
|
||||
- ...
|
||||
|
||||
## FAILED
|
||||
- <basename>.png — <motiv: missing/invalid JSON>
|
||||
- ...
|
||||
```
|
||||
|
||||
Secțiuni goale se omit (dacă REJECTED e gol, nu scrii secțiunea).
|
||||
|
||||
### Fază 6 — Afișează summary user-ului
|
||||
|
||||
Format scurt în terminal:
|
||||
|
||||
```
|
||||
/batch terminat. Total <N> screenshot-uri.
|
||||
OK: <n_ok> (trade-uri #<id1>, #<id2>, ...)
|
||||
REJECTED: <n_rej> (mutate la screenshots/needs_review/)
|
||||
FAILED: <n_fail> (mutate la screenshots/needs_review/, JSON lipsă)
|
||||
Regenerat data/jurnal.md.
|
||||
Summary scris la data/extractions/_batch_<utc_timestamp>.md.
|
||||
```
|
||||
|
||||
## Reguli
|
||||
|
||||
- **Hard cap concurrency la 5**. Chiar dacă `max_parallel` argumentat e mai mare, clamp la 5.
|
||||
- **Append serial obligatoriu**. `append_extraction` citește CSV, computează `next_id`, scrie atomic; rulat paralel = ID-uri duplicate 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 (orchestrator) muți screenshot-uri și rejected JSON-uri.
|
||||
- `/batch` folosește mereu `source=vision`. Pentru calibration, rulează `/backtest --calibration` individual pe fiecare screenshot.
|
||||
@@ -1,142 +0,0 @@
|
||||
---
|
||||
description: Adaugă rapid un trade în jurnal.csv. 6 câmpuri minim, restul derivate auto. Manual entry rapidă pentru backtest/forward paper.
|
||||
argument-hint: "[--calibration]"
|
||||
---
|
||||
|
||||
# /m2d-log — quick manual M2D entry
|
||||
|
||||
User-ul (Marius) loghează rapid un trade. 6 câmpuri esențiale obligatorii, restul derivate automat de `scripts/manual_log.py`.
|
||||
|
||||
## Workflow
|
||||
|
||||
1. **Parse `$ARGUMENTS`** — flag `--calibration` produce `source=manual_calibration`; altfel `source=manual`.
|
||||
|
||||
2. **Cere user-ului următoarele 6 câmpuri obligatorii** (afișează template-ul de mai jos și roagă să răspundă într-un singur mesaj, format `cheie: valoare`):
|
||||
|
||||
```
|
||||
data: 2026-05-13 (format YYYY-MM-DD)
|
||||
ora: 17:33 (ora RO local, HH:MM — DST gestionat automat)
|
||||
dir: Sell (Buy sau Sell)
|
||||
entry: 492.47 (prețul de intrare = close trigger candle)
|
||||
sl: 492.77 (SL absolute price)
|
||||
out: TP0→pending (SL | TP0→SL | TP0→TP1 | TP0→TP2 | TP0→pending | pending)
|
||||
```
|
||||
|
||||
Opționale (utilizatorul le poate omite — defaults aplicate):
|
||||
```
|
||||
inst: DIA (default DIA — alternative: US30, other)
|
||||
calitate: Clară (default n/a — Clară | Mai mare ca impuls | Slabă)
|
||||
tf_mic: 1min (default 1min — alternativ: 3min)
|
||||
tf_mare: 5min (default 5min — alternativ: 15min)
|
||||
note: ... (default empty)
|
||||
```
|
||||
|
||||
3. **Parsează răspunsul user-ului**. Tolerant la spații în jurul valorilor. Dacă lipsește un câmp obligatoriu → afișează ce lipsește și ceri din nou.
|
||||
|
||||
4. **Construiește dict-ul** apelând helper-ul Python (printr-un singur `python -c`):
|
||||
|
||||
```bash
|
||||
python -c "
|
||||
import json
|
||||
from scripts.manual_log import build_extraction
|
||||
d = build_extraction(
|
||||
data='<data>',
|
||||
ora_ro='<ora>',
|
||||
directie='<dir>',
|
||||
entry=<entry>,
|
||||
sl=<sl>,
|
||||
outcome_path='<out>',
|
||||
instrument='<inst_or_DIA>',
|
||||
tf_mare='<tf_mare_or_5min>',
|
||||
tf_mic='<tf_mic_or_1min>',
|
||||
calitate='<calitate_or_n/a>',
|
||||
note='<note_or_empty>',
|
||||
)
|
||||
import pathlib
|
||||
basename_no_ext = d['screenshot_file'].rsplit('.', 1)[0]
|
||||
p = pathlib.Path(f'data/extractions/{basename_no_ext}.manual.json')
|
||||
p.parent.mkdir(parents=True, exist_ok=True)
|
||||
p.write_text(json.dumps(d, ensure_ascii=False, indent=2), encoding='utf-8')
|
||||
print(json.dumps({'json_path': str(p), 'screenshot_file': d['screenshot_file']}, default=str))
|
||||
"
|
||||
```
|
||||
|
||||
Helper-ul calculează automat:
|
||||
- `ora_utc` din `ora_ro` (DST-aware Europe/Bucharest)
|
||||
- `tp0` = entry ± 0.4·|entry−sl| (sign în funcție de directie)
|
||||
- `tp1` = entry ± 0.6·|entry−sl|
|
||||
- `tp2` = entry ± |entry−sl| (simetric SL)
|
||||
- `risc_pct` = 100·|entry−sl|/entry
|
||||
- `max_reached` din `outcome_path` (SL→SL_first, TP0→SL→TP0, TP0→TP1→TP1, ...)
|
||||
- `be_moved` din `outcome_path` (False pentru SL/pending, True pentru orice TP0→...)
|
||||
- `screenshot_file` generat dacă nu e prezent: `<data>-<inst>-<ora_compactă>.png`
|
||||
- Cross-field ordering validat (Buy: sl<entry<tp0<tp1<tp2; Sell invers)
|
||||
|
||||
5. **Append la CSV**:
|
||||
|
||||
```bash
|
||||
python -c "
|
||||
from pathlib import Path
|
||||
from scripts.append_row import append_extraction
|
||||
import json
|
||||
r = append_extraction(Path('<json_path>'), source='<source>')
|
||||
print(json.dumps(r, default=str))
|
||||
"
|
||||
```
|
||||
|
||||
6. **Dacă `status == "ok"`**:
|
||||
|
||||
```bash
|
||||
python -m scripts.regenerate_md
|
||||
```
|
||||
|
||||
Apoi afișează concis:
|
||||
|
||||
```
|
||||
✅ Trade #<id> adăugat — set=<set>, outcome=<outcome_path>, pl_marius=<pl>, pl_theoretical=<pl_t>
|
||||
```
|
||||
|
||||
7. **Dacă `status == "rejected"`**:
|
||||
|
||||
```
|
||||
❌ Respins: <reason>
|
||||
```
|
||||
|
||||
Dacă `reason` conține "duplicate" → trade-ul cu acel `(screenshot_file, source)` există deja. Dacă vrei să-l suprascrii, șterge linia din `data/jurnal.csv` și re-rulează (sau cere user-ului să specifice `note: <diferit>` ca să forțeze basename diferit).
|
||||
|
||||
Dacă `reason` conține "validation" → câmpurile au violat constraint-urile pydantic; reîntrebezi user-ul ce să corecteze.
|
||||
|
||||
8. **Errori în parsing user**: dacă user-ul răspunde ambiguu (ex: lipsește `dir`, sau `entry` nu e număr), afișează ce trebuie corectat și revii la step 2 cu valorile parțiale păstrate.
|
||||
|
||||
## Reguli
|
||||
|
||||
- NU edita CSV direct.
|
||||
- NU regenera MD pe rejection.
|
||||
- Helper-ul `build_extraction` ridică `ValueError` dacă: `entry == sl`, `Buy` cu `sl >= entry`, `Sell` cu `sl <= entry`. Propaga eroarea către user cu mesaj clar.
|
||||
|
||||
## Exemple
|
||||
|
||||
**Cel mai scurt input valid**:
|
||||
```
|
||||
data: 2026-05-13
|
||||
ora: 17:33
|
||||
dir: Sell
|
||||
entry: 492.47
|
||||
sl: 492.77
|
||||
out: TP0→pending
|
||||
```
|
||||
→ generează screenshot_file=`2026-05-13-dia-1733.png`, calculează ora_utc=14:33, tp0=492.35, tp1=492.29, tp2=492.17, risc_pct=0.0609, max_reached=TP0, be_moved=True, set=A2 (Mie 17:33).
|
||||
|
||||
**Cu calitate și note**:
|
||||
```
|
||||
data: 2026-05-13
|
||||
ora: 17:33
|
||||
dir: Sell
|
||||
entry: 492.47
|
||||
sl: 492.77
|
||||
out: TP0→TP1
|
||||
calitate: Clară
|
||||
note: bună retragere dimineața, news risc zero
|
||||
```
|
||||
|
||||
**Calibrare**: `/m2d-log --calibration` → source=manual_calibration. Folosit cu `/backtest --calibration <screenshot>` pe același screenshot pentru P4 mismatch report.
|
||||
@@ -1,57 +0,0 @@
|
||||
---
|
||||
description: Afișează statistici WR / expectancy / per Set (din data/jurnal.csv). Cu --calibration arată raport P4.
|
||||
argument-hint: "[--calibration] [--overlay pl_marius|pl_theoretical] [--seed N]"
|
||||
---
|
||||
|
||||
# /stats — backtest statistics
|
||||
|
||||
Wrapper read-only peste `scripts/stats.py`. Afișează raportul stat ca atare; eventual adăugă highlight la final dacă observi un Set care îndeplinește pragul STOPPING_RULE.
|
||||
|
||||
## Arguments
|
||||
|
||||
- `--calibration` (flag) — afișează raportul P4 (mismatch field-by-field pe perechi `manual_calibration` ↔ `vision_calibration` join-uite pe `screenshot_file`).
|
||||
- `--overlay pl_marius|pl_theoretical` (opțional, default `pl_marius`) — care P/L overlay folosește.
|
||||
- `--seed N` (opțional) — seed pentru bootstrap RNG. Folosește pentru 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, pe overlay `pl_marius`.
|
||||
|
||||
## Workflow
|
||||
|
||||
1. **Parse `$ARGUMENTS`** și pasează-le direct prin:
|
||||
|
||||
```bash
|
||||
python -m scripts.stats $ARGUMENTS
|
||||
```
|
||||
|
||||
`--csv data/jurnal.csv` e default-ul scriptului; nu îl pasezi.
|
||||
|
||||
2. Rulează prin Bash. Output-ul vine pe stdout în UTF-8.
|
||||
|
||||
3. **Afișează output-ul as-is** către user. NU reformata, NU re-rezuma, NU inventa numere. Scriptul are deja format ales (tabele + secțiuni text).
|
||||
|
||||
4. **Highlight STOPPING_RULE** la final, DOAR dacă observi în output un Set care îndeplinește toate cele 3 thresholds din `STOPPING_RULE.md`:
|
||||
- `N ≥ 40` trade-uri non-pending pe acel Set
|
||||
- `WR ≥ 55%` cu Wilson 95% CI lower bound ≥ 45%
|
||||
- `Expectancy ≥ +0.20R` pe overlay `pl_marius`
|
||||
|
||||
Dacă DA pe vreun Set:
|
||||
|
||||
```
|
||||
🚀 STOPPING RULE: Set <X> îndeplinește pragurile (N=<n>, WR=<wr>, Wilson_LB=<lb>, E=<exp>R). Discută cu user dacă pornește forward paper trading la 0.25R per trade pe acest Set.
|
||||
```
|
||||
|
||||
Dacă **niciun Set** nu îndeplinește toate: nu adăuga highlight. Lasă raportul scriptului să vorbească.
|
||||
|
||||
5. **Highlight calibration P4** (în modul `--calibration`):
|
||||
- Dacă perechi `(manual_calibration, vision_calibration)` < 10 → adăugă: `Insuficient pentru P4 — continuă să acumulezi calibrare (minim 10 perechi).`
|
||||
- Dacă ≥ 10 perechi și mismatch rate > 10% pe câmpuri core (`entry/sl/tp0/tp1/tp2/outcome_path/max_reached/directie`) → adăugă: `⚠️ P4 FAIL: mismatch > 10% pe câmpuri core. Fix promptul vision agent (.claude/agents/m2d-extractor.md) și re-rulează calibrarea.`
|
||||
- Dacă ≥ 10 perechi și mismatch ≤ 10% → adăugă: `✅ P4 PASS: mismatch ≤ 10% pe câmpuri core.`
|
||||
|
||||
6. 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; doar le citești și aplici regulile STOPPING_RULE.
|
||||
- `calitate` e descriptor biased post-outcome (vezi `STOPPING_RULE.md` §3) — raportul îl afișează informational only. NU sugera user-ului să folosească `calitate` ca filtru pentru GO LIVE.
|
||||
- Highlight-ul `🚀 STOPPING RULE` e doar trigger pentru discuție; decizia GO LIVE rămâne a user-ului, cu caveats-urile semnate în `STOPPING_RULE.md`.
|
||||
Reference in New Issue
Block a user