""" Timeframe 매핑 및 OHLCV 데이터 무결성 테스트. 이 테스트는 다음 버그를 사전에 발견하기 위해 작성됨: - main.py의 minutes_to_timeframe()과 indicators.py의 tf_map 불일치 - candle_count 설정으로 인한 SMA 계산 데이터 부족 버그 배경: - minutes_to_timeframe()이 "240m"을 반환했으나 tf_map에 매핑이 없어 pyupbit에 그대로 전달되어 일봉 데이터가 반환됨 (2025-12-17 발견) """ import pytest class TestTimeframeMappingIntegrity: """main.py와 indicators.py 간 timeframe 매핑 일관성 테스트.""" def test_all_minutes_to_timeframe_outputs_are_in_tf_map(self): """minutes_to_timeframe()이 반환 가능한 모든 값이 tf_map에 매핑되어 있어야 함. 이 테스트가 실패하면: - indicators.py의 tf_map에 누락된 timeframe 매핑이 있음 - 해당 timeframe 사용 시 잘못된 데이터가 반환될 수 있음 """ from main import minutes_to_timeframe # indicators.py에서 tf_map 추출 # (함수 내부에 있으므로 직접 정의 - 동기화 필요) tf_map = { "1m": "minute1", "3m": "minute3", "5m": "minute5", "10m": "minute10", "15m": "minute15", "30m": "minute30", "60m": "minute60", "240m": "minute240", "1h": "minute60", "4h": "minute240", "1d": "day", "1w": "week", } # Upbit이 지원하는 분봉 + 일봉 입력값 test_minutes = [1, 3, 5, 10, 15, 30, 60, 240, 1440] missing_mappings = [] for minutes in test_minutes: timeframe = minutes_to_timeframe(minutes) if timeframe not in tf_map: missing_mappings.append((minutes, timeframe)) if missing_mappings: error_msg = f"tf_map에 누락된 매핑: {missing_mappings}" pytest.fail(error_msg) def test_edge_case_timeframes_are_mapped(self): """비표준 분봉 입력도 tf_map에 매핑된 값으로 변환되어야 함. 예: 120분 → 60m (근사값), 300분 → 240m """ from main import minutes_to_timeframe tf_map = { "1m": "minute1", "3m": "minute3", "5m": "minute5", "10m": "minute10", "15m": "minute15", "30m": "minute30", "60m": "minute60", "240m": "minute240", "1h": "minute60", "4h": "minute240", "1d": "day", "1w": "week", } # 비표준 분봉 → 근사 변환 → tf_map에 있어야 함 edge_cases = [2, 7, 45, 90, 120, 180, 300, 500] for minutes in edge_cases: timeframe = minutes_to_timeframe(minutes) if timeframe not in tf_map: pytest.fail(f"minutes_to_timeframe({minutes}) = '{timeframe}'가 tf_map에 없음") class TestCandleCountConfiguration: """candle_count 설정 관련 테스트.""" def test_candle_count_sufficient_for_sma200(self): """candle_count가 SMA200 계산에 충분해야 함. signals.py에서 미완성 봉 1개를 제외하므로, SMA200 계산에는 최소 201개가 필요함. """ from src.config import load_config config = load_config() candle_count = config.get("candle_count", 200) sma_long = config.get("sma_long", 200) # 미완성 봉 1개 제외 후에도 SMA 계산 가능해야 함 effective_candles = candle_count - 1 if effective_candles < sma_long: error_msg = ( f"candle_count={candle_count}, effective={effective_candles}, " f"sma_long={sma_long}. config.json의 candle_count를 {sma_long + 1}로 설정하세요." ) pytest.fail(error_msg) class TestOHLCVDataInterval: """OHLCV 데이터 간격 무결성 테스트.""" @pytest.mark.parametrize( "timeframe,expected_minutes", [ ("1m", 1), ("5m", 5), ("15m", 15), ("60m", 60), ("240m", 240), # 핵심: 4시간 = 240분 ("1h", 60), ("4h", 240), ], ) def test_tf_map_produces_correct_pyupbit_interval(self, timeframe, expected_minutes): """tf_map 변환 결과가 pyupbit 형식에 맞고, 올바른 간격을 의미해야 함.""" tf_map = { "1m": "minute1", "3m": "minute3", "5m": "minute5", "10m": "minute10", "15m": "minute15", "30m": "minute30", "60m": "minute60", "240m": "minute240", "1h": "minute60", "4h": "minute240", "1d": "day", "1w": "week", } pyupbit_interval = tf_map.get(timeframe) assert pyupbit_interval is not None, f"tf_map에 '{timeframe}' 매핑 누락" # pyupbit 형식 검증: "minuteN" 또는 "day"/"week" if timeframe not in ("1d", "1w"): if not pyupbit_interval.startswith("minute"): pytest.fail(f"분봉 timeframe '{timeframe}'의 pyupbit 형식 오류: {pyupbit_interval}") actual_minutes = int(pyupbit_interval.replace("minute", "")) if actual_minutes != expected_minutes: pytest.fail(f"'{timeframe}' 간격 불일치: 예상={expected_minutes}분, 실제={actual_minutes}분")