Skip to content

fix: positions 구버전 UNIQUE(symbol) 재구축 — 유령 -41% 낙폭의 뿌리#452

Merged
easygap merged 1 commit into
mainfrom
fix/position-unique-per-account
Jul 10, 2026
Merged

fix: positions 구버전 UNIQUE(symbol) 재구축 — 유령 -41% 낙폭의 뿌리#452
easygap merged 1 commit into
mainfrom
fix/position-unique-per-account

Conversation

@easygap

@easygap easygap commented Jul 10, 2026

Copy link
Copy Markdown
Owner

배경 — 유령 -41% 낙폭의 뿌리

7/7 소액 트랙 v2 전환(트랙레코드 재시작) 직후 첫 매수에서: 매매 기록은 남았는데 포지션만 사라지고, 스냅샷이 현금만 집계돼 -41.28% 낙폭으로 찍혔다. 이 유령 MDD가 리스크 가드(신규 매수 차단)를 발동시켜 7/8~7/10 사이클이 3일 연속 아무것도 못 샀다.

원인은 #439(스냅샷 UNIQUE(date))와 같은 계열의 레거시 스키마다: account_key 도입 전 시절의 UNIQUE (symbol) 단독 제약이 물리 테이블에 잔존 — 모델은 이미 (account_key, symbol) 복합 제약인데 create_all은 기존 테이블을 바꾸지 못한다. 재시작으로 아카이브 키(...@archived-20260707)에 069500이 남은 상태에서 본 키가 069500을 재매수하자 IntegrityError로 포지션 저장이 실패했다(에러는 로그에만 남고 사이클은 계속 진행).

트랙 재시작만의 문제가 아니다 — 서로 다른 계좌(바스켓·전략 트랙·아카이브)가 같은 종목을 드는 순간 언제든 터질 지뢰였다. 분산 바스켓(005930 보유)과 전략 트랙이 삼성전자를 겹쳐 사는 평범한 시나리오도 동일하게 유실된다.

수정

  • _migrate_position_unique_constraint — 스냅샷 재구축과 동일 절차(rename → 신 스키마 생성 → INSERT OR IGNORE 복사 → 행수 검증 → 구 테이블 삭제). 멱등, 중단 시 재개 가능, 행수 감소 시 RuntimeError로 legacy 보존
  • init_database에서 조용히 삼키지 않음(#439와 같은 이유 — 포지션이 계속 유실되는 상태를 숨기면 안 됨). partial_tp_done 컬럼 추가 마이그레이션 뒤에 실행해 복사 컬럼을 보장

검증

  • 단위 7개: 행 보존 + 재구축 후 두 계좌 동일 종목 공존 / 같은 계좌 중복은 여전히 차단 / 신 스키마 no-op 멱등 / rename 직후 중단 재개 / 7/7 실측 시나리오 재연(아카이브 키 보유 중 본 키 매수 — 전: IntegrityError, 후: 공존)
  • 운영 DB 실적용: 7/8 첫 사이클의 init_database가 자동 수행 — positions 재구축 완료: 10행, 복합 유니크(account_key, symbol) 로그 확인, legacy 테이블 제거·행수 보존 검증 완료
  • 전체 스위트 통과

후속 운영 조치 (이 PR 머지 후)

스키마는 고쳤지만 7/7 사고의 잔해(고아 매매 1건 + 깨진 스냅샷 4개, 유령 MDD)는 데이터에 남아 있다 — DB 백업 후 해당 잔해를 제거하고 v2 첫 사이클을 재실행해 트랙레코드를 깨끗한 0/60에서 시작한다.

…1% 낙폭의 뿌리

트랙 재시작 직후 첫 매수에서 매매 기록은 남고 포지션만 사라졌다. 스냅샷이
현금만 집계돼 -41% 낙폭으로 찍혔고, 이 유령 MDD가 리스크 가드를 발동시켜
이후 사이클의 매수를 전부 차단했다.

원인은 스냅샷 UNIQUE(date) 때와 같은 계열: account_key 도입 전 시절의
UNIQUE(symbol) 단독 제약이 물리 테이블에 잔존(모델은 이미 복합 제약 —
create_all은 기존 테이블을 못 바꾼다). 아카이브 키에 069500이 남은 상태에서
본 키가 재매수하자 IntegrityError로 포지션 저장이 실패했다. 재시작만이
아니라 바스켓과 전략 트랙이 같은 종목을 겹쳐 드는 순간 언제든 터질 지뢰.

- 표준 재구축(rename -> 생성 -> INSERT OR IGNORE 복사 -> 행수 검증 -> 삭제),
  멱등·중단 재개 가능, 행수 감소 시 RuntimeError로 legacy 보존
- init_database에서 조용히 삼키지 않음. partial_tp_done 컬럼 마이그레이션
  뒤에 실행해 복사 컬럼 보장
- 테스트 7종: 행 보존, 재구축 후 두 계좌 동일 종목 공존, 같은 계좌 중복
  차단 유지, no-op 멱등, 중단 재개, 실측 사고 시나리오 재연

운영 DB에는 이미 적용됨(다음날 첫 사이클의 init이 자동 수행, 10행 보존
로그 확인). 잔해(고아 매매·깨진 스냅샷) 정리는 별도 운영 조치로 진행.
@easygap
easygap merged commit 18a0d0b into main Jul 10, 2026
1 check passed
@easygap
easygap deleted the fix/position-unique-per-account branch July 10, 2026 02:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant