# Broker Integration & Order Lifecycle

## 1. 목적

이 문서는 브로커 연동 레이어와 주문 생명주기 규칙을 정의합니다.
Phase 6에서는 최소 1개 브로커 reference 구현이 필요하며, 기본 reference는 **Alpaca paper** 입니다.
그러나 상위 코드가 특정 브로커 SDK에 직접 결합되면 안 되므로, 내부적으로는 **broker abstraction layer**를 사용합니다.

## 2. Broker Adapter 인터페이스

모든 adapter는 아래 메서드를 제공해야 합니다.

```python
class BrokerAdapter(Protocol):
    def get_health(self) -> BrokerHealth: ...
    def get_account(self) -> BrokerAccountSnapshot: ...
    def get_positions(self) -> list[BrokerPositionSnapshot]: ...
    def get_open_orders(self) -> list[BrokerOrderSnapshot]: ...
    def submit_order(self, order: PlannedOrder) -> SubmitResult: ...
    def cancel_order(self, broker_order_id: str) -> CancelResult: ...
    def replace_order(self, broker_order_id: str, patch: ReplacePatch) -> ReplaceResult: ...
    def fetch_order(self, broker_order_id: str) -> BrokerOrderSnapshot: ...
    def fetch_fills(self, since: datetime) -> list[BrokerFill]: ...
    def stream_events(self, on_event: Callable[[RawBrokerEvent], None]) -> None: ...
```

### 금지사항
- adapter 바깥에서 브로커 SDK raw object를 직접 참조하지 않는다.
- business logic이 브로커별 order status string에 의존하지 않는다.
- adapter 내부에서 전략 점수나 리스크 결정을 하지 않는다.

## 3. Canonical Order Types

Phase 6 v1에서 허용하는 주문 타입은 아래로 제한한다.

- `market`
- `limit`
- `stop`
- `stop_limit`
- `market_on_open` (브로커 지원 시)
- `market_on_close` (브로커 지원 시)

v1에서는 다음을 금지한다.

- 복합 브래킷 주문에 전략 로직을 위임하는 방식
- 옵션/멀티레그 주문
- 알고리즘 주문(TWAP/VWAP 등)
- after-hours 진입 전략 주문

## 4. Client Order ID 규칙

내부 시스템은 반드시 **deterministic client_order_id**를 생성해야 한다.
형식 예시:

```text
{env}-{strategy_id}-{trade_date}-{symbol}-{leg}-{attempt}
```

예시:

```text
paper-corelong-2026-03-12-NVDA-entry-01
paper-corelong-2026-03-12-NVDA-exit-half-01
```

규칙:
- 같은 logical order를 재전송할 때는 같은 id를 재사용할지, 새 시퀀스를 부여할지 명확히 해야 한다.
- submit timeout 후 실제로 브로커가 주문을 받았는지 불명확할 경우, 무조건 새 주문을 보내지 말고 먼저 `fetch by client id` 또는 reconciliation을 수행한다.

## 5. PlannedOrder 구조

PlannedOrder는 최소 아래 필드를 가져야 한다.

- `order_plan_id`
- `candidate_id`
- `symbol`
- `side`
- `qty`
- `notional` (optional)
- `order_type`
- `time_in_force`
- `limit_price` (optional)
- `stop_price` (optional)
- `submission_window_start`
- `submission_window_end`
- `expiry_policy`
- `broker_route_hint` (optional)
- `reason_code`
- `risk_snapshot_id`

## 6. Order Lifecycle

### 6.1 내부 상태

```text
planned
-> approved
-> submitted
-> accepted
-> partially_filled
-> filled
-> cancel_requested
-> cancelled
-> replace_requested
-> replaced
-> rejected
-> expired
-> busted
```

### 6.2 전이 규칙

- `planned -> approved` : approval mode에서 승인 완료 또는 auto mode에서 risk guard 통과
- `approved -> submitted` : broker adapter submit 성공
- `submitted -> accepted` : broker가 ack 반환
- `accepted -> partially_filled` : 부분체결 event 수신
- `partially_filled -> filled` : 남은 수량이 0
- `accepted -> cancel_requested` : TTL 만료 / 운영자 cancel / risk halt
- `accepted -> rejected` : broker reject
- `accepted -> expired` : broker day order expired
- `partially_filled -> cancel_requested` : 잔량 취소 요청
- `replace_requested -> replaced` : 새 broker order 또는 수정 반영 확인

## 7. Entry / Exit 주문 정책

### 7.1 Entry
v1 entry는 아래 정책만 허용한다.
- 다음 시초가 근처 market/limit 진입
- 장중 제한된 window 안의 limit 진입
- stop-limit 진입은 paper에서 충분히 검증된 전략에만 허용

### 7.2 Stop Exit
- 하드 stop을 브로커에 즉시 상주시키는지, 내부 synthetic stop으로 운용하는지 전략별로 고정한다.
- v1에서는 gap risk를 고려해 synthetic stop + session-based exit를 기본으로 한다.
- 단, 연결 장애가 잦은 환경에서는 broker-native stop을 허용할 수 있다.

### 7.3 Time Exit
- planned_time_exit 시각 또는 session close 기준으로 자동 청산 계획을 생성한다.
- time exit는 별도의 logical order leg로 기록한다.

## 8. Partial Fill 처리

partial fill은 예외가 아니라 정상 흐름이다.

정책:
- 평균 체결가를 지속 업데이트한다.
- 남은 수량이 min_fill_threshold 미만이면 즉시 취소/시장가 전환 여부를 전략별로 결정한다.
- exit 주문은 filled qty를 기준으로만 생성한다.
- stop/target leg는 filled qty와 정합해야 한다.

## 9. Cancel / Replace 정책

v1 원칙:
- replace는 실제로 필요한 경우에만 사용한다.
- submit 직후 수 초 내 교체를 반복하는 logic은 금지한다.
- 취소와 교체가 동시에 걸릴 수 있으므로, reconciliation 전 finality를 가정하지 않는다.

## 10. 브로커 event canonicalization

브로커 raw event는 아래 canonical event로 변환한다.

- `order_submitted`
- `order_accepted`
- `order_rejected`
- `order_partially_filled`
- `order_filled`
- `order_cancel_requested`
- `order_cancelled`
- `order_replaced`
- `order_expired`
- `trade_bust`
- `heartbeat_lost`
- `heartbeat_restored`

모든 event는 `event_id`, `event_time`, `source`, `broker_order_id`, `client_order_id`, `payload_hash`를 포함해야 한다.
