주식 매도 히스토리 API 추가 (/api/portfolio/sell-history)

- sell_history 테이블 신규 생성 (db.py init_db)
- CRUD 함수 추가: add_sell_history, get_sell_history, delete_sell_history
- GET /api/portfolio/sell-history (broker, days 필터)
- POST /api/portfolio/sell-history (id 포함 저장된 레코드 반환)
- DELETE /api/portfolio/sell-history/{record_id}

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-19 22:31:17 +09:00
parent 5d6fe2f04b
commit c7401c5d9f
2 changed files with 99 additions and 0 deletions

View File

@@ -66,6 +66,23 @@ def init_db():
)
""")
conn.execute("""
CREATE TABLE IF NOT EXISTS sell_history (
id INTEGER PRIMARY KEY AUTOINCREMENT,
broker TEXT NOT NULL,
ticker TEXT NOT NULL,
name TEXT NOT NULL,
quantity INTEGER NOT NULL,
avg_price REAL NOT NULL,
sell_price REAL NOT NULL,
buy_amount REAL NOT NULL,
sell_amount REAL NOT NULL,
realized_profit REAL NOT NULL,
realized_rate REAL NOT NULL,
sold_at TEXT NOT NULL
)
""")
def save_articles(articles: List[Dict[str, str]]) -> int:
count = 0
with _conn() as conn:
@@ -188,6 +205,49 @@ def upsert_asset_snapshot(date: str, total_eval: int, total_cash: int, total_ass
""", (date, total_eval, total_cash, total_assets, now))
# --- Sell History CRUD ---
def add_sell_history(data: Dict[str, Any]) -> Dict[str, Any]:
with _conn() as conn:
cur = conn.execute("""
INSERT INTO sell_history
(broker, ticker, name, quantity, avg_price, sell_price,
buy_amount, sell_amount, realized_profit, realized_rate, sold_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""", (
data["broker"], data["ticker"], data["name"], data["quantity"],
data["avg_price"], data["sell_price"], data["buy_amount"],
data["sell_amount"], data["realized_profit"], data["realized_rate"],
data["sold_at"],
))
row = conn.execute("SELECT * FROM sell_history WHERE id = ?", (cur.lastrowid,)).fetchone()
return dict(row)
def get_sell_history(broker: str = None, days: int = None) -> List[Dict[str, Any]]:
conditions = []
params = []
if broker:
conditions.append("broker = ?")
params.append(broker)
if days:
conditions.append("sold_at >= datetime('now', ? || ' days')")
params.append(f"-{days}")
where = f"WHERE {' AND '.join(conditions)}" if conditions else ""
with _conn() as conn:
rows = conn.execute(
f"SELECT * FROM sell_history {where} ORDER BY sold_at DESC",
params,
).fetchall()
return [dict(r) for r in rows]
def delete_sell_history(record_id: int) -> bool:
with _conn() as conn:
cur = conn.execute("DELETE FROM sell_history WHERE id = ?", (record_id,))
return cur.rowcount > 0
def get_asset_snapshots(days: int = 30) -> List[Dict[str, Any]]:
with _conn() as conn:
if days == 0:

View File

@@ -15,6 +15,7 @@ from .db import (
update_portfolio_item, delete_portfolio_item,
upsert_broker_cash, get_all_broker_cash, get_broker_cash, delete_broker_cash,
upsert_asset_snapshot, get_asset_snapshots,
add_sell_history, get_sell_history, delete_sell_history,
)
from .scraper import fetch_market_news, fetch_major_indices, fetch_overseas_news
from .price_fetcher import get_current_prices
@@ -361,3 +362,41 @@ def get_snapshot_history(days: int = Query(30, ge=0)):
return {"snapshots": snapshots}
# --- Sell History API ---
class SellHistoryRequest(BaseModel):
broker: str
ticker: str
name: str
quantity: int
avg_price: float
sell_price: float
buy_amount: float
sell_amount: float
realized_profit: float
realized_rate: float
sold_at: str
@app.get("/api/portfolio/sell-history")
def list_sell_history(broker: Optional[str] = None, days: Optional[int] = None):
"""매도 내역 조회 (broker, days 필터 선택)"""
records = get_sell_history(broker=broker, days=days)
return {"records": records}
@app.post("/api/portfolio/sell-history")
def create_sell_history(req: SellHistoryRequest):
"""매도 기록 저장"""
record = add_sell_history(req.model_dump())
return record
@app.delete("/api/portfolio/sell-history/{record_id}")
def remove_sell_history(record_id: int):
"""매도 기록 삭제"""
if not delete_sell_history(record_id):
return JSONResponse(status_code=404, content={"error": "not found"})
return {"ok": True}