Audit log (broker_order_events)
broker_order_events 테이블은 모든 주문 시도와 broker 응답을 시간순으로 기록합니다. 트러블슈팅, 컴플라이언스, reconciliation의 source-of-truth입니다.
📸 broker_order_events 테이블 뷰 (추후 자동 캡처 예정)
이벤트 타입
| event_type | 발생 시점 |
|---|---|
placed | broker가 주문 접수 |
rejected | risk-rule 위반 또는 broker 거부 |
canceled | 사용자 취소 |
filled | 부분/전체 체결 |
expired | TIF 만료 |
live_request_confirm | 라이브 활성화 토큰 발급 |
live_enabled / live_disabled | 라이브 모드 토글 |
컬럼 구조
| 컬럼 | 타입 | 의미 |
|---|---|---|
id | bigint | PK |
order_id | text | client_order_id 또는 합성 ID |
user_id | int | 소유자 (FK) |
broker | text | binance / alpaca / kis / live_gating |
event_type | text | 위 표 참고 |
payload | jsonb | 요청/응답 원본 |
correlation_id | text | 같은 주문 흐름 묶기 |
created_at | timestamptz | 이벤트 발생 시각 |
UI 노출
현재 별도 Audit 페이지는 없습니다. 다음 위치에서 간접 활용됩니다.
| 위치 | 역할 |
|---|---|
| Trades 페이지 | filled 이벤트 → 거래 행 생성 |
| Reconciliation | broker drift 추적 |
| Equity 페이지 cancel 버튼 | canceled 이벤트 기록 |
전용 viewer는 후속 작업이며, 현재는 SQL 직접 조회를 권장합니다.
SQL 조회 예시
SELECT id, broker, event_type, created_at, payload->>'reason' AS reason
FROM broker_order_events
WHERE user_id = $1
AND created_at > now() - interval '1 day'
ORDER BY created_at DESC
LIMIT 100;
거부 이벤트 분석
event_type = 'rejected' 행의 payload.code로 거부 사유를 그룹화하면 자주 발생하는 문제를 파악할 수 있습니다.
| code | 원인 |
|---|---|
RISK_RULE_VIOLATION | per-trade cap, daily loss 등 위반 |
MARKET_CLOSED | 정규장 외 주문 |
INSUFFICIENT_FUNDS | 매수 가능 한도 부족 |
BROKER_REJECTED | broker 자체 거부 (호가 단위, 거래 정지 등) |
BROKER_UNAVAILABLE | broker API 5xx / timeout |
절대 룰
멀티사용자 격리
모든 SELECT는 반드시 user_id = current_user.id 필터를 포함해야 합니다. 라우터 레이어에서 강제하지만, 직접 SQL을 작성할 때도 동일하게 적용하세요.
자주 묻는 질문
Q. live 활성화 흐름도 여기 기록되나요?
A. 네. event_type='live_request_confirm' / 'live_enabled' / 'live_disabled'로 broker 컬럼은 live_gating이 됩니다.
Q. payload가 너무 커요. 인덱싱은?
A. (user_id, created_at) 복합 인덱스가 기본입니다. JSONB 키를 자주 검색하면 GIN 인덱스를 추가하세요.
Q. 데이터 보관 기간은?
A. 기본 무기한. 운영 정책에 따라 90일/365일 partition을 두는 것을 권장합니다.