恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
Python股票期权分析:从选股到定价预测的完整决策链
首页
资讯中心
/
Python股票期权分析:从选股到定价预测的完整决策链
Python股票期权分析:从选股到定价预测的完整决策链
发布时间:2026/10/14 7:43:22
简介面向股票期权分析场景的工具包适用于对量化选股、期权定价与预测性分析感兴趣的投资者、数据科学学习者和金融科技开发者。资源源自作者基于Excel原型构建的Web项目整合了数据分析、数据可视化、推荐引擎、决策支持与博弈论视角并计划引入机器学习模块。主要功能包括输入股票代码后基于历史波动率估算30天乃至一年价格区间结合Wyckoff方法给出多空倾向的算法判断同时提供类似推荐系统的选股参考与内置计算器帮助读者把股票和期权挑选从主观经验转化为可复现的数据流程。压缩包整体约325.82MB具体文件构成暂未公开明细可参考项目结构按功能模块查看。目前已有162人浏览/学习适合作为中小型金融预测项目的参考实现。1. 股票期权分析它不只是一个计算器而是一条“选股—定价—预测—建议”的完整决策链如果把“股票期权分析”拆开看最容易误解的部分是“分析”这两个字。你可能会认为它就是一个能算期权价格和希腊字母的计算器填上标的价、行权价、波动率就能出一串数字。但真正拆开这个Python资源后你会发现它的核心是一条完整的决策链先用行情数据筛选股票再做期权定价与隐含波动率反推接着用机器学习做方向性预测最后给出具体期权的挑选建议。它解决的是“该不该买、买哪一档、参数怎么设”的问题而不是“明天涨还是跌”的玄学问题。适合刚开始接触期权量化、手里有一份免费行情源、想少走弯路的从业者。2. 股票筛选层用pandas把行情数据变成可以下手的候选池期权分析的第一个环节永远是“标的选择”。如果你的候选池里根本没有基本面和技术面都看得过去的股票后面算出来的任何期权价格都只是数字游戏。这个资源里的股票筛选思路很简单拉取历史日线计算动量、波动率和成交量三个因子再把它们合成一个综合分按分数排序得到候选池。2.1 数据管道从行情源到DataFrame的清洗流程常见做法是用免费的行情接口拉日线数据。下面的函数把单只股票的历史行情变成一份干净的DataFrame下载、选列、改名、去空值一步到位import yfinance as yf import pandas as pd def load_history(ticker: str, start: str, end: str) - pd.DataFrame: # 这里填写你要分析的标的代码例如 STOCK_A、STOCK_B df yf.download(ticker, startstart, endend, progressFalse, auto_adjustTrue) df df[[Open, High, Low, Close, Volume]].copy() df.columns [c.lower() for c in df.columns] df df.dropna() return dfauto_adjustTrue是第一个关键参数。它会返回复权价发生分红除权时价格不会出现跳空假象。对于期权分析来说复权价能让技术指标保持连续但要注意研究期权行权价时如果对标的是未复权的盘面价买卖点会错位后面需要单独对齐。dropna()会直接丢掉停牌导致的空行对于日线级别的因子计算来说空值会把 rolling 窗口的统计量污染掉丢行比补值更干净。start和end建议至少留出半年的数据因为后面要计算60日波动率样本太短算出来的波动率没有参考意义。数据拉回来后我会先做一次单调性检查。指数必须是严格升序否则后续merge多个标的时候行与行之间会错位。这一步的代价很小但能省掉后面一大半的排查时间# 确认索引升序防止数据源返回乱序 assert df.index.is_monotonic_increasing, 日期索引不是升序请按时间排序2.2 选股因子怎么组合动量、波动率、成交量的取舍候选池用三个因子打分动量代表趋势方向波动率代表风险和期权定价成本成交量代表流动性。三者缺一不可只看动量容易追高只看波动率容易选到没人交易的冷门票。下面的函数对字典形式的行情数据批量打分import numpy as np def score_stocks(data_dict, mom_window20, vol_window60, volume_window20): records [] for ticker, df in data_dict.items(): df df.copy() # 20日动量衡量近期趋势强度 df[momentum] df[close] / df[close].shift(mom_window) - 1 # 60日年化波动率rolling标准差乘以根号252 df[volatility] df[close].pct_change().rolling(vol_window).std() * np.sqrt(252) # 20日平均成交量过滤掉流动性不足的标的 df[volume_ma] df[Volume].rolling(volume_window).mean() last df.iloc[-1] records.append({ ticker: ticker, momentum: last[momentum], volatility: last[volatility], volume_ma: last[volume_ma] }) score_df pd.DataFrame(records).dropna() # 动量越大分越高波动率越小分越高成交量越大分越高 score_df[mom_rank] score_df[momentum].rank(pctTrue) score_df[vol_rank] 1 - score_df[volatility].rank(pctTrue) score_df[volume_rank] score_df[volume_ma].rank(pctTrue) score_df[total_score] score_df[[mom_rank, vol_rank, volume_rank]].mean(axis1) return score_df.sort_values(total_score, ascendingFalse)这里三个基础窗口参数直接决定选股性格。mom_window20看的是一个月的趋势改成60会捕捉中期趋势但同时也会让反应变迟钝。vol_window60是年化波动率的标准计算窗口少于30天真值波动会非常大。volume_window20与动量窗口保持一致目的是用同一周期刻画趋势和资金的匹配度。三个因子按等权平均合成。如果你发现这个工具选出来的标的过于激进可以把vol_rank的权重调高到0.5让低波动因子成为主导。rank(pctTrue)的作用是把绝对值换成百分位排名这样不同股票的价格水平、成交量量级差异被抹平因子之间可比。2.3 可视化把候选池的“性价比”画出来评分结果不是最直观的。我会用散点图把候选池摊开横轴是动量、纵轴是波动率、点的大小代表成交量、颜色代表综合分。高动量低波动的左上角区域是期权买方最理想的标的区间import matplotlib.pyplot as plt fig, ax plt.subplots(figsize(10, 7)) sc ax.scatter( score_df[momentum], score_df[volatility], sscore_df[volume_ma] / score_df[volume_ma].max() * 500, cscore_df[total_score], cmapRdYlGn, alpha0.7 ) for _, row in score_df.head(10).iterrows(): ax.annotate(row[ticker], (row[momentum], row[volatility]), fontsize8) ax.set_xlabel(20日动量) ax.set_ylabel(60日年化波动率) plt.colorbar(sc, label综合分) plt.show()s参数把成交量映射到点大小最大成交量的点会变成500倍基准面积量级差异一眼可见。annotate只标注前10名避免图上全是标签根本看不清。颜色用RdYlGn主要是为了读图直觉绿代表高分红代表低分。alpha0.7让重叠点不至于完全盖住彼此。这张图在这个资源里承担的作用是选股“过滤器”看完图再进计算器环节而不是盲跑公式。注意可视化只是辅助判断真正下单之前要再把候选池里的每只股票翻到日线级别看一遍用因子筛会漏掉突发利空和财务数据这类行情接口看不出来的东西。3. 期权计算器层定价、隐含波动率和希腊字母的参数边界当候选股确定以后才轮到计算器部分。这个资源里的期权计算不是简单把盘面价拿过来用而是把定价拆成三层理论价格、隐含波动率、希腊字母。三层叠起来才能回答“这一档期权到底贵不贵”的问题。3.1 Black-Scholes定价的代码实现与参数陷阱Black-Scholes公式是整个期权计算器的基础适用场景是欧式期权。实际交易中很多期权带有美式行权特征但短期限、无分红标的下用BSM做近似定价误差很小。实现如下from scipy.stats import norm def bs_price(S, K, T, r, sigma, option_typecall): # 边界保护T和sigma必须为正 if T 0 or sigma 0: return None d1 (np.log(S / K) (r 0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T)) d2 d1 - sigma * np.sqrt(T) if option_type call: price S * norm.cdf(d1) - K * np.exp(-r * T) * norm.cdf(d2) else: price K * np.exp(-r * T) * norm.cdf(-d2) - S * norm.cdf(-d1) return price参数口径是这个计算器最容易出错的地方。T必须是年化期限如果从到期日算出还剩45天要写45 / 365直接传45进去会把时间价值放大到荒谬的程度。r用连续复利利率一般取无风险利率国内常用十年期国债收益率近似海外标的用对应期限的Libor或SOFR。sigma是小数形式35%的波动率写0.35不是35。return None这个保护逻辑值得注意。当你传入的T0时函数直接退出而不是抛异常这在批量计算期权链时很有用——有些行权价离到期日太近理论价已经没有意义让它在后续流程里被当作无效值过滤掉就好。3.2 隐含波动率反推二分法比牛顿法更适合新手理论价格算出来以后真正的分析才开始。盘面上你能看到的是期权成交价把成交价倒推进公式里求出的波动率才是隐含波动率IV。IV才是市场对这只标的未来波动的真实定价。反推IV常见做法是牛顿法和二分法我推荐二分法因为BSM公式在sigma上是单调函数二分法保证收敛不会像牛顿法那样在某些参数组合下震荡发散def implied_vol(market_price, S, K, T, r, tol1e-4, lo1e-3, hi5.0): # 用二分法在区间[0.1%, 500%]内反推波动率 for _ in range(100): mid (lo hi) / 2 est bs_price(S, K, T, r, mid) if est is None: return np.nan if est market_price: hi mid else: lo mid if hi - lo tol: break return (lo hi) / 2tol1e-4意味着波动率精度到0.01%这个精度对期权定价已经足够。lo1e-3到hi5.0覆盖了从0.1%到500%的波动率范围实际市场极少超出这个区间。如果返回值是np.nan说明市价落在理论价格范围之外常见原因是数据源里出现了异常成交价或者买卖价没有取中间值。遇到nan不要硬填充直接丢弃对应的期权行因为反推IV本身已经失真后面的希腊字母也没有参考意义。3.3 希腊字母是挑选期权的主要决策依据价格算完只是第一步。真正决定期权挑哪一档的是希腊字母结构。把希腊字母的计算一起写进资源里的计算器模块这是整个工具最实用的部分def greeks(S, K, T, r, sigma, option_typecall): d1 (np.log(S / K) (r 0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T)) d2 d1 - sigma * np.sqrt(T) delta norm.cdf(d1) if option_type call else norm.cdf(d1) - 1 gamma norm.pdf(d1) / (S * sigma * np.sqrt(T)) theta (-(S * norm.pdf(d1) * sigma) / (2 * np.sqrt(T)) - r * K * np.exp(-r * T) * norm.cdf(d2)) / 365 vega S * norm.pdf(d1) * np.sqrt(T) return {delta: delta, gamma: gamma, theta: theta, vega: vega}theta除以365是把年化时间衰减折算成每天损失这样四个希腊字母的单位风格统一方便横向比较。挑选期权时这张表是关键依据希腊字母含义数值区间挑选场景Delta期权价格对标的变动的敏感度0~1call-1~0put想线性跟涨选高Delta想对冲选负DeltaGammaDelta的变动速度平值期权附近最大博弈短期大幅波动选高GammaTheta时间衰减速度通常为负期权卖方靠Theta赚钱买方要避开高ThetaVega对IV变化的敏感度IV高时放大预期波动率放大时选高Vega同一个标的行权价不同、到期日不同希腊字母结构完全不同。离到期日远、平值附近的期权Gamma和Vega都很高深度实值的期权Delta接近1但Gamma很低涨跌几乎等同于直接持有股票。这个资源里给出的建议很简单如果你的观点是“未来两周可能有大行情”选平值附近高Gamma的档位如果你的观点是“方向明确但幅度温和”选Delta高一点的档位。别只看价格便宜就下手便宜往往来自低Delta和远到期它不是“划算”只是“慢”。4. 预测与推荐层机器学习特征、模型选型和建议逻辑怎么接这套工具里最有想象空间的部分在最后把机器学习接到期权选择上。它不是让你全自动交易而是把预测结果当成“建议”的上游信号再用赔率思维决定买哪一档。这里的机器学习不是黑匣子它的作用边界要提前划清楚预测的是标的未来5日收益的方向和概率而不是期权的精确价格。4.1 特征工程从历史行情到训练样本一份原始日线数据不能直接送进模型。需要把价格序列转成技术特征并构造未来的收益标签。下面的函数完成特征构建def make_features(df: pd.DataFrame, horizon: int 5) - pd.DataFrame: out df.copy() # 收益率特征1日、5日、20日短期趋势 out[ret_1] out[close].pct_change(1) out[ret_5] out[close].pct_change(5) out[ret_20] out[close].pct_change(20) # 波动率特征滚动标准差衡量近期风险 out[vol_10] out[ret_1].rolling(10).std() out[vol_60] out[ret_1].rolling(60).std() # 标签未来horizon天的实际收益率注意这是唯一能使用未来数据的地方 out[future_ret] out[close].shift(-horizon) / out[close] - 1 return out.dropna()shift(-horizon)把未来5天的收益挪到当前行这行数据就成了监督学习的标签。训练模型时特征只能用截止当天已有的数据这也是为什么特征列全部使用pct_change和rolling——它们天然向右看不会泄漏未来信息。dropna()会丢掉前60行和最后5行这是滚动窗和未来标签的代价样本会减少大约65个对日线级别的数据集来说可以接受。如果你想预测更长的周期比如未来10天把horizon改成10就行但样本量会进一步缩水训练集太小的时候模型容易出现高方差。4.2 模型选型为什么不要一上来就上深度学习金融时序数据量通常只够支撑浅层模型。这个资源里我建议用随机森林回归而不是堆LSTM或Transformer原因有三个数据量小几万行日线数据根本喂不饱深度模型随机森林自带特征重要性你能看到动量因子到底起了多大作用它的非线性能抓住“波动率高时不追涨”这类浅层模型学不到的条件关系。from sklearn.ensemble import RandomForestRegressor from sklearn.model_selection import TimeSeriesSplit X_cols [ret_1, ret_5, ret_20, vol_10, vol_60] tscv TimeSeriesSplit(n_splits4) model RandomForestRegressor( n_estimators200, max_depth5, min_samples_leaf20, random_state42, n_jobs-1 )TimeSeriesSplit(n_splits4)是按时间顺序切分的交叉验证而不是随机打乱的 KFold。金融数据一旦随机打乱训练集里就会出现测试期之后的数据模型评估结果会虚高这是金融机器学习和普通分类任务的最大区别。max_depth5和min_samples_leaf20是刻意的正则化设置防止模型记住噪声。这个资源里跑下来深度超过5层之后训练集误差继续下降验证集误差反而上升过拟合点就在那里。n_estimators200是速度和效果之间的折中500棵树精度的提升非常有限但训练时间会增加一倍。4.3 建议逻辑用game theory的思路做持仓取舍模型预测出来的是未来收益的期望值但“预测涨”不等于“该买期权”。这里要引入赔率思维也就是资源里提到的game theory视角。看涨一个标的时你面对的是一条期权链不同行权价、不同到期日对应不同的权利金和赔率。胜率再高如果权利金过贵期望收益可能仍然是负的。def pick_options(chain_df, win_rate, ev_threshold0.1, max_theta0.02): result chain_df.copy() # 胜率来自模型预测赔率来自行权价空间和权利金的比值 result[win_rate] win_rate result[ev] result[win_rate] * result[payoff] - (1 - result[win_rate]) * result[cost] # 过滤掉期望收益不足的档位和时间衰减过快的档位 result result[result[ev] ev_threshold] result result[result[theta_abs] max_theta] return result.sort_values(ev, ascendingFalse)这个过滤器的逻辑是典型的赔率决策payoff是行权价到目标价的空间cost是权利金。一句话总结就是预测只告诉你方向赔率才告诉你值不值得下注。ev_threshold0.1表示期望收益至少要覆盖权利金的10%如果你的资金量小可以把这个阈值提高到0.3过滤掉更多低性价比的档位。max_theta0.02限制每天时间损耗不超过权利金的2%这是为了防止你买到离到期太近、时间价值正在快速蒸发的期权。过滤完成后按EV排序排在最前面的档位就是这轮建议里“性价比”最高的选择。5. 避坑排查这个工具最容易翻车的五个点把避坑章节放在这么靠后不是因为它不重要而是只有你真动手跑起来才会遇到。下面五个点我在与此类似的模拟项目X上几乎全踩过每条按现象、原因、解决的顺序写你复现时可以直接对照。5.1 数据层的两个坑现象回测收益曲线又稳又高换成实盘完全对不上。常见现象是训练集里模型得分极高但上盘后买入信号频繁失效。原因特征里混进了未来信息这是金融数据分析最典型的前视偏差。最常见的来源是构建样本时把整段数据先归一化再切训练集测试集或者rolling函数的方向没有确认。解决每一行的特征只能用这一行之前的数据计算标签才能用未来。训练和回测时都坚持滚动窗口不用全局归一化。一个低成本的自检方式是把future_ret整体再shift错一位如果模型评分没有剧烈恶化说明特征和标签之间可能已经出现了泄漏。现象多标的跑预测时行数对不上推荐结果里出现空值。原因不同股票的交易日历并不完全一致遇到停牌、临时休市各只股票的DataFrame长度会不同直接concat就会出现索引错位。解决在load_history之后统一按主交易日的日期序列合并用inner join再丢掉缺失行。跑之前加一行assert df.index.is_monotonic_increasing这个断言能挡住大部分垃圾数据。5.2 计算与决策层的三个坑现象理论期权价格和盘面价格差得离谱。原因把历史波动率直接当作公式里的sigma使用。市场定价参照的是隐含波动率IV和HV经常差出10个百分点以上尤其财报前后差异更大。解决计算器里加入二分法反推IV有盘面成交价就先反推sigma再用这个sigma算希腊字母。不要拿历史波动率直接比较期权贵贱那样比较出来的结论基本是错的。现象模型预测方向很准建议却总是买在局部高点。原因动量因子的权重太高模型本质上成了强趋势跟踪器行情反转时这类模型胜率快速下降。另一个隐藏原因是标签用了对数收益而回测统计按算术收益口径不一致导致模型的学习目标和回测评价标准脱节。解决把超过5%的动量值截断给极端行情降权标签统一使用pct_change()的返回值训练和回测用同一套收益口径。现象一跑全市场选股内存直接爆炸。原因对全市场做高频rolling计算时数据量指数级增长而且反复调用download会把内存堆满。解决候选池控制在50个以内先下载到本地csv缓存后面运行直接读缓存避免每次重启都重新请求。特征计算尽量向量化不要在循环里逐行做运算pandas的滚动窗口在大多数情况下已经够快。6. 从“能跑”到“可信”回测框架与波动率冲击测试一个工具能跑和可信之间差一个回测框架。我拿到这个资源后做的事很简单不看它的推荐结果先自己把流程完整走一遍。回测的目的不是证明它有多准而是检验“预测是否真的领先于行情”。下面的框架按时间顺序滚动训练每次都只用截止当天之前的数据def backtest(history, model, feature_cols, horizon5, train_size300): preds, actuals [], [] for i in range(train_size, len(history) - horizon): # 只用前train_size天拟合严格避免前视 train history.iloc[i - train_size:i] test history.iloc[i:i 1] model.fit(train[feature_cols], train[future_ret]) pred model.predict(test[feature_cols])[0] actual test[future_ret].values[0] preds.append(pred) actuals.append(actual) # 返回方向命中率0.52以上才说明有参考价值 hit_rate float(np.mean(np.sign(preds) np.sign(actuals))) return hit_rate, preds, actualstrain_size300意味着用大约一年半的日线数据训练每次向前滚动一天。这个框架的代价是训练次数等于测试样本数跑起来有点慢但金融时序回测必须这么做任何一次性划分训练集测试集的做法都会引入前视风险。回测之外还要做波动率冲击测试。期权的盈亏对IV变化异常敏感如果IV在持仓期内上升20%你的期权价格可能会大幅偏离买入时的理论价。简单的做法是把IV按上下浮动20%分别定价看持仓风险是否在承受范围内def vol_shock(S, K, T, r, iv_base, shock_pct0.2): # 模拟IV波动对期权价格的影响 for label, p in [(-20%, 1 - shock_pct), (base, 1), (20%, 1 shock_pct)]: price bs_price(S, K, T, r, iv_base * p) print(f{label}: {price:.2f})这个函数把波动率从数字变成了资金管理约束。如果IV上移20%后价格变化幅度超过你的单笔亏损上限即使模型预测看涨也不该上大仓位。从那以后我每次跑完回测都会强制加一道标签方向自检把future_ret的shift正负号核对一遍再去看建议结果。这个习惯帮我过滤掉了大量“看起来正确”的信号。希望帮到你。本文还有配套的精品资源点击获取