RSI DCA 전략 (신규) - rsi_dca_engine/runner/control: 1분봉 RSI(14) 종목별 기준선 상향 돌파 시 정액 매수, 일 상한, 신호 45분 유효, 킬스위치, 인터벌 변경 시 커서 재초기화, 체결가 거래소 보정 - scripts: 3_run_rsi_dca(.py/_cron.sh), 백테스트·인터벌 비교, go-live 스위치 - 설정: RSI_DCA_* (모드·종목·기준선·종목별 오버라이드·일 상한 등) 모니터 (vol_live_monitor / vol_monitor_chart) - 분봉 탭(/api/candles), RSI(14) 패널·종목별 기준선, 3패널 시간축 정렬, KST 표기 - 자동매수 ON/OFF 패널(/api/rsi/status·toggle), 빌드 해시 기반 자동 새로고침, 지연 경고 - 요약표: 거래소 평균매입가 기준 보유원금·수익률, 총평가 손익, 수익률순 동적 정렬 - 잔고 스냅샷을 계좌 전체 조회 1회로 통합, 실시간 시세 반영 데이터·수집 - candle_store/loader: SQL 범위·LIMIT 조회로 대형 테이블 전량 스캔 제거 - 절전·재부팅 후 공백 자동 백필(gap_backfill, 00_backfill_gaps) 및 cron 연동 - 수집 cron 분할(매분 핵심 분봉·5분 전체), 프로젝트 한정 lock 패턴, exec 제거로 lock 정리 복구 - 모니터 종목(VOL_MONITOR_SYMBOLS)·수집 종목 7종 분리, 한글 코인명 추가 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
48 lines
2.0 KiB
Python
48 lines
2.0 KiB
Python
"""절전·재부팅 후 캔들 공백 백필 계획 테스트."""
|
||
|
||
from __future__ import annotations
|
||
|
||
from datetime import datetime, timedelta
|
||
|
||
from bithumb.data.gap_backfill import coverage_minutes, days_needed, plan_backfill
|
||
|
||
|
||
def test_coverage_and_days() -> None:
|
||
assert coverage_minutes(1) == 180.0 # 200봉 × 1분 × 0.9
|
||
assert coverage_minutes(15) == 2700.0
|
||
assert days_needed(50) == 2 # 1일 미만 공백 → ceil(0.03)+1
|
||
assert days_needed(1500) == 3 # 1.04일 → 2+1
|
||
|
||
|
||
def test_plan_no_gap_within_incremental_range() -> None:
|
||
now = datetime(2026, 9, 10, 8, 11)
|
||
db = {"XRP": {1: now - timedelta(minutes=50), 15: now - timedelta(minutes=60)}}
|
||
plan = plan_backfill(db, now=now, intervals=[1, 15])
|
||
assert plan.empty and plan.describe() == "no gap"
|
||
|
||
|
||
def test_plan_detects_1m_gap_only() -> None:
|
||
now = datetime(2026, 9, 10, 8, 11)
|
||
# 1분봉 5시간 공백(> 180분) → 백필, 15분봉은 200봉(50h) 범위 내
|
||
db = {
|
||
"XRP": {1: now - timedelta(hours=5), 15: now - timedelta(hours=5)},
|
||
"SUI": {1: now - timedelta(minutes=30), 15: now - timedelta(minutes=30)},
|
||
}
|
||
plan = plan_backfill(db, now=now, intervals=[1, 15])
|
||
assert plan.needs == {"XRP": {1: 2}}
|
||
assert plan.symbols() == ["XRP"] and plan.intervals() == [1] and plan.max_days() == 2
|
||
|
||
|
||
def test_plan_long_outage_multiple_intervals_and_cap() -> None:
|
||
now = datetime(2026, 9, 20, 0, 0)
|
||
db = {"ETH": {1: now - timedelta(days=5), 15: now - timedelta(days=5), 1440: now - timedelta(days=5)}}
|
||
plan = plan_backfill(db, now=now, intervals=[1, 15, 1440], max_days=4)
|
||
# 1m·15m은 5일 공백 → 6일 필요하나 상한 4일, 일봉은 200봉(180일) 범위 내
|
||
assert plan.needs == {"ETH": {1: 4, 15: 4}}
|
||
|
||
|
||
def test_plan_skips_never_loaded_tables() -> None:
|
||
now = datetime(2026, 9, 10, 8, 11)
|
||
plan = plan_backfill({"NEW": {1: None, 15: None}}, now=now, intervals=[1, 15])
|
||
assert plan.empty
|