diff --git a/.env.example b/.env.example
index 789730b..894f8de 100644
--- a/.env.example
+++ b/.env.example
@@ -1,6 +1,9 @@
ACCESS_KEY=
SECRET_KEY=
+# 트레일링 스탑: 최고가 대비 -N% 도달 시 청산
+STOP_LOSS_PCT=5
+
# 타임 스탑: N시간 경과 후 수익률 M% 미만이면 청산
TIME_STOP_HOURS=24
TIME_STOP_MIN_GAIN_PCT=3
diff --git a/core/monitor.py b/core/monitor.py
index d2dbd59..a4538eb 100644
--- a/core/monitor.py
+++ b/core/monitor.py
@@ -10,7 +10,7 @@ from . import trader
logger = logging.getLogger(__name__)
-STOP_LOSS_PCT = 0.10 # 최고가 대비 -10% → 트레일링 스탑
+STOP_LOSS_PCT = float(os.getenv("STOP_LOSS_PCT", "5")) / 100 # 최고가 대비 -N% → 트레일링 스탑
CHECK_INTERVAL = 10 # 10초마다 체크
# 타임 스탑: N시간 경과 후 수익률이 M% 미만이면 청산
diff --git a/core/notify.py b/core/notify.py
new file mode 100644
index 0000000..4526382
--- /dev/null
+++ b/core/notify.py
@@ -0,0 +1,51 @@
+"""Telegram 매매 알림."""
+
+from __future__ import annotations
+
+import logging
+import os
+
+import requests
+
+logger = logging.getLogger(__name__)
+
+_TOKEN = os.getenv("TELEGRAM_TRADE_TOKEN", "")
+_CHAT_ID = os.getenv("TELEGRAM_CHAT_ID", "")
+_API = "https://api.telegram.org/bot{token}/sendMessage"
+
+
+def _send(text: str) -> None:
+ if not _TOKEN or not _CHAT_ID:
+ logger.warning("Telegram 설정 없음, 알림 스킵")
+ return
+ try:
+ requests.post(
+ _API.format(token=_TOKEN),
+ json={"chat_id": _CHAT_ID, "text": text, "parse_mode": "HTML"},
+ timeout=5,
+ )
+ except Exception as e:
+ logger.error(f"Telegram 알림 실패: {e}")
+
+
+def notify_buy(ticker: str, price: float, amount: float, invested_krw: int) -> None:
+ _send(
+ f"📈 [매수] {ticker}\n"
+ f"가격: {price:,.0f}원\n"
+ f"수량: {amount}\n"
+ f"투자금: {invested_krw:,}원"
+ )
+
+
+def notify_sell(ticker: str, price: float, pnl_pct: float, reason: str) -> None:
+ emoji = "✅" if pnl_pct >= 0 else "🔴"
+ _send(
+ f"{emoji} [매도] {ticker}\n"
+ f"가격: {price:,.0f}원\n"
+ f"수익률: {pnl_pct:+.1f}%\n"
+ f"사유: {reason}"
+ )
+
+
+def notify_error(message: str) -> None:
+ _send(f"⚠️ [오류]\n{message}")
diff --git a/core/trader.py b/core/trader.py
index b50b3ff..22786ec 100644
--- a/core/trader.py
+++ b/core/trader.py
@@ -11,6 +11,7 @@ from typing import Optional
import pyupbit
from dotenv import load_dotenv
+from .notify import notify_buy, notify_sell, notify_error
load_dotenv()
@@ -80,9 +81,11 @@ def buy(ticker: str) -> bool:
f"[매수] {ticker} @ {current:,.0f}원 | "
f"수량={amount} | 투자금={order_krw:,}원"
)
+ notify_buy(ticker, current, amount, order_krw)
return True
except Exception as e:
logger.error(f"매수 예외 {ticker}: {e}")
+ notify_error(f"매수 실패 {ticker}: {e}")
return False
@@ -106,10 +109,12 @@ def sell(ticker: str, reason: str = "") -> bool:
f"[매도] {ticker} @ {current:,.0f}원 | "
f"수익률={pnl:+.1f}% | 사유={reason}"
)
+ notify_sell(ticker, current, pnl, reason)
del _positions[ticker]
return True
except Exception as e:
logger.error(f"매도 예외 {ticker}: {e}")
+ notify_error(f"매도 실패 {ticker}: {e}")
return False