176 lines
5.1 KiB
Python
176 lines
5.1 KiB
Python
"""Tests for scripts.manual_log — derivation logic + vision_schema compatibility."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import pytest
|
|
|
|
from scripts.manual_log import build_extraction, OUTCOME_TO_BE_MOVED, OUTCOME_TO_MAX_REACHED, ro_to_utc
|
|
from scripts.vision_schema import M2DExtraction
|
|
|
|
|
|
def test_buy_happy_path():
|
|
d = build_extraction(
|
|
data="2026-05-13",
|
|
ora_ro="17:33",
|
|
directie="Buy",
|
|
entry=497.42,
|
|
sl=496.80,
|
|
outcome_path="TP0→TP1",
|
|
)
|
|
# Satisfies pydantic
|
|
M2DExtraction.model_validate(d)
|
|
assert d["directie"] == "Buy"
|
|
assert d["entry"] == 497.42
|
|
assert d["sl"] == 496.80
|
|
# tp0 = entry + 0.4 * 0.62 = 497.42 + 0.248 = 497.668
|
|
assert abs(d["tp0"] - 497.668) < 0.01
|
|
assert abs(d["tp1"] - 497.792) < 0.01
|
|
assert abs(d["tp2"] - 498.04) < 0.01
|
|
assert d["max_reached"] == "TP1"
|
|
assert d["be_moved"] is True
|
|
|
|
|
|
def test_sell_happy_path():
|
|
d = build_extraction(
|
|
data="2026-05-13",
|
|
ora_ro="17:33",
|
|
directie="Sell",
|
|
entry=492.47,
|
|
sl=492.77,
|
|
outcome_path="TP0→TP1",
|
|
)
|
|
M2DExtraction.model_validate(d)
|
|
assert d["directie"] == "Sell"
|
|
# For Sell: tp0 = entry - 0.4 * 0.30 = 492.47 - 0.12 = 492.35
|
|
assert abs(d["tp0"] - 492.35) < 0.01
|
|
assert abs(d["tp1"] - 492.29) < 0.01
|
|
assert abs(d["tp2"] - 492.17) < 0.01
|
|
assert d["sl"] > d["entry"] > d["tp0"] > d["tp1"] > d["tp2"]
|
|
|
|
|
|
def test_ora_utc_dst_summer():
|
|
# May = EEST = UTC+3
|
|
d = build_extraction(
|
|
data="2026-05-13", ora_ro="17:33", directie="Sell",
|
|
entry=492.47, sl=492.77, outcome_path="pending",
|
|
)
|
|
assert d["ora_utc"] == "14:33"
|
|
|
|
|
|
def test_ora_utc_dst_winter():
|
|
# January = EET = UTC+2
|
|
d = build_extraction(
|
|
data="2026-01-15", ora_ro="17:00", directie="Buy",
|
|
entry=400, sl=399, outcome_path="pending",
|
|
)
|
|
assert d["ora_utc"] == "15:00"
|
|
|
|
|
|
def test_outcome_sl_max_reached_consistent():
|
|
d = build_extraction(
|
|
data="2026-05-13", ora_ro="17:33", directie="Buy",
|
|
entry=400, sl=399, outcome_path="SL",
|
|
)
|
|
M2DExtraction.model_validate(d)
|
|
assert d["max_reached"] == "SL_first"
|
|
assert d["be_moved"] is False
|
|
|
|
|
|
def test_outcome_tp0_pending_max_reached_tp0():
|
|
d = build_extraction(
|
|
data="2026-05-13", ora_ro="17:33", directie="Buy",
|
|
entry=400, sl=399, outcome_path="TP0→pending",
|
|
)
|
|
M2DExtraction.model_validate(d)
|
|
assert d["max_reached"] == "TP0"
|
|
assert d["be_moved"] is True
|
|
|
|
|
|
def test_outcome_tp0_sl_be_moved_true():
|
|
d = build_extraction(
|
|
data="2026-05-13", ora_ro="17:33", directie="Buy",
|
|
entry=400, sl=399, outcome_path="TP0→SL",
|
|
)
|
|
M2DExtraction.model_validate(d)
|
|
assert d["max_reached"] == "TP0"
|
|
assert d["be_moved"] is True # rule-enforced
|
|
|
|
|
|
def test_explicit_max_reached_override():
|
|
d = build_extraction(
|
|
data="2026-05-13", ora_ro="17:33", directie="Buy",
|
|
entry=400, sl=399, outcome_path="pending",
|
|
max_reached="TP0",
|
|
)
|
|
M2DExtraction.model_validate(d)
|
|
assert d["max_reached"] == "TP0"
|
|
|
|
|
|
def test_screenshot_file_generated():
|
|
d = build_extraction(
|
|
data="2026-05-13", ora_ro="17:33", directie="Sell",
|
|
entry=492.47, sl=492.77, outcome_path="pending",
|
|
instrument="DIA",
|
|
)
|
|
assert d["screenshot_file"] == "2026-05-13-dia-1733.png"
|
|
|
|
|
|
def test_screenshot_file_explicit():
|
|
d = build_extraction(
|
|
data="2026-05-13", ora_ro="17:33", directie="Sell",
|
|
entry=492.47, sl=492.77, outcome_path="pending",
|
|
screenshot_file="custom.png",
|
|
)
|
|
assert d["screenshot_file"] == "custom.png"
|
|
|
|
|
|
def test_risc_pct_computed():
|
|
d = build_extraction(
|
|
data="2026-05-13", ora_ro="17:33", directie="Sell",
|
|
entry=500.00, sl=501.00, outcome_path="pending",
|
|
)
|
|
assert abs(d["risc_pct"] - 0.20) < 0.001 # 1/500 = 0.002 = 0.20%
|
|
|
|
|
|
def test_entry_equals_sl_raises():
|
|
with pytest.raises(ValueError, match="zero risk"):
|
|
build_extraction(
|
|
data="2026-05-13", ora_ro="17:33", directie="Buy",
|
|
entry=500, sl=500, outcome_path="pending",
|
|
)
|
|
|
|
|
|
def test_buy_inverted_ordering_raises():
|
|
with pytest.raises(ValueError, match="Buy"):
|
|
build_extraction(
|
|
data="2026-05-13", ora_ro="17:33", directie="Buy",
|
|
entry=400, sl=401, outcome_path="pending", # sl > entry for Buy
|
|
)
|
|
|
|
|
|
def test_sell_inverted_ordering_raises():
|
|
with pytest.raises(ValueError, match="Sell"):
|
|
build_extraction(
|
|
data="2026-05-13", ora_ro="17:33", directie="Sell",
|
|
entry=400, sl=399, outcome_path="pending", # sl < entry for Sell
|
|
)
|
|
|
|
|
|
def test_optional_fields_defaults():
|
|
d = build_extraction(
|
|
data="2026-05-13", ora_ro="17:33", directie="Buy",
|
|
entry=400, sl=399, outcome_path="pending",
|
|
)
|
|
assert d["instrument"] == "DIA"
|
|
assert d["tf_mare"] == "5min"
|
|
assert d["tf_mic"] == "1min"
|
|
assert d["calitate"] == "n/a"
|
|
assert d["confidence"] == "high"
|
|
assert d["ambiguities"] == []
|
|
assert d["note"] == ""
|
|
|
|
|
|
def test_ro_to_utc_helper():
|
|
assert ro_to_utc("2026-05-13", "17:33") == "14:33"
|
|
assert ro_to_utc("2026-01-15", "10:00") == "08:00"
|