feat(stock-lab): 포트폴리오 매입가(purchase_price) 컬럼 분리 + 원달러 환율 부호 보정

원달러 환율:
- 네이버 환율 change_value에 부호가 없어 프론트에서 항상 상승으로 인식되던 문제
- direction(red/blue) 기반으로 +/- 부호 prepend

포트폴리오:
- portfolio 테이블에 purchase_price 컬럼 추가 (기존 row는 avg_price로 백필)
- avg_price(평균단가): 손익률 계산 기준 (cost_basis)
- purchase_price(매입가): 총 매입 금액 요약 표시 기준
- API: PortfolioItemRequest/UpdateRequest에 purchase_price(Optional) 추가
- GET /api/portfolio 응답 holdings에 purchase_price 포함, summary.total_buy는 매입가 합계, total_profit_rate는 평균단가 기준

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-15 01:58:02 +09:00
parent 7acc1979c8
commit de015a2440
3 changed files with 48 additions and 19 deletions

View File

@@ -1,7 +1,7 @@
import sqlite3
import os
import hashlib
from typing import List, Dict, Any
from typing import List, Dict, Any, Optional
DB_PATH = "/app/data/stock.db"
@@ -41,11 +41,18 @@ def init_db():
name TEXT NOT NULL,
quantity INTEGER NOT NULL,
avg_price INTEGER NOT NULL,
purchase_price INTEGER,
created_at TEXT DEFAULT (datetime('now','localtime')),
updated_at TEXT DEFAULT (datetime('now','localtime'))
)
""")
# 마이그레이션: 기존 DB에 purchase_price 컬럼 없으면 추가 후 avg_price로 백필
_pf_cols = {r["name"] for r in conn.execute("PRAGMA table_info(portfolio)").fetchall()}
if "purchase_price" not in _pf_cols:
conn.execute("ALTER TABLE portfolio ADD COLUMN purchase_price INTEGER")
conn.execute("UPDATE portfolio SET purchase_price = avg_price WHERE purchase_price IS NULL")
conn.execute("""
CREATE TABLE IF NOT EXISTS broker_cash (
id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -125,11 +132,17 @@ def get_latest_articles(limit: int = 20, category: str = None) -> List[Dict[str,
# --- Portfolio CRUD ---
def add_portfolio_item(broker: str, ticker: str, name: str, quantity: int, avg_price: int) -> int:
def add_portfolio_item(
broker: str, ticker: str, name: str, quantity: int, avg_price: int,
purchase_price: Optional[int] = None,
) -> int:
# purchase_price 미입력 시 avg_price로 기본값 설정 (하위호환)
if purchase_price is None:
purchase_price = avg_price
with _conn() as conn:
cur = conn.execute(
"INSERT INTO portfolio (broker, ticker, name, quantity, avg_price) VALUES (?, ?, ?, ?, ?)",
(broker, ticker, name, quantity, avg_price),
"INSERT INTO portfolio (broker, ticker, name, quantity, avg_price, purchase_price) VALUES (?, ?, ?, ?, ?, ?)",
(broker, ticker, name, quantity, avg_price, purchase_price),
)
return cur.lastrowid
@@ -147,7 +160,7 @@ def get_portfolio_item(item_id: int) -> Dict[str, Any] | None:
def update_portfolio_item(item_id: int, **kwargs) -> bool:
allowed = {"broker", "ticker", "name", "quantity", "avg_price"}
allowed = {"broker", "ticker", "name", "quantity", "avg_price", "purchase_price"}
fields = {k: v for k, v in kwargs.items() if k in allowed and v is not None}
if not fields:
return False