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:
dsyoon
2026-06-29 08:31:14 +09:00
parent 8413bbd536
commit 72de8d534e
58 changed files with 6008 additions and 1286 deletions

View File

@@ -0,0 +1,77 @@
"""exchange reconcile 단위 테스트."""
from __future__ import annotations
from datetime import datetime, timedelta
from bithumb.operations.exchange_reconcile import (
_match_orders_to_signals,
_trade_from_exchange_order,
)
def test_match_orders_to_signals_by_side_and_time() -> None:
"""같은 side·시간 창 안에서 주문-신호 1:1 매칭."""
sig_dt = datetime(2026, 6, 14, 10, 48, 0)
signals = [
{"datetime": sig_dt.strftime("%Y-%m-%d %H:%M:%S"), "side": "buy", "bar_index": 1},
]
order_dt = sig_dt + timedelta(minutes=30)
orders = [
{
"uuid": "order-1",
"side": "bid",
"created_at": order_dt.isoformat(),
"executed_volume": "0.001",
"executed_funds": "100000",
},
]
matches = _match_orders_to_signals(
orders,
signals,
match_window_min=720,
known_uuids=set(),
)
assert len(matches) == 1
assert matches[0][0]["side"] == "buy"
assert matches[0][1]["uuid"] == "order-1"
def test_match_skips_known_uuid() -> None:
"""이미 history에 있는 uuid는 재매칭하지 않는다."""
sig_dt = datetime(2026, 6, 14, 11, 0, 0)
signals = [
{"datetime": sig_dt.strftime("%Y-%m-%d %H:%M:%S"), "side": "sell", "bar_index": 2},
]
orders = [
{
"uuid": "already-used",
"side": "ask",
"created_at": (sig_dt + timedelta(minutes=5)).isoformat(),
"executed_volume": "0.001",
"executed_funds": "100000",
},
]
matches = _match_orders_to_signals(
orders,
signals,
match_window_min=720,
known_uuids={"already-used"},
)
assert matches == []
def test_trade_from_exchange_order_buy() -> None:
"""매수 체결 → executed TradeResult."""
sig = {"datetime": "2026-06-14 10:48:00", "side": "buy", "price": 140_000_000.0}
order = {
"uuid": "x",
"side": "bid",
"executed_volume": "0.002",
"executed_funds": "280000",
}
trade = _trade_from_exchange_order(sig, order)
assert trade.executed is True
assert trade.side == "buy"
assert trade.order_coin == 0.002
assert trade.api_response == order