diff --git a/app_streamlit.py b/app_streamlit.py index 3f8e666..a61f8e2 100644 --- a/app_streamlit.py +++ b/app_streamlit.py @@ -55,7 +55,7 @@ KST = timedelta(hours=9) _last_alert = {"strong_long": 0, "strong_short": 0, "long": 0, "short": 0, "vol_long": 0, "vol_short": 0, "short_caution": 0} _last_fired_candle = {"strong_long": None, "strong_short": None, "long": None, "short": None, "vol_long": None, "vol_short": None, "short_caution": None} -STOP_LOSS_PCT = 0.10 +STOP_LOSS_PCT = 0.03 LONG_SIGNALS = {"strong_long_signal", "long_signal", "vol_long_signal"} SHORT_SIGNALS = {"strong_short_signal", "short_signal", "vol_short_signal"} TF_LABEL_MAP = { @@ -77,19 +77,23 @@ def send_telegram(message: str): except Exception as e: print(f"[텔레그램 오류] {e}") +SIG_DEFS = [ + ("strong_long_signal", "strong_long", "🟢 강한 롱", "long"), + ("strong_short_signal", "strong_short", "🔴 강한 숏", "short"), + ("long_signal", "long", "🔼 일반 롱", "long"), + ("short_signal", "short", "🔽 일반 숏", "short"), + ("vol_long_signal", "vol_long", "🔼 볼륨 롱", "long"), + ("vol_short_signal", "vol_short", "🔽 볼륨 숏", "short"), + ("short_caution_signal","short_caution","⚠️ 숏 주의", "caution"), +] + def check_and_alert(df, symbol, interval): global _long_entry, _short_entry now = time.time() recent = df.tail(3) - for sig, key, label in [ - ("strong_long_signal", "strong_long", "🟢 강한 롱 진입 신호"), - ("strong_short_signal", "strong_short", "🔴 강한 숏 진입 신호"), - ("long_signal", "long", "🔼 롱 진입 신호"), - ("short_signal", "short", "🔽 숏 진입 신호"), - ("vol_long_signal", "vol_long", "🔼 볼륨급등 롱 신호"), - ("vol_short_signal", "vol_short", "🔽 볼륨급등 숏 신호"), - ("short_caution_signal","short_caution","⚠️ 숏 진입 주의 신호"), - ]: + + eligible = [] + for sig, key, sub_label, direction in SIG_DEFS: if sig not in recent.columns: continue triggered = recent[recent[sig].fillna(False)] @@ -100,51 +104,72 @@ def check_and_alert(df, symbol, interval): continue if now - _last_alert[key] <= ALERT_COOLDOWN: continue + eligible.append({ + "sig": sig, "key": key, "sub_label": sub_label, + "direction": direction, "candle_time": candle_time, "row": triggered.iloc[-1], + }) - tf_label = TF_LABEL_MAP.get(interval, interval) + if not eligible: + groups = {} + else: + groups = {"long": [], "short": [], "caution": []} + for e in eligible: + groups[e["direction"]].append(e) + + tf_label = TF_LABEL_MAP.get(interval, interval) + + def _send_group(group): + if not group: + return + candle_time = group[0]["candle_time"] candle_time_str = pd.Timestamp(candle_time).strftime("%Y-%m-%d %H:%M") - - if sig in LONG_SIGNALS: - entry_price = float(triggered.iloc[-1]["open"]) - stop_price = entry_price * (1 - STOP_LOSS_PCT) + sub_labels = " + ".join(e["sub_label"] for e in group) + direction = group[0]["direction"] + if direction == "caution": msg = ( - f"{label}\n{symbol} {tf_label}\n" - f"시간: {candle_time_str}\n" - f"진입가: {entry_price:.2f}\n" - f"손절가: {stop_price:.2f}" - ) - _long_entry = {"price": entry_price, "stop": stop_price, "open_time": candle_time, "entry_msg": msg} - elif sig in SHORT_SIGNALS: - entry_price = float(triggered.iloc[-1]["open"]) - stop_price = entry_price * (1 + STOP_LOSS_PCT) - msg = ( - f"{label}\n{symbol} {tf_label}\n" - f"시간: {candle_time_str}\n" - f"진입가: {entry_price:.2f}\n" - f"손절가: {stop_price:.2f}" - ) - _short_entry = {"price": entry_price, "stop": stop_price, "open_time": candle_time, "entry_msg": msg} - else: - msg = ( - f"{label}\n{symbol} {tf_label}\n" + f"{sub_labels} 신호\n{symbol} {tf_label}\n" f"시간: {candle_time_str}" ) + send_telegram(msg) + else: + entry_price = float(group[0]["row"]["open"]) + if direction == "long": + stop_price = entry_price * (1 - STOP_LOSS_PCT) + else: + stop_price = entry_price * (1 + STOP_LOSS_PCT) + msg = ( + f"{sub_labels} 진입 신호\n{symbol} {tf_label}\n" + f"시간: {candle_time_str}\n" + f"진입가: {entry_price:,.2f}\n" + f"손절가: {stop_price:,.2f}" + ) + entry_record = {"price": entry_price, "stop": stop_price, "open_time": candle_time, "entry_msg": msg} + if direction == "long": + global _long_entry + _long_entry = entry_record + else: + global _short_entry + _short_entry = entry_record + send_telegram(msg) + for e in group: + _last_alert[e["key"]] = now + _last_fired_candle[e["key"]] = e["candle_time"] - send_telegram(msg) - _last_alert[key] = now - _last_fired_candle[key] = candle_time + _send_group(groups.get("long", [])) + _send_group(groups.get("short", [])) + _send_group(groups.get("caution", [])) current_price = float(df.iloc[-1]["close"]) if _long_entry is not None and current_price <= _long_entry["stop"]: send_telegram( f"[손절가알림]\n{_long_entry['entry_msg']}\n" - f"현재가: {current_price:.2f}" + f"현재가: {current_price:,.2f}" ) _long_entry = None if _short_entry is not None and current_price >= _short_entry["stop"]: send_telegram( f"[손절가알림]\n{_short_entry['entry_msg']}\n" - f"현재가: {current_price:.2f}" + f"현재가: {current_price:,.2f}" ) _short_entry = None