完整源码链接https://pan.quark.cn/s/1e54aa2ae950我*终于搞完了第二版这次用的是真实数据直接从akshare拉再也不用那个破几何布朗运动生成假数据了。上次导师看了一眼说你这数据走势也太完美了吧一看就是生成的我当场尴尬得想钻地缝。行吧那就改成真实数据呗结果这坑比我想象的深多了。下面看下效果展示先说说改了什么原来的 data_generator.py 整个删了换成了 data_fetcher.py用 akshare 库拉真实 A 股数据。然后 main.py 改成交互式平台了启动之后能选股票跑全部流程出7张图。visualize.py 加了第7张综合看板一页集成所有关键信息。先给你们看最终的项目结构2-基于LSTM-Attention的股票价格预测与可视化平台/ ├── data_fetcher.py # 真实数据获取 (akshare 代理绕过) ├── model.py # LSTM Attention (没动) ├── train.py # 训练 (微调) ├── predict.py # 预测评估 (微调) ├── visualize.py # 可视化 综合看板 ├── main.py # 交互式平台主入口 ├── requirements.txt └── charts/ ├── 000001_平安银行_price_trend.png ├── 000001_平安银行_pred_vs_actual.png ├── 000001_平安银行_attention_heatmap.png ├── 000001_平安银行_loss_curves.png ├── 000001_平安银行_error_dist.png ├── 000001_平安银行_future_pred.png └── 000001_平安银行_dashboard.png好先说数据获取这块他*的踩了一个史诗级大坑。akshare 这个库底层用的是 requests而我的电脑上挂着****软件C*la*h*结果 requests 请求访问东方财富的 API直接被拒连接。proxy 这个破问题折腾了我两天查了各种资料最后发现在创建 Session 的时候把 proxies 强行设成 None 才行。import requests _old_request requests.Session.request def _new_request(*args, **kwargs): kwargs[proxies] {http: None, https: None} return _old_request(*args, **kwargs) requests.Session.request _new_request这行代码必须放在所有 import 的前面在 akshare 被加载之前就把 requests 给 patch 了不然没用。还要额外清空环境变量os.environ[HTTP_PROXY] os.environ[HTTPS_PROXY] os.environ[http_proxy] os.environ[https_proxy] 这里有个坑是他的 agent 顺序问题因为 akshare 自己内部也会 import requests所以这个 monkey patch 必须在 import akshare 之前执行。我把这段代码塞到了 data_fetcher.py 最顶上import 语句的前面才总算把代理问题解决了。然后还有个问题akshare 获取全市场股票列表的那个接口ak.stock_zh_a_spot_em()经常抽风连不上我加了缓存机制和兜底方案。缓存一个小时有效如果接口挂了就用预设的 18 只热门股票。这 18 只够用了平安银行、茅台、宁德时代这些。PRESET_STOCKS { 000001: 平安银行, 000002: 万科A, 000333: 美的集团, 000651: 格力电器, 000858: 五粮液, 002415: 海康威视, 002475: 立讯精密, 300750: 宁德时代, 600036: 招商银行, 600519: 贵州茅台, 600900: 长江电力, 601012: 隆基绿能, 601318: 中国平安, 601398: 工商银行, ... }真实数据获取用ak.stock_zh_a_hist()参数是 symbol6位代码、perioddaily、start_date、end_date、adjustqfq 前复权。前复权这个很重要不调整的话分红送股会导致价格跳空模型学到错误模式。def fetch_stock_data(symbol, start_date, end_date, max_retries3): for attempt in range(max_retries): try: time.sleep(random.uniform(0.15, 0.35)) df ak.stock_zh_a_hist( symbolsymbol, perioddaily, start_datestart_date, end_dateend_date, adjustqfq ) ...随机延时 0.15 到 0.35 秒加了三重重试避免被东方财富封 IP。实测拉平安银行5年数据1500多条不到1秒就搞定了速度还行。数据拿到以后列名是中文的得映射成英文。这里我当时没注意到「成交额」「振幅」「涨跌幅」这些列名返回可能有差异不同股票返回的字段居然不一样有的有换手率有的没有所以用了一个字典映射只在列存在的时候才改名。rename_map {} for old, new in [(日期, date), (开盘, open), (收盘, close), (最高, high), (最低, low), (成交量, volume), (成交额, amount), (振幅, amplitude), (涨跌幅, pct_chg), (涨跌额, price_change), (换手率, turnover)]: if old in df.columns: rename_map[old] new df df.rename(columnsrename_map)技术指标加的跟之前差不多MA5、MA20、波动率、成交量变化率、价格区间位置这些。但真实数据有个问题——缺失值多。因为股票有停牌、节假日不交易rolling 窗口计算的时候会出现 NaN。比如刚上市的新股前20天算不了MA20。所以必须 dropna丢掉的数据有点多但没办法。df[ma5] df[close].rolling(5).mean() df[ma20] df[close].rolling(20).mean() df[volatility] df[ret].rolling(20).std() df[price_position] ((df[close] - df[low].rolling(20).min()) / (df[high].rolling(20).max() - df[low].rolling(20).min() 1e-8)) df df.dropna().reset_index(dropTrue)这里有个他吗的 bug 我找了半天price_position 那一行我一开始写的是rolling(20)多了一个单引号Python 直接报 SyntaxError。我*这种低级错误 debug 了半小时才发现眼瞎了属于是。然后是主程序 main.py 改成平台化的交互模式启动先显示 ASCII art 的 banner然后列出18只热门股票让用户选。平台分四个阶段跑获取真实行情数据训练 LSTM-Attention 模型模型评估生成可视化图表7张def run_platform(): code, name select_stock() print(f选定股票: {code} - {name}) df fetch_and_prepare(code, years5) result train_model(dfdf, feature_colsFEATURE_COLS, ...) metrics evaluate_model(...) # 生成7张图表 ...训练的时候有个问题真实数据比生成数据噪音大得多模型收敛慢。用真实数据跑平安银行Loss 从 0.49 降到 0.036验证集MAPE 大概 4.78%。4.78% 的误差看起来还行但方向准确率只有 49.66%几乎就是瞎猜。这说明模型学到了价格的平均水平但完全判断不了涨跌方向。这其实很正常股票预测要是能超过 50% 的方向准确率我还写什么毕设早去量化私募了。看综合看板那张图dashboard.png一页集成了所有信息左上大图是完整价格走势 MA20 预测分界线 未来预测中上是测试集预测对比中中是注意力热力图中下是损失曲线左下是误差分布直方图中是散点图右下面板展示所有指标Attention 热力图这张图还是挺有东西的模型对不同的样本关注的时间段不一样。有的样本关注近期最后几个时间步权重高有的关注中期。说明 Attention 机制确实在学习不同的时间模式不只是机械地看最后几天的价格。未来30天预测那个图因为用的是滚动预测误差会累积。平安银行当前价格 10.76预测30天后大概在 10.5 左右震荡。这种预测其实准确性存疑但趋势方向可能有点参考价值。看历史数据的话平安银行从 2024 年开始就在 10-12 块之间横盘确实没什么大波动。有一次我训练贵州茅台600519的数据MAPE 直接干到 2% 以下因为茅台的走势太稳了几乎是一条斜线往上模型学起来当然容易。现在这版代码里有个问题我还没解决——获取全市场股票列表的 API 有时会超时。我加了每小时缓存的机制第一次跑的时候如果列表 API 挂了就用预设的 18 只热门股作为备选不影响正常使用。还有个 bug 是数据量不够的问题。像科创板有些股票上市不到一年数据量可能不到 120 条60 条序列长度 至少 60 条训练模型没法训练。data_fetcher 里我加了判断不够的话自动扩大时间范围重试。if df is None or len(df) 120: start end - timedelta(daysint(years * 365 * 2)) df fetch_stock_data(symbol, start.strftime(%Y%m%d), end.strftime(%Y%m%d)) if df is None or len(df) 120: raise ValueError(f无法获取 {symbol} 足够数据)SimHei 字体的问题这次也遇到了因为换了新电脑重新装的环境matplotlib 又找不到 SimHei 了。plt.rcParams[font.sysfont]试了半天没用最后确认必须是plt.rcParams[font.sans-serif] [SimHei]而且axes.unicode_minus必须设 False 不然负号显示异常。总的来说这版用真实数据比之前的好太多了至少答辩的时候能说数据来自东方财富真实A股行情听起来就专业。交互式选股的功能也让这个项目更像是平台而不只是一个脚本。而且真实数据的噪声和不确定性让结果更有说服力——MAPE 4.78% 不是完美的结果但真实可信。如果导师问为什么方向准确率不高标准回答是股票市场受多种因素影响单纯量价数据无法完全预测涨跌方向这恰恰说明了市场的有效性笑。最后再强调一下跑之前确保网络能正常访问东方财富的 API如果挂了实在不行就用我预设的18只股票也是国内最有代表性的公司了。
基于LSTM-Attention的股票价格预测与可视化平台
完整源码链接https://pan.quark.cn/s/1e54aa2ae950我*终于搞完了第二版这次用的是真实数据直接从akshare拉再也不用那个破几何布朗运动生成假数据了。上次导师看了一眼说你这数据走势也太完美了吧一看就是生成的我当场尴尬得想钻地缝。行吧那就改成真实数据呗结果这坑比我想象的深多了。下面看下效果展示先说说改了什么原来的 data_generator.py 整个删了换成了 data_fetcher.py用 akshare 库拉真实 A 股数据。然后 main.py 改成交互式平台了启动之后能选股票跑全部流程出7张图。visualize.py 加了第7张综合看板一页集成所有关键信息。先给你们看最终的项目结构2-基于LSTM-Attention的股票价格预测与可视化平台/ ├── data_fetcher.py # 真实数据获取 (akshare 代理绕过) ├── model.py # LSTM Attention (没动) ├── train.py # 训练 (微调) ├── predict.py # 预测评估 (微调) ├── visualize.py # 可视化 综合看板 ├── main.py # 交互式平台主入口 ├── requirements.txt └── charts/ ├── 000001_平安银行_price_trend.png ├── 000001_平安银行_pred_vs_actual.png ├── 000001_平安银行_attention_heatmap.png ├── 000001_平安银行_loss_curves.png ├── 000001_平安银行_error_dist.png ├── 000001_平安银行_future_pred.png └── 000001_平安银行_dashboard.png好先说数据获取这块他*的踩了一个史诗级大坑。akshare 这个库底层用的是 requests而我的电脑上挂着****软件C*la*h*结果 requests 请求访问东方财富的 API直接被拒连接。proxy 这个破问题折腾了我两天查了各种资料最后发现在创建 Session 的时候把 proxies 强行设成 None 才行。import requests _old_request requests.Session.request def _new_request(*args, **kwargs): kwargs[proxies] {http: None, https: None} return _old_request(*args, **kwargs) requests.Session.request _new_request这行代码必须放在所有 import 的前面在 akshare 被加载之前就把 requests 给 patch 了不然没用。还要额外清空环境变量os.environ[HTTP_PROXY] os.environ[HTTPS_PROXY] os.environ[http_proxy] os.environ[https_proxy] 这里有个坑是他的 agent 顺序问题因为 akshare 自己内部也会 import requests所以这个 monkey patch 必须在 import akshare 之前执行。我把这段代码塞到了 data_fetcher.py 最顶上import 语句的前面才总算把代理问题解决了。然后还有个问题akshare 获取全市场股票列表的那个接口ak.stock_zh_a_spot_em()经常抽风连不上我加了缓存机制和兜底方案。缓存一个小时有效如果接口挂了就用预设的 18 只热门股票。这 18 只够用了平安银行、茅台、宁德时代这些。PRESET_STOCKS { 000001: 平安银行, 000002: 万科A, 000333: 美的集团, 000651: 格力电器, 000858: 五粮液, 002415: 海康威视, 002475: 立讯精密, 300750: 宁德时代, 600036: 招商银行, 600519: 贵州茅台, 600900: 长江电力, 601012: 隆基绿能, 601318: 中国平安, 601398: 工商银行, ... }真实数据获取用ak.stock_zh_a_hist()参数是 symbol6位代码、perioddaily、start_date、end_date、adjustqfq 前复权。前复权这个很重要不调整的话分红送股会导致价格跳空模型学到错误模式。def fetch_stock_data(symbol, start_date, end_date, max_retries3): for attempt in range(max_retries): try: time.sleep(random.uniform(0.15, 0.35)) df ak.stock_zh_a_hist( symbolsymbol, perioddaily, start_datestart_date, end_dateend_date, adjustqfq ) ...随机延时 0.15 到 0.35 秒加了三重重试避免被东方财富封 IP。实测拉平安银行5年数据1500多条不到1秒就搞定了速度还行。数据拿到以后列名是中文的得映射成英文。这里我当时没注意到「成交额」「振幅」「涨跌幅」这些列名返回可能有差异不同股票返回的字段居然不一样有的有换手率有的没有所以用了一个字典映射只在列存在的时候才改名。rename_map {} for old, new in [(日期, date), (开盘, open), (收盘, close), (最高, high), (最低, low), (成交量, volume), (成交额, amount), (振幅, amplitude), (涨跌幅, pct_chg), (涨跌额, price_change), (换手率, turnover)]: if old in df.columns: rename_map[old] new df df.rename(columnsrename_map)技术指标加的跟之前差不多MA5、MA20、波动率、成交量变化率、价格区间位置这些。但真实数据有个问题——缺失值多。因为股票有停牌、节假日不交易rolling 窗口计算的时候会出现 NaN。比如刚上市的新股前20天算不了MA20。所以必须 dropna丢掉的数据有点多但没办法。df[ma5] df[close].rolling(5).mean() df[ma20] df[close].rolling(20).mean() df[volatility] df[ret].rolling(20).std() df[price_position] ((df[close] - df[low].rolling(20).min()) / (df[high].rolling(20).max() - df[low].rolling(20).min() 1e-8)) df df.dropna().reset_index(dropTrue)这里有个他吗的 bug 我找了半天price_position 那一行我一开始写的是rolling(20)多了一个单引号Python 直接报 SyntaxError。我*这种低级错误 debug 了半小时才发现眼瞎了属于是。然后是主程序 main.py 改成平台化的交互模式启动先显示 ASCII art 的 banner然后列出18只热门股票让用户选。平台分四个阶段跑获取真实行情数据训练 LSTM-Attention 模型模型评估生成可视化图表7张def run_platform(): code, name select_stock() print(f选定股票: {code} - {name}) df fetch_and_prepare(code, years5) result train_model(dfdf, feature_colsFEATURE_COLS, ...) metrics evaluate_model(...) # 生成7张图表 ...训练的时候有个问题真实数据比生成数据噪音大得多模型收敛慢。用真实数据跑平安银行Loss 从 0.49 降到 0.036验证集MAPE 大概 4.78%。4.78% 的误差看起来还行但方向准确率只有 49.66%几乎就是瞎猜。这说明模型学到了价格的平均水平但完全判断不了涨跌方向。这其实很正常股票预测要是能超过 50% 的方向准确率我还写什么毕设早去量化私募了。看综合看板那张图dashboard.png一页集成了所有信息左上大图是完整价格走势 MA20 预测分界线 未来预测中上是测试集预测对比中中是注意力热力图中下是损失曲线左下是误差分布直方图中是散点图右下面板展示所有指标Attention 热力图这张图还是挺有东西的模型对不同的样本关注的时间段不一样。有的样本关注近期最后几个时间步权重高有的关注中期。说明 Attention 机制确实在学习不同的时间模式不只是机械地看最后几天的价格。未来30天预测那个图因为用的是滚动预测误差会累积。平安银行当前价格 10.76预测30天后大概在 10.5 左右震荡。这种预测其实准确性存疑但趋势方向可能有点参考价值。看历史数据的话平安银行从 2024 年开始就在 10-12 块之间横盘确实没什么大波动。有一次我训练贵州茅台600519的数据MAPE 直接干到 2% 以下因为茅台的走势太稳了几乎是一条斜线往上模型学起来当然容易。现在这版代码里有个问题我还没解决——获取全市场股票列表的 API 有时会超时。我加了每小时缓存的机制第一次跑的时候如果列表 API 挂了就用预设的 18 只热门股作为备选不影响正常使用。还有个 bug 是数据量不够的问题。像科创板有些股票上市不到一年数据量可能不到 120 条60 条序列长度 至少 60 条训练模型没法训练。data_fetcher 里我加了判断不够的话自动扩大时间范围重试。if df is None or len(df) 120: start end - timedelta(daysint(years * 365 * 2)) df fetch_stock_data(symbol, start.strftime(%Y%m%d), end.strftime(%Y%m%d)) if df is None or len(df) 120: raise ValueError(f无法获取 {symbol} 足够数据)SimHei 字体的问题这次也遇到了因为换了新电脑重新装的环境matplotlib 又找不到 SimHei 了。plt.rcParams[font.sysfont]试了半天没用最后确认必须是plt.rcParams[font.sans-serif] [SimHei]而且axes.unicode_minus必须设 False 不然负号显示异常。总的来说这版用真实数据比之前的好太多了至少答辩的时候能说数据来自东方财富真实A股行情听起来就专业。交互式选股的功能也让这个项目更像是平台而不只是一个脚本。而且真实数据的噪声和不确定性让结果更有说服力——MAPE 4.78% 不是完美的结果但真实可信。如果导师问为什么方向准确率不高标准回答是股票市场受多种因素影响单纯量价数据无法完全预测涨跌方向这恰恰说明了市场的有效性笑。最后再强调一下跑之前确保网络能正常访问东方财富的 API如果挂了实在不行就用我预设的18只股票也是国内最有代表性的公司了。