<!-- task=m-paper-trading-readiness/01_domain_paper_model plan=0 tag=PAPER_DOMAIN -->

# Plan - PAPER_DOMAIN

## 이 파일을 읽는 구현 에이전트에게

구현 완료의 마지막 단계는 반드시 active `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채우는 것이다. 검증 명령을 실행하고 실제 stdout/stderr를 붙여 넣은 뒤 active 파일을 그대로 두고 리뷰 준비를 보고한다. 최종 판정, log rename, `complete.log`, archive 이동은 code-review-skill 전용이다. 구현 중 사용자 결정, 사용자 소유 외부 환경/secret/서비스 준비, 또는 범위 충돌 없이는 안전하게 진행할 수 없으면 review stub의 `사용자 리뷰 요청` 섹션을 증거와 함께 채우고 중단한다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 해소할 수 있는 검증 증거 공백은 사용자 리뷰 요청이 아니다.

## 배경

`Paper Trading Readiness`는 backtest에서 이미 쓰는 portfolio/fill vocabulary를 paper trading에서도 뒤집지 않고 재사용해야 한다. 현재 domain model에는 account, order type, daily bar fill policy, risk guard skeleton이 없어 worker/API가 paper flow를 안정적으로 붙일 기준이 없다. 이 작업은 broker/persistence/transport 없이 domain-only paper foundation을 만든다.

## 사용자 리뷰 요청 흐름

구현 중 blocker는 active review stub의 `사용자 리뷰 요청` 섹션에 기록한다. 이 섹션은 `agent-ops/skills/common/_templates/implementation-user-review-request-section.md`에서 복사된 형식이며, code-review가 정당성을 검증하고 실제 `USER_REVIEW.md` 작성을 소유한다.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/trading-expansion/milestones/paper-trading-readiness.md`
- Task ids:
  - `paper-account`: paper account와 portfolio state
  - `virtual-orders`: 가상 주문과 체결 모델
  - `risk-guard`: 최소 risk guard skeleton
- Completion mode: check-on-pass

## 분석 결과

### 읽은 파일

- `agent-roadmap/current.md`
- `agent-roadmap/phase/trading-expansion/PHASE.md`
- `agent-roadmap/phase/trading-expansion/milestones/paper-trading-readiness.md`
- `agent-test/local/rules.md`
- `agent-test/local/domain-model-smoke.md`
- `agent-test/local/worker-smoke.md`
- `agent-test/local/api-smoke.md`
- `agent-test/local/contracts-smoke.md`
- `agent-test/local/operations-smoke.md`
- `agent-ops/rules/project/domain/domain-model/rules.md`
- `agent-ops/rules/project/domain/worker/rules.md`
- `agent-ops/rules/project/domain/api/rules.md`
- `agent-ops/rules/project/domain/contracts/rules.md`
- `agent-ops/rules/project/domain/operations/rules.md`
- `packages/domain/backtest/types.go`
- `packages/domain/backtest/types_test.go`
- `packages/domain/market/types.go`
- `packages/domain/market/types_test.go`
- `packages/domain/go.mod`

### 테스트 환경 규칙

- test_env: `local`.
- `agent-test/local/rules.md`를 읽었고, `packages/domain/**` 변경으로 `domain-model-smoke`를 적용한다.
- 적용 profile: `agent-test/local/domain-model-smoke.md`.
- 적용 명령: 작은 domain-only 변경은 `go test ./packages/domain/...`; milestone/cross-module 검증은 `bin/test`, `bin/lint`.
- 사전 확인: `go test ./packages/domain/...`, `bin/test`, `bin/lint`가 현재 checkout에서 실행 가능했다. `bin/lint`는 exit 0이지만 기존 Mattermost `avoid_print` info 13건을 출력한다.

### 테스트 커버리지 공백

- `paper-account`: 기존 테스트 없음. 새 `packages/domain/backtest/types_test.go` 테스트를 추가한다.
- `virtual-orders`: 기존 `ApplyFill` 테스트는 fill accounting만 다룬다. order type과 daily OHLC fill policy 회귀 테스트를 추가한다.
- `risk-guard`: 기존 테스트 없음. default/noop 또는 basic guard skeleton 테스트를 추가한다.

### 심볼 참조

- renamed/removed symbols: none.
- additive fields/types only. 기존 `OrderIntent`와 `Fill` call site는 field 추가 시 composite literals가 명명 필드라서 유지 가능하다.

### 분할 판단

- split decision policy를 계획 파일 선택 전에 평가했다.
- 공유 task group: `m-paper-trading-readiness`.
- sibling plans:
  - `01_domain_paper_model`: no predecessor. domain paper account/order/fill/risk model.
  - `02+01_worker_paper_runtime`: depends on `01`; worker execution loop uses domain model.
  - `03+01,02_contract_api_cli_monitoring`: depends on `01` and `02`; API/CLI monitoring needs domain and worker surface.
- 이 plan은 첫 subtask라 predecessor 없음.

### 범위 결정 근거

- 포함: `packages/domain/backtest/types.go`, `packages/domain/backtest/types_test.go` 중심의 additive domain vocabulary.
- 제외: worker runtime, protobuf contracts, API, CLI, persistence migration, live broker adapter. 이들은 후속 split plan에서 다룬다.
- 새 Go package를 만들지 않고 기존 `backtest` package에 additive paper vocabulary를 둔다. 현재 strategy/portfolio/fill vocabulary가 이미 이 package에 있고, 대규모 shared trading package 추출은 현재 마일스톤보다 큰 refactor다.

### 빌드 등급

- build: `local-G06`, review: `local-G06`.
- 근거: domain-only additive change이며 exact decimal/portfolio logic 위험은 있지만 테스트로 충분히 containment 가능하다.

## 구현 체크리스트

- [ ] `packages/domain/backtest/types.go`에 paper account, order type, daily bar fill policy, minimal risk guard vocabulary를 추가한다.
- [ ] `packages/domain/backtest/types_test.go`에 account state, market/limit daily fill, insufficient cash/position, risk guard skeleton 테스트를 추가한다.
- [ ] `go test ./packages/domain/... -count=1`와 `git diff --check`를 실행한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

## [PAPER_DOMAIN-1] Domain Paper Model

### 문제

`packages/domain/backtest/types.go:112`의 `OrderIntent`는 side/quantity만 있어 market/limit 구분과 limit price를 표현하지 못한다. `packages/domain/backtest/types.go:118`의 `Fill`은 체결 price만 있어 daily paper fill policy와 trace를 남길 공간이 없다. `PortfolioState`는 `packages/domain/backtest/types.go:131`에 있으나 paper account wrapper와 risk guard skeleton이 없다.

현재 코드:

```go
// packages/domain/backtest/types.go:112
type OrderIntent struct {
	InstrumentID market.InstrumentID
	Side         OrderSide
	Quantity     market.Quantity
}

type Fill struct {
	InstrumentID market.InstrumentID
	Side         OrderSide
	Quantity     market.Quantity
	Price        market.Price
}
```

### 해결 방법

`backtest` package에 additive types/functions를 추가한다.

```go
type OrderType string

const (
	OrderTypeMarket OrderType = "market"
	OrderTypeLimit  OrderType = "limit"
)

type PaperAccountID string

type PaperAccount struct {
	ID          PaperAccountID
	Portfolio   PortfolioState
	UpdatedAt   time.Time
	FillPolicy  FillPolicy
	RiskSettings RiskSettings
}

type FillPolicy string

const FillPolicyDailyNextBarOHLC FillPolicy = "daily_next_bar_ohlc"

type RiskSettings struct {
	AllowShortSelling bool
}

type RiskDecision struct {
	Allowed bool
	Reason  string
}
```

Extend existing structs with named additive fields:

```go
type OrderIntent struct {
	InstrumentID market.InstrumentID
	Side         OrderSide
	Quantity     market.Quantity
	Type         OrderType
	LimitPrice   market.Price
}

type Fill struct {
	InstrumentID market.InstrumentID
	Side         OrderSide
	Quantity     market.Quantity
	Price        market.Price
	Timestamp    time.Time
}
```

Add pure helpers:

```go
func FillOrderOnDailyBar(order OrderIntent, bar market.Bar) (Fill, bool, error)
func CheckRisk(account PaperAccount, order OrderIntent) RiskDecision
```

Rules:
- empty `OrderIntent.Type` defaults to `OrderTypeMarket` for existing strategies.
- market order fills at `bar.Open`.
- limit buy fills when `bar.Low <= limit <= bar.High`; limit sell uses the same crossing rule.
- limit order without limit price is an error.
- `CheckRisk` rejects short selling when `AllowShortSelling` is false and the account lacks enough position for sell order.

### 수정 파일 및 체크리스트

- [ ] `packages/domain/backtest/types.go`: additive paper types, helper functions, and validations.
- [ ] `packages/domain/backtest/types_test.go`: tests for market fill, limit crossing/no-crossing, missing limit, paper account state, and short-sell risk rejection.

### 테스트 작성

- Write tests in `packages/domain/backtest/types_test.go`.
- Test names:
  - `TestFillOrderOnDailyBarMarketUsesOpen`
  - `TestFillOrderOnDailyBarLimitCrossing`
  - `TestFillOrderOnDailyBarRejectsMissingLimit`
  - `TestPaperAccountCarriesPortfolioState`
  - `TestCheckRiskRejectsShortSellByDefault`

### 중간 검증

```bash
go test ./packages/domain/... -count=1
git diff --check
```

Expected: both commands exit 0.

## 수정 파일 요약

| 파일 | 항목 |
|---|---|
| `packages/domain/backtest/types.go` | PAPER_DOMAIN-1 |
| `packages/domain/backtest/types_test.go` | PAPER_DOMAIN-1 |

## 최종 검증

```bash
go test ./packages/domain/... -count=1
git diff --check
```

Expected: both commands exit 0. Fresh execution matters, so use `-count=1`.

모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.
