# 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`를 포함해야 한다.