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,78 @@
#!/usr/bin/env python3
"""ledger backlog 신호 조회·일괄 처리 (dry-run / execute)."""
from __future__ import annotations
import argparse
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
SRC = ROOT / "src"
if str(SRC) not in sys.path:
sys.path.insert(0, str(SRC))
from bithumb.config import load_settings
from bithumb.operations.reconcile import inspect_ops_backlog
from bithumb.operations.runner import OperationsRunner
def main() -> int:
"""CLI 진입점."""
parser = argparse.ArgumentParser(description="운영 backlog 신호 reconcile")
parser.add_argument(
"--dry-run",
action="store_true",
help="pending 목록만 출력 (기본)",
)
parser.add_argument(
"--execute",
action="store_true",
help="OperationsRunner tick 1회로 backlog 처리",
)
parser.add_argument(
"--no-sync",
action="store_true",
help="execute 시 캔들 sync 생략",
)
args = parser.parse_args()
settings = load_settings()
info = inspect_ops_backlog(settings)
merged = info["merged_pending"]
summary = info["summary"]
print("=== backlog inspect ===")
print(f"signal_refresh: {info.get('signal_refresh')}")
print(f"force_tail_refresh: {info.get('force_tail_refresh')}")
print(f"ledger_pending: {len(info['ledger_pending'])}")
print(f"catchup_pending: {len(info['catchup_pending'])}")
print(f"merged_pending: {len(merged)}")
print(f"backlog_signal_count: {summary['backlog_signal_count']}")
print(f"backlog_oldest: {summary['backlog_oldest_datetime']}")
print(f"backlog_dropped (per tick limit): {info['backlog_dropped']}")
for sig in merged[:50]:
print(f" {sig['datetime']} {sig['side']} bar={sig.get('bar_index')}")
if len(merged) > 50:
print(f" ... 외 {len(merged) - 50}")
if args.execute:
from bithumb.operations.runner import OperationsRunner as OpsRunner
if settings.ops_mode == "live":
print("\n경고: live execute — 실제 주문이 발생할 수 있습니다.")
runner = OpsRunner(settings)
report = runner.tick(sync_candles=not args.no_sync)
print(f"\nexecute 완료: 체결 {len(report.get('executions', []))}")
print(f"ledger_pending_count: {report.get('ledger_pending_count')}")
print(f"backlog_dropped_count: {report.get('backlog_dropped_count')}")
return 0
if not args.dry_run and not args.execute:
print("\n(--dry-run 기본, --execute 로 처리)")
return 0
if __name__ == "__main__":
raise SystemExit(main())