From 572e1cc163f8afe0b1f3b9cf506ad0e2d72c1d77 Mon Sep 17 00:00:00 2001 From: dsyoon Date: Sat, 2 Aug 2025 10:52:30 +0900 Subject: [PATCH] init --- stock_monitor.py | 69 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 57 insertions(+), 12 deletions(-) diff --git a/stock_monitor.py b/stock_monitor.py index 574a444..866cfa0 100644 --- a/stock_monitor.py +++ b/stock_monitor.py @@ -89,6 +89,44 @@ def calculate_technical_indicators(data): return data + +def get_daily_bollinger_distance(data): + """일봉 기준 볼린저 밴드 하단 근접도를 계산하여 distance, 하단 밴드, 종가를 반환한다.""" + # 데이터 인덱스가 datetime이 아니면 변환 + df = data.copy() + if not isinstance(df.index, pd.DatetimeIndex): + if 'datetime' in df.columns: + df = df.set_index('datetime') + # 일봉 리샘플링 + daily = df.resample('D').agg({'Open': 'first', + 'High': 'max', + 'Low': 'min', + 'Close': 'last', + 'Volume': 'sum'}).dropna() + + # 볼린저 밴드 계산 + daily = calculate_bollinger_bands(daily) + + if len(daily) < BOLLINGER_PERIOD: + return None, None, None + + latest_daily = daily.iloc[-1] + + # 볼린저 밴드 값이 존재하는지 확인 + if pd.isna(latest_daily['Upper']) or pd.isna(latest_daily['Lower']): + return None, None, None + + upper_band = latest_daily['Upper'] + lower_band = latest_daily['Lower'] + current_price = latest_daily['Close'] + + # 밴드 폭이 0이면 계산 불가 + if upper_band - lower_band == 0: + return None, None, None + + distance = (current_price - lower_band) / (upper_band - lower_band) + return distance, lower_band, current_price + def check_buy_signals(symbol, data): if len(data) < 60: # 최소 60일치 데이터 필요 return None @@ -96,18 +134,25 @@ def check_buy_signals(symbol, data): latest = data.iloc[-1] prev = data.iloc[-2] - # 볼린저 밴드 신호 - bb_signal = False - if isinstance(latest['Upper'], float): - upper_band = latest['Upper'] - lower_band = latest['Lower'] - current_price = latest['Close'] - else: - upper_band = latest['Upper'].iloc[0] - lower_band = latest['Lower'].iloc[0] - current_price = latest['Close'].iloc[0] - - distance = (current_price - lower_band) / (upper_band - lower_band) + # 볼린저 밴드 신호 (일봉 기준) + distance, lower_band, current_price = get_daily_bollinger_distance(data) + + if distance is None: + # 일봉 볼린저 계산이 불가능한 경우 기존 주기 데이터를 사용 + if isinstance(latest['Upper'], float): + upper_band = latest['Upper'] + lower_band = latest['Lower'] + current_price = latest['Close'] + else: + upper_band = latest['Upper'].iloc[0] + lower_band = latest['Lower'].iloc[0] + current_price = latest['Close'].iloc[0] + + if upper_band - lower_band != 0: + distance = (current_price - lower_band) / (upper_band - lower_band) + else: + distance = 1 # 밴드폭이 0이면 신호 미충족으로 간주 + bb_signal = distance < BOLLINGER_THRESHOLD # U자 반등 후 이전 고점 돌파 여부 계산 (BREAKOUT)