用XGBoost撸一个预测股票涨跌的小工具,顺便把Streamlit和akshare也整明白了

226 阅读6分钟

用XGBoost撸一个预测股票涨跌的小工具,顺便把Streamlit和akshare也整明白了


搞个预测股价的玩意儿,你说香不香?

有一天我发呆,盯着手里的几只票,突然脑子里冒出一个念头:“要是我能提前知道明天涨不涨就好了。”——哦对,这话听着这么玄乎呢? 但等等,我们不是搞玄学,我们搞机器学习,搞XGBoost,搞点建模实战,搞一搞看它能不能“多赚一毛是一毛”。

你可能在想:

“XGBoost不是那种比赛里用来夺冠的东西吗?我拿来炒股是不是有点离谱?”

离谱是真离谱,但是!——不试你怎么知道呢。

image.png


先搞明白几个概念,不然一脸懵

我们要做的事是这样的:

  • 输入过去几年的股价
  • 算出一些指标(什么MA、RSI、MACD这些)
  • 判断:明天这玩意儿会不会涨

不是预测涨多少,只是一个涨 or 不涨,就是一个0 or 1的二分类问题。

听懂了吧?这玩意儿可以喂给XGBoost吃。


Step 1:数据从哪儿来?akshare走一个

你想撸股票的数据,akshare 是宝藏。话说回来,akshare文档是有点随缘(我本人对它的接口命名体感也不太好),但谁让它能免费抓A股呢。

import akshare as ak

stock_code = "000001"  # 平安银行
df = ak.stock_zh_a_hist(
    symbol=stock_code,
    period="daily",
    start_date="20220101",
    end_date="20250514",
    adjust="qfq"  # 可选:'qfq'(前复权)、'hfq'(后复权)、''(不复权)
)
print(df)

注意事项:

  • 不要忘了复权,不然高低点一对比,全是错觉。
  • 日期是字符串,不是datetime,这种小坑很多人踩。

Step 2:特征工程——能提炼点有用信息吗?

光拿收盘价没啥意思,我们得构造一些“技术指标”,给模型点启发。

  • MA(移动平均)
  • RSI(相对强弱指数)
  • MACD(指数平滑移动平均差值)

也可以用其它指标,这里暂时用这3个做演示

咱直接撸代码,不讲大道理:

import talib
import numpy as np

df['MA5'] = talib.SMA(df['收盘'], timeperiod=5)
df['RSI'] = talib.RSI(df['收盘'], timeperiod=14)
df['MACD'], _, _ = talib.MACD(df['收盘'], fastperiod=12, slowperiod=26, signalperiod=9)

还有一个核心点:我们要构造“目标变量”——也就是“明天是否上涨”:

df['target'] = (df['收盘'].shift(-1) > df['收盘']).astype(int)

注意!

  • shift(-1) 的意思是“看明天”
  • astype(int) 是让它变成 0 和 1,XGBoost爱吃这个

再来一招:

df.dropna(inplace=True)  # 特征工程之后一定要丢掉NA,不然模型直接闹罢工

Step 3:XGBoost走起,别怕,这模型不难

安装一下:

pip install xgboost scikit-learn

代码整上:

from xgboost import XGBClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score

features = ['MA5', 'RSI', 'MACD']
X = df[features]
y = df['target']

X_train, X_test, y_train, y_test = train_test_split(X, y, shuffle=False, test_size=0.2)

model = XGBClassifier(n_estimators=100, max_depth=3, learning_rate=0.1)
model.fit(X_train, y_train)

y_pred = model.predict(X_test)
acc = accuracy_score(y_test, y_pred)
print(f"准确率:{acc:.2f}")

问题来了——不shuffle数据可以吗?

可以!在时序数据里,不能乱洗牌! 否则模型就提前知道答案,那是作弊,不是预测。


Step 4:搞个界面给自己和朋友玩玩

别拿命令行唬人,咱用 Streamlit,一点不费劲。

pip install streamlit plotly

Streamlit版本完整代码

import streamlit as st
import akshare as ak
import pandas as pd
import numpy as np
import plotly.graph_objects as go
from xgboost import XGBClassifier
import joblib
import talib
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split

# 用于技术指标计算
def add_features(df):
    df['MA5'] = talib.SMA(df['close'], timeperiod=5)
    df['RSI'] = talib.RSI(df['close'], timeperiod=14)
    df['MACD'], _, _ = talib.MACD(df['close'], fastperiod=12, slowperiod=26, signalperiod=9)
    return df

def prepare_data(df):
    df['target'] = (df['close'].shift(-1) > df['close']).astype(int)
    df = add_features(df)
    df.dropna(inplace=True)
    return df

# 页面标题
st.set_page_config(page_title="股票涨跌预测", layout="wide")
st.title("股票涨跌预测系统")

# 用户输入
stock_code = st.text_input("请输入股票代码(如000001)", "000001")
start_date = st.date_input("起始日期", pd.to_datetime("2023-01-01"))
end_date = st.date_input("结束日期", pd.to_datetime("2025-05-31"))

if st.button("开始预测"):
    try:
        st.info("正在获取数据并处理...")

        df_raw = ak.stock_zh_a_hist(symbol=stock_code, period="daily", start_date=start_date.strftime('%Y%m%d'),
                                    end_date=end_date.strftime('%Y%m%d'), adjust="qfq") # 可选:'qfq'(前复权)、'hfq'(后复权)、''(不复权)
        
        df = df_raw.rename(columns={
            "开盘": "open",
            "收盘": "close",
            "最高": "high",
            "最低": "low",
            "成交量": "volume",
            "日期": "date"
        })
        
        df['date'] = pd.to_datetime(df['date'])
        df.set_index("date", inplace=True)
        df = prepare_data(df)

        features = ['MA5', 'RSI', 'MACD']
        X = df[features]
        y = df['target']

        X_train, X_test, y_train, y_test = train_test_split(X, y, shuffle=False, test_size=0.2)

        model = XGBClassifier(n_estimators=100, max_depth=3, learning_rate=0.1)
        model.fit(X_train, y_train)

        y_pred = model.predict(X_test)
        acc = accuracy_score(y_test, y_pred)
        print(f"预测准确率:{acc:.2f}")
        st.success(f"预测准确率:{acc:.2f}")
        print(df)
        print(len(y_pred))
        # df['pred'] = y_pred
        # df['correct'] = df['pred'] == df['target']
        # 只给测试集对应的df行赋值
        df.loc[X_test.index, 'pred'] = y_pred
        df.loc[X_test.index, 'correct'] = df.loc[X_test.index, 'pred'] == df.loc[X_test.index, 'target']
        print(df)
        
        # 绘制K线图
        fig = go.Figure(data=[
            go.Candlestick(
                x=df.index,
                open=df['open'],
                high=df['high'],
                low=df['low'],
                close=df['close'],
                name="K线"
            )
        ])
        df.dropna(inplace=True)
        # 正确上涨(预测1,实际1)
        correct_df = df[df['correct'] & (df['pred'] == 1)]
        fig.add_trace(go.Scatter(
            x=correct_df.index,
            y=correct_df['close'],
            mode='markers',
            marker=dict(symbol='triangle-up', color='green', size=10),
            name='预测正确上涨'
        ))

        # 预测上涨但错了
        wrong_df = df[~df['correct'] & (df['pred'] == 1)]
        fig.add_trace(go.Scatter(
            x=wrong_df.index,
            y=wrong_df['close'],
            mode='markers',
            marker=dict(symbol='triangle-down', color='red', size=10),
            name='预测错误上涨'
        ))

        st.plotly_chart(fig, use_container_width=True)

        st.caption("本应用仅用于技术演示,不构成投资建议。股市有风险,入市需谨慎。")

    except Exception as e:
        st.error(f"出错啦:{e}")

预测正确的点你可以用绿色三角,错误的红色倒三角标出来。记得加点 legend,别整一堆符号让人看不懂。


模型导出导入,别让你CPU白跑

如果模型训练出来的准确率很高,那模型必须保存下来,方便下次直接使用。

保存模型(推荐原生格式)

model.save_model("xgb_stock_model.json")

也可以用 joblib:

import joblib
joblib.dump(model, "xgb_stock_model.pkl")

加载模型再预测

loaded_model = XGBClassifier()
loaded_model.load_model("xgb_stock_model.json")
pred = loaded_model.predict(X_test)

或者 joblib 方式:

loaded_model = joblib.load("xgb_stock_model.pkl")
pred = loaded_model.predict(X_test)

👀 注意!

  • 预测时的特征顺序必须和训练时一致!不然模型疯了你也疯。
  • 用了 scaler?它也要存!
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
joblib.dump(scaler, "scaler.pkl")

再加载回来:

scaler = joblib.load("scaler.pkl")
X_test = scaler.transform(X_test)

风险提示,不然我怕你报警

这套玩意儿,说白了就是个技术演示,不是让你all in。XGBoost再牛,也只是数学模型,预测不可能100%准。

聪明的你一定会发现——其实搞个预测涨跌的项目,门槛并不高,关键是你肯不肯动手。

如果你觉得文章不错,记得帮花姐点点再看