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
浙公网安备 33010602011771号