[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:
@@ -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
@@ -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":
|
||||
|
||||
Reference in New Issue
Block a user