"""M20 教学图：到期盈亏，以及只减仓的波动率缩放。

权利金和到期价是给定的。缩放调用仓库函数，上限是 1。
"""

from __future__ import annotations

from pathlib import Path

import matplotlib.pyplot as plt

from quant.risk.volatility import target_volatility_scale

plt.rcParams["font.sans-serif"] = ["Microsoft YaHei", "SimHei", "DejaVu Sans"]
plt.rcParams["axes.unicode_minus"] = False

OUT = Path(__file__).resolve().parent / "payoff_and_scale.png"


def covered_call(spot: float, strike: float, premium: float, start: float) -> float:
    # 卖出的看涨由持有的股票覆盖。到期高于行权价的部分不再属于股票持有人。
    return spot - max(spot - strike, 0.0) + premium - start


def cash_put(spot: float, strike: float, premium: float) -> float:
    # 现金要覆盖行权价。权利金不是保证金。
    return -max(strike - spot, 0.0) + premium


def protective_put(spot: float, strike: float, premium: float, start: float) -> float:
    return (spot - start) + max(strike - spot, 0.0) - premium


def main() -> None:
    start = 100.0
    for spot in (90.0, 100.0, 120.0):
        print(
            "cc", spot,
            f"{covered_call(spot, 105.0, 3.0, start):.2f}",
            "stock", f"{spot - start:.2f}",
        )
    print("cc_breakeven", f"{start - 3.0:.2f}")
    print("cc_cap", f"{105.0 - start + 3.0:.2f}")
    for spot in (80.0, 95.0, 110.0):
        print("csp", spot, f"{cash_put(spot, 95.0, 2.0):.2f}")
    print("premium_over_strike", f"{2.0 / 95.0:.6f}")
    for spot in (70.0, 100.0, 130.0):
        print("put", spot, f"{protective_put(spot, 90.0, 2.0, start):.2f}")
    print("put_floor", f"{90.0 - start - 2.0:.2f}")

    front, second = 16.0, 20.0
    print("contango_roll", f"{(second - front) / front:.6f}")
    front_b, second_b = 28.0, 22.0
    print("backward_roll", f"{(second_b - front_b) / front_b:.6f}")

    high = target_volatility_scale(0.20, 0.10, max_scale=1.0)
    low = target_volatility_scale(0.08, 0.10, max_scale=1.0)
    print("scale_high", f"{high:.4f}")
    print("scale_low", f"{low:.4f}")
    print("wanted_low", f"{0.10 / 0.08:.4f}")

    fig, axes = plt.subplots(1, 2, figsize=(11.2, 4.8))
    ax = axes[0]
    spots = [90.0, 100.0, 120.0]
    cc = [covered_call(spot, 105.0, 3.0, start) for spot in spots]
    stock = [spot - start for spot in spots]
    xpos = range(len(spots))
    ax.bar([x - 0.16 for x in xpos], stock, width=0.32, color="#1f4e79", label="只持有股票")
    ax.bar([x + 0.16 for x in xpos], cc, width=0.32, color="#c4a35a", label="备兑看涨")
    ax.set_xticks(list(xpos), [str(int(spot)) for spot in spots])
    ax.axhline(0.0, color="#333333", linewidth=0.8)
    ax.set_xlabel("到期价")
    ax.set_ylabel("相对 100 的盈亏")
    ax.set_title("行权价 105，权利金 3")
    ax.legend(frameon=False)
    ax = axes[1]
    ax.bar(["波动 20%", "波动 8%"], [high, low], color=["#a33b3b", "#5b8e7d"])
    ax.axhline(1.0, color="#1f4e79", linestyle="--", label="上限 1")
    ax.set_ylabel("仓位倍数")
    ax.set_title("目标波动 10%，函数不放大")
    ax.legend(frameon=False)
    fig.suptitle("M20：到期盈亏与只减仓的缩放；权利金不是历史报价")
    fig.tight_layout()
    fig.savefig(OUT, dpi=140)
    print("wrote", OUT)


if __name__ == "__main__":
    main()
