[2026-05-18] 수익 현황 확인 스크립트 추가 (status.py)
This commit is contained in:
@@ -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())
|
||||
Reference in New Issue
Block a user