39 lines
1.6 KiB
Python
39 lines
1.6 KiB
Python
import sys
|
|
from pathlib import Path
|
|
from datetime import datetime, timedelta
|
|
|
|
import pandas as pd
|
|
|
|
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
|
from app.services.altcoin_confirm import _is_candidate_fresh, _event_time_from_age
|
|
|
|
|
|
def test_candidate_fresh_when_state_detected_recently_without_current_trigger():
|
|
cand = {"symbol": "TEST/USDT", "detected_at": datetime.now().isoformat()}
|
|
ok, reason, events = _is_candidate_fresh(cand, [], max_hours=6)
|
|
assert ok is True
|
|
assert reason == "fresh_candidate_state"
|
|
assert events and events[0]["age_hours"] <= 6
|
|
|
|
|
|
def test_candidate_stale_when_no_recent_trigger_and_old_state():
|
|
cand = {"symbol": "TEST/USDT", "detected_at": (datetime.now() - timedelta(hours=8)).isoformat()}
|
|
ok, reason, events = _is_candidate_fresh(cand, [], max_hours=6)
|
|
assert ok is False
|
|
assert reason == "stale_structure_background_only"
|
|
assert events and events[0]["age_hours"] > 6
|
|
|
|
|
|
def test_candidate_unknown_age_is_not_fresh_without_current_trigger():
|
|
ok, reason, events = _is_candidate_fresh({"symbol": "TEST/USDT"}, [], max_hours=6)
|
|
assert ok is False
|
|
assert reason == "structure_candidate_unknown_age"
|
|
assert events == []
|
|
|
|
|
|
def test_event_time_from_age_maps_latest_and_previous_bar():
|
|
now = pd.Timestamp(datetime.now().replace(minute=0, second=0, microsecond=0))
|
|
df = pd.DataFrame({"timestamp": [now - pd.Timedelta(hours=2), now - pd.Timedelta(hours=1), now]})
|
|
assert _event_time_from_age(df, 0) == now.to_pydatetime()
|
|
assert _event_time_from_age(df, 1) == (now - pd.Timedelta(hours=1)).to_pydatetime()
|