From 49aee49a82f03cd651b5791da2266353d2700172 Mon Sep 17 00:00:00 2001 From: dsyoon Date: Sun, 7 Sep 2025 19:21:48 +0900 Subject: [PATCH] init --- monitor.py | 137 ++++++++++++++++++++++++----------------------------- 1 file changed, 62 insertions(+), 75 deletions(-) diff --git a/monitor.py b/monitor.py index 736472f..99de20a 100644 --- a/monitor.py +++ b/monitor.py @@ -227,18 +227,26 @@ class Monitor(HTS): # ------------- Strategy ------------- def buy_sell_ticker_1h(self, symbol: str, data: pd.DataFrame, balances=None, is_inverse: bool = False) -> bool: try: + # BUY_MINUTE_LIMIT 이내라면 매수하지 않음 + current_time = datetime.now() + last_buy_dt = self.buy_cooldown.get(symbol, {}).get('buy', {}).get('datetime') + if last_buy_dt: + time_diff = current_time - last_buy_dt + if time_diff.total_seconds() < BUY_MINUTE_LIMIT: + print(f"{symbol}: 매수 금지 중 (남은 시간: {BUY_MINUTE_LIMIT - time_diff.total_seconds():.0f}초)") + return False + # 신호 생성 및 최신 포인트 확인 data = self.annotate_signals(symbol, data) if data['point'].iloc[-1] != 1: return False - # 인버스 데이터: 매수 신호를 매도로 처리 (fall_6p, deviation40 만 허용) if is_inverse: + # 인버스 데이터: 매수 신호를 매도로 처리 (fall_6p, deviation40 만 허용) # 허용된 인버스 매도 신호만 처리 last_signal = str(data['signal'].iloc[-1]) if 'signal' in data.columns else '' if last_signal not in ['fall_6p', 'deviation40']: return False - current_time = datetime.now() available_balance = 0 try: if balances and symbol in balances: @@ -260,91 +268,70 @@ class Monitor(HTS): self.sendMsg("[KRW-COIN]\n" + f"• 매도 [COIN] {KR_COINS[symbol]} ({symbol}): {data['signal'].iloc[-1]} ({'₩'}{data['Close'].iloc[-1]:.4f})") return True - check_5_week_lowest = False - - # 5주봉이 20주봉이나 40주봉보다 아래에 있는지 체크 - try: - # Convert hourly data to week-based rolling periods (5, 20, 40 weeks) - hours_in_week = 24 * 7 # 168 hours - period_5w = 5 * hours_in_week # 840 hours - period_20w = 20 * hours_in_week # 3,360 hours - period_40w = 40 * hours_in_week # 6,720 hours - - if len(data) >= period_40w: - wma5 = data['Close'].rolling(window=period_5w).mean().iloc[-1] - wma20 = data['Close'].rolling(window=period_20w).mean().iloc[-1] - wma40 = data['Close'].rolling(window=period_40w).mean().iloc[-1] - - # 5-week MA is the lowest among 5, 20, 40 week MAs - if (wma5 < wma20) and (wma5 < wma40): - check_5_week_lowest = True - - except Exception: - # Ignore errors in MA calculation so as not to block trading logic - pass - - buy_amount = 5100 - current_time = datetime.now() - if data['signal'].iloc[-1] == 'fall_6p': - if data['Close'].iloc[-1] > 100: - buy_amount = 600000 - else: - buy_amount = 300000 - - last_buy_dt = self.buy_cooldown.get(symbol, {}).get('buy', {}).get('datetime') - if last_buy_dt and self.last_signal.get(symbol) == 'fall_6p': - time_diff = current_time - last_buy_dt - if time_diff.total_seconds() < 4000: - print(f"{symbol}: 매수 금지 중 (남은 시간: {600 - time_diff.total_seconds():.0f}초)") - return False else: - last_buy_dt = self.buy_cooldown.get(symbol, {}).get('buy', {}).get('datetime') - if last_buy_dt: - time_diff = current_time - last_buy_dt - if time_diff.total_seconds() < BUY_MINUTE_LIMIT: - print(f"{symbol}: 매수 금지 중 (남은 시간: {BUY_MINUTE_LIMIT - time_diff.total_seconds():.0f}초)") - return False + check_5_week_lowest = False - if data['signal'].iloc[-1] == 'movingaverage': - buy_amount = 10000 - elif data['signal'].iloc[-1] == 'deviation40': - buy_amount = 50000 - elif data['signal'].iloc[-1] == 'deviation240': - buy_amount = 60000 - elif data['signal'].iloc[-1] == 'deviation1440': - if symbol in ['BONK', 'PEPE', 'TON']: - buy_amount = 100000 + try: + # 5주봉이 20주봉이나 40주봉보다 아래에 있는지 체크 + # Convert hourly data to week-based rolling periods (5, 20, 40 weeks) + hours_in_week = 24 * 7 # 168 hours + period_5w = 5 * hours_in_week # 840 hours + period_20w = 20 * hours_in_week # 3,360 hours + period_40w = 40 * hours_in_week # 6,720 hours + + if len(data) >= period_40w: + wma5 = data['Close'].rolling(window=period_5w).mean().iloc[-1] + wma20 = data['Close'].rolling(window=period_20w).mean().iloc[-1] + wma40 = data['Close'].rolling(window=period_40w).mean().iloc[-1] + + # 5-week MA is the lowest among 5, 20, 40 week MAs + if (wma5 < wma20) and (wma5 < wma40): + check_5_week_lowest = True + + except Exception: + # Ignore errors in MA calculation so as not to block trading logic + pass + + # 체크: fall_6p + buy_amount = 5100 + current_time = datetime.now() + if data['signal'].iloc[-1] == 'fall_6p': + if data['Close'].iloc[-1] > 100: + buy_amount = 300000 else: buy_amount = 150000 + elif data['signal'].iloc[-1] == 'movingaverage': + buy_amount = 10000 + elif data['signal'].iloc[-1] == 'deviation40': + buy_amount = 30000 + elif data['signal'].iloc[-1] == 'deviation240': + buy_amount = 7000 + elif data['signal'].iloc[-1] == 'deviation1440': + if symbol in ['BONK', 'PEPE', 'TON']: + buy_amount = 50000 + else: + buy_amount = 70000 if data['signal'].iloc[-1] in ['movingaverage', 'deviation40', 'deviation240', 'deviation1440']: if check_5_week_lowest: buy_amount *= 2 - """ - # 분봉 시스널이 없을 때는 이전 봉보다 높으면 60분 마다 매수 - if data['point'].iloc[-1] != 1: - last_buy_dt = self.buy_cooldown.get(symbol, {}).get('buy', {}).get('datetime') - if last_buy_dt: - time_diff = current_time - last_buy_dt - if time_diff.total_seconds() < 3600: - print(f"{symbol}: 매수 금지 중 (남은 시간: {3600 - time_diff.total_seconds():.0f}초)") - return False - """ - buy_amount = self.hts.buyCoinMarket(symbol, buy_amount) + # 매수를 진행함 + buy_amount = self.hts.buyCoinMarket(symbol, buy_amount) - if self.cooldown_file is not None: # 최근 매수 신호를 함께 기록하여 [신규] 포맷으로 저장 - try: - self.last_signal[symbol] = str(data['signal'].iloc[-1]) - except Exception: - self.last_signal[symbol] = '' - self.buy_cooldown.setdefault(symbol, {})['buy'] = {'datetime': current_time, 'signal': str(data['signal'].iloc[-1])} - # 매수를 저장함 - self._save_buy_cooldown() + if self.cooldown_file is not None: + try: + self.last_signal[symbol] = str(data['signal'].iloc[-1]) + except Exception: + self.last_signal[symbol] = '' + self.buy_cooldown.setdefault(symbol, {})['buy'] = {'datetime': current_time, 'signal': str(data['signal'].iloc[-1])} - print(f"{KR_COINS[symbol]} ({symbol}) [{data['signal'].iloc[-1]}], 현재가: {data['Close'].iloc[-1]:.4f}, 20분간 매수 금지 시작") - self.sendMsg("{}".format(self.format_message(symbol, KR_COINS[symbol], data['Close'].iloc[-1], data['signal'].iloc[-1], buy_amount))) + # 매수를 저장함 + self._save_buy_cooldown() + + print(f"{KR_COINS[symbol]} ({symbol}) [{data['signal'].iloc[-1]}], 현재가: {data['Close'].iloc[-1]:.4f}, 20분간 매수 금지 시작") + self.sendMsg("{}".format(self.format_message(symbol, KR_COINS[symbol], data['Close'].iloc[-1], data['signal'].iloc[-1], buy_amount))) except Exception as e: print(f"Error buying {symbol}: {str(e)}") return False