feat(vol_breakout): 15m 현물 롱 라이브·모니터·cron 운영 추가
vol_breakout 멀티종목 tick, vol_live HTML 모니터, 마감 봉만 저장하는 캔들 다운로드, 텔레그램 체결 알림, cron/watch 감시 스크립트 및 테스트를 포함한다. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
73
tests/test_candle_bars.py
Normal file
73
tests/test_candle_bars.py
Normal file
@@ -0,0 +1,73 @@
|
||||
"""캔들 봉 마감·다운로드 필터."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from bithumb.data.candle_bars import (
|
||||
is_closed_candle,
|
||||
last_closed_bar_open,
|
||||
only_closed_candles,
|
||||
)
|
||||
|
||||
|
||||
def test_is_closed_candle_15m() -> None:
|
||||
"""15분봉은 마감 시각 이후에만 closed."""
|
||||
bar_open = datetime(2026, 6, 27, 16, 15, 0)
|
||||
assert not is_closed_candle(
|
||||
bar_open, 15, now=datetime(2026, 6, 27, 16, 29, 59)
|
||||
)
|
||||
assert is_closed_candle(
|
||||
bar_open, 15, now=datetime(2026, 6, 27, 16, 30, 0)
|
||||
)
|
||||
|
||||
|
||||
def test_only_closed_candles_filters_forming_bar() -> None:
|
||||
"""API 배치에서 진행 중 봉은 제외."""
|
||||
rows = [
|
||||
("2026-06-27 16:00:00", 100.0, 110.0, 95.0, 105.0, 1.0),
|
||||
("2026-06-27 16:15:00", 105.0, 105.0, 105.0, 105.0, 0.1),
|
||||
]
|
||||
now = datetime(2026, 6, 27, 16, 20, 0)
|
||||
closed = only_closed_candles(rows, 15, now=now)
|
||||
assert len(closed) == 1
|
||||
assert closed[0][0] == "2026-06-27 16:00:00"
|
||||
|
||||
|
||||
def test_last_closed_bar_open() -> None:
|
||||
"""진행 중인 16:15~16:30 봉 기준 최근 마감은 16:00."""
|
||||
now = datetime(2026, 6, 27, 16, 20, 0)
|
||||
assert last_closed_bar_open(now, 15) == datetime(2026, 6, 27, 16, 0, 0)
|
||||
|
||||
now = datetime(2026, 6, 27, 16, 30, 0)
|
||||
assert last_closed_bar_open(now, 15) == datetime(2026, 6, 27, 16, 15, 0)
|
||||
|
||||
|
||||
def test_only_closed_candles_keeps_all_historical() -> None:
|
||||
"""과거 봉은 모두 마감으로 간주."""
|
||||
rows = [
|
||||
("2026-06-26 20:00:00", 1.0, 2.0, 0.5, 1.5, 10.0),
|
||||
("2026-06-26 20:15:00", 1.5, 2.5, 1.0, 2.0, 8.0),
|
||||
]
|
||||
now = datetime(2026, 6, 27, 22, 0, 0)
|
||||
assert len(only_closed_candles(rows, 15, now=now)) == 2
|
||||
|
||||
|
||||
def test_delete_incomplete_tail(tmp_path) -> None:
|
||||
"""미마감 최신 봉이 DB에서 제거된다."""
|
||||
from bithumb.data.candle_store import CandleStore
|
||||
|
||||
db = tmp_path / "t.db"
|
||||
store = CandleStore(db)
|
||||
rows = [
|
||||
("2026-06-27 16:00:00", 100.0, 110.0, 95.0, 105.0, 1.0),
|
||||
("2026-06-27 16:15:00", 105.0, 105.0, 105.0, 105.0, 0.1),
|
||||
]
|
||||
store.upsert_rows("NEAR", "NEAR", 15, rows)
|
||||
deleted = store.delete_incomplete_tail(
|
||||
"NEAR", 15, now=datetime(2026, 6, 27, 16, 20, 0)
|
||||
)
|
||||
assert deleted == 1
|
||||
_, _, db_max = store.get_range("NEAR", 15)
|
||||
assert db_max == datetime(2026, 6, 27, 16, 0, 0)
|
||||
store.close()
|
||||
Reference in New Issue
Block a user