10% 손절가 도달 알림 추가

## 동작
- 진입 신호(strong/일반/볼륨급등 × long/short)가 발화한 캔들의 close 가격을
  진입가로 기록.
- 롱 진입 → 손절가 = 진입가 * 0.90, 현재가가 손절가 이하로 내려가면 알림.
- 숏 진입 → 손절가 = 진입가 * 1.10, 현재가가 손절가 이상으로 올라가면 알림.
- 발화 시 해당 방향의 진입 상태를 클리어 → 새 진입 신호 전까지 같은 손절가
  알림은 재발송되지 않음.
- 기본 비율은 STOP_LOSS_PCT 상수(0.10)로 분리. 추후 조정 용이.

## 메시지 예시
🛑 롱 손절가 도달 (-10%)
BTCUSDT 5m
진입가: 76200.00
손절가: 68580.00
현재가: 68500.00

## 주의
- short_caution_signal 은 진입 신호가 아니므로 손절가 추적 대상에서 제외.
- 롱/숏 진입 상태는 독립 추적 — 한쪽 손절 도달이 다른 쪽 상태에 영향 없음.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
ILSEON-RYU
2026-05-01 10:10:14 +09:00
parent 2449c7766d
commit 43a5c52ec5
+32
View File
@@ -55,6 +55,12 @@ 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_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} _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
LONG_SIGNALS = {"strong_long_signal", "long_signal", "vol_long_signal"}
SHORT_SIGNALS = {"strong_short_signal", "short_signal", "vol_short_signal"}
_long_entry = None
_short_entry = None
# ────────────────────────────────────────────── # ──────────────────────────────────────────────
# 텔레그램 # 텔레그램
# ────────────────────────────────────────────── # ──────────────────────────────────────────────
@@ -66,6 +72,7 @@ def send_telegram(message: str):
print(f"[텔레그램 오류] {e}") print(f"[텔레그램 오류] {e}")
def check_and_alert(df, symbol, interval): def check_and_alert(df, symbol, interval):
global _long_entry, _short_entry
now = time.time() now = time.time()
recent = df.tail(3) recent = df.tail(3)
for sig, key, msg in [ for sig, key, msg in [
@@ -91,6 +98,31 @@ def check_and_alert(df, symbol, interval):
_last_alert[key] = now _last_alert[key] = now
_last_fired_candle[key] = candle_time _last_fired_candle[key] = candle_time
if sig in LONG_SIGNALS:
entry_price = float(triggered.iloc[-1]["close"])
_long_entry = {"price": entry_price, "stop": entry_price * (1 - STOP_LOSS_PCT), "open_time": candle_time}
elif sig in SHORT_SIGNALS:
entry_price = float(triggered.iloc[-1]["close"])
_short_entry = {"price": entry_price, "stop": entry_price * (1 + STOP_LOSS_PCT), "open_time": candle_time}
current_price = float(df.iloc[-1]["close"])
if _long_entry is not None and current_price <= _long_entry["stop"]:
send_telegram(
f"🛑 롱 손절가 도달 (-{int(STOP_LOSS_PCT * 100)}%)\n{symbol} {interval}\n"
f"진입가: {_long_entry['price']:.2f}\n"
f"손절가: {_long_entry['stop']:.2f}\n"
f"현재가: {current_price:.2f}"
)
_long_entry = None
if _short_entry is not None and current_price >= _short_entry["stop"]:
send_telegram(
f"🛑 숏 손절가 도달 (+{int(STOP_LOSS_PCT * 100)}%)\n{symbol} {interval}\n"
f"진입가: {_short_entry['price']:.2f}\n"
f"손절가: {_short_entry['stop']:.2f}\n"
f"현재가: {current_price:.2f}"
)
_short_entry = None
# ────────────────────────────────────────────── # ──────────────────────────────────────────────
# 데이터 수집 # 데이터 수집
# ────────────────────────────────────────────── # ──────────────────────────────────────────────