[2026-05-18] 수익 현황 확인 스크립트 추가 (status.py)

This commit is contained in:
2026-05-18 10:46:25 +09:00
parent 566ecd678e
commit d7190cb1be
+131
View File
@@ -0,0 +1,131 @@
"""
app/ai/status.py
현재 수익 현황 조회 — 실현손익 + 미실현손익 합산
"""
import asyncio
import os
import sys
from datetime import datetime
from pathlib import Path
ROOT = Path(__file__).parent.parent.parent
sys.path.insert(0, str(ROOT))
def load_env():
env_path = ROOT / ".env"
if not env_path.exists():
return
for line in env_path.read_text(encoding="utf-8").splitlines():
line = line.strip()
if not line or line.startswith("#") or "=" not in line:
continue
k, _, v = line.partition("=")
k, v = k.strip(), v.strip()
if " #" in v:
v = v[:v.index(" #")]
v = v.strip().strip('"').strip("'")
if k and v and k not in os.environ:
os.environ[k] = v
load_env()
from app.db.models import get_conn
from app.execution.kis_client import KISClient
async def get_status():
today = datetime.now().strftime("%Y-%m-%d")
now_str = datetime.now().strftime("%H:%M:%S")
# ── 실현손익 (오늘 종료된 거래) ──
with get_conn() as conn:
rows = conn.execute("""
SELECT ticker, name, entry_price, exit_price, quantity, pnl, exit_reason
FROM trades
WHERE date=? AND exit_time IS NOT NULL
ORDER BY exit_time DESC
""", (today,)).fetchall()
open_rows = conn.execute("""
SELECT ticker, name, entry_price, quantity
FROM positions
""").fetchall()
realized_trades = rows
realized_pnl = sum(r[5] for r in rows if r[5] is not None)
wins = sum(1 for r in rows if r[5] and r[5] > 0)
losses = sum(1 for r in rows if r[5] and r[5] <= 0)
# ── 미실현손익 (현재 포지션) ──
unrealized_pnl = 0.0
position_details = []
if open_rows:
kis = KISClient()
await kis.ensure_token()
for ticker, name, entry_price, qty in open_rows:
try:
info = await kis.get_price(ticker)
current = info.get("current", 0)
if entry_price and current:
unreal = (current - entry_price) * qty
pct = (current - entry_price) / entry_price * 100
unrealized_pnl += unreal
position_details.append({
"ticker": ticker, "name": name,
"entry": entry_price, "current": current,
"qty": qty, "pnl": unreal, "pct": pct,
})
except Exception as e:
position_details.append({
"ticker": ticker, "name": name,
"entry": entry_price, "current": 0,
"qty": qty, "pnl": 0.0, "pct": 0.0,
"error": str(e),
})
await asyncio.sleep(0.5)
total_pnl = realized_pnl + unrealized_pnl
# ── 출력 ──
print(f"\n{'='*50}")
print(f" StockBot 현황 [{today} {now_str}]")
print(f"{'='*50}")
# 총 손익
sign = "+" if total_pnl >= 0 else ""
print(f"\n 총 손익: {sign}{total_pnl:,.0f}")
print(f" 실현손익: {'+' if realized_pnl >= 0 else ''}{realized_pnl:,.0f}원 ({wins}{losses}패)")
print(f" 미실현손익: {'+' if unrealized_pnl >= 0 else ''}{unrealized_pnl:,.0f}원 (보유 {len(position_details)}종목)")
# 보유 포지션
if position_details:
print(f"\n [보유 포지션]")
for p in position_details:
if "error" in p:
print(f" {p['name']}({p['ticker']}) {p['qty']}주 — 현재가 조회 실패")
else:
sign = "+" if p['pnl'] >= 0 else ""
print(f" {p['name']}({p['ticker']}) {p['qty']}주 | "
f"매수 {p['entry']:,.0f} → 현재 {p['current']:,.0f} | "
f"{sign}{p['pnl']:,.0f}원 ({sign}{p['pct']:.2f}%)")
# 오늘 거래 내역
if realized_trades:
print(f"\n [오늘 체결]")
for r in realized_trades[:10]:
ticker, name, ep, xp, qty, pnl, reason = r
pnl_str = f"{'+' if pnl and pnl >= 0 else ''}{pnl:,.0f}" if pnl else "0원"
print(f" {name}({ticker}) {qty}주 | {ep:,.0f}{xp:,.0f} | {pnl_str} [{reason}]")
if len(realized_trades) > 10:
print(f" ... 외 {len(realized_trades)-10}")
else:
print(f"\n 오늘 체결 내역 없음")
print(f"\n{'='*50}\n")
return {"total_pnl": total_pnl, "realized": realized_pnl, "unrealized": unrealized_pnl}
if __name__ == "__main__":
asyncio.run(get_status())