feat: 시작 시 브로커 포지션 → DB 동기화 및 국내주식 이중 매수 방지 (#206) #228

Merged
jihoson merged 1 commits from feature/issue-206-startup-position-sync into main 2026-02-23 17:04:01 +09:00
Collaborator

변경 내용

시스템 시작 시 실제 브로커 잔고를 로컬 DB와 동기화하는 루틴을 추가합니다.
아울러 국내주식 BUY 이중 방지 로직이 해외주식에만 적용되던 버그도 수정합니다.

문제 (issue #206)

  • get_open_position()은 SQLite DB만 참조 → 실전 계좌에 기존 보유 종목이 있어도 DB가 비면 이중 매수 발생
  • 이중 매수 방지를 위한 broker balance 체크가 해외주식에만 적용되고 국내주식은 미적용

해결

1. sync_positions_from_broker() 함수 추가 (src/main.py)

  • 시스템 시작 시 enabled_market_list의 모든 마켓에 대해 잔고 조회
  • DB에 없는 포지션은 synthetic BUY 레코드로 삽입
  • 국내: broker.get_balance(), 해외: overseas_broker.get_overseas_balance(exchange_code)
  • ConnectionError 발생 시 해당 마켓 skip (non-fatal)
  • seen_exchange_codes로 중복 조회 방지
  • run() 초기화 후 최초 한 번 호출
Startup sync: KR/005930 recorded as open position (qty=5)
Startup sync complete: 1 position(s) synced from broker

2. 국내주식 이중 매수 방지 로직 확장

기존 (해외 전용):

if not existing_position and not market.is_domestic:
    broker_qty = _extract_held_qty_from_balance(..., is_domestic=False)

수정 후 (국내/해외 공통):

if not existing_position:
    broker_qty = _extract_held_qty_from_balance(..., is_domestic=market.is_domestic)

trading_cycle 및 run_daily_session 양쪽 모두 적용.

테스트 결과

827 passed, 4 warnings

추가된 테스트

  • TestSyncPositionsFromBroker (6개)
    • 국내 포지션 동기화
    • DB에 이미 있으면 skip
    • 해외 포지션 동기화
    • 빈 잔고 → synced=0
    • ConnectionError 처리 (non-fatal)
    • 중복 exchange_code dedup
  • TestDomesticBuyDoublePreventionTradingCycle (1개)
    • 국내 보유 주식 BUY 억제 검증

Closes #206

## 변경 내용 시스템 시작 시 실제 브로커 잔고를 로컬 DB와 동기화하는 루틴을 추가합니다. 아울러 국내주식 BUY 이중 방지 로직이 해외주식에만 적용되던 버그도 수정합니다. ### 문제 (issue #206) - get_open_position()은 SQLite DB만 참조 → 실전 계좌에 기존 보유 종목이 있어도 DB가 비면 이중 매수 발생 - 이중 매수 방지를 위한 broker balance 체크가 해외주식에만 적용되고 국내주식은 미적용 ### 해결 **1. sync_positions_from_broker() 함수 추가 (src/main.py)** - 시스템 시작 시 enabled_market_list의 모든 마켓에 대해 잔고 조회 - DB에 없는 포지션은 synthetic BUY 레코드로 삽입 - 국내: broker.get_balance(), 해외: overseas_broker.get_overseas_balance(exchange_code) - ConnectionError 발생 시 해당 마켓 skip (non-fatal) - seen_exchange_codes로 중복 조회 방지 - run() 초기화 후 최초 한 번 호출 ``` Startup sync: KR/005930 recorded as open position (qty=5) Startup sync complete: 1 position(s) synced from broker ``` **2. 국내주식 이중 매수 방지 로직 확장** 기존 (해외 전용): ```python if not existing_position and not market.is_domestic: broker_qty = _extract_held_qty_from_balance(..., is_domestic=False) ``` 수정 후 (국내/해외 공통): ```python if not existing_position: broker_qty = _extract_held_qty_from_balance(..., is_domestic=market.is_domestic) ``` trading_cycle 및 run_daily_session 양쪽 모두 적용. ## 테스트 결과 827 passed, 4 warnings ### 추가된 테스트 - TestSyncPositionsFromBroker (6개) - 국내 포지션 동기화 - DB에 이미 있으면 skip - 해외 포지션 동기화 - 빈 잔고 → synced=0 - ConnectionError 처리 (non-fatal) - 중복 exchange_code dedup - TestDomesticBuyDoublePreventionTradingCycle (1개) - 국내 보유 주식 BUY 억제 검증 Closes #206
agentson added 1 commit 2026-02-23 16:57:34 +09:00
- sync_positions_from_broker() 함수 추가
  - 시스템 시작 시 브로커 잔고를 조회해 DB에 없는 포지션을 BUY 레코드로 삽입
  - 국내: get_balance(), 해외: get_overseas_balance(exchange_code) 순회
  - ConnectionError는 경고 로그만 남기고 계속 진행 (non-fatal)
  - 동일 exchange_code 중복 조회 방지 (seen_exchange_codes 집합)
  - run() 초기화 후 최초 한 번 자동 호출

- 국내주식 BUY 이중 방지 로직 확장
  - trading_cycle 및 run_daily_session에서 기존에 해외 전용(not market.is_domestic)
    으로만 적용하던 broker balance 체크를 국내/해외 공통으로 변경
  - _extract_held_qty_from_balance(is_domestic=market.is_domestic)

- 테스트 (827 passed)
  - TestSyncPositionsFromBroker (6개): 국내/해외 동기화, 중복 skip, 공란, ConnectionError, dedup
  - TestDomesticBuyDoublePreventionTradingCycle (1개): 국내 보유 주식 BUY 억제

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
agentson force-pushed feature/issue-206-startup-position-sync from d01f4d93a3 to ff9c4d6082 2026-02-23 17:03:30 +09:00 Compare
jihoson merged commit 5a41f86112 into main 2026-02-23 17:04:01 +09:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: jihoson/The-Ouroboros#228