決済日 営業日 計算:二国間で祝日が異なる取引を確認する

取引をまたぐ二国の営業日に合わせて決済するバックオフィスは、両国が営業している日に決済日を置く必要があり、通貨ごとの祝日で日付が動きます。Narwhal は国ごとの祝日を適用範囲と地域区分つきで返すので、短いスクリプトで取引一覧を両方のカレンダーに通し、平日だけの計算と日付が異なる取引にすべて印を付けられます。

国際取引に祝日カレンダーが二つ必要な理由

USD/JPY の取引を決済するブローカーのバックオフィスは、日本と米国の両方が営業している日を探す必要があります。期間内にどちらかの国の祝日があると平日だけの計算は誤りになり、1日ずれた決済指示は失敗するか遅れて届きます。祝日エンドポイントは1回に1か国ずつ答えるため、バックオフィスは各レッグについて問い合わせて結果を合わせます。このリクエストは、日本に 2026年11月23日の祝日があるかを尋ねます。

curl、ある1日の日本の祝日
curl -s -H "Authorization: Bearer $NARWHAL_API_KEY" \
  "https://api.narwhalapi.com/v1/calendars/holidays/JPN/on/2026-11-23"
レスポンス
{
  "country": "JPN",
  "coverage": [
    {
      "scope": "national_public",
      "subdivision_codes": []
    }
  ],
  "holidays": [
    {
      "date": "2026-11-23",
      "name": "Labor Thanksgiving Day",
      "holiday_key": "labor_thanksgiving_day",
      "local_name": "勤労感謝の日",
      "scope": "national_public",
      "subdivision_codes": []
    }
  ]
}

該当するエントリは Labor Thanksgiving Day で、scope は national_public です。subdivision_codes が空のリストなら全国に適用されます。米国の 11月26日について同じリクエストを送ると、scope が federal_government の Thanksgiving Day が返ります。国ごとのリストが独自の scope を持つので、バックオフィスはどのリストを使うかを記録しておきます。平日だけの計算では、11月23日(月)もまだ決済日と見なされてしまいます。

取引一覧を二つのカレンダーに通す

スクリプトは取引一覧の各取引を、2つの通貨それぞれの国のカレンダーに照らして決済し、平日だけの計算で出る日付と実際の日付を並べて表示します。2つが異なる取引には CHECK と印が付き、これが運用責任者の確認対象リストになります。

  1. 各レッグの国を指定する JPY には日本と米国を、GBP には英国(デスクの地域区分 GB-ENG)と米国を指定します。
  2. 年ごとに1回だけ読み込む holidays は国と年ごとの祝日リストを取得して保持するので、数百件の取引でもリクエストは数回で済みます。12月31日に近い取引は、日付をたどって翌年に入った時点で翌年分を読み込みます。
  3. どちらかの国の週末と休業日を飛ばす closures は、対象のいずれかの国で当日にあたる祝日を返し、このリストが空の日だけが N 日の数え方に入ります。地域区分リストを持つ祝日は、その区分がリストに含まれる場合にだけデスクを休業させます。
    Python、2つの通貨ペアの決済取引一覧
    import os
    from datetime import date, timedelta
    from functools import cache
    import requests
    
    HEADERS = {"Authorization": f"Bearer {os.environ['NARWHAL_API_KEY']}"}
    
    
    @cache
    def holidays(country, year):
        r = requests.get(
            f"https://api.narwhalapi.com/v1/calendars/holidays/{country}/{year}",
            headers=HEADERS,
            timeout=10,
        )
        r.raise_for_status()
        return r.json()["holidays"]
    
    
    def closures(day, places):
        out = []
        for country, region in places.items():
            for h in holidays(country, day.year):
                # an empty subdivision list means the holiday applies nationwide
                if h["date"] == day.isoformat() and (not h["subdivision_codes"] or region in h["subdivision_codes"]):
                    out.append(f"{country} {h['name']}")
        return out
    
    
    def settle(trade, n, places):
        day, held = trade, []
        while n > 0:
            day += timedelta(1)
            if day.weekday() >= 5:
                continue
            shut = closures(day, places)
            if shut:
                held.append(f"{day:%d %b} {'; '.join(shut)}")
            else:
                n -= 1
        return day, held
    
    
    JPY = {"USA": None, "JPN": None}
    GBP = {"GBR": "GB-ENG", "USA": None}  # country -> subdivision of the desk
    blotter = [
        ("USD/JPY", date(2026, 11, 20), 2, JPY),
        ("USD/JPY", date(2026, 11, 24), 2, JPY),
        ("GBP/USD", date(2026, 12, 23), 2, GBP),
    ]
    for pair, trade, n, places in blotter:
        day, held = settle(trade, n, places)
        plain, _ = settle(trade, n, {})
        flag = "CHECK" if day != plain else "same"
        print(f"{pair} {trade} T+{n} settles {day}, weekdays only {plain} [{flag}]", "; ".join(held))
スクリプトの出力
USD/JPY 2026-11-20 T+2 settles 2026-11-25, weekdays only 2026-11-24 [CHECK] 23 Nov JPN Labor Thanksgiving Day
USD/JPY 2026-11-24 T+2 settles 2026-11-27, weekdays only 2026-11-26 [CHECK] 26 Nov USA Thanksgiving Day
GBP/USD 2026-12-23 T+2 settles 2026-12-29, weekdays only 2026-12-25 [CHECK] 25 Dec GBR Christmas Day; USA Christmas Day; 28 Dec GBR Boxing Day

最初の取引は 11月20日(金)で、平日だけなら 11月24日(火)に決済されます。ところが日本は 11月23日(月)が休みなので、2日目は 11月25日(水)に移ります。2件目の取引は米国の Thanksgiving Day に引っかかります。3件目は 12月28日(月)まで延び、両国の Christmas Day と英国の Boxing Day が重なるためです。この取引一覧のすべての行が、平日だけの計算と異なっています。

バックオフィスが今も手作業で確認すること

確認出どころ
週末土曜と日曜を飛ばすループ
各通貨の国の祝日祝日エンドポイント。レッグごとに1か国
ブッキングデスクの地域の祝日subdivision_codes を、デスクの地域区分と照合する
取引所や決済システムの休業日その取引所独自のリストを、ループが調べる集合に加える
日数。T+1 か T+2 か商品と取引所の規則。API は定めない

最後の2行はバックオフィスの担当です。祝日リストは取引所ではなく国に属するものだからです。

取引の近くに休業日があればトレーダーに警告する

GET /v1/calendars/holidays/{country}/next?from_date=2026-11-20 は、その日以降で最初の祝日を holiday オブジェクトで返します。日本では 11月23日、米国では 11月26日が返り、どちらも T+2 の期間内でした。取引を登録するたびにレッグごとに実行し、休業日が近いことをトレーダーに伝えます。日付そのものの判定には、年のリストを使います。

このリストは取引所や決済システムのカレンダーと一致しますか。

国の祝日を適用範囲と地域区分つきで返します。取引所や決済システムだけの休業日は含まれません。2026年の米国リストは 11件で、一部の取引所が休む 4月3日の Good Friday は入っていません。取引所の休業日は、ループが調べる集合に自分で加えてください。

その年のカレンダーが国にない場合はどうなりますか。

API は、コード calendar_not_published の 404 問題レスポンスを返します。英国の 2028年のリクエストがそうでした。raise_for_status() でスクリプトが止まりますが、誤った指示を出さないためにはこれが安全な動作です。平日だけに切り替えて日付に印を付けるか、待つかを決めてください。

通貨ペアはどの日を飛ばすべきですか。

どちらかの国が休みの日は飛ばします。USD/JPY の取引は、東京と米国の両方が営業している日まで待ちます。T+1 か T+2 か、途中の日の例外の扱いは市場の規則で決まり、API は示さないので、商品ごとの慣行を確認してください。

公開。

次のステップ