""" 실리콘투 볼린저밴드 카운팅 디버깅 v5 확정 규칙: 1. 시초가 > 240일선 ← 신규 추가 2. 종가 > BB 상단 × 1.03 3. 60일선 아래 다녀온 후: 시초가 > 이전 피크 종가 (추가 조건) 4. 240일선 아래 → 완전 리셋 5. 60일선 아래 → 카운트 유지 """ import pandas as pd import numpy as np import FinanceDataReader as fdr import warnings from datetime import datetime, timezone, timedelta warnings.filterwarnings('ignore') _KST = timezone(timedelta(hours=9)) TICKER = '420770' DEBUG_FROM = '2025-05-02' DEBUG_TO = datetime.now(_KST).strftime('%Y-%m-%d') # 오늘(KST) — 매 실행시 최신(장중이면 현재가) BB_WINDOW = 20 BB_STD = 2 MA_240 = 240 MA_60 = 60 BB_THRESHOLD = 1.030 print(f"({TICKER}) 데이터 로드 중...") df = fdr.DataReader(TICKER, '2022-01-01', DEBUG_TO) print(f"데이터: {len(df)}일치\n") df['ma20'] = df['Close'].rolling(BB_WINDOW).mean() df['std20'] = df['Close'].rolling(BB_WINDOW).std() df['bb_upper'] = df['ma20'] + BB_STD * df['std20'] df['bb_lower'] = df['ma20'] - BB_STD * df['std20'] df['ma60'] = df['Close'].rolling(MA_60).mean() df['ma240'] = df['Close'].rolling(MA_240).mean() count = 0 peak_close = 0 was_below_60 = False counts = [] peak_list = [] flags = [] for i in range(len(df)): c = df['Close'].iloc[i] o = df['Open'].iloc[i] u = df['bb_upper'].iloc[i] m60 = df['ma60'].iloc[i] m240 = df['ma240'].iloc[i] if pd.isna(m240) or pd.isna(u) or pd.isna(m60): counts.append(count) peak_list.append(peak_close) flags.append('데이터부족') continue if c < m240: count = 0 peak_close = 0 was_below_60 = False flag = '240선이탈→리셋' elif c < m60: was_below_60 = True flag = f'60선이탈→유지(count={count})' else: # 핵심 조건들 open_above_240 = (o > m240) # ← 신규: 시초가 > 240일선 clearly_above = (c > u * BB_THRESHOLD) # 종가 > BB상단 × 1.020 if not open_above_240: flag = f'시초가({o:,.0f})<240선({m240:,.0f})→무효' elif clearly_above: if was_below_60: if o > peak_close: count += 1 peak_close = c was_below_60 = False flag = f'+{count} (60선복귀,시초가>{peak_close:,.0f})' else: flag = f'BB돌파but시초가({o:,.0f})≤피크({peak_close:,.0f})→스킵' else: count += 1 peak_close = max(peak_close, c) flag = f'+{count}' else: pct = (c / u - 1) * 100 flag = f'종가미달(BB대비{pct:+.1f}%)' counts.append(count) peak_list.append(peak_close) flags.append(flag) df['bb_count'] = counts df['peak_close'] = peak_list df['flag'] = flags mask = (df.index >= DEBUG_FROM) & (df.index <= DEBUG_TO) debug_df = df[mask].copy() print("=" * 115) print(f"날짜별 카운팅 ({DEBUG_FROM} ~ {DEBUG_TO})") print("=" * 115) print(f"{'날짜':<12} {'종가':>7} {'시가':>7} {'BB상단':>8} {'BB×1.03':>9} " f"{'60MA':>7} {'240MA':>7} {'피크':>8} {'카운트':>6} 판정") print("-" * 115) prev_count = 0 for date, row in debug_df.iterrows(): cnt = int(row['bb_count']) flag = row['flag'] is_notable = (cnt != prev_count or '리셋' in flag or '이탈' in flag or '복귀' in flag or '스킵' in flag or '무효' in flag) if is_notable: mark = ' ◀' if cnt > prev_count else '' print(f"{str(date)[:10]:<12} " f"{row['Close']:>7,.0f} " f"{row['Open']:>7,.0f} " f"{row['bb_upper']:>8,.0f} " f"{row['bb_upper']*BB_THRESHOLD:>9,.0f} " f"{row['ma60']:>7,.0f} " f"{row['ma240']:>7,.0f} " f"{row['peak_close']:>8,.0f} " f"{cnt:>6} {flag}{mark}") prev_count = cnt print("\n" + "=" * 60) print("카운트 증가한 날들 요약") print("=" * 60) prev = 0 for date, row in debug_df.iterrows(): cnt = int(row['bb_count']) if cnt > prev: print(f" {str(date)[:10]}: {row['flag']}" f" (종가:{row['Close']:,.0f}, 시가:{row['Open']:,.0f}, " f"BB×1.03:{row['bb_upper']*BB_THRESHOLD:,.0f}, " f"240MA:{row['ma240']:,.0f})") prev = cnt print(f"\n최대 카운트: {debug_df['bb_count'].max()}")