LabHub

ブログ

IBシステムアーキテクチャ — ディールパイプラインからシンジケーションまで

한국어English日本語

はじめに

銀行ITについて語られるとき、ほとんどの資料はリテールバンキング(預金・貸出・勘定系)に集中しています。しかし投資銀行(IB)部門のシステムはまったく異なる姿をしています。数百万の口座ではなく数十件のディール(案件)を扱い、毎秒数千件の取引ではなく数か月にわたるワークフローを扱います。トランザクション量は小さいものの、1件のディールの金額は数百億円から数兆円に達し、未公開重要情報(MNPI)が流れる場所であるため、統制の失敗コストは計り知れません。

本記事は、IB部門のシステムを初めて設計・保守するエンジニアのために、業務ドメインマップからディールのライフサイクル、コンフリクトチェック、チャイニーズウォール(情報遮断壁)のシステム的実装、ブックビルディング、シンジケートローン管理、ドキュメント管理、規制報告までを一枚の絵として整理します。韓国資本市場(資本市場法、金融投資業規定)の文脈も併せて扱います。

なお、本記事はシステム設計の観点からの技術資料であり、投資助言や法律助言ではありません。規制に関する内容は必ず所属機関のコンプライアンス部門にご確認ください。

IB業務ドメインマップ

IB部門は大きく5つのドメインに分けられます。

+---------------------------------------------------------------+
|                     Investment Banking Division                |
|                                                               |
|  +-----------+  +-----------+  +-----------+                  |
|  |   ECM     |  |   DCM     |  |   M&A     |                  |
|  | 株式資本   |  | 負債資本   |  | 買収合併   |                  |
|  | 市場      |  | 市場      |  | アドバイザリ|                  |
|  | IPO/増資  |  | 社債/ABS  |  | 売手/買手  |                  |
|  | /CB      |  | /MTN     |  | フェアネス  |                  |
|  +-----------+  +-----------+  +-----------+                  |
|                                                               |
|  +------------------+  +---------------------------+          |
|  | Acquisition      |  | Loan Syndication          |          |
|  | Finance (買収金融)|  | (シンジケートローン組成)     |          |
|  | LBO/ブリッジローン|  | アレンジャー/エージェント銀行 |          |
|  +------------------+  +---------------------------+          |
|                                                               |
|  共通基盤: ディールパイプライン / コンフリクトチェック /          |
|           チャイニーズウォール / ドキュメント管理 /              |
|           インサイダーリスト / 規制報告 / CRM                   |
+---------------------------------------------------------------+

各ドメインのシステム要件を比較すると次のとおりです。

ドメイン主要成果物中核システム機能ライフサイクルの長さ
ECMIPO、公募増資、転換社債ブックビルディング、需要予測、配分3〜12か月
DCM社債、ABS、MTNプログラム発行日程管理、プライシング、需要把握1〜6か月
M&A売手/買手アドバイザリ、バリュエーションディールルーム(VDR)、文書管理、コンフリクトチェック6〜18か月
買収金融LBOローン、ブリッジローン限度・エクスポージャー管理、与信承認ワークフロー3〜9か月
シンジケーションシンジケートローン組成参加機関管理、手数料配分、エージェント業務融資満期まで数年

リテールとの最大の違いは、すべてのドメインがディール単位で動く点です。システムの第一級オブジェクトは口座ではなくディールであり、チーム、文書、承認、情報アクセス権限のすべてがディールにぶら下がります。

ディールのライフサイクルとパイプライン管理

ディールは通常、次の段階を経ます。

[Origination]      [Execution]                    [Closing]      [Post-Close]
     |                  |                             |               |
 アイデア/ピッチ --> マンデート獲得 --> デューデリ --> マーケティング/ --> プライシング
     |             |                |           ブックビルディング      |
     v             v                v              |                  v
 コンフリクト    エンゲージメント   ディールルーム  インサイダー       配分・決済・
 チェック       レター締結        開設(VDR)    リスト拡大          手数料精算
 (事前審査)                                                          |
                                                                    v
                                                          事後管理/リーグテーブル

ディールパイプライン管理システムの中核要件は次のとおりです。

ステージ遷移をコードで強制することが重要です。スプレッドシートでパイプラインを管理すると、コンフリクトチェックを迂回したディールが必ず生まれます。

コンフリクトチェック — 案件を受ける前に止める

コンフリクトチェックは、新規案件を受任する前に、既存のディール・顧客関係と利益相反がないかを確認する手続きです。たとえばA社の売却アドバイザリを受任しようとしているのに、すでにB社によるA社買収のアドバイザリを担当している場合、両方を同時に受任することはできません。

システムの観点では、コンフリクトチェックはディール作成ワークフローのゲートです。

-- コンフリクトチェックの中核クエリ: 同一・関連対象企業について
-- アクティブなディールが存在するかを検出
SELECT d.deal_id,
       d.deal_type,
       d.stage,
       c.company_name,
       r.relation_type        -- TARGET / ACQUIRER / ISSUER / SPONSOR
FROM   deal d
JOIN   deal_company_rel r ON r.deal_id = d.deal_id
JOIN   company c          ON c.company_id = r.company_id
WHERE  d.stage NOT IN ('CLOSED', 'DEAD')
AND    c.company_group_id IN (
         -- 新規ディール対象企業と同じ企業グループまで拡張
         SELECT company_group_id FROM company
         WHERE  company_id = :new_deal_target_id
       );

実務上の注意点:

チャイニーズウォール(情報遮断壁)のシステム的実装

チャイニーズウォールは、IB部門(プライベートサイド、MNPI保有)とリサーチ・セールス&トレーディング部門(パブリックサイド)の間の情報の流れを遮断する統制です。韓国では資本市場法上の情報交流遮断(利益相反防止)規制として実装が義務付けられています。システム的には3つのレイヤーで実装します。

レイヤー1 — 権限(アクセス制御)

ディール情報はデフォルトでdeny-allとし、ディールチームのメンバーシップによってのみ開放します。

# ディール単位アクセスポリシーの例(ポリシーエンジン入力)
policy:
  resource: deal/PRJ-FALCON
  default: deny
  rules:
    - effect: allow
      subjects:
        - group: deal-team/PRJ-FALCON        # ディールチームメンバー
        - group: compliance-surveillance      # コンプライアンス(常時閲覧権)
      actions: [read, write]
    - effect: allow
      subjects:
        - group: ib-management                # 部門経営陣
      actions: [read-summary]                 # 要約のみ、文書アクセス不可
    - effect: deny
      subjects:
        - group: research                     # リサーチ部門は明示的に遮断
        - group: sales-trading
      actions: [read, write, read-summary]
      override: true                          # allowより優先

中核となる設計原則:

レイヤー2 — ウォーターマーク(透かし)

ディール文書は流出時に出所を追跡できる必要があります。ダウンロード・閲覧時点でユーザーごとの透かしを動的に付与します。

# 文書閲覧時の動的ウォーターマーク適用(概念例)
from pypdf import PdfReader, PdfWriter
from reportlab.pdfgen import canvas
from io import BytesIO
import datetime

def apply_watermark(src_pdf: bytes, user_id: str, deal_code: str) -> bytes:
    stamp_buf = BytesIO()
    c = canvas.Canvas(stamp_buf)
    text = f"{deal_code} / {user_id} / {datetime.datetime.utcnow().isoformat()}"
    c.setFont("Helvetica", 7)
    c.setFillColorRGB(0.6, 0.6, 0.6, alpha=0.4)
    # 斜め方向の繰り返しウォーターマーク
    c.saveState()
    c.translate(300, 400)
    c.rotate(45)
    for y in range(-400, 400, 60):
        c.drawString(-250, y, text)
    c.restoreState()
    c.save()
    stamp_buf.seek(0)

    stamp = PdfReader(stamp_buf).pages[0]
    reader = PdfReader(BytesIO(src_pdf))
    writer = PdfWriter()
    for page in reader.pages:
        page.merge_page(stamp)
        writer.add_page(page)
    out = BytesIO()
    writer.write(out)
    return out.getvalue()

可視的な透かしに加えて、メタデータやステガノグラフィーに基づく不可視の透かしを併用する機関もあります。重要なのは、透かし適用イベント自体がアクセスログと結び付いていることです。

レイヤー3 — ロギングと監視

MNPI管理とインサイダーリスト

インサイダーリストは、特定のディールの未公開重要情報にアクセスした人の名簿です。EUのMAR(市場濫用規制)は発行体とアドバイザーにインサイダーリストの作成を明示的に義務付けており、韓国でも未公開重要情報利用行為規制(資本市場法第174条)への対応のため同様の統制を運用します。

CREATE TABLE insider_list_entry (
    entry_id        BIGINT PRIMARY KEY,
    deal_id         BIGINT NOT NULL REFERENCES deal(deal_id),
    person_id       BIGINT NOT NULL REFERENCES person(person_id),
    reason          VARCHAR(200) NOT NULL,   -- 登録事由(チーム、ウォールクロス、外部アドバイザー等)
    obtained_at     TIMESTAMP NOT NULL,      -- MNPIアクセス開始時刻
    notified_at     TIMESTAMP,               -- 本人への通知時刻(義務的告知)
    removed_at      TIMESTAMP,               -- リスト除外時刻
    removed_reason  VARCHAR(200),
    created_by      VARCHAR(50) NOT NULL,
    created_at      TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);

-- インサイダーリストは訂正履歴まで保存する必要があるためUPDATE禁止、
-- 変更は新規row + 旧rowの無効化で処理(バイテンポラルパターン)
CREATE INDEX idx_insider_deal ON insider_list_entry (deal_id, obtained_at);
CREATE INDEX idx_insider_person ON insider_list_entry (person_id, obtained_at);

運用ポイント:

ブックビルディングシステム — 需要予測と配分

ECM/DCMディールのハイライトはブックビルディングです。機関投資家から需要(価格、数量)を受け付けて需要曲線を作り、発行価格を決定した後、物量を配分します。韓国のIPOでは機関投資家の需要予測が金融投資協会の規定に従って運営されます。

ブックビルディングシステムの中核モデルは注文(order)と配分(allocation)です。

# 単純化した配分エンジンの概念例
# 実際の配分は定性評価(アンカー投資家、長期保有性向)が組み合わさる
# 審査プロセスであり、以下は比例配分の骨格のみを示す。
from dataclasses import dataclass

@dataclass
class Order:
    investor_id: str
    quantity: int          # 申込数量
    limit_price: int       # 希望価格、成行はレンジ上限として処理
    investor_grade: str    # 内部格付: ANCHOR / LONG_ONLY / HEDGE / RETAIL_INST

GRADE_WEIGHT = {"ANCHOR": 1.5, "LONG_ONLY": 1.2, "HEDGE": 0.8, "RETAIL_INST": 1.0}

def build_demand_curve(orders, price_grid):
    """価格帯別の累積需要曲線: その価格以上を提示した注文の合計"""
    return {
        p: sum(o.quantity for o in orders if o.limit_price >= p)
        for p in price_grid
    }

def allocate(orders, final_price, total_shares):
    eligible = [o for o in orders if o.limit_price >= final_price]
    weighted = sum(o.quantity * GRADE_WEIGHT[o.investor_grade] for o in eligible)
    allocations = {}
    remaining = total_shares
    for o in sorted(eligible, key=lambda x: GRADE_WEIGHT[x.investor_grade], reverse=True):
        share = int(total_shares * o.quantity * GRADE_WEIGHT[o.investor_grade] / weighted)
        share = min(share, o.quantity, remaining)
        allocations[o.investor_id] = share
        remaining -= share
    # 残余物量は端数補正ルールで再配分(省略)
    return allocations, remaining

システム設計で重要な点:

シンジケートローン管理 — 数年がかりの運用システム

シンジケートローンは、複数の金融機関が1つの借入人に共同で融資する構造です。アレンジャー(主幹事)がディールを組成し、エージェント銀行が融資期間を通じて利息計算、元利金の配分、借入人と参加機関の間の通知を担当します。LMA(欧州)/LSTA(米国)の標準契約が事実上の業界標準です。

システムが管理すべき中核データ:

領域内容
ファシリティ構造Term Loan A/B、RCFなどのトランシェ構造と限度額
参加機関の持分機関別コミットメント金額、譲渡(assignment)履歴
引出・返済引出依頼、利息期間、返済スケジュール
利息・手数料基準金利+マージン、コミットメントフィー、アレンジメントフィー、エージェントフィー
配分利息・元本・手数料の参加機関別按分計算と支払

手数料配分計算の例:

# 利息期間終了時の参加機関別配分計算(概念例)
from decimal import Decimal, ROUND_HALF_UP

def distribute_interest(total_interest: Decimal, participations: dict) -> dict:
    """participations: {institution_id: outstanding_amount}"""
    total_outstanding = sum(participations.values())
    result = {}
    allocated = Decimal("0")
    items = sorted(participations.items())
    for inst, amount in items[:-1]:
        share = (total_interest * amount / total_outstanding).quantize(
            Decimal("0.01"), rounding=ROUND_HALF_UP)
        result[inst] = share
        allocated += share
    # 最後の機関が端数を吸収 — 合計は必ず一致しなければならない
    last_inst = items[-1][0]
    result[last_inst] = total_interest - allocated
    assert sum(result.values()) == total_interest
    return result

運用上もっとも厄介な部分:

買収金融の限度とエクスポージャー管理

買収金融(LBOファイナンス、ブリッジローン)は銀行の自己資本が直接さらされる領域であり、限度管理が中核です。

エクスポージャー集計の概念:
  総エクスポージャー(企業グループG)
    = Σ 実行残高(グループ内の全借入人)
    + Σ 未引出コミットメント × 信用換算率(CCF)
    + Σ 引受ポジション(セルダウン未完了分)
  限度チェックは新規ディール承認時点 + 日次バッチの両方で実行

ディールドキュメント管理 — バージョンと署名

M&Aとシンジケーションでは、文書が成果物のほぼすべてです。要件:

規制報告と韓国資本市場の文脈

韓国においてIB業務は、資本市場と金融投資業に関する法律(資本市場法)および金融投資業規定の適用を受けます。システムの観点でよく出会う報告・開示義務は次のとおりです(正確な要件は時点・機関類型によって異なるため、コンプライアンス部門への確認が必須です)。

設計上の示唆は明確です。規制報告を別システムの仕事として後回しにせず、ディールシステムのデータモデル自体を報告可能な形(不変履歴、時点照会)で作るべきです。

CRMとの統合

IBのCRMは営業CRMとは性質が異なります。カバレッジバンカーが顧客企業の経営陣とのミーティング、ピッチ履歴、ディール機会を管理します。統合ポイント:

データモデルの例

中核エンティティの関係をERDで整理すると次のとおりです。

+--------------+        +-------------------+        +--------------+
|   COMPANY    |        |       DEAL        |        |    PERSON    |
+--------------+        +-------------------+        +--------------+
| company_id PK|---+    | deal_id PK        |    +---| person_id PK |
| group_id     |   |    | code_name UQ      |    |   | dept         |
| lei          |   +--<-| deal_type         |    |   | side (pub/pr)|
| name         |        | stage             |    |   +--------------+
+--------------+        | expected_fee      |    |
       ^                | probability       |    |
       |                +-------------------+    |
       |                  |       |      |       |
+------+--------+         |       |      +-------+----------+
| DEAL_COMPANY_ |---------+       |              |          |
| REL           |        +--------+-----+  +-----+------+  +-+----------+
| (TARGET/      |        | DEAL_DOCUMENT|  | DEAL_TEAM  |  | INSIDER_   |
|  ACQUIRER/    |        +--------------+  +------------+  | LIST_ENTRY |
|  ISSUER...)   |        | doc_id PK    |  | deal_id FK |  +------------+
+---------------+        | deal_id FK   |  | person_id  |  | deal_id FK |
                         | version      |  | role       |  | person_id  |
+---------------+        | status       |  | granted_at |  | obtained_at|
| ORDER (ブック) |        | sha256       |  | revoked_at |  | removed_at |
+---------------+        +--------------+  +------------+  +------------+
| order_id PK   |
| deal_id FK    |        +-----------------+   +----------------------+
| investor_id   |        | FACILITY (ローン)|   | PARTICIPATION (持分) |
| qty / price   |        +-----------------+   +----------------------+
| superseded_by |        | facility_id PK  |---| facility_id FK       |
+---------------+        | deal_id FK      |   | institution_id       |
                         | tranche_type    |   | commitment_amt       |
+---------------+        | limit_amt       |   | effective_from/to    |
| ALLOCATION    |        | margin_bps      |   +----------------------+
+---------------+        +-----------------+
| order_id FK   |
| alloc_qty     |
| adjusted_by   |
+---------------+

モデリング原則:

ワークフローエンジン設計

ディールのライフサイクル、ウォールクロッシング、与信承認、署名追跡はすべてワークフローです。自作するにせよエンジン(Camunda、Temporalなど)を使うにせよ、IBドメインで要求される特性は次のとおりです。

# ディールのステージ遷移を明示的な状態機械で強制する例
from enum import Enum

class Stage(Enum):
    PITCH = "PITCH"
    CONFLICT_CHECK = "CONFLICT_CHECK"
    MANDATED = "MANDATED"
    DUE_DILIGENCE = "DUE_DILIGENCE"
    MARKETING = "MARKETING"
    PRICING = "PRICING"
    CLOSED = "CLOSED"
    DEAD = "DEAD"

TRANSITIONS = {
    Stage.PITCH:          {Stage.CONFLICT_CHECK, Stage.DEAD},
    Stage.CONFLICT_CHECK: {Stage.MANDATED, Stage.DEAD},
    Stage.MANDATED:       {Stage.DUE_DILIGENCE, Stage.DEAD},
    Stage.DUE_DILIGENCE:  {Stage.MARKETING, Stage.DEAD},
    Stage.MARKETING:      {Stage.PRICING, Stage.DEAD},
    Stage.PRICING:        {Stage.CLOSED, Stage.DEAD},
}

REQUIRED_APPROVALS = {
    (Stage.PITCH, Stage.CONFLICT_CHECK): ["team_head"],
    (Stage.CONFLICT_CHECK, Stage.MANDATED): ["compliance", "business_head"],
    (Stage.MARKETING, Stage.PRICING): ["syndicate_head", "compliance"],
}

def transition(deal, target: Stage, approvals: set, actor: str):
    if target not in TRANSITIONS[deal.stage]:
        raise ValueError(f"illegal transition {deal.stage} -> {target}")
    needed = set(REQUIRED_APPROVALS.get((deal.stage, target), []))
    if not needed.issubset(approvals):
        raise PermissionError(f"missing approvals: {needed - approvals}")
    audit_log(deal.deal_id, deal.stage, target, actor, approvals)
    deal.stage = target

加えて必要なもの:

落とし穴とアンチパターン

構築チェックリスト

おわりに

IBシステムの本質は取引処理ではなく統制された協業です。少数の高額ディールを取り巻く情報アクセス、承認、記録が正確に統制されることがシステムの存在理由です。機能要件(パイプライン、ブックビルディング、ローン管理)は比較的平易ですが、非機能要件 — 不変履歴、時点照会、デフォルト遮断の権限、自動回収 — が設計の成否を分けます。新しくシステムを作るなら、画面よりも先にデータモデルと権限モデルから設計することをお勧めします。

繰り返しになりますが、本記事は技術資料であり、投資・法律助言ではありません。

参考資料

コメント

まだコメントはありません。

ログインするとコメントできます