1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

系統用蓄電池の事業性シミュレーターを、UIを作らずMCPツールだけで公開した

1
Last updated at Posted at 2026-09-07

系統用蓄電池の事業性シミュレーターを、UIを作らずMCPツールだけで公開した

——「画面のない機能」という選択肢と、公表試算との突合で見つかった抜け

何をしたか

太陽光を伴わない系統用蓄電池(系統直結・アービトラージ専業)の
事業性を試算するツールを作った。

ただし、Gradioの画面は1つも作っていない。
既存のFIP転シミュレーター(pv-sim-fip)の中に
MCPツールとしてだけ生やしている。

https://hachinai-pv-sim-fip.hf.space/gradio_api/mcp/
ツール名 機能
validate_grid_battery_params パラメータの検証・正規化・警告提示
simulate_grid_battery 系統用蓄電池単独の事業性試算

ブラウザでこのSpaceを開いても、
系統用蓄電池のタブはどこにも無い。
MCPクライアントから繋いだときにだけ見える機能だ。

30秒で試す

MCPクライアントから

Claude Codeならこの1行で繋がる。

claude mcp add --scope user --transport http pv-sim-fip https://hachinai-pv-sim-fip.hf.space/gradio_api/mcp/

MCPクライアントを持っていなくても試せる

画面は無いが、HTTP APIとしては叩ける。

pip install gradio_client
from gradio_client import Client

client = Client("hachinai/pv-sim-fip")

result = client.predict(
    jepx_area="東京",
    jepx_fiscal_years=[2020, 2021, 2022, 2023],
    battery_capacity_kwh=1000,
    battery_max_charge_kw=333,                     # 3時間率
    battery_max_discharge_kw=333,
    battery_cost_yen_per_kwh=60000,
    capacity_market_price_yen_per_kw_year=8348,    # 容量市場(デフォルトは0=含まない)
    wheeling_fee_yen_per_kwh=0.88,
    api_name="/simulate_grid_battery",
)

k = result["kpis"]
print("IRR                 :", k["project_irr_pct"], "%")
print("NPV                 :", f'{k["project_npv_yen"]:,}', "円")
print("サイクル強度比      :", k["cycle_intensity_ratio"])
print("実効劣化率          :", k["effective_bat_degrade_pct_per_year"], "%/年")
print("アービトラージ(理論):", f'{k["annual_arbitrage_revenue_theoretical_yen"]:,}', "円/年")
print("アービトラージ(実現):", f'{k["annual_arbitrage_revenue_realized_yen"]:,}', "円/年")

for c in result["caveats"]:
    print("[caveat]", c)
IRR                 : -1.08 %
NPV                 : -15,654,462 円
サイクル強度比      : 1.21
実効劣化率          : 1.21 %/年
アービトラージ(理論): 3,312,818 円/年
アービトラージ(実現): 2,815,895 円/年

指定しなかった引数はサーバ側のデフォルトが使われる。

capacity_market_price_yen_per_kw_yearを外す(デフォルトの0に戻す)と
一気に赤字が深くなる。この挙動の意味は後述する。

なぜ画面を作らなかったか

普通なら新しいSpaceを立てるところだ。
そうしなかった理由は3つある。

1. 計算資産の9割が再利用できた

系統用蓄電池に必要なものを並べると、
ほぼ全部がFIP転シミュレーターに既にあった。

  • JEPXスポット価格DB(9エリア×6年度×17,520コマ)
  • 30分×365日のLP最適化エンジン(PuLP+CBC)
  • キャッシュフロー・IRR・NPVの計算
  • MCPの配管(validate→simulateの2段階プロトコル)

新しく書いたのは、LPからPV発電・売電・出力抑制の項を
落とした縮小版(充電=系統からの買電、放電=系統への売電)と、
単一収益ストリーム用のCF計算だけだ。

2. DBを複製したくなかった

JEPX価格DBは約60MBある。
新しいSpaceを作ると、これをまるごと複製することになる。

3. Hugging Face Spacesの同時稼働枠

CPU Basicはアカウント全体で同時Running数に上限がある。
実際、4本目をデプロイしたときに
You've reached your CPU Basic quota limit across all your Spaces
が出て、優先度の低いSpaceを止めて枠を空ける羽目になった。

MCPツールとして生やすなら、この3つとも回避できる。
UIを作らないという選択が現実的になったのは、
MCPが「計算」と「画面」を分離してくれるからだ。

もちろん代償はある。
ブラウザで開いても存在に気づいてもらえない。
人に見せる看板にはならない機能だと割り切っている。

収益源を2つに絞り、残りは「対象外」と明示した

系統用蓄電池の収益源はいくつもある。
このツールが扱うのは2つだけだ。

収益源 扱い
JEPXスポット市場アービトラージ LPで最適化
容量市場(kW価値) 円/kW/年の固定単価×定格放電出力。デフォルト0円
需給調整市場(EPRX1/2/3等) 明示的にスコープ外

需給調整市場を外したのは、
商品区分ごとに応動時間・拘束時間・入札方式が違い、
実勢価格データも簡単には手に入らないからだ。
精度の低い金額を出すより、対象外だと書く方を選んだ。

容量市場のデフォルトを0円にしたのも同じ理由で、
確認済みの実勢価格を提示できない以上、
**「明示的な未入力=含まない」**の方が誠実だと判断した。

ただし、この割り切りには副作用がある。
デフォルト設定のままだと、現実的なパラメータで
ほぼ必ず「不成立」という答えが出る。

これをそのまま返すと、エージェント経由の利用者は
「系統用蓄電池は儲からない」という結論を持ち帰ってしまう。
実際には需給調整市場の収益を計算に入れていないだけだ。

だからcaveatsに必ずこう書いて返している。

需給調整市場(EPRX1/2/3等の調整力公募)は対象外です。
商品区分ごとに応動時間・拘束時間が異なり、実勢価格データの
入手も容易でないため、精度の低い金額を出すより「対象外」と
明示する方針としています(このツールはスポット市場のみが対象です)

守備範囲を機械可読な形で返すのは、
数字を返すのと同じくらい重要だと思っている。

完全予見のLPは、1日1回では済まない充放電をする

ここからは実装していて見つかった問題の話だ。

蓄電池の劣化率はデフォルト1.0%/年にしていた。
これは三菱総研の試算諸元に合わせた値で、
**「1日1サイクル・20年で容量80%」**という前提に基づく。

ところが系統用蓄電池のLPは、
1年分のJEPX価格を完全予見して最適化する。
その結果どうなるか。

実測すると、東京・2020〜2023年度平均の条件で
年間443.1サイクル(SOC使用幅基準)。
1日あたり1.21回だ。九州のデフォルト年度だと1.76回まで上がる。

前提の1日1回を大きく超えている。
このまま1.0%/年を使うと劣化を過小評価する=収益を過大評価する。

そこで劣化率をサイクル数でスケールするようにした。

実効劣化率 = 入力劣化率 × (実際の年間サイクル数 / 前提サイクル数)

前提サイクル数はdegrade_baseline_cycles_per_year(デフォルト365)
としてパラメータ化してある。

実測での影響(九州・333kW/1000kWh・託送0・容量市場0):

モデル 劣化率 NPV
旧(カレンダー依存) 1.00 %/年 −57,392,091 円
新(サイクル依存) 1.76 %/年 −60,364,071 円

同条件での容量市場単価の損益分岐点も
約10,540円/kW/年 → 約11,086円/kW/年 に上がった。
劣化が速くなった分、採算が厳しく評価されるようになった。

なおこの補正は系統用蓄電池モジュールだけに入れている。
PV併設ケースの蓄電池は、実際のサイクル強度が
どれだけ前提から乖離しているか未検証なので触っていない。

「等価フルサイクル数」がどっちの意味か分からない問題

上のサイクル数を出力に入れるとき、地味に困った。

equivalent_full_cycles という1つのフィールドだと、
定格容量基準なのか、実際に使える容量基準なのかが
読み手に分からない。

SOC下限10%・上限90%で運用していれば、
使える容量は定格の80%しかない。
どちらを分母にするかで数字が25%変わる。

結局、両方返すことにした。

{
  "equivalent_full_cycles_nameplate": 354.5,
  "equivalent_full_cycles_usable_soc": 443.1,
  "cycle_basis_note": "nameplateは放電量÷定格容量、usable_socは放電量÷実使用可能容量(SOC上下限の幅)で算出。劣化率のサイクル補正にはusable_socを使用しています"
}

劣化率の補正には後者(業界のサイクル寿命規格に近い基準)を使う。
どちらを使ったかも書いて返す。

エージェント経由だと「この数字はどっちの定義か」を
聞き返してもらえない。名前とnoteで自己記述させるしかない。

公表試算と突合したら、費用項目が4つ抜けていた

三菱総研の資料には
「シナリオ別の収益性構造比較」というチャートがある。
そこと突き合わせてみた。

純アービトラージ収益は
当社4.98万円/kWh・資料ベース4.97万円/kWh で差0.2%。
計算ロジックは合っている。

にもかかわらずIRRが合わない。
原因はこちらに費用項目が足りていなかったことだった。
ロジックのバグではなく、単純な計上漏れだ。

追加したのは4つ。

1. 固定資産税(PV併設ケースにも共通で追加)
簿価逓減方式で計上。標準税率1.4%/年、初年度半年分の減価、
残存価額の下限5%。耐用年数はPVが法定17年、蓄電池はbattery_life_years。

2. 発電側課金(系統用蓄電池のみ)
75.13円/kW/月 × 定格放電出力 × 12ヶ月の固定年額。

3. 再エネ賦課金(系統用蓄電池のみ)
3.49円/kWh を充放電ロス量(充電量−放電量)に課税する近似。
系統から充電する以上こちらは需要家なので、この費用が発生する。
PV併設の蓄電池には構造的に生じない。

4. 託送料金のkW建て——これは入れなかった。理由は次節。

この3つを入れた結果:

IRR NPV
追加前 +1.50 % −2,688,577 円
追加後 −1.08 % −15,654,462 円
三菱総研の参考値 −1.50 % —

差は0.42ptまで縮んだ。

出典のある数字でも、合わなければ入れない

4つ目の託送料金kW建てだけは、パラメータを作った上で
デフォルト0円のままにしてある。

資料には503.80円/kW/月という値が載っている。
これをそのまま適用すると、20年で40,264円/kWhになる。

ところが同じ資料のチャートを実測すると、
託送料金(kW建て+kWh建ての合計)は
20年で3,900〜4,200円/kWhしかない。約10倍の開きがある。

チャートの読み取り精度を疑ったが、
同じ方法で読んだ他の項目(運転維持費・廃止措置費用)は
こちらの計算と0.2%以内で一致した。読み取りは信用できる。

単位の転記違いか、契約容量の捉え方の相違か、
現時点では特定できていない。

出典がある数字でも、その出典自身の別の記述と
10倍合わないなら入れない。

正しい値が判明するまでデフォルト0円とし、
その旨を警告に出している。

wheeling_fee_yen_per_kw_month=0円のため託送料金の基本料金
(kW建て)相当分を考慮していません。MRI引用値503.80円/kW/月を
そのまま適用すると20年40,264円/kWhとなりMRI自身のチャート実測値
(3,900〜4,200円/kWh、既知項目は<0.2%精度で一致)の約10倍に達し
不整合なため、正しい値が判明するまで意図的に未設定としています

「入れていない」だけでなく
「なぜ入れていないか」まで返すようにしている。
これが無いと、この試算は単に楽観的なだけのものに見えてしまう。

実測値

条件: 東京・1,000kWh/333kW(3時間率)・6万円/kWh・
JEPX 2020〜2023年度平均・容量市場8,348円/kW/年・託送0.88円/kWh

項目 値
正味CAPEX 60,000,000 円
年間充電量 392,814 kWh
年間放電量 354,515 kWh
充放電ロス 38,299 kWh
等価フルサイクル(定格基準/SOC幅基準) 354.5 / 443.1 回
サイクル強度比 → 実効劣化率 1.21 → 1.21 %/年
アービトラージ収益(LP理論値) 3,312,818 円/年
アービトラージ収益(実現率85%後) 2,815,895 円/年
容量市場収益 2,779,884 円/年
固定資産税(初年度) 798,000 円
発電側課金 300,219 円/年
再エネ賦課金(初年度) 133,665 円
Project IRR −1.08 %
NPV −15,654,462 円

実現率85%はPV併設から借りてきた値だ

もう1つ、直していない既知の問題を書いておく。

アービトラージ実現率のデフォルト85%は、
PV併設ケース向けに決めた値の転用だ。

PV併設の蓄電池価値は、約6割が出力制御回避
(系統指令なので物理的に予見しやすい)で、
純アービトラージは約4割しかない。

系統用蓄電池は100%がスポット価格の予測精度に依存する。
本来なら85%より低いはずだ。

とはいえ根拠のある数字を出せないので、
デフォルトは変えず、docstringと記録に
「この値の系統用への妥当性は未検証」と書いて残している。

理論値と補正後の値を両方返しているので、
利用者が自分の見立てで割り戻せる。

まとめ

  • 系統用蓄電池の試算ツールを、Gradio画面を作らず
    MCPツールとしてだけ公開した。
    既存Spaceの計算資産・60MBのDB・HFの稼働枠を
    まとめて節約できた
  • 収益源をJEPXスポット+容量市場に絞り、
    需給調整市場は「対象外」と機械可読な形で返している。
    デフォルトでは不成立と出るツールなので、
    守備範囲を返さないと誤った悲観的結論を招く
  • 完全予見LPは1日1.21〜1.76回充放電する。
    「1日1回」前提の劣化率をそのまま使うと収益を過大評価するので、
    実サイクル数でスケールするようにした
  • 等価フルサイクル数は定格基準とSOC幅基準の両方を返し、
    どちらを劣化補正に使ったかも書く
  • 公表試算との突合で費用項目の抜けを3つ埋め、
    IRRの差を0.42ptまで縮めた
  • 4つ目(託送料金kW建て)は出典の値が
    その出典自身のチャートと10倍合わないため、
    理由を書いた上でデフォルト0円に留めている
  • MCPエンドポイント:
    https://hachinai-pv-sim-fip.hf.space/gradio_api/mcp/

FIP転そのものの記事は別に書いている。
そちらでは、基準価格とプレミアムを取り違えていて
IRRが+7.11%→−3.03%変わったバグの話などを扱っている。

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?