[2026-05-18] 포지션 DB 동기화 + pnl 계산 수정

- order_executor: _update_trade_exit에 pnl 계산 저장 추가
- main: 매수 시 positions DB INSERT, 매도 시 DELETE
- main: 재시작 시 DB에서 positions 복원 (_restore_positions_from_db)
This commit is contained in:
2026-05-18 13:32:43 +09:00
parent a3832dd5a8
commit bf041e4d18
2 changed files with 67 additions and 8 deletions
+13 -7
View File
@@ -88,15 +88,21 @@ class OrderExecutor:
def _update_trade_exit(self, ticker, exit_price,
qty, reason, fee):
with get_conn() as conn:
row = conn.execute("""
SELECT id, entry_price, quantity FROM trades
WHERE ticker=? AND exit_time IS NULL
ORDER BY id DESC LIMIT 1
""", (ticker,)).fetchone()
if not row:
return
trade_id, entry_price, trade_qty = row
actual_qty = qty if qty else trade_qty
pnl = (exit_price - entry_price) * actual_qty - fee
conn.execute("""
UPDATE trades
SET exit_time=?, exit_price=?, exit_reason=?, fee=fee+?
WHERE id = (
SELECT id FROM trades
WHERE ticker=? AND exit_time IS NULL
ORDER BY id DESC LIMIT 1
)
SET exit_time=?, exit_price=?, exit_reason=?, fee=fee+?, pnl=?
WHERE id=?
""", (
datetime.now().strftime("%H:%M:%S"),
exit_price, reason, fee, ticker,
exit_price, reason, fee, pnl, trade_id,
))
+54 -1
View File
@@ -103,9 +103,53 @@ class StockBot:
cash = balance["cash"]
self.risk = RiskManager(init_cash=cash)
logger.info(f"초기 예수금: {cash:,}")
# DB에서 열린 포지션 복원 (재시작 시)
self._restore_positions_from_db()
await send(f"[시작] 단타봇 가동 | 예수금: {cash:,}원 | "
f"{'모의투자' if self.kis.is_mock else '실거래'}")
def _restore_positions_from_db(self):
"""재시작 시 DB positions 테이블에서 인메모리 복원"""
with get_conn() as conn:
rows = conn.execute("SELECT * FROM positions").fetchall()
for r in rows:
ticker, name, entry_time, entry_price, qty, tp1_done, target_price, stop_price, ai_boosted = r
self.positions[ticker] = {
"name" : name,
"entry" : entry_price,
"qty" : qty,
"tp1_done" : bool(tp1_done),
"entry_time": datetime.strptime(entry_time, "%H:%M:%S").replace(
year=datetime.now().year,
month=datetime.now().month,
day=datetime.now().day),
"sl_price" : stop_price,
"boosted" : bool(ai_boosted),
}
if self.positions:
logger.info(f"DB 포지션 복원: {list(self.positions.keys())}")
def _db_save_position(self, ticker: str, pos: dict, target_price: float):
with get_conn() as conn:
conn.execute("""
INSERT OR REPLACE INTO positions
(ticker, name, entry_time, entry_price, quantity,
tp1_done, target_price, stop_price, ai_boosted)
VALUES (?,?,?,?,?,?,?,?,?)
""", (
ticker, pos["name"],
pos["entry_time"].strftime("%H:%M:%S"),
pos["entry"], pos["qty"],
1 if pos.get("tp1_done") else 0,
target_price, pos["sl_price"],
1 if pos.get("boosted") else 0,
))
def _db_delete_position(self, ticker: str):
with get_conn() as conn:
conn.execute("DELETE FROM positions WHERE ticker=?", (ticker,))
# ─────────────────────────────────────────
# 유니버스 갱신 (08:30)
# ─────────────────────────────────────────
@@ -289,7 +333,7 @@ class StockBot:
entry_price = result["price"] or current
sl_price = entry_price * (1 - self.risk.get_sl_pct())
self.positions[ticker] = {
pos = {
"name" : name,
"entry" : entry_price,
"qty" : qty,
@@ -298,6 +342,11 @@ class StockBot:
"sl_price" : sl_price,
"boosted" : signal.get("boosted", False),
}
self.positions[ticker] = pos
self._db_save_position(
ticker, pos,
target_price=self.strategy.get_target(ticker),
)
await notify_buy(
ticker=ticker, name=name,
@@ -370,10 +419,14 @@ class StockBot:
pos["qty"] -= qty
if pos["qty"] <= 0:
del self.positions[ticker]
self._db_delete_position(ticker)
else:
self._db_save_position(ticker, pos, self.strategy.get_target(ticker))
await notify_tp1(ticker, name, pnl_pct)
elif reason in ("TP2", "SL", "TIME", "FORCE"):
del self.positions[ticker]
self._db_delete_position(ticker)
if reason == "TP2":
await notify_tp2(ticker, name, pnl_pct)
elif reason == "SL":