<!-- task=m-paper-trading-readiness/02+01_worker_paper_runtime plan=0 tag=PAPER_WORKER -->

# Plan - PAPER_WORKER

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

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

## 배경

Domain paper model이 준비되면 worker는 broker 없이 strategy를 daily paper execution loop에서 돌릴 수 있어야 한다. 현재 `services/worker/internal/backtest/engine.go`는 backtest 결과 저장용 loop이고 현재 bar close fill을 사용한다. 이 작업은 backtest engine을 뒤집지 않고 worker-internal paper runtime을 별도 package로 추가한다.

## 사용자 리뷰 요청 흐름

구현 중 blocker는 active review stub의 `사용자 리뷰 요청` 섹션에 기록한다. code-review가 정당성을 검증하고 실제 `USER_REVIEW.md` 작성을 소유한다.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/trading-expansion/milestones/paper-trading-readiness.md`
- Task ids:
  - `strategy-run`: 전략을 paper execution loop에서 실행할 수 있다.
- Completion mode: check-on-pass

## 분석 결과

### 읽은 파일

- `agent-roadmap/current.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-ops/rules/project/domain/domain-model/rules.md`
- `agent-ops/rules/project/domain/worker/rules.md`
- `packages/domain/backtest/types.go`
- `packages/domain/backtest/types_test.go`
- `packages/domain/market/types.go`
- `services/worker/internal/backtest/bar_source.go`
- `services/worker/internal/backtest/engine.go`
- `services/worker/internal/backtest/strategies.go`
- `services/worker/internal/backtest/bar_source_test.go`
- `services/worker/internal/backtest/engine_test.go`
- `services/worker/internal/backtest/strategies_test.go`
- `services/worker/internal/jobs/backtest_jobs.go`
- `services/worker/internal/jobs/backtest_starter.go`
- `services/worker/internal/jobs/builtin.go`
- `services/worker/internal/jobs/job.go`
- `services/worker/internal/jobs/runner.go`
- `services/worker/internal/jobs/backtest_jobs_test.go`
- `services/worker/internal/jobs/backtest_starter_test.go`
- `services/worker/cmd/alt-worker/main.go`

### 테스트 환경 규칙

- test_env: `local`.
- 적용 profile: `domain-model-smoke`, `worker-smoke`.
- 적용 명령: `go test ./packages/domain/...`, `go test ./services/worker/...`, milestone confidence는 `bin/test`.
- 사전 확인: `go test ./services/worker/...`, `go test ./packages/domain/...`, `bin/test`가 현재 checkout에서 exit 0.

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

- Existing worker backtest tests cover close-price backtest fills, not next-bar paper fills.
- No worker package currently runs strategy with pending paper orders and risk guard.
- New worker tests must prove daily next-bar execution and state snapshots.

### 심볼 참조

- renamed/removed symbols: none.
- This task depends on additive domain symbols from `01_domain_paper_model`. If unavailable, stop and report predecessor missing.

### 분할 판단

- split decision policy evaluated.
- This subtask directory is `02+01_worker_paper_runtime`; predecessor index `01` must be satisfied by `agent-task/m-paper-trading-readiness/01_domain_paper_model/complete.log` or its matching archive path before implementation starts.
- Current state when plan was written: predecessor active plan exists, no `complete.log` yet.

### 범위 결정 근거

- Include: worker-internal paper runtime package and tests.
- Exclude: protobuf/API/CLI monitoring, database migrations, durable paper account persistence, live broker adapter.
- Initial state may be in-memory for worker package tests. API-visible state is handled in split `03`.

### 빌드 등급

- build: `local-G07`, review: `local-G07`.
- 근거: worker execution loop and state transitions are higher risk than pure domain, but split predecessor and deterministic unit tests keep it local-reviewable.

## 구현 체크리스트

- [x] Predecessor `01_domain_paper_model` PASS `complete.log`를 확인한다.
- [x] `services/worker/internal/papertrading/` package를 추가해 daily bar paper execution loop를 구현한다.
- [x] strategy decisions, pending orders, next-bar fills, portfolio/equity snapshots, risk guard rejection을 테스트한다.
- [x] `go test ./services/worker/... -count=1`, `go test ./packages/domain/... -count=1`, `git diff --check`를 실행한다.
- [x] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

## 의존 관계 및 구현 순서

- Depends on `01_domain_paper_model`.
- Required predecessor evidence: `agent-task/m-paper-trading-readiness/01_domain_paper_model/complete.log` or `agent-task/archive/*/*/m-paper-trading-readiness/01_domain_paper_model/complete.log`.
- Do not implement until predecessor completed; the runtime directory name `02+01_worker_paper_runtime` is the source of truth.

## [PAPER_WORKER-1] Worker Paper Runtime

### 문제

`services/worker/internal/backtest/engine.go:88` fills every order at the current bar close. Paper trading standard line requires orders decided on one daily bar to fill on the next daily bar open for market orders and next-bar high/low crossing for limit orders. Reusing the backtest engine directly would blur backtest result persistence with paper account state.

Current code:

```go
// services/worker/internal/backtest/engine.go:88
for _, order := range orders {
	fill := backtest.Fill{
		InstrumentID: order.InstrumentID,
		Side:         order.Side,
		Quantity:     order.Quantity,
		Price:        bar.Close,
	}
```

### 해결 방법

Add `services/worker/internal/papertrading/engine.go` and tests.

Core shape:

```go
type Engine struct {
	barSource    backtest.BarSource
	strategyPort backtest.StrategyPort
}

type RunRequest struct {
	Account backtest.PaperAccount
	Run     backtest.Run
}

type Snapshot struct {
	Account     backtest.PaperAccount
	Fills       []backtest.Fill
	Rejected    []RejectedOrder
	EquityCurve []backtest.EquityPoint
}
```

Loop:
- Load/sort bars with existing `BarSource`.
- Carry `pending []backtest.OrderIntent` from previous bar.
- At each bar start, try to fill pending orders through `backtest.FillOrderOnDailyBar`.
- Apply `CheckRisk` before applying a fill/order.
- Call strategy with current account portfolio after fills/marks.
- Store current decisions as pending for the next bar.
- Mark current instrument close and append equity.

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

- [x] Add `services/worker/internal/papertrading/engine.go`.
- [x] Add `services/worker/internal/papertrading/engine_test.go`.
- [x] Keep `services/worker/internal/backtest/engine.go` behavior unchanged.

### 테스트 작성

- Write tests in `services/worker/internal/papertrading/engine_test.go`.
- Test names:
  - `TestEngineFillsMarketOrderOnNextBarOpen`
  - `TestEngineFillsLimitOrderOnDailyCrossing`
  - `TestEngineLeavesNonCrossedLimitPending`
  - `TestEngineRejectsRiskDeniedOrder`
  - `TestEngineRecordsEquitySnapshots`

### 중간 검증

```bash
go test ./services/worker/internal/papertrading ./services/worker/internal/backtest -count=1
git diff --check
```

Expected: exit 0.

## 수정 파일 요약

| 파일 | 항목 |
|---|---|
| `services/worker/internal/papertrading/engine.go` | PAPER_WORKER-1 |
| `services/worker/internal/papertrading/engine_test.go` | PAPER_WORKER-1 |

## 최종 검증

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

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

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