0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

PythonでMax Painを計算する:満期の混在と欠損OIを先に止める

0
Posted at

同じ満期のオプション建玉から、満期時の本源的価値の合計が最小になる候補価格を求める、小さな検証用コードです。AIの支援で下書きし、掲載コードはローカルで実行検証しています。本番アプリの実装そのものではありません。

価格を S、行使価格を K とすると、コール側は max(S−K, 0)、プット側は max(K−S, 0)。それぞれに建玉を掛けて合計し、候補価格を比較します。

ここでは同一銘柄・同一満期・同一受渡条件の標準契約を入力前提とし、共通の正の契約倍率は最小点を変えないため省略します。調整契約や異なる倍率を混ぜる用途には使いません。満期・受渡条件を絞ったつもりでも、重複行があると二重計上になります。欠損OIをゼロに変換してから計算する処理も避けます。

from decimal import Decimal

def max_pain(rows):
    """One expiry, standard unadjusted contracts, identical multiplier.
    Synthetic normalized input only; unknown OI is an error.
    """
    if not rows:
        raise ValueError("empty chain")
    normalized = []
    seen = set()
    series = set()
    for r in rows:
        k = Decimal(str(r["strike"]))
        if not k.is_finite() or k <= 0:
            raise ValueError("bad strike")
        if r["right"] not in ("C", "P"):
            raise ValueError("bad right")
        oi = r["oi"]
        if type(oi) is not int or oi < 0:
            raise ValueError("unknown or bad OI")
        group = (r["symbol"], r["expiry"], r["deliverable"])
        series.add(group)
        contract = group + (r["right"], k)
        if contract in seen:
            raise ValueError("duplicate contract")
        seen.add(contract)
        normalized.append((k, r["right"], oi))
    if len(series) != 1:
        raise ValueError("mixed chains")
    if sum(oi for _, _, oi in normalized) == 0:
        raise ValueError("no open interest")
    losses = {}
    for s in sorted({k for k, _, _ in normalized}):
        losses[s] = sum(
            (max(s-k, Decimal(0)) if right == "C"
             else max(k-s, Decimal(0))) * oi
            for k, right, oi in normalized
        )
    minimum = min(losses.values())
    return [s for s, loss in losses.items() if loss == minimum]

# All values below are invented fixtures, not market observations.
base = dict(symbol="TEST", expiry="2099-01-01", deliverable="standard")
rows = [dict(base, strike="100", right="C", oi=1),
        dict(base, strike="100", right="P", oi=1)]
assert max_pain(rows) == [Decimal("100")]
print(max_pain(rows))

候補は入力に含まれる行使価格です。最小値が同じ候補はすべて返します。行使価格の間で合計が平らになる場合は、その区間全体が最小になり得ます。このコードは区間推定をせず、同点の候補を返すだけです。

計算量は候補数×契約数。まず正しい入力と同点の扱いを確認するための単純な実装です。高速化するなら区分線形関数の傾きを更新する方法もありますが、ここでは検証しやすさを優先しました。

これは建玉を固定した集計であり、価格がそこへ向かう確率や利益を計算するモデルではありません。建玉から参加者の保有方向やヘッジも確定できません。

制作している米国株データアプリ:SIGNUM HQ
https://www.signumhq.com/app?from=qiita_1017

参考

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?