143 lines
5.1 KiB
Markdown
143 lines
5.1 KiB
Markdown
---
|
||
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.
|