fix: log parser duplicate position bug and selective CI/CD deployment

- Log parser: fix entry dedup to check direction instead of price tolerance,
  preventing duplicate OPEN trades for the same position
- Log parser: close all OPEN trades on position close, delete stale duplicates
- Jenkinsfile: detect changed files and only build/deploy affected services,
  allowing dashboard-only changes without restarting the bot

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
21in7
2026-03-05 20:52:23 +09:00
parent 07ba510fd8
commit f75ad9f6b1
2 changed files with 127 additions and 51 deletions

View File

@@ -385,22 +385,20 @@ class LogParser:
if leverage is None:
leverage = self._bot_config.get("leverage", 10)
# 메모리 내 중복 체크
if self._current_position:
if abs(self._current_position["entry_price"] - entry_price) < 0.0001:
return
# 중복 체크 — 같은 방향의 OPEN 포지션이 이미 있으면 스킵
# (봇은 동시에 같은 방향 포지션을 2개 이상 열지 않음)
if self._current_position and self._current_position.get("direction") == direction:
return
# DB 내 중복 체크 — 같은 방향·가격의 OPEN 포지션이 이미 있으면 스킵
existing = self.conn.execute(
"SELECT id FROM trades WHERE status='OPEN' AND direction=? "
"AND ABS(entry_price - ?) < 0.0001",
(direction, entry_price),
"SELECT id, entry_price FROM trades WHERE status='OPEN' AND direction=?",
(direction,),
).fetchone()
if existing:
self._current_position = {
"id": existing["id"],
"direction": direction,
"entry_price": entry_price,
"entry_price": existing["entry_price"],
"entry_time": ts,
}
return
@@ -427,18 +425,17 @@ class LogParser:
# ── 포지션 청산 핸들러 ───────────────────────────────────────
def _handle_close(self, ts, exit_price, expected_pnl, commission, net_pnl, reason):
if not self._current_position:
# 열린 포지션 없으면 DB에서 찾기
row = self.conn.execute(
"SELECT id, entry_price, direction FROM trades WHERE status='OPEN' ORDER BY id DESC LIMIT 1"
).fetchone()
if row:
self._current_position = dict(row)
else:
print(f"[LogParser] 경고: 청산 감지했으나 열린 포지션 없음")
return
# 모든 OPEN 거래를 닫음 (봇은 동시에 1개 포지션만 보유)
open_trades = self.conn.execute(
"SELECT id FROM trades WHERE status='OPEN' ORDER BY id DESC"
).fetchall()
trade_id = self._current_position["id"]
if not open_trades:
print(f"[LogParser] 경고: 청산 감지했으나 열린 포지션 없음")
return
# 가장 최근 OPEN에 실제 PnL 기록
primary_id = open_trades[0]["id"]
self.conn.execute(
"""UPDATE trades SET
exit_time=?, exit_price=?, expected_pnl=?,
@@ -447,9 +444,18 @@ class LogParser:
WHERE id=?""",
(ts, exit_price, expected_pnl,
expected_pnl, commission, net_pnl,
reason, trade_id)
reason, primary_id)
)
# 나머지 OPEN 거래는 중복이므로 삭제
if len(open_trades) > 1:
stale_ids = [r["id"] for r in open_trades[1:]]
self.conn.execute(
f"DELETE FROM trades WHERE id IN ({','.join('?' * len(stale_ids))})",
stale_ids,
)
print(f"[LogParser] 중복 OPEN 거래 {len(stale_ids)}건 삭제")
# 일별 요약 갱신
day = ts[:10]
win = 1 if net_pnl > 0 else 0