from packages.domain.aggregate import aggregate_move_points, build_move_samples, move_points_stats_payload from packages.domain.range import resolve_range def test_build_move_samples_settled_and_pending(): from datetime import datetime from zoneinfo import ZoneInfo sh = ZoneInfo("Asia/Shanghai") ts = int(datetime(2026, 7, 30, 10, 0, tzinfo=sh).timestamp() * 1000) rows = [ {"ts_ms": ts, "side": "C", "expiry_ymd": "260720", "index_px": 3400}, # settled {"ts_ms": ts, "side": "P", "expiry_ymd": "260720", "index_px": 3450}, {"ts_ms": ts, "side": "C", "expiry_ymd": "991231", "index_px": 3500}, # pending far ] settlements = { "260720": {"settle_ts_ms": ts - 1000, "settle_index_px": 3500}, } samples, meta = build_move_samples(rows, settlements, side="both", now_ms=ts) assert meta["settled_count"] == 2 assert meta["pending_count"] == 1 assert meta["pending_expiry"] is True assert samples[0]["move_signed"] == 100.0 # 3500-3400 assert samples[1]["move_abs"] == 50.0 def test_aggregate_move_points(): from datetime import datetime from zoneinfo import ZoneInfo sh = ZoneInfo("Asia/Shanghai") ts = int(datetime(2026, 7, 30, 14, 20, tzinfo=sh).timestamp() * 1000) samples = [ {"ts_ms": ts, "move_signed": 100, "move_abs": 100}, {"ts_ms": ts, "move_signed": -40, "move_abs": 40}, ] buckets = aggregate_move_points(samples, bucket_minutes=60) b14 = next(b for b in buckets if b["bucket_start_min"] == 14 * 60) assert b14["n"] == 2 assert b14["mean_signed"] == 30.0 assert b14["mean_abs"] == 70.0 def test_move_points_payload_pending_message(): info = resolve_range("day", "2026-07-30") payload = move_points_stats_payload( [{"ts_ms": info["start_ms"] + 3600_000, "side": "C", "expiry_ymd": "991231", "index_px": 1}], {}, range_info=info, bucket_minutes=60, side="both", now_ms=info["start_ms"], ) assert payload["status"] == "ok" assert payload["pending_expiry"] is True assert payload["settled_count"] == 0 assert payload["sample_count"] == 0