news 2026/1/29 4:08:37

Day 72:【99天精通Python】金融数据看板 - 数据层实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Day 72:【99天精通Python】金融数据看板 - 数据层实现

Day 72:【99天精通Python】金融数据看板 - 数据层实现

前言

欢迎来到第72天!

在昨天的课程中,我们规划了项目的蓝图。今天,我们要开始打地基——构建数据层
一个没有数据的看板就是个空壳。我们需要做两件事:

  1. 定义模型:告诉 Flask 数据库长什么样。
  2. 填充数据:编写脚本,从 BaoStock 接口把股票数据抓回来存进数据库。

本节内容:

  • 配置 Flask-SQLAlchemy
  • 编写models.py
  • 编写数据抓取服务 (services/data_fetcher.py)
  • 初始化数据库并测试抓取

一、配置 Flask 与 数据库

config.py中存放配置信息。

# config.pyimportos BASE_DIR=os.path.abspath(os.path.dirname(__file__))classConfig:# 数据库文件路径SQLALCHEMY_DATABASE_URI='sqlite:///'+os.path.join(BASE_DIR,'finance.db')# 关闭追踪修改,节省内存SQLALCHEMY_TRACK_MODIFICATIONS=False

app.py中初始化 Flask 和 DB。

# app.pyfromflaskimportFlaskfromflask_sqlalchemyimportSQLAlchemyfromconfigimportConfig app=Flask(__name__)app.config.from_object(Config)# 初始化 SQLAlchemydb=SQLAlchemy(app)# 临时路由测试@app.route("/")defindex():return"Finance Board API"if__name__=="__main__":app.run(debug=True)

二、编写数据模型 (models.py)

根据昨天的设计,我们需要两个模型。

# models.pyfromappimportdbclassStockBasic(db.Model):__tablename__='stock_basic'# code 是主键,如 'sh.600519'code=db.Column(db.String(20),primary_key=True)code_name=db.Column(db.String(50))industry=db.Column(db.String(50))update_date=db.Column(db.Date)# 上次更新数据的日期def__repr__(self):returnf"<Stock{self.code_name}>"classStockDaily(db.Model):__tablename__='stock_daily'id=db.Column(db.Integer,primary_key=True)# 建立索引加快查询速度code=db.Column(db.String(20),index=True)date=db.Column(db.Date,index=True)open=db.Column(db.Float)high=db.Column(db.Float)low=db.Column(db.Float)close=db.Column(db.Float)volume=db.Column(db.Float)# 联合唯一索引:同一只股票同一天只能有一条数据__table_args__=(db.UniqueConstraint('code','date',name='unique_code_date'),)

初始化数据库:
打开终端进入项目目录:

python>>>from appimportdb, app>>>with app.app_context():... db.create_all()>>>exit()

此时目录下应该生成了finance.db


三、编写数据抓取服务

我们需要一个独立的模块来处理与 BaoStock 的交互。

新建services/data_fetcher.py

importbaostockasbsimportpandasaspdfromdatetimeimportdatetime,timedeltafromappimportdb,appfrommodelsimportStockBasic,StockDailyfromsqlalchemy.excimportIntegrityErrorclassDataFetcher:def__init__(self):self.system=bs.login()def__del__(self):bs.logout()deffetch_stock_list(self):"""获取所有 A 股列表并存入 StockBasic"""# 获取当天日期date=datetime.now().strftime("%Y-%m-%d")# query_all_stock 接口获取所有股票rs=bs.query_all_stock(day=date)data_list=[]whilers.next():data_list.append(rs.get_row_data())# BaoStock 返回: code, tradeStatus, code_nameforrowindata_list:code,status,name=row# 只存还在交易的股票ifstatus=='1'andcode.startswith(('sh','sz')):# 存入数据库stock=StockBasic.query.get(code)ifnotstock:stock=StockBasic(code=code,code_name=name)db.session.add(stock)db.session.commit()print(f"股票列表更新完成,共{len(data_list)}条")deffetch_daily_data(self,code,start_date=None,end_date=None):"""获取某只股票的日线数据"""ifnotend_date:end_date=datetime.now().strftime("%Y-%m-%d")ifnotstart_date:# 默认抓取过去 1 年start_date=(datetime.now()-timedelta(days=365)).strftime("%Y-%m-%d")rs=bs.query_history_k_data_plus(code,"date,open,high,low,close,volume",start_date=start_date,end_date=end_date,frequency="d",adjustflag="3")data_list=[]whilers.next():data_list.append(rs.get_row_data())# 批量入库forrowindata_list:# row: [date, open, high, low, close, volume]try:daily=StockDaily(code=code,date=datetime.strptime(row[0],"%Y-%m-%d").date(),open=float(row[1]),high=float(row[2]),low=float(row[3]),close=float(row[4]),volume=float(row[5])ifrow[5]else0)db.session.add(daily)exceptValueError:continue# 跳过无效数据try:db.session.commit()print(f"{code}数据导入成功,共{len(data_list)}条")exceptIntegrityError:db.session.rollback()print(f"{code}数据部分重复,已回滚")# 测试代码if__name__=="__main__":# 需要在 app context 下运行,因为用到了 dbwithapp.app_context():fetcher=DataFetcher()# 1. 更新股票列表 (跑一次就行)# fetcher.fetch_stock_list()# 2. 更新茅台数据fetcher.fetch_daily_data("sh.600519")

四、运行与验证

  1. 运行python services/data_fetcher.py
  2. 观察控制台输出,确认数据下载成功。
  3. 使用 DB Browser 打开finance.db,查看stock_daily表,应该能看到一整年的茅台股价数据。

五、小结

query_all_stock

query_history_k

db.session.add

DataFetcher

BaoStock API

Models

股票列表

日线数据

SQLite

关键要点

  1. ORM 模型StockBasic存元数据,StockDaily存时间序列数据。
  2. UniqueConstraint:数据库层面的约束,防止同一天插入两条重复数据。
  3. App Context:在独立的脚本中使用 Flask 的db对象时,必须包裹在app.app_context()中。

六、课后作业

  1. 完善抓取逻辑:目前的fetch_stock_list比较慢,尝试只抓取我们感兴趣的几只股票(如上证50)。
  2. 断点更新:修改fetch_daily_data,每次抓取前先查询数据库,找出这只股票最新的一条数据的日期,然后只抓取那个日期之后的数据(增量更新)。
  3. 异常处理:如果在抓取过程中网络断了怎么办?给fetcher增加重试机制。

下节预告

Day 73:金融数据看板 - 后端接口与数据分析- 数据有了,明天我们写 API 接口,并计算 MA 均线,为前端绘图提供 JSON 数据。


系列导航

  • 上一篇:Day 71 - 项目篇开篇
  • 下一篇:Day 73 - 金融数据看板后端逻辑(待更新)
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/1/25 4:11:30

树莓派5安装ROS2高效配置方法总结

树莓派5安装ROS2&#xff1a;从零开始的高效部署实战 你是不是也经历过这样的场景&#xff1f;刚拿到崭新的树莓派5&#xff0c;满心欢喜地想搭建一个机器人控制系统&#xff0c;结果在“安装ROS2”这一步卡了整整三天——依赖报错、编译失败、版本冲突……最后只能放弃&#…

作者头像 李华
网站建设 2026/1/17 2:19:55

Intel平台下eSPI枚举过程解析:深度剖析

Intel平台下eSPI枚举过程解析&#xff1a;从协议到实战的深度拆解你有没有遇到过这样的情况——笔记本明明按了电源键&#xff0c;却像“死机”一样毫无反应&#xff1f;或者系统进入睡眠后无法唤醒&#xff0c;风扇狂转、屏幕黑屏&#xff1f;在排查这类低功耗管理故障时&…

作者头像 李华
网站建设 2026/1/23 11:51:14

GTE中文语义相似度服务代码实例:自动化标注系统开发

GTE中文语义相似度服务代码实例&#xff1a;自动化标注系统开发 1. 引言 1.1 业务场景描述 在自然语言处理&#xff08;NLP&#xff09;的实际工程落地中&#xff0c;文本语义相似度计算是一项高频且关键的需求。无论是智能客服中的意图匹配、推荐系统中的内容去重&#xff…

作者头像 李华
网站建设 2026/1/26 9:58:50

想换模型怎么操作?麦橘超然扩展性说明

想换模型怎么操作&#xff1f;麦橘超然扩展性说明 1. 引言&#xff1a;轻量化图像生成的可扩展性需求 随着AI绘画在个人设备和边缘计算场景中的普及&#xff0c;用户对模型多样性的需求日益增长。尽管“麦橘超然 - Flux 离线图像生成控制台”默认集成了 majicflus_v1 模型并采…

作者头像 李华
网站建设 2026/1/22 11:26:53

Z-Image-ComfyUI工作流分享:导出导入JSON文件的操作步骤

Z-Image-ComfyUI工作流分享&#xff1a;导出导入JSON文件的操作步骤 1. 引言 1.1 业务场景描述 在当前AIGC&#xff08;人工智能生成内容&#xff09;快速发展的背景下&#xff0c;图像生成模型的应用日益广泛。Z-Image-ComfyUI作为基于阿里最新开源文生图大模型Z-Image的可…

作者头像 李华
网站建设 2026/1/24 20:30:10

树莓派换源零基础指南:网络环境要求

树莓派换源实战指南&#xff1a;从卡顿到飞速的秘诀你有没有遇到过这种情况&#xff1a;刚入手树莓派&#xff0c;兴致勃勃地打开终端准备安装Python库或者升级系统&#xff0c;结果一条sudo apt update执行下去&#xff0c;半天不动&#xff0c;进度条像被冻住了一样&#xff…

作者头像 李华