stock 실계좌 예수금 정보 추가
This commit is contained in:
@@ -46,6 +46,15 @@ def init_db():
|
|||||||
)
|
)
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
conn.execute("""
|
||||||
|
CREATE TABLE IF NOT EXISTS broker_cash (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
broker TEXT UNIQUE NOT NULL,
|
||||||
|
cash INTEGER NOT NULL DEFAULT 0,
|
||||||
|
updated_at TEXT DEFAULT (datetime('now','localtime'))
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
|
||||||
def save_articles(articles: List[Dict[str, str]]) -> int:
|
def save_articles(articles: List[Dict[str, str]]) -> int:
|
||||||
count = 0
|
count = 0
|
||||||
with _conn() as conn:
|
with _conn() as conn:
|
||||||
@@ -120,3 +129,33 @@ def delete_portfolio_item(item_id: int) -> bool:
|
|||||||
with _conn() as conn:
|
with _conn() as conn:
|
||||||
cur = conn.execute("DELETE FROM portfolio WHERE id = ?", (item_id,))
|
cur = conn.execute("DELETE FROM portfolio WHERE id = ?", (item_id,))
|
||||||
return cur.rowcount > 0
|
return cur.rowcount > 0
|
||||||
|
|
||||||
|
|
||||||
|
# --- Broker Cash CRUD ---
|
||||||
|
|
||||||
|
def upsert_broker_cash(broker: str, cash: int) -> None:
|
||||||
|
now = __import__("datetime").datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
with _conn() as conn:
|
||||||
|
conn.execute("""
|
||||||
|
INSERT INTO broker_cash (broker, cash, updated_at)
|
||||||
|
VALUES (?, ?, ?)
|
||||||
|
ON CONFLICT(broker) DO UPDATE SET cash = excluded.cash, updated_at = excluded.updated_at
|
||||||
|
""", (broker, cash, now))
|
||||||
|
|
||||||
|
|
||||||
|
def get_all_broker_cash() -> List[Dict[str, Any]]:
|
||||||
|
with _conn() as conn:
|
||||||
|
rows = conn.execute("SELECT * FROM broker_cash ORDER BY broker").fetchall()
|
||||||
|
return [dict(r) for r in rows]
|
||||||
|
|
||||||
|
|
||||||
|
def get_broker_cash(broker: str) -> Dict[str, Any] | None:
|
||||||
|
with _conn() as conn:
|
||||||
|
row = conn.execute("SELECT * FROM broker_cash WHERE broker = ?", (broker,)).fetchone()
|
||||||
|
return dict(row) if row else None
|
||||||
|
|
||||||
|
|
||||||
|
def delete_broker_cash(broker: str) -> bool:
|
||||||
|
with _conn() as conn:
|
||||||
|
cur = conn.execute("DELETE FROM broker_cash WHERE broker = ?", (broker,))
|
||||||
|
return cur.rowcount > 0
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ from .db import (
|
|||||||
init_db, save_articles, get_latest_articles,
|
init_db, save_articles, get_latest_articles,
|
||||||
add_portfolio_item, get_all_portfolio, get_portfolio_item,
|
add_portfolio_item, get_all_portfolio, get_portfolio_item,
|
||||||
update_portfolio_item, delete_portfolio_item,
|
update_portfolio_item, delete_portfolio_item,
|
||||||
|
upsert_broker_cash, get_all_broker_cash, get_broker_cash, delete_broker_cash,
|
||||||
)
|
)
|
||||||
from .scraper import fetch_market_news, fetch_major_indices, fetch_overseas_news
|
from .scraper import fetch_market_news, fetch_major_indices, fetch_overseas_news
|
||||||
from .price_fetcher import get_current_prices
|
from .price_fetcher import get_current_prices
|
||||||
@@ -155,10 +156,24 @@ class PortfolioUpdateRequest(BaseModel):
|
|||||||
|
|
||||||
@app.get("/api/portfolio")
|
@app.get("/api/portfolio")
|
||||||
def get_portfolio():
|
def get_portfolio():
|
||||||
"""전체 포트폴리오 조회 (현재가 + 손익 포함)"""
|
"""전체 포트폴리오 조회 (현재가 + 손익 + 예수금 포함)"""
|
||||||
items = get_all_portfolio()
|
items = get_all_portfolio()
|
||||||
|
cash_rows = get_all_broker_cash()
|
||||||
|
total_cash = sum(r["cash"] for r in cash_rows)
|
||||||
|
|
||||||
if not items:
|
if not items:
|
||||||
return {"holdings": [], "summary": {"total_buy": 0, "total_eval": 0, "total_profit": 0, "total_profit_rate": 0.0}}
|
return {
|
||||||
|
"holdings": [],
|
||||||
|
"cash": cash_rows,
|
||||||
|
"summary": {
|
||||||
|
"total_buy": 0,
|
||||||
|
"total_eval": 0,
|
||||||
|
"total_profit": 0,
|
||||||
|
"total_profit_rate": 0.0,
|
||||||
|
"total_cash": total_cash,
|
||||||
|
"total_assets": total_cash,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
tickers = list({item["ticker"] for item in items})
|
tickers = list({item["ticker"] for item in items})
|
||||||
prices = get_current_prices(tickers)
|
prices = get_current_prices(tickers)
|
||||||
@@ -196,11 +211,14 @@ def get_portfolio():
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
"holdings": holdings,
|
"holdings": holdings,
|
||||||
|
"cash": cash_rows,
|
||||||
"summary": {
|
"summary": {
|
||||||
"total_buy": total_buy,
|
"total_buy": total_buy,
|
||||||
"total_eval": total_eval,
|
"total_eval": total_eval,
|
||||||
"total_profit": total_profit,
|
"total_profit": total_profit,
|
||||||
"total_profit_rate": total_profit_rate,
|
"total_profit_rate": total_profit_rate,
|
||||||
|
"total_cash": total_cash,
|
||||||
|
"total_assets": total_eval + total_cash,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -229,3 +247,31 @@ def delete_portfolio(item_id: int):
|
|||||||
return {"ok": True}
|
return {"ok": True}
|
||||||
|
|
||||||
|
|
||||||
|
# --- Broker Cash API ---
|
||||||
|
|
||||||
|
class BrokerCashRequest(BaseModel):
|
||||||
|
broker: str
|
||||||
|
cash: int
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/api/portfolio/cash")
|
||||||
|
def list_broker_cash():
|
||||||
|
"""증권사별 예수금 전체 조회"""
|
||||||
|
return get_all_broker_cash()
|
||||||
|
|
||||||
|
|
||||||
|
@app.put("/api/portfolio/cash")
|
||||||
|
def set_broker_cash(req: BrokerCashRequest):
|
||||||
|
"""증권사 예수금 등록 또는 수정 (upsert)"""
|
||||||
|
upsert_broker_cash(req.broker, req.cash)
|
||||||
|
return {"ok": True, "broker": req.broker, "cash": req.cash}
|
||||||
|
|
||||||
|
|
||||||
|
@app.delete("/api/portfolio/cash/{broker}")
|
||||||
|
def remove_broker_cash(broker: str):
|
||||||
|
"""증권사 예수금 삭제"""
|
||||||
|
if not delete_broker_cash(broker):
|
||||||
|
return JSONResponse(status_code=404, content={"error": "Broker not found"})
|
||||||
|
return {"ok": True}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user