可以免费打广告的网站,营销是什么意思,怎么收录网站,灵山县建设局网站Python实战#xff1a;用牛顿插值法预测股票走势#xff08;附完整代码#xff09; 最近和几个做量化研究的朋友聊天#xff0c;大家不约而同地提到了一个现象#xff1a;很多刚入门的朋友一提到预测#xff0c;就直奔机器学习、深度学习这些复杂模型#xff0c;结果往往…Python实战用牛顿插值法预测股票走势附完整代码最近和几个做量化研究的朋友聊天大家不约而同地提到了一个现象很多刚入门的朋友一提到预测就直奔机器学习、深度学习这些复杂模型结果往往被数据清洗、特征工程、模型调参搞得焦头烂额反而忽略了数学工具箱里一些经典而优雅的方法。这让我想起了牛顿插值法——一个在数值分析课本里静静躺着的工具其实在金融时间序列的初步探索和短期趋势刻画上有着意想不到的简洁与实用价值。今天我们就抛开那些复杂的理论推导直接上手看看如何用Python实现牛顿插值法并把它应用在股票价格这类典型的等距时间序列数据上。我们的目标不是构建一个“圣杯”般的预测系统而是掌握一种快速、直观的数据拟合与外推技术用于辅助判断、验证想法或者作为更复杂模型的前置分析步骤。无论你是刚开始接触量化分析的Python开发者还是对金融数据分析感兴趣的数据科学爱好者这篇文章都将带你走通从原理理解、代码实现到结果可视化的完整流程。1. 为什么选择牛顿插值法处理股价数据在讨论代码之前我们得先搞清楚面对每日收盘价这样的数据牛顿插值法能做什么不能做什么。股票价格序列本质上是时间序列其数据点通常是等距的例如每个交易日。这正是牛顿前向差分公式大显身手的前提条件。牛顿插值法的核心优势在于其“增量计算”的便利性。想象一下你根据过去5天的数据拟合了一个多项式明天收盘后新增了一个数据点。如果使用其他方法你可能需要重新计算整个模型。但牛顿插值法只需要在已有的差分表上增加一行一列计算新的高阶差分即可这非常符合金融数据逐日更新的特性。然而我们必须清醒地认识到它的局限性。多项式插值尤其是高阶插值存在著名的“龙格现象”Runges phenomenon在数据区间边缘多项式可能会产生剧烈的振荡导致预测结果完全失真。因此用高阶多项式去严格拟合股价并做长期外推是极其危险的。我们的策略是利用低阶多项式例如3次或4次来捕捉近期数据的局部趋势进行非常短期的、谨慎的外推更多的是将其作为一种平滑和趋势观察的工具。为了更清晰地对比其适用场景我们可以参考下表特性牛顿插值法在股价分析中的应用注意事项与局限数据要求完美契合等距时间序列日/周/月线非等距数据需使用更一般的差商形式计算效率O(n²)复杂度计算速度快适合在线更新数据点过多n20时高阶差分可能数值不稳定趋势刻画低阶多项式能有效平滑噪声显示短期趋势高阶多项式必然导致过拟合和边缘振荡预测能力仅适用于极短期的趋势外推如1-2个时间点绝对不可用于中长期预测外推风险极高主要用途数据平滑、缺失值插补、趋势可视化、模型基准对比是分析工具而非独立的交易信号生成器理解了这些我们就能摆正对它的期望。接下来我们进入实战环节从构建最关键的差分表开始。2. 构建差分表从股价序列到多项式系数一切的核心在于差分表。它以一种系统化的方式将原始数据点的信息逐步转化为多项式各项的系数。对于股价序列[P0, P1, P2, ..., Pn]其中P0代表最早的价格差分表的构建是一个递推过程。一阶差分衡量相邻日期的价格变化Δ¹Pi P(i1) - P(i)。 二阶差分衡量一阶差分的变化即价格加速度Δ²Pi Δ¹P(i1) - Δ¹P(i)。 以此类推k阶差分揭示了数据在k阶变化率上的信息。下面是一个用Python函数构建差分表的例子。我们刻意避免了使用复杂的矩阵运算库以便你能看清每一步的逻辑def build_forward_difference_table(price_sequence): 构建前向差分表。 参数: price_sequence (list or np.array): 等距时间点上的价格序列。 返回: np.ndarray: 二维数组形式的差分表左上角元素为f[0]。 n len(price_sequence) # 初始化一个n x n的矩阵用零填充 diff_table np.zeros((n, n)) # 第一列是原始价格数据 diff_table[:, 0] price_sequence # 递推计算各阶差分 for j in range(1, n): # j代表差分阶数 for i in range(n - j): # i代表当前阶差分计算的起始行 diff_table[i, j] diff_table[i1, j-1] - diff_table[i, j-1] return diff_table注意这个函数返回的diff_table[0, :]就是牛顿前向差分公式中需要的各阶差分起始值Δ^k f0。例如diff_table[0, 0]是f0起始价格diff_table[0, 1]是Δ¹f0依此类推。让我们用一小段真实的股价数据为了演示我们截取某股票6个交易日的收盘价来演示一下import numpy as np # 示例6个交易日的收盘价单位元 closing_prices np.array([100.5, 102.3, 101.8, 105.2, 107.0, 106.5]) diff_table build_forward_difference_table(closing_prices) print(构建的前向差分表) print(行(i) | 价格 f(i) | 一阶差分 Δ¹ | 二阶差分 Δ² | 三阶差分 Δ³ | ...) print(- * 60) for i in range(len(closing_prices)): row_str f{i:3d} | {diff_table[i,0]:7.2f} for j in range(1, len(closing_prices)-i): row_str f| {diff_table[i, j]:7.2f} print(row_str)运行这段代码你会看到一个清晰的差分表。观察高阶差分比如三阶、四阶的值如果它们已经接近于零或者在零附近随机波动通常意味着用一个较低阶的多项式比如二阶或三阶来拟合数据可能是合适的。这是决定最终插值多项式阶数的一个实用技巧。3. 实现牛顿前向插值函数有了差分表我们就可以构造牛顿前向插值多项式了。公式的核心是P(t) f0 (t choose 1)*Δ¹f0 (t choose 2)*Δ²f0 ... (t choose k)*Δ^k f0其中t (x - x0) / hh是时间间隔对于日线数据h1天(t choose k)是广义二项式系数t(t-1)...(t-k1)/k!。我们的目标是写一个函数它接受一个时间点x比如你想预测明天x6返回插值多项式给出的预测价格P(x)。def newton_forward_interpolate(x_target, x_start, step, diff_table_row): 使用牛顿前向插值公式进行计算。 参数: x_target (float): 需要插值或预测的目标时间点。 x_start (float): 时间序列的起始点通常为0。 step (float): 时间步长等距间隔对于日数据为1。 diff_table_row (np.array): 差分表的第一行即 [f0, Δ¹f0, Δ²f0, ...]。 返回: float: 在x_target处的插值结果。 # 计算标准化变量 t t (x_target - x_start) / step result diff_table_row[0] # 从f0开始 term 1.0 # 用于累乘 (t-0), (t-1), ..., (t-k1) # k从1开始对应第k阶差分 for k in range(1, len(diff_table_row)): term * (t - (k - 1)) / k result term * diff_table_row[k] return result这个函数是算法的引擎。diff_table_row参数就是我们上一步得到的diff_table[0, :]。step在日线数据中为1如果你处理的是周线间隔为7天则step7。现在让我们用之前的价格数据尝试“预测”第6天之后的一个时点比如第6.5天这只是一个插值演示并非实际交易日# 假设时间点为0,1,2,3,4,5 x_start 0 step 1 # 获取差分表第一行 first_row diff_table[0, :] # 对已知点进行插值验证应该等于原值 print(对已知数据点的插值验证) for i in range(len(closing_prices)): px newton_forward_interpolate(i, x_start, step, first_row) print(f 时间点 x{i}: 原始价格 {closing_prices[i]:.2f}, 插值结果 {px:.2f}) # 尝试一个内插点比如第2.5天 x_interp 2.5 p_interp newton_forward_interpolate(x_interp, x_start, step, first_row) print(f\n内插点 x{x_interp} 的预测价格: {p_interp:.2f})你会看到对于已知的整数时间点0,1,2,3,4,5插值结果与原始价格完全一致可能存在极小的浮点误差这验证了插值函数的正确性。对于非整数点如2.5函数给出了一个平滑的插值结果。4. 实战应用于股票数据与可视化分析理论说得再多不如看一个完整的案例。我们使用yfinance库获取一段真实的股票历史数据应用我们的牛顿插值法并进行可视化对比。首先确保安装必要的库pip install yfinance matplotlib numpy。import yfinance as yf import matplotlib.pyplot as plt import numpy as np from datetime import datetime, timedelta # 1. 获取股票数据 def fetch_stock_data(ticker, start_date, end_date): 获取指定股票在给定时间段的日收盘价。 stock yf.Ticker(ticker) hist stock.history(startstart_date, endend_date) # 确保数据按日期排序 hist hist.sort_index() # 使用调整后收盘价以避免分红、拆股的影响 prices hist[Close].values dates hist.index return dates, prices # 2. 封装完整的牛顿插值流程 def newton_stock_interpolation(ticker, lookback_days10, forecast_days2): 对股票数据进行牛顿插值并做短期外推。 参数: ticker (str): 股票代码。 lookback_days (int): 用于拟合的历史数据天数。 forecast_days (int): 外推预测的天数谨慎使用建议2。 end_date datetime.today() start_date end_date - timedelta(dayslookback_days 20) # 多取一些数据备用 dates, prices_all fetch_stock_data(ticker, start_date, end_date) # 取最近lookback_days天数据用于拟合 fit_prices prices_all[-lookback_days:] fit_dates dates[-lookback_days:] print(f分析股票: {ticker}) print(f拟合数据日期范围: {fit_dates[0].date()} 至 {fit_dates[-1].date()}) print(f拟合价格序列: {fit_prices.round(2)}) # 构建差分表 n len(fit_prices) diff_table np.zeros((n, n)) diff_table[:, 0] fit_prices for j in range(1, n): for i in range(n - j): diff_table[i, j] diff_table[i1, j-1] - diff_table[i, j-1] first_row diff_table[0, :] # 定义插值函数时间点从0开始步长为1天 def interp_func(t): return newton_forward_interpolate(t, 0, 1, first_row) # 3. 在历史区间内进行密集插值绘制平滑曲线 t_fine np.linspace(0, n-1, 200) # 在[0, n-1]区间生成200个点 p_fine np.array([interp_func(t) for t in t_fine]) # 4. 短期外推非常谨慎 t_forecast np.arange(n, n forecast_days) p_forecast np.array([interp_func(t) for t in t_forecast]) forecast_dates [fit_dates[-1] timedelta(daysint(i1)) for i in range(forecast_days)] # 5. 可视化 plt.figure(figsize(12, 7)) # 绘制原始数据点 plt.scatter(range(n), fit_prices, colordarkred, s80, label历史收盘价, zorder5) # 绘制牛顿插值平滑曲线 plt.plot(t_fine, p_fine, b-, linewidth2, label牛顿插值拟合曲线, alpha0.8) # 绘制外推预测点 plt.scatter(t_forecast, p_forecast, colorgreen, s120, markers, edgecolorsblack, labelf{forecast_days}日外推预测, zorder6) for i, (tx, px, dt) in enumerate(zip(t_forecast, p_forecast, forecast_dates)): plt.annotate(f{px:.2f}, xy(tx, px), xytext(0, 10), textcoordsoffset points, hacenter, fontsize9, colorgreen) plt.title(f{ticker} 股价牛顿插值分析与短期外推, fontsize14, fontweightbold) plt.xlabel(时间序列 (交易日), fontsize12) plt.ylabel(价格 (元), fontsize12) plt.grid(True, linestyle--, alpha0.5) plt.legend(locbest) plt.tight_layout() # 6. 打印预测结果 print(\n--- 短期外推预测 ---) for i in range(forecast_days): print(f 预测日期 {forecast_dates[i].date()}: {p_forecast[i]:.2f} 元) # 7. 重要警告 print(\n *60) print(重要提示) print(1. 此预测基于纯数学插值未考虑任何市场基本面、新闻或情绪。) print(2. 多项式外推特别是高阶外推在金融领域风险极高极易产生荒谬结果。) print(3. 本结果仅供方法演示与趋势观察绝对不可作为实际投资依据) print(*60) plt.show() return interp_func, p_forecast # 运行示例使用苹果公司股票代码AAPL请自行替换为有效的股票代码和时间 # 注意由于yfinance需要网络访问且股票数据实时变化以下代码需在可联网环境运行。 # 这里我们注释掉执行部分你可以取消注释并替换ticker进行测试。 # if __name__ __main__: # # 示例分析苹果股票最近10个交易日并外推2天 # newton_stock_interpolation(AAPL, lookback_days10, forecast_days2)这段代码是一个完整的工具链。它完成了数据获取、差分表计算、插值函数构建、历史数据平滑拟合以及谨慎的短期外推。可视化部分将历史数据点、拟合曲线和预测点清晰地展示在同一张图上。关键点在于对预测结果的解读。图中绿色的方块代表外推预测值。你需要观察拟合曲线蓝色在历史数据区间内是否平滑地穿过了所有数据点如果是说明插值成功。拟合曲线在最后几个数据点处的切线方向趋势是什么是向上、走平还是向下牛顿插值的外推本质上是将这个局部趋势线性或非线性地延伸出去。预测点绿色相对于最后一个历史数据点的变化是否合理一个突然的、大幅度的跳跃很可能就是“龙格现象”开始显现的征兆此时应完全不相信这个预测。提示在实际使用中建议将lookback_days参数设置在5到15之间并使用低阶多项式通过观察差分表选择高阶差分接近零的阶数。forecast_days永远不要大于2并且要明白第2天的预测误差通常会远大于第1天。5. 进阶技巧模型评估与阶数选择一个负责任的数据分析必须包含评估环节。对于插值我们可以在历史数据内部进行“模拟外推”来检验其效果这类似于时间序列中的“样本外测试”但更简单。思路是我们只用前m个数据点来拟合牛顿插值多项式然后用这个多项式去“预测”第m1个数据点这个点实际上是已知的计算预测误差。通过滚动这个窗口我们可以评估插值法在近期数据上的表现。def evaluate_newton_interpolation(prices, max_degreeNone): 评估牛顿插值法在历史数据上的滚动预测误差。 参数: prices (np.array): 价格序列。 max_degree (int, optional): 限制使用的最高多项式阶数。默认为数据点-2。 返回: dict: 包含不同阶数下的平均绝对误差(MAE)。 n len(prices) if max_degree is None: max_degree min(n - 2, 6) # 默认最高测试到6阶或n-2阶 errors {k: [] for k in range(1, max_degree 1)} # 滚动窗口用前window_size个点预测第window_size个点 for window_size in range(3, n): # 至少需要3个点才能有1阶以上的差分 train_prices prices[:window_size] true_value prices[window_size] # 要预测的下一个真实值 # 为当前窗口数据构建差分表 current_n len(train_prices) diff_table np.zeros((current_n, current_n)) diff_table[:, 0] train_prices for j in range(1, current_n): for i in range(current_n - j): diff_table[i, j] diff_table[i1, j-1] - diff_table[i, j-1] # 测试不同阶数的多项式 for degree in range(1, min(max_degree, current_n-1) 1): # 只取前degree1阶差分对应degree次多项式 first_row diff_table[0, :degree1] # 预测时间点 t window_size (因为训练数据时间点为0,1,...,window_size-1) pred newton_forward_interpolate(window_size, 0, 1, first_row) error abs(pred - true_value) errors[degree].append(error) # 计算每个阶数的平均绝对误差 mae {k: np.mean(v) if v else np.nan for k, v in errors.items()} # 可视化误差对比 degrees list(mae.keys()) maes list(mae.values()) plt.figure(figsize(10, 5)) bars plt.bar(degrees, maes, colorskyblue, edgecolorblack) plt.xlabel(插值多项式阶数, fontsize12) plt.ylabel(平均绝对误差 (MAE), fontsize12) plt.title(牛顿插值法不同阶数滚动预测误差对比, fontsize14) plt.grid(axisy, linestyle--, alpha0.7) # 在柱子上标注误差值 for bar, m in zip(bars, maes): height bar.get_height() plt.text(bar.get_x() bar.get_width()/2., height 0.005, f{m:.4f}, hacenter, vabottom, fontsize9) # 找出最优误差最小的阶数 valid_mae {k: v for k, v in mae.items() if not np.isnan(v)} if valid_mae: best_degree min(valid_mae, keyvalid_mae.get) best_mae valid_mae[best_degree] print(f\n误差分析结果) for d in degrees: if d in valid_mae: print(f 阶数 {d}: 平均绝对误差 {valid_mae[d]:.4f}) print(f - 建议使用阶数: {best_degree} (MAE {best_mae:.4f})) else: print(数据不足无法进行有效误差分析。) plt.tight_layout() plt.show() return mae # 使用示例数据进行分析 sample_prices np.array([100.0, 101.5, 99.8, 102.2, 103.5, 101.0, 104.0, 102.5]) evaluate_newton_interpolation(sample_prices)运行这个评估函数你会看到一个柱状图清晰地展示不同阶数多项式在滚动预测中的平均绝对误差。一个常见的现象是随着阶数增加误差先下降后上升。下降是因为模型拟合能力增强上升则是因为过拟合开始捕捉数据噪声导致外推能力变差。那个误差最小的阶数往往就是最适合用于短期外推的阶数。这个评估流程为我们使用牛顿插值法提供了一个数据驱动的阶数选择依据而不是盲目猜测。在实际分析股票时你可以定期例如每周运行这个评估看看近期数据的最优阶数是否发生了变化这本身也可能是一种市场波动性变化的暗示。6. 与其他方法的简单对比及注意事项在量化分析的武器库里牛顿插值法应该被放在什么位置为了更直观我们将其与另外两种常见的时间序列处理方法进行快速对比方法核心思想在股价分析中的适用场景与牛顿插值法的对比简单移动平均(SMA)取最近N期的算术平均值平滑短期波动。识别趋势方向、支撑/阻力位。极度简单、稳定。牛顿法更灵活。SMA是线性平滑牛顿法低阶是非线性平滑能更好地贴合局部波动。SMA无法外推牛顿法可以。线性回归拟合一条直线 y a b*t最小化误差平方和。判断长期基本趋势上涨/下跌及其斜率。牛顿法更局部。线性回归强调整体趋势对近期变化不敏感。牛顿插值对近期数据点赋予绝对权重能反映最新变化。线性回归外推是直线牛顿法外推是曲线由多项式阶数决定。牛顿插值法构造一个通过所有给定数据点的多项式。短期数据平滑、极短期趋势外推、缺失数据插补。自身特点完全拟合历史点外推形式明确但风险高。计算速度快适合快速原型验证。最后必须再次强调几个至关重要的注意事项这是我在实际使用中踩过坑才深刻体会到的外推的诅咒金融市场是复杂系统过去的价格模式未来几乎必然失效。牛顿插值法乃至任何基于历史价格模式的数学外推给出的“预测”其准确性随时间呈指数级衰减。把它看作一个“如果近期趋势延续明天可能在哪”的情景模拟而非真正的预测。阶数选择是关键永远不要用与数据点数量相同阶数的多项式那会完美拟合所有噪声。通过前面介绍的滚动误差评估来选择阶数或者保守地直接使用2阶二次或3阶三次多项式。高阶多项式在图表上看起来“拟合”得更好但它的预测往往更离谱。处理非等距数据如果遇到节假日导致的非交易日你的时间序列就不再严格等距。此时需要切换到基于差商的牛顿插值通用形式或者先对数据进行预处理如填充、重采样到等距时间戳。它是分析起点而非终点牛顿插值法生成的平滑曲线和短期外推值可以作为你进一步分析的输入。例如你可以计算实际价格与插值曲线的偏差研究这些“残差”的 pattern或者将插值预测与其他简单指标如SMA的预测进行组合。写完这些代码和分析我回想起最初用它分析一只波动剧烈的股票时曾因为使用了过高的阶数导致外推预测在两天后指向一个荒谬的负价格闹了笑话。这让我彻底明白在金融数据这个领域模型的简洁和可解释性往往比复杂的拟合精度更重要。牛顿插值法就像一把精致的瑞士军刀在正确理解其局限性的前提下用于快速刻画局部趋势、插补缺失值或作为更复杂模型的基准它依然锋利而有效。希望这些代码和思路能为你自己的数据分析工具箱增添一件趁手的工具。