この記事はAIを用いて作成した独立した検証例です。製品への導入実績を示すものではありません。価格を計算する前に、欠損・非有限値・交差気配を区別する入力検証を扱います。
中間値の式そのものが正しくても、ask が bid より低い、片側が欠ける、NaN が入るといった入力では比較値として扱えません。ここでは価格を文字列で受け、Decimalに変換します。入力を勝手に0へ補うことはしません。
from decimal import Decimal, InvalidOperation
def midpoint(bid, ask):
if bid is None or ask is None:
return None, "missing"
try:
b, a = Decimal(bid), Decimal(ask)
except (InvalidOperation, TypeError, ValueError):
return None, "invalid"
if not b.is_finite() or not a.is_finite():
return None, "nonfinite"
if b < 0 or a < b:
return None, "invalid_quote"
return (a + b) / 2, "ok"
# 架空の入力。市場の実測値ではありません。
assert midpoint("1.00", "1.20") == (Decimal("1.10"), "ok")
assert midpoint(None, "1.20")[1] == "missing"
assert midpoint("NaN", "1.20")[1] == "nonfinite"
assert midpoint("1.20", "1.00")[1] == "invalid_quote"
assert midpoint("bad", "1.20")[1] == "invalid"
この例は気配値の新鮮さ、注文数量、取引所の状態まで検証しません。両側が0の気配も計算上は通るため、実運用では商品と提供元に応じた利用可否を別に定める必要があります。中間値は約定可能価格の保証でもありません。価格の検証と、観測時刻・同一契約の検証は独立して残します。
参考: https://docs.python.org/3/library/decimal.html
実際のアプリ画面での扱い
SIGNUM HQ(米国株のオプション・決算を扱う個人開発アプリ)の公開ページ「決算検証レシート」では、気配値や反応値をそのまま比べず、オプションセッション・満期・保存時刻(UTC)・「事前時点を確認できず」といった区分を行ごとに表示しています。下の画像は2026-10-19週の画面で、上の例のように「値の有無」と「観測時点」を別々に扱う必要がある理由を示しています。

アプリの公開ページ: https://www.signumhq.com/ja/receipts/earnings?from=qiita_1026
アプリ本体: https://www.signumhq.com/app?from=qiita_1026
※ この記事はAIの支援を受けて作成しました。投資助言ではなく、特定の銘柄や売買の推奨を意図するものではありません。