135 lines
4.3 KiB
Python
135 lines
4.3 KiB
Python
"""Helper for manual M2D trade entry — derives full M2DExtraction dict from minimal user inputs.
|
||
|
||
User provides 6 required fields: data, ora_ro, directie, entry, sl, outcome_path.
|
||
All other fields default or are computed:
|
||
- tp0 = entry ± 0.4 × |entry - sl|
|
||
- tp1 = entry ± 0.6 × |entry - sl|
|
||
- tp2 = entry ± 1.0 × |entry - sl| (symmetric with sl)
|
||
- risc_pct = 100 × |entry - sl| / entry
|
||
- ora_utc = ora_ro converted via Europe/Bucharest (DST-aware)
|
||
- max_reached derived from outcome_path
|
||
- be_moved = True if outcome contains TP0 else False
|
||
- tf_mare/tf_mic default 5min/1min
|
||
- calitate default 'n/a'
|
||
- confidence = 'high' (manual entry)
|
||
- screenshot_file generated if not provided: <data>-<instrument>-<ora_ro>.png
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from datetime import date, datetime, time
|
||
from typing import Literal
|
||
from zoneinfo import ZoneInfo
|
||
|
||
RO_TZ = ZoneInfo("Europe/Bucharest")
|
||
UTC_TZ = ZoneInfo("UTC")
|
||
|
||
|
||
OUTCOME_TO_MAX_REACHED = {
|
||
"SL": "SL_first",
|
||
"TP0→SL": "TP0",
|
||
"TP0→TP1": "TP1",
|
||
"TP0→TP2": "TP2",
|
||
"TP0→pending": "TP0",
|
||
"pending": "SL_first", # placeholder; user can override
|
||
}
|
||
|
||
OUTCOME_TO_BE_MOVED = {
|
||
"SL": False,
|
||
"TP0→SL": True, # BE move should have happened; True = rule-enforced
|
||
"TP0→TP1": True,
|
||
"TP0→TP2": True,
|
||
"TP0→pending": True,
|
||
"pending": False,
|
||
}
|
||
|
||
|
||
def ro_to_utc(data_iso: str, ora_ro_str: str) -> str:
|
||
"""Convert (YYYY-MM-DD, HH:MM RO) -> HH:MM UTC string, DST-aware."""
|
||
d = date.fromisoformat(data_iso)
|
||
t = datetime.strptime(ora_ro_str, "%H:%M").time()
|
||
dt_ro = datetime.combine(d, t, tzinfo=RO_TZ)
|
||
dt_utc = dt_ro.astimezone(UTC_TZ)
|
||
return dt_utc.strftime("%H:%M")
|
||
|
||
|
||
def build_extraction(
|
||
data: str,
|
||
ora_ro: str,
|
||
directie: Literal["Buy", "Sell"],
|
||
entry: float,
|
||
sl: float,
|
||
outcome_path: Literal["SL", "TP0→SL", "TP0→TP1", "TP0→TP2", "TP0→pending", "pending"],
|
||
instrument: Literal["DIA", "US30", "other"] = "DIA",
|
||
tf_mare: Literal["5min", "15min"] = "5min",
|
||
tf_mic: Literal["1min", "3min"] = "1min",
|
||
calitate: Literal["Clară", "Mai mare ca impuls", "Slabă", "n/a"] = "n/a",
|
||
max_reached: Literal["SL_first", "TP0", "TP1", "TP2"] | None = None,
|
||
be_moved: bool | None = None,
|
||
screenshot_file: str | None = None,
|
||
note: str = "",
|
||
) -> dict:
|
||
"""Build a M2DExtraction-compatible dict from minimal manual inputs.
|
||
|
||
Derived fields:
|
||
- ora_utc from ora_ro (DST-aware)
|
||
- tp0/tp1/tp2 from entry/sl geometry
|
||
- risc_pct from |entry-sl|/entry
|
||
- max_reached/be_moved from outcome_path (overridable)
|
||
- screenshot_file generated from data+instrument+ora_ro if not provided
|
||
|
||
The returned dict satisfies scripts.vision_schema.M2DExtraction.
|
||
"""
|
||
if entry == sl:
|
||
raise ValueError("entry == sl — zero risk distance")
|
||
|
||
risk_abs = abs(entry - sl)
|
||
risc_pct = round(100 * risk_abs / entry, 4)
|
||
|
||
if directie == "Buy":
|
||
if sl >= entry:
|
||
raise ValueError(f"Buy: sl ({sl}) must be < entry ({entry})")
|
||
tp0 = round(entry + 0.4 * risk_abs, 4)
|
||
tp1 = round(entry + 0.6 * risk_abs, 4)
|
||
tp2 = round(entry + risk_abs, 4)
|
||
else: # Sell
|
||
if sl <= entry:
|
||
raise ValueError(f"Sell: sl ({sl}) must be > entry ({entry})")
|
||
tp0 = round(entry - 0.4 * risk_abs, 4)
|
||
tp1 = round(entry - 0.6 * risk_abs, 4)
|
||
tp2 = round(entry - risk_abs, 4)
|
||
|
||
ora_utc = ro_to_utc(data, ora_ro)
|
||
|
||
if max_reached is None:
|
||
max_reached = OUTCOME_TO_MAX_REACHED[outcome_path]
|
||
if be_moved is None:
|
||
be_moved = OUTCOME_TO_BE_MOVED[outcome_path]
|
||
|
||
if screenshot_file is None:
|
||
ora_compact = ora_ro.replace(":", "")
|
||
screenshot_file = f"{data}-{instrument.lower()}-{ora_compact}.png"
|
||
|
||
return {
|
||
"screenshot_file": screenshot_file,
|
||
"data": data,
|
||
"ora_utc": ora_utc,
|
||
"instrument": instrument,
|
||
"directie": directie,
|
||
"tf_mare": tf_mare,
|
||
"tf_mic": tf_mic,
|
||
"calitate": calitate,
|
||
"entry": round(float(entry), 4),
|
||
"sl": round(float(sl), 4),
|
||
"tp0": tp0,
|
||
"tp1": tp1,
|
||
"tp2": tp2,
|
||
"risc_pct": risc_pct,
|
||
"outcome_path": outcome_path,
|
||
"max_reached": max_reached,
|
||
"be_moved": be_moved,
|
||
"confidence": "high",
|
||
"ambiguities": [],
|
||
"note": note,
|
||
}
|