小隐的博客

人生在世,笑饮一生
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

商品期权的保证金计算

Posted on 2026-01-10 21:18  隐客  阅读(5)  评论(0)    收藏  举报
info_margin_ratio=0.19
price_und=18888 #商品的结算价
mult=15
und_margin = price_und * mult * info_margin_ratio
info_option_type="call"
info_strike_price=20000
price_opt=805.5

#57573
if info_option_type == "call":
    otm_amount = max(info_strike_price - price_und, 0) * mult
else:
    otm_amount = max(price_und - info_strike_price, 0) * mult

method_1 = (price_opt * mult) + und_margin - (0.5 * otm_amount)
method_2 = (price_opt * mult) + (0.5 * und_margin)

margin=max(method_1, method_2)
print(margin)

大约意思是:

期权保证金=权利金+Max(标的期货合约交易保证金-1/2期权虚值额,1/2标的期货合约交易保证金)

虚值额计算公式:

看涨期权虚值额= Max(期权合约执行价格-标的期货合约昨日结算价,0)*标的期货合约交易单位
看跌期权虚值额= Max(标的期货合约昨日结算价-期权合约执行价格,0)*标的期货合约交易单位

 

股指和ETF的见相关文件;

from enum import Enum
from typing import Optional, Literal
from dataclasses import dataclass
import json
from pathlib import Path

from pythongo import infini


class ProductType(Enum):
    """品种类型枚举"""

    FUTURE = "1"  # 期货
    COMMODITY_OPT = "2"  # 商品期权
    INDEX_OPT = "h"  # 股指期权
    ETF_OPT = "8"  # ETF期权


class OptionType(Enum):
    """期权类型枚举"""

    NONE = "0"  # 非期权
    CALL = "1"  # 看涨期权
    PUT = "2"  # 看跌期权


@dataclass
class InstrumentInfo:
    """合约信息数据类"""

    product_id: str  # 品种代码
    product_type: str  # 品种类型
    multiple: int  # 合约乘数
    margin_ratio: Optional[float] = None  # 保证金率
    strike_price: Optional[float] = None  # 行权价
    option_type: Optional[OptionType] = None  # 期权类型
    underlying_symbol: Optional[str] = None  # 标的代码
    underlying_exchange: Optional[str] = None  # 标的交易所


@dataclass
class PrePriceData:
    """昨价数据类"""

    pre_settlement: float = 0.0  # 昨结算价
    underlying_pre_settlement: Optional[float] = None  # 标的昨结算价
    underlying_pre_close: Optional[float] = None  # 标的昨收盘价


class MarginRateConfig:
    """保证金率管理器"""

    def __init__(self, filepath: str = "", default_ratio: Optional[float] = None):
        """默认保证金率表"""
        self.default_ratio = default_ratio  # 设置默认保证金率
        self._ratios: dict[str, float] = {}  # 初始化空字典用于存储保证金率

        self._config_path = Path(filepath) if filepath else None
        if self._config_path:
            self.load_from_json(self._config_path)

    def load_from_json(self, filepath: Path):
        """尝试从文件读取配置覆盖默认值"""
        if not filepath.exists():
            infini.write_log(
                f"[margin_log] 文件 {filepath} 不存在,将使用默认保证金率进行计算。"
            )
            return

        try:
            with open(filepath, "r", encoding="utf-8") as f:
                data = json.load(f)
                self._ratios.update(data)
                infini.write_log(f"[margin_log] 保证金配置文件 {filepath} 读取成功。")
        except Exception as e:
            infini.write_log(f"[margin_log] 读取保证金配置文件失败: {e}")

    def save(self, filepath: str = ""):
        """把当前配置存入文件"""
        target_path = Path(filepath) if filepath else self._config_path

        if not target_path:
            infini.write_log("[margin_log] 未指定保存路径")
            return

        try:
            with open(target_path, "w", encoding="utf-8") as f:
                json.dump(self._ratios, f, indent=4, ensure_ascii=False)
            infini.write_log(f"[margin_log] 配置已保存至 {target_path}")
        except Exception as e:
            infini.write_log(f"[margin_log] 保存保证金配置文件失败: {e}")

    def get_ratio(self, exchange: str, instrument_id: str) -> Optional[float]:
        """
        核心查找逻辑:先查合约,再查品种,最后默认
        """

        # 优先检索合约代码
        if instrument_id in self._ratios:
            return self._ratios[instrument_id]

        # 其次检索品种代码,没检索到返回默认保证金率
        product_id = infini.get_instrument(exchange, instrument_id)["ProductID"]
        if product_id in self._ratios:
            return self._ratios[product_id]
        else:
            return self.default_ratio

    def set_ratio(self, target_id: str, new_ratio: float):
        """更新单个保证金率配置"""
        self._ratios[target_id] = new_ratio

    def update_ratios(self, updates: dict[str, float]):
        """批量更新保证金率配置"""
        self._ratios.update(updates)


class MarginCalculator:
    """期权保证金计算器"""

    # 股指期权标的映射表
    INDEX_MAPPING = {
        "HO": "000016",  # 上证50
        "IO": "000300",  # 沪深300
        "MO": "000852",  # 中证1000
    }

    def __init__(self, config_path: str = "", default_ratio: Optional[float] = None):
        self.margin_ratios = MarginRateConfig(config_path, default_ratio)

    def _instrument_data(self, exchange: str, instrument_id: str) -> InstrumentInfo:
        """提取计算期权保证金所需的合约信息"""
        raw_data = infini.get_instrument(exchange, instrument_id)

        product_id = raw_data["ProductID"]
        product_type = raw_data["ProductClass"]
        margin_ratio = self.margin_ratios.get_ratio(
            exchange=exchange, instrument_id=instrument_id
        )

        base_info = InstrumentInfo(
            product_id=product_id,
            product_type=product_type,
            multiple=raw_data["VolumeMultiple"],
            margin_ratio=margin_ratio,
        )

        # 检查是否为期权
        options_type_val = raw_data["OptionsType"]
        if options_type_val == OptionType.NONE.value:
            return base_info

        # 补全期权信息
        option_enum = OptionType(options_type_val)

        # 处理标的代码逻辑
        raw_underlying = raw_data["UnderlyingInstrID"]
        is_index_opt = product_type == ProductType.INDEX_OPT.value

        base_info.strike_price = raw_data["StrikePrice"]
        base_info.option_type = option_enum
        base_info.underlying_symbol = (
            self.INDEX_MAPPING.get(product_id, "") if is_index_opt else raw_underlying
        )
        base_info.underlying_exchange = "SSE" if is_index_opt else exchange

        return base_info

    def _get_calc_option_prices(
        self,
        product_type: Literal[
            ProductType.COMMODITY_OPT,
            ProductType.INDEX_OPT,
            ProductType.ETF_OPT,
        ],
        pre_data: PrePriceData,
        is_history_position: bool = False,
        option_trade_price: Optional[float] = None,
    ):
        """根据今仓/昨仓取计算保证金的价格"""

        # 确定标的价格
        calc_und_price = 0.0

        if product_type == ProductType.COMMODITY_OPT:
            if pre_data.underlying_pre_settlement is None:
                raise ValueError(
                    "计算商品期权保证金需要提供 underlying_pre_settlement (标的昨结)"
                )
            calc_und_price = pre_data.underlying_pre_settlement
        elif product_type in [ProductType.INDEX_OPT, ProductType.ETF_OPT]:
            if pre_data.underlying_pre_close is None:
                raise ValueError(
                    "计算股指/ETF期权保证金需要提供 underlying_pre_close (标的昨收)"
                )
            calc_und_price = pre_data.underlying_pre_close

        # 确定期权价格
        calc_opt_price = 0.0

        # 判断是否需要使用昨结价
        use_pre_settlement = is_history_position or product_type == ProductType.ETF_OPT

        if use_pre_settlement:
            if pre_data.pre_settlement is None:
                msg = (
                    "计算【昨仓】"
                    if is_history_position
                    else f"计算【{product_type}】今仓"
                )
                raise ValueError(f"{msg}需提供 pre_settlement (期权昨结)")
            calc_opt_price = pre_data.pre_settlement
        else:
            # 商品/股指今仓:使用最新成交价/报单价
            if option_trade_price is None:
                raise ValueError(
                    f"计算【{product_type}】今仓需提供 option_trade_price(期权最新价/报单价)"
                )
            calc_opt_price = option_trade_price

        return calc_opt_price, calc_und_price

    def calc_commodity_option_margin(
        self,
        exchange: str,
        instrument_id: str,
        pre_data: PrePriceData,
        is_history_position: bool = False,
        option_trade_price: float = None,
    ) -> Optional[None]:
        """商品期权保证金计算"""

        price_opt, price_und = self._get_calc_option_prices(
            product_type=ProductType.COMMODITY_OPT,
            pre_data=pre_data,
            is_history_position=is_history_position,
            option_trade_price=option_trade_price,
        )

        info = self._instrument_data(exchange, instrument_id)
        mult = info.multiple
        if info.margin_ratio is None:
            infini.write_log(
                f"[margin_log] 未查到 {instrument_id} 的标的合约保证金率,无法计算商品期权保证金,请补充!"
            )
            return None
        und_margin = price_und * mult * info.margin_ratio

        if info.option_type == OptionType.CALL:
            otm_amount = max(info.strike_price - price_und, 0) * mult
        else:
            otm_amount = max(price_und - info.strike_price, 0) * mult

        method_1 = (price_opt * mult) + und_margin - (0.5 * otm_amount)
        method_2 = (price_opt * mult) + (0.5 * und_margin)

        return max(method_1, method_2)

    def calc_index_option_margin(
        self,
        exchange: str,
        instrument_id: str,
        is_history_position: bool,
        pre_data: PrePriceData,
        option_trade_price: float = None,
        margin_adj_coeff: float = 0.12,
        min_assurance_coeff: float = 0.5,
    ) -> float:
        """股指期权保证金计算"""

        price_opt, price_und = self._get_calc_option_prices(
            product_type=ProductType.INDEX_OPT,
            pre_data=pre_data,
            is_history_position=is_history_position,
            option_trade_price=option_trade_price,
        )

        info = self._instrument_data(exchange, instrument_id)
        mult = info.multiple
        und_exposure = price_und * mult * margin_adj_coeff

        if info.option_type == OptionType.CALL:
            otm_amount = max((info.strike_price - price_und) * mult, 0)
            risk_component = max(
                und_exposure - otm_amount,
                min_assurance_coeff * und_exposure,
            )
        else:
            otm_amount = max((price_und - info.strike_price) * mult, 0)
            min_assurance = (
                min_assurance_coeff * info.strike_price * mult * margin_adj_coeff
            )
            risk_component = max(und_exposure - otm_amount, min_assurance)

        return (price_opt * mult) + risk_component

    def calc_etf_option_margin(
        self,
        exchange: str,
        instrument_id: str,
        is_history_position: bool,
        pre_data: PrePriceData,
        option_trade_price: float = None,
        ratio_a: float = 0.12,
        ratio_b: float = 0.07,
    ) -> float:
        """ETF期权保证金计算"""

        price_opt, price_und = self._get_calc_option_prices(
            product_type=ProductType.ETF_OPT,
            pre_data=pre_data,
            is_history_position=is_history_position,
            option_trade_price=option_trade_price,
        )

        info = self._instrument_data(exchange, instrument_id)

        if info.option_type == OptionType.CALL:
            otm_per_unit = max(info.strike_price - price_und, 0)
            risk_part = max(ratio_a * price_und - otm_per_unit, ratio_b * price_und)
            margin = (price_opt + risk_part) * info.multiple
        else:
            otm_per_unit = max(price_und - info.strike_price, 0)
            risk_part = max(
                ratio_a * price_und - otm_per_unit, ratio_b * info.strike_price
            )
            margin = min(price_opt + risk_part, info.strike_price) * info.multiple

        return margin

    def calculate_order_margin(
        self,
        exchange: str,
        instrument_id: str,
        price: float,
        volume: int,
        order_direction: str,
        pre_data: PrePriceData,
    ) -> float:
        """根据报单参数预算保证金或资金占用"""
        info = self._instrument_data(exchange, instrument_id)

        # 期货
        if info.product_type == ProductType.FUTURE.value:
            if info.margin_ratio is None:
                infini.write_log(f"[margin_log] 未查到期货合约保证金率,无法计算保证金")
                return None
            return price * volume * info.multiple * info.margin_ratio

        # 期权买方,返回开仓权利金
        if order_direction == "buy":
            return price * volume * info.multiple

        # 期权卖方 - 义务仓保证金
        if info.product_type == ProductType.COMMODITY_OPT.value:
            if info.margin_ratio is None:
                infini.write_log(
                    f"[margin_log] 未查到商品期权 {instrument_id} 的标的合约保证金率,无法计算保证金"
                )
                return None
            margin_per_lot = self.calc_commodity_option_margin(
                exchange=exchange,
                instrument_id=instrument_id,
                pre_data=pre_data,
                is_history_position=False,
                option_trade_price=price,
            )
        elif info.product_type == ProductType.INDEX_OPT.value:
            margin_per_lot = self.calc_index_option_margin(
                exchange=exchange,
                instrument_id=instrument_id,
                is_history_position=False,
                pre_data=pre_data,
                option_trade_price=price,
            )
        elif info.product_type == ProductType.ETF_OPT.value:
            margin_per_lot = self.calc_etf_option_margin(
                exchange=exchange,
                instrument_id=instrument_id,
                is_history_position=False,
                pre_data=pre_data,
                option_trade_price=price,
            )
        else:
            # 防止未知的品种类型
            return None

        return margin_per_lot * volume
View Code

 

QQ交流