<!-- canonical: https://docs.axelabs.ai/services/index/financial-model -->
<!-- source: content/services/index/financial-model.mdx -->

---
title: 재무 모델 (xlsx → DB)
description: index 의 핵심 차별성 — financial_model 6-table SoT + fund_investment N:M + DSL formula + topo evaluator + xlsx round-trip. Iippo / Sentry / Canopy 실 데이터로 schema 검증.
---

# 재무 모델 — xlsx 를 DB 의 일급 entity 로

## 한 줄 정의

ic skill 의 `ic/finance/{3fs_*,scenario_deltas,exit_matrix,irr_analysis}` 산출물을 **6-table typed model** 로 승격. xlsx 가 source 가 아니라 **DB 가 source, xlsx 는 ingest 입력 + export 출력**. DSL formula + topo evaluator 가 cell 재계산 deterministic. fund_investment 가 cash flow + IRR carrier — 한 deal 의 여러 fund × 여러 round 별 IRR 독립 산출.

> 본 페이지는 index 의 **L1 (relational compute substrate)** 이다. 이 relational 값을 *지배하는 판단* (가정·calibration·comp·base_rate) 은 그 위 **L2 — artifact-first judgment layer** ([D-index-45](/ops/decisions)) 가 typed fact 로 보존하며, citation `index_field` (ref `{deal,field}`) 로 여기 relational field 에 링크된다. "왜 이 driver/exit_multiple/probability 가 이 값인가" 추적 = `get_judgment_provenance`. 3-layer 전체 = [/services/index § 3-layer 모델](/services/index#3-layer-모델).

## 왜 필요한가

ic skill 의 현재 흐름:

```
회사 IR pack
  ↓ vision_ingest
dataroom/*.{pdf,pptx,xlsx}
  ↓ /ic 19-agent orchestration (3-5시간)
ic/finance/3fs_base/v7.xlsx     ← scenario 4종 × 3FS 모델 (8 sheet)
ic/finance/scenario_deltas/v7.yaml ← 4 scenario × ~15 driver
ic/finance/exit_matrix/v7.yaml  ← scenario × timing × path leaf 트리
ic/finance/irr_analysis/v7.xlsx ← 확률가중 IRR 산출
ic/memo/v8.md                   ← IRR 23.4% 라는 한 줄 cite
  ↓ PDF render + Teams reply
산출물 휘발 — 다음 deal 도 grep 으로 재추출
```

5 격차:

| 격차 | 현재 (xlsx + yaml + markdown) | 결과 |
|---|---|---|
| Cross-deal driver benchmark | portfolio 의 churn / gross_margin / IRR 시계열 grep 으로 못 풀음 | "axec/axev 포폴 평균 gross margin?" 답 토큰 폭증 |
| Citation chain | IC memo "IRR 23.4%" 가 xlsx 어느 cell, 어느 driver 에서 왔는지 trace 불가 | LP audit 시 "한 숫자라도 잡히면" IRR 신뢰 무너짐 (ic skill `citation_trace.py` 본질) |
| Time-travel | Q2 IC 시점의 IRR vs 현재 IRR diff 안 됨 | 의사결정 archaeology 불가 |
| 펀드별 IRR 분리 | 1호 + 2호 동시 투자 시 각각 IRR 산출 수작업 | Iippo / Sentry 같은 multi-fund deal 의 정합성 안 잡힘 |
| Sanity check 휘발 | ic skill 의 4 check 가 매번 재실행. 누적 audit 없음 | 모순 반복 (Superman v4 "11,250백만원 (1,200억 × 18.75%)" 4-number 모순 같은 사고) |

→ DB 가 source 면 위 5 격차 모두 SQL aggregate / FK / trigger 로 자연 해결.

## 6-table SoT

```
financial_model (deal 별 1+, version)
  │
  ├─ financial_driver (driver tree DAG — code, kind, formula DSL, dependencies[])
  │     ↑ topological sort 로 평가 순서 결정
  │
  ├─ financial_scenario (Upside/Base/Mgmt/Downside, probability, driver_changes)
  │     ↑ ic skill 의 PR-J 4-scenario 정합
  │
  ├─ financial_driver_value (model × driver × scenario × period_index → value)
  │     ↑ scenario 가 baseline 위에 override 만 — driver_changes 의 DB 형태
  │
  ├─ financial_output (model × scenario × output_code × period?)
  │     ↑ irr / moic / npv / exit_val / runway / revenue_year_5 ...
  │     ↑ inputs_hash 가 driver_value snapshot sha256 — reproducibility lock
  │
  └─ exit_matrix_leaf (scenario × timing × path)
        ↑ ic skill exit_matrix yaml 의 3-level tree 의 DB 형태
        ↑ probability × proceeds_krw + dead_leaf/concentration flag (check_exit_matrix.py 결과 캐시)
```

### Schema (SQL skeleton)

```sql
-- per-entity schema (axe_ia_001 / axe_ia_002 / ...)

CREATE TABLE financial_model (
  id                    uuid PRIMARY KEY,
  deal_id               uuid NOT NULL REFERENCES shared.deal(id),
  version               int  NOT NULL,
  name                  text NOT NULL,                    -- e.g., "Iippo IC v8 base model"
  time_axis_kind        text NOT NULL CHECK (time_axis_kind IN ('year','quarter','month')),
  start_period          date NOT NULL,                    -- e.g., 2025-01-01 (Y0)
  horizon_periods       int  NOT NULL,                    -- e.g., 7 (Y0~Y7)
  currency              text NOT NULL DEFAULT 'KRW',
  unit_scale            numeric NOT NULL DEFAULT 1,       -- 1 / 1000 / 1000000 (천원/백만원)
  base_revenue_period0  numeric,                          -- e.g., 120,000,000 (Iippo Y0 매출)
  created_by            text NOT NULL,
  created_at            timestamptz NOT NULL DEFAULT now(),
  locked_at             timestamptz,                      -- 후 immutable
  source_xlsx_sha256    text,
  source_xlsx_citation  jsonb,                            -- {kind:"onedrive", drive_item_id, version, sheet}
  source_yaml_citations jsonb DEFAULT '[]'::jsonb,        -- scenario_deltas/exit_matrix yaml refs
  UNIQUE (deal_id, version)
);

CREATE TABLE financial_driver (
  id              uuid PRIMARY KEY,
  model_id        uuid NOT NULL REFERENCES financial_model(id) ON DELETE CASCADE,
  code            text NOT NULL,                          -- e.g., "revenue_growth_y1"
  kind            text NOT NULL CHECK (kind IN ('input','derived','output')),
  formula         text,                                   -- DSL — null if input
  dependencies    text[] NOT NULL DEFAULT '{}',           -- 다른 driver code list
  units           text NOT NULL,                          -- '%', 'KRW', 'count', 'months', 'multiple'
  category        text NOT NULL,                          -- 'revenue', 'cost', 'opex', 'capex', 'financing', 'wc'
  description     text,
  display_order   int,
  UNIQUE (model_id, code)
);

CREATE TABLE financial_scenario (
  id                     uuid PRIMARY KEY,
  model_id               uuid NOT NULL REFERENCES financial_model(id) ON DELETE CASCADE,
  code                   text NOT NULL,                   -- 'base', 'upside', 'mgmt', 'downside', 'custom_*'
  name                   text NOT NULL,                   -- 'AXE Base'
  kind                   text NOT NULL CHECK (kind IN ('base','upside','mgmt','downside','custom')),
  probability            numeric(5,4) NOT NULL,           -- 0.0000 ~ 1.0000 (per-scenario, 합 = 1)
  description            text,
  driver_changes         jsonb NOT NULL DEFAULT '{}'::jsonb, -- narrative {story, rationale[], key_assumption_overrides}
  parent_scenario_id     uuid REFERENCES financial_scenario(id), -- fork chain
  locked_at              timestamptz,
  UNIQUE (model_id, code)
);

CREATE TABLE financial_driver_value (
  model_id           uuid NOT NULL,
  driver_code        text NOT NULL,
  scenario_id        uuid NOT NULL REFERENCES financial_scenario(id) ON DELETE CASCADE,
  period_index       int  NOT NULL,                       -- 0 ~ horizon_periods
  value              numeric,
  is_override        bool NOT NULL DEFAULT false,         -- true = scenario override on baseline
  PRIMARY KEY (model_id, driver_code, scenario_id, period_index),
  FOREIGN KEY (model_id, driver_code) REFERENCES financial_driver(model_id, code)
);

CREATE TABLE financial_output (
  id              uuid PRIMARY KEY,
  model_id        uuid NOT NULL REFERENCES financial_model(id) ON DELETE CASCADE,
  scenario_id     uuid NOT NULL REFERENCES financial_scenario(id) ON DELETE CASCADE,
  output_code     text NOT NULL,                          -- 'irr', 'moic', 'npv', 'exit_valuation', 'axe_proceeds_total'
  period_index    int,                                    -- null = terminal output, int = time series
  value           numeric,
  computed_at     timestamptz NOT NULL DEFAULT now(),
  inputs_hash     text NOT NULL,                          -- sha256(driver_value snapshot) — reproducibility
  UNIQUE (model_id, scenario_id, output_code, period_index)
);

CREATE TABLE exit_matrix_leaf (
  id                  uuid PRIMARY KEY,
  scenario_id         uuid NOT NULL REFERENCES financial_scenario(id) ON DELETE CASCADE,
  timing              text NOT NULL,                      -- '3y', '5y', '7y', 'never'
  path                text NOT NULL,                      -- 'ma', 'ipo', 'secondary', 'writedown', 'ma_strategic', 'partial'
  probability         numeric(5,4) NOT NULL,              -- 0.0000~1.0000 (per timing, 합 = 1)
  description         text,
  proceeds_krw        numeric,                            -- 회사 전체 exit proceeds (EqV or EV)
  axe_proceeds_krw    numeric,                            -- AXE 회수 (proceeds × ownership_pct_fd × dilution)
  recovery_rate       numeric(5,4),                       -- writedown 시 5-15% 잔존 (D-PR 22 wd-assumption)
  dead_leaf           bool NOT NULL DEFAULT false,        -- check_exit_matrix.py 결과
  concentration       bool NOT NULL DEFAULT false,        -- 단일 leaf P > 10% (concentration warning)
  UNIQUE (scenario_id, timing, path)
);
```

상세 RLS · trigger · index 는 [Phase 0 migration SQL](/services/index/schema-catalog#migration-sql) 참조.

## fund_investment

본 서비스의 두 번째 핵심 abstraction ([D-index-5](/ops/decisions)).

**deal 은 회사 단위 thesis. fund_investment 는 cash flow + IRR carrier.**

### 왜 3차원 N:M?

실 데이터:
- **Iippo**: 1 deal × **2 fund** (1호 99,998,912 + 2호 100,000,000, 같은 round Pre-A)
- **Sentry**: 1 deal × 1 fund × **multi-round option** (1호 5억 + 후속 5억 pro-rata option future)
- **Canopy**: 1 deal × 1 fund × **다른 시점 multi-round** (1호 BW 5억 initial + 1호 신주 follow 1억 v5)

세 deal 모두 N:M 의 다른 축. 단일 (deal, fund) FK pair 로는 부족.

### Schema

```sql
CREATE TABLE shared.fund_investment (
  id                       uuid PRIMARY KEY,
  deal_id                  uuid NOT NULL REFERENCES shared.deal(id),
  fund_entity_id           text NOT NULL REFERENCES shared.entity(id),  -- e.g., 'axe_ia_001'
  round_label              text NOT NULL,                                -- '초기', 'follow-on v5', 'pro-rata option'
  instrument               text NOT NULL,                                -- 'RCPS', 'BW', 'SAFE', '신주', '보통주'
  committed_krw            numeric NOT NULL,                             -- 약정 금액
  invested_krw             numeric,                                      -- 실 납입 (paid 후 set)
  share_count              numeric,                                      -- 주식 수
  price_per_share          numeric,                                      -- 주당 가격
  entry_date               date,                                         -- 결의/계약일
  paid_date                date,                                         -- 납입일 (status=paid 후 set)
  pre_money_krw            numeric,                                      -- 진입 시점
  post_money_krw           numeric,
  ownership_pct_post       numeric(7,6),                                 -- 0.000000 ~ 1.000000
  ownership_pct_fd         numeric(7,6),                                 -- F/D 희석 후
  status                   text NOT NULL CHECK (status IN
                              ('committed','paid','locked_in','written_off','partial_exited','exited')),
  conditions               jsonb DEFAULT '[]'::jsonb,                    -- PCC trigger 같은 조건
  conditions_met_at        timestamptz,                                  -- 조건부 → 무조건 전환 시점
  evidence_citations       jsonb DEFAULT '[]'::jsonb,                    -- 계약서 OneDrive ref 등
  created_at               timestamptz NOT NULL DEFAULT now(),
  updated_at               timestamptz NOT NULL DEFAULT now(),
  UNIQUE (deal_id, fund_entity_id, round_label)
);

CREATE INDEX fund_investment_deal_idx ON shared.fund_investment (deal_id);
CREATE INDEX fund_investment_fund_idx ON shared.fund_investment (fund_entity_id);
CREATE INDEX fund_investment_status_idx ON shared.fund_investment (status) WHERE status IN ('committed','paid','locked_in');
```

### Per-fund IRR 계산

```sql
-- financial_output 의 axe_proceeds_total 산출 → fund_investment.ratio 로 split
-- Iippo 케이스:
--   financial_output (scenario=base, output_code=axe_proceeds_total) = 9.4억 (Y5 회수)
--   fund_investment Iippo×axe_ia_001 ratio = 99,998,912 / 199,998,912 = 0.500003
--   fund_investment Iippo×axe_ia_002 ratio = 100,000,000 / 199,998,912 = 0.499997
--   ↓
--   1호 회수 = 9.4억 × 0.500003 ≈ 4.70억
--   1호 IRR = irr(cash_flow = [-99,998,912 @ 2026-05-15, +4.70억 @ 2030-06-30])
--   2호 회수 = 9.4억 × 0.499997 ≈ 4.70억
--   2호 IRR = irr(cash_flow = [-100,000,000 @ 2026-09-?? (PCC 6 후), +4.70억 @ 2030-06-30])
--
-- 같은 회사 같은 회수 비율이지만 paid_date 가 다르면 IRR 다름 (2호 납입 지연 → IRR 약간 ↓)
```

view `fund_investment_irr`:

```sql
CREATE VIEW shared.fund_investment_irr AS
SELECT
  fi.id              AS fund_investment_id,
  fi.deal_id,
  fi.fund_entity_id,
  fi.round_label,
  fi.committed_krw,
  fi.invested_krw,
  fi.paid_date,
  fs.scenario_id,
  fs.code            AS scenario_code,
  fs.probability     AS scenario_probability,
  fo.value           AS total_axe_proceeds_krw,
  -- fund_investment 비례 split
  fi.committed_krw / SUM(fi.committed_krw) OVER (PARTITION BY fi.deal_id) AS ratio,
  fo.value * (fi.committed_krw / SUM(fi.committed_krw) OVER (PARTITION BY fi.deal_id))
                     AS fund_share_proceeds_krw,
  -- IRR 계산은 Rust 측 compute_outputs 가 채워서 별 row 로 저장 (output_code='irr_per_fund')
FROM shared.fund_investment fi
JOIN financial_scenario fs ON fs.model_id IN (
  SELECT id FROM financial_model WHERE deal_id = fi.deal_id ORDER BY version DESC LIMIT 1
)
JOIN financial_output fo ON fo.scenario_id = fs.id AND fo.output_code = 'axe_proceeds_total';
```

이 view 위에 확률가중 IRR 합산도 SQL aggregate 한 줄.

## DSL Formula

### 왜 한정 DSL?

xlsx formula 를 그대로 못 옮김 — cell ref (`B14`) 를 driver ref (`revenue_y2`) 로 매핑 필요. 또한 Excel 의 ~500 함수 중 IC memo cite 하는 건 10개 미만. 한정 DSL 이 (a) parser 단순, (b) audit 가능, (c) 보안 (eval 안 함).

### 문법 (10 operators)

```
expr        = term (('+' | '-') term)*
term        = factor (('*' | '/') factor)*
factor      = number | driver_ref | func_call | '(' expr ')'
driver_ref  = identifier '[' period ']'  | identifier
period      = 'y0' | 'y1' | ... | 'y<n>' | 'current'
func_call   = func_name '(' args ')'
func_name   = 'min' | 'max' | 'if' | 'sum' | 'avg' | 'lag' | 'growth' | 'cum'
```

예제:

```
# 매출 = 직전 매출 × (1 + 성장률)
revenue[y1] = revenue[y0] * (1 + revenue_growth_y1)
revenue[y2] = revenue[y1] * (1 + revenue_growth_y2)
# ...
revenue[y_n] = lag(revenue, 1) * (1 + lag(revenue_growth, 0))

# 매출원가 = 매출 × (1 - 매출총이익률)
cogs = revenue * (1 - gross_margin_terminal)

# 영업이익 = 매출 - 매출원가 - 판관비
ebit = revenue - cogs - opex_sm - opex_rnd - opex_ga
opex_sm = revenue * opex_sm_per_revenue
opex_rnd = revenue * opex_rnd_per_revenue
opex_ga = revenue * opex_ga_per_revenue

# 운전자본
ar = revenue * ar_days / 365
inventory = cogs * inventory_days / 365
ap = cogs * ap_days / 365

# 세후 영업이익 + CAPEX
nopat = ebit * (1 - tax_rate)
capex = revenue * capex_per_revenue

# 자유현금흐름 + 잔여가치 + IRR
fcf = nopat + depreciation - capex - delta_wc
terminal_value = ebit[exit_year] * exit_multiple
exit_valuation = if(path == 'ipo', terminal_value, terminal_value * 0.85)  # M&A discount
axe_proceeds = exit_valuation * ownership_pct_fd
```

### Topological evaluator

Rust `petgraph::algo::toposort`:

1. DAG 구성: `(driver_code) -[depends_on]-> (driver_code)` edge
2. cycle detect — `toposort` 가 cycle 감지 시 fail-fast (운영자에게 명시적 오류)
3. period 별 evaluation:
   - p=0: input driver value 읽기 (driver_value 테이블)
   - p=1..horizon: derived driver 를 toposort 순서로 평가
   - `lag(driver, n)` 는 p-n 의 cached value 조회
4. scenario 별 4 단계 반복 (base / upside / mgmt / downside)
5. 결과 → `financial_output` row insert (output_code = irr / moic / npv / exit_valuation / etc)
6. `inputs_hash` = sha256(serialized driver_value snapshot) — 같은 input 으로 다시 돌리면 같은 hash + 같은 output

### Reproducibility lock

```
compute_outputs(model_id=X, scenario_id=Y) 호출
  ↓ driver_value snapshot 읽기 → sha256
  ↓ financial_output 의 (model, scenario, output_code) 마다 inputs_hash 비교
  ↓ 같으면 skip (이미 계산됨), 다르면 재산출 + inputs_hash update
```

→ ic skill 의 `tests/fixtures/{DEAL}.json` (lockfile baseline) 패턴 완성. PR 머지 전 fixture diff 자동 검출.

## xlsx round-trip

### ingest (xlsx → DB)

`ingest_financial_model_xlsx(deal_id, xlsx_blob, hint?)` MCP tool. LLM 보조 driver tree extract:

```
1. xlsx 의 sheet 목록 + cell range 파싱 (Rust `calamine`)
2. LLM prompt (Anthropic Sonnet, 1 call ~ 60s):
   "Sheet 'Base' 의 row 'Revenue', 'COGS', 'Gross Profit', ...
    의 cell B14:H14 가 무엇의 어떤 시계열을 표현하는지 추정.
    Schema = financial_driver row 예시 [...]."
3. LLM 응답 → 제안 financial_driver rows + dependencies
4. ctx review queue 에 propose (사용자 confirm/edit/reject)
5. confirmed → financial_driver + financial_driver_value 적재
6. exit_matrix yaml 은 직접 파싱 (LLM 불필요) — schema 정형
7. scenario_deltas yaml 도 직접 (driver value override 만)
8. xlsx 의 IRR cell 과 DB compute_outputs 결과 비교 → ±1pp diff 시 warn
```

vision-heavy 시점만 API direct ([D-bp-artifact-7](/ops/decisions) layer 1 정합). 일상 propose 는 Claude Code OAuth (Max plan).

### export (DB → xlsx)

`export_xlsx(model_id, scenarios[])`. driver tree → xlsx sheet 재생성:

```
1. 시나리오마다 한 sheet (Base / Upside / Mgmt / Downside)
2. row = driver (display_order 순), column = period (y0..y_n)
3. cell value = financial_driver_value.value (input) 또는 evaluator 산출 (derived)
4. derived cell 은 xlsx formula 도 출력 ('=B14*(1+B$3)') — 사용자가 xlsx 안에서 trace 가능
5. exit_matrix → 별 sheet (scenario × timing × path 표)
6. financial_output (IRR / MOIC / NPV) → Output sheet
```

ic skill 의 기존 `ic/finance/3fs_base/v8.xlsx` 와 cell-by-cell 비교 가능 — Phase 0 acceptance test.

## Sanity check 흡수

ic skill 의 4 check 가 index 의 DB CHECK + trigger + `validate_financial_model` tool 로 이전:

| ic skill check | index 측 대응 |
|---|---|
| `xlsx_integrity.py` (5 assertion: BS/CF formula coverage, IS Y-o-Y, Retained Earnings flow, Driver range sanity, cross-scenario shape) | trigger `validate_driver_tree` — DAG cycle / orphan driver / dependency 누락 검출. cross-scenario shape = scenario × driver_code coverage matrix 검사 |
| `check_arithmetic.py` (paragraph self-claim 산수 검증) | memo artifact propose 시 numeric claim 추출 → financial_output cell 비교. `validate_memo_arithmetic(memo_artifact_id, model_id)` tool |
| `check_exit_matrix.py` (dead leaf / concentration / downside semantic / WD recovery 0%) | trigger `exit_matrix_sanity` — INSERT/UPDATE 시 자동 검사. dead_leaf / concentration flag 자동 set |
| `citation_trace.py` (memo 의 단위 동반 숫자 trace) | artifact citation 의 `kind: "index.financial_output"` 가 강제. trace 안 되는 숫자 = orphan citation warning |

ic skill 의 4 Python script 가 index Phase 1 후 deprecate — DB-side enforcement 가 더 결정론적.

## 3 deal worked example (실 데이터 기반)

### Iippo (킵코퍼레이션, iipuda)

기초 데이터: `/Users/axe/Library/CloudStorage/OneDrive-AXE/AXE - Ventures/1_Project/Prj_Iippo/ic/finance/*` (memo v8, scenario_deltas v7, exit_matrix v7).

**target_company** + **deal**:

```yaml
target_company:
  legal_name: "주식회사 킵코퍼레이션"
  brand_name: "iipuda"
  biz_no: "TBD (dataroom 06.정관_등본)"
  sector: "Medical Tourism Platform"
  founding_date: "TBD"

deal:
  deal_code: "Iippo"
  target_company: Iippo
  primary_entity_id: "axe_ia_001"        # 1호가 리드 (블라인드 펀드)
  stage: "Closed"                         # 2026-05-06 IC v8 통과
  owner_email: "ai@axellc.com"
```

**fund_investment** — Iippo 의 1:N split (D-index-5 의 핵심 검증 케이스):

```yaml
fund_investments:
  - fund_entity_id: "axe_ia_001"
    round_label: "Pre-A RCPS 1호 v9.0"
    instrument: "RCPS"
    committed_krw: 99998912                    # 16,832주 × 5,941원
    share_count: 16832
    price_per_share: 5941
    entry_date: "2026-04-30"                    # v9.0 sign-off APPROVED
    paid_date: "2026-05-15"
    pre_money_krw: 6000000000                   # 60억
    post_money_krw: 6200000000                  # 62억
    ownership_pct_post: 0.016130                # 1호 단독 1.613% (1호 + 2호 = 3.226%)
    ownership_pct_fd: 0.010325                  # F/D 1.0325% (희석 0.64x 후)
    status: "paid"
    conditions: []                              # 무조건 GO
    evidence_citations:
      - kind: onedrive
        path: "dataroom/07.리드투자_서류준비/RCPS_v9.0/"

  - fund_entity_id: "axe_ia_002"
    round_label: "Pre-A RCPS 2호 v6 (Conditional)"
    instrument: "RCPS"
    committed_krw: 100000000                    # 잠정 1.5-2.0억 미확정 (강태훈 IC #2)
    share_count: null                            # 협상 진행 중
    price_per_share: 5941                        # 같은 round 같은 단가
    entry_date: null                             # 협상 미완
    paid_date: null                              # 6 PCC 후
    pre_money_krw: 6000000000
    post_money_krw: 6200000000
    ownership_pct_post: 0.016129                # 1.613%
    ownership_pct_fd: 0.010325
    status: "committed"
    conditions:                                 # 6 PCC trigger
      - "PCC #3 — KPI 정합성 + AXEV 소개 병원 status"
      - "PCC #4 — 앤틀러 RCPS amend + Michael Lin SAFE amend"
      - "PCC #5 — 이사회 관찰권 + Pro-rata 명시"
      - "PCC #6 — Q4 anti-dilution 법무 검토"
      - "운영 신호 — 샤오홍슈 캠페인 + Q1-Q2 2026 경영성과"
      - "품질 KPI — iipuda Guide 인증 dashboard 가동"
    evidence_citations:
      - kind: onedrive
        path: "ic/memo/v8.md#1.1.5"
```

**financial_model** (v7 base 기준):

```yaml
financial_model:
  deal_code: "Iippo"
  version: 7
  name: "Iippo IC v7 base model"
  time_axis_kind: "year"
  start_period: "2025-01-01"                    # Y0 = 2025 가결산
  horizon_periods: 7                            # Y0 ~ Y7 (2025~2032)
  currency: "KRW"
  unit_scale: 1
  base_revenue_period0: 120000000               # 1.20억 = 2025 가결산
  source_xlsx_citation:
    kind: onedrive
    path: "Prj_Iippo/ic/finance/3fs_base/v7.xlsx"
```

**financial_scenario** (4종):

```yaml
financial_scenarios:
  - code: base
    name: "AXE Base"
    kind: base
    probability: 0.50
    description: "AXE 보수화 — 회사 IR 8개월 200병원 시뮬레이션을 시장 ramp 평균 (월 +5병원) 으로 할인"
    driver_changes:
      story: "병원 ramp 월 +5, Y3 매출 34.6억, 2030 EBITDA 38억 × EV/EBITDA 12x = 456억 EV"
      key_assumption_overrides:
        hospital_ramp: "월 +5병원 (vs Mgmt 월 +25)"
        china_share: "60% 유지, 다변화 Y3 부터"
        exit_multiple: "EV/EBITDA 12x"

  - code: mgmt
    name: "Mgmt Case"
    kind: mgmt
    probability: 0.10
    description: "회사 IR 가정 그대로 lift"
    driver_changes:
      story: "Y1 200병원/14.75억, Y5 275억, 회사 주장 2030 EV 2조"

  - code: upside
    name: "Upside"
    kind: upside
    probability: 0.30                            # base 보다 약간 낮음
    driver_changes:
      story: "닥터스클럽 leverage 폭발 + 일본/대만 가속 + M&A premium 18x"

  - code: downside
    name: "Downside"
    kind: downside
    probability: 0.10
    driver_changes:
      story: "Y3 매출 stall + 중국 의존 충격"
```

**financial_driver** (15 driver — scenario_deltas yaml 정합):

```yaml
financial_drivers:
  - code: revenue_growth_y1
    kind: input
    units: multiple
    category: revenue
    description: "Y1 매출 성장 multiplier (revenue[y1] / revenue[y0])"
  - code: revenue_growth_y2
    kind: input
    units: multiple
    category: revenue
  - code: revenue_growth_y3
    kind: input
    # ... y4, y5, y6, y7
  - code: gross_margin_terminal
    kind: input
    units: percent
    category: cost
  - code: opex_sm_per_revenue
    kind: input
    units: percent
    category: opex
  - code: opex_rnd_per_revenue
    kind: input
  - code: opex_ga_per_revenue
    kind: input
  - code: ar_days
    kind: input
    units: count
    category: wc
  - code: inventory_days
    kind: input
  - code: ap_days
    kind: input
  - code: capex_per_revenue
    kind: input
    units: percent
    category: capex
  - code: depreciation_years
    kind: input
    units: count
  - code: tax_rate
    kind: input
    units: percent

  # Derived
  - code: revenue
    kind: derived
    formula: "lag(revenue, 1) * (1 + revenue_growth_y[period])"
    dependencies: ["revenue", "revenue_growth_y1", ..., "revenue_growth_y7"]
    units: KRW
    category: revenue
  - code: ebit
    kind: derived
    formula: "revenue * gross_margin_terminal - revenue * (opex_sm_per_revenue + opex_rnd_per_revenue + opex_ga_per_revenue)"
    dependencies: ["revenue", "gross_margin_terminal", "opex_sm_per_revenue", "opex_rnd_per_revenue", "opex_ga_per_revenue"]
    units: KRW
    category: revenue
  # ... 추가 derived (fcf / nopat / exit_valuation / axe_proceeds)
```

**financial_driver_value** (base scenario 일부):

```yaml
financial_driver_values:
  # base scenario
  - { scenario: base, driver: revenue_growth_y1, period: 1, value: 6.5 }      # Y1 = 7.8억 (Y0 × 6.5)
  - { scenario: base, driver: revenue_growth_y2, period: 2, value: 2.4 }      # Y2 = 18.7억
  - { scenario: base, driver: revenue_growth_y3, period: 3, value: 1.85 }     # Y3 = 34.6억
  - { scenario: base, driver: revenue_growth_y4, period: 4, value: 1.55 }
  - { scenario: base, driver: revenue_growth_y5, period: 5, value: 1.40 }     # Y5 = 75억
  - { scenario: base, driver: revenue_growth_y6, period: 6, value: 1.30 }
  - { scenario: base, driver: revenue_growth_y7, period: 7, value: 1.25 }
  - { scenario: base, driver: gross_margin_terminal, period: 5, value: 0.85 }
  - { scenario: base, driver: opex_sm_per_revenue, period: 5, value: 0.35 }
  - { scenario: base, driver: opex_rnd_per_revenue, period: 5, value: 0.12 }
  - { scenario: base, driver: opex_ga_per_revenue, period: 5, value: 0.20 }
  - { scenario: base, driver: ar_days, period: 5, value: 30 }
  - { scenario: base, driver: inventory_days, period: 5, value: 0 }
  - { scenario: base, driver: ap_days, period: 5, value: 30 }
  - { scenario: base, driver: capex_per_revenue, period: 5, value: 0.02 }
  - { scenario: base, driver: depreciation_years, period: 5, value: 5 }
  - { scenario: base, driver: tax_rate, period: 5, value: 0.22 }
  # ... upside / mgmt / downside override (scenario_deltas yaml 의 다른 값)
```

**exit_matrix_leaf** (Iippo base 5y 일부):

```yaml
exit_matrix_leaves:
  # base × 5y × ma
  - { scenario: base, timing: '5y', path: ma, probability: 0.55, axe_proceeds_krw: 1235000000 }
  # base × 5y × ipo
  - { scenario: base, timing: '5y', path: ipo, probability: 0.20, axe_proceeds_krw: 1000000000 }
  # base × 5y × secondary
  - { scenario: base, timing: '5y', path: secondary, probability: 0.20, axe_proceeds_krw: 600000000 }
  # base × 5y × writedown
  - { scenario: base, timing: '5y', path: writedown, probability: 0.05, recovery_rate: 0.10 }
  # ... 3y / 7y / mgmt / upside / downside 동형
```

**financial_output** (compute_outputs 산출):

```yaml
financial_outputs:
  - { scenario: base, output_code: axe_proceeds_total, value: 940000000 }     # Y5 base 회수 9.4억
  - { scenario: base, output_code: irr, value: 0.470 }                          # 47.0%
  - { scenario: base, output_code: moic, value: 4.70 }                          # 회수/투자 ≈ 4.7x

  # 확률가중 (4 scenario × probability)
  - { scenario: weighted, output_code: irr_success_only, value: 0.539 }         # 성공 IRR 53.9% (memo §1.1)
  - { scenario: weighted, output_code: irr_loss_included, value: 0.370 }        # 손실 포함 IRR 37.0%
  - { scenario: weighted, output_code: moic_loss_included, value: 14.9 }        # MoM 14.9x
```

**Per-fund IRR** (fund_investment 별):

```yaml
# financial_output 의 axe_proceeds_total = 9.4억 (base)
# 1호 ratio = 99,998,912 / 199,998,912 = 0.500003
# 2호 ratio = 100,000,000 / 199,998,912 = 0.499997
fund_investment_irrs:
  - { fund: axe_ia_001, scenario: base,
      paid_date: "2026-05-15", proceeds_at: "2030-06-30",
      cash_flow: [-99998912, +470004700],
      irr: 0.4732, moic: 4.70 }
  - { fund: axe_ia_002, scenario: base,
      paid_date: "2026-08-15",                                  # PCC 6 후 3 개월 가정
      proceeds_at: "2030-06-30",
      cash_flow: [-100000000, +469995300],
      irr: 0.4801, moic: 4.70 }                                  # 같은 회수, paid 3 개월 지연 → IRR 약간 ↑ (보유 기간 짧음)
```

### Sentry (센티넬딥액티브)

기초 데이터: `/Users/axe/Library/CloudStorage/OneDrive-AXE/AXE - Ventures/1_Project/Prj_Sentry/ic/{memo/v4.md, finance/*}`.

```yaml
target_company:
  legal_name: "주식회사 센티넬딥액티브"
  biz_no: "708-86-02988"
  sector: "AI Asset Management"
  founding_date: "2023"

deal:
  deal_code: "Sentry"
  primary_entity_id: "axe_ia_001"
  stage: "Closed"                        # 2026-05-14 v4 만장 찬성

fund_investments:
  - fund_entity_id: "axe_ia_001"
    round_label: "Pre-A Bridge RCPS"
    instrument: "RCPS"
    committed_krw: 500068750              # 4,495주 × 111,250원
    share_count: 4495
    price_per_share: 111250
    entry_date: "2026-05-06"              # 현대차 5-6 날인
    paid_date: "2026-05-08"
    pre_money_krw: 9290000000             # 92.9억
    post_money_krw: 9790000000            # 97.9억
    ownership_pct_post: 0.0473            # 4.73%
    ownership_pct_fd: 0.0446              # 4.46% F/D
    status: "paid"

  - fund_entity_id: "axe_ia_001"
    round_label: "Series A Pro-rata Option"
    instrument: "TBD"                     # 후속 라운드 시 결정
    committed_krw: 500000000              # 추가 5억 option
    status: "committed"                   # 옵션 — 6 개월 KPI 충족 시 행사
    conditions:
      - "Series A 라운드 진입 확정"
      - "후속 6개월 KPI 충족"

# financial_model (Sentry v3 기준 — 본 docs 의 worked example 은 abbreviate)
financial_model:
  version: 3
  source_xlsx_citation:
    path: "Prj_Sentry/ic/finance/3fs_base/v3.xlsx"
  scenarios:
    - { code: upside, probability: 0.19, axe_proceeds: 23600000000 }   # 236억 (5y M&A_strategic or IPO)
    - { code: base, probability: 0.49 }
    - { code: mgmt, probability: 0.05 }
    - { code: downside, probability: 0.27 }
  outputs:
    irr_success_only: 0.599                # 59.9% (memo §1.1)
    irr_loss_included: 0.414               # 41.4%
    moic_success_only: 16.8
    moic_loss_included: 13.2
```

Sentry 는 **1 deal × 1 fund × 2 round** (initial + future option) — fund_investment 2 row.

### Canopy (사이클로이드, bardream)

기초 데이터: `Prj_Canopy/ic/{memo/v5_final.md, finance/*}`.

```yaml
target_company:
  legal_name: "주식회사 사이클로이드"
  brand_name: "bardream"
  sector: "Electric Mobility Subscription"

deal:
  deal_code: "Canopy"
  primary_entity_id: "axe_ia_001"
  stage: "Closed"                        # v5 final 2026-05-15

fund_investments:
  - fund_entity_id: "axe_ia_001"
    round_label: "Initial BW (Pre-A)"
    instrument: "BW"                     # Bond with Warrants
    committed_krw: 500000000             # 5억
    entry_date: "TBD"
    paid_date: "TBD"
    pre_money_krw: 6500000000            # v4 기준 (v5 협상 anchor 80억 F/D 81.28억)
    status: "paid"

  - fund_entity_id: "axe_ia_001"
    round_label: "v5 신주 follow-on"
    instrument: "신주"
    committed_krw: 100000000             # 1억 follow (Soohun framing)
    entry_date: "2026-05-15"             # v5 final
    paid_date: "TBD"
    pre_money_krw: 8000000000            # O/S Pre 80억 (Post 100억, F/D Pre 81.28억)
    post_money_krw: 10000000000
    status: "committed"
    conditions:
      - "AXEC 자금 별도 plug (§10 CP-5)"
      - "컨소시엄 분담 시도 (부산창경 등)"

# financial_model (Canopy v1)
financial_model:
  version: 1
  source_xlsx_citation:
    path: "Prj_Canopy/ic/finance/3fs_base/v1.xlsx"
  scenarios:
    - { code: upside, probability: 0.25 }
    - { code: base, probability: 0.45 }
    - { code: downside, probability: 0.30 }
  exit_assumptions:
    fleet_year_5: 5500                   # V3 base 5,500대
    market_share: 0.30                   # 30% (서울시 PM 25만대 expansion)
    ebitda_year_5_krw: 6641000000        # 66.41억 (57.5 + 광고 element 8.91)
    exit_multiple_ev_ebitda: 12
    exit_ev_krw: 79700000000             # 797억
    exit_net_debt_krw: 30000000000       # 300억
    exit_eqv_fd_krw: 49800000000         # 498.28억
  outputs:
    # D-index-16 (2026-05-28): per-leaf methodology 통일. V5_final 정합 데이터로
    # per-leaf 가 27% 매칭 검증 완료 (Canopy 26.39%, baseline 27%, Δ=0.01).
    # 아래 anchor 산출은 deprecated — V1 → V5_final 정합 후 자동 일치.
    irr_loss_included_per_leaf: 0.27     # ⭐ IC PASS (per-leaf, D-index-16)
    hurdle_irr: 0.15
    irr_excess: 0.119                    # +11.9pp 여유
```

Canopy 는 **1 deal × 1 fund × 2 round** (다른 시점 + 다른 instrument BW → 신주).

## 3 deal cross-check 표

| 항목 | Iippo | Sentry | Canopy |
|---|---|---|---|
| Multi-fund | **2 fund** (1호+2호) ⭐ | 1 fund | 1 fund |
| Multi-round | 1 round | 2 round (initial + future option) | **2 round (initial + follow-on)** ⭐ |
| Instrument 변화 | RCPS only | RCPS → TBD (옵션) | BW → 신주 ⭐ |
| Total committed | 199,998,912원 | 500,068,750원 (+ 5억 option) | 500,000,000원 (+ 1억 follow) |
| Scenarios | 4 (base/mgmt/upside/downside) | 4 | 3 (upside/base/downside) |
| Probability base | 0.50 | 0.49 | 0.45 |
| Success IRR (per-leaf) | 53.9% | 59.9% | 44.1% (V5 정합 후) |
| IC pass IRR (loss-included, per-leaf) | **37%** ⭐ | **41%** ⭐ | **27%** ⭐ |
| Hold years | 4.17y | 5.4y | 4.67y |
| Exit multiple | EV/EBITDA 12x | PER 15x (upside) / 8x (base) | EV/EBITDA 12x |
| Key memo | v8 (2026-05-06) | v4 (2026-05-14) | v5_final (2026-05-15) |

세 deal 이 fund_investment 의 3 차원 N:M 을 모두 cover — Phase 0 acceptance fixture ([D-index-10](/ops/decisions)).

## MCP tools (financial 관련)

> **⚠ design-era 이름** — 아래 표는 D-index-4/5 설계 시 가안한 tool 명세다. served tool 의 현행 SoT 는 [/services/index § MCP 도구 카탈로그](/services/index#mcp-도구-카탈로그-27) (27개). 매핑: `ingest_financial_model_xlsx`→`import_xlsx`/`propose_artifacts_from_ingest`, `compare_drivers_across_deals`→`cross_deal_benchmark`, `export_xlsx`→`export_lp_model`/`export_lp_bundle`, `get_financial_model`→`get_deal`. IRR 은 `weighting` 인자 없이 **IRR(E[CF]) 가 유일 canonical** ([D-index-25](/ops/decisions)/[32](/ops/decisions) — `success_only`/`loss_included` weighting 폐기). `compute_full_model` (DSL evaluator) + `compute_3fs` (IS/BS/CF) 가 추가됨.

| Tool | Scope | 기능 |
|---|---|---|
| `ingest_financial_model_xlsx(deal_id, xlsx_blob, hint?)` | write | LLM 보조 driver tree extract → propose financial_model + drivers + values |
| `propose_financial_model(deal_id, drivers, scenarios, values, exit_matrix)` | write | DB 직접 (xlsx 없이) |
| `update_driver(model_id, code, formula?, dependencies?, units?)` | write | unlocked 만 |
| `update_driver_value(model_id, driver_code, scenario_id, period_index, value)` | write | unlocked 만 |
| `create_scenario(model_id, code, name, kind, probability, driver_changes, overrides{})` | write | parent_scenario fork chain |
| `compute_outputs(model_id, scenario_id?)` | write | topo evaluator 실행 → output row 적재. inputs_hash 비교 |
| `get_financial_model(model_id, include_drivers?, include_values?)` | read | model + components |
| `list_scenarios(model_id)` | read | scenario 4종 + probability |
| `query_driver_series(deal_id, driver_code, periods[], scenarios[])` | read | cross-deal driver benchmark — 본 서비스 핵심 query |
| `query_irr(deal_id, fund_investment_id?, weighting='success_only'|'loss_included')` | read | 확률가중 IRR 한 줄 답 |
| `compare_drivers_across_deals(driver_code, deal_ids[], scenarios[])` | read | cross-portfolio benchmark |
| `lock_financial_model(model_id)` | approve | immutable 전환 + locked_at set |
| `export_xlsx(model_id, scenario_ids[])` | read | round-trip 재생성 |
| `validate_financial_model(model_id)` | read | ic skill 의 4 check 통합 결과 |

## Phase 0 Acceptance Bar

[D-index-10](/ops/decisions) 의 검증 fixture:

| Test | 조건 |
|---|---|
| **A1** | 3 deal seed yaml → index DB ingest 성공 (target_company + deal + fund_investment + financial_model + financial_driver + financial_scenario + financial_driver_value + exit_matrix_leaf 모두 적재) |
| **A2** | `compute_outputs(iippo_model_id, base_scenario_id)` 결과의 `axe_proceeds_total` 가 ic skill 의 `irr_analysis/v7.xlsx` cell J42 와 ±1% 일치 |
| **A3** | `query_irr(iippo_deal_id, fund_investment_id=axe_ia_001, weighting='success_only')` = 53.9% ±0.5pp |
| **A4** | `query_irr(iippo_deal_id, fund_investment_id=axe_ia_002, weighting='success_only')` 가 1호 IRR 과 다름 (paid_date 지연 반영) |
| **A5** | `compare_drivers_across_deals('gross_margin_terminal', [iippo, sentry, canopy], ['base'])` 가 3 row 반환 |
| **A6** | `export_xlsx(iippo_model_id, [base_scenario_id])` 결과 xlsx 가 원본 `3fs_base/v7.xlsx` 와 ±0 driver cell diff |
| **A7** | `validate_financial_model(iippo_model_id)` 4 check 모두 PASS (또는 ic skill 의 기존 warning 과 100% 일치) |
| **A8** | Blueprint artifact 의 IC memo citation = `{ kind: "index.financial_output", model_id, scenario_id: "base", output_code: "irr" }` resolve 시 0.470 반환 |

8 test 통과 시 Phase 0 완료.

## 관련 페이지

- [/services/index](/services/index) — 서비스 main
- [/services/index/schema-catalog](/services/index/schema-catalog) — 14 schema 상세 spec
- [/services/index/skill-evolution](/services/index/skill-evolution) — `--push-to-index` flow + ic skill 진화
- [/services/index/skill-integration](/services/index/skill-integration) — 3 skill 통합 아키텍처 + atomic propose + idempotency + 3-layer error model
- [/ops/decisions D-index-4](/ops/decisions) — financial_model 6-table 결정
- [/ops/decisions D-index-5](/ops/decisions) — fund_investment N:M 결정
- [/ops/decisions D-index-10](/ops/decisions) — 3 deal Phase 0 fixture
- [/ops/roadmap M7](/ops/roadmap#m7--index-투자-도메인-backend) — 3 Phase 구조
